From 615dc52d77a6e9a051245ce7840f147ac3b3ebf3 Mon Sep 17 00:00:00 2001 From: Lee Yi Jie Joel Date: Thu, 2 Jan 2020 14:10:30 -0800 Subject: [PATCH 001/825] Add are you alive example Signed-off-by: Lee Yi Jie Joel --- examples/are-you-alive/Gopkg.lock | 123 ++++++ examples/are-you-alive/Gopkg.toml | 29 ++ examples/are-you-alive/Makefile | 15 + examples/are-you-alive/README.md | 84 +++++ .../are-you-alive/cmd/are-you-alive/main.go | 352 ++++++++++++++++++ examples/are-you-alive/deploy/README.md | 5 + examples/are-you-alive/docker-compose.yml | 29 ++ examples/are-you-alive/pkg/client/client.go | 347 +++++++++++++++++ examples/are-you-alive/prometheus.yml | 34 ++ examples/are-you-alive/schemas/README.md | 9 + .../schemas/create_test_table.sql | 6 + examples/are-you-alive/schemas/vschema.json | 18 + 12 files changed, 1051 insertions(+) create mode 100644 examples/are-you-alive/Gopkg.lock create mode 100644 examples/are-you-alive/Gopkg.toml create mode 100644 examples/are-you-alive/Makefile create mode 100644 examples/are-you-alive/README.md create mode 100644 examples/are-you-alive/cmd/are-you-alive/main.go create mode 100644 examples/are-you-alive/deploy/README.md create mode 100644 examples/are-you-alive/docker-compose.yml create mode 100644 examples/are-you-alive/pkg/client/client.go create mode 100644 examples/are-you-alive/prometheus.yml create mode 100644 examples/are-you-alive/schemas/README.md create mode 100644 examples/are-you-alive/schemas/create_test_table.sql create mode 100644 examples/are-you-alive/schemas/vschema.json diff --git a/examples/are-you-alive/Gopkg.lock b/examples/are-you-alive/Gopkg.lock new file mode 100644 index 00000000000..06d76526790 --- /dev/null +++ b/examples/are-you-alive/Gopkg.lock @@ -0,0 +1,123 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "UT" + revision = "4b2b341e8d7715fae06375aa633dbb6e91b3fb46" + version = "v1.0.0" + +[[projects]] + digest = "1:ec6f9bf5e274c833c911923c9193867f3f18788c461f76f05f62bb1510e0ae65" + name = "github.com/go-sql-driver/mysql" + packages = ["."] + pruneopts = "UT" + revision = "72cd26f257d44c1114970e19afddcd812016007e" + version = "v1.4.1" + +[[projects]] + digest = "1:318f1c959a8a740366fce4b1e1eb2fd914036b4af58fbd0a003349b305f118ad" + name = "github.com/golang/protobuf" + packages = ["proto"] + pruneopts = "UT" + revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30" + version = "v1.3.1" + +[[projects]] + digest = "1:31e761d97c76151dde79e9d28964a812c46efc5baee4085b86f68f0c654450de" + name = "github.com/konsorten/go-windows-terminal-sequences" + packages = ["."] + pruneopts = "UT" + revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e" + version = "v1.0.2" + +[[projects]] + digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "UT" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + +[[projects]] + digest = "1:1db19cfa69213ff994b19451023750122862f39643a5f37728126e1b5180eaac" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/internal", + "prometheus/promauto", + "prometheus/promhttp", + ] + pruneopts = "UT" + revision = "50c4339db732beb2165735d2cde0bff78eb3c5a5" + version = "v0.9.3" + +[[projects]] + branch = "master" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "UT" + revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8" + +[[projects]] + digest = "1:8dcedf2e8f06c7f94e48267dea0bc0be261fa97b377f3ae3e87843a92a549481" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "UT" + revision = "17f5ca1748182ddf24fc33a5a7caaaf790a52fcc" + version = "v0.4.1" + +[[projects]] + branch = "master" + digest = "1:77b841555ca2ce5a45d7ba8b3eea5bd6e2e50c139d979b9b10f57a30fbd1132c" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/fs", + ] + pruneopts = "UT" + revision = "a7aeb8df3389edaf53efcc319ad0063cb27c3960" + +[[projects]] + digest = "1:04457f9f6f3ffc5fea48e71d62f2ca256637dee0a04d710288e27e05c8b41976" + name = "github.com/sirupsen/logrus" + packages = ["."] + pruneopts = "UT" + revision = "839c75faf7f98a33d445d181f3018b5c3409a45e" + version = "v1.4.2" + +[[projects]] + branch = "master" + digest = "1:9ca267f99487ef450fff0c2a49eaf0788d9ff3562bbaeff19f564d380f84bc6d" + name = "golang.org/x/sys" + packages = ["unix"] + pruneopts = "UT" + revision = "dbbf3f1254d491605cf4a0034ce25d0dc71b0c58" + +[[projects]] + digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3" + name = "google.golang.org/appengine" + packages = ["cloudsql"] + pruneopts = "UT" + revision = "4c25cacc810c02874000e4f7071286a8e96b2515" + version = "v1.6.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/go-sql-driver/mysql", + "github.com/prometheus/client_golang/prometheus", + "github.com/prometheus/client_golang/prometheus/promauto", + "github.com/prometheus/client_golang/prometheus/promhttp", + "github.com/sirupsen/logrus", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/examples/are-you-alive/Gopkg.toml b/examples/are-you-alive/Gopkg.toml new file mode 100644 index 00000000000..2765dc95c4e --- /dev/null +++ b/examples/are-you-alive/Gopkg.toml @@ -0,0 +1,29 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + +[prune] + go-tests = true + unused-packages = true diff --git a/examples/are-you-alive/Makefile b/examples/are-you-alive/Makefile new file mode 100644 index 00000000000..5461370576d --- /dev/null +++ b/examples/are-you-alive/Makefile @@ -0,0 +1,15 @@ +# We are using the "dev" project in our registry for this +NAME := "us.gcr.io/planetscale-dev/are-you-alive" +TAG := $$(git log -1 --pretty=%H) +IMG := ${NAME}:${TAG} +LATEST := ${NAME}:latest + +.PHONY: build push + +build: + @docker build -f build/release/Dockerfile -t ${IMG} . + @docker tag ${IMG} ${LATEST} + +push: + @docker push ${IMG} + @docker push ${LATEST} diff --git a/examples/are-you-alive/README.md b/examples/are-you-alive/README.md new file mode 100644 index 00000000000..889ba4f0e01 --- /dev/null +++ b/examples/are-you-alive/README.md @@ -0,0 +1,84 @@ +# Are You Alive? + +What does it mean to be alive? + +Well we don't know what it means for you, but we know what it means for our +Cloud Database! + +This project contains a simulated client application that can be used to measure +the health of a Vitess cluster over time. + +## Design + +For now, there is a specific database schema and vschema that you must apply to +the database that you are using for this test. + +This client application: + +1. Hammers the database with random data (not a load test though). +1. Measures all the important things: + - Client connection errors + - Write latency + - Read latency from masters + - Read latency from replicas + - Write errors + - Read errors on masters + - Write errors on replicas + - Errors in other operations on masters and replicas (e.g. COUNT) + - Latency on other operations on masters and replicas (e.g. COUNT) + - Data loss (by writing predictable data and testing for that) +1. Reports all these metrics to Prometheus. + +That's it! Keep it as simple and generic as possible, and someday our customers +can use this to test their clusters too! + +## Usage + +First, [initialize your database with the correct schemas](schemas/README.md). + +Run `are-you-alive --help` for usage. You can us the command line flags to +control the dataset size, whether to target reads at masters and replicas, your +mysql connection string, and the rate at which to send requests. + +Example: + +``` +./are-you-alive --mysql_connection_string +``` + +Where `` points to the database you are trying to test, +and everything else will be set to defaults. + +## Building + +``` +dep ensure -v +go build github.com/planetscale/are-you-alive/cmd/are-you-alive +``` + +## Testing + +First, [install docker compose](https://docs.docker.com/compose/install/) and +make sure it's working. Then run: + +``` +dep ensure -v +docker-compose build +docker-compose up +``` + +This will create a local mysqld and a local prometheus to scrape the app. It +will also start the app with the `--initialize` flag which tells it to +automatically create the test database. You might have to run this twice to +give mysql a chance to do its first initialization. + +After you run docker compose, navigate to `http://localhost:9090` to see +Prometheus and `http://localhost:8080/metrics` to see the raw metrics being +exported. + +## Push to Registry + +``` +make build +make push +``` diff --git a/examples/are-you-alive/cmd/are-you-alive/main.go b/examples/are-you-alive/cmd/are-you-alive/main.go new file mode 100644 index 00000000000..5433f4d9bf5 --- /dev/null +++ b/examples/are-you-alive/cmd/are-you-alive/main.go @@ -0,0 +1,352 @@ +package main + +import ( + "database/sql" + "flag" + "fmt" + "github.com/go-sql-driver/mysql" + "github.com/planetscale/are-you-alive/pkg/client" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" + "math/rand" + "net/http" + "os" + "os/signal" + "sync" + "time" +) + +/* +* To measure data loss, we need predictable writes. We do this with "minPage" +* and "maxPage". Once the difference between them is our desired dataset size, +* we can start deleting old records, but we expect to find one record for every +* "page" number between "minPage" and "maxPage". +* +* We don't measure "update loss" with this client right now. + */ + +var ( + maxPage = 0 + minPage = 0 + dataLossEvents = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "are_you_alive_data_loss_events", + Help: "Data loss events", + }, + []string{"database_name"}, + ) +) + +func writeNextRecord(environmentName string, connectionString string) error { + + // 1. Call monitored client + err := client.Write(environmentName, connectionString, maxPage) + if err != nil { + // Check to see if this is a duplicate key error. We've seen this + // sometimes happen, and when it does this client app gets stuck in an + // infinite loop of failure to write a duplicate key. It's possible + // that happens because a write is succesful but something goes wrong + // before the client recieves a response, so the client thinks the write + // failed and does not increment the count. + // + // So when we specifically see a duplicate key error, assume that's what + // happened, bump the count, and move on. + // + // See https://github.com/planetscale/planetscale-operator/issues/1776 + if me, ok := err.(*mysql.MySQLError); ok && me.Number == 1062 { + logrus.WithError(err).Warnf( + "Key '%d' already found, incrementing count", maxPage) + maxPage = maxPage + 1 + return nil + } + + logrus.WithError(err).Error("Error writing record") + return err + } + + // 2. Increment "maxPage" + maxPage = maxPage + 1 + return nil +} + +func readRandomRecord(environmentName string, connectionString string) error { + + // 1. Pick Random Number Between "minPage" and "maxPage" + if minPage == maxPage { + logrus.Warn("Nothing has been inserted yet!") + return nil + } + page := (rand.Int() % (maxPage - minPage)) + minPage + + // 2. Read Record + readID, readMsg, err := client.Read(environmentName, connectionString, page) + if err != nil { + if err == sql.ErrNoRows { + // This races with deletion, but if our page is greater than minPage + // we know that it should be in there. If it's less than minPage + // assume we are just racing the deletion goroutine and ignore the + // error. + if page <= minPage { + return nil + } + // For replicas, there is a chance we are suffering from replication + // lag, so ignore the missing row if we are a replica. + // TODO: Should we attempt to roughly figure out replication lag in + // this client, at least to catch major failures? We could probably + // multiply delay by the difference betwen maxCount and the page we + // are trying to read to figure out how long ago the row we were + // trying to write was written. + if client.ParseTabletType(connectionString) == "replica" || + client.ParseTabletType(connectionString) == "rdonly" { + return nil + } + logrus.WithError(err).WithFields(logrus.Fields{ + "page": page, + "minPage": minPage, + "maxPage": maxPage, + }).Error("Query succeeded but record not found, may mean data loss") + dataLossEvents.With( + prometheus.Labels{"database_name": client.ParseDBName(connectionString)}).Inc() + return err + } + logrus.WithError(err).Error("Error reading record") + return err + } + // Add zero here just so the metric exists for this database, even if it's + // zero. + dataLossEvents.With( + prometheus.Labels{"database_name": client.ParseDBName(connectionString)}).Add(0) + logrus.WithFields(logrus.Fields{ + "readID": readID, + "readMsg": readMsg, + }).Debug("Read row!") + + return nil +} + +func runCount(environmentName string, connectionString string) error { + + // 1. Run Count + count, err := client.Count(environmentName, connectionString) + if err != nil { + logrus.WithError(err).Error("Error counting records") + return err + } + logrus.WithFields(logrus.Fields{ + "count": count, + }).Debug("Counted rows!") + + // 2. Log if COUNT != "minPage" - "maxPage" + return nil +} + +func deleteLastRecordIfNecessary(environmentName string, connectionString string) error { + + // 1. Compare "maxPage" - "minPage" to Desired Dataset Size + if (maxPage - minPage) < *datasetSize { + return nil + } + logrus.WithFields(logrus.Fields{ + "current": maxPage - minPage, + "desired": *datasetSize, + }).Debug("Deleting last record") + + // 2. Delete Record If We Are Above Desired Size + err := client.Delete(environmentName, connectionString, minPage) + if err != nil { + logrus.WithError(err).Error("Error deleting record") + return err + } + + // 3. Increment "minPage" + minPage = minPage + 1 + return nil +} + +var ( + mysqlConnectionString = flag.String( + "mysql_connection_string", "", "Connection string for db to test") + prometheusMetricsAddress = flag.String( + "prometheus_metrics_address", ":8080", "Address on which to serve prometheus metrics") + debug = flag.Bool("debug", false, "Enable debug logging") + useVtgate = flag.Bool("vtgate", false, "Using vtgate (for @master and @replica)") + readFromReplica = flag.Bool("replica", false, "Read from replica") + readFromReadOnly = flag.Bool("rdonly", false, "Read from rdonly") + initialize = flag.Bool("initialize", false, "Initialize database (for testing)") + sleepTime = flag.Int("delay", 1*1000*1000*1000, "Delay in nanoseconds between ops") + datasetSize = flag.Int("dataset_size", 10, "Number of total records in database") + environmentName = flag.String("environment_name", "prod", + "Environment the database is deployed in that this client is pointing at") +) + +type runner struct { + connString string + envName string + fn func(string, string) error + errMessage string + sleepTime time.Duration +} + +func (r *runner) run() { + for { + time.Sleep(r.sleepTime) + err := r.fn(r.envName, r.connString) + if err != nil { + logrus.WithError(err).Error(r.errMessage) + } + } +} + +func waitForCtrlC() { + var endWaiter sync.WaitGroup + endWaiter.Add(1) + var signalChannel chan os.Signal + signalChannel = make(chan os.Signal, 1) + signal.Notify(signalChannel, os.Interrupt) + go func() { + <-signalChannel + endWaiter.Done() + }() + endWaiter.Wait() +} + +func runPrometheus() { + http.Handle("/metrics", promhttp.Handler()) + logrus.Fatal(http.ListenAndServe(*prometheusMetricsAddress, nil)) +} + +func main() { + + // 0. Handle Arguments + flag.Parse() + if *debug { + logrus.SetLevel(logrus.DebugLevel) + } + logrus.WithFields(logrus.Fields{ + "mysqlConnectionString": *mysqlConnectionString, + "prometheusMetricsAddress": *prometheusMetricsAddress, + "debug": *debug, + }).Debug("Command line arguments") + + connectionString := "" + if *mysqlConnectionString != "" { + connectionString = *mysqlConnectionString + } else if os.Getenv("MYSQL_CONN_STRING") != "" { + connectionString = os.Getenv("MYSQL_CONN_STRING") + } + masterConnectionString := connectionString + replicaConnectionString := connectionString + rdonlyConnectionString := connectionString + // When using vtgate, we want to append @master and @replica to the DSN, but + // this will fail against normal mysql which we're using for testing. See: + // https://vitess.io/docs/user-guides/faq/#how-do-i-choose-between-master-vs-replica-for-queries + if *useVtgate { + // We need to pass interpolateParams when using a vtgate because + // prepare is not supported. + // + // See: + // - https://github.com/go-sql-driver/mysql/blob/master/README.md#interpolateparams + // - https://github.com/src-d/go-mysql-server/issues/428 + // - https://github.com/vitessio/vitess/pull/3862 + masterConnectionString = fmt.Sprintf("%s@master?interpolateParams=true", connectionString) + replicaConnectionString = fmt.Sprintf("%s@replica?interpolateParams=true", connectionString) + rdonlyConnectionString = fmt.Sprintf("%s@rdonly?interpolateParams=true", connectionString) + } + fmt.Println("masterConnectionString:", masterConnectionString) + fmt.Println("replicaConnectionString:", replicaConnectionString) + fmt.Println("rdonlyConnectionString:", rdonlyConnectionString) + + // 1. Set Up Prometheus Metrics + logrus.Info("Prometheus Go") + go runPrometheus() + + // 2. Initialize Database + logrus.Info("Initializing database") + // For local testing, does not initialize vschema + if *initialize { + client.InitializeDatabase(*environmentName, masterConnectionString, "are_you_alive_messages") + } + client.WipeTestTable(*environmentName, masterConnectionString, "are_you_alive_messages") + + // 3. Start goroutines to do various things + logrus.Info("Starting client goroutines") + deleter := runner{ + connString: masterConnectionString, + envName: *environmentName, + fn: deleteLastRecordIfNecessary, + errMessage: "Recieved error deleting last record", + sleepTime: time.Duration(*sleepTime), + } + go deleter.run() + writer := runner{ + connString: masterConnectionString, + envName: *environmentName, + fn: writeNextRecord, + errMessage: "Recieved error writing next record", + sleepTime: time.Duration(*sleepTime), + } + go writer.run() + reader := runner{ + connString: masterConnectionString, + envName: *environmentName, + fn: readRandomRecord, + errMessage: "Recieved error reading record", + sleepTime: time.Duration(*sleepTime), + } + go reader.run() + counter := runner{ + connString: masterConnectionString, + envName: *environmentName, + fn: runCount, + errMessage: "Recieved error running count", + sleepTime: time.Duration(*sleepTime), + } + go counter.run() + + // Only bother starting a replica reader/counter if we are using a vtgate + // and actually are asking to do replica reads + if *useVtgate && *readFromReplica { + replicaReader := runner{ + connString: replicaConnectionString, + envName: *environmentName, + fn: readRandomRecord, + errMessage: "Recieved error reading record from replica", + sleepTime: time.Duration(*sleepTime), + } + go replicaReader.run() + replicaRowCounter := runner{ + connString: replicaConnectionString, + envName: *environmentName, + fn: runCount, + errMessage: "Recieved error running count on replica", + sleepTime: time.Duration(*sleepTime), + } + go replicaRowCounter.run() + } + + // Only bother starting a rdonly reader/counter if we are using a vtgate and + // actually are asking to do rdonly reads + if *useVtgate && *readFromReadOnly { + replicaReader := runner{ + connString: rdonlyConnectionString, + envName: *environmentName, + fn: readRandomRecord, + errMessage: "Recieved error reading record from rdonly", + sleepTime: time.Duration(*sleepTime), + } + go replicaReader.run() + replicaRowCounter := runner{ + connString: rdonlyConnectionString, + envName: *environmentName, + fn: runCount, + errMessage: "Recieved error running count on rdonly", + sleepTime: time.Duration(*sleepTime), + } + go replicaRowCounter.run() + } + + logrus.Info("Press Ctrl+C to end\n") + waitForCtrlC() + logrus.Info("\n") +} diff --git a/examples/are-you-alive/deploy/README.md b/examples/are-you-alive/deploy/README.md new file mode 100644 index 00000000000..57592ab6665 --- /dev/null +++ b/examples/are-you-alive/deploy/README.md @@ -0,0 +1,5 @@ +# Kubernetes Deployment + +Configuration for deploying `are-you-alive` on Kubernetes. + +Also deploys Prometheus and Alertmanager. diff --git a/examples/are-you-alive/docker-compose.yml b/examples/are-you-alive/docker-compose.yml new file mode 100644 index 00000000000..a8203b09f56 --- /dev/null +++ b/examples/are-you-alive/docker-compose.yml @@ -0,0 +1,29 @@ +# https://www.firehydrant.io/blog/developer-a-go-app-with-docker-compose/ +version: '3' +services: + app: + build: build/dev + image: are-you-alive-dev + volumes: + - .:/go/src/github.com/planetscale/are-you-alive + working_dir: /go/src/github.com/planetscale/are-you-alive + environment: + MYSQL_CONN_STRING: root:mysql@tcp(mysql)/testfixture + depends_on: + - mysql + ports: + - 8080:8080 + prom: + image: quay.io/prometheus/prometheus:v2.0.0 + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + command: "--config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/prometheus" + ports: + - 9090:9090 + depends_on: + - app + mysql: + image: mysql:5.7 + environment: + MYSQL_DATABASE: testfixture + MYSQL_ROOT_PASSWORD: mysql diff --git a/examples/are-you-alive/pkg/client/client.go b/examples/are-you-alive/pkg/client/client.go new file mode 100644 index 00000000000..e4f35d3e882 --- /dev/null +++ b/examples/are-you-alive/pkg/client/client.go @@ -0,0 +1,347 @@ +package client + +import ( + "database/sql" + "fmt" + mysql "github.com/go-sql-driver/mysql" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/sirupsen/logrus" + "strings" +) + +/* + * This package is meant to provide a client that includes prometheus metrics + * for common database issues. + */ + +var ( + defaultBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} + countErrorLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_count_error_latency_seconds", + Help: "Latency to recieve a count error", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name", "tablet_type"}, + ) + readErrorLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_read_error_latency_seconds", + Help: "Latency to recieve a read error", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name", "tablet_type"}, + ) + deleteErrorLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_delete_error_latency_seconds", + Help: "Latency to recieve a delete error", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name"}, + ) + writeErrorLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_write_error_latency_seconds", + Help: "Latency to recieve a write error", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name"}, + ) + connectErrorLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_connect_error_latency_seconds", + Help: "Latency to recieve a connect error", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name", "tablet_type"}, + ) + countLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_count_latency_seconds", + Help: "Time it takes to count to the database", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name", "tablet_type"}, + ) + readLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_read_latency_seconds", + Help: "Time it takes to read to the database", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name", "tablet_type"}, + ) + deleteLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_delete_latency_seconds", + Help: "Time it takes to delete to the database", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name"}, + ) + writeLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_write_latency_seconds", + Help: "Time it takes to write to the database", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name"}, + ) + connectLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "are_you_alive_connect_latency_seconds", + Help: "Time it takes to connect to the database", + Buckets: defaultBuckets, + }, + []string{"environment_name", "database_name", "tablet_type"}, + ) +) + +// ParseDBName extracts the database name from a mysql connection string. +func ParseDBName(connectionString string) string { + mysqlConfig, err := mysql.ParseDSN(connectionString) + if err != nil { + logrus.WithError(err).Fatal("Error parsing DSN!") + } + return mysqlConfig.DBName +} + +// ParseTabletType extracts the tablet type from a vitess specific mysql +// connection string. +// +// See https://vitess.io/docs/faq/queries/ for where these come from. +func ParseTabletType(connectionString string) string { + databaseName := ParseDBName(connectionString) + if strings.HasSuffix(databaseName, "@master") { + return "master" + } else if strings.HasSuffix(databaseName, "@replica") { + return "replica" + } else if strings.HasSuffix(databaseName, "@rdonly") { + return "rdonly" + } else { + return "default" + } +} + +func openDatabase(environmentName string, connectionString string) (*sql.DB, error) { + databaseName := ParseDBName(connectionString) + tabletType := ParseTabletType(connectionString) + // NOTE: This is probably not measuring open connections. I think they + // connections are created/fetched from the pool when an operation is + // actually performed. We could force this with a ping probably, but for + // now this is here just as a sanity check that this is actually all + // happening locally. We should just see everything complete within + // milliseconds. + labels := prometheus.Labels{ + "environment_name": environmentName, + "database_name": databaseName, + "tablet_type": tabletType} + connectTimer := prometheus.NewTimer(connectLatency.With(labels)) + connectErrorTimer := prometheus.NewTimer(connectErrorLatency.With(labels)) + db, err := sql.Open("mysql", connectionString) + if err != nil { + logrus.WithError(err).Error("Error connecting to database") + connectErrorTimer.ObserveDuration() + return nil, err + } + connectTimer.ObserveDuration() + return db, nil +} + +// InitializeDatabase will connect to the given connectionString, drop the +// given tableName, and recreate it with the schema that the rest of the client +// expects. This is not something any normal client would do but is convenient +// here because we are just using this client for monitoring. +func InitializeDatabase(environmentName string, connectionString string, tableName string) error { + + // 0. Create logger + log := logrus.WithField("connection_string", connectionString) + + // 1. Open client to database + db, err := openDatabase(environmentName, connectionString) + if err != nil { + log.WithError(err).Error("Error opening database") + return err + } + defer db.Close() + + // 2. Delete test table, but continue if it's not there + if _, err := db.Exec(fmt.Sprintf("DROP TABLE %s", tableName)); err != nil { + log.WithError(err).Warn("Error deleting database") + } + + // 3. Create table + createSQL := fmt.Sprintf( + "CREATE TABLE IF NOT EXISTS %s(page INT, message VARCHAR(255) NOT NULL, PRIMARY KEY (page))", tableName) + if _, err := db.Exec(createSQL); err != nil { + log.WithError(err).Error("Error creating database") + return err + } + return nil +} + +// WipeTestTable connects to the database given by connectionString and deletes +// everything in the table given by tableName because this client expects the +// table to be empty. No client would normally do this, but it's convenient for +// testing. +func WipeTestTable(environmentName string, connectionString string, tableName string) error { + + // 0. Create logger + log := logrus.WithField("connection_string", connectionString) + + // 1. Open client to database + db, err := openDatabase(environmentName, connectionString) + if err != nil { + log.WithError(err).Error("Error opening database") + return err + } + defer db.Close() + + // 2. Clear database + if _, err := db.Exec(fmt.Sprintf("DELETE FROM %s", tableName)); err != nil { + log.WithError(err).Warn("Error clearing table") + } + return nil +} + +// Write will write the record given by page to the test table in the database +// referenced by connectionString. +func Write(environmentName string, connectionString string, page int) error { + + // 0. Create logger + log := logrus.WithField("connection_string", connectionString) + + // 1. Open client to database + databaseName := ParseDBName(connectionString) + db, err := openDatabase(environmentName, connectionString) + if err != nil { + log.WithError(err).Error("Error opening database") + return err + } + defer db.Close() + + // 2. Write record + labels := prometheus.Labels{ + "environment_name": environmentName, + "database_name": databaseName} + writeTimer := prometheus.NewTimer(writeLatency.With(labels)) + writeErrorTimer := prometheus.NewTimer(writeErrorLatency.With(labels)) + if _, err := db.Exec("INSERT INTO are_you_alive_messages (page, message) VALUES (?, ?)", page, "foo"); err != nil { + log.WithError(err).Error("Error inserting into database") + writeErrorTimer.ObserveDuration() + return err + } + writeTimer.ObserveDuration() + return nil +} + +// Read will read the record given by page from the test table in the database +// referenced by connectionString. +func Read(environmentName string, connectionString string, page int) (int, string, error) { + + // 0. Create logger + log := logrus.WithField("connection_string", connectionString) + + // 1. Open client to database + databaseName := ParseDBName(connectionString) + db, err := openDatabase(environmentName, connectionString) + if err != nil { + log.WithError(err).Error("Error opening database") + return 0, "", err + } + defer db.Close() + + // 2. Read record + tabletType := ParseTabletType(connectionString) + labels := prometheus.Labels{ + "environment_name": environmentName, + "database_name": databaseName, + "tablet_type": tabletType} + readTimer := prometheus.NewTimer(readLatency.With(labels)) + readErrorTimer := prometheus.NewTimer(readErrorLatency.With(labels)) + row := db.QueryRow("SELECT * FROM are_you_alive_messages WHERE page=?", page) + var readID int + var readMsg string + if err := row.Scan(&readID, &readMsg); err != nil { + if err == sql.ErrNoRows { + // If our error is just that we didn't find anything, don't treat + // this as an error or a success. Just return and let the caller + // deal with it so we don't mess up our metrics. + return 0, "", err + } + log.WithError(err).Error("Error connecting to database") + readErrorTimer.ObserveDuration() + return 0, "", err + } + logrus.WithFields(logrus.Fields{ + "readId": readID, + "readMsg": readMsg, + }).Debug("Successfully read row") + readTimer.ObserveDuration() + return readID, readMsg, nil +} + +// Count will count all the documents in the test table in the database +// referenced by connectionString. +func Count(environmentName string, connectionString string) (int, error) { + + // 0. Create logger + log := logrus.WithField("connection_string", connectionString) + + // 1. Open client to database + databaseName := ParseDBName(connectionString) + db, err := openDatabase(environmentName, connectionString) + if err != nil { + log.WithError(err).Error("Error opening database") + return 0, err + } + defer db.Close() + + // 2. Run Count + tabletType := ParseTabletType(connectionString) + labels := prometheus.Labels{ + "environment_name": environmentName, + "database_name": databaseName, + "tablet_type": tabletType} + countTimer := prometheus.NewTimer(countLatency.With(labels)) + countErrorTimer := prometheus.NewTimer(countErrorLatency.With(labels)) + row := db.QueryRow("SELECT COUNT(*) FROM are_you_alive_messages") + var count int + if err := row.Scan(&count); err != nil { + log.WithError(err).Error("Error running count") + countErrorTimer.ObserveDuration() + return 0, err + } + logrus.WithFields(logrus.Fields{ + "count": count, + }).Debug("Successfully ran count") + countTimer.ObserveDuration() + return count, nil +} + +// Delete will delete the record given by page from the test table in the +// database referenced by connectionString. +func Delete(environmentName string, connectionString string, page int) error { + + // 0. Create logger + log := logrus.WithFields(logrus.Fields{ + "connection_string": connectionString, + "page": page, + }) + + // 1. Open client to database + databaseName := ParseDBName(connectionString) + labels := prometheus.Labels{ + "environment_name": environmentName, + "database_name": databaseName} + deleteTimer := prometheus.NewTimer(deleteLatency.With(labels)) + deleteErrorTimer := prometheus.NewTimer(deleteErrorLatency.With(labels)) + db, err := openDatabase(environmentName, connectionString) + if err != nil { + log.WithError(err).Error("Error opening database") + deleteErrorTimer.ObserveDuration() + return err + } + defer db.Close() + + // 2. Delete record + if _, err := db.Exec("DELETE FROM are_you_alive_messages WHERE page=?", page); err != nil { + log.WithError(err).Error("Error deleting record") + deleteErrorTimer.ObserveDuration() + return err + } + deleteTimer.ObserveDuration() + return nil +} diff --git a/examples/are-you-alive/prometheus.yml b/examples/are-you-alive/prometheus.yml new file mode 100644 index 00000000000..d968a28136d --- /dev/null +++ b/examples/are-you-alive/prometheus.yml @@ -0,0 +1,34 @@ +# my global config +global: + scrape_interval: 5s # By default, scrape targets every 5 seconds. + evaluation_interval: 5s # By default, scrape targets every 5 seconds. + # scrape_timeout is set to the global default (10s). + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'local-integration-test' + +# Load rules once and periodically evaluate them according to the global 'evaluation_interval'. +rule_files: + # - "first.rules" + # - "second.rules" + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: 'prometheus' + + # Override the global default and scrape targets from this job every 5 seconds. + scrape_interval: 5s + + # metrics_path defaults to '/metrics' + # scheme defaults to 'http'. + + static_configs: + - targets: ['localhost:9090'] + - job_name: "app" + scrape_interval: "5s" + static_configs: + - targets: ['app:8080'] diff --git a/examples/are-you-alive/schemas/README.md b/examples/are-you-alive/schemas/README.md new file mode 100644 index 00000000000..e119ab33506 --- /dev/null +++ b/examples/are-you-alive/schemas/README.md @@ -0,0 +1,9 @@ +# Test Schemas + +MySQL schemas and Vitess schemas that must be applied to your cluster to use +this test helper. They should be applied using `vtctlclient` like this: + +``` +vtctlclient -server "" ApplySchema -sql "$(cat create_test_table.sql)" +vtctlclient -server "" ApplyVSchema -vschema "$(cat vschema.json)" +``` diff --git a/examples/are-you-alive/schemas/create_test_table.sql b/examples/are-you-alive/schemas/create_test_table.sql new file mode 100644 index 00000000000..c382d343529 --- /dev/null +++ b/examples/are-you-alive/schemas/create_test_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS are_you_alive_messages ( + page INT, + message VARCHAR(255) NOT NULL, + PRIMARY KEY (page) +) + diff --git a/examples/are-you-alive/schemas/vschema.json b/examples/are-you-alive/schemas/vschema.json new file mode 100644 index 00000000000..bf8df1b6e64 --- /dev/null +++ b/examples/are-you-alive/schemas/vschema.json @@ -0,0 +1,18 @@ +{ + "sharded": true, + "vindexes": { + "hash": { + "type": "hash" + } + }, + "tables": { + "are_you_alive_messages": { + "column_vindexes": [ + { + "column": "page", + "name": "hash" + } + ] + } + } +} From b186af3829c7deac5251f81cc23d9223354c1872 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 25 Dec 2019 22:41:08 -0800 Subject: [PATCH 002/825] vstream: journal handling initial cut Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/vstream_manager.go | 114 +++++++++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 8 deletions(-) diff --git a/go/vt/vtgate/vstream_manager.go b/go/vt/vtgate/vstream_manager.go index 79b54655af4..7749832ea31 100644 --- a/go/vt/vtgate/vstream_manager.go +++ b/go/vt/vtgate/vstream_manager.go @@ -17,6 +17,8 @@ limitations under the License. package vtgate import ( + "fmt" + "io" "sync" "github.com/golang/protobuf/proto" @@ -39,15 +41,16 @@ type vstreamManager struct { // vstream contains the metadata for one VStream request. type vstream struct { - // mu protects parts of vgtid, and the semantics of a send. + // mu protects parts of vgtid, the semantics of a send, and journaler. // Once streaming begins, the Gtid within each ShardGtid will be updated on each event. // Also, the list of ShardGtids can change on a journaling event. // All other parts of vgtid can be read without a lock. // The lock is also held to ensure that all grouped events are sent together. // This can happen if vstreamer breaks up large transactions into smaller chunks. - mu sync.Mutex - vgtid *binlogdatapb.VGtid - send func(events []*binlogdatapb.VEvent) error + mu sync.Mutex + vgtid *binlogdatapb.VGtid + send func(events []*binlogdatapb.VEvent) error + journaler map[int64]*journalEvent // err can only be set once. once sync.Once @@ -62,6 +65,12 @@ type vstream struct { wg sync.WaitGroup } +type journalEvent struct { + journal *binlogdatapb.Journal + participants map[*binlogdatapb.ShardGtid]bool + done chan struct{} +} + func newVStreamManager(resolver *srvtopo.Resolver, serv srvtopo.Server, cell string) *vstreamManager { return &vstreamManager{ resolver: resolver, @@ -175,11 +184,19 @@ func (vs *vstream) startOneStream(ctx context.Context, sgtid *binlogdatapb.Shard // streamFromTablet streams from one shard. If transactions come in separate chunks, they are grouped and sent. func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.ShardGtid) error { + // If streamCtx is canceled, we ignore all events until we exit. + // This is used to ensure that we don't send more events after a valid journal is encountered. + streamCtx, streamCancel := context.WithCancel(ctx) + defer streamCancel() + errCount := 0 for { select { case <-ctx.Done(): return ctx.Err() + case <-streamCtx.Done(): + // Unreachable + return nil default: } @@ -197,15 +214,38 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha select { case <-ctx.Done(): return ctx.Err() + case <-streamCtx.Done(): + // Unreachable + return io.EOF default: } - // Remove all heartbeat events for now. - // Otherwise they can accumulate indefinitely if there are no real events. - // TODO(sougou): figure out a model for this. for i := 0; i < len(events); i++ { - if events[i].Type == binlogdatapb.VEventType_HEARTBEAT { + switch events[i].Type { + case binlogdatapb.VEventType_HEARTBEAT: + // Remove all heartbeat events for now. + // Otherwise they can accumulate indefinitely if there are no real events. + // TODO(sougou): figure out a model for this. events = append(events[:i], events[i+1:]...) + case binlogdatapb.VEventType_JOURNAL: + journal := events[i].Journal + // Don't send journals even if ignored. + events = append(events[:i], events[i+1:]...) + je, err := vs.getJournalEvent(ctx, sgtid, journal) + if err != nil { + return err + } + if je == nil { + continue + } + // Wait till all other participants converge and return EOF. + select { + case <-ctx.Done(): + return ctx.Err() + case <-je.done: + streamCancel() + return io.EOF + } } } if len(events) == 0 { @@ -268,3 +308,61 @@ func (vs *vstream) convertAndSend(sgtid *binlogdatapb.ShardGtid, eventss [][]*bi } return nil } + +func (vs *vstream) getJournalEvent(ctx context.Context, sgtid *binlogdatapb.ShardGtid, journal *binlogdatapb.Journal) (*journalEvent, error) { + if journal.MigrationType == binlogdatapb.MigrationType_TABLES { + return nil, nil + } + + vs.mu.Lock() + defer vs.mu.Unlock() + + je, ok := vs.journaler[journal.Id] + if !ok { + log.Infof("Journal encountered: %v", journal) + je = &journalEvent{ + journal: journal, + participants: make(map[*binlogdatapb.ShardGtid]bool), + done: make(chan struct{}), + } + matched := false + for _, jks := range journal.Participants { + participantMatched := false + for _, inner := range vs.vgtid.ShardGtids { + if inner.Keyspace == jks.Keyspace && inner.Shard == jks.Shard { + je.participants[inner] = false + if sgtid == inner { + matched = true + } + } + } + if !participantMatched { + return nil, fmt.Errorf("not all journaling participants are in the stream: %v", jks) + } + } + if !matched { + // This sgtid is not a participant. + return nil, nil + } + vs.journaler[journal.Id] = je + } + + je.participants[sgtid] = true + + for _, active := range je.participants { + if !active { + return je, nil + } + } + for i := 0; i < len(vs.vgtid.ShardGtids); i++ { + if je.participants[vs.vgtid.ShardGtids[i]] { + vs.vgtid.ShardGtids = append(vs.vgtid.ShardGtids[:i], vs.vgtid.ShardGtids[i+1:]...) + } + } + for _, sgtid := range je.journal.ShardGtids { + vs.vgtid.ShardGtids = append(vs.vgtid.ShardGtids, sgtid) + vs.startOneStream(ctx, sgtid) + } + close(je.done) + return je, nil +} From 521296a902cbff70d585da405c97ca874c736e18 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 27 Dec 2019 10:04:05 -0800 Subject: [PATCH 003/825] vstream: journal handling initial tests Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/vstream_manager.go | 132 ++++++---- go/vt/vtgate/vstream_manager_test.go | 306 ++++++++++++++++++++-- go/vt/vttablet/sandboxconn/sandboxconn.go | 10 + 3 files changed, 366 insertions(+), 82 deletions(-) diff --git a/go/vt/vtgate/vstream_manager.go b/go/vt/vtgate/vstream_manager.go index 7749832ea31..b358c9fdb81 100644 --- a/go/vt/vtgate/vstream_manager.go +++ b/go/vt/vtgate/vstream_manager.go @@ -90,6 +90,7 @@ func (vsm *vstreamManager) VStream(ctx context.Context, tabletType topodatapb.Ta filter: filter, send: send, resolver: vsm.resolver, + journaler: make(map[int64]*journalEvent), } return vs.stream(ctx) } @@ -184,17 +185,16 @@ func (vs *vstream) startOneStream(ctx context.Context, sgtid *binlogdatapb.Shard // streamFromTablet streams from one shard. If transactions come in separate chunks, they are grouped and sent. func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.ShardGtid) error { - // If streamCtx is canceled, we ignore all events until we exit. - // This is used to ensure that we don't send more events after a valid journal is encountered. - streamCtx, streamCancel := context.WithCancel(ctx) - defer streamCancel() + // jounralDone is assigned a channel when a journal event is encountered. + // It will be closed when all journal events converge. + var journalDone chan struct{} errCount := 0 for { select { case <-ctx.Done(): return ctx.Err() - case <-streamCtx.Done(): + case <-journalDone: // Unreachable return nil default: @@ -214,60 +214,83 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha select { case <-ctx.Done(): return ctx.Err() - case <-streamCtx.Done(): + case <-journalDone: // Unreachable return io.EOF default: } - for i := 0; i < len(events); i++ { - switch events[i].Type { + sendevents := make([]*binlogdatapb.VEvent, 0, len(events)) + for _, event := range events { + switch event.Type { + case binlogdatapb.VEventType_GTID: + // Update the VGtid and send that instead. + sgtid.Gtid = event.Gtid + sendevents = append(sendevents, &binlogdatapb.VEvent{ + Type: binlogdatapb.VEventType_VGTID, + Vgtid: proto.Clone(vs.vgtid).(*binlogdatapb.VGtid), + }) + case binlogdatapb.VEventType_FIELD: + // Update table names and send. + ev := proto.Clone(event).(*binlogdatapb.VEvent) + ev.FieldEvent.TableName = sgtid.Keyspace + "." + ev.FieldEvent.TableName + sendevents = append(sendevents, ev) + case binlogdatapb.VEventType_ROW: + // Update table names and send. + ev := proto.Clone(event).(*binlogdatapb.VEvent) + ev.RowEvent.TableName = sgtid.Keyspace + "." + ev.RowEvent.TableName + sendevents = append(sendevents, ev) case binlogdatapb.VEventType_HEARTBEAT: // Remove all heartbeat events for now. // Otherwise they can accumulate indefinitely if there are no real events. // TODO(sougou): figure out a model for this. - events = append(events[:i], events[i+1:]...) case binlogdatapb.VEventType_JOURNAL: - journal := events[i].Journal - // Don't send journals even if ignored. - events = append(events[:i], events[i+1:]...) + journal := event.Journal + // Journal events are not sent to clients. je, err := vs.getJournalEvent(ctx, sgtid, journal) if err != nil { return err } - if je == nil { - continue - } - // Wait till all other participants converge and return EOF. - select { - case <-ctx.Done(): - return ctx.Err() - case <-je.done: - streamCancel() - return io.EOF + if je != nil { + // Wait till all other participants converge and return EOF. + journalDone = je.done + select { + case <-ctx.Done(): + return ctx.Err() + case <-journalDone: + return io.EOF + } } + default: + sendevents = append(sendevents, event) } } - if len(events) == 0 { + if len(sendevents) == 0 { return nil } // We received a valid event. Reset error count. errCount = 0 - eventss = append(eventss, events) - lastEvent := events[len(events)-1] + eventss = append(eventss, sendevents) + lastEvent := sendevents[len(sendevents)-1] switch lastEvent.Type { case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL: - if err := vs.convertAndSend(sgtid, eventss); err != nil { + if err := vs.sendAll(sgtid, eventss); err != nil { return err } eventss = nil } return nil }) + // If stream was ended (by a journal event), return nil without checking for error. + select { + case <-journalDone: + return nil + default: + } if err == nil { // Unreachable. - err = vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "vstream ended unexpectedly") + err = vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "vstream ended unexpectedly") } if vterrors.Code(err) != vtrpcpb.Code_FAILED_PRECONDITION && vterrors.Code(err) != vtrpcpb.Code_UNAVAILABLE { log.Errorf("vstream for %s/%s error: %v", sgtid.Keyspace, sgtid.Shard, err) @@ -278,30 +301,17 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha log.Errorf("vstream for %s/%s had three consecutive failures: %v", sgtid.Keyspace, sgtid.Shard, err) return err } + log.Infof("vstream for %s/%s error, retrying: %v", sgtid.Keyspace, sgtid.Shard, err) } } -func (vs *vstream) convertAndSend(sgtid *binlogdatapb.ShardGtid, eventss [][]*binlogdatapb.VEvent) error { +// sendAll sends a group of events together while holding the lock. +func (vs *vstream) sendAll(sgtid *binlogdatapb.ShardGtid, eventss [][]*binlogdatapb.VEvent) error { vs.mu.Lock() defer vs.mu.Unlock() // Send all chunks while holding the lock. for _, evs := range eventss { - // Replace GTID and table names. - for _, ev := range evs { - switch ev.Type { - case binlogdatapb.VEventType_GTID: - // Update the VGtid and send that instead. - sgtid.Gtid = ev.Gtid - ev.Type = binlogdatapb.VEventType_VGTID - ev.Gtid = "" - ev.Vgtid = proto.Clone(vs.vgtid).(*binlogdatapb.VGtid) - case binlogdatapb.VEventType_FIELD: - ev.FieldEvent.TableName = sgtid.Keyspace + "." + ev.FieldEvent.TableName - case binlogdatapb.VEventType_ROW: - ev.RowEvent.TableName = sgtid.Keyspace + "." + ev.RowEvent.TableName - } - } if err := vs.send(evs); err != nil { return err } @@ -320,49 +330,63 @@ func (vs *vstream) getJournalEvent(ctx context.Context, sgtid *binlogdatapb.Shar je, ok := vs.journaler[journal.Id] if !ok { log.Infof("Journal encountered: %v", journal) + // Identify the list of ShardGtids that match the participants of the journal. je = &journalEvent{ journal: journal, participants: make(map[*binlogdatapb.ShardGtid]bool), done: make(chan struct{}), } - matched := false for _, jks := range journal.Participants { participantMatched := false for _, inner := range vs.vgtid.ShardGtids { if inner.Keyspace == jks.Keyspace && inner.Shard == jks.Shard { + participantMatched = true je.participants[inner] = false - if sgtid == inner { - matched = true - } + break } } if !participantMatched { return nil, fmt.Errorf("not all journaling participants are in the stream: %v", jks) } } - if !matched { + if _, ok := je.participants[sgtid]; !ok { // This sgtid is not a participant. + // Only a participant should add je to the journaler. return nil, nil } vs.journaler[journal.Id] = je } + if _, ok := je.participants[sgtid]; !ok { + // This sgtid is not a participant. + return nil, nil + } je.participants[sgtid] = true - for _, active := range je.participants { - if !active { + for _, waiting := range je.participants { + if !waiting { + // Some participants are yet to join the wait. return je, nil } } - for i := 0; i < len(vs.vgtid.ShardGtids); i++ { - if je.participants[vs.vgtid.ShardGtids[i]] { - vs.vgtid.ShardGtids = append(vs.vgtid.ShardGtids[:i], vs.vgtid.ShardGtids[i+1:]...) + // All participants are waiting. Replace old shard gtids with new ones. + newsgtids := make([]*binlogdatapb.ShardGtid, 0, len(vs.vgtid.ShardGtids)-len(je.participants)+len(je.journal.ShardGtids)) + log.Infof("Removing shard gtids: %v", je.participants) + for _, cursgtid := range vs.vgtid.ShardGtids { + if je.participants[cursgtid] { + continue } + newsgtids = append(newsgtids, cursgtid) } + + log.Infof("Adding shard gtids: %v", je.journal.ShardGtids) for _, sgtid := range je.journal.ShardGtids { - vs.vgtid.ShardGtids = append(vs.vgtid.ShardGtids, sgtid) + newsgtids = append(newsgtids, sgtid) + // It's ok to start the streams eventhough ShardGtids is not updated yet. + // This is because we're still holding the lock. vs.startOneStream(ctx, sgtid) } + vs.vgtid.ShardGtids = newsgtids close(je.done) return je, nil } diff --git a/go/vt/vtgate/vstream_manager_test.go b/go/vt/vtgate/vstream_manager_test.go index 1845603d06e..a386ac0366a 100644 --- a/go/vt/vtgate/vstream_manager_test.go +++ b/go/vt/vtgate/vstream_manager_test.go @@ -34,7 +34,7 @@ import ( "vitess.io/vitess/go/vt/vtgate/gateway" ) -func TestVStream(t *testing.T) { +func TestVStreamEvents(t *testing.T) { name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -47,15 +47,14 @@ func TestVStream(t *testing.T) { {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t0"}}, {Type: binlogdatapb.VEventType_COMMIT}, } - wantvgtid1 := &binlogdatapb.VGtid{ - ShardGtids: []*binlogdatapb.ShardGtid{{ - Keyspace: name, - Shard: "-20", - Gtid: "gtid01", - }}, - } want1 := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ - {Type: binlogdatapb.VEventType_VGTID, Vgtid: wantvgtid1}, + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid01", + }}, + }}, {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "TestVStream.f0"}}, {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "TestVStream.t0"}}, {Type: binlogdatapb.VEventType_COMMIT}, @@ -82,7 +81,6 @@ func TestVStream(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - count := 1 vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -90,26 +88,29 @@ func TestVStream(t *testing.T) { Gtid: "pos", }}, } - err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { - defer func() { count++ }() - - got := &binlogdatapb.VStreamResponse{Events: events} - want := want1 - if count == 2 { - want = want2 + ch := make(chan *binlogdatapb.VStreamResponse) + go func() { + err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { + ch <- &binlogdatapb.VStreamResponse{Events: events} + return nil + }) + wantErr := "context canceled" + if err == nil || !strings.Contains(err.Error(), wantErr) { + t.Errorf("vstream end: %v, must contain %v", err.Error(), wantErr) } - if !proto.Equal(got, want) { - t.Fatalf("vstream(%d):\n%v, want\n%v", count, got, want) - } - if count == 2 { - cancel() - } - return nil - }) - wantErr := "context canceled" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Errorf("vstream end: %v, must contain %v", err.Error(), wantErr) + ch <- nil + }() + got := <-ch + if !proto.Equal(got, want1) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want1) + } + got = <-ch + if !proto.Equal(got, want2) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want2) } + cancel() + // Ensure the go func error return was verified. + <-ch } // TestVStreamChunks ensures that a transaction that's broken @@ -352,6 +353,255 @@ func TestVStreamHeartbeat(t *testing.T) { } } +func TestVStreamJournalOneToMany(t *testing.T) { + name := "TestVStream" + _ = createSandbox(name) + hc := discovery.NewFakeHealthCheck() + vsm := newTestVStreamManager(hc, new(sandboxTopo), "aa") + sbc0 := hc.AddTestTablet("aa", "1.1.1.1", 1001, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil) + sbc1 := hc.AddTestTablet("aa", "1.1.1.1", 1002, name, "-10", topodatapb.TabletType_MASTER, true, 1, nil) + sbc2 := hc.AddTestTablet("aa", "1.1.1.1", 1003, name, "10-20", topodatapb.TabletType_MASTER, true, 1, nil) + + send1 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid01"}, + {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "f0"}}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t0"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + want1 := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid01", + }}, + }}, + {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "TestVStream.f0"}}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "TestVStream.t0"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + }} + sbc0.AddVStreamEvents(send1, nil) + + send2 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_BEGIN}, + {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ + Id: 1, + MigrationType: binlogdatapb.MigrationType_SHARDS, + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-10", + Gtid: "pos10", + }, { + Keyspace: name, + Shard: "10-20", + Gtid: "pos1020", + }}, + Participants: []*binlogdatapb.KeyspaceShard{{ + Keyspace: name, + Shard: "-20", + }}, + }}, + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid02"}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + sbc0.AddVStreamEvents(send2, nil) + + send3 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid03"}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t3"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + sbc1.ExpectVStreamStartPos("pos10") + sbc1.AddVStreamEvents(send3, nil) + + send4 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid04"}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t4"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + sbc2.ExpectVStreamStartPos("pos1020") + sbc2.AddVStreamEvents(send4, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + vgtid := &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "pos", + }}, + } + ch := make(chan *binlogdatapb.VStreamResponse) + go func() { + err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { + ch <- &binlogdatapb.VStreamResponse{Events: events} + return nil + }) + wantErr := "context canceled" + if err == nil || !strings.Contains(err.Error(), wantErr) { + t.Errorf("vstream end: %v, must contain %v", err, wantErr) + } + ch <- nil + }() + got := <-ch + if !proto.Equal(got, want1) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want1) + } + // The following two events from the different shards can come in any order. + // But the resulting VGTID should be the same after both are received. + <-ch + got = <-ch + wantevent := &binlogdatapb.VEvent{ + Type: binlogdatapb.VEventType_VGTID, + Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-10", + Gtid: "gtid03", + }, { + Keyspace: name, + Shard: "10-20", + Gtid: "gtid04", + }}, + }, + } + if !proto.Equal(got.Events[0], wantevent) { + t.Errorf("vgtid: %v, want %v", got.Events[0], wantevent) + } + cancel() + <-ch +} + +func TestVStreamJournalManyToOne(t *testing.T) { + // Variable names are maintained like in OneToMany, but order is different.1 + name := "TestVStream" + _ = createSandbox(name) + hc := discovery.NewFakeHealthCheck() + vsm := newTestVStreamManager(hc, new(sandboxTopo), "aa") + sbc0 := hc.AddTestTablet("aa", "1.1.1.1", 1001, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil) + sbc1 := hc.AddTestTablet("aa", "1.1.1.1", 1002, name, "-10", topodatapb.TabletType_MASTER, true, 1, nil) + sbc2 := hc.AddTestTablet("aa", "1.1.1.1", 1003, name, "10-20", topodatapb.TabletType_MASTER, true, 1, nil) + + send3 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid03"}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t3"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + sbc1.ExpectVStreamStartPos("pos10") + sbc1.AddVStreamEvents(send3, nil) + + send4 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid04"}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t4"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + sbc2.ExpectVStreamStartPos("pos1020") + sbc2.AddVStreamEvents(send4, nil) + + send2 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_BEGIN}, + {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ + Id: 1, + MigrationType: binlogdatapb.MigrationType_SHARDS, + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "pos20", + }}, + Participants: []*binlogdatapb.KeyspaceShard{{ + Keyspace: name, + Shard: "-10", + }, { + Keyspace: name, + Shard: "10-20", + }}, + }}, + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid02"}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + // Journal event has to be sent by both shards. + sbc1.AddVStreamEvents(send2, nil) + // Copy before sending so they don't overlap. + send2 = append(([]*binlogdatapb.VEvent)(nil), send2...) + sbc2.AddVStreamEvents(send2, nil) + + send1 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid01"}, + {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "f0"}}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t0"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + want1 := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid01", + }}, + }}, + {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "TestVStream.f0"}}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "TestVStream.t0"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + }} + sbc0.ExpectVStreamStartPos("pos20") + sbc0.AddVStreamEvents(send1, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + vgtid := &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-10", + Gtid: "pos10", + }, { + Keyspace: name, + Shard: "10-20", + Gtid: "pos1020", + }}, + } + ch := make(chan *binlogdatapb.VStreamResponse) + go func() { + err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { + ch <- &binlogdatapb.VStreamResponse{Events: events} + return nil + }) + wantErr := "context canceled" + if err == nil || !strings.Contains(err.Error(), wantErr) { + t.Errorf("vstream end: %v, must contain %v", err, wantErr) + } + ch <- nil + }() + // The following two events from the different shards can come in any order. + // But the resulting VGTID should be the same after both are received. + <-ch + got := <-ch + wantevent := &binlogdatapb.VEvent{ + Type: binlogdatapb.VEventType_VGTID, + Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-10", + Gtid: "gtid03", + }, { + Keyspace: name, + Shard: "10-20", + Gtid: "gtid04", + }}, + }, + } + if !proto.Equal(got.Events[0], wantevent) { + t.Errorf("vgtid: %v, want %v", got.Events[0], wantevent) + } + got = <-ch + if !proto.Equal(got, want1) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want1) + } + cancel() + <-ch +} + func TestResolveVStreamParams(t *testing.T) { name := "TestVStream" _ = createSandbox(name) diff --git a/go/vt/vttablet/sandboxconn/sandboxconn.go b/go/vt/vttablet/sandboxconn/sandboxconn.go index 5c460c180f7..bc47b69fae4 100644 --- a/go/vt/vttablet/sandboxconn/sandboxconn.go +++ b/go/vt/vttablet/sandboxconn/sandboxconn.go @@ -86,6 +86,8 @@ type SandboxConn struct { MessageIDs []*querypb.Value + // vstream expectations. + StartPos string VStreamEvents [][]*binlogdatapb.VEvent VStreamErrors []error @@ -361,6 +363,11 @@ func (sbc *SandboxConn) UpdateStream(ctx context.Context, target *querypb.Target return fmt.Errorf("not implemented in test") } +// ExpectVStreamStartPos makes the conn verify that that the next vstream request has the right startPos. +func (sbc *SandboxConn) ExpectVStreamStartPos(startPos string) { + sbc.StartPos = startPos +} + // AddVStreamEvents adds a set of VStream events to be returned. func (sbc *SandboxConn) AddVStreamEvents(events []*binlogdatapb.VEvent, err error) { sbc.VStreamEvents = append(sbc.VStreamEvents, events) @@ -369,6 +376,9 @@ func (sbc *SandboxConn) AddVStreamEvents(events []*binlogdatapb.VEvent, err erro // VStream is part of the QueryService interface. func (sbc *SandboxConn) VStream(ctx context.Context, target *querypb.Target, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { + if sbc.StartPos != "" && sbc.StartPos != startPos { + return fmt.Errorf("startPos(%v): %v, want %v", target, startPos, sbc.StartPos) + } for len(sbc.VStreamEvents) != 0 { ev := sbc.VStreamEvents[0] err := sbc.VStreamErrors[0] From 26b6dc0004b5d715b0f02abd883ac993e83e13bd Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 27 Dec 2019 13:12:12 -0800 Subject: [PATCH 004/825] vstream: journal handling error cases Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/vstream_manager.go | 34 ++-- go/vt/vtgate/vstream_manager_test.go | 260 +++++++++++++++++++++++++-- 2 files changed, 272 insertions(+), 22 deletions(-) diff --git a/go/vt/vtgate/vstream_manager.go b/go/vt/vtgate/vstream_manager.go index b358c9fdb81..066d37fa7c9 100644 --- a/go/vt/vtgate/vstream_manager.go +++ b/go/vt/vtgate/vstream_manager.go @@ -336,29 +336,43 @@ func (vs *vstream) getJournalEvent(ctx context.Context, sgtid *binlogdatapb.Shar participants: make(map[*binlogdatapb.ShardGtid]bool), done: make(chan struct{}), } + const ( + undecided = iota + matchAll + matchNone + ) + mode := undecided + nextParticipant: for _, jks := range journal.Participants { - participantMatched := false for _, inner := range vs.vgtid.ShardGtids { if inner.Keyspace == jks.Keyspace && inner.Shard == jks.Shard { - participantMatched = true - je.participants[inner] = false - break + switch mode { + case undecided, matchAll: + mode = matchAll + je.participants[inner] = false + case matchNone: + return nil, fmt.Errorf("not all journaling participants are in the stream: journal: %v, stream: %v", journal.Participants, vs.vgtid.ShardGtids) + } + continue nextParticipant } } - if !participantMatched { - return nil, fmt.Errorf("not all journaling participants are in the stream: %v", jks) + switch mode { + case undecided, matchNone: + mode = matchNone + case matchAll: + return nil, fmt.Errorf("not all journaling participants are in the stream: journal: %v, stream: %v", journal.Participants, vs.vgtid.ShardGtids) } } - if _, ok := je.participants[sgtid]; !ok { - // This sgtid is not a participant. - // Only a participant should add je to the journaler. + if mode == matchNone { + // Unreachable. Journal events are only added to participants. + // But if we do receive such an event, the right action will be to ignore it. return nil, nil } vs.journaler[journal.Id] = je } if _, ok := je.participants[sgtid]; !ok { - // This sgtid is not a participant. + // Unreachable. See above. return nil, nil } je.participants[sgtid] = true diff --git a/go/vt/vtgate/vstream_manager_test.go b/go/vt/vtgate/vstream_manager_test.go index a386ac0366a..d9c0f0ffaf1 100644 --- a/go/vt/vtgate/vstream_manager_test.go +++ b/go/vt/vtgate/vstream_manager_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/vt/discovery" + "vitess.io/vitess/go/vt/proto/binlogdata" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" @@ -65,15 +66,14 @@ func TestVStreamEvents(t *testing.T) { {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid02"}, {Type: binlogdatapb.VEventType_DDL}, } - wantvgtid2 := &binlogdatapb.VGtid{ - ShardGtids: []*binlogdatapb.ShardGtid{{ - Keyspace: name, - Shard: "-20", - Gtid: "gtid02", - }}, - } want2 := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ - {Type: binlogdatapb.VEventType_VGTID, Vgtid: wantvgtid2}, + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid02", + }}, + }}, {Type: binlogdatapb.VEventType_DDL}, }} sbc0.AddVStreamEvents(send2, nil) @@ -383,7 +383,6 @@ func TestVStreamJournalOneToMany(t *testing.T) { sbc0.AddVStreamEvents(send1, nil) send2 := []*binlogdatapb.VEvent{ - {Type: binlogdatapb.VEventType_BEGIN}, {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ Id: 1, MigrationType: binlogdatapb.MigrationType_SHARDS, @@ -500,7 +499,6 @@ func TestVStreamJournalManyToOne(t *testing.T) { sbc2.AddVStreamEvents(send4, nil) send2 := []*binlogdatapb.VEvent{ - {Type: binlogdatapb.VEventType_BEGIN}, {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ Id: 1, MigrationType: binlogdatapb.MigrationType_SHARDS, @@ -522,8 +520,6 @@ func TestVStreamJournalManyToOne(t *testing.T) { } // Journal event has to be sent by both shards. sbc1.AddVStreamEvents(send2, nil) - // Copy before sending so they don't overlap. - send2 = append(([]*binlogdatapb.VEvent)(nil), send2...) sbc2.AddVStreamEvents(send2, nil) send1 := []*binlogdatapb.VEvent{ @@ -602,6 +598,246 @@ func TestVStreamJournalManyToOne(t *testing.T) { <-ch } +func TestVStreamJournalNoMatch(t *testing.T) { + name := "TestVStream" + _ = createSandbox(name) + hc := discovery.NewFakeHealthCheck() + vsm := newTestVStreamManager(hc, new(sandboxTopo), "aa") + sbc0 := hc.AddTestTablet("aa", "1.1.1.1", 1001, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil) + + send1 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid01"}, + {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "f0"}}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t0"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + want1 := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid01", + }}, + }}, + {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "TestVStream.f0"}}, + {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "TestVStream.t0"}}, + {Type: binlogdatapb.VEventType_COMMIT}, + }} + sbc0.AddVStreamEvents(send1, nil) + + tableJournal := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ + Id: 1, + MigrationType: binlogdatapb.MigrationType_TABLES, + }}, + {Type: binlogdatapb.VEventType_GTID, Gtid: "jn1"}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + wantjn1 := &binlogdata.VStreamResponse{Events: []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "jn1", + }}, + }}, + {Type: binlogdatapb.VEventType_COMMIT}, + }} + sbc0.AddVStreamEvents(tableJournal, nil) + + send2 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid02"}, + {Type: binlogdatapb.VEventType_DDL}, + } + want2 := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid02", + }}, + }}, + {Type: binlogdatapb.VEventType_DDL}, + }} + sbc0.AddVStreamEvents(send2, nil) + + shardJournal := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ + Id: 2, + MigrationType: binlogdatapb.MigrationType_SHARDS, + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "c0-", + Gtid: "posc0", + }}, + Participants: []*binlogdatapb.KeyspaceShard{{ + Keyspace: name, + Shard: "c0-e0", + }, { + Keyspace: name, + Shard: "e0-", + }}, + }}, + {Type: binlogdatapb.VEventType_GTID, Gtid: "jn2"}, + {Type: binlogdatapb.VEventType_COMMIT}, + } + wantjn2 := &binlogdata.VStreamResponse{Events: []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "jn2", + }}, + }}, + {Type: binlogdatapb.VEventType_COMMIT}, + }} + sbc0.AddVStreamEvents(shardJournal, nil) + + send3 := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_GTID, Gtid: "gtid03"}, + {Type: binlogdatapb.VEventType_DDL}, + } + want3 := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid03", + }}, + }}, + {Type: binlogdatapb.VEventType_DDL}, + }} + sbc0.AddVStreamEvents(send3, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + vgtid := &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "pos", + }}, + } + ch := make(chan *binlogdatapb.VStreamResponse) + go func() { + err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { + ch <- &binlogdatapb.VStreamResponse{Events: events} + return nil + }) + wantErr := "context canceled" + if err == nil || !strings.Contains(err.Error(), wantErr) { + t.Errorf("vstream end: %v, must contain %v", err.Error(), wantErr) + } + ch <- nil + }() + got := <-ch + if !proto.Equal(got, want1) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want1) + } + got = <-ch + if !proto.Equal(got, wantjn1) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want2) + } + got = <-ch + if !proto.Equal(got, want2) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want2) + } + got = <-ch + if !proto.Equal(got, wantjn2) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want2) + } + got = <-ch + if !proto.Equal(got, want3) { + t.Errorf("vstream(1):\n%v, want\n%v", got, want3) + } + cancel() + // Ensure the go func error return was verified. + <-ch +} + +func TestVStreamJournalPartialMatch(t *testing.T) { + // Variable names are maintained like in OneToMany, but order is different.1 + name := "TestVStream" + _ = createSandbox(name) + hc := discovery.NewFakeHealthCheck() + vsm := newTestVStreamManager(hc, new(sandboxTopo), "aa") + _ = hc.AddTestTablet("aa", "1.1.1.1", 1002, name, "-10", topodatapb.TabletType_MASTER, true, 1, nil) + sbc2 := hc.AddTestTablet("aa", "1.1.1.1", 1003, name, "10-20", topodatapb.TabletType_MASTER, true, 1, nil) + + send := []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ + Id: 1, + MigrationType: binlogdatapb.MigrationType_SHARDS, + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "10-30", + Gtid: "pos1040", + }}, + Participants: []*binlogdatapb.KeyspaceShard{{ + Keyspace: name, + Shard: "10-20", + }, { + Keyspace: name, + Shard: "20-30", + }}, + }}, + } + sbc2.AddVStreamEvents(send, nil) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + vgtid := &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-10", + Gtid: "pos10", + }, { + Keyspace: name, + Shard: "10-20", + Gtid: "pos1020", + }}, + } + err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { + t.Errorf("unexpected events: %v", events) + return nil + }) + wantErr := "not all journaling participants are in the stream" + if err == nil || !strings.Contains(err.Error(), wantErr) { + t.Errorf("vstream end: %v, must contain %v", err, wantErr) + } + + // Try a different order (different code path) + send = []*binlogdatapb.VEvent{ + {Type: binlogdatapb.VEventType_JOURNAL, Journal: &binlogdatapb.Journal{ + Id: 1, + MigrationType: binlogdatapb.MigrationType_SHARDS, + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "10-30", + Gtid: "pos1040", + }}, + Participants: []*binlogdatapb.KeyspaceShard{{ + Keyspace: name, + Shard: "20-30", + }, { + Keyspace: name, + Shard: "10-20", + }}, + }}, + } + sbc2.AddVStreamEvents(send, nil) + err = vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { + t.Errorf("unexpected events: %v", events) + return nil + }) + if err == nil || !strings.Contains(err.Error(), wantErr) { + t.Errorf("vstream end: %v, must contain %v", err, wantErr) + } + cancel() +} + func TestResolveVStreamParams(t *testing.T) { name := "TestVStream" _ = createSandbox(name) From d9a8ed5b0fe324ca31ba26e049e8ec314d0000da Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 27 Dec 2019 21:35:33 -0800 Subject: [PATCH 005/825] vstream: journal handling, refactored tests Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/vstream_manager_test.go | 233 ++++++++++----------------- 1 file changed, 81 insertions(+), 152 deletions(-) diff --git a/go/vt/vtgate/vstream_manager_test.go b/go/vt/vtgate/vstream_manager_test.go index d9c0f0ffaf1..673c00d6b82 100644 --- a/go/vt/vtgate/vstream_manager_test.go +++ b/go/vt/vtgate/vstream_manager_test.go @@ -36,6 +36,9 @@ import ( ) func TestVStreamEvents(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -78,9 +81,6 @@ func TestVStreamEvents(t *testing.T) { }} sbc0.AddVStreamEvents(send2, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -100,22 +100,19 @@ func TestVStreamEvents(t *testing.T) { } ch <- nil }() - got := <-ch - if !proto.Equal(got, want1) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want1) - } - got = <-ch - if !proto.Equal(got, want2) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want2) - } - cancel() + verifyEvents(t, ch, want1, want2) + // Ensure the go func error return was verified. + cancel() <-ch } // TestVStreamChunks ensures that a transaction that's broken // into chunks is sent together. func TestVStreamChunks(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -129,9 +126,6 @@ func TestVStreamChunks(t *testing.T) { } sbc1.AddVStreamEvents([]*binlogdatapb.VEvent{{Type: binlogdatapb.VEventType_COMMIT}}, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - rowEncountered := false doneCounting := false rowCount := 0 @@ -186,6 +180,9 @@ func TestVStreamChunks(t *testing.T) { } func TestVStreamMulti(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -205,11 +202,6 @@ func TestVStreamMulti(t *testing.T) { } sbc1.AddVStreamEvents(send1, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - var got *binlogdatapb.VGtid - i := 0 vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -221,18 +213,15 @@ func TestVStreamMulti(t *testing.T) { Gtid: "pos", }}, } - _ = vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { - defer func() { i++ }() - for _, ev := range events { - if ev.Type == binlogdatapb.VEventType_VGTID { - got = ev.Vgtid - } - } - if i == 1 { - cancel() + ch := startVStream(ctx, t, vsm, vgtid) + <-ch + response := <-ch + var got *binlogdatapb.VGtid + for _, ev := range response.Events { + if ev.Type == binlogdatapb.VEventType_VGTID { + got = ev.Vgtid } - return nil - }) + } want := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -250,29 +239,25 @@ func TestVStreamMulti(t *testing.T) { } func TestVStreamRetry(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() vsm := newTestVStreamManager(hc, new(sandboxTopo), "aa") sbc0 := hc.AddTestTablet("aa", "1.1.1.1", 1001, name, "-20", topodatapb.TabletType_MASTER, true, 1, nil) - send1 := []*binlogdatapb.VEvent{ + commit := []*binlogdatapb.VEvent{ {Type: binlogdatapb.VEventType_COMMIT}, } - sbc0.AddVStreamEvents(send1, nil) + sbc0.AddVStreamEvents(commit, nil) sbc0.AddVStreamEvents(nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "aa")) - - send2 := []*binlogdatapb.VEvent{ - {Type: binlogdatapb.VEventType_COMMIT}, - } - sbc0.AddVStreamEvents(send2, nil) + sbc0.AddVStreamEvents(commit, nil) sbc0.AddVStreamEvents(nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "bb")) sbc0.AddVStreamEvents(nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cc")) sbc0.AddVStreamEvents(nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "final error")) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - count := 0 vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ @@ -285,9 +270,7 @@ func TestVStreamRetry(t *testing.T) { count++ return nil }) - if count != 2 { - t.Errorf("count: %d, want 2", count) - } + assert.Equal(t, 2, count) wantErr := "final error" if err == nil || !strings.Contains(err.Error(), wantErr) { t.Errorf("vstream end: %v, must contain %v", err.Error(), wantErr) @@ -295,6 +278,9 @@ func TestVStreamRetry(t *testing.T) { } func TestVStreamHeartbeat(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -314,24 +300,20 @@ func TestVStreamHeartbeat(t *testing.T) { {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "t0"}}, {Type: binlogdatapb.VEventType_COMMIT}, } - wantvgtid := &binlogdatapb.VGtid{ - ShardGtids: []*binlogdatapb.ShardGtid{{ - Keyspace: name, - Shard: "-20", - Gtid: "gtid01", - }}, - } want := &binlogdatapb.VStreamResponse{Events: []*binlogdatapb.VEvent{ - {Type: binlogdatapb.VEventType_VGTID, Vgtid: wantvgtid}, + {Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ + ShardGtids: []*binlogdatapb.ShardGtid{{ + Keyspace: name, + Shard: "-20", + Gtid: "gtid01", + }}, + }}, {Type: binlogdatapb.VEventType_FIELD, FieldEvent: &binlogdatapb.FieldEvent{TableName: "TestVStream.f0"}}, {Type: binlogdatapb.VEventType_ROW, RowEvent: &binlogdatapb.RowEvent{TableName: "TestVStream.t0"}}, {Type: binlogdatapb.VEventType_COMMIT}, }} sbc0.AddVStreamEvents(send1, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -339,21 +321,14 @@ func TestVStreamHeartbeat(t *testing.T) { Gtid: "pos", }}, } - err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { - got := &binlogdatapb.VStreamResponse{Events: events} - if !proto.Equal(got, want) { - t.Fatalf("vstream:\n%v, want\n%v", got, want) - } - cancel() - return nil - }) - wantErr := "context canceled" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Errorf("vstream end: %v, must contain %v", err.Error(), wantErr) - } + ch := startVStream(ctx, t, vsm, vgtid) + verifyEvents(t, ch, want) } func TestVStreamJournalOneToMany(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -421,9 +396,6 @@ func TestVStreamJournalOneToMany(t *testing.T) { sbc2.ExpectVStreamStartPos("pos1020") sbc2.AddVStreamEvents(send4, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -431,26 +403,13 @@ func TestVStreamJournalOneToMany(t *testing.T) { Gtid: "pos", }}, } - ch := make(chan *binlogdatapb.VStreamResponse) - go func() { - err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { - ch <- &binlogdatapb.VStreamResponse{Events: events} - return nil - }) - wantErr := "context canceled" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Errorf("vstream end: %v, must contain %v", err, wantErr) - } - ch <- nil - }() - got := <-ch - if !proto.Equal(got, want1) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want1) - } + ch := startVStream(ctx, t, vsm, vgtid) + verifyEvents(t, ch, want1) + // The following two events from the different shards can come in any order. // But the resulting VGTID should be the same after both are received. <-ch - got = <-ch + got := <-ch wantevent := &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_VGTID, Vgtid: &binlogdatapb.VGtid{ @@ -468,12 +427,13 @@ func TestVStreamJournalOneToMany(t *testing.T) { if !proto.Equal(got.Events[0], wantevent) { t.Errorf("vgtid: %v, want %v", got.Events[0], wantevent) } - cancel() - <-ch } func TestVStreamJournalManyToOne(t *testing.T) { - // Variable names are maintained like in OneToMany, but order is different.1 + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Variable names are maintained like in OneToMany, but order is different. name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -543,9 +503,6 @@ func TestVStreamJournalManyToOne(t *testing.T) { sbc0.ExpectVStreamStartPos("pos20") sbc0.AddVStreamEvents(send1, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -557,18 +514,7 @@ func TestVStreamJournalManyToOne(t *testing.T) { Gtid: "pos1020", }}, } - ch := make(chan *binlogdatapb.VStreamResponse) - go func() { - err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { - ch <- &binlogdatapb.VStreamResponse{Events: events} - return nil - }) - wantErr := "context canceled" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Errorf("vstream end: %v, must contain %v", err, wantErr) - } - ch <- nil - }() + ch := startVStream(ctx, t, vsm, vgtid) // The following two events from the different shards can come in any order. // But the resulting VGTID should be the same after both are received. <-ch @@ -590,15 +536,13 @@ func TestVStreamJournalManyToOne(t *testing.T) { if !proto.Equal(got.Events[0], wantevent) { t.Errorf("vgtid: %v, want %v", got.Events[0], wantevent) } - got = <-ch - if !proto.Equal(got, want1) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want1) - } - cancel() - <-ch + verifyEvents(t, ch, want1) } func TestVStreamJournalNoMatch(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + name := "TestVStream" _ = createSandbox(name) hc := discovery.NewFakeHealthCheck() @@ -709,9 +653,6 @@ func TestVStreamJournalNoMatch(t *testing.T) { }} sbc0.AddVStreamEvents(send3, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -719,44 +660,14 @@ func TestVStreamJournalNoMatch(t *testing.T) { Gtid: "pos", }}, } - ch := make(chan *binlogdatapb.VStreamResponse) - go func() { - err := vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { - ch <- &binlogdatapb.VStreamResponse{Events: events} - return nil - }) - wantErr := "context canceled" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Errorf("vstream end: %v, must contain %v", err.Error(), wantErr) - } - ch <- nil - }() - got := <-ch - if !proto.Equal(got, want1) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want1) - } - got = <-ch - if !proto.Equal(got, wantjn1) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want2) - } - got = <-ch - if !proto.Equal(got, want2) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want2) - } - got = <-ch - if !proto.Equal(got, wantjn2) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want2) - } - got = <-ch - if !proto.Equal(got, want3) { - t.Errorf("vstream(1):\n%v, want\n%v", got, want3) - } - cancel() - // Ensure the go func error return was verified. - <-ch + ch := startVStream(ctx, t, vsm, vgtid) + verifyEvents(t, ch, want1, wantjn1, want2, wantjn2, want3) } func TestVStreamJournalPartialMatch(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // Variable names are maintained like in OneToMany, but order is different.1 name := "TestVStream" _ = createSandbox(name) @@ -785,9 +696,6 @@ func TestVStreamJournalPartialMatch(t *testing.T) { } sbc2.AddVStreamEvents(send, nil) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - vgtid := &binlogdatapb.VGtid{ ShardGtids: []*binlogdatapb.ShardGtid{{ Keyspace: name, @@ -970,3 +878,24 @@ func newTestVStreamManager(hc discovery.HealthCheck, serv srvtopo.Server, cell s srvResolver := srvtopo.NewResolver(serv, gw, cell) return newVStreamManager(srvResolver, serv, cell) } + +func startVStream(ctx context.Context, t *testing.T, vsm *vstreamManager, vgtid *binlogdatapb.VGtid) <-chan *binlogdatapb.VStreamResponse { + ch := make(chan *binlogdatapb.VStreamResponse) + go func() { + _ = vsm.VStream(ctx, topodatapb.TabletType_MASTER, vgtid, nil, func(events []*binlogdatapb.VEvent) error { + ch <- &binlogdatapb.VStreamResponse{Events: events} + return nil + }) + }() + return ch +} + +func verifyEvents(t *testing.T, ch <-chan *binlogdatapb.VStreamResponse, wants ...*binlogdatapb.VStreamResponse) { + t.Helper() + for i, want := range wants { + got := <-ch + if !proto.Equal(got, want) { + t.Errorf("vstream(%d):\n%v, want\n%v", i, got, want) + } + } +} From 171730b2b857831e89a6363a0230f709d77c6f55 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 28 Dec 2019 10:05:20 -0800 Subject: [PATCH 006/825] vstream: fix race & robustize code Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/vstream_manager.go | 49 +++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/go/vt/vtgate/vstream_manager.go b/go/vt/vtgate/vstream_manager.go index 066d37fa7c9..b693db6d19d 100644 --- a/go/vt/vtgate/vstream_manager.go +++ b/go/vt/vtgate/vstream_manager.go @@ -211,6 +211,9 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha } // Safe to access sgtid.Gtid here (because it can't change until streaming begins). err = rss[0].QueryService.VStream(ctx, rss[0].Target, sgtid.Gtid, vs.filter, func(events []*binlogdatapb.VEvent) error { + // We received a valid event. Reset error count. + errCount = 0 + select { case <-ctx.Done(): return ctx.Err() @@ -223,13 +226,6 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha sendevents := make([]*binlogdatapb.VEvent, 0, len(events)) for _, event := range events { switch event.Type { - case binlogdatapb.VEventType_GTID: - // Update the VGtid and send that instead. - sgtid.Gtid = event.Gtid - sendevents = append(sendevents, &binlogdatapb.VEvent{ - Type: binlogdatapb.VEventType_VGTID, - Vgtid: proto.Clone(vs.vgtid).(*binlogdatapb.VGtid), - }) case binlogdatapb.VEventType_FIELD: // Update table names and send. ev := proto.Clone(event).(*binlogdatapb.VEvent) @@ -240,6 +236,14 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha ev := proto.Clone(event).(*binlogdatapb.VEvent) ev.RowEvent.TableName = sgtid.Keyspace + "." + ev.RowEvent.TableName sendevents = append(sendevents, ev) + case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL: + sendevents = append(sendevents, event) + eventss = append(eventss, sendevents) + if err := vs.sendAll(sgtid, eventss); err != nil { + return err + } + eventss = nil + sendevents = nil case binlogdatapb.VEventType_HEARTBEAT: // Remove all heartbeat events for now. // Otherwise they can accumulate indefinitely if there are no real events. @@ -265,20 +269,8 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha sendevents = append(sendevents, event) } } - if len(sendevents) == 0 { - return nil - } - // We received a valid event. Reset error count. - errCount = 0 - - eventss = append(eventss, sendevents) - lastEvent := sendevents[len(sendevents)-1] - switch lastEvent.Type { - case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL: - if err := vs.sendAll(sgtid, eventss); err != nil { - return err - } - eventss = nil + if len(sendevents) != 0 { + eventss = append(eventss, sendevents) } return nil }) @@ -311,8 +303,19 @@ func (vs *vstream) sendAll(sgtid *binlogdatapb.ShardGtid, eventss [][]*binlogdat defer vs.mu.Unlock() // Send all chunks while holding the lock. - for _, evs := range eventss { - if err := vs.send(evs); err != nil { + for _, events := range eventss { + // convert all gtids to vgtids. This should be done here while holding the lock. + for j, event := range events { + if event.Type == binlogdatapb.VEventType_GTID { + // Update the VGtid and send that instead. + sgtid.Gtid = event.Gtid + events[j] = &binlogdatapb.VEvent{ + Type: binlogdatapb.VEventType_VGTID, + Vgtid: proto.Clone(vs.vgtid).(*binlogdatapb.VGtid), + } + } + } + if err := vs.send(events); err != nil { return err } } From 5c890dc888df5ec6319d5b890378c34ebc88a27a Mon Sep 17 00:00:00 2001 From: deepthi Date: Tue, 21 Jan 2020 19:53:10 -0800 Subject: [PATCH 007/825] delete deprecated flags Signed-off-by: deepthi --- go/vt/mysqlctl/gcsbackupstorage/gcs.go | 2 -- go/vt/topo/locks.go | 5 ----- 2 files changed, 7 deletions(-) diff --git a/go/vt/mysqlctl/gcsbackupstorage/gcs.go b/go/vt/mysqlctl/gcsbackupstorage/gcs.go index 513a15ae16c..5473be3217a 100644 --- a/go/vt/mysqlctl/gcsbackupstorage/gcs.go +++ b/go/vt/mysqlctl/gcsbackupstorage/gcs.go @@ -37,8 +37,6 @@ import ( ) var ( - _ = flag.String("gcs_backup_storage_project", "", "This flag is unused and deprecated. It will be removed entirely in a future release.") - // bucket is where the backups will go. bucket = flag.String("gcs_backup_storage_bucket", "", "Google Cloud Storage bucket to use for backups") diff --git a/go/vt/topo/locks.go b/go/vt/topo/locks.go index cdebbe4c03c..bf1e2eb1e95 100644 --- a/go/vt/topo/locks.go +++ b/go/vt/topo/locks.go @@ -42,11 +42,6 @@ var ( // Now used only for unlock operations defaultLockTimeout = 30 * time.Second - // Deprecated - // LockTimeout is the command line flag that introduces a shorter - // timeout for locking topology structures. - _ = flag.Duration("lock_timeout", defaultLockTimeout, "deprecated: timeout for acquiring topology locks, use remote_operation_timeout") - // RemoteOperationTimeout is used for operations where we have to // call out to another process. // Used for RPC calls (including topo server calls) From 0506f5cd503e43cecfe4869144e5fc4c584f437e Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Fri, 24 Jan 2020 23:21:23 +0530 Subject: [PATCH 008/825] Add escape and quote to mysql password Signed-off-by: Yuvraj --- go/vt/mysqlctl/mysqld.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index 0133e850bca..daead198112 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -1059,6 +1059,7 @@ func (mysqld *Mysqld) executeMysqlScript(connParams *mysql.ConnParams, sql io.Re // 'defer os.Remove()' statement. func (mysqld *Mysqld) defaultsExtraFile(connParams *mysql.ConnParams) (string, error) { var contents string + connParams.Pass = strings.Replace(connParams.Pass, "#", "\\#", -1) if connParams.UnixSocket == "" { contents = fmt.Sprintf(` [client] From 57a084db73b545ece0844967641ac16965da9d8f Mon Sep 17 00:00:00 2001 From: Alex Charis Date: Fri, 24 Jan 2020 14:02:36 -0500 Subject: [PATCH 009/825] sql grammar: support USE INDEX () Signed-off-by: Alex Charis --- go/vt/sqlparser/sql.go | 3772 ++++++++++++++++++++-------------------- go/vt/sqlparser/sql.y | 4 + 2 files changed, 1923 insertions(+), 1853 deletions(-) diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index ef0a39342ff..6fe7a6a0891 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -804,23 +804,23 @@ var yyExca = [...]int{ 162, 302, -2, 290, -1, 322, - 113, 644, - -2, 640, - -1, 323, 113, 645, -2, 641, + -1, 323, + 113, 646, + -2, 642, -1, 391, - 83, 893, + 83, 894, -2, 63, -1, 392, - 83, 811, + 83, 812, -2, 64, -1, 397, - 83, 780, - -2, 606, + 83, 781, + -2, 607, -1, 399, - 83, 841, - -2, 608, + 83, 842, + -2, 609, -1, 695, 1, 355, 5, 355, @@ -850,294 +850,317 @@ var yyExca = [...]int{ 56, 44, -2, 48, -1, 848, - 113, 647, - -2, 643, + 113, 648, + -2, 644, -1, 1079, 5, 30, - -2, 440, + -2, 441, -1, 1109, 5, 29, - -2, 580, + -2, 581, -1, 1355, 5, 30, - -2, 581, + -2, 582, -1, 1408, 5, 29, - -2, 583, - -1, 1486, - 5, 30, -2, 584, + -1, 1487, + 5, 30, + -2, 585, } const yyPrivate = 57344 -const yyLast = 16546 +const yyLast = 17143 var yyAct = [...]int{ - 323, 1520, 1510, 1317, 1204, 1388, 1112, 1474, 1420, 353, - 651, 1130, 1257, 340, 57, 935, 962, 301, 1258, 1375, - 1291, 1254, 1113, 1042, 971, 933, 650, 3, 327, 1157, - 1264, 961, 81, 797, 1005, 1270, 266, 1229, 884, 266, - 292, 1183, 1070, 873, 811, 396, 975, 880, 711, 1136, - 937, 1174, 958, 922, 902, 850, 588, 1001, 692, 520, - 710, 390, 385, 915, 582, 594, 310, 266, 81, 325, - 382, 691, 266, 603, 266, 387, 700, 665, 300, 56, - 1513, 1497, 61, 1508, 1484, 293, 294, 295, 296, 1505, - 1318, 299, 991, 1024, 1496, 1483, 540, 1246, 1347, 525, - 253, 555, 314, 251, 1285, 255, 666, 1023, 63, 64, - 65, 66, 67, 1449, 616, 615, 625, 626, 618, 619, - 620, 621, 622, 623, 624, 617, 952, 365, 627, 371, - 372, 369, 370, 368, 367, 366, 1028, 261, 257, 258, - 259, 1286, 1287, 373, 374, 1022, 1145, 298, 576, 1144, - 953, 954, 1146, 712, 571, 713, 297, 1165, 572, 569, - 570, 984, 1206, 1378, 1395, 992, 1338, 557, 1336, 559, - 574, 291, 564, 565, 785, 1208, 786, 783, 1507, 1504, - 1475, 1203, 916, 1467, 1524, 1528, 976, 541, 883, 527, - 1421, 551, 255, 1209, 790, 1019, 1016, 1017, 254, 1015, - 556, 558, 978, 1423, 1280, 1429, 1230, 575, 774, 784, - 24, 25, 52, 27, 28, 1207, 787, 1131, 1133, 252, - 1279, 978, 1200, 1278, 523, 530, 268, 256, 1202, 43, - 1036, 1026, 1029, 1035, 29, 48, 49, 1088, 639, 640, - 978, 1456, 1358, 1085, 1232, 620, 621, 622, 623, 624, - 617, 1158, 266, 627, 1215, 38, 260, 266, 959, 54, - 1141, 1098, 1064, 266, 822, 706, 607, 627, 1021, 266, - 521, 1422, 81, 547, 81, 948, 81, 81, 1234, 81, - 1238, 81, 1233, 1450, 1231, 554, 816, 81, 617, 1236, - 1020, 627, 1522, 812, 1132, 1523, 992, 1521, 1235, 977, - 537, 819, 1465, 521, 602, 1191, 1482, 1430, 1428, 1303, - 1084, 1237, 1239, 1438, 1268, 1248, 857, 81, 977, 590, - 31, 32, 34, 33, 36, 1201, 50, 1199, 903, 1025, - 855, 856, 854, 591, 776, 1189, 519, 977, 578, 579, - 639, 640, 974, 972, 1027, 973, 639, 640, 37, 44, - 45, 970, 976, 46, 47, 35, 543, 544, 545, 821, - 1304, 601, 600, 534, 1083, 535, 1082, 70, 536, 39, - 40, 600, 41, 42, 714, 813, 601, 600, 602, 1044, - 266, 266, 266, 601, 600, 825, 826, 602, 553, 81, - 592, 1163, 985, 602, 352, 81, 903, 820, 1095, 981, - 602, 1470, 1190, 71, 597, 982, 690, 1195, 1192, 1185, - 1193, 1188, 1488, 1184, 601, 600, 1186, 1187, 618, 619, - 620, 621, 622, 623, 624, 617, 79, 1384, 627, 1529, - 1194, 602, 1463, 1383, 601, 600, 601, 600, 526, 907, - 1061, 1062, 1063, 1250, 668, 670, 672, 674, 676, 678, - 679, 602, 699, 602, 53, 1178, 393, 250, 1043, 581, - 704, 54, 395, 1490, 708, 560, 1177, 561, 562, 1530, - 563, 853, 566, 669, 671, 1166, 675, 677, 577, 680, - 1466, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 1344, 1402, 627, 616, 615, 625, 626, - 618, 619, 620, 621, 622, 623, 624, 617, 1381, 1212, - 627, 840, 842, 843, 1175, 528, 529, 841, 1047, 874, - 266, 875, 379, 380, 1147, 81, 1148, 22, 354, 51, - 266, 266, 81, 81, 81, 1071, 1426, 1506, 266, 1492, - 581, 266, 1426, 1478, 266, 1426, 581, 581, 266, 1320, - 81, 81, 1426, 1457, 1435, 81, 81, 81, 266, 81, - 81, 1426, 1425, 1373, 1372, 81, 81, 1158, 616, 615, + 323, 1521, 1511, 1317, 1475, 1204, 1112, 1388, 651, 1375, + 1421, 353, 1257, 1130, 962, 340, 1291, 935, 301, 1258, + 991, 1254, 1113, 1042, 57, 1005, 958, 971, 327, 961, + 1264, 1229, 81, 396, 873, 1270, 266, 1070, 880, 266, + 1157, 884, 922, 1174, 811, 650, 3, 797, 933, 1183, + 975, 711, 691, 937, 902, 588, 850, 520, 390, 1001, + 710, 1136, 915, 329, 582, 385, 594, 266, 81, 325, + 310, 603, 266, 382, 266, 387, 700, 292, 665, 56, + 1514, 1498, 61, 1509, 1485, 540, 666, 551, 1506, 1024, + 985, 692, 1318, 1497, 1484, 1246, 1347, 300, 525, 555, + 712, 1145, 713, 1023, 1144, 314, 1285, 1146, 63, 64, + 65, 66, 67, 276, 261, 257, 258, 259, 952, 553, + 1286, 1287, 293, 294, 295, 296, 253, 576, 299, 251, + 298, 255, 1028, 953, 954, 297, 1165, 984, 286, 1378, + 992, 1022, 1450, 616, 615, 625, 626, 618, 619, 620, + 621, 622, 623, 624, 617, 1206, 365, 627, 371, 372, + 369, 370, 368, 367, 366, 557, 291, 559, 1338, 1336, + 571, 1395, 373, 374, 572, 569, 570, 786, 564, 565, + 574, 1208, 783, 785, 1508, 1505, 575, 393, 883, 269, + 1476, 1019, 1016, 1017, 1468, 1015, 272, 976, 556, 558, + 1203, 916, 1529, 255, 280, 275, 541, 1525, 1207, 1209, + 1422, 1430, 1131, 1133, 527, 790, 1230, 787, 784, 978, + 774, 1280, 978, 1424, 254, 1200, 1279, 1026, 1029, 1278, + 978, 1202, 530, 260, 523, 268, 256, 278, 1036, 1457, + 1088, 1035, 1358, 285, 1215, 252, 639, 640, 1141, 1158, + 1098, 1085, 266, 1064, 1232, 822, 706, 266, 607, 547, + 521, 959, 617, 266, 1021, 627, 627, 948, 819, 266, + 270, 521, 81, 1303, 81, 812, 81, 81, 816, 81, + 602, 81, 1466, 554, 1439, 537, 1020, 81, 1234, 1132, + 1238, 1423, 1233, 1044, 1231, 1268, 1191, 282, 273, 1236, + 283, 284, 289, 992, 519, 1483, 274, 277, 1235, 271, + 288, 287, 1451, 1431, 1429, 1523, 977, 81, 1524, 977, + 1522, 1237, 1239, 857, 1304, 1025, 1189, 977, 1201, 590, + 1199, 714, 974, 972, 70, 973, 316, 855, 856, 854, + 1027, 970, 976, 639, 640, 543, 544, 545, 534, 600, + 535, 1248, 591, 536, 639, 640, 903, 813, 1095, 903, + 776, 560, 1163, 561, 562, 602, 563, 1083, 566, 1082, + 71, 1471, 1043, 1084, 577, 578, 579, 840, 842, 843, + 266, 266, 266, 841, 597, 981, 601, 600, 637, 81, + 592, 982, 526, 1190, 352, 81, 1464, 1489, 1195, 1192, + 1185, 1193, 1188, 602, 1184, 601, 600, 1186, 1187, 616, + 615, 625, 626, 618, 619, 620, 621, 622, 623, 624, + 617, 1194, 602, 627, 601, 600, 79, 1384, 1383, 690, + 615, 625, 626, 618, 619, 620, 621, 622, 623, 624, + 617, 602, 54, 627, 695, 668, 670, 672, 674, 676, + 678, 679, 853, 669, 671, 699, 675, 677, 1178, 680, + 704, 1177, 395, 1071, 708, 611, 1166, 614, 821, 528, + 529, 1491, 1467, 628, 629, 630, 631, 632, 633, 634, + 1402, 612, 613, 610, 616, 615, 625, 626, 618, 619, + 620, 621, 622, 623, 624, 617, 825, 826, 627, 620, + 621, 622, 623, 624, 617, 1381, 820, 627, 393, 625, + 626, 618, 619, 620, 621, 622, 623, 624, 617, 1212, + 266, 627, 1320, 601, 600, 81, 250, 1530, 354, 51, + 266, 266, 81, 81, 81, 601, 600, 874, 266, 875, + 602, 266, 1250, 1175, 266, 601, 600, 1147, 266, 1148, + 81, 81, 602, 1047, 1158, 81, 81, 81, 266, 81, + 81, 1153, 602, 1427, 1507, 81, 81, 1531, 616, 615, 625, 626, 618, 619, 620, 621, 622, 623, 624, 617, - 51, 1153, 627, 1350, 1360, 581, 1434, 305, 306, 1357, - 581, 1310, 1309, 799, 81, 1306, 1307, 1306, 1305, 266, - 1077, 581, 919, 581, 1300, 81, 886, 581, 827, 876, - 796, 791, 795, 777, 775, 772, 721, 720, 1137, 549, - 851, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 542, 533, 627, 532, 1255, 979, 320, - 1267, 846, 343, 342, 345, 346, 347, 348, 1137, 81, - 24, 344, 349, 848, 924, 927, 928, 929, 925, 829, - 926, 930, 919, 58, 1271, 1272, 395, 54, 395, 702, - 395, 395, 702, 395, 1107, 395, 1267, 1218, 844, 1108, - 888, 395, 81, 81, 24, 886, 1353, 893, 896, 266, - 24, 1437, 1267, 904, 942, 919, 701, 266, 266, 54, - 307, 266, 266, 918, 1077, 266, 266, 266, 81, 877, - 878, 605, 703, 1407, 705, 703, 773, 701, 1308, 1149, - 951, 81, 1077, 780, 781, 782, 943, 900, 1101, 919, - 945, 1100, 912, 54, 1277, 1077, 701, 707, 823, 54, - 580, 800, 801, 789, 1498, 1390, 802, 803, 804, 54, - 806, 807, 799, 986, 1365, 1006, 808, 809, 1296, 1271, - 1272, 1205, 941, 1152, 1002, 997, 996, 1391, 946, 949, - 950, 1009, 1515, 1511, 1298, 266, 81, 393, 81, 1274, - 1255, 966, 1179, 395, 266, 266, 266, 266, 266, 716, - 266, 266, 817, 793, 266, 81, 1124, 1276, 1122, 1121, - 552, 1125, 552, 1123, 552, 552, 1007, 552, 835, 552, - 1120, 266, 1502, 266, 266, 552, 1495, 1126, 266, 928, - 929, 311, 312, 1214, 1049, 595, 1003, 1004, 993, 994, - 995, 1500, 1059, 1058, 583, 51, 1170, 719, 596, 595, - 550, 593, 1162, 889, 890, 1472, 584, 895, 898, 899, - 636, 1471, 596, 638, 1405, 1160, 1052, 1154, 1351, 924, - 927, 928, 929, 925, 851, 926, 930, 1386, 848, 1012, - 792, 932, 911, 1443, 913, 914, 308, 309, 302, 303, - 1053, 649, 58, 653, 654, 655, 656, 657, 658, 659, + 51, 799, 627, 1061, 1062, 1063, 1493, 581, 306, 876, + 22, 379, 380, 796, 81, 1427, 1479, 1427, 581, 266, + 924, 927, 928, 929, 925, 81, 926, 930, 795, 827, + 1271, 1272, 773, 777, 791, 1427, 1458, 1427, 1426, 780, + 781, 782, 851, 618, 619, 620, 621, 622, 623, 624, + 617, 775, 320, 627, 1373, 1372, 581, 800, 801, 585, + 589, 848, 802, 803, 804, 772, 806, 807, 549, 81, + 305, 542, 808, 809, 533, 846, 608, 1360, 581, 1357, + 581, 1310, 1309, 829, 1306, 1307, 395, 532, 395, 702, + 395, 395, 1436, 395, 852, 395, 844, 1306, 1305, 1435, + 907, 395, 81, 81, 1077, 581, 1300, 893, 896, 266, + 702, 652, 1137, 904, 919, 581, 1218, 266, 266, 888, + 663, 266, 266, 886, 581, 266, 266, 266, 81, 877, + 878, 605, 703, 24, 705, 343, 342, 345, 346, 347, + 348, 81, 979, 912, 344, 349, 58, 847, 900, 721, + 720, 1255, 1267, 703, 1267, 701, 919, 1107, 1137, 918, + 799, 1077, 1108, 24, 942, 886, 701, 1353, 1438, 943, + 24, 919, 1308, 945, 1149, 951, 993, 994, 995, 1101, + 695, 941, 54, 307, 695, 919, 949, 1077, 695, 1100, + 950, 946, 1407, 1077, 701, 266, 81, 707, 81, 966, + 823, 789, 1267, 395, 266, 266, 266, 266, 266, 716, + 266, 266, 54, 54, 266, 81, 1499, 1007, 1390, 54, + 552, 986, 552, 1365, 552, 552, 1006, 552, 1296, 552, + 1152, 266, 54, 266, 266, 552, 1271, 1272, 266, 1002, + 997, 987, 988, 989, 990, 996, 1205, 393, 1003, 1004, + 1391, 1009, 1516, 1512, 1298, 51, 1274, 998, 999, 1000, + 963, 1255, 1179, 889, 890, 817, 793, 895, 898, 899, + 636, 835, 1124, 638, 1122, 1277, 848, 1125, 1126, 1123, + 928, 929, 1276, 1011, 1121, 1013, 851, 1120, 311, 312, + 1052, 1503, 911, 1496, 913, 914, 1214, 1049, 1501, 1053, + 1059, 649, 1040, 653, 654, 655, 656, 657, 658, 659, 660, 661, 1054, 664, 667, 667, 667, 673, 667, 667, - 673, 667, 681, 682, 683, 684, 685, 686, 1057, 696, - 1066, 1137, 1442, 1393, 573, 1089, 1056, 1517, 1516, 395, - 266, 266, 266, 266, 266, 1086, 395, 395, 395, 810, - 598, 1517, 266, 1453, 1379, 266, 818, 60, 62, 266, - 55, 1, 1109, 266, 395, 395, 1509, 1114, 1319, 395, - 395, 395, 1387, 395, 395, 1018, 1094, 1473, 1419, 395, - 395, 888, 81, 1290, 969, 960, 69, 1011, 1138, 1013, - 641, 642, 643, 644, 645, 646, 647, 648, 1150, 1116, - 1117, 518, 1119, 68, 1127, 1464, 1040, 968, 831, 1135, - 967, 1115, 1427, 1139, 1118, 1140, 847, 1377, 980, 605, - 1142, 1164, 395, 983, 1297, 1159, 1161, 1469, 727, 725, - 81, 81, 726, 724, 729, 728, 723, 279, 388, 931, - 1155, 1156, 715, 1060, 1008, 599, 72, 1198, 1197, 1014, - 815, 828, 567, 568, 281, 635, 1055, 1143, 394, 1262, - 81, 824, 587, 879, 1169, 1441, 1171, 1172, 1173, 1176, - 1392, 1093, 662, 552, 901, 1182, 266, 328, 1196, 905, - 552, 552, 552, 839, 341, 81, 338, 339, 830, 1106, - 1075, 1076, 609, 326, 318, 694, 909, 910, 552, 552, - 1167, 1168, 687, 552, 552, 552, 1211, 552, 552, 1092, - 885, 887, 923, 552, 552, 921, 393, 920, 383, 329, - 1273, 1269, 395, 693, 1217, 1346, 1448, 1247, 834, 963, - 1222, 81, 81, 1221, 1256, 395, 1228, 26, 1259, 59, - 313, 19, 1241, 987, 988, 989, 990, 1240, 18, 17, - 20, 16, 1052, 15, 14, 81, 1114, 1261, 538, 998, - 999, 1000, 30, 21, 848, 13, 12, 11, 10, 9, - 81, 1282, 81, 81, 1275, 8, 7, 6, 5, 4, - 304, 23, 1266, 2, 0, 1281, 0, 51, 1289, 0, - 395, 0, 395, 0, 0, 0, 0, 0, 1293, 0, - 266, 1288, 653, 0, 0, 1294, 1295, 0, 1284, 395, - 0, 0, 0, 0, 0, 0, 0, 0, 266, 0, - 0, 0, 1181, 0, 81, 0, 0, 81, 81, 81, - 266, 847, 0, 0, 0, 0, 81, 395, 0, 266, - 1301, 1302, 0, 0, 1312, 934, 0, 0, 0, 696, - 0, 1210, 0, 696, 0, 1325, 0, 1313, 0, 1315, - 0, 0, 0, 0, 1327, 0, 0, 0, 0, 849, - 0, 0, 858, 859, 860, 861, 862, 863, 864, 865, - 866, 867, 868, 869, 870, 871, 872, 1326, 0, 1334, - 0, 0, 0, 0, 0, 0, 0, 0, 1352, 0, - 0, 1361, 0, 0, 0, 0, 0, 0, 0, 81, - 1362, 0, 0, 0, 0, 0, 1114, 81, 0, 0, - 1371, 0, 0, 0, 552, 1150, 552, 908, 0, 0, - 0, 0, 81, 905, 0, 0, 1073, 0, 316, 81, - 1074, 0, 0, 552, 0, 0, 0, 0, 1079, 1080, - 1081, 0, 0, 0, 0, 1087, 0, 0, 1090, 1091, - 0, 0, 586, 0, 1097, 0, 0, 0, 1099, 0, - 963, 1102, 1103, 1104, 1105, 0, 395, 0, 81, 81, - 0, 81, 0, 1259, 0, 0, 81, 0, 81, 81, - 81, 266, 1406, 1129, 81, 0, 1065, 0, 264, 1413, - 0, 290, 0, 1408, 0, 1414, 1418, 1415, 1416, 1417, - 0, 81, 266, 1431, 1424, 0, 0, 0, 1380, 0, - 1382, 0, 0, 0, 1180, 395, 317, 0, 0, 386, - 1439, 0, 0, 0, 264, 0, 264, 1259, 0, 0, - 1454, 0, 0, 0, 637, 1394, 0, 81, 1462, 0, - 1461, 0, 0, 1432, 395, 1433, 1455, 0, 81, 81, - 0, 0, 0, 1476, 1110, 1111, 0, 0, 696, 696, - 696, 696, 696, 1220, 1480, 1477, 0, 81, 0, 395, - 1485, 0, 0, 934, 0, 1134, 0, 0, 266, 0, - 0, 696, 0, 0, 0, 0, 81, 0, 0, 0, - 695, 0, 1114, 1494, 0, 0, 0, 1251, 0, 0, - 0, 0, 0, 395, 0, 1067, 1068, 1069, 1501, 81, - 1499, 0, 905, 1385, 0, 1263, 1265, 0, 0, 0, - 0, 0, 1514, 0, 0, 1226, 1227, 0, 1503, 1525, - 0, 697, 0, 0, 0, 0, 0, 0, 0, 1265, - 0, 0, 0, 0, 0, 0, 0, 0, 963, 552, - 963, 0, 0, 0, 395, 0, 395, 1292, 0, 0, - 0, 0, 0, 611, 0, 614, 0, 263, 0, 0, - 0, 628, 629, 630, 631, 632, 633, 634, 552, 612, - 613, 610, 616, 615, 625, 626, 618, 619, 620, 621, - 622, 623, 624, 617, 0, 0, 627, 0, 384, 0, - 0, 0, 0, 522, 264, 524, 0, 0, 1316, 264, - 0, 1321, 1322, 1323, 1220, 264, 1349, 0, 0, 0, - 395, 264, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 585, 589, 1331, 1332, 0, 1333, 0, 0, 1335, - 0, 1337, 0, 0, 0, 0, 0, 1260, 608, 51, - 0, 0, 0, 0, 616, 615, 625, 626, 618, 619, - 620, 621, 622, 623, 624, 617, 0, 0, 627, 0, - 0, 1328, 905, 0, 0, 0, 0, 0, 0, 1330, - 0, 0, 0, 652, 0, 0, 0, 963, 0, 0, - 1339, 1340, 663, 395, 0, 1374, 0, 0, 0, 0, - 0, 1376, 0, 0, 0, 0, 0, 0, 0, 0, - 1354, 1355, 1356, 0, 1359, 0, 395, 1389, 0, 0, - 852, 1224, 1225, 395, 0, 0, 0, 0, 0, 0, - 0, 1370, 264, 264, 264, 1242, 1243, 0, 1244, 1245, - 625, 626, 618, 619, 620, 621, 622, 623, 624, 617, - 1252, 1253, 627, 0, 0, 0, 0, 696, 0, 0, - 0, 0, 1410, 1411, 0, 1412, 0, 0, 0, 0, + 673, 667, 681, 682, 683, 684, 685, 686, 1058, 696, + 814, 1066, 595, 1170, 719, 550, 1162, 595, 852, 395, + 266, 266, 266, 266, 266, 596, 395, 395, 395, 583, + 596, 1473, 266, 593, 1472, 266, 1405, 837, 838, 266, + 1160, 584, 847, 266, 395, 395, 1154, 1114, 1351, 395, + 395, 395, 1386, 395, 395, 1012, 1094, 792, 932, 395, + 395, 1109, 81, 641, 642, 643, 644, 645, 646, 647, + 648, 1057, 1115, 1127, 1444, 1118, 1150, 308, 309, 1056, + 888, 580, 302, 695, 695, 695, 695, 695, 831, 303, + 652, 1138, 1135, 891, 892, 58, 1443, 1393, 695, 605, + 1137, 573, 395, 1142, 1089, 1139, 695, 1140, 1167, 1168, + 81, 81, 1116, 1117, 1086, 1119, 1159, 810, 1169, 598, + 1171, 1172, 1173, 1060, 1155, 1156, 924, 927, 928, 929, + 925, 1518, 926, 930, 1518, 1517, 1454, 1379, 818, 60, + 81, 1176, 62, 879, 55, 1, 1510, 1319, 1387, 1018, + 1474, 957, 1420, 552, 1290, 969, 266, 960, 69, 905, + 552, 552, 552, 518, 68, 81, 1196, 1465, 968, 967, + 1075, 1076, 1428, 1377, 980, 1164, 909, 910, 552, 552, + 983, 963, 1297, 552, 552, 552, 1211, 552, 552, 1092, + 1161, 1470, 1182, 552, 552, 727, 725, 726, 1181, 724, + 729, 728, 395, 723, 1221, 1222, 279, 388, 931, 715, + 1008, 81, 81, 599, 1256, 395, 1241, 1247, 1240, 1228, + 1259, 72, 1198, 1197, 1014, 815, 567, 1210, 568, 281, + 635, 1055, 848, 1143, 394, 81, 1114, 1262, 824, 587, + 1442, 1392, 1093, 662, 901, 328, 1052, 839, 341, 338, + 81, 339, 81, 81, 1275, 830, 1261, 1050, 1051, 1106, + 589, 1282, 609, 326, 318, 1281, 1289, 51, 694, 687, + 395, 923, 395, 921, 1266, 920, 383, 1273, 1269, 1288, + 266, 1293, 653, 693, 1220, 1217, 1346, 1449, 834, 395, + 26, 59, 313, 19, 1301, 1302, 1294, 1295, 266, 18, + 1284, 17, 20, 16, 81, 15, 14, 81, 81, 81, + 266, 538, 30, 21, 13, 12, 81, 395, 1251, 266, + 11, 10, 9, 1078, 8, 934, 7, 6, 5, 696, + 4, 304, 23, 696, 2, 1325, 0, 0, 0, 0, + 1096, 0, 849, 0, 0, 858, 859, 860, 861, 862, + 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, + 1334, 1312, 0, 0, 0, 0, 0, 1327, 0, 963, + 0, 963, 828, 0, 1313, 0, 1315, 0, 1352, 1326, + 0, 0, 695, 0, 0, 0, 0, 0, 0, 81, + 1362, 1361, 0, 0, 0, 0, 1114, 81, 0, 581, + 908, 1371, 0, 1150, 552, 0, 552, 0, 0, 0, + 0, 0, 81, 905, 0, 0, 0, 0, 0, 81, + 0, 1331, 1332, 552, 1333, 0, 1380, 1335, 1382, 1337, + 0, 885, 887, 0, 0, 1220, 616, 615, 625, 626, + 618, 619, 620, 621, 622, 623, 624, 617, 0, 0, + 627, 0, 0, 1394, 0, 0, 395, 0, 81, 81, + 0, 81, 0, 0, 0, 1259, 81, 0, 81, 81, + 81, 266, 0, 1406, 81, 1414, 1065, 1415, 1417, 1418, + 1413, 1213, 0, 1374, 0, 0, 0, 0, 1419, 0, + 1425, 81, 266, 0, 0, 1432, 0, 0, 0, 1385, + 1440, 0, 1408, 0, 1180, 395, 0, 1433, 963, 1434, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1259, + 1455, 0, 0, 0, 0, 0, 0, 0, 81, 0, + 0, 1463, 1249, 1462, 395, 0, 0, 0, 1389, 81, + 81, 0, 0, 0, 1110, 1111, 1477, 0, 696, 696, + 696, 696, 696, 1481, 1478, 1456, 0, 0, 81, 395, + 0, 1486, 0, 934, 0, 1134, 0, 0, 0, 266, + 0, 696, 0, 0, 1283, 0, 0, 81, 0, 0, + 0, 0, 0, 1114, 0, 1495, 0, 0, 1067, 1068, + 1069, 0, 0, 395, 0, 0, 0, 0, 1500, 1502, + 81, 0, 905, 0, 0, 1263, 1265, 0, 0, 1504, + 0, 0, 0, 1515, 0, 0, 0, 0, 0, 0, + 1526, 0, 0, 0, 0, 0, 0, 0, 0, 1265, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 552, + 0, 0, 0, 0, 395, 0, 395, 1292, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1073, 1389, 963, + 0, 1074, 0, 0, 0, 0, 0, 0, 552, 1079, + 1080, 1081, 0, 0, 0, 0, 1087, 0, 0, 1090, + 1091, 0, 0, 0, 586, 1097, 1350, 1348, 1344, 1099, + 0, 0, 1102, 1103, 1104, 1105, 0, 652, 1316, 0, + 0, 1321, 1322, 1323, 0, 1363, 0, 0, 1364, 0, + 395, 1366, 0, 0, 1129, 0, 0, 0, 0, 0, + 264, 0, 0, 290, 616, 615, 625, 626, 618, 619, + 620, 621, 622, 623, 624, 617, 0, 1260, 627, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 317, 0, + 0, 386, 0, 0, 0, 0, 264, 0, 264, 0, + 0, 0, 905, 616, 615, 625, 626, 618, 619, 620, + 621, 622, 623, 624, 617, 0, 0, 627, 0, 0, + 0, 0, 0, 395, 0, 0, 0, 0, 0, 0, + 0, 1376, 0, 0, 0, 0, 0, 1349, 0, 0, + 0, 0, 0, 0, 1224, 1225, 395, 0, 0, 0, + 0, 0, 0, 395, 0, 0, 0, 0, 1242, 1243, + 0, 1244, 1245, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1252, 1253, 616, 615, 625, 626, 618, + 619, 620, 621, 622, 623, 624, 617, 696, 0, 627, + 0, 0, 1410, 1411, 0, 1412, 1226, 1227, 0, 0, 1376, 0, 1376, 1376, 1376, 0, 0, 0, 1292, 0, - 0, 0, 0, 531, 0, 1345, 0, 0, 539, 0, - 0, 0, 0, 0, 546, 1376, 1401, 0, 0, 0, - 548, 0, 0, 0, 0, 0, 695, 0, 0, 0, - 695, 0, 1299, 0, 695, 0, 0, 1367, 1368, 1369, - 615, 625, 626, 618, 619, 620, 621, 622, 623, 624, - 617, 1468, 0, 627, 0, 0, 1389, 963, 0, 0, - 0, 0, 395, 395, 0, 0, 1444, 1445, 1446, 1447, - 552, 0, 0, 1451, 1452, 0, 1343, 0, 905, 0, - 0, 1487, 0, 0, 0, 1458, 1459, 1460, 0, 0, - 0, 0, 264, 1329, 0, 0, 0, 0, 0, 0, - 1493, 0, 264, 264, 0, 0, 0, 0, 0, 0, - 264, 0, 1260, 264, 0, 1409, 264, 0, 1481, 0, - 798, 0, 814, 1376, 0, 1486, 0, 0, 0, 0, - 264, 689, 0, 698, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1491, 0, 1436, 0, 0, 0, 837, - 838, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 0, 0, 627, 1260, 0, 51, 0, - 1223, 264, 0, 0, 0, 0, 0, 0, 0, 0, - 798, 0, 0, 0, 852, 0, 0, 0, 1526, 1527, + 0, 0, 0, 0, 0, 1345, 0, 0, 0, 0, + 1480, 652, 0, 0, 0, 1376, 0, 697, 0, 0, + 1223, 0, 0, 0, 0, 1299, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1367, 1368, 1369, 616, 615, 625, 626, 618, 619, 620, 621, 622, 623, - 624, 617, 652, 0, 627, 891, 892, 0, 0, 0, - 0, 1396, 1397, 1398, 1399, 1400, 0, 0, 0, 1403, - 1404, 276, 317, 0, 0, 0, 0, 317, 317, 0, - 0, 317, 317, 317, 0, 0, 0, 906, 0, 1342, - 0, 0, 0, 0, 0, 0, 286, 0, 0, 695, - 695, 695, 695, 695, 1341, 0, 317, 317, 317, 317, - 0, 264, 0, 957, 695, 0, 1512, 0, 0, 264, - 939, 722, 695, 264, 264, 0, 0, 264, 947, 798, - 0, 778, 779, 0, 0, 0, 0, 0, 0, 788, - 0, 0, 384, 0, 0, 794, 0, 269, 0, 0, - 0, 0, 0, 0, 272, 0, 0, 0, 0, 805, - 0, 0, 280, 275, 616, 615, 625, 626, 618, 619, - 620, 621, 622, 623, 624, 617, 0, 0, 627, 616, + 624, 617, 1469, 263, 627, 0, 0, 0, 0, 0, + 0, 0, 0, 395, 395, 0, 264, 0, 0, 0, + 552, 264, 0, 0, 744, 0, 0, 264, 0, 905, + 0, 0, 1488, 264, 384, 0, 1329, 0, 0, 522, + 0, 524, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1494, 0, 0, 1343, 0, 0, 0, 0, 0, + 0, 0, 1260, 0, 0, 1409, 0, 0, 0, 0, + 0, 0, 0, 0, 1376, 0, 0, 0, 0, 0, + 0, 0, 1328, 0, 0, 0, 0, 0, 0, 0, + 1330, 0, 0, 0, 0, 1437, 0, 0, 0, 0, + 0, 1339, 1340, 732, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1260, 0, 51, 0, + 0, 1354, 1355, 1356, 0, 1359, 0, 0, 0, 616, 615, 625, 626, 618, 619, 620, 621, 622, 623, 624, - 617, 0, 0, 627, 0, 278, 0, 264, 0, 0, - 836, 285, 0, 0, 0, 0, 264, 264, 264, 264, - 264, 0, 264, 264, 0, 0, 264, 0, 0, 1050, - 1051, 0, 589, 0, 0, 0, 0, 0, 270, 0, - 0, 0, 1518, 264, 0, 1045, 1046, 0, 0, 0, - 264, 0, 0, 0, 0, 798, 0, 0, 0, 0, - 1072, 0, 0, 0, 0, 282, 273, 317, 283, 284, - 289, 0, 0, 0, 274, 277, 0, 271, 288, 287, - 616, 615, 625, 626, 618, 619, 620, 621, 622, 623, - 624, 617, 0, 744, 627, 1078, 0, 0, 0, 0, - 917, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1096, 944, 317, 317, 616, 615, 625, 626, - 618, 619, 620, 621, 622, 623, 624, 617, 0, 0, - 627, 0, 0, 317, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 906, 264, 264, 264, 264, 264, 0, 0, 0, - 0, 0, 0, 0, 1128, 0, 0, 264, 0, 0, - 0, 939, 732, 0, 0, 264, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1010, 0, 0, 0, - 0, 0, 0, 0, 0, 1030, 1031, 1032, 1033, 1034, - 0, 1037, 1038, 0, 0, 1039, 0, 0, 695, 0, - 745, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1041, 0, 0, 0, 0, 0, 0, 1048, - 0, 0, 0, 758, 761, 762, 763, 764, 765, 766, - 0, 767, 768, 769, 770, 771, 746, 747, 748, 749, - 730, 731, 759, 1213, 733, 0, 734, 735, 736, 737, - 738, 739, 740, 741, 742, 743, 750, 751, 752, 753, - 754, 755, 756, 757, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 317, 0, - 0, 0, 0, 0, 1249, 0, 0, 0, 0, 0, - 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 760, 0, 0, 0, 0, 0, - 0, 798, 0, 0, 0, 0, 0, 0, 0, 0, - 906, 0, 0, 0, 0, 0, 1283, 0, 0, 0, + 617, 745, 1370, 627, 264, 264, 264, 0, 0, 0, + 0, 0, 0, 0, 1396, 1397, 1398, 1399, 1400, 0, + 0, 0, 1403, 1404, 758, 761, 762, 763, 764, 765, + 766, 0, 767, 768, 769, 770, 771, 746, 747, 748, + 749, 730, 731, 759, 0, 733, 0, 734, 735, 736, + 737, 738, 739, 740, 741, 742, 743, 750, 751, 752, + 753, 754, 755, 756, 757, 0, 0, 1401, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1513, 0, 531, + 0, 0, 0, 0, 539, 0, 0, 0, 1416, 0, + 546, 0, 0, 0, 0, 0, 548, 0, 0, 0, + 0, 0, 0, 0, 24, 25, 52, 27, 28, 0, + 0, 0, 0, 0, 0, 760, 0, 1445, 1446, 1447, + 1448, 0, 0, 43, 1452, 1453, 0, 0, 29, 48, + 49, 0, 0, 0, 0, 0, 1459, 0, 1460, 1461, + 0, 0, 0, 0, 264, 0, 0, 0, 0, 38, + 0, 0, 0, 54, 264, 264, 0, 0, 0, 0, + 0, 0, 264, 0, 0, 264, 0, 0, 264, 0, + 1482, 0, 798, 0, 0, 0, 1342, 1487, 0, 0, + 0, 0, 264, 0, 0, 0, 1519, 0, 0, 0, + 0, 0, 0, 0, 0, 1492, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 689, 0, 698, + 0, 0, 0, 0, 31, 32, 34, 33, 36, 0, + 50, 0, 0, 264, 0, 0, 0, 0, 0, 0, + 0, 0, 798, 0, 0, 0, 0, 0, 0, 0, + 1527, 1528, 37, 44, 45, 0, 0, 46, 47, 35, + 0, 616, 615, 625, 626, 618, 619, 620, 621, 622, + 623, 624, 617, 39, 40, 627, 41, 42, 0, 0, + 0, 0, 0, 0, 317, 0, 0, 0, 0, 317, + 317, 0, 0, 317, 317, 317, 0, 0, 0, 906, + 0, 0, 0, 0, 0, 0, 0, 1341, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 317, 317, + 317, 317, 1072, 264, 0, 0, 0, 0, 0, 0, + 0, 264, 939, 0, 0, 264, 264, 0, 0, 264, + 947, 798, 616, 615, 625, 626, 618, 619, 620, 621, + 622, 623, 624, 617, 0, 0, 627, 722, 53, 0, + 0, 0, 0, 0, 0, 0, 0, 778, 779, 0, + 0, 0, 0, 0, 0, 788, 0, 0, 384, 0, + 0, 794, 616, 615, 625, 626, 618, 619, 620, 621, + 622, 623, 624, 617, 0, 805, 627, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, + 0, 0, 0, 0, 0, 0, 0, 0, 264, 264, + 264, 264, 264, 0, 264, 264, 0, 0, 264, 0, + 0, 0, 0, 0, 0, 0, 836, 0, 0, 0, + 0, 0, 0, 0, 0, 264, 0, 1045, 1046, 0, + 0, 0, 264, 0, 0, 0, 0, 798, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 317, 317, 0, 0, + 0, 0, 0, 0, 0, 0, 917, 0, 0, 0, + 0, 0, 0, 0, 0, 317, 0, 0, 0, 944, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 906, 264, 264, 264, 264, 264, 0, + 0, 0, 0, 0, 0, 0, 1128, 0, 0, 264, + 0, 0, 0, 939, 0, 0, 0, 264, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1010, 0, 0, 0, 0, 0, 0, 0, + 0, 1030, 1031, 1032, 1033, 1034, 0, 1037, 1038, 0, + 0, 1039, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1041, 0, + 0, 0, 0, 0, 0, 1048, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 798, 0, 0, 0, 0, 0, 0, + 0, 0, 906, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, - 0, 264, 0, 0, 0, 0, 0, 0, 0, 1348, - 0, 0, 0, 0, 0, 0, 0, 1216, 0, 652, - 0, 0, 0, 0, 0, 0, 0, 1363, 0, 0, - 1364, 0, 0, 1366, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, + 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 906, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1216, 0, 0, 0, 0, 0, 0, + 0, 0, 906, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1145,18 +1168,56 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1311, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 939, 0, 0, 0, 0, 0, 1314, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1324, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 939, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1311, 0, 0, + 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, + 0, 0, 0, 0, 0, 1314, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1324, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1479, 652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 906, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 906, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 264, 0, 0, 0, 0, 0, 0, 505, 493, 0, + 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 505, 493, 0, 450, 508, 423, + 440, 516, 441, 444, 481, 408, 463, 165, 438, 0, + 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, + 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, + 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, + 509, 439, 479, 510, 0, 0, 0, 80, 0, 964, + 965, 0, 0, 0, 0, 0, 101, 0, 476, 504, + 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, + 431, 432, 1151, 0, 0, 0, 0, 0, 0, 453, + 462, 487, 447, 0, 0, 0, 0, 0, 0, 1441, + 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, + 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, + 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, + 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, + 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, + 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, + 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, + 227, 214, 151, 131, 132, 87, 1490, 189, 110, 117, + 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, + 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, + 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, + 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, + 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, + 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, + 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, + 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, + 91, 138, 246, 186, 116, 235, 401, 414, 109, 424, + 0, 0, 437, 442, 443, 455, 457, 458, 459, 460, + 467, 474, 475, 477, 483, 484, 485, 486, 491, 498, + 517, 84, 85, 92, 98, 104, 108, 112, 115, 120, + 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, + 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, + 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, + 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, + 213, 216, 222, 223, 232, 239, 242, 505, 493, 0, 450, 508, 423, 440, 516, 441, 444, 481, 408, 463, 165, 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, @@ -1164,16 +1225,16 @@ var yyAct = [...]int{ 482, 413, 471, 509, 439, 479, 510, 0, 0, 0, 80, 0, 964, 965, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, - 409, 515, 500, 431, 432, 1151, 0, 0, 0, 0, + 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, - 0, 0, 0, 1440, 429, 0, 470, 0, 0, 0, + 0, 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 1489, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, @@ -1196,7 +1257,7 @@ var yyAct = [...]int{ 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, - 0, 0, 0, 80, 0, 964, 965, 0, 0, 0, + 54, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, @@ -1230,11 +1291,11 @@ var yyAct = [...]int{ 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, - 439, 479, 510, 54, 0, 0, 80, 0, 0, 0, + 439, 479, 510, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, - 487, 447, 0, 0, 0, 0, 0, 0, 0, 0, + 487, 447, 0, 0, 0, 0, 0, 0, 1219, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, @@ -1264,12 +1325,12 @@ var yyAct = [...]int{ 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, - 413, 471, 509, 439, 479, 510, 0, 0, 0, 80, + 413, 471, 509, 439, 479, 510, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, - 0, 1219, 0, 429, 0, 470, 0, 0, 0, 410, + 0, 948, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, @@ -1299,11 +1360,11 @@ var yyAct = [...]int{ 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, - 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, - 0, 0, 0, 0, 948, 0, 429, 0, 470, 0, + 0, 0, 0, 0, 845, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, @@ -1333,11 +1394,11 @@ var yyAct = [...]int{ 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, - 479, 510, 0, 0, 0, 322, 0, 0, 0, 0, + 479, 510, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, - 447, 0, 0, 0, 0, 0, 0, 845, 0, 429, + 447, 0, 0, 0, 0, 0, 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, @@ -1367,7 +1428,7 @@ var yyAct = [...]int{ 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, - 471, 509, 439, 479, 510, 0, 0, 0, 80, 0, + 471, 509, 439, 479, 510, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, @@ -1402,7 +1463,7 @@ var yyAct = [...]int{ 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, 0, - 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, @@ -1415,11 +1476,11 @@ var yyAct = [...]int{ 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 94, 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 181, 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, 116, 235, @@ -1436,7 +1497,7 @@ var yyAct = [...]int{ 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, - 510, 0, 0, 0, 80, 0, 0, 0, 0, 0, + 510, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, @@ -1449,11 +1510,11 @@ var yyAct = [...]int{ 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 399, 397, 127, 209, 134, 141, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, @@ -1470,7 +1531,7 @@ var yyAct = [...]int{ 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, - 509, 439, 479, 510, 0, 0, 0, 265, 0, 0, + 509, 439, 479, 510, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, @@ -1481,13 +1542,13 @@ var yyAct = [...]int{ 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, + 218, 238, 245, 208, 86, 217, 709, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, + 107, 164, 224, 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, + 244, 0, 0, 100, 118, 113, 181, 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, @@ -1515,14 +1576,14 @@ var yyAct = [...]int{ 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 709, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 389, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, + 399, 397, 392, 391, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, 116, 235, 401, @@ -1534,841 +1595,707 @@ var yyAct = [...]int{ 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 505, 493, 0, 450, 508, 423, 440, 516, 441, 444, - 481, 408, 463, 165, 438, 0, 427, 403, 434, 404, - 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, - 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, - 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, - 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, - 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, - 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, - 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, - 0, 0, 0, 0, 0, 0, 0, 429, 0, 470, - 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, - 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, - 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, - 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 389, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 398, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, - 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 399, 397, 392, 391, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, - 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, - 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, - 116, 235, 401, 414, 109, 424, 0, 0, 437, 442, - 443, 455, 457, 458, 459, 460, 467, 474, 475, 477, - 483, 484, 485, 486, 491, 498, 517, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 881, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 882, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 955, 0, - 54, 0, 0, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 956, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 0, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 165, 0, 0, 881, 0, 324, 0, 0, 0, 111, + 0, 321, 0, 0, 0, 137, 882, 364, 139, 0, + 0, 211, 153, 0, 0, 0, 0, 355, 356, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, + 322, 343, 342, 345, 346, 347, 348, 0, 0, 101, + 344, 349, 350, 351, 0, 0, 0, 319, 336, 0, + 363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 333, 334, 315, 0, 0, 0, 377, 0, 335, 0, + 0, 330, 331, 332, 337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, + 0, 375, 0, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, + 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, + 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 365, 376, 371, 372, 369, 370, 368, + 367, 366, 378, 357, 358, 359, 360, 362, 0, 373, + 374, 361, 82, 91, 138, 246, 186, 116, 235, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 581, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 0, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 165, 0, 0, 0, 0, 324, 0, 0, 0, 111, + 0, 321, 0, 0, 0, 137, 0, 364, 139, 0, + 0, 211, 153, 0, 0, 0, 0, 355, 356, 0, + 0, 0, 0, 0, 0, 955, 0, 54, 0, 0, + 322, 343, 342, 345, 346, 347, 348, 0, 0, 101, + 344, 349, 350, 351, 956, 0, 0, 319, 336, 0, + 363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 333, 334, 0, 0, 0, 0, 377, 0, 335, 0, + 0, 330, 331, 332, 337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, + 0, 375, 0, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, + 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, + 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 365, 376, 371, 372, 369, 370, 368, + 367, 366, 378, 357, 358, 359, 360, 362, 0, 373, + 374, 361, 82, 91, 138, 246, 186, 116, 235, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 165, 0, 0, 0, 0, 324, 0, 0, 0, 111, + 0, 321, 0, 0, 0, 137, 0, 364, 139, 0, + 0, 211, 153, 0, 0, 0, 0, 355, 356, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 581, + 322, 343, 342, 345, 346, 347, 348, 0, 0, 101, + 344, 349, 350, 351, 0, 0, 0, 319, 336, 0, + 363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 333, 334, 0, 0, 0, 0, 377, 0, 335, 0, + 0, 330, 331, 332, 337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, + 0, 375, 0, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, + 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, + 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 365, 376, 371, 372, 369, 370, 368, + 367, 366, 378, 357, 358, 359, 360, 362, 0, 373, + 374, 361, 82, 91, 138, 246, 186, 116, 235, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 897, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 165, 0, 0, 0, 0, 324, 0, 0, 0, 111, + 0, 321, 0, 0, 0, 137, 0, 364, 139, 0, + 0, 211, 153, 0, 0, 0, 0, 355, 356, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, + 322, 343, 342, 345, 346, 347, 348, 0, 0, 101, + 344, 349, 350, 351, 0, 0, 0, 319, 336, 0, + 363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 333, 334, 315, 0, 0, 0, 377, 0, 335, 0, + 0, 330, 331, 332, 337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, + 0, 375, 0, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, + 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, + 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 365, 376, 371, 372, 369, 370, 368, + 367, 366, 378, 357, 358, 359, 360, 362, 0, 373, + 374, 361, 82, 91, 138, 246, 186, 116, 235, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 894, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 165, 0, 0, 0, 0, 324, 0, 0, 0, 111, + 0, 321, 0, 0, 0, 137, 0, 364, 139, 0, + 0, 211, 153, 0, 0, 0, 0, 355, 356, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, + 322, 343, 897, 345, 346, 347, 348, 0, 0, 101, + 344, 349, 350, 351, 0, 0, 0, 319, 336, 0, + 363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 333, 334, 315, 0, 0, 0, 377, 0, 335, 0, + 0, 330, 331, 332, 337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, + 0, 375, 0, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, + 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, + 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 365, 376, 371, 372, 369, 370, 368, + 367, 366, 378, 357, 358, 359, 360, 362, 0, 373, + 374, 361, 82, 91, 138, 246, 186, 116, 235, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 24, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, - 324, 0, 0, 0, 111, 0, 321, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 319, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 324, 0, 0, 0, 111, 0, 321, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 319, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 0, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 1519, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 581, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 0, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 0, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 0, 0, 627, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 604, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 606, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 601, 600, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 602, 0, 0, + 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 165, 0, 0, 0, 0, 324, 0, 0, 0, 111, + 0, 321, 0, 0, 0, 137, 0, 364, 139, 0, + 0, 211, 153, 0, 0, 0, 0, 355, 356, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, + 322, 343, 894, 345, 346, 347, 348, 0, 0, 101, + 344, 349, 350, 351, 0, 0, 0, 319, 336, 0, + 363, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 333, 334, 315, 0, 0, 0, 377, 0, 335, 0, + 0, 330, 331, 332, 337, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, + 0, 375, 0, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, + 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, + 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 365, 376, 371, 372, 369, 370, 368, + 367, 366, 378, 357, 358, 359, 360, 362, 0, 373, + 374, 361, 82, 91, 138, 246, 186, 116, 235, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 165, 0, 0, 0, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 0, 0, 0, 0, 377, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 378, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 0, 0, 0, 0, 377, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 378, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 0, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 0, 0, 0, 0, 377, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 1520, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 378, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 581, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 0, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 0, 0, 0, 0, 377, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 378, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 0, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 0, 0, 0, 0, 377, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 378, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 76, 77, 0, 73, 0, 0, 0, 78, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 75, + 0, 0, 0, 0, 0, 0, 0, 0, 616, 615, + 625, 626, 618, 619, 620, 621, 622, 623, 624, 617, + 0, 0, 627, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 938, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 604, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 940, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 606, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 601, 600, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 24, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 165, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, 0, 80, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 76, 77, 0, + 73, 0, 0, 0, 78, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 0, 0, 267, 0, 0, 0, 0, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, - 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 0, 0, 212, 234, 249, 99, 0, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 938, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, + 0, 0, 265, 0, 940, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 24, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 265, - 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 24, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 165, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 54, 0, 0, 80, 0, 0, 0, 0, 0, + 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 938, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, - 0, 940, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, + 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 165, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 936, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 265, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 0, 832, 0, 0, 833, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 938, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 718, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 717, 0, 0, 0, 0, 0, 0, 101, 0, + 0, 0, 0, 0, 0, 0, 265, 0, 940, 0, + 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 936, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 265, - 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 0, 0, 832, + 0, 0, 833, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, - 0, 940, 0, 0, 0, 0, 0, 0, 101, 0, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 0, 718, 0, 0, + 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 0, 717, 0, + 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 606, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 581, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 688, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, - 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 381, - 0, 0, 0, 0, 0, 0, 165, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2401,14 +2328,14 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 265, 0, 940, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 262, 0, 267, 0, 0, 0, 0, 184, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, @@ -2434,7 +2361,7 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 80, 0, 606, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2464,7 +2391,7 @@ var yyAct = [...]int{ 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 0, 0, 0, 688, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, @@ -2474,73 +2401,206 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 381, 0, 0, 0, + 0, 0, 0, 165, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, + 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, + 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, + 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, + 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, + 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, + 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, + 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, + 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, + 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 91, 138, 246, 186, + 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, + 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, + 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, + 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, + 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, + 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, + 232, 239, 242, 165, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, + 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 0, 262, + 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, + 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, + 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, + 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, + 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, + 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, + 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, + 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 91, 138, 246, 186, + 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, + 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, + 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, + 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, + 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, + 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, + 232, 239, 242, 165, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, + 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, + 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, + 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, + 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, + 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, + 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, + 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, + 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, + 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 91, 138, 246, 186, + 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, + 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, + 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, + 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, + 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, + 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, + 232, 239, 242, 165, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, + 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, + 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, + 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, + 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, + 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, + 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, + 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, + 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, + 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 322, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 91, 138, 246, 186, + 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, + 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, + 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, + 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, + 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, + 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, + 232, 239, 242, 165, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, + 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, + 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, + 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, + 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, + 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, + 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, + 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, + 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, + 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 91, 138, 246, 186, + 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, + 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, + 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, + 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, + 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, + 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, + 232, 239, 242, } var yyPact = [...]int{ - 204, -1000, -269, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 2058, -1000, -269, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 867, 932, -1000, -1000, -1000, -1000, -1000, -1000, - 312, 11217, -23, 103, 14, 15208, 102, 1958, 15868, -1000, - 4, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -63, -72, - -1000, 684, -1000, -1000, -1000, -1000, -1000, 861, 863, 694, - 856, 780, -1000, 7905, 64, 64, 14878, 6585, -1000, -1000, - 245, 15868, 99, 15868, -151, 60, 60, 60, -1000, -1000, + -1000, -1000, 980, 1034, -1000, -1000, -1000, -1000, -1000, -1000, + 279, 11484, 3, 112, -9, 15805, 111, 80, 16465, -1000, + -1, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -84, -89, + -1000, 744, -1000, -1000, -1000, -1000, -1000, 965, 973, 757, + 957, 827, -1000, 8172, 75, 75, 15475, 6852, -1000, -1000, + 213, 16465, 109, 16465, -152, 85, 85, 85, -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, @@ -2558,21 +2618,21 @@ 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, - 101, 15868, 578, 576, 247, -1000, 15868, 58, 575, 58, - 58, 58, 15868, -1000, 160, -1000, -1000, -1000, 15868, 561, - 810, 3498, 43, 3498, -1000, 3498, 3498, -1000, 3498, 11, - 3498, -65, 902, 8, -12, -1000, 3498, -1000, -1000, -1000, + 108, 16465, 609, 596, 232, -1000, 16465, 77, 593, 77, + 77, 77, 16465, -1000, 146, -1000, -1000, -1000, 16465, 590, + 885, 3765, 41, 3765, -1000, 3765, 3765, -1000, 3765, 17, + 3765, -49, 989, 18, -33, -1000, 3765, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 490, 815, 9237, 9237, 867, -1000, 684, -1000, -1000, -1000, - 804, -1000, -1000, 338, 919, -1000, 10887, 153, -1000, 9237, - 1478, 612, -1000, -1000, 612, -1000, -1000, 124, -1000, -1000, - 10227, 10227, 10227, 10227, 10227, 10227, 10227, 10227, -1000, -1000, + 579, 910, 9504, 9504, 980, -1000, 744, -1000, -1000, -1000, + 896, -1000, -1000, 318, 1008, -1000, 11154, 145, -1000, 9504, + 390, 738, -1000, -1000, 738, -1000, -1000, 132, -1000, -1000, + 10494, 10494, 10494, 10494, 10494, 10494, 10494, 10494, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 612, -1000, 8907, 612, 612, 612, 612, 612, - 612, 612, 612, 9237, 612, 612, 612, 612, 612, 612, - 612, 612, 612, 612, 612, 612, 612, 612, 612, 14541, - 13551, 15868, 661, 658, -1000, -1000, 152, 681, 6242, -79, - -1000, -1000, -1000, 291, 13221, -1000, -1000, -1000, 807, -1000, + -1000, -1000, 738, -1000, 9174, 738, 738, 738, 738, 738, + 738, 738, 738, 9504, 738, 738, 738, 738, 738, 738, + 738, 738, 738, 738, 738, 738, 738, 738, 738, 15138, + 14148, 16465, 679, 658, -1000, -1000, 143, 721, 6509, -132, + -1000, -1000, -1000, 248, 13488, -1000, -1000, -1000, 884, -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, @@ -2584,132 +2644,132 @@ 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, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 560, 15868, - -1000, 2173, -1000, 557, 3498, 82, 556, 259, 555, 15868, - 15868, 3498, 3498, 3498, 18, 50, 52, 15868, 687, 67, - 15868, 847, 740, 15868, 554, 552, -1000, 5899, -1000, 3498, - 3498, -1000, -1000, -1000, 3498, 3498, 3498, 15868, 3498, 3498, - -1000, -1000, -1000, -1000, 3498, 3498, -1000, 918, 282, -1000, - -1000, -1000, -1000, 9237, 195, -1000, 739, -1000, -1000, -1000, - -1000, -1000, -1000, 927, 208, 341, 151, 682, -1000, 361, - 861, 490, 780, 12891, 764, -1000, -1000, -1000, 15868, -1000, - 9237, 9237, 442, -1000, 14211, -1000, -1000, 4527, 214, 10227, - 406, 239, 10227, 10227, 10227, 10227, 10227, 10227, 10227, 10227, - 10227, 10227, 10227, 10227, 10227, 10227, 10227, 461, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 551, -1000, 684, 583, - 583, 159, 159, 159, 159, 159, 159, 159, 10557, 6915, - 490, 550, 303, 8907, 7905, 7905, 9237, 9237, 8565, 8235, - 7905, 818, 249, 303, 16198, -1000, -1000, 9897, -1000, -1000, - -1000, -1000, -1000, 490, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 15538, 15538, 7905, 7905, 7905, 7905, 29, 15868, -1000, - 673, 816, -1000, -1000, -1000, 849, 12231, 12561, 29, 640, - 13551, 15868, -1000, -1000, 13551, 15868, 4184, 5556, 681, -79, - 664, -1000, -107, -85, 7245, 150, -1000, -1000, -1000, -1000, - 3155, 212, 581, 330, -54, -1000, -1000, -1000, 698, -1000, - 698, 698, 698, 698, -25, -25, -25, -25, -1000, -1000, - -1000, -1000, -1000, 711, 710, -1000, 698, 698, 698, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 673, 16465, + -1000, 1814, -1000, 587, 3765, 94, 573, 285, 555, 16465, + 16465, 3765, 3765, 3765, 23, 59, 53, 16465, 725, 88, + 16465, 934, 793, 16465, 550, 535, -1000, 6166, -1000, 3765, + 3765, -1000, -1000, -1000, 3765, 3765, 3765, 16465, 3765, 3765, + -1000, -1000, -1000, -1000, 3765, 3765, -1000, 1006, 264, -1000, + -1000, -1000, -1000, 9504, 187, -1000, 792, -1000, -1000, -1000, + -1000, -1000, -1000, 1029, 175, 450, 142, 724, -1000, 472, + 965, 579, 827, 13158, 807, -1000, -1000, -1000, 16465, -1000, + 9504, 9504, 308, -1000, 14808, -1000, -1000, 4794, 190, 10494, + 387, 246, 10494, 10494, 10494, 10494, 10494, 10494, 10494, 10494, + 10494, 10494, 10494, 10494, 10494, 10494, 10494, 479, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 531, -1000, 744, 656, + 656, 158, 158, 158, 158, 158, 158, 158, 10824, 7182, + 579, 647, 332, 9174, 8172, 8172, 9504, 9504, 8832, 8502, + 8172, 891, 280, 332, 16795, -1000, -1000, 10164, -1000, -1000, + -1000, -1000, -1000, 579, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 16135, 16135, 8172, 8172, 8172, 8172, 48, 16465, -1000, + 709, 983, -1000, -1000, -1000, 936, 12498, 12828, 48, 690, + 14148, 16465, -1000, -1000, 14148, 16465, 4451, 5823, 721, -132, + 699, -1000, -115, -102, 7512, 153, -1000, -1000, -1000, -1000, + 3422, 202, 665, 316, -78, -1000, -1000, -1000, 746, -1000, + 746, 746, 746, 746, -50, -50, -50, -50, -1000, -1000, + -1000, -1000, -1000, 770, 765, -1000, 746, 746, 746, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 709, 709, 709, - 700, 700, 717, -1000, 15868, 3498, 846, 3498, -1000, 78, - -1000, -1000, -1000, 15868, 15868, 15868, 15868, 15868, 112, 15868, - 15868, 680, -1000, 15868, 3498, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 764, 764, 764, + 751, 751, 777, -1000, 16465, 3765, 932, 3765, -1000, 74, + -1000, -1000, -1000, 16465, 16465, 16465, 16465, 16465, 120, 16465, + 16465, 718, -1000, 16465, 3765, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 15868, 367, 15868, 15868, 303, -1000, 458, 15868, -1000, 785, - 9237, 9237, 5213, 9237, -1000, -1000, -1000, 815, -1000, 818, - 897, -1000, 798, 797, 7905, -1000, -1000, 214, 297, -1000, - -1000, 371, -1000, -1000, -1000, -1000, 149, 612, -1000, 2132, - -1000, -1000, -1000, -1000, 406, 10227, 10227, 10227, 387, 2132, - 2096, 1634, 1715, 159, 145, 145, 183, 183, 183, 183, - 183, 320, 320, -1000, -1000, -1000, 490, -1000, -1000, -1000, - 490, 7905, 7905, 679, -1000, -1000, 9237, -1000, 490, 544, - 544, 310, 288, 232, 914, 544, 226, 904, 544, 544, - 7905, 317, -1000, 9237, 490, -1000, 148, -1000, 402, 675, - 672, 544, 490, 544, 544, 644, 612, -1000, 16198, 13551, - 13551, 13551, 13551, 13551, -1000, 767, 756, -1000, 755, 753, - 774, 15868, -1000, 546, 12231, 166, 612, -1000, 13881, -1000, - -1000, 899, 13551, 606, -1000, 606, -1000, 147, -1000, -1000, - 664, -79, -88, -1000, -1000, -1000, -1000, 303, -1000, 466, - 663, 2812, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 708, - 523, -1000, 829, 174, 193, 509, 827, -1000, -1000, -1000, - 813, -1000, 322, -59, -1000, -1000, 414, -25, -25, -1000, - -1000, 150, 806, 150, 150, 150, 454, 454, -1000, -1000, - -1000, -1000, 405, -1000, -1000, -1000, 394, -1000, 729, 15538, - 3498, -1000, -1000, -1000, -1000, 277, 277, 200, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 28, - 707, -1000, -1000, -1000, -1000, 3, 16, 66, -1000, 3498, - -1000, 282, -1000, 449, 9237, -1000, -1000, -1000, -1000, 783, - 303, 303, 141, -1000, -1000, 15868, -1000, -1000, -1000, -1000, - 666, -1000, -1000, -1000, 3841, 7905, -1000, 387, 2132, 1866, - -1000, 10227, 10227, -1000, -1000, 544, 544, 7905, 303, -1000, - -1000, -1000, 97, 461, 97, 10227, 10227, -1000, 10227, 10227, - -1000, -163, 648, 233, -1000, 9237, 363, -1000, 5213, -1000, - 10227, 10227, -1000, -1000, -1000, -1000, 727, 16198, 612, -1000, - 11889, 15538, 636, -1000, 231, 816, 706, 726, 611, -1000, - -1000, -1000, -1000, 754, -1000, 691, -1000, -1000, -1000, -1000, - -1000, 98, 95, 79, 15538, -1000, 867, 9237, 606, -1000, - -1000, 175, -1000, -1000, -130, -97, -1000, -1000, -1000, 3155, - -1000, 3155, 15538, 46, -1000, 509, 509, -1000, -1000, -1000, - 703, 721, 10227, -1000, -1000, -1000, 547, 150, 150, -1000, - 251, -1000, -1000, -1000, 541, -1000, 539, 662, 535, 15868, + 16465, 281, 16465, 16465, 332, -1000, 493, 16465, -1000, 838, + 9504, 9504, 5480, 9504, -1000, -1000, -1000, 910, -1000, 891, + 960, -1000, 873, 845, 8172, -1000, -1000, 190, 275, -1000, + -1000, 514, -1000, -1000, -1000, -1000, 140, 738, -1000, 474, + -1000, -1000, -1000, -1000, 387, 10494, 10494, 10494, 315, 474, + 2198, 413, 335, 158, 399, 399, 157, 157, 157, 157, + 157, 525, 525, -1000, -1000, -1000, 579, -1000, -1000, -1000, + 579, 8172, 8172, 717, -1000, -1000, 9504, -1000, 579, 628, + 628, 313, 351, 240, 1003, 628, 229, 993, 628, 628, + 8172, 277, -1000, 9504, 579, -1000, 137, -1000, 1242, 713, + 703, 628, 579, 628, 628, 707, 738, -1000, 16795, 14148, + 14148, 14148, 14148, 14148, -1000, 824, 821, -1000, 811, 809, + 815, 16465, -1000, 638, 12498, 161, 738, -1000, 14478, -1000, + -1000, 988, 14148, 680, -1000, 680, -1000, 135, -1000, -1000, + 699, -132, -133, -1000, -1000, -1000, -1000, 332, -1000, 489, + 698, 3079, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 755, + 503, -1000, 918, 194, 191, 496, 912, -1000, -1000, -1000, + 887, -1000, 293, -80, -1000, -1000, 405, -50, -50, -1000, + -1000, 153, 883, 153, 153, 153, 483, 483, -1000, -1000, + -1000, -1000, 400, -1000, -1000, -1000, 397, -1000, 789, 16135, + 3765, -1000, -1000, -1000, -1000, 268, 268, 203, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 47, + 772, -1000, -1000, -1000, -1000, -4, 22, 82, -1000, 3765, + -1000, 264, -1000, 459, 9504, -1000, -1000, -1000, -1000, 836, + 332, 332, 131, -1000, -1000, 16465, -1000, -1000, -1000, -1000, + 685, -1000, -1000, -1000, 4108, 8172, -1000, 315, 474, 1716, + -1000, 10494, 10494, -1000, -1000, 628, 628, 8172, 332, -1000, + -1000, -1000, 107, 479, 107, 10494, 10494, -1000, 10494, 10494, + -1000, -165, 711, 269, -1000, 9504, 462, -1000, 5480, -1000, + 10494, 10494, -1000, -1000, -1000, -1000, 788, 16795, 738, -1000, + 12156, 16135, 726, -1000, 212, 983, 763, 783, 557, -1000, + -1000, -1000, -1000, 819, -1000, 812, -1000, -1000, -1000, -1000, + -1000, 104, 101, 96, 16135, -1000, 980, 9504, 680, -1000, + -1000, 167, -1000, -1000, -128, -118, -1000, -1000, -1000, 3422, + -1000, 3422, 16135, 57, -1000, 496, 496, -1000, -1000, -1000, + 753, 781, 10494, -1000, -1000, -1000, 629, 153, 153, -1000, + 215, -1000, -1000, -1000, 621, -1000, 608, 696, 605, 16465, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 15868, -1000, -1000, - -1000, -1000, -1000, 15538, -173, 491, 15538, 15538, 15538, 15868, - -1000, 367, -1000, 303, -1000, 4870, -1000, 899, 13551, -1000, - -1000, 490, -1000, 10227, 2132, 2132, -1000, -1000, -1000, 490, - 698, 698, -1000, 698, 700, -1000, 698, -7, 698, -9, - 490, 490, 2005, 1990, 1827, 474, 612, -158, -1000, 303, - 9237, -1000, 1550, 527, -1000, 831, 584, 630, -1000, -1000, - 7575, 490, 533, 129, 528, -1000, 867, 16198, 9237, -1000, - -1000, 9237, 699, -1000, 9237, -1000, -1000, -1000, 612, 612, - 612, 528, 861, 303, -1000, -1000, -1000, -1000, 2812, -1000, - 507, -1000, 698, -1000, -1000, -1000, 15538, -49, 925, 2132, - -1000, -1000, -1000, -1000, -1000, -25, 448, -25, 372, -1000, - 366, 3498, -1000, -1000, -1000, -1000, 841, -1000, 4870, -1000, - -1000, 690, 713, -1000, -1000, -1000, 900, 639, -1000, 2132, - -1000, -1000, 106, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 10227, 10227, 10227, 10227, 10227, 490, 434, 303, 10227, - 10227, 826, -1000, 612, -1000, -1000, 678, 15538, 15538, -1000, - 15538, 861, -1000, 303, 303, 15538, 303, 15538, 15538, 15538, - 11547, -1000, 136, 15538, -1000, 505, -1000, 177, -1000, -114, - 150, -1000, 150, 529, 497, -1000, 612, 635, -1000, 230, - 15538, 15868, 898, 857, -1000, -1000, 402, 402, 402, 402, - 20, -1000, -1000, 402, 402, 924, -1000, 612, -1000, 684, - 128, -1000, -1000, -1000, 496, 489, 489, 489, 166, 136, - -1000, 374, 219, 420, -1000, 41, 15538, 334, 823, -1000, - 817, -1000, -1000, -1000, -1000, -1000, 27, 4870, 3155, 486, - -1000, -1000, 9237, 9237, -1000, -1000, -1000, -1000, 490, 45, - -180, -1000, -1000, 16198, 630, 490, 15538, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 351, -1000, -1000, 15868, -1000, -1000, - 403, -1000, -1000, 483, -1000, 15538, -1000, -1000, 707, 303, - 629, -1000, 776, -168, -184, 620, -1000, -1000, -1000, 689, - -1000, -1000, 27, 796, -173, -1000, 772, -1000, 15538, -1000, - 24, -1000, -174, 480, 22, -181, 720, 612, -185, 719, - -1000, 908, 9567, -1000, -1000, 922, 154, 154, 402, 490, - -1000, -1000, -1000, 48, 400, -1000, -1000, -1000, -1000, -1000, - -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16465, -1000, -1000, + -1000, -1000, -1000, 16135, -171, 464, 16135, 16135, 16135, 16465, + -1000, 281, -1000, 332, -1000, 5137, -1000, 988, 14148, -1000, + -1000, 579, -1000, 10494, 474, 474, -1000, -1000, -1000, 579, + 746, 746, -1000, 746, 751, -1000, 746, -6, 746, -7, + 579, 579, 2238, 2117, 1855, 1569, 738, -160, -1000, 332, + 9504, -1000, 1641, 1530, -1000, 921, 678, 691, -1000, -1000, + 7842, 579, 603, 129, 601, -1000, 980, 16795, 9504, -1000, + -1000, 9504, 748, -1000, 9504, -1000, -1000, -1000, 738, 738, + 738, 601, 965, 332, -1000, -1000, -1000, -1000, 3079, -1000, + 578, -1000, 746, -1000, -1000, -1000, 16135, -73, 1028, 474, + -1000, -1000, -1000, -1000, -1000, -50, 445, -50, 367, -1000, + 366, 3765, -1000, -1000, -1000, -1000, 926, -1000, 5137, -1000, + -1000, 743, 776, -1000, -1000, -1000, 984, 695, -1000, 474, + -1000, -1000, 113, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 10494, 10494, 10494, 10494, 10494, 579, 420, 332, 10494, + 10494, 908, -1000, 738, -1000, -1000, 737, 16135, 16135, -1000, + 16135, 965, -1000, 332, 332, 16135, 332, 13818, 16135, 16135, + 11814, -1000, 156, 16135, -1000, 561, -1000, 183, -1000, -85, + 153, -1000, 153, 622, 615, -1000, 738, 692, -1000, 201, + 16135, 16465, 982, 958, -1000, -1000, 1242, 1242, 1242, 1242, + 49, -1000, -1000, 1242, 1242, 1027, -1000, 738, -1000, 744, + 126, -1000, -1000, -1000, 559, 541, -1000, 541, 541, 161, + 156, -1000, 338, 199, 412, -1000, 52, 16135, 304, 906, + -1000, 903, -1000, -1000, -1000, -1000, -1000, 37, 5137, 3422, + 539, -1000, -1000, 9504, 9504, -1000, -1000, -1000, -1000, 579, + 44, -180, -1000, -1000, 16795, 691, 579, 16135, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 336, -1000, -1000, 16465, -1000, + -1000, 411, -1000, -1000, 530, -1000, 16135, -1000, -1000, 772, + 332, 689, -1000, 833, -169, -184, 676, -1000, -1000, -1000, + 741, -1000, -1000, 37, 843, -171, -1000, 831, -1000, 16135, + -1000, 30, -1000, -175, 507, 28, -181, 780, 738, -185, + 779, -1000, 1025, 9834, -1000, -1000, 1022, 177, 177, 1242, + 579, -1000, -1000, -1000, 65, 498, -1000, -1000, -1000, -1000, + -1000, -1000, } var yyPgo = [...]int{ - 0, 1163, 26, 527, 1161, 1160, 1159, 1158, 1157, 1156, - 1155, 1149, 1148, 1147, 1146, 1145, 1143, 1142, 1138, 1134, - 1133, 1131, 1130, 1129, 1128, 1121, 82, 1120, 1119, 1117, - 65, 1108, 66, 1106, 1105, 42, 188, 47, 38, 1318, - 1104, 25, 71, 58, 1103, 35, 1101, 1100, 70, 1098, - 1097, 53, 1095, 1092, 1521, 1082, 62, 1075, 11, 49, - 1074, 1073, 1072, 1069, 69, 639, 1068, 1067, 13, 1066, - 1064, 106, 1063, 55, 10, 12, 9, 18, 1057, 1099, - 28, 1054, 54, 1052, 1051, 1050, 1045, 14, 1042, 56, - 1041, 17, 64, 1039, 19, 63, 30, 21, 6, 75, - 60, 1038, 22, 61, 48, 1037, 1036, 457, 1035, 1034, - 44, 1033, 1032, 23, 1030, 96, 438, 1029, 1028, 1027, - 1026, 45, 0, 394, 388, 73, 1025, 1024, 1022, 1342, - 33, 50, 15, 1019, 40, 191, 43, 1018, 1017, 37, - 1016, 1015, 1014, 1013, 1012, 1009, 1008, 392, 1007, 1006, - 1004, 92, 52, 1003, 1001, 57, 34, 998, 997, 992, - 51, 59, 990, 987, 46, 29, 985, 983, 981, 966, - 965, 31, 16, 964, 20, 963, 8, 958, 24, 957, - 7, 955, 5, 952, 3, 948, 4, 41, 1, 946, - 2, 941, 940, 528, 439, 76, 938, 77, + 0, 1234, 45, 590, 1232, 1231, 1230, 1228, 1227, 1226, + 1224, 1222, 1221, 1220, 1215, 1214, 1213, 1212, 1211, 1206, + 1205, 1203, 1202, 1201, 1199, 1193, 82, 1192, 1191, 1190, + 66, 1188, 70, 1187, 1186, 37, 188, 38, 41, 336, + 1185, 48, 52, 91, 1183, 35, 1178, 1177, 73, 1176, + 1175, 42, 1173, 1171, 1787, 1169, 65, 1168, 13, 61, + 1164, 1163, 1162, 1159, 69, 632, 1155, 1151, 15, 1149, + 1148, 86, 1147, 56, 8, 12, 11, 19, 1145, 63, + 28, 1144, 54, 1143, 1142, 1141, 1140, 24, 1139, 55, + 1138, 18, 64, 1137, 9, 62, 30, 21, 6, 75, + 60, 1134, 22, 58, 51, 1133, 1131, 526, 1130, 1129, + 44, 1128, 1126, 23, 1125, 85, 392, 1124, 1123, 1122, + 1121, 33, 0, 394, 119, 71, 1113, 1110, 1109, 1584, + 47, 53, 17, 1108, 77, 87, 34, 1107, 1106, 31, + 1103, 1101, 1100, 1099, 1097, 1096, 1095, 90, 1091, 1090, + 1082, 20, 26, 1080, 1075, 59, 25, 1074, 1073, 1072, + 43, 57, 1069, 1068, 50, 40, 1067, 1064, 1063, 1058, + 1057, 29, 14, 1055, 16, 1054, 10, 1052, 27, 1050, + 4, 1049, 7, 1048, 3, 1047, 5, 49, 1, 1046, + 2, 1045, 1044, 528, 680, 76, 1042, 78, } var yyR1 = [...]int{ @@ -2752,32 +2812,32 @@ var yyR1 = [...]int{ 94, 96, 96, 44, 44, 44, 44, 45, 45, 46, 46, 47, 47, 133, 133, 132, 132, 132, 131, 131, 50, 50, 50, 52, 51, 51, 51, 51, 53, 53, - 55, 55, 54, 54, 56, 58, 58, 58, 58, 59, - 59, 39, 39, 39, 39, 39, 39, 39, 108, 108, - 61, 61, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 72, 72, 72, 72, 72, 72, 62, 62, - 62, 62, 62, 62, 62, 35, 35, 73, 73, 73, - 79, 74, 74, 65, 65, 65, 65, 65, 65, 65, + 55, 55, 54, 54, 56, 58, 58, 58, 58, 58, + 59, 59, 39, 39, 39, 39, 39, 39, 39, 108, + 108, 61, 61, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 72, 72, 72, 72, 72, 72, 62, + 62, 62, 62, 62, 62, 62, 35, 35, 73, 73, + 73, 79, 74, 74, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 69, 69, 69, 69, 67, + 65, 65, 65, 65, 65, 65, 69, 69, 69, 69, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 197, 197, - 71, 70, 70, 70, 70, 70, 70, 33, 33, 33, - 33, 33, 136, 136, 139, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 83, 83, 34, - 34, 81, 81, 82, 84, 84, 80, 80, 80, 64, - 64, 64, 64, 64, 64, 64, 64, 66, 66, 66, - 85, 85, 86, 86, 87, 87, 88, 88, 89, 90, - 90, 90, 91, 91, 91, 91, 92, 92, 92, 63, - 63, 63, 63, 63, 63, 93, 93, 93, 93, 97, - 97, 75, 75, 77, 77, 76, 78, 98, 98, 102, - 99, 99, 103, 103, 103, 103, 101, 101, 101, 128, - 128, 128, 106, 106, 115, 115, 116, 116, 107, 107, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 118, 118, 118, 119, 119, 120, 120, 120, 127, 127, - 123, 123, 124, 124, 129, 129, 130, 130, 121, 121, + 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 197, + 197, 71, 70, 70, 70, 70, 70, 70, 33, 33, + 33, 33, 33, 136, 136, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 83, 83, + 34, 34, 81, 81, 82, 84, 84, 80, 80, 80, + 64, 64, 64, 64, 64, 64, 64, 64, 66, 66, + 66, 85, 85, 86, 86, 87, 87, 88, 88, 89, + 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, + 63, 63, 63, 63, 63, 63, 93, 93, 93, 93, + 97, 97, 75, 75, 77, 77, 76, 78, 98, 98, + 102, 99, 99, 103, 103, 103, 103, 101, 101, 101, + 128, 128, 128, 106, 106, 115, 115, 116, 116, 107, + 107, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 118, 118, 118, 119, 119, 120, 120, 120, 127, + 127, 123, 123, 124, 124, 129, 129, 130, 130, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, @@ -2789,7 +2849,7 @@ var yyR1 = [...]int{ 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, - 121, 121, 121, 121, 121, 121, 122, 122, 122, 122, + 121, 121, 121, 121, 121, 121, 121, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, @@ -2806,8 +2866,8 @@ var yyR1 = [...]int{ 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 193, 194, 134, 135, 135, - 135, + 122, 122, 122, 122, 122, 122, 193, 194, 134, 135, + 135, 135, } var yyR2 = [...]int{ @@ -2850,31 +2910,32 @@ var yyR2 = [...]int{ 3, 1, 3, 4, 4, 4, 3, 2, 4, 0, 1, 0, 2, 0, 1, 0, 1, 2, 1, 1, 1, 2, 2, 1, 2, 3, 2, 3, 2, 2, - 2, 1, 1, 3, 3, 0, 5, 5, 5, 0, - 2, 1, 3, 3, 2, 3, 1, 2, 0, 3, - 1, 1, 3, 3, 4, 4, 5, 3, 4, 5, - 6, 2, 1, 2, 1, 2, 1, 2, 1, 1, - 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, - 3, 1, 3, 1, 1, 1, 1, 1, 3, 3, + 2, 1, 1, 3, 3, 0, 5, 4, 5, 5, + 0, 2, 1, 3, 3, 2, 3, 1, 2, 0, + 3, 1, 1, 3, 3, 4, 4, 5, 3, 4, + 5, 6, 2, 1, 2, 1, 2, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, + 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, - 3, 1, 1, 1, 1, 4, 5, 5, 6, 4, - 4, 6, 6, 6, 8, 8, 8, 8, 9, 7, - 5, 4, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 8, 8, 0, 2, - 3, 4, 4, 4, 4, 4, 4, 0, 3, 4, - 7, 3, 1, 1, 2, 3, 3, 1, 2, 2, - 1, 2, 1, 2, 2, 1, 2, 0, 1, 0, - 2, 1, 2, 4, 0, 2, 1, 3, 5, 1, - 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, 2, - 1, 3, 5, 4, 6, 1, 3, 3, 5, 0, - 5, 1, 3, 1, 2, 3, 1, 1, 3, 3, - 1, 3, 3, 3, 3, 3, 1, 2, 1, 1, - 1, 1, 1, 1, 0, 2, 0, 3, 0, 1, + 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, + 2, 3, 1, 1, 1, 1, 4, 5, 5, 6, + 4, 4, 6, 6, 6, 8, 8, 8, 8, 9, + 7, 5, 4, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 8, 8, 0, + 2, 3, 4, 4, 4, 4, 4, 4, 0, 3, + 4, 7, 3, 1, 1, 2, 3, 3, 1, 2, + 2, 1, 2, 1, 2, 2, 1, 2, 0, 1, + 0, 2, 1, 2, 4, 0, 2, 1, 3, 5, + 1, 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, + 2, 1, 3, 5, 4, 6, 1, 3, 3, 5, + 0, 5, 1, 3, 1, 2, 3, 1, 1, 3, + 3, 1, 3, 3, 3, 3, 3, 1, 2, 1, + 1, 1, 1, 1, 1, 0, 2, 0, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 0, 1, 1, 0, 2, + 1, 0, 1, 1, 1, 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, @@ -2903,9 +2964,8 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, - 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 1, } var yyChk = [...]int{ @@ -3050,176 +3110,176 @@ var yyChk = [...]int{ -151, 60, -151, 61, 61, -135, 26, -183, -182, -124, 55, 54, -85, 13, -151, 58, -65, -65, -65, -65, -65, -194, 60, -65, -65, 28, -77, 35, -2, -193, - -123, -123, -123, -91, -94, -94, -94, -94, -132, -177, - -176, 54, 135, 67, -174, 57, 56, -159, 131, 28, - 130, -68, -152, -152, 57, 57, -193, 56, 83, -94, - -54, -86, 14, 16, -194, -194, -194, -194, -33, 93, - 263, -194, -194, 9, -75, -2, 113, 57, -194, -194, - -194, -58, -176, 58, -166, 83, 60, 142, -123, -148, - 67, 28, 28, -179, -180, 153, -182, -172, 57, -39, - -74, -194, 261, 50, 264, -98, -194, -123, 61, -54, - 60, -194, 56, -123, -186, 40, 262, 265, 55, -180, - 35, -184, 40, -94, 155, 263, 57, 156, 264, -189, - -190, 53, -193, 265, -190, 53, 10, 9, -65, 152, - -188, 143, 138, 141, 30, -188, -194, -194, 137, 29, - 69, + -123, -123, -123, -91, -94, -94, -194, -94, -94, -132, + -177, -176, 54, 135, 67, -174, 57, 56, -159, 131, + 28, 130, -68, -152, -152, 57, 57, -193, 56, 83, + -94, -54, -86, 14, 16, -194, -194, -194, -194, -33, + 93, 263, -194, -194, 9, -75, -2, 113, 57, -194, + -194, -194, -58, -176, 58, -166, 83, 60, 142, -123, + -148, 67, 28, 28, -179, -180, 153, -182, -172, 57, + -39, -74, -194, 261, 50, 264, -98, -194, -123, 61, + -54, 60, -194, 56, -123, -186, 40, 262, 265, 55, + -180, 35, -184, 40, -94, 155, 263, 57, 156, 264, + -189, -190, 53, -193, 265, -190, 53, 10, 9, -65, + 152, -188, 143, 138, 141, 30, -188, -194, -194, 137, + 29, 69, } var yyDef = [...]int{ 23, -2, 2, -2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 564, 0, 319, 319, 319, 319, 319, 319, - 0, 635, 618, 0, 0, 0, 0, -2, 306, 307, - 0, 309, 310, 937, 937, 937, 937, 937, 0, 0, - 937, 0, 35, 36, 935, 1, 3, 572, 0, 0, - 323, 326, 321, 0, 618, 618, 0, 0, 65, 66, - 0, 0, 0, 924, 0, 616, 616, 616, 636, 637, - 640, 641, 766, 767, 768, 769, 770, 771, 772, 773, - 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, - 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, - 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, - 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, - 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, - 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, - 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, - 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, - 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, - 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, - 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, - 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, - 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, - 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, - 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, - 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, - 0, 0, 0, 0, 0, 619, 0, 614, 0, 614, - 614, 614, 0, 258, 392, 644, 645, 924, 0, 0, - 0, 938, 0, 938, 270, 938, 938, 273, 938, 0, - 938, 0, 280, 0, 0, 286, 938, 303, 304, 291, - 305, 308, 311, 312, 313, 314, 315, 937, 937, 318, - 29, 576, 0, 0, 564, 31, 0, 319, 324, 325, - 329, 327, 328, 320, 0, 338, 342, 0, 401, 0, - 406, 408, -2, -2, 0, 443, 444, 445, 446, 447, - 0, 0, 0, 0, 0, 0, 0, 0, 471, 472, - 473, 474, 549, 550, 551, 552, 553, 554, 555, 556, - 410, 411, 546, 596, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 537, 0, 508, 508, 508, 508, 508, - 508, 508, 508, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 44, 46, 392, 50, 0, 913, - 600, -2, -2, 0, 0, 642, 643, -2, 779, -2, - 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, - 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, - 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, - 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, - 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, - 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, - 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, - 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, - 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, - 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, - 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, - 758, 759, 760, 761, 762, 763, 764, 765, 0, 0, - 84, 0, 82, 0, 938, 0, 0, 0, 0, 0, - 0, 938, 938, 938, 0, 0, 0, 0, 249, 0, - 0, 0, 0, 0, 0, 0, 257, 0, 259, 938, - 938, 262, 939, 940, 938, 938, 938, 0, 938, 938, - 269, 271, 272, 274, 938, 938, 276, 0, 294, 292, + 21, 22, 565, 0, 319, 319, 319, 319, 319, 319, + 0, 636, 619, 0, 0, 0, 0, -2, 306, 307, + 0, 309, 310, 938, 938, 938, 938, 938, 0, 0, + 938, 0, 35, 36, 936, 1, 3, 573, 0, 0, + 323, 326, 321, 0, 619, 619, 0, 0, 65, 66, + 0, 0, 0, 925, 0, 617, 617, 617, 637, 638, + 641, 642, 767, 768, 769, 770, 771, 772, 773, 774, + 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, + 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, + 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, + 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, + 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, + 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, + 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, + 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, + 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, + 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, + 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, + 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, + 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, + 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, + 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, + 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, + 0, 0, 0, 0, 0, 620, 0, 615, 0, 615, + 615, 615, 0, 258, 392, 645, 646, 925, 0, 0, + 0, 939, 0, 939, 270, 939, 939, 273, 939, 0, + 939, 0, 280, 0, 0, 286, 939, 303, 304, 291, + 305, 308, 311, 312, 313, 314, 315, 938, 938, 318, + 29, 577, 0, 0, 565, 31, 0, 319, 324, 325, + 329, 327, 328, 320, 0, 338, 342, 0, 402, 0, + 407, 409, -2, -2, 0, 444, 445, 446, 447, 448, + 0, 0, 0, 0, 0, 0, 0, 0, 472, 473, + 474, 475, 550, 551, 552, 553, 554, 555, 556, 557, + 411, 412, 547, 597, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 538, 0, 509, 509, 509, 509, 509, + 509, 509, 509, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 44, 46, 392, 50, 0, 914, + 601, -2, -2, 0, 0, 643, 644, -2, 780, -2, + 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, + 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, + 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, + 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, + 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, + 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, + 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, + 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, + 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, + 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, + 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, + 759, 760, 761, 762, 763, 764, 765, 766, 0, 0, + 84, 0, 82, 0, 939, 0, 0, 0, 0, 0, + 0, 939, 939, 939, 0, 0, 0, 0, 249, 0, + 0, 0, 0, 0, 0, 0, 257, 0, 259, 939, + 939, 262, 940, 941, 939, 939, 939, 0, 939, 939, + 269, 271, 272, 274, 939, 939, 276, 0, 294, 292, 293, 288, 289, 0, 300, 283, 284, 287, 316, 317, - 30, 936, 24, 0, 0, 573, 0, 565, 566, 569, - 572, 29, 326, 0, 332, 330, 331, 322, 0, 339, - 0, 0, 0, 343, 0, 345, 346, 0, 404, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 428, 429, - 430, 431, 432, 433, 434, 407, 0, 421, 0, 0, - 0, 463, 464, 465, 466, 467, 468, 469, 0, 334, - 29, 0, 441, 0, 0, 0, 0, 0, 0, 0, - 0, 329, 0, 538, 0, 492, 500, 0, 493, 501, - 494, 502, 495, 0, 496, 503, 497, 504, 498, 499, - 505, 0, 0, 0, 334, 0, 0, 48, 0, 391, + 30, 937, 24, 0, 0, 574, 0, 566, 567, 570, + 573, 29, 326, 0, 332, 330, 331, 322, 0, 339, + 0, 0, 0, 343, 0, 345, 346, 0, 405, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 429, 430, + 431, 432, 433, 434, 435, 408, 0, 422, 0, 0, + 0, 464, 465, 466, 467, 468, 469, 470, 0, 334, + 29, 0, 442, 0, 0, 0, 0, 0, 0, 0, + 0, 329, 0, 539, 0, 493, 501, 0, 494, 502, + 495, 503, 496, 0, 497, 504, 498, 505, 499, 500, + 506, 0, 0, 0, 334, 0, 0, 48, 0, 391, 0, 349, 351, 352, 353, -2, 0, 375, -2, 0, - 0, 0, 42, 43, 0, 0, 0, 0, 51, 913, - 53, 54, 0, 0, 0, 162, 609, 610, 611, 607, + 0, 0, 42, 43, 0, 0, 0, 0, 51, 914, + 53, 54, 0, 0, 0, 162, 610, 611, 612, 608, 206, 0, 0, 150, 146, 90, 91, 92, 139, 94, 139, 139, 139, 139, 159, 159, 159, 159, 122, 123, 124, 125, 126, 0, 0, 109, 139, 139, 139, 113, 129, 130, 131, 132, 133, 134, 135, 136, 95, 96, 97, 98, 99, 100, 101, 102, 103, 141, 141, 141, - 143, 143, 638, 68, 0, 938, 0, 938, 80, 0, + 143, 143, 639, 68, 0, 939, 0, 939, 80, 0, 220, 222, 223, 0, 0, 0, 0, 0, 0, 0, - 0, 252, 615, 0, 938, 255, 256, 393, 646, 647, + 0, 252, 616, 0, 939, 255, 256, 393, 647, 648, 260, 261, 263, 264, 265, 266, 267, 268, 275, 279, - 0, 297, 0, 0, 281, 282, 0, 0, 577, 0, - 0, 0, 0, 0, 568, 570, 571, 576, 32, 329, - 0, 557, 0, 0, 0, 333, 27, 402, 403, 405, - 422, 0, 424, 426, 344, 340, 0, 547, -2, 412, - 413, 437, 438, 439, 0, 0, 0, 0, 435, 417, - 0, 448, 449, 450, 451, 452, 453, 454, 455, 456, - 457, 458, 459, 462, 522, 523, 0, 460, 461, 470, - 0, 0, 0, 335, 336, 440, 0, 595, 29, 0, - 0, 0, 0, 445, 549, 0, 445, 549, 0, 0, - 0, 544, 541, 0, 0, 546, 0, 509, 0, 0, + 0, 297, 0, 0, 281, 282, 0, 0, 578, 0, + 0, 0, 0, 0, 569, 571, 572, 577, 32, 329, + 0, 558, 0, 0, 0, 333, 27, 403, 404, 406, + 423, 0, 425, 427, 344, 340, 0, 548, -2, 413, + 414, 438, 439, 440, 0, 0, 0, 0, 436, 418, + 0, 449, 450, 451, 452, 453, 454, 455, 456, 457, + 458, 459, 460, 463, 523, 524, 0, 461, 462, 471, + 0, 0, 0, 335, 336, 441, 0, 596, 29, 0, + 0, 0, 0, 446, 550, 0, 446, 550, 0, 0, + 0, 545, 542, 0, 0, 547, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, 390, 0, 0, 0, 0, 0, 0, 380, 0, 0, 383, 0, 0, - 0, 0, 374, 0, 0, 395, 858, 376, 0, 378, - 379, 399, 0, 399, 45, 399, 47, 0, 394, 601, - 52, 0, 0, 57, 58, 602, 603, 604, 605, 0, + 0, 0, 374, 0, 0, 395, 859, 376, 0, 378, + 379, 400, 0, 400, 45, 400, 47, 0, 394, 602, + 52, 0, 0, 57, 58, 603, 604, 605, 606, 0, 81, 207, 209, 212, 213, 214, 85, 86, 87, 0, 0, 194, 0, 0, 188, 188, 0, 186, 187, 83, 153, 151, 0, 148, 147, 93, 0, 159, 159, 116, 117, 162, 0, 162, 162, 162, 0, 0, 110, 111, 112, 104, 0, 105, 106, 107, 0, 108, 0, 0, - 938, 70, 617, 71, 937, 0, 0, 630, 221, 620, - 621, 622, 623, 624, 625, 626, 627, 628, 629, 0, - 72, 225, 227, 226, 230, 0, 0, 0, 250, 938, + 939, 70, 618, 71, 938, 0, 0, 631, 221, 621, + 622, 623, 624, 625, 626, 627, 628, 629, 630, 0, + 72, 225, 227, 226, 230, 0, 0, 0, 250, 939, 254, 294, 278, 0, 0, 295, 296, 301, 285, 0, - 574, 575, 0, 567, 25, 0, 612, 613, 558, 559, - 347, 423, 425, 427, 0, 334, 414, 435, 418, 0, - 415, 0, 0, 409, 475, 0, 0, 0, 442, -2, - 479, 480, 0, 0, 0, 0, 0, 515, 0, 0, - 516, 0, 564, 0, 542, 0, 0, 491, 0, 510, - 0, 0, 511, 512, 513, 514, 589, 0, 0, -2, - 0, 0, 399, 597, 0, 350, 369, 371, 0, 366, + 575, 576, 0, 568, 25, 0, 613, 614, 559, 560, + 347, 424, 426, 428, 0, 334, 415, 436, 419, 0, + 416, 0, 0, 410, 476, 0, 0, 0, 443, -2, + 480, 481, 0, 0, 0, 0, 0, 516, 0, 0, + 517, 0, 565, 0, 543, 0, 0, 492, 0, 511, + 0, 0, 512, 513, 514, 515, 590, 0, 0, -2, + 0, 0, 400, 598, 0, 350, 369, 371, 0, 366, 381, 382, 384, 0, 386, 0, 388, 389, 354, 356, - 357, 0, 0, 0, 0, 377, 564, 0, 399, 40, + 357, 0, 0, 0, 0, 377, 565, 0, 400, 40, 41, 0, 55, 56, 0, 0, 62, 163, 164, 0, 210, 0, 0, 0, 181, 188, 188, 184, 189, 185, 0, 155, 0, 152, 89, 149, 0, 162, 162, 118, 0, 119, 120, 121, 0, 137, 0, 0, 0, 0, - 639, 69, 215, 937, 232, 233, 234, 235, 236, 237, - 238, 239, 240, 241, 242, 243, 937, 0, 937, 631, - 632, 633, 634, 0, 75, 0, 0, 0, 0, 0, - 253, 297, 298, 299, 578, 0, 26, 399, 0, 341, - 548, 0, 416, 0, 436, 419, 476, 477, 337, 0, - 139, 139, 527, 139, 143, 530, 139, 532, 139, 535, - 0, 0, 0, 0, 0, 0, 0, 539, 490, 545, - 0, 547, 0, 0, 33, 0, 589, 579, 591, 593, - 0, 29, 0, 585, 0, 361, 564, 0, 0, 363, + 640, 69, 215, 938, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 938, 0, 938, 632, + 633, 634, 635, 0, 75, 0, 0, 0, 0, 0, + 253, 297, 298, 299, 579, 0, 26, 400, 0, 341, + 549, 0, 417, 0, 437, 420, 477, 478, 337, 0, + 139, 139, 528, 139, 143, 531, 139, 533, 139, 536, + 0, 0, 0, 0, 0, 0, 0, 540, 491, 546, + 0, 548, 0, 0, 33, 0, 590, 580, 592, 594, + 0, 29, 0, 586, 0, 361, 565, 0, 0, 363, 370, 0, 0, 364, 0, 365, 385, 387, 0, 0, - 0, 0, 572, 400, 39, 59, 60, 61, 208, 211, + 0, 0, 573, 401, 39, 59, 60, 61, 208, 211, 0, 190, 139, 193, 182, 183, 0, 157, 0, 154, 140, 114, 115, 160, 161, 159, 0, 159, 0, 144, - 0, 938, 216, 217, 218, 219, 0, 224, 0, 73, - 74, 0, 0, 229, 251, 277, 560, 348, 478, 420, - 481, 524, 159, 528, 529, 531, 533, 534, 536, 483, - 482, 0, 0, 0, 0, 0, 0, 0, 543, 0, - 0, 0, 34, 0, 594, -2, 0, 0, 0, 49, - 0, 572, 598, 599, 367, 0, 372, 0, 0, 0, + 0, 939, 216, 217, 218, 219, 0, 224, 0, 73, + 74, 0, 0, 229, 251, 277, 561, 348, 479, 421, + 482, 525, 159, 529, 530, 532, 534, 535, 537, 484, + 483, 0, 0, 0, 0, 0, 0, 0, 544, 0, + 0, 0, 34, 0, 595, -2, 0, 0, 0, 49, + 0, 573, 599, 600, 367, 0, 372, 0, 0, 0, 375, 38, 173, 0, 192, 0, 359, 165, 158, 0, 162, 138, 162, 0, 0, 67, 0, 76, 77, 0, - 0, 0, 562, 0, 525, 526, 0, 0, 0, 0, - 517, 489, 540, 0, 0, 0, 592, 0, -2, 0, - 587, 586, 362, 37, 0, 0, 0, 0, 395, 172, - 174, 0, 179, 0, 191, 0, 0, 170, 0, 167, - 169, 156, 127, 128, 142, 145, 0, 0, 0, 0, - 231, 28, 0, 0, 484, 486, 485, 487, 0, 0, - 0, 506, 507, 0, 582, 29, 0, 368, 396, 397, - 398, 358, 175, 176, 0, 180, 178, 0, 360, 88, - 0, 166, 168, 0, 245, 0, 78, 79, 72, 563, - 561, 488, 0, 0, 0, 590, -2, 588, 177, 0, - 171, 244, 0, 0, 75, 518, 0, 521, 0, 246, - 0, 228, 519, 0, 0, 0, 195, 0, 0, 196, - 197, 0, 0, 520, 198, 0, 0, 0, 0, 0, - 199, 201, 202, 0, 0, 200, 247, 248, 203, 204, - 205, + 0, 0, 563, 0, 526, 527, 0, 0, 0, 0, + 518, 490, 541, 0, 0, 0, 593, 0, -2, 0, + 588, 587, 362, 37, 0, 0, 397, 0, 0, 395, + 172, 174, 0, 179, 0, 191, 0, 0, 170, 0, + 167, 169, 156, 127, 128, 142, 145, 0, 0, 0, + 0, 231, 28, 0, 0, 485, 487, 486, 488, 0, + 0, 0, 507, 508, 0, 583, 29, 0, 368, 396, + 398, 399, 358, 175, 176, 0, 180, 178, 0, 360, + 88, 0, 166, 168, 0, 245, 0, 78, 79, 72, + 564, 562, 489, 0, 0, 0, 591, -2, 589, 177, + 0, 171, 244, 0, 0, 75, 519, 0, 522, 0, + 246, 0, 228, 520, 0, 0, 0, 195, 0, 0, + 196, 197, 0, 0, 521, 198, 0, 0, 0, 0, + 0, 199, 201, 202, 0, 0, 200, 247, 248, 203, + 204, 205, } var yyTok1 = [...]int{ @@ -5894,364 +5954,364 @@ yydefault: yyVAL.indexHints = &IndexHints{Type: UseStr, Indexes: yyDollar[4].columns} } case 397: - yyDollar = yyS[yypt-5 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2137 { - yyVAL.indexHints = &IndexHints{Type: IgnoreStr, Indexes: yyDollar[4].columns} + yyVAL.indexHints = &IndexHints{Type: UseStr} } case 398: yyDollar = yyS[yypt-5 : yypt+1] //line sql.y:2141 { - yyVAL.indexHints = &IndexHints{Type: ForceStr, Indexes: yyDollar[4].columns} + yyVAL.indexHints = &IndexHints{Type: IgnoreStr, Indexes: yyDollar[4].columns} } case 399: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2146 + yyDollar = yyS[yypt-5 : yypt+1] +//line sql.y:2145 { - yyVAL.expr = nil + yyVAL.indexHints = &IndexHints{Type: ForceStr, Indexes: yyDollar[4].columns} } case 400: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:2150 { - yyVAL.expr = yyDollar[2].expr + yyVAL.expr = nil } case 401: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2156 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2154 { - yyVAL.expr = yyDollar[1].expr + yyVAL.expr = yyDollar[2].expr } case 402: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2160 { - yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} + yyVAL.expr = yyDollar[1].expr } case 403: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2164 { - yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} + yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 404: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2168 { - yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} + yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 405: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2172 { - yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} + yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} } case 406: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2176 { - yyVAL.expr = yyDollar[1].expr + yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} } case 407: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2180 { - yyVAL.expr = &Default{ColName: yyDollar[2].str} + yyVAL.expr = yyDollar[1].expr } case 408: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2186 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2184 { - yyVAL.str = "" + yyVAL.expr = &Default{ColName: yyDollar[2].str} } case 409: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:2190 { - yyVAL.str = string(yyDollar[2].bytes) + yyVAL.str = "" } case 410: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2196 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2194 { - yyVAL.boolVal = BoolVal(true) + yyVAL.str = string(yyDollar[2].bytes) } case 411: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2200 { - yyVAL.boolVal = BoolVal(false) + yyVAL.boolVal = BoolVal(true) } case 412: - yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2206 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2204 { - yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} + yyVAL.boolVal = BoolVal(false) } case 413: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2210 { - yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} + yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } case 414: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2214 { - yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} + yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } case 415: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2218 { - yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} + yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } case 416: - yyDollar = yyS[yypt-5 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2222 { - yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} + yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } case 417: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-5 : yypt+1] //line sql.y:2226 { - yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} + yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } case 418: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2230 { - yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} + yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } case 419: - yyDollar = yyS[yypt-5 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2234 { - yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} + yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } case 420: - yyDollar = yyS[yypt-6 : yypt+1] + yyDollar = yyS[yypt-5 : yypt+1] //line sql.y:2238 { - yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} + yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } case 421: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-6 : yypt+1] //line sql.y:2242 { - yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} + yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } case 422: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2248 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2246 { - yyVAL.str = IsNullStr + yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } case 423: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2252 { - yyVAL.str = IsNotNullStr + yyVAL.str = IsNullStr } case 424: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2256 { - yyVAL.str = IsTrueStr + yyVAL.str = IsNotNullStr } case 425: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2260 { - yyVAL.str = IsNotTrueStr + yyVAL.str = IsTrueStr } case 426: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2264 { - yyVAL.str = IsFalseStr + yyVAL.str = IsNotTrueStr } case 427: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2268 { - yyVAL.str = IsNotFalseStr + yyVAL.str = IsFalseStr } case 428: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2274 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2272 { - yyVAL.str = EqualStr + yyVAL.str = IsNotFalseStr } case 429: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2278 { - yyVAL.str = LessThanStr + yyVAL.str = EqualStr } case 430: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2282 { - yyVAL.str = GreaterThanStr + yyVAL.str = LessThanStr } case 431: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2286 { - yyVAL.str = LessEqualStr + yyVAL.str = GreaterThanStr } case 432: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2290 { - yyVAL.str = GreaterEqualStr + yyVAL.str = LessEqualStr } case 433: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2294 { - yyVAL.str = NotEqualStr + yyVAL.str = GreaterEqualStr } case 434: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2298 { - yyVAL.str = NullSafeEqualStr + yyVAL.str = NotEqualStr } case 435: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2303 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2302 { - yyVAL.expr = nil + yyVAL.str = NullSafeEqualStr } case 436: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:2307 { - yyVAL.expr = yyDollar[2].expr + yyVAL.expr = nil } case 437: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2313 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2311 { - yyVAL.colTuple = yyDollar[1].valTuple + yyVAL.expr = yyDollar[2].expr } case 438: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2317 { - yyVAL.colTuple = yyDollar[1].subquery + yyVAL.colTuple = yyDollar[1].valTuple } case 439: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2321 { - yyVAL.colTuple = ListArg(yyDollar[1].bytes) + yyVAL.colTuple = yyDollar[1].subquery } case 440: - yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2327 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2325 { - yyVAL.subquery = &Subquery{yyDollar[2].selStmt} + yyVAL.colTuple = ListArg(yyDollar[1].bytes) } case 441: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2333 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2331 { - yyVAL.exprs = Exprs{yyDollar[1].expr} + yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } case 442: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2337 { - yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) + yyVAL.exprs = Exprs{yyDollar[1].expr} } case 443: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2343 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2341 { - yyVAL.expr = yyDollar[1].expr + yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 444: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2347 { - yyVAL.expr = yyDollar[1].boolVal + yyVAL.expr = yyDollar[1].expr } case 445: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2351 { - yyVAL.expr = yyDollar[1].colName + yyVAL.expr = yyDollar[1].boolVal } case 446: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2355 { - yyVAL.expr = yyDollar[1].expr + yyVAL.expr = yyDollar[1].colName } case 447: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2359 { - yyVAL.expr = yyDollar[1].subquery + yyVAL.expr = yyDollar[1].expr } case 448: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2363 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} + yyVAL.expr = yyDollar[1].subquery } case 449: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2367 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } case 450: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2371 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } case 451: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2375 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } case 452: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2379 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } case 453: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2383 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } case 454: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2387 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } case 455: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2391 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } case 456: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2395 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } case 457: yyDollar = yyS[yypt-3 : yypt+1] @@ -6263,53 +6323,59 @@ yydefault: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2403 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 459: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2407 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } case 460: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2411 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONExtractOp, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} } case 461: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2415 { - yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONUnquoteExtractOp, Right: yyDollar[3].expr} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONExtractOp, Right: yyDollar[3].expr} } case 462: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2419 { - yyVAL.expr = &CollateExpr{Expr: yyDollar[1].expr, Charset: yyDollar[3].str} + yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONUnquoteExtractOp, Right: yyDollar[3].expr} } case 463: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2423 { - yyVAL.expr = &UnaryExpr{Operator: BinaryStr, Expr: yyDollar[2].expr} + yyVAL.expr = &CollateExpr{Expr: yyDollar[1].expr, Charset: yyDollar[3].str} } case 464: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2427 { - yyVAL.expr = &UnaryExpr{Operator: UBinaryStr, Expr: yyDollar[2].expr} + yyVAL.expr = &UnaryExpr{Operator: BinaryStr, Expr: yyDollar[2].expr} } case 465: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2431 { - yyVAL.expr = &UnaryExpr{Operator: Utf8mb4Str, Expr: yyDollar[2].expr} + yyVAL.expr = &UnaryExpr{Operator: UBinaryStr, Expr: yyDollar[2].expr} } case 466: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2435 + { + yyVAL.expr = &UnaryExpr{Operator: Utf8mb4Str, Expr: yyDollar[2].expr} + } + case 467: + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2439 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -6317,9 +6383,9 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UPlusStr, Expr: yyDollar[2].expr} } } - case 467: + case 468: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2443 +//line sql.y:2447 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -6333,21 +6399,21 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UMinusStr, Expr: yyDollar[2].expr} } } - case 468: + case 469: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2457 +//line sql.y:2461 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } - case 469: + case 470: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2461 +//line sql.y:2465 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } - case 470: + case 471: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2465 +//line sql.y:2469 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -6355,17 +6421,11 @@ yydefault: // will be non-trivial because of grammar conflicts. yyVAL.expr = &IntervalExpr{Expr: yyDollar[2].expr, Unit: yyDollar[3].colIdent.String()} } - case 475: - yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2483 - { - yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} - } case 476: - yyDollar = yyS[yypt-5 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2487 { - yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} + yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } case 477: yyDollar = yyS[yypt-5 : yypt+1] @@ -6374,28 +6434,28 @@ yydefault: yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } case 478: - yyDollar = yyS[yypt-6 : yypt+1] + yyDollar = yyS[yypt-5 : yypt+1] //line sql.y:2495 { - yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} + yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } case 479: - yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2505 + yyDollar = yyS[yypt-6 : yypt+1] +//line sql.y:2499 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("left"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } case 480: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2509 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("right"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("left"), Exprs: yyDollar[3].selectExprs} } case 481: - yyDollar = yyS[yypt-6 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2513 { - yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} + yyVAL.expr = &FuncExpr{Name: NewColIdent("right"), Exprs: yyDollar[3].selectExprs} } case 482: yyDollar = yyS[yypt-6 : yypt+1] @@ -6407,13 +6467,13 @@ yydefault: yyDollar = yyS[yypt-6 : yypt+1] //line sql.y:2521 { - yyVAL.expr = &ConvertUsingExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].str} + yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } case 484: - yyDollar = yyS[yypt-8 : yypt+1] + yyDollar = yyS[yypt-6 : yypt+1] //line sql.y:2525 { - yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} + yyVAL.expr = &ConvertUsingExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].str} } case 485: yyDollar = yyS[yypt-8 : yypt+1] @@ -6425,7 +6485,7 @@ yydefault: yyDollar = yyS[yypt-8 : yypt+1] //line sql.y:2533 { - yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} + yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } case 487: yyDollar = yyS[yypt-8 : yypt+1] @@ -6434,160 +6494,160 @@ yydefault: yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } case 488: - yyDollar = yyS[yypt-9 : yypt+1] + yyDollar = yyS[yypt-8 : yypt+1] //line sql.y:2541 { - yyVAL.expr = &MatchExpr{Columns: yyDollar[3].selectExprs, Expr: yyDollar[7].expr, Option: yyDollar[8].str} + yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } case 489: - yyDollar = yyS[yypt-7 : yypt+1] + yyDollar = yyS[yypt-9 : yypt+1] //line sql.y:2545 { - yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} + yyVAL.expr = &MatchExpr{Columns: yyDollar[3].selectExprs, Expr: yyDollar[7].expr, Option: yyDollar[8].str} } case 490: - yyDollar = yyS[yypt-5 : yypt+1] + yyDollar = yyS[yypt-7 : yypt+1] //line sql.y:2549 { - yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} + yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} } case 491: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-5 : yypt+1] //line sql.y:2553 { - yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} + yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } case 492: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2563 + yyDollar = yyS[yypt-4 : yypt+1] +//line sql.y:2557 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("current_timestamp")} + yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} } case 493: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2567 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_timestamp")} + yyVAL.expr = &FuncExpr{Name: NewColIdent("current_timestamp")} } case 494: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2571 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_time")} + yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_timestamp")} } case 495: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2576 +//line sql.y:2575 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_date")} + yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_time")} } case 496: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2581 +//line sql.y:2580 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("localtime")} + yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_date")} } case 497: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2586 +//line sql.y:2585 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("localtimestamp")} + yyVAL.expr = &FuncExpr{Name: NewColIdent("localtime")} } case 498: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2592 +//line sql.y:2590 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("current_date")} + yyVAL.expr = &FuncExpr{Name: NewColIdent("localtimestamp")} } case 499: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2597 +//line sql.y:2596 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("current_time")} + yyVAL.expr = &FuncExpr{Name: NewColIdent("current_date")} } case 500: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2602 +//line sql.y:2601 { - yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_timestamp"), Fsp: yyDollar[2].expr} + yyVAL.expr = &FuncExpr{Name: NewColIdent("current_time")} } case 501: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2606 { - yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_timestamp"), Fsp: yyDollar[2].expr} + yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_timestamp"), Fsp: yyDollar[2].expr} } case 502: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2610 { - yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_time"), Fsp: yyDollar[2].expr} + yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_timestamp"), Fsp: yyDollar[2].expr} } case 503: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2615 +//line sql.y:2614 { - yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtime"), Fsp: yyDollar[2].expr} + yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_time"), Fsp: yyDollar[2].expr} } case 504: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2620 +//line sql.y:2619 { - yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtimestamp"), Fsp: yyDollar[2].expr} + yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtime"), Fsp: yyDollar[2].expr} } case 505: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2625 +//line sql.y:2624 { - yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_time"), Fsp: yyDollar[2].expr} + yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtimestamp"), Fsp: yyDollar[2].expr} } case 506: - yyDollar = yyS[yypt-8 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2629 { - yyVAL.expr = &TimestampFuncExpr{Name: string("timestampadd"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} + yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_time"), Fsp: yyDollar[2].expr} } case 507: yyDollar = yyS[yypt-8 : yypt+1] //line sql.y:2633 { - yyVAL.expr = &TimestampFuncExpr{Name: string("timestampdiff"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} + yyVAL.expr = &TimestampFuncExpr{Name: string("timestampadd"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } - case 510: - yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2643 + case 508: + yyDollar = yyS[yypt-8 : yypt+1] +//line sql.y:2637 { - yyVAL.expr = yyDollar[2].expr + yyVAL.expr = &TimestampFuncExpr{Name: string("timestampdiff"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } case 511: - yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2653 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2647 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = yyDollar[2].expr } case 512: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2657 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("database"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } case 513: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2661 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("database"), Exprs: yyDollar[3].selectExprs} } case 514: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2665 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } case 515: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2669 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } case 516: yyDollar = yyS[yypt-4 : yypt+1] @@ -6596,40 +6656,40 @@ yydefault: yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 517: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2679 + yyDollar = yyS[yypt-4 : yypt+1] +//line sql.y:2677 { - yyVAL.str = "" + yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 518: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:2683 { - yyVAL.str = BooleanModeStr + yyVAL.str = "" } case 519: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2687 { - yyVAL.str = NaturalLanguageModeStr + yyVAL.str = BooleanModeStr } case 520: - yyDollar = yyS[yypt-7 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2691 { - yyVAL.str = NaturalLanguageModeWithQueryExpansionStr + yyVAL.str = NaturalLanguageModeStr } case 521: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-7 : yypt+1] //line sql.y:2695 { - yyVAL.str = QueryExpansionStr + yyVAL.str = NaturalLanguageModeWithQueryExpansionStr } case 522: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2701 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2699 { - yyVAL.str = string(yyDollar[1].bytes) + yyVAL.str = QueryExpansionStr } case 523: yyDollar = yyS[yypt-1 : yypt+1] @@ -6638,63 +6698,63 @@ yydefault: yyVAL.str = string(yyDollar[1].bytes) } case 524: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2711 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2709 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.str = string(yyDollar[1].bytes) } case 525: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2715 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Operator: CharacterSetStr} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 526: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2719 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Operator: CharacterSetStr} } case 527: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2723 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].bytes)} } case 528: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2727 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 529: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2731 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} - yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 530: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2737 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2735 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale } case 531: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2741 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 532: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2745 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 533: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2749 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} @@ -6703,143 +6763,149 @@ yydefault: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2753 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 535: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2757 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 536: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2761 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 537: + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2765 + { + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + } + case 538: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2766 +//line sql.y:2770 { yyVAL.expr = nil } - case 538: + case 539: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2770 +//line sql.y:2774 { yyVAL.expr = yyDollar[1].expr } - case 539: + case 540: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2775 +//line sql.y:2779 { yyVAL.str = string("") } - case 540: + case 541: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2779 +//line sql.y:2783 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } - case 541: + case 542: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2785 +//line sql.y:2789 { yyVAL.whens = []*When{yyDollar[1].when} } - case 542: + case 543: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2789 +//line sql.y:2793 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } - case 543: + case 544: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2795 +//line sql.y:2799 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } - case 544: + case 545: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2800 +//line sql.y:2804 { yyVAL.expr = nil } - case 545: + case 546: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2804 +//line sql.y:2808 { yyVAL.expr = yyDollar[2].expr } - case 546: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2810 - { - yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} - } case 547: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2814 { - yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} + yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } case 548: - yyDollar = yyS[yypt-5 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2818 { - yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} + yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } case 549: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2824 + yyDollar = yyS[yypt-5 : yypt+1] +//line sql.y:2822 { - yyVAL.expr = NewStrVal(yyDollar[1].bytes) + yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } case 550: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2828 { - yyVAL.expr = NewHexVal(yyDollar[1].bytes) + yyVAL.expr = NewStrVal(yyDollar[1].bytes) } case 551: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2832 { - yyVAL.expr = NewBitVal(yyDollar[1].bytes) + yyVAL.expr = NewHexVal(yyDollar[1].bytes) } case 552: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2836 { - yyVAL.expr = NewIntVal(yyDollar[1].bytes) + yyVAL.expr = NewBitVal(yyDollar[1].bytes) } case 553: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2840 { - yyVAL.expr = NewFloatVal(yyDollar[1].bytes) + yyVAL.expr = NewIntVal(yyDollar[1].bytes) } case 554: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2844 { - yyVAL.expr = NewHexNum(yyDollar[1].bytes) + yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } case 555: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2848 { - yyVAL.expr = NewValArg(yyDollar[1].bytes) + yyVAL.expr = NewHexNum(yyDollar[1].bytes) } case 556: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2852 { - yyVAL.expr = &NullVal{} + yyVAL.expr = NewValArg(yyDollar[1].bytes) } case 557: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2858 +//line sql.y:2856 + { + yyVAL.expr = &NullVal{} + } + case 558: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2862 { // TODO(sougou): Deprecate this construct. if yyDollar[1].colIdent.Lowered() != "value" { @@ -6848,239 +6914,239 @@ yydefault: } yyVAL.expr = NewIntVal([]byte("1")) } - case 558: + case 559: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2867 +//line sql.y:2871 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 559: + case 560: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2871 +//line sql.y:2875 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 560: + case 561: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2876 +//line sql.y:2880 { yyVAL.exprs = nil } - case 561: + case 562: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2880 +//line sql.y:2884 { yyVAL.exprs = yyDollar[3].exprs } - case 562: + case 563: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2885 +//line sql.y:2889 { yyVAL.expr = nil } - case 563: + case 564: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2889 +//line sql.y:2893 { yyVAL.expr = yyDollar[2].expr } - case 564: + case 565: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2894 +//line sql.y:2898 { yyVAL.orderBy = nil } - case 565: + case 566: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2898 +//line sql.y:2902 { yyVAL.orderBy = yyDollar[3].orderBy } - case 566: + case 567: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2904 +//line sql.y:2908 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } - case 567: + case 568: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2908 +//line sql.y:2912 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } - case 568: + case 569: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2914 +//line sql.y:2918 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } - case 569: + case 570: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2919 +//line sql.y:2923 { yyVAL.str = AscScr } - case 570: + case 571: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2923 +//line sql.y:2927 { yyVAL.str = AscScr } - case 571: + case 572: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2927 +//line sql.y:2931 { yyVAL.str = DescScr } - case 572: + case 573: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2932 +//line sql.y:2936 { yyVAL.limit = nil } - case 573: + case 574: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2936 +//line sql.y:2940 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } - case 574: + case 575: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2940 +//line sql.y:2944 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } - case 575: + case 576: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2944 +//line sql.y:2948 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } - case 576: + case 577: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2949 +//line sql.y:2953 { yyVAL.str = "" } - case 577: + case 578: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2953 +//line sql.y:2957 { yyVAL.str = ForUpdateStr } - case 578: + case 579: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2957 +//line sql.y:2961 { yyVAL.str = ShareModeStr } - case 579: + case 580: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2970 +//line sql.y:2974 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } - case 580: + case 581: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2974 +//line sql.y:2978 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } - case 581: + case 582: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2978 +//line sql.y:2982 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } - case 582: + case 583: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2983 +//line sql.y:2987 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } - case 583: + case 584: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2987 +//line sql.y:2991 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } - case 584: + case 585: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2991 +//line sql.y:2995 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } - case 585: + case 586: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2998 +//line sql.y:3002 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 586: + case 587: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3002 +//line sql.y:3006 { yyVAL.columns = Columns{yyDollar[3].colIdent} } - case 587: + case 588: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3006 +//line sql.y:3010 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 588: + case 589: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3010 +//line sql.y:3014 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } - case 589: + case 590: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3015 +//line sql.y:3019 { yyVAL.updateExprs = nil } - case 590: + case 591: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3019 +//line sql.y:3023 { yyVAL.updateExprs = yyDollar[5].updateExprs } - case 591: + case 592: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3025 +//line sql.y:3029 { yyVAL.values = Values{yyDollar[1].valTuple} } - case 592: + case 593: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3029 +//line sql.y:3033 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } - case 593: + case 594: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3035 +//line sql.y:3039 { yyVAL.valTuple = yyDollar[1].valTuple } - case 594: + case 595: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3039 +//line sql.y:3043 { yyVAL.valTuple = ValTuple{} } - case 595: + case 596: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3045 +//line sql.y:3049 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } - case 596: + case 597: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3051 +//line sql.y:3055 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -7088,195 +7154,189 @@ yydefault: yyVAL.expr = yyDollar[1].valTuple } } - case 597: + case 598: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3061 +//line sql.y:3065 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } - case 598: + case 599: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3065 +//line sql.y:3069 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } - case 599: + case 600: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3071 +//line sql.y:3075 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } - case 600: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3077 - { - yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} - } case 601: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:3081 { - yyVAL.setExprs = append(yyDollar[1].setExprs, yyDollar[3].setExpr) + yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} } case 602: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3087 +//line sql.y:3085 { - yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("on"))} + yyVAL.setExprs = append(yyDollar[1].setExprs, yyDollar[3].setExpr) } case 603: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:3091 { - yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("off"))} + yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("on"))} } case 604: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:3095 { - yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: yyDollar[3].expr} + yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("off"))} } case 605: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:3099 + { + yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: yyDollar[3].expr} + } + case 606: + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:3103 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(string(yyDollar[1].bytes)), Expr: yyDollar[2].expr} } - case 607: + case 608: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3106 +//line sql.y:3110 { yyVAL.bytes = []byte("charset") } - case 609: + case 610: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3113 +//line sql.y:3117 { yyVAL.expr = NewStrVal([]byte(yyDollar[1].colIdent.String())) } - case 610: + case 611: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3117 +//line sql.y:3121 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } - case 611: + case 612: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3121 +//line sql.y:3125 { yyVAL.expr = &Default{} } - case 614: + case 615: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3130 +//line sql.y:3134 { yyVAL.byt = 0 } - case 615: + case 616: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3132 +//line sql.y:3136 { yyVAL.byt = 1 } - case 616: + case 617: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3135 +//line sql.y:3139 { yyVAL.empty = struct{}{} } - case 617: + case 618: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3137 +//line sql.y:3141 { yyVAL.empty = struct{}{} } - case 618: + case 619: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3140 +//line sql.y:3144 { yyVAL.str = "" } - case 619: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3142 - { - yyVAL.str = IgnoreStr - } case 620: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:3146 { - yyVAL.empty = struct{}{} + yyVAL.str = IgnoreStr } case 621: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3148 +//line sql.y:3150 { yyVAL.empty = struct{}{} } case 622: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3150 +//line sql.y:3152 { yyVAL.empty = struct{}{} } case 623: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3152 +//line sql.y:3154 { yyVAL.empty = struct{}{} } case 624: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3154 +//line sql.y:3156 { yyVAL.empty = struct{}{} } case 625: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3156 +//line sql.y:3158 { yyVAL.empty = struct{}{} } case 626: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3158 +//line sql.y:3160 { yyVAL.empty = struct{}{} } case 627: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3160 +//line sql.y:3162 { yyVAL.empty = struct{}{} } case 628: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3162 +//line sql.y:3164 { yyVAL.empty = struct{}{} } case 629: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3164 +//line sql.y:3166 { yyVAL.empty = struct{}{} } case 630: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3167 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3168 { yyVAL.empty = struct{}{} } case 631: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3169 + yyDollar = yyS[yypt-0 : yypt+1] +//line sql.y:3171 { yyVAL.empty = struct{}{} } case 632: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3171 +//line sql.y:3173 { yyVAL.empty = struct{}{} } @@ -7288,45 +7348,45 @@ yydefault: } case 634: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3177 +//line sql.y:3179 { yyVAL.empty = struct{}{} } case 635: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3180 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3181 { yyVAL.empty = struct{}{} } case 636: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3182 + yyDollar = yyS[yypt-0 : yypt+1] +//line sql.y:3184 { yyVAL.empty = struct{}{} } case 637: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3184 +//line sql.y:3186 { yyVAL.empty = struct{}{} } case 638: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3187 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3188 { - yyVAL.colIdent = ColIdent{} + yyVAL.empty = struct{}{} } case 639: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3189 + yyDollar = yyS[yypt-0 : yypt+1] +//line sql.y:3191 { - yyVAL.colIdent = yyDollar[2].colIdent + yyVAL.colIdent = ColIdent{} } case 640: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:3193 { - yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) + yyVAL.colIdent = yyDollar[2].colIdent } case 641: yyDollar = yyS[yypt-1 : yypt+1] @@ -7334,17 +7394,17 @@ yydefault: { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 643: + case 642: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3204 +//line sql.y:3201 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 644: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3210 +//line sql.y:3208 { - yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) + yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 645: yyDollar = yyS[yypt-1 : yypt+1] @@ -7352,41 +7412,41 @@ yydefault: { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 647: + case 646: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3218 + { + yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) + } + case 648: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3221 +//line sql.y:3225 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 935: + case 936: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3534 +//line sql.y:3538 { if incNesting(yylex) { yylex.Error("max nesting level reached") return 1 } } - case 936: + case 937: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3543 +//line sql.y:3547 { decNesting(yylex) } - case 937: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3548 - { - skipToEnd(yylex) - } case 938: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3553 +//line sql.y:3552 { skipToEnd(yylex) } case 939: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:3557 { skipToEnd(yylex) @@ -7397,6 +7457,12 @@ yydefault: { skipToEnd(yylex) } + case 941: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3565 + { + skipToEnd(yylex) + } } goto yystack /* stack new state and value */ } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index d1828c91e8e..448d3300ed6 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -2133,6 +2133,10 @@ index_hint_list: { $$ = &IndexHints{Type: UseStr, Indexes: $4} } +| USE INDEX openb closeb + { + $$ = &IndexHints{Type: UseStr} + } | IGNORE INDEX openb column_list closeb { $$ = &IndexHints{Type: IgnoreStr, Indexes: $4} From 378b8492a54ae876518b3beee55a8d84de5ce402 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 27 Jan 2020 10:45:26 +0100 Subject: [PATCH 010/825] Cleanup of main() code Signed-off-by: Andres Taylor --- go/visitorgen/main/main.go | 68 +++++++++++++++----------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/go/visitorgen/main/main.go b/go/visitorgen/main/main.go index ae09ad7efdf..3ba2bca047b 100644 --- a/go/visitorgen/main/main.go +++ b/go/visitorgen/main/main.go @@ -17,12 +17,17 @@ limitations under the License. package main import ( + "bytes" "flag" "fmt" "go/parser" "go/token" + "io/ioutil" "os" + "vitess.io/vitess/go/exit" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/visitorgen" ) @@ -33,28 +38,24 @@ var ( const usage = `Usage of visitorgen: -go run go/visitorgen/main/main.go -input=/path/to/ast.go -output=/path/to/rewriter.go +go run ./go/visitorgen/main -input=/path/to/ast.go -output=/path/to/rewriter.go ` func main() { - flag.Usage = func() { - os.Stderr.WriteString(usage) - os.Stderr.WriteString("\nOptions:\n") - flag.PrintDefaults() - - } + defer exit.Recover() + flag.Usage = printUsage flag.Parse() if *inputFile == "" || *outputFile == "" { - fmt.Println("> " + *inputFile) - fmt.Println("> " + *outputFile) - panic("need input and output file") + printUsage() + exit.Return(1) } fs := token.NewFileSet() file, err := parser.ParseFile(fs, *inputFile, nil, parser.DeclarationErrors) if err != nil { - panic(err) + log.Error(err) + exit.Return(1) } astWalkResult := visitorgen.Walk(file) @@ -64,42 +65,25 @@ func main() { replacementMethods := visitorgen.EmitReplacementMethods(vd) typeSwitch := visitorgen.EmitTypeSwitches(vd) - fw := newFileWriter(*outputFile) - defer fw.Close() - - fw.writeln(fileHeader) - fw.writeln(replacementMethods) - fw.write(applyHeader) - fw.writeln(typeSwitch) - fw.writeln(fileFooter) -} - -type fileWriter struct { - file *os.File -} - -func newFileWriter(file string) *fileWriter { - f, err := os.Create(file) - if err != nil { - panic(err) - } - return &fileWriter{file: f} -} - -func (fw *fileWriter) writeln(s string) { - fw.write(s) - fw.write("\n") -} + b := &bytes.Buffer{} + fmt.Fprint(b, fileHeader) + fmt.Fprintln(b) + fmt.Fprintln(b, replacementMethods) + fmt.Fprint(b, applyHeader) + fmt.Fprintln(b, typeSwitch) + fmt.Fprintln(b, fileFooter) -func (fw *fileWriter) write(s string) { - _, err := fw.file.WriteString(s) + err = ioutil.WriteFile(*outputFile, b.Bytes(), 0644) if err != nil { - panic(err) + log.Error(err) + exit.Return(1) } } -func (fw *fileWriter) Close() { - fw.file.Close() +func printUsage() { + os.Stderr.WriteString(usage) + os.Stderr.WriteString("\nOptions:\n") + flag.PrintDefaults() } const fileHeader = `// Code generated by visitorgen/main/main.go. DO NOT EDIT. From e581793d462cb17dd27503e5aeb9c9c0c6df2ce7 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 27 Jan 2020 11:16:37 +0100 Subject: [PATCH 011/825] Clean up Makefile and pre-commit check Signed-off-by: Andres Taylor --- Makefile | 4 +--- go/visitorgen/main/main.go | 24 +++++++++++++++++++----- go/vt/sqlparser/rewriter.go | 2 +- misc/git/hooks/visitorgen | 9 +-------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index acf5363a00b..b71fa046b99 100644 --- a/Makefile +++ b/Makefile @@ -89,9 +89,7 @@ parser: make -C go/vt/sqlparser visitor: - go build -o visitorgen go/visitorgen/main/main.go - ./visitorgen -input=go/vt/sqlparser/ast.go -output=$(REWRITER) - rm ./visitorgen + go generate go/vt/sqlparser/rewriter.go # To pass extra flags, run test.go manually. # For example: go run test.go -docker=false -- --extra-flag diff --git a/go/visitorgen/main/main.go b/go/visitorgen/main/main.go index 3ba2bca047b..e51bce9396d 100644 --- a/go/visitorgen/main/main.go +++ b/go/visitorgen/main/main.go @@ -34,6 +34,7 @@ import ( var ( inputFile = flag.String("input", "", "input file to use") outputFile = flag.String("output", "", "output file") + compare = flag.Bool("compareOnly", false, "instead of writing to the output file, compare if the generated visitor is still valid for this ast.go") ) const usage = `Usage of visitorgen: @@ -73,11 +74,24 @@ func main() { fmt.Fprintln(b, typeSwitch) fmt.Fprintln(b, fileFooter) - err = ioutil.WriteFile(*outputFile, b.Bytes(), 0644) - if err != nil { - log.Error(err) - exit.Return(1) + if *compare { + currentFile, err := ioutil.ReadFile(*outputFile) + if err != nil { + log.Error(err) + exit.Return(1) + } + if !bytes.Equal(b.Bytes(), currentFile) { + fmt.Println("rewriter needs to be re-generated: go generate " + *outputFile) + exit.Return(1) + } + } else { + err = ioutil.WriteFile(*outputFile, b.Bytes(), 0644) + if err != nil { + log.Error(err) + exit.Return(1) + } } + } func printUsage() { @@ -90,7 +104,7 @@ const fileHeader = `// Code generated by visitorgen/main/main.go. DO NOT EDIT. package sqlparser -//go:generate make visitor +//go:generate go run ../../visitorgen/main -input=ast.go -output=rewriter.go import ( "reflect" diff --git a/go/vt/sqlparser/rewriter.go b/go/vt/sqlparser/rewriter.go index 28d5ba52688..2f8da297fed 100644 --- a/go/vt/sqlparser/rewriter.go +++ b/go/vt/sqlparser/rewriter.go @@ -2,7 +2,7 @@ package sqlparser -//go:generate make visitor +//go:generate go run ../../visitorgen/main -input=ast.go -output=rewriter.go import ( "reflect" diff --git a/misc/git/hooks/visitorgen b/misc/git/hooks/visitorgen index 7eb1c41360f..5fa203e9c79 100755 --- a/misc/git/hooks/visitorgen +++ b/misc/git/hooks/visitorgen @@ -15,11 +15,4 @@ # this script, which should run before committing code, makes sure that the visitor is re-generated when the ast changes -make visitor REWRITER=tmp_rewriter.go -if ! cmp -s "tmp_rewriter.go" "go/vt/sqlparser/rewriter.go"; then - echo "The ast.go has changed, but not rewriter.go" - echo "You should 'make visitor' to update the generated rewriter" - rm -f tmp_rewriter.go - exit 1 -fi -rm -f tmp_rewriter.go \ No newline at end of file +go run ./go/visitorgen/main -compareOnly=true -input=go/vt/sqlparser/ast.go -output=go/vt/sqlparser/rewriter.go \ No newline at end of file From 86a3ce61ff3e780c45dbcfc809b3aa3eef066d07 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 27 Jan 2020 11:25:39 +0100 Subject: [PATCH 012/825] Moved visitorgen to be next to the AST Signed-off-by: Andres Taylor --- go/vt/sqlparser/rewriter.go | 2 +- go/{ => vt/sqlparser}/visitorgen/ast_walker.go | 0 go/{ => vt/sqlparser}/visitorgen/ast_walker_test.go | 0 go/{ => vt/sqlparser}/visitorgen/main/main.go | 6 +++--- go/{ => vt/sqlparser}/visitorgen/sast.go | 0 go/{ => vt/sqlparser}/visitorgen/struct_producer.go | 0 go/{ => vt/sqlparser}/visitorgen/struct_producer_test.go | 0 go/{ => vt/sqlparser}/visitorgen/transformer.go | 0 go/{ => vt/sqlparser}/visitorgen/transformer_test.go | 0 go/{ => vt/sqlparser}/visitorgen/visitor_emitter.go | 0 go/{ => vt/sqlparser}/visitorgen/visitor_emitter_test.go | 0 go/{ => vt/sqlparser}/visitorgen/visitorgen.go | 0 misc/git/hooks/visitorgen | 2 +- 13 files changed, 5 insertions(+), 5 deletions(-) rename go/{ => vt/sqlparser}/visitorgen/ast_walker.go (100%) rename go/{ => vt/sqlparser}/visitorgen/ast_walker_test.go (100%) rename go/{ => vt/sqlparser}/visitorgen/main/main.go (94%) rename go/{ => vt/sqlparser}/visitorgen/sast.go (100%) rename go/{ => vt/sqlparser}/visitorgen/struct_producer.go (100%) rename go/{ => vt/sqlparser}/visitorgen/struct_producer_test.go (100%) rename go/{ => vt/sqlparser}/visitorgen/transformer.go (100%) rename go/{ => vt/sqlparser}/visitorgen/transformer_test.go (100%) rename go/{ => vt/sqlparser}/visitorgen/visitor_emitter.go (100%) rename go/{ => vt/sqlparser}/visitorgen/visitor_emitter_test.go (100%) rename go/{ => vt/sqlparser}/visitorgen/visitorgen.go (100%) diff --git a/go/vt/sqlparser/rewriter.go b/go/vt/sqlparser/rewriter.go index 2f8da297fed..2f6c22879fe 100644 --- a/go/vt/sqlparser/rewriter.go +++ b/go/vt/sqlparser/rewriter.go @@ -2,7 +2,7 @@ package sqlparser -//go:generate go run ../../visitorgen/main -input=ast.go -output=rewriter.go +//go:generate go run visitorgen/main -input=ast.go -output=rewriter.go import ( "reflect" diff --git a/go/visitorgen/ast_walker.go b/go/vt/sqlparser/visitorgen/ast_walker.go similarity index 100% rename from go/visitorgen/ast_walker.go rename to go/vt/sqlparser/visitorgen/ast_walker.go diff --git a/go/visitorgen/ast_walker_test.go b/go/vt/sqlparser/visitorgen/ast_walker_test.go similarity index 100% rename from go/visitorgen/ast_walker_test.go rename to go/vt/sqlparser/visitorgen/ast_walker_test.go diff --git a/go/visitorgen/main/main.go b/go/vt/sqlparser/visitorgen/main/main.go similarity index 94% rename from go/visitorgen/main/main.go rename to go/vt/sqlparser/visitorgen/main/main.go index e51bce9396d..5b53b3d268f 100644 --- a/go/visitorgen/main/main.go +++ b/go/vt/sqlparser/visitorgen/main/main.go @@ -28,7 +28,7 @@ import ( "vitess.io/vitess/go/exit" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/visitorgen" + "vitess.io/vitess/go/vt/sqlparser/visitorgen" ) var ( @@ -39,7 +39,7 @@ var ( const usage = `Usage of visitorgen: -go run ./go/visitorgen/main -input=/path/to/ast.go -output=/path/to/rewriter.go +go run /path/to/visitorgen/main -input=/path/to/ast.go -output=/path/to/rewriter.go ` func main() { @@ -104,7 +104,7 @@ const fileHeader = `// Code generated by visitorgen/main/main.go. DO NOT EDIT. package sqlparser -//go:generate go run ../../visitorgen/main -input=ast.go -output=rewriter.go +//go:generate go run visitorgen/main -input=ast.go -output=rewriter.go import ( "reflect" diff --git a/go/visitorgen/sast.go b/go/vt/sqlparser/visitorgen/sast.go similarity index 100% rename from go/visitorgen/sast.go rename to go/vt/sqlparser/visitorgen/sast.go diff --git a/go/visitorgen/struct_producer.go b/go/vt/sqlparser/visitorgen/struct_producer.go similarity index 100% rename from go/visitorgen/struct_producer.go rename to go/vt/sqlparser/visitorgen/struct_producer.go diff --git a/go/visitorgen/struct_producer_test.go b/go/vt/sqlparser/visitorgen/struct_producer_test.go similarity index 100% rename from go/visitorgen/struct_producer_test.go rename to go/vt/sqlparser/visitorgen/struct_producer_test.go diff --git a/go/visitorgen/transformer.go b/go/vt/sqlparser/visitorgen/transformer.go similarity index 100% rename from go/visitorgen/transformer.go rename to go/vt/sqlparser/visitorgen/transformer.go diff --git a/go/visitorgen/transformer_test.go b/go/vt/sqlparser/visitorgen/transformer_test.go similarity index 100% rename from go/visitorgen/transformer_test.go rename to go/vt/sqlparser/visitorgen/transformer_test.go diff --git a/go/visitorgen/visitor_emitter.go b/go/vt/sqlparser/visitorgen/visitor_emitter.go similarity index 100% rename from go/visitorgen/visitor_emitter.go rename to go/vt/sqlparser/visitorgen/visitor_emitter.go diff --git a/go/visitorgen/visitor_emitter_test.go b/go/vt/sqlparser/visitorgen/visitor_emitter_test.go similarity index 100% rename from go/visitorgen/visitor_emitter_test.go rename to go/vt/sqlparser/visitorgen/visitor_emitter_test.go diff --git a/go/visitorgen/visitorgen.go b/go/vt/sqlparser/visitorgen/visitorgen.go similarity index 100% rename from go/visitorgen/visitorgen.go rename to go/vt/sqlparser/visitorgen/visitorgen.go diff --git a/misc/git/hooks/visitorgen b/misc/git/hooks/visitorgen index 5fa203e9c79..65c04d613db 100755 --- a/misc/git/hooks/visitorgen +++ b/misc/git/hooks/visitorgen @@ -15,4 +15,4 @@ # this script, which should run before committing code, makes sure that the visitor is re-generated when the ast changes -go run ./go/visitorgen/main -compareOnly=true -input=go/vt/sqlparser/ast.go -output=go/vt/sqlparser/rewriter.go \ No newline at end of file +go run ./go/vt/sqlparser/visitorgen/main -compareOnly=true -input=go/vt/sqlparser/ast.go -output=go/vt/sqlparser/rewriter.go \ No newline at end of file From 2e66f6f0dd97117691ff90006fd9867b0b1d5b4b Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 27 Jan 2020 19:54:33 +0100 Subject: [PATCH 013/825] Add check that the visitor has been updated to the CI build Signed-off-by: Andres Taylor --- test/config.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/config.json b/test/config.json index a61ce65eab0..e493c43cac8 100644 --- a/test/config.json +++ b/test/config.json @@ -38,6 +38,17 @@ "RetryMax": 1, "Tags": [] }, + "check_make_visitor": { + "File": "", + "Args": [], + "Command": [ + "misc/git/hooks/visitorgen" + ], + "Manual": false, + "Shard": 5, + "RetryMax": 1, + "Tags": [] + }, "java": { "File": "", "Args": [], From 93506294f6a0547cba263a7ed015db40f30a24d9 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Mon, 13 Jan 2020 17:14:04 +0530 Subject: [PATCH 014/825] Code coverage tracking (#11) * added code coverage using sonar and codecov Signed-off-by: Arindam Nayak Signed-off-by: Arindam Nayak --- .github/workflows/sonar_analysis.yml | 55 +++++++++++++++ .gitignore | 3 + README.md | 1 + go/test/endtoend/cluster/cluster_process.go | 9 +++ go/test/endtoend/cluster/cluster_util.go | 15 ++++ go/test/endtoend/cluster/mysqlctl_process.go | 19 +++-- go/test/endtoend/cluster/vtctl_process.go | 22 ++++-- .../endtoend/cluster/vtctlclient_process.go | 19 +++-- go/test/endtoend/cluster/vtctld_process.go | 3 + go/test/endtoend/cluster/vtgate_process.go | 4 ++ go/test/endtoend/cluster/vttablet_process.go | 3 + go/test/endtoend/cluster/vtworker_process.go | 9 +++ sonar-project.properties | 12 ++++ tools/all_test_for_coverage.sh | 69 +++++++++++++++++++ tools/coverage-go/Readme.md | 6 ++ tools/coverage-go/mysqlctl_test.go | 22 ++++++ tools/coverage-go/vtctl_test.go | 24 +++++++ tools/coverage-go/vtctlclient_test.go | 7 ++ tools/coverage-go/vtctld_test.go | 22 ++++++ tools/coverage-go/vtgate_test.go | 22 ++++++ tools/coverage-go/vttablet_test.go | 25 +++++++ tools/coverage-go/vtworker_test.go | 22 ++++++ tools/coverage-go/vtworkerclient_test.go | 22 ++++++ 23 files changed, 401 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/sonar_analysis.yml create mode 100644 sonar-project.properties create mode 100755 tools/all_test_for_coverage.sh create mode 100644 tools/coverage-go/Readme.md create mode 100644 tools/coverage-go/mysqlctl_test.go create mode 100644 tools/coverage-go/vtctl_test.go create mode 100644 tools/coverage-go/vtctlclient_test.go create mode 100644 tools/coverage-go/vtctld_test.go create mode 100644 tools/coverage-go/vtgate_test.go create mode 100644 tools/coverage-go/vttablet_test.go create mode 100644 tools/coverage-go/vtworker_test.go create mode 100644 tools/coverage-go/vtworkerclient_test.go diff --git a/.github/workflows/sonar_analysis.yml b/.github/workflows/sonar_analysis.yml new file mode 100644 index 00000000000..5f604240732 --- /dev/null +++ b/.github/workflows/sonar_analysis.yml @@ -0,0 +1,55 @@ +name: sonar_analysis +on: + push: + branches: + - 'master' +jobs: + + build: + runs-on: ubuntu-latest + + steps: + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.13 + + - name: Check out code + uses: actions/checkout@v2 + + - name: Get dependencies + run: | + sudo apt-get update + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo service mysql stop + sudo service etcd stop + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld + go mod download + + - name: Execute unit test and cluster endtoend test + run: | + eatmydata -- ./tools/all_test_for_coverage.sh + mkdir report + cp /tmp/*.out ./report/. + + - name: Analyse sonar + run: | + export SONAR_SCANNER_VERSION=4.2.0.1873 + export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux + rm -rf $SONAR_SCANNER_HOME + mkdir -p $SONAR_SCANNER_HOME + curl -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip + unzip $HOME/.sonar/sonar-sct Analysisanner.zip -d $HOME/.sonar/ + rm $HOME/.sonar/sonar-scanner.zip + export PATH=$SONAR_SCANNER_HOME/bin:$PATH + export SONAR_SCANNER_OPTS="-server" + sonar-scanner \ + -Dsonar.projectKey=vitessio \ + -Dsonar.organization=vitess \ + -Dsonar.host.url=https://sonarcloud.io \ + -Dsonar.login=${SONAR_TOKEN} \ + -Dsonar.go.coverage.reportPaths=report/*.out + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore index be8fe727562..a6b4a89918e 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,6 @@ releases /vthook/ /bin/ /vtdataroot/ + +.scannerwork +report \ No newline at end of file diff --git a/README.md b/README.md index bbe10c4c8c7..c541c544693 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Go Report Card](https://goreportcard.com/badge/vitess.io/vitess)](https://goreportcard.com/report/vitess.io/vitess) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fvitessio%2Fvitess.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fvitessio%2Fvitess?ref=badge_shield) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1724/badge)](https://bestpractices.coreinfrastructure.org/projects/1724) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=vitessio&metric=coverage)](https://sonarcloud.io/dashboard?id=vitessio) # Vitess diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index 20dea3b5aeb..3944a7bdb8f 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -39,6 +39,7 @@ const ( var ( keepData = flag.Bool("keep-data", false, "don't delete the per-test VTDATAROOT subfolders") topoFlavor = flag.String("topo-flavor", "etcd2", "choose a topo server from etcd2, zk2 or consul") + isCoverage = flag.Bool("is-coverage", false, "whether coverage is required") ) // LocalProcessCluster Testcases need to use this to iniate a cluster @@ -639,3 +640,11 @@ func (cluster *LocalProcessCluster) StartVttablet(tablet *Vttablet, servingStatu tablet.VttabletProcess.ServingStatus = servingStatus return tablet.VttabletProcess.Setup() } + +func getCoveragePath(fileName string) string { + covDir := os.Getenv("COV_DIR") + if covDir == "" { + covDir = os.TempDir() + } + return path.Join(covDir, fileName) +} diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 8ec5cee2344..c18ac2df15c 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -120,3 +120,18 @@ func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet { portMap["grpc"] = int32(tabletGrpcPort) return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap} } + +func filterResultWhenRunsForCoverage(input string) string { + lines := strings.Split(input, "\n") + var result string + for _, line := range lines { + if strings.Contains(line, "=== RUN") { + continue + } + if strings.Contains(line, "--- PASS:") || strings.Contains(line, "PASS") { + break + } + result = result + line + "\n" + } + return result +} diff --git a/go/test/endtoend/cluster/mysqlctl_process.go b/go/test/endtoend/cluster/mysqlctl_process.go index d7bf2f3e020..882a678cf6d 100644 --- a/go/test/endtoend/cluster/mysqlctl_process.go +++ b/go/test/endtoend/cluster/mysqlctl_process.go @@ -43,14 +43,17 @@ type MysqlctlProcess struct { // InitDb executes mysqlctl command to add cell info func (mysqlctl *MysqlctlProcess) InitDb() (err error) { - tmpProcess := exec.Command( - mysqlctl.Binary, - "-log_dir", mysqlctl.LogDirectory, + args := []string{"-log_dir", mysqlctl.LogDirectory, "-tablet_uid", fmt.Sprintf("%d", mysqlctl.TabletUID), "-mysql_port", fmt.Sprintf("%d", mysqlctl.MySQLPort), "init", - "-init_db_sql_file", mysqlctl.InitDBFile, - ) + "-init_db_sql_file", mysqlctl.InitDBFile} + if *isCoverage { + args = append([]string{"-test.coverprofile=" + getCoveragePath("mysql-initdb.out"), "-test.v"}, args...) + } + tmpProcess := exec.Command( + mysqlctl.Binary, + args...) return tmpProcess.Run() } @@ -71,6 +74,9 @@ func (mysqlctl *MysqlctlProcess) StartProcess() (*exec.Cmd, error) { "-tablet_uid", fmt.Sprintf("%d", mysqlctl.TabletUID), "-mysql_port", fmt.Sprintf("%d", mysqlctl.MySQLPort), ) + if *isCoverage { + tmpProcess.Args = append(tmpProcess.Args, []string{"-test.coverprofile=" + getCoveragePath("mysql-start.out")}...) + } if len(mysqlctl.ExtraArgs) > 0 { tmpProcess.Args = append(tmpProcess.Args, mysqlctl.ExtraArgs...) @@ -100,6 +106,9 @@ func (mysqlctl *MysqlctlProcess) StopProcess() (*exec.Cmd, error) { mysqlctl.Binary, "-tablet_uid", fmt.Sprintf("%d", mysqlctl.TabletUID), ) + if *isCoverage { + tmpProcess.Args = append(tmpProcess.Args, []string{"-test.coverprofile=" + getCoveragePath("mysql-stop.out")}...) + } if len(mysqlctl.ExtraArgs) > 0 { tmpProcess.Args = append(tmpProcess.Args, mysqlctl.ExtraArgs...) } diff --git a/go/test/endtoend/cluster/vtctl_process.go b/go/test/endtoend/cluster/vtctl_process.go index a8005e91200..cf75e24cd15 100644 --- a/go/test/endtoend/cluster/vtctl_process.go +++ b/go/test/endtoend/cluster/vtctl_process.go @@ -43,11 +43,15 @@ func (vtctl *VtctlProcess) AddCellInfo(Cell string) (err error) { "-topo_implementation", vtctl.TopoImplementation, "-topo_global_server_address", vtctl.TopoGlobalAddress, "-topo_global_root", vtctl.TopoGlobalRoot, + ) + if *isCoverage { + tmpProcess.Args = append(tmpProcess.Args, "-test.coverprofile="+getCoveragePath("vtctl-addcell.out")) + } + tmpProcess.Args = append(tmpProcess.Args, "AddCellInfo", "-root", vtctl.TopoRootPath+Cell, "-server_address", vtctl.TopoServerAddress, - Cell, - ) + Cell) log.Info(fmt.Sprintf("Adding Cell into Keyspace with arguments %v", strings.Join(tmpProcess.Args, " "))) fmt.Println(fmt.Sprintf("Adding Cell into Keyspace with arguments %v", strings.Join(tmpProcess.Args, " "))) return tmpProcess.Run() @@ -60,8 +64,12 @@ func (vtctl *VtctlProcess) CreateKeyspace(keyspace string) (err error) { "-topo_implementation", vtctl.TopoImplementation, "-topo_global_server_address", vtctl.TopoGlobalAddress, "-topo_global_root", vtctl.TopoGlobalRoot, - "CreateKeyspace", keyspace, ) + if *isCoverage { + tmpProcess.Args = append(tmpProcess.Args, "-test.coverprofile="+getCoveragePath("vtctl-create-ks.out")) + } + tmpProcess.Args = append(tmpProcess.Args, + "CreateKeyspace", keyspace) log.Info(fmt.Sprintf("Starting CreateKeyspace with arguments %v", strings.Join(tmpProcess.Args, " "))) return tmpProcess.Run() } @@ -73,13 +81,16 @@ func (vtctl *VtctlProcess) ExecuteCommandWithOutput(args ...string) (result stri "-topo_implementation", vtctl.TopoImplementation, "-topo_global_server_address", vtctl.TopoGlobalAddress, "-topo_global_root", vtctl.TopoGlobalRoot}, args...) + if *isCoverage { + args = append([]string{"-test.coverprofile=" + getCoveragePath("vtctl-o-"+args[0]+".out"), "-test.v"}, args...) + } tmpProcess := exec.Command( vtctl.Binary, args..., ) log.Info(fmt.Sprintf("Executing vtctlclient with arguments %v", strings.Join(tmpProcess.Args, " "))) resultByte, err := tmpProcess.CombinedOutput() - return string(resultByte), err + return filterResultWhenRunsForCoverage(string(resultByte)), err } // ExecuteCommand executes any vtctlclient command @@ -89,6 +100,9 @@ func (vtctl *VtctlProcess) ExecuteCommand(args ...string) (err error) { "-topo_implementation", vtctl.TopoImplementation, "-topo_global_server_address", vtctl.TopoGlobalAddress, "-topo_global_root", vtctl.TopoGlobalRoot}, args...) + if *isCoverage { + args = append([]string{"-test.coverprofile=" + getCoveragePath("vtctl-"+args[0]+".out"), "-test.v"}, args...) + } tmpProcess := exec.Command( vtctl.Binary, args..., diff --git a/go/test/endtoend/cluster/vtctlclient_process.go b/go/test/endtoend/cluster/vtctlclient_process.go index f5ae4adaf68..6b9f41619ac 100644 --- a/go/test/endtoend/cluster/vtctlclient_process.go +++ b/go/test/endtoend/cluster/vtctlclient_process.go @@ -62,10 +62,15 @@ func (vtctlclient *VtctlClientProcess) ApplyVSchema(Keyspace string, JSON string // ExecuteCommand executes any vtctlclient command func (vtctlclient *VtctlClientProcess) ExecuteCommand(args ...string) (err error) { - args = append([]string{"-server", vtctlclient.Server}, args...) + pArgs := []string{"-server", vtctlclient.Server} + + if *isCoverage { + pArgs = append(pArgs, "-test.coverprofile="+getCoveragePath("vtctlclient-"+args[0]+".out"), "-test.v") + } + pArgs = append(pArgs, args...) tmpProcess := exec.Command( vtctlclient.Binary, - args..., + pArgs..., ) println(fmt.Sprintf("Executing vtctlclient with arguments %v", strings.Join(tmpProcess.Args, " "))) log.Info(fmt.Sprintf("Executing vtctlclient with arguments %v", strings.Join(tmpProcess.Args, " "))) @@ -74,15 +79,19 @@ func (vtctlclient *VtctlClientProcess) ExecuteCommand(args ...string) (err error // ExecuteCommandWithOutput executes any vtctlclient command and returns output func (vtctlclient *VtctlClientProcess) ExecuteCommandWithOutput(args ...string) (result string, err error) { - args = append([]string{"-server", vtctlclient.Server}, args...) + pArgs := []string{"-server", vtctlclient.Server} + if *isCoverage { + pArgs = append(pArgs, "-test.coverprofile="+getCoveragePath("vtctlclient-"+args[0]+".out"), "-test.v") + } + pArgs = append(pArgs, args...) tmpProcess := exec.Command( vtctlclient.Binary, - args..., + pArgs..., ) println(fmt.Sprintf("Executing vtctlclient with arguments %v", strings.Join(tmpProcess.Args, " "))) log.Info(fmt.Sprintf("Executing vtctlclient with arguments %v", strings.Join(tmpProcess.Args, " "))) resultByte, err := tmpProcess.CombinedOutput() - return string(resultByte), err + return filterResultWhenRunsForCoverage(string(resultByte)), err } // VtctlClientProcessInstance returns a VtctlProcess handle for vtctlclient process diff --git a/go/test/endtoend/cluster/vtctld_process.go b/go/test/endtoend/cluster/vtctld_process.go index 89a3ef799d6..28690cfd1e6 100644 --- a/go/test/endtoend/cluster/vtctld_process.go +++ b/go/test/endtoend/cluster/vtctld_process.go @@ -70,6 +70,9 @@ func (vtctld *VtctldProcess) Setup(cell string, extraArgs ...string) (err error) "-grpc_port", fmt.Sprintf("%d", vtctld.GrpcPort), "-pid_file", vtctld.PidFile, ) + if *isCoverage { + vtctld.proc.Args = append(vtctld.proc.Args, "-test.coverprofile="+getCoveragePath("vtctld.out")) + } vtctld.proc.Args = append(vtctld.proc.Args, extraArgs...) errFile, _ := os.Create(path.Join(vtctld.LogDir, "vtctld-stderr.txt")) diff --git a/go/test/endtoend/cluster/vtgate_process.go b/go/test/endtoend/cluster/vtgate_process.go index 12dad980510..1c8a830bc13 100644 --- a/go/test/endtoend/cluster/vtgate_process.go +++ b/go/test/endtoend/cluster/vtgate_process.go @@ -83,6 +83,10 @@ func (vtgate *VtgateProcess) Setup() (err error) { "-mysql_auth_server_impl", vtgate.MySQLAuthServerImpl, "-pid_file", vtgate.PidFile, ) + if *isCoverage { + vtgate.proc.Args = append(vtgate.proc.Args, "-test.coverprofile="+getCoveragePath("vtgate.out")) + } + vtgate.proc.Args = append(vtgate.proc.Args, vtgate.ExtraArgs...) errFile, _ := os.Create(path.Join(vtgate.LogDir, "vtgate-stderr.txt")) diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index f4a98715d12..6e4aaaac303 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -99,6 +99,9 @@ func (vttablet *VttabletProcess) Setup() (err error) { "-service_map", vttablet.ServiceMap, "-vtctld_addr", vttablet.VtctldAddress, ) + if *isCoverage { + vttablet.proc.Args = append(vttablet.proc.Args, "-test.coverprofile="+getCoveragePath("vttablet.out")) + } if vttablet.SupportsBackup { vttablet.proc.Args = append(vttablet.proc.Args, "-restore_from_backup") diff --git a/go/test/endtoend/cluster/vtworker_process.go b/go/test/endtoend/cluster/vtworker_process.go index 46ab0fb715b..273992f6436 100644 --- a/go/test/endtoend/cluster/vtworker_process.go +++ b/go/test/endtoend/cluster/vtworker_process.go @@ -68,6 +68,9 @@ func (vtworker *VtworkerProcess) Setup(cell string) (err error) { "-cell", cell, "-command_display_interval", "10ms", ) + if *isCoverage { + vtworker.proc.Args = append(vtworker.proc.Args, "-test.coverprofile=vtworker.out", "-test.v") + } vtworker.proc.Args = append(vtworker.proc.Args, vtworker.ExtraArgs...) vtworker.proc.Stderr = os.Stderr @@ -140,6 +143,9 @@ func (vtworker *VtworkerProcess) TearDown() error { func (vtworker *VtworkerProcess) ExecuteCommand(args ...string) (err error) { args = append([]string{"-vtworker_client_protocol", "grpc", "-server", vtworker.Server, "-log_dir", vtworker.LogDir, "-stderrthreshold", "info"}, args...) + if *isCoverage { + args = append([]string{"-test.coverprofile=" + getCoveragePath("vtworkerclient-exec-cmd.out")}, args...) + } tmpProcess := exec.Command( "vtworkerclient", args..., @@ -162,6 +168,9 @@ func (vtworker *VtworkerProcess) ExecuteVtworkerCommand(port int, grpcPort int, "-grpc_port", fmt.Sprintf("%d", grpcPort), "-cell", vtworker.Cell, "-log_dir", vtworker.LogDir, "-stderrthreshold", "1"}, args...) + if *isCoverage { + args = append([]string{"-test.coverprofile=" + getCoveragePath("vtworker-exec-cmd.out")}, args...) + } tmpProcess := exec.Command( "vtworker", args..., diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000000..7ae2bcf9864 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,12 @@ +sonar.projectKey=vitessio +sonar.projectName=vitessio +sonar.sources=go +sonar.exclusions=**/*_test.go,**/vendor/**,**/java/**,go/test/endtoend/**,**/*.pb.go +sonar.tests=. +sonar.test.inclusions=**/*_test.go +sonar.test.exclusions=**/vendor/** + + +# Change the host.url to point to the +# sonarqube server (default localhost) +sonar.host.url=http://localhost:9000 diff --git a/tools/all_test_for_coverage.sh b/tools/all_test_for_coverage.sh new file mode 100755 index 00000000000..434ef60125a --- /dev/null +++ b/tools/all_test_for_coverage.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# These test uses excutables and launch them as process +# After that all tests run, here we are testing those + +# All Go packages with test files. +# Output per line: * + + +### Execute unit testcase ### +source build.env +make tools +make build +echo "--------- executing unit testcases ---------" +packages_with_all_tests=$(go list -f '{{if len .TestGoFiles}}{{.ImportPath}} {{join .TestGoFiles " "}}{{end}}' ./go/... | sort) +all_except_endtoend_tests=$(echo "$packages_with_all_tests" | grep -v "endtoend" | cut -d" " -f1 ) + +counter=0 +for pkg in $all_except_endtoend_tests +do + go test -coverpkg=vitess.io/vitess/go/... -coverprofile "/tmp/unit_$counter.out" $pkg -v -p=1 || : + counter=$((counter+1)) +done + +## Copy the test files to get instrumented binaries ### +cp ./tools/coverage-go/vtctl_test.go ./go/cmd/vtctl/vtctl_test.go +cp ./tools/coverage-go/vtctld_test.go ./go/cmd/vtctld/vtctld_test.go +cp ./tools/coverage-go/mysqlctl_test.go ./go/cmd/mysqlctl/mysqlctl_test.go +cp ./tools/coverage-go/vtctlclient_test.go ./go/cmd/vtctlclient/vtctlclient_test.go +cp ./tools/coverage-go/vttablet_test.go ./go/cmd/vttablet/vttablet_test.go +cp ./tools/coverage-go/vtgate_test.go ./go/cmd/vtgate/vtgate_test.go +cp ./tools/coverage-go/vtworker_test.go ./go/cmd/vtworker/vtworker_test.go +cp ./tools/coverage-go/vtworkerclient_test.go ./go/cmd/vtworkerclient/vtworkerclient_test.go + +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/vtctl -o ./bin/vtctl +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/vtctld -o ./bin/vtctld +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/mysqlctl -o ./bin/mysqlctl +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/vtctlclient -o ./bin/vtctlclient +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/vttablet -o ./bin/vttablet +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/vtgate -o ./bin/vtgate +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/vtworker -o ./bin/vtworker +go test -coverpkg=vitess.io/vitess/go/... -c vitess.io/vitess/go/cmd/vtworkerclient -o ./bin/vtworkerclient + +### Execute go/test/endtoend testcase ### +echo "--------- executing endtoend testcases ---------" +cluster_tests=$(echo "$packages_with_all_tests" | grep -E "go/test/endtoend" | cut -d" " -f1) + + +# Run cluster test sequentially +for i in $cluster_tests +do + echo "starting test for $i" + go test $i -v -p=1 -is-coverage=true || : +done + diff --git a/tools/coverage-go/Readme.md b/tools/coverage-go/Readme.md new file mode 100644 index 00000000000..58d27615d7a --- /dev/null +++ b/tools/coverage-go/Readme.md @@ -0,0 +1,6 @@ +#### Purpose + +- To get the coverage when we run the end to end testcase,we need instrumented binaries. +- To get such binaries, we have to put a test file under go/cmd/, so that if we execute `go test ... -c .. -o ..` command, it will produce such files. +- This directory contains the test files which will copied to go/cmd/ via some script and then it instrumented binaries will be produced. +- The end to end test can be configured to run in coverage mode, which will utilize the binaries to produce coverage report. \ No newline at end of file diff --git a/tools/coverage-go/mysqlctl_test.go b/tools/coverage-go/mysqlctl_test.go new file mode 100644 index 00000000000..8015ad4c2de --- /dev/null +++ b/tools/coverage-go/mysqlctl_test.go @@ -0,0 +1,22 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import "testing" + +func TestMysqlCtl(t *testing.T) { + main() +} diff --git a/tools/coverage-go/vtctl_test.go b/tools/coverage-go/vtctl_test.go new file mode 100644 index 00000000000..497d0b21622 --- /dev/null +++ b/tools/coverage-go/vtctl_test.go @@ -0,0 +1,24 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "testing" +) + +func TestVtctl(t *testing.T) { + main() +} diff --git a/tools/coverage-go/vtctlclient_test.go b/tools/coverage-go/vtctlclient_test.go new file mode 100644 index 00000000000..4e78872b67a --- /dev/null +++ b/tools/coverage-go/vtctlclient_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func TestVtclient(t *testing.T) { + main() +} diff --git a/tools/coverage-go/vtctld_test.go b/tools/coverage-go/vtctld_test.go new file mode 100644 index 00000000000..bf3704696a2 --- /dev/null +++ b/tools/coverage-go/vtctld_test.go @@ -0,0 +1,22 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import "testing" + +func TestVtctld(t *testing.T) { + main() +} diff --git a/tools/coverage-go/vtgate_test.go b/tools/coverage-go/vtgate_test.go new file mode 100644 index 00000000000..57d030895f4 --- /dev/null +++ b/tools/coverage-go/vtgate_test.go @@ -0,0 +1,22 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import "testing" + +func TestVtgate(t *testing.T) { + main() +} diff --git a/tools/coverage-go/vttablet_test.go b/tools/coverage-go/vttablet_test.go new file mode 100644 index 00000000000..2fc3525ad11 --- /dev/null +++ b/tools/coverage-go/vttablet_test.go @@ -0,0 +1,25 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" +) + +func TestVttablet(t *testing.T) { + main() +} diff --git a/tools/coverage-go/vtworker_test.go b/tools/coverage-go/vtworker_test.go new file mode 100644 index 00000000000..1a077e21b4a --- /dev/null +++ b/tools/coverage-go/vtworker_test.go @@ -0,0 +1,22 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import "testing" + +func TestVtworker(t *testing.T) { + main() +} diff --git a/tools/coverage-go/vtworkerclient_test.go b/tools/coverage-go/vtworkerclient_test.go new file mode 100644 index 00000000000..e1e6fa1ce0d --- /dev/null +++ b/tools/coverage-go/vtworkerclient_test.go @@ -0,0 +1,22 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import "testing" + +func TestVtworkerclient(t *testing.T) { + main() +} From a157f15d4bc954c75378ca0d37c156c0050f098b Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Mon, 13 Jan 2020 18:42:39 +0530 Subject: [PATCH 015/825] Update sonar_analysis.yml Signed-off-by: Arindam Nayak --- .github/workflows/sonar_analysis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sonar_analysis.yml b/.github/workflows/sonar_analysis.yml index 5f604240732..6c91f4b673b 100644 --- a/.github/workflows/sonar_analysis.yml +++ b/.github/workflows/sonar_analysis.yml @@ -40,16 +40,21 @@ jobs: rm -rf $SONAR_SCANNER_HOME mkdir -p $SONAR_SCANNER_HOME curl -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip - unzip $HOME/.sonar/sonar-sct Analysisanner.zip -d $HOME/.sonar/ + unzip $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ rm $HOME/.sonar/sonar-scanner.zip export PATH=$SONAR_SCANNER_HOME/bin:$PATH export SONAR_SCANNER_OPTS="-server" + + curl -LsS https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip > build-wrapper-linux-x86.zip + unzip build-wrapper-linux-x86.zip + build-wrapper-linux-x86-64 --out-dir bw-output make clean all sonar-scanner \ -Dsonar.projectKey=vitessio \ -Dsonar.organization=vitess \ -Dsonar.host.url=https://sonarcloud.io \ -Dsonar.login=${SONAR_TOKEN} \ -Dsonar.go.coverage.reportPaths=report/*.out + -Dsonar.cfamily.build-wrapper-output=bw-output env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 25d2a29ea7bc0d67ded586d227ecefde020c155c Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Tue, 14 Jan 2020 11:56:43 +0530 Subject: [PATCH 016/825] Fix coverage flag check Signed-off-by: Arindam Nayak --- go/test/endtoend/cluster/cluster_util.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index c18ac2df15c..99a3bcb4485 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -122,6 +122,9 @@ func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet { } func filterResultWhenRunsForCoverage(input string) string { + if !*isCoverage { + return input + } lines := strings.Split(input, "\n") var result string for _, line := range lines { From fa0a8c1ca41137955c3ee29d6c56652c0e04999e Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Wed, 22 Jan 2020 12:17:19 +0530 Subject: [PATCH 017/825] updated script comment Signed-off-by: Arindam Nayak --- tools/all_test_for_coverage.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/all_test_for_coverage.sh b/tools/all_test_for_coverage.sh index 434ef60125a..e01298f185b 100755 --- a/tools/all_test_for_coverage.sh +++ b/tools/all_test_for_coverage.sh @@ -14,11 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -# These test uses excutables and launch them as process -# After that all tests run, here we are testing those - -# All Go packages with test files. -# Output per line: * +# The purpose of this script is to run testcase and get coverage report +# These script runs all unit testcases including go endtoend testcase +# Here we ignore any error from testcase as the purpose is to collect coverage. +# So if there is a flaky test, it will get only chance to run, if it fails we ignore coverage from that. ### Execute unit testcase ### From 9ba943cb9d93996406af84d311fc2b862fb5efc0 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Thu, 30 Jan 2020 18:05:19 +0530 Subject: [PATCH 018/825] messaging: cluster launch completed. Signed-off-by: pradip parmar --- go/test/endtoend/messaging/main_test.go | 133 ++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 go/test/endtoend/messaging/main_test.go diff --git a/go/test/endtoend/messaging/main_test.go b/go/test/endtoend/messaging/main_test.go new file mode 100644 index 00000000000..74bd0ef6049 --- /dev/null +++ b/go/test/endtoend/messaging/main_test.go @@ -0,0 +1,133 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package messaging + +import ( + "flag" + "fmt" + "os" + "testing" + + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + hostname = "localhost" + keyspaceName = "test_keyspace" + testingID = 1 + tableName = "vt_prepare_stmt_test" + cell = "zone1" + createShardedMessage = `create table sharded_message( + time_scheduled bigint, + id bigint, + time_next bigint, + epoch bigint, + time_created bigint, + time_acked bigint, + message varchar(128), + primary key(time_scheduled, id), + unique index id_idx(id), + index next_idx(time_next, epoch) + ) comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` + createUnshardedMessage = `create table unsharded_message( + time_scheduled bigint, + id bigint, + time_next bigint, + epoch bigint, + time_created bigint, + time_acked bigint, + message varchar(128), + primary key(time_scheduled, id), + unique index id_idx(id), + index next_idx(time_next, epoch) + ) comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` + userVschema = `{ + "sharded": true, + "vindexes": { + "hash_index": { + "type": "hash" + } + }, + "tables": { + "sharded_message": { + "column_vindexes": [ + { + "column": "id", + "name": "hash_index" + } + ] + } + } + }` + lookupVschema = `{ + "sharded": false, + "tables": { + "unsharded_message": { + "type": "sequence" + } + } + }` +) + +func TestMain(m *testing.M) { + flag.Parse() + + exitcode, err := func() (int, error) { + clusterInstance = &cluster.LocalProcessCluster{Cell: cell, Hostname: hostname} + defer clusterInstance.Teardown() + + // Start topo server + if err := clusterInstance.StartTopo(); err != nil { + return 1, err + } + + // Start unsharded keyspace + keyspace := &cluster.Keyspace{ + Name: "lookup", + SchemaSQL: createUnshardedMessage, + VSchema: lookupVschema, + } + if err := clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil { + return 1, err + } + + // Start sharded keyspace + keyspace = &cluster.Keyspace{ + Name: "user", + SchemaSQL: createShardedMessage, + VSchema: userVschema, + } + if err := clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 1, false); err != nil { + return 1, err + } + + // Start vtgate + if err := clusterInstance.StartVtgate(); err != nil { + return 1, err + } + + return m.Run(), nil + }() + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } else { + os.Exit(exitcode) + } + +} From e3ed6c0c2526d5a5ae1974911c9dbe6167f6646f Mon Sep 17 00:00:00 2001 From: Jacques Grove Date: Thu, 30 Jan 2020 13:40:05 -0800 Subject: [PATCH 019/825] Do the minimum to fix issue #5725; adds handling for privilege statements (GRANT/REVOKE) in the vstreamer; but does not add any handling for vtgate. Signed-off-by: Jacques Grove --- go/vt/sqlparser/analyzer.go | 5 +++++ go/vt/sqlparser/analyzer_test.go | 2 ++ go/vt/vttablet/tabletserver/vstreamer/vstreamer.go | 7 +++++-- go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go | 8 +++++--- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/go/vt/sqlparser/analyzer.go b/go/vt/sqlparser/analyzer.go index 69f8bd2621f..3d78e3e993d 100644 --- a/go/vt/sqlparser/analyzer.go +++ b/go/vt/sqlparser/analyzer.go @@ -52,6 +52,7 @@ const ( StmtOther StmtUnknown StmtComment + StmtPriv ) // Preview analyzes the beginning of the query using a simpler and faster @@ -110,6 +111,8 @@ func Preview(sql string) StatementType { return StmtUse case "analyze", "describe", "desc", "explain", "repair", "optimize": return StmtOther + case "grant", "revoke": + return StmtPriv } return StmtUnknown } @@ -144,6 +147,8 @@ func (s StatementType) String() string { return "USE" case StmtOther: return "OTHER" + case StmtPriv: + return "PRIV" default: return "UNKNOWN" } diff --git a/go/vt/sqlparser/analyzer_test.go b/go/vt/sqlparser/analyzer_test.go index c73f691632d..b140703849f 100644 --- a/go/vt/sqlparser/analyzer_test.go +++ b/go/vt/sqlparser/analyzer_test.go @@ -71,6 +71,8 @@ func TestPreview(t *testing.T) { {"explain", StmtOther}, {"repair", StmtOther}, {"optimize", StmtOther}, + {"grant", StmtPriv}, + {"revoke", StmtPriv}, {"truncate", StmtDDL}, {"unknown", StmtUnknown}, diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 24c0a459351..d60e56bbc99 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -408,8 +408,11 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e // If the DDL adds a column, comparing with an older snapshot of the // schema will make us think that a column was dropped and error out. vs.se.Reload(vs.ctx) - case sqlparser.StmtOther: - // These are DBA statements like REPAIR that can be ignored. + case sqlparser.StmtOther, sqlparser.StmtPriv: + // These are either: + // 1) DBA statements like REPAIR that can be ignored. + // 2) Privilege-altering statements like GRANT/REVOKE + // that we want to keep out of the stream for now. vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_GTID, Gtid: mysql.EncodePosition(vs.pos), diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index c610e2a3d9d..02cdc496b9e 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -121,9 +121,9 @@ func TestStatements(t *testing.T) { runCases(t, nil, testcases, "current") } -// TestOther tests "other" statements. These statements produce -// very different events depending on the version of mysql or mariadb -// So, we just show that vreplication transmits "OTHER" events +// TestOther tests "other" and "priv" statements. These statements can +// produce very different events depending on the version of mysql or +// mariadb. So, we just show that vreplication transmits "OTHER" events // if the binlog is affected by the statement. func TestOther(t *testing.T) { if testing.Short() { @@ -148,6 +148,8 @@ func TestOther(t *testing.T) { "set @val=1", "show tables", "describe stream1", + "grant select on stream1 to current_user()", + "revoke select on stream1 from current_user()", } // customRun is a modified version of runCases. From c32b5ccf7bc1e78860a34874e3a11e988f413e72 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Fri, 31 Jan 2020 16:05:38 +0530 Subject: [PATCH 020/825] messaging: some changes in main test. Signed-off-by: pradip parmar --- go/test/endtoend/messaging/main_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/go/test/endtoend/messaging/main_test.go b/go/test/endtoend/messaging/main_test.go index 74bd0ef6049..21da2592dbc 100644 --- a/go/test/endtoend/messaging/main_test.go +++ b/go/test/endtoend/messaging/main_test.go @@ -27,11 +27,17 @@ import ( var ( clusterInstance *cluster.LocalProcessCluster + shard0Master *cluster.Vttablet + shard0Replica *cluster.Vttablet + shard1Master *cluster.Vttablet + lookupMaster *cluster.Vttablet hostname = "localhost" keyspaceName = "test_keyspace" testingID = 1 tableName = "vt_prepare_stmt_test" cell = "zone1" + userKeyspace = "user" + lookupKeyspace = "lookup" createShardedMessage = `create table sharded_message( time_scheduled bigint, id bigint, @@ -98,7 +104,7 @@ func TestMain(m *testing.M) { // Start unsharded keyspace keyspace := &cluster.Keyspace{ - Name: "lookup", + Name: lookupKeyspace, SchemaSQL: createUnshardedMessage, VSchema: lookupVschema, } @@ -108,7 +114,7 @@ func TestMain(m *testing.M) { // Start sharded keyspace keyspace = &cluster.Keyspace{ - Name: "user", + Name: userKeyspace, SchemaSQL: createShardedMessage, VSchema: userVschema, } @@ -121,6 +127,11 @@ func TestMain(m *testing.M) { return 1, err } + shard0Master = clusterInstance.Keyspaces[1].Shards[0].MasterTablet() + shard1Master = clusterInstance.Keyspaces[1].Shards[1].MasterTablet() + lookupMaster = clusterInstance.Keyspaces[0].Shards[0].MasterTablet() + shard0Replica = clusterInstance.Keyspaces[1].Shards[0].Replica() + return m.Run(), nil }() if err != nil { From ed88f2808c5104c6e5130724ddceec08913d9fea Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 1 Feb 2020 09:12:35 -0700 Subject: [PATCH 021/825] Add a go fmt check Signed-off-by: Morgan Tocker --- .github/workflows/check_formatting.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/check_formatting.yml diff --git a/.github/workflows/check_formatting.yml b/.github/workflows/check_formatting.yml new file mode 100644 index 00000000000..7233082da38 --- /dev/null +++ b/.github/workflows/check_formatting.yml @@ -0,0 +1,21 @@ +name: check_formatting +on: [pull_request] +jobs: + + build: + name: Check Formatting + runs-on: ubuntu-latest + steps: + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.13 + + - name: Check out code + uses: actions/checkout@v2 + + - name: Run go fmt + run: | + gofmt -l . | grep -vF vendor/ && exit 1 || echo "All files formatted correctly" + From e6120980f626227701f468bfdbcf3456c707cf89 Mon Sep 17 00:00:00 2001 From: deepthi Date: Sat, 1 Feb 2020 08:12:36 -0800 Subject: [PATCH 022/825] populate local_metadata after agent has been initialized Signed-off-by: deepthi --- .../tabletmanager/tablet_health_test.go | 1 + go/test/endtoend/tabletmanager/tablet_test.go | 88 +++++++++++++++++++ go/vt/vttablet/tabletmanager/action_agent.go | 15 +++- go/vt/vttablet/tabletmanager/init_tablet.go | 23 ++--- .../tabletmanager/init_tablet_test.go | 13 --- 5 files changed, 108 insertions(+), 32 deletions(-) create mode 100644 go/test/endtoend/tabletmanager/tablet_test.go diff --git a/go/test/endtoend/tabletmanager/tablet_health_test.go b/go/test/endtoend/tabletmanager/tablet_health_test.go index f6a6f180be2..82c0c9ba011 100644 --- a/go/test/endtoend/tabletmanager/tablet_health_test.go +++ b/go/test/endtoend/tabletmanager/tablet_health_test.go @@ -261,6 +261,7 @@ func TestIgnoreHealthError(t *testing.T) { tablet := clusterInstance.GetVttabletInstance("replica", 0, "") tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory) err := tablet.MysqlctlProcess.Start() + assert.Nil(t, err) // start vttablet process tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort, diff --git a/go/test/endtoend/tabletmanager/tablet_test.go b/go/test/endtoend/tabletmanager/tablet_test.go new file mode 100644 index 00000000000..4f7b7b03bc8 --- /dev/null +++ b/go/test/endtoend/tabletmanager/tablet_test.go @@ -0,0 +1,88 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tabletmanager + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/vt/log" +) + +// TestLocalMetadata tests the contents of local_metadata table after vttablet startup +func TestLocalMetadata(t *testing.T) { + // by default tablets are started with -restore_from_backup + // so metadata should exist + cluster.VerifyLocalMetadata(t, &replicaTablet, keyspaceName, shardName, cell) + + // Create new tablet + rTablet := clusterInstance.GetVttabletInstance("replica", 0, "") + + // Init Tablet + err := clusterInstance.VtctlclientProcess.InitTablet(rTablet, cell, keyspaceName, hostname, shardName) + require.NoError(t, err) + + clusterInstance.VtTabletExtraArgs = []string{ + "-lock_tables_timeout", "5s", + "-init_populate_metadata", + } + rTablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(rTablet.TabletUID, rTablet.MySQLPort, clusterInstance.TmpDirectory) + err = rTablet.MysqlctlProcess.Start() + assert.Nil(t, err) + + log.Info(fmt.Sprintf("Started vttablet %v", rTablet)) + // SupportsBackup=False prevents vttablet from trying to restore + // Start vttablet process + err = clusterInstance.StartVttablet(rTablet, "SERVING", false, cell, keyspaceName, hostname, shardName) + assert.Nil(t, err) + + cluster.VerifyLocalMetadata(t, rTablet, keyspaceName, shardName, cell) + + // Create another new tablet + rTablet2 := clusterInstance.GetVttabletInstance("replica", 0, "") + + // Init Tablet + err = clusterInstance.VtctlclientProcess.InitTablet(rTablet2, cell, keyspaceName, hostname, shardName) + require.NoError(t, err) + + // start with -init_populate_metadata false (default) + clusterInstance.VtTabletExtraArgs = []string{ + "-lock_tables_timeout", "5s", + } + rTablet2.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(rTablet2.TabletUID, rTablet2.MySQLPort, clusterInstance.TmpDirectory) + err = rTablet2.MysqlctlProcess.Start() + assert.Nil(t, err) + + log.Info(fmt.Sprintf("Started vttablet %v", rTablet2)) + // SupportsBackup=False prevents vttablet from trying to restore + // Start vttablet process + err = clusterInstance.StartVttablet(rTablet2, "SERVING", false, cell, keyspaceName, hostname, shardName) + assert.Nil(t, err) + + // check that tablet did _not_ get populated + qr, err := rTablet2.VttabletProcess.QueryTablet("select * from _vt.local_metadata", keyspaceName, false) + assert.Nil(t, err) + assert.Nil(t, qr.Rows) + + // Reset the VtTabletExtraArgs and kill tablets + clusterInstance.VtTabletExtraArgs = []string{} + killTablets(t, rTablet, rTablet2) +} diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index 8c1d54b21ff..5382d9006c8 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -80,8 +80,9 @@ const ( ) var ( - tabletHostname = flag.String("tablet_hostname", "", "if not empty, this hostname will be assumed instead of trying to resolve it") - demoteMasterType = flag.String("demote_master_type", "REPLICA", "the tablet type a demoted master will transition to") + tabletHostname = flag.String("tablet_hostname", "", "if not empty, this hostname will be assumed instead of trying to resolve it") + demoteMasterType = flag.String("demote_master_type", "REPLICA", "the tablet type a demoted master will transition to") + initPopulateMetadata = flag.Bool("init_populate_metadata", false, "(init parameter) populate metadata tables") ) // ActionAgent is the main class for the agent. @@ -355,6 +356,16 @@ func NewActionAgent( agent.initHealthCheck() }() } else { + // optionally populate metadata records + if *initPopulateMetadata { + // we can use agent.initialTablet here because it is set by Start() + localMetadata := agent.getLocalMetadataValues(agent.initialTablet.Type) + err := mysqlctl.PopulateMetadataTables(agent.MysqlDaemon, localMetadata, topoproto.TabletDbName(agent.initialTablet)) + if err != nil { + return nil, vterrors.Wrap(err, "failed to -init_populate_metadata") + } + } + // Update our state (need the action lock). if err := agent.lock(batchCtx); err != nil { return nil, err diff --git a/go/vt/vttablet/tabletmanager/init_tablet.go b/go/vt/vttablet/tabletmanager/init_tablet.go index 0bf7ae9504f..f65a4864e15 100644 --- a/go/vt/vttablet/tabletmanager/init_tablet.go +++ b/go/vt/vttablet/tabletmanager/init_tablet.go @@ -30,7 +30,6 @@ import ( "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" - "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/topotools" @@ -40,13 +39,12 @@ import ( ) var ( - initDbNameOverride = flag.String("init_db_name_override", "", "(init parameter) override the name of the db used by vttablet. Without this flag, the db name defaults to vt_") - initKeyspace = flag.String("init_keyspace", "", "(init parameter) keyspace to use for this tablet") - initShard = flag.String("init_shard", "", "(init parameter) shard to use for this tablet") - initTags flagutil.StringMapValue - initTabletType = flag.String("init_tablet_type", "", "(init parameter) the tablet type to use for this tablet.") - initTimeout = flag.Duration("init_timeout", 1*time.Minute, "(init parameter) timeout to use for the init phase.") - initPopulateMetadata = flag.Bool("init_populate_metadata", false, "(init parameter) populate metadata tables") + initDbNameOverride = flag.String("init_db_name_override", "", "(init parameter) override the name of the db used by vttablet. Without this flag, the db name defaults to vt_") + initKeyspace = flag.String("init_keyspace", "", "(init parameter) keyspace to use for this tablet") + initShard = flag.String("init_shard", "", "(init parameter) shard to use for this tablet") + initTags flagutil.StringMapValue + initTabletType = flag.String("init_tablet_type", "", "(init parameter) the tablet type to use for this tablet.") + initTimeout = flag.Duration("init_timeout", 1*time.Minute, "(init parameter) timeout to use for the init phase.") ) func init() { @@ -248,14 +246,5 @@ func (agent *ActionAgent) InitTablet(port, gRPCPort int32) error { } agent.setTablet(tablet) - // optionally populate metadata records - if *initPopulateMetadata { - localMetadata := agent.getLocalMetadataValues(tablet.Type) - err := mysqlctl.PopulateMetadataTables(agent.MysqlDaemon, localMetadata, topoproto.TabletDbName(tablet)) - if err != nil { - return vterrors.Wrap(err, "failed to -init_populate_metadata") - } - } - return nil } diff --git a/go/vt/vttablet/tabletmanager/init_tablet_test.go b/go/vt/vttablet/tabletmanager/init_tablet_test.go index cac9c0a0673..409fb6d77c9 100644 --- a/go/vt/vttablet/tabletmanager/init_tablet_test.go +++ b/go/vt/vttablet/tabletmanager/init_tablet_test.go @@ -26,7 +26,6 @@ import ( "vitess.io/vitess/go/history" "vitess.io/vitess/go/mysql/fakesqldb" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/mysqlctl/fakemysqldaemon" "vitess.io/vitess/go/vt/topo" @@ -173,17 +172,6 @@ func TestInitTablet(t *testing.T) { } db := fakesqldb.New(t) defer db.Close() - db.AddQueryPattern(`(SET|CREATE|BEGIN|INSERT|COMMIT|ALTER|UPDATE)\b.*`, &sqltypes.Result{}) - /* - db.AddQuery("SET @@session.sql_log_bin = 0", &sqltypes.Result{}) - db.AddQuery("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.local_metadata.*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.shard_metadata.*`, &sqltypes.Result{}) - db.AddQuery("BEGIN", &sqltypes.Result{}) - db.AddQueryPattern(`INSERT INTO _vt.local_metadata.*`, &sqltypes.Result{}) - db.AddQueryPattern(`INSERT INTO _vt.shard_metadata.*`, &sqltypes.Result{}) - db.AddQuery("COMMIT", &sqltypes.Result{}) - */ // start with a tablet record that doesn't exist port := int32(1234) @@ -209,7 +197,6 @@ func TestInitTablet(t *testing.T) { *initKeyspace = "test_keyspace" *initShard = "-C0" *initTabletType = "replica" - *initPopulateMetadata = true tabletAlias = &topodatapb.TabletAlias{ Cell: "cell1", Uid: 2, From 5ec624efc33d41cdc303ab36e39f6e8c008f39ac Mon Sep 17 00:00:00 2001 From: deepthi Date: Sat, 1 Feb 2020 08:30:55 -0800 Subject: [PATCH 023/825] remove non-functional pylint hook Signed-off-by: deepthi --- misc/git/hooks/pylint | 91 ------------------------------------------- 1 file changed, 91 deletions(-) delete mode 100755 misc/git/hooks/pylint diff --git a/misc/git/hooks/pylint b/misc/git/hooks/pylint deleted file mode 100755 index 35f04cee37c..00000000000 --- a/misc/git/hooks/pylint +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# This file is based on the Go linter precommit hook -# "golint". Therefore, both files are very similar. - -# misc/git/hooks/pylint -# -# To use, store as .git/hooks/pre-commit inside your repository and make sure -# it has execute permissions. - -function msg() { - echo "$(date) $@" -} - -PYLINT=${PYLINT:-/usr/bin/gpylint} -pylint_script=$VTROOT/tools/pylint.sh - -# This script does not handle file names that contain spaces. -pyfiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.*\.py$' | grep -v '^py/vtproto/') -if [ -z "$pyfiles" ] ; then - msg "No python files changed." - exit 0 -fi - -if [ ! -x $PYLINT ] ; then - msg "PYLINT=$PYLINT not found." - msg "Please double-check your python code for lint errors." - exit 0 -fi - -errors= - -# Run on one file at a time because a single invocation of $PYLINT -# with multiple files requires the files to all be in one package. -pyfiles_with_warnings=() -for pyfile in $pyfiles -do - errcount=$($pylint_script $pyfile | egrep '^[^:]+:[^:]+:[CWE][0-9]{4}:' | wc -l) - if [ "$errcount" -gt "0" ]; then - if [ -z "$errors" ] ; then - msg "$PYLINT found one or more issues:" - errors=YES - fi - echo " $pyfile: $errcount issues" - pyfiles_with_warnings+=($pyfile) - fi -done - -[ -z "$errors" ] && exit 0 - -# git doesn't give us access to user input, so let's steal it. -exec < /dev/tty -if [[ $? -eq 0 ]]; then - # interactive shell. Prompt the user. - - for pyfile in "${pyfiles_with_warnings[@]}" - do - echo - msg "Press enter to show the warnings for $pyfile:" - read -p " \$VTROOT/tools/pylint.sh $pyfile" - $pylint_script $pyfile - done - read -r -p \ - 'Type "ack" to ignore issues and commit anyway. Press enter to cancel: ' - if [ "$REPLY" = "ack" ]; then - exit 0 - else - msg "Please try to fix $PYLINT issues before committing." - exit 1 - fi -else - # non-interactive shell (e.g. called from Eclipse). Just display the errors. - for pyfile in "${pyfiles_with_warnings[@]}" - do - $pylint_script $pyfile - done -fi -exit 1 From 5ca63b351700435bde8f6703ea29befee5d33a32 Mon Sep 17 00:00:00 2001 From: deepthi Date: Sat, 1 Feb 2020 08:34:40 -0800 Subject: [PATCH 024/825] correct email address Signed-off-by: deepthi --- MAINTAINERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 6eb672ab6a2..4ac0a3104f5 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -2,7 +2,7 @@ This page lists all active maintainers and their areas of expertise. This can be The following is the full list, alphabetically ordered. -* Andres Taylor ([systay](https://github.com/systay)) antaylor@squareup.com +* Andres Taylor ([systay](https://github.com/systay)) andres@planetscale.com * Anthony Yeh ([enisoc](https://github.com/enisoc)) enisoc@planetscale.com * Dan Kozlowski ([dkhenry](https://github.com/dkhenry)) koz@planetscale.com * David Weitzman ([dweitzman](https://github.com/dweitzman)) dweitzman@pinterest.com From 8b8014059dab140f9ff4164f9e667fff4f24a5e0 Mon Sep 17 00:00:00 2001 From: deepthi Date: Mon, 3 Feb 2020 19:47:54 -0800 Subject: [PATCH 025/825] resurrect unit_test target, change test target back to running integration tests Signed-off-by: deepthi --- .github/workflows/unit.yml | 2 +- Makefile | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index af1ac347a4a..ecccf5e2ad9 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -75,4 +75,4 @@ jobs: - name: unit run: | - eatmydata -- make test + eatmydata -- make unit_test diff --git a/Makefile b/Makefile index ef55c30f4f1..d8b9f9a4c50 100644 --- a/Makefile +++ b/Makefile @@ -92,9 +92,8 @@ visitor: # To pass extra flags, run test.go manually. # For example: go run test.go -docker=false -- --extra-flag # For more info see: go run test.go -help -test: build dependency_check - echo $$(date): Running unit tests - tools/unit_test_runner.sh +test: + go run test.go -docker=false site_test: unit_test site_integration_test @@ -112,6 +111,10 @@ cleanall: clean # Remind people to run bootstrap.sh again echo "Please run 'make tools' again to setup your environment" +unit_test: build dependency_check + echo $$(date): Running unit tests + tools/unit_test_runner.sh + e2e_test: build echo $$(date): Running endtoend tests go test $(VT_GO_PARALLEL) ./go/.../endtoend/... From 0995d2a4f8020f01658aef735c70c306643d74b4 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 4 Feb 2020 20:01:35 +0100 Subject: [PATCH 026/825] Only rewrite database() against dual Signed-off-by: Andres Taylor --- go/vt/sqlparser/expression_rewriting.go | 28 +++++++++++++++++--- go/vt/sqlparser/expression_rewriting_test.go | 23 ++++++++-------- go/vt/vtgate/executor.go | 1 - 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/go/vt/sqlparser/expression_rewriting.go b/go/vt/sqlparser/expression_rewriting.go index 89a5c9ebfe8..fb934f266cd 100644 --- a/go/vt/sqlparser/expression_rewriting.go +++ b/go/vt/sqlparser/expression_rewriting.go @@ -32,6 +32,7 @@ func PrepareAST(in Statement, bindVars map[string]*querypb.BindVariable, prefix // RewriteAST rewrites the whole AST, replacing function calls and adding column aliases to queries func RewriteAST(in Statement) (*RewriteASTResult, error) { er := new(expressionRewriter) + er.shouldRewriteDatabaseFunc = shouldRewriteDatabaseFunc(in) Rewrite(in, er.goingDown, nil) return &RewriteASTResult{ @@ -41,6 +42,25 @@ func RewriteAST(in Statement) (*RewriteASTResult, error) { }, nil } +func shouldRewriteDatabaseFunc(in Statement) bool { + selct, ok := in.(*Select) + if !ok { + return false + } + if len(selct.From) != 1 { + return false + } + aliasedTable, ok := selct.From[0].(*AliasedTableExpr) + if !ok { + return false + } + tableName, ok := aliasedTable.Expr.(TableName) + if !ok { + return false + } + return tableName.Name.String() == "dual" +} + // RewriteASTResult contains the rewritten ast and meta information about it type RewriteASTResult struct { AST Statement @@ -49,8 +69,9 @@ type RewriteASTResult struct { } type expressionRewriter struct { - lastInsertID, database bool - err error + lastInsertID, database bool + shouldRewriteDatabaseFunc bool + err error } const ( @@ -67,6 +88,7 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { buf := NewTrackedBuffer(nil) node.Expr.Format(buf) inner := new(expressionRewriter) + inner.shouldRewriteDatabaseFunc = er.shouldRewriteDatabaseFunc tmp := Rewrite(node.Expr, inner.goingDown, nil) newExpr, ok := tmp.(Expr) if !ok { @@ -91,7 +113,7 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { cursor.Replace(bindVarExpression(LastInsertIDName)) er.lastInsertID = true } - case node.Name.EqualString("database"): + case node.Name.EqualString("database") && er.shouldRewriteDatabaseFunc: if len(node.Exprs) > 0 { er.err = vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Syntax error. DATABASE() takes no arguments") } else { diff --git a/go/vt/sqlparser/expression_rewriting_test.go b/go/vt/sqlparser/expression_rewriting_test.go index 9bd5cf89375..d8d53de3ebb 100644 --- a/go/vt/sqlparser/expression_rewriting_test.go +++ b/go/vt/sqlparser/expression_rewriting_test.go @@ -55,14 +55,19 @@ func TestRewrites(in *testing.T) { db: true, liid: true, }, { - in: "select (select database() from test) from test", - expected: "select (select :__vtdbname as `database()` from test) as `(select database() from test)` from test", + in: "select (select database() from dual) from dual", + expected: "select (select :__vtdbname as `database()` from dual) as `(select database() from dual)` from dual", db: true, liid: false, }, { in: "select id from user where database()", - expected: "select id from user where :__vtdbname", - db: true, liid: false, + expected: "select id from user where database()", + db: false, liid: false, + }, + { + in: "select table_name from information_schema.tables where table_schema = database()", + expected: "select table_name from information_schema.tables where table_schema = database()", + db: false, liid: false, }, } @@ -77,16 +82,10 @@ func TestRewrites(in *testing.T) { expected, err := Parse(tc.expected) require.NoError(t, err) - s := toString(expected) - require.Equal(t, s, toString(result.AST)) + s := String(expected) + require.Equal(t, s, String(result.AST)) require.Equal(t, tc.liid, result.NeedLastInsertID, "should need last insert id") require.Equal(t, tc.db, result.NeedDatabase, "should need database name") }) } } - -func toString(node SQLNode) string { - buf := NewTrackedBuffer(nil) - node.Format(buf) - return buf.String() -} diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 725ec31f2c9..940695cf1af 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -282,7 +282,6 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql sql = comments.Leading + normalized + comments.Trailing if rewriteResult.NeedDatabase { keyspace, _, _, _ := e.ParseDestinationTarget(safeSession.TargetString) - log.Warningf("This is the keyspace name: ---> %v", keyspace) if keyspace == "" { bindVars[sqlparser.DBVarName] = sqltypes.NullBindVariable } else { From 1398e7c054c0db8679f7addbd2b669345b5547c3 Mon Sep 17 00:00:00 2001 From: Derrick Laird Date: Tue, 4 Feb 2020 18:37:01 -0700 Subject: [PATCH 027/825] vitessdriver: convert sqltypes.Timestamp to time.Time values When trying to do a scan into a time.Time type it will fail with error unsupported Scan, storing driver.Value type []uint8 into type *time.Time This should remedy this behavior and allow scans into time.Time. Signed-off-by: Derrick Laird --- go/vt/vitessdriver/convert.go | 2 +- go/vt/vitessdriver/convert_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/vitessdriver/convert.go b/go/vt/vitessdriver/convert.go index 3a0e875b0d9..16d1b61134d 100644 --- a/go/vt/vitessdriver/convert.go +++ b/go/vt/vitessdriver/convert.go @@ -31,7 +31,7 @@ type converter struct { func (cv *converter) ToNative(v sqltypes.Value) (interface{}, error) { switch v.Type() { - case sqltypes.Datetime: + case sqltypes.Datetime, sqltypes.Timestamp: return DatetimeToNative(v, cv.location) case sqltypes.Date: return DateToNative(v, cv.location) diff --git a/go/vt/vitessdriver/convert_test.go b/go/vt/vitessdriver/convert_test.go index a8ccc6ffab1..be5aef82d60 100644 --- a/go/vt/vitessdriver/convert_test.go +++ b/go/vt/vitessdriver/convert_test.go @@ -40,7 +40,7 @@ func TestToNative(t *testing.T) { }, { convert: &converter{}, in: sqltypes.TestValue(sqltypes.Timestamp, "2012-02-24 23:19:43"), - out: []byte("2012-02-24 23:19:43"), // TIMESTAMP is not handled + out: time.Date(2012, 02, 24, 23, 19, 43, 0, time.UTC), }, { convert: &converter{}, in: sqltypes.TestValue(sqltypes.Time, "23:19:43"), From fa96b5017595d581af651222da8aa85c930f72b1 Mon Sep 17 00:00:00 2001 From: Shaun Verch Date: Tue, 4 Feb 2020 20:42:39 -0500 Subject: [PATCH 028/825] Change registry link to planetscale-vitess Signed-off-by: Shaun Verch --- examples/are-you-alive/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/are-you-alive/Makefile b/examples/are-you-alive/Makefile index 5461370576d..d6cc94e5f39 100644 --- a/examples/are-you-alive/Makefile +++ b/examples/are-you-alive/Makefile @@ -1,5 +1,5 @@ -# We are using the "dev" project in our registry for this -NAME := "us.gcr.io/planetscale-dev/are-you-alive" +# We are using the public "planetscale-vitess" registry for this +NAME := "us.gcr.io/planetscale-vitess/are-you-alive" TAG := $$(git log -1 --pretty=%H) IMG := ${NAME}:${TAG} LATEST := ${NAME}:latest From ec2a1ce0421492d7305dcaf10dadd2506add5501 Mon Sep 17 00:00:00 2001 From: Shaun Verch Date: Tue, 4 Feb 2020 20:45:11 -0500 Subject: [PATCH 029/825] Add Dockerfiles This wasn't added because it was gitignored. Signed-off-by: Shaun Verch --- examples/are-you-alive/build/dev/Dockerfile | 13 +++++++++++++ examples/are-you-alive/build/dev/reflex.conf | 2 ++ examples/are-you-alive/build/release/Dockerfile | 7 +++++++ 3 files changed, 22 insertions(+) create mode 100644 examples/are-you-alive/build/dev/Dockerfile create mode 100644 examples/are-you-alive/build/dev/reflex.conf create mode 100644 examples/are-you-alive/build/release/Dockerfile diff --git a/examples/are-you-alive/build/dev/Dockerfile b/examples/are-you-alive/build/dev/Dockerfile new file mode 100644 index 00000000000..3b079cfcf8a --- /dev/null +++ b/examples/are-you-alive/build/dev/Dockerfile @@ -0,0 +1,13 @@ +# Use a [multi stage +# build](https://docs.docker.com/develop/develop-images/multistage-build/) to +# build [reflex](https://github.com/cespare/reflex), a tool that will allow us +# to automatically rerun the project when any files change. +FROM golang:1.12.5 AS build +# Build reflex as a static binary (CGO_ENABLED=0) so we can run it in our final +# container. +RUN CGO_ENABLED=0 go get -v github.com/cespare/reflex + +FROM golang:1.12.5-alpine AS runtime +COPY --from=build /go/bin/reflex /go/bin/reflex +COPY reflex.conf / +ENTRYPOINT ["/go/bin/reflex", "-c", "/reflex.conf"] diff --git a/examples/are-you-alive/build/dev/reflex.conf b/examples/are-you-alive/build/dev/reflex.conf new file mode 100644 index 00000000000..5e368bf1f93 --- /dev/null +++ b/examples/are-you-alive/build/dev/reflex.conf @@ -0,0 +1,2 @@ +# Rerun "go run" every time a ".go" file changes. +-r '(\.go$)' -s -- go run github.com/planetscale/are-you-alive/cmd/are-you-alive --initialize diff --git a/examples/are-you-alive/build/release/Dockerfile b/examples/are-you-alive/build/release/Dockerfile new file mode 100644 index 00000000000..51a3fbc36cf --- /dev/null +++ b/examples/are-you-alive/build/release/Dockerfile @@ -0,0 +1,7 @@ +FROM golang:1.12.5 AS build +COPY . /go/src/github.com/planetscale/are-you-alive +RUN CGO_ENABLED=0 go install github.com/planetscale/are-you-alive/cmd/are-you-alive + +FROM debian:stretch-slim AS runtime +COPY --from=build /go/bin/are-you-alive /go/bin/are-you-alive +ENTRYPOINT ["/go/bin/are-you-alive"] From af5e293a888db26b4df0be4004f895fb23e13ba8 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Wed, 5 Feb 2020 11:52:47 +0530 Subject: [PATCH 030/825] first commit for merge sharding Signed-off-by: Ajeet jain --- .../mergesharding/mergesharding_base.go | 659 ++++++++++++++++++ .../mergesharding/v3/mergesharding_v3_test.go | 29 + go/vt/worker/split_diff.go | 4 + 3 files changed, 692 insertions(+) create mode 100644 go/test/endtoend/sharding/mergesharding/mergesharding_base.go create mode 100644 go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go diff --git a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go new file mode 100644 index 00000000000..d8534bc54c1 --- /dev/null +++ b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go @@ -0,0 +1,659 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mergesharding + +import ( + "context" + "encoding/json" + "fmt" + "os/exec" + "path" + "strings" + "testing" + "time" + + "vitess.io/vitess/go/sqltypes" + + "vitess.io/vitess/go/mysql" + + "github.com/prometheus/common/log" + + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/sharding" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/proto/topodata" + + "github.com/stretchr/testify/assert" +) + +var ( + // ClusterInstance instance to be used for test with different params + clusterInstance *cluster.LocalProcessCluster + hostname = "localhost" + keyspaceName = "ks" + cell = "zone1" + createTabletTemplate = ` + create table %s( + custom_ksid_col %s not null, + msg varchar(64), + id bigint not null, + parent_id bigint not null, + primary key (parent_id, id), + index by_msg (msg) + ) Engine=InnoDB; + ` + createViewTemplate = ` + create view %s (parent_id, id, msg, custom_ksid_col) as select parent_id, id, msg, custom_ksid_col from %s; + ` + fixedParentID = 86 + tableName = "resharding1" + vSchema = ` + { + "sharded": true, + "vindexes": { + "hash_index": { + "type": "hash" + } + }, + "tables": { + "resharding1": { + "column_vindexes": [ + { + "column": "custom_ksid_col", + "name": "hash_index" + } + ] + } + } + } + ` + // insertTabletTemplateKsID common insert format + insertTabletTemplateKsID = `insert into %s (parent_id, id, msg, custom_ksid_col) values (%d, %d, '%s', %d) /* vtgate:: keyspace_id:%d */ /* id:%d */` + + // initial shards + // range -40, 40-80 & 80- + shard0 = &cluster.Shard{Name: "-40"} + shard1 = &cluster.Shard{Name: "40-80"} + shard2 = &cluster.Shard{Name: "80-"} + + // merge shard + // merging -40 & 40-80 t0 -80 + shard3 = &cluster.Shard{Name: "-80"} + + // Sharding keys + key1 uint64 = 1152921504606846976 // Key redirect to shard 0 + key2 uint64 = 5764607523034234880 // key redirect to shard 1 + key3 uint64 = 14987979559889010688 // Key redirect to shard 2 + + key4 uint64 = 2305843009213693952 // Key redirect to shard 0 + key5 uint64 = 5764607523034234880 //6917529027641081856 // Key redirect to shard 1 +) + +// TestMergesharding covers the workflow for a sharding merge. +// We start with 3 shards: -40, 40-80, and 80-. We then merge -40 and 40-80 into -80. +// Note this test is just testing the full workflow, not corner cases or error +// cases. These are mostly done by the other resharding tests. +func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { + clusterInstance = cluster.NewCluster(cell, hostname) + defer clusterInstance.Teardown() + + // Launch keyspace + keyspace := &cluster.Keyspace{Name: keyspaceName} + + // Start topo server + err := clusterInstance.StartTopo() + assert.Nil(t, err) + + // Defining all the tablets + shard0Master := clusterInstance.GetVttabletInstance("replica", 0, "") + shard0Replica := clusterInstance.GetVttabletInstance("replica", 0, "") + shard0Rdonly := clusterInstance.GetVttabletInstance("rdonly", 0, "") + + shard1Master := clusterInstance.GetVttabletInstance("replica", 0, "") + shard1Replica := clusterInstance.GetVttabletInstance("replica", 0, "") + shard1Rdonly := clusterInstance.GetVttabletInstance("rdonly", 0, "") + + shard2Master := clusterInstance.GetVttabletInstance("replica", 0, "") + shard2Replica := clusterInstance.GetVttabletInstance("replica", 0, "") + shard2Rdonly := clusterInstance.GetVttabletInstance("rdonly", 0, "") + + shard3Master := clusterInstance.GetVttabletInstance("replica", 0, "") + shard3Replica := clusterInstance.GetVttabletInstance("replica", 0, "") + shard3Rdonly := clusterInstance.GetVttabletInstance("rdonly", 0, "") + + shard0.Vttablets = []*cluster.Vttablet{shard0Master, shard0Replica, shard0Rdonly} + shard1.Vttablets = []*cluster.Vttablet{shard1Master, shard1Replica, shard1Rdonly} + shard2.Vttablets = []*cluster.Vttablet{shard2Master, shard2Replica, shard2Rdonly} + shard3.Vttablets = []*cluster.Vttablet{shard3Master, shard3Replica, shard3Rdonly} + + clusterInstance.VtTabletExtraArgs = []string{ + "-vreplication_healthcheck_topology_refresh", "1s", + "-vreplication_healthcheck_retry_delay", "1s", + "-vreplication_retry_delay", "1s", + "-degraded_threshold", "5s", + "-lock_tables_timeout", "5s", + "-watch_replication_stream", + "-enable_semi_sync", + "-enable_replication_reporter", + "-enable-tx-throttler", + "-binlog_use_v3_resharding_mode=true", + } + + shardingColumnType := "bigint(20) unsigned" + shardingKeyType := querypb.Type_UINT64 + + if useVarbinaryShardingKeyType { + shardingColumnType = "varbinary(64)" + shardingKeyType = querypb.Type_VARBINARY + } + + // Initialize Cluster + err = clusterInstance.LaunchCluster(keyspace, []cluster.Shard{*shard0, *shard1, *shard2, *shard3}) + assert.Nil(t, err) + assert.Equal(t, len(clusterInstance.Keyspaces[0].Shards), 4) + + //Start MySql + var mysqlCtlProcessList []*exec.Cmd + for _, shard := range clusterInstance.Keyspaces[0].Shards { + for _, tablet := range shard.Vttablets { + fmt.Println("Starting MySql for tablet ", tablet.Alias) + if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { + t.Fatal(err) + } else { + mysqlCtlProcessList = append(mysqlCtlProcessList, proc) + } + } + } + + // Wait for mysql processes to start + for _, proc := range mysqlCtlProcessList { + if err := proc.Wait(); err != nil { + t.Fatal(err) + } + } + + // Rebuild keyspace Graph + err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) + assert.Nil(t, err) + + // Get Keyspace and verify the structure + srvKeyspace := sharding.GetSrvKeyspace(t, cell, keyspaceName, *clusterInstance) + assert.Equal(t, "", srvKeyspace.GetShardingColumnName()) + + //Start Tablets and Wait for the Process + for _, shard := range clusterInstance.Keyspaces[0].Shards { + for _, tablet := range shard.Vttablets { + // Init Tablet + err := clusterInstance.VtctlclientProcess.InitTablet(tablet, tablet.Cell, keyspaceName, hostname, shard.Name) + assert.Nil(t, err) + + // Start the tablet + err = tablet.VttabletProcess.Setup() + assert.Nil(t, err) + + // Create Database + _, err = tablet.VttabletProcess.QueryTablet(fmt.Sprintf("create database vt_%s", + keyspace.Name), keyspace.Name, false) + assert.Nil(t, err) + } + } + + // Init Shard Master + err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", + "-force", fmt.Sprintf("%s/%s", keyspaceName, shard0.Name), shard0Master.Alias) + assert.Nil(t, err) + err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", + "-force", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name), shard1Master.Alias) + assert.Nil(t, err) + + err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", + "-force", fmt.Sprintf("%s/%s", keyspaceName, shard2.Name), shard2Master.Alias) + assert.Nil(t, err) + + // Init Shard Master on Merge Shard + err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", + "-force", fmt.Sprintf("%s/%s", keyspaceName, shard3.Name), shard3Master.Alias) + assert.Nil(t, err) + + // Wait for tablets to come in Service state + err = shard0Master.VttabletProcess.WaitForTabletType("SERVING") + assert.Nil(t, err) + err = shard1Master.VttabletProcess.WaitForTabletType("SERVING") + assert.Nil(t, err) + err = shard2Master.VttabletProcess.WaitForTabletType("SERVING") + assert.Nil(t, err) + err = shard3Master.VttabletProcess.WaitForTabletType("SERVING") + assert.Nil(t, err) + + // keyspace/shard name fields + shard0Ks := fmt.Sprintf("%s/%s", keyspaceName, shard0.Name) + shard1Ks := fmt.Sprintf("%s/%s", keyspaceName, shard1.Name) + shard3Ks := fmt.Sprintf("%s/%s", keyspaceName, shard3.Name) + + // check for shards + result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("FindAllShardsInKeyspace", keyspaceName) + assert.Nil(t, err) + resultMap := make(map[string]interface{}) + err = json.Unmarshal([]byte(result), &resultMap) + assert.Nil(t, err) + assert.Equal(t, 4, len(resultMap), "No of shards should be 4") + + // Apply Schema + err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding1", shardingColumnType)) + assert.Nil(t, err) + //err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createViewTemplate, "view1", "resharding3")) + //assert.Nil(t, err) + + // Apply VSchema + err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) + assert.Nil(t, err) + + // Insert Data + //insertStartupValues(t) + + // run a health check on source replicas so they respond to discovery + // (for binlog players) and on the source rdonlys (for workers) + for _, shard := range keyspace.Shards { + for _, tablet := range shard.Vttablets { + err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) + assert.Nil(t, err) + } + } + + // Rebuild keyspace Graph + err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) + assert.Nil(t, err) + + // check srv keyspace + expectedPartitions := map[topodata.TabletType][]string{} + expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name, shard2.Name} + expectedPartitions[topodata.TabletType_REPLICA] = []string{shard0.Name, shard1.Name, shard2.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard0.Name, shard1.Name, shard2.Name} + sharding.CheckSrvKeyspace(t, cell, keyspaceName, "", 0, expectedPartitions, *clusterInstance) + + // we need to create the schema, and the worker will do data copying + err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", + shard0.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard3.Name)) + assert.Nil(t, err) + + // Run vtworker as daemon for the following SplitClone commands. -use_v3_resharding_mode default is true + err = clusterInstance.StartVtworker(cell, "--command_display_interval", "10ms") + assert.Nil(t, err) + /* + // Initial clone (online). + err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", + "--offline=false", + "--chunk_count", "10", + "--min_rows_per_chunk", "1", + "--min_healthy_rdonly_tablets", "1", + "--max_tps", "9999", + shard3Ks) + assert.Nil(t, err) + + // Check values in the merge shard + checkValues(t, *shard3.MasterTablet(), []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, + 1, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) + checkValues(t, *shard3.MasterTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, + 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) + + // Reset vtworker such that we can run the next command. + err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") + assert.Nil(t, err) + + // Delete row 2 (provokes an insert). + _, err = shard3Master.VttabletProcess.QueryTablet("delete from resharding1 where id=2", keyspaceName, true) + assert.Nil(t, err) + // Update row 3 (provokes an update). + _, err = shard3Master.VttabletProcess.QueryTablet("update resharding1 set msg='msg-not-1' where id=1", keyspaceName, true) + assert.Nil(t, err) + + // Insert row 4 (provokes a delete). + insertValue(t, shard3.MasterTablet(), keyspaceName, tableName, 4, "msg4", key3) + */ + err = clusterInstance.VtworkerProcess.ExecuteCommand( + "SplitClone", + "--chunk_count", "10", + "--min_rows_per_chunk", "1", + "--min_healthy_rdonly_tablets", "1", + "--max_tps", "9999", + shard3Ks) + assert.Nil(t, err) + + // Change tablet, which was taken offline, back to rdonly. + err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard0Rdonly.Alias, "rdonly") + assert.Nil(t, err) + err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Rdonly.Alias, "rdonly") + assert.Nil(t, err) + + // Terminate worker daemon because it is no longer needed. + err = clusterInstance.VtworkerProcess.TearDown() + assert.Nil(t, err) + + // Check startup values + //checkStartupValues(t, shardingKeyType) + + // check the schema too + err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", keyspaceName) + assert.Nil(t, err) + + // Verify vreplication table entries + qr, err := shard3.MasterTablet().VttabletProcess.QueryTabletWithDB("select * from vreplication", "_vt") + assert.Nil(t, err) + assert.Equal(t, 2, len(qr.Rows)) + assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") + assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"-40\" key_range: "`) + assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"40-80\" key_range: "`) + + // check the binlog players are running and exporting vars + sharding.CheckDestinationMaster(t, *shard3Master, []string{shard1Ks, shard0Ks}, *clusterInstance) + + // When the binlog players/filtered replication is turned on, the query + // service must be turned off on the destination masters. + // The tested behavior is a safeguard to prevent that somebody can + // accidentally modify data on the destination masters while they are not + // migrated yet and the source shards are still the source of truth. + err = shard3Master.VttabletProcess.WaitForTabletType("NOT_SERVING") + assert.Nil(t, err) + + // check that binlog server exported the stats vars + sharding.CheckBinlogServerVars(t, *shard0Replica, 0, 0, false) + sharding.CheckBinlogServerVars(t, *shard1Replica, 0, 0, false) + + // testing filtered replication: insert a bunch of data on shard 1, check we get most of it after a few seconds, + // wait for binlog server timeout, check we get all of it. + log.Debug("Inserting lots of data on source shard") + insertLots(t, 100, 0, tableName, fixedParentID, keyspaceName) + + //Checking 100 percent of data is sent quickly + assert.True(t, checkLotsTimeout(t, 100, 0, tableName, keyspaceName, shardingKeyType)) + + sharding.CheckBinlogPlayerVars(t, *shard3Master, []string{shard1Ks, shard0Ks}, 30) + + sharding.CheckBinlogServerVars(t, *shard0Replica, 100, 100, false) + sharding.CheckBinlogServerVars(t, *shard1Replica, 100, 100, false) + + // use vtworker to compare the data (after health-checking the destination + // rdonly tablets so discovery works) + err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard3Rdonly.Alias) + assert.Nil(t, err) + + // use vtworker to compare the data + clusterInstance.VtworkerProcess.Cell = cell + + // Compare using SplitDiff + log.Debug("Running vtworker SplitDiff") + err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), + clusterInstance.GetAndReservePort(), + "--use_v3_resharding_mode=true", + "SplitDiff", + "--exclude_tables", "unrelated", + "--min_healthy_rdonly_tablets", "1", + "--source_uid", "1", + shard3Ks) + assert.Nil(t, err) + + fmt.Println(err) + time.Sleep(10 * time.Minute) + + err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard0Rdonly.Alias, "rdonly") + assert.Nil(t, err) + err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard3Rdonly.Alias, "rdonly") + assert.Nil(t, err) + + log.Debug("Running vtworker SplitDiff on second half") + + err = clusterInstance.VtworkerProcess.ExecuteVtworkerCommand(clusterInstance.GetAndReservePort(), + clusterInstance.GetAndReservePort(), + "--use_v3_resharding_mode=true", + "SplitDiff", + "--exclude_tables", "unrelated", + "--min_healthy_rdonly_tablets", "1", + "--source_uid", "2", + shard3Ks) + assert.Nil(t, err) + + err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Rdonly.Alias, "rdonly") + assert.Nil(t, err) + err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard3Rdonly.Alias, "rdonly") + assert.Nil(t, err) + + // get status for destination master tablets, make sure we have it all + sharding.CheckRunningBinlogPlayer(t, *shard3Master, 300, 100) + + sharding.CheckTabletQueryService(t, *shard3Master, "NOT_SERVING", false, *clusterInstance) + streamHealth, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( + "VtTabletStreamHealth", + "-count", "1", shard3Master.Alias) + assert.Nil(t, err) + log.Debug("Got health: ", streamHealth) + + var streamHealthResponse querypb.StreamHealthResponse + err = json.Unmarshal([]byte(streamHealth), &streamHealthResponse) + assert.Nil(t, err) + assert.Equal(t, streamHealthResponse.Serving, false) + assert.NotNil(t, streamHealthResponse.RealtimeStats) + + // check the destination master 3 is healthy, even though its query + // service is not running (if not healthy this would exception out) + sharding.VerifyTabletHealth(t, *shard3Master, hostname) + + // now serve rdonly from the split shards, in cell1 only + err = clusterInstance.VtctlclientProcess.ExecuteCommand( + "MigrateServedTypes", shard3Ks, "rdonly") + assert.Nil(t, err) + + // check srv keyspace + expectedPartitions = map[topodata.TabletType][]string{} + expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name, shard2.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard2.Name, shard3.Name} + expectedPartitions[topodata.TabletType_REPLICA] = []string{shard0.Name, shard1.Name, shard2.Name} + sharding.CheckSrvKeyspace(t, cell, keyspaceName, "", 0, expectedPartitions, *clusterInstance) + + sharding.CheckTabletQueryService(t, *shard0Rdonly, "NOT_SERVING", true, *clusterInstance) + sharding.CheckTabletQueryService(t, *shard1Rdonly, "NOT_SERVING", true, *clusterInstance) + + // Now serve replica from the split shards + //destinationShards := []cluster.Shard{*shard2, *shard3} + err = clusterInstance.VtctlclientProcess.ExecuteCommand( + "MigrateServedTypes", shard3Ks, "replica") + assert.Nil(t, err) + + expectedPartitions = map[topodata.TabletType][]string{} + expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name, shard2.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard2.Name, shard3.Name} + expectedPartitions[topodata.TabletType_REPLICA] = []string{shard2.Name, shard3.Name} + sharding.CheckSrvKeyspace(t, cell, keyspaceName, "", 0, expectedPartitions, *clusterInstance) + + // now serve master from the split shards + err = clusterInstance.VtctlclientProcess.ExecuteCommand( + "MigrateServedTypes", shard3Ks, "master") + assert.Nil(t, err) + + expectedPartitions = map[topodata.TabletType][]string{} + expectedPartitions[topodata.TabletType_MASTER] = []string{shard2.Name, shard3.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard2.Name, shard3.Name} + expectedPartitions[topodata.TabletType_REPLICA] = []string{shard2.Name, shard3.Name} + sharding.CheckSrvKeyspace(t, cell, keyspaceName, "", 0, expectedPartitions, *clusterInstance) + + sharding.CheckTabletQueryService(t, *shard0Master, "NOT_SERVING", true, *clusterInstance) + sharding.CheckTabletQueryService(t, *shard1Master, "NOT_SERVING", true, *clusterInstance) + + // check destination shards are serving + sharding.CheckTabletQueryService(t, *shard3Master, "SERVING", false, *clusterInstance) + + // check the binlog players are gone now + err = shard3Master.VttabletProcess.WaitForBinLogPlayerCount(0) + assert.Nil(t, err) + + // delete the original tablets in the original shard + for _, shard := range []cluster.Shard{*shard0, *shard1} { + for _, tablet := range shard.Vttablets { + _ = tablet.MysqlctlProcess.Stop() + _ = tablet.VttabletProcess.TearDown() + } + } + + for _, tablet := range []cluster.Vttablet{*shard0Replica, *shard1Replica, *shard0Rdonly, *shard1Rdonly} { + err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) + assert.Nil(t, err) + } + + for _, tablet := range []cluster.Vttablet{*shard0Master, *shard1Master} { + err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", "-allow_master", tablet.Alias) + assert.Nil(t, err) + } + + // rebuild the serving graph, all mentions of the old shards should be gone + err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) + assert.Nil(t, err) + + // delete the original shard + err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard0Ks) + assert.Nil(t, err) + err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard1Ks) + assert.NotNil(t, err) + +} + +func insertStartupValues(t *testing.T) { + insertSQL := fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 1, "msg1", key1, key1, 1) + sharding.InsertToTablet(t, insertSQL, *shard0.MasterTablet(), keyspaceName, false) + + insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 2, "msg2", key2, key2, 2) + sharding.InsertToTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) + + insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 3, "msg3", key3, key3, 3) + sharding.InsertToTablet(t, insertSQL, *shard2.MasterTablet(), keyspaceName, false) +} + +func insertValue(t *testing.T, tablet *cluster.Vttablet, keyspaceName string, tableName string, id int, msg string, ksID uint64) { + insertSQL := fmt.Sprintf(insertTabletTemplateKsID, tableName, fixedParentID, id, msg, ksID, ksID, id) + sharding.InsertToTablet(t, insertSQL, *tablet, keyspaceName, false) +} + +func checkStartupValues(t *testing.T, shardingKeyType querypb.Type) { + + for _, tablet := range shard3.Vttablets { + checkValues(t, *tablet, []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, + 1, true, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) + + checkValues(t, *tablet, []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, + 2, true, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) + } +} + +// checkLotsTimeout waits till all values are inserted +func checkLotsTimeout(t *testing.T, count uint64, base uint64, table string, keyspaceName string, keyType querypb.Type) bool { + timeout := time.Now().Add(10 * time.Second) + for time.Now().Before(timeout) { + percentFound := checkLots(t, count, base, table, keyspaceName, keyType) + if percentFound == 100 { + return true + } + time.Sleep(300 * time.Millisecond) + } + return false +} + +func checkLots(t *testing.T, count uint64, base uint64, table string, keyspaceName string, keyType querypb.Type) float32 { + shard3Replica := *shard3.Vttablets[1] + + ctx := context.Background() + dbParams := getDBparams(shard3Replica, keyspaceName) + dbConn, _ := mysql.Connect(ctx, &dbParams) + defer dbConn.Close() + + var isFound bool + var totalFound int + var i uint64 + for i = 0; i < count; i++ { + isFound = checkValues(t, shard3Replica, []string{"INT64(86)", + fmt.Sprintf("INT64(%d)", 10000+base+i), + fmt.Sprintf(`VARCHAR("msg-range0-%d")`, 10000+base+i), + fmt.Sprintf("UINT64(%d)", key4)}, + 10000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn) + if isFound { + totalFound++ + } + + isFound = checkValues(t, shard3Replica, []string{"INT64(86)", + fmt.Sprintf("INT64(%d)", 20000+base+i), + fmt.Sprintf(`VARCHAR("msg-range1-%d")`, 20000+base+i), + fmt.Sprintf("UINT64(%d)", key4)}, + 20000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn) + if isFound { + totalFound++ + } + } + return float32(totalFound * 100 / int(count) / 2) +} + +func checkValues(t *testing.T, vttablet cluster.Vttablet, values []string, id uint64, exists bool, tableName string, + parentID int, ks string, keyType querypb.Type, dbConn *mysql.Conn) bool { + query := fmt.Sprintf("select parent_id, id, msg, custom_ksid_col from %s where parent_id = %d and id = %d", tableName, parentID, id) + var result *sqltypes.Result + var err error + if dbConn != nil { + result, err = dbConn.ExecuteFetch(query, 1000, true) + assert.Nil(t, err) + } else { + result, err = vttablet.VttabletProcess.QueryTablet(query, ks, true) + assert.Nil(t, err) + } + + isFound := false + if exists && len(result.Rows) > 0 { + isFound = assert.Equal(t, result.Rows[0][0].String(), values[0]) + isFound = isFound && assert.Equal(t, result.Rows[0][1].String(), values[1]) + isFound = isFound && assert.Equal(t, result.Rows[0][2].String(), values[2]) + if keyType == querypb.Type_VARBINARY { + r := strings.NewReplacer("UINT64(", "VARBINARY(\"", ")", "\")") + expected := r.Replace(values[3]) + isFound = isFound && assert.Equal(t, result.Rows[0][3].String(), expected) + } else { + isFound = isFound && assert.Equal(t, result.Rows[0][3].String(), values[3]) + } + + } else { + assert.Equal(t, len(result.Rows), 0) + } + return isFound +} + +// insertLots inserts multiple values to vttablet +func insertLots(t *testing.T, count uint64, base uint64, table string, parentID int, ks string) { + var query1, query2 string + var i uint64 + for i = 0; i < count; i++ { + query1 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 10000+base+i, + fmt.Sprintf("msg-range0-%d", 10000+base+i), key4, key4, 10000+base+i) + query2 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 20000+base+i, + fmt.Sprintf("msg-range1-%d", 20000+base+i), key5, key5, 20000+base+i) + + sharding.InsertToTablet(t, query1, *shard0.MasterTablet(), ks, false) + sharding.InsertToTablet(t, query2, *shard1.MasterTablet(), ks, false) + } +} + +func getDBparams(vttablet cluster.Vttablet, ks string) mysql.ConnParams { + dbParams := mysql.ConnParams{ + Uname: "vt_dba", + UnixSocket: path.Join(vttablet.VttabletProcess.Directory, "mysql.sock"), + DbName: "vt_" + ks, + } + return dbParams +} diff --git a/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go b/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go new file mode 100644 index 00000000000..438f14189ec --- /dev/null +++ b/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go @@ -0,0 +1,29 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v3 + +import ( + "testing" + + sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" +) + +// TestV3MergeSharding - main tests merge sharding using a INT column +func TestV3MergeSharding(t *testing.T) { + sharding.TestMergesharding(t, false) + +} diff --git a/go/vt/worker/split_diff.go b/go/vt/worker/split_diff.go index 22e851b83fe..32df6403008 100644 --- a/go/vt/worker/split_diff.go +++ b/go/vt/worker/split_diff.go @@ -459,6 +459,8 @@ func (sdw *SplitDiffWorker) diff(ctx context.Context) error { // source or destination keyrange. If it matches either, // we'll just ask for all the data. If the overlap is a subset, // we'll filter. + sdw.wr.Logger().Infof("Left keyrange %v", sdw.shardInfo.KeyRange) + sdw.wr.Logger().Infof("Right keyrange %v", sdw.sourceShard.KeyRange) overlap, err := key.KeyRangesOverlap(sdw.shardInfo.KeyRange, sdw.sourceShard.KeyRange) if err != nil { return vterrors.Wrap(err, "Source shard doesn't overlap with destination") @@ -496,6 +498,8 @@ func (sdw *SplitDiffWorker) diff(ctx context.Context) error { // On the source, see if we need a full scan // or a filtered scan. var sourceQueryResultReader *QueryResultReader + sdw.wr.Logger().Infof("Left keyrange %v", overlap) + sdw.wr.Logger().Infof("Right keyrange %v", sdw.sourceShard.KeyRange) if key.KeyRangeEqual(overlap, sdw.sourceShard.KeyRange) { sourceQueryResultReader, err = TableScan(ctx, sdw.wr.Logger(), sdw.wr.TopoServer(), sdw.sourceAlias, tableDefinition) } else { From 59c96e41441835348542e4a967ade119176f8cc4 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 5 Feb 2020 00:09:22 -0800 Subject: [PATCH 031/825] vstream: address review comments Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/vstream_manager.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/go/vt/vtgate/vstream_manager.go b/go/vt/vtgate/vstream_manager.go index b693db6d19d..4ef60074a4d 100644 --- a/go/vt/vtgate/vstream_manager.go +++ b/go/vt/vtgate/vstream_manager.go @@ -185,7 +185,7 @@ func (vs *vstream) startOneStream(ctx context.Context, sgtid *binlogdatapb.Shard // streamFromTablet streams from one shard. If transactions come in separate chunks, they are grouped and sent. func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.ShardGtid) error { - // jounralDone is assigned a channel when a journal event is encountered. + // journalDone is assigned a channel when a journal event is encountered. // It will be closed when all journal events converge. var journalDone chan struct{} @@ -195,7 +195,9 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha case <-ctx.Done(): return ctx.Err() case <-journalDone: - // Unreachable + // Unreachable. + // This can happen if a server misbehaves and does not end + // the stream after we return an error. return nil default: } @@ -218,7 +220,9 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha case <-ctx.Done(): return ctx.Err() case <-journalDone: - // Unreachable + // Unreachable. + // This can happen if a server misbehaves and does not end + // the stream after we return an error. return io.EOF default: } @@ -228,6 +232,8 @@ func (vs *vstream) streamFromTablet(ctx context.Context, sgtid *binlogdatapb.Sha switch event.Type { case binlogdatapb.VEventType_FIELD: // Update table names and send. + // If we're streaming from multiple keyspaces, this will disambiguate + // duplicate table names. ev := proto.Clone(event).(*binlogdatapb.VEvent) ev.FieldEvent.TableName = sgtid.Keyspace + "." + ev.FieldEvent.TableName sendevents = append(sendevents, ev) @@ -322,8 +328,20 @@ func (vs *vstream) sendAll(sgtid *binlogdatapb.ShardGtid, eventss [][]*binlogdat return nil } +// getJournalEvent returns a journalEvent. The caller has to wait on its done channel. +// Once it closes, the caller has to return (end their stream). +// The function has three parts: +// Part 1: For the first stream that encounters an event, it creates a journal event. +// Part 2: Every stream joins the journalEvent. If all have not joined, the journalEvent +// is returned to the caller. +// Part 3: If all streams have joined, then new streams are created to replace existing +// streams, the done channel is closed and returned. This section is executed exactly +// once after the last stream joins. func (vs *vstream) getJournalEvent(ctx context.Context, sgtid *binlogdatapb.ShardGtid, journal *binlogdatapb.Journal) (*journalEvent, error) { if journal.MigrationType == binlogdatapb.MigrationType_TABLES { + // We cannot support table migrations yet because there is no + // good model for it yet. For example, what if a table is migrated + // out of the current keyspace we're streaming from. return nil, nil } @@ -344,6 +362,8 @@ func (vs *vstream) getJournalEvent(ctx context.Context, sgtid *binlogdatapb.Shar matchAll matchNone ) + // We start off as undecided. Once we transition to + // matchAll or matchNone, we have to stay in that state. mode := undecided nextParticipant: for _, jks := range journal.Participants { From 4b4982953e3b38047b04d7f8f31cf16b239fdd8c Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Wed, 5 Feb 2020 16:40:27 +0530 Subject: [PATCH 032/825] merge sharding completed in go Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 2 +- go/test/endtoend/sharding/base_sharding.go | 8 +- .../mergesharding/mergesharding_base.go | 103 +++++++----------- .../string/mergesharding_string_test.go | 29 +++++ go/vt/worker/split_diff.go | 4 - test/config.json | 44 ++++---- 6 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index fad6e302642..a6670856869 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22] steps: - name: Set up Go diff --git a/go/test/endtoend/sharding/base_sharding.go b/go/test/endtoend/sharding/base_sharding.go index f7c8b4f2801..6794045a42b 100644 --- a/go/test/endtoend/sharding/base_sharding.go +++ b/go/test/endtoend/sharding/base_sharding.go @@ -183,16 +183,16 @@ func CheckBinlogPlayerVars(t *testing.T, vttablet cluster.Vttablet, sourceShards replicationSourceObj := reflect.ValueOf(tabletVars["VReplicationSource"]) replicationSourceValue := []string{} - assert.Equal(t, - fmt.Sprintf("%v", replicationSourceObj.MapKeys()), - fmt.Sprintf("%v", reflect.ValueOf(tabletVars["VReplicationSourceTablet"]).MapKeys())) + assert.Equal(t, len(replicationSourceObj.MapKeys()), len(reflect.ValueOf(tabletVars["VReplicationSourceTablet"]).MapKeys())) for _, key := range replicationSourceObj.MapKeys() { replicationSourceValue = append(replicationSourceValue, fmt.Sprintf("%v", replicationSourceObj.MapIndex(key))) } - assert.True(t, reflect.DeepEqual(replicationSourceValue, sourceShards)) + for _, shard := range sourceShards { + assert.Containsf(t, replicationSourceValue, shard, "Source shard is not matched with vReplication shard value") + } if secondBehindMaster != 0 { secondBehindMaserMaxStr := fmt.Sprintf("%v", reflect.ValueOf(tabletVars["VReplicationSecondsBehindMasterMax"])) diff --git a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go index d8534bc54c1..cc5d136cc1c 100644 --- a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go +++ b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go @@ -56,9 +56,6 @@ var ( index by_msg (msg) ) Engine=InnoDB; ` - createViewTemplate = ` - create view %s (parent_id, id, msg, custom_ksid_col) as select parent_id, id, msg, custom_ksid_col from %s; - ` fixedParentID = 86 tableName = "resharding1" vSchema = ` @@ -95,12 +92,9 @@ var ( shard3 = &cluster.Shard{Name: "-80"} // Sharding keys - key1 uint64 = 1152921504606846976 // Key redirect to shard 0 - key2 uint64 = 5764607523034234880 // key redirect to shard 1 - key3 uint64 = 14987979559889010688 // Key redirect to shard 2 - - key4 uint64 = 2305843009213693952 // Key redirect to shard 0 - key5 uint64 = 5764607523034234880 //6917529027641081856 // Key redirect to shard 1 + key1 uint64 = 1 // Key redirect to shard 0 [-40] + key2 uint64 = 3 // key redirect to shard 1 [40-80] + key3 uint64 = 4 // Key redirect to shard 2 [80-] ) // TestMergesharding covers the workflow for a sharding merge. @@ -255,15 +249,13 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Apply Schema err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding1", shardingColumnType)) assert.Nil(t, err) - //err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createViewTemplate, "view1", "resharding3")) - //assert.Nil(t, err) // Apply VSchema err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) assert.Nil(t, err) // Insert Data - //insertStartupValues(t) + insertStartupValues(t) // run a health check on source replicas so they respond to discovery // (for binlog players) and on the source rdonlys (for workers) @@ -293,37 +285,37 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Run vtworker as daemon for the following SplitClone commands. -use_v3_resharding_mode default is true err = clusterInstance.StartVtworker(cell, "--command_display_interval", "10ms") assert.Nil(t, err) - /* - // Initial clone (online). - err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", - "--offline=false", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--min_healthy_rdonly_tablets", "1", - "--max_tps", "9999", - shard3Ks) - assert.Nil(t, err) - // Check values in the merge shard - checkValues(t, *shard3.MasterTablet(), []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, - 1, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - checkValues(t, *shard3.MasterTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, - 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) + // Initial clone (online). + err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", + "--offline=false", + "--chunk_count", "10", + "--min_rows_per_chunk", "1", + "--min_healthy_rdonly_tablets", "1", + "--max_tps", "9999", + shard3Ks) + assert.Nil(t, err) - // Reset vtworker such that we can run the next command. - err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - assert.Nil(t, err) + // Check values in the merge shard + checkValues(t, *shard3.MasterTablet(), []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, + 1, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) + checkValues(t, *shard3.MasterTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, + 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) - // Delete row 2 (provokes an insert). - _, err = shard3Master.VttabletProcess.QueryTablet("delete from resharding1 where id=2", keyspaceName, true) - assert.Nil(t, err) - // Update row 3 (provokes an update). - _, err = shard3Master.VttabletProcess.QueryTablet("update resharding1 set msg='msg-not-1' where id=1", keyspaceName, true) - assert.Nil(t, err) + // Reset vtworker such that we can run the next command. + err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") + assert.Nil(t, err) + + // Delete row 2 (provokes an insert). + _, err = shard3Master.VttabletProcess.QueryTablet("delete from resharding1 where id=2", keyspaceName, true) + assert.Nil(t, err) + // Update row 3 (provokes an update). + _, err = shard3Master.VttabletProcess.QueryTablet("update resharding1 set msg='msg-not-1' where id=1", keyspaceName, true) + assert.Nil(t, err) + + // Insert row 4 (provokes a delete). + insertValue(t, shard3.MasterTablet(), keyspaceName, tableName, 4, "msg4", key3) - // Insert row 4 (provokes a delete). - insertValue(t, shard3.MasterTablet(), keyspaceName, tableName, 4, "msg4", key3) - */ err = clusterInstance.VtworkerProcess.ExecuteCommand( "SplitClone", "--chunk_count", "10", @@ -344,7 +336,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { assert.Nil(t, err) // Check startup values - //checkStartupValues(t, shardingKeyType) + checkStartupValues(t, shardingKeyType) // check the schema too err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", keyspaceName) @@ -406,9 +398,6 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { shard3Ks) assert.Nil(t, err) - fmt.Println(err) - time.Sleep(10 * time.Minute) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard0Rdonly.Alias, "rdonly") assert.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard3Rdonly.Alias, "rdonly") @@ -459,7 +448,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // check srv keyspace expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name, shard2.Name} - expectedPartitions[topodata.TabletType_RDONLY] = []string{shard2.Name, shard3.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard3.Name, shard2.Name} expectedPartitions[topodata.TabletType_REPLICA] = []string{shard0.Name, shard1.Name, shard2.Name} sharding.CheckSrvKeyspace(t, cell, keyspaceName, "", 0, expectedPartitions, *clusterInstance) @@ -467,15 +456,14 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { sharding.CheckTabletQueryService(t, *shard1Rdonly, "NOT_SERVING", true, *clusterInstance) // Now serve replica from the split shards - //destinationShards := []cluster.Shard{*shard2, *shard3} err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", shard3Ks, "replica") assert.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name, shard2.Name} - expectedPartitions[topodata.TabletType_RDONLY] = []string{shard2.Name, shard3.Name} - expectedPartitions[topodata.TabletType_REPLICA] = []string{shard2.Name, shard3.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard3.Name, shard2.Name} + expectedPartitions[topodata.TabletType_REPLICA] = []string{shard3.Name, shard2.Name} sharding.CheckSrvKeyspace(t, cell, keyspaceName, "", 0, expectedPartitions, *clusterInstance) // now serve master from the split shards @@ -484,9 +472,9 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { assert.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} - expectedPartitions[topodata.TabletType_MASTER] = []string{shard2.Name, shard3.Name} - expectedPartitions[topodata.TabletType_RDONLY] = []string{shard2.Name, shard3.Name} - expectedPartitions[topodata.TabletType_REPLICA] = []string{shard2.Name, shard3.Name} + expectedPartitions[topodata.TabletType_MASTER] = []string{shard3.Name, shard2.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard3.Name, shard2.Name} + expectedPartitions[topodata.TabletType_REPLICA] = []string{shard3.Name, shard2.Name} sharding.CheckSrvKeyspace(t, cell, keyspaceName, "", 0, expectedPartitions, *clusterInstance) sharding.CheckTabletQueryService(t, *shard0Master, "NOT_SERVING", true, *clusterInstance) @@ -521,12 +509,6 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) assert.Nil(t, err) - // delete the original shard - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard0Ks) - assert.Nil(t, err) - err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard1Ks) - assert.NotNil(t, err) - } func insertStartupValues(t *testing.T) { @@ -546,7 +528,6 @@ func insertValue(t *testing.T, tablet *cluster.Vttablet, keyspaceName string, ta } func checkStartupValues(t *testing.T, shardingKeyType querypb.Type) { - for _, tablet := range shard3.Vttablets { checkValues(t, *tablet, []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, 1, true, "resharding1", fixedParentID, keyspaceName, shardingKeyType, nil) @@ -584,7 +565,7 @@ func checkLots(t *testing.T, count uint64, base uint64, table string, keyspaceNa isFound = checkValues(t, shard3Replica, []string{"INT64(86)", fmt.Sprintf("INT64(%d)", 10000+base+i), fmt.Sprintf(`VARCHAR("msg-range0-%d")`, 10000+base+i), - fmt.Sprintf("UINT64(%d)", key4)}, + fmt.Sprintf("UINT64(%d)", key1)}, 10000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn) if isFound { totalFound++ @@ -593,7 +574,7 @@ func checkLots(t *testing.T, count uint64, base uint64, table string, keyspaceNa isFound = checkValues(t, shard3Replica, []string{"INT64(86)", fmt.Sprintf("INT64(%d)", 20000+base+i), fmt.Sprintf(`VARCHAR("msg-range1-%d")`, 20000+base+i), - fmt.Sprintf("UINT64(%d)", key4)}, + fmt.Sprintf("UINT64(%d)", key2)}, 20000+base+i, true, table, fixedParentID, keyspaceName, keyType, dbConn) if isFound { totalFound++ @@ -640,9 +621,9 @@ func insertLots(t *testing.T, count uint64, base uint64, table string, parentID var i uint64 for i = 0; i < count; i++ { query1 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 10000+base+i, - fmt.Sprintf("msg-range0-%d", 10000+base+i), key4, key4, 10000+base+i) + fmt.Sprintf("msg-range0-%d", 10000+base+i), key1, key1, 10000+base+i) query2 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 20000+base+i, - fmt.Sprintf("msg-range1-%d", 20000+base+i), key5, key5, 20000+base+i) + fmt.Sprintf("msg-range1-%d", 20000+base+i), key2, key2, 20000+base+i) sharding.InsertToTablet(t, query1, *shard0.MasterTablet(), ks, false) sharding.InsertToTablet(t, query2, *shard1.MasterTablet(), ks, false) diff --git a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go new file mode 100644 index 00000000000..e9e54193758 --- /dev/null +++ b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go @@ -0,0 +1,29 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v3 + +import ( + "testing" + + sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" +) + +// TestV3MergeShardingString - main tests merge sharding using a Byte column +func TestV3MergeShardingString(t *testing.T) { + sharding.TestMergesharding(t, true) + +} diff --git a/go/vt/worker/split_diff.go b/go/vt/worker/split_diff.go index 32df6403008..22e851b83fe 100644 --- a/go/vt/worker/split_diff.go +++ b/go/vt/worker/split_diff.go @@ -459,8 +459,6 @@ func (sdw *SplitDiffWorker) diff(ctx context.Context) error { // source or destination keyrange. If it matches either, // we'll just ask for all the data. If the overlap is a subset, // we'll filter. - sdw.wr.Logger().Infof("Left keyrange %v", sdw.shardInfo.KeyRange) - sdw.wr.Logger().Infof("Right keyrange %v", sdw.sourceShard.KeyRange) overlap, err := key.KeyRangesOverlap(sdw.shardInfo.KeyRange, sdw.sourceShard.KeyRange) if err != nil { return vterrors.Wrap(err, "Source shard doesn't overlap with destination") @@ -498,8 +496,6 @@ func (sdw *SplitDiffWorker) diff(ctx context.Context) error { // On the source, see if we need a full scan // or a filtered scan. var sourceQueryResultReader *QueryResultReader - sdw.wr.Logger().Infof("Left keyrange %v", overlap) - sdw.wr.Logger().Infof("Right keyrange %v", sdw.sourceShard.KeyRange) if key.KeyRangeEqual(overlap, sdw.sourceShard.KeyRange) { sourceQueryResultReader, err = TableScan(ctx, sdw.wr.Logger(), sdw.wr.TopoServer(), sdw.sourceAlias, tableDefinition) } else { diff --git a/test/config.json b/test/config.json index d00849c9733..951c7f9770b 100644 --- a/test/config.json +++ b/test/config.json @@ -74,28 +74,6 @@ "RetryMax": 0, "Tags": [] }, - "merge_sharding": { - "File": "merge_sharding.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 4, - "RetryMax": 0, - "Tags": [ - "worker_test" - ] - }, - "merge_sharding_bytes": { - "File": "merge_sharding_bytes.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 4, - "RetryMax": 0, - "Tags": [ - "worker_test" - ] - }, "messaging": { "File": "messaging.py", "Args": [], @@ -318,6 +296,28 @@ "site_test" ] }, + "merge_sharding": { + "File": "mergesharding_v3_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/v3"], + "Command": [], + "Manual": false, + "Shard": 22, + "RetryMax": 0, + "Tags": [ + "worker_test" + ] + }, + "merge_sharding_bytes": { + "File": "mergesharding_string_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/string"], + "Command": [], + "Manual": false, + "Shard": 22, + "RetryMax": 0, + "Tags": [ + "worker_test" + ] + }, "mysqlctl": { "File": "mysqlctl.go", "Args": ["vitess.io/vitess/go/test/endtoend/mysqlctl"], From 29f1889c0df9201a3da082539b2959e25d884c98 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Wed, 5 Feb 2020 17:01:00 +0530 Subject: [PATCH 033/825] messaging testcase completed. Signed-off-by: pradip parmar --- go/test/endtoend/messaging/main_test.go | 15 +- go/test/endtoend/messaging/messaging_test.go | 296 +++++++++++++++++++ 2 files changed, 304 insertions(+), 7 deletions(-) create mode 100644 go/test/endtoend/messaging/messaging_test.go diff --git a/go/test/endtoend/messaging/main_test.go b/go/test/endtoend/messaging/main_test.go index 21da2592dbc..8528d83e750 100644 --- a/go/test/endtoend/messaging/main_test.go +++ b/go/test/endtoend/messaging/main_test.go @@ -22,6 +22,8 @@ import ( "os" "testing" + _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" + "vitess.io/vitess/go/test/endtoend/cluster" ) @@ -32,7 +34,6 @@ var ( shard1Master *cluster.Vttablet lookupMaster *cluster.Vttablet hostname = "localhost" - keyspaceName = "test_keyspace" testingID = 1 tableName = "vt_prepare_stmt_test" cell = "zone1" @@ -94,7 +95,7 @@ func TestMain(m *testing.M) { flag.Parse() exitcode, err := func() (int, error) { - clusterInstance = &cluster.LocalProcessCluster{Cell: cell, Hostname: hostname} + clusterInstance = cluster.NewCluster(cell, hostname) defer clusterInstance.Teardown() // Start topo server @@ -103,22 +104,22 @@ func TestMain(m *testing.M) { } // Start unsharded keyspace - keyspace := &cluster.Keyspace{ + keyspace := cluster.Keyspace{ Name: lookupKeyspace, SchemaSQL: createUnshardedMessage, VSchema: lookupVschema, } - if err := clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil { + if err := clusterInstance.StartUnshardedKeyspace(keyspace, 1, false); err != nil { return 1, err } // Start sharded keyspace - keyspace = &cluster.Keyspace{ + keyspace = cluster.Keyspace{ Name: userKeyspace, SchemaSQL: createShardedMessage, VSchema: userVschema, } - if err := clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 1, false); err != nil { + if err := clusterInstance.StartKeyspace(keyspace, []string{"-80", "80-"}, 1, false); err != nil { return 1, err } @@ -130,7 +131,7 @@ func TestMain(m *testing.M) { shard0Master = clusterInstance.Keyspaces[1].Shards[0].MasterTablet() shard1Master = clusterInstance.Keyspaces[1].Shards[1].MasterTablet() lookupMaster = clusterInstance.Keyspaces[0].Shards[0].MasterTablet() - shard0Replica = clusterInstance.Keyspaces[1].Shards[0].Replica() + shard0Replica = clusterInstance.Keyspaces[1].Shards[0].Vttablets[1] return m.Run(), nil }() diff --git a/go/test/endtoend/messaging/messaging_test.go b/go/test/endtoend/messaging/messaging_test.go new file mode 100644 index 00000000000..47c6059f10d --- /dev/null +++ b/go/test/endtoend/messaging/messaging_test.go @@ -0,0 +1,296 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package messaging + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/recovery" + "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtgate/vtgateconn" +) + +func TestSharded(t *testing.T) { + testMessaging(t, "sharded_message", userKeyspace) +} + +func TestUnsharded(t *testing.T) { + testMessaging(t, "unsharded_message", lookupKeyspace) +} + +func TestRepareting(t *testing.T) { + name := "sharded_message" + + ctx := context.Background() + stream, err := NewConn(ctx, clusterInstance) + require.Nil(t, err) + defer stream.Close() + stream.MessageStream(userKeyspace, "", nil, name) + + assert.Equal(t, 1, getClientCount(shard0Master)) + assert.Equal(t, 0, getClientCount(shard0Replica)) + assert.Equal(t, 1, getClientCount(shard1Master)) + + // planned reparenting + clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( + "PlannedReparentShard", + "-keyspace_shard", userKeyspace+"/-80", + "-new_master", shard0Replica.Alias) + // validate topology + err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") + assert.Nil(t, err) + + // Verify connection has migrated. + // The wait must be at least 6s which is how long vtgate will + // wait before retrying: that is 30s/5 where 30s is the default + // message_stream_grace_period. + time.Sleep(10 * time.Second) + assert.Equal(t, 0, getClientCount(shard0Master)) + assert.Equal(t, 1, getClientCount(shard0Replica)) + assert.Equal(t, 1, getClientCount(shard1Master)) + session := stream.Session("@master", nil) + recovery.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (3,'hello world 3')") + + stream.Next() + + clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( + "PlannedReparentShard", + "-keyspace_shard", userKeyspace+"/-80", + "-new_master", shard0Master.Alias) + // validate topology + err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") + assert.Nil(t, err) + time.Sleep(10 * time.Second) + assert.Equal(t, 1, getClientCount(shard0Master)) + assert.Equal(t, 0, getClientCount(shard0Replica)) + assert.Equal(t, 1, getClientCount(shard1Master)) + + stream.MessageAck(ctx, userKeyspace, name, keyRange(3)) + +} + +func TestConnection(t *testing.T) { + + name := "sharded_message" + + assert.Equal(t, 0, getClientCount(shard0Master)) + assert.Equal(t, 0, getClientCount(shard1Master)) + + ctx := context.Background() + stream, err := NewConn(ctx, clusterInstance) + require.Nil(t, err) + stream.MessageStream(userKeyspace, "", nil, name) + + assert.Equal(t, 1, getClientCount(shard0Master)) + assert.Equal(t, 1, getClientCount(shard1Master)) + + stream1, err := NewConn(ctx, clusterInstance) + require.Nil(t, err) + stream1.MessageStream(userKeyspace, "", nil, name) + + assert.Equal(t, 2, getClientCount(shard0Master)) + assert.Equal(t, 2, getClientCount(shard1Master)) + + session := stream.Session("@master", nil) + recovery.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (2,'hello world 2')") + recovery.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (5,'hello world 5')") + + stream.Next() + stream.Next() + + stream.MessageAck(ctx, userKeyspace, name, keyRange(2, 5)) + + stream.Close() + time.Sleep(1 * time.Second) + assert.Equal(t, 1, getClientCount(shard0Master)) + assert.Equal(t, 1, getClientCount(shard1Master)) + + stream1.Close() +} + +func testMessaging(t *testing.T, name, ks string) { + ctx := context.Background() + stream, err := NewConn(ctx, clusterInstance) + require.Nil(t, err) + defer stream.Close() + + session := stream.Session("@master", nil) + recovery.ExecuteQueriesUsingVtgate(t, session, "insert into "+name+" (id, message) values (1,'hello world 1')") + recovery.ExecuteQueriesUsingVtgate(t, session, "insert into "+name+" (id, message) values (4,'hello world 4')") + + // validate fields + res := stream.MessageStream(ks, "", nil, name) + require.Equal(t, 3, len(res.Fields)) + validateField(t, res.Fields[0], "id", query.Type_INT64) + validateField(t, res.Fields[1], "time_scheduled", query.Type_INT64) + validateField(t, res.Fields[2], "message", query.Type_VARCHAR) + + // validate recieved msgs + resMap := make(map[string]string) + res = stream.Next() + for _, row := range res.Rows { + resMap[row[0].ToString()] = row[2].ToString() + } + + res = stream.Next() + for _, row := range res.Rows { + resMap[row[0].ToString()] = row[2].ToString() + } + + assert.Equal(t, "hello world 1", resMap["1"]) + assert.Equal(t, "hello world 4", resMap["4"]) + + // validate message ack with id 4 + count, err := stream.MessageAck(ctx, ks, name, keyRange(4)) + assert.Nil(t, err) + assert.Equal(t, int64(1), count) + res = stream.Next() + for _, row := range res.Rows { + resMap[row[0].ToString()] = row[2].ToString() + } + + res = stream.Next() + for _, row := range res.Rows { + resMap[row[0].ToString()] = row[2].ToString() + } + + assert.Equal(t, "hello world 1", resMap["1"]) + + // validate message ack with 1 and 4, only 1 should be ack + count, err = stream.MessageAck(ctx, ks, name, keyRange(1, 4)) + assert.Nil(t, err) + assert.Equal(t, int64(1), count) +} + +func validateField(t *testing.T, field *query.Field, name string, _type query.Type) { + assert.Equal(t, name, field.Name) + assert.Equal(t, _type, field.Type) +} + +// MsgStream handles all meta required for grpc connection with vtgate. +type VTGateStream struct { + ctx context.Context + host string + respChan chan *sqltypes.Result + *vtgateconn.VTGateConn +} + +// NewConn create new msg stream for grpc connection with vtgate. +func NewConn(ctx context.Context, cluster *cluster.LocalProcessCluster) (*VTGateStream, error) { + stream := new(VTGateStream) + stream.ctx = ctx + stream.host = fmt.Sprintf("%s:%d", cluster.Hostname, cluster.VtgateProcess.GrpcPort) + conn, err := vtgateconn.Dial(ctx, stream.host) + // init components + stream.respChan = make(chan *sqltypes.Result) + stream.VTGateConn = conn + + return stream, err +} + +// MessageStream strarts the stream for the corresponding connection. +func (stream *VTGateStream) MessageStream(ks, shard string, keyRange *topodata.KeyRange, name string) *sqltypes.Result { + go stream.VTGateConn.MessageStream(stream.ctx, ks, shard, keyRange, name, func(s *sqltypes.Result) error { + stream.respChan <- s + return nil + }) + + return stream.Next() +} + +// Next reads the new msg available in stream. +func (stream *VTGateStream) Next() *sqltypes.Result { + ticker := time.Tick(10 * time.Second) + select { + case s := <-stream.respChan: + return s + case <-ticker: + panic(fmt.Errorf("time limit exceeded")) + } +} + +// getClientCount read connected client count from the vttablet debug vars. +func getClientCount(vttablet *cluster.Vttablet) int { + vars, err := getVar(vttablet) + if err != nil { + return 0 + } + + msg, ok := vars["Messages"] + if !ok { + return 0 + } + + v, ok := msg.(map[string]interface{}) + if !ok { + return 0 + } + + countStr, ok := v["sharded_message.ClientCount"] + if !ok { + return 0 + } + + i, err := strconv.ParseInt(fmt.Sprint(countStr), 10, 16) + if err != nil { + return 0 + } + + return int(i) +} + +// getVar read debug vars from the vttablet. +func getVar(vttablet *cluster.Vttablet) (map[string]interface{}, error) { + resp, err := http.Get(fmt.Sprintf("http://%s:%d/debug/vars", vttablet.VttabletProcess.TabletHostname, vttablet.HTTPPort)) + if err != nil { + return nil, err + } + if resp.StatusCode == 200 { + resultMap := make(map[string]interface{}) + respByte, _ := ioutil.ReadAll(resp.Body) + err := json.Unmarshal(respByte, &resultMap) + return resultMap, err + } + return nil, nil +} + +// keyRange created keyRange array for correponding ids +func keyRange(s ...int) []*query.Value { + out := make([]*query.Value, 0, len(s)) + + for _, v := range s { + q := new(query.Value) + q.Type = query.Type_INT64 + q.Value = []byte(fmt.Sprint(v)) + + out = append(out, q) + } + + return out +} From fe0436ce05c5567afca2bc924d18151c578f1f91 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Wed, 5 Feb 2020 17:20:22 +0530 Subject: [PATCH 034/825] config file added. Signed-off-by: pradip parmar --- test/config.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/config.json b/test/config.json index a61ce65eab0..0c6d1c8c458 100644 --- a/test/config.json +++ b/test/config.json @@ -105,15 +105,6 @@ "worker_test" ] }, - "messaging": { - "File": "messaging.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 3, - "RetryMax": 0, - "Tags": [] - }, "python_client": { "File": "python_client_test.py", "Args": [], @@ -279,6 +270,15 @@ "RetryMax": 0, "Tags": [] }, + "messaging": { + "File": "messaging_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/messaging"], + "Command": [], + "Manual": false, + "Shard": 12, + "RetryMax": 0, + "Tags": [] + }, "clustertest": { "File": "clustertest.go", "Args": ["vitess.io/vitess/go/test/endtoend/clustertest"], From e1b4096c1d14fe38d5de4c708a1f4ee7f6625a5a Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Wed, 5 Feb 2020 17:00:05 +0530 Subject: [PATCH 035/825] minor text changes to retrigger the travis Signed-off-by: Ajeet jain --- .../sharding/mergesharding/string/mergesharding_string_test.go | 2 +- .../endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go index e9e54193758..80fd40f89cc 100644 --- a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go +++ b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go @@ -22,7 +22,7 @@ import ( sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" ) -// TestV3MergeShardingString - main tests merge sharding using a Byte column +// TestV3MergeShardingString - tests merge sharding using a Byte column func TestV3MergeShardingString(t *testing.T) { sharding.TestMergesharding(t, true) diff --git a/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go b/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go index 438f14189ec..2eb6a058a1b 100644 --- a/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go +++ b/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go @@ -22,7 +22,7 @@ import ( sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" ) -// TestV3MergeSharding - main tests merge sharding using a INT column +// TestV3MergeSharding - tests merge sharding using a INT column func TestV3MergeSharding(t *testing.T) { sharding.TestMergesharding(t, false) From 2eb73f664f225f30c9613bc5839bdad4923dbe2a Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 28 Dec 2019 17:44:52 -0800 Subject: [PATCH 036/825] vrepl: improve failure handling Tablet dialers were set to not fail fast. This was causing grpc to redial the same tablet indefinitely eventhough it was permanently down. This has now been changed to fail fast. The tablet picker was only taking health errors into account, which happens when tablets report themseleves as unhealthy. But if a tablet is unreachable, we have to check LastError. Also fixed some staticcheck errors. Signed-off-by: Sugu Sougoumarane --- go/vt/binlog/grpcbinlogplayer/player.go | 2 +- go/vt/discovery/utils.go | 2 +- go/vt/discovery/utils_test.go | 67 ++++++++++--------- .../vreplication/vstreamer_client.go | 2 +- .../vreplication/vstreamer_client_test.go | 16 +---- 5 files changed, 40 insertions(+), 49 deletions(-) diff --git a/go/vt/binlog/grpcbinlogplayer/player.go b/go/vt/binlog/grpcbinlogplayer/player.go index 21eb81cbb38..1a854f5c35f 100644 --- a/go/vt/binlog/grpcbinlogplayer/player.go +++ b/go/vt/binlog/grpcbinlogplayer/player.go @@ -52,7 +52,7 @@ func (client *client) Dial(tablet *topodatapb.Tablet) error { if err != nil { return err } - client.cc, err = grpcclient.Dial(addr, grpcclient.FailFast(false), opt) + client.cc, err = grpcclient.Dial(addr, grpcclient.FailFast(true), opt) if err != nil { return err } diff --git a/go/vt/discovery/utils.go b/go/vt/discovery/utils.go index 07e4642fdf3..ac57b032ded 100644 --- a/go/vt/discovery/utils.go +++ b/go/vt/discovery/utils.go @@ -30,7 +30,7 @@ func RemoveUnhealthyTablets(tabletStatsList []TabletStats) []TabletStats { // source and destination, and the source is not serving (disabled by // TabletControl). When we switch the tablet to 'worker', it will // go back to serving state. - if ts.Stats == nil || ts.Stats.HealthError != "" || IsReplicationLagHigh(&ts) { + if ts.Stats == nil || ts.Stats.HealthError != "" || ts.LastError != nil || IsReplicationLagHigh(&ts) { continue } result = append(result, ts) diff --git a/go/vt/discovery/utils_test.go b/go/vt/discovery/utils_test.go index 488ea45e713..4a0266072c9 100644 --- a/go/vt/discovery/utils_test.go +++ b/go/vt/discovery/utils_test.go @@ -17,6 +17,7 @@ limitations under the License. package discovery import ( + "errors" "testing" querypb "vitess.io/vitess/go/vt/proto/query" @@ -28,38 +29,35 @@ func TestRemoveUnhealthyTablets(t *testing.T) { desc string input []TabletStats want []TabletStats - }{ - { - desc: "tablets missing Stats", - input: []TabletStats{replica(1), replica(2)}, - want: []TabletStats{}, - }, - { - desc: "all tablets healthy", - input: []TabletStats{healthy(replica(1)), healthy(replica(2))}, - want: []TabletStats{healthy(replica(1)), healthy(replica(2))}, - }, - { - desc: "one unhealthy tablet (error)", - input: []TabletStats{healthy(replica(1)), unhealthyError(replica(2))}, - want: []TabletStats{healthy(replica(1))}, - }, - { - desc: "one unhealthy tablet (lag)", - input: []TabletStats{healthy(replica(1)), unhealthyLag(replica(2))}, - want: []TabletStats{healthy(replica(1))}, - }, - { - desc: "no filtering by tablet type", - input: []TabletStats{healthy(master(1)), healthy(replica(2)), healthy(rdonly(3))}, - want: []TabletStats{healthy(master(1)), healthy(replica(2)), healthy(rdonly(3))}, - }, - { - desc: "non-serving tablets won't be removed", - input: []TabletStats{notServing(healthy(replica(1)))}, - want: []TabletStats{notServing(healthy(replica(1)))}, - }, - } + }{{ + desc: "tablets missing Stats", + input: []TabletStats{replica(1), replica(2)}, + want: []TabletStats{}, + }, { + desc: "all tablets healthy", + input: []TabletStats{healthy(replica(1)), healthy(replica(2))}, + want: []TabletStats{healthy(replica(1)), healthy(replica(2))}, + }, { + desc: "one unhealthy tablet (error)", + input: []TabletStats{healthy(replica(1)), unhealthyError(replica(2))}, + want: []TabletStats{healthy(replica(1))}, + }, { + desc: "one error tablet", + input: []TabletStats{healthy(replica(1)), unhealthyLastError(replica(2))}, + want: []TabletStats{healthy(replica(1))}, + }, { + desc: "one unhealthy tablet (lag)", + input: []TabletStats{healthy(replica(1)), unhealthyLag(replica(2))}, + want: []TabletStats{healthy(replica(1))}, + }, { + desc: "no filtering by tablet type", + input: []TabletStats{healthy(master(1)), healthy(replica(2)), healthy(rdonly(3))}, + want: []TabletStats{healthy(master(1)), healthy(replica(2)), healthy(rdonly(3))}, + }, { + desc: "non-serving tablets won't be removed", + input: []TabletStats{notServing(healthy(replica(1)))}, + want: []TabletStats{notServing(healthy(replica(1)))}, + }} for _, tc := range testcases { got := RemoveUnhealthyTablets(tc.input) @@ -123,6 +121,11 @@ func unhealthyError(ts TabletStats) TabletStats { return ts } +func unhealthyLastError(ts TabletStats) TabletStats { + ts.LastError = errors.New("err") + return ts +} + func notServing(ts TabletStats) TabletStats { ts.Serving = false return ts diff --git a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go index dba2f9bb080..cf5f604166f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go @@ -103,7 +103,7 @@ func (vsClient *TabletVStreamerClient) Open(ctx context.Context) (err error) { } vsClient.isOpen = true - vsClient.tsQueryService, err = tabletconn.GetDialer()(vsClient.tablet, grpcclient.FailFast(false)) + vsClient.tsQueryService, err = tabletconn.GetDialer()(vsClient.tablet, grpcclient.FailFast(true)) return err } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go index 1afe8e1a906..1a50bfe18dc 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go @@ -26,9 +26,6 @@ import ( "golang.org/x/net/context" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/vt/vttablet/queryservice" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" @@ -40,10 +37,7 @@ func TestTabletVStreamerClientOpen(t *testing.T) { defer deleteTablet(tablet) type fields struct { - isOpen bool - tablet *topodatapb.Tablet - target *querypb.Target - tsQueryService queryservice.QueryService + tablet *topodatapb.Tablet } type args struct { ctx context.Context @@ -100,10 +94,7 @@ func TestTabletVStreamerClientClose(t *testing.T) { defer deleteTablet(tablet) type fields struct { - isOpen bool - tablet *topodatapb.Tablet - target *querypb.Target - tsQueryService queryservice.QueryService + tablet *topodatapb.Tablet } type args struct { ctx context.Context @@ -296,7 +287,6 @@ func TestNewMySQLVStreamerClient(t *testing.T) { func TestMySQLVStreamerClientOpen(t *testing.T) { type fields struct { - isOpen bool sourceConnParams *mysql.ConnParams } type args struct { @@ -365,8 +355,6 @@ func TestMySQLVStreamerClientClose(t *testing.T) { type fields struct { isOpen bool sourceConnParams *mysql.ConnParams - vsEngine *vstreamer.Engine - sourceSe *schema.Engine } type args struct { ctx context.Context From 29b576981e23a90575dea559c62c5ca4a26837fd Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 28 Dec 2019 23:08:31 -0800 Subject: [PATCH 037/825] vrepl: fail vstream if curpos is less than request It's possible that vrepication can be ahead of a source repica. In those cases, the vstreamer of the source should fail requests. There are situations where mysql has unpredictable behavior, especially if the requested position is one above the current one. Signed-off-by: Sugu Sougoumarane --- .../tabletserver/vstreamer/engine_test.go | 10 ++--- .../tabletserver/vstreamer/vstreamer.go | 18 ++++---- .../tabletserver/vstreamer/vstreamer_test.go | 41 +++++++++++++++++++ 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine_test.go b/go/vt/vttablet/tabletserver/vstreamer/engine_test.go index b45e2402654..ca4eef44d98 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine_test.go @@ -81,16 +81,16 @@ func TestUpdateVSchema(t *testing.T) { // We have to start at least one stream to start the vschema watcher. ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - + cancel() filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ Match: "/.*/", }}, } - - _ = startStream(ctx, t, filter, "") - cancel() + // Stream should terminate immediately due to canceled context. + _ = engine.Stream(ctx, "current", filter, func(_ []*binlogdatapb.VEvent) error { + return nil + }) startCount := expectUpdateCount(t, 1) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index d60e56bbc99..fe1a049adc6 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -125,15 +125,20 @@ func (vs *vstreamer) Cancel() { func (vs *vstreamer) Stream() error { defer vs.cancel() + curpos, err := vs.currentPosition() + if err != nil { + return vterrors.Wrap(err, "could not obtain current position") + } if vs.startPos == "current" { - if err := vs.useCurrentPosition(); err != nil { - return vterrors.Wrap(err, "could not obtain current position") - } + vs.pos = curpos } else { pos, err := mysql.DecodePosition(vs.startPos) if err != nil { return vterrors.Wrap(err, "could not decode position") } + if !curpos.AtLeast(pos) { + return fmt.Errorf("requested position %v is ahead of current position %v", mysql.EncodePosition(pos), mysql.EncodePosition(curpos)) + } vs.pos = pos } @@ -157,14 +162,13 @@ func (vs *vstreamer) Stream() error { return wrapError(err, vs.pos) } -func (vs *vstreamer) useCurrentPosition() error { +func (vs *vstreamer) currentPosition() (mysql.Position, error) { conn, err := mysql.Connect(vs.ctx, vs.cp) if err != nil { - return err + return mysql.Position{}, err } defer conn.Close() - vs.pos, err = conn.MasterPosition() - return err + return conn.MasterPosition() } func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.BinlogEvent) error { diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 02cdc496b9e..abcab7cf476 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -18,6 +18,7 @@ package vstreamer import ( "fmt" + "strconv" "strings" "testing" "time" @@ -1085,6 +1086,46 @@ func TestHeartbeat(t *testing.T) { assert.Equal(t, binlogdatapb.VEventType_HEARTBEAT, evs[0].Type) } +func TestNoFutureGTID(t *testing.T) { + if testing.Short() { + t.Skip() + } + + // Execute something to make sure we have ranges in GTIDs. + execStatements(t, []string{ + "create table stream1(id int, val varbinary(128), primary key(id))", + }) + defer execStatements(t, []string{ + "drop table stream1", + }) + engine.se.Reload(context.Background()) + + pos := masterPosition(t) + t.Logf("current position: %v", pos) + // Both mysql and mariadb have '-' in their gtids. + // Invent a GTID in the future. + index := strings.LastIndexByte(pos, '-') + num, err := strconv.Atoi(pos[index+1:]) + require.NoError(t, err) + future := pos[:index+1] + fmt.Sprintf("%d", num+1) + t.Logf("future position: %v", future) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ch := make(chan []*binlogdatapb.VEvent) + go func() { + for range ch { + } + }() + defer close(ch) + err = vstream(ctx, t, future, nil, ch) + want := "is ahead of current position" + if err == nil || !strings.Contains(err.Error(), want) { + t.Errorf("err: %v, must contain %s", err, want) + } +} + func runCases(t *testing.T, filter *binlogdatapb.Filter, testcases []testcase, position string) { t.Helper() ctx, cancel := context.WithCancel(context.Background()) From 6151d4ca38631a716cca85e04a9c177c4bac5871 Mon Sep 17 00:00:00 2001 From: deepthi Date: Wed, 5 Feb 2020 17:16:47 -0800 Subject: [PATCH 038/825] fix failing plan_test, add more rewriting tests for database() Signed-off-by: deepthi --- go/vt/sqlparser/expression_rewriting_test.go | 15 +++++++++++++++ .../vtgate/planbuilder/testdata/filter_cases.txt | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/go/vt/sqlparser/expression_rewriting_test.go b/go/vt/sqlparser/expression_rewriting_test.go index d8d53de3ebb..cf9eef04cef 100644 --- a/go/vt/sqlparser/expression_rewriting_test.go +++ b/go/vt/sqlparser/expression_rewriting_test.go @@ -44,6 +44,11 @@ func TestRewrites(in *testing.T) { expected: "SELECT :__vtdbname as `database()`", db: true, liid: false, }, + { + in: "SELECT database() from test", + expected: "SELECT database() from test", + db: false, liid: false, + }, { in: "SELECT last_insert_id() as test", expected: "SELECT :__lastInsertId as test", @@ -54,6 +59,16 @@ func TestRewrites(in *testing.T) { expected: "SELECT :__lastInsertId + :__vtdbname as `last_insert_id() + database()`", db: true, liid: true, }, + { + in: "select (select database()) from test", + expected: "select (select database() from dual) from test", + db: false, liid: false, + }, + { + in: "select (select database() from dual) from test", + expected: "select (select database() from dual) from test", + db: false, liid: false, + }, { in: "select (select database() from dual) from dual", expected: "select (select :__vtdbname as `database()` from dual) as `(select database() from dual)` from dual", diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index 38cf17ea0a9..53fae69ba05 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -1078,7 +1078,7 @@ "Name": "user", "Sharded": true }, - "Query": "select id from user where :__vtdbname", + "Query": "select id from user where database()", "FieldQuery": "select id from user where 1 != 1", "Table": "user" } From a5e363b58b3090ad4f7ebfb1c5f651b523920ff2 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Thu, 6 Feb 2020 10:50:03 +0530 Subject: [PATCH 039/825] address review comments Signed-off-by: Ajeet jain --- .../mergesharding_int_test.go} | 6 +++--- .../endtoend/sharding/mergesharding/mergesharding_base.go | 2 +- .../mergesharding/string/mergesharding_string_test.go | 6 +++--- test/config.json | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) rename go/test/endtoend/sharding/mergesharding/{v3/mergesharding_v3_test.go => int/mergesharding_int_test.go} (76%) diff --git a/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go b/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go similarity index 76% rename from go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go rename to go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go index 2eb6a058a1b..0fd07989922 100644 --- a/go/test/endtoend/sharding/mergesharding/v3/mergesharding_v3_test.go +++ b/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go @@ -22,8 +22,8 @@ import ( sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" ) -// TestV3MergeSharding - tests merge sharding using a INT column -func TestV3MergeSharding(t *testing.T) { - sharding.TestMergesharding(t, false) +// TestMergeShardingIntShardingKey - tests merge sharding using a INT column +func TestMergeShardingIntShardingKey(t *testing.T) { + sharding.TestMergesharding(t /* useVarbinaryShardingKeyType */, false) } diff --git a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go index cc5d136cc1c..228452877c3 100644 --- a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go +++ b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go @@ -88,7 +88,7 @@ var ( shard2 = &cluster.Shard{Name: "80-"} // merge shard - // merging -40 & 40-80 t0 -80 + // merging -40 & 40-80 to -80 shard3 = &cluster.Shard{Name: "-80"} // Sharding keys diff --git a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go index 80fd40f89cc..adf07bd7fbf 100644 --- a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go +++ b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go @@ -22,8 +22,8 @@ import ( sharding "vitess.io/vitess/go/test/endtoend/sharding/mergesharding" ) -// TestV3MergeShardingString - tests merge sharding using a Byte column -func TestV3MergeShardingString(t *testing.T) { - sharding.TestMergesharding(t, true) +// TestMergeShardingStringShardingKey - tests merge sharding using a String column +func TestMergeShardingStringShardingKey(t *testing.T) { + sharding.TestMergesharding(t /* useVarbinaryShardingKeyType */, true) } diff --git a/test/config.json b/test/config.json index 951c7f9770b..a0884f82a1c 100644 --- a/test/config.json +++ b/test/config.json @@ -297,8 +297,8 @@ ] }, "merge_sharding": { - "File": "mergesharding_v3_test.go", - "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/v3"], + "File": "mergesharding_int_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/int"], "Command": [], "Manual": false, "Shard": 22, From 85410f111532617c51c3fd7d59fe71f16a89aac5 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Thu, 6 Feb 2020 11:00:57 +0530 Subject: [PATCH 040/825] minor changes to comment to avoid override by go fmt Signed-off-by: Ajeet jain --- .../sharding/mergesharding/int/mergesharding_int_test.go | 2 +- .../sharding/mergesharding/string/mergesharding_string_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go b/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go index 0fd07989922..94efd03bf4e 100644 --- a/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go +++ b/go/test/endtoend/sharding/mergesharding/int/mergesharding_int_test.go @@ -24,6 +24,6 @@ import ( // TestMergeShardingIntShardingKey - tests merge sharding using a INT column func TestMergeShardingIntShardingKey(t *testing.T) { - sharding.TestMergesharding(t /* useVarbinaryShardingKeyType */, false) + sharding.TestMergesharding(t, false /* useVarbinaryShardingKeyType */) } diff --git a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go index adf07bd7fbf..95b1dbf01f7 100644 --- a/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go +++ b/go/test/endtoend/sharding/mergesharding/string/mergesharding_string_test.go @@ -24,6 +24,6 @@ import ( // TestMergeShardingStringShardingKey - tests merge sharding using a String column func TestMergeShardingStringShardingKey(t *testing.T) { - sharding.TestMergesharding(t /* useVarbinaryShardingKeyType */, true) + sharding.TestMergesharding(t, true /* useVarbinaryShardingKeyType */) } From b2f1b353667378a6d30e45c5bb552b728b8fdd0b Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 4 Feb 2020 20:01:35 +0100 Subject: [PATCH 041/825] Only rewrite database() against dual Signed-off-by: Andres Taylor --- go/vt/sqlparser/expression_rewriting.go | 28 +++++++++++++++++--- go/vt/sqlparser/expression_rewriting_test.go | 23 ++++++++-------- go/vt/vtgate/executor.go | 1 - 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/go/vt/sqlparser/expression_rewriting.go b/go/vt/sqlparser/expression_rewriting.go index 89a5c9ebfe8..fb934f266cd 100644 --- a/go/vt/sqlparser/expression_rewriting.go +++ b/go/vt/sqlparser/expression_rewriting.go @@ -32,6 +32,7 @@ func PrepareAST(in Statement, bindVars map[string]*querypb.BindVariable, prefix // RewriteAST rewrites the whole AST, replacing function calls and adding column aliases to queries func RewriteAST(in Statement) (*RewriteASTResult, error) { er := new(expressionRewriter) + er.shouldRewriteDatabaseFunc = shouldRewriteDatabaseFunc(in) Rewrite(in, er.goingDown, nil) return &RewriteASTResult{ @@ -41,6 +42,25 @@ func RewriteAST(in Statement) (*RewriteASTResult, error) { }, nil } +func shouldRewriteDatabaseFunc(in Statement) bool { + selct, ok := in.(*Select) + if !ok { + return false + } + if len(selct.From) != 1 { + return false + } + aliasedTable, ok := selct.From[0].(*AliasedTableExpr) + if !ok { + return false + } + tableName, ok := aliasedTable.Expr.(TableName) + if !ok { + return false + } + return tableName.Name.String() == "dual" +} + // RewriteASTResult contains the rewritten ast and meta information about it type RewriteASTResult struct { AST Statement @@ -49,8 +69,9 @@ type RewriteASTResult struct { } type expressionRewriter struct { - lastInsertID, database bool - err error + lastInsertID, database bool + shouldRewriteDatabaseFunc bool + err error } const ( @@ -67,6 +88,7 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { buf := NewTrackedBuffer(nil) node.Expr.Format(buf) inner := new(expressionRewriter) + inner.shouldRewriteDatabaseFunc = er.shouldRewriteDatabaseFunc tmp := Rewrite(node.Expr, inner.goingDown, nil) newExpr, ok := tmp.(Expr) if !ok { @@ -91,7 +113,7 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { cursor.Replace(bindVarExpression(LastInsertIDName)) er.lastInsertID = true } - case node.Name.EqualString("database"): + case node.Name.EqualString("database") && er.shouldRewriteDatabaseFunc: if len(node.Exprs) > 0 { er.err = vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Syntax error. DATABASE() takes no arguments") } else { diff --git a/go/vt/sqlparser/expression_rewriting_test.go b/go/vt/sqlparser/expression_rewriting_test.go index 9bd5cf89375..d8d53de3ebb 100644 --- a/go/vt/sqlparser/expression_rewriting_test.go +++ b/go/vt/sqlparser/expression_rewriting_test.go @@ -55,14 +55,19 @@ func TestRewrites(in *testing.T) { db: true, liid: true, }, { - in: "select (select database() from test) from test", - expected: "select (select :__vtdbname as `database()` from test) as `(select database() from test)` from test", + in: "select (select database() from dual) from dual", + expected: "select (select :__vtdbname as `database()` from dual) as `(select database() from dual)` from dual", db: true, liid: false, }, { in: "select id from user where database()", - expected: "select id from user where :__vtdbname", - db: true, liid: false, + expected: "select id from user where database()", + db: false, liid: false, + }, + { + in: "select table_name from information_schema.tables where table_schema = database()", + expected: "select table_name from information_schema.tables where table_schema = database()", + db: false, liid: false, }, } @@ -77,16 +82,10 @@ func TestRewrites(in *testing.T) { expected, err := Parse(tc.expected) require.NoError(t, err) - s := toString(expected) - require.Equal(t, s, toString(result.AST)) + s := String(expected) + require.Equal(t, s, String(result.AST)) require.Equal(t, tc.liid, result.NeedLastInsertID, "should need last insert id") require.Equal(t, tc.db, result.NeedDatabase, "should need database name") }) } } - -func toString(node SQLNode) string { - buf := NewTrackedBuffer(nil) - node.Format(buf) - return buf.String() -} diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 725ec31f2c9..940695cf1af 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -282,7 +282,6 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql sql = comments.Leading + normalized + comments.Trailing if rewriteResult.NeedDatabase { keyspace, _, _, _ := e.ParseDestinationTarget(safeSession.TargetString) - log.Warningf("This is the keyspace name: ---> %v", keyspace) if keyspace == "" { bindVars[sqlparser.DBVarName] = sqltypes.NullBindVariable } else { From f245c5a6388bb2714dce8d668475c91f71b6082f Mon Sep 17 00:00:00 2001 From: deepthi Date: Wed, 5 Feb 2020 17:16:47 -0800 Subject: [PATCH 042/825] fix failing plan_test, add more rewriting tests for database() Signed-off-by: deepthi --- go/vt/sqlparser/expression_rewriting_test.go | 15 +++++++++++++++ .../vtgate/planbuilder/testdata/filter_cases.txt | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/go/vt/sqlparser/expression_rewriting_test.go b/go/vt/sqlparser/expression_rewriting_test.go index d8d53de3ebb..cf9eef04cef 100644 --- a/go/vt/sqlparser/expression_rewriting_test.go +++ b/go/vt/sqlparser/expression_rewriting_test.go @@ -44,6 +44,11 @@ func TestRewrites(in *testing.T) { expected: "SELECT :__vtdbname as `database()`", db: true, liid: false, }, + { + in: "SELECT database() from test", + expected: "SELECT database() from test", + db: false, liid: false, + }, { in: "SELECT last_insert_id() as test", expected: "SELECT :__lastInsertId as test", @@ -54,6 +59,16 @@ func TestRewrites(in *testing.T) { expected: "SELECT :__lastInsertId + :__vtdbname as `last_insert_id() + database()`", db: true, liid: true, }, + { + in: "select (select database()) from test", + expected: "select (select database() from dual) from test", + db: false, liid: false, + }, + { + in: "select (select database() from dual) from test", + expected: "select (select database() from dual) from test", + db: false, liid: false, + }, { in: "select (select database() from dual) from dual", expected: "select (select :__vtdbname as `database()` from dual) as `(select database() from dual)` from dual", diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index 38cf17ea0a9..53fae69ba05 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -1078,7 +1078,7 @@ "Name": "user", "Sharded": true }, - "Query": "select id from user where :__vtdbname", + "Query": "select id from user where database()", "FieldQuery": "select id from user where 1 != 1", "Table": "user" } From 31c8e55f89c2ca14057ea724910c46adedc8a6e2 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Thu, 6 Feb 2020 12:28:47 +0530 Subject: [PATCH 043/825] messaging: review changes. Signed-off-by: pradip parmar --- go/test/endtoend/cluster/cluster_util.go | 6 + go/test/endtoend/messaging/messaging_test.go | 104 +++++++++++------- go/test/endtoend/recovery/recovery_util.go | 5 - .../shardedrecovery/sharded_recovery_test.go | 15 +-- .../unshardedrecovery/recovery_test.go | 1 + 5 files changed, 81 insertions(+), 50 deletions(-) diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 8ec5cee2344..9fbd66cc9b2 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/require" tabletpb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtgate/vtgateconn" tmc "vitess.io/vitess/go/vt/vttablet/grpctmclient" ) @@ -120,3 +121,8 @@ func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet { portMap["grpc"] = int32(tabletGrpcPort) return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap} } + +func ExecuteQueriesUsingVtgate(t *testing.T, session *vtgateconn.VTGateSession, query string) { + _, err := session.Execute(context.Background(), query, nil) + assert.Nil(t, err) +} diff --git a/go/test/endtoend/messaging/messaging_test.go b/go/test/endtoend/messaging/messaging_test.go index 47c6059f10d..0c665e2cba8 100644 --- a/go/test/endtoend/messaging/messaging_test.go +++ b/go/test/endtoend/messaging/messaging_test.go @@ -30,34 +30,40 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/recovery" "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/vtgate/vtgateconn" ) func TestSharded(t *testing.T) { + // validate the messaging for sharded keyspace(user) testMessaging(t, "sharded_message", userKeyspace) } func TestUnsharded(t *testing.T) { + // validate messaging for unsharded keyspace(lookup) testMessaging(t, "unsharded_message", lookupKeyspace) } +// TestRepareting checks the client connection count after reparenting. func TestRepareting(t *testing.T) { name := "sharded_message" ctx := context.Background() - stream, err := NewConn(ctx, clusterInstance) + // start grpc connection with vtgate and validate client + // connection counts in tablets + stream, err := VtgateGrpcConn(ctx, clusterInstance) require.Nil(t, err) defer stream.Close() - stream.MessageStream(userKeyspace, "", nil, name) + _, err = stream.MessageStream(userKeyspace, "", nil, name) + require.Nil(t, err) assert.Equal(t, 1, getClientCount(shard0Master)) assert.Equal(t, 0, getClientCount(shard0Replica)) assert.Equal(t, 1, getClientCount(shard1Master)) - // planned reparenting + // do planned reparenting, make one replica as master + // and validate client connection count in correspond tablets clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( "PlannedReparentShard", "-keyspace_shard", userKeyspace+"/-80", @@ -75,10 +81,12 @@ func TestRepareting(t *testing.T) { assert.Equal(t, 1, getClientCount(shard0Replica)) assert.Equal(t, 1, getClientCount(shard1Master)) session := stream.Session("@master", nil) - recovery.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (3,'hello world 3')") + cluster.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (3,'hello world 3')") + // validate that we have received inserted message stream.Next() + // make old master again as new master clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( "PlannedReparentShard", "-keyspace_shard", userKeyspace+"/-80", @@ -91,43 +99,56 @@ func TestRepareting(t *testing.T) { assert.Equal(t, 0, getClientCount(shard0Replica)) assert.Equal(t, 1, getClientCount(shard1Master)) - stream.MessageAck(ctx, userKeyspace, name, keyRange(3)) - + _, err = stream.MessageAck(ctx, userKeyspace, name, keyRange(3)) + assert.Nil(t, err) } +// TestConnection validate the connection count and message streaming. func TestConnection(t *testing.T) { name := "sharded_message" + // create two grpc connection with vtgate and verify + // client connection count in vttablet of the master assert.Equal(t, 0, getClientCount(shard0Master)) assert.Equal(t, 0, getClientCount(shard1Master)) ctx := context.Background() - stream, err := NewConn(ctx, clusterInstance) + // first connection with vtgate + stream, err := VtgateGrpcConn(ctx, clusterInstance) require.Nil(t, err) - stream.MessageStream(userKeyspace, "", nil, name) - + _, err = stream.MessageStream(userKeyspace, "", nil, name) + require.Nil(t, err) + // validate client count of vttablet assert.Equal(t, 1, getClientCount(shard0Master)) assert.Equal(t, 1, getClientCount(shard1Master)) - - stream1, err := NewConn(ctx, clusterInstance) + // second connection with vtgate, secont connection + // will only be used for client connection counts + stream1, err := VtgateGrpcConn(ctx, clusterInstance) require.Nil(t, err) - stream1.MessageStream(userKeyspace, "", nil, name) - + _, err = stream1.MessageStream(userKeyspace, "", nil, name) + require.Nil(t, err) + // validate client count of vttablet assert.Equal(t, 2, getClientCount(shard0Master)) assert.Equal(t, 2, getClientCount(shard1Master)) + // insert data in master and validate that we receive this + // in message stream session := stream.Session("@master", nil) - recovery.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (2,'hello world 2')") - recovery.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (5,'hello world 5')") - - stream.Next() - stream.Next() - - stream.MessageAck(ctx, userKeyspace, name, keyRange(2, 5)) + // insert data in master + cluster.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (2,'hello world 2')") + cluster.ExecuteQueriesUsingVtgate(t, session, "insert into sharded_message (id, message) values (5,'hello world 5')") + // validate in msg stream + _, err = stream.Next() + assert.Nil(t, err) + _, err = stream.Next() + assert.Nil(t, err) + _, err = stream.MessageAck(ctx, userKeyspace, name, keyRange(2, 5)) + assert.Nil(t, err) + // After closing one stream, ensure vttablets have dropped it. stream.Close() - time.Sleep(1 * time.Second) + time.Sleep(time.Second) assert.Equal(t, 1, getClientCount(shard0Master)) assert.Equal(t, 1, getClientCount(shard1Master)) @@ -136,16 +157,17 @@ func TestConnection(t *testing.T) { func testMessaging(t *testing.T, name, ks string) { ctx := context.Background() - stream, err := NewConn(ctx, clusterInstance) + stream, err := VtgateGrpcConn(ctx, clusterInstance) require.Nil(t, err) defer stream.Close() session := stream.Session("@master", nil) - recovery.ExecuteQueriesUsingVtgate(t, session, "insert into "+name+" (id, message) values (1,'hello world 1')") - recovery.ExecuteQueriesUsingVtgate(t, session, "insert into "+name+" (id, message) values (4,'hello world 4')") + cluster.ExecuteQueriesUsingVtgate(t, session, "insert into "+name+" (id, message) values (1,'hello world 1')") + cluster.ExecuteQueriesUsingVtgate(t, session, "insert into "+name+" (id, message) values (4,'hello world 4')") // validate fields - res := stream.MessageStream(ks, "", nil, name) + res, err := stream.MessageStream(ks, "", nil, name) + require.Nil(t, err) require.Equal(t, 3, len(res.Fields)) validateField(t, res.Fields[0], "id", query.Type_INT64) validateField(t, res.Fields[1], "time_scheduled", query.Type_INT64) @@ -153,12 +175,14 @@ func testMessaging(t *testing.T, name, ks string) { // validate recieved msgs resMap := make(map[string]string) - res = stream.Next() + res, err = stream.Next() + require.Nil(t, err) for _, row := range res.Rows { resMap[row[0].ToString()] = row[2].ToString() } - res = stream.Next() + res, err = stream.Next() + require.Nil(t, err) for _, row := range res.Rows { resMap[row[0].ToString()] = row[2].ToString() } @@ -166,16 +190,19 @@ func testMessaging(t *testing.T, name, ks string) { assert.Equal(t, "hello world 1", resMap["1"]) assert.Equal(t, "hello world 4", resMap["4"]) + resMap = make(map[string]string) // validate message ack with id 4 count, err := stream.MessageAck(ctx, ks, name, keyRange(4)) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, int64(1), count) - res = stream.Next() + res, err = stream.Next() + require.Nil(t, err) for _, row := range res.Rows { resMap[row[0].ToString()] = row[2].ToString() } - res = stream.Next() + res, err = stream.Next() + require.Nil(t, err) for _, row := range res.Rows { resMap[row[0].ToString()] = row[2].ToString() } @@ -201,8 +228,8 @@ type VTGateStream struct { *vtgateconn.VTGateConn } -// NewConn create new msg stream for grpc connection with vtgate. -func NewConn(ctx context.Context, cluster *cluster.LocalProcessCluster) (*VTGateStream, error) { +// VtgateGrpcConn create new msg stream for grpc connection with vtgate. +func VtgateGrpcConn(ctx context.Context, cluster *cluster.LocalProcessCluster) (*VTGateStream, error) { stream := new(VTGateStream) stream.ctx = ctx stream.host = fmt.Sprintf("%s:%d", cluster.Hostname, cluster.VtgateProcess.GrpcPort) @@ -215,23 +242,24 @@ func NewConn(ctx context.Context, cluster *cluster.LocalProcessCluster) (*VTGate } // MessageStream strarts the stream for the corresponding connection. -func (stream *VTGateStream) MessageStream(ks, shard string, keyRange *topodata.KeyRange, name string) *sqltypes.Result { +func (stream *VTGateStream) MessageStream(ks, shard string, keyRange *topodata.KeyRange, name string) (*sqltypes.Result, error) { + // start message stream which send received message to the respChan go stream.VTGateConn.MessageStream(stream.ctx, ks, shard, keyRange, name, func(s *sqltypes.Result) error { stream.respChan <- s return nil }) - + // wait for field details return stream.Next() } // Next reads the new msg available in stream. -func (stream *VTGateStream) Next() *sqltypes.Result { +func (stream *VTGateStream) Next() (*sqltypes.Result, error) { ticker := time.Tick(10 * time.Second) select { case s := <-stream.respChan: - return s + return s, nil case <-ticker: - panic(fmt.Errorf("time limit exceeded")) + return nil, fmt.Errorf("time limit exceeded") } } diff --git a/go/test/endtoend/recovery/recovery_util.go b/go/test/endtoend/recovery/recovery_util.go index c3ef62b1bc0..68c8994a5ec 100644 --- a/go/test/endtoend/recovery/recovery_util.go +++ b/go/test/endtoend/recovery/recovery_util.go @@ -33,11 +33,6 @@ func VerifyQueriesUsingVtgate(t *testing.T, session *vtgateconn.VTGateSession, q assert.Equal(t, value, fmt.Sprintf("%v", qr.Rows[0][0])) } -func ExecuteQueriesUsingVtgate(t *testing.T, session *vtgateconn.VTGateSession, query string) { - _, err := session.Execute(context.Background(), query, nil) - assert.Nil(t, err) -} - func RestoreTablet(t *testing.T, localCluster *cluster.LocalProcessCluster, tablet *cluster.Vttablet, restoreKSName string, shardName string, keyspaceName string, commonTabletArg []string) { err := cluster.ResetTabletDirectory(*tablet) assert.Nil(t, err) diff --git a/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go b/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go index e08e4cdfcc7..4366a565b82 100644 --- a/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go +++ b/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" "testing" + "vitess.io/vitess/go/test/endtoend/recovery" "github.com/stretchr/testify/assert" @@ -236,11 +237,11 @@ func TestUnShardedRecoveryAfterSharding(t *testing.T) { recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from recovery_keyspace.vt_insert_test", "INT64(2)") // check that new tablet is accessible with 'use ks' - recovery.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace@replica`") + cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace@replica`") recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") // check that new tablet is accessible with `use ks:shard` - recovery.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:0@replica`") + cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:0@replica`") recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") vtgateConn.Close() @@ -409,8 +410,8 @@ func TestShardedRecovery(t *testing.T) { vtgateConn, err := vtgateconn.Dial(context.Background(), grpcAddress) assert.Nil(t, err) session := vtgateConn.Session("@master", nil) - recovery.ExecuteQueriesUsingVtgate(t, session, "insert into vt_insert_test (id, msg) values (2,'test 2')") - recovery.ExecuteQueriesUsingVtgate(t, session, "insert into vt_insert_test (id, msg) values (3,'test 3')") + cluster.ExecuteQueriesUsingVtgate(t, session, "insert into vt_insert_test (id, msg) values (2,'test 2')") + cluster.ExecuteQueriesUsingVtgate(t, session, "insert into vt_insert_test (id, msg) values (3,'test 3')") vtgateConn.Close() err = vtgateInstance.TearDown() @@ -454,15 +455,15 @@ func TestShardedRecovery(t *testing.T) { recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from recovery_keyspace.vt_insert_test", "INT64(2)") // check that new keyspace is accessible with 'use ks' - recovery.ExecuteQueriesUsingVtgate(t, session, "use recovery_keyspace@replica") + cluster.ExecuteQueriesUsingVtgate(t, session, "use recovery_keyspace@replica") recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") // check that new tablet is accessible with use `ks:shard` - recovery.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:-80@replica`") + cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:-80@replica`") recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64("+shard0CountStr+")") recovery.VerifyQueriesUsingVtgate(t, session, "select id from vt_insert_test", "INT64("+shard0TestId+")") - recovery.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:80-@replica`") + cluster.ExecuteQueriesUsingVtgate(t, session, "use `recovery_keyspace:80-@replica`") recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64("+shard1CountStr+")") recovery.VerifyQueriesUsingVtgate(t, session, "select id from vt_insert_test", "INT64("+shard1TestId+")") diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go b/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go index 490267c86fa..6cfaee4ff4e 100644 --- a/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go @@ -21,6 +21,7 @@ import ( "fmt" "os/exec" "testing" + "vitess.io/vitess/go/test/endtoend/recovery" "vitess.io/vitess/go/vt/vtgate/vtgateconn" From ca1627f885a76dc04aa908451a6cf927c4f9ef3f Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Thu, 6 Feb 2020 15:52:31 +0530 Subject: [PATCH 044/825] messaging: 1 sec sleep added to avoid the invalid connection count. Signed-off-by: pradip parmar --- go/test/endtoend/messaging/messaging_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go/test/endtoend/messaging/messaging_test.go b/go/test/endtoend/messaging/messaging_test.go index 0c665e2cba8..70e6c3333c1 100644 --- a/go/test/endtoend/messaging/messaging_test.go +++ b/go/test/endtoend/messaging/messaging_test.go @@ -108,6 +108,9 @@ func TestConnection(t *testing.T) { name := "sharded_message" + // 1 sec sleep added to avoid invalid connection count + time.Sleep(time.Second) + // create two grpc connection with vtgate and verify // client connection count in vttablet of the master assert.Equal(t, 0, getClientCount(shard0Master)) From 6d8e8674de6fecd68c9297407b4dc23710c808b4 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 6 Feb 2020 11:23:10 +0100 Subject: [PATCH 045/825] Added end-to-end test for database() call Signed-off-by: Andres Taylor --- .../dbnameoverride/tablet_master_test.go | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go diff --git a/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go new file mode 100644 index 00000000000..1f88c12e0b0 --- /dev/null +++ b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package master + +import ( + "context" + "flag" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" + + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + vtParams mysql.ConnParams + hostname = "localhost" + keyspaceName = "ks" + cell = "zone1" + sqlSchema = ` + create table t1( + id bigint, + value varchar(16), + primary key(id) + ) Engine=InnoDB; +` + + vSchema = ` + { + "sharded": true, + "vindexes": { + "hash": { + "type": "hash" + } + }, + "tables": { + "t1": { + "column_vindexes": [ + { + "column": "id", + "name": "hash" + } + ] + } + } + }` +) + +const dbName = "myDbName" + +func TestMain(m *testing.M) { + flag.Parse() + + exitCode := func() int { + clusterInstance = cluster.NewCluster(cell, hostname) + defer clusterInstance.Teardown() + + // Start topo server + err := clusterInstance.StartTopo() + if err != nil { + return 1 + } + + // Set extra tablet args for lock timeout + clusterInstance.VtTabletExtraArgs = []string{ + "-init_db_name_override", dbName, + } + + // Start keyspace + keyspace := &cluster.Keyspace{ + Name: keyspaceName, + SchemaSQL: sqlSchema, + VSchema: vSchema, + } + + if err = clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil { + return 1 + } + + if err = clusterInstance.StartVtgate(); err != nil { + return 1 + } + vtParams = mysql.ConnParams{ + Host: clusterInstance.Hostname, + Port: clusterInstance.VtgateMySQLPort, + } + + return m.Run() + }() + os.Exit(exitCode) +} + +func TestDbNameOverride(t *testing.T) { + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + qr, err := conn.ExecuteFetch("SELECT database() FROM information_schema.tables WHERE table_schema = database()", 1000, true) + + require.NoError(t, err) + require.Equal(t, 1, len(qr.Rows), "did not get enough rows back") + require.Equal(t, strings.ToLower(dbName), qr.Rows[0][0].ToString()) +} From a2dd7a0b382975a9b94dcd20909d889543c343c5 Mon Sep 17 00:00:00 2001 From: Roni HE Date: Thu, 6 Feb 2020 08:14:51 -0800 Subject: [PATCH 046/825] Fix: Vitess show character set where right now the show character set are hardcoded to be filtered with where statement. since we don't really want to expand the results or forward the query to an underlying mysql because vitess only supports these 2 charsets. closes #5584 * separate show charset vs show charset in parser * changed the executor accordingly * use where_expression_opt in parser, changed AST and executor accordingly Signed-off-by: roni --- go.mod | 2 + go/vt/sqlparser/ast.go | 3 + go/vt/sqlparser/parse_test.go | 10 +- go/vt/sqlparser/sql.go | 3470 +++++++++++++++++---------------- go/vt/sqlparser/sql.y | 10 +- go/vt/vtgate/executor.go | 122 +- go/vt/vtgate/executor_test.go | 114 +- 7 files changed, 1971 insertions(+), 1760 deletions(-) diff --git a/go.mod b/go.mod index 5310d66e0b1..25077ce841a 100644 --- a/go.mod +++ b/go.mod @@ -52,6 +52,8 @@ require ( github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 68de87b3a29..7f30c6a1984 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -1191,6 +1191,9 @@ func (node *Show) Format(buf *TrackedBuffer) { if node.Type == "collation" && node.ShowCollationFilterOpt != nil { buf.Myprintf(" where %v", *node.ShowCollationFilterOpt) } + if node.Type == "charset" && node.ShowTablesOpt != nil { + buf.Myprintf("%v", node.ShowTablesOpt.Filter) + } if node.HasTable() { buf.Myprintf(" %v", node.Table) } diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 537a2c4ca37..b3683e717bb 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1138,13 +1138,19 @@ var ( output: "show charset", }, { input: "show character set like '%foo'", - output: "show charset", + output: "show charset like '%foo'", }, { input: "show charset", output: "show charset", }, { input: "show charset like '%foo'", - output: "show charset", + output: "show charset like '%foo'", + }, { + input: "show charset where 'charset' = 'utf8'", + output: "show charset where 'charset' = 'utf8'", + }, { + input: "show charset where 'charset' = '%foo'", + output: "show charset where 'charset' = '%foo'", }, { input: "show collation", output: "show collation", diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index ef0a39342ff..7063bd9dffd 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -821,7 +821,7 @@ var yyExca = [...]int{ -1, 399, 83, 841, -2, 608, - -1, 695, + -1, 697, 1, 355, 5, 355, 12, 355, @@ -845,17 +845,17 @@ var yyExca = [...]int{ 57, 355, 348, 355, -2, 373, - -1, 698, + -1, 700, 54, 44, 56, 44, -2, 48, - -1, 848, + -1, 852, 113, 647, -2, 643, - -1, 1079, + -1, 1081, 5, 30, -2, 440, - -1, 1109, + -1, 1111, 5, 29, -2, 580, -1, 1355, @@ -871,334 +871,304 @@ var yyExca = [...]int{ const yyPrivate = 57344 -const yyLast = 16546 +const yyLast = 16589 var yyAct = [...]int{ - 323, 1520, 1510, 1317, 1204, 1388, 1112, 1474, 1420, 353, - 651, 1130, 1257, 340, 57, 935, 962, 301, 1258, 1375, - 1291, 1254, 1113, 1042, 971, 933, 650, 3, 327, 1157, - 1264, 961, 81, 797, 1005, 1270, 266, 1229, 884, 266, - 292, 1183, 1070, 873, 811, 396, 975, 880, 711, 1136, - 937, 1174, 958, 922, 902, 850, 588, 1001, 692, 520, - 710, 390, 385, 915, 582, 594, 310, 266, 81, 325, - 382, 691, 266, 603, 266, 387, 700, 665, 300, 56, - 1513, 1497, 61, 1508, 1484, 293, 294, 295, 296, 1505, - 1318, 299, 991, 1024, 1496, 1483, 540, 1246, 1347, 525, - 253, 555, 314, 251, 1285, 255, 666, 1023, 63, 64, - 65, 66, 67, 1449, 616, 615, 625, 626, 618, 619, - 620, 621, 622, 623, 624, 617, 952, 365, 627, 371, - 372, 369, 370, 368, 367, 366, 1028, 261, 257, 258, - 259, 1286, 1287, 373, 374, 1022, 1145, 298, 576, 1144, - 953, 954, 1146, 712, 571, 713, 297, 1165, 572, 569, - 570, 984, 1206, 1378, 1395, 992, 1338, 557, 1336, 559, - 574, 291, 564, 565, 785, 1208, 786, 783, 1507, 1504, - 1475, 1203, 916, 1467, 1524, 1528, 976, 541, 883, 527, - 1421, 551, 255, 1209, 790, 1019, 1016, 1017, 254, 1015, - 556, 558, 978, 1423, 1280, 1429, 1230, 575, 774, 784, - 24, 25, 52, 27, 28, 1207, 787, 1131, 1133, 252, - 1279, 978, 1200, 1278, 523, 530, 268, 256, 1202, 43, - 1036, 1026, 1029, 1035, 29, 48, 49, 1088, 639, 640, - 978, 1456, 1358, 1085, 1232, 620, 621, 622, 623, 624, - 617, 1158, 266, 627, 1215, 38, 260, 266, 959, 54, - 1141, 1098, 1064, 266, 822, 706, 607, 627, 1021, 266, - 521, 1422, 81, 547, 81, 948, 81, 81, 1234, 81, - 1238, 81, 1233, 1450, 1231, 554, 816, 81, 617, 1236, - 1020, 627, 1522, 812, 1132, 1523, 992, 1521, 1235, 977, - 537, 819, 1465, 521, 602, 1191, 1482, 1430, 1428, 1303, - 1084, 1237, 1239, 1438, 1268, 1248, 857, 81, 977, 590, - 31, 32, 34, 33, 36, 1201, 50, 1199, 903, 1025, - 855, 856, 854, 591, 776, 1189, 519, 977, 578, 579, - 639, 640, 974, 972, 1027, 973, 639, 640, 37, 44, - 45, 970, 976, 46, 47, 35, 543, 544, 545, 821, - 1304, 601, 600, 534, 1083, 535, 1082, 70, 536, 39, - 40, 600, 41, 42, 714, 813, 601, 600, 602, 1044, - 266, 266, 266, 601, 600, 825, 826, 602, 553, 81, - 592, 1163, 985, 602, 352, 81, 903, 820, 1095, 981, - 602, 1470, 1190, 71, 597, 982, 690, 1195, 1192, 1185, - 1193, 1188, 1488, 1184, 601, 600, 1186, 1187, 618, 619, - 620, 621, 622, 623, 624, 617, 79, 1384, 627, 1529, - 1194, 602, 1463, 1383, 601, 600, 601, 600, 526, 907, - 1061, 1062, 1063, 1250, 668, 670, 672, 674, 676, 678, - 679, 602, 699, 602, 53, 1178, 393, 250, 1043, 581, - 704, 54, 395, 1490, 708, 560, 1177, 561, 562, 1530, - 563, 853, 566, 669, 671, 1166, 675, 677, 577, 680, - 1466, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 1344, 1402, 627, 616, 615, 625, 626, - 618, 619, 620, 621, 622, 623, 624, 617, 1381, 1212, - 627, 840, 842, 843, 1175, 528, 529, 841, 1047, 874, - 266, 875, 379, 380, 1147, 81, 1148, 22, 354, 51, - 266, 266, 81, 81, 81, 1071, 1426, 1506, 266, 1492, - 581, 266, 1426, 1478, 266, 1426, 581, 581, 266, 1320, - 81, 81, 1426, 1457, 1435, 81, 81, 81, 266, 81, - 81, 1426, 1425, 1373, 1372, 81, 81, 1158, 616, 615, - 625, 626, 618, 619, 620, 621, 622, 623, 624, 617, - 51, 1153, 627, 1350, 1360, 581, 1434, 305, 306, 1357, - 581, 1310, 1309, 799, 81, 1306, 1307, 1306, 1305, 266, - 1077, 581, 919, 581, 1300, 81, 886, 581, 827, 876, - 796, 791, 795, 777, 775, 772, 721, 720, 1137, 549, - 851, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 542, 533, 627, 532, 1255, 979, 320, - 1267, 846, 343, 342, 345, 346, 347, 348, 1137, 81, - 24, 344, 349, 848, 924, 927, 928, 929, 925, 829, - 926, 930, 919, 58, 1271, 1272, 395, 54, 395, 702, - 395, 395, 702, 395, 1107, 395, 1267, 1218, 844, 1108, - 888, 395, 81, 81, 24, 886, 1353, 893, 896, 266, - 24, 1437, 1267, 904, 942, 919, 701, 266, 266, 54, - 307, 266, 266, 918, 1077, 266, 266, 266, 81, 877, - 878, 605, 703, 1407, 705, 703, 773, 701, 1308, 1149, - 951, 81, 1077, 780, 781, 782, 943, 900, 1101, 919, - 945, 1100, 912, 54, 1277, 1077, 701, 707, 823, 54, - 580, 800, 801, 789, 1498, 1390, 802, 803, 804, 54, - 806, 807, 799, 986, 1365, 1006, 808, 809, 1296, 1271, - 1272, 1205, 941, 1152, 1002, 997, 996, 1391, 946, 949, - 950, 1009, 1515, 1511, 1298, 266, 81, 393, 81, 1274, - 1255, 966, 1179, 395, 266, 266, 266, 266, 266, 716, - 266, 266, 817, 793, 266, 81, 1124, 1276, 1122, 1121, - 552, 1125, 552, 1123, 552, 552, 1007, 552, 835, 552, - 1120, 266, 1502, 266, 266, 552, 1495, 1126, 266, 928, - 929, 311, 312, 1214, 1049, 595, 1003, 1004, 993, 994, - 995, 1500, 1059, 1058, 583, 51, 1170, 719, 596, 595, - 550, 593, 1162, 889, 890, 1472, 584, 895, 898, 899, - 636, 1471, 596, 638, 1405, 1160, 1052, 1154, 1351, 924, - 927, 928, 929, 925, 851, 926, 930, 1386, 848, 1012, - 792, 932, 911, 1443, 913, 914, 308, 309, 302, 303, - 1053, 649, 58, 653, 654, 655, 656, 657, 658, 659, - 660, 661, 1054, 664, 667, 667, 667, 673, 667, 667, - 673, 667, 681, 682, 683, 684, 685, 686, 1057, 696, - 1066, 1137, 1442, 1393, 573, 1089, 1056, 1517, 1516, 395, - 266, 266, 266, 266, 266, 1086, 395, 395, 395, 810, - 598, 1517, 266, 1453, 1379, 266, 818, 60, 62, 266, - 55, 1, 1109, 266, 395, 395, 1509, 1114, 1319, 395, - 395, 395, 1387, 395, 395, 1018, 1094, 1473, 1419, 395, - 395, 888, 81, 1290, 969, 960, 69, 1011, 1138, 1013, - 641, 642, 643, 644, 645, 646, 647, 648, 1150, 1116, - 1117, 518, 1119, 68, 1127, 1464, 1040, 968, 831, 1135, - 967, 1115, 1427, 1139, 1118, 1140, 847, 1377, 980, 605, - 1142, 1164, 395, 983, 1297, 1159, 1161, 1469, 727, 725, - 81, 81, 726, 724, 729, 728, 723, 279, 388, 931, - 1155, 1156, 715, 1060, 1008, 599, 72, 1198, 1197, 1014, - 815, 828, 567, 568, 281, 635, 1055, 1143, 394, 1262, - 81, 824, 587, 879, 1169, 1441, 1171, 1172, 1173, 1176, - 1392, 1093, 662, 552, 901, 1182, 266, 328, 1196, 905, - 552, 552, 552, 839, 341, 81, 338, 339, 830, 1106, - 1075, 1076, 609, 326, 318, 694, 909, 910, 552, 552, - 1167, 1168, 687, 552, 552, 552, 1211, 552, 552, 1092, - 885, 887, 923, 552, 552, 921, 393, 920, 383, 329, - 1273, 1269, 395, 693, 1217, 1346, 1448, 1247, 834, 963, - 1222, 81, 81, 1221, 1256, 395, 1228, 26, 1259, 59, - 313, 19, 1241, 987, 988, 989, 990, 1240, 18, 17, - 20, 16, 1052, 15, 14, 81, 1114, 1261, 538, 998, - 999, 1000, 30, 21, 848, 13, 12, 11, 10, 9, - 81, 1282, 81, 81, 1275, 8, 7, 6, 5, 4, - 304, 23, 1266, 2, 0, 1281, 0, 51, 1289, 0, - 395, 0, 395, 0, 0, 0, 0, 0, 1293, 0, - 266, 1288, 653, 0, 0, 1294, 1295, 0, 1284, 395, - 0, 0, 0, 0, 0, 0, 0, 0, 266, 0, - 0, 0, 1181, 0, 81, 0, 0, 81, 81, 81, - 266, 847, 0, 0, 0, 0, 81, 395, 0, 266, - 1301, 1302, 0, 0, 1312, 934, 0, 0, 0, 696, - 0, 1210, 0, 696, 0, 1325, 0, 1313, 0, 1315, - 0, 0, 0, 0, 1327, 0, 0, 0, 0, 849, - 0, 0, 858, 859, 860, 861, 862, 863, 864, 865, - 866, 867, 868, 869, 870, 871, 872, 1326, 0, 1334, - 0, 0, 0, 0, 0, 0, 0, 0, 1352, 0, - 0, 1361, 0, 0, 0, 0, 0, 0, 0, 81, - 1362, 0, 0, 0, 0, 0, 1114, 81, 0, 0, - 1371, 0, 0, 0, 552, 1150, 552, 908, 0, 0, - 0, 0, 81, 905, 0, 0, 1073, 0, 316, 81, - 1074, 0, 0, 552, 0, 0, 0, 0, 1079, 1080, - 1081, 0, 0, 0, 0, 1087, 0, 0, 1090, 1091, - 0, 0, 586, 0, 1097, 0, 0, 0, 1099, 0, - 963, 1102, 1103, 1104, 1105, 0, 395, 0, 81, 81, - 0, 81, 0, 1259, 0, 0, 81, 0, 81, 81, - 81, 266, 1406, 1129, 81, 0, 1065, 0, 264, 1413, - 0, 290, 0, 1408, 0, 1414, 1418, 1415, 1416, 1417, - 0, 81, 266, 1431, 1424, 0, 0, 0, 1380, 0, - 1382, 0, 0, 0, 1180, 395, 317, 0, 0, 386, - 1439, 0, 0, 0, 264, 0, 264, 1259, 0, 0, - 1454, 0, 0, 0, 637, 1394, 0, 81, 1462, 0, - 1461, 0, 0, 1432, 395, 1433, 1455, 0, 81, 81, - 0, 0, 0, 1476, 1110, 1111, 0, 0, 696, 696, - 696, 696, 696, 1220, 1480, 1477, 0, 81, 0, 395, - 1485, 0, 0, 934, 0, 1134, 0, 0, 266, 0, - 0, 696, 0, 0, 0, 0, 81, 0, 0, 0, - 695, 0, 1114, 1494, 0, 0, 0, 1251, 0, 0, - 0, 0, 0, 395, 0, 1067, 1068, 1069, 1501, 81, - 1499, 0, 905, 1385, 0, 1263, 1265, 0, 0, 0, - 0, 0, 1514, 0, 0, 1226, 1227, 0, 1503, 1525, - 0, 697, 0, 0, 0, 0, 0, 0, 0, 1265, - 0, 0, 0, 0, 0, 0, 0, 0, 963, 552, - 963, 0, 0, 0, 395, 0, 395, 1292, 0, 0, - 0, 0, 0, 611, 0, 614, 0, 263, 0, 0, - 0, 628, 629, 630, 631, 632, 633, 634, 552, 612, - 613, 610, 616, 615, 625, 626, 618, 619, 620, 621, - 622, 623, 624, 617, 0, 0, 627, 0, 384, 0, - 0, 0, 0, 522, 264, 524, 0, 0, 1316, 264, - 0, 1321, 1322, 1323, 1220, 264, 1349, 0, 0, 0, - 395, 264, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 585, 589, 1331, 1332, 0, 1333, 0, 0, 1335, - 0, 1337, 0, 0, 0, 0, 0, 1260, 608, 51, - 0, 0, 0, 0, 616, 615, 625, 626, 618, 619, - 620, 621, 622, 623, 624, 617, 0, 0, 627, 0, - 0, 1328, 905, 0, 0, 0, 0, 0, 0, 1330, - 0, 0, 0, 652, 0, 0, 0, 963, 0, 0, - 1339, 1340, 663, 395, 0, 1374, 0, 0, 0, 0, - 0, 1376, 0, 0, 0, 0, 0, 0, 0, 0, - 1354, 1355, 1356, 0, 1359, 0, 395, 1389, 0, 0, - 852, 1224, 1225, 395, 0, 0, 0, 0, 0, 0, - 0, 1370, 264, 264, 264, 1242, 1243, 0, 1244, 1245, - 625, 626, 618, 619, 620, 621, 622, 623, 624, 617, - 1252, 1253, 627, 0, 0, 0, 0, 696, 0, 0, - 0, 0, 1410, 1411, 0, 1412, 0, 0, 0, 0, - 1376, 0, 1376, 1376, 1376, 0, 0, 0, 1292, 0, - 0, 0, 0, 531, 0, 1345, 0, 0, 539, 0, - 0, 0, 0, 0, 546, 1376, 1401, 0, 0, 0, - 548, 0, 0, 0, 0, 0, 695, 0, 0, 0, - 695, 0, 1299, 0, 695, 0, 0, 1367, 1368, 1369, - 615, 625, 626, 618, 619, 620, 621, 622, 623, 624, - 617, 1468, 0, 627, 0, 0, 1389, 963, 0, 0, - 0, 0, 395, 395, 0, 0, 1444, 1445, 1446, 1447, - 552, 0, 0, 1451, 1452, 0, 1343, 0, 905, 0, - 0, 1487, 0, 0, 0, 1458, 1459, 1460, 0, 0, - 0, 0, 264, 1329, 0, 0, 0, 0, 0, 0, - 1493, 0, 264, 264, 0, 0, 0, 0, 0, 0, - 264, 0, 1260, 264, 0, 1409, 264, 0, 1481, 0, - 798, 0, 814, 1376, 0, 1486, 0, 0, 0, 0, - 264, 689, 0, 698, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1491, 0, 1436, 0, 0, 0, 837, - 838, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 0, 0, 627, 1260, 0, 51, 0, - 1223, 264, 0, 0, 0, 0, 0, 0, 0, 0, - 798, 0, 0, 0, 852, 0, 0, 0, 1526, 1527, - 616, 615, 625, 626, 618, 619, 620, 621, 622, 623, - 624, 617, 652, 0, 627, 891, 892, 0, 0, 0, - 0, 1396, 1397, 1398, 1399, 1400, 0, 0, 0, 1403, - 1404, 276, 317, 0, 0, 0, 0, 317, 317, 0, - 0, 317, 317, 317, 0, 0, 0, 906, 0, 1342, - 0, 0, 0, 0, 0, 0, 286, 0, 0, 695, - 695, 695, 695, 695, 1341, 0, 317, 317, 317, 317, - 0, 264, 0, 957, 695, 0, 1512, 0, 0, 264, - 939, 722, 695, 264, 264, 0, 0, 264, 947, 798, - 0, 778, 779, 0, 0, 0, 0, 0, 0, 788, - 0, 0, 384, 0, 0, 794, 0, 269, 0, 0, - 0, 0, 0, 0, 272, 0, 0, 0, 0, 805, - 0, 0, 280, 275, 616, 615, 625, 626, 618, 619, - 620, 621, 622, 623, 624, 617, 0, 0, 627, 616, - 615, 625, 626, 618, 619, 620, 621, 622, 623, 624, - 617, 0, 0, 627, 0, 278, 0, 264, 0, 0, - 836, 285, 0, 0, 0, 0, 264, 264, 264, 264, - 264, 0, 264, 264, 0, 0, 264, 0, 0, 1050, - 1051, 0, 589, 0, 0, 0, 0, 0, 270, 0, - 0, 0, 1518, 264, 0, 1045, 1046, 0, 0, 0, - 264, 0, 0, 0, 0, 798, 0, 0, 0, 0, - 1072, 0, 0, 0, 0, 282, 273, 317, 283, 284, - 289, 0, 0, 0, 274, 277, 0, 271, 288, 287, - 616, 615, 625, 626, 618, 619, 620, 621, 622, 623, - 624, 617, 0, 744, 627, 1078, 0, 0, 0, 0, - 917, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1096, 944, 317, 317, 616, 615, 625, 626, - 618, 619, 620, 621, 622, 623, 624, 617, 0, 0, - 627, 0, 0, 317, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 906, 264, 264, 264, 264, 264, 0, 0, 0, - 0, 0, 0, 0, 1128, 0, 0, 264, 0, 0, - 0, 939, 732, 0, 0, 264, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1010, 0, 0, 0, - 0, 0, 0, 0, 0, 1030, 1031, 1032, 1033, 1034, - 0, 1037, 1038, 0, 0, 1039, 0, 0, 695, 0, - 745, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1041, 0, 0, 0, 0, 0, 0, 1048, - 0, 0, 0, 758, 761, 762, 763, 764, 765, 766, - 0, 767, 768, 769, 770, 771, 746, 747, 748, 749, - 730, 731, 759, 1213, 733, 0, 734, 735, 736, 737, - 738, 739, 740, 741, 742, 743, 750, 751, 752, 753, - 754, 755, 756, 757, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 317, 0, - 0, 0, 0, 0, 1249, 0, 0, 0, 0, 0, - 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 760, 0, 0, 0, 0, 0, - 0, 798, 0, 0, 0, 0, 0, 0, 0, 0, - 906, 0, 0, 0, 0, 0, 1283, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 323, 1520, 1510, 1317, 1474, 1206, 327, 1114, 653, 1375, + 966, 1420, 1388, 1132, 340, 1291, 1257, 939, 1258, 301, + 57, 962, 353, 1115, 562, 652, 3, 1254, 975, 1009, + 329, 995, 81, 965, 1264, 1270, 266, 551, 799, 266, + 1229, 1072, 396, 888, 877, 1159, 815, 1185, 1176, 713, + 941, 884, 854, 906, 584, 590, 926, 520, 385, 1005, + 937, 354, 51, 712, 390, 605, 596, 266, 81, 979, + 325, 919, 266, 382, 266, 310, 387, 300, 702, 667, + 56, 1513, 1138, 1028, 1497, 1508, 668, 1484, 61, 693, + 540, 1505, 393, 989, 1318, 1496, 1483, 1027, 1246, 1347, + 525, 694, 292, 1286, 1287, 1285, 555, 314, 261, 257, + 258, 259, 956, 51, 63, 64, 65, 66, 67, 253, + 578, 306, 251, 298, 255, 365, 1032, 371, 372, 369, + 370, 368, 367, 366, 1147, 1026, 297, 1146, 957, 958, + 1148, 373, 374, 714, 1167, 715, 988, 293, 294, 295, + 296, 1208, 1378, 299, 1449, 618, 617, 627, 628, 620, + 621, 622, 623, 624, 625, 626, 619, 1395, 996, 629, + 1338, 573, 557, 276, 559, 574, 571, 572, 1336, 577, + 291, 566, 567, 788, 576, 1023, 1020, 1021, 1210, 1019, + 787, 785, 1507, 1504, 1475, 1205, 920, 1524, 286, 1467, + 980, 1528, 1421, 541, 1209, 556, 558, 527, 255, 982, + 1230, 1211, 982, 887, 792, 1423, 776, 254, 1280, 1279, + 1429, 1030, 1033, 789, 1202, 786, 537, 260, 1278, 982, + 1204, 523, 530, 1456, 268, 256, 1133, 1135, 252, 521, + 1040, 1358, 1160, 1039, 641, 642, 1215, 1143, 1232, 269, + 1090, 1100, 266, 1303, 1066, 826, 272, 266, 1025, 708, + 609, 963, 547, 266, 280, 275, 629, 816, 619, 266, + 1087, 629, 952, 823, 81, 1248, 81, 81, 820, 81, + 1024, 81, 1234, 1422, 1238, 521, 1233, 81, 1231, 534, + 554, 535, 70, 1236, 536, 604, 553, 278, 1465, 996, + 1438, 1268, 1235, 285, 1304, 1522, 981, 1482, 1523, 981, + 1521, 978, 976, 1134, 977, 1237, 1239, 81, 519, 1029, + 974, 980, 1430, 1428, 1450, 592, 981, 1203, 71, 1201, + 270, 861, 593, 716, 1031, 561, 907, 561, 561, 907, + 561, 1097, 561, 602, 911, 859, 860, 858, 561, 817, + 543, 544, 545, 641, 642, 639, 778, 282, 273, 604, + 283, 284, 289, 1165, 1193, 1470, 274, 277, 51, 271, + 288, 287, 599, 641, 642, 552, 603, 602, 985, 1488, + 266, 266, 266, 638, 986, 1529, 640, 1384, 1383, 81, + 1180, 1179, 1086, 604, 1191, 81, 594, 1063, 1064, 1065, + 580, 581, 620, 621, 622, 623, 624, 625, 626, 619, + 1168, 697, 629, 393, 651, 1490, 655, 656, 657, 658, + 659, 660, 661, 662, 663, 1530, 666, 669, 669, 669, + 675, 669, 669, 675, 669, 683, 684, 685, 686, 687, + 688, 692, 698, 603, 602, 526, 670, 672, 674, 676, + 678, 680, 681, 671, 673, 701, 677, 679, 1466, 682, + 604, 1192, 706, 829, 830, 710, 1197, 1194, 1187, 1195, + 1190, 825, 1186, 1402, 250, 1188, 1189, 1381, 352, 622, + 623, 624, 625, 626, 619, 1177, 1049, 629, 1085, 1196, + 1084, 618, 617, 627, 628, 620, 621, 622, 623, 624, + 625, 626, 619, 804, 54, 629, 1463, 603, 602, 824, + 79, 583, 603, 602, 857, 844, 846, 847, 1426, 1506, + 266, 845, 528, 529, 604, 81, 603, 602, 1320, 604, + 266, 266, 81, 81, 81, 878, 583, 879, 266, 379, + 380, 266, 22, 604, 266, 1073, 395, 1149, 266, 1150, + 81, 1492, 583, 1426, 1478, 81, 81, 81, 266, 81, + 81, 343, 342, 345, 346, 347, 348, 81, 81, 1160, + 344, 349, 1155, 618, 617, 627, 628, 620, 621, 622, + 623, 624, 625, 626, 619, 880, 561, 629, 803, 798, + 801, 603, 602, 561, 561, 561, 81, 797, 1250, 1426, + 583, 266, 305, 1426, 1457, 1426, 1425, 81, 604, 1373, + 1372, 561, 831, 779, 793, 777, 561, 561, 561, 1435, + 561, 561, 1360, 583, 1357, 583, 1310, 1309, 561, 561, + 1306, 1307, 1306, 1305, 851, 855, 1079, 583, 923, 583, + 890, 583, 1434, 856, 774, 582, 723, 722, 850, 704, + 704, 81, 852, 549, 542, 618, 617, 627, 628, 620, + 621, 622, 623, 624, 625, 626, 619, 897, 900, 629, + 833, 533, 848, 908, 532, 1300, 928, 931, 932, 933, + 929, 892, 930, 934, 81, 81, 1271, 1272, 1139, 983, + 24, 266, 705, 705, 707, 703, 24, 1139, 1255, 266, + 266, 1267, 51, 266, 266, 1218, 1267, 266, 266, 266, + 81, 890, 881, 882, 1109, 58, 946, 655, 703, 1110, + 1353, 1437, 922, 81, 24, 1407, 923, 1308, 1151, 697, + 904, 955, 923, 697, 393, 1103, 1102, 697, 916, 54, + 839, 1267, 1079, 703, 709, 54, 54, 967, 923, 827, + 1079, 801, 395, 791, 395, 395, 1079, 395, 1498, 395, + 938, 1390, 990, 947, 698, 395, 950, 949, 698, 997, + 998, 999, 945, 54, 953, 954, 307, 266, 81, 1365, + 81, 970, 1010, 1296, 1271, 1272, 266, 266, 266, 266, + 266, 1154, 266, 266, 1006, 607, 266, 81, 1001, 1000, + 1207, 1391, 1013, 1011, 627, 628, 620, 621, 622, 623, + 624, 625, 626, 619, 1515, 266, 629, 266, 266, 1511, + 1298, 1274, 266, 1502, 1255, 54, 991, 992, 993, 994, + 1007, 1008, 1181, 821, 795, 1126, 1277, 1276, 1123, 561, + 1127, 561, 1002, 1003, 1004, 1124, 1128, 1122, 932, 933, + 1125, 851, 1495, 1046, 311, 312, 1214, 1051, 561, 1500, + 1061, 1060, 1172, 721, 597, 1054, 597, 395, 550, 852, + 893, 894, 585, 718, 899, 902, 903, 598, 1164, 598, + 595, 855, 1472, 1055, 586, 1471, 1056, 1405, 1162, 856, + 928, 931, 932, 933, 929, 1156, 930, 934, 1351, 915, + 1386, 917, 918, 1016, 794, 936, 308, 309, 1059, 302, + 1443, 1068, 303, 1067, 58, 1442, 1058, 1393, 1139, 575, + 1517, 1516, 1517, 1091, 266, 266, 266, 266, 266, 1116, + 1088, 814, 600, 1453, 320, 1379, 266, 822, 832, 266, + 60, 62, 55, 266, 1, 1111, 1509, 266, 1319, 1387, + 1022, 1473, 1419, 1290, 697, 697, 697, 697, 697, 1096, + 973, 964, 69, 518, 892, 68, 81, 1464, 972, 697, + 971, 1427, 1377, 984, 1166, 987, 1152, 697, 1297, 1163, + 1469, 1112, 1113, 729, 727, 698, 698, 698, 698, 698, + 967, 1129, 728, 1137, 726, 731, 730, 889, 891, 725, + 938, 279, 1136, 395, 388, 1144, 935, 1140, 698, 717, + 395, 395, 395, 1117, 81, 81, 1120, 1171, 1012, 1173, + 1174, 1175, 601, 1169, 1170, 1161, 1118, 1119, 395, 1121, + 1141, 72, 1142, 395, 395, 395, 1200, 395, 395, 1199, + 1018, 819, 316, 569, 81, 395, 395, 1157, 1158, 570, + 1178, 281, 1062, 637, 1057, 1145, 394, 1262, 266, 828, + 589, 1441, 1392, 1095, 664, 905, 328, 81, 1198, 843, + 341, 338, 339, 834, 835, 1108, 561, 611, 326, 318, + 696, 689, 927, 925, 924, 607, 383, 1273, 395, 1269, + 695, 1220, 1213, 1217, 1346, 1448, 838, 26, 59, 1077, + 1078, 313, 19, 18, 17, 561, 20, 16, 15, 14, + 538, 1222, 30, 81, 81, 1247, 1116, 1256, 1094, 1221, + 21, 1184, 13, 1228, 12, 1251, 11, 1241, 10, 883, + 1240, 9, 8, 1259, 7, 6, 5, 81, 1261, 1054, + 4, 304, 23, 852, 2, 909, 0, 0, 0, 0, + 0, 0, 81, 0, 81, 81, 1275, 0, 0, 1282, + 0, 0, 913, 914, 1289, 0, 0, 0, 0, 0, + 0, 1281, 1260, 0, 51, 0, 967, 0, 967, 0, + 0, 0, 266, 0, 1293, 1288, 0, 0, 395, 0, + 0, 1301, 1302, 0, 0, 0, 0, 1266, 0, 0, + 266, 395, 0, 1294, 1295, 0, 81, 0, 0, 81, + 81, 81, 266, 0, 0, 0, 81, 0, 0, 266, + 0, 0, 0, 1284, 0, 1075, 0, 0, 0, 1076, + 0, 0, 0, 0, 0, 0, 0, 1081, 1082, 1083, + 1220, 0, 0, 0, 1089, 0, 0, 1092, 1093, 697, + 0, 1325, 560, 1099, 0, 0, 395, 1101, 395, 0, + 1104, 1105, 1106, 1107, 1334, 643, 644, 645, 646, 647, + 648, 649, 650, 0, 1116, 395, 0, 0, 0, 1327, + 698, 0, 1131, 0, 1352, 0, 0, 1361, 1312, 81, + 0, 1362, 0, 0, 0, 0, 0, 81, 0, 1152, + 1326, 1313, 1371, 1315, 0, 395, 0, 0, 1345, 0, + 0, 0, 81, 967, 0, 0, 0, 0, 0, 81, + 0, 0, 0, 0, 1331, 1332, 0, 1333, 0, 0, + 1335, 0, 1337, 0, 0, 0, 0, 1380, 0, 1382, + 1367, 1368, 1369, 1389, 0, 587, 591, 617, 627, 628, + 620, 621, 622, 623, 624, 625, 626, 619, 81, 81, + 629, 81, 610, 0, 1394, 0, 81, 0, 81, 81, + 81, 266, 1406, 561, 81, 1414, 1259, 1415, 1416, 1417, + 0, 1413, 1408, 0, 699, 0, 1374, 0, 1418, 1424, + 0, 81, 266, 0, 1431, 0, 0, 654, 0, 0, + 1439, 909, 1432, 0, 1433, 0, 665, 0, 0, 0, + 0, 0, 0, 0, 0, 1260, 0, 0, 1409, 0, + 263, 0, 1226, 1227, 1454, 0, 0, 81, 0, 0, + 1259, 1462, 1461, 0, 0, 1455, 0, 0, 81, 81, + 0, 0, 0, 588, 395, 0, 0, 0, 1436, 1477, + 1476, 384, 1480, 0, 0, 0, 522, 81, 524, 0, + 1116, 1485, 1389, 967, 0, 0, 0, 0, 266, 1260, + 0, 51, 0, 0, 0, 0, 81, 0, 0, 264, + 0, 0, 290, 0, 1494, 0, 0, 0, 0, 0, + 0, 0, 1182, 395, 0, 0, 0, 1499, 1501, 81, + 0, 0, 0, 0, 0, 0, 0, 317, 1503, 0, + 386, 0, 1514, 0, 0, 264, 0, 264, 0, 1525, + 0, 0, 395, 0, 0, 0, 0, 0, 563, 564, + 0, 565, 0, 568, 0, 0, 0, 0, 0, 579, + 0, 0, 0, 0, 0, 395, 853, 0, 0, 862, + 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, + 873, 874, 875, 876, 0, 0, 1328, 0, 0, 1512, + 0, 0, 0, 0, 1330, 0, 0, 0, 0, 395, + 0, 0, 0, 0, 0, 1339, 1340, 0, 909, 0, + 0, 1263, 1265, 0, 0, 0, 805, 0, 0, 0, + 0, 0, 0, 0, 912, 1354, 1355, 1356, 0, 1359, + 0, 0, 0, 0, 0, 1265, 0, 0, 818, 0, + 0, 0, 0, 0, 0, 0, 1370, 0, 0, 0, + 395, 0, 395, 1292, 0, 0, 531, 0, 0, 0, + 0, 539, 0, 0, 0, 841, 842, 546, 613, 0, + 616, 0, 0, 548, 0, 0, 630, 631, 632, 633, + 634, 635, 636, 0, 614, 615, 612, 618, 617, 627, + 628, 620, 621, 622, 623, 624, 625, 626, 619, 1350, + 0, 629, 0, 0, 1316, 0, 0, 1321, 1322, 1323, + 0, 1401, 0, 0, 395, 264, 0, 0, 654, 0, + 264, 895, 896, 0, 0, 0, 264, 0, 0, 0, + 0, 0, 264, 0, 0, 0, 0, 618, 617, 627, + 628, 620, 621, 622, 623, 624, 625, 626, 619, 0, + 0, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1444, 1445, 1446, 1447, 0, 909, 0, 1451, 1452, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 961, + 1458, 1459, 1460, 0, 691, 0, 700, 395, 0, 0, + 0, 0, 0, 0, 0, 1376, 0, 775, 0, 0, + 0, 0, 0, 0, 782, 783, 784, 0, 0, 0, + 395, 0, 0, 1481, 1069, 1070, 1071, 395, 0, 0, + 1486, 0, 802, 0, 0, 0, 0, 806, 807, 808, + 0, 810, 811, 0, 0, 0, 0, 0, 1491, 812, + 813, 0, 0, 264, 264, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1410, 1411, 0, 1412, + 0, 0, 0, 1349, 1376, 0, 1376, 1376, 1376, 0, + 0, 0, 1292, 0, 24, 25, 52, 27, 28, 0, + 0, 0, 0, 1526, 1527, 0, 0, 1052, 1053, 1376, + 591, 0, 0, 43, 0, 0, 0, 0, 29, 48, + 49, 618, 617, 627, 628, 620, 621, 622, 623, 624, + 625, 626, 619, 0, 0, 629, 0, 0, 0, 38, + 0, 0, 0, 54, 724, 1468, 0, 0, 0, 0, + 0, 0, 0, 0, 780, 781, 395, 395, 0, 0, + 0, 0, 790, 0, 0, 384, 0, 0, 796, 0, + 0, 0, 909, 1080, 0, 1487, 0, 0, 0, 0, + 0, 0, 809, 0, 0, 0, 0, 0, 0, 0, + 1098, 0, 0, 0, 1493, 0, 0, 0, 0, 0, + 0, 0, 0, 264, 31, 32, 34, 33, 36, 0, + 50, 0, 0, 264, 264, 0, 0, 1376, 0, 1344, + 0, 264, 0, 0, 264, 840, 0, 264, 0, 0, + 0, 800, 37, 44, 45, 0, 0, 46, 47, 35, + 0, 264, 0, 0, 0, 0, 0, 0, 1224, 1225, + 0, 0, 0, 39, 40, 0, 41, 42, 0, 0, + 0, 0, 1242, 1243, 0, 1244, 1245, 0, 0, 0, + 1015, 0, 1017, 0, 0, 0, 1343, 1252, 1253, 0, + 0, 0, 0, 0, 264, 0, 0, 0, 0, 1044, + 0, 0, 0, 800, 618, 617, 627, 628, 620, 621, + 622, 623, 624, 625, 626, 619, 0, 0, 629, 0, + 0, 0, 0, 0, 0, 921, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 948, 0, + 0, 0, 0, 0, 0, 317, 0, 0, 53, 1299, + 317, 317, 0, 0, 317, 317, 317, 0, 1342, 0, + 910, 618, 617, 627, 628, 620, 621, 622, 623, 624, + 625, 626, 619, 0, 0, 629, 0, 0, 0, 317, + 317, 317, 317, 1341, 264, 0, 0, 0, 0, 0, + 1249, 0, 264, 943, 0, 0, 264, 264, 0, 0, + 264, 951, 800, 0, 0, 0, 0, 0, 1329, 0, + 0, 1014, 0, 0, 0, 0, 0, 0, 0, 0, + 1034, 1035, 1036, 1037, 1038, 0, 1041, 1042, 0, 0, + 1043, 0, 1283, 618, 617, 627, 628, 620, 621, 622, + 623, 624, 625, 626, 619, 0, 0, 629, 0, 1045, + 0, 0, 0, 0, 0, 0, 1050, 0, 618, 617, + 627, 628, 620, 621, 622, 623, 624, 625, 626, 619, + 264, 1223, 629, 0, 0, 0, 0, 0, 0, 264, + 264, 264, 264, 264, 0, 264, 264, 0, 0, 264, + 0, 618, 617, 627, 628, 620, 621, 622, 623, 624, + 625, 626, 619, 0, 0, 629, 746, 0, 264, 0, + 1047, 1048, 0, 0, 0, 264, 0, 1183, 0, 0, + 800, 0, 0, 0, 0, 0, 1396, 1397, 1398, 1399, + 1400, 0, 317, 0, 1403, 1404, 0, 0, 0, 0, + 0, 0, 0, 1348, 0, 0, 1212, 0, 0, 0, + 0, 0, 0, 654, 0, 0, 0, 0, 0, 0, + 0, 1363, 0, 0, 1364, 0, 1074, 1366, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 317, + 317, 0, 0, 0, 0, 734, 618, 617, 627, 628, + 620, 621, 622, 623, 624, 625, 626, 619, 317, 0, + 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 910, 264, 264, 264, + 264, 264, 0, 747, 0, 0, 0, 0, 0, 1130, + 0, 0, 264, 0, 0, 0, 943, 0, 0, 0, + 264, 0, 0, 0, 0, 0, 760, 763, 764, 765, + 766, 767, 768, 0, 769, 770, 771, 772, 773, 748, + 749, 750, 751, 732, 733, 761, 0, 735, 0, 736, + 737, 738, 739, 740, 741, 742, 743, 744, 745, 752, + 753, 754, 755, 756, 757, 758, 759, 0, 0, 0, + 0, 0, 1216, 0, 0, 0, 0, 1518, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1479, 654, 762, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 317, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 800, 0, 0, 0, 0, 0, + 0, 0, 0, 910, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1385, 0, 1311, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1314, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1324, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, - 0, 264, 0, 0, 0, 0, 0, 0, 0, 1348, - 0, 0, 0, 0, 0, 0, 0, 1216, 0, 652, - 0, 0, 0, 0, 0, 0, 0, 1363, 0, 0, - 1364, 0, 0, 1366, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 906, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 910, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1311, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 939, 0, 0, 0, 0, 0, 1314, + 0, 0, 0, 0, 0, 0, 1440, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1324, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1479, 652, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 943, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 906, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 264, 0, 0, 0, 0, 0, 0, 505, 493, 0, - 450, 508, 423, 440, 516, 441, 444, 481, 408, 463, - 165, 438, 0, 427, 403, 434, 404, 425, 452, 111, - 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, - 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, - 482, 413, 471, 509, 439, 479, 510, 0, 0, 0, - 80, 0, 964, 965, 0, 0, 0, 0, 0, 101, - 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, - 409, 515, 500, 431, 432, 1151, 0, 0, 0, 0, - 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, - 0, 0, 0, 1440, 429, 0, 470, 0, 0, 0, - 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, - 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, - 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, - 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 1489, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, - 421, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, - 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, - 502, 468, 82, 91, 138, 246, 186, 116, 235, 401, - 414, 109, 424, 0, 0, 437, 442, 443, 455, 457, - 458, 459, 460, 467, 474, 475, 477, 483, 484, 485, - 486, 491, 498, 517, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 0, 0, 1489, 0, 0, 0, 0, 0, 0, 0, 505, 493, 0, 450, 508, 423, 440, 516, 441, 444, 481, 408, 463, 165, 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, - 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, + 514, 139, 472, 0, 211, 153, 0, 910, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, - 0, 0, 0, 80, 0, 964, 965, 0, 0, 0, + 0, 264, 0, 80, 0, 968, 969, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, - 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, + 473, 0, 406, 409, 515, 500, 431, 432, 1153, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, @@ -1230,7 +1200,7 @@ var yyAct = [...]int{ 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, - 439, 479, 510, 54, 0, 0, 80, 0, 0, 0, + 439, 479, 510, 0, 0, 0, 80, 0, 968, 969, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, @@ -1264,12 +1234,12 @@ var yyAct = [...]int{ 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, - 413, 471, 509, 439, 479, 510, 0, 0, 0, 80, + 413, 471, 509, 439, 479, 510, 54, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, - 0, 1219, 0, 429, 0, 470, 0, 0, 0, 410, + 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, @@ -1299,11 +1269,11 @@ var yyAct = [...]int{ 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, - 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, - 0, 0, 0, 0, 948, 0, 429, 0, 470, 0, + 0, 0, 0, 0, 1219, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, @@ -1333,11 +1303,11 @@ var yyAct = [...]int{ 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, - 479, 510, 0, 0, 0, 322, 0, 0, 0, 0, + 479, 510, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, - 447, 0, 0, 0, 0, 0, 0, 845, 0, 429, + 447, 0, 0, 0, 0, 0, 0, 952, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, @@ -1367,12 +1337,12 @@ var yyAct = [...]int{ 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, - 471, 509, 439, 479, 510, 0, 0, 0, 80, 0, + 471, 509, 439, 479, 510, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, 0, - 0, 0, 429, 0, 470, 0, 0, 0, 410, 407, + 849, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, @@ -1402,7 +1372,7 @@ var yyAct = [...]int{ 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, 0, - 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, @@ -1436,7 +1406,7 @@ var yyAct = [...]int{ 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, - 510, 0, 0, 0, 80, 0, 0, 0, 0, 0, + 510, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, @@ -1449,11 +1419,11 @@ var yyAct = [...]int{ 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 399, 397, 127, 209, 134, 141, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, @@ -1470,7 +1440,7 @@ var yyAct = [...]int{ 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, - 509, 439, 479, 510, 0, 0, 0, 265, 0, 0, + 509, 439, 479, 510, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, @@ -1483,11 +1453,11 @@ var yyAct = [...]int{ 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, + 107, 164, 224, 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, + 244, 0, 0, 100, 118, 113, 181, 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, @@ -1505,7 +1475,7 @@ var yyAct = [...]int{ 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, @@ -1515,14 +1485,14 @@ var yyAct = [...]int{ 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 709, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, 116, 235, 401, @@ -1550,13 +1520,13 @@ var yyAct = [...]int{ 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 389, 102, 203, 88, 227, 214, 151, 131, + 86, 217, 711, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 399, 397, 392, 391, 134, 141, 188, + 118, 113, 181, 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, @@ -1568,642 +1538,842 @@ var yyAct = [...]int{ 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 881, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 882, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 232, 239, 242, 505, 493, 0, 450, 508, 423, 440, + 516, 441, 444, 481, 408, 463, 165, 438, 0, 427, + 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, + 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, + 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, + 439, 479, 510, 0, 0, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, + 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, + 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, + 487, 447, 0, 0, 0, 0, 0, 0, 0, 0, + 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, + 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, + 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, + 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 389, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 398, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 399, 397, 392, 391, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, + 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, + 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, + 138, 246, 186, 116, 235, 401, 414, 109, 424, 0, + 0, 437, 442, 443, 455, 457, 458, 459, 460, 467, + 474, 475, 477, 483, 484, 485, 486, 491, 498, 517, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 885, + 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, + 0, 137, 886, 364, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 322, 343, 342, 345, + 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, + 0, 0, 0, 319, 336, 0, 363, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 333, 334, 315, 0, + 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, + 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, + 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 955, 0, - 54, 0, 0, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 956, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 0, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 581, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 0, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, + 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, + 0, 959, 0, 54, 0, 0, 322, 343, 342, 345, + 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, + 960, 0, 0, 319, 336, 0, 363, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 333, 334, 0, 0, + 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, + 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, + 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 342, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 897, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, + 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 583, 322, 343, 342, 345, + 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, + 0, 0, 0, 319, 336, 0, 363, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 333, 334, 0, 0, + 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, + 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, + 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 165, 0, 0, 0, 0, 324, 0, - 0, 0, 111, 0, 321, 0, 0, 0, 137, 0, - 364, 139, 0, 0, 211, 153, 0, 0, 0, 0, - 355, 356, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 322, 343, 894, 345, 346, 347, 348, - 0, 0, 101, 344, 349, 350, 351, 0, 0, 0, - 319, 336, 0, 363, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 333, 334, 315, 0, 0, 0, 377, - 0, 335, 0, 0, 330, 331, 332, 337, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 375, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 365, 376, 371, 372, - 369, 370, 368, 367, 366, 378, 357, 358, 359, 360, - 362, 0, 373, 374, 361, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 24, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, - 324, 0, 0, 0, 111, 0, 321, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 319, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, + 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 322, 343, 342, 345, + 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, + 0, 0, 0, 319, 336, 0, 363, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 333, 334, 315, 0, + 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, + 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, + 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 324, 0, 0, 0, 111, 0, 321, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 319, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 0, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 1519, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, + 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 322, 343, 901, 345, + 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, + 0, 0, 0, 319, 336, 0, 363, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 333, 334, 315, 0, + 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, + 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, + 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 581, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 0, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 364, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 355, 356, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 322, 343, 342, 345, 346, - 347, 348, 0, 0, 101, 344, 349, 350, 351, 0, - 0, 0, 0, 336, 0, 363, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 333, 334, 0, 0, 0, - 0, 377, 0, 335, 0, 0, 330, 331, 332, 337, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 375, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 365, 376, - 371, 372, 369, 370, 368, 367, 366, 378, 357, 358, - 359, 360, 362, 0, 373, 374, 361, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, + 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, + 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 322, 343, 898, 345, + 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, + 0, 0, 0, 319, 336, 0, 363, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 333, 334, 315, 0, + 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, + 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, + 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, + 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 165, 0, + 0, 0, 0, 324, 0, 0, 0, 111, 0, 321, + 0, 0, 0, 137, 0, 364, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 355, 356, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 322, 343, + 342, 345, 346, 347, 348, 0, 0, 101, 344, 349, + 350, 351, 0, 0, 0, 319, 336, 0, 363, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 333, 334, + 0, 0, 0, 0, 377, 0, 335, 0, 0, 330, + 331, 332, 337, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 375, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 365, 376, 371, 372, 369, 370, 368, 367, 366, + 378, 357, 358, 359, 360, 362, 0, 373, 374, 361, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 616, 615, 625, 626, 618, 619, 620, 621, 622, - 623, 624, 617, 0, 0, 627, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 324, 0, 0, 0, 111, 0, 321, + 0, 0, 0, 137, 0, 364, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 355, 356, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 322, 343, + 342, 345, 346, 347, 348, 0, 0, 101, 344, 349, + 350, 351, 0, 0, 0, 319, 336, 0, 363, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 333, 334, + 0, 0, 0, 0, 377, 0, 335, 0, 0, 330, + 331, 332, 337, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 375, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 365, 376, 371, 372, 369, 370, 368, 367, 366, + 378, 357, 358, 359, 360, 362, 0, 373, 374, 361, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 604, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 364, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 355, 356, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 322, 343, + 342, 345, 346, 347, 348, 0, 0, 101, 344, 349, + 350, 351, 0, 0, 0, 0, 336, 0, 363, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 333, 334, + 0, 0, 0, 0, 377, 0, 335, 0, 0, 330, + 331, 332, 337, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 375, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 1519, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 365, 376, 371, 372, 369, 370, 368, 367, 366, + 378, 357, 358, 359, 360, 362, 0, 373, 374, 361, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 606, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 601, 600, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 364, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 355, 356, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 583, 322, 343, + 342, 345, 346, 347, 348, 0, 0, 101, 344, 349, + 350, 351, 0, 0, 0, 0, 336, 0, 363, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 333, 334, + 0, 0, 0, 0, 377, 0, 335, 0, 0, 330, + 331, 332, 337, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 375, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 365, 376, 371, 372, 369, 370, 368, 367, 366, + 378, 357, 358, 359, 360, 362, 0, 373, 374, 361, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 364, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 355, 356, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 322, 343, + 342, 345, 346, 347, 348, 0, 0, 101, 344, 349, + 350, 351, 0, 0, 0, 0, 336, 0, 363, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 333, 334, + 0, 0, 0, 0, 377, 0, 335, 0, 0, 330, + 331, 332, 337, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 375, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 365, 376, 371, 372, 369, 370, 368, 367, 366, + 378, 357, 358, 359, 360, 362, 0, 373, 374, 361, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 618, 617, 627, 628, 620, 621, + 622, 623, 624, 625, 626, 619, 0, 0, 629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 76, 77, 0, 73, 0, 0, 0, 78, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 75, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 938, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 940, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 606, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, + 608, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 603, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 604, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 24, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 165, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, 0, 80, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 76, 77, 0, 73, 0, 0, 0, + 78, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 0, 0, 267, 0, 0, 0, 0, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, - 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 0, 0, 212, 234, 249, 99, 0, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 942, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, + 944, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 24, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 265, - 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 938, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, - 0, 940, 0, 0, 0, 0, 0, 0, 101, 0, + 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, + 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, + 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, + 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 936, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, + 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, + 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, + 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, + 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 942, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 265, 0, 944, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 940, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 0, 836, 0, 0, 837, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 720, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 719, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 265, 0, 944, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 80, 0, 608, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 0, 832, 0, 0, 833, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 690, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 381, 0, 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, - 718, 0, 0, 0, 137, 0, 0, 139, 0, 0, + 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 717, 0, 0, 0, 0, 0, 0, 101, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, + 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2235,14 +2405,14 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 265, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, + 0, 0, 0, 119, 0, 262, 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, @@ -2268,8 +2438,8 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, - 0, 940, 0, 0, 0, 0, 0, 0, 101, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2301,8 +2471,8 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, - 0, 606, 0, 0, 0, 0, 0, 0, 101, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, + 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2331,10 +2501,10 @@ var yyAct = [...]int{ 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 0, 0, 0, 688, 111, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2363,185 +2533,18 @@ var yyAct = [...]int{ 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 381, - 0, 0, 0, 0, 0, 0, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 262, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 322, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, + 206, 207, 213, 216, 222, 223, 232, 239, 242, } var yyPact = [...]int{ - 204, -1000, -269, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 867, 932, -1000, -1000, -1000, -1000, -1000, -1000, - 312, 11217, -23, 103, 14, 15208, 102, 1958, 15868, -1000, - 4, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -63, -72, - -1000, 684, -1000, -1000, -1000, -1000, -1000, 861, 863, 694, - 856, 780, -1000, 7905, 64, 64, 14878, 6585, -1000, -1000, - 245, 15868, 99, 15868, -151, 60, 60, 60, -1000, -1000, + 1848, -1000, -268, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 899, 935, -1000, -1000, -1000, -1000, -1000, -1000, + 237, 11260, -4, 111, -15, 15251, 110, 140, 15911, -1000, + 13, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -83, -96, + -1000, 718, -1000, -1000, -1000, -1000, -1000, 892, 896, 770, + 886, 813, -1000, 7948, 80, 80, 14921, 6628, -1000, -1000, + 227, 15911, 106, 15911, -150, 78, 78, 78, -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, @@ -2558,22 +2561,22 @@ 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, - 101, 15868, 578, 576, 247, -1000, 15868, 58, 575, 58, - 58, 58, 15868, -1000, 160, -1000, -1000, -1000, 15868, 561, - 810, 3498, 43, 3498, -1000, 3498, 3498, -1000, 3498, 11, - 3498, -65, 902, 8, -12, -1000, 3498, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 490, 815, 9237, 9237, 867, -1000, 684, -1000, -1000, -1000, - 804, -1000, -1000, 338, 919, -1000, 10887, 153, -1000, 9237, - 1478, 612, -1000, -1000, 612, -1000, -1000, 124, -1000, -1000, - 10227, 10227, 10227, 10227, 10227, 10227, 10227, 10227, -1000, -1000, + 108, 15911, 616, 613, 173, -1000, 15911, 74, 596, 74, + 74, 74, 15911, -1000, 149, -1000, -1000, -1000, 15911, 595, + 838, 284, 48, 3541, -1000, 3541, 3541, -1000, 3541, 20, + 3541, -48, 907, 22, -40, -1000, 3541, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 612, -1000, 8907, 612, 612, 612, 612, 612, - 612, 612, 612, 9237, 612, 612, 612, 612, 612, 612, - 612, 612, 612, 612, 612, 612, 612, 612, 612, 14541, - 13551, 15868, 661, 658, -1000, -1000, 152, 681, 6242, -79, - -1000, -1000, -1000, 291, 13221, -1000, -1000, -1000, 807, -1000, + 454, 853, 9280, 9280, 899, -1000, 718, -1000, -1000, -1000, + 843, -1000, -1000, 306, 921, -1000, 10930, 147, -1000, 9280, + 1573, 691, -1000, -1000, 691, -1000, -1000, 130, -1000, -1000, + 10270, 10270, 10270, 10270, 10270, 10270, 10270, 10270, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 691, -1000, 8950, 691, 691, 691, 691, 691, + 691, 691, 691, 9280, 691, 691, 691, 691, 691, 691, + 691, 691, 691, 691, 691, 691, 691, 691, 691, 14584, + 13594, 15911, 639, 638, -1000, -1000, 146, 688, 6285, -89, + -1000, -1000, -1000, 250, 13264, -1000, -1000, -1000, 833, -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, @@ -2584,132 +2587,133 @@ 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, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 560, 15868, - -1000, 2173, -1000, 557, 3498, 82, 556, 259, 555, 15868, - 15868, 3498, 3498, 3498, 18, 50, 52, 15868, 687, 67, - 15868, 847, 740, 15868, 554, 552, -1000, 5899, -1000, 3498, - 3498, -1000, -1000, -1000, 3498, 3498, 3498, 15868, 3498, 3498, - -1000, -1000, -1000, -1000, 3498, 3498, -1000, 918, 282, -1000, - -1000, -1000, -1000, 9237, 195, -1000, 739, -1000, -1000, -1000, - -1000, -1000, -1000, 927, 208, 341, 151, 682, -1000, 361, - 861, 490, 780, 12891, 764, -1000, -1000, -1000, 15868, -1000, - 9237, 9237, 442, -1000, 14211, -1000, -1000, 4527, 214, 10227, - 406, 239, 10227, 10227, 10227, 10227, 10227, 10227, 10227, 10227, - 10227, 10227, 10227, 10227, 10227, 10227, 10227, 461, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 551, -1000, 684, 583, - 583, 159, 159, 159, 159, 159, 159, 159, 10557, 6915, - 490, 550, 303, 8907, 7905, 7905, 9237, 9237, 8565, 8235, - 7905, 818, 249, 303, 16198, -1000, -1000, 9897, -1000, -1000, - -1000, -1000, -1000, 490, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 15538, 15538, 7905, 7905, 7905, 7905, 29, 15868, -1000, - 673, 816, -1000, -1000, -1000, 849, 12231, 12561, 29, 640, - 13551, 15868, -1000, -1000, 13551, 15868, 4184, 5556, 681, -79, - 664, -1000, -107, -85, 7245, 150, -1000, -1000, -1000, -1000, - 3155, 212, 581, 330, -54, -1000, -1000, -1000, 698, -1000, - 698, 698, 698, 698, -25, -25, -25, -25, -1000, -1000, - -1000, -1000, -1000, 711, 710, -1000, 698, 698, 698, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 709, 709, 709, - 700, 700, 717, -1000, 15868, 3498, 846, 3498, -1000, 78, - -1000, -1000, -1000, 15868, 15868, 15868, 15868, 15868, 112, 15868, - 15868, 680, -1000, 15868, 3498, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 15868, 367, 15868, 15868, 303, -1000, 458, 15868, -1000, 785, - 9237, 9237, 5213, 9237, -1000, -1000, -1000, 815, -1000, 818, - 897, -1000, 798, 797, 7905, -1000, -1000, 214, 297, -1000, - -1000, 371, -1000, -1000, -1000, -1000, 149, 612, -1000, 2132, - -1000, -1000, -1000, -1000, 406, 10227, 10227, 10227, 387, 2132, - 2096, 1634, 1715, 159, 145, 145, 183, 183, 183, 183, - 183, 320, 320, -1000, -1000, -1000, 490, -1000, -1000, -1000, - 490, 7905, 7905, 679, -1000, -1000, 9237, -1000, 490, 544, - 544, 310, 288, 232, 914, 544, 226, 904, 544, 544, - 7905, 317, -1000, 9237, 490, -1000, 148, -1000, 402, 675, - 672, 544, 490, 544, 544, 644, 612, -1000, 16198, 13551, - 13551, 13551, 13551, 13551, -1000, 767, 756, -1000, 755, 753, - 774, 15868, -1000, 546, 12231, 166, 612, -1000, 13881, -1000, - -1000, 899, 13551, 606, -1000, 606, -1000, 147, -1000, -1000, - 664, -79, -88, -1000, -1000, -1000, -1000, 303, -1000, 466, - 663, 2812, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 708, - 523, -1000, 829, 174, 193, 509, 827, -1000, -1000, -1000, - 813, -1000, 322, -59, -1000, -1000, 414, -25, -25, -1000, - -1000, 150, 806, 150, 150, 150, 454, 454, -1000, -1000, - -1000, -1000, 405, -1000, -1000, -1000, 394, -1000, 729, 15538, - 3498, -1000, -1000, -1000, -1000, 277, 277, 200, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 28, - 707, -1000, -1000, -1000, -1000, 3, 16, 66, -1000, 3498, - -1000, 282, -1000, 449, 9237, -1000, -1000, -1000, -1000, 783, - 303, 303, 141, -1000, -1000, 15868, -1000, -1000, -1000, -1000, - 666, -1000, -1000, -1000, 3841, 7905, -1000, 387, 2132, 1866, - -1000, 10227, 10227, -1000, -1000, 544, 544, 7905, 303, -1000, - -1000, -1000, 97, 461, 97, 10227, 10227, -1000, 10227, 10227, - -1000, -163, 648, 233, -1000, 9237, 363, -1000, 5213, -1000, - 10227, 10227, -1000, -1000, -1000, -1000, 727, 16198, 612, -1000, - 11889, 15538, 636, -1000, 231, 816, 706, 726, 611, -1000, - -1000, -1000, -1000, 754, -1000, 691, -1000, -1000, -1000, -1000, - -1000, 98, 95, 79, 15538, -1000, 867, 9237, 606, -1000, - -1000, 175, -1000, -1000, -130, -97, -1000, -1000, -1000, 3155, - -1000, 3155, 15538, 46, -1000, 509, 509, -1000, -1000, -1000, - 703, 721, 10227, -1000, -1000, -1000, 547, 150, 150, -1000, - 251, -1000, -1000, -1000, 541, -1000, 539, 662, 535, 15868, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 15868, -1000, -1000, - -1000, -1000, -1000, 15538, -173, 491, 15538, 15538, 15538, 15868, - -1000, 367, -1000, 303, -1000, 4870, -1000, 899, 13551, -1000, - -1000, 490, -1000, 10227, 2132, 2132, -1000, -1000, -1000, 490, - 698, 698, -1000, 698, 700, -1000, 698, -7, 698, -9, - 490, 490, 2005, 1990, 1827, 474, 612, -158, -1000, 303, - 9237, -1000, 1550, 527, -1000, 831, 584, 630, -1000, -1000, - 7575, 490, 533, 129, 528, -1000, 867, 16198, 9237, -1000, - -1000, 9237, 699, -1000, 9237, -1000, -1000, -1000, 612, 612, - 612, 528, 861, 303, -1000, -1000, -1000, -1000, 2812, -1000, - 507, -1000, 698, -1000, -1000, -1000, 15538, -49, 925, 2132, - -1000, -1000, -1000, -1000, -1000, -25, 448, -25, 372, -1000, - 366, 3498, -1000, -1000, -1000, -1000, 841, -1000, 4870, -1000, - -1000, 690, 713, -1000, -1000, -1000, 900, 639, -1000, 2132, - -1000, -1000, 106, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 10227, 10227, 10227, 10227, 10227, 490, 434, 303, 10227, - 10227, 826, -1000, 612, -1000, -1000, 678, 15538, 15538, -1000, - 15538, 861, -1000, 303, 303, 15538, 303, 15538, 15538, 15538, - 11547, -1000, 136, 15538, -1000, 505, -1000, 177, -1000, -114, - 150, -1000, 150, 529, 497, -1000, 612, 635, -1000, 230, - 15538, 15868, 898, 857, -1000, -1000, 402, 402, 402, 402, - 20, -1000, -1000, 402, 402, 924, -1000, 612, -1000, 684, - 128, -1000, -1000, -1000, 496, 489, 489, 489, 166, 136, - -1000, 374, 219, 420, -1000, 41, 15538, 334, 823, -1000, - 817, -1000, -1000, -1000, -1000, -1000, 27, 4870, 3155, 486, - -1000, -1000, 9237, 9237, -1000, -1000, -1000, -1000, 490, 45, - -180, -1000, -1000, 16198, 630, 490, 15538, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 351, -1000, -1000, 15868, -1000, -1000, - 403, -1000, -1000, 483, -1000, 15538, -1000, -1000, 707, 303, - 629, -1000, 776, -168, -184, 620, -1000, -1000, -1000, 689, - -1000, -1000, 27, 796, -173, -1000, 772, -1000, 15538, -1000, - 24, -1000, -174, 480, 22, -181, 720, 612, -185, 719, - -1000, 908, 9567, -1000, -1000, 922, 154, 154, 402, 490, - -1000, -1000, -1000, 48, 400, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 590, 15911, + -1000, 2226, -1000, 586, 3541, 90, 557, 281, 555, 15911, + 15911, 3541, 3541, 3541, 32, 66, 59, 15911, 697, 87, + 15911, 881, 781, 15911, 539, 531, -1000, 5942, -1000, 3541, + 284, -1000, 443, 9280, 3541, 3541, 3541, 15911, 3541, 3541, + -1000, -1000, -1000, -1000, -1000, -1000, 3541, 3541, -1000, 920, + 256, -1000, -1000, -1000, -1000, 9280, 187, -1000, 780, -1000, + -1000, -1000, -1000, -1000, -1000, 928, 180, 453, 142, 693, + -1000, 439, 892, 454, 813, 12934, 696, -1000, -1000, -1000, + 15911, -1000, 9280, 9280, 446, -1000, 14254, -1000, -1000, 4570, + 205, 10270, 449, 254, 10270, 10270, 10270, 10270, 10270, 10270, + 10270, 10270, 10270, 10270, 10270, 10270, 10270, 10270, 10270, 477, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 527, -1000, + 718, 502, 502, 158, 158, 158, 158, 158, 158, 158, + 10600, 6958, 454, 584, 303, 8950, 7948, 7948, 9280, 9280, + 8608, 8278, 7948, 845, 257, 303, 16241, -1000, -1000, 9940, + -1000, -1000, -1000, -1000, -1000, 454, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 15581, 15581, 7948, 7948, 7948, 7948, 43, + 15911, -1000, 692, 847, -1000, -1000, -1000, 883, 12274, 12604, + 43, 662, 13594, 15911, -1000, -1000, 13594, 15911, 4227, 5599, + 688, -89, 675, -1000, -121, -97, 7288, 153, -1000, -1000, + -1000, -1000, 3198, 181, 632, 309, -69, -1000, -1000, -1000, + 707, -1000, 707, 707, 707, 707, -22, -22, -22, -22, + -1000, -1000, -1000, -1000, -1000, 744, 743, -1000, 707, 707, + 707, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 739, + 739, 739, 727, 727, 748, -1000, 15911, 3541, 880, 3541, + -1000, 68, -1000, -1000, -1000, 15911, 15911, 15911, 15911, 15911, + 122, 15911, 15911, 687, -1000, 15911, 3541, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 303, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 15911, 284, 15911, 15911, 303, -1000, + 426, 15911, -1000, 818, 9280, 9280, 5256, 9280, -1000, -1000, + -1000, 853, -1000, 845, 897, -1000, 826, 825, 7948, -1000, + -1000, 205, 269, -1000, -1000, 328, -1000, -1000, -1000, -1000, + 141, 691, -1000, 561, -1000, -1000, -1000, -1000, 449, 10270, + 10270, 10270, 397, 561, 2242, 708, 1252, 158, 379, 379, + 163, 163, 163, 163, 163, 304, 304, -1000, -1000, -1000, + 454, -1000, -1000, -1000, 454, 7948, 7948, 686, -1000, -1000, + 9280, -1000, 454, 580, 580, 434, 370, 259, 919, 580, + 239, 912, 580, 580, 7948, 260, -1000, 9280, 454, -1000, + 138, -1000, 479, 680, 679, 580, 454, 580, 580, 684, + 691, -1000, 16241, 13594, 13594, 13594, 13594, 13594, -1000, 804, + 795, -1000, 802, 792, 803, 15911, -1000, 582, 12274, 185, + 691, -1000, 13924, -1000, -1000, 906, 13594, 676, -1000, 676, + -1000, 134, -1000, -1000, 675, -89, -100, -1000, -1000, -1000, + -1000, 303, -1000, 489, 672, 2855, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 736, 514, -1000, 867, 201, 184, 511, + 860, -1000, -1000, -1000, 849, -1000, 294, -72, -1000, -1000, + 349, -22, -22, -1000, -1000, 153, 832, 153, 153, 153, + 425, 425, -1000, -1000, -1000, -1000, 330, -1000, -1000, -1000, + 329, -1000, 779, 15581, 3541, -1000, -1000, -1000, -1000, 336, + 336, 202, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 42, 746, -1000, -1000, -1000, -1000, -8, + 29, 84, -1000, 3541, -1000, 256, -1000, -1000, -1000, -1000, + -1000, 816, 303, 303, 133, -1000, -1000, 15911, -1000, -1000, + -1000, -1000, 694, -1000, -1000, -1000, 3884, 7948, -1000, 397, + 561, 2147, -1000, 10270, 10270, -1000, -1000, 580, 580, 7948, + 303, -1000, -1000, -1000, 101, 477, 101, 10270, 10270, -1000, + 10270, 10270, -1000, -162, 700, 193, -1000, 9280, 518, -1000, + 5256, -1000, 10270, 10270, -1000, -1000, -1000, -1000, 771, 16241, + 691, -1000, 11932, 15581, 685, -1000, 218, 847, 731, 768, + 633, -1000, -1000, -1000, -1000, 794, -1000, 793, -1000, -1000, + -1000, -1000, -1000, 103, 94, 93, 15581, -1000, 899, 9280, + 676, -1000, -1000, 172, -1000, -1000, -129, -135, -1000, -1000, + -1000, 3198, -1000, 3198, 15581, 60, -1000, 511, 511, -1000, + -1000, -1000, 728, 767, 10270, -1000, -1000, -1000, 618, 153, + 153, -1000, 195, -1000, -1000, -1000, 576, -1000, 574, 671, + 570, 15911, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 15911, + -1000, -1000, -1000, -1000, -1000, 15581, -169, 470, 15581, 15581, + 15581, 15911, -1000, 284, -1000, 4913, -1000, 906, 13594, -1000, + -1000, 454, -1000, 10270, 561, 561, -1000, -1000, -1000, 454, + 707, 707, -1000, 707, 727, -1000, 707, 3, 707, -5, + 454, 454, 2114, 2089, 2017, 1960, 691, -157, -1000, 303, + 9280, -1000, 1787, 1623, -1000, 871, 645, 664, -1000, -1000, + 7618, 454, 568, 128, 566, -1000, 899, 16241, 9280, -1000, + -1000, 9280, 724, -1000, 9280, -1000, -1000, -1000, 691, 691, + 691, 566, 892, 303, -1000, -1000, -1000, -1000, 2855, -1000, + 553, -1000, 707, -1000, -1000, -1000, 15581, -60, 926, 561, + -1000, -1000, -1000, -1000, -1000, -22, 417, -22, 327, -1000, + 326, 3541, -1000, -1000, -1000, -1000, 874, -1000, 4913, -1000, + -1000, 706, 747, -1000, -1000, -1000, 904, 670, -1000, 561, + -1000, -1000, 109, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 10270, 10270, 10270, 10270, 10270, 454, 413, 303, 10270, + 10270, 859, -1000, 691, -1000, -1000, 690, 15581, 15581, -1000, + 15581, 892, -1000, 303, 303, 15581, 303, 15581, 15581, 15581, + 11590, -1000, 148, 15581, -1000, 549, -1000, 192, -1000, -116, + 153, -1000, 153, 585, 562, -1000, 691, 665, -1000, 217, + 15581, 15911, 901, 894, -1000, -1000, 479, 479, 479, 479, + 61, -1000, -1000, 479, 479, 924, -1000, 691, -1000, 718, + 120, -1000, -1000, -1000, 547, 543, 543, 543, 185, 148, + -1000, 448, 215, 398, -1000, 57, 15581, 298, 857, -1000, + 854, -1000, -1000, -1000, -1000, -1000, 41, 4913, 3198, 497, + -1000, -1000, 9280, 9280, -1000, -1000, -1000, -1000, 454, 46, + -177, -1000, -1000, 16241, 664, 454, 15581, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 318, -1000, -1000, 15911, -1000, -1000, + 355, -1000, -1000, 495, -1000, 15581, -1000, -1000, 746, 303, + 655, -1000, 812, -167, -181, 650, -1000, -1000, -1000, 703, + -1000, -1000, 41, 824, -169, -1000, 783, -1000, 15581, -1000, + 38, -1000, -172, 462, 36, -179, 766, 691, -184, 761, + -1000, 911, 9610, -1000, -1000, 913, 167, 167, 479, 454, + -1000, -1000, -1000, 64, 356, -1000, -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 1163, 26, 527, 1161, 1160, 1159, 1158, 1157, 1156, - 1155, 1149, 1148, 1147, 1146, 1145, 1143, 1142, 1138, 1134, - 1133, 1131, 1130, 1129, 1128, 1121, 82, 1120, 1119, 1117, - 65, 1108, 66, 1106, 1105, 42, 188, 47, 38, 1318, - 1104, 25, 71, 58, 1103, 35, 1101, 1100, 70, 1098, - 1097, 53, 1095, 1092, 1521, 1082, 62, 1075, 11, 49, - 1074, 1073, 1072, 1069, 69, 639, 1068, 1067, 13, 1066, - 1064, 106, 1063, 55, 10, 12, 9, 18, 1057, 1099, - 28, 1054, 54, 1052, 1051, 1050, 1045, 14, 1042, 56, - 1041, 17, 64, 1039, 19, 63, 30, 21, 6, 75, - 60, 1038, 22, 61, 48, 1037, 1036, 457, 1035, 1034, - 44, 1033, 1032, 23, 1030, 96, 438, 1029, 1028, 1027, - 1026, 45, 0, 394, 388, 73, 1025, 1024, 1022, 1342, - 33, 50, 15, 1019, 40, 191, 43, 1018, 1017, 37, - 1016, 1015, 1014, 1013, 1012, 1009, 1008, 392, 1007, 1006, - 1004, 92, 52, 1003, 1001, 57, 34, 998, 997, 992, - 51, 59, 990, 987, 46, 29, 985, 983, 981, 966, - 965, 31, 16, 964, 20, 963, 8, 958, 24, 957, - 7, 955, 5, 952, 3, 948, 4, 41, 1, 946, - 2, 941, 940, 528, 439, 76, 938, 77, + 0, 1144, 25, 542, 1142, 1141, 1140, 1136, 1135, 1134, + 1132, 1131, 1128, 1126, 1124, 1122, 1120, 1112, 1110, 1109, + 1108, 1107, 1106, 1104, 1103, 1102, 88, 1101, 1098, 1097, + 66, 1096, 75, 1095, 1094, 41, 213, 51, 43, 1042, + 1093, 60, 89, 101, 1090, 35, 1089, 1087, 73, 1086, + 1084, 56, 1083, 1082, 1384, 1081, 58, 1080, 13, 82, + 1079, 1078, 1077, 1075, 70, 934, 1073, 1072, 14, 1071, + 1070, 86, 1069, 52, 8, 16, 22, 18, 1066, 30, + 6, 1065, 53, 1064, 1063, 1062, 1061, 20, 1060, 55, + 1059, 19, 54, 1057, 9, 71, 34, 27, 7, 76, + 63, 1056, 23, 64, 49, 1055, 1054, 474, 1053, 1051, + 46, 1049, 1043, 37, 1041, 90, 445, 1040, 1039, 1036, + 1031, 42, 0, 478, 24, 65, 1022, 1018, 1009, 1443, + 38, 50, 17, 1006, 102, 1252, 44, 1004, 1001, 40, + 999, 996, 995, 994, 992, 984, 983, 93, 980, 979, + 978, 31, 21, 975, 974, 59, 29, 973, 972, 971, + 48, 57, 970, 968, 69, 45, 967, 965, 963, 962, + 961, 33, 10, 960, 15, 953, 11, 952, 28, 951, + 4, 950, 12, 949, 3, 948, 5, 47, 1, 946, + 2, 944, 942, 61, 344, 78, 941, 79, } var yyR1 = [...]int{ @@ -2964,73 +2968,73 @@ var yyChk = [...]int{ -161, 58, -54, 125, -54, 250, -116, 129, -116, -116, 124, -54, 58, 58, 116, 118, 121, 53, -18, -54, -115, 129, 58, -115, -115, -115, -54, 113, -54, 58, - 30, -135, -193, -124, 242, 58, 157, 124, 158, 126, - -135, -135, -135, -135, 161, 162, -135, -112, -111, 224, - 225, 219, 223, 12, 162, 219, 160, -135, -134, -134, - -194, 57, -92, 19, 31, -39, -129, -88, -89, -39, - -87, -2, -26, 37, -30, 21, 34, 66, 11, -126, - 74, 73, 90, -125, 22, -123, 60, 113, -39, -62, - 93, 75, 91, 92, 77, 95, 94, 105, 98, 99, - 100, 101, 102, 103, 104, 96, 97, 108, 83, 84, - 85, 86, 87, 88, 89, -108, -193, -79, -193, 114, - 115, -65, -65, -65, -65, -65, -65, -65, -65, -193, - -2, -74, -39, -193, -193, -193, -193, -193, -193, -193, - -193, -193, -83, -39, -193, -197, -71, -193, -197, -71, - -197, -71, -197, -193, -197, -71, -197, -71, -197, -197, - -71, -193, -193, -193, -193, -193, -193, -55, 26, -54, - -41, -42, -43, -44, -57, -79, -193, -54, -54, -48, - -195, 56, 11, 54, -195, 56, 113, 56, -99, 167, - -100, -104, 232, 234, 83, -128, -123, 60, 29, 30, - 57, 56, -54, -140, -143, -145, -144, -146, -141, -142, - 187, 188, 109, 191, 193, 194, 195, 196, 197, 198, - 199, 200, 201, 202, 30, 147, 183, 184, 185, 186, - 203, 204, 205, 206, 207, 208, 209, 210, 170, 189, - 261, 171, 172, 173, 174, 175, 176, 178, 179, 180, - 181, 182, 58, -135, 126, 58, 75, 58, -54, -54, - -135, -135, -135, 159, 159, 124, 124, 164, -54, 56, - 127, -48, 23, 53, -54, 58, 58, -130, -129, -121, - -135, -135, -135, -135, -135, -54, -135, -135, -135, -135, - 11, -110, 11, 93, -39, -114, 91, 53, 9, 93, - 56, 18, 113, 56, -90, 24, 25, -91, -194, -32, - -66, -123, 61, 64, -31, 44, -54, -39, -39, -72, - 69, 75, 70, 71, -125, 100, -130, -124, -121, -65, - -73, -76, -79, 65, 93, 91, 92, 77, -65, -65, - -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, - -65, -65, -65, -136, 58, 60, 58, -64, -64, -123, - -37, 21, 34, -36, -38, -194, 56, -194, -2, -36, - -36, -39, -39, -80, 60, -36, -80, 60, -36, -36, - -30, -81, -82, 79, -80, -123, -129, -194, -65, -123, - -123, -36, -37, -36, -36, -95, 153, -54, 30, 56, - -50, -52, -51, -53, 43, 47, 49, 44, 45, 46, - 50, -133, 22, -41, -193, -132, 153, -131, 22, -129, - 60, -95, 54, -41, -54, -41, -56, -129, 100, -103, - -100, 56, 233, 235, 236, 53, 72, -39, -152, 108, - -170, -171, -172, -124, 60, 61, -161, -162, -163, -173, - 139, -178, 131, 133, 130, -164, 140, 125, 28, 57, - -157, 69, 75, -153, 215, -147, 55, -147, -147, -147, - -147, -151, 190, -151, -151, -151, 55, 55, -147, -147, - -147, -155, 55, -155, -155, -156, 55, -156, -127, 54, - -54, -135, 23, -135, -117, 121, 118, 119, -181, 117, - 212, 190, 67, 29, 15, 251, 153, 266, 58, 154, - -54, -54, -54, -54, -54, 121, 118, -54, -54, -54, - -135, -54, -113, 91, 12, -129, -129, 60, -54, 39, - -39, -39, -130, -89, -92, -106, 19, 11, 35, 35, - -36, 69, 70, 71, 113, -193, -73, -65, -65, -65, - -35, 148, 74, -194, -194, -36, -36, 56, -39, -194, - -194, -194, 56, 54, 22, 11, 11, -194, 11, 11, - -194, -194, -36, -84, -82, 81, -39, -194, 113, -194, - 56, 56, -194, -194, -194, -194, -63, 30, 35, -2, - -193, -193, -98, -102, -80, -42, -43, -43, -42, -43, - 43, 43, 43, 48, 43, 48, 43, -51, -129, -194, - -58, 51, 128, 52, -193, -131, -59, 12, -41, -59, - -59, 113, -104, -105, 237, 234, 240, 58, 60, 56, - -172, 83, 55, 58, 28, -164, -164, -165, 58, -165, - 28, -149, 29, 69, -154, 216, 61, -151, -151, -152, - 30, -152, -152, -152, -160, 60, -160, 61, 61, 53, - -123, -135, -134, -187, 136, 132, 139, 140, 134, 58, - 125, 28, 131, 133, 153, 130, -187, -118, -119, 127, - 22, 125, 28, 153, -186, 54, 159, 212, 159, 127, - -135, -110, 60, -39, 40, 113, -54, -40, 11, 100, + 30, -113, 91, 12, 242, 58, 157, 124, 158, 126, + -135, -193, -124, -135, -135, -135, 161, 162, -135, -112, + -111, 224, 225, 219, 223, 12, 162, 219, 160, -135, + -134, -134, -194, 57, -92, 19, 31, -39, -129, -88, + -89, -39, -87, -2, -26, 37, -30, 21, 34, 66, + 11, -126, 74, 73, 90, -125, 22, -123, 60, 113, + -39, -62, 93, 75, 91, 92, 77, 95, 94, 105, + 98, 99, 100, 101, 102, 103, 104, 96, 97, 108, + 83, 84, 85, 86, 87, 88, 89, -108, -193, -79, + -193, 114, 115, -65, -65, -65, -65, -65, -65, -65, + -65, -193, -2, -74, -39, -193, -193, -193, -193, -193, + -193, -193, -193, -193, -83, -39, -193, -197, -71, -193, + -197, -71, -197, -71, -197, -193, -197, -71, -197, -71, + -197, -197, -71, -193, -193, -193, -193, -193, -193, -55, + 26, -54, -41, -42, -43, -44, -57, -79, -193, -54, + -54, -48, -195, 56, 11, 54, -195, 56, 113, 56, + -99, 167, -100, -104, 232, 234, 83, -128, -123, 60, + 29, 30, 57, 56, -54, -140, -143, -145, -144, -146, + -141, -142, 187, 188, 109, 191, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 30, 147, 183, 184, + 185, 186, 203, 204, 205, 206, 207, 208, 209, 210, + 170, 189, 261, 171, 172, 173, 174, 175, 176, 178, + 179, 180, 181, 182, 58, -135, 126, 58, 75, 58, + -54, -54, -135, -135, -135, 159, 159, 124, 124, 164, + -54, 56, 127, -48, 23, 53, -54, 58, 58, -130, + -129, -121, -135, -113, 60, -39, -135, -135, -135, -54, + -135, -135, -135, -135, 11, -110, 11, 93, -39, -114, + 91, 53, 9, 93, 56, 18, 113, 56, -90, 24, + 25, -91, -194, -32, -66, -123, 61, 64, -31, 44, + -54, -39, -39, -72, 69, 75, 70, 71, -125, 100, + -130, -124, -121, -65, -73, -76, -79, 65, 93, 91, + 92, 77, -65, -65, -65, -65, -65, -65, -65, -65, + -65, -65, -65, -65, -65, -65, -65, -136, 58, 60, + 58, -64, -64, -123, -37, 21, 34, -36, -38, -194, + 56, -194, -2, -36, -36, -39, -39, -80, 60, -36, + -80, 60, -36, -36, -30, -81, -82, 79, -80, -123, + -129, -194, -65, -123, -123, -36, -37, -36, -36, -95, + 153, -54, 30, 56, -50, -52, -51, -53, 43, 47, + 49, 44, 45, 46, 50, -133, 22, -41, -193, -132, + 153, -131, 22, -129, 60, -95, 54, -41, -54, -41, + -56, -129, 100, -103, -100, 56, 233, 235, 236, 53, + 72, -39, -152, 108, -170, -171, -172, -124, 60, 61, + -161, -162, -163, -173, 139, -178, 131, 133, 130, -164, + 140, 125, 28, 57, -157, 69, 75, -153, 215, -147, + 55, -147, -147, -147, -147, -151, 190, -151, -151, -151, + 55, 55, -147, -147, -147, -155, 55, -155, -155, -156, + 55, -156, -127, 54, -54, -135, 23, -135, -117, 121, + 118, 119, -181, 117, 212, 190, 67, 29, 15, 251, + 153, 266, 58, 154, -54, -54, -54, -54, -54, 121, + 118, -54, -54, -54, -135, -54, -113, -129, -129, 60, + -54, 39, -39, -39, -130, -89, -92, -106, 19, 11, + 35, 35, -36, 69, 70, 71, 113, -193, -73, -65, + -65, -65, -35, 148, 74, -194, -194, -36, -36, 56, + -39, -194, -194, -194, 56, 54, 22, 11, 11, -194, + 11, 11, -194, -194, -36, -84, -82, 81, -39, -194, + 113, -194, 56, 56, -194, -194, -194, -194, -63, 30, + 35, -2, -193, -193, -98, -102, -80, -42, -43, -43, + -42, -43, 43, 43, 43, 48, 43, 48, 43, -51, + -129, -194, -58, 51, 128, 52, -193, -131, -59, 12, + -41, -59, -59, 113, -104, -105, 237, 234, 240, 58, + 60, 56, -172, 83, 55, 58, 28, -164, -164, -165, + 58, -165, 28, -149, 29, 69, -154, 216, 61, -151, + -151, -152, 30, -152, -152, -152, -160, 60, -160, 61, + 61, 53, -123, -135, -134, -187, 136, 132, 139, 140, + 134, 58, 125, 28, 131, 133, 153, 130, -187, -118, + -119, 127, 22, 125, 28, 153, -186, 54, 159, 212, + 159, 127, -135, -110, 40, 113, -54, -40, 11, 100, -124, -37, -35, 74, -65, -65, -194, -194, -38, -139, 109, 187, 147, 185, 181, 201, 192, 214, 183, 215, -136, -139, -65, -65, -65, -65, 260, -87, 82, -39, @@ -3093,7 +3097,7 @@ var yyDef = [...]int{ 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 0, 0, 0, 0, 0, 619, 0, 614, 0, 614, 614, 614, 0, 258, 392, 644, 645, 924, 0, 0, - 0, 938, 0, 938, 270, 938, 938, 273, 938, 0, + 0, 297, 0, 938, 270, 938, 938, 273, 938, 0, 938, 0, 280, 0, 0, 286, 938, 303, 304, 291, 305, 308, 311, 312, 313, 314, 315, 937, 937, 318, 29, 576, 0, 0, 564, 31, 0, 319, 324, 325, @@ -3121,73 +3125,73 @@ var yyDef = [...]int{ 84, 0, 82, 0, 938, 0, 0, 0, 0, 0, 0, 938, 938, 938, 0, 0, 0, 0, 249, 0, 0, 0, 0, 0, 0, 0, 257, 0, 259, 938, - 938, 262, 939, 940, 938, 938, 938, 0, 938, 938, - 269, 271, 272, 274, 938, 938, 276, 0, 294, 292, - 293, 288, 289, 0, 300, 283, 284, 287, 316, 317, - 30, 936, 24, 0, 0, 573, 0, 565, 566, 569, - 572, 29, 326, 0, 332, 330, 331, 322, 0, 339, - 0, 0, 0, 343, 0, 345, 346, 0, 404, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 428, 429, - 430, 431, 432, 433, 434, 407, 0, 421, 0, 0, - 0, 463, 464, 465, 466, 467, 468, 469, 0, 334, - 29, 0, 441, 0, 0, 0, 0, 0, 0, 0, - 0, 329, 0, 538, 0, 492, 500, 0, 493, 501, - 494, 502, 495, 0, 496, 503, 497, 504, 498, 499, - 505, 0, 0, 0, 334, 0, 0, 48, 0, 391, - 0, 349, 351, 352, 353, -2, 0, 375, -2, 0, - 0, 0, 42, 43, 0, 0, 0, 0, 51, 913, - 53, 54, 0, 0, 0, 162, 609, 610, 611, 607, - 206, 0, 0, 150, 146, 90, 91, 92, 139, 94, - 139, 139, 139, 139, 159, 159, 159, 159, 122, 123, - 124, 125, 126, 0, 0, 109, 139, 139, 139, 113, - 129, 130, 131, 132, 133, 134, 135, 136, 95, 96, - 97, 98, 99, 100, 101, 102, 103, 141, 141, 141, - 143, 143, 638, 68, 0, 938, 0, 938, 80, 0, - 220, 222, 223, 0, 0, 0, 0, 0, 0, 0, - 0, 252, 615, 0, 938, 255, 256, 393, 646, 647, - 260, 261, 263, 264, 265, 266, 267, 268, 275, 279, - 0, 297, 0, 0, 281, 282, 0, 0, 577, 0, - 0, 0, 0, 0, 568, 570, 571, 576, 32, 329, - 0, 557, 0, 0, 0, 333, 27, 402, 403, 405, - 422, 0, 424, 426, 344, 340, 0, 547, -2, 412, - 413, 437, 438, 439, 0, 0, 0, 0, 435, 417, - 0, 448, 449, 450, 451, 452, 453, 454, 455, 456, - 457, 458, 459, 462, 522, 523, 0, 460, 461, 470, - 0, 0, 0, 335, 336, 440, 0, 595, 29, 0, - 0, 0, 0, 445, 549, 0, 445, 549, 0, 0, - 0, 544, 541, 0, 0, 546, 0, 509, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 390, 0, 0, - 0, 0, 0, 0, 380, 0, 0, 383, 0, 0, - 0, 0, 374, 0, 0, 395, 858, 376, 0, 378, - 379, 399, 0, 399, 45, 399, 47, 0, 394, 601, - 52, 0, 0, 57, 58, 602, 603, 604, 605, 0, - 81, 207, 209, 212, 213, 214, 85, 86, 87, 0, - 0, 194, 0, 0, 188, 188, 0, 186, 187, 83, - 153, 151, 0, 148, 147, 93, 0, 159, 159, 116, - 117, 162, 0, 162, 162, 162, 0, 0, 110, 111, - 112, 104, 0, 105, 106, 107, 0, 108, 0, 0, - 938, 70, 617, 71, 937, 0, 0, 630, 221, 620, - 621, 622, 623, 624, 625, 626, 627, 628, 629, 0, - 72, 225, 227, 226, 230, 0, 0, 0, 250, 938, - 254, 294, 278, 0, 0, 295, 296, 301, 285, 0, - 574, 575, 0, 567, 25, 0, 612, 613, 558, 559, - 347, 423, 425, 427, 0, 334, 414, 435, 418, 0, - 415, 0, 0, 409, 475, 0, 0, 0, 442, -2, - 479, 480, 0, 0, 0, 0, 0, 515, 0, 0, - 516, 0, 564, 0, 542, 0, 0, 491, 0, 510, - 0, 0, 511, 512, 513, 514, 589, 0, 0, -2, - 0, 0, 399, 597, 0, 350, 369, 371, 0, 366, - 381, 382, 384, 0, 386, 0, 388, 389, 354, 356, - 357, 0, 0, 0, 0, 377, 564, 0, 399, 40, - 41, 0, 55, 56, 0, 0, 62, 163, 164, 0, - 210, 0, 0, 0, 181, 188, 188, 184, 189, 185, - 0, 155, 0, 152, 89, 149, 0, 162, 162, 118, - 0, 119, 120, 121, 0, 137, 0, 0, 0, 0, - 639, 69, 215, 937, 232, 233, 234, 235, 236, 237, - 238, 239, 240, 241, 242, 243, 937, 0, 937, 631, - 632, 633, 634, 0, 75, 0, 0, 0, 0, 0, - 253, 297, 298, 299, 578, 0, 26, 399, 0, 341, + 297, 262, 0, 0, 938, 938, 938, 0, 938, 938, + 269, 939, 940, 271, 272, 274, 938, 938, 276, 0, + 294, 292, 293, 288, 289, 0, 300, 283, 284, 287, + 316, 317, 30, 936, 24, 0, 0, 573, 0, 565, + 566, 569, 572, 29, 326, 0, 332, 330, 331, 322, + 0, 339, 0, 0, 0, 343, 0, 345, 346, 0, + 404, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 428, 429, 430, 431, 432, 433, 434, 407, 0, 421, + 0, 0, 0, 463, 464, 465, 466, 467, 468, 469, + 0, 334, 29, 0, 441, 0, 0, 0, 0, 0, + 0, 0, 0, 329, 0, 538, 0, 492, 500, 0, + 493, 501, 494, 502, 495, 0, 496, 503, 497, 504, + 498, 499, 505, 0, 0, 0, 334, 0, 0, 48, + 0, 391, 0, 349, 351, 352, 353, -2, 0, 375, + -2, 0, 0, 0, 42, 43, 0, 0, 0, 0, + 51, 913, 53, 54, 0, 0, 0, 162, 609, 610, + 611, 607, 206, 0, 0, 150, 146, 90, 91, 92, + 139, 94, 139, 139, 139, 139, 159, 159, 159, 159, + 122, 123, 124, 125, 126, 0, 0, 109, 139, 139, + 139, 113, 129, 130, 131, 132, 133, 134, 135, 136, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 141, + 141, 141, 143, 143, 638, 68, 0, 938, 0, 938, + 80, 0, 220, 222, 223, 0, 0, 0, 0, 0, + 0, 0, 0, 252, 615, 0, 938, 255, 256, 393, + 646, 647, 260, 261, 298, 299, 263, 264, 265, 266, + 267, 268, 275, 279, 0, 297, 0, 0, 281, 282, + 0, 0, 577, 0, 0, 0, 0, 0, 568, 570, + 571, 576, 32, 329, 0, 557, 0, 0, 0, 333, + 27, 402, 403, 405, 422, 0, 424, 426, 344, 340, + 0, 547, -2, 412, 413, 437, 438, 439, 0, 0, + 0, 0, 435, 417, 0, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 457, 458, 459, 462, 522, 523, + 0, 460, 461, 470, 0, 0, 0, 335, 336, 440, + 0, 595, 29, 0, 0, 0, 0, 445, 549, 0, + 445, 549, 0, 0, 0, 544, 541, 0, 0, 546, + 0, 509, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 390, 0, 0, 0, 0, 0, 0, 380, 0, + 0, 383, 0, 0, 0, 0, 374, 0, 0, 395, + 858, 376, 0, 378, 379, 399, 0, 399, 45, 399, + 47, 0, 394, 601, 52, 0, 0, 57, 58, 602, + 603, 604, 605, 0, 81, 207, 209, 212, 213, 214, + 85, 86, 87, 0, 0, 194, 0, 0, 188, 188, + 0, 186, 187, 83, 153, 151, 0, 148, 147, 93, + 0, 159, 159, 116, 117, 162, 0, 162, 162, 162, + 0, 0, 110, 111, 112, 104, 0, 105, 106, 107, + 0, 108, 0, 0, 938, 70, 617, 71, 937, 0, + 0, 630, 221, 620, 621, 622, 623, 624, 625, 626, + 627, 628, 629, 0, 72, 225, 227, 226, 230, 0, + 0, 0, 250, 938, 254, 294, 278, 295, 296, 301, + 285, 0, 574, 575, 0, 567, 25, 0, 612, 613, + 558, 559, 347, 423, 425, 427, 0, 334, 414, 435, + 418, 0, 415, 0, 0, 409, 475, 0, 0, 0, + 442, -2, 479, 480, 0, 0, 0, 0, 0, 515, + 0, 0, 516, 0, 564, 0, 542, 0, 0, 491, + 0, 510, 0, 0, 511, 512, 513, 514, 589, 0, + 0, -2, 0, 0, 399, 597, 0, 350, 369, 371, + 0, 366, 381, 382, 384, 0, 386, 0, 388, 389, + 354, 356, 357, 0, 0, 0, 0, 377, 564, 0, + 399, 40, 41, 0, 55, 56, 0, 0, 62, 163, + 164, 0, 210, 0, 0, 0, 181, 188, 188, 184, + 189, 185, 0, 155, 0, 152, 89, 149, 0, 162, + 162, 118, 0, 119, 120, 121, 0, 137, 0, 0, + 0, 0, 639, 69, 215, 937, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 937, 0, + 937, 631, 632, 633, 634, 0, 75, 0, 0, 0, + 0, 0, 253, 297, 578, 0, 26, 399, 0, 341, 548, 0, 416, 0, 436, 419, 476, 477, 337, 0, 139, 139, 527, 139, 143, 530, 139, 532, 139, 535, 0, 0, 0, 0, 0, 0, 0, 539, 490, 545, @@ -5088,108 +5092,110 @@ yydefault: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:1506 { - yyVAL.statement = &Show{Type: CharsetStr} + showTablesOpt := &ShowTablesOpt{Filter: yyDollar[4].showFilter} + yyVAL.statement = &Show{Type: CharsetStr, ShowTablesOpt: showTablesOpt} } case 262: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1510 +//line sql.y:1511 { - yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} + showTablesOpt := &ShowTablesOpt{Filter: yyDollar[3].showFilter} + yyVAL.statement = &Show{Type: string(yyDollar[2].bytes), ShowTablesOpt: showTablesOpt} } case 263: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1514 +//line sql.y:1516 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 264: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1519 +//line sql.y:1521 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 265: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1523 +//line sql.y:1525 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 266: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1527 +//line sql.y:1529 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes), Table: yyDollar[4].tableName} } case 267: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1531 +//line sql.y:1533 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 268: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1535 +//line sql.y:1537 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 269: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1539 +//line sql.y:1541 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 270: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1543 +//line sql.y:1545 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 271: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1547 +//line sql.y:1549 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 272: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1551 +//line sql.y:1553 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 273: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1555 +//line sql.y:1557 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 274: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1559 +//line sql.y:1561 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 275: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1563 +//line sql.y:1565 { yyVAL.statement = &Show{Scope: yyDollar[2].str, Type: string(yyDollar[3].bytes)} } case 276: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1567 +//line sql.y:1569 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 277: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1571 +//line sql.y:1573 { showTablesOpt := &ShowTablesOpt{Full: yyDollar[2].str, DbName: yyDollar[6].str, Filter: yyDollar[7].showFilter} yyVAL.statement = &Show{Type: string(yyDollar[3].str), ShowTablesOpt: showTablesOpt, OnTable: yyDollar[5].tableName} } case 278: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1576 +//line sql.y:1578 { // this is ugly, but I couldn't find a better way for now if yyDollar[3].str == "processlist" { @@ -5201,19 +5207,19 @@ yydefault: } case 279: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1586 +//line sql.y:1588 { yyVAL.statement = &Show{Scope: yyDollar[2].str, Type: string(yyDollar[3].bytes)} } case 280: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1590 +//line sql.y:1592 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 281: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1594 +//line sql.y:1596 { // Cannot dereference $4 directly, or else the parser stackcannot be pooled. See yyParsePooled showCollationFilterOpt := yyDollar[4].expr @@ -5221,429 +5227,429 @@ yydefault: } case 282: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1600 +//line sql.y:1602 { showTablesOpt := &ShowTablesOpt{Filter: yyDollar[4].showFilter} yyVAL.statement = &Show{Scope: string(yyDollar[2].bytes), Type: string(yyDollar[3].bytes), ShowTablesOpt: showTablesOpt} } case 283: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1605 +//line sql.y:1607 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 284: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1609 +//line sql.y:1611 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 285: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1613 +//line sql.y:1615 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes), OnTable: yyDollar[5].tableName} } case 286: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1617 +//line sql.y:1619 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 287: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1631 +//line sql.y:1633 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 288: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1637 +//line sql.y:1639 { yyVAL.str = string(yyDollar[1].bytes) } case 289: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1641 +//line sql.y:1643 { yyVAL.str = string(yyDollar[1].bytes) } case 290: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1647 +//line sql.y:1649 { yyVAL.str = "" } case 291: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1651 +//line sql.y:1653 { yyVAL.str = "full " } case 292: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1657 +//line sql.y:1659 { yyVAL.str = string(yyDollar[1].bytes) } case 293: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1661 +//line sql.y:1663 { yyVAL.str = string(yyDollar[1].bytes) } case 294: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1667 +//line sql.y:1669 { yyVAL.str = "" } case 295: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1671 +//line sql.y:1673 { yyVAL.str = yyDollar[2].tableIdent.v } case 296: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1675 +//line sql.y:1677 { yyVAL.str = yyDollar[2].tableIdent.v } case 297: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1681 +//line sql.y:1683 { yyVAL.showFilter = nil } case 298: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1685 +//line sql.y:1687 { yyVAL.showFilter = &ShowFilter{Like: string(yyDollar[2].bytes)} } case 299: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1689 +//line sql.y:1691 { yyVAL.showFilter = &ShowFilter{Filter: yyDollar[2].expr} } case 300: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1695 +//line sql.y:1697 { yyVAL.showFilter = nil } case 301: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1699 +//line sql.y:1701 { yyVAL.showFilter = &ShowFilter{Like: string(yyDollar[2].bytes)} } case 302: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1705 +//line sql.y:1707 { yyVAL.str = "" } case 303: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1709 +//line sql.y:1711 { yyVAL.str = SessionStr } case 304: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1713 +//line sql.y:1715 { yyVAL.str = GlobalStr } case 305: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1719 +//line sql.y:1721 { yyVAL.statement = &Use{DBName: yyDollar[2].tableIdent} } case 306: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1723 +//line sql.y:1725 { yyVAL.statement = &Use{DBName: TableIdent{v: ""}} } case 307: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1729 +//line sql.y:1731 { yyVAL.statement = &Begin{} } case 308: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1733 +//line sql.y:1735 { yyVAL.statement = &Begin{} } case 309: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1739 +//line sql.y:1741 { yyVAL.statement = &Commit{} } case 310: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1745 +//line sql.y:1747 { yyVAL.statement = &Rollback{} } case 311: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1751 +//line sql.y:1753 { yyVAL.statement = &OtherRead{} } case 312: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1755 +//line sql.y:1757 { yyVAL.statement = &OtherRead{} } case 313: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1759 +//line sql.y:1761 { yyVAL.statement = &OtherRead{} } case 314: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1763 +//line sql.y:1765 { yyVAL.statement = &OtherAdmin{} } case 315: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1767 +//line sql.y:1769 { yyVAL.statement = &OtherAdmin{} } case 316: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1771 +//line sql.y:1773 { yyVAL.statement = &OtherAdmin{} } case 317: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1775 +//line sql.y:1777 { yyVAL.statement = &OtherAdmin{} } case 318: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1781 +//line sql.y:1783 { yyVAL.statement = &DDL{Action: FlushStr} } case 319: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1785 +//line sql.y:1787 { setAllowComments(yylex, true) } case 320: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1789 +//line sql.y:1791 { yyVAL.bytes2 = yyDollar[2].bytes2 setAllowComments(yylex, false) } case 321: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1795 +//line sql.y:1797 { yyVAL.bytes2 = nil } case 322: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1799 +//line sql.y:1801 { yyVAL.bytes2 = append(yyDollar[1].bytes2, yyDollar[2].bytes) } case 323: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1805 +//line sql.y:1807 { yyVAL.str = UnionStr } case 324: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1809 +//line sql.y:1811 { yyVAL.str = UnionAllStr } case 325: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1813 +//line sql.y:1815 { yyVAL.str = UnionDistinctStr } case 326: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1818 +//line sql.y:1820 { yyVAL.str = "" } case 327: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1822 +//line sql.y:1824 { yyVAL.str = SQLNoCacheStr } case 328: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1826 +//line sql.y:1828 { yyVAL.str = SQLCacheStr } case 329: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1831 +//line sql.y:1833 { yyVAL.str = "" } case 330: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1835 +//line sql.y:1837 { yyVAL.str = DistinctStr } case 331: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1839 +//line sql.y:1841 { yyVAL.str = DistinctStr } case 332: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1844 +//line sql.y:1846 { yyVAL.str = "" } case 333: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1848 +//line sql.y:1850 { yyVAL.str = StraightJoinHint } case 334: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1853 +//line sql.y:1855 { yyVAL.selectExprs = nil } case 335: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1857 +//line sql.y:1859 { yyVAL.selectExprs = yyDollar[1].selectExprs } case 336: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1863 +//line sql.y:1865 { yyVAL.selectExprs = SelectExprs{yyDollar[1].selectExpr} } case 337: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1867 +//line sql.y:1869 { yyVAL.selectExprs = append(yyVAL.selectExprs, yyDollar[3].selectExpr) } case 338: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1873 +//line sql.y:1875 { yyVAL.selectExpr = &StarExpr{} } case 339: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1877 +//line sql.y:1879 { yyVAL.selectExpr = &AliasedExpr{Expr: yyDollar[1].expr, As: yyDollar[2].colIdent} } case 340: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1881 +//line sql.y:1883 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Name: yyDollar[1].tableIdent}} } case 341: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1885 +//line sql.y:1887 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}} } case 342: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1890 +//line sql.y:1892 { yyVAL.colIdent = ColIdent{} } case 343: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1894 +//line sql.y:1896 { yyVAL.colIdent = yyDollar[1].colIdent } case 344: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1898 +//line sql.y:1900 { yyVAL.colIdent = yyDollar[2].colIdent } case 346: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1905 +//line sql.y:1907 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 347: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1910 +//line sql.y:1912 { yyVAL.tableExprs = TableExprs{&AliasedTableExpr{Expr: TableName{Name: NewTableIdent("dual")}}} } case 348: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1914 +//line sql.y:1916 { yyVAL.tableExprs = yyDollar[2].tableExprs } case 349: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1920 +//line sql.y:1922 { yyVAL.tableExprs = TableExprs{yyDollar[1].tableExpr} } case 350: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1924 +//line sql.y:1926 { yyVAL.tableExprs = append(yyVAL.tableExprs, yyDollar[3].tableExpr) } case 353: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1934 +//line sql.y:1936 { yyVAL.tableExpr = yyDollar[1].aliasedTableName } case 354: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1938 +//line sql.y:1940 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery, As: yyDollar[3].tableIdent} } case 355: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1942 +//line sql.y:1944 { // missed alias for subquery yylex.Error("Every derived table must have its own alias") @@ -5651,199 +5657,199 @@ yydefault: } case 356: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1948 +//line sql.y:1950 { yyVAL.tableExpr = &ParenTableExpr{Exprs: yyDollar[2].tableExprs} } case 357: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1954 +//line sql.y:1956 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].tableIdent, Hints: yyDollar[3].indexHints} } case 358: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1958 +//line sql.y:1960 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, Partitions: yyDollar[4].partitions, As: yyDollar[6].tableIdent, Hints: yyDollar[7].indexHints} } case 359: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1964 +//line sql.y:1966 { yyVAL.columns = Columns{yyDollar[1].colIdent} } case 360: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1968 +//line sql.y:1970 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } case 361: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1974 +//line sql.y:1976 { yyVAL.partitions = Partitions{yyDollar[1].colIdent} } case 362: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1978 +//line sql.y:1980 { yyVAL.partitions = append(yyVAL.partitions, yyDollar[3].colIdent) } case 363: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1991 +//line sql.y:1993 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 364: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1995 +//line sql.y:1997 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 365: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1999 +//line sql.y:2001 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 366: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2003 +//line sql.y:2005 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr} } case 367: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2009 +//line sql.y:2011 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } case 368: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2011 +//line sql.y:2013 { yyVAL.joinCondition = JoinCondition{Using: yyDollar[3].columns} } case 369: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2015 +//line sql.y:2017 { yyVAL.joinCondition = JoinCondition{} } case 370: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2017 +//line sql.y:2019 { yyVAL.joinCondition = yyDollar[1].joinCondition } case 371: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2021 +//line sql.y:2023 { yyVAL.joinCondition = JoinCondition{} } case 372: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2023 +//line sql.y:2025 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } case 373: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2026 +//line sql.y:2028 { yyVAL.empty = struct{}{} } case 374: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2028 +//line sql.y:2030 { yyVAL.empty = struct{}{} } case 375: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2031 +//line sql.y:2033 { yyVAL.tableIdent = NewTableIdent("") } case 376: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2035 +//line sql.y:2037 { yyVAL.tableIdent = yyDollar[1].tableIdent } case 377: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2039 +//line sql.y:2041 { yyVAL.tableIdent = yyDollar[2].tableIdent } case 379: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2046 +//line sql.y:2048 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 380: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2052 +//line sql.y:2054 { yyVAL.str = JoinStr } case 381: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2056 +//line sql.y:2058 { yyVAL.str = JoinStr } case 382: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2060 +//line sql.y:2062 { yyVAL.str = JoinStr } case 383: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2066 +//line sql.y:2068 { yyVAL.str = StraightJoinStr } case 384: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2072 +//line sql.y:2074 { yyVAL.str = LeftJoinStr } case 385: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2076 +//line sql.y:2078 { yyVAL.str = LeftJoinStr } case 386: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2080 +//line sql.y:2082 { yyVAL.str = RightJoinStr } case 387: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2084 +//line sql.y:2086 { yyVAL.str = RightJoinStr } case 388: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2090 +//line sql.y:2092 { yyVAL.str = NaturalJoinStr } case 389: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2094 +//line sql.y:2096 { if yyDollar[2].str == LeftJoinStr { yyVAL.str = NaturalLeftJoinStr @@ -5853,463 +5859,463 @@ yydefault: } case 390: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2104 +//line sql.y:2106 { yyVAL.tableName = yyDollar[2].tableName } case 391: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2108 +//line sql.y:2110 { yyVAL.tableName = yyDollar[1].tableName } case 392: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2114 +//line sql.y:2116 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } case 393: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2118 +//line sql.y:2120 { yyVAL.tableName = TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent} } case 394: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2124 +//line sql.y:2126 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } case 395: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2129 +//line sql.y:2131 { yyVAL.indexHints = nil } case 396: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2133 +//line sql.y:2135 { yyVAL.indexHints = &IndexHints{Type: UseStr, Indexes: yyDollar[4].columns} } case 397: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2137 +//line sql.y:2139 { yyVAL.indexHints = &IndexHints{Type: IgnoreStr, Indexes: yyDollar[4].columns} } case 398: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2141 +//line sql.y:2143 { yyVAL.indexHints = &IndexHints{Type: ForceStr, Indexes: yyDollar[4].columns} } case 399: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2146 +//line sql.y:2148 { yyVAL.expr = nil } case 400: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2150 +//line sql.y:2152 { yyVAL.expr = yyDollar[2].expr } case 401: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2156 +//line sql.y:2158 { yyVAL.expr = yyDollar[1].expr } case 402: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2160 +//line sql.y:2162 { yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 403: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2164 +//line sql.y:2166 { yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 404: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2168 +//line sql.y:2170 { yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} } case 405: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2172 +//line sql.y:2174 { yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} } case 406: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2176 +//line sql.y:2178 { yyVAL.expr = yyDollar[1].expr } case 407: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2180 +//line sql.y:2182 { yyVAL.expr = &Default{ColName: yyDollar[2].str} } case 408: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2186 +//line sql.y:2188 { yyVAL.str = "" } case 409: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2190 +//line sql.y:2192 { yyVAL.str = string(yyDollar[2].bytes) } case 410: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2196 +//line sql.y:2198 { yyVAL.boolVal = BoolVal(true) } case 411: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2200 +//line sql.y:2202 { yyVAL.boolVal = BoolVal(false) } case 412: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2206 +//line sql.y:2208 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } case 413: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2210 +//line sql.y:2212 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } case 414: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2214 +//line sql.y:2216 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } case 415: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2218 +//line sql.y:2220 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } case 416: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2222 +//line sql.y:2224 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } case 417: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2226 +//line sql.y:2228 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } case 418: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2230 +//line sql.y:2232 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } case 419: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2234 +//line sql.y:2236 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } case 420: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2238 +//line sql.y:2240 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } case 421: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2242 +//line sql.y:2244 { yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } case 422: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2248 +//line sql.y:2250 { yyVAL.str = IsNullStr } case 423: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2252 +//line sql.y:2254 { yyVAL.str = IsNotNullStr } case 424: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2256 +//line sql.y:2258 { yyVAL.str = IsTrueStr } case 425: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2260 +//line sql.y:2262 { yyVAL.str = IsNotTrueStr } case 426: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2264 +//line sql.y:2266 { yyVAL.str = IsFalseStr } case 427: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2268 +//line sql.y:2270 { yyVAL.str = IsNotFalseStr } case 428: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2274 +//line sql.y:2276 { yyVAL.str = EqualStr } case 429: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2278 +//line sql.y:2280 { yyVAL.str = LessThanStr } case 430: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2282 +//line sql.y:2284 { yyVAL.str = GreaterThanStr } case 431: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2286 +//line sql.y:2288 { yyVAL.str = LessEqualStr } case 432: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2290 +//line sql.y:2292 { yyVAL.str = GreaterEqualStr } case 433: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2294 +//line sql.y:2296 { yyVAL.str = NotEqualStr } case 434: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2298 +//line sql.y:2300 { yyVAL.str = NullSafeEqualStr } case 435: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2303 +//line sql.y:2305 { yyVAL.expr = nil } case 436: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2307 +//line sql.y:2309 { yyVAL.expr = yyDollar[2].expr } case 437: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2313 +//line sql.y:2315 { yyVAL.colTuple = yyDollar[1].valTuple } case 438: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2317 +//line sql.y:2319 { yyVAL.colTuple = yyDollar[1].subquery } case 439: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2321 +//line sql.y:2323 { yyVAL.colTuple = ListArg(yyDollar[1].bytes) } case 440: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2327 +//line sql.y:2329 { yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } case 441: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2333 +//line sql.y:2335 { yyVAL.exprs = Exprs{yyDollar[1].expr} } case 442: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2337 +//line sql.y:2339 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 443: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2343 +//line sql.y:2345 { yyVAL.expr = yyDollar[1].expr } case 444: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2347 +//line sql.y:2349 { yyVAL.expr = yyDollar[1].boolVal } case 445: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2351 +//line sql.y:2353 { yyVAL.expr = yyDollar[1].colName } case 446: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2355 +//line sql.y:2357 { yyVAL.expr = yyDollar[1].expr } case 447: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2359 +//line sql.y:2361 { yyVAL.expr = yyDollar[1].subquery } case 448: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2363 +//line sql.y:2365 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } case 449: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2367 +//line sql.y:2369 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } case 450: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2371 +//line sql.y:2373 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } case 451: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2375 +//line sql.y:2377 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } case 452: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2379 +//line sql.y:2381 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } case 453: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2383 +//line sql.y:2385 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } case 454: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2387 +//line sql.y:2389 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } case 455: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2391 +//line sql.y:2393 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } case 456: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2395 +//line sql.y:2397 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 457: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2399 +//line sql.y:2401 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 458: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2403 +//line sql.y:2405 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } case 459: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2407 +//line sql.y:2409 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} } case 460: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2411 +//line sql.y:2413 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONExtractOp, Right: yyDollar[3].expr} } case 461: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2415 +//line sql.y:2417 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONUnquoteExtractOp, Right: yyDollar[3].expr} } case 462: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2419 +//line sql.y:2421 { yyVAL.expr = &CollateExpr{Expr: yyDollar[1].expr, Charset: yyDollar[3].str} } case 463: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2423 +//line sql.y:2425 { yyVAL.expr = &UnaryExpr{Operator: BinaryStr, Expr: yyDollar[2].expr} } case 464: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2427 +//line sql.y:2429 { yyVAL.expr = &UnaryExpr{Operator: UBinaryStr, Expr: yyDollar[2].expr} } case 465: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2431 +//line sql.y:2433 { yyVAL.expr = &UnaryExpr{Operator: Utf8mb4Str, Expr: yyDollar[2].expr} } case 466: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2435 +//line sql.y:2437 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -6319,7 +6325,7 @@ yydefault: } case 467: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2443 +//line sql.y:2445 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -6335,19 +6341,19 @@ yydefault: } case 468: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2457 +//line sql.y:2459 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } case 469: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2461 +//line sql.y:2463 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } case 470: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2465 +//line sql.y:2467 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -6357,319 +6363,319 @@ yydefault: } case 475: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2483 +//line sql.y:2485 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } case 476: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2487 +//line sql.y:2489 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } case 477: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2491 +//line sql.y:2493 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } case 478: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2495 +//line sql.y:2497 { yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } case 479: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2505 +//line sql.y:2507 { yyVAL.expr = &FuncExpr{Name: NewColIdent("left"), Exprs: yyDollar[3].selectExprs} } case 480: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2509 +//line sql.y:2511 { yyVAL.expr = &FuncExpr{Name: NewColIdent("right"), Exprs: yyDollar[3].selectExprs} } case 481: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2513 +//line sql.y:2515 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } case 482: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2517 +//line sql.y:2519 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } case 483: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2521 +//line sql.y:2523 { yyVAL.expr = &ConvertUsingExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].str} } case 484: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2525 +//line sql.y:2527 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } case 485: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2529 +//line sql.y:2531 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } case 486: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2533 +//line sql.y:2535 { yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } case 487: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2537 +//line sql.y:2539 { yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } case 488: yyDollar = yyS[yypt-9 : yypt+1] -//line sql.y:2541 +//line sql.y:2543 { yyVAL.expr = &MatchExpr{Columns: yyDollar[3].selectExprs, Expr: yyDollar[7].expr, Option: yyDollar[8].str} } case 489: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:2545 +//line sql.y:2547 { yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} } case 490: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2549 +//line sql.y:2551 { yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } case 491: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2553 +//line sql.y:2555 { yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} } case 492: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2563 +//line sql.y:2565 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_timestamp")} } case 493: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2567 +//line sql.y:2569 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_timestamp")} } case 494: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2571 +//line sql.y:2573 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_time")} } case 495: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2576 +//line sql.y:2578 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_date")} } case 496: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2581 +//line sql.y:2583 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtime")} } case 497: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2586 +//line sql.y:2588 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtimestamp")} } case 498: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2592 +//line sql.y:2594 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_date")} } case 499: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2597 +//line sql.y:2599 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_time")} } case 500: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2602 +//line sql.y:2604 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_timestamp"), Fsp: yyDollar[2].expr} } case 501: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2606 +//line sql.y:2608 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_timestamp"), Fsp: yyDollar[2].expr} } case 502: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2610 +//line sql.y:2612 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_time"), Fsp: yyDollar[2].expr} } case 503: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2615 +//line sql.y:2617 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtime"), Fsp: yyDollar[2].expr} } case 504: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2620 +//line sql.y:2622 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtimestamp"), Fsp: yyDollar[2].expr} } case 505: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2625 +//line sql.y:2627 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_time"), Fsp: yyDollar[2].expr} } case 506: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2629 +//line sql.y:2631 { yyVAL.expr = &TimestampFuncExpr{Name: string("timestampadd"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } case 507: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2633 +//line sql.y:2635 { yyVAL.expr = &TimestampFuncExpr{Name: string("timestampdiff"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } case 510: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2643 +//line sql.y:2645 { yyVAL.expr = yyDollar[2].expr } case 511: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2653 +//line sql.y:2655 { yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } case 512: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2657 +//line sql.y:2659 { yyVAL.expr = &FuncExpr{Name: NewColIdent("database"), Exprs: yyDollar[3].selectExprs} } case 513: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2661 +//line sql.y:2663 { yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } case 514: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2665 +//line sql.y:2667 { yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } case 515: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2669 +//line sql.y:2671 { yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 516: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2673 +//line sql.y:2675 { yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 517: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2679 +//line sql.y:2681 { yyVAL.str = "" } case 518: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2683 +//line sql.y:2685 { yyVAL.str = BooleanModeStr } case 519: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2687 +//line sql.y:2689 { yyVAL.str = NaturalLanguageModeStr } case 520: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:2691 +//line sql.y:2693 { yyVAL.str = NaturalLanguageModeWithQueryExpansionStr } case 521: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2695 +//line sql.y:2697 { yyVAL.str = QueryExpansionStr } case 522: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2701 +//line sql.y:2703 { yyVAL.str = string(yyDollar[1].bytes) } case 523: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2705 +//line sql.y:2707 { yyVAL.str = string(yyDollar[1].bytes) } case 524: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2711 +//line sql.y:2713 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 525: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2715 +//line sql.y:2717 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Operator: CharacterSetStr} } case 526: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2719 +//line sql.y:2721 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].bytes)} } case 527: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2723 +//line sql.y:2725 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 528: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2727 +//line sql.y:2729 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 529: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2731 +//line sql.y:2733 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length @@ -6677,169 +6683,169 @@ yydefault: } case 530: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2737 +//line sql.y:2739 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 531: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2741 +//line sql.y:2743 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 532: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2745 +//line sql.y:2747 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 533: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2749 +//line sql.y:2751 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 534: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2753 +//line sql.y:2755 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 535: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2757 +//line sql.y:2759 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 536: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2761 +//line sql.y:2763 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 537: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2766 +//line sql.y:2768 { yyVAL.expr = nil } case 538: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2770 +//line sql.y:2772 { yyVAL.expr = yyDollar[1].expr } case 539: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2775 +//line sql.y:2777 { yyVAL.str = string("") } case 540: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2779 +//line sql.y:2781 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } case 541: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2785 +//line sql.y:2787 { yyVAL.whens = []*When{yyDollar[1].when} } case 542: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2789 +//line sql.y:2791 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } case 543: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2795 +//line sql.y:2797 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } case 544: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2800 +//line sql.y:2802 { yyVAL.expr = nil } case 545: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2804 +//line sql.y:2806 { yyVAL.expr = yyDollar[2].expr } case 546: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2810 +//line sql.y:2812 { yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } case 547: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2814 +//line sql.y:2816 { yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } case 548: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2818 +//line sql.y:2820 { yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } case 549: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2824 +//line sql.y:2826 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } case 550: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2828 +//line sql.y:2830 { yyVAL.expr = NewHexVal(yyDollar[1].bytes) } case 551: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2832 +//line sql.y:2834 { yyVAL.expr = NewBitVal(yyDollar[1].bytes) } case 552: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2836 +//line sql.y:2838 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } case 553: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2840 +//line sql.y:2842 { yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } case 554: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2844 +//line sql.y:2846 { yyVAL.expr = NewHexNum(yyDollar[1].bytes) } case 555: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2848 +//line sql.y:2850 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } case 556: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2852 +//line sql.y:2854 { yyVAL.expr = &NullVal{} } case 557: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2858 +//line sql.y:2860 { // TODO(sougou): Deprecate this construct. if yyDollar[1].colIdent.Lowered() != "value" { @@ -6850,237 +6856,237 @@ yydefault: } case 558: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2867 +//line sql.y:2869 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } case 559: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2871 +//line sql.y:2873 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } case 560: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2876 +//line sql.y:2878 { yyVAL.exprs = nil } case 561: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2880 +//line sql.y:2882 { yyVAL.exprs = yyDollar[3].exprs } case 562: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2885 +//line sql.y:2887 { yyVAL.expr = nil } case 563: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2889 +//line sql.y:2891 { yyVAL.expr = yyDollar[2].expr } case 564: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2894 +//line sql.y:2896 { yyVAL.orderBy = nil } case 565: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2898 +//line sql.y:2900 { yyVAL.orderBy = yyDollar[3].orderBy } case 566: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2904 +//line sql.y:2906 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } case 567: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2908 +//line sql.y:2910 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } case 568: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2914 +//line sql.y:2916 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } case 569: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2919 +//line sql.y:2921 { yyVAL.str = AscScr } case 570: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2923 +//line sql.y:2925 { yyVAL.str = AscScr } case 571: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2927 +//line sql.y:2929 { yyVAL.str = DescScr } case 572: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2932 +//line sql.y:2934 { yyVAL.limit = nil } case 573: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2936 +//line sql.y:2938 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } case 574: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2940 +//line sql.y:2942 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } case 575: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2944 +//line sql.y:2946 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } case 576: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2949 +//line sql.y:2951 { yyVAL.str = "" } case 577: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2953 +//line sql.y:2955 { yyVAL.str = ForUpdateStr } case 578: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2957 +//line sql.y:2959 { yyVAL.str = ShareModeStr } case 579: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2970 +//line sql.y:2972 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } case 580: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2974 +//line sql.y:2976 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } case 581: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2978 +//line sql.y:2980 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } case 582: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2983 +//line sql.y:2985 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } case 583: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2987 +//line sql.y:2989 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } case 584: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2991 +//line sql.y:2993 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } case 585: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2998 +//line sql.y:3000 { yyVAL.columns = Columns{yyDollar[1].colIdent} } case 586: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3002 +//line sql.y:3004 { yyVAL.columns = Columns{yyDollar[3].colIdent} } case 587: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3006 +//line sql.y:3008 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } case 588: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3010 +//line sql.y:3012 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } case 589: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3015 +//line sql.y:3017 { yyVAL.updateExprs = nil } case 590: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3019 +//line sql.y:3021 { yyVAL.updateExprs = yyDollar[5].updateExprs } case 591: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3025 +//line sql.y:3027 { yyVAL.values = Values{yyDollar[1].valTuple} } case 592: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3029 +//line sql.y:3031 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } case 593: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3035 +//line sql.y:3037 { yyVAL.valTuple = yyDollar[1].valTuple } case 594: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3039 +//line sql.y:3041 { yyVAL.valTuple = ValTuple{} } case 595: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3045 +//line sql.y:3047 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } case 596: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3051 +//line sql.y:3053 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -7090,277 +7096,277 @@ yydefault: } case 597: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3061 +//line sql.y:3063 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } case 598: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3065 +//line sql.y:3067 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } case 599: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3071 +//line sql.y:3073 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } case 600: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3077 +//line sql.y:3079 { yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} } case 601: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3081 +//line sql.y:3083 { yyVAL.setExprs = append(yyDollar[1].setExprs, yyDollar[3].setExpr) } case 602: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3087 +//line sql.y:3089 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("on"))} } case 603: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3091 +//line sql.y:3093 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("off"))} } case 604: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3095 +//line sql.y:3097 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: yyDollar[3].expr} } case 605: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3099 +//line sql.y:3101 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(string(yyDollar[1].bytes)), Expr: yyDollar[2].expr} } case 607: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3106 +//line sql.y:3108 { yyVAL.bytes = []byte("charset") } case 609: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3113 +//line sql.y:3115 { yyVAL.expr = NewStrVal([]byte(yyDollar[1].colIdent.String())) } case 610: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3117 +//line sql.y:3119 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } case 611: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3121 +//line sql.y:3123 { yyVAL.expr = &Default{} } case 614: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3130 +//line sql.y:3132 { yyVAL.byt = 0 } case 615: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3132 +//line sql.y:3134 { yyVAL.byt = 1 } case 616: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3135 +//line sql.y:3137 { yyVAL.empty = struct{}{} } case 617: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3137 +//line sql.y:3139 { yyVAL.empty = struct{}{} } case 618: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3140 +//line sql.y:3142 { yyVAL.str = "" } case 619: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3142 +//line sql.y:3144 { yyVAL.str = IgnoreStr } case 620: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3146 +//line sql.y:3148 { yyVAL.empty = struct{}{} } case 621: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3148 +//line sql.y:3150 { yyVAL.empty = struct{}{} } case 622: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3150 +//line sql.y:3152 { yyVAL.empty = struct{}{} } case 623: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3152 +//line sql.y:3154 { yyVAL.empty = struct{}{} } case 624: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3154 +//line sql.y:3156 { yyVAL.empty = struct{}{} } case 625: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3156 +//line sql.y:3158 { yyVAL.empty = struct{}{} } case 626: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3158 +//line sql.y:3160 { yyVAL.empty = struct{}{} } case 627: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3160 +//line sql.y:3162 { yyVAL.empty = struct{}{} } case 628: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3162 +//line sql.y:3164 { yyVAL.empty = struct{}{} } case 629: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3164 +//line sql.y:3166 { yyVAL.empty = struct{}{} } case 630: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3167 +//line sql.y:3169 { yyVAL.empty = struct{}{} } case 631: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3169 +//line sql.y:3171 { yyVAL.empty = struct{}{} } case 632: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3171 +//line sql.y:3173 { yyVAL.empty = struct{}{} } case 633: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3175 +//line sql.y:3177 { yyVAL.empty = struct{}{} } case 634: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3177 +//line sql.y:3179 { yyVAL.empty = struct{}{} } case 635: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3180 +//line sql.y:3182 { yyVAL.empty = struct{}{} } case 636: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3182 +//line sql.y:3184 { yyVAL.empty = struct{}{} } case 637: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3184 +//line sql.y:3186 { yyVAL.empty = struct{}{} } case 638: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3187 +//line sql.y:3189 { yyVAL.colIdent = ColIdent{} } case 639: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3189 +//line sql.y:3191 { yyVAL.colIdent = yyDollar[2].colIdent } case 640: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3193 +//line sql.y:3195 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 641: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3197 +//line sql.y:3199 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 643: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3204 +//line sql.y:3206 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 644: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3210 +//line sql.y:3212 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 645: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3214 +//line sql.y:3216 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 647: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3221 +//line sql.y:3223 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 935: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3534 +//line sql.y:3536 { if incNesting(yylex) { yylex.Error("max nesting level reached") @@ -7369,31 +7375,31 @@ yydefault: } case 936: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3543 +//line sql.y:3545 { decNesting(yylex) } case 937: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3548 +//line sql.y:3550 { skipToEnd(yylex) } case 938: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3553 +//line sql.y:3555 { skipToEnd(yylex) } case 939: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3557 +//line sql.y:3559 { skipToEnd(yylex) } case 940: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3561 +//line sql.y:3563 { skipToEnd(yylex) } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index d1828c91e8e..a80d99ca2b6 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -1502,13 +1502,15 @@ show_statement: $$ = &Show{Type: string($2) + " " + string($3)} } /* SHOW CHARACTER SET and SHOW CHARSET are equivalent */ -| SHOW CHARACTER SET ddl_skip_to_end +| SHOW CHARACTER SET like_or_where_opt { - $$ = &Show{Type: CharsetStr} + showTablesOpt := &ShowTablesOpt{Filter: $4} + $$ = &Show{Type: CharsetStr, ShowTablesOpt: showTablesOpt} } -| SHOW CHARSET ddl_skip_to_end +| SHOW CHARSET like_or_where_opt { - $$ = &Show{Type: string($2)} + showTablesOpt := &ShowTablesOpt{Filter: $3} + $$ = &Show{Type: string($2), ShowTablesOpt: showTablesOpt} } | SHOW CREATE DATABASE ddl_skip_to_end { diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 725ec31f2c9..ec9e8312066 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "net/http" + "regexp" "sort" "strings" "sync" @@ -67,6 +68,13 @@ var ( queriesRoutedByTable = stats.NewCountersWithMultiLabels("QueriesRoutedByTable", "Queries routed from vtgate to vttablet by plan type, keyspace and table", []string{"Plan", "Keyspace", "Table"}) ) +const ( + utf8 = "utf8" + utf8mb4 = "utf8mb4" + both = "both" + charset = "charset" +) + func init() { topoproto.TabletTypeVar(&defaultTabletType, "default_tablet_type", topodatapb.TabletType_MASTER, "The default tablet type to set for queries, when one is not explicitly selected") } @@ -838,23 +846,20 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql fields := buildVarCharFields("Charset", "Description", "Default collation") maxLenField := &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32} fields = append(fields, maxLenField) - rows := make([][]sqltypes.Value, 0, 4) - row0 := buildVarCharRow( - "utf8", - "UTF-8 Unicode", - "utf8_general_ci") - row0 = append(row0, sqltypes.NewInt32(3)) - row1 := buildVarCharRow( - "utf8mb4", - "UTF-8 Unicode", - "utf8mb4_general_ci") - row1 = append(row1, sqltypes.NewInt32(4)) - rows = append(rows, row0, row1) + + charsets := []string{utf8, utf8mb4} + filter := show.ShowTablesOpt.Filter + rows, err := generateCharsetRows(filter, charsets) + if err != nil { + return nil, err + } + rowsAffected := uint64(len(rows)) + return &sqltypes.Result{ Fields: fields, Rows: rows, - RowsAffected: 2, - }, nil + RowsAffected: rowsAffected, + }, err case "create table": if destKeyspace == "" && show.HasTable() { // For "show create table", if there isn't a targeted keyspace already @@ -1538,6 +1543,95 @@ func buildVarCharRow(values ...string) []sqltypes.Value { return row } +func generateCharsetRows(showFilter *sqlparser.ShowFilter, colNames []string) ([][]sqltypes.Value, error) { + if showFilter == nil { + return buildCharsetRows(both), nil + } + + var filteredColName string + var err error + + if showFilter.Like != "" { + filteredColName, err = checkLikeOpt(showFilter.Like, colNames) + if err != nil { + return nil, err + } + + } else { + cmpExp, ok := showFilter.Filter.(*sqlparser.ComparisonExpr) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expect a 'LIKE' or '=' expression") + } + + left, ok := cmpExp.Left.(*sqlparser.ColName) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expect left side to be 'charset'") + } + leftOk := left.Name.EqualString(charset) + + if leftOk { + sqlVal, ok := cmpExp.Right.(*sqlparser.SQLVal) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "we expect the right side to be a string") + } + rightString := string(sqlVal.Val) + + switch cmpExp.Operator { + case sqlparser.EqualStr: + for _, colName := range colNames { + if rightString == colName { + filteredColName = colName + } + } + case sqlparser.LikeStr: + filteredColName, err = checkLikeOpt(rightString, colNames) + } + } + + } + + return buildCharsetRows(filteredColName), nil +} + +func buildCharsetRows(colName string) [][]sqltypes.Value { + row0 := buildVarCharRow( + "utf8", + "UTF-8 Unicode", + "utf8_general_ci") + row0 = append(row0, sqltypes.NewInt32(3)) + row1 := buildVarCharRow( + "utf8mb4", + "UTF-8 Unicode", + "utf8mb4_general_ci") + row1 = append(row1, sqltypes.NewInt32(4)) + + switch colName { + case utf8: + return [][]sqltypes.Value{row0} + case utf8mb4: + return [][]sqltypes.Value{row1} + case both: + return [][]sqltypes.Value{row0, row1} + } + + return [][]sqltypes.Value{} +} + +func checkLikeOpt(likeOpt string, colNames []string) (string, error) { + likeRegexp := strings.ReplaceAll(likeOpt, "%", ".*") + for _, v := range colNames { + match, err := regexp.MatchString(likeRegexp, v) + if err != nil { + return "", err + } + if match { + return v, nil + } + } + + return "", nil +} + // Prepare executes a prepare statements. func (e *Executor) Prepare(ctx context.Context, method string, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (fld []*querypb.Field, err error) { logStats := NewLogStats(ctx, method, sql, bindVars) diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 683b6b3a2e2..115bf89da7a 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -49,6 +49,7 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestExecutorResultsExceeded(t *testing.T) { @@ -829,16 +830,43 @@ func TestExecutorShow(t *testing.T) { t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) } - _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show full columns from unknown from %v", KsTestUnsharded), nil) - if err != nil { - t.Errorf("Unexpected error: %v", err) + for _, query := range []string{"show charset", "show character set"} { + qr, err := executor.Execute(context.Background(), "TestExecute", session, query, nil) + require.NoError(t, err) + wantqr := &sqltypes.Result{ + Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), + Rows: [][]sqltypes.Value{ + append(buildVarCharRow( + "utf8", + "UTF-8 Unicode", + "utf8_general_ci"), sqltypes.NewInt32(3)), + append(buildVarCharRow( + "utf8mb4", + "UTF-8 Unicode", + "utf8mb4_general_ci"), + sqltypes.NewInt32(4)), + }, + RowsAffected: 2, + } + if !reflect.DeepEqual(qr, wantqr) { + t.Errorf("%v:\n%+v, want\n%+v", query, qr, wantqr) + } } - - for _, query := range []string{"show charset", "show charset like '%foo'", "show character set", "show character set like '%foo'"} { + for _, query := range []string{"show charset like '%foo'", "show character set like 'foo%'", "show charset like 'foo%'", "show character set where foo like 'utf8'", "show character set where charset like '%foo'", "show charset where charset = '%foo'"} { qr, err := executor.Execute(context.Background(), "TestExecute", session, query, nil) - if err != nil { - t.Error(err) + require.NoError(t, err) + wantqr := &sqltypes.Result{ + Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), + Rows: [][]sqltypes.Value{}, + RowsAffected: 0, + } + if !reflect.DeepEqual(qr, wantqr) { + t.Errorf("%v:\n%+v, want\n%+v", query, qr, wantqr) } + } + for _, query := range []string{"show charset like 'utf8'", "show character set like 'utf8'", "show charset where charset = 'utf8'", "show character set where charset = 'utf8'"} { + qr, err := executor.Execute(context.Background(), "TestExecute", session, query, nil) + require.NoError(t, err) wantqr := &sqltypes.Result{ Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), Rows: [][]sqltypes.Value{ @@ -846,18 +874,32 @@ func TestExecutorShow(t *testing.T) { "utf8", "UTF-8 Unicode", "utf8_general_ci"), sqltypes.NewInt32(3)), + }, + RowsAffected: 1, + } + if !reflect.DeepEqual(qr, wantqr) { + t.Errorf("%v:\n%+v, want\n%+v", query, qr, wantqr) + } + } + for _, query := range []string{"show charset like 'utf8mb4'", "show character set like 'utf8mb4'", "show charset where charset = 'utf8mb4'", "show character set where charset = 'utf8mb4'"} { + qr, err := executor.Execute(context.Background(), "TestExecute", session, query, nil) + require.NoError(t, err) + wantqr := &sqltypes.Result{ + Fields: append(buildVarCharFields("Charset", "Description", "Default collation"), &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}), + Rows: [][]sqltypes.Value{ append(buildVarCharRow( "utf8mb4", "UTF-8 Unicode", "utf8mb4_general_ci"), sqltypes.NewInt32(4)), }, - RowsAffected: 2, + RowsAffected: 1, } if !reflect.DeepEqual(qr, wantqr) { t.Errorf("%v:\n%+v, want\n%+v", query, qr, wantqr) } } + qr, err = executor.Execute(context.Background(), "TestExecute", session, "show engines", nil) if err != nil { t.Error(err) @@ -2624,6 +2666,62 @@ func TestDebugVSchema(t *testing.T) { } } +func TestGenerateCharsetRows(t *testing.T) { + rows := make([][]sqltypes.Value, 0, 4) + rows0 := [][]sqltypes.Value{ + append(buildVarCharRow( + "utf8", + "UTF-8 Unicode", + "utf8_general_ci"), + sqltypes.NewInt32(3)), + } + rows1 := [][]sqltypes.Value{ + append(buildVarCharRow( + "utf8mb4", + "UTF-8 Unicode", + "utf8mb4_general_ci"), + sqltypes.NewInt32(4)), + } + rows2 := [][]sqltypes.Value{ + append(buildVarCharRow( + "utf8", + "UTF-8 Unicode", + "utf8_general_ci"), + sqltypes.NewInt32(3)), + append(buildVarCharRow( + "utf8mb4", + "UTF-8 Unicode", + "utf8mb4_general_ci"), + sqltypes.NewInt32(4)), + } + + testcases := []struct { + input string + expected [][]sqltypes.Value + }{ + {input: "show charset", expected: rows2}, + {input: "show character set", expected: rows2}, + {input: "show charset where charset like 'foo%'", expected: rows}, + {input: "show charset where charset like 'utf8%'", expected: rows0}, + {input: "show charset where charset = 'utf8'", expected: rows0}, + {input: "show charset where charset = 'foo%'", expected: rows}, + {input: "show charset where charset = 'utf8mb4'", expected: rows1}, + } + + charsets := []string{"utf8", "utf8mb4"} + + for _, tc := range testcases { + t.Run(tc.input, func(t *testing.T) { + stmt, err := sqlparser.Parse(tc.input) + require.NoError(t, err) + match := stmt.(*sqlparser.Show) + filter := match.ShowTablesOpt.Filter + actual, err := generateCharsetRows(filter, charsets) + require.Equal(t, tc.expected, actual) + }) + } +} + func makeComments(text string) sqlparser.MarginComments { return sqlparser.MarginComments{Trailing: text} } From 50403f55bd4a3aca7175cbe186e5399e8030915f Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 6 Feb 2020 08:07:35 -0800 Subject: [PATCH 047/825] stats: stop using CountersFuncWithMultiLabels Signed-off-by: Sugu Sougoumarane --- .../tabletmanager/vreplication/stats.go | 4 +- go/vt/vttablet/tabletserver/query_engine.go | 105 +++--------------- 2 files changed, 16 insertions(+), 93 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/stats.go b/go/vt/vttablet/tabletmanager/vreplication/stats.go index be66fae9997..ea688fa4774 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/stats.go +++ b/go/vt/vttablet/tabletmanager/vreplication/stats.go @@ -58,11 +58,9 @@ type vrStats struct { func (st *vrStats) register() { stats.NewGaugeFunc("VReplicationStreamCount", "Number of vreplication streams", st.numControllers) stats.NewGaugeFunc("VReplicationSecondsBehindMasterMax", "Max vreplication seconds behind master", st.maxSecondsBehindMaster) - stats.NewCountersFuncWithMultiLabels( + stats.NewGaugesFuncWithMultiLabels( "VReplicationSecondsBehindMaster", "vreplication seconds behind master per stream", - // CAUTION: Always keep this label as "counts" because the Google - // internal monitoring depends on this specific value. []string{"counts"}, func() map[string]int64 { st.mu.Lock() diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index b182b7ba12a..be7e121ec0c 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -107,6 +107,12 @@ func (ep *TabletPlan) buildAuthorized() { } } +var ( + // Global stats vars. + // TODO(sougou): unglobalize after componentizing TabletServer. + queryCounts, queryTimes, queryRowCounts, queryErrorCounts *stats.CountersWithMultiLabels +) + //_______________________________________________ // QueryEngine implements the core functionality of tabletserver. @@ -126,9 +132,6 @@ type QueryEngine struct { plans *cache.LRUCache queryRuleSources *rules.Map - queryStatsMu sync.RWMutex - queryStats map[string]*QueryStats - // Pools conns *connpool.Pool streamConns *connpool.Pool @@ -186,7 +189,6 @@ func NewQueryEngine(checker connpool.MySQLChecker, se *schema.Engine, config tab plans: cache.NewLRUCache(int64(config.QueryPlanCacheSize)), queryRuleSources: rules.NewMap(), queryPoolWaiterCap: sync2.NewAtomicInt64(int64(config.QueryPoolWaiterCap)), - queryStats: make(map[string]*QueryStats), } qe.conns = connpool.New( @@ -259,10 +261,10 @@ func NewQueryEngine(checker connpool.MySQLChecker, se *schema.Engine, config tab stats.Publish("QueryCacheOldest", stats.StringFunc(func() string { return fmt.Sprintf("%v", qe.plans.Oldest()) })) - _ = stats.NewCountersFuncWithMultiLabels("QueryCounts", "query counts", []string{"Table", "Plan"}, qe.getQueryCount) - _ = stats.NewCountersFuncWithMultiLabels("QueryTimesNs", "query times in ns", []string{"Table", "Plan"}, qe.getQueryTime) - _ = stats.NewCountersFuncWithMultiLabels("QueryRowCounts", "query row counts", []string{"Table", "Plan"}, qe.getQueryRowCount) - _ = stats.NewCountersFuncWithMultiLabels("QueryErrorCounts", "query error counts", []string{"Table", "Plan"}, qe.getQueryErrorCount) + queryCounts = stats.NewCountersWithMultiLabels("QueryCounts", "query counts", []string{"Table", "Plan"}) + queryTimes = stats.NewCountersWithMultiLabels("QueryTimesNs", "query times in ns", []string{"Table", "Plan"}) + queryRowCounts = stats.NewCountersWithMultiLabels("QueryRowCounts", "query row counts", []string{"Table", "Plan"}) + queryErrorCounts = stats.NewCountersWithMultiLabels("QueryErrorCounts", "query error counts", []string{"Table", "Plan"}) http.Handle("/debug/hotrows", qe.txSerializer) @@ -483,91 +485,14 @@ func (qe *QueryEngine) QueryPlanCacheCap() int { return int(qe.plans.Capacity()) } -// QueryStats tracks query stats for export per planName/tableName -type QueryStats struct { - mu sync.Mutex - queryCount int64 - time time.Duration - mysqlTime time.Duration - rowCount int64 - errorCount int64 -} - // AddStats adds the given stats for the planName.tableName func (qe *QueryEngine) AddStats(planName, tableName string, queryCount int64, duration, mysqlTime time.Duration, rowCount, errorCount int64) { // table names can contain "." characters, replace them! - key := strings.Replace(tableName, ".", "_", -1) + "." + planName - - qe.queryStatsMu.RLock() - stats, ok := qe.queryStats[key] - qe.queryStatsMu.RUnlock() - - if !ok { - // Check again with the write lock held and - // create a new record only if none exists - qe.queryStatsMu.Lock() - if stats, ok = qe.queryStats[key]; !ok { - stats = &QueryStats{} - qe.queryStats[key] = stats - } - qe.queryStatsMu.Unlock() - } - - stats.mu.Lock() - stats.queryCount += queryCount - stats.time += duration - stats.mysqlTime += mysqlTime - stats.rowCount += rowCount - stats.errorCount += errorCount - stats.mu.Unlock() -} - -func (qe *QueryEngine) getQueryCount() map[string]int64 { - qstats := make(map[string]int64) - qe.queryStatsMu.RLock() - defer qe.queryStatsMu.RUnlock() - for k, qs := range qe.queryStats { - qs.mu.Lock() - qstats[k] = qs.queryCount - qs.mu.Unlock() - } - return qstats -} - -func (qe *QueryEngine) getQueryTime() map[string]int64 { - qstats := make(map[string]int64) - qe.queryStatsMu.RLock() - defer qe.queryStatsMu.RUnlock() - for k, qs := range qe.queryStats { - qs.mu.Lock() - qstats[k] = int64(qs.time) - qs.mu.Unlock() - } - return qstats -} - -func (qe *QueryEngine) getQueryRowCount() map[string]int64 { - qstats := make(map[string]int64) - qe.queryStatsMu.RLock() - defer qe.queryStatsMu.RUnlock() - for k, qs := range qe.queryStats { - qs.mu.Lock() - qstats[k] = qs.rowCount - qs.mu.Unlock() - } - return qstats -} - -func (qe *QueryEngine) getQueryErrorCount() map[string]int64 { - qstats := make(map[string]int64) - qe.queryStatsMu.RLock() - defer qe.queryStatsMu.RUnlock() - for k, qs := range qe.queryStats { - qs.mu.Lock() - qstats[k] = qs.errorCount - qs.mu.Unlock() - } - return qstats + keys := []string{strings.Replace(tableName, ".", "_", -1), planName} + queryCounts.Add(keys, queryCount) + queryTimes.Add(keys, int64(duration)) + queryRowCounts.Add(keys, rowCount) + queryErrorCounts.Add(keys, errorCount) } type perQueryStats struct { From ad277d9c75dd1fb3baf1e3148337679b74ba085b Mon Sep 17 00:00:00 2001 From: deepthi Date: Thu, 6 Feb 2020 09:10:12 -0800 Subject: [PATCH 048/825] remove ToLower from expected value, since the test uses camel case for db name Signed-off-by: deepthi --- .../tabletmanager/dbnameoverride/tablet_master_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go index 1f88c12e0b0..6441a161e72 100644 --- a/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go +++ b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go @@ -19,7 +19,6 @@ import ( "context" "flag" "os" - "strings" "testing" "github.com/stretchr/testify/require" @@ -116,5 +115,5 @@ func TestDbNameOverride(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(qr.Rows), "did not get enough rows back") - require.Equal(t, strings.ToLower(dbName), qr.Rows[0][0].ToString()) + require.Equal(t, dbName, qr.Rows[0][0].ToString()) } From c0c927a0a9eb3ce3df98de595e6f61946017ece6 Mon Sep 17 00:00:00 2001 From: Saif Al-Harthi Date: Thu, 6 Feb 2020 13:30:09 -0800 Subject: [PATCH 049/825] Support SHOW CREATE TABLE with qualifier (#5585) * Support SHOW CREATE TABLE with qualifer Signed-off-by: Saif Alharthi --- go.mod | 2 -- go/vt/vtgate/executor.go | 28 +++++++++++----------------- go/vt/vtgate/executor_test.go | 13 +++++++++++-- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 25077ce841a..5310d66e0b1 100644 --- a/go.mod +++ b/go.mod @@ -52,8 +52,6 @@ require ( github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 45e126c6f2f..e2ac0b2dca8 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -785,7 +785,6 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql } execStart := time.Now() defer func() { logStats.ExecuteTime = time.Since(execStart) }() - switch strings.ToLower(show.Type) { case sqlparser.KeywordString(sqlparser.COLLATION), sqlparser.KeywordString(sqlparser.VARIABLES): if show.Scope == sqlparser.VitessMetadataStr { @@ -860,24 +859,19 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql RowsAffected: rowsAffected, }, err case "create table": - if destKeyspace == "" && show.HasTable() { - // For "show create table", if there isn't a targeted keyspace already - // we can either get a keyspace from the statement or potentially from - // the vschema. - - if !show.Table.Qualifier.IsEmpty() { - // Explicit keyspace was passed. Use that for targeting but remove from the query itself. - destKeyspace = show.Table.Qualifier.String() - show.Table.Qualifier = sqlparser.NewTableIdent("") - sql = sqlparser.String(show) - } else { - // No keyspace was indicated. Try to find one using the vschema. - tbl, err := e.VSchema().FindTable("", show.Table.Name.String()) - if err == nil { - destKeyspace = tbl.Keyspace.Name - } + if !show.Table.Qualifier.IsEmpty() { + // Explicit keyspace was passed. Use that for targeting but remove from the query itself. + destKeyspace = show.Table.Qualifier.String() + show.Table.Qualifier = sqlparser.NewTableIdent("") + } else { + // No keyspace was indicated. Try to find one using the vschema. + tbl, err := e.VSchema().FindTable(destKeyspace, show.Table.Name.String()) + if err != nil { + return nil, err } + destKeyspace = tbl.Keyspace.Name } + sql = sqlparser.String(show) case sqlparser.KeywordString(sqlparser.COLUMNS): if !show.OnTable.Qualifier.IsEmpty() { destKeyspace = show.OnTable.Qualifier.String() diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 115bf89da7a..e6da29eba0d 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -802,9 +802,10 @@ func TestExecutorShow(t *testing.T) { t.Errorf("%v:\n%+v, want\n%+v", query, qr, wantqr) } + wantErrNoTable := "table unknown_table not found" _, err = executor.Execute(context.Background(), "TestExecute", session, "show create table unknown_table", nil) - if err != errNoKeyspace { - t.Errorf("Got: %v. Want: %v", err, errNoKeyspace) + if err.Error() != wantErrNoTable { + t.Errorf("Got: %v. Want: %v", err, wantErrNoTable) } // SHOW CREATE table using vschema to find keyspace. @@ -830,6 +831,14 @@ func TestExecutorShow(t *testing.T) { t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) } + // Set desitation keyspace in session + session.TargetString = KsTestUnsharded + _, err = executor.Execute(context.Background(), "TestExecute", session, "show create table unknown", nil) + require.NoError(t, err) + // Reset target string so other tests dont fail. + session.TargetString = "@master" + _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show full columns from unknown from %v", KsTestUnsharded), nil) + require.NoError(t, err) for _, query := range []string{"show charset", "show character set"} { qr, err := executor.Execute(context.Background(), "TestExecute", session, query, nil) require.NoError(t, err) From 535ec7227dd570755b25d243217605de5a304cd5 Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Thu, 6 Feb 2020 15:53:58 -0800 Subject: [PATCH 050/825] First pass in executor tests Signed-off-by: Saif Alharthi --- go.mod | 2 - go/vt/vtgate/executor_scatter_stats_test.go | 26 +- go/vt/vtgate/executor_select_test.go | 265 +++++--------------- go/vt/vtgate/executor_test.go | 188 ++++---------- go/vt/vtgate/mysql_protocol_test.go | 16 +- go/vt/vtgate/queryz_test.go | 17 +- go/vt/vtgate/resolver_test.go | 9 +- go/vt/vtgate/scatter_conn_test.go | 21 +- go/vt/vtgate/tx_conn_test.go | 13 +- go/vt/vtgate/vtgate_test.go | 53 ++-- 10 files changed, 163 insertions(+), 447 deletions(-) diff --git a/go.mod b/go.mod index 25077ce841a..5310d66e0b1 100644 --- a/go.mod +++ b/go.mod @@ -52,8 +52,6 @@ require ( github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/go/vt/vtgate/executor_scatter_stats_test.go b/go/vt/vtgate/executor_scatter_stats_test.go index 333a06be91f..9183df9a130 100644 --- a/go/vt/vtgate/executor_scatter_stats_test.go +++ b/go/vt/vtgate/executor_scatter_stats_test.go @@ -22,7 +22,7 @@ import ( "net/http/httptest" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" ) @@ -32,11 +32,11 @@ func TestScatterStatsWithNoScatterQuery(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{TargetString: "@master"}) _, err := executor.Execute(context.Background(), "TestExecutorResultsExceeded", session, "select * from main1", nil) - assert.NoError(t, err) + require.NoError(t, err) result, err := executor.gatherScatterStats() - assert.NoError(t, err) - assert.Equal(t, 0, len(result.Items)) + require.NoError(t, err) + require.Equal(t, 0, len(result.Items)) } func TestScatterStatsWithSingleScatterQuery(t *testing.T) { @@ -44,11 +44,11 @@ func TestScatterStatsWithSingleScatterQuery(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{TargetString: "@master"}) _, err := executor.Execute(context.Background(), "TestExecutorResultsExceeded", session, "select * from user", nil) - assert.NoError(t, err) + require.NoError(t, err) result, err := executor.gatherScatterStats() - assert.NoError(t, err) - assert.Equal(t, 1, len(result.Items)) + require.NoError(t, err) + require.Equal(t, 1, len(result.Items)) } func TestScatterStatsHttpWriting(t *testing.T) { @@ -56,23 +56,23 @@ func TestScatterStatsHttpWriting(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{TargetString: "@master"}) _, err := executor.Execute(context.Background(), "TestExecutorResultsExceeded", session, "select * from user", nil) - assert.NoError(t, err) + require.NoError(t, err) _, err = executor.Execute(context.Background(), "TestExecutorResultsExceeded", session, "select * from user where Id = 15", nil) - assert.NoError(t, err) + require.NoError(t, err) _, err = executor.Execute(context.Background(), "TestExecutorResultsExceeded", session, "select * from user where Id > 15", nil) - assert.NoError(t, err) + require.NoError(t, err) query4 := "select * from user as u1 join user as u2 on u1.Id = u2.Id" _, err = executor.Execute(context.Background(), "TestExecutorResultsExceeded", session, query4, nil) - assert.NoError(t, err) + require.NoError(t, err) recorder := httptest.NewRecorder() executor.WriteScatterStats(recorder) // Here we are checking that the template was executed correctly. // If it wasn't, instead of html, we'll get an error message - assert.Contains(t, recorder.Body.String(), query4) - assert.NoError(t, err) + require.Contains(t, recorder.Body.String(), query4) + require.NoError(t, err) } diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index a94f0b5c6d5..14363bb6449 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/vt/vterrors" @@ -44,9 +45,7 @@ func TestSelectNext(t *testing.T) { query := "select next :n values from user_seq" bv := map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(2)} _, err := executorExec(executor, query, bv) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: query, BindVariables: map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(2)}, @@ -67,9 +66,7 @@ func TestSelectDBA(t *testing.T) { query, map[string]*querypb.BindVariable{}, ) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: query, BindVariables: map[string]*querypb.BindVariable{}, @@ -83,9 +80,7 @@ func TestUnsharded(t *testing.T) { executor, _, _, sbclookup := createExecutorEnv() _, err := executorExec(executor, "select id from music_user_map where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id from music_user_map where id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -99,9 +94,7 @@ func TestUnshardedComments(t *testing.T) { executor, _, _, sbclookup := createExecutorEnv() _, err := executorExec(executor, "/* leading */ select id from music_user_map where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "/* leading */ select id from music_user_map where id = 1 /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -111,9 +104,7 @@ func TestUnshardedComments(t *testing.T) { } _, err = executorExec(executor, "update music_user_map set id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "/* leading */ select id from music_user_map where id = 1 /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -127,9 +118,7 @@ func TestUnshardedComments(t *testing.T) { sbclookup.Queries = nil _, err = executorExec(executor, "delete from music_user_map /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "delete from music_user_map /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -140,9 +129,7 @@ func TestUnshardedComments(t *testing.T) { sbclookup.Queries = nil _, err = executorExec(executor, "insert into music_user_map values (1) /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "insert into music_user_map values (1) /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -159,9 +146,7 @@ func TestStreamUnsharded(t *testing.T) { sql := "select id from music_user_map where id = 1" result, err := executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := sandboxconn.StreamRowResult if !result.Equal(wantResult) { t.Errorf("result: %+v, want %+v", result, wantResult) @@ -204,9 +189,7 @@ func TestStreamBuffering(t *testing.T) { }, ) close(results) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResults := []*sqltypes.Result{{ Fields: []*querypb.Field{ {Name: "id", Type: sqltypes.Int32}, @@ -243,9 +226,7 @@ func TestSelectLastInsertId(t *testing.T) { sql := "select last_insert_id()" _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select :__lastInsertId as `last_insert_id()` from dual", BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, @@ -259,9 +240,7 @@ func TestSelectLastInsertIdInUnion(t *testing.T) { executor.normalize = true sql := "select last_insert_id() as id union select id from user" _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select :__lastInsertId as id from dual union select id from user", BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, @@ -278,9 +257,7 @@ func TestSelectLastInsertIdInWhere(t *testing.T) { sql := "select id from music_user_map where id = last_insert_id()" _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id from music_user_map where id = :__lastInsertId", BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, @@ -306,9 +283,7 @@ func TestLastInsertIDInVirtualTable(t *testing.T) { }} sbc1.SetResults(result1) _, err := executorExec(executor, "select * from (select last_insert_id()) as t", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select * from (select :__lastInsertId as `last_insert_id()` from dual) as t", BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, @@ -334,9 +309,7 @@ func TestLastInsertIDInSubQueryExpression(t *testing.T) { }} sbc1.SetResults(result1) _, err := executorExec(executor, "select (select last_insert_id()) as x", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select (select :__lastInsertId as `last_insert_id()` from dual) as x from dual", BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, @@ -359,9 +332,7 @@ func TestSelectDatabase(t *testing.T) { sql, map[string]*querypb.BindVariable{}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select :__vtdbname as `database()` from dual", BindVariables: map[string]*querypb.BindVariable{"__vtdbname": sqltypes.StringBindVariable("TestExecutor")}, @@ -379,9 +350,7 @@ func TestSelectBindvars(t *testing.T) { _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{ "id": sqltypes.Int64BindVariable(1), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id from user where id = :id", BindVariables: map[string]*querypb.BindVariable{"id": sqltypes.Int64BindVariable(1)}, @@ -401,9 +370,7 @@ func TestSelectBindvars(t *testing.T) { "name1": sqltypes.StringBindVariable("foo1"), "name2": sqltypes.StringBindVariable("foo2"), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where name in ::__vals", BindVariables: map[string]*querypb.BindVariable{ @@ -426,9 +393,7 @@ func TestSelectBindvars(t *testing.T) { "name1": sqltypes.BytesBindVariable([]byte("foo1")), "name2": sqltypes.BytesBindVariable([]byte("foo2")), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where name in ::__vals", BindVariables: map[string]*querypb.BindVariable{ @@ -461,9 +426,7 @@ func TestSelectBindvars(t *testing.T) { _, err = executorExec(executor, sql, map[string]*querypb.BindVariable{ "name": sqltypes.StringBindVariable("nonexistent"), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // When there are no matching rows in the vindex, vtgate still needs the field info wantQueries = []*querypb.BoundQuery{{ @@ -495,9 +458,7 @@ func TestSelectEqual(t *testing.T) { executor, sbc1, sbc2, sbclookup := createExecutorEnv() _, err := executorExec(executor, "select id from user where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id from user where id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -511,9 +472,7 @@ func TestSelectEqual(t *testing.T) { sbc1.Queries = nil _, err = executorExec(executor, "select id from user where id = 3", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where id = 3", BindVariables: map[string]*querypb.BindVariable{}, @@ -530,9 +489,7 @@ func TestSelectEqual(t *testing.T) { sbc2.Queries = nil _, err = executorExec(executor, "select id from user where id = '3'", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where id = '3'", BindVariables: map[string]*querypb.BindVariable{}, @@ -549,9 +506,7 @@ func TestSelectEqual(t *testing.T) { sbc2.Queries = nil _, err = executorExec(executor, "select id from user where name = 'foo'", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where name = 'foo'", BindVariables: map[string]*querypb.BindVariable{}, @@ -574,9 +529,7 @@ func TestSelectDual(t *testing.T) { executor, sbc1, _, lookup := createExecutorEnv() _, err := executorExec(executor, "select @@aa.bb from dual", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select @@aa.bb from dual", BindVariables: map[string]*querypb.BindVariable{}, @@ -586,9 +539,7 @@ func TestSelectDual(t *testing.T) { } _, err = executorExec(executor, "select @@aa.bb from TestUnsharded.dual", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(lookup.Queries, wantQueries) { t.Errorf("sbc1.Queries: %+v, want %+v\n", sbc1.Queries, wantQueries) } @@ -598,9 +549,7 @@ func TestSelectComments(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() _, err := executorExec(executor, "/* leading */ select id from user where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "/* leading */ select id from user where id = 1 /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -619,9 +568,7 @@ func TestSelectNormalize(t *testing.T) { executor.normalize = true _, err := executorExec(executor, "/* leading */ select id from user where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "/* leading */ select id from user where id = :vtg1 /* trailing */", BindVariables: map[string]*querypb.BindVariable{ @@ -639,9 +586,7 @@ func TestSelectNormalize(t *testing.T) { // Force the query to go to the "wrong" shard and ensure that normalization still happens masterSession.TargetString = "TestExecutor/40-60" _, err = executorExec(executor, "/* leading */ select id from user where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "/* leading */ select id from user where id = :vtg1 /* trailing */", BindVariables: map[string]*querypb.BindVariable{ @@ -662,9 +607,7 @@ func TestSelectCaseSensitivity(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() _, err := executorExec(executor, "select Id from user where iD = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select Id from user where iD = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -683,9 +626,7 @@ func TestStreamSelectEqual(t *testing.T) { sql := "select id from user where id = 1" result, err := executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := sandboxconn.StreamRowResult if !result.Equal(wantResult) { t.Errorf("result: %+v, want %+v", result, wantResult) @@ -696,9 +637,7 @@ func TestSelectKeyRange(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() _, err := executorExec(executor, "select krcol_unique, krcol from keyrange_table where krcol = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select krcol_unique, krcol from keyrange_table where krcol = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -716,9 +655,7 @@ func TestSelectKeyRangeUnique(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() _, err := executorExec(executor, "select krcol_unique, krcol from keyrange_table where krcol_unique = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select krcol_unique, krcol from keyrange_table where krcol_unique = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -737,9 +674,7 @@ func TestSelectIN(t *testing.T) { // Constant in IN clause is just a number, not a bind variable. _, err := executorExec(executor, "select id from user where id in (1)", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id from user where id in ::__vals", BindVariables: map[string]*querypb.BindVariable{ @@ -758,9 +693,7 @@ func TestSelectIN(t *testing.T) { sbc1.Queries = nil sbc2.Queries = nil _, err = executorExec(executor, "select id from user where id in (1, 3)", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where id in ::__vals", BindVariables: map[string]*querypb.BindVariable{ @@ -787,9 +720,7 @@ func TestSelectIN(t *testing.T) { _, err = executorExec(executor, "select id from user where id in ::vals", map[string]*querypb.BindVariable{ "vals": sqltypes.TestBindVariable([]interface{}{int64(1), int64(3)}), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where id in ::__vals", BindVariables: map[string]*querypb.BindVariable{ @@ -815,9 +746,7 @@ func TestSelectIN(t *testing.T) { sbc1.Queries = nil sbc2.Queries = nil _, err = executorExec(executor, "select id from user where name = 'foo'", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select id from user where name = 'foo'", BindVariables: map[string]*querypb.BindVariable{}, @@ -841,9 +770,7 @@ func TestStreamSelectIN(t *testing.T) { sql := "select id from user where id in (1)" result, err := executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := sandboxconn.StreamRowResult if !result.Equal(wantResult) { t.Errorf("result: %+v, want %+v", result, wantResult) @@ -851,9 +778,7 @@ func TestStreamSelectIN(t *testing.T) { sql = "select id from user where id in (1, 3)" result, err = executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult = &sqltypes.Result{ Fields: sandboxconn.StreamRowResult.Fields, Rows: [][]sqltypes.Value{ @@ -868,9 +793,7 @@ func TestStreamSelectIN(t *testing.T) { sql = "select id from user where name = 'foo'" result, err = executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult = sandboxconn.StreamRowResult if !result.Equal(wantResult) { t.Errorf("result: %+v, want %+v", result, wantResult) @@ -907,9 +830,7 @@ func TestSelectScatter(t *testing.T) { defer QueryLogger.Unsubscribe(logChan) _, err := executorExec(executor, "select id from user", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id from user", BindVariables: map[string]*querypb.BindVariable{}, @@ -959,9 +880,7 @@ func TestSelectScatterPartial(t *testing.T) { // Fail 1 of N with the directive succeeds with 7 rows results, err = executorExec(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if results == nil || len(results.Rows) != 7 { t.Errorf("want 7 results, got %v", results) } @@ -977,9 +896,7 @@ func TestSelectScatterPartial(t *testing.T) { conns[7].MustFailCodes[vtrpcpb.Code_RESOURCE_EXHAUSTED] = 1000 results, err = executorExec(executor, "select /*vt+ SCATTER_ERRORS_AS_WARNINGS=1 */ id from user", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if results == nil || len(results.Rows) != 0 { t.Errorf("want 0 result rows, got %v", results) } @@ -1003,9 +920,7 @@ func TestStreamSelectScatter(t *testing.T) { sql := "select id from user" result, err := executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := &sqltypes.Result{ Fields: sandboxconn.SingleRowResult.Fields, Rows: [][]sqltypes.Value{ @@ -1059,9 +974,7 @@ func TestSelectScatterOrderBy(t *testing.T) { query := "select col1, col2 from user order by col2 desc" gotResult, err := executorExec(executor, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: query, @@ -1132,9 +1045,7 @@ func TestSelectScatterOrderByVarChar(t *testing.T) { query := "select col1, textcol from user order by textcol desc" gotResult, err := executorExec(executor, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select col1, textcol, weight_string(textcol) from user order by textcol desc", @@ -1200,9 +1111,7 @@ func TestStreamSelectScatterOrderBy(t *testing.T) { query := "select id, col from user order by col desc" gotResult, err := executorStream(executor, query) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: query, @@ -1264,9 +1173,7 @@ func TestStreamSelectScatterOrderByVarChar(t *testing.T) { query := "select id, textcol from user order by textcol desc" gotResult, err := executorStream(executor, query) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id, textcol, weight_string(textcol) from user order by textcol desc", @@ -1328,9 +1235,7 @@ func TestSelectScatterAggregate(t *testing.T) { query := "select col, sum(foo) from user group by col" gotResult, err := executorExec(executor, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: query + " order by col asc", @@ -1393,9 +1298,7 @@ func TestStreamSelectScatterAggregate(t *testing.T) { query := "select col, sum(foo) from user group by col" gotResult, err := executorStream(executor, query) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: query + " order by col asc", @@ -1458,9 +1361,7 @@ func TestSelectScatterLimit(t *testing.T) { query := "select col1, col2 from user order by col2 desc limit 3" gotResult, err := executorExec(executor, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select col1, col2 from user order by col2 desc limit :__upper_limit", @@ -1532,9 +1433,7 @@ func TestStreamSelectScatterLimit(t *testing.T) { query := "select col1, col2 from user order by col2 desc limit 3" gotResult, err := executorStream(executor, query) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select col1, col2 from user order by col2 desc limit :__upper_limit", @@ -1580,9 +1479,7 @@ func TestSimpleJoin(t *testing.T) { sql := "select u1.id, u2.id from user u1 join user u2 where u1.id = 1 and u2.id = 3" result, err := executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -1624,9 +1521,7 @@ func TestJoinComments(t *testing.T) { sql := "select u1.id, u2.id from user u1 join user u2 where u1.id = 1 and u2.id = 3 /* trailing */" _, err := executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id from user as u1 where u1.id = 1 /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -1652,9 +1547,7 @@ func TestSimpleJoinStream(t *testing.T) { sql := "select u1.id, u2.id from user u1 join user u2 where u1.id = 1 and u2.id = 3" result, err := executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -1709,9 +1602,7 @@ func TestVarJoin(t *testing.T) { sbc1.SetResults(result1) sql := "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1" _, err := executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id, u1.col from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -1749,9 +1640,7 @@ func TestVarJoinStream(t *testing.T) { sbc1.SetResults(result1) sql := "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1" _, err := executorStream(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id, u1.col from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -1794,9 +1683,7 @@ func TestLeftJoin(t *testing.T) { sbc2.SetResults(emptyResult) sql := "select u1.id, u2.id from user u1 left join user u2 on u2.id = u1.col where u1.id = 1" result, err := executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ sandboxconn.SingleRowResult.Fields[0], @@ -1840,9 +1727,7 @@ func TestLeftJoinStream(t *testing.T) { sbc1.SetResults(result1) sbc2.SetResults(emptyResult) result, err := executorStream(executor, "select u1.id, u2.id from user u1 left join user u2 on u2.id = u1.col where u1.id = 1") - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := &sqltypes.Result{ Fields: []*querypb.Field{ sandboxconn.SingleRowResult.Fields[0], @@ -1875,9 +1760,7 @@ func TestEmptyJoin(t *testing.T) { }, }}) result, err := executorExec(executor, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id, u1.col from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -1915,9 +1798,7 @@ func TestEmptyJoinStream(t *testing.T) { }, }}) result, err := executorStream(executor, "select u1.id, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1") - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id, u1.col from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -1959,9 +1840,7 @@ func TestEmptyJoinRecursive(t *testing.T) { }, }}) result, err := executorExec(executor, "select u1.id, u2.id, u3.id from user u1 join (user u2 join user u3 on u3.id = u2.col) where u1.id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -2007,9 +1886,7 @@ func TestEmptyJoinRecursiveStream(t *testing.T) { }, }}) result, err := executorStream(executor, "select u1.id, u2.id, u3.id from user u1 join (user u2 join user u3 on u3.id = u2.col) where u1.id = 1") - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -2053,9 +1930,7 @@ func TestCrossShardSubquery(t *testing.T) { }} sbc1.SetResults(result1) result, err := executorExec(executor, "select id1 from (select u1.id id1, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1) as t", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id as id1, u1.col from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -2100,9 +1975,7 @@ func TestCrossShardSubqueryStream(t *testing.T) { }} sbc1.SetResults(result1) result, err := executorStream(executor, "select id1 from (select u1.id id1, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1) as t") - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id as id1, u1.col from user as u1 where u1.id = 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -2145,9 +2018,7 @@ func TestCrossShardSubqueryGetFields(t *testing.T) { }} sbc1.SetResults(result1) result, err := executorExec(executor, "select main1.col, t.id1 from main1 join (select u1.id id1, u2.id from user u1 join user u2 on u2.id = u1.col where u1.id = 1) as t", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select u1.id as id1, u1.col from user as u1 where 1 != 1", BindVariables: map[string]*querypb.BindVariable{}, @@ -2181,9 +2052,7 @@ func TestSelectBindvarswithPrepare(t *testing.T) { _, err := executorPrepare(executor, sql, map[string]*querypb.BindVariable{ "id": sqltypes.Int64BindVariable(1), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select id from user where 1 != 1", diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 115bf89da7a..853ab15605f 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -549,9 +549,7 @@ func TestExecutorSetMetadata(t *testing.T) { show = "show vitess_metadata variables" gotqr, err = executor.Execute(context.Background(), "TestExecute", session, show, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) assert.Equal(t, wantqr.Fields, gotqr.Fields) assert.ElementsMatch(t, wantqr.Rows, gotqr.Rows) @@ -748,17 +746,11 @@ func TestExecutorShow(t *testing.T) { } } _, err := executor.Execute(context.Background(), "TestExecute", session, "show variables", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = executor.Execute(context.Background(), "TestExecute", session, "show collation", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = executor.Execute(context.Background(), "TestExecute", session, "show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = executor.Execute(context.Background(), "TestExecute", session, "show tables", nil) if err != errNoKeyspace { @@ -783,9 +775,7 @@ func TestExecutorShow(t *testing.T) { query := fmt.Sprintf("show tables from %v", KsTestUnsharded) qr, err := executor.Execute(context.Background(), "TestExecute", session, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(sbclookup.Queries) != 1 { t.Errorf("Tablet should have received one 'show' query. Instead received: %v", sbclookup.Queries) @@ -901,9 +891,7 @@ func TestExecutorShow(t *testing.T) { } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show engines", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: buildVarCharFields("Engine", "Support", "Comment", "Transactions", "XA", "Savepoints"), Rows: [][]sqltypes.Value{ @@ -921,9 +909,7 @@ func TestExecutorShow(t *testing.T) { t.Errorf("show engines:\n%+v, want\n%+v", qr, wantqr) } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show plugins", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: buildVarCharFields("Name", "Status", "Type", "Library", "License"), Rows: [][]sqltypes.Value{ @@ -954,9 +940,7 @@ func TestExecutorShow(t *testing.T) { } } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vitess_shards", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Test SHOW FULL COLUMNS FROM where query has a qualifier _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show full columns from %v.table1", KsTestUnsharded), nil) @@ -979,9 +963,7 @@ func TestExecutorShow(t *testing.T) { } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vitess_tablets", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Just test for first & last. qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]} wantqr = &sqltypes.Result{ @@ -997,9 +979,7 @@ func TestExecutorShow(t *testing.T) { } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: buildVarCharFields("Keyspace", "Name", "Type", "Params", "Owner"), Rows: [][]sqltypes.Value{ @@ -1021,9 +1001,7 @@ func TestExecutorShow(t *testing.T) { } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on TestExecutor.user", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"), Rows: [][]sqltypes.Value{ @@ -1050,9 +1028,7 @@ func TestExecutorShow(t *testing.T) { session.TargetString = "TestExecutor" qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on user", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"), Rows: [][]sqltypes.Value{ @@ -1067,9 +1043,7 @@ func TestExecutorShow(t *testing.T) { session.TargetString = "TestExecutor" qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema vindexes on user2", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"), Rows: [][]sqltypes.Value{ @@ -1089,9 +1063,7 @@ func TestExecutorShow(t *testing.T) { } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show warnings", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: []*querypb.Field{ {Name: "Level", Type: sqltypes.VarChar}, @@ -1108,9 +1080,7 @@ func TestExecutorShow(t *testing.T) { session.Warnings = []*querypb.QueryWarning{} qr, err = executor.Execute(context.Background(), "TestExecute", session, "show warnings", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: []*querypb.Field{ {Name: "Level", Type: sqltypes.VarChar}, @@ -1129,9 +1099,7 @@ func TestExecutorShow(t *testing.T) { {Code: mysql.EROutOfResources, Message: "ks/-40: query timed out"}, } qr, err = executor.Execute(context.Background(), "TestExecute", session, "show warnings", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: []*querypb.Field{ {Name: "Level", Type: sqltypes.VarChar}, @@ -1152,9 +1120,7 @@ func TestExecutorShow(t *testing.T) { // Make sure it still works when one of the keyspaces is in a bad state getSandbox("TestExecutor").SrvKeyspaceMustFail++ qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vitess_shards", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Just test for first & last. qr.Rows = [][]sqltypes.Value{qr.Rows[0], qr.Rows[len(qr.Rows)-1]} wantqr = &sqltypes.Result{ @@ -1171,9 +1137,7 @@ func TestExecutorShow(t *testing.T) { session = NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}) qr, err = executor.Execute(context.Background(), "TestExecute", session, "show vschema tables", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqr = &sqltypes.Result{ Fields: buildVarCharFields("Tables"), Rows: [][]sqltypes.Value{ @@ -1507,9 +1471,7 @@ func TestExecutorAlterVSchemaKeyspace(t *testing.T) { stmt := "alter vschema create vindex TestExecutor.test_vindex using hash" _, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, vindex := waitForVindex(t, "TestExecutor", "test_vindex", vschemaUpdates, executor) assert.Equal(t, vindex.Type, "hash") @@ -1537,9 +1499,7 @@ func TestExecutorCreateVindexDDL(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema create vindex test_vindex using hash" _, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, vindex := waitForVindex(t, ks, "test_vindex", vschemaUpdates, executor) if vindex == nil || vindex.Type != "hash" { @@ -1615,16 +1575,12 @@ func TestExecutorAddDropVschemaTableDDL(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add table test_table" _, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _ = waitForVschemaTables(t, ks, append(vschemaTables, "test_table"), executor) stmt = "alter vschema add table test_table2" _, err = executor.Execute(context.Background(), "TestExecute", session, stmt, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _ = waitForVschemaTables(t, ks, append(vschemaTables, []string{"test_table", "test_table2"}...), executor) // Should fail adding a table on a sharded keyspace @@ -1666,9 +1622,7 @@ func TestExecutorAddSequenceDDL(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{TargetString: ks}) stmt := "alter vschema add sequence test_seq" _, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _ = waitForVschemaTables(t, ks, append(vschemaTables, []string{"test_seq"}...), executor) vschema = executor.vm.GetCurrentSrvVschema() table := vschema.Keyspaces[ks].Tables["test_seq"] @@ -2058,9 +2012,7 @@ func TestExecutorVindexDDLNewKeyspace(t *testing.T) { session := NewSafeSession(&vtgatepb.Session{TargetString: ksName}) stmt := "alter vschema create vindex test_hash using hash" _, err := executor.Execute(context.Background(), "TestExecute", session, stmt, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) time.Sleep(50 * time.Millisecond) @@ -2173,9 +2125,7 @@ func TestExecutorMessageAckSharded(t *testing.T) { Value: []byte("1"), }} count, err := executor.MessageAck(context.Background(), "", "user", ids) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 1 { t.Errorf("count: %d, want 1", count) } @@ -2198,9 +2148,7 @@ func TestExecutorMessageAckSharded(t *testing.T) { Value: []byte("3"), }} count, err = executor.MessageAck(context.Background(), "", "user", ids) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 2 { t.Errorf("count: %d, want 2", count) } @@ -2251,9 +2199,7 @@ func TestGetPlanUnnormalized(t *testing.T) { logStats1 := NewLogStats(context.Background(), "Test", "", nil) query1 := "select * from music_user_map where id = 1" plan1, err := r.getPlan(emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantSQL := query1 + " /* comment */" if logStats1.SQL != wantSQL { t.Errorf("logstats sql want \"%s\" got \"%s\"", wantSQL, logStats1.SQL) @@ -2261,9 +2207,7 @@ func TestGetPlanUnnormalized(t *testing.T) { logStats2 := NewLogStats(context.Background(), "Test", "", nil) plan2, err := r.getPlan(emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats2) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan1 != plan2 { t.Errorf("getPlan(query1): plans must be equal: %p %p", plan1, plan2) } @@ -2278,9 +2222,7 @@ func TestGetPlanUnnormalized(t *testing.T) { } logStats3 := NewLogStats(context.Background(), "Test", "", nil) plan3, err := r.getPlan(unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats3) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan1 == plan3 { t.Errorf("getPlan(query1, ks): plans must not be equal: %p %p", plan1, plan3) } @@ -2289,9 +2231,7 @@ func TestGetPlanUnnormalized(t *testing.T) { } logStats4 := NewLogStats(context.Background(), "Test", "", nil) plan4, err := r.getPlan(unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats4) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan3 != plan4 { t.Errorf("getPlan(query1, ks): plans must be equal: %p %p", plan3, plan4) } @@ -2313,9 +2253,7 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { query1 := "select * from music_user_map where id = 1" logStats1 := NewLogStats(context.Background(), "Test", "", nil) _, err := r.getPlan(emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true /* skipQueryPlanCache */, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if r.plans.Size() != 0 { t.Errorf("getPlan() expected cache to have size 0, but got: %b", r.plans.Size()) } @@ -2325,9 +2263,7 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { } logStats2 := NewLogStats(context.Background(), "Test", "", nil) _, err = r.getPlan(emptyvc, query1, makeComments(" /* comment 2 */"), map[string]*querypb.BindVariable{}, false /* skipQueryPlanCache */, logStats2) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if r.plans.Size() != 1 { t.Errorf("getPlan() expected cache to have size 1, but got: %b", r.plans.Size()) } @@ -2343,18 +2279,14 @@ func TestGetPlanCacheUnnormalized(t *testing.T) { query1 = "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" logStats1 = NewLogStats(context.Background(), "Test", "", nil) _, err = r.getPlan(unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(r.plans.Keys()) != 0 { t.Errorf("Plan keys should be 0, got: %v", len(r.plans.Keys())) } query1 = "insert into user(id) values (1), (2)" _, err = r.getPlan(unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(r.plans.Keys()) != 1 { t.Errorf("Plan keys should be 1, got: %v", len(r.plans.Keys())) } @@ -2367,9 +2299,7 @@ func TestGetPlanCacheNormalized(t *testing.T) { query1 := "select * from music_user_map where id = 1" logStats1 := NewLogStats(context.Background(), "Test", "", nil) _, err := r.getPlan(emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, true /* skipQueryPlanCache */, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if r.plans.Size() != 0 { t.Errorf("getPlan() expected cache to have size 0, but got: %b", r.plans.Size()) } @@ -2379,9 +2309,7 @@ func TestGetPlanCacheNormalized(t *testing.T) { } logStats2 := NewLogStats(context.Background(), "Test", "", nil) _, err = r.getPlan(emptyvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false /* skipQueryPlanCache */, logStats2) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if r.plans.Size() != 1 { t.Errorf("getPlan() expected cache to have size 1, but got: %b", r.plans.Size()) } @@ -2397,18 +2325,14 @@ func TestGetPlanCacheNormalized(t *testing.T) { query1 = "insert /*vt+ SKIP_QUERY_PLAN_CACHE=1 */ into user(id) values (1), (2)" logStats1 = NewLogStats(context.Background(), "Test", "", nil) _, err = r.getPlan(unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(r.plans.Keys()) != 0 { t.Errorf("Plan keys should be 0, got: %v", len(r.plans.Keys())) } query1 = "insert into user(id) values (1), (2)" _, err = r.getPlan(unshardedvc, query1, makeComments(" /* comment */"), map[string]*querypb.BindVariable{}, false, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(r.plans.Keys()) != 1 { t.Errorf("Plan keys should be 1, got: %v", len(r.plans.Keys())) } @@ -2425,14 +2349,10 @@ func TestGetPlanNormalized(t *testing.T) { normalized := "select * from music_user_map where id = :vtg1" logStats1 := NewLogStats(context.Background(), "Test", "", nil) plan1, err := r.getPlan(emptyvc, query1, makeComments(" /* comment 1 */"), map[string]*querypb.BindVariable{}, false, logStats1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) logStats2 := NewLogStats(context.Background(), "Test", "", nil) plan2, err := r.getPlan(emptyvc, query1, makeComments(" /* comment 2 */"), map[string]*querypb.BindVariable{}, false, logStats2) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan1 != plan2 { t.Errorf("getPlan(query1): plans must be equal: %p %p", plan1, plan2) } @@ -2454,9 +2374,7 @@ func TestGetPlanNormalized(t *testing.T) { logStats3 := NewLogStats(context.Background(), "Test", "", nil) plan3, err := r.getPlan(emptyvc, query2, makeComments(" /* comment 3 */"), map[string]*querypb.BindVariable{}, false, logStats3) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan1 != plan3 { t.Errorf("getPlan(query2): plans must be equal: %p %p", plan1, plan3) } @@ -2467,9 +2385,7 @@ func TestGetPlanNormalized(t *testing.T) { logStats4 := NewLogStats(context.Background(), "Test", "", nil) plan4, err := r.getPlan(emptyvc, normalized, makeComments(" /* comment 4 */"), map[string]*querypb.BindVariable{}, false, logStats4) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan1 != plan4 { t.Errorf("getPlan(normalized): plans must be equal: %p %p", plan1, plan4) } @@ -2480,9 +2396,7 @@ func TestGetPlanNormalized(t *testing.T) { logStats5 := NewLogStats(context.Background(), "Test", "", nil) plan3, err = r.getPlan(unshardedvc, query1, makeComments(" /* comment 5 */"), map[string]*querypb.BindVariable{}, false, logStats5) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan1 == plan3 { t.Errorf("getPlan(query1, ks): plans must not be equal: %p %p", plan1, plan3) } @@ -2493,9 +2407,7 @@ func TestGetPlanNormalized(t *testing.T) { logStats6 := NewLogStats(context.Background(), "Test", "", nil) plan4, err = r.getPlan(unshardedvc, query1, makeComments(" /* comment 6 */"), map[string]*querypb.BindVariable{}, false, logStats6) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if plan3 != plan4 { t.Errorf("getPlan(query1, ks): plans must be equal: %p %p", plan3, plan4) } @@ -2530,9 +2442,7 @@ func TestPassthroughDDL(t *testing.T) { masterSession.TargetString = "TestExecutor" _, err := executorExec(executor, "/* leading */ create table passthrough_ddl (col bigint default 123) /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "/* leading */ create table passthrough_ddl (col bigint default 123) /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -2551,9 +2461,7 @@ func TestPassthroughDDL(t *testing.T) { executor.normalize = true _, err = executorExec(executor, "/* leading */ create table passthrough_ddl (col bigint default 123) /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if sbc1.Queries != nil { t.Errorf("sbc1.Queries: %+v, want nil\n", sbc1.Queries) } @@ -2568,9 +2476,7 @@ func TestPassthroughDDL(t *testing.T) { executor.normalize = true _, err = executorExec(executor, "/* leading */ create table passthrough_ddl (col bigint default 123) /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(sbc1.Queries, wantQueries) { t.Errorf("sbc2.Queries: %+v, want %+v\n", sbc1.Queries, wantQueries) } diff --git a/go/vt/vtgate/mysql_protocol_test.go b/go/vt/vtgate/mysql_protocol_test.go index b2e496c38b2..5a93c224e7e 100644 --- a/go/vt/vtgate/mysql_protocol_test.go +++ b/go/vt/vtgate/mysql_protocol_test.go @@ -47,9 +47,7 @@ func TestMySQLProtocolExecute(t *testing.T) { defer c.Close() qr, err := c.ExecuteFetch("select id from t1", 10, true /* wantfields */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(sandboxconn.SingleRowResult, qr) { t.Errorf("want \n%+v, got \n%+v", sandboxconn.SingleRowResult, qr) } @@ -74,14 +72,10 @@ func TestMySQLProtocolStreamExecute(t *testing.T) { defer c.Close() _, err = c.ExecuteFetch("set workload='olap'", 1, true /* wantfields */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) qr, err := c.ExecuteFetch("select id from t1", 10, true /* wantfields */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(sandboxconn.SingleRowResult, qr) { t.Errorf("want \n%+v, got \n%+v", sandboxconn.SingleRowResult, qr) } @@ -167,9 +161,7 @@ func TestMySQLProtocolClientFoundRows(t *testing.T) { defer c.Close() qr, err := c.ExecuteFetch("select id from t1", 10, true /* wantfields */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(sandboxconn.SingleRowResult, qr) { t.Errorf("want \n%+v, got \n%+v", sandboxconn.SingleRowResult, qr) } diff --git a/go/vt/vtgate/queryz_test.go b/go/vt/vtgate/queryz_test.go index 1c1d0af4384..40119b31e89 100644 --- a/go/vt/vtgate/queryz_test.go +++ b/go/vt/vtgate/queryz_test.go @@ -25,6 +25,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/vtgate/engine" @@ -40,9 +41,7 @@ func TestQueryzHandler(t *testing.T) { // single shard query sql := "select id from user where id = 1" _, err := executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) result, ok := executor.plans.Get("@master:" + sql) if !ok { t.Fatalf("couldn't get plan from cache") @@ -53,9 +52,7 @@ func TestQueryzHandler(t *testing.T) { // scatter sql = "select id from user" _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) result, ok = executor.plans.Get("@master:" + sql) if !ok { t.Fatalf("couldn't get plan from cache") @@ -68,9 +65,7 @@ func TestQueryzHandler(t *testing.T) { "id": sqltypes.Uint64BindVariable(1), "name": sqltypes.BytesBindVariable([]byte("myname")), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) result, ok = executor.plans.Get("@master:" + sql) if !ok { t.Fatalf("couldn't get plan from cache") @@ -91,9 +86,7 @@ func TestQueryzHandler(t *testing.T) { "name": sqltypes.BytesBindVariable([]byte("myname")), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) plan3.ExecTime = time.Duration(100 * time.Millisecond) plan4.ExecTime = time.Duration(200 * time.Millisecond) diff --git a/go/vt/vtgate/resolver_test.go b/go/vt/vtgate/resolver_test.go index d8f37cd6ec0..8389ac26803 100644 --- a/go/vt/vtgate/resolver_test.go +++ b/go/vt/vtgate/resolver_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/discovery" @@ -460,9 +461,7 @@ func TestResolverMessageAckSharded(t *testing.T) { }, } count, err := res.MessageAckKeyspaceIds(context.Background(), name, "user", idKeyspaceIDs) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 2 { t.Errorf("count: %d, want 2", count) } @@ -504,9 +503,7 @@ func TestResolverMessageAckUnsharded(t *testing.T) { }, } count, err := res.MessageAckKeyspaceIds(context.Background(), KsTestUnsharded, "user", idKeyspaceIDs) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 2 { t.Errorf("count: %d, want 2", count) } diff --git a/go/vt/vtgate/scatter_conn_test.go b/go/vt/vtgate/scatter_conn_test.go index 9eafc852113..66d2a30fae2 100644 --- a/go/vt/vtgate/scatter_conn_test.go +++ b/go/vt/vtgate/scatter_conn_test.go @@ -25,6 +25,7 @@ import ( "golang.org/x/net/context" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" @@ -589,9 +590,7 @@ func TestScatterConnSingleDB(t *testing.T) { // SingleDb (legacy) session := NewSafeSession(&vtgatepb.Session{InTransaction: true, SingleDb: true}) _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("Multi DB exec: %v, must contain %s", err, want) @@ -600,9 +599,7 @@ func TestScatterConnSingleDB(t *testing.T) { // TransactionMode_SINGLE in session session = NewSafeSession(&vtgatepb.Session{InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}) _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("Multi DB exec: %v, must contain %s", err, want) @@ -612,9 +609,7 @@ func TestScatterConnSingleDB(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_SINGLE session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("Multi DB exec: %v, must contain %s", err, want) @@ -624,13 +619,9 @@ func TestScatterConnSingleDB(t *testing.T) { sc.txConn.mode = vtgatepb.TransactionMode_MULTI session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestAppendResult(t *testing.T) { diff --git a/go/vt/vtgate/tx_conn_test.go b/go/vt/vtgate/tx_conn_test.go index d05e98abd12..b094f7524b0 100644 --- a/go/vt/vtgate/tx_conn_test.go +++ b/go/vt/vtgate/tx_conn_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/vt/discovery" @@ -603,9 +604,7 @@ func TestTxConnResolveOnPrepare(t *testing.T) { }}, }} err := sc.txConn.Resolve(context.Background(), dtid) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if c := sbc0.SetRollbackCount.Get(); c != 1 { t.Errorf("sbc0.SetRollbackCount: %d, want 1", c) } @@ -894,9 +893,7 @@ func TestTxConnMultiGoSessions(t *testing.T) { err = txc.runSessions(input, func(s *vtgatepb.Session_ShardSession) error { return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestTxConnMultiGoTargets(t *testing.T) { @@ -932,9 +929,7 @@ func TestTxConnMultiGoTargets(t *testing.T) { err = txc.runTargets(input, func(t *querypb.Target) error { return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func newTestTxConnEnv(t *testing.T, name string) (sc *ScatterConn, sbc0, sbc1 *sandboxconn.SandboxConn, rss0, rss1, rss01 []*srvtopo.ResolvedShard) { diff --git a/go/vt/vtgate/vtgate_test.go b/go/vt/vtgate/vtgate_test.go index 3ba27750603..560aca613b0 100644 --- a/go/vt/vtgate/vtgate_test.go +++ b/go/vt/vtgate/vtgate_test.go @@ -29,6 +29,7 @@ import ( "golang.org/x/net/context" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/discovery" "vitess.io/vitess/go/vt/key" @@ -95,9 +96,7 @@ func TestVTGateBegin(t *testing.T) { rpcVTGate.txConn.mode = vtgatepb.TransactionMode_SINGLE got, err := rpcVTGate.Begin(context.Background(), true) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantSession := &vtgatepb.Session{ InTransaction: true, SingleDb: true, @@ -114,9 +113,7 @@ func TestVTGateBegin(t *testing.T) { rpcVTGate.txConn.mode = vtgatepb.TransactionMode_MULTI got, err = rpcVTGate.Begin(context.Background(), true) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantSession = &vtgatepb.Session{ InTransaction: true, SingleDb: true, @@ -126,9 +123,7 @@ func TestVTGateBegin(t *testing.T) { } got, err = rpcVTGate.Begin(context.Background(), false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantSession = &vtgatepb.Session{ InTransaction: true, } @@ -138,9 +133,7 @@ func TestVTGateBegin(t *testing.T) { rpcVTGate.txConn.mode = vtgatepb.TransactionMode_TWOPC got, err = rpcVTGate.Begin(context.Background(), true) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantSession = &vtgatepb.Session{ InTransaction: true, SingleDb: true, @@ -150,9 +143,7 @@ func TestVTGateBegin(t *testing.T) { } got, err = rpcVTGate.Begin(context.Background(), false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantSession = &vtgatepb.Session{ InTransaction: true, } @@ -182,9 +173,7 @@ func TestVTGateCommit(t *testing.T) { InTransaction: true, } err = rpcVTGate.Commit(context.Background(), false, session) - if err != nil { - t.Error(err) - } + require.NoError(t, err) rpcVTGate.txConn.mode = vtgatepb.TransactionMode_MULTI session = &vtgatepb.Session{ @@ -199,33 +188,25 @@ func TestVTGateCommit(t *testing.T) { InTransaction: true, } err = rpcVTGate.Commit(context.Background(), false, session) - if err != nil { - t.Error(err) - } + require.NoError(t, err) rpcVTGate.txConn.mode = vtgatepb.TransactionMode_TWOPC session = &vtgatepb.Session{ InTransaction: true, } err = rpcVTGate.Commit(context.Background(), true, session) - if err != nil { - t.Error(err) - } + require.NoError(t, err) session = &vtgatepb.Session{ InTransaction: true, } err = rpcVTGate.Commit(context.Background(), false, session) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestVTGateRollbackNil(t *testing.T) { err := rpcVTGate.Rollback(context.Background(), nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestVTGateExecute(t *testing.T) { @@ -253,9 +234,7 @@ func TestVTGateExecute(t *testing.T) { } session, err := rpcVTGate.Begin(context.Background(), false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !session.InTransaction { t.Errorf("want true, got false") } @@ -1438,9 +1417,7 @@ func TestVTGateMessageAck(t *testing.T) { Value: []byte("2"), }} count, err := rpcVTGate.MessageAck(context.Background(), ks, "msg", ids) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 2 { t.Errorf("MessageAck: %d, want 2", count) } @@ -1469,9 +1446,7 @@ func TestVTGateMessageAckKeyspaceIds(t *testing.T) { }, } count, err := rpcVTGate.MessageAckKeyspaceIds(context.Background(), ks, "msg", idKeyspaceIDs) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 2 { t.Errorf("MessageAck: %d, want 2", count) } From e94925b3e2f283c3194ad462a2b2bb74aa750aa5 Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Thu, 6 Feb 2020 16:05:27 -0800 Subject: [PATCH 051/825] Full pass on codebase. Need to double check later Signed-off-by: Saif Alharthi --- go/cmd/vttestserver/vttestserver_test.go | 5 +- go/proc/proc_flaky_test.go | 14 +- go/sqltypes/bind_variables_test.go | 5 +- go/vt/binlog/event_streamer_test.go | 13 +- go/vt/dtids/dtids_test.go | 9 +- go/vt/sqlparser/ast_test.go | 53 ++--- go/vt/topo/test/vschema.go | 17 +- go/vt/vitessdriver/driver_test.go | 5 +- go/vt/vitessdriver/rows_test.go | 9 +- go/vt/vitessdriver/streaming_rows_test.go | 17 +- go/vt/vtgate/engine/limit_test.go | 65 ++---- go/vt/vtgate/engine/merge_sort_test.go | 13 +- go/vt/vtgate/engine/pullout_subquery_test.go | 9 +- go/vt/vtgate/executor_dml_test.go | 200 +++++------------- go/vt/vtgate/executor_stream_test.go | 9 +- go/vt/vtgate/vindexes/binary_test.go | 5 +- go/vt/vtgate/vindexes/binarymd5_test.go | 5 +- .../vtgate/vindexes/consistent_lookup_test.go | 29 +-- go/vt/vtgate/vindexes/hash_test.go | 9 +- go/vt/vtgate/vindexes/lookup_hash_test.go | 61 ++---- .../vindexes/lookup_hash_unique_test.go | 41 +--- go/vt/vtgate/vindexes/lookup_test.go | 53 ++--- .../lookup_unicodeloosemd5_hash_test.go | 53 ++--- go/vt/vtgate/vindexes/lookup_unique_test.go | 37 +--- go/vt/vtgate/vindexes/null_test.go | 5 +- .../vindexes/numeric_static_map_test.go | 8 +- go/vt/vtgate/vindexes/numeric_test.go | 13 +- go/vt/vtgate/vindexes/reverse_bits_test.go | 9 +- go/vt/vtgate/vindexes/vschema_test.go | 56 ++--- go/vt/vtgate/vtgateconntest/client.go | 149 ++++--------- go/vt/vttablet/endtoend/message_test.go | 29 +-- go/vt/vttablet/endtoend/misc_test.go | 17 +- go/vt/vttablet/endtoend/transaction_test.go | 73 ++----- .../tabletserver/messager/engine_test.go | 5 +- .../tabletserver/planbuilder/plan_test.go | 5 +- .../tabletserver/tabletserver_flaky_test.go | 129 +++-------- .../vttablet/tabletserver/tx_executor_test.go | 89 ++------ .../tabletserver/tx_prep_pool_test.go | 30 +-- .../vstreamer/rowstreamer_test.go | 5 +- 39 files changed, 368 insertions(+), 990 deletions(-) diff --git a/go/cmd/vttestserver/vttestserver_test.go b/go/cmd/vttestserver/vttestserver_test.go index 0fcee741e33..799b942f013 100644 --- a/go/cmd/vttestserver/vttestserver_test.go +++ b/go/cmd/vttestserver/vttestserver_test.go @@ -27,6 +27,7 @@ import ( "vitess.io/vitess/go/vt/tlstest" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/vttest" @@ -187,9 +188,7 @@ func assertColumnVindex(t *testing.T, cluster vttest.LocalCluster, expected colu assertEqual(t, columnVindex.Name, expected.vindex, "Actual vindex name different from expected") assertEqual(t, columnVindex.Columns[0], expected.column, "Actual vindex column different from expected") }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func assertEqual(t *testing.T, actual string, expected string, message string) { diff --git a/go/proc/proc_flaky_test.go b/go/proc/proc_flaky_test.go index 3058b44859e..4c9d6aa6c2e 100644 --- a/go/proc/proc_flaky_test.go +++ b/go/proc/proc_flaky_test.go @@ -28,6 +28,8 @@ import ( "syscall" "testing" "time" + + "github.com/stretchr/testify/require" ) func TestRestart(t *testing.T) { @@ -65,19 +67,13 @@ func testLaunch(t *testing.T) { cmd2 := launchServer(t, port, 2) defer cmd2.Process.Kill() err = cmd1.Wait() - if err != nil { - t.Error(err) - } + require.NoError(t, err) testPid(t, port, cmd2.Process.Pid) err = syscall.Kill(cmd2.Process.Pid, syscall.SIGTERM) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = cmd2.Wait() - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func launchServer(t *testing.T, port string, num int) *exec.Cmd { diff --git a/go/sqltypes/bind_variables_test.go b/go/sqltypes/bind_variables_test.go index d5559d07227..73fbd754485 100644 --- a/go/sqltypes/bind_variables_test.go +++ b/go/sqltypes/bind_variables_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -524,9 +525,7 @@ func TestValidateBindVariable(t *testing.T) { func TestBindVariableToValue(t *testing.T) { v, err := BindVariableToValue(Int64BindVariable(1)) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := MakeTrusted(querypb.Type_INT64, []byte("1")) if !reflect.DeepEqual(v, want) { t.Errorf("BindVarToValue(1): %v, want %v", v, want) diff --git a/go/vt/binlog/event_streamer_test.go b/go/vt/binlog/event_streamer_test.go index 1a487efaf46..fc5434717a6 100644 --- a/go/vt/binlog/event_streamer_test.go +++ b/go/vt/binlog/event_streamer_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" @@ -90,9 +91,7 @@ func TestSetErrors(t *testing.T) { } before := binlogStreamerErrors.Counts()["EventStreamer"] err := evs.transactionToEvent(nil, statements) - if err != nil { - t.Error(err) - } + require.NoError(t, err) got := binlogStreamerErrors.Counts()["EventStreamer"] if got != before+1 { t.Errorf("got: %v, want: %+v", got, before+1) @@ -160,9 +159,7 @@ func TestDMLEvent(t *testing.T) { }, } err := evs.transactionToEvent(eventToken, statements) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestDDLEvent(t *testing.T) { @@ -208,7 +205,5 @@ func TestDDLEvent(t *testing.T) { }, } err := evs.transactionToEvent(eventToken, statements) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } diff --git a/go/vt/dtids/dtids_test.go b/go/vt/dtids/dtids_test.go index e5a8fc86750..acb00bdfe84 100644 --- a/go/vt/dtids/dtids_test.go +++ b/go/vt/dtids/dtids_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -41,9 +42,7 @@ func TestDTID(t *testing.T) { t.Errorf("generateDTID: %s, want %s", dtid, want) } out, err := ShardSession(dtid) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !proto.Equal(in, out) { t.Errorf("ShardSession: %+v, want %+v", out, in) } @@ -61,9 +60,7 @@ func TestDTID(t *testing.T) { func TestTransactionID(t *testing.T) { out, err := TransactionID("aa:0:1") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if out != 1 { t.Errorf("TransactionID(aa:0:1): %d, want 1", out) } diff --git a/go/vt/sqlparser/ast_test.go b/go/vt/sqlparser/ast_test.go index 1f18bcb07fa..579efc45dfd 100644 --- a/go/vt/sqlparser/ast_test.go +++ b/go/vt/sqlparser/ast_test.go @@ -24,15 +24,14 @@ import ( "testing" "unsafe" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" ) func TestAppend(t *testing.T) { query := "select * from t where a = 1" tree, err := Parse(query) - if err != nil { - t.Error(err) - } + require.NoError(t, err) var b strings.Builder Append(&b, tree) got := b.String() @@ -50,9 +49,7 @@ func TestAppend(t *testing.T) { func TestSelect(t *testing.T) { tree, err := Parse("select * from t where a = 1") - if err != nil { - t.Error(err) - } + require.NoError(t, err) expr := tree.(*Select).Where.Expr sel := &Select{} @@ -88,9 +85,7 @@ func TestSelect(t *testing.T) { // OR clauses must be parenthesized. tree, err = Parse("select * from t where a = 1 or b = 1") - if err != nil { - t.Error(err) - } + require.NoError(t, err) expr = tree.(*Select).Where.Expr sel = &Select{} sel.AddWhere(expr) @@ -133,14 +128,10 @@ func TestRemoveHints(t *testing.T) { func TestAddOrder(t *testing.T) { src, err := Parse("select foo, bar from baz order by foo") - if err != nil { - t.Error(err) - } + require.NoError(t, err) order := src.(*Select).OrderBy[0] dst, err := Parse("select * from t") - if err != nil { - t.Error(err) - } + require.NoError(t, err) dst.(*Select).AddOrder(order) buf := NewTrackedBuffer(nil) dst.Format(buf) @@ -149,9 +140,7 @@ func TestAddOrder(t *testing.T) { t.Errorf("order: %q, want %s", buf.String(), want) } dst, err = Parse("select * from t union select * from s") - if err != nil { - t.Error(err) - } + require.NoError(t, err) dst.(*Union).AddOrder(order) buf = NewTrackedBuffer(nil) dst.Format(buf) @@ -163,14 +152,10 @@ func TestAddOrder(t *testing.T) { func TestSetLimit(t *testing.T) { src, err := Parse("select foo, bar from baz limit 4") - if err != nil { - t.Error(err) - } + require.NoError(t, err) limit := src.(*Select).Limit dst, err := Parse("select * from t") - if err != nil { - t.Error(err) - } + require.NoError(t, err) dst.(*Select).SetLimit(limit) buf := NewTrackedBuffer(nil) dst.Format(buf) @@ -179,9 +164,7 @@ func TestSetLimit(t *testing.T) { t.Errorf("limit: %q, want %s", buf.String(), want) } dst, err = Parse("select * from t union select * from s") - if err != nil { - t.Error(err) - } + require.NoError(t, err) dst.(*Union).SetLimit(limit) buf = NewTrackedBuffer(nil) dst.Format(buf) @@ -269,9 +252,7 @@ func TestDDL(t *testing.T) { func TestSetAutocommitON(t *testing.T) { stmt, err := Parse("SET autocommit=ON") - if err != nil { - t.Error(err) - } + require.NoError(t, err) s, ok := stmt.(*Set) if !ok { t.Errorf("SET statement is not Set: %T", s) @@ -296,9 +277,7 @@ func TestSetAutocommitON(t *testing.T) { } stmt, err = Parse("SET @@session.autocommit=ON") - if err != nil { - t.Error(err) - } + require.NoError(t, err) s, ok = stmt.(*Set) if !ok { t.Errorf("SET statement is not Set: %T", s) @@ -325,9 +304,7 @@ func TestSetAutocommitON(t *testing.T) { func TestSetAutocommitOFF(t *testing.T) { stmt, err := Parse("SET autocommit=OFF") - if err != nil { - t.Error(err) - } + require.NoError(t, err) s, ok := stmt.(*Set) if !ok { t.Errorf("SET statement is not Set: %T", s) @@ -352,9 +329,7 @@ func TestSetAutocommitOFF(t *testing.T) { } stmt, err = Parse("SET @@session.autocommit=OFF") - if err != nil { - t.Error(err) - } + require.NoError(t, err) s, ok = stmt.(*Set) if !ok { t.Errorf("SET statement is not Set: %T", s) diff --git a/go/vt/topo/test/vschema.go b/go/vt/topo/test/vschema.go index e86cb3df2f6..0349e6b63fa 100644 --- a/go/vt/topo/test/vschema.go +++ b/go/vt/topo/test/vschema.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/vt/topo" @@ -53,9 +54,7 @@ func checkVSchema(t *testing.T, ts *topo.Server) { } got, err := ts.GetVSchema(ctx, "test_keyspace") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := &vschemapb.Keyspace{ Tables: map[string]*vschemapb.Table{ "unsharded": {}, @@ -68,14 +67,10 @@ func checkVSchema(t *testing.T, ts *topo.Server) { err = ts.SaveVSchema(ctx, "test_keyspace", &vschemapb.Keyspace{ Sharded: true, }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) got, err = ts.GetVSchema(ctx, "test_keyspace") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = &vschemapb.Keyspace{ Sharded: true, } @@ -113,9 +108,7 @@ func checkRoutingRules(t *testing.T, ts *topo.Server) { } got, err := ts.GetRoutingRules(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !proto.Equal(got, want) { t.Errorf("GetRoutingRules: %v, want %v", got, want) } diff --git a/go/vt/vitessdriver/driver_test.go b/go/vt/vitessdriver/driver_test.go index 57e1d9fb6db..1c0e78cd284 100644 --- a/go/vt/vitessdriver/driver_test.go +++ b/go/vt/vitessdriver/driver_test.go @@ -28,6 +28,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "vitess.io/vitess/go/sqltypes" @@ -160,9 +161,7 @@ func TestOpen_InvalidJson(t *testing.T) { func TestBeginIsolation(t *testing.T) { db, err := Open(testAddress, "@master") - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer db.Close() _, err = db.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead}) want := errIsolationUnsupported.Error() diff --git a/go/vt/vitessdriver/rows_test.go b/go/vt/vitessdriver/rows_test.go index 6a40402dbea..e6e7502c114 100644 --- a/go/vt/vitessdriver/rows_test.go +++ b/go/vt/vitessdriver/rows_test.go @@ -22,6 +22,7 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -106,9 +107,7 @@ func TestRows(t *testing.T) { } gotRow := make([]driver.Value, len(wantRow)) err := ri.Next(gotRow) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(gotRow, wantRow) { t.Errorf("row1: %#v, want %#v type: %T", gotRow, wantRow, wantRow[3]) logMismatchedTypes(t, gotRow, wantRow) @@ -122,9 +121,7 @@ func TestRows(t *testing.T) { uint64(18446744073709551615), } err = ri.Next(gotRow) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(gotRow, wantRow) { t.Errorf("row1: %v, want %v", gotRow, wantRow) logMismatchedTypes(t, gotRow, wantRow) diff --git a/go/vt/vitessdriver/streaming_rows_test.go b/go/vt/vitessdriver/streaming_rows_test.go index d084f0755b8..171da527cb3 100644 --- a/go/vt/vitessdriver/streaming_rows_test.go +++ b/go/vt/vitessdriver/streaming_rows_test.go @@ -24,6 +24,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -102,9 +103,7 @@ func TestStreamingRows(t *testing.T) { } gotRow := make([]driver.Value, 3) err := ri.Next(gotRow) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(gotRow, wantRow) { t.Errorf("row1: %v, want %v", gotRow, wantRow) } @@ -115,9 +114,7 @@ func TestStreamingRows(t *testing.T) { []byte("value2"), } err = ri.Next(gotRow) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(gotRow, wantRow) { t.Errorf("row1: %v, want %v", gotRow, wantRow) } @@ -146,9 +143,7 @@ func TestStreamingRowsReversed(t *testing.T) { } gotRow := make([]driver.Value, 3) err := ri.Next(gotRow) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(gotRow, wantRow) { t.Errorf("row1: %v, want %v", gotRow, wantRow) } @@ -216,9 +211,7 @@ func TestStreamingRowsError(t *testing.T) { ri = newStreamingRows(&adapter{c: c, err: errors.New("error after rows")}, &converter{}) gotRow = make([]driver.Value, 3) err = ri.Next(gotRow) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = ri.Next(gotRow) wantErr = "error after rows" if err == nil || !strings.Contains(err.Error(), wantErr) { diff --git a/go/vt/vtgate/engine/limit_test.go b/go/vt/vtgate/engine/limit_test.go index 146171e1b99..e0dd4ddadaf 100644 --- a/go/vt/vtgate/engine/limit_test.go +++ b/go/vt/vtgate/engine/limit_test.go @@ -21,6 +21,7 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -48,9 +49,7 @@ func TestLimitExecute(t *testing.T) { // Test with limit smaller than input. result, err := l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := sqltypes.MakeTestResult( fields, "a|1", @@ -82,9 +81,7 @@ func TestLimitExecute(t *testing.T) { } result, err = l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, inputResult) { t.Errorf("l.Execute:\n%v, want\n%v", result, wantResult) } @@ -105,9 +102,7 @@ func TestLimitExecute(t *testing.T) { } result, err = l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n%v, want\n%v", result, wantResult) } @@ -133,9 +128,7 @@ func TestLimitExecute(t *testing.T) { } result, err = l.Execute(nil, map[string]*querypb.BindVariable{"l": sqltypes.Int64BindVariable(2)}, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n%v, want\n%v", result, wantResult) } @@ -168,9 +161,7 @@ func TestLimitOffsetExecute(t *testing.T) { // Test with offset 0 result, err := l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := sqltypes.MakeTestResult( fields, "a|1", @@ -206,9 +197,7 @@ func TestLimitOffsetExecute(t *testing.T) { "c|3", ) result, err = l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n got %v, want\n%v", result, wantResult) } @@ -238,9 +227,7 @@ func TestLimitOffsetExecute(t *testing.T) { "c|6", ) result, err = l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n got %v, want\n%v", result, wantResult) } @@ -271,9 +258,7 @@ func TestLimitOffsetExecute(t *testing.T) { "c|6", ) result, err = l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n got %v, want\n%v", result, wantResult) } @@ -302,9 +287,7 @@ func TestLimitOffsetExecute(t *testing.T) { "c|6", ) result, err = l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n got %v, want\n%v", result, wantResult) } @@ -332,9 +315,7 @@ func TestLimitOffsetExecute(t *testing.T) { fields, ) result, err = l.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n got %v, want\n%v", result, wantResult) } @@ -360,9 +341,7 @@ func TestLimitOffsetExecute(t *testing.T) { Input: fp, } result, err = l.Execute(nil, map[string]*querypb.BindVariable{"l": sqltypes.Int64BindVariable(1), "o": sqltypes.Int64BindVariable(1)}, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(result, wantResult) { t.Errorf("l.Execute:\n got %v, want\n%v", result, wantResult) } @@ -395,9 +374,7 @@ func TestLimitStreamExecute(t *testing.T) { results = append(results, qr) return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResults := sqltypes.MakeTestStreamingResults( fields, "a|1", @@ -415,9 +392,7 @@ func TestLimitStreamExecute(t *testing.T) { results = append(results, qr) return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(results, wantResults) { t.Errorf("l.StreamExecute:\n%s, want\n%s", sqltypes.PrintResults(results), sqltypes.PrintResults(wantResults)) } @@ -430,9 +405,7 @@ func TestLimitStreamExecute(t *testing.T) { results = append(results, qr) return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResults = sqltypes.MakeTestStreamingResults( fields, "a|1", @@ -452,9 +425,7 @@ func TestLimitStreamExecute(t *testing.T) { results = append(results, qr) return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // wantResults is same as before. if !reflect.DeepEqual(results, wantResults) { t.Errorf("l.StreamExecute:\n%s, want\n%s", sqltypes.PrintResults(results), sqltypes.PrintResults(wantResults)) @@ -473,9 +444,7 @@ func TestLimitGetFields(t *testing.T) { l := &Limit{Input: fp} got, err := l.GetFields(nil, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(got, result) { t.Errorf("l.GetFields:\n%v, want\n%v", got, result) } diff --git a/go/vt/vtgate/engine/merge_sort_test.go b/go/vt/vtgate/engine/merge_sort_test.go index b1c74c903b2..f8e0e34903c 100644 --- a/go/vt/vtgate/engine/merge_sort_test.go +++ b/go/vt/vtgate/engine/merge_sort_test.go @@ -21,6 +21,7 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" @@ -69,9 +70,7 @@ func TestMergeSortNormal(t *testing.T) { results = append(results, qr) return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Results are returned one row at a time. wantResults := sqltypes.MakeTestStreamingResults(idColFields, @@ -139,9 +138,7 @@ func TestMergeSortDescending(t *testing.T) { results = append(results, qr) return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Results are returned one row at a time. wantResults := sqltypes.MakeTestStreamingResults(idColFields, @@ -198,9 +195,7 @@ func TestMergeSortEmptyResults(t *testing.T) { results = append(results, qr) return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Results are returned one row at a time. wantResults := sqltypes.MakeTestStreamingResults(idColFields, diff --git a/go/vt/vtgate/engine/pullout_subquery_test.go b/go/vt/vtgate/engine/pullout_subquery_test.go index 273e667836a..dbf9c8dad95 100644 --- a/go/vt/vtgate/engine/pullout_subquery_test.go +++ b/go/vt/vtgate/engine/pullout_subquery_test.go @@ -20,6 +20,7 @@ import ( "errors" "testing" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" @@ -59,9 +60,7 @@ func TestPulloutSubqueryValueGood(t *testing.T) { } result, err := ps.Execute(nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) sfp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" false`}) ufp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" sq: type:INT64 value:"1" false`}) expectResult(t, "ps.Execute", result, underlyingResult) @@ -317,9 +316,7 @@ func TestPulloutSubqueryStream(t *testing.T) { } result, err := wrapStreamExecute(ps, nil, bindVars, false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) sfp.ExpectLog(t, []string{`Execute aa: type:INT64 value:"1" false`}) ufp.ExpectLog(t, []string{`StreamExecute aa: type:INT64 value:"1" sq: type:INT64 value:"1" false`}) expectResult(t, "ps.StreamExecute", result, underlyingResult) diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index e5bba560ce5..9d3bc5ba8e7 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -42,9 +42,7 @@ func TestUpdateEqual(t *testing.T) { // Update by primary vindex. _, err := executorExec(executor, "update user set a=2 where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "update user set a = 2 where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{}, @@ -59,9 +57,7 @@ func TestUpdateEqual(t *testing.T) { sbc1.Queries = nil _, err = executorExec(executor, "update user set a=2 where id = 3", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "update user set a = 2 where id = 3 /* vtgate:: keyspace_id:4eb190c9a2fa169c */", BindVariables: map[string]*querypb.BindVariable{}, @@ -78,9 +74,7 @@ func TestUpdateEqual(t *testing.T) { sbc2.Queries = nil sbclookup.SetResults([]*sqltypes.Result{{}}) _, err = executorExec(executor, "update music set a=2 where id = 2", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select user_id from music_user_map where music_id = :music_id", BindVariables: map[string]*querypb.BindVariable{ @@ -102,9 +96,7 @@ func TestUpdateEqual(t *testing.T) { sbc2.Queries = nil sbclookup.Queries = nil _, err = executorExec(executor, "update user2 set name='myname', lastname='mylastname' where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{ { Sql: "select name, lastname from user2 where id = 1 for update", @@ -271,9 +263,7 @@ func TestUpdateComments(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() _, err := executorExec(executor, "update user set a=2 where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "update user set a = 2 where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -291,9 +281,7 @@ func TestUpdateNormalize(t *testing.T) { executor.normalize = true _, err := executorExec(executor, "/* leading */ update user set a=2 where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "/* leading */ update user set a = :vtg1 where id = :vtg2 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ /* trailing */", BindVariables: map[string]*querypb.BindVariable{ @@ -312,9 +300,7 @@ func TestUpdateNormalize(t *testing.T) { // Force the query to go to the "wrong" shard and ensure that normalization still happens masterSession.TargetString = "TestExecutor/40-60" _, err = executorExec(executor, "/* leading */ update user set a=2 where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "/* leading */ update user set a = :vtg1 where id = :vtg2 /* trailing *//* vtgate:: filtered_replication_unfriendly */", BindVariables: map[string]*querypb.BindVariable{ @@ -346,9 +332,7 @@ func TestDeleteEqual(t *testing.T) { }}, }}) _, err := executorExec(executor, "delete from user where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select name from user where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, @@ -375,9 +359,7 @@ func TestDeleteEqual(t *testing.T) { sbclookup.Queries = nil sbc.SetResults([]*sqltypes.Result{{}}) _, err = executorExec(executor, "delete from user where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select name from user where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, @@ -396,9 +378,7 @@ func TestDeleteEqual(t *testing.T) { sbclookup.Queries = nil sbclookup.SetResults([]*sqltypes.Result{{}}) _, err = executorExec(executor, "delete from music where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "select user_id from music_user_map where music_id = :music_id", BindVariables: map[string]*querypb.BindVariable{ @@ -416,9 +396,7 @@ func TestDeleteEqual(t *testing.T) { sbclookup.Queries = nil sbclookup.SetResults([]*sqltypes.Result{{}}) _, err = executorExec(executor, "delete from user_extra where user_id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "delete from user_extra where user_id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{}, @@ -433,9 +411,7 @@ func TestDeleteEqual(t *testing.T) { sbc.Queries = nil sbclookup.Queries = nil _, err = executorExec(executor, "delete from user2 where id = 1", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{ { Sql: "select name, lastname from user2 where id = 1 for update", @@ -469,9 +445,7 @@ func TestDeleteEqual(t *testing.T) { func TestUpdateScatter(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() _, err := executorExec(executor, "update user_extra set col = 2", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Queries get annotatted. wantQueries := []*querypb.BoundQuery{{ Sql: "update user_extra set col = 2/* vtgate:: filtered_replication_unfriendly */", @@ -488,9 +462,7 @@ func TestUpdateScatter(t *testing.T) { func TestDeleteScatter(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() _, err := executorExec(executor, "delete from user_extra", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Queries get annotatted. wantQueries := []*querypb.BoundQuery{{ Sql: "delete from user_extra/* vtgate:: filtered_replication_unfriendly */", @@ -508,9 +480,7 @@ func TestDeleteByDestination(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() // This query is not supported in v3, so we know for sure is taking the DeleteByDestination route _, err := executorExec(executor, "delete from `TestExecutor[-]`.user_extra limit 10", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Queries get annotatted. wantQueries := []*querypb.BoundQuery{{ Sql: "delete from user_extra limit 10/* vtgate:: filtered_replication_unfriendly */", @@ -538,9 +508,7 @@ func TestDeleteComments(t *testing.T) { }}, }}) _, err := executorExec(executor, "delete from user where id = 1 /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select name from user where id = 1 for update /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, @@ -571,9 +539,7 @@ func TestInsertSharded(t *testing.T) { defer QueryLogger.Unsubscribe(logChan) _, err := executorExec(executor, "insert into user(id, v, name) values (1, 2, 'myname')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into user(id, v, name) values (:_Id0, 2, :_name0) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -605,9 +571,7 @@ func TestInsertSharded(t *testing.T) { sbc1.Queries = nil sbclookup.Queries = nil _, err = executorExec(executor, "insert into user(id, v, name) values (3, 2, 'myname2')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "insert into user(id, v, name) values (:_Id0, 2, :_name0) /* vtgate:: keyspace_id:4eb190c9a2fa169c */", BindVariables: map[string]*querypb.BindVariable{ @@ -635,9 +599,7 @@ func TestInsertSharded(t *testing.T) { sbc1.Queries = nil _, err = executorExec(executor, "insert into user2(id, name, lastname) values (2, 'myname', 'mylastname')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "insert into user2(id, name, lastname) values (:_id0, :_name0, :_lastname0) /* vtgate:: keyspace_id:06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -711,9 +673,7 @@ func TestInsertShardedAutocommitLookup(t *testing.T) { executor, sbc1, sbc2, sbclookup := createCustomExecutor(vschema) _, err := executorExec(executor, "insert into user(id, v, name) values (1, 2, 'myname')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into user(id, v, name) values (:_Id0, 2, :_name0) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -781,9 +741,7 @@ func TestInsertShardedIgnore(t *testing.T) { // Sixth row: second shard (because 3 hash maps to 40-60). query := "insert ignore into insert_ignore_test(pv, owned, verify) values (1, 1, 1), (2, 2, 2), (3, 3, 1), (4, 4, 4), (5, 5, 1), (6, 6, 3)" _, err := executorExec(executor, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert ignore into insert_ignore_test(pv, owned, verify) values (:_pv0, :_owned0, :_verify0),(:_pv4, :_owned4, :_verify4) /* vtgate:: keyspace_id:166b40b44aba4bd6,166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -906,9 +864,7 @@ func TestInsertShardedIgnore(t *testing.T) { }) query = "insert ignore into insert_ignore_test(pv, owned, verify) values (1, 1, 1)" qr, err := executorExec(executor, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(qr, &sqltypes.Result{}) { t.Errorf("qr: %v, want empty result", qr) } @@ -935,9 +891,7 @@ func TestInsertOnDupKey(t *testing.T) { executor, sbc1, sbc2, sbclookup := createExecutorEnv() query := "insert into insert_ignore_test(pv, owned, verify) values (1, 1, 1) on duplicate key update col = 2" _, err := executorExec(executor, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into insert_ignore_test(pv, owned, verify) values (:_pv0, :_owned0, :_verify0) on duplicate key update col = 2 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -979,9 +933,7 @@ func TestInsertComments(t *testing.T) { executor, sbc1, sbc2, sbclookup := createExecutorEnv() _, err := executorExec(executor, "insert into user(id, v, name) values (1, 2, 'myname') /* trailing */", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into user(id, v, name) values (:_Id0, 2, :_name0) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ /* trailing */", BindVariables: map[string]*querypb.BindVariable{ @@ -1019,9 +971,7 @@ func TestInsertGeneratorSharded(t *testing.T) { InsertID: 1, }}) result, err := executorExec(executor, "insert into user(v, name) values (2, 'myname')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into user(v, name, id) values (2, :_name0, :_Id0) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -1066,9 +1016,7 @@ func TestInsertAutoincSharded(t *testing.T) { } sbc.SetResults([]*sqltypes.Result{wantResult}) result, err := executorExec(router, "insert into user_extra(user_id) values (2)", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into user_extra(user_id) values (:_user_id0) /* vtgate:: keyspace_id:06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1086,9 +1034,7 @@ func TestInsertAutoincSharded(t *testing.T) { func TestInsertGeneratorUnsharded(t *testing.T) { executor, _, _, sbclookup := createExecutorEnv() result, err := executorExec(executor, "insert into main1(id, name) values (null, 'myname')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "select next :n values from user_seq", BindVariables: map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(1)}, @@ -1123,9 +1069,7 @@ func TestInsertAutoincUnsharded(t *testing.T) { sbclookup.SetResults([]*sqltypes.Result{wantResult}) result, err := executorExec(router, query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: query, BindVariables: map[string]*querypb.BindVariable{}, @@ -1142,9 +1086,7 @@ func TestInsertLookupOwned(t *testing.T) { executor, sbc, _, sbclookup := createExecutorEnv() _, err := executorExec(executor, "insert into music(user_id, id) values (2, 3)", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into music(user_id, id) values (:_user_id0, :_id0) /* vtgate:: keyspace_id:06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1179,9 +1121,7 @@ func TestInsertLookupOwnedGenerator(t *testing.T) { InsertID: 1, }}) result, err := executorExec(executor, "insert into music(user_id) values (2)", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into music(user_id, id) values (:_user_id0, :_id0) /* vtgate:: keyspace_id:06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1217,9 +1157,7 @@ func TestInsertLookupUnowned(t *testing.T) { executor, sbc, _, sbclookup := createExecutorEnv() _, err := executorExec(executor, "insert into music_extra(user_id, music_id) values (2, 3)", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into music_extra(user_id, music_id) values (:_user_id0, :_music_id0) /* vtgate:: keyspace_id:06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1246,9 +1184,7 @@ func TestInsertLookupUnownedUnsupplied(t *testing.T) { executor, sbc, _, sbclookup := createExecutorEnv() _, err := executorExec(executor, "insert into music_extra_reversed(music_id) values (3)", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into music_extra_reversed(music_id, user_id) values (:_music_id0, :_user_id0) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -1317,9 +1253,7 @@ func TestMultiInsertSharded(t *testing.T) { executor, sbc1, sbc2, sbclookup := createExecutorEnv() _, err := executorExec(executor, "insert into user(id, v, name) values (1, 1, 'myname1'),(3, 3, 'myname3')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries1 := []*querypb.BoundQuery{{ Sql: "insert into user(id, v, name) values (:_Id0, 1, :_name0) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -1368,9 +1302,7 @@ func TestMultiInsertSharded(t *testing.T) { sbclookup.Queries = nil sbc2.Queries = nil _, err = executorExec(executor, "insert into user(id, v, name) values (1, 1, 'myname1'),(2, 2, 'myname2')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into user(id, v, name) values (:_Id0, 1, :_name0),(:_Id1, 2, :_name1) /* vtgate:: keyspace_id:166b40b44aba4bd6,06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1407,9 +1339,7 @@ func TestMultiInsertSharded(t *testing.T) { sbclookup.Queries = nil sbc2.Queries = nil _, err = executorExec(executor, "insert into user2(id, name, lastname) values (2, 'myname', 'mylastname'), (3, 'myname2', 'mylastname2')", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ Sql: "insert into user2(id, name, lastname) values (:_id0, :_name0, :_lastname0) /* vtgate:: keyspace_id:06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1451,9 +1381,7 @@ func TestMultiInsertGenerator(t *testing.T) { InsertID: 1, }}) result, err := executorExec(executor, "insert into music(user_id, name) values (:u, 'myname1'),(:u, 'myname2')", map[string]*querypb.BindVariable{"u": sqltypes.Int64BindVariable(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into music(user_id, name, id) values (:_user_id0, 'myname1', :_id0),(:_user_id1, 'myname2', :_id1) /* vtgate:: keyspace_id:06e7ea22ce92708f,06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1502,9 +1430,7 @@ func TestMultiInsertGeneratorSparse(t *testing.T) { InsertID: 1, }}) result, err := executorExec(executor, "insert into music(id, user_id, name) values (NULL, :u, 'myname1'),(2, :u, 'myname2'), (NULL, :u, 'myname3')", map[string]*querypb.BindVariable{"u": sqltypes.Int64BindVariable(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ Sql: "insert into music(id, user_id, name) values (:_id0, :_user_id0, 'myname1'),(:_id1, :_user_id1, 'myname2'),(:_id2, :_user_id2, 'myname3') /* vtgate:: keyspace_id:06e7ea22ce92708f,06e7ea22ce92708f,06e7ea22ce92708f */", BindVariables: map[string]*querypb.BindVariable{ @@ -1588,9 +1514,7 @@ func TestKeyDestRangeQuery(t *testing.T) { masterSession.TargetString = "TestExecutor[40-60]" _, err := executorExec(executor, "DELETE FROM sharded_user_msgs LIMIT 1000", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) sql := "DELETE FROM sharded_user_msgs LIMIT 1000" wantQueries := []*querypb.BoundQuery{{ Sql: sql + "/* vtgate:: filtered_replication_unfriendly */", @@ -1609,9 +1533,7 @@ func TestKeyDestRangeQuery(t *testing.T) { masterSession.TargetString = "TestExecutor[-60]" _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) testQueries(t, "sbc1", sbc1, wantQueries) testQueries(t, "sbc1", sbc2, wantQueries) @@ -1622,9 +1544,7 @@ func TestKeyDestRangeQuery(t *testing.T) { masterSession.TargetString = "TestExecutor[-]" _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) testQueries(t, "sbc1", sbc1, wantQueries) testQueries(t, "sbc2", sbc2, wantQueries) @@ -1640,9 +1560,7 @@ func TestKeyDestRangeQuery(t *testing.T) { }} _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) testQueries(t, "sbc1", sbc1, wantQueries) testQueries(t, "sbc2", sbc2, wantQueries) @@ -1659,9 +1577,7 @@ func TestKeyDestRangeQuery(t *testing.T) { }} _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) testQueries(t, "sbc1", sbc1, wantQueries) testQueries(t, "sbc2", sbc2, wantQueries) @@ -1688,9 +1604,7 @@ func TestKeyShardDestQuery(t *testing.T) { masterSession.TargetString = "TestExecutor:40-60" _, err := executorExec(executor, "DELETE FROM sharded_user_msgs LIMIT 1000", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) sql := "DELETE FROM sharded_user_msgs LIMIT 1000" wantQueries := []*querypb.BoundQuery{{ Sql: sql + "/* vtgate:: filtered_replication_unfriendly */", @@ -1708,9 +1622,7 @@ func TestKeyShardDestQuery(t *testing.T) { masterSession.TargetString = "TestExecutor:40-60" _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) testQueries(t, "sbc2", sbc2, wantQueries) @@ -1725,9 +1637,7 @@ func TestKeyShardDestQuery(t *testing.T) { }} _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(sbc1.Queries) != 0 { t.Errorf("sbc1.Queries: %+v, want %+v\n", sbc1.Queries, []*querypb.BoundQuery{}) @@ -1748,9 +1658,7 @@ func TestKeyShardDestQuery(t *testing.T) { _, err = executorExec(executor, sql, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(sbc1.Queries) != 0 { t.Errorf("sbc1.Queries: %+v, want %+v\n", sbc1.Queries, []*querypb.BoundQuery{}) @@ -1770,9 +1678,7 @@ func TestKeyShardDestQuery(t *testing.T) { Sql: sql + "/* vtgate:: filtered_replication_unfriendly */", BindVariables: map[string]*querypb.BindVariable{}, }} - if err != nil { - t.Error(err) - } + require.NoError(t, err) if len(sbc1.Queries) != 0 { t.Errorf("sbc1.Queries: %+v, want %+v\n", sbc1.Queries, []*querypb.BoundQuery{}) @@ -1796,9 +1702,7 @@ func TestUpdateEqualWithPrepare(t *testing.T) { "a0": sqltypes.Int64BindVariable(3), "id0": sqltypes.Int64BindVariable(2), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) var wantQueries []*querypb.BoundQuery @@ -1823,9 +1727,7 @@ func TestInsertShardedWithPrepare(t *testing.T) { "_name0": sqltypes.BytesBindVariable([]byte("myname")), "__seq0": sqltypes.Int64BindVariable(1), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) var wantQueries []*querypb.BoundQuery @@ -1846,9 +1748,7 @@ func TestDeleteEqualWithPrepare(t *testing.T) { _, err := executorPrepare(executor, "delete from user where id = :id0", map[string]*querypb.BindVariable{ "id0": sqltypes.Int64BindVariable(1), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) var wantQueries []*querypb.BoundQuery diff --git a/go/vt/vtgate/executor_stream_test.go b/go/vt/vtgate/executor_stream_test.go index 65c2e773705..4ed3cf526a8 100644 --- a/go/vt/vtgate/executor_stream_test.go +++ b/go/vt/vtgate/executor_stream_test.go @@ -20,6 +20,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/discovery" @@ -36,9 +37,7 @@ func TestStreamSQLUnsharded(t *testing.T) { sql := "stream * from user_msgs" result, err := executorStreamMessages(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := sandboxconn.StreamRowResult if !result.Equal(wantResult) { t.Errorf("result: %+v, want %+v", result, wantResult) @@ -62,9 +61,7 @@ func TestStreamSQLSharded(t *testing.T) { sql := "stream * from sharded_user_msgs" result, err := executorStreamMessages(executor, sql) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := &sqltypes.Result{ Fields: sandboxconn.SingleRowResult.Fields, Rows: [][]sqltypes.Value{ diff --git a/go/vt/vtgate/vindexes/binary_test.go b/go/vt/vtgate/vindexes/binary_test.go index a9c4554e12f..9ca5c57a4a1 100644 --- a/go/vt/vtgate/vindexes/binary_test.go +++ b/go/vt/vtgate/vindexes/binary_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" ) @@ -78,9 +79,7 @@ func TestBinaryVerify(t *testing.T) { func TestBinaryReverseMap(t *testing.T) { got, err := binOnlyVindex.(Reversible).ReverseMap(nil, [][]byte{[]byte("\x00\x00\x00\x00\x00\x00\x00\x01")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []sqltypes.Value{sqltypes.NewVarBinary("\x00\x00\x00\x00\x00\x00\x00\x01")} if !reflect.DeepEqual(got, want) { t.Errorf("ReverseMap(): %+v, want %+v", got, want) diff --git a/go/vt/vtgate/vindexes/binarymd5_test.go b/go/vt/vtgate/vindexes/binarymd5_test.go index 51d557003af..7e214d8862d 100644 --- a/go/vt/vtgate/vindexes/binarymd5_test.go +++ b/go/vt/vtgate/vindexes/binarymd5_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" ) @@ -80,9 +81,7 @@ func TestBinaryMD5Verify(t *testing.T) { func TestSQLValue(t *testing.T) { val := sqltypes.NewVarBinary("Test") got, err := binVindex.Map(nil, []sqltypes.Value{val}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) out := string(got[0].(key.DestinationKeyspaceID)) want := "\f\xbcf\x11\xf5T\vЀ\x9a8\x8d\xc9Za[" if out != want { diff --git a/go/vt/vtgate/vindexes/consistent_lookup_test.go b/go/vt/vtgate/vindexes/consistent_lookup_test.go index 0125c9ea06c..54a41c005e7 100644 --- a/go/vt/vtgate/vindexes/consistent_lookup_test.go +++ b/go/vt/vtgate/vindexes/consistent_lookup_test.go @@ -26,6 +26,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" querypb "vitess.io/vitess/go/vt/proto/query" @@ -72,9 +73,7 @@ func TestConsistentLookupMap(t *testing.T) { vc.AddResult(makeTestResult(2), nil) got, err := lookup.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ []byte("1"), @@ -106,9 +105,7 @@ func TestConsistentLookupMapWriteOnly(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup", true) got, err := lookup.Map(nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -129,9 +126,7 @@ func TestConsistentLookupUniqueMap(t *testing.T) { vc.AddResult(makeTestResult(1), nil) got, err := lookup.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationNone{}, key.DestinationKeyspaceID([]byte("1")), @@ -157,9 +152,7 @@ func TestConsistentLookupUniqueMapWriteOnly(t *testing.T) { lookup := createConsistentLookup(t, "consistent_lookup_unique", true) got, err := lookup.Map(nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -180,9 +173,7 @@ func TestConsistentLookupMapAbsent(t *testing.T) { vc.AddResult(makeTestResult(0), nil) got, err := lookup.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationNone{}, key.DestinationNone{}, @@ -203,9 +194,7 @@ func TestConsistentLookupVerify(t *testing.T) { vc.AddResult(makeTestResult(1), nil) _, err := lookup.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("test1"), []byte("test2")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) vc.verifyLog(t, []string{ "ExecutePre select fromc1 from t where fromc1 = :fromc1 and toc = :toc [{fromc1 1} {toc test1}] false", "ExecutePre select fromc1 from t where fromc1 = :fromc1 and toc = :toc [{fromc1 2} {toc test2}] false", @@ -222,9 +211,7 @@ func TestConsistentLookupVerify(t *testing.T) { // Test write_only. lookup = createConsistentLookup(t, "consistent_lookup", true) got, err := lookup.Verify(nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantBools := []bool{true, true} if !reflect.DeepEqual(got, wantBools) { t.Errorf("lookup.Verify(writeOnly): %v, want %v", got, wantBools) diff --git a/go/vt/vtgate/vindexes/hash_test.go b/go/vt/vtgate/vindexes/hash_test.go index 9550ce9535b..601cad26f93 100644 --- a/go/vt/vtgate/vindexes/hash_test.go +++ b/go/vt/vtgate/vindexes/hash_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" ) @@ -58,9 +59,7 @@ func TestHashMap(t *testing.T) { sqltypes.NewUint64(9223372036854775807), // 2^63 - 1 sqltypes.NewInt64(-9223372036854775808), // - 2^63 }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceID([]byte("\x16k@\xb4J\xbaK\xd6")), key.DestinationKeyspaceID([]byte("\x06\xe7\xea\"Βp\x8f")), @@ -120,9 +119,7 @@ func TestHashReverseMap(t *testing.T) { []byte("\xf7}H\xaaݡ\xf1\xbb"), []byte("\x95\xf8\xa5\xe5\xdd1\xd9\x00"), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) neg1 := int64(-1) negmax := int64(-9223372036854775808) want := []sqltypes.Value{ diff --git a/go/vt/vtgate/vindexes/lookup_hash_test.go b/go/vt/vtgate/vindexes/lookup_hash_test.go index 076b17d49e8..ba6ff9223ed 100644 --- a/go/vt/vtgate/vindexes/lookup_hash_test.go +++ b/go/vt/vtgate/vindexes/lookup_hash_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -68,9 +69,7 @@ func TestLookupHashMap(t *testing.T) { vc := &vcursor{numRows: 2} got, err := lookuphash.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ []byte("\x16k@\xb4J\xbaK\xd6"), @@ -91,9 +90,7 @@ func TestLookupHashMap(t *testing.T) { "notint", ) got, err = lookuphash.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []key.Destination{key.DestinationKeyspaceIDs([][]byte{})} if !reflect.DeepEqual(got, want) { t.Errorf("Map(): %#v, want %#v", got, want) @@ -114,9 +111,7 @@ func TestLookupHashMapAbsent(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lookuphash.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationNone{}, key.DestinationNone{}, @@ -128,9 +123,7 @@ func TestLookupHashMapAbsent(t *testing.T) { // writeOnly true should return full keyranges. lookuphash = createLookup(t, "lookup_hash", true) got, err = lookuphash.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -149,9 +142,7 @@ func TestLookupHashMapNull(t *testing.T) { vc := &vcursor{numRows: 1} got, err := lookuphash.Map(vc, []sqltypes.Value{sqltypes.NULL}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ []byte("\x16k@\xb4J\xbaK\xd6"), @@ -164,9 +155,7 @@ func TestLookupHashMapNull(t *testing.T) { // writeOnly true should return full keyranges. lookuphash = createLookup(t, "lookup_hash", true) got, err = lookuphash.Map(vc, []sqltypes.Value{sqltypes.NULL}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -186,9 +175,7 @@ func TestLookupHashVerify(t *testing.T) { got, err := lookuphash.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []bool{true, true} if !reflect.DeepEqual(got, want) { t.Errorf("lookuphash.Verify(match): %v, want %v", got, want) @@ -196,9 +183,7 @@ func TestLookupHashVerify(t *testing.T) { vc.numRows = 0 got, err = lookuphash.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []bool{false} if !reflect.DeepEqual(got, want) { t.Errorf("lookuphash.Verify(mismatch): %v, want %v", got, want) @@ -215,9 +200,7 @@ func TestLookupHashVerify(t *testing.T) { vc.queries = nil got, err = lookuphash.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if vc.queries != nil { t.Errorf("lookuphash.Verify(scatter), queries: %v, want nil", vc.queries) } @@ -232,18 +215,14 @@ func TestLookupHashCreate(t *testing.T) { vc := &vcursor{} err := lookuphash.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } vc.queries = nil err = lookuphash.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NULL}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } @@ -260,18 +239,14 @@ func TestLookupHashDelete(t *testing.T) { vc := &vcursor{} err := lookuphash.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, []byte("\x16k@\xb4J\xbaK\xd6")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } vc.queries = nil err = lookuphash.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NULL}}, []byte("\x16k@\xb4J\xbaK\xd6")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } @@ -288,18 +263,14 @@ func TestLookupHashUpdate(t *testing.T) { vc := &vcursor{} err := lookuphash.(Lookup).Update(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 2; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } vc.queries = nil err = lookuphash.(Lookup).Update(vc, []sqltypes.Value{sqltypes.NULL}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 2; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } diff --git a/go/vt/vtgate/vindexes/lookup_hash_unique_test.go b/go/vt/vtgate/vindexes/lookup_hash_unique_test.go index 445f12fde76..d5031717fba 100644 --- a/go/vt/vtgate/vindexes/lookup_hash_unique_test.go +++ b/go/vt/vtgate/vindexes/lookup_hash_unique_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -68,9 +69,7 @@ func TestLookupHashUniqueMap(t *testing.T) { vc := &vcursor{numRows: 1} got, err := lhu.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceID([]byte("\x16k@\xb4J\xbaK\xd6")), key.DestinationKeyspaceID([]byte("\x16k@\xb4J\xbaK\xd6")), @@ -81,9 +80,7 @@ func TestLookupHashUniqueMap(t *testing.T) { vc.numRows = 0 got, err = lhu.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []key.Destination{ key.DestinationNone{}, key.DestinationNone{}, @@ -105,9 +102,7 @@ func TestLookupHashUniqueMap(t *testing.T) { "notint", ) got, err = lhu.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []key.Destination{ key.DestinationNone{}, } @@ -130,9 +125,7 @@ func TestLookupHashUniqueMapWriteOnly(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lhu.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -155,9 +148,7 @@ func TestLookupHashUniqueVerify(t *testing.T) { got, err := lhu.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []bool{true, true} if !reflect.DeepEqual(got, want) { t.Errorf("lhu.Verify(match): %v, want %v", got, want) @@ -165,9 +156,7 @@ func TestLookupHashUniqueVerify(t *testing.T) { vc.numRows = 0 got, err = lhu.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []bool{false} if !reflect.DeepEqual(got, want) { t.Errorf("lhu.Verify(mismatch): %v, want %v", got, want) @@ -185,9 +174,7 @@ func TestLookupHashUniqueVerifyWriteOnly(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lhu.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("test")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []bool{true} if !reflect.DeepEqual(got, want) { t.Errorf("lhu.Verify: %v, want %v", got, want) @@ -202,9 +189,7 @@ func TestLookupHashUniqueCreate(t *testing.T) { vc := &vcursor{} err := lhu.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } @@ -221,9 +206,7 @@ func TestLookupHashUniqueDelete(t *testing.T) { vc := &vcursor{} err := lhu.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, []byte("\x16k@\xb4J\xbaK\xd6")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } @@ -240,9 +223,7 @@ func TestLookupHashUniqueUpdate(t *testing.T) { vc := &vcursor{} err := lhu.(Lookup).Update(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 2; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } diff --git a/go/vt/vtgate/vindexes/lookup_test.go b/go/vt/vtgate/vindexes/lookup_test.go index 5393af37586..961a63725a9 100644 --- a/go/vt/vtgate/vindexes/lookup_test.go +++ b/go/vt/vtgate/vindexes/lookup_test.go @@ -24,6 +24,7 @@ import ( "strings" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" @@ -137,9 +138,7 @@ func TestLookupNonUniqueMap(t *testing.T) { vc := &vcursor{numRows: 2} got, err := lookupNonUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ []byte("1"), @@ -193,9 +192,7 @@ func TestLookupNonUniqueMapAutocommit(t *testing.T) { vc := &vcursor{numRows: 2} got, err := lookupNonUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ []byte("1"), @@ -235,9 +232,7 @@ func TestLookupNonUniqueMapWriteOnly(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lookupNonUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -256,9 +251,7 @@ func TestLookupNonUniqueMapAbsent(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lookupNonUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationNone{}, key.DestinationNone{}, @@ -273,9 +266,7 @@ func TestLookupNonUniqueVerify(t *testing.T) { vc := &vcursor{numRows: 1} _, err := lookupNonUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("test1"), []byte("test2")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "select fromc from t where fromc = :fromc and toc = :toc", @@ -308,9 +299,7 @@ func TestLookupNonUniqueVerify(t *testing.T) { vc.queries = nil got, err := lookupNonUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if vc.queries != nil { t.Errorf("lookup.Verify(writeOnly), queries: %v, want nil", vc.queries) } @@ -334,9 +323,7 @@ func TestLookupNonUniqueVerifyAutocommit(t *testing.T) { vc := &vcursor{numRows: 1} _, err = lookupNonUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("test1"), []byte("test2")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "select fromc from t where fromc = :fromc and toc = :toc", @@ -365,9 +352,7 @@ func TestLookupNonUniqueCreate(t *testing.T) { vc := &vcursor{} err := lookupNonUnique.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}, {sqltypes.NewInt64(2)}}, [][]byte{[]byte("test1"), []byte("test2")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "insert into t(fromc, toc) values(:fromc0, :toc0), (:fromc1, :toc1)", @@ -385,9 +370,7 @@ func TestLookupNonUniqueCreate(t *testing.T) { // With ignore. vc.queries = nil err = lookupNonUnique.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(2)}, {sqltypes.NewInt64(1)}}, [][]byte{[]byte("test2"), []byte("test1")}, true /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries[0].Sql = "insert ignore into t(fromc, toc) values(:fromc0, :toc0), (:fromc1, :toc1)" if !reflect.DeepEqual(vc.queries, wantqueries) { @@ -432,9 +415,7 @@ func TestLookupNonUniqueCreateAutocommit(t *testing.T) { }}, [][]byte{[]byte("test1"), []byte("test2")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "insert into t(from1, from2, toc) values(:from10, :from20, :toc0), (:from11, :from21, :toc1) on duplicate key update from1=values(from1), from2=values(from2), toc=values(toc)", @@ -461,9 +442,7 @@ func TestLookupNonUniqueDelete(t *testing.T) { vc := &vcursor{} err := lookupNonUnique.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}, {sqltypes.NewInt64(2)}}, []byte("test")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "delete from t where fromc = :fromc and toc = :toc", @@ -509,9 +488,7 @@ func TestLookupNonUniqueDeleteAutocommit(t *testing.T) { vc := &vcursor{} err := lookupNonUnique.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}, {sqltypes.NewInt64(2)}}, []byte("test")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery(nil) if !reflect.DeepEqual(vc.queries, wantqueries) { @@ -524,9 +501,7 @@ func TestLookupNonUniqueUpdate(t *testing.T) { vc := &vcursor{} err := lookupNonUnique.(Lookup).Update(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, []byte("test"), []sqltypes.Value{sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "delete from t where fromc = :fromc and toc = :toc", diff --git a/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go b/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go index abe193974e7..3ed75447d3e 100644 --- a/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go +++ b/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_hash_test.go @@ -20,6 +20,7 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" @@ -39,9 +40,7 @@ func TestLookupUnicodeLooseMD5HashMap(t *testing.T) { vc := &vcursor{numRows: 2} got, err := lookup.Map(vc, []sqltypes.Value{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ []byte("\x16k@\xb4J\xbaK\xd6"), @@ -96,9 +95,7 @@ func TestLookupUnicodeLooseMD5HashMapAutocommit(t *testing.T) { vc := &vcursor{numRows: 2} got, err := lookupNonUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceIDs([][]byte{ []byte("\x16k@\xb4J\xbaK\xd6"), @@ -138,9 +135,7 @@ func TestLookupUnicodeLooseMD5HashMapWriteOnly(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lookupNonUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -159,9 +154,7 @@ func TestLookupUnicodeLooseMD5HashMapAbsent(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lookupNonUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationNone{}, key.DestinationNone{}, @@ -178,9 +171,7 @@ func TestLookupUnicodeLooseMD5HashVerify(t *testing.T) { got, err := lookupNonUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantResult := []bool{true, true} if !reflect.DeepEqual(got, wantResult) { t.Errorf("lookuphash.Verify(match): %v, want %v", got, wantResult) @@ -217,9 +208,7 @@ func TestLookupUnicodeLooseMD5HashVerify(t *testing.T) { vc.queries = nil got, err = lookupNonUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}, [][]byte{[]byte(""), []byte("")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if vc.queries != nil { t.Errorf("lookup.Verify(writeOnly), queries: %v, want nil", vc.queries) } @@ -244,9 +233,7 @@ func TestLookupUnicodeLooseMD5HashVerifyAutocommit(t *testing.T) { _, err = lookupNonUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(10), sqltypes.NewInt64(20)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "select fromc from t where fromc = :fromc and toc = :toc", @@ -276,9 +263,7 @@ func TestLookupUnicodeLooseMD5HashCreate(t *testing.T) { err := lookupNonUnique.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(10)}, {sqltypes.NewInt64(20)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "insert into t(fromc, toc) values(:fromc0, :toc0), (:fromc1, :toc1)", @@ -297,9 +282,7 @@ func TestLookupUnicodeLooseMD5HashCreate(t *testing.T) { vc.queries = nil err = lookupNonUnique.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(10)}, {sqltypes.NewInt64(20)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}, true /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries[0].Sql = "insert ignore into t(fromc, toc) values(:fromc0, :toc0), (:fromc1, :toc1)" if !reflect.DeepEqual(vc.queries, wantqueries) { @@ -344,9 +327,7 @@ func TestLookupUnicodeLooseMD5HashCreateAutocommit(t *testing.T) { }}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "insert into t(from1, from2, toc) values(:from10, :from20, :toc0), (:from11, :from21, :toc1) on duplicate key update from1=values(from1), from2=values(from2), toc=values(toc)", @@ -373,9 +354,7 @@ func TestLookupUnicodeLooseMD5HashDelete(t *testing.T) { vc := &vcursor{} err := lookupNonUnique.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NewInt64(10)}, {sqltypes.NewInt64(20)}}, []byte("\x16k@\xb4J\xbaK\xd6")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "delete from t where fromc = :fromc and toc = :toc", @@ -421,9 +400,7 @@ func TestLookupUnicodeLooseMD5HashDeleteAutocommit(t *testing.T) { vc := &vcursor{} err := lookupNonUnique.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NewInt64(10)}, {sqltypes.NewInt64(20)}}, []byte("\x16k@\xb4J\xbaK\xd6")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery(nil) if !reflect.DeepEqual(vc.queries, wantqueries) { @@ -436,9 +413,7 @@ func TestLookupUnicodeLooseMD5HashUpdate(t *testing.T) { vc := &vcursor{} err := lookupNonUnique.(Lookup).Update(vc, []sqltypes.Value{sqltypes.NewInt64(10)}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(20)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "delete from t where fromc = :fromc and toc = :toc", diff --git a/go/vt/vtgate/vindexes/lookup_unique_test.go b/go/vt/vtgate/vindexes/lookup_unique_test.go index 9165bab6c7a..2f1b5dec60b 100644 --- a/go/vt/vtgate/vindexes/lookup_unique_test.go +++ b/go/vt/vtgate/vindexes/lookup_unique_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" querypb "vitess.io/vitess/go/vt/proto/query" @@ -68,9 +69,7 @@ func TestLookupUniqueMap(t *testing.T) { vc := &vcursor{numRows: 1} got, err := lookupUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceID([]byte("1")), key.DestinationKeyspaceID([]byte("1")), @@ -81,9 +80,7 @@ func TestLookupUniqueMap(t *testing.T) { vc.numRows = 0 got, err = lookupUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = []key.Destination{ key.DestinationNone{}, key.DestinationNone{}, @@ -114,9 +111,7 @@ func TestLookupUniqueMapWriteOnly(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lookupUnique.Map(vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyRange{ KeyRange: &topodatapb.KeyRange{}, @@ -135,9 +130,7 @@ func TestLookupUniqueVerify(t *testing.T) { vc := &vcursor{numRows: 1} _, err := lookupUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("test")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } @@ -148,9 +141,7 @@ func TestLookupUniqueVerifyWriteOnly(t *testing.T) { vc := &vcursor{numRows: 0} got, err := lookupUnique.Verify(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("test")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []bool{true} if !reflect.DeepEqual(got, want) { t.Errorf("lookupUnique.Verify: %v, want %v", got, want) @@ -173,9 +164,7 @@ func TestLookupUniqueCreate(t *testing.T) { vc := &vcursor{} err = lookupUnique.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("test")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantqueries := []*querypb.BoundQuery{{ Sql: "insert into t(from, toc) values(:from0, :toc0)", @@ -198,9 +187,7 @@ func TestLookupUniqueCreateAutocommit(t *testing.T) { vc := &vcursor{} err := lookupUnique.(Lookup).Create(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("test")}, false /* ignoreMode */) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } @@ -211,9 +198,7 @@ func TestLookupUniqueDelete(t *testing.T) { vc := &vcursor{} err := lookupUnique.(Lookup).Delete(vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, []byte("test")) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 1; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } @@ -224,9 +209,7 @@ func TestLookupUniqueUpdate(t *testing.T) { vc := &vcursor{} err := lookupUnique.(Lookup).Update(vc, []sqltypes.Value{sqltypes.NewInt64(1)}, []byte("test"), []sqltypes.Value{sqltypes.NewInt64(2)}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got, want := len(vc.queries), 2; got != want { t.Errorf("vc.queries length: %v, want %v", got, want) } diff --git a/go/vt/vtgate/vindexes/null_test.go b/go/vt/vtgate/vindexes/null_test.go index 8210c3ae805..a357aba1dd7 100644 --- a/go/vt/vtgate/vindexes/null_test.go +++ b/go/vt/vtgate/vindexes/null_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" ) @@ -51,9 +52,7 @@ func TestNullMap(t *testing.T) { sqltypes.NewInt64(5), sqltypes.NewInt64(6), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceID([]byte{0}), key.DestinationKeyspaceID([]byte{0}), diff --git a/go/vt/vtgate/vindexes/numeric_static_map_test.go b/go/vt/vtgate/vindexes/numeric_static_map_test.go index bd54cef1c61..ad8a1ce5132 100644 --- a/go/vt/vtgate/vindexes/numeric_static_map_test.go +++ b/go/vt/vtgate/vindexes/numeric_static_map_test.go @@ -63,9 +63,7 @@ func TestNumericStaticMapMap(t *testing.T) { sqltypes.NewInt64(7), sqltypes.NewInt64(8), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // in the third slice, we expect 2 instead of 3 as numeric_static_map_test.json // has 3 mapped to 2 @@ -93,9 +91,7 @@ func TestNumericStaticMapVerify(t *testing.T) { got, err := numericStaticMap.Verify(nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("\x00\x00\x00\x00\x00\x00\x00\x01"), []byte("\x00\x00\x00\x00\x00\x00\x00\x01")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []bool{true, false} if !reflect.DeepEqual(got, want) { t.Errorf("lhu.Verify(match): %v, want %v", got, want) diff --git a/go/vt/vtgate/vindexes/numeric_test.go b/go/vt/vtgate/vindexes/numeric_test.go index 326d23dcf13..57dd11b68b9 100644 --- a/go/vt/vtgate/vindexes/numeric_test.go +++ b/go/vt/vtgate/vindexes/numeric_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" ) @@ -51,9 +52,7 @@ func TestNumericMap(t *testing.T) { sqltypes.NewInt64(7), sqltypes.NewInt64(8), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceID([]byte("\x00\x00\x00\x00\x00\x00\x00\x01")), key.DestinationKeyspaceID([]byte("\x00\x00\x00\x00\x00\x00\x00\x02")), @@ -74,9 +73,7 @@ func TestNumericVerify(t *testing.T) { got, err := numeric.Verify(nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("\x00\x00\x00\x00\x00\x00\x00\x01"), []byte("\x00\x00\x00\x00\x00\x00\x00\x01")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []bool{true, false} if !reflect.DeepEqual(got, want) { t.Errorf("lhu.Verify(match): %v, want %v", got, want) @@ -92,9 +89,7 @@ func TestNumericVerify(t *testing.T) { func TestNumericReverseMap(t *testing.T) { got, err := numeric.(Reversible).ReverseMap(nil, [][]byte{[]byte("\x00\x00\x00\x00\x00\x00\x00\x01")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []sqltypes.Value{sqltypes.NewUint64(1)} if !reflect.DeepEqual(got, want) { t.Errorf("ReverseMap(): %v, want %v", got, want) diff --git a/go/vt/vtgate/vindexes/reverse_bits_test.go b/go/vt/vtgate/vindexes/reverse_bits_test.go index 4cc12cbb962..2da36546f43 100644 --- a/go/vt/vtgate/vindexes/reverse_bits_test.go +++ b/go/vt/vtgate/vindexes/reverse_bits_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" ) @@ -52,9 +53,7 @@ func TestReverseBitsMap(t *testing.T) { sqltypes.NewInt64(5), sqltypes.NewInt64(6), }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []key.Destination{ key.DestinationKeyspaceID([]byte("\x80\x00\x00\x00\x00\x00\x00\x00")), key.DestinationKeyspaceID([]byte("@\x00\x00\x00\x00\x00\x00\x00")), @@ -91,9 +90,7 @@ func TestReverseBitsVerify(t *testing.T) { func TestReverseBitsReverseMap(t *testing.T) { got, err := reverseBits.(Reversible).ReverseMap(nil, [][]byte{[]byte("\x80\x00\x00\x00\x00\x00\x00\x00")}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []sqltypes.Value{sqltypes.NewUint64(uint64(1))} if !reflect.DeepEqual(got, want) { t.Errorf("ReverseMap(): %v, want %v", got, want) diff --git a/go/vt/vtgate/vindexes/vschema_test.go b/go/vt/vtgate/vindexes/vschema_test.go index 184a1eb5222..b90250cc522 100644 --- a/go/vt/vtgate/vindexes/vschema_test.go +++ b/go/vt/vtgate/vindexes/vschema_test.go @@ -207,9 +207,7 @@ func TestUnshardedVSchema(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["unsharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "unsharded", } @@ -264,9 +262,7 @@ func TestVSchemaColumns(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["unsharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "unsharded", } @@ -330,9 +326,7 @@ func TestVSchemaColumnListAuthoritative(t *testing.T) { }, } got, err := BuildVSchema(&good) - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "unsharded", } @@ -417,9 +411,7 @@ func TestVSchemaPinned(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["sharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "sharded", Sharded: true, @@ -495,9 +487,7 @@ func TestShardedVSchemaOwned(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["sharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "sharded", Sharded: true, @@ -625,9 +615,7 @@ func TestShardedVSchemaOwnerInfo(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["sharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) results := []struct { name string keyspace string @@ -1062,9 +1050,7 @@ func TestFindVindexForSharding(t *testing.T) { }, } res, err := FindVindexForSharding(t1.Name.String(), t1.ColumnVindexes) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(res, t1.ColumnVindexes[0]) { t.Errorf("FindVindexForSharding:\n got\n%v, want\n%v", res, t1.ColumnVindexes[0]) } @@ -1138,9 +1124,7 @@ func TestFindVindexForSharding2(t *testing.T) { }, } res, err := FindVindexForSharding(t1.Name.String(), t1.ColumnVindexes) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !reflect.DeepEqual(res, t1.ColumnVindexes[1]) { t.Errorf("FindVindexForSharding:\n got\n%v, want\n%v", res, t1.ColumnVindexes[1]) } @@ -1175,9 +1159,7 @@ func TestShardedVSchemaMultiColumnVindex(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["sharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "sharded", Sharded: true, @@ -1270,9 +1252,7 @@ func TestShardedVSchemaNotOwned(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["sharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "sharded", Sharded: true, @@ -1592,9 +1572,7 @@ func TestBuildVSchemaDupVindex(t *testing.T) { got, _ := BuildVSchema(&good) err := got.Keyspaces["ksa"].Error err1 := got.Keyspaces["ksb"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) if err1 != nil { t.Error(err1) } @@ -1895,9 +1873,7 @@ func TestSequence(t *testing.T) { } got, _ := BuildVSchema(&good) err := got.Keyspaces["sharded"].Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) err1 := got.Keyspaces["unsharded"].Error if err1 != nil { t.Error(err1) @@ -2392,9 +2368,7 @@ func TestBuildKeyspaceSchema(t *testing.T) { } got, _ := BuildKeyspaceSchema(good, "ks") err := got.Error - if err != nil { - t.Error(err) - } + require.NoError(t, err) ks := &Keyspace{ Name: "ks", } @@ -2434,9 +2408,7 @@ func TestValidate(t *testing.T) { }, } err := ValidateKeyspace(good) - if err != nil { - t.Error(err) - } + require.NoError(t, err) bad := &vschemapb.Keyspace{ Sharded: true, Vindexes: map[string]*vschemapb.Vindex{ diff --git a/go/vt/vtgate/vtgateconntest/client.go b/go/vt/vtgate/vtgateconntest/client.go index f6f0ab9325c..9908dfd33cd 100644 --- a/go/vt/vtgate/vtgateconntest/client.go +++ b/go/vt/vtgate/vtgateconntest/client.go @@ -27,6 +27,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" @@ -1126,18 +1127,14 @@ func verifyErrorString(t *testing.T, err error, method string) { func testBegin(t *testing.T, conn *vtgateconn.VTGateConn) { _, err := conn.Begin(newContext()) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func testExecute(t *testing.T, session *vtgateconn.VTGateSession) { ctx := newContext() execCase := execMap["request1"] qr, err := session.Execute(ctx, execCase.execQuery.SQL, execCase.execQuery.BindVariables) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !qr.Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got\n%#v want\n%#v", qr, execCase.result) } @@ -1168,9 +1165,7 @@ func testExecuteBatch(t *testing.T, session *vtgateconn.VTGateSession) { ctx := newContext() execCase := execMap["request1"] qr, err := session.ExecuteBatch(ctx, []string{execCase.execQuery.SQL}, []map[string]*querypb.BindVariable{execCase.execQuery.BindVariables}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !qr[0].QueryResult.Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got\n%#v want\n%#v", qr, execCase.result) } @@ -1201,9 +1196,7 @@ func testExecuteShards(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() execCase := execMap["request1"] qr, err := conn.ExecuteShards(ctx, execCase.shardQuery.SQL, execCase.shardQuery.Keyspace, execCase.shardQuery.Shards, execCase.shardQuery.BindVariables, execCase.shardQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !qr.Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got %+v want %+v", qr, execCase.result) } @@ -1234,9 +1227,7 @@ func testExecuteKeyspaceIds(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() execCase := execMap["request1"] qr, err := conn.ExecuteKeyspaceIds(ctx, execCase.keyspaceIDQuery.SQL, execCase.keyspaceIDQuery.Keyspace, execCase.keyspaceIDQuery.KeyspaceIds, execCase.keyspaceIDQuery.BindVariables, execCase.keyspaceIDQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !qr.Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got %+v want %+v", qr, execCase.result) } @@ -1267,9 +1258,7 @@ func testExecuteKeyRanges(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() execCase := execMap["request1"] qr, err := conn.ExecuteKeyRanges(ctx, execCase.keyRangeQuery.SQL, execCase.keyRangeQuery.Keyspace, execCase.keyRangeQuery.KeyRanges, execCase.keyRangeQuery.BindVariables, execCase.keyRangeQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !qr.Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got %+v want %+v", qr, execCase.result) } @@ -1300,9 +1289,7 @@ func testExecuteEntityIds(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() execCase := execMap["request1"] qr, err := conn.ExecuteEntityIds(ctx, execCase.entityIdsQuery.SQL, execCase.entityIdsQuery.Keyspace, execCase.entityIdsQuery.EntityColumnName, execCase.entityIdsQuery.EntityKeyspaceIDs, execCase.entityIdsQuery.BindVariables, execCase.entityIdsQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !qr.Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got %+v want %+v", qr, execCase.result) } @@ -1333,9 +1320,7 @@ func testExecuteBatchShards(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() execCase := execMap["request1"] ql, err := conn.ExecuteBatchShards(ctx, execCase.batchQueryShard.Queries, execCase.batchQueryShard.TabletType, execCase.batchQueryShard.AsTransaction, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !ql[0].Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got %+v want %+v", ql, execCase.result) } @@ -1368,9 +1353,7 @@ func testExecuteBatchKeyspaceIds(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() execCase := execMap["request1"] ql, err := conn.ExecuteBatchKeyspaceIds(ctx, execCase.keyspaceIDBatchQuery.Queries, execCase.keyspaceIDBatchQuery.TabletType, execCase.batchQueryShard.AsTransaction, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !ql[0].Equal(execCase.result) { t.Errorf("Unexpected result from Execute: got %+v want %+v", ql, execCase.result) } @@ -1729,87 +1712,51 @@ func testTxPass(t *testing.T, conn *vtgateconn.VTGateConn) { // ExecuteShards tx, err := conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = tx.ExecuteShards(ctx, execCase.shardQuery.SQL, execCase.shardQuery.Keyspace, execCase.shardQuery.Shards, execCase.shardQuery.BindVariables, execCase.shardQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // ExecuteKeyspaceIds tx, err = conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = tx.ExecuteKeyspaceIds(ctx, execCase.keyspaceIDQuery.SQL, execCase.keyspaceIDQuery.Keyspace, execCase.keyspaceIDQuery.KeyspaceIds, execCase.keyspaceIDQuery.BindVariables, execCase.keyspaceIDQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // ExecuteKeyRanges tx, err = conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = tx.ExecuteKeyRanges(ctx, execCase.keyRangeQuery.SQL, execCase.keyRangeQuery.Keyspace, execCase.keyRangeQuery.KeyRanges, execCase.keyRangeQuery.BindVariables, execCase.keyRangeQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // ExecuteEntityIds tx, err = conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = tx.ExecuteEntityIds(ctx, execCase.entityIdsQuery.SQL, execCase.entityIdsQuery.Keyspace, execCase.entityIdsQuery.EntityColumnName, execCase.entityIdsQuery.EntityKeyspaceIDs, execCase.entityIdsQuery.BindVariables, execCase.entityIdsQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // ExecuteBatchShards tx, err = conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = tx.ExecuteBatchShards(ctx, execCase.batchQueryShard.Queries, execCase.batchQueryShard.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // ExecuteBatchKeyspaceIds tx, err = conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = tx.ExecuteBatchKeyspaceIds(ctx, execCase.keyspaceIDBatchQuery.Queries, execCase.keyspaceIDBatchQuery.TabletType, testExecuteOptions) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func testResolveTransaction(t *testing.T, conn *vtgateconn.VTGateConn) { @@ -1831,9 +1778,7 @@ func testCommitError(t *testing.T, conn *vtgateconn.VTGateConn, fake *fakeVTGate tx, err := conn.Begin(ctx) fake.forceBeginSuccess = false - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Commit(ctx) verifyError(t, err, "Commit") } @@ -1845,9 +1790,7 @@ func testRollbackError(t *testing.T, conn *vtgateconn.VTGateConn, fake *fakeVTGa tx, err := conn.Begin(ctx) fake.forceBeginSuccess = false - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) verifyError(t, err, "Rollback") } @@ -1870,9 +1813,7 @@ func testCommitPanic(t *testing.T, conn *vtgateconn.VTGateConn, fake *fakeVTGate tx, err := conn.Begin(ctx) fake.forceBeginSuccess = false - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Commit(ctx) expectPanic(t, err) } @@ -1884,9 +1825,7 @@ func testRollbackPanic(t *testing.T, conn *vtgateconn.VTGateConn, fake *fakeVTGa tx, err := conn.Begin(ctx) fake.forceBeginSuccess = false - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) expectPanic(t, err) } @@ -1899,9 +1838,7 @@ func testResolveTransactionPanic(t *testing.T, conn *vtgateconn.VTGateConn, fake func testTxFail(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() tx, err := conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Commit(ctx) want := "commit: session mismatch" if err == nil || !strings.Contains(err.Error(), want) { @@ -1951,14 +1888,10 @@ func testTxFail(t *testing.T, conn *vtgateconn.VTGateConn) { } err = tx.Rollback(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) tx, err = conn.Begin(ctx) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = tx.Rollback(ctx) want = "rollback: session mismatch" if err == nil || !strings.Contains(err.Error(), want) { @@ -1974,9 +1907,7 @@ func testMessageStream(t *testing.T, conn *vtgateconn.VTGateConn) { } return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func testMessageStreamError(t *testing.T, conn *vtgateconn.VTGateConn) { @@ -2001,9 +1932,7 @@ func testMessageAck(t *testing.T, conn *vtgateconn.VTGateConn) { if got != messageAckRowsAffected { t.Errorf("MessageAck: %d, want %d", got, messageAckRowsAffected) } - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func testMessageAckError(t *testing.T, conn *vtgateconn.VTGateConn) { @@ -2024,9 +1953,7 @@ func testMessageAckKeyspaceIds(t *testing.T, conn *vtgateconn.VTGateConn) { if got != messageAckRowsAffected { t.Errorf("MessageAckKeyspaceIds: %d, want %d", got, messageAckRowsAffected) } - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func testMessageAckKeyspaceIdsError(t *testing.T, conn *vtgateconn.VTGateConn) { diff --git a/go/vt/vttablet/endtoend/message_test.go b/go/vt/vttablet/endtoend/message_test.go index a6d3f768cf4..3dc4b0540ab 100644 --- a/go/vt/vttablet/endtoend/message_test.go +++ b/go/vt/vttablet/endtoend/message_test.go @@ -23,6 +23,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/vttablet/endtoend/framework" @@ -104,9 +105,7 @@ func TestMessage(t *testing.T) { got = <-ch // Check time_scheduled separately. scheduled, err := sqltypes.ToInt64(got.Rows[0][1]) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if now := time.Now().UnixNano(); now-scheduled >= int64(10*time.Second) { t.Errorf("scheduled: %v, must be close to %v", scheduled, now) } @@ -168,9 +167,7 @@ func TestMessage(t *testing.T) { // Ack the message. count, err := client.MessageAck("vitess_message", []string{"1"}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 1 { t.Errorf("count: %d, want 1", count) } @@ -291,9 +288,7 @@ func TestThreeColMessage(t *testing.T) { // Verify Ack. count, err := client.MessageAck("vitess_message3", []string{"1"}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 1 { t.Errorf("count: %d, want 1", count) } @@ -404,9 +399,7 @@ func TestMessageAuto(t *testing.T) { } _, err = client.MessageAck("vitess_message_auto", []string{"1, 2, 5"}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Ensure msg6 is eventually received. got := <-ch @@ -565,15 +558,11 @@ func TestMessageTopic(t *testing.T) { // ack the first subscriber _, err = client.MessageAck("vitess_topic_subscriber_1", []string{"1, 2, 3"}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // ack the second subscriber _, err = client.MessageAck("vitess_topic_subscriber_2", []string{"1, 2, 3"}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // // phase 2 tests deleting one of the subscribers and making sure @@ -658,9 +647,7 @@ func TestMessageTopic(t *testing.T) { // ack the second subscriber _, err = client.MessageAck("vitess_topic_subscriber_2", []string{"4, 5, 6"}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // // phase 3 tests deleting the last subscriber and making sure diff --git a/go/vt/vttablet/endtoend/misc_test.go b/go/vt/vttablet/endtoend/misc_test.go index d35f2f1223f..eac102015d5 100644 --- a/go/vt/vttablet/endtoend/misc_test.go +++ b/go/vt/vttablet/endtoend/misc_test.go @@ -28,6 +28,7 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" @@ -668,9 +669,7 @@ func TestClientFoundRows(t *testing.T) { t.Error(err) } qr, err := client.Execute("update vitess_test set charval='aa' where intval=124", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if qr.RowsAffected != 0 { t.Errorf("Execute(rowsFound==false): %d, want 0", qr.RowsAffected) } @@ -683,9 +682,7 @@ func TestClientFoundRows(t *testing.T) { t.Error(err) } qr, err = client.Execute("update vitess_test set charval='aa' where intval=124", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if qr.RowsAffected != 1 { t.Errorf("Execute(rowsFound==true): %d, want 1", qr.RowsAffected) } @@ -713,9 +710,7 @@ func TestLastInsertId(t *testing.T) { } qr, err := client.Execute("update vitess_autoinc_seq set sequence=last_insert_id(sequence + 1) where name='foo'", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) insID := res.InsertID @@ -724,9 +719,7 @@ func TestLastInsertId(t *testing.T) { } qr, err = client.Execute("select sequence from vitess_autoinc_seq where name = 'foo'", nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantCol := sqltypes.NewUint64(insID + uint64(1)) if !reflect.DeepEqual(qr.Rows[0][0], wantCol) { diff --git a/go/vt/vttablet/endtoend/transaction_test.go b/go/vt/vttablet/endtoend/transaction_test.go index e663fd63f70..eecb7d74526 100644 --- a/go/vt/vttablet/endtoend/transaction_test.go +++ b/go/vt/vttablet/endtoend/transaction_test.go @@ -24,6 +24,7 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/mysql" @@ -551,13 +552,9 @@ func TestMMCommitFlow(t *testing.T) { query := "insert into vitess_test (intval, floatval, charval, binval) " + "values(4, null, null, null)" err := client.Begin(false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = client.Execute(query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = client.CreateTransaction("aa", []*querypb.Target{{ Keyspace: "test1", @@ -566,9 +563,7 @@ func TestMMCommitFlow(t *testing.T) { Keyspace: "test2", Shard: "1", }}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = client.CreateTransaction("aa", []*querypb.Target{}) want := "Duplicate entry" @@ -577,9 +572,7 @@ func TestMMCommitFlow(t *testing.T) { } err = client.StartCommit("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = client.SetRollback("aa", 0) want = "could not transition to ROLLBACK: aa (CallerID: dev)" @@ -588,9 +581,7 @@ func TestMMCommitFlow(t *testing.T) { } info, err := client.ReadTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) info.TimeCreated = 0 wantInfo := &querypb.TransactionMetadata{ Dtid: "aa", @@ -610,14 +601,10 @@ func TestMMCommitFlow(t *testing.T) { } err = client.ConcludeTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) info, err = client.ReadTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) wantInfo = &querypb.TransactionMetadata{} if !proto.Equal(info, wantInfo) { t.Errorf("ReadTransaction: %#v, want %#v", info, wantInfo) @@ -631,13 +618,9 @@ func TestMMRollbackFlow(t *testing.T) { query := "insert into vitess_test (intval, floatval, charval, binval) " + "values(4, null, null, null)" err := client.Begin(false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = client.Execute(query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = client.CreateTransaction("aa", []*querypb.Target{{ Keyspace: "test1", @@ -646,20 +629,14 @@ func TestMMRollbackFlow(t *testing.T) { Keyspace: "test2", Shard: "1", }}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) client.Rollback() err = client.SetRollback("aa", 0) - if err != nil { - t.Error(err) - } + require.NoError(t, err) info, err := client.ReadTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) info.TimeCreated = 0 wantInfo := &querypb.TransactionMetadata{ Dtid: "aa", @@ -679,9 +656,7 @@ func TestMMRollbackFlow(t *testing.T) { } err = client.ConcludeTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestWatchdog(t *testing.T) { @@ -690,13 +665,9 @@ func TestWatchdog(t *testing.T) { query := "insert into vitess_test (intval, floatval, charval, binval) " + "values(4, null, null, null)" err := client.Begin(false) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = client.Execute(query, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) start := time.Now() err = client.CreateTransaction("aa", []*querypb.Target{{ @@ -706,9 +677,7 @@ func TestWatchdog(t *testing.T) { Keyspace: "test2", Shard: "1", }}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // The watchdog should kick in after 1 second. dtid := <-framework.ResolveChan @@ -721,13 +690,9 @@ func TestWatchdog(t *testing.T) { } err = client.SetRollback("aa", 0) - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = client.ConcludeTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Make sure the watchdog stops sending messages. // Check twice. Sometimes, a race can still cause diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index 698380651ad..e345fd0807c 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -24,6 +24,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/mysql/fakesqldb" @@ -214,9 +215,7 @@ func TestGenerateLoadMessagesQuery(t *testing.T) { }, []string{"t1"}, nil, nil) q, err := engine.GenerateLoadMessagesQuery("t1") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := "select time_next, epoch, time_created, id, time_scheduled, message from t1 where :#pk" if q.Query != want { t.Errorf("GenerateLoadMessagesQuery: %s, want %s", q.Query, want) diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go index 3d0ecaf1a3e..020fffc9d0d 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go @@ -29,6 +29,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/tableacl" @@ -187,9 +188,7 @@ func TestDDLPlan(t *testing.T) { func TestMessageStreamingPlan(t *testing.T) { testSchema := loadSchema("schema_test.json") plan, err := BuildMessageStreaming("msg", testSchema) - if err != nil { - t.Error(err) - } + require.NoError(t, err) bout, _ := json.Marshal(plan) planJSON := string(bout) diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index 6e385dcf79f..e985f42c4d9 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -30,6 +30,7 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/mysql" @@ -135,9 +136,7 @@ func TestTabletServerInitDBConfig(t *testing.T) { } tsv.setState(StateNotConnected) err = tsv.InitDBConfig(target, dbcfgs) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestDecideAction(t *testing.T) { @@ -149,24 +148,18 @@ func TestDecideAction(t *testing.T) { target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} dbcfgs := testUtils.newDBConfigs(db) err := tsv.InitDBConfig(target, dbcfgs) - if err != nil { - t.Error(err) - } + require.NoError(t, err) tsv.setState(StateNotConnected) action, err := tsv.decideAction(topodatapb.TabletType_MASTER, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if action != actionNone { t.Errorf("decideAction: %v, want %v", action, actionNone) } tsv.setState(StateNotConnected) action, err = tsv.decideAction(topodatapb.TabletType_MASTER, true, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if action != actionFullStart { t.Errorf("decideAction: %v, want %v", action, actionFullStart) } @@ -176,18 +169,14 @@ func TestDecideAction(t *testing.T) { tsv.setState(StateNotServing) action, err = tsv.decideAction(topodatapb.TabletType_MASTER, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if action != actionNone { t.Errorf("decideAction: %v, want %v", action, actionNone) } tsv.setState(StateNotServing) action, err = tsv.decideAction(topodatapb.TabletType_MASTER, true, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if action != actionServeNewType { t.Errorf("decideAction: %v, want %v", action, actionServeNewType) } @@ -197,9 +186,7 @@ func TestDecideAction(t *testing.T) { tsv.setState(StateServing) action, err = tsv.decideAction(topodatapb.TabletType_MASTER, false, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if action != actionGracefulStop { t.Errorf("decideAction: %v, want %v", action, actionGracefulStop) } @@ -209,9 +196,7 @@ func TestDecideAction(t *testing.T) { tsv.setState(StateServing) action, err = tsv.decideAction(topodatapb.TabletType_REPLICA, true, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if action != actionServeNewType { t.Errorf("decideAction: %v, want %v", action, actionServeNewType) } @@ -222,9 +207,7 @@ func TestDecideAction(t *testing.T) { tsv.setState(StateServing) action, err = tsv.decideAction(topodatapb.TabletType_MASTER, true, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if action != actionNone { t.Errorf("decideAction: %v, want %v", action, actionNone) } @@ -256,44 +239,34 @@ func TestSetServingType(t *testing.T) { dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.InitDBConfig(target, dbcfgs) - if err != nil { - t.Error(err) - } + require.NoError(t, err) stateChanged, err := tsv.SetServingType(topodatapb.TabletType_REPLICA, false, nil) if stateChanged != false { t.Errorf("SetServingType() should NOT have changed the QueryService state, but did") } - if err != nil { - t.Error(err) - } + require.NoError(t, err) checkTabletServerState(t, tsv, StateNotConnected) stateChanged, err = tsv.SetServingType(topodatapb.TabletType_REPLICA, true, nil) if stateChanged != true { t.Errorf("SetServingType() should have changed the QueryService state, but did not") } - if err != nil { - t.Error(err) - } + require.NoError(t, err) checkTabletServerState(t, tsv, StateServing) stateChanged, err = tsv.SetServingType(topodatapb.TabletType_RDONLY, true, nil) if stateChanged != true { t.Errorf("SetServingType() should have changed the tablet type, but did not") } - if err != nil { - t.Error(err) - } + require.NoError(t, err) checkTabletServerState(t, tsv, StateServing) stateChanged, err = tsv.SetServingType(topodatapb.TabletType_SPARE, false, nil) if stateChanged != true { t.Errorf("SetServingType() should have changed the QueryService state, but did not") } - if err != nil { - t.Error(err) - } + require.NoError(t, err) checkTabletServerState(t, tsv, StateNotServing) // Verify that we exit lameduck when SetServingType is called. @@ -305,9 +278,7 @@ func TestSetServingType(t *testing.T) { if stateChanged != true { t.Errorf("SetServingType() should have changed the QueryService state, but did not") } - if err != nil { - t.Error(err) - } + require.NoError(t, err) checkTabletServerState(t, tsv, StateServing) if stateName := tsv.GetState(); stateName != "SERVING" { t.Errorf("GetState: %s, want SERVING", stateName) @@ -449,9 +420,7 @@ func TestTabletServerReconnect(t *testing.T) { t.Fatalf("TabletServer.StartService should success but get error: %v", err) } _, err = tsv.Execute(context.Background(), &target, query, nil, 0, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // make mysql conn fail db.Close() @@ -470,13 +439,9 @@ func TestTabletServerReconnect(t *testing.T) { db.AddQuery("select addr from test_table where 1 != 1", &sqltypes.Result{}) dbcfgs = testUtils.newDBConfigs(db) err = tsv.StartService(target, dbcfgs) - if err != nil { - t.Error(err) - } + require.NoError(t, err) _, err = tsv.Execute(context.Background(), &target, query, nil, 0, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestTabletServerTarget(t *testing.T) { @@ -613,9 +578,7 @@ func TestTabletServerMasterToReplica(t *testing.T) { ctx := context.Background() target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} txid1, err := tsv.Begin(ctx, &target, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if _, err := tsv.Execute(ctx, &target, "update test_table set name = 2 where pk = 1", nil, txid1, nil); err != nil { t.Error(err) } @@ -623,14 +586,10 @@ func TestTabletServerMasterToReplica(t *testing.T) { t.Error(err) } txid2, err := tsv.Begin(ctx, &target, nil) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // This makes txid2 busy conn2, err := tsv.te.txPool.Get(txid2, "for query") - if err != nil { - t.Error(err) - } + require.NoError(t, err) ch := make(chan bool) go func() { tsv.SetServingType(topodatapb.TabletType_REPLICA, true, []topodatapb.TabletType{topodatapb.TabletType_MASTER}) @@ -766,9 +725,7 @@ func TestTabletServerCreateTransaction(t *testing.T) { Keyspace: "t1", Shard: "0", }}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestTabletServerStartCommit(t *testing.T) { @@ -782,9 +739,7 @@ func TestTabletServerStartCommit(t *testing.T) { db.AddQuery(commitTransition, &sqltypes.Result{RowsAffected: 1}) txid := newTxForPrep(tsv) err := tsv.StartCommit(ctx, &target, txid, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) db.AddQuery(commitTransition, &sqltypes.Result{}) txid = newTxForPrep(tsv) @@ -806,9 +761,7 @@ func TestTabletserverSetRollback(t *testing.T) { db.AddQuery(rollbackTransition, &sqltypes.Result{RowsAffected: 1}) txid := newTxForPrep(tsv) err := tsv.SetRollback(ctx, &target, "aa", txid) - if err != nil { - t.Error(err) - } + require.NoError(t, err) db.AddQuery(rollbackTransition, &sqltypes.Result{}) txid = newTxForPrep(tsv) @@ -828,9 +781,7 @@ func TestTabletServerReadTransaction(t *testing.T) { db.AddQuery("select dtid, state, time_created from `_vt`.dt_state where dtid = 'aa'", &sqltypes.Result{}) got, err := tsv.ReadTransaction(ctx, &target, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := &querypb.TransactionMetadata{} if !proto.Equal(got, want) { t.Errorf("ReadTransaction: %v, want %v", got, want) @@ -863,9 +814,7 @@ func TestTabletServerReadTransaction(t *testing.T) { }}, }) got, err = tsv.ReadTransaction(ctx, &target, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = &querypb.TransactionMetadata{ Dtid: "aa", State: querypb.TransactionState_PREPARE, @@ -899,9 +848,7 @@ func TestTabletServerReadTransaction(t *testing.T) { db.AddQuery("select dtid, state, time_created from `_vt`.dt_state where dtid = 'aa'", txResult) want.State = querypb.TransactionState_COMMIT got, err = tsv.ReadTransaction(ctx, &target, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !proto.Equal(got, want) { t.Errorf("ReadTransaction: %v, want %v", got, want) } @@ -921,9 +868,7 @@ func TestTabletServerReadTransaction(t *testing.T) { db.AddQuery("select dtid, state, time_created from `_vt`.dt_state where dtid = 'aa'", txResult) want.State = querypb.TransactionState_ROLLBACK got, err = tsv.ReadTransaction(ctx, &target, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !proto.Equal(got, want) { t.Errorf("ReadTransaction: %v, want %v", got, want) } @@ -939,9 +884,7 @@ func TestTabletServerConcludeTransaction(t *testing.T) { db.AddQuery("delete from `_vt`.dt_state where dtid = 'aa'", &sqltypes.Result{}) db.AddQuery("delete from `_vt`.dt_participant where dtid = 'aa'", &sqltypes.Result{}) err := tsv.ConcludeTransaction(ctx, &target, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestTabletServerBeginFail(t *testing.T) { @@ -2215,9 +2158,7 @@ func TestMessageAck(t *testing.T) { ) db.AddQueryPattern("update msg set time_acked = .*", &sqltypes.Result{RowsAffected: 1}) count, err := tsv.MessageAck(ctx, &target, "msg", ids) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 1 { t.Errorf("count: %d, want 1", count) } @@ -2258,9 +2199,7 @@ func TestRescheduleMessages(t *testing.T) { ) db.AddQueryPattern("update msg set time_next = .*", &sqltypes.Result{RowsAffected: 1}) count, err := tsv.PostponeMessages(ctx, &target, "msg", []string{"1", "2"}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 1 { t.Errorf("count: %d, want 1", count) } @@ -2301,9 +2240,7 @@ func TestPurgeMessages(t *testing.T) { ) db.AddQuery("delete from msg where (time_scheduled = 1 and id = 1) /* _stream msg (time_scheduled id ) (1 1 ); */", &sqltypes.Result{RowsAffected: 1}) count, err := tsv.PurgeMessages(ctx, &target, "msg", 3) - if err != nil { - t.Error(err) - } + require.NoError(t, err) if count != 1 { t.Errorf("count: %d, want 1", count) } diff --git a/go/vt/vttablet/tabletserver/tx_executor_test.go b/go/vt/vttablet/tabletserver/tx_executor_test.go index 75c0517bb99..c7085bbf78a 100644 --- a/go/vt/vttablet/tabletserver/tx_executor_test.go +++ b/go/vt/vttablet/tabletserver/tx_executor_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/mysql/fakesqldb" @@ -43,9 +44,7 @@ func TestTxExecutorEmptyPrepare(t *testing.T) { defer tsv.StopService() txid := newTransaction(tsv, nil) err := txe.Prepare(txid, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Nothing should be prepared. if len(txe.te.preparedPool.conns) != 0 { t.Errorf("len(txe.te.preparedPool.conns): %d, want 0", len(txe.te.preparedPool.conns)) @@ -58,23 +57,15 @@ func TestTxExecutorPrepare(t *testing.T) { defer tsv.StopService() txid := newTxForPrep(tsv) err := txe.Prepare(txid, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = txe.RollbackPrepared("aa", 1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // A retry should still succeed. err = txe.RollbackPrepared("aa", 1) - if err != nil { - t.Error(err) - } + require.NoError(t, err) // A retry with no original id should also succeed. err = txe.RollbackPrepared("aa", 0) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestTxExecutorPrepareNotInTx(t *testing.T) { @@ -95,9 +86,7 @@ func TestTxExecutorPreparePoolFail(t *testing.T) { txid1 := newTxForPrep(tsv) txid2 := newTxForPrep(tsv) err := txe.Prepare(txid1, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer txe.RollbackPrepared("aa", 0) err = txe.Prepare(txid2, "bb") want := "prepared transactions exceeded limit" @@ -153,18 +142,12 @@ func TestTxExecutorCommit(t *testing.T) { defer tsv.StopService() txid := newTxForPrep(tsv) err := txe.Prepare(txid, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = txe.CommitPrepared("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) // Committing an absent transaction should succeed. err = txe.CommitPrepared("bb") - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestTxExecutorCommitRedoFail(t *testing.T) { @@ -175,9 +158,7 @@ func TestTxExecutorCommitRedoFail(t *testing.T) { // Allow all additions to redo logs to succeed db.AddQueryPattern("insert into `_vt`\\.redo_state.*", &sqltypes.Result{}) err := txe.Prepare(txid, "bb") - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer txe.RollbackPrepared("bb", 0) db.AddQuery("update `_vt`.redo_state set state = 'Failed' where dtid = 'bb'", &sqltypes.Result{}) err = txe.CommitPrepared("bb") @@ -199,9 +180,7 @@ func TestTxExecutorCommitRedoCommitFail(t *testing.T) { defer tsv.StopService() txid := newTxForPrep(tsv) err := txe.Prepare(txid, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer txe.RollbackPrepared("aa", 0) db.AddRejectedQuery("commit", errors.New("commit fail")) err = txe.CommitPrepared("aa") @@ -217,9 +196,7 @@ func TestTxExecutorRollbackBeginFail(t *testing.T) { defer tsv.StopService() txid := newTxForPrep(tsv) err := txe.Prepare(txid, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) db.AddRejectedQuery("begin", errors.New("begin fail")) err = txe.RollbackPrepared("aa", txid) want := "begin fail" @@ -236,9 +213,7 @@ func TestTxExecutorRollbackRedoFail(t *testing.T) { // Allow all additions to redo logs to succeed db.AddQueryPattern("insert into `_vt`\\.redo_state.*", &sqltypes.Result{}) err := txe.Prepare(txid, "bb") - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = txe.RollbackPrepared("bb", txid) want := "is not supported" if err == nil || !strings.Contains(err.Error(), want) { @@ -257,9 +232,7 @@ func TestExecutorCreateTransaction(t *testing.T) { Keyspace: "t1", Shard: "0", }}) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestExecutorStartCommit(t *testing.T) { @@ -271,9 +244,7 @@ func TestExecutorStartCommit(t *testing.T) { db.AddQuery(commitTransition, &sqltypes.Result{RowsAffected: 1}) txid := newTxForPrep(tsv) err := txe.StartCommit(txid, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) db.AddQuery(commitTransition, &sqltypes.Result{}) txid = newTxForPrep(tsv) @@ -293,9 +264,7 @@ func TestExecutorSetRollback(t *testing.T) { db.AddQuery(rollbackTransition, &sqltypes.Result{RowsAffected: 1}) txid := newTxForPrep(tsv) err := txe.SetRollback("aa", txid) - if err != nil { - t.Error(err) - } + require.NoError(t, err) db.AddQuery(rollbackTransition, &sqltypes.Result{}) txid = newTxForPrep(tsv) @@ -314,9 +283,7 @@ func TestExecutorConcludeTransaction(t *testing.T) { db.AddQuery("delete from `_vt`.dt_state where dtid = 'aa'", &sqltypes.Result{}) db.AddQuery("delete from `_vt`.dt_participant where dtid = 'aa'", &sqltypes.Result{}) err := txe.ConcludeTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestExecutorReadTransaction(t *testing.T) { @@ -326,9 +293,7 @@ func TestExecutorReadTransaction(t *testing.T) { db.AddQuery("select dtid, state, time_created from `_vt`.dt_state where dtid = 'aa'", &sqltypes.Result{}) got, err := txe.ReadTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := &querypb.TransactionMetadata{} if !proto.Equal(got, want) { t.Errorf("ReadTransaction: %v, want %v", got, want) @@ -361,9 +326,7 @@ func TestExecutorReadTransaction(t *testing.T) { }}, }) got, err = txe.ReadTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want = &querypb.TransactionMetadata{ Dtid: "aa", State: querypb.TransactionState_PREPARE, @@ -397,9 +360,7 @@ func TestExecutorReadTransaction(t *testing.T) { db.AddQuery("select dtid, state, time_created from `_vt`.dt_state where dtid = 'aa'", txResult) want.State = querypb.TransactionState_COMMIT got, err = txe.ReadTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !proto.Equal(got, want) { t.Errorf("ReadTransaction: %v, want %v", got, want) } @@ -419,9 +380,7 @@ func TestExecutorReadTransaction(t *testing.T) { db.AddQuery("select dtid, state, time_created from `_vt`.dt_state where dtid = 'aa'", txResult) want.State = querypb.TransactionState_ROLLBACK got, err = txe.ReadTransaction("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if !proto.Equal(got, want) { t.Errorf("ReadTransaction: %v, want %v", got, want) } @@ -449,9 +408,7 @@ func TestExecutorReadAllTransactions(t *testing.T) { }}, }) got, _, _, err := txe.ReadTwopcInflight() - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := []*DistributedTx{{ Dtid: "dtid0", State: "PREPARE", diff --git a/go/vt/vttablet/tabletserver/tx_prep_pool_test.go b/go/vt/vttablet/tabletserver/tx_prep_pool_test.go index 9716d57a54a..7d6724643b7 100644 --- a/go/vt/vttablet/tabletserver/tx_prep_pool_test.go +++ b/go/vt/vttablet/tabletserver/tx_prep_pool_test.go @@ -18,6 +18,8 @@ package tabletserver import ( "testing" + + "github.com/stretchr/testify/require" ) func TestEmptyPrep(t *testing.T) { @@ -32,13 +34,9 @@ func TestEmptyPrep(t *testing.T) { func TestPrepPut(t *testing.T) { pp := NewTxPreparedPool(2) err := pp.Put(nil, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = pp.Put(nil, "bb") - if err != nil { - t.Error(err) - } + require.NoError(t, err) want := "prepared transactions exceeded limit: 2" err = pp.Put(nil, "cc") if err == nil || err.Error() != want { @@ -50,9 +48,7 @@ func TestPrepPut(t *testing.T) { t.Errorf("Put err: %v, want %s", err, want) } _, err = pp.FetchForCommit("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) err = pp.Put(nil, "aa") want = "duplicate DTID in Prepare: aa" if err == nil || err.Error() != want { @@ -60,9 +56,7 @@ func TestPrepPut(t *testing.T) { } pp.Forget("aa") err = pp.Put(nil, "aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestPrepFetchForRollback(t *testing.T) { @@ -87,17 +81,13 @@ func TestPrepFetchForCommit(t *testing.T) { pp := NewTxPreparedPool(2) conn := &TxConnection{} got, err := pp.FetchForCommit("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got != nil { t.Errorf("Get(aa): %v, want nil", got) } pp.Put(conn, "aa") got, err = pp.FetchForCommit("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got != conn { t.Errorf("pp.Get(aa): %p, want %p", got, conn) } @@ -120,9 +110,7 @@ func TestPrepFetchForCommit(t *testing.T) { } pp.Forget("aa") got, err = pp.FetchForCommit("aa") - if err != nil { - t.Error(err) - } + require.NoError(t, err) if got != nil { t.Errorf("Get(aa): %v, want nil", got) } diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index c57135f4d12..9942108a961 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -22,6 +22,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -172,9 +173,7 @@ func TestStreamRowsUnicode(t *testing.T) { } return nil }) - if err != nil { - t.Error(err) - } + require.NoError(t, err) } func TestStreamRowsKeyRange(t *testing.T) { From 04471b29b03335d274dc09d8583ee219ba969a39 Mon Sep 17 00:00:00 2001 From: prince Date: Fri, 7 Feb 2020 06:25:27 +0530 Subject: [PATCH 052/825] migrating mysqlctld python testcases to go (#5774) * initial commit for backup_only Signed-off-by: Ajeet jain * changes in package structure Signed-off-by: Ajeet jain * updating package name and fixing a test Signed-off-by: Ajeet jain * removed unrequired teardown code Signed-off-by: Ajeet jain * Removed debug code Signed-off-by: Ajeet jain * inital commit for xtrabackup Signed-off-by: Ajeet jain * fix sequencing of cleanup Signed-off-by: Ajeet jain * fix terminate restore for xtrabackup Signed-off-by: Ajeet jain * updated config for xtrabackup Signed-off-by: Ajeet jain * backup-mysqlctld: mysqlctld setup module created. Signed-off-by: pradip parmar * added xtrabackup stream mode Signed-off-by: Ajeet jain * updated config for xtrabackup stream Signed-off-by: Ajeet jain * minor changes to config Signed-off-by: Ajeet jain * initial commit for backup_only Signed-off-by: Ajeet jain * changes in package structure Signed-off-by: Ajeet jain * updating package name and fixing a test Signed-off-by: Ajeet jain * removed unrequired teardown code Signed-off-by: Ajeet jain * Removed debug code Signed-off-by: Ajeet jain * inital commit for xtrabackup Signed-off-by: Ajeet jain * fix sequencing of cleanup Signed-off-by: Ajeet jain * fix terminate restore for xtrabackup Signed-off-by: Ajeet jain * updated config for xtrabackup Signed-off-by: Ajeet jain * added xtrabackup stream mode Signed-off-by: Ajeet jain * updated config for xtrabackup stream Signed-off-by: Ajeet jain * minor changes to config Signed-off-by: Ajeet jain * rebased to resolve conflict Signed-off-by: Arindam Nayak * backup-mysqlctld: mysqlctld health check changes. Signed-off-by: pradip parmar * backup_mysqlctld: mysqlctld teardown fixes. vttablet restart method created. Signed-off-by: pradip parmar * backup-mysqlctld: mysqlctld restart fixed, backup utils refactor. Signed-off-by: pradip parmar * backup_transform_mysqlctld: backup_transform testing using mysqlctld. Signed-off-by: pradip parmar * Added percona 56 new dependency Signed-off-by: Ajeet jain * Putting the dependency at right place Signed-off-by: Ajeet jain * updated apt tp apt-get Signed-off-by: Ajeet jain * corrected the typo Signed-off-by: Ajeet jain * fixed a comma in config.json Signed-off-by: Ajeet jain * review changes. Signed-off-by: pradip parmar * test added in config. Signed-off-by: pradip parmar * package name changed. Signed-off-by: pradip parmar * mysql_ctld: file name change. Signed-off-by: pradip parmar * mysqlctld: review changes, code refactor, removed some functions. Signed-off-by: pradip parmar * backup_transform: refator. Signed-off-by: pradip parmar * backup_mysqlctld: config changes. Signed-off-by: pradip parmar * restore vtBackup test Signed-off-by: Ajeet jain * redistribute travis tests Signed-off-by: Ajeet jain * updated shard matrix for transform test Signed-off-by: Ajeet jain * mysqlctld: mysqlctld teardown issue resolved. Signed-off-by: pradip parmar * mysqlctld: mysqlctld teardown issue resolved. Signed-off-by: pradip parmar * mysql-ctld: process teardown changes. Signed-off-by: pradip parmar * review changes and newConnection method modified. Signed-off-by: pradip parmar * mysqlctld: process hang issue resolved. Signed-off-by: pradip parmar Co-authored-by: Ajeet Jain Co-authored-by: Arindam Nayak Co-authored-by: Deepthi Sigireddi --- .github/workflows/cluster_endtoend.yml | 2 +- .../backup/mysqlctld/backup_mysqlctld_test.go | 28 +++ .../backup/transform/backup_transform_test.go | 30 +++ .../transform/backup_transform_utils.go} | 179 ++++++++++++++++-- .../backup_transform_mysqlctld_test.go | 34 ++++ .../backup/vtbackup/backup_only_test.go | 75 ++++---- .../backup/vtctlbackup/backup_test.go | 15 +- .../backup/vtctlbackup/backup_utils.go | 164 +++++++++------- .../backup/xtrabackup/xtrabackup_test.go | 11 +- .../xtrabackup_stream_test.go | 11 +- go/test/endtoend/backuptransform/main_test.go | 154 --------------- go/test/endtoend/cluster/cluster_process.go | 37 ++-- go/test/endtoend/cluster/cluster_util.go | 48 ++++- go/test/endtoend/cluster/mysqlctl_process.go | 10 +- go/test/endtoend/cluster/mysqlctld_process.go | 170 +++++++++++++++++ go/test/endtoend/cluster/vttablet_process.go | 16 +- go/test/endtoend/mysqlctl/mysqlctl_test.go | 10 +- go/test/endtoend/mysqlctld/mysqlctld_test.go | 164 ++++++++++++++++ go/test/endtoend/recovery/recovery_util.go | 5 +- test/config.json | 77 ++++---- 20 files changed, 855 insertions(+), 385 deletions(-) create mode 100644 go/test/endtoend/backup/mysqlctld/backup_mysqlctld_test.go create mode 100644 go/test/endtoend/backup/transform/backup_transform_test.go rename go/test/endtoend/{backuptransform/backup_transform_test.go => backup/transform/backup_transform_utils.go} (56%) create mode 100644 go/test/endtoend/backup/transform/mysqlctld/backup_transform_mysqlctld_test.go delete mode 100644 go/test/endtoend/backuptransform/main_test.go create mode 100644 go/test/endtoend/cluster/mysqlctld_process.go create mode 100644 go/test/endtoend/mysqlctld/mysqlctld_test.go diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index a6670856869..efd2ff72943 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22] steps: - name: Set up Go diff --git a/go/test/endtoend/backup/mysqlctld/backup_mysqlctld_test.go b/go/test/endtoend/backup/mysqlctld/backup_mysqlctld_test.go new file mode 100644 index 00000000000..2ee11103c72 --- /dev/null +++ b/go/test/endtoend/backup/mysqlctld/backup_mysqlctld_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysqlctld + +import ( + "testing" + + backup "vitess.io/vitess/go/test/endtoend/backup/vtctlbackup" +) + +// TestBackupMysqlctld - tests the backup using mysqlctld. +func TestBackupMysqlctld(t *testing.T) { + backup.TestBackup(t, backup.Mysqlctld, "", 0) +} diff --git a/go/test/endtoend/backup/transform/backup_transform_test.go b/go/test/endtoend/backup/transform/backup_transform_test.go new file mode 100644 index 00000000000..071f7d536e3 --- /dev/null +++ b/go/test/endtoend/backup/transform/backup_transform_test.go @@ -0,0 +1,30 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package transform + +import "testing" + +func TestMain(m *testing.M) { + TestMainSetup(m, false) +} + +func TestBackupTransform(t *testing.T) { + TestBackupTransformImpl(t) +} +func TestBackupTransformError(t *testing.T) { + TestBackupTransformErrorImpl(t) +} diff --git a/go/test/endtoend/backuptransform/backup_transform_test.go b/go/test/endtoend/backup/transform/backup_transform_utils.go similarity index 56% rename from go/test/endtoend/backuptransform/backup_transform_test.go rename to go/test/endtoend/backup/transform/backup_transform_utils.go index e8d0254ff7b..8b1c43482f1 100644 --- a/go/test/endtoend/backuptransform/backup_transform_test.go +++ b/go/test/endtoend/backup/transform/backup_transform_utils.go @@ -14,29 +14,173 @@ See the License for the specific language governing permissions and limitations under the License. */ -package backuptransform +package transform import ( "encoding/json" + "flag" "fmt" "io/ioutil" "os" + "os/exec" + "path" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" + "vitess.io/vitess/go/vt/log" ) +// test main part of the testcase +var ( + master *cluster.Vttablet + replica1 *cluster.Vttablet + replica2 *cluster.Vttablet + localCluster *cluster.LocalProcessCluster + newInitDBFile string + cell = cluster.DefaultCell + hostname = "localhost" + keyspaceName = "ks" + dbPassword = "VtDbaPass" + shardKsName = fmt.Sprintf("%s/%s", keyspaceName, shardName) + dbCredentialFile string + shardName = "0" + commonTabletArg = []string{ + "-vreplication_healthcheck_topology_refresh", "1s", + "-vreplication_healthcheck_retry_delay", "1s", + "-vreplication_retry_delay", "1s", + "-degraded_threshold", "5s", + "-lock_tables_timeout", "5s", + "-watch_replication_stream", + "-enable_replication_reporter", + "-serving_state_grace_period", "1s"} +) + +func TestMainSetup(m *testing.M, useMysqlctld bool) { + flag.Parse() + + exitCode, err := func() (int, error) { + localCluster = cluster.NewCluster(cell, hostname) + defer localCluster.Teardown() + + // Start topo server + err := localCluster.StartTopo() + if err != nil { + return 1, err + } + + // Start keyspace + keyspace := &cluster.Keyspace{ + Name: keyspaceName, + Shards: []cluster.Shard{ + { + Name: shardName, + }, + }, + } + localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) + shard := &keyspace.Shards[0] + // changing password for mysql user + dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) + initDb, _ := ioutil.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) + sql := string(initDb) + newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") + sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) + ioutil.WriteFile(newInitDBFile, []byte(sql), 0666) + + extraArgs := []string{"-db-credentials-file", dbCredentialFile} + commonTabletArg = append(commonTabletArg, "-db-credentials-file", dbCredentialFile) + + // start mysql process for all replicas and master + var mysqlProcs []*exec.Cmd + for i := 0; i < 3; i++ { + tabletType := "replica" + tablet := localCluster.GetVttabletInstance(tabletType, 0, cell) + tablet.VttabletProcess = localCluster.GetVtprocessInstanceFromVttablet(tablet, shard.Name, keyspaceName) + tablet.VttabletProcess.DbPassword = dbPassword + tablet.VttabletProcess.ExtraArgs = commonTabletArg + tablet.VttabletProcess.SupportsBackup = true + tablet.VttabletProcess.EnableSemiSync = true + + if useMysqlctld { + tablet.MysqlctldProcess = *cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) + tablet.MysqlctldProcess.InitDBFile = newInitDBFile + tablet.MysqlctldProcess.ExtraArgs = extraArgs + tablet.MysqlctldProcess.Password = tablet.VttabletProcess.DbPassword + err := tablet.MysqlctldProcess.Start() + if err != nil { + return 1, err + } + + shard.Vttablets = append(shard.Vttablets, tablet) + continue + } + + tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) + tablet.MysqlctlProcess.InitDBFile = newInitDBFile + tablet.MysqlctlProcess.ExtraArgs = extraArgs + proc, err := tablet.MysqlctlProcess.StartProcess() + if err != nil { + return 1, err + } + mysqlProcs = append(mysqlProcs, proc) + + shard.Vttablets = append(shard.Vttablets, tablet) + } + for _, proc := range mysqlProcs { + if err := proc.Wait(); err != nil { + return 1, err + } + } + + // initialize tablets + master = shard.Vttablets[0] + replica1 = shard.Vttablets[1] + replica2 = shard.Vttablets[2] + + for _, tablet := range []*cluster.Vttablet{master, replica1} { + if err := localCluster.VtctlclientProcess.InitTablet(tablet, cell, keyspaceName, hostname, shard.Name); err != nil { + return 1, err + } + } + + // create database for master and replica + for _, tablet := range []cluster.Vttablet{*master, *replica1} { + if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { + return 1, err + } + if err := tablet.VttabletProcess.Setup(); err != nil { + return 1, err + } + } + + // initialize master and start replication + if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard.Name, cell, master.TabletUID); err != nil { + return 1, err + } + return m.Run(), nil + }() + + if err != nil { + log.Error(err.Error()) + os.Exit(1) + } else { + os.Exit(exitCode) + } + +} + // create query for test table creation var vtInsertTest = `create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB` + id bigint auto_increment, + msg varchar(64), + primary key (id) + ) Engine=InnoDB` -func TestBackupTransform(t *testing.T) { +func TestBackupTransformImpl(t *testing.T) { // insert data in master, validate same in slave verifyInitialReplication(t) @@ -73,15 +217,22 @@ func TestBackupTransform(t *testing.T) { // restore replica2 from backup, should not give any error // Note: we don't need to pass in the backup_storage_transform parameter, // as it is read from the MANIFEST. - replica2.MysqlctlProcess.ExtraArgs = []string{ - "-db-credentials-file", dbCredentialFile} // clear replica2 - replica2.MysqlctlProcess.Stop() - os.RemoveAll(replica2.VttabletProcess.Directory) - // start replica2 from backup - err = replica2.MysqlctlProcess.Start() - require.Nil(t, err) + if replica2.MysqlctlProcess.TabletUID > 0 { + replica2.MysqlctlProcess.Stop() + os.RemoveAll(replica2.VttabletProcess.Directory) + // start replica2 from backup + err = replica2.MysqlctlProcess.Start() + require.Nil(t, err) + } else { + replica2.MysqlctldProcess.Stop() + os.RemoveAll(replica2.VttabletProcess.Directory) + // start replica2 from backup + err = replica2.MysqlctldProcess.Start() + require.Nil(t, err) + } + err = localCluster.VtctlclientProcess.InitTablet(replica2, cell, keyspaceName, hostname, shardName) assert.Nil(t, err) replica2.VttabletProcess.CreateDB(keyspaceName) @@ -114,7 +265,7 @@ func TestBackupTransform(t *testing.T) { // TestBackupTransformError validate backup with test_backup_error // backup_storage_hook, which should fail. -func TestBackupTransformError(t *testing.T) { +func TestBackupTransformErrorImpl(t *testing.T) { // restart the replica with transform hook parameter err := replica1.VttabletProcess.TearDown() require.Nil(t, err) diff --git a/go/test/endtoend/backup/transform/mysqlctld/backup_transform_mysqlctld_test.go b/go/test/endtoend/backup/transform/mysqlctld/backup_transform_mysqlctld_test.go new file mode 100644 index 00000000000..0a3b11227da --- /dev/null +++ b/go/test/endtoend/backup/transform/mysqlctld/backup_transform_mysqlctld_test.go @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysqlctld + +import ( + "testing" + + "vitess.io/vitess/go/test/endtoend/backup/transform" +) + +func TestMain(m *testing.M) { + transform.TestMainSetup(m, true) +} + +func TestBackupTransform(t *testing.T) { + transform.TestBackupTransformImpl(t) +} +func TestBackupTransformError(t *testing.T) { + transform.TestBackupTransformErrorImpl(t) +} diff --git a/go/test/endtoend/backup/vtbackup/backup_only_test.go b/go/test/endtoend/backup/vtbackup/backup_only_test.go index 9adb53a3ec6..5a6de0d8a56 100644 --- a/go/test/endtoend/backup/vtbackup/backup_only_test.go +++ b/go/test/endtoend/backup/vtbackup/backup_only_test.go @@ -17,7 +17,6 @@ limitations under the License. package vtbackup import ( - "bufio" "fmt" "os" "path" @@ -25,6 +24,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/endtoend/cluster" "github.com/stretchr/testify/assert" @@ -54,8 +55,7 @@ func TestTabletInitialBackup(t *testing.T) { // - list the backups, remove them vtBackup(t, true) - backups := countBackups(t) - assert.Equal(t, 1, backups) + verifyBackupCount(t, shardKsName, 1) // Initialize the tablets initTablets(t, false, false) @@ -107,10 +107,11 @@ func firstBackupTest(t *testing.T, tabletType string) { // - list the backup, remove it // Store initial backup counts - backupsCount := countBackups(t) + backups, err := listBackups(shardKsName) + require.Nil(t, err) // insert data on master, wait for slave to get it - _, err := master.VttabletProcess.QueryTablet(vtInsertTest, keyspaceName, true) + _, err = master.VttabletProcess.QueryTablet(vtInsertTest, keyspaceName, true) assert.Nil(t, err) // Add a single row with value 'test1' to the master tablet _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test1')", keyspaceName, true) @@ -125,8 +126,7 @@ func firstBackupTest(t *testing.T, tabletType string) { log.Info("done taking backup %s", time.Now()) // check that the backup shows up in the listing - backups := countBackups(t) - assert.Equal(t, backups, backupsCount+1) + verifyBackupCount(t, shardKsName, len(backups)+1) // insert more data on the master _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) @@ -155,8 +155,7 @@ func firstBackupTest(t *testing.T, tabletType string) { } removeBackups(t) - backups = countBackups(t) - assert.Equal(t, 0, backups) + verifyBackupCount(t, shardKsName, 0) } @@ -168,50 +167,46 @@ func vtBackup(t *testing.T, initialBackup bool) { assert.Nil(t, err) } -func listBackups(t *testing.T) string { - // Get a list of backup names for the current shard. - localCluster.VtctlProcess = *cluster.VtctlProcessInstance(localCluster.TopoPort, localCluster.Hostname) +func verifyBackupCount(t *testing.T, shardKsName string, expected int) []string { + backups, err := listBackups(shardKsName) + assert.Nil(t, err) + assert.Equalf(t, expected, len(backups), "invalid number of backups") + return backups +} + +func listBackups(shardKsName string) ([]string, error) { backups, err := localCluster.VtctlProcess.ExecuteCommandWithOutput( "-backup_storage_implementation", "file", "-file_backup_storage_root", path.Join(os.Getenv("VTDATAROOT"), "tmp", "backupstorage"), "ListBackups", shardKsName, ) - assert.Nil(t, err) - return backups -} - -func countBackups(t *testing.T) int { - // Count the number of backups available in current shard. - backupList := listBackups(t) - backupCount := 0 - // Counts the available backups - scanner := bufio.NewScanner(strings.NewReader(backupList)) - for scanner.Scan() { - if scanner.Text() != "" { - backupCount++ + if err != nil { + return nil, err + } + result := strings.Split(backups, "\n") + var returnResult []string + for _, str := range result { + if str != "" { + returnResult = append(returnResult, str) } } - return backupCount + return returnResult, nil } func removeBackups(t *testing.T) { // Remove all the backups from the shard - backupList := listBackups(t) - - scanner := bufio.NewScanner(strings.NewReader(backupList)) - for scanner.Scan() { - if scanner.Text() != "" { - _, err := localCluster.VtctlProcess.ExecuteCommandWithOutput( - "-backup_storage_implementation", "file", - "-file_backup_storage_root", - path.Join(os.Getenv("VTDATAROOT"), "tmp", "backupstorage"), - "RemoveBackup", shardKsName, scanner.Text(), - ) - assert.Nil(t, err) - } + backups, err := listBackups(shardKsName) + assert.Nil(t, err) + for _, backup := range backups { + _, err := localCluster.VtctlProcess.ExecuteCommandWithOutput( + "-backup_storage_implementation", "file", + "-file_backup_storage_root", + path.Join(os.Getenv("VTDATAROOT"), "tmp", "backupstorage"), + "RemoveBackup", shardKsName, backup, + ) + assert.Nil(t, err) } - } func initTablets(t *testing.T, startTablet bool, initShardMaster bool) { diff --git a/go/test/endtoend/backup/vtctlbackup/backup_test.go b/go/test/endtoend/backup/vtctlbackup/backup_test.go index f00f793b47d..6f233eafeda 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_test.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_test.go @@ -16,18 +16,11 @@ limitations under the License. package vtctlbackup -import "testing" +import ( + "testing" +) // TestBackupMain - main tests backup using vtctl commands func TestBackupMain(t *testing.T) { - code, err := LaunchCluster(false, "", 0) - if err != nil { - t.Errorf("setup failed with status code %d", code) - } - - // Run all the backup tests - TestBackup(t) - - // Teardown the cluster - TearDownCluster() + TestBackup(t, Backup, "", 0) } diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index 33c955467ab..0fdc62e1796 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -34,9 +34,16 @@ import ( "vitess.io/vitess/go/vt/proto/topodata" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/cluster" ) +const ( + ExtraBackup = iota + Backup + Mysqlctld +) + var ( master *cluster.Vttablet replica1 *cluster.Vttablet @@ -44,8 +51,6 @@ var ( localCluster *cluster.LocalProcessCluster newInitDBFile string useXtrabackup bool - xbStreamMode string - xbStripes int cell = cluster.DefaultCell hostname = "localhost" keyspaceName = "ks" @@ -72,7 +77,8 @@ var ( ) Engine=InnoDB` ) -func LaunchCluster(xtrabackup bool, streamMode string, stripes int) (int, error) { +// LaunchCluster : starts the cluster as per given params. +func LaunchCluster(setupType int, streamMode string, stripes int) (int, error) { localCluster = cluster.NewCluster(cell, hostname) // Start topo server @@ -101,16 +107,14 @@ func LaunchCluster(xtrabackup bool, streamMode string, stripes int) (int, error) commonTabletArg = append(commonTabletArg, "-db-credentials-file", dbCredentialFile) // Update arguments for xtrabackup - if xtrabackup { - useXtrabackup = xtrabackup - xbStreamMode = streamMode - xbStripes = stripes + if setupType == ExtraBackup { + useXtrabackup = true xtrabackupArgs := []string{ "-backup_engine_implementation", "xtrabackup", - fmt.Sprintf("-xtrabackup_stream_mode=%s", xbStreamMode), + fmt.Sprintf("-xtrabackup_stream_mode=%s", streamMode), "-xtrabackup_user=vt_dba", - fmt.Sprintf("-xtrabackup_stripes=%d", xbStripes), + fmt.Sprintf("-xtrabackup_stripes=%d", stripes), "-xtrabackup_backup_flags", fmt.Sprintf("--password=%s", dbPassword), } @@ -134,14 +138,27 @@ func LaunchCluster(xtrabackup bool, streamMode string, stripes int) (int, error) tablet.VttabletProcess.SupportsBackup = true tablet.VttabletProcess.EnableSemiSync = true + if setupType == Mysqlctld { + tablet.MysqlctldProcess = *cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) + tablet.MysqlctldProcess.InitDBFile = newInitDBFile + tablet.MysqlctldProcess.ExtraArgs = extraArgs + tablet.MysqlctldProcess.Password = tablet.VttabletProcess.DbPassword + if err := tablet.MysqlctldProcess.Start(); err != nil { + return 1, err + } + shard.Vttablets = append(shard.Vttablets, tablet) + continue + } + tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) tablet.MysqlctlProcess.InitDBFile = newInitDBFile tablet.MysqlctlProcess.ExtraArgs = extraArgs - if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { + proc, err := tablet.MysqlctlProcess.StartProcess() + if err != nil { return 1, err - } else { - mysqlProcs = append(mysqlProcs, proc) } + mysqlProcs = append(mysqlProcs, proc) + shard.Vttablets = append(shard.Vttablets, tablet) } for _, proc := range mysqlProcs { @@ -179,35 +196,58 @@ func TearDownCluster() { localCluster.Teardown() } -func TestBackup(t *testing.T) { - // Run all the backup tests - t.Run("TestReplicaBackup", func(t *testing.T) { - vtctlBackup(t, "replica") - }) - - t.Run("TestRdonlyBackup", func(t *testing.T) { - vtctlBackup(t, "rdonly") - }) - - t.Run("TestMasterBackup", func(t *testing.T) { - masterBackup(t) - }) +func TestBackup(t *testing.T, setupType int, streamMode string, stripes int) { + + testMethods := []struct { + name string + method func(t *testing.T) + }{ + { + name: "TestReplicaBackup", + method: func(t *testing.T) { + vtctlBackup(t, "replica") + }, + }, // + { + name: "TestRdonlyBackup", + method: func(t *testing.T) { + vtctlBackup(t, "rdonly") + }, + }, // + { + name: "TestMasterBackup", + method: masterBackup, + }, // + { + name: "TestMasterReplicaSameBackup", + method: masterReplicaSameBackup, + }, // + { + name: "TestRestoreOldMasterByRestart", + method: restoreOldMasterByRestart, + }, // + { + name: "TestRestoreOldMasterInPlace", + method: restoreOldMasterInPlace, + }, // + { + name: "TestTerminatedRestore", + method: terminatedRestore, + }, // + } - t.Run("TestMasterReplicaSameBackup", func(t *testing.T) { - masterReplicaSameBackup(t) - }) + // setup cluster for the testing + code, err := LaunchCluster(setupType, streamMode, stripes) + require.Nilf(t, err, "setup failed with status code %d", code) - t.Run("TestRestoreOldMasterByRestart", func(t *testing.T) { - restoreOldMasterByRestart(t) - }) + // Teardown the cluster + defer TearDownCluster() - t.Run("TestRestoreOldMasterInPlace", func(t *testing.T) { - restoreOldMasterInPlace(t) - }) + // Run all the backup tests - t.Run("TestTerminatedRestore", func(t *testing.T) { - terminatedRestore(t) - }) + for _, test := range testMethods { + t.Run(test.name, test.method) + } } @@ -228,14 +268,12 @@ func masterBackup(t *testing.T) { assert.NotNil(t, err) assert.Contains(t, output, "type MASTER cannot take backup. if you really need to do this, rerun the backup command with -allow_master") - backups := listBackups(t) - assert.Equal(t, len(backups), 0) + localCluster.VerifyBackupCount(t, shardKsName, 0) err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", "-allow_master=true", master.Alias) assert.Nil(t, err) - backups = listBackups(t) - assert.Equal(t, len(backups), 1) + backups := localCluster.VerifyBackupCount(t, shardKsName, 1) assert.Contains(t, backups[0], master.Alias) _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) @@ -383,14 +421,15 @@ func restartMasterReplica(t *testing.T) { stopAllTablets() // remove all backups - backups := listBackups(t) - for _, backup := range backups { - err := localCluster.VtctlclientProcess.ExecuteCommand("RemoveBackup", shardKsName, backup) - assert.Nil(t, err) - } + localCluster.RemoveAllBackups(t, shardKsName) // start all tablet and mysql instances var mysqlProcs []*exec.Cmd - for _, tablet := range []*cluster.Vttablet{master, replica1} { + for _, tablet := range []*cluster.Vttablet{master, replica1, replica2} { + if tablet.MysqlctldProcess.TabletUID > 0 { + err := tablet.MysqlctldProcess.Start() + require.Nilf(t, err, "error while starting mysqlctld, tabletUID %v", tablet.TabletUID) + continue + } proc, _ := tablet.MysqlctlProcess.StartProcess() mysqlProcs = append(mysqlProcs, proc) } @@ -413,6 +452,11 @@ func stopAllTablets() { var mysqlProcs []*exec.Cmd for _, tablet := range []*cluster.Vttablet{master, replica1, replica2} { tablet.VttabletProcess.TearDown() + if tablet.MysqlctldProcess.TabletUID > 0 { + tablet.MysqlctldProcess.Stop() + localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "-allow_master", tablet.Alias) + continue + } proc, _ := tablet.MysqlctlProcess.StopProcess() mysqlProcs = append(mysqlProcs, proc) localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "-allow_master", tablet.Alias) @@ -489,8 +533,7 @@ func vtctlBackup(t *testing.T, tabletType string) { err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) assert.Nil(t, err) - backups := listBackups(t) - assert.Equal(t, len(backups), 1) + backups := localCluster.VerifyBackupCount(t, shardKsName, 1) _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) assert.Nil(t, err) @@ -529,7 +572,7 @@ func verifyInitialReplication(t *testing.T) { // to restore a previous backup successfully regardless of this setting. func restoreWaitForBackup(t *testing.T, tabletType string) { replica2.Type = tabletType - resetTabletDir(t, replica2) + replica2.ValidateTabletRestart(t) replicaTabletArgs := commonTabletArg replicaTabletArgs = append(replicaTabletArgs, "-backup_engine_implementation", "fake_implementation") replicaTabletArgs = append(replicaTabletArgs, "-wait_for_backup_interval", "1s") @@ -540,17 +583,6 @@ func restoreWaitForBackup(t *testing.T, tabletType string) { assert.Nil(t, err) } -func resetTabletDir(t *testing.T, tablet *cluster.Vttablet) { - err := cluster.ResetTabletDirectory(*tablet) - assert.Nil(t, err) -} - -func listBackups(t *testing.T) []string { - output, err := localCluster.ListBackups(shardKsName) - assert.Nil(t, err) - return output -} - func verifyAfterRemovingBackupNoBackupShouldBePresent(t *testing.T, backups []string) { // Remove the backup for _, backup := range backups { @@ -559,17 +591,14 @@ func verifyAfterRemovingBackupNoBackupShouldBePresent(t *testing.T, backups []st } // Now, there should not be no backup - backups = listBackups(t) - assert.Equal(t, len(backups), 0) + localCluster.VerifyBackupCount(t, shardKsName, 0) } func verifyRestoreTablet(t *testing.T, tablet *cluster.Vttablet, status string) { - err := tablet.VttabletProcess.TearDown() - assert.Nil(t, err) - resetTabletDir(t, tablet) + tablet.ValidateTabletRestart(t) tablet.VttabletProcess.ServingStatus = "" - err = tablet.VttabletProcess.Setup() + err := tablet.VttabletProcess.Setup() assert.Nil(t, err) if status != "" { err = tablet.VttabletProcess.WaitForTabletTypesForTimeout([]string{status}, 25*time.Second) @@ -596,6 +625,7 @@ func terminateRestore(t *testing.T) { stopRestoreMsg := "Copying file 10" if useXtrabackup { stopRestoreMsg = "Restore: Preparing" + useXtrabackup = false } args := append([]string{"-server", localCluster.VtctlclientProcess.Server, "-alsologtostderr"}, "RestoreFromBackup", master.Alias) diff --git a/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go b/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go index 4b94086bcd3..0802d116c93 100644 --- a/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go +++ b/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go @@ -24,14 +24,5 @@ import ( // TestXtraBackup - tests the backup using xtrabackup func TestXtrabackup(t *testing.T) { - code, err := backup.LaunchCluster(true, "tar", 0) - if err != nil { - t.Errorf("setup failed with status code %d", code) - } - - // Run all the backup tests - backup.TestBackup(t) - - // Teardown the cluster - backup.TearDownCluster() + backup.TestBackup(t, backup.ExtraBackup, "tar", 0) } diff --git a/go/test/endtoend/backup/xtrabackupstream/xtrabackup_stream_test.go b/go/test/endtoend/backup/xtrabackupstream/xtrabackup_stream_test.go index 5e86e73719e..0f1544e8f14 100644 --- a/go/test/endtoend/backup/xtrabackupstream/xtrabackup_stream_test.go +++ b/go/test/endtoend/backup/xtrabackupstream/xtrabackup_stream_test.go @@ -24,14 +24,5 @@ import ( // TestXtrabackupStream - tests the backup using xtrabackup with xbstream mode func TestXtrabackupStream(t *testing.T) { - code, err := backup.LaunchCluster(true, "xbstream", 8) - if err != nil { - t.Errorf("setup failed with status code %d", code) - } - - // Run all the backup tests - backup.TestBackup(t) - - // Teardown the cluster - backup.TearDownCluster() + backup.TestBackup(t, backup.ExtraBackup, "xbstream", 8) } diff --git a/go/test/endtoend/backuptransform/main_test.go b/go/test/endtoend/backuptransform/main_test.go deleted file mode 100644 index ffaeb542ace..00000000000 --- a/go/test/endtoend/backuptransform/main_test.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package backuptransform - -import ( - "flag" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path" - "testing" - - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" - "vitess.io/vitess/go/vt/log" -) - -var ( - master *cluster.Vttablet - replica1 *cluster.Vttablet - replica2 *cluster.Vttablet - localCluster *cluster.LocalProcessCluster - newInitDBFile string - cell = cluster.DefaultCell - hostname = "localhost" - keyspaceName = "ks" - dbPassword = "VtDbaPass" - shardKsName = fmt.Sprintf("%s/%s", keyspaceName, shardName) - dbCredentialFile string - shardName = "0" - commonTabletArg = []string{ - "-vreplication_healthcheck_topology_refresh", "1s", - "-vreplication_healthcheck_retry_delay", "1s", - "-vreplication_retry_delay", "1s", - "-degraded_threshold", "5s", - "-lock_tables_timeout", "5s", - "-watch_replication_stream", - "-enable_replication_reporter", - "-serving_state_grace_period", "1s"} -) - -func TestMain(m *testing.M) { - flag.Parse() - - exitCode, err := func() (int, error) { - localCluster = cluster.NewCluster(cell, hostname) - defer localCluster.Teardown() - - // Start topo server - err := localCluster.StartTopo() - if err != nil { - return 1, err - } - - // Start keyspace - keyspace := &cluster.Keyspace{ - Name: keyspaceName, - } - localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) - - // changing password for mysql user - dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) - initDb, _ := ioutil.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) - sql := string(initDb) - newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") - sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) - ioutil.WriteFile(newInitDBFile, []byte(sql), 0666) - - extraArgs := []string{"-db-credentials-file", dbCredentialFile} - commonTabletArg = append(commonTabletArg, "-db-credentials-file", dbCredentialFile) - - shard := cluster.Shard{ - Name: shardName, - } - - // start mysql process for all replicas and master - var mysqlProcs []*exec.Cmd - for i := 0; i < 3; i++ { - tabletType := "replica" - tablet := localCluster.GetVttabletInstance(tabletType, 0, cell) - tablet.VttabletProcess = localCluster.GetVtprocessInstanceFromVttablet(tablet, shard.Name, keyspaceName) - tablet.VttabletProcess.DbPassword = dbPassword - tablet.VttabletProcess.ExtraArgs = commonTabletArg - tablet.VttabletProcess.SupportsBackup = true - tablet.VttabletProcess.EnableSemiSync = true - - tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) - tablet.MysqlctlProcess.InitDBFile = newInitDBFile - tablet.MysqlctlProcess.ExtraArgs = extraArgs - proc, err := tablet.MysqlctlProcess.StartProcess() - if err != nil { - return 1, err - } - mysqlProcs = append(mysqlProcs, proc) - - shard.Vttablets = append(shard.Vttablets, tablet) - } - for _, proc := range mysqlProcs { - if err := proc.Wait(); err != nil { - return 1, err - } - } - - // initialize tablets - master = shard.Vttablets[0] - replica1 = shard.Vttablets[1] - replica2 = shard.Vttablets[2] - - for _, tablet := range []*cluster.Vttablet{master, replica1} { - if err := localCluster.VtctlclientProcess.InitTablet(tablet, cell, keyspaceName, hostname, shard.Name); err != nil { - return 1, err - } - } - - // create database for master and replica - for _, tablet := range []cluster.Vttablet{*master, *replica1} { - if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { - return 1, err - } - if err := tablet.VttabletProcess.Setup(); err != nil { - return 1, err - } - } - - // initialize master and start replication - if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard.Name, cell, master.TabletUID); err != nil { - return 1, err - } - return m.Run(), nil - }() - - if err != nil { - log.Error(err.Error()) - os.Exit(1) - } else { - os.Exit(exitCode) - } - -} diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index 20dea3b5aeb..d05e3fb5d75 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -80,6 +80,22 @@ type LocalProcessCluster struct { EnableSemiSync bool } +// Vttablet stores the properties needed to start a vttablet process +type Vttablet struct { + Type string + TabletUID int + HTTPPort int + GrpcPort int + MySQLPort int + Alias string + Cell string + + // background executable processes + MysqlctlProcess MysqlctlProcess + MysqlctldProcess MysqlctldProcess + VttabletProcess *VttabletProcess +} + // Keyspace : Cluster accepts keyspace to launch it type Keyspace struct { Name string @@ -120,21 +136,6 @@ func (shard *Shard) Replica() *Vttablet { return nil } -// Vttablet stores the properties needed to start a vttablet process -type Vttablet struct { - Type string - TabletUID int - HTTPPort int - GrpcPort int - MySQLPort int - Alias string - Cell string - - // background executable processes - MysqlctlProcess MysqlctlProcess - VttabletProcess *VttabletProcess -} - // StartTopo starts topology server func (cluster *LocalProcessCluster) StartTopo() (err error) { if cluster.Cell == "" { @@ -468,6 +469,12 @@ func (cluster *LocalProcessCluster) Teardown() { mysqlctlProcessList = append(mysqlctlProcessList, proc) } } + if tablet.MysqlctldProcess.TabletUID > 0 { + if err := tablet.MysqlctldProcess.Stop(); err != nil { + log.Errorf("Error in mysqlctl teardown - %s", err.Error()) + } + } + if err := tablet.VttabletProcess.TearDown(); err != nil { log.Errorf("Error in vttablet teardown - %s", err.Error()) } diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 8ec5cee2344..4cc6404b54c 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" tabletpb "vitess.io/vitess/go/vt/proto/topodata" tmc "vitess.io/vitess/go/vt/vttablet/grpctmclient" ) @@ -35,6 +36,32 @@ var ( tmClient = tmc.NewClient() ) +// Restart restarts vttablet and mysql. +func (tablet *Vttablet) Restart() error { + if tablet.MysqlctlProcess.TabletUID|tablet.MysqlctldProcess.TabletUID == 0 { + return fmt.Errorf("no mysql process is running") + } + + if tablet.MysqlctlProcess.TabletUID > 0 { + tablet.MysqlctlProcess.Stop() + tablet.VttabletProcess.TearDown() + os.RemoveAll(tablet.VttabletProcess.Directory) + + return tablet.MysqlctlProcess.Start() + } + + tablet.MysqlctldProcess.Stop() + tablet.VttabletProcess.TearDown() + os.RemoveAll(tablet.VttabletProcess.Directory) + + return tablet.MysqlctldProcess.Start() +} + +// ValidateTabletRestart restarts the tablet and validate error if there is any. +func (tablet *Vttablet) ValidateTabletRestart(t *testing.T) { + require.Nilf(t, tablet.Restart(), "tablet restart failed") +} + // GetMasterPosition gets the master position of required vttablet func GetMasterPosition(t *testing.T, vttablet Vttablet, hostname string) (string, string) { ctx := context.Background() @@ -50,7 +77,7 @@ func VerifyRowsInTablet(t *testing.T, vttablet *Vttablet, ksName string, expecte timeout := time.Now().Add(10 * time.Second) for time.Now().Before(timeout) { qr, err := vttablet.VttabletProcess.QueryTablet("select * from vt_insert_test", ksName, true) - assert.Nil(t, err) + require.Nil(t, err) if len(qr.Rows) == expectedRows { return } @@ -120,3 +147,22 @@ func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet { portMap["grpc"] = int32(tabletGrpcPort) return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap} } + +// NewConnParams creates ConnParams corresponds to given arguments. +func NewConnParams(port int, password, socketPath, keyspace string) mysql.ConnParams { + if port != 0 { + socketPath = "" + } + cp := mysql.ConnParams{ + Uname: "vt_dba", + Port: port, + UnixSocket: socketPath, + Pass: password, + } + + if keyspace != "" { + cp.DbName = "vt_" + keyspace + } + + return cp +} diff --git a/go/test/endtoend/cluster/mysqlctl_process.go b/go/test/endtoend/cluster/mysqlctl_process.go index d7bf2f3e020..2ceb6cc1995 100644 --- a/go/test/endtoend/cluster/mysqlctl_process.go +++ b/go/test/endtoend/cluster/mysqlctl_process.go @@ -91,7 +91,6 @@ func (mysqlctl *MysqlctlProcess) Stop() (err error) { return err } return tmpProcess.Wait() - } // StopProcess executes mysqlctl command to stop mysql instance and returns process reference @@ -134,11 +133,7 @@ func MysqlCtlProcessInstance(tabletUID int, mySQLPort int, tmpDirectory string) // StartMySQL starts mysqlctl process func StartMySQL(ctx context.Context, tablet *Vttablet, username string, tmpDirectory string) error { tablet.MysqlctlProcess = *MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, tmpDirectory) - err := tablet.MysqlctlProcess.Start() - if err != nil { - return err - } - return nil + return tablet.MysqlctlProcess.Start() } // StartMySQLAndGetConnection create a connection to tablet mysql @@ -153,8 +148,7 @@ func StartMySQLAndGetConnection(ctx context.Context, tablet *Vttablet, username UnixSocket: path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", tablet.TabletUID), "/mysql.sock"), } - conn, err := mysql.Connect(ctx, ¶ms) - return conn, err + return mysql.Connect(ctx, ¶ms) } // ExecuteCommandWithOutput executes any mysqlctl command and returns output diff --git a/go/test/endtoend/cluster/mysqlctld_process.go b/go/test/endtoend/cluster/mysqlctld_process.go new file mode 100644 index 00000000000..855315fbe7d --- /dev/null +++ b/go/test/endtoend/cluster/mysqlctld_process.go @@ -0,0 +1,170 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cluster + +import ( + "context" + "fmt" + "os" + "os/exec" + "path" + "strings" + "time" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/vt/log" +) + +// MysqlctldProcess is a generic handle for a running mysqlctld command . +// It can be spawned manually +type MysqlctldProcess struct { + Name string + Binary string + LogDirectory string + Password string + TabletUID int + MySQLPort int + InitDBFile string + ExtraArgs []string + process *exec.Cmd + exit chan error + InitMysql bool + exitSignalReceived bool +} + +// InitDb executes mysqlctld command to add cell info +func (mysqlctld *MysqlctldProcess) InitDb() (err error) { + tmpProcess := exec.Command( + mysqlctld.Binary, + "-log_dir", mysqlctld.LogDirectory, + "-tablet_uid", fmt.Sprintf("%d", mysqlctld.TabletUID), + "-mysql_port", fmt.Sprintf("%d", mysqlctld.MySQLPort), + "-init_db_sql_file", mysqlctld.InitDBFile, + ) + return tmpProcess.Run() +} + +// Start starts the mysqlctld and returns the error. +func (mysqlctld *MysqlctldProcess) Start() error { + if mysqlctld.process != nil { + return fmt.Errorf("process is already running") + } + _ = createDirectory(mysqlctld.LogDirectory, 0700) + tempProcess := exec.Command( + mysqlctld.Binary, + "-log_dir", mysqlctld.LogDirectory, + "-tablet_uid", fmt.Sprintf("%d", mysqlctld.TabletUID), + "-mysql_port", fmt.Sprintf("%d", mysqlctld.MySQLPort), + ) + + tempProcess.Args = append(tempProcess.Args, mysqlctld.ExtraArgs...) + + if mysqlctld.InitMysql { + tempProcess.Args = append(tempProcess.Args, + "-init_db_sql_file", mysqlctld.InitDBFile) + } + + errFile, _ := os.Create(path.Join(mysqlctld.LogDirectory, "mysqlctld-stderr.txt")) + tempProcess.Stderr = errFile + + tempProcess.Env = append(tempProcess.Env, os.Environ()...) + tempProcess.Stdout = os.Stdout + tempProcess.Stderr = os.Stderr + + log.Infof("%v %v", strings.Join(tempProcess.Args, " ")) + + err := tempProcess.Start() + if err != nil { + return err + } + + mysqlctld.process = tempProcess + + mysqlctld.exit = make(chan error) + go func(mysqlctld *MysqlctldProcess) { + err := mysqlctld.process.Wait() + if !mysqlctld.exitSignalReceived { + fmt.Printf("mysqlctld stopped unexpectedly, tabletUID %v, mysql port %v, PID %v\n", mysqlctld.TabletUID, mysqlctld.MySQLPort, mysqlctld.process.Process.Pid) + } + mysqlctld.process = nil + mysqlctld.exitSignalReceived = false + mysqlctld.exit <- err + }(mysqlctld) + + timeout := time.Now().Add(60 * time.Second) + for time.Now().Before(timeout) { + if mysqlctld.IsHealthy() { + return nil + } + select { + case err := <-mysqlctld.exit: + return fmt.Errorf("process '%s' exited prematurely (err: %s)", mysqlctld.Name, err) + default: + time.Sleep(300 * time.Millisecond) + } + } + + return fmt.Errorf("process '%s' timed out after 60s (err: %s)", mysqlctld.Name, mysqlctld.Stop()) + +} + +// Stop executes mysqlctld command to stop mysql instance +func (mysqlctld *MysqlctldProcess) Stop() error { + // if mysqlctld.process == nil || mysqlctld.exit == nil { + // return nil + // } + mysqlctld.exitSignalReceived = true + tmpProcess := exec.Command( + "mysqlctl", + "-tablet_uid", fmt.Sprintf("%d", mysqlctld.TabletUID), + ) + tmpProcess.Args = append(tmpProcess.Args, mysqlctld.ExtraArgs...) + tmpProcess.Args = append(tmpProcess.Args, "shutdown") + err := tmpProcess.Run() + if err != nil { + return err + } + return <-mysqlctld.exit +} + +// CleanupFiles clean the mysql files to make sure we can start the same process again +func (mysqlctld *MysqlctldProcess) CleanupFiles(tabletUID int) { + os.RemoveAll(path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", tabletUID))) +} + +// MysqlCtldProcessInstance returns a Mysqlctld handle for mysqlctld process +// configured with the given Config. +func MysqlCtldProcessInstance(tabletUID int, mySQLPort int, tmpDirectory string) *MysqlctldProcess { + mysqlctld := &MysqlctldProcess{ + Name: "mysqlctld", + Binary: "mysqlctld", + LogDirectory: tmpDirectory, + InitDBFile: path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"), + } + mysqlctld.MySQLPort = mySQLPort + mysqlctld.TabletUID = tabletUID + mysqlctld.InitMysql = true + return mysqlctld +} + +// IsHealthy gives the health status of mysql. +func (mysqlctld *MysqlctldProcess) IsHealthy() bool { + socketFile := path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d", mysqlctld.TabletUID), "/mysql.sock") + params := NewConnParams(0, mysqlctld.Password, socketFile, "") + _, err := mysql.Connect(context.Background(), ¶ms) + return err == nil +} diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index f4a98715d12..c919f97555a 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -295,20 +295,10 @@ func (vttablet *VttabletProcess) CreateDB(keyspace string) error { // QueryTablet lets you execute a query in this tablet and get the result func (vttablet *VttabletProcess) QueryTablet(query string, keyspace string, useDb bool) (*sqltypes.Result, error) { - dbParams := mysql.ConnParams{ - Uname: "vt_dba", - } - if vttablet.DbPort > 0 { - dbParams.Port = vttablet.DbPort - } else { - dbParams.UnixSocket = path.Join(vttablet.Directory, "mysql.sock") - } - if useDb { - dbParams.DbName = "vt_" + keyspace - } - if vttablet.DbPassword != "" { - dbParams.Pass = vttablet.DbPassword + if !useDb { + keyspace = "" } + dbParams := NewConnParams(vttablet.DbPort, vttablet.DbPassword, path.Join(vttablet.Directory, "mysql.sock"), keyspace) return executeQuery(dbParams, query) } diff --git a/go/test/endtoend/mysqlctl/mysqlctl_test.go b/go/test/endtoend/mysqlctl/mysqlctl_test.go index b5f70954eb9..abe7aa59622 100644 --- a/go/test/endtoend/mysqlctl/mysqlctl_test.go +++ b/go/test/endtoend/mysqlctl/mysqlctl_test.go @@ -43,7 +43,7 @@ func TestMain(m *testing.M) { flag.Parse() exitCode := func() int { - clusterInstance = &cluster.LocalProcessCluster{Cell: cell, Hostname: hostname} + clusterInstance = cluster.NewCluster(cell, hostname) defer clusterInstance.Teardown() // Start topo server @@ -58,7 +58,7 @@ func TestMain(m *testing.M) { initCluster([]string{"0"}, 2) - // Collect table paths and ports + // Collect tablet paths and ports tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets for _, tablet := range tablets { if tablet.Type == "master" { @@ -97,11 +97,11 @@ func initCluster(shardNames []string, totalTabletsRequired int) { } // Start Mysqlctl process tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory) - if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { + proc, err := tablet.MysqlctlProcess.StartProcess() + if err != nil { return - } else { - mysqlCtlProcessList = append(mysqlCtlProcessList, proc) } + mysqlCtlProcessList = append(mysqlCtlProcessList, proc) // start vttablet process tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort, diff --git a/go/test/endtoend/mysqlctld/mysqlctld_test.go b/go/test/endtoend/mysqlctld/mysqlctld_test.go new file mode 100644 index 00000000000..d57ef1f5a59 --- /dev/null +++ b/go/test/endtoend/mysqlctld/mysqlctld_test.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mysqlctld + +import ( + "flag" + "fmt" + "os" + "testing" + + "vitess.io/vitess/go/vt/log" + + "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + masterTablet *cluster.Vttablet + replicaTablet *cluster.Vttablet + hostname = "localhost" + keyspaceName = "test_keyspace" + shardName = "0" + cell = "zone1" +) + +func TestMain(m *testing.M) { + flag.Parse() + + exitCode := func() int { + clusterInstance = cluster.NewCluster(cell, hostname) + defer clusterInstance.Teardown() + + // Start topo server + err := clusterInstance.StartTopo() + if err != nil { + return 1 + } + + if err := clusterInstance.VtctlProcess.CreateKeyspace(keyspaceName); err != nil { + return 1 + } + + if err := initCluster([]string{"0"}, 2); err != nil { + return 1 + } + + // Collect tablet paths and ports + tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets + for _, tablet := range tablets { + if tablet.Type == "master" { + masterTablet = tablet + } else if tablet.Type != "rdonly" { + replicaTablet = tablet + } + } + + return m.Run() + }() + os.Exit(exitCode) +} + +func initCluster(shardNames []string, totalTabletsRequired int) error { + keyspace := cluster.Keyspace{ + Name: keyspaceName, + } + for _, shardName := range shardNames { + shard := &cluster.Shard{ + Name: shardName, + } + for i := 0; i < totalTabletsRequired; i++ { + // instantiate vttablet object with reserved ports + tabletUID := clusterInstance.GetAndReserveTabletUID() + tablet := &cluster.Vttablet{ + TabletUID: tabletUID, + HTTPPort: clusterInstance.GetAndReservePort(), + GrpcPort: clusterInstance.GetAndReservePort(), + MySQLPort: clusterInstance.GetAndReservePort(), + Alias: fmt.Sprintf("%s-%010d", clusterInstance.Cell, tabletUID), + } + if i == 0 { // Make the first one as master + tablet.Type = "master" + } + // Start Mysqlctld process + tablet.MysqlctldProcess = *cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory) + err := tablet.MysqlctldProcess.Start() + if err != nil { + return err + } + + // start vttablet process + tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort, + tablet.GrpcPort, + tablet.TabletUID, + clusterInstance.Cell, + shardName, + keyspaceName, + clusterInstance.VtctldProcess.Port, + tablet.Type, + clusterInstance.TopoProcess.Port, + clusterInstance.Hostname, + clusterInstance.TmpDirectory, + clusterInstance.VtTabletExtraArgs, + clusterInstance.EnableSemiSync) + tablet.Alias = tablet.VttabletProcess.TabletPath + + shard.Vttablets = append(shard.Vttablets, tablet) + } + + for _, tablet := range shard.Vttablets { + if _, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("create database vt_%s", keyspace.Name), "", false); err != nil { + log.Error(err.Error()) + return err + } + } + + keyspace.Shards = append(keyspace.Shards, *shard) + } + clusterInstance.Keyspaces = append(clusterInstance.Keyspaces, keyspace) + + return nil +} + +func TestRestart(t *testing.T) { + err := masterTablet.MysqlctldProcess.Stop() + assert.Nil(t, err) + masterTablet.MysqlctldProcess.CleanupFiles(masterTablet.TabletUID) + err = masterTablet.MysqlctldProcess.Start() + assert.Nil(t, err) +} + +func TestAutoDetect(t *testing.T) { + + // Start up tablets with an empty MYSQL_FLAVOR, which means auto-detect + sqlFlavor := os.Getenv("MYSQL_FLAVOR") + os.Setenv("MYSQL_FLAVOR", "") + + err := clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].VttabletProcess.Setup() + assert.Nil(t, err, "error should be nil") + err = clusterInstance.Keyspaces[0].Shards[0].Vttablets[1].VttabletProcess.Setup() + assert.Nil(t, err, "error should be nil") + + // Reparent tablets, which requires flavor detection + err = clusterInstance.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, masterTablet.TabletUID) + assert.Nil(t, err, "error should be nil") + + //Reset flavor + os.Setenv("MYSQL_FLAVOR", sqlFlavor) + +} diff --git a/go/test/endtoend/recovery/recovery_util.go b/go/test/endtoend/recovery/recovery_util.go index c3ef62b1bc0..cb98c7c2409 100644 --- a/go/test/endtoend/recovery/recovery_util.go +++ b/go/test/endtoend/recovery/recovery_util.go @@ -39,11 +39,10 @@ func ExecuteQueriesUsingVtgate(t *testing.T, session *vtgateconn.VTGateSession, } func RestoreTablet(t *testing.T, localCluster *cluster.LocalProcessCluster, tablet *cluster.Vttablet, restoreKSName string, shardName string, keyspaceName string, commonTabletArg []string) { - err := cluster.ResetTabletDirectory(*tablet) - assert.Nil(t, err) + tablet.ValidateTabletRestart(t) tm := time.Now().UTC() tm.Format(time.RFC3339) - _, err = localCluster.VtctlProcess.ExecuteCommandWithOutput("CreateKeyspace", + _, err := localCluster.VtctlProcess.ExecuteCommandWithOutput("CreateKeyspace", "-keyspace_type=SNAPSHOT", "-base_keyspace="+keyspaceName, "-snapshot_time", tm.Format(time.RFC3339), restoreKSName) assert.Nil(t, err) diff --git a/test/config.json b/test/config.json index bdd3f426435..5e5a1b0c6e9 100644 --- a/test/config.json +++ b/test/config.json @@ -1,23 +1,5 @@ { "Tests": { - "backup_mysqlctld": { - "File": "backup_mysqlctld.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 0, - "RetryMax": 0, - "Tags": [] - }, - "backup_transform_mysqlctld": { - "File": "backup_transform_mysqlctld.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 0, - "RetryMax": 0, - "Tags": [] - }, "check_make_parser": { "File": "", "Args": [], @@ -48,7 +30,7 @@ "java_test" ], "Manual": false, - "Shard": 4, + "Shard": 0, "RetryMax": 0, "Tags": [] }, @@ -57,7 +39,7 @@ "Args": [], "Command": [], "Manual": false, - "Shard": 4, + "Shard": 1, "RetryMax": 0, "Tags": [ "worker_test" @@ -81,7 +63,7 @@ "test/client_test.sh" ], "Manual": false, - "Shard": 3, + "Shard": 2, "RetryMax": 0, "Tags": [] }, @@ -108,7 +90,7 @@ "Args": [], "Command": [], "Manual": false, - "Shard": 4, + "Shard": 2, "RetryMax": 0, "Tags": [] }, @@ -117,7 +99,7 @@ "Args": [], "Command": [], "Manual": false, - "Shard": 4, + "Shard": 2, "RetryMax": 0, "Tags": [] }, @@ -126,7 +108,7 @@ "Args": [], "Command": [], "Manual": false, - "Shard": 4, + "Shard": 1, "RetryMax": 0, "Tags": [ "webdriver" @@ -170,6 +152,15 @@ "RetryMax": 0, "Tags": [] }, + "backup_mysqlctld": { + "File": "backup_mysqlctld_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/backup/mysqlctld"], + "Command": [], + "Manual": false, + "Shard": 21, + "RetryMax": 0, + "Tags": [] + }, "backup_only": { "File": "backup_only.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtbackup"], @@ -179,6 +170,24 @@ "RetryMax": 0, "Tags": [] }, + "backup_transform": { + "File": "backup_transform_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/backup/transform"], + "Command": [], + "Manual": false, + "Shard": 19, + "RetryMax": 0, + "Tags": [] + }, + "backup_transform_mysqlctld": { + "File": "backup_transform_mysqlctld_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/backup/transform/mysqlctld"], + "Command": [], + "Manual": false, + "Shard": 21, + "RetryMax": 0, + "Tags": [] + }, "backup_xtrabackup": { "File": "xtrabackup.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/xtrabackup"], @@ -215,15 +224,6 @@ "RetryMax": 0, "Tags": [] }, - "backup_transform": { - "File": "backup_transform_test.go", - "Args": ["vitess.io/vitess/go/test/endtoend/backuptransform"], - "Command": [], - "Manual": false, - "Shard": 12, - "RetryMax": 0, - "Tags": [] - }, "prepare_statement": { "File": "stmt_methods_test.go", "Args": ["vitess.io/vitess/go/test/endtoend/preparestmt"], @@ -340,6 +340,17 @@ "site_test" ] }, + "mysqlctld": { + "File": "mysqlctld_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/mysqlctld"], + "Command": [], + "Manual": false, + "Shard": 12, + "RetryMax": 0, + "Tags": [ + "site_test" + ] + }, "recovery": { "File": "recovery.go", "Args": ["vitess.io/vitess/go/test/endtoend/recovery/unshardedrecovery"], From 7bc9bf5e001b3542c671fa609131b469b3af3b40 Mon Sep 17 00:00:00 2001 From: deepthi Date: Thu, 6 Feb 2020 17:37:26 -0800 Subject: [PATCH 053/825] add more docs to flag, wait for mysqld before attempting to populate metadata Signed-off-by: deepthi --- go/vt/vttablet/tabletmanager/action_agent.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index 5382d9006c8..336ab496d97 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -82,7 +82,7 @@ const ( var ( tabletHostname = flag.String("tablet_hostname", "", "if not empty, this hostname will be assumed instead of trying to resolve it") demoteMasterType = flag.String("demote_master_type", "REPLICA", "the tablet type a demoted master will transition to") - initPopulateMetadata = flag.Bool("init_populate_metadata", false, "(init parameter) populate metadata tables") + initPopulateMetadata = flag.Bool("init_populate_metadata", false, "(init parameter) populate metadata tables even if restore_from_backup is disabled. If restore_from_backup is enabled, metadata tables are always populated regardless of this flag.") ) // ActionAgent is the main class for the agent. @@ -358,8 +358,16 @@ func NewActionAgent( } else { // optionally populate metadata records if *initPopulateMetadata { - // we can use agent.initialTablet here because it is set by Start() + // we use initialTablet here because it has the intended tabletType. + // the tablet returned by agent.Tablet() will have type UNKNOWN until we call + // refreshTablet localMetadata := agent.getLocalMetadataValues(agent.initialTablet.Type) + if agent.Cnf != nil { // we are managing mysqld + // we'll use batchCtx here because we are still initializing and can't proceed unless this succeeds + if err := agent.MysqlDaemon.Wait(batchCtx, agent.Cnf); err != nil { + return nil, err + } + } err := mysqlctl.PopulateMetadataTables(agent.MysqlDaemon, localMetadata, topoproto.TabletDbName(agent.initialTablet)) if err != nil { return nil, vterrors.Wrap(err, "failed to -init_populate_metadata") From 591b895f8fc2f2f18988142a31fed6759e55d2c4 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Fri, 7 Feb 2020 09:55:16 +0530 Subject: [PATCH 054/825] messaging: review changes. Signed-off-by: pradip parmar --- go/test/endtoend/messaging/main_test.go | 4 +--- go/test/endtoend/messaging/messaging_test.go | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/go/test/endtoend/messaging/main_test.go b/go/test/endtoend/messaging/main_test.go index 8528d83e750..f9ae7adfd5f 100644 --- a/go/test/endtoend/messaging/main_test.go +++ b/go/test/endtoend/messaging/main_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Vitess Authors. +Copyright 2020 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -34,8 +34,6 @@ var ( shard1Master *cluster.Vttablet lookupMaster *cluster.Vttablet hostname = "localhost" - testingID = 1 - tableName = "vt_prepare_stmt_test" cell = "zone1" userKeyspace = "user" lookupKeyspace = "lookup" diff --git a/go/test/endtoend/messaging/messaging_test.go b/go/test/endtoend/messaging/messaging_test.go index 70e6c3333c1..7f2768c9cd7 100644 --- a/go/test/endtoend/messaging/messaging_test.go +++ b/go/test/endtoend/messaging/messaging_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Vitess Authors. +Copyright 2020 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 8f1f4c6ce1ee077cc0341bae4af61e99365139d1 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 7 Feb 2020 12:21:43 +0530 Subject: [PATCH 055/825] updated config.json to use dummy file name Signed-off-by: Ajeet jain --- test/config.json | 91 +++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/test/config.json b/test/config.json index 5e5a1b0c6e9..da2ba9add77 100644 --- a/test/config.json +++ b/test/config.json @@ -144,7 +144,7 @@ "Tags": [] }, "backup": { - "File": "backup.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtctlbackup"], "Command": [], "Manual": false, @@ -153,7 +153,7 @@ "Tags": [] }, "backup_mysqlctld": { - "File": "backup_mysqlctld_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/mysqlctld"], "Command": [], "Manual": false, @@ -162,7 +162,7 @@ "Tags": [] }, "backup_only": { - "File": "backup_only.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtbackup"], "Command": [], "Manual": false, @@ -171,7 +171,7 @@ "Tags": [] }, "backup_transform": { - "File": "backup_transform_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/transform"], "Command": [], "Manual": false, @@ -180,7 +180,7 @@ "Tags": [] }, "backup_transform_mysqlctld": { - "File": "backup_transform_mysqlctld_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/transform/mysqlctld"], "Command": [], "Manual": false, @@ -189,7 +189,7 @@ "Tags": [] }, "backup_xtrabackup": { - "File": "xtrabackup.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/xtrabackup"], "Command": [], "Manual": false, @@ -198,7 +198,7 @@ "Tags": [] }, "backup_xtrabackup_xbstream": { - "File": "xtrabackup_xbstream.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/xtrabackupstream"], "Command": [], "Manual": false, @@ -207,7 +207,7 @@ "Tags": [] }, "binlog": { - "File": "binlog.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/binlog"], "Command": [], "Manual": false, @@ -216,7 +216,7 @@ "Tags": [] }, "cellalias": { - "File": "cellalias.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/cellalias"], "Command": [], "Manual": false, @@ -225,7 +225,7 @@ "Tags": [] }, "prepare_statement": { - "File": "stmt_methods_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/preparestmt"], "Command": [], "Manual": false, @@ -234,7 +234,7 @@ "Tags": [] }, "mysql_server": { - "File": "mysql_server_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/mysqlserver"], "Command": [], "Manual": false, @@ -243,7 +243,7 @@ "Tags": [] }, "clustertest": { - "File": "clustertest.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/clustertest"], "Command": [], "Manual": false, @@ -252,7 +252,7 @@ "Tags": [] }, "encrypted_replication": { - "File": "encrypted_replication.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/encryption/encryptedreplication"], "Command": [], "Manual": false, @@ -261,7 +261,7 @@ "Tags": [] }, "encrypted_transport": { - "File": "encrypted_transport.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/encryption/encryptedtransport"], "Command": [], "Manual": false, @@ -270,7 +270,7 @@ "Tags": [] }, "initial_sharding": { - "File": "initial_sharding.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/initialsharding/v3"], "Command": [], "Manual": false, @@ -279,7 +279,7 @@ "Tags": [] }, "initial_sharding_bytes": { - "File": "initial_sharding_bytes.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/initialsharding/bytes"], "Command": [], "Manual": false, @@ -288,7 +288,7 @@ "Tags": [] }, "initial_sharding_multi": { - "File": "initial_sharding_multi.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/initialsharding/multi"], "Command": [], "Manual": false, @@ -297,7 +297,7 @@ "Tags": [] }, "keyspace": { - "File": "keyspace.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/keyspace"], "Command": [], "Manual": false, @@ -308,7 +308,7 @@ ] }, "merge_sharding": { - "File": "mergesharding_int_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/int"], "Command": [], "Manual": false, @@ -319,7 +319,7 @@ ] }, "merge_sharding_bytes": { - "File": "mergesharding_string_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/mergesharding/string"], "Command": [], "Manual": false, @@ -330,7 +330,7 @@ ] }, "mysqlctl": { - "File": "mysqlctl.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/mysqlctl"], "Command": [], "Manual": false, @@ -341,7 +341,7 @@ ] }, "mysqlctld": { - "File": "mysqlctld_test.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/mysqlctld"], "Command": [], "Manual": false, @@ -352,7 +352,7 @@ ] }, "recovery": { - "File": "recovery.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/recovery/unshardedrecovery"], "Command": [], "Manual": false, @@ -361,7 +361,7 @@ "Tags": [] }, "reparent": { - "File": "reparent.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/reparent"], "Command": [], "Manual": false, @@ -370,7 +370,7 @@ "Tags": [] }, "resharding": { - "File": "resharding.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/resharding/v3"], "Command": [], "Manual": false, @@ -381,7 +381,7 @@ ] }, "resharding_bytes": { - "File": "resharding_bytes.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/resharding/string"], "Command": [], "Manual": false, @@ -392,7 +392,7 @@ ] }, "sharded": { - "File": "sharded.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharded"], "Command": [], "Manual": false, @@ -401,7 +401,7 @@ "Tags": [] }, "sharded_recovery": { - "File": "sharded_recovery.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/recovery/shardedrecovery"], "Command": [], "Manual": false, @@ -410,7 +410,7 @@ "Tags": [] }, "tabletmanager": { - "File": "tabletmanager.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/tabletmanager"], "Command": [], "Manual": false, @@ -420,10 +420,21 @@ "site_test" ] }, + "tabletmanager_master": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/tabletmanager/master"], + "Command": [], + "Manual": false, + "Shard": 18, + "RetryMax": 0, + "Tags": [ + "site_test" + ] + }, "tabletmanager_consul": { - "File": "tabletmanager.go", + "File": "unused.go", "Args": [ - "vitess.io/vitess/go/test/endtoend/tabletmanager","--topo-server-flavor=consul" + "vitess.io/vitess/go/test/endtoend/tabletmanager","--topo-flavor=consul" ], "Command": [], "Manual": false, @@ -434,9 +445,9 @@ ] }, "tabletmanager_zk2": { - "File": "tabletmanager.go", + "File": "unused.go", "Args": [ - "vitess.io/vitess/go/test/endtoend/tabletmanager","--topo-server-flavor=zk2" + "vitess.io/vitess/go/test/endtoend/tabletmanager","--topo-flavor=zk2" ], "Command": [], "Manual": false, @@ -447,7 +458,7 @@ ] }, "vertical_split": { - "File": "vertical_split.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/sharding/verticalsplit"], "Command": [], "Manual": false, @@ -458,7 +469,7 @@ ] }, "vtgate": { - "File": "vtgate.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate"], "Command": [], "Manual": false, @@ -467,7 +478,7 @@ "Tags": [] }, "vtgate_buffer": { - "File": "vtgate_buffer.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/buffer"], "Command": [], "Manual": false, @@ -476,7 +487,7 @@ "Tags": [] }, "vtgate_schema": { - "File": "vtgate_schema.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/schema"], "Command": [], "Manual": false, @@ -485,7 +496,7 @@ "Tags": [] }, "vtgate_sequence": { - "File": "vtgate_sequence.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/sequence"], "Command": [], "Manual": false, @@ -494,7 +505,7 @@ "Tags": [] }, "vtgate_transaction": { - "File": "vtgate_transaction.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/transaction"], "Command": [], "Manual": false, @@ -503,7 +514,7 @@ "Tags": [] }, "vtgate_vschema": { - "File": "vtgate_vschema.go", + "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/vschema"], "Command": [], "Manual": false, From 258018895978dd704b941659f35b28a16e05f196 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 7 Feb 2020 13:35:03 +0530 Subject: [PATCH 056/825] fixed test.go framework for go cluster tests Signed-off-by: Ajeet jain --- test.go | 1 - tools/e2e_go_test.sh | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test.go b/test.go index f5646f407b2..b6be41a86e6 100755 --- a/test.go +++ b/test.go @@ -167,7 +167,6 @@ func (t *Test) run(dir, dataDir string) ([]byte, error) { if strings.Contains(fmt.Sprintf("%v", t.File), ".go") { testCmd = []string{"tools/e2e_go_test.sh"} testCmd = append(testCmd, t.Args...) - testCmd = append(testCmd, "--skip-build", "--keep-logs") } else { testCmd = []string{"test/" + t.File, "-v", "--skip-build", "--keep-logs"} testCmd = append(testCmd, t.Args...) diff --git a/tools/e2e_go_test.sh b/tools/e2e_go_test.sh index 7f65d17feee..9148d80e156 100755 --- a/tools/e2e_go_test.sh +++ b/tools/e2e_go_test.sh @@ -1,4 +1,4 @@ #!/bin/bash source build.env -echo "running tests for $1" -go test -v $1 -count=1 \ No newline at end of file +echo "running tests for $@ " +go test -v $@ -count=1 \ No newline at end of file From 17712ebb5d1919b4eaa6e34ad9110dd65e100d9e Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 7 Feb 2020 15:04:40 +0530 Subject: [PATCH 057/825] added consul and zookeeper in the endtoend yaml Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index efd2ff72943..95606b699f9 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -20,7 +20,7 @@ jobs: - name: Get dependencies run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd consul zookeeper curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ From 3a31d29c176417546671f5ad7a63cbbc7111111d Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 7 Feb 2020 16:28:55 +0530 Subject: [PATCH 058/825] download and unzip consul Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 95606b699f9..2492be80974 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -20,7 +20,7 @@ jobs: - name: Get dependencies run: | sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd consul zookeeper curl git wget eatmydata + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata sudo service mysql stop sudo service etcd stop sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ @@ -29,6 +29,8 @@ jobs: wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb sudo apt-get install -y gnupg2 sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb + wget https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_linux_amd64.zip + unzip consul_1.4.0_linux_amd64.zip sudo apt-get update sudo apt-get install percona-xtrabackup-24 From c7f1cadcf32d33d4bf583916e94748493e7d8d88 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 7 Feb 2020 17:04:39 +0530 Subject: [PATCH 059/825] trying different way for consul Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 7 +++++-- tools/e2e_go_test.sh | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 2492be80974..87c7970d5a0 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -29,10 +29,13 @@ jobs: wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb sudo apt-get install -y gnupg2 sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb - wget https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_linux_amd64.zip - unzip consul_1.4.0_linux_amd64.zip sudo apt-get update sudo apt-get install percona-xtrabackup-24 + if [ ${{matrix.name}} = 18 ]; then + wget https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_linux_amd64.zip + unzip consul_1.4.0_linux_amd64.zip + export PATH="$PATH:$PWD" + if - name: sharded cluster_endtoend run: | diff --git a/tools/e2e_go_test.sh b/tools/e2e_go_test.sh index 9148d80e156..37c5191eec5 100755 --- a/tools/e2e_go_test.sh +++ b/tools/e2e_go_test.sh @@ -1,4 +1,4 @@ #!/bin/bash -source build.env +# source build.env echo "running tests for $@ " go test -v $@ -count=1 \ No newline at end of file From 9ad72ccc48a48b248332574589ee195e3fb2c437 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 7 Feb 2020 03:44:04 -0800 Subject: [PATCH 060/825] vreplication: fix bug on stop position Fixes #5800 The function hasAnother commit is supposed to separate out events that have to be applied in a transaction from events that are applied as autocommits. But it was not checking for the OTHER and JOURNAL events. They should not be batched with regular transactions. This caused a bug where a regular transaction occured followed by an OTHER event. The apply of this event happened assuming autocommit behavior, but it actually got added to the previous uncommitted transaction. If the stop position is reached at this point, vplayer exits because it thinks the autocommit happened, and the entire transaction gets rolled back, and the stop position ends up not being actually reached. The copier which expects this behavior will then start applying the next set of rows, but they are not consistent with the current stop position. This will cause the target to go out of sync. Signed-off-by: Sugu Sougoumarane --- .../tabletmanager/vreplication/vplayer.go | 16 ++- .../vreplication/vplayer_test.go | 108 ++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 511bf716dde..ed8571a4104 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -292,7 +292,7 @@ func hasAnotherCommit(items [][]*binlogdatapb.VEvent, i, j int) bool { switch items[i][j].Type { case binlogdatapb.VEventType_COMMIT: return true - case binlogdatapb.VEventType_DDL: + case binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER, binlogdatapb.VEventType_JOURNAL: return false } j++ @@ -367,6 +367,11 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m return err } case binlogdatapb.VEventType_OTHER: + if vp.vr.dbClient.InTransaction { + // Unreachable + log.Errorf("internal error: vplayer is in a transaction on event: %v", event) + return fmt.Errorf("internal error: vplayer is in a transaction on event: %v", event) + } // Just update the position. posReached, err := vp.updatePos(event.Timestamp) if err != nil { @@ -377,7 +382,9 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m } case binlogdatapb.VEventType_DDL: if vp.vr.dbClient.InTransaction { - return fmt.Errorf("unexpected state: DDL encountered in the middle of a transaction: %v", event.Ddl) + // Unreachable + log.Errorf("internal error: vplayer is in a transaction on event: %v", event) + return fmt.Errorf("internal error: vplayer is in a transaction on event: %v", event) } switch vp.vr.source.OnDdl { case binlogdatapb.OnDDLAction_IGNORE: @@ -427,6 +434,11 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m } } case binlogdatapb.VEventType_JOURNAL: + if vp.vr.dbClient.InTransaction { + // Unreachable + log.Errorf("internal error: vplayer is in a transaction on event: %v", event) + return fmt.Errorf("internal error: vplayer is in a transaction on event: %v", event) + } // Ensure that we don't have a partial set of table matches in the journal. switch event.Journal.MigrationType { case binlogdatapb.MigrationType_SHARDS: diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go index d84bfb3fdc0..777e6177cc6 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go @@ -1221,6 +1221,114 @@ func TestPlayerStopPos(t *testing.T) { }) } +func TestPlayerStopAtOther(t *testing.T) { + t.Skip("This test was written to verify a bug fix, but is extremely flaky. Only a manual test is possible") + + defer deleteTablet(addTablet(100)) + + execStatements(t, []string{ + "create table t1(id int, val varbinary(128), primary key(id))", + fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), + }) + defer execStatements(t, []string{ + "drop table t1", + fmt.Sprintf("drop table %s.t1", vrepldb), + }) + env.SchemaEngine.Reload(context.Background()) + + // Insert a source row. + execStatements(t, []string{ + "insert into t1 values(1, 'aaa')", + }) + startPos := masterPosition(t) + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "/.*", + }}, + } + bls := &binlogdatapb.BinlogSource{ + Keyspace: env.KeyspaceName, + Shard: env.ShardName, + Filter: filter, + OnDdl: binlogdatapb.OnDDLAction_IGNORE, + } + query := binlogplayer.CreateVReplicationState("test", bls, startPos, binlogplayer.BlpStopped, vrepldb) + qr, err := playerEngine.Exec(query) + if err != nil { + t.Fatal(err) + } + id := uint32(qr.InsertID) + for q := range globalDBQueries { + if strings.HasPrefix(q, "insert into _vt.vreplication") { + break + } + } + defer func() { + if _, err := playerEngine.Exec(fmt.Sprintf("delete from _vt.vreplication where id = %d", id)); err != nil { + t.Fatal(err) + } + expectDeleteQueries(t) + }() + + vconn := &realDBClient{nolog: true} + if err := vconn.Connect(); err != nil { + t.Error(err) + } + defer vconn.Close() + + // Insert the same row on the target and lock it. + if _, err := vconn.ExecuteFetch("insert into t1 values(1, 'aaa')", 1); err != nil { + t.Error(err) + } + if _, err := vconn.ExecuteFetch("begin", 1); err != nil { + t.Error(err) + } + if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil { + t.Error(err) + } + + // Start a VReplication where the first transaction updates the locked row. + // It will cause the apply to wait, which will cause the other two events + // to accumulate. The stop position will be on the grant. + // We're testing the behavior where an OTHER transaction is part of a batch, + // we have to commit its stop position correctly. + execStatements(t, []string{ + "update t1 set val='ccc' where id=1", + "insert into t1 values(2, 'ddd')", + "grant select on *.* to 'vt_app'@'127.0.0.1'", + }) + stopPos := masterPosition(t) + query = binlogplayer.StartVReplicationUntil(id, stopPos) + if _, err := playerEngine.Exec(query); err != nil { + t.Fatal(err) + } + + // Wait for the begin. The update will be blocked. + expectDBClientQueries(t, []string{ + "/update.*'Running'", + // Second update is from vreplicator. + "/update.*'Running'", + "begin", + }) + + // Give time for the other two transactions to reach the relay log. + time.Sleep(100 * time.Millisecond) + _, _ = vconn.ExecuteFetch("rollback", 1) + + // This is approximately the expected sequence of updates. + expectDBClientQueries(t, []string{ + "update t1 set val='ccc' where id=1", + "/update _vt.vreplication set pos=", + "commit", + "begin", + "insert into t1(id,val) values (2,'ddd')", + "/update _vt.vreplication set pos=", + "commit", + fmt.Sprintf("/update _vt.vreplication set pos='%s'", stopPos), + "/update.*'Stopped'", + }) +} + func TestPlayerIdleUpdate(t *testing.T) { defer deleteTablet(addTablet(100)) From e51813579c98e2be48702efcce869e83ce98aaf5 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 7 Feb 2020 17:22:46 +0530 Subject: [PATCH 061/825] fix the typo Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 87c7970d5a0..76b50600e3f 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -35,7 +35,7 @@ jobs: wget https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_linux_amd64.zip unzip consul_1.4.0_linux_amd64.zip export PATH="$PATH:$PWD" - if + fi - name: sharded cluster_endtoend run: | From 50dff3852123a3e129c559e6850389c98b8affe6 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 7 Feb 2020 04:42:50 -0800 Subject: [PATCH 062/825] vreplication: add more logging Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletmanager/vreplication/engine.go | 2 ++ go/vt/vttablet/tabletmanager/vreplication/vcopier.go | 5 ++++- go/vt/vttablet/tabletmanager/vreplication/vplayer.go | 2 ++ go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index aff689f5569..00ac39d04aa 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -118,6 +118,7 @@ func (vre *Engine) Open(ctx context.Context) error { if vre.isOpen { return nil } + log.Infof("Starting VReplication engine") vre.ctx, vre.cancel = context.WithCancel(ctx) vre.isOpen = true @@ -219,6 +220,7 @@ func (vre *Engine) Close() { if !vre.isOpen { return } + log.Infof("Shutting down VReplication engine") vre.cancel() // We still have to wait for all controllers to stop. diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go index b663efe6e03..7e3b0fe4fc9 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go @@ -190,6 +190,7 @@ func (vc *vcopier) copyTable(ctx context.Context, tableName string, copyState ma var pkfields []*querypb.Field var updateCopyState *sqlparser.ParsedQuery + var bv map[string]*querypb.BindVariable err = vc.vr.sourceVStreamer.VStreamRows(ctx, initialPlan.SendRule.Filter, lastpkpb, func(rows *binlogdatapb.VStreamRowsResponse) error { select { case <-ctx.Done(): @@ -243,7 +244,7 @@ func (vc *vcopier) copyTable(ctx context.Context, tableName string, copyState ma if err != nil { return err } - bv := map[string]*querypb.BindVariable{ + bv = map[string]*querypb.BindVariable{ "lastpk": { Type: sqltypes.VarBinary, Value: buf.Bytes(), @@ -265,12 +266,14 @@ func (vc *vcopier) copyTable(ctx context.Context, tableName string, copyState ma // If there was a timeout, return without an error. select { case <-ctx.Done(): + log.Infof("Copy of %v stopped at lastpk: %v", tableName, bv) return nil default: } if err != nil { return err } + log.Infof("Copy of %v finished at lastpk: %v", tableName, bv) buf := sqlparser.NewTrackedBuffer(nil) buf.Myprintf("delete from _vt.copy_state where vrepl_id=%s and table_name=%s", strconv.Itoa(int(vc.vr.id)), encodeString(tableName)) if _, err := vc.vr.dbClient.Execute(buf.String()); err != nil { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index ed8571a4104..b3b551d452c 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -78,6 +78,7 @@ func newVPlayer(vr *vreplicator, settings binlogplayer.VRSettings, copyState map // play is not resumable. If pausePos is set, play returns without updating the vreplication state. func (vp *vplayer) play(ctx context.Context) error { if !vp.stopPos.IsZero() && vp.startPos.AtLeast(vp.stopPos) { + log.Infof("Stop position %v already reached: %v", vp.startPos, vp.stopPos) if vp.saveStop { return vp.vr.setState(binlogplayer.BlpStopped, fmt.Sprintf("Stop position %v already reached: %v", vp.startPos, vp.stopPos)) } @@ -211,6 +212,7 @@ func (vp *vplayer) updatePos(ts int64) (posReached bool, err error) { vp.vr.stats.SetLastPosition(vp.pos) posReached = !vp.stopPos.IsZero() && vp.pos.AtLeast(vp.stopPos) if posReached { + log.Infof("Stopped at position: %v", vp.stopPos) if vp.saveStop { if err := vp.vr.setState(binlogplayer.BlpStopped, fmt.Sprintf("Stopped at position %v", vp.stopPos)); err != nil { return false, err diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go index dc98078286d..71254a81689 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go @@ -290,6 +290,7 @@ func (rs *rowStreamer) startStreaming(conn *mysql.Conn) (string, error) { return "", err } + log.Infof("Streaming query: %v\n", rs.sendQuery) if err := conn.ExecuteStreamFetch(rs.sendQuery); err != nil { return "", err } From d028aa7a7063057017837894442a2c2b819bb053 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 7 Feb 2020 08:32:51 -0800 Subject: [PATCH 063/825] vrepl: address review comment Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/vstreamer/vstreamer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index fe1a049adc6..cc482dc5e21 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -125,19 +125,19 @@ func (vs *vstreamer) Cancel() { func (vs *vstreamer) Stream() error { defer vs.cancel() - curpos, err := vs.currentPosition() + curPos, err := vs.currentPosition() if err != nil { return vterrors.Wrap(err, "could not obtain current position") } if vs.startPos == "current" { - vs.pos = curpos + vs.pos = curPos } else { pos, err := mysql.DecodePosition(vs.startPos) if err != nil { return vterrors.Wrap(err, "could not decode position") } - if !curpos.AtLeast(pos) { - return fmt.Errorf("requested position %v is ahead of current position %v", mysql.EncodePosition(pos), mysql.EncodePosition(curpos)) + if !curPos.AtLeast(pos) { + return fmt.Errorf("requested position %v is ahead of current position %v", mysql.EncodePosition(pos), mysql.EncodePosition(curPos)) } vs.pos = pos } From 7953a5a8e4fb41924a7a5c76486f415b292911a4 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Fri, 7 Feb 2020 09:52:17 -0700 Subject: [PATCH 064/825] Re-enable MySQL 8.0 unit test Fetch MariaDB 10.1 from MariaDB repos (works more consistently) Signed-off-by: Morgan Tocker --- .github/workflows/unit.yml | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index ecccf5e2ad9..7acfc878219 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [percona56, mysql57, mariadb101, mariadb102, mariadb103] + name: [percona56, mysql57, mysql80, mariadb101, mariadb102, mariadb103] steps: - name: Set up Go @@ -19,41 +19,52 @@ jobs: - name: Get dependencies run: | + export DEBIAN_FRONTEND="noninteractive" sudo apt-get update if [ ${{matrix.name}} = "mysql57" ]; then sudo apt-get install -y mysql-server mysql-client else # Uninstall likely installed MySQL first - sudo apt-get remove -y mysql-server mysql-client - + sudo systemctl stop apparmor + sudo apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo apt-get -y autoremove + sudo apt-get -y autoclean + sudo deluser mysql + sudo rm -rf /var/lib/mysql + sudo rm -rf /etc/mysql + if [ ${{matrix.name}} = "percona56" ]; then sudo rm -rf /var/lib/mysql sudo apt install -y gnupg2 wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb sudo apt update - sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y percona-server-server-5.6 percona-server-client-5.6 + sudo apt-get install -y percona-server-server-5.6 percona-server-client-5.6 elif [ ${{matrix.name}} = "mysql80" ]; then wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.14-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections - sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* + sudo dpkg -i mysql-apt-config* sudo apt-get update - sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client + sudo apt-get install -y mysql-server mysql-client elif [ ${{matrix.name}} = "mariadb101" ]; then - sudo apt install -y mariadb-server mariadb-client + sudo apt-get install -y software-properties-common + sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 + sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.1/ubuntu bionic main' + sudo apt update + sudo apt install -y mariadb-server elif [ ${{matrix.name}} = "mariadb102" ]; then sudo apt-get install -y software-properties-common sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.2/ubuntu bionic main' sudo apt update - sudo DEBIAN_FRONTEND="noninteractive" apt install -y mariadb-server + sudo apt install -y mariadb-server elif [ ${{matrix.name}} = "mariadb103" ]; then sudo apt-get install -y software-properties-common sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.3/ubuntu bionic main' sudo apt update - sudo DEBIAN_FRONTEND="noninteractive" apt install -y mariadb-server + sudo apt install -y mariadb-server fi fi From 997835076afaf528d30dc8bf2cea2b634f30f76d Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Mon, 30 Dec 2019 21:11:57 -0800 Subject: [PATCH 065/825] vrepl: document vstreamer.go, binlogdata.proto Signed-off-by: Sugu Sougoumarane --- go/vt/proto/binlogdata/binlogdata.pb.go | 84 ++++++++++---- .../tabletserver/vstreamer/vstreamer.go | 46 +++++++- proto/binlogdata.proto | 107 ++++++++++++++---- 3 files changed, 187 insertions(+), 50 deletions(-) diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index 804e5f414e6..7078b5a9ea6 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -599,7 +599,8 @@ func (m *StreamTablesResponse) GetBinlogTransaction() *BinlogTransaction { // Rule represents one rule. type Rule struct { // match can be a table name or a regular expression - // delineated by '/' and '/'. + // if it starts with a '/'. For example, "/.*" matches + // all tables. Match string `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` // filter can be an empty string or keyrange if the match // is a regular expression. Otherwise, it must be a select @@ -699,8 +700,8 @@ func (m *Filter) GetFieldEventMode() Filter_FieldEventMode { } // BinlogSource specifies the source and filter parameters for -// Filtered Replication. It currently supports a keyrange -// or a list of tables. +// Filtered Replication. KeyRange and Tables are legacy. Filter +// is the new way to specify the filtering rules. type BinlogSource struct { // the source keyspace Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` @@ -816,7 +817,10 @@ func (m *BinlogSource) GetStopAfterCopy() bool { return false } -// RowChange represents one row change +// RowChange represents one row change. +// If Before is set and not After, it's a delete. +// If After is set and not Before, it's an insert. +// If both are set, it's an update. type RowChange struct { Before *query.Row `protobuf:"bytes,1,opt,name=before,proto3" json:"before,omitempty"` After *query.Row `protobuf:"bytes,2,opt,name=after,proto3" json:"after,omitempty"` @@ -864,7 +868,7 @@ func (m *RowChange) GetAfter() *query.Row { return nil } -// RowEvent represent row events for one table +// RowEvent represent row events for one table. type RowEvent struct { TableName string `protobuf:"bytes,1,opt,name=table_name,json=tableName,proto3" json:"table_name,omitempty"` RowChanges []*RowChange `protobuf:"bytes,2,rep,name=row_changes,json=rowChanges,proto3" json:"row_changes,omitempty"` @@ -912,6 +916,7 @@ func (m *RowEvent) GetRowChanges() []*RowChange { return nil } +// FieldEvent represents the field info for a table. type FieldEvent struct { TableName string `protobuf:"bytes,1,opt,name=table_name,json=tableName,proto3" json:"table_name,omitempty"` Fields []*query.Field `protobuf:"bytes,2,rep,name=fields,proto3" json:"fields,omitempty"` @@ -959,6 +964,11 @@ func (m *FieldEvent) GetFields() []*query.Field { return nil } +// ShardGtid contains the GTID position for one shard. +// It's used in a request for requesting a starting position. +// It's used in a response to transmit the current position +// of a shard. It's also used in a Journal to indicate the +// list of targets and shard positions to migrate to. type ShardGtid struct { Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` @@ -1014,6 +1024,7 @@ func (m *ShardGtid) GetGtid() string { return "" } +// A VGtid is a list of ShardGtids. type VGtid struct { ShardGtids []*ShardGtid `protobuf:"bytes,1,rep,name=shard_gtids,json=shardGtids,proto3" json:"shard_gtids,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1053,6 +1064,7 @@ func (m *VGtid) GetShardGtids() []*ShardGtid { return nil } +// KeyspaceShard represents a keyspace and shard. type KeyspaceShard struct { Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` @@ -1100,17 +1112,25 @@ func (m *KeyspaceShard) GetShard() string { return "" } +// Journal contains the metadata for a journal event. type Journal struct { - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - MigrationType MigrationType `protobuf:"varint,2,opt,name=migration_type,json=migrationType,proto3,enum=binlogdata.MigrationType" json:"migration_type,omitempty"` - Tables []string `protobuf:"bytes,3,rep,name=tables,proto3" json:"tables,omitempty"` - LocalPosition string `protobuf:"bytes,4,opt,name=local_position,json=localPosition,proto3" json:"local_position,omitempty"` - ShardGtids []*ShardGtid `protobuf:"bytes,5,rep,name=shard_gtids,json=shardGtids,proto3" json:"shard_gtids,omitempty"` - Participants []*KeyspaceShard `protobuf:"bytes,6,rep,name=participants,proto3" json:"participants,omitempty"` - SourceWorkflows []string `protobuf:"bytes,7,rep,name=source_workflows,json=sourceWorkflows,proto3" json:"source_workflows,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // Id represents a unique journal id. + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + MigrationType MigrationType `protobuf:"varint,2,opt,name=migration_type,json=migrationType,proto3,enum=binlogdata.MigrationType" json:"migration_type,omitempty"` + // Tables is set if the journal represents a TABLES migration. + Tables []string `protobuf:"bytes,3,rep,name=tables,proto3" json:"tables,omitempty"` + // LocalPosition is the source position at which the migration happened. + LocalPosition string `protobuf:"bytes,4,opt,name=local_position,json=localPosition,proto3" json:"local_position,omitempty"` + // ShardGtids is the list of targets to which the migration took place. + ShardGtids []*ShardGtid `protobuf:"bytes,5,rep,name=shard_gtids,json=shardGtids,proto3" json:"shard_gtids,omitempty"` + // Participants is the list of source participants for a migration. + Participants []*KeyspaceShard `protobuf:"bytes,6,rep,name=participants,proto3" json:"participants,omitempty"` + // SourceWorkflows is the list of workflows in the source shard that need + // to be migrated to the target. + SourceWorkflows []string `protobuf:"bytes,7,rep,name=source_workflows,json=sourceWorkflows,proto3" json:"source_workflows,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Journal) Reset() { *m = Journal{} } @@ -1187,18 +1207,32 @@ func (m *Journal) GetSourceWorkflows() []string { return nil } -// VEvent represents a vstream event +// VEvent represents a vstream event. +// A FieldEvent is sent once for every table, just before +// the first event for that table. The client is expected +// to cache this information and match it against the RowEvent +// which contains the table name. type VEvent struct { - Type VEventType `protobuf:"varint,1,opt,name=type,proto3,enum=binlogdata.VEventType" json:"type,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Gtid string `protobuf:"bytes,3,opt,name=gtid,proto3" json:"gtid,omitempty"` - Ddl string `protobuf:"bytes,4,opt,name=ddl,proto3" json:"ddl,omitempty"` - RowEvent *RowEvent `protobuf:"bytes,5,opt,name=row_event,json=rowEvent,proto3" json:"row_event,omitempty"` + Type VEventType `protobuf:"varint,1,opt,name=type,proto3,enum=binlogdata.VEventType" json:"type,omitempty"` + // Timestamp is the binlog timestamp in seconds. + // It's set for all events. + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // Gtid is set if the event type is GTID. + Gtid string `protobuf:"bytes,3,opt,name=gtid,proto3" json:"gtid,omitempty"` + // Ddl is set if the event type is DDL. + Ddl string `protobuf:"bytes,4,opt,name=ddl,proto3" json:"ddl,omitempty"` + // RowEvent is set if the event type is ROW. + RowEvent *RowEvent `protobuf:"bytes,5,opt,name=row_event,json=rowEvent,proto3" json:"row_event,omitempty"` + // FieldEvent is set if the event type is FIELD. FieldEvent *FieldEvent `protobuf:"bytes,6,opt,name=field_event,json=fieldEvent,proto3" json:"field_event,omitempty"` - Vgtid *VGtid `protobuf:"bytes,7,opt,name=vgtid,proto3" json:"vgtid,omitempty"` - Journal *Journal `protobuf:"bytes,8,opt,name=journal,proto3" json:"journal,omitempty"` - Dml string `protobuf:"bytes,9,opt,name=dml,proto3" json:"dml,omitempty"` - // current_time specifies the current time to handle clock skew. + // Vgtid is set if the event type is VGTID. + // This event is only generated by VTGate's VStream function. + Vgtid *VGtid `protobuf:"bytes,7,opt,name=vgtid,proto3" json:"vgtid,omitempty"` + // Journal is set if the event type is JOURNAL. + Journal *Journal `protobuf:"bytes,8,opt,name=journal,proto3" json:"journal,omitempty"` + // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. + Dml string `protobuf:"bytes,9,opt,name=dml,proto3" json:"dml,omitempty"` + // CurrentType specifies the current time to handle clock skew. CurrentTime int64 `protobuf:"varint,20,opt,name=current_time,json=currentTime,proto3" json:"current_time,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index cc482dc5e21..6c993a5a101 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -64,6 +64,7 @@ func NewVStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, // TODO(sougou): find a better way for this. var vschemaUpdateCount sync2.AtomicInt64 +// vstreamer is for serving a single vreplication stream on the source side. type vstreamer struct { ctx context.Context cancel func() @@ -85,12 +86,25 @@ type vstreamer struct { } // streamerPlan extends the original plan to also include -// the TableMap which is used to extract values from the binlog events. +// the TableMap, which comes from the binlog. It's used +// to extract values from the ROW events. type streamerPlan struct { *Plan TableMap *mysql.TableMap } +// newVStreamer creates a new vstreamer. +// cp: the mysql conn params. +// se: the schema engine. The vstreamer uses it to convert the TableMap into field info. +// startPos: a flavor compliant position to stream from. This can also contain the special +// value "current", which means start from the current position. +// filter: the list of filtering rules. If a rule has a select expressinon for its filter, +// the select list can only reference direct columns. No other experssions are allowed. +// The select expression is allowed to contain the special 'keyspace_id()' function which +// will return the keyspace id of the row. For more info, see the documentation +// for binlogdatapb.Filter. +// vschema: the current vschema. This value can later be changed through the SetVSchema method. +// send: callback function to send events. func newVStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { ctx, cancel := context.WithCancel(ctx) return &vstreamer{ @@ -107,7 +121,7 @@ func newVStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, } } -// SetVSchema updates all existing streams against the new vschema. +// SetVSchema updates the vstreamer against the new vschema. func (vs *vstreamer) SetVSchema(vschema *localVSchema) { // Since vs.Stream is a single-threaded loop. We just send an event to // that thread, which helps us avoid mutexes to update the plans. @@ -117,14 +131,16 @@ func (vs *vstreamer) SetVSchema(vschema *localVSchema) { } } +// Cancel stops the streaming. func (vs *vstreamer) Cancel() { vs.cancel() } -// Stream runs a single-threaded loop. +// Stream streams binlog events. func (vs *vstreamer) Stream() error { defer vs.cancel() + // Validate the request against the current position. curPos, err := vs.currentPosition() if err != nil { return vterrors.Wrap(err, "could not obtain current position") @@ -171,13 +187,22 @@ func (vs *vstreamer) currentPosition() (mysql.Position, error) { return conn.MasterPosition() } +// parseEvents parses and sends events. func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.BinlogEvent) error { // bufferAndTransmit uses bufferedEvents and curSize to buffer events. var ( bufferedEvents []*binlogdatapb.VEvent curSize int ) - // Buffering only takes row lengths into consideration. + // Only the following patterns are possible: + // BEGIN->ROWs or Statements->GTID->COMMIT. In the case of large transactions, this can be broken into chunks. + // BEGIN->JOURNAL->GTID->COMMIT + // GTID->DDL + // GTID->OTHER + // HEARTBEAT is issued if there's inactivity, which is likely + // to heppend between one group of events and another. + // + // Buffering only takes row or statement lengths into consideration. // Length of other events is considered negligible. // If a new row event causes the packet size to be exceeded, // all existing rows are sent without the new row. @@ -186,9 +211,14 @@ func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog switch vevent.Type { case binlogdatapb.VEventType_GTID, binlogdatapb.VEventType_BEGIN, binlogdatapb.VEventType_FIELD, binlogdatapb.VEventType_JOURNAL: // We never have to send GTID, BEGIN, FIELD events on their own. + // A JOURNAL event is always preceded by a BEGIN and followed by a COMMIT. + // So, we don't have to send it right away. bufferedEvents = append(bufferedEvents, vevent) case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER, binlogdatapb.VEventType_HEARTBEAT: // COMMIT, DDL, OTHER and HEARTBEAT must be immediately sent. + // Although unlikely, it's possible to get a HEARTBEAT in the middle + // of a transaction. If so, we still send the partial transaction along + // with the heartbeat. bufferedEvents = append(bufferedEvents, vevent) vevents := bufferedEvents bufferedEvents = nil @@ -287,8 +317,8 @@ func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog } } +// parseEvent parses an event from the binlog and converts it to a list of VEvents. func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, error) { - // Validate the buffer before reading fields from it. if !ev.IsValid() { return nil, fmt.Errorf("can't parse binlog event: invalid data: %#v", ev) } @@ -428,6 +458,11 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e } case ev.IsTableMap(): // This is very frequent. It precedes every row event. + // If it's the first time for a table, we generate a FIELD + // event, and also cache the plan. Subsequent TableMap events + // for that table id don't generate VEvents. + // A schema change will result in a change in table id, which + // will generate a new plan and FIELD event. id := ev.TableID(vs.format) if _, ok := vs.plans[id]; ok { return nil, nil @@ -437,6 +472,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e return nil, err } if tm.Database == "_vt" && tm.Name == "resharding_journal" { + // A journal is a special case that generates a JOURNAL event. return nil, vs.buildJournalPlan(id, tm) } if tm.Database != "" && tm.Database != vs.cp.DbName { diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 110ba8902c7..95ca6aaef6c 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -117,23 +117,37 @@ message StreamTablesResponse { // Rule represents one rule. message Rule { - // match can be a table name or a regular expression - // delineated by '/' and '/'. + // Match can be a table name or a regular expression. + // If it starts with a '/', it's a regular expression. + // For example, "t" matches a table named "t", whereas + // "/t.*" matches all tables that begin with 't'. string match = 1; - // filter can be an empty string or keyrange if the match - // is a regular expression. Otherwise, it must be a select - // query. + // Filter: If empty, all columns and rows of the matching tables + // are sent. If it's a keyrange, only rows that match the + // keyrange are sent. + // If Match is a table name instead of a regular expression, + // the Filter can also be a select expression. + // What is allowed in a select expression depends on whether + // it's a vstreamer or vreplication request. string filter = 2; } -// Filter represents a list of ordered rules. First match -// wins. +// Filter represents a list of ordered rules. The first +// match wins. message Filter { repeated Rule rules = 1; enum FieldEventMode { ERR_ON_MISMATCH = 0; BEST_EFFORT = 1; } + // FieldEventMode specifies the behavior if there is a mismatch + // between the current schema and the fields in the binlog. This + // can happen if the binlog position is before a DDL that would + // cause the fields to change. If vstreamer detects such + // an inconsistency, the behavior depends on the FieldEventMode. + // If the value is ERR_ON_MISMATCH (default), then it errors out. + // If it's BEST_EFFORT, it sends a field event with fake column + // names as "@1", "@2", etc. FieldEventMode fieldEventMode = 2; } @@ -146,8 +160,8 @@ enum OnDDLAction { } // BinlogSource specifies the source and filter parameters for -// Filtered Replication. It currently supports a keyrange -// or a list of tables. +// Filtered Replication. KeyRange and Tables are legacy. Filter +// is the new way to specify the filtering rules. message BinlogSource { // the source keyspace string keyspace = 1; @@ -158,30 +172,29 @@ message BinlogSource { // the source tablet type topodata.TabletType tablet_type = 3; - // key_range is set if the request is for a keyrange + // KeyRange is set if the request is for a keyrange topodata.KeyRange key_range = 4; - // tables is set if the request is for a list of tables + // Tables is set if the request is for a list of tables repeated string tables = 5; - // filter is set if we're using the generalized representation + // Filter is set if we're using the generalized representation // for the filter. Filter filter = 6; - // on_ddl specifies the action to be taken when a DDL is encountered. + // OnDdl specifies the action to be taken when a DDL is encountered. OnDDLAction on_ddl = 7; // Source is an external mysql. This attribute should be set to the username // to use in the connection string external_mysql = 8; - // stop_after_copy specifies if vreplication should be stopped + // StopAfterCopy specifies if vreplication should be stopped // after copying is done. bool stop_after_copy = 9; } -// VEventType enumerates the event types. -// This list is comprehensive. Many of these types +// VEventType enumerates the event types. Many of these types // will not be encountered in RBR mode. enum VEventType { UNKNOWN = 0; @@ -190,46 +203,65 @@ enum VEventType { COMMIT = 3; ROLLBACK = 4; DDL = 5; + // INSERT, REPLACE, UPDATE, DELETE and SET will not be seen in RBR mode. INSERT = 6; REPLACE = 7; UPDATE = 8; DELETE = 9; SET = 10; + // OTHER is a dummy event. If encountered, the current GTID must be + // recorded by the client to be able to resume. OTHER = 11; ROW = 12; FIELD = 13; + // HEARTBEAT is sent if there is inactivity. If a client does not + // receive events beyond the hearbeat interval, it can assume that it's + // lost connection to the vstreamer. HEARTBEAT = 14; + // VGTID is generated by VTGate's VStream that combines multiple + // GTIDs. VGTID = 15; JOURNAL = 16; } -// RowChange represents one row change +// RowChange represents one row change. +// If Before is set and not After, it's a delete. +// If After is set and not Before, it's an insert. +// If both are set, it's an update. message RowChange { query.Row before = 1; query.Row after = 2; } -// RowEvent represent row events for one table +// RowEvent represent row events for one table. message RowEvent { string table_name = 1; repeated RowChange row_changes = 2; } +// FieldEvent represents the field info for a table. message FieldEvent { string table_name = 1; repeated query.Field fields = 2; } +// ShardGtid contains the GTID position for one shard. +// It's used in a request for requesting a starting position. +// It's used in a response to transmit the current position +// of a shard. It's also used in a Journal to indicate the +// list of targets and shard positions to migrate to. message ShardGtid { string keyspace = 1; string shard = 2; string gtid = 3; } +// A VGtid is a list of ShardGtids. message VGtid { repeated ShardGtid shard_gtids = 1; } +// KeyspaceShard represents a keyspace and shard. message KeyspaceShard { string keyspace = 1; string shard = 2; @@ -241,28 +273,63 @@ enum MigrationType { SHARDS = 1; } +// Journal contains the metadata for a journal event. +// The commit of a journal event indicates the point of no return +// for a migration. message Journal { + // Id represents a unique journal id. int64 id = 1; MigrationType migration_type = 2; + // Tables is set if the journal represents a TABLES migration. repeated string tables = 3; + // LocalPosition is the source position at which the migration happened. string local_position = 4; + // ShardGtids is the list of targets to which the migration took place. repeated ShardGtid shard_gtids = 5; + // Participants is the list of source participants for a migration. + // Every participant is expected to have an identical journal entry. + // While streaming, the client must wait for the journal entry to + // be received from all pariticipants, and then replace them with new + // streams specified by ShardGtid. + // If a stream does not have all participants, a consistent migration + // is not possible. repeated KeyspaceShard participants = 6; + // SourceWorkflows is the list of workflows in the source shard that + // were migrated to the target. If a migration fails after a Journal + // is committed, this information is used to start the target streams + // that were created prior to the creation of the journal. repeated string source_workflows = 7; } -// VEvent represents a vstream event +// VEvent represents a vstream event. +// A FieldEvent is sent once for every table, just before +// the first event for that table. The client is expected +// to cache this information and match it against the RowEvent +// which contains the table name. +// A GTID event always precedes a commitable event, which can be +// COMMIT, DDL or OTHER. +// OTHER events are non-material events that have no metadata. message VEvent { VEventType type = 1; + // Timestamp is the binlog timestamp in seconds. + // It's set for all events. int64 timestamp = 2; + // Gtid is set if the event type is GTID. string gtid = 3; + // Ddl is set if the event type is DDL. string ddl = 4; + // RowEvent is set if the event type is ROW. RowEvent row_event = 5; + // FieldEvent is set if the event type is FIELD. FieldEvent field_event = 6; + // Vgtid is set if the event type is VGTID. + // This event is only generated by VTGate's VStream function. VGtid vgtid = 7; + // Journal is set if the event type is JOURNAL. Journal journal = 8; + // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. string dml = 9; - // current_time specifies the current time to handle clock skew. + // CurrentType specifies the current time to handle clock skew. int64 current_time = 20; } From e2761175b9aa7b4fc068250aba0ee89aaeef2f85 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 31 Dec 2019 14:56:44 -0800 Subject: [PATCH 066/825] vrepl: more comments for vstreamer & binlogdata Signed-off-by: Sugu Sougoumarane --- go/vt/proto/binlogdata/binlogdata.pb.go | 118 ++++++++++++------ .../vreplication/table_plan_builder.go | 1 + .../tabletserver/vstreamer/vstreamer.go | 44 ++++--- proto/binlogdata.proto | 27 ++-- 4 files changed, 127 insertions(+), 63 deletions(-) diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index 7078b5a9ea6..583f9d2fcb1 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -56,29 +56,36 @@ func (OnDDLAction) EnumDescriptor() ([]byte, []int) { return fileDescriptor_5fd02bcb2e350dad, []int{0} } -// VEventType enumerates the event types. -// This list is comprehensive. Many of these types +// VEventType enumerates the event types. Many of these types // will not be encountered in RBR mode. type VEventType int32 const ( - VEventType_UNKNOWN VEventType = 0 - VEventType_GTID VEventType = 1 - VEventType_BEGIN VEventType = 2 - VEventType_COMMIT VEventType = 3 - VEventType_ROLLBACK VEventType = 4 - VEventType_DDL VEventType = 5 - VEventType_INSERT VEventType = 6 - VEventType_REPLACE VEventType = 7 - VEventType_UPDATE VEventType = 8 - VEventType_DELETE VEventType = 9 - VEventType_SET VEventType = 10 - VEventType_OTHER VEventType = 11 - VEventType_ROW VEventType = 12 - VEventType_FIELD VEventType = 13 + VEventType_UNKNOWN VEventType = 0 + VEventType_GTID VEventType = 1 + VEventType_BEGIN VEventType = 2 + VEventType_COMMIT VEventType = 3 + VEventType_ROLLBACK VEventType = 4 + VEventType_DDL VEventType = 5 + // INSERT, REPLACE, UPDATE, DELETE and SET will not be seen in RBR mode. + VEventType_INSERT VEventType = 6 + VEventType_REPLACE VEventType = 7 + VEventType_UPDATE VEventType = 8 + VEventType_DELETE VEventType = 9 + VEventType_SET VEventType = 10 + // OTHER is a dummy event. If encountered, the current GTID must be + // recorded by the client to be able to resume. + VEventType_OTHER VEventType = 11 + VEventType_ROW VEventType = 12 + VEventType_FIELD VEventType = 13 + // HEARTBEAT is sent if there is inactivity. If a client does not + // receive events beyond the hearbeat interval, it can assume that it's + // lost connection to the vstreamer. VEventType_HEARTBEAT VEventType = 14 - VEventType_VGTID VEventType = 15 - VEventType_JOURNAL VEventType = 16 + // VGTID is generated by VTGate's VStream that combines multiple + // GTIDs. + VEventType_VGTID VEventType = 15 + VEventType_JOURNAL VEventType = 16 ) var VEventType_name = map[int32]string{ @@ -596,15 +603,28 @@ func (m *StreamTablesResponse) GetBinlogTransaction() *BinlogTransaction { return nil } -// Rule represents one rule. +// Rule represents one rule in a Filter. type Rule struct { - // match can be a table name or a regular expression - // if it starts with a '/'. For example, "/.*" matches - // all tables. + // Match can be a table name or a regular expression. + // If it starts with a '/', it's a regular expression. + // For example, "t" matches a table named "t", whereas + // "/t.*" matches all tables that begin with 't'. Match string `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` - // filter can be an empty string or keyrange if the match - // is a regular expression. Otherwise, it must be a select - // query. + // Filter: If empty, all columns and rows of the matching tables + // are sent. If it's a keyrange like "-80", only rows that + // match the keyrange are sent. + // If Match is a table name instead of a regular expression, + // the Filter can also be a select expression like this: + // "select * from t", same as an empty Filter, or + // "select * from t where in_keyrange('-80')", same as "-80", or + // "select col1, col2 from t where in_keyrange(col1, 'hash', '-80'), or + // What is allowed in a select expression depends on whether + // it's a vstreamer or vreplication request. For more details, + // please refer to the specific package documentation. + // On the vreplication side, Filter can also accept a special + // "exclude" value, which will cause the matched tables + // to be excluded. + // TODO(sougou): support this on vstreamer side also. Filter string `protobuf:"bytes,2,opt,name=filter,proto3" json:"filter,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -650,10 +670,18 @@ func (m *Rule) GetFilter() string { return "" } -// Filter represents a list of ordered rules. First match -// wins. +// Filter represents a list of ordered rules. The first +// match wins. type Filter struct { - Rules []*Rule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"` + Rules []*Rule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"` + // FieldEventMode specifies the behavior if there is a mismatch + // between the current schema and the fields in the binlog. This + // can happen if the binlog position is before a DDL that would + // cause the fields to change. If vstreamer detects such + // an inconsistency, the behavior depends on the FieldEventMode. + // If the value is ERR_ON_MISMATCH (default), then it errors out. + // If it's BEST_EFFORT, it sends a field event with fake column + // names as "@1", "@2", etc. FieldEventMode Filter_FieldEventMode `protobuf:"varint,2,opt,name=fieldEventMode,proto3,enum=binlogdata.Filter_FieldEventMode" json:"fieldEventMode,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -709,19 +737,19 @@ type BinlogSource struct { Shard string `protobuf:"bytes,2,opt,name=shard,proto3" json:"shard,omitempty"` // the source tablet type TabletType topodata.TabletType `protobuf:"varint,3,opt,name=tablet_type,json=tabletType,proto3,enum=topodata.TabletType" json:"tablet_type,omitempty"` - // key_range is set if the request is for a keyrange + // KeyRange is set if the request is for a keyrange KeyRange *topodata.KeyRange `protobuf:"bytes,4,opt,name=key_range,json=keyRange,proto3" json:"key_range,omitempty"` - // tables is set if the request is for a list of tables + // Tables is set if the request is for a list of tables Tables []string `protobuf:"bytes,5,rep,name=tables,proto3" json:"tables,omitempty"` - // filter is set if we're using the generalized representation + // Filter is set if we're using the generalized representation // for the filter. Filter *Filter `protobuf:"bytes,6,opt,name=filter,proto3" json:"filter,omitempty"` - // on_ddl specifies the action to be taken when a DDL is encountered. + // OnDdl specifies the action to be taken when a DDL is encountered. OnDdl OnDDLAction `protobuf:"varint,7,opt,name=on_ddl,json=onDdl,proto3,enum=binlogdata.OnDDLAction" json:"on_ddl,omitempty"` // Source is an external mysql. This attribute should be set to the username // to use in the connection ExternalMysql string `protobuf:"bytes,8,opt,name=external_mysql,json=externalMysql,proto3" json:"external_mysql,omitempty"` - // stop_after_copy specifies if vreplication should be stopped + // StopAfterCopy specifies if vreplication should be stopped // after copying is done. StopAfterCopy bool `protobuf:"varint,9,opt,name=stop_after_copy,json=stopAfterCopy,proto3" json:"stop_after_copy,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1113,6 +1141,8 @@ func (m *KeyspaceShard) GetShard() string { } // Journal contains the metadata for a journal event. +// The commit of a journal event indicates the point of no return +// for a migration. type Journal struct { // Id represents a unique journal id. Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` @@ -1124,9 +1154,17 @@ type Journal struct { // ShardGtids is the list of targets to which the migration took place. ShardGtids []*ShardGtid `protobuf:"bytes,5,rep,name=shard_gtids,json=shardGtids,proto3" json:"shard_gtids,omitempty"` // Participants is the list of source participants for a migration. + // Every participant is expected to have an identical journal entry. + // While streaming, the client must wait for the journal entry to + // be received from all pariticipants, and then replace them with new + // streams specified by ShardGtid. + // If a stream does not have all participants, a consistent migration + // is not possible. Participants []*KeyspaceShard `protobuf:"bytes,6,rep,name=participants,proto3" json:"participants,omitempty"` - // SourceWorkflows is the list of workflows in the source shard that need - // to be migrated to the target. + // SourceWorkflows is the list of workflows in the source shard that + // were migrated to the target. If a migration fails after a Journal + // is committed, this information is used to start the target streams + // that were created prior to the creation of the journal. SourceWorkflows []string `protobuf:"bytes,7,rep,name=source_workflows,json=sourceWorkflows,proto3" json:"source_workflows,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -1212,6 +1250,9 @@ func (m *Journal) GetSourceWorkflows() []string { // the first event for that table. The client is expected // to cache this information and match it against the RowEvent // which contains the table name. +// A GTID event always precedes a commitable event, which can be +// COMMIT, DDL or OTHER. +// OTHER events are non-material events that have no additional metadata. type VEvent struct { Type VEventType `protobuf:"varint,1,opt,name=type,proto3,enum=binlogdata.VEventType" json:"type,omitempty"` // Timestamp is the binlog timestamp in seconds. @@ -1232,7 +1273,8 @@ type VEvent struct { Journal *Journal `protobuf:"bytes,8,opt,name=journal,proto3" json:"journal,omitempty"` // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. Dml string `protobuf:"bytes,9,opt,name=dml,proto3" json:"dml,omitempty"` - // CurrentType specifies the current time to handle clock skew. + // CurrentTime specifies the current time when the message was sent. + // This can be used to compenssate for clock skew. CurrentTime int64 `protobuf:"varint,20,opt,name=current_time,json=currentTime,proto3" json:"current_time,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -1334,7 +1376,7 @@ func (m *VEvent) GetCurrentTime() int64 { return 0 } -// VStreamRequest is the payload for VStream +// VStreamRequest is the payload for VStreamer type VStreamRequest struct { EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId,proto3" json:"effective_caller_id,omitempty"` ImmediateCallerId *query.VTGateCallerID `protobuf:"bytes,2,opt,name=immediate_caller_id,json=immediateCallerId,proto3" json:"immediate_caller_id,omitempty"` @@ -1406,7 +1448,7 @@ func (m *VStreamRequest) GetFilter() *Filter { return nil } -// VStreamResponse is the response from VStream +// VStreamResponse is the response from VStreamer type VStreamResponse struct { Events []*VEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` diff --git a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go index 95104a0a944..808d4d87437 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go +++ b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go @@ -29,6 +29,7 @@ import ( ) // ExcludeStr is the filter value for excluding tables that match a rule. +// TODO(sougou): support this on vstreamer side also. const ExcludeStr = "exclude" type tablePlanBuilder struct { diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 6c993a5a101..3315f9a941d 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -101,8 +101,14 @@ type streamerPlan struct { // filter: the list of filtering rules. If a rule has a select expressinon for its filter, // the select list can only reference direct columns. No other experssions are allowed. // The select expression is allowed to contain the special 'keyspace_id()' function which -// will return the keyspace id of the row. For more info, see the documentation -// for binlogdatapb.Filter. +// will return the keyspace id of the row. Examples: +// "select * from t", same as an empty Filter, +// "select * from t where in_keyrange('-80')", same as "-80", +// "select * from t where in_keyrange(col1, 'hash', '-80')", +// "select col1, col2 from t where...", +// "select col1, keyspace_id() from t where...". +// Only "in_keyrange" expressions are supported in the where clause. +// Other constructs like joins, group by, etc. are not supported. // vschema: the current vschema. This value can later be changed through the SetVSchema method. // send: callback function to send events. func newVStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { @@ -529,6 +535,9 @@ func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { Name: "_vt.resharding_journal", Columns: st.Columns[:len(tm.Types)], } + // Build a normal table plan, which means, return all rows + // and columns as is. Special handling is done when we actually + // receive the row event. We'll build a JOURNAL event instead. plan, err := buildREPlan(table, nil, "") if err != nil { return err @@ -620,24 +629,27 @@ nextrow: return nil, err } if !afterOK { + // This can happen if someone manually deleted rows. continue } + // Exclude events that don't match the db_name. for i, fld := range plan.fields() { - switch fld.Name { - case "db_name": - if afterValues[i].ToString() != vs.cp.DbName { - continue nextrow - } - case "val": - journal := &binlogdatapb.Journal{} - if err := proto.UnmarshalText(afterValues[i].ToString(), journal); err != nil { - return nil, err - } - vevents = append(vevents, &binlogdatapb.VEvent{ - Type: binlogdatapb.VEventType_JOURNAL, - Journal: journal, - }) + if fld.Name == "db_name" && afterValues[i].ToString() != vs.cp.DbName { + continue nextrow + } + } + for i, fld := range plan.fields() { + if fld.Name != "val" { + continue + } + journal := &binlogdatapb.Journal{} + if err := proto.UnmarshalText(afterValues[i].ToString(), journal); err != nil { + return nil, err } + vevents = append(vevents, &binlogdatapb.VEvent{ + Type: binlogdatapb.VEventType_JOURNAL, + Journal: journal, + }) } } return vevents, nil diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 95ca6aaef6c..22dbaec214a 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -115,7 +115,7 @@ message StreamTablesResponse { BinlogTransaction binlog_transaction = 1; } -// Rule represents one rule. +// Rule represents one rule in a Filter. message Rule { // Match can be a table name or a regular expression. // If it starts with a '/', it's a regular expression. @@ -123,12 +123,20 @@ message Rule { // "/t.*" matches all tables that begin with 't'. string match = 1; // Filter: If empty, all columns and rows of the matching tables - // are sent. If it's a keyrange, only rows that match the - // keyrange are sent. + // are sent. If it's a keyrange like "-80", only rows that + // match the keyrange are sent. // If Match is a table name instead of a regular expression, - // the Filter can also be a select expression. + // the Filter can also be a select expression like this: + // "select * from t", same as an empty Filter, or + // "select * from t where in_keyrange('-80')", same as "-80", or + // "select col1, col2 from t where in_keyrange(col1, 'hash', '-80'), or // What is allowed in a select expression depends on whether - // it's a vstreamer or vreplication request. + // it's a vstreamer or vreplication request. For more details, + // please refer to the specific package documentation. + // On the vreplication side, Filter can also accept a special + // "exclude" value, which will cause the matched tables + // to be excluded. + // TODO(sougou): support this on vstreamer side also. string filter = 2; } @@ -308,7 +316,7 @@ message Journal { // which contains the table name. // A GTID event always precedes a commitable event, which can be // COMMIT, DDL or OTHER. -// OTHER events are non-material events that have no metadata. +// OTHER events are non-material events that have no additional metadata. message VEvent { VEventType type = 1; // Timestamp is the binlog timestamp in seconds. @@ -329,11 +337,12 @@ message VEvent { Journal journal = 8; // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. string dml = 9; - // CurrentType specifies the current time to handle clock skew. + // CurrentTime specifies the current time when the message was sent. + // This can be used to compenssate for clock skew. int64 current_time = 20; } -// VStreamRequest is the payload for VStream +// VStreamRequest is the payload for VStreamer message VStreamRequest { vtrpc.CallerID effective_caller_id = 1; query.VTGateCallerID immediate_caller_id = 2; @@ -343,7 +352,7 @@ message VStreamRequest { Filter filter = 5; } -// VStreamResponse is the response from VStream +// VStreamResponse is the response from VStreamer message VStreamResponse { repeated VEvent events = 1; } From c48e83f05cdb2ea61b2d90364804a3841dec84ce Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Fri, 7 Feb 2020 09:58:47 -0700 Subject: [PATCH 067/825] Add non-interactive bits back Signed-off-by: Morgan Tocker --- .github/workflows/unit.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 7acfc878219..72f851beb82 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -27,7 +27,7 @@ jobs: else # Uninstall likely installed MySQL first sudo systemctl stop apparmor - sudo apt-get remove -y --purge mysql-server mysql-client mysql-common + sudo DEBIAN_FRONTEND="noninteractive" apt-get remove -y --purge mysql-server mysql-client mysql-common sudo apt-get -y autoremove sudo apt-get -y autoclean sudo deluser mysql @@ -40,31 +40,31 @@ jobs: wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb sudo apt update - sudo apt-get install -y percona-server-server-5.6 percona-server-client-5.6 + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y percona-server-server-5.6 percona-server-client-5.6 elif [ ${{matrix.name}} = "mysql80" ]; then wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.14-1_all.deb echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections - sudo dpkg -i mysql-apt-config* + sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config* sudo apt-get update - sudo apt-get install -y mysql-server mysql-client + sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client elif [ ${{matrix.name}} = "mariadb101" ]; then sudo apt-get install -y software-properties-common sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.1/ubuntu bionic main' sudo apt update - sudo apt install -y mariadb-server + sudo DEBIAN_FRONTEND="noninteractive" apt install -y mariadb-server elif [ ${{matrix.name}} = "mariadb102" ]; then sudo apt-get install -y software-properties-common sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.2/ubuntu bionic main' sudo apt update - sudo apt install -y mariadb-server + sudo DEBIAN_FRONTEND="noninteractive" apt install -y mariadb-server elif [ ${{matrix.name}} = "mariadb103" ]; then sudo apt-get install -y software-properties-common sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 sudo add-apt-repository 'deb [arch=amd64,arm64,ppc64el] http://nyc2.mirrors.digitalocean.com/mariadb/repo/10.3/ubuntu bionic main' sudo apt update - sudo apt install -y mariadb-server + sudo DEBIAN_FRONTEND="noninteractive" apt install -y mariadb-server fi fi From 19132b9ed639f2efb043261e73bc9e2e35d992a9 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Fri, 7 Feb 2020 10:15:40 -0700 Subject: [PATCH 068/825] Disable last 2 travis shards Signed-off-by: Morgan Tocker --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 733f8213a21..c23b98c5341 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,8 +50,6 @@ env: - TEST_MATRIX="-shard 0" - TEST_MATRIX="-shard 1" - TEST_MATRIX="-shard 2" - - TEST_MATRIX="-shard 3" - - TEST_MATRIX="-shard 4" script: - go run test.go $TEST_FLAGS $TEST_MATRIX # Uncomment the next line to verify the GOMAXPROCS value (should be 2 as of 09/2017). From 631322d5c045a440d379a4a5a6fba13172b7fd7e Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 11:13:21 -0800 Subject: [PATCH 069/825] WIP - refactor examples Signed-off-by: Morgan Tocker --- examples/local/101_initial_cluster.sh | 29 +-- examples/local/201_customer_keyspace.sh | 5 +- examples/local/202_customer_tablets.sh | 19 +- examples/local/203_vertical_split.sh | 11 +- .../local/204_vertical_migrate_replicas.sh | 7 +- examples/local/205_vertical_migrate_master.sh | 6 +- examples/local/206_clean_commerce.sh | 12 +- examples/local/301_customer_sharded.sh | 12 +- examples/local/302_new_shards.sh | 24 ++- examples/local/303_horizontal_split.sh | 24 +-- examples/local/304_migrate_replicas.sh | 8 +- examples/local/305_migrate_master.sh | 5 +- examples/local/306_down_shard_0.sh | 10 +- examples/local/307_delete_shard_0.sh | 6 +- examples/local/401_teardown.sh | 15 +- examples/local/mysqlctl-down.sh | 3 + examples/local/mysqlctl-up.sh | 44 +++++ examples/local/vtctld-up.sh | 41 ++--- examples/local/vtgate-up.sh | 95 ++-------- examples/local/vttablet-up.sh | 171 ++++-------------- 20 files changed, 192 insertions(+), 355 deletions(-) create mode 100755 examples/local/mysqlctl-down.sh create mode 100755 examples/local/mysqlctl-up.sh diff --git a/examples/local/101_initial_cluster.sh b/examples/local/101_initial_cluster.sh index 1a484d98d5d..6e5b61cbf8f 100755 --- a/examples/local/101_initial_cluster.sh +++ b/examples/local/101_initial_cluster.sh @@ -17,35 +17,38 @@ # this script brings up zookeeper and all the vitess components # required for a single shard deployment. -set -e +source ./env.sh -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") -source "${script_root}/env.sh" +set -e # start topo server if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 "$script_root/zk-up.sh" + CELL=zone1 ./zk-up.sh else - CELL=zone1 "$script_root/etcd-up.sh" + CELL=zone1 ./etcd-up.sh fi # start vtctld -CELL=zone1 "$script_root/vtctld-up.sh" +CELL=zone1 ./vtctld-up.sh & # start vttablets for keyspace commerce -CELL=zone1 KEYSPACE=commerce UID_BASE=100 "$script_root/vttablet-up.sh" +for i in 100 101 102; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + CELL=zone1 KEYSPACE=commerce TABLET_UID=$i ./vttablet-up.sh & +done + +sleep 20 # set one of the replicas to master -./lvtctl.sh InitShardMaster -force commerce/0 zone1-100 +vtctlclient -server localhost:15999 InitShardMaster -force commerce/0 zone1-100 # create the schema -./lvtctl.sh ApplySchema -sql-file create_commerce_schema.sql commerce +vtctlclient -server localhost:15999 ApplySchema -sql-file create_commerce_schema.sql commerce # create the vschema -./lvtctl.sh ApplyVSchema -vschema_file vschema_commerce_initial.json commerce +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_initial.json commerce # start vtgate -CELL=zone1 "$script_root/vtgate-up.sh" +CELL=zone1 ./vtgate-up.sh & -disown -a +sleep 20 diff --git a/examples/local/201_customer_keyspace.sh b/examples/local/201_customer_keyspace.sh index 72c415e6527..f948175195c 100755 --- a/examples/local/201_customer_keyspace.sh +++ b/examples/local/201_customer_keyspace.sh @@ -16,8 +16,5 @@ # this script creates a new keyspace in preparation for vertical resharding -set -e +vtctlclient -server localhost:15999 CreateKeyspace -served_from='master:commerce,replica:commerce,rdonly:commerce' customer -./lvtctl.sh CreateKeyspace -served_from='master:commerce,replica:commerce,rdonly:commerce' customer - -disown -a diff --git a/examples/local/202_customer_tablets.sh b/examples/local/202_customer_tablets.sh index 9420ba19713..ccc006becd5 100755 --- a/examples/local/202_customer_tablets.sh +++ b/examples/local/202_customer_tablets.sh @@ -18,16 +18,19 @@ # resharding it also splits the vschema between the two keyspaces # old (commerce) and new (customer) +source ./env.sh + set -e -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +for i in 200 201 202; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & +done -CELL=zone1 KEYSPACE=customer UID_BASE=200 "$script_root/vttablet-up.sh" +sleep 20 -./lvtctl.sh InitShardMaster -force customer/0 zone1-200 -./lvtctl.sh CopySchemaShard -tables customer,corder commerce/0 customer/0 -./lvtctl.sh ApplyVSchema -vschema_file vschema_commerce_vsplit.json commerce -./lvtctl.sh ApplyVSchema -vschema_file vschema_customer_vsplit.json customer +vtctlclient -server localhost:15999 InitShardMaster -force customer/0 zone1-200 +vtctlclient -server localhost:15999 CopySchemaShard -tables customer,corder commerce/0 customer/0 +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_vsplit.json commerce +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_customer_vsplit.json customer -disown -a diff --git a/examples/local/203_vertical_split.sh b/examples/local/203_vertical_split.sh index 5429430f052..f7b25d9e215 100755 --- a/examples/local/203_vertical_split.sh +++ b/examples/local/203_vertical_split.sh @@ -17,16 +17,10 @@ # this script copies over all the data from commerce keyspace to # customer keyspace for the customer and corder tables -set -e - -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +source ./env.sh -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source "$script_root/env.sh" +set -e -# shellcheck disable=SC2086 vtworker \ $TOPOLOGY_FLAGS \ -cell zone1 \ @@ -35,4 +29,3 @@ vtworker \ -use_v3_resharding_mode \ VerticalSplitClone -min_healthy_tablets=1 -tables=customer,corder customer/0 -disown -a diff --git a/examples/local/204_vertical_migrate_replicas.sh b/examples/local/204_vertical_migrate_replicas.sh index 1e13f62a4b3..45ada0aaf63 100755 --- a/examples/local/204_vertical_migrate_replicas.sh +++ b/examples/local/204_vertical_migrate_replicas.sh @@ -17,9 +17,6 @@ # this script migrates traffic for the new customer keyspace to the new # tablets of types rdonly and replica -set -e +vtctlclient -server localhost:15999 MigrateServedFrom customer/0 rdonly +vtctlclient -server localhost:15999 MigrateServedFrom customer/0 replica -./lvtctl.sh MigrateServedFrom customer/0 rdonly -./lvtctl.sh MigrateServedFrom customer/0 replica - -disown -a diff --git a/examples/local/205_vertical_migrate_master.sh b/examples/local/205_vertical_migrate_master.sh index 3aebc52a05c..211d27eb6a7 100755 --- a/examples/local/205_vertical_migrate_master.sh +++ b/examples/local/205_vertical_migrate_master.sh @@ -17,8 +17,4 @@ # this script migrates master traffic for the customer keyspace to the # new master tablet -set -e - -./lvtctl.sh MigrateServedFrom customer/0 master - -disown -a +vtctlclient -server localhost:15999 MigrateServedFrom customer/0 master diff --git a/examples/local/206_clean_commerce.sh b/examples/local/206_clean_commerce.sh index 6139558c385..7111a4a91fc 100755 --- a/examples/local/206_clean_commerce.sh +++ b/examples/local/206_clean_commerce.sh @@ -17,11 +17,7 @@ # this script removes the customer and corder tables from the commerce # keyspace -set -e - -./lvtctl.sh ApplySchema -sql-file drop_commerce_tables.sql commerce -./lvtctl.sh SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 rdonly -./lvtctl.sh SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 replica -./lvtctl.sh SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 master - -disown -a +vtctlclient -server localhost:15999 ApplySchema -sql-file drop_commerce_tables.sql commerce +vtctlclient -server localhost:15999 SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 rdonly +vtctlclient -server localhost:15999 SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 replica +vtctlclient -server localhost:15999 SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 master diff --git a/examples/local/301_customer_sharded.sh b/examples/local/301_customer_sharded.sh index 4d7bacf01d4..5335a0e7392 100755 --- a/examples/local/301_customer_sharded.sh +++ b/examples/local/301_customer_sharded.sh @@ -20,11 +20,7 @@ # it also changes the customer vschema from unsharded to sharded and # sets up the necessary vindexes -set -e - -./lvtctl.sh ApplySchema -sql-file create_commerce_seq.sql commerce -./lvtctl.sh ApplyVSchema -vschema_file vschema_commerce_seq.json commerce -./lvtctl.sh ApplySchema -sql-file create_customer_sharded.sql customer -./lvtctl.sh ApplyVSchema -vschema_file vschema_customer_sharded.json customer - -disown -a +vtctlclient -server localhost:15999 ApplySchema -sql-file create_commerce_seq.sql commerce +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_seq.json commerce +vtctlclient -server localhost:15999 ApplySchema -sql-file create_customer_sharded.sql customer +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_customer_sharded.json customer diff --git a/examples/local/302_new_shards.sh b/examples/local/302_new_shards.sh index 8475a83f1b2..629e3e19517 100755 --- a/examples/local/302_new_shards.sh +++ b/examples/local/302_new_shards.sh @@ -17,17 +17,21 @@ # this script brings up new tablets for the two new shards that we will # be creating in the customer keyspace and copies the schema -set -e +source ./env.sh -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +set -e -SHARD=-80 CELL=zone1 KEYSPACE=customer UID_BASE=300 "$script_root/vttablet-up.sh" -SHARD=80- CELL=zone1 KEYSPACE=customer UID_BASE=400 "$script_root/vttablet-up.sh" +for i in 300 301 302; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & +done -./lvtctl.sh InitShardMaster -force customer/-80 zone1-300 -./lvtctl.sh InitShardMaster -force customer/80- zone1-400 -./lvtctl.sh CopySchemaShard customer/0 customer/-80 -./lvtctl.sh CopySchemaShard customer/0 customer/80- +for i in 400 401 402; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + SHARD=80- CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & +done -disown -a +vtctlclient -server localhost:15999 InitShardMaster -force customer/-80 zone1-300 +vtctlclient -server localhost:15999 InitShardMaster -force customer/80- zone1-400 +vtctlclient -server localhost:15999 CopySchemaShard customer/0 customer/-80 +vtctlclient -server localhost:15999 CopySchemaShard customer/0 customer/80- diff --git a/examples/local/303_horizontal_split.sh b/examples/local/303_horizontal_split.sh index ea1d9285fc4..afd5d5c1d10 100755 --- a/examples/local/303_horizontal_split.sh +++ b/examples/local/303_horizontal_split.sh @@ -17,22 +17,14 @@ # this script copies the data from customer/0 to customer/-80 and customer/80- # each row will be copied to exactly one shard based on the vindex value -set -e - -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +source ./env.sh -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source "${script_root}/env.sh" +set -e -# shellcheck disable=SC2086 vtworker \ - $TOPOLOGY_FLAGS \ - -cell zone1 \ - -log_dir "$VTDATAROOT"/tmp \ - -alsologtostderr \ - -use_v3_resharding_mode \ - SplitClone -min_healthy_rdonly_tablets=1 customer/0 - -disown -a + $TOPOLOGY_FLAGS \ + -cell zone1 \ + -log_dir "$VTDATAROOT"/tmp \ + -alsologtostderr \ + -use_v3_resharding_mode \ + SplitClone -min_healthy_rdonly_tablets=1 customer/0 diff --git a/examples/local/304_migrate_replicas.sh b/examples/local/304_migrate_replicas.sh index 917d650fdb4..3aed031ebab 100755 --- a/examples/local/304_migrate_replicas.sh +++ b/examples/local/304_migrate_replicas.sh @@ -16,9 +16,5 @@ # this script migrates traffic for the rdonly and replica tablets -set -e - -./lvtctl.sh MigrateServedTypes customer/0 rdonly -./lvtctl.sh MigrateServedTypes customer/0 replica - -disown -a +vtctlclient -server localhost:15999 MigrateServedTypes customer/0 rdonly +vtctlclient -server localhost:15999 MigrateServedTypes customer/0 replica diff --git a/examples/local/305_migrate_master.sh b/examples/local/305_migrate_master.sh index e5ebed81e77..a9da079ea7c 100755 --- a/examples/local/305_migrate_master.sh +++ b/examples/local/305_migrate_master.sh @@ -16,9 +16,6 @@ # this script migrates traffic for the master tablet -set -e - -./lvtctl.sh MigrateServedTypes customer/0 master +vtctlclient -server localhost:15999 MigrateServedTypes customer/0 master # data has been copied over to shards, and databases for the new shards are now available -disown -a diff --git a/examples/local/306_down_shard_0.sh b/examples/local/306_down_shard_0.sh index 79bdd6f3318..d5d82a8cc05 100755 --- a/examples/local/306_down_shard_0.sh +++ b/examples/local/306_down_shard_0.sh @@ -15,11 +15,7 @@ # limitations under the License. # this script brings down the tablets for customer/0 keyspace -set -e +for i in 200 201 202; do + CELL=zone1 TABLET_UID=$i ./vttablet-down.sh +done -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") - -CELL=zone1 UID_BASE=200 "$script_root/vttablet-down.sh" - -disown -a diff --git a/examples/local/307_delete_shard_0.sh b/examples/local/307_delete_shard_0.sh index 6076b0fabbe..662bb7cdfe9 100755 --- a/examples/local/307_delete_shard_0.sh +++ b/examples/local/307_delete_shard_0.sh @@ -16,8 +16,4 @@ # this script deletes the old shard 0 which has been replaced by 2 shards -set -e - -./lvtctl.sh DeleteShard -recursive customer/0 - -disown -a +vtctlclient -server localhost:15999 DeleteShard -recursive customer/0 diff --git a/examples/local/401_teardown.sh b/examples/local/401_teardown.sh index b0168ae133d..f53991f4a9f 100755 --- a/examples/local/401_teardown.sh +++ b/examples/local/401_teardown.sh @@ -17,23 +17,22 @@ # We should not assume that any of the steps have been executed. # This makes it possible for a user to cleanup at any point. -set -e +source ./env.sh -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +set -e ./vtgate-down.sh -for TABLET in 100 200 300 400; do - ./lvtctl.sh GetTablet zone1-$TABLET >/dev/null 2>&1 && CELL=zone1 UID_BASE=$TABLET "$script_root/vttablet-down.sh" -done; +# for TABLET in 100 200 300 400; do +# vtctlclient -server localhost:15999 GetTablet zone1-$TABLET >/dev/null 2>&1 && CELL=zone1 UID_BASE=$TABLET ./vttablet-down.sh +# done; ./vtctld-down.sh if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 "$script_root/zk-down.sh" + CELL=zone1 ./zk-down.sh else - CELL=zone1 "$script_root/etcd-down.sh" + CELL=zone1 ./etcd-down.sh fi # pedantic check: grep for any remaining processes diff --git a/examples/local/mysqlctl-down.sh b/examples/local/mysqlctl-down.sh new file mode 100755 index 00000000000..ed2d92318cd --- /dev/null +++ b/examples/local/mysqlctl-down.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "TODO" diff --git a/examples/local/mysqlctl-up.sh b/examples/local/mysqlctl-up.sh new file mode 100755 index 00000000000..05ca1c18721 --- /dev/null +++ b/examples/local/mysqlctl-up.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is an example script that creates a single shard vttablet deployment. + +source ./env.sh + +set -xe + +cell=${CELL:-'test'} +uid=$TABLET_UID +mysql_port=$[17000 + $uid] +printf -v alias '%s-%010d' $cell $uid +printf -v tablet_dir 'vt_%010d' $uid + +mkdir -p $VTDATAROOT/backups + +echo "Starting MySQL for tablet $alias..." +action="init" + +if [ -d $VTDATAROOT/$tablet_dir ]; then + echo "Resuming from existing vttablet dir:" + echo " $VTDATAROOT/$tablet_dir" + action='start' +fi + +mysqlctl \ + -log_dir $VTDATAROOT/tmp \ + -tablet_uid $uid \ + -mysql_port $mysql_port \ + $action diff --git a/examples/local/vtctld-up.sh b/examples/local/vtctld-up.sh index b1965e06baf..075ac33e578 100755 --- a/examples/local/vtctld-up.sh +++ b/examples/local/vtctld-up.sh @@ -16,38 +16,25 @@ # This is an example script that starts vtctld. +source ./env.sh + set -e cell=${CELL:-'test'} grpc_port=15999 -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -optional_auth_args='' -if [ "$1" = "--enable-grpc-static-auth" ]; -then - echo "Enabling Auth with static authentication in grpc" - optional_auth_args='-grpc_auth_static_client_creds ./grpc_static_client_auth.json ' -fi - echo "Starting vtctld..." # shellcheck disable=SC2086 vtctld \ - $TOPOLOGY_FLAGS \ - -cell $cell \ - -workflow_manager_init \ - -workflow_manager_use_election \ - -service_map 'grpc-vtctl' \ - -backup_storage_implementation file \ - -file_backup_storage_root $VTDATAROOT/backups \ - -log_dir $VTDATAROOT/tmp \ - -port $vtctld_web_port \ - -grpc_port $grpc_port \ - -pid_file $VTDATAROOT/tmp/vtctld.pid \ - $optional_auth_args \ - > $VTDATAROOT/tmp/vtctld.out 2>&1 & -disown -a - -echo "Access vtctld web UI at http://$hostname:$vtctld_web_port" -echo "Send commands with: vtctlclient -server $hostname:$grpc_port ..." + $TOPOLOGY_FLAGS \ + -cell $cell \ + -workflow_manager_init \ + -workflow_manager_use_election \ + -service_map 'grpc-vtctl' \ + -backup_storage_implementation file \ + -file_backup_storage_root $VTDATAROOT/backups \ + -log_dir $VTDATAROOT/tmp \ + -port $vtctld_web_port \ + -grpc_port $grpc_port \ + -pid_file $VTDATAROOT/tmp/vtctld.pid \ + > $VTDATAROOT/tmp/vtctld.out 2>&1 diff --git a/examples/local/vtgate-up.sh b/examples/local/vtgate-up.sh index e4fb1dd5c10..6e448b8c0cc 100755 --- a/examples/local/vtgate-up.sh +++ b/examples/local/vtgate-up.sh @@ -16,7 +16,9 @@ # This is an example script that starts a single vtgate. -set -e +source ./env.sh + +set -xe cell=${CELL:-'test'} web_port=15001 @@ -24,82 +26,23 @@ grpc_port=15991 mysql_server_port=15306 mysql_server_socket_path="/tmp/mysql.sock" -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -# When this script is run with the argument "--enable-tls", then it will generate signed certs and -# configure VTGate to accept only TLS connections with client authentication. This allows for end-to-end -# SSL testing (see also the "client_jdbc.sh" script). -optional_tls_args='' -if [ "$1" = "--enable-tls" ]; -then - echo "Enabling TLS with client authentication" - config_dir=../../java/grpc-client/src/test/resources - cert_dir=$VTDATAROOT/tls - rm -Rf $cert_dir - mkdir -p $cert_dir - - # Create CA - openssl genrsa -out $cert_dir/ca-key.pem - openssl req -new -x509 -nodes -days 3600 -batch -config $config_dir/ca.config -key $cert_dir/ca-key.pem -out $cert_dir/ca-cert.pem - - # Create server-side signed cert - openssl req -newkey rsa:2048 -days 3600 -nodes -batch -config $config_dir/cert.config -keyout $cert_dir/server-key.pem -out $cert_dir/server-req.pem - openssl x509 -req -in $cert_dir/server-req.pem -days 3600 -CA $cert_dir/ca-cert.pem -CAkey $cert_dir/ca-key.pem -set_serial 01 -out $cert_dir/server-cert.pem - - # Create client-side signed cert - openssl req -newkey rsa:2048 -days 3600 -nodes -batch -config $config_dir/cert.config -keyout $cert_dir/client-key.pem -out $cert_dir/client-req.pem - openssl x509 -req -in $cert_dir/client-req.pem -days 3600 -CA $cert_dir/ca-cert.pem -CAkey $cert_dir/ca-key.pem -set_serial 02 -out $cert_dir/client-cert.pem - - optional_tls_args="-grpc_cert $cert_dir/server-cert.pem -grpc_key $cert_dir/server-key.pem -grpc_ca $cert_dir/ca-cert.pem" -fi +rm -rf /tmp/mysql.sock # todo: handle in vtgate to check for file -optional_auth_args='-mysql_auth_server_impl none' -optional_grpc_auth_args='' -if [ "$1" = "--enable-grpc-static-auth" ]; -then - echo "Enabling Auth with static authentication in grpc" - optional_grpc_auth_args='-grpc_auth_static_client_creds ./grpc_static_client_auth.json' -fi - -if [ "$1" = "--enable-mysql-static-auth" ]; -then - echo "Enabling Auth with mysql static authentication" - optional_auth_args='-mysql_auth_server_static_file ./mysql_auth_server_static_creds.json' -fi - -# Start vtgate. -# shellcheck disable=SC2086 vtgate \ - $TOPOLOGY_FLAGS \ - -log_dir $VTDATAROOT/tmp \ - -log_queries_to_file $VTDATAROOT/tmp/vtgate_querylog.txt \ - -port $web_port \ - -grpc_port $grpc_port \ - -mysql_server_port $mysql_server_port \ - -mysql_server_socket_path $mysql_server_socket_path \ - -cell $cell \ - -cells_to_watch $cell \ - -tablet_types_to_wait MASTER,REPLICA \ - -gateway_implementation discoverygateway \ - -service_map 'grpc-vtgateservice' \ - -pid_file $VTDATAROOT/tmp/vtgate.pid \ - $optional_auth_args \ - $optional_grpc_auth_args \ - $optional_tls_args \ + -topo_implementation etcd2 \ + -topo_global_server_address localhost:2379 \ + -topo_global_root /vitess/global \ + -log_dir $VTDATAROOT/tmp \ + -log_queries_to_file $VTDATAROOT/tmp/vtgate_querylog.txt \ + -port $web_port \ + -grpc_port $grpc_port \ + -mysql_server_port $mysql_server_port \ + -mysql_server_socket_path $mysql_server_socket_path \ + -cell $cell \ + -cells_to_watch $cell \ + -tablet_types_to_wait MASTER,REPLICA \ + -gateway_implementation discoverygateway \ + -service_map 'grpc-vtgateservice' \ + -pid_file $VTDATAROOT/tmp/vtgate.pid \ > $VTDATAROOT/tmp/vtgate.out 2>&1 & -# Block waiting for vtgate to be listening -# Not the same as healthy - -echo "Waiting for vtgate to be up..." -while true; do - curl -I "http://$hostname:$web_port/debug/status" >/dev/null 2>&1 && break - sleep 0.1 -done; -echo "vtgate is up!" - -echo "Access vtgate at http://$hostname:$web_port/debug/status" - -disown -a - diff --git a/examples/local/vttablet-up.sh b/examples/local/vttablet-up.sh index 4914a65493b..c771084b7fb 100755 --- a/examples/local/vttablet-up.sh +++ b/examples/local/vttablet-up.sh @@ -14,148 +14,47 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is an example script that creates a single shard vttablet deployment. +source ./env.sh -set -e +set -xe cell=${CELL:-'test'} keyspace=${KEYSPACE:-'test_keyspace'} shard=${SHARD:-'0'} -uid_base=${UID_BASE:-'100'} -port_base=$[15000 + $uid_base] -grpc_port_base=$[16000 + $uid_base] -mysql_port_base=$[17000 + $uid_base] +uid=$TABLET_UID +mysql_port=$[17000 + $uid] +port=$[15000 + $uid] +grpc_port=$[16000 + $uid] +printf -v alias '%s-%010d' $cell $uid +printf -v tablet_dir 'vt_%010d' $uid tablet_hostname='' +printf -v tablet_logfile 'vttablet_%010d_querylog.txt' $uid -# Travis hostnames are too long for MySQL, so we use IP. -# Otherwise, blank hostname means the tablet auto-detects FQDN. -if [ "$TRAVIS" == true ]; then - tablet_hostname=`hostname -i` +tablet_type=replica +if [[ "${uid: -1}" -gt 1 ]]; then + tablet_type=rdonly fi -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -mkdir -p $VTDATAROOT/backups - -# Start 3 vttablets by default. -# Pass TABLETS_UIDS indices as env variable to change -uids=${TABLETS_UIDS:-'0 1 2'} - -# Start all mysqlds in background. -for uid_index in $uids; do - uid=$[$uid_base + $uid_index] - mysql_port=$[$mysql_port_base + $uid_index] - printf -v alias '%s-%010d' $cell $uid - printf -v tablet_dir 'vt_%010d' $uid - - export KEYSPACE=$keyspace - export SHARD=$shard - export TABLET_ID=$alias - export TABLET_DIR=$tablet_dir - export MYSQL_PORT=$mysql_port - - tablet_type=replica - if [[ $uid_index -gt 1 ]]; then - tablet_type=rdonly - fi - - export TABLET_TYPE=$tablet_type - - echo "Starting MySQL for tablet $alias..." - action="init" - if [ -d $VTDATAROOT/$tablet_dir ]; then - echo "Resuming from existing vttablet dir:" - echo " $VTDATAROOT/$tablet_dir" - action='start' - fi - - set +e - - mysqlctl \ - -log_dir $VTDATAROOT/tmp \ - -tablet_uid $uid \ - -mysql_port $mysql_port \ - $action - - err=$? - if [[ $err -ne 0 ]]; then - fail "This script fails to start mysqld, possibly due to apparmor or selinux protection. - Utilities to help investigate: - apparmor: \"sudo aa-status\" - selinux: \"sudo sestatus\" - Please disable if so indicated. - You may also need to empty your \$VTDATAROOT to start clean." - fi - - set -e - -done - -# Wait for all mysqld to start up. -wait - -optional_auth_args='' -if [ "$1" = "--enable-grpc-static-auth" ]; -then - echo "Enabling Auth with static authentication in grpc" - optional_auth_args='-grpc_auth_mode static -grpc_auth_static_password_file ./grpc_static_auth.json -grpc_auth_static_client_creds ./grpc_static_client_auth.json' -fi - -# Start all vttablets in background. -for uid_index in $uids; do - uid=$[$uid_base + $uid_index] - port=$[$port_base + $uid_index] - grpc_port=$[$grpc_port_base + $uid_index] - printf -v alias '%s-%010d' $cell $uid - printf -v tablet_dir 'vt_%010d' $uid - printf -v tablet_logfile 'vttablet_%010d_querylog.txt' $uid - tablet_type=replica - if [[ $uid_index -gt 1 ]]; then - tablet_type=rdonly - fi - - echo "Starting vttablet for $alias..." - # shellcheck disable=SC2086 - vttablet \ - $TOPOLOGY_FLAGS \ - -log_dir $VTDATAROOT/tmp \ - -log_queries_to_file $VTDATAROOT/tmp/$tablet_logfile \ - -tablet-path $alias \ - -tablet_hostname "$tablet_hostname" \ - -init_keyspace $keyspace \ - -init_shard $shard \ - -init_tablet_type $tablet_type \ - -health_check_interval 5s \ - -enable_semi_sync \ - -enable_replication_reporter \ - -backup_storage_implementation file \ - -file_backup_storage_root $VTDATAROOT/backups \ - -restore_from_backup \ - -port $port \ - -grpc_port $grpc_port \ - -service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ - -pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ - -vtctld_addr http://$hostname:$vtctld_web_port/ \ - $optional_auth_args \ - > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 & - - echo "Access tablet $alias at http://$hostname:$port/debug/status" -done - -# Block waiting for all tablets to be listening -# Not the same as healthy - -echo "Waiting for tablets to be listening..." -for uid_index in $uids; do - port=$[$port_base + $uid_index] - for i in $(seq 0 300); do - curl -I "http://$hostname:$port/debug/status" >/dev/null 2>&1 && break - sleep 0.1 - done; - # check one last time - curl -I "http://$hostname:$port/debug/status" || fail "tablets could not be started!" -done; -echo "Tablets up!" - -disown -a +echo "Starting vttablet for $alias..." +# shellcheck disable=SC2086 +vttablet \ + $TOPOLOGY_FLAGS \ + -log_dir $VTDATAROOT/tmp \ + -log_queries_to_file $VTDATAROOT/tmp/$tablet_logfile \ + -tablet-path $alias \ + -tablet_hostname "$tablet_hostname" \ + -init_keyspace $keyspace \ + -init_shard $shard \ + -init_tablet_type $tablet_type \ + -health_check_interval 5s \ + -enable_semi_sync \ + -enable_replication_reporter \ + -backup_storage_implementation file \ + -file_backup_storage_root $VTDATAROOT/backups \ + -restore_from_backup \ + -port $port \ + -grpc_port $grpc_port \ + -service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ + -pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ + -vtctld_addr http://$hostname:$vtctld_web_port/ \ + > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 From c2f2dc936cbb050fc1d97d13144097c062c7d82c Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 11:13:21 -0800 Subject: [PATCH 070/825] WIP - refactor examples Signed-off-by: Morgan Tocker --- examples/local/101_initial_cluster.sh | 29 +-- examples/local/201_customer_keyspace.sh | 5 +- examples/local/202_customer_tablets.sh | 19 +- examples/local/203_vertical_split.sh | 11 +- .../local/204_vertical_migrate_replicas.sh | 7 +- examples/local/205_vertical_migrate_master.sh | 6 +- examples/local/206_clean_commerce.sh | 12 +- examples/local/301_customer_sharded.sh | 12 +- examples/local/302_new_shards.sh | 24 ++- examples/local/303_horizontal_split.sh | 24 +-- examples/local/304_migrate_replicas.sh | 8 +- examples/local/305_migrate_master.sh | 5 +- examples/local/306_down_shard_0.sh | 10 +- examples/local/307_delete_shard_0.sh | 6 +- examples/local/401_teardown.sh | 15 +- examples/local/mysqlctl-down.sh | 3 + examples/local/mysqlctl-up.sh | 44 +++++ examples/local/vtctld-down.sh | 10 +- examples/local/vtctld-up.sh | 41 ++--- examples/local/vtgate-up.sh | 97 ++-------- examples/local/vttablet-up.sh | 171 ++++-------------- 21 files changed, 197 insertions(+), 362 deletions(-) create mode 100755 examples/local/mysqlctl-down.sh create mode 100755 examples/local/mysqlctl-up.sh diff --git a/examples/local/101_initial_cluster.sh b/examples/local/101_initial_cluster.sh index 1a484d98d5d..6e5b61cbf8f 100755 --- a/examples/local/101_initial_cluster.sh +++ b/examples/local/101_initial_cluster.sh @@ -17,35 +17,38 @@ # this script brings up zookeeper and all the vitess components # required for a single shard deployment. -set -e +source ./env.sh -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") -source "${script_root}/env.sh" +set -e # start topo server if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 "$script_root/zk-up.sh" + CELL=zone1 ./zk-up.sh else - CELL=zone1 "$script_root/etcd-up.sh" + CELL=zone1 ./etcd-up.sh fi # start vtctld -CELL=zone1 "$script_root/vtctld-up.sh" +CELL=zone1 ./vtctld-up.sh & # start vttablets for keyspace commerce -CELL=zone1 KEYSPACE=commerce UID_BASE=100 "$script_root/vttablet-up.sh" +for i in 100 101 102; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + CELL=zone1 KEYSPACE=commerce TABLET_UID=$i ./vttablet-up.sh & +done + +sleep 20 # set one of the replicas to master -./lvtctl.sh InitShardMaster -force commerce/0 zone1-100 +vtctlclient -server localhost:15999 InitShardMaster -force commerce/0 zone1-100 # create the schema -./lvtctl.sh ApplySchema -sql-file create_commerce_schema.sql commerce +vtctlclient -server localhost:15999 ApplySchema -sql-file create_commerce_schema.sql commerce # create the vschema -./lvtctl.sh ApplyVSchema -vschema_file vschema_commerce_initial.json commerce +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_initial.json commerce # start vtgate -CELL=zone1 "$script_root/vtgate-up.sh" +CELL=zone1 ./vtgate-up.sh & -disown -a +sleep 20 diff --git a/examples/local/201_customer_keyspace.sh b/examples/local/201_customer_keyspace.sh index 72c415e6527..f948175195c 100755 --- a/examples/local/201_customer_keyspace.sh +++ b/examples/local/201_customer_keyspace.sh @@ -16,8 +16,5 @@ # this script creates a new keyspace in preparation for vertical resharding -set -e +vtctlclient -server localhost:15999 CreateKeyspace -served_from='master:commerce,replica:commerce,rdonly:commerce' customer -./lvtctl.sh CreateKeyspace -served_from='master:commerce,replica:commerce,rdonly:commerce' customer - -disown -a diff --git a/examples/local/202_customer_tablets.sh b/examples/local/202_customer_tablets.sh index 9420ba19713..ccc006becd5 100755 --- a/examples/local/202_customer_tablets.sh +++ b/examples/local/202_customer_tablets.sh @@ -18,16 +18,19 @@ # resharding it also splits the vschema between the two keyspaces # old (commerce) and new (customer) +source ./env.sh + set -e -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +for i in 200 201 202; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & +done -CELL=zone1 KEYSPACE=customer UID_BASE=200 "$script_root/vttablet-up.sh" +sleep 20 -./lvtctl.sh InitShardMaster -force customer/0 zone1-200 -./lvtctl.sh CopySchemaShard -tables customer,corder commerce/0 customer/0 -./lvtctl.sh ApplyVSchema -vschema_file vschema_commerce_vsplit.json commerce -./lvtctl.sh ApplyVSchema -vschema_file vschema_customer_vsplit.json customer +vtctlclient -server localhost:15999 InitShardMaster -force customer/0 zone1-200 +vtctlclient -server localhost:15999 CopySchemaShard -tables customer,corder commerce/0 customer/0 +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_vsplit.json commerce +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_customer_vsplit.json customer -disown -a diff --git a/examples/local/203_vertical_split.sh b/examples/local/203_vertical_split.sh index 5429430f052..f7b25d9e215 100755 --- a/examples/local/203_vertical_split.sh +++ b/examples/local/203_vertical_split.sh @@ -17,16 +17,10 @@ # this script copies over all the data from commerce keyspace to # customer keyspace for the customer and corder tables -set -e - -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +source ./env.sh -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source "$script_root/env.sh" +set -e -# shellcheck disable=SC2086 vtworker \ $TOPOLOGY_FLAGS \ -cell zone1 \ @@ -35,4 +29,3 @@ vtworker \ -use_v3_resharding_mode \ VerticalSplitClone -min_healthy_tablets=1 -tables=customer,corder customer/0 -disown -a diff --git a/examples/local/204_vertical_migrate_replicas.sh b/examples/local/204_vertical_migrate_replicas.sh index 1e13f62a4b3..45ada0aaf63 100755 --- a/examples/local/204_vertical_migrate_replicas.sh +++ b/examples/local/204_vertical_migrate_replicas.sh @@ -17,9 +17,6 @@ # this script migrates traffic for the new customer keyspace to the new # tablets of types rdonly and replica -set -e +vtctlclient -server localhost:15999 MigrateServedFrom customer/0 rdonly +vtctlclient -server localhost:15999 MigrateServedFrom customer/0 replica -./lvtctl.sh MigrateServedFrom customer/0 rdonly -./lvtctl.sh MigrateServedFrom customer/0 replica - -disown -a diff --git a/examples/local/205_vertical_migrate_master.sh b/examples/local/205_vertical_migrate_master.sh index 3aebc52a05c..211d27eb6a7 100755 --- a/examples/local/205_vertical_migrate_master.sh +++ b/examples/local/205_vertical_migrate_master.sh @@ -17,8 +17,4 @@ # this script migrates master traffic for the customer keyspace to the # new master tablet -set -e - -./lvtctl.sh MigrateServedFrom customer/0 master - -disown -a +vtctlclient -server localhost:15999 MigrateServedFrom customer/0 master diff --git a/examples/local/206_clean_commerce.sh b/examples/local/206_clean_commerce.sh index 6139558c385..7111a4a91fc 100755 --- a/examples/local/206_clean_commerce.sh +++ b/examples/local/206_clean_commerce.sh @@ -17,11 +17,7 @@ # this script removes the customer and corder tables from the commerce # keyspace -set -e - -./lvtctl.sh ApplySchema -sql-file drop_commerce_tables.sql commerce -./lvtctl.sh SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 rdonly -./lvtctl.sh SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 replica -./lvtctl.sh SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 master - -disown -a +vtctlclient -server localhost:15999 ApplySchema -sql-file drop_commerce_tables.sql commerce +vtctlclient -server localhost:15999 SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 rdonly +vtctlclient -server localhost:15999 SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 replica +vtctlclient -server localhost:15999 SetShardTabletControl -blacklisted_tables=customer,corder -remove commerce/0 master diff --git a/examples/local/301_customer_sharded.sh b/examples/local/301_customer_sharded.sh index 4d7bacf01d4..5335a0e7392 100755 --- a/examples/local/301_customer_sharded.sh +++ b/examples/local/301_customer_sharded.sh @@ -20,11 +20,7 @@ # it also changes the customer vschema from unsharded to sharded and # sets up the necessary vindexes -set -e - -./lvtctl.sh ApplySchema -sql-file create_commerce_seq.sql commerce -./lvtctl.sh ApplyVSchema -vschema_file vschema_commerce_seq.json commerce -./lvtctl.sh ApplySchema -sql-file create_customer_sharded.sql customer -./lvtctl.sh ApplyVSchema -vschema_file vschema_customer_sharded.json customer - -disown -a +vtctlclient -server localhost:15999 ApplySchema -sql-file create_commerce_seq.sql commerce +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_seq.json commerce +vtctlclient -server localhost:15999 ApplySchema -sql-file create_customer_sharded.sql customer +vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_customer_sharded.json customer diff --git a/examples/local/302_new_shards.sh b/examples/local/302_new_shards.sh index 8475a83f1b2..629e3e19517 100755 --- a/examples/local/302_new_shards.sh +++ b/examples/local/302_new_shards.sh @@ -17,17 +17,21 @@ # this script brings up new tablets for the two new shards that we will # be creating in the customer keyspace and copies the schema -set -e +source ./env.sh -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +set -e -SHARD=-80 CELL=zone1 KEYSPACE=customer UID_BASE=300 "$script_root/vttablet-up.sh" -SHARD=80- CELL=zone1 KEYSPACE=customer UID_BASE=400 "$script_root/vttablet-up.sh" +for i in 300 301 302; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & +done -./lvtctl.sh InitShardMaster -force customer/-80 zone1-300 -./lvtctl.sh InitShardMaster -force customer/80- zone1-400 -./lvtctl.sh CopySchemaShard customer/0 customer/-80 -./lvtctl.sh CopySchemaShard customer/0 customer/80- +for i in 400 401 402; do + CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + SHARD=80- CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & +done -disown -a +vtctlclient -server localhost:15999 InitShardMaster -force customer/-80 zone1-300 +vtctlclient -server localhost:15999 InitShardMaster -force customer/80- zone1-400 +vtctlclient -server localhost:15999 CopySchemaShard customer/0 customer/-80 +vtctlclient -server localhost:15999 CopySchemaShard customer/0 customer/80- diff --git a/examples/local/303_horizontal_split.sh b/examples/local/303_horizontal_split.sh index ea1d9285fc4..afd5d5c1d10 100755 --- a/examples/local/303_horizontal_split.sh +++ b/examples/local/303_horizontal_split.sh @@ -17,22 +17,14 @@ # this script copies the data from customer/0 to customer/-80 and customer/80- # each row will be copied to exactly one shard based on the vindex value -set -e - -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +source ./env.sh -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source "${script_root}/env.sh" +set -e -# shellcheck disable=SC2086 vtworker \ - $TOPOLOGY_FLAGS \ - -cell zone1 \ - -log_dir "$VTDATAROOT"/tmp \ - -alsologtostderr \ - -use_v3_resharding_mode \ - SplitClone -min_healthy_rdonly_tablets=1 customer/0 - -disown -a + $TOPOLOGY_FLAGS \ + -cell zone1 \ + -log_dir "$VTDATAROOT"/tmp \ + -alsologtostderr \ + -use_v3_resharding_mode \ + SplitClone -min_healthy_rdonly_tablets=1 customer/0 diff --git a/examples/local/304_migrate_replicas.sh b/examples/local/304_migrate_replicas.sh index 917d650fdb4..3aed031ebab 100755 --- a/examples/local/304_migrate_replicas.sh +++ b/examples/local/304_migrate_replicas.sh @@ -16,9 +16,5 @@ # this script migrates traffic for the rdonly and replica tablets -set -e - -./lvtctl.sh MigrateServedTypes customer/0 rdonly -./lvtctl.sh MigrateServedTypes customer/0 replica - -disown -a +vtctlclient -server localhost:15999 MigrateServedTypes customer/0 rdonly +vtctlclient -server localhost:15999 MigrateServedTypes customer/0 replica diff --git a/examples/local/305_migrate_master.sh b/examples/local/305_migrate_master.sh index e5ebed81e77..a9da079ea7c 100755 --- a/examples/local/305_migrate_master.sh +++ b/examples/local/305_migrate_master.sh @@ -16,9 +16,6 @@ # this script migrates traffic for the master tablet -set -e - -./lvtctl.sh MigrateServedTypes customer/0 master +vtctlclient -server localhost:15999 MigrateServedTypes customer/0 master # data has been copied over to shards, and databases for the new shards are now available -disown -a diff --git a/examples/local/306_down_shard_0.sh b/examples/local/306_down_shard_0.sh index 79bdd6f3318..d5d82a8cc05 100755 --- a/examples/local/306_down_shard_0.sh +++ b/examples/local/306_down_shard_0.sh @@ -15,11 +15,7 @@ # limitations under the License. # this script brings down the tablets for customer/0 keyspace -set -e +for i in 200 201 202; do + CELL=zone1 TABLET_UID=$i ./vttablet-down.sh +done -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") - -CELL=zone1 UID_BASE=200 "$script_root/vttablet-down.sh" - -disown -a diff --git a/examples/local/307_delete_shard_0.sh b/examples/local/307_delete_shard_0.sh index 6076b0fabbe..662bb7cdfe9 100755 --- a/examples/local/307_delete_shard_0.sh +++ b/examples/local/307_delete_shard_0.sh @@ -16,8 +16,4 @@ # this script deletes the old shard 0 which has been replaced by 2 shards -set -e - -./lvtctl.sh DeleteShard -recursive customer/0 - -disown -a +vtctlclient -server localhost:15999 DeleteShard -recursive customer/0 diff --git a/examples/local/401_teardown.sh b/examples/local/401_teardown.sh index b0168ae133d..f53991f4a9f 100755 --- a/examples/local/401_teardown.sh +++ b/examples/local/401_teardown.sh @@ -17,23 +17,22 @@ # We should not assume that any of the steps have been executed. # This makes it possible for a user to cleanup at any point. -set -e +source ./env.sh -# shellcheck disable=SC2128 -script_root=$(dirname "${BASH_SOURCE}") +set -e ./vtgate-down.sh -for TABLET in 100 200 300 400; do - ./lvtctl.sh GetTablet zone1-$TABLET >/dev/null 2>&1 && CELL=zone1 UID_BASE=$TABLET "$script_root/vttablet-down.sh" -done; +# for TABLET in 100 200 300 400; do +# vtctlclient -server localhost:15999 GetTablet zone1-$TABLET >/dev/null 2>&1 && CELL=zone1 UID_BASE=$TABLET ./vttablet-down.sh +# done; ./vtctld-down.sh if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 "$script_root/zk-down.sh" + CELL=zone1 ./zk-down.sh else - CELL=zone1 "$script_root/etcd-down.sh" + CELL=zone1 ./etcd-down.sh fi # pedantic check: grep for any remaining processes diff --git a/examples/local/mysqlctl-down.sh b/examples/local/mysqlctl-down.sh new file mode 100755 index 00000000000..ed2d92318cd --- /dev/null +++ b/examples/local/mysqlctl-down.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "TODO" diff --git a/examples/local/mysqlctl-up.sh b/examples/local/mysqlctl-up.sh new file mode 100755 index 00000000000..05ca1c18721 --- /dev/null +++ b/examples/local/mysqlctl-up.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is an example script that creates a single shard vttablet deployment. + +source ./env.sh + +set -xe + +cell=${CELL:-'test'} +uid=$TABLET_UID +mysql_port=$[17000 + $uid] +printf -v alias '%s-%010d' $cell $uid +printf -v tablet_dir 'vt_%010d' $uid + +mkdir -p $VTDATAROOT/backups + +echo "Starting MySQL for tablet $alias..." +action="init" + +if [ -d $VTDATAROOT/$tablet_dir ]; then + echo "Resuming from existing vttablet dir:" + echo " $VTDATAROOT/$tablet_dir" + action='start' +fi + +mysqlctl \ + -log_dir $VTDATAROOT/tmp \ + -tablet_uid $uid \ + -mysql_port $mysql_port \ + $action diff --git a/examples/local/vtctld-down.sh b/examples/local/vtctld-down.sh index 398311ca043..372fcd4a382 100755 --- a/examples/local/vtctld-down.sh +++ b/examples/local/vtctld-down.sh @@ -14,13 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is an example script that starts vtctld. +# This is an example script that stops vtctld. -set -e +source ./env.sh -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh +set -xe -pid=`cat $VTDATAROOT/tmp/vtctld.pid` echo "Stopping vtctld..." -kill -9 $pid +kill -9 `cat $VTDATAROOT/tmp/vtctld.pid` diff --git a/examples/local/vtctld-up.sh b/examples/local/vtctld-up.sh index b1965e06baf..075ac33e578 100755 --- a/examples/local/vtctld-up.sh +++ b/examples/local/vtctld-up.sh @@ -16,38 +16,25 @@ # This is an example script that starts vtctld. +source ./env.sh + set -e cell=${CELL:-'test'} grpc_port=15999 -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -optional_auth_args='' -if [ "$1" = "--enable-grpc-static-auth" ]; -then - echo "Enabling Auth with static authentication in grpc" - optional_auth_args='-grpc_auth_static_client_creds ./grpc_static_client_auth.json ' -fi - echo "Starting vtctld..." # shellcheck disable=SC2086 vtctld \ - $TOPOLOGY_FLAGS \ - -cell $cell \ - -workflow_manager_init \ - -workflow_manager_use_election \ - -service_map 'grpc-vtctl' \ - -backup_storage_implementation file \ - -file_backup_storage_root $VTDATAROOT/backups \ - -log_dir $VTDATAROOT/tmp \ - -port $vtctld_web_port \ - -grpc_port $grpc_port \ - -pid_file $VTDATAROOT/tmp/vtctld.pid \ - $optional_auth_args \ - > $VTDATAROOT/tmp/vtctld.out 2>&1 & -disown -a - -echo "Access vtctld web UI at http://$hostname:$vtctld_web_port" -echo "Send commands with: vtctlclient -server $hostname:$grpc_port ..." + $TOPOLOGY_FLAGS \ + -cell $cell \ + -workflow_manager_init \ + -workflow_manager_use_election \ + -service_map 'grpc-vtctl' \ + -backup_storage_implementation file \ + -file_backup_storage_root $VTDATAROOT/backups \ + -log_dir $VTDATAROOT/tmp \ + -port $vtctld_web_port \ + -grpc_port $grpc_port \ + -pid_file $VTDATAROOT/tmp/vtctld.pid \ + > $VTDATAROOT/tmp/vtctld.out 2>&1 diff --git a/examples/local/vtgate-up.sh b/examples/local/vtgate-up.sh index e4fb1dd5c10..00a0da5e5a2 100755 --- a/examples/local/vtgate-up.sh +++ b/examples/local/vtgate-up.sh @@ -16,7 +16,9 @@ # This is an example script that starts a single vtgate. -set -e +source ./env.sh + +set -xe cell=${CELL:-'test'} web_port=15001 @@ -24,82 +26,23 @@ grpc_port=15991 mysql_server_port=15306 mysql_server_socket_path="/tmp/mysql.sock" -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -# When this script is run with the argument "--enable-tls", then it will generate signed certs and -# configure VTGate to accept only TLS connections with client authentication. This allows for end-to-end -# SSL testing (see also the "client_jdbc.sh" script). -optional_tls_args='' -if [ "$1" = "--enable-tls" ]; -then - echo "Enabling TLS with client authentication" - config_dir=../../java/grpc-client/src/test/resources - cert_dir=$VTDATAROOT/tls - rm -Rf $cert_dir - mkdir -p $cert_dir - - # Create CA - openssl genrsa -out $cert_dir/ca-key.pem - openssl req -new -x509 -nodes -days 3600 -batch -config $config_dir/ca.config -key $cert_dir/ca-key.pem -out $cert_dir/ca-cert.pem - - # Create server-side signed cert - openssl req -newkey rsa:2048 -days 3600 -nodes -batch -config $config_dir/cert.config -keyout $cert_dir/server-key.pem -out $cert_dir/server-req.pem - openssl x509 -req -in $cert_dir/server-req.pem -days 3600 -CA $cert_dir/ca-cert.pem -CAkey $cert_dir/ca-key.pem -set_serial 01 -out $cert_dir/server-cert.pem - - # Create client-side signed cert - openssl req -newkey rsa:2048 -days 3600 -nodes -batch -config $config_dir/cert.config -keyout $cert_dir/client-key.pem -out $cert_dir/client-req.pem - openssl x509 -req -in $cert_dir/client-req.pem -days 3600 -CA $cert_dir/ca-cert.pem -CAkey $cert_dir/ca-key.pem -set_serial 02 -out $cert_dir/client-cert.pem - - optional_tls_args="-grpc_cert $cert_dir/server-cert.pem -grpc_key $cert_dir/server-key.pem -grpc_ca $cert_dir/ca-cert.pem" -fi +rm -rf /tmp/mysql.sock # todo: handle in vtgate to check for file -optional_auth_args='-mysql_auth_server_impl none' -optional_grpc_auth_args='' -if [ "$1" = "--enable-grpc-static-auth" ]; -then - echo "Enabling Auth with static authentication in grpc" - optional_grpc_auth_args='-grpc_auth_static_client_creds ./grpc_static_client_auth.json' -fi - -if [ "$1" = "--enable-mysql-static-auth" ]; -then - echo "Enabling Auth with mysql static authentication" - optional_auth_args='-mysql_auth_server_static_file ./mysql_auth_server_static_creds.json' -fi - -# Start vtgate. -# shellcheck disable=SC2086 vtgate \ - $TOPOLOGY_FLAGS \ - -log_dir $VTDATAROOT/tmp \ - -log_queries_to_file $VTDATAROOT/tmp/vtgate_querylog.txt \ - -port $web_port \ - -grpc_port $grpc_port \ - -mysql_server_port $mysql_server_port \ - -mysql_server_socket_path $mysql_server_socket_path \ - -cell $cell \ - -cells_to_watch $cell \ - -tablet_types_to_wait MASTER,REPLICA \ - -gateway_implementation discoverygateway \ - -service_map 'grpc-vtgateservice' \ - -pid_file $VTDATAROOT/tmp/vtgate.pid \ - $optional_auth_args \ - $optional_grpc_auth_args \ - $optional_tls_args \ - > $VTDATAROOT/tmp/vtgate.out 2>&1 & - -# Block waiting for vtgate to be listening -# Not the same as healthy - -echo "Waiting for vtgate to be up..." -while true; do - curl -I "http://$hostname:$web_port/debug/status" >/dev/null 2>&1 && break - sleep 0.1 -done; -echo "vtgate is up!" - -echo "Access vtgate at http://$hostname:$web_port/debug/status" - -disown -a + -topo_implementation etcd2 \ + -topo_global_server_address localhost:2379 \ + -topo_global_root /vitess/global \ + -log_dir $VTDATAROOT/tmp \ + -log_queries_to_file $VTDATAROOT/tmp/vtgate_querylog.txt \ + -port $web_port \ + -grpc_port $grpc_port \ + -mysql_server_port $mysql_server_port \ + -mysql_server_socket_path $mysql_server_socket_path \ + -cell $cell \ + -cells_to_watch $cell \ + -tablet_types_to_wait MASTER,REPLICA \ + -gateway_implementation discoverygateway \ + -service_map 'grpc-vtgateservice' \ + -pid_file $VTDATAROOT/tmp/vtgate.pid \ + > $VTDATAROOT/tmp/vtgate.out 2>&1 & diff --git a/examples/local/vttablet-up.sh b/examples/local/vttablet-up.sh index 4914a65493b..c771084b7fb 100755 --- a/examples/local/vttablet-up.sh +++ b/examples/local/vttablet-up.sh @@ -14,148 +14,47 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is an example script that creates a single shard vttablet deployment. +source ./env.sh -set -e +set -xe cell=${CELL:-'test'} keyspace=${KEYSPACE:-'test_keyspace'} shard=${SHARD:-'0'} -uid_base=${UID_BASE:-'100'} -port_base=$[15000 + $uid_base] -grpc_port_base=$[16000 + $uid_base] -mysql_port_base=$[17000 + $uid_base] +uid=$TABLET_UID +mysql_port=$[17000 + $uid] +port=$[15000 + $uid] +grpc_port=$[16000 + $uid] +printf -v alias '%s-%010d' $cell $uid +printf -v tablet_dir 'vt_%010d' $uid tablet_hostname='' +printf -v tablet_logfile 'vttablet_%010d_querylog.txt' $uid -# Travis hostnames are too long for MySQL, so we use IP. -# Otherwise, blank hostname means the tablet auto-detects FQDN. -if [ "$TRAVIS" == true ]; then - tablet_hostname=`hostname -i` +tablet_type=replica +if [[ "${uid: -1}" -gt 1 ]]; then + tablet_type=rdonly fi -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -mkdir -p $VTDATAROOT/backups - -# Start 3 vttablets by default. -# Pass TABLETS_UIDS indices as env variable to change -uids=${TABLETS_UIDS:-'0 1 2'} - -# Start all mysqlds in background. -for uid_index in $uids; do - uid=$[$uid_base + $uid_index] - mysql_port=$[$mysql_port_base + $uid_index] - printf -v alias '%s-%010d' $cell $uid - printf -v tablet_dir 'vt_%010d' $uid - - export KEYSPACE=$keyspace - export SHARD=$shard - export TABLET_ID=$alias - export TABLET_DIR=$tablet_dir - export MYSQL_PORT=$mysql_port - - tablet_type=replica - if [[ $uid_index -gt 1 ]]; then - tablet_type=rdonly - fi - - export TABLET_TYPE=$tablet_type - - echo "Starting MySQL for tablet $alias..." - action="init" - if [ -d $VTDATAROOT/$tablet_dir ]; then - echo "Resuming from existing vttablet dir:" - echo " $VTDATAROOT/$tablet_dir" - action='start' - fi - - set +e - - mysqlctl \ - -log_dir $VTDATAROOT/tmp \ - -tablet_uid $uid \ - -mysql_port $mysql_port \ - $action - - err=$? - if [[ $err -ne 0 ]]; then - fail "This script fails to start mysqld, possibly due to apparmor or selinux protection. - Utilities to help investigate: - apparmor: \"sudo aa-status\" - selinux: \"sudo sestatus\" - Please disable if so indicated. - You may also need to empty your \$VTDATAROOT to start clean." - fi - - set -e - -done - -# Wait for all mysqld to start up. -wait - -optional_auth_args='' -if [ "$1" = "--enable-grpc-static-auth" ]; -then - echo "Enabling Auth with static authentication in grpc" - optional_auth_args='-grpc_auth_mode static -grpc_auth_static_password_file ./grpc_static_auth.json -grpc_auth_static_client_creds ./grpc_static_client_auth.json' -fi - -# Start all vttablets in background. -for uid_index in $uids; do - uid=$[$uid_base + $uid_index] - port=$[$port_base + $uid_index] - grpc_port=$[$grpc_port_base + $uid_index] - printf -v alias '%s-%010d' $cell $uid - printf -v tablet_dir 'vt_%010d' $uid - printf -v tablet_logfile 'vttablet_%010d_querylog.txt' $uid - tablet_type=replica - if [[ $uid_index -gt 1 ]]; then - tablet_type=rdonly - fi - - echo "Starting vttablet for $alias..." - # shellcheck disable=SC2086 - vttablet \ - $TOPOLOGY_FLAGS \ - -log_dir $VTDATAROOT/tmp \ - -log_queries_to_file $VTDATAROOT/tmp/$tablet_logfile \ - -tablet-path $alias \ - -tablet_hostname "$tablet_hostname" \ - -init_keyspace $keyspace \ - -init_shard $shard \ - -init_tablet_type $tablet_type \ - -health_check_interval 5s \ - -enable_semi_sync \ - -enable_replication_reporter \ - -backup_storage_implementation file \ - -file_backup_storage_root $VTDATAROOT/backups \ - -restore_from_backup \ - -port $port \ - -grpc_port $grpc_port \ - -service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ - -pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ - -vtctld_addr http://$hostname:$vtctld_web_port/ \ - $optional_auth_args \ - > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 & - - echo "Access tablet $alias at http://$hostname:$port/debug/status" -done - -# Block waiting for all tablets to be listening -# Not the same as healthy - -echo "Waiting for tablets to be listening..." -for uid_index in $uids; do - port=$[$port_base + $uid_index] - for i in $(seq 0 300); do - curl -I "http://$hostname:$port/debug/status" >/dev/null 2>&1 && break - sleep 0.1 - done; - # check one last time - curl -I "http://$hostname:$port/debug/status" || fail "tablets could not be started!" -done; -echo "Tablets up!" - -disown -a +echo "Starting vttablet for $alias..." +# shellcheck disable=SC2086 +vttablet \ + $TOPOLOGY_FLAGS \ + -log_dir $VTDATAROOT/tmp \ + -log_queries_to_file $VTDATAROOT/tmp/$tablet_logfile \ + -tablet-path $alias \ + -tablet_hostname "$tablet_hostname" \ + -init_keyspace $keyspace \ + -init_shard $shard \ + -init_tablet_type $tablet_type \ + -health_check_interval 5s \ + -enable_semi_sync \ + -enable_replication_reporter \ + -backup_storage_implementation file \ + -file_backup_storage_root $VTDATAROOT/backups \ + -restore_from_backup \ + -port $port \ + -grpc_port $grpc_port \ + -service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ + -pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ + -vtctld_addr http://$hostname:$vtctld_web_port/ \ + > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 From 4979d1cc6c417d12c3b1ec4371ed206417496633 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 12:19:54 -0800 Subject: [PATCH 071/825] More refactoring Signed-off-by: Morgan Tocker --- examples/local/101_initial_cluster.sh | 14 +-- examples/local/202_customer_tablets.sh | 4 +- examples/local/302_new_shards.sh | 6 +- examples/local/306_down_shard_0.sh | 2 +- examples/local/401_teardown.sh | 8 +- examples/local/lvtctl.sh | 19 ---- examples/local/mysqlctl-down.sh | 3 - examples/local/{ => scripts}/etcd-down.sh | 11 +-- examples/local/{ => scripts}/etcd-up.sh | 10 +- .../mysqlctl-down.sh} | 14 +-- examples/local/{ => scripts}/mysqlctl-up.sh | 2 +- examples/local/{ => scripts}/vtctld-down.sh | 0 examples/local/{ => scripts}/vtctld-up.sh | 0 examples/local/{ => scripts}/vtgate-down.sh | 0 .../{vtworker-up.sh => scripts/vtgate-up.sh} | 36 +++++--- .../{lmysql.sh => scripts/vttablet-down.sh} | 16 +++- examples/local/{ => scripts}/vttablet-up.sh | 2 +- examples/local/{ => scripts}/zk-down.sh | 5 +- examples/local/{ => scripts}/zk-up.sh | 5 +- examples/local/vtgate-up.sh | 91 ------------------- examples/local/vttablet-down.sh | 53 ----------- 21 files changed, 74 insertions(+), 227 deletions(-) delete mode 100755 examples/local/lvtctl.sh delete mode 100755 examples/local/mysqlctl-down.sh rename examples/local/{ => scripts}/etcd-down.sh (79%) rename examples/local/{ => scripts}/etcd-up.sh (92%) rename examples/local/{vtworker-down.sh => scripts/mysqlctl-down.sh} (77%) rename examples/local/{ => scripts}/mysqlctl-up.sh (99%) rename examples/local/{ => scripts}/vtctld-down.sh (100%) rename examples/local/{ => scripts}/vtctld-up.sh (100%) rename examples/local/{ => scripts}/vtgate-down.sh (100%) rename examples/local/{vtworker-up.sh => scripts/vtgate-up.sh} (50%) rename examples/local/{lmysql.sh => scripts/vttablet-down.sh} (66%) rename examples/local/{ => scripts}/vttablet-up.sh (99%) rename examples/local/{ => scripts}/zk-down.sh (92%) rename examples/local/{ => scripts}/zk-up.sh (96%) delete mode 100755 examples/local/vtgate-up.sh delete mode 100755 examples/local/vttablet-down.sh diff --git a/examples/local/101_initial_cluster.sh b/examples/local/101_initial_cluster.sh index 2faaea0c595..42f7525ac6c 100755 --- a/examples/local/101_initial_cluster.sh +++ b/examples/local/101_initial_cluster.sh @@ -23,18 +23,18 @@ set -e # start topo server if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 ./zk-up.sh + CELL=zone1 ./scripts/zk-up.sh else - CELL=zone1 ./etcd-up.sh + CELL=zone1 ./scripts/etcd-up.sh fi # start vtctld -CELL=zone1 ./vtctld-up.sh & +CELL=zone1 ./scripts/vtctld-up.sh & # start vttablets for keyspace commerce for i in 100 101 102; do - CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh - CELL=zone1 KEYSPACE=commerce TABLET_UID=$i ./vttablet-up.sh & + CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh + CELL=zone1 KEYSPACE=commerce TABLET_UID=$i ./scripts/vttablet-up.sh & done sleep 20 # @TODO: replace with wait for tablets command @@ -49,6 +49,6 @@ vtctlclient -server localhost:15999 ApplySchema -sql-file create_commerce_schema vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_initial.json commerce # start vtgate -CELL=zone1 ./vtgate-up.sh & +CELL=zone1 ./scripts/vtgate-up.sh & -sleep 20 +sleep 20 # @TODO: replace with a wait command diff --git a/examples/local/202_customer_tablets.sh b/examples/local/202_customer_tablets.sh index 2e30983c7a1..0f40ea87132 100755 --- a/examples/local/202_customer_tablets.sh +++ b/examples/local/202_customer_tablets.sh @@ -23,8 +23,8 @@ source ./env.sh set -e for i in 200 201 202; do - CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh - CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & + CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh + CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh & done sleep 20 # @TODO: replace with wait for tablets command diff --git a/examples/local/302_new_shards.sh b/examples/local/302_new_shards.sh index 5bc7959f124..a6e6ba6eaa2 100755 --- a/examples/local/302_new_shards.sh +++ b/examples/local/302_new_shards.sh @@ -23,12 +23,12 @@ set -e for i in 300 301 302; do CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh - SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & + SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh & done for i in 400 401 402; do - CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh - SHARD=80- CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./vttablet-up.sh & + CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh + SHARD=80- CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh & done sleep 20 # TODO: replace by wait for tablets diff --git a/examples/local/306_down_shard_0.sh b/examples/local/306_down_shard_0.sh index ad6d3b85a7a..722ffe840dd 100755 --- a/examples/local/306_down_shard_0.sh +++ b/examples/local/306_down_shard_0.sh @@ -16,7 +16,7 @@ # this script brings down the tablets for customer/0 keyspace for i in 200 201 202; do - CELL=zone1 TABLET_UID=$i ./vttablet-down.sh + CELL=zone1 TABLET_UID=$i ./scripts/vttablet-down.sh #TODO: shutdown mysqlctl as well done diff --git a/examples/local/401_teardown.sh b/examples/local/401_teardown.sh index f53991f4a9f..c82fa18a5e7 100755 --- a/examples/local/401_teardown.sh +++ b/examples/local/401_teardown.sh @@ -21,18 +21,18 @@ source ./env.sh set -e -./vtgate-down.sh +./scripts/vtgate-down.sh # for TABLET in 100 200 300 400; do # vtctlclient -server localhost:15999 GetTablet zone1-$TABLET >/dev/null 2>&1 && CELL=zone1 UID_BASE=$TABLET ./vttablet-down.sh # done; -./vtctld-down.sh +./scripts/vtctld-down.sh if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 ./zk-down.sh + CELL=zone1 ./scripts/k-down.sh else - CELL=zone1 ./etcd-down.sh + CELL=zone1 ./scripts/etcd-down.sh fi # pedantic check: grep for any remaining processes diff --git a/examples/local/lvtctl.sh b/examples/local/lvtctl.sh deleted file mode 100755 index 0197fb79b33..00000000000 --- a/examples/local/lvtctl.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is a convenience script to run vtctlclient against the local example. - -exec vtctlclient -server localhost:15999 "$@" diff --git a/examples/local/mysqlctl-down.sh b/examples/local/mysqlctl-down.sh deleted file mode 100755 index ed2d92318cd..00000000000 --- a/examples/local/mysqlctl-down.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -echo "TODO" diff --git a/examples/local/etcd-down.sh b/examples/local/scripts/etcd-down.sh similarity index 79% rename from examples/local/etcd-down.sh rename to examples/local/scripts/etcd-down.sh index cb2d5b1023f..640a32fb3a0 100755 --- a/examples/local/etcd-down.sh +++ b/examples/local/scripts/etcd-down.sh @@ -16,14 +16,9 @@ # This is an example script that stops the etcd servers started by etcd-up.sh. -set -e - -script_root=$(dirname "${BASH_SOURCE[0]}") +source ./env.sh -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source "${script_root}/env.sh" +set -e -pid=`cat $VTDATAROOT/tmp/etcd.pid` echo "Stopping etcd..." -kill -9 $pid +kill -9 `cat $VTDATAROOT/tmp/etcd.pid` diff --git a/examples/local/etcd-up.sh b/examples/local/scripts/etcd-up.sh similarity index 92% rename from examples/local/etcd-up.sh rename to examples/local/scripts/etcd-up.sh index 0e87537f0b0..b0527a9c8c0 100755 --- a/examples/local/etcd-up.sh +++ b/examples/local/scripts/etcd-up.sh @@ -16,16 +16,12 @@ # This is an example script that creates a quorum of ZooKeeper servers. +source ./env.sh + set -e cell=${CELL:-'test'} - -script_root=$(dirname "${BASH_SOURCE[0]}") export ETCDCTL_API=2 -# shellcheck source=./env.sh -# shellcheck disable=SC1091 -source "${script_root}/env.sh" - # Check that etcd is not already running curl "http://${ETCD_SERVER}" > /dev/null 2>&1 && fail "etcd is already running. Exiting." @@ -37,7 +33,6 @@ sleep 5 echo "add /vitess/global" etcdctl --endpoints "http://${ETCD_SERVER}" mkdir /vitess/global & - echo "add /vitess/$cell" etcdctl --endpoints "http://${ETCD_SERVER}" mkdir /vitess/$cell & @@ -55,4 +50,3 @@ set -e echo "etcd start done..." - diff --git a/examples/local/vtworker-down.sh b/examples/local/scripts/mysqlctl-down.sh similarity index 77% rename from examples/local/vtworker-down.sh rename to examples/local/scripts/mysqlctl-down.sh index 0ec047a5638..bf0cff91c2c 100755 --- a/examples/local/vtworker-down.sh +++ b/examples/local/scripts/mysqlctl-down.sh @@ -14,12 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -e +# This is an example script that stops the mysqld and vttablet instances +# created by vttablet-up.sh -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh +source ./env.sh -pid=`cat $VTDATAROOT/tmp/vtworker.pid` -echo "Stopping vtworker..." -kill $pid +uid=${TABLET_UID} + +mysqlctl \ + -tablet_uid $uid \ + shutdown diff --git a/examples/local/mysqlctl-up.sh b/examples/local/scripts/mysqlctl-up.sh similarity index 99% rename from examples/local/mysqlctl-up.sh rename to examples/local/scripts/mysqlctl-up.sh index 05ca1c18721..38ea7e5df41 100755 --- a/examples/local/mysqlctl-up.sh +++ b/examples/local/scripts/mysqlctl-up.sh @@ -18,7 +18,7 @@ source ./env.sh -set -xe +set -e cell=${CELL:-'test'} uid=$TABLET_UID diff --git a/examples/local/vtctld-down.sh b/examples/local/scripts/vtctld-down.sh similarity index 100% rename from examples/local/vtctld-down.sh rename to examples/local/scripts/vtctld-down.sh diff --git a/examples/local/vtctld-up.sh b/examples/local/scripts/vtctld-up.sh similarity index 100% rename from examples/local/vtctld-up.sh rename to examples/local/scripts/vtctld-up.sh diff --git a/examples/local/vtgate-down.sh b/examples/local/scripts/vtgate-down.sh similarity index 100% rename from examples/local/vtgate-down.sh rename to examples/local/scripts/vtgate-down.sh diff --git a/examples/local/vtworker-up.sh b/examples/local/scripts/vtgate-up.sh similarity index 50% rename from examples/local/vtworker-up.sh rename to examples/local/scripts/vtgate-up.sh index ec1a4e5ec41..026f977b1a3 100755 --- a/examples/local/vtworker-up.sh +++ b/examples/local/scripts/vtgate-up.sh @@ -14,22 +14,34 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is an example script that runs vtworker. +# This is an example script that starts a single vtgate. + +source ./env.sh set -e cell=${CELL:-'test'} -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh +web_port=15001 +grpc_port=15991 +mysql_server_port=15306 +mysql_server_socket_path="/tmp/mysql.sock" -echo "Starting vtworker..." -vtworker \ +# Start vtgate. +# shellcheck disable=SC2086 +vtgate \ $TOPOLOGY_FLAGS \ - -cell $cell \ -log_dir $VTDATAROOT/tmp \ - -alsologtostderr \ - -service_map=grpc-vtworker \ - -grpc_port 15033 \ - -port 15032 \ - -pid_file $VTDATAROOT/tmp/vtworker.pid \ - -use_v3_resharding_mode & + -log_queries_to_file $VTDATAROOT/tmp/vtgate_querylog.txt \ + -port $web_port \ + -grpc_port $grpc_port \ + -mysql_server_port $mysql_server_port \ + -mysql_server_socket_path $mysql_server_socket_path \ + -cell $cell \ + -cells_to_watch $cell \ + -tablet_types_to_wait MASTER,REPLICA \ + -gateway_implementation discoverygateway \ + -service_map 'grpc-vtgateservice' \ + -pid_file $VTDATAROOT/tmp/vtgate.pid \ + -mysql_auth_server_impl none \ + > $VTDATAROOT/tmp/vtgate.out 2>&1 + diff --git a/examples/local/lmysql.sh b/examples/local/scripts/vttablet-down.sh similarity index 66% rename from examples/local/lmysql.sh rename to examples/local/scripts/vttablet-down.sh index b043ed69b15..9264a20a78d 100755 --- a/examples/local/lmysql.sh +++ b/examples/local/scripts/vttablet-down.sh @@ -14,6 +14,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is a convenience script to run mysql client against the local vtgate. +# This is an example script that stops the mysqld and vttablet instances +# created by vttablet-up.sh + +source ./env.sh + +uid=${TABLET_UID} +printf -v tablet_dir 'vt_%010d' $uid +pid=`cat $VTDATAROOT/$tablet_dir/vttablet.pid` + +kill $pid + +# Wait for vttablet to die. +while ps -p $pid > /dev/null; do sleep 1; done + -mysql -h 127.0.0.1 -P 15306 -u mysql_user diff --git a/examples/local/vttablet-up.sh b/examples/local/scripts/vttablet-up.sh similarity index 99% rename from examples/local/vttablet-up.sh rename to examples/local/scripts/vttablet-up.sh index c771084b7fb..c5f55c9d5b6 100755 --- a/examples/local/vttablet-up.sh +++ b/examples/local/scripts/vttablet-up.sh @@ -16,7 +16,7 @@ source ./env.sh -set -xe +set -e cell=${CELL:-'test'} keyspace=${KEYSPACE:-'test_keyspace'} diff --git a/examples/local/zk-down.sh b/examples/local/scripts/zk-down.sh similarity index 92% rename from examples/local/zk-down.sh rename to examples/local/scripts/zk-down.sh index bd6cffd0f7b..5a8f3d72648 100755 --- a/examples/local/zk-down.sh +++ b/examples/local/scripts/zk-down.sh @@ -16,10 +16,9 @@ # This is an example script that stops the ZooKeeper servers started by zk-up.sh. -set -e +source ./env.sh -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh +set -e # Stop ZooKeeper servers. echo "Stopping zk servers..." diff --git a/examples/local/zk-up.sh b/examples/local/scripts/zk-up.sh similarity index 96% rename from examples/local/zk-up.sh rename to examples/local/scripts/zk-up.sh index 39585320f5b..83b5ada17ca 100755 --- a/examples/local/zk-up.sh +++ b/examples/local/scripts/zk-up.sh @@ -16,13 +16,12 @@ # This is an example script that creates a quorum of ZooKeeper servers. +source ./env.sh + set -e cell=${CELL:-'test'} -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - # Start ZooKeeper servers. # The "zkctl init" command won't return until the server is able to contact its # peers, so we need to start them all in the background and then wait for them. diff --git a/examples/local/vtgate-up.sh b/examples/local/vtgate-up.sh deleted file mode 100755 index 9595c741dad..00000000000 --- a/examples/local/vtgate-up.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that starts a single vtgate. - -set -e - -cell=${CELL:-'test'} -web_port=15001 -grpc_port=15991 -mysql_server_port=15306 -mysql_server_socket_path="/tmp/mysql.sock" - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -# When this script is run with the argument "--enable-tls", then it will generate signed certs and -# configure VTGate to accept only TLS connections with client authentication. This allows for end-to-end -# SSL testing (see also the "client_jdbc.sh" script). -optional_tls_args='' -if [ "$1" = "--enable-tls" ]; -then - echo "Enabling TLS with client authentication" - config_dir=../../java/grpc-client/src/test/resources - cert_dir=$VTDATAROOT/tls - rm -Rf $cert_dir - mkdir -p $cert_dir - - # Create CA - openssl genrsa -out $cert_dir/ca-key.pem - openssl req -new -x509 -nodes -days 3600 -batch -config $config_dir/ca.config -key $cert_dir/ca-key.pem -out $cert_dir/ca-cert.pem - - # Create server-side signed cert - openssl req -newkey rsa:2048 -days 3600 -nodes -batch -config $config_dir/cert.config -keyout $cert_dir/server-key.pem -out $cert_dir/server-req.pem - openssl x509 -req -in $cert_dir/server-req.pem -days 3600 -CA $cert_dir/ca-cert.pem -CAkey $cert_dir/ca-key.pem -set_serial 01 -out $cert_dir/server-cert.pem - - # Create client-side signed cert - openssl req -newkey rsa:2048 -days 3600 -nodes -batch -config $config_dir/cert.config -keyout $cert_dir/client-key.pem -out $cert_dir/client-req.pem - openssl x509 -req -in $cert_dir/client-req.pem -days 3600 -CA $cert_dir/ca-cert.pem -CAkey $cert_dir/ca-key.pem -set_serial 02 -out $cert_dir/client-cert.pem - - optional_tls_args="-grpc_cert $cert_dir/server-cert.pem -grpc_key $cert_dir/server-key.pem -grpc_ca $cert_dir/ca-cert.pem" -fi - -optional_auth_args='-mysql_auth_server_impl none' -optional_grpc_auth_args='' -if [ "$1" = "--enable-grpc-static-auth" ]; -then - echo "Enabling Auth with static authentication in grpc" - optional_grpc_auth_args='-grpc_auth_static_client_creds ./grpc_static_client_auth.json' -fi - -if [ "$1" = "--enable-mysql-static-auth" ]; -then - echo "Enabling Auth with mysql static authentication" - optional_auth_args='-mysql_auth_server_static_file ./mysql_auth_server_static_creds.json' -fi - -# Start vtgate. -# shellcheck disable=SC2086 -vtgate \ - $TOPOLOGY_FLAGS \ - -log_dir $VTDATAROOT/tmp \ - -log_queries_to_file $VTDATAROOT/tmp/vtgate_querylog.txt \ - -port $web_port \ - -grpc_port $grpc_port \ - -mysql_server_port $mysql_server_port \ - -mysql_server_socket_path $mysql_server_socket_path \ - -cell $cell \ - -cells_to_watch $cell \ - -tablet_types_to_wait MASTER,REPLICA \ - -gateway_implementation discoverygateway \ - -service_map 'grpc-vtgateservice' \ - -pid_file $VTDATAROOT/tmp/vtgate.pid \ - $optional_auth_args \ - $optional_grpc_auth_args \ - $optional_tls_args \ - > $VTDATAROOT/tmp/vtgate.out 2>&1 - diff --git a/examples/local/vttablet-down.sh b/examples/local/vttablet-down.sh deleted file mode 100755 index 44aae662bd0..00000000000 --- a/examples/local/vttablet-down.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops the mysqld and vttablet instances -# created by vttablet-up.sh - -cell=${CELL:-'test'} -uid_base=${UID_BASE:-'100'} - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -# Stop 3 vttablets by default. -# Pass a list of UID indices on the command line to override. -uids=${@:-'0 1 2'} - -wait_pids='' - -for uid_index in $uids; do - uid=$[$uid_base + $uid_index] - printf -v alias '%s-%010d' $cell $uid - printf -v tablet_dir 'vt_%010d' $uid - - echo "Stopping vttablet for $alias..." - pid=`cat $VTDATAROOT/$tablet_dir/vttablet.pid` - kill $pid - wait_pids="$wait_pids $pid" - - echo "Stopping MySQL for tablet $alias..." - mysqlctl \ - -tablet_uid $uid \ - shutdown & -done - -# Wait for vttablets to die. -while ps -p $wait_pids > /dev/null; do sleep 1; done - -# Wait for 'mysqlctl shutdown' commands to finish. -wait - From 2b743fdbaa80896ea0d604a27ecc407b2f545e5c Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 12:24:49 -0800 Subject: [PATCH 072/825] More refactoring Signed-off-by: Morgan Tocker --- examples/local/302_new_shards.sh | 2 +- go.mod | 2 + misc/git/hooks/checkstyle | 44 ------------------ misc/git/hooks/goimports | 38 --------------- misc/git/hooks/golangci-lint | 26 ----------- misc/git/hooks/golint | 79 -------------------------------- misc/git/hooks/govet | 54 ---------------------- misc/git/hooks/shellcheck | 48 ------------------- misc/git/hooks/staticcheck | 70 ---------------------------- misc/git/hooks/tslint | 56 ---------------------- misc/git/hooks/visitorgen | 18 -------- 11 files changed, 3 insertions(+), 434 deletions(-) delete mode 100755 misc/git/hooks/checkstyle delete mode 100755 misc/git/hooks/goimports delete mode 100755 misc/git/hooks/golangci-lint delete mode 100755 misc/git/hooks/golint delete mode 100755 misc/git/hooks/govet delete mode 100755 misc/git/hooks/shellcheck delete mode 100755 misc/git/hooks/staticcheck delete mode 100755 misc/git/hooks/tslint delete mode 100755 misc/git/hooks/visitorgen diff --git a/examples/local/302_new_shards.sh b/examples/local/302_new_shards.sh index a6e6ba6eaa2..188de9a2567 100755 --- a/examples/local/302_new_shards.sh +++ b/examples/local/302_new_shards.sh @@ -22,7 +22,7 @@ source ./env.sh set -e for i in 300 301 302; do - CELL=zone1 TABLET_UID=$i ./mysqlctl-up.sh + CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh & done diff --git a/go.mod b/go.mod index 5310d66e0b1..25077ce841a 100644 --- a/go.mod +++ b/go.mod @@ -52,6 +52,8 @@ require ( github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/misc/git/hooks/checkstyle b/misc/git/hooks/checkstyle deleted file mode 100755 index 777dd512430..00000000000 --- a/misc/git/hooks/checkstyle +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -set -e - -function get_module() { - local path=$1; - while true; do - path=$(dirname $path); - if [ -f "$path/pom.xml" ]; then - echo "$path"; - return; - elif [[ "./" =~ "$path" ]]; then - return; - fi - done -} - -cd java; - -modules=(); - -for file in $(git diff --relative --name-only --cached \*.java); do - module=$(get_module "$file"); - if [ "" != "$module" ] \ - && [[ ! " ${modules[@]} " =~ " $module " ]]; then - modules+=("$module"); - fi -done; - -if [ ${#modules[@]} -eq 0 ]; then - exit; -fi - -modules_arg=$(printf ",%s" "${modules[@]}"); -modules_arg=${modules_arg:1}; - -export MAVEN_OPTS="-client - -XX:+TieredCompilation - -XX:TieredStopAtLevel=1 - -Xverify:none"; - -mvn -q -pl "$modules_arg" checkstyle:check; - -cd -; diff --git a/misc/git/hooks/goimports b/misc/git/hooks/goimports deleted file mode 100755 index 786e7ab517e..00000000000 --- a/misc/git/hooks/goimports +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# git goimports pre-commit hook -# -# To use, store as .git/hooks/pre-commit inside your repository and make sure -# it has execute permissions. -# -# This script does not handle file names that contain spaces. -gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$') - -[ -z "$gofiles" ] && exit 0 -unformatted=$(goimports -l=true $gofiles 2>&1 | awk -F: '{print $1}') -[ -z "$unformatted" ] && exit 0 - -# Some files are not goimports'd. Print message and fail. - -echo >&2 "Go files must be formatted with goimports. Please run:" -echo >&2 -echo -n >&2 " goimports -w" -for fn in $unformatted; do - echo -n >&2 " $PWD/$fn" -done -echo - -exit 1 diff --git a/misc/git/hooks/golangci-lint b/misc/git/hooks/golangci-lint deleted file mode 100755 index ea7da67e6b9..00000000000 --- a/misc/git/hooks/golangci-lint +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Unfortunately golangci-lint does not work well on checking just modified files. -# We will enable it for everything here, but with most of the linters disabled. -# See: https://github.com/vitessio/vitess/issues/5503 - -GOLANGCI_LINT=$(command -v golangci-lint >/dev/null 2>&1) -if [ $? -eq 1 ]; then - echo "Downloading golangci-lint..." - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.21.0 -fi - -golangci-lint run --disable=ineffassign,unused,gosimple,staticcheck,errcheck,structcheck,varcheck,deadcode diff --git a/misc/git/hooks/golint b/misc/git/hooks/golint deleted file mode 100755 index a2a3382af07..00000000000 --- a/misc/git/hooks/golint +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# git golint pre-commit hook -# -# To use, store as .git/hooks/pre-commit inside your repository and make sure -# it has execute permissions. - -if [ -z "$GOPATH" ]; then - echo "ERROR: pre-commit hook for golint: \$GOPATH is empty. Please run 'source dev.env' to set the correct \$GOPATH." - exit 1 -fi - -# This script does not handle file names that contain spaces. -gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$' | grep -v '^go/vt/proto/' | grep -v 'go/vt/sqlparser/sql.go') - -errors= - -# Run on one file at a time because a single invocation of golint -# with multiple files requires the files to all be in one package. -gofiles_with_warnings=() -for gofile in $gofiles -do - errcount=$(go run golang.org/x/lint/golint $gofile | wc -l) - if [ "$errcount" -gt "0" ]; then - errors=YES - echo "$errcount suggestions for:" - echo "go run golang.org/x/lint/golint $gofile" - gofiles_with_warnings+=($gofile) - fi -done - -[ -z "$errors" ] && exit 0 - -# git doesn't give us access to user input, so let's steal it. -exec < /dev/tty -if [[ $? -eq 0 ]]; then - # interactive shell. Prompt the user. - echo - echo "Lint suggestions were found. They're not enforced, but we're pausing" - echo "to let you know before they get clobbered in the scrollback buffer." - echo - read -r -p 'Press enter to cancel, "s" to step through the warnings or type "ack" to continue: ' - if [ "$REPLY" = "ack" ]; then - exit 0 - fi - if [ "$REPLY" = "s" ]; then - first_file="true" - for gofile in "${gofiles_with_warnings[@]}" - do - echo - if [ "$first_file" != "true" ]; then - echo "Press enter to show the warnings for the next file." - read - fi - go run golang.org/x/lint/golint $gofile - first_file="false" - done - fi -else - # non-interactive shell (e.g. called from Eclipse). Just display the errors. - for gofile in "${gofiles_with_warnings[@]}" - do - go run golang.org/x/lint/golint $gofile - done -fi -exit 1 diff --git a/misc/git/hooks/govet b/misc/git/hooks/govet deleted file mode 100755 index 72951834cbd..00000000000 --- a/misc/git/hooks/govet +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# git go vet pre-commit hook -# -# To use, store as .git/hooks/pre-commit inside your repository and make sure -# it has execute permissions. - -if [ -z "$GOPATH" ]; then - echo "ERROR: pre-commit hook for go vet: \$GOPATH is empty. Please run 'source dev.env' to set the correct \$GOPATH." - exit 1 -fi - -# This script does not handle file names that contain spaces. -gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$' | grep -v '^go/vt/proto/' | grep -v 'go/vt/sqlparser/sql.go') -if [ "$gofiles" = "" ]; then - exit 0 -fi - -# xargs -n1 because dirname on MacOS does not support multiple arguments. -gopackages=$(echo $gofiles | xargs -n1 dirname | sort -u) - -errors= - -# If any checks are found to be useless, they can be disabled here. -# See the output of "go doc cmd/vet" for a list of flags. -vetflags="" - -# Run on one package at a time -for gopackage in $gopackages -do - if ! go vet $vetflags "vitess.io/vitess/$gopackage" 2>&1; then - errors=YES - fi -done - -[ -z "$errors" ] && exit 0 - -echo -echo "Please fix the go vet warnings above. To disable certain checks, change vetflags in misc/git/hooks/govet." -exit 1 - diff --git a/misc/git/hooks/shellcheck b/misc/git/hooks/shellcheck deleted file mode 100755 index af9d8f8b37b..00000000000 --- a/misc/git/hooks/shellcheck +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# This file is based on the Go linter precommit hook -# "golint". Therefore, both files are very similar. - -# This script does not handle file names that contain spaces. -shfiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.*\.sh$') -if [ -z "$shfiles" ] ; then - # No .sh files modified. - exit 0 -fi - -if [ -z "$(command -v shellcheck)" ]; then - echo "shellcheck not found, please run: brew or apt-get install shellcheck" - exit 0 -fi - -errors= -for file in $shfiles -do - # The -e SC1090,SC1091 suppressing warnings about trying to find - # files imported with "source foo.sh". We only want to lint - # the files modified as part of this current diff. - errors+=$(shellcheck -e SC1090,SC1091 "$file" 2>&1) -done - -[ -z "$errors" ] && exit 0 - -# git doesn't give us access to user input, so let's steal it. -if exec < /dev/tty; then - # interactive shell. Prompt the user. - echo - echo "shellcheck suggestions were found. They're not enforced, but we're pausing" - echo "to let you know before they get clobbered in the scrollback buffer." - echo - read -r -p 'Press enter to cancel, "s" to show all warnings or type "ack" to continue: ' - if [ "$REPLY" = "ack" ]; then - exit 0 - fi - if [ "$REPLY" = "s" ]; then - echo - echo "$errors" - fi -else - # non-interactive shell (e.g. called from Eclipse). Just display the errors. - echo "$errors" -fi - -exit 1 diff --git a/misc/git/hooks/staticcheck b/misc/git/hooks/staticcheck deleted file mode 100755 index f0ea8d5d543..00000000000 --- a/misc/git/hooks/staticcheck +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash - -# git staticcheck pre-commit hook - -if [ -z "$GOPATH" ]; then - echo "ERROR: pre-commit hook for staticcheck: \$GOPATH is empty. Please run 'source dev.env' to set the correct \$GOPATH." - exit 1 -fi - -# This script does not handle file names that contain spaces. -# Exclude auto-generated files (from proto or yacc compile). -gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$' | grep -v '^go/vt/proto/' | grep -v 'go/vt/sqlparser/sql.go') -if [ "$gofiles" = "" ]; then - exit 0 -fi - -# xargs -n1 because dirname on MacOS does not support multiple arguments. -gopackages=$(echo $gofiles | xargs -n1 dirname | sort -u) - -warnings= - -# Run on one package at a time -gopackages_with_warnings=() -for gopackage in $gopackages -do - warningcount="$(go run honnef.co/go/tools/cmd/staticcheck "vitess.io/vitess/$gopackage" | wc -l)" - if [ "$warningcount" -gt "0" ]; then - warnings=YES - echo "$warningcount reports for:" - echo "go run honnef.co/go/tools/cmd/staticcheck vitess.io/vitess/$gopackage" - gopackages_with_warnings+=($gopackage) - fi -done - -[ -z "$warnings" ] && exit 0 - -# git doesn't give us access to user input, so let's steal it. -exec < /dev/tty -if [[ $? -eq 0 ]]; then - # interactive shell. Prompt the user. - echo - echo "Suggestions from the go 'staticcheck' program were found." - echo "They're not enforced, but we're pausing to let you know" - echo "before they get clobbered in the scrollback buffer." - echo - read -r -p 'Press enter to cancel, "s" to step through the warnings or type "ack" to continue: ' - if [ "$REPLY" = "ack" ]; then - exit 0 - fi - if [ "$REPLY" = "s" ]; then - first_file="true" - for gopackage in "${gopackages_with_warnings[@]}" - do - echo - if [ "$first_file" != "true" ]; then - echo "Press enter to show the warnings for the next file." - read - fi - go run honnef.co/go/tools/cmd/staticcheck "vitess.io/vitess/$gopackage" - first_file="false" - done - fi -else - # non-interactive shell (e.g. called from Eclipse). Just display the warnings. - for gopackage in "${gopackages_with_warnings[@]}" - do - go run honnef.co/go/tools/cmd/staticcheck "vitess.io/vitess/$gopackage" - done -fi -exit 1 diff --git a/misc/git/hooks/tslint b/misc/git/hooks/tslint deleted file mode 100755 index 2256ce288c0..00000000000 --- a/misc/git/hooks/tslint +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -# -# Precommit hook which runs 'tslint' to lint TypeScript code. -# -# It gets only triggered when a file below $vtctld_web_src was changed. -vtctld_web="web/vtctld2" - -git diff --cached --name-only --diff-filter=ACM | grep -q "^${vtctld_web}/src" -if [ $? -ne 0 ]; then - # No potential TypeScript file changed. Return early. - exit 0 -fi - -if [ -z "$(which tslint)" ]; then - echo "tslint not found, please run: npm install -g tslint typescript" - exit 1 -fi - -cd $vtctld_web -if [[ $? != 0 ]]; then - echo "Failed to change to the vtctld web directory ($vtctld_web)" - exit 1 -fi - -# Check for lint errors. -# Suppress npm logs to avoid that it creates a npm-debug.log file in $CWD. -errors=$(npm --loglevel=silent run lint 2>&1) -if [ $? -eq 0 ]; then - # No lint errors. Return early. - exit 0 -fi - -# Ask the user how to proceed. - -# git doesn't give us access to user input, so let's steal it. -exec < /dev/tty -if [[ $? -eq 0 ]]; then - # interactive shell. Prompt the user. - echo - echo "tslint suggestions were found. They're not enforced, but we're pausing" - echo "to let you know before they get clobbered in the scrollback buffer." - echo - read -r -p 'Press enter to cancel, "s" to show all warnings or type "ack" to continue: ' - if [ "$REPLY" = "ack" ]; then - exit 0 - fi - if [ "$REPLY" = "s" ]; then - echo - echo "$errors" - fi -else - # non-interactive shell (e.g. called from Eclipse). Just display the errors. - echo "$errors" -fi - -exit 1 diff --git a/misc/git/hooks/visitorgen b/misc/git/hooks/visitorgen deleted file mode 100755 index 65c04d613db..00000000000 --- a/misc/git/hooks/visitorgen +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# this script, which should run before committing code, makes sure that the visitor is re-generated when the ast changes - -go run ./go/vt/sqlparser/visitorgen/main -compareOnly=true -input=go/vt/sqlparser/ast.go -output=go/vt/sqlparser/rewriter.go \ No newline at end of file From aa3ab37b3ab963e931aac03de14e857ab0e92bd8 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 13:40:13 -0800 Subject: [PATCH 073/825] Remove sleep calls from scripts, cleanup Signed-off-by: Morgan Tocker --- examples/local/101_initial_cluster.sh | 8 ++------ examples/local/202_customer_tablets.sh | 4 +--- examples/local/302_new_shards.sh | 6 ++---- examples/local/306_down_shard_0.sh | 2 +- examples/local/401_teardown.sh | 16 ++++++++++++---- examples/local/scripts/mysqlctl-down.sh | 6 +----- examples/local/scripts/vtgate-down.sh | 9 +++------ examples/local/scripts/vtgate-up.sh | 15 ++++++++++++++- examples/local/scripts/vttablet-down.sh | 3 +-- examples/local/scripts/vttablet-up.sh | 13 ++++++++++++- 10 files changed, 49 insertions(+), 33 deletions(-) diff --git a/examples/local/101_initial_cluster.sh b/examples/local/101_initial_cluster.sh index 42f7525ac6c..c7b843dec2c 100755 --- a/examples/local/101_initial_cluster.sh +++ b/examples/local/101_initial_cluster.sh @@ -34,11 +34,9 @@ CELL=zone1 ./scripts/vtctld-up.sh & # start vttablets for keyspace commerce for i in 100 101 102; do CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - CELL=zone1 KEYSPACE=commerce TABLET_UID=$i ./scripts/vttablet-up.sh & + CELL=zone1 KEYSPACE=commerce TABLET_UID=$i ./scripts/vttablet-up.sh done -sleep 20 # @TODO: replace with wait for tablets command - # set one of the replicas to master vtctlclient -server localhost:15999 InitShardMaster -force commerce/0 zone1-100 @@ -49,6 +47,4 @@ vtctlclient -server localhost:15999 ApplySchema -sql-file create_commerce_schema vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_initial.json commerce # start vtgate -CELL=zone1 ./scripts/vtgate-up.sh & - -sleep 20 # @TODO: replace with a wait command +CELL=zone1 ./scripts/vtgate-up.sh diff --git a/examples/local/202_customer_tablets.sh b/examples/local/202_customer_tablets.sh index 0f40ea87132..baf443daf58 100755 --- a/examples/local/202_customer_tablets.sh +++ b/examples/local/202_customer_tablets.sh @@ -24,11 +24,9 @@ set -e for i in 200 201 202; do CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh & + CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh done -sleep 20 # @TODO: replace with wait for tablets command - vtctlclient -server localhost:15999 InitShardMaster -force customer/0 zone1-200 vtctlclient -server localhost:15999 CopySchemaShard -tables customer,corder commerce/0 customer/0 vtctlclient -server localhost:15999 ApplyVSchema -vschema_file vschema_commerce_vsplit.json commerce diff --git a/examples/local/302_new_shards.sh b/examples/local/302_new_shards.sh index 188de9a2567..7706c633f7c 100755 --- a/examples/local/302_new_shards.sh +++ b/examples/local/302_new_shards.sh @@ -23,16 +23,14 @@ set -e for i in 300 301 302; do CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh & + SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh done for i in 400 401 402; do CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh - SHARD=80- CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh & + SHARD=80- CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh done -sleep 20 # TODO: replace by wait for tablets - vtctlclient -server localhost:15999 InitShardMaster -force customer/-80 zone1-300 vtctlclient -server localhost:15999 InitShardMaster -force customer/80- zone1-400 vtctlclient -server localhost:15999 CopySchemaShard customer/0 customer/-80 diff --git a/examples/local/306_down_shard_0.sh b/examples/local/306_down_shard_0.sh index 722ffe840dd..25c23ab2111 100755 --- a/examples/local/306_down_shard_0.sh +++ b/examples/local/306_down_shard_0.sh @@ -17,6 +17,6 @@ for i in 200 201 202; do CELL=zone1 TABLET_UID=$i ./scripts/vttablet-down.sh - #TODO: shutdown mysqlctl as well + CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-down.sh done diff --git a/examples/local/401_teardown.sh b/examples/local/401_teardown.sh index c82fa18a5e7..5f02ddb0c5e 100755 --- a/examples/local/401_teardown.sh +++ b/examples/local/401_teardown.sh @@ -23,14 +23,22 @@ set -e ./scripts/vtgate-down.sh -# for TABLET in 100 200 300 400; do -# vtctlclient -server localhost:15999 GetTablet zone1-$TABLET >/dev/null 2>&1 && CELL=zone1 UID_BASE=$TABLET ./vttablet-down.sh -# done; +# vtctlclient -server localhost:15999 ListShardTablets commerce/0 +for tablet in 100 200 300 400; do + if vtctlclient -server localhost:15999 GetTablet zone1-$tablet >/dev/null 2>&1 ; then + # The zero tablet is up. Try to shutdown 0-2 tablet + mysqlctl + for i in 0 1 2; do + uid=$[$tablet + $i] + CELL=zone1 TABLET_UID=$uid ./scripts/vttablet-down.sh + CELL=zone1 TABLET_UID=$uid ./scripts/mysqlctl-down.sh + done + fi +done ./scripts/vtctld-down.sh if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 ./scripts/k-down.sh + CELL=zone1 ./scripts/zk-down.sh else CELL=zone1 ./scripts/etcd-down.sh fi diff --git a/examples/local/scripts/mysqlctl-down.sh b/examples/local/scripts/mysqlctl-down.sh index bf0cff91c2c..9e5491d1d4c 100755 --- a/examples/local/scripts/mysqlctl-down.sh +++ b/examples/local/scripts/mysqlctl-down.sh @@ -19,9 +19,5 @@ source ./env.sh -uid=${TABLET_UID} - -mysqlctl \ - -tablet_uid $uid \ - shutdown +mysqlctl -tablet_uid $TABLET_UID shutdown diff --git a/examples/local/scripts/vtgate-down.sh b/examples/local/scripts/vtgate-down.sh index d396cceab51..bd06f227936 100755 --- a/examples/local/scripts/vtgate-down.sh +++ b/examples/local/scripts/vtgate-down.sh @@ -16,13 +16,10 @@ # This is an example script that stops the instance started by vtgate-up.sh. -set -e +source ./env.sh -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh +set -e # Stop vtgate. -pid=`cat $VTDATAROOT/tmp/vtgate.pid` echo "Stopping vtgate..." -kill $pid - +kill `cat $VTDATAROOT/tmp/vtgate.pid` diff --git a/examples/local/scripts/vtgate-up.sh b/examples/local/scripts/vtgate-up.sh index 026f977b1a3..052b68df37c 100755 --- a/examples/local/scripts/vtgate-up.sh +++ b/examples/local/scripts/vtgate-up.sh @@ -43,5 +43,18 @@ vtgate \ -service_map 'grpc-vtgateservice' \ -pid_file $VTDATAROOT/tmp/vtgate.pid \ -mysql_auth_server_impl none \ - > $VTDATAROOT/tmp/vtgate.out 2>&1 + > $VTDATAROOT/tmp/vtgate.out 2>&1 & +# Block waiting for vtgate to be listening +# Not the same as healthy + +echo "Waiting for vtgate to be up..." +while true; do + curl -I "http://$hostname:$web_port/debug/status" >/dev/null 2>&1 && break + sleep 0.1 +done; +echo "vtgate is up!" + +echo "Access vtgate at http://$hostname:$web_port/debug/status" + +disown -a diff --git a/examples/local/scripts/vttablet-down.sh b/examples/local/scripts/vttablet-down.sh index 9264a20a78d..47b881b9793 100755 --- a/examples/local/scripts/vttablet-down.sh +++ b/examples/local/scripts/vttablet-down.sh @@ -19,8 +19,7 @@ source ./env.sh -uid=${TABLET_UID} -printf -v tablet_dir 'vt_%010d' $uid +printf -v tablet_dir 'vt_%010d' $TABLET_UID pid=`cat $VTDATAROOT/$tablet_dir/vttablet.pid` kill $pid diff --git a/examples/local/scripts/vttablet-up.sh b/examples/local/scripts/vttablet-up.sh index c5f55c9d5b6..5bc8c6e87f5 100755 --- a/examples/local/scripts/vttablet-up.sh +++ b/examples/local/scripts/vttablet-up.sh @@ -57,4 +57,15 @@ vttablet \ -service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' \ -pid_file $VTDATAROOT/$tablet_dir/vttablet.pid \ -vtctld_addr http://$hostname:$vtctld_web_port/ \ - > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 + > $VTDATAROOT/$tablet_dir/vttablet.out 2>&1 & + +# Block waiting for the tablet to be listening +# Not the same as healthy + +for i in $(seq 0 300); do + curl -I "http://$hostname:$port/debug/status" >/dev/null 2>&1 && break + sleep 0.1 +done + +# check one last time +curl -I "http://$hostname:$port/debug/status" || fail "tablet could not be started!" From 08ff7c69782617c3735591647bcfddcd3e55490c Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 13:51:48 -0800 Subject: [PATCH 074/825] Fix accidentally unstaged files Signed-off-by: Morgan Tocker --- go.mod | 2 - misc/git/hooks/checkstyle | 44 ++++++++++++++++++++ misc/git/hooks/goimports | 38 +++++++++++++++++ misc/git/hooks/golangci-lint | 26 ++++++++++++ misc/git/hooks/golint | 79 ++++++++++++++++++++++++++++++++++++ misc/git/hooks/govet | 54 ++++++++++++++++++++++++ misc/git/hooks/shellcheck | 48 ++++++++++++++++++++++ misc/git/hooks/staticcheck | 70 ++++++++++++++++++++++++++++++++ misc/git/hooks/tslint | 56 +++++++++++++++++++++++++ misc/git/hooks/visitorgen | 18 ++++++++ 10 files changed, 433 insertions(+), 2 deletions(-) create mode 100755 misc/git/hooks/checkstyle create mode 100755 misc/git/hooks/goimports create mode 100755 misc/git/hooks/golangci-lint create mode 100755 misc/git/hooks/golint create mode 100755 misc/git/hooks/govet create mode 100755 misc/git/hooks/shellcheck create mode 100755 misc/git/hooks/staticcheck create mode 100755 misc/git/hooks/tslint create mode 100755 misc/git/hooks/visitorgen diff --git a/go.mod b/go.mod index 25077ce841a..5310d66e0b1 100644 --- a/go.mod +++ b/go.mod @@ -52,8 +52,6 @@ require ( github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/misc/git/hooks/checkstyle b/misc/git/hooks/checkstyle new file mode 100755 index 00000000000..777dd512430 --- /dev/null +++ b/misc/git/hooks/checkstyle @@ -0,0 +1,44 @@ +#!/bin/bash + +set -e + +function get_module() { + local path=$1; + while true; do + path=$(dirname $path); + if [ -f "$path/pom.xml" ]; then + echo "$path"; + return; + elif [[ "./" =~ "$path" ]]; then + return; + fi + done +} + +cd java; + +modules=(); + +for file in $(git diff --relative --name-only --cached \*.java); do + module=$(get_module "$file"); + if [ "" != "$module" ] \ + && [[ ! " ${modules[@]} " =~ " $module " ]]; then + modules+=("$module"); + fi +done; + +if [ ${#modules[@]} -eq 0 ]; then + exit; +fi + +modules_arg=$(printf ",%s" "${modules[@]}"); +modules_arg=${modules_arg:1}; + +export MAVEN_OPTS="-client + -XX:+TieredCompilation + -XX:TieredStopAtLevel=1 + -Xverify:none"; + +mvn -q -pl "$modules_arg" checkstyle:check; + +cd -; diff --git a/misc/git/hooks/goimports b/misc/git/hooks/goimports new file mode 100755 index 00000000000..786e7ab517e --- /dev/null +++ b/misc/git/hooks/goimports @@ -0,0 +1,38 @@ +#!/bin/bash +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# git goimports pre-commit hook +# +# To use, store as .git/hooks/pre-commit inside your repository and make sure +# it has execute permissions. +# +# This script does not handle file names that contain spaces. +gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$') + +[ -z "$gofiles" ] && exit 0 +unformatted=$(goimports -l=true $gofiles 2>&1 | awk -F: '{print $1}') +[ -z "$unformatted" ] && exit 0 + +# Some files are not goimports'd. Print message and fail. + +echo >&2 "Go files must be formatted with goimports. Please run:" +echo >&2 +echo -n >&2 " goimports -w" +for fn in $unformatted; do + echo -n >&2 " $PWD/$fn" +done +echo + +exit 1 diff --git a/misc/git/hooks/golangci-lint b/misc/git/hooks/golangci-lint new file mode 100755 index 00000000000..ea7da67e6b9 --- /dev/null +++ b/misc/git/hooks/golangci-lint @@ -0,0 +1,26 @@ +#!/bin/bash +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Unfortunately golangci-lint does not work well on checking just modified files. +# We will enable it for everything here, but with most of the linters disabled. +# See: https://github.com/vitessio/vitess/issues/5503 + +GOLANGCI_LINT=$(command -v golangci-lint >/dev/null 2>&1) +if [ $? -eq 1 ]; then + echo "Downloading golangci-lint..." + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.21.0 +fi + +golangci-lint run --disable=ineffassign,unused,gosimple,staticcheck,errcheck,structcheck,varcheck,deadcode diff --git a/misc/git/hooks/golint b/misc/git/hooks/golint new file mode 100755 index 00000000000..a2a3382af07 --- /dev/null +++ b/misc/git/hooks/golint @@ -0,0 +1,79 @@ +#!/bin/bash +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# git golint pre-commit hook +# +# To use, store as .git/hooks/pre-commit inside your repository and make sure +# it has execute permissions. + +if [ -z "$GOPATH" ]; then + echo "ERROR: pre-commit hook for golint: \$GOPATH is empty. Please run 'source dev.env' to set the correct \$GOPATH." + exit 1 +fi + +# This script does not handle file names that contain spaces. +gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$' | grep -v '^go/vt/proto/' | grep -v 'go/vt/sqlparser/sql.go') + +errors= + +# Run on one file at a time because a single invocation of golint +# with multiple files requires the files to all be in one package. +gofiles_with_warnings=() +for gofile in $gofiles +do + errcount=$(go run golang.org/x/lint/golint $gofile | wc -l) + if [ "$errcount" -gt "0" ]; then + errors=YES + echo "$errcount suggestions for:" + echo "go run golang.org/x/lint/golint $gofile" + gofiles_with_warnings+=($gofile) + fi +done + +[ -z "$errors" ] && exit 0 + +# git doesn't give us access to user input, so let's steal it. +exec < /dev/tty +if [[ $? -eq 0 ]]; then + # interactive shell. Prompt the user. + echo + echo "Lint suggestions were found. They're not enforced, but we're pausing" + echo "to let you know before they get clobbered in the scrollback buffer." + echo + read -r -p 'Press enter to cancel, "s" to step through the warnings or type "ack" to continue: ' + if [ "$REPLY" = "ack" ]; then + exit 0 + fi + if [ "$REPLY" = "s" ]; then + first_file="true" + for gofile in "${gofiles_with_warnings[@]}" + do + echo + if [ "$first_file" != "true" ]; then + echo "Press enter to show the warnings for the next file." + read + fi + go run golang.org/x/lint/golint $gofile + first_file="false" + done + fi +else + # non-interactive shell (e.g. called from Eclipse). Just display the errors. + for gofile in "${gofiles_with_warnings[@]}" + do + go run golang.org/x/lint/golint $gofile + done +fi +exit 1 diff --git a/misc/git/hooks/govet b/misc/git/hooks/govet new file mode 100755 index 00000000000..72951834cbd --- /dev/null +++ b/misc/git/hooks/govet @@ -0,0 +1,54 @@ +#!/bin/bash +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# git go vet pre-commit hook +# +# To use, store as .git/hooks/pre-commit inside your repository and make sure +# it has execute permissions. + +if [ -z "$GOPATH" ]; then + echo "ERROR: pre-commit hook for go vet: \$GOPATH is empty. Please run 'source dev.env' to set the correct \$GOPATH." + exit 1 +fi + +# This script does not handle file names that contain spaces. +gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$' | grep -v '^go/vt/proto/' | grep -v 'go/vt/sqlparser/sql.go') +if [ "$gofiles" = "" ]; then + exit 0 +fi + +# xargs -n1 because dirname on MacOS does not support multiple arguments. +gopackages=$(echo $gofiles | xargs -n1 dirname | sort -u) + +errors= + +# If any checks are found to be useless, they can be disabled here. +# See the output of "go doc cmd/vet" for a list of flags. +vetflags="" + +# Run on one package at a time +for gopackage in $gopackages +do + if ! go vet $vetflags "vitess.io/vitess/$gopackage" 2>&1; then + errors=YES + fi +done + +[ -z "$errors" ] && exit 0 + +echo +echo "Please fix the go vet warnings above. To disable certain checks, change vetflags in misc/git/hooks/govet." +exit 1 + diff --git a/misc/git/hooks/shellcheck b/misc/git/hooks/shellcheck new file mode 100755 index 00000000000..af9d8f8b37b --- /dev/null +++ b/misc/git/hooks/shellcheck @@ -0,0 +1,48 @@ +#!/bin/bash +# This file is based on the Go linter precommit hook +# "golint". Therefore, both files are very similar. + +# This script does not handle file names that contain spaces. +shfiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.*\.sh$') +if [ -z "$shfiles" ] ; then + # No .sh files modified. + exit 0 +fi + +if [ -z "$(command -v shellcheck)" ]; then + echo "shellcheck not found, please run: brew or apt-get install shellcheck" + exit 0 +fi + +errors= +for file in $shfiles +do + # The -e SC1090,SC1091 suppressing warnings about trying to find + # files imported with "source foo.sh". We only want to lint + # the files modified as part of this current diff. + errors+=$(shellcheck -e SC1090,SC1091 "$file" 2>&1) +done + +[ -z "$errors" ] && exit 0 + +# git doesn't give us access to user input, so let's steal it. +if exec < /dev/tty; then + # interactive shell. Prompt the user. + echo + echo "shellcheck suggestions were found. They're not enforced, but we're pausing" + echo "to let you know before they get clobbered in the scrollback buffer." + echo + read -r -p 'Press enter to cancel, "s" to show all warnings or type "ack" to continue: ' + if [ "$REPLY" = "ack" ]; then + exit 0 + fi + if [ "$REPLY" = "s" ]; then + echo + echo "$errors" + fi +else + # non-interactive shell (e.g. called from Eclipse). Just display the errors. + echo "$errors" +fi + +exit 1 diff --git a/misc/git/hooks/staticcheck b/misc/git/hooks/staticcheck new file mode 100755 index 00000000000..f0ea8d5d543 --- /dev/null +++ b/misc/git/hooks/staticcheck @@ -0,0 +1,70 @@ +#!/bin/bash + +# git staticcheck pre-commit hook + +if [ -z "$GOPATH" ]; then + echo "ERROR: pre-commit hook for staticcheck: \$GOPATH is empty. Please run 'source dev.env' to set the correct \$GOPATH." + exit 1 +fi + +# This script does not handle file names that contain spaces. +# Exclude auto-generated files (from proto or yacc compile). +gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '^go/.*\.go$' | grep -v '^go/vt/proto/' | grep -v 'go/vt/sqlparser/sql.go') +if [ "$gofiles" = "" ]; then + exit 0 +fi + +# xargs -n1 because dirname on MacOS does not support multiple arguments. +gopackages=$(echo $gofiles | xargs -n1 dirname | sort -u) + +warnings= + +# Run on one package at a time +gopackages_with_warnings=() +for gopackage in $gopackages +do + warningcount="$(go run honnef.co/go/tools/cmd/staticcheck "vitess.io/vitess/$gopackage" | wc -l)" + if [ "$warningcount" -gt "0" ]; then + warnings=YES + echo "$warningcount reports for:" + echo "go run honnef.co/go/tools/cmd/staticcheck vitess.io/vitess/$gopackage" + gopackages_with_warnings+=($gopackage) + fi +done + +[ -z "$warnings" ] && exit 0 + +# git doesn't give us access to user input, so let's steal it. +exec < /dev/tty +if [[ $? -eq 0 ]]; then + # interactive shell. Prompt the user. + echo + echo "Suggestions from the go 'staticcheck' program were found." + echo "They're not enforced, but we're pausing to let you know" + echo "before they get clobbered in the scrollback buffer." + echo + read -r -p 'Press enter to cancel, "s" to step through the warnings or type "ack" to continue: ' + if [ "$REPLY" = "ack" ]; then + exit 0 + fi + if [ "$REPLY" = "s" ]; then + first_file="true" + for gopackage in "${gopackages_with_warnings[@]}" + do + echo + if [ "$first_file" != "true" ]; then + echo "Press enter to show the warnings for the next file." + read + fi + go run honnef.co/go/tools/cmd/staticcheck "vitess.io/vitess/$gopackage" + first_file="false" + done + fi +else + # non-interactive shell (e.g. called from Eclipse). Just display the warnings. + for gopackage in "${gopackages_with_warnings[@]}" + do + go run honnef.co/go/tools/cmd/staticcheck "vitess.io/vitess/$gopackage" + done +fi +exit 1 diff --git a/misc/git/hooks/tslint b/misc/git/hooks/tslint new file mode 100755 index 00000000000..2256ce288c0 --- /dev/null +++ b/misc/git/hooks/tslint @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Precommit hook which runs 'tslint' to lint TypeScript code. +# +# It gets only triggered when a file below $vtctld_web_src was changed. +vtctld_web="web/vtctld2" + +git diff --cached --name-only --diff-filter=ACM | grep -q "^${vtctld_web}/src" +if [ $? -ne 0 ]; then + # No potential TypeScript file changed. Return early. + exit 0 +fi + +if [ -z "$(which tslint)" ]; then + echo "tslint not found, please run: npm install -g tslint typescript" + exit 1 +fi + +cd $vtctld_web +if [[ $? != 0 ]]; then + echo "Failed to change to the vtctld web directory ($vtctld_web)" + exit 1 +fi + +# Check for lint errors. +# Suppress npm logs to avoid that it creates a npm-debug.log file in $CWD. +errors=$(npm --loglevel=silent run lint 2>&1) +if [ $? -eq 0 ]; then + # No lint errors. Return early. + exit 0 +fi + +# Ask the user how to proceed. + +# git doesn't give us access to user input, so let's steal it. +exec < /dev/tty +if [[ $? -eq 0 ]]; then + # interactive shell. Prompt the user. + echo + echo "tslint suggestions were found. They're not enforced, but we're pausing" + echo "to let you know before they get clobbered in the scrollback buffer." + echo + read -r -p 'Press enter to cancel, "s" to show all warnings or type "ack" to continue: ' + if [ "$REPLY" = "ack" ]; then + exit 0 + fi + if [ "$REPLY" = "s" ]; then + echo + echo "$errors" + fi +else + # non-interactive shell (e.g. called from Eclipse). Just display the errors. + echo "$errors" +fi + +exit 1 diff --git a/misc/git/hooks/visitorgen b/misc/git/hooks/visitorgen new file mode 100755 index 00000000000..65c04d613db --- /dev/null +++ b/misc/git/hooks/visitorgen @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# this script, which should run before committing code, makes sure that the visitor is re-generated when the ast changes + +go run ./go/vt/sqlparser/visitorgen/main -compareOnly=true -input=go/vt/sqlparser/ast.go -output=go/vt/sqlparser/rewriter.go \ No newline at end of file From a5ba64567be413c928e6c971a0802c70c83c61e7 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 13:59:36 -0800 Subject: [PATCH 075/825] Fix vtctld not backgrounding Signed-off-by: Morgan Tocker --- examples/local/101_initial_cluster.sh | 2 +- examples/local/scripts/vtctld-up.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/local/101_initial_cluster.sh b/examples/local/101_initial_cluster.sh index c7b843dec2c..69bcc97a80e 100755 --- a/examples/local/101_initial_cluster.sh +++ b/examples/local/101_initial_cluster.sh @@ -29,7 +29,7 @@ else fi # start vtctld -CELL=zone1 ./scripts/vtctld-up.sh & +CELL=zone1 ./scripts/vtctld-up.sh # start vttablets for keyspace commerce for i in 100 101 102; do diff --git a/examples/local/scripts/vtctld-up.sh b/examples/local/scripts/vtctld-up.sh index 075ac33e578..4ce582122b7 100755 --- a/examples/local/scripts/vtctld-up.sh +++ b/examples/local/scripts/vtctld-up.sh @@ -37,4 +37,4 @@ vtctld \ -port $vtctld_web_port \ -grpc_port $grpc_port \ -pid_file $VTDATAROOT/tmp/vtctld.pid \ - > $VTDATAROOT/tmp/vtctld.out 2>&1 + > $VTDATAROOT/tmp/vtctld.out 2>&1 & From 5c515698e617273bdca75a89b0a6ca9a66d7d992 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 14:12:15 -0800 Subject: [PATCH 076/825] Fix client_test breakage Signed-off-by: Morgan Tocker --- {examples/local => test}/client.go | 0 {examples/local => test}/client.py | 0 {examples/local => test}/client.sh | 0 {examples/local => test}/client_java.sh | 0 {examples/local => test}/client_jdbc.sh | 0 test/client_test.sh | 38 +++++++++++++++---------- 6 files changed, 23 insertions(+), 15 deletions(-) rename {examples/local => test}/client.go (100%) rename {examples/local => test}/client.py (100%) rename {examples/local => test}/client.sh (100%) rename {examples/local => test}/client_java.sh (100%) rename {examples/local => test}/client_jdbc.sh (100%) diff --git a/examples/local/client.go b/test/client.go similarity index 100% rename from examples/local/client.go rename to test/client.go diff --git a/examples/local/client.py b/test/client.py similarity index 100% rename from examples/local/client.py rename to test/client.py diff --git a/examples/local/client.sh b/test/client.sh similarity index 100% rename from examples/local/client.sh rename to test/client.sh diff --git a/examples/local/client_java.sh b/test/client_java.sh similarity index 100% rename from examples/local/client_java.sh rename to test/client_java.sh diff --git a/examples/local/client_jdbc.sh b/test/client_jdbc.sh similarity index 100% rename from examples/local/client_jdbc.sh rename to test/client_jdbc.sh diff --git a/test/client_test.sh b/test/client_test.sh index 6ad69b2023f..5465d6f45ab 100755 --- a/test/client_test.sh +++ b/test/client_test.sh @@ -22,34 +22,42 @@ source build.env set -xe cd "$VTROOT/examples/local" -CELL=test ./etcd-up.sh -CELL=test ./vtctld-up.sh +CELL=test ./scripts/etcd-up.sh +CELL=test ./scripts/vtctld-up.sh -CELL=test KEYSPACE=test_keyspace UID_BASE=100 ./vttablet-up.sh +for i in 100 101 102; do + CELL=test TABLET_UID=$i ./scripts/mysqlctl-up.sh + CELL=test KEYSPACE=test_keyspace TABLET_UID=$i ./scripts/vttablet-up.sh +done -./lvtctl.sh InitShardMaster -force test_keyspace/0 test-100 +vtctlclient -server localhost:15999 InitShardMaster -force test_keyspace/0 test-100 -./lvtctl.sh ApplySchema -sql-file create_test_table.sql test_keyspace -./lvtctl.sh RebuildVSchemaGraph +vtctlclient -server localhost:15999 ApplySchema -sql-file create_test_table.sql test_keyspace +vtctlclient -server localhost:15999 RebuildVSchemaGraph -CELL=test ./vtgate-up.sh +CELL=test ./scripts/vtgate-up.sh echo "Run Python client script.." -./client.sh +$VTROOT/test/client.sh echo "Run Go client script..." -go run client.go -server=localhost:15991 +go run $VTROOT/test/client.go -server=localhost:15991 echo "Run Java client script..." -./client_java.sh +$VTROOT/test/client_java.sh echo "Run JDBC client script..." -./client_jdbc.sh +$VTROOT/test/client_jdbc.sh # Clean up -./vtgate-down.sh -CELL=test UID_BASE=100 ./vttablet-down.sh -./vtctld-down.sh -CELL=test ./etcd-down.sh +./scripts/vtgate-down.sh + +for i in 100 101 102; do + CELL=test TABLET_UID=$i ./scripts/vttablet-down.sh + CELL=test TABLET_UID=$i ./scripts/mysqlctl-down.sh +done + +./scripts/tctld-down.sh +CELL=test ./scripts/etcd-down.sh From 690a4f452d40da5882d6c45284d872c0b654852c Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Sat, 8 Feb 2020 14:15:22 -0800 Subject: [PATCH 077/825] Fix client_test Signed-off-by: Morgan Tocker --- examples/local/101_initial_cluster.sh | 2 -- examples/local/202_customer_tablets.sh | 2 -- examples/local/203_vertical_split.sh | 2 -- examples/local/302_new_shards.sh | 2 -- examples/local/303_horizontal_split.sh | 2 -- examples/local/401_teardown.sh | 3 --- examples/local/env.sh | 2 ++ examples/local/scripts/etcd-down.sh | 2 -- examples/local/scripts/etcd-up.sh | 1 - examples/local/scripts/mysqlctl-up.sh | 2 -- examples/local/scripts/vtctld-down.sh | 2 -- examples/local/scripts/vtctld-up.sh | 2 -- examples/local/scripts/vtgate-down.sh | 2 -- examples/local/scripts/vtgate-up.sh | 2 -- examples/local/scripts/vttablet-up.sh | 2 -- examples/local/scripts/zk-down.sh | 2 -- examples/local/scripts/zk-up.sh | 2 -- test/client_java.sh | 6 ++---- test/client_jdbc.sh | 6 ++---- test/client_test.sh | 2 +- 20 files changed, 7 insertions(+), 41 deletions(-) diff --git a/examples/local/101_initial_cluster.sh b/examples/local/101_initial_cluster.sh index 69bcc97a80e..62e4542cc09 100755 --- a/examples/local/101_initial_cluster.sh +++ b/examples/local/101_initial_cluster.sh @@ -19,8 +19,6 @@ source ./env.sh -set -e - # start topo server if [ "${TOPO}" = "zk2" ]; then CELL=zone1 ./scripts/zk-up.sh diff --git a/examples/local/202_customer_tablets.sh b/examples/local/202_customer_tablets.sh index baf443daf58..ec77226342f 100755 --- a/examples/local/202_customer_tablets.sh +++ b/examples/local/202_customer_tablets.sh @@ -20,8 +20,6 @@ source ./env.sh -set -e - for i in 200 201 202; do CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh diff --git a/examples/local/203_vertical_split.sh b/examples/local/203_vertical_split.sh index f7b25d9e215..372c4817a90 100755 --- a/examples/local/203_vertical_split.sh +++ b/examples/local/203_vertical_split.sh @@ -19,8 +19,6 @@ source ./env.sh -set -e - vtworker \ $TOPOLOGY_FLAGS \ -cell zone1 \ diff --git a/examples/local/302_new_shards.sh b/examples/local/302_new_shards.sh index 7706c633f7c..c8830f97d0e 100755 --- a/examples/local/302_new_shards.sh +++ b/examples/local/302_new_shards.sh @@ -19,8 +19,6 @@ source ./env.sh -set -e - for i in 300 301 302; do CELL=zone1 TABLET_UID=$i ./scripts/mysqlctl-up.sh SHARD=-80 CELL=zone1 KEYSPACE=customer TABLET_UID=$i ./scripts/vttablet-up.sh diff --git a/examples/local/303_horizontal_split.sh b/examples/local/303_horizontal_split.sh index afd5d5c1d10..aaa019559d4 100755 --- a/examples/local/303_horizontal_split.sh +++ b/examples/local/303_horizontal_split.sh @@ -19,8 +19,6 @@ source ./env.sh -set -e - vtworker \ $TOPOLOGY_FLAGS \ -cell zone1 \ diff --git a/examples/local/401_teardown.sh b/examples/local/401_teardown.sh index 5f02ddb0c5e..d044b568afd 100755 --- a/examples/local/401_teardown.sh +++ b/examples/local/401_teardown.sh @@ -19,11 +19,8 @@ source ./env.sh -set -e - ./scripts/vtgate-down.sh -# vtctlclient -server localhost:15999 ListShardTablets commerce/0 for tablet in 100 200 300 400; do if vtctlclient -server localhost:15999 GetTablet zone1-$tablet >/dev/null 2>&1 ; then # The zero tablet is up. Try to shutdown 0-2 tablet + mysqlctl diff --git a/examples/local/env.sh b/examples/local/env.sh index 0211bcbbc71..987dcc2f379 100644 --- a/examples/local/env.sh +++ b/examples/local/env.sh @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +set -e + hostname=`hostname -f` vtctld_web_port=15000 export VTDATAROOT="${VTDATAROOT:-${PWD}/vtdataroot}" diff --git a/examples/local/scripts/etcd-down.sh b/examples/local/scripts/etcd-down.sh index 640a32fb3a0..018af7432a3 100755 --- a/examples/local/scripts/etcd-down.sh +++ b/examples/local/scripts/etcd-down.sh @@ -18,7 +18,5 @@ source ./env.sh -set -e - echo "Stopping etcd..." kill -9 `cat $VTDATAROOT/tmp/etcd.pid` diff --git a/examples/local/scripts/etcd-up.sh b/examples/local/scripts/etcd-up.sh index b0527a9c8c0..d8c0a869a7e 100755 --- a/examples/local/scripts/etcd-up.sh +++ b/examples/local/scripts/etcd-up.sh @@ -18,7 +18,6 @@ source ./env.sh -set -e cell=${CELL:-'test'} export ETCDCTL_API=2 diff --git a/examples/local/scripts/mysqlctl-up.sh b/examples/local/scripts/mysqlctl-up.sh index 38ea7e5df41..e4c9f58f819 100755 --- a/examples/local/scripts/mysqlctl-up.sh +++ b/examples/local/scripts/mysqlctl-up.sh @@ -18,8 +18,6 @@ source ./env.sh -set -e - cell=${CELL:-'test'} uid=$TABLET_UID mysql_port=$[17000 + $uid] diff --git a/examples/local/scripts/vtctld-down.sh b/examples/local/scripts/vtctld-down.sh index 372fcd4a382..d96fa3b927f 100755 --- a/examples/local/scripts/vtctld-down.sh +++ b/examples/local/scripts/vtctld-down.sh @@ -18,7 +18,5 @@ source ./env.sh -set -xe - echo "Stopping vtctld..." kill -9 `cat $VTDATAROOT/tmp/vtctld.pid` diff --git a/examples/local/scripts/vtctld-up.sh b/examples/local/scripts/vtctld-up.sh index 4ce582122b7..662a234ae48 100755 --- a/examples/local/scripts/vtctld-up.sh +++ b/examples/local/scripts/vtctld-up.sh @@ -18,8 +18,6 @@ source ./env.sh -set -e - cell=${CELL:-'test'} grpc_port=15999 diff --git a/examples/local/scripts/vtgate-down.sh b/examples/local/scripts/vtgate-down.sh index bd06f227936..9da0a7179df 100755 --- a/examples/local/scripts/vtgate-down.sh +++ b/examples/local/scripts/vtgate-down.sh @@ -18,8 +18,6 @@ source ./env.sh -set -e - # Stop vtgate. echo "Stopping vtgate..." kill `cat $VTDATAROOT/tmp/vtgate.pid` diff --git a/examples/local/scripts/vtgate-up.sh b/examples/local/scripts/vtgate-up.sh index 052b68df37c..e3fe2d17f82 100755 --- a/examples/local/scripts/vtgate-up.sh +++ b/examples/local/scripts/vtgate-up.sh @@ -18,8 +18,6 @@ source ./env.sh -set -e - cell=${CELL:-'test'} web_port=15001 grpc_port=15991 diff --git a/examples/local/scripts/vttablet-up.sh b/examples/local/scripts/vttablet-up.sh index 5bc8c6e87f5..c3f66fa806d 100755 --- a/examples/local/scripts/vttablet-up.sh +++ b/examples/local/scripts/vttablet-up.sh @@ -16,8 +16,6 @@ source ./env.sh -set -e - cell=${CELL:-'test'} keyspace=${KEYSPACE:-'test_keyspace'} shard=${SHARD:-'0'} diff --git a/examples/local/scripts/zk-down.sh b/examples/local/scripts/zk-down.sh index 5a8f3d72648..18dd7933bc9 100755 --- a/examples/local/scripts/zk-down.sh +++ b/examples/local/scripts/zk-down.sh @@ -18,8 +18,6 @@ source ./env.sh -set -e - # Stop ZooKeeper servers. echo "Stopping zk servers..." for zkid in $zkids; do diff --git a/examples/local/scripts/zk-up.sh b/examples/local/scripts/zk-up.sh index 83b5ada17ca..6671d1063a6 100755 --- a/examples/local/scripts/zk-up.sh +++ b/examples/local/scripts/zk-up.sh @@ -18,8 +18,6 @@ source ./env.sh -set -e - cell=${CELL:-'test'} # Start ZooKeeper servers. diff --git a/test/client_java.sh b/test/client_java.sh index 1a87e53f37b..7e6975a1fef 100755 --- a/test/client_java.sh +++ b/test/client_java.sh @@ -19,10 +19,8 @@ set -e -script_root=`dirname "${BASH_SOURCE}"` - # We have to install the "example" module first because Maven cannot resolve # them when we run "exec:java". See also: http://stackoverflow.com/questions/11091311/maven-execjava-goal-on-a-multi-module-project # Install only "example". See also: http://stackoverflow.com/questions/1114026/maven-modules-building-a-single-specific-module -mvn -f $script_root/../../java/pom.xml -pl example -am install -DskipTests -mvn -f $script_root/../../java/example/pom.xml exec:java -Dexec.cleanupDaemonThreads=false -Dexec.mainClass="io.vitess.example.VitessClientExample" -Dexec.args="localhost:15991" +mvn -f $VTROOT/java/pom.xml -pl example -am install -DskipTests +mvn -f $VTROOT/java/example/pom.xml exec:java -Dexec.cleanupDaemonThreads=false -Dexec.mainClass="io.vitess.example.VitessClientExample" -Dexec.args="localhost:15991" diff --git a/test/client_jdbc.sh b/test/client_jdbc.sh index 720a62fb470..7576c454fd0 100755 --- a/test/client_jdbc.sh +++ b/test/client_jdbc.sh @@ -19,8 +19,6 @@ set -e -script_root=`dirname "${BASH_SOURCE}"` - # When this script is run with the argument "--enable-tls", then it will connect to VTGate using TLS with # client authentication. This option depends upon the "vtgate-up.sh" script having also been run with # a corresponding "--enable-tls" argument. @@ -47,8 +45,8 @@ fi # We have to install the "example" module first because Maven cannot resolve # them when we run "exec:java". See also: http://stackoverflow.com/questions/11091311/maven-execjava-goal-on-a-multi-module-project # Install only "example". See also: http://stackoverflow.com/questions/1114026/maven-modules-building-a-single-specific-module -mvn -f $script_root/../../java/pom.xml -pl example -am install -DskipTests -mvn -f $script_root/../../java/example/pom.xml exec:java \ +mvn -f $VTROOT/java/pom.xml -pl example -am install -DskipTests +mvn -f $VTROOT/java/example/pom.xml exec:java \ -Dexec.cleanupDaemonThreads=false \ -Dexec.mainClass="io.vitess.example.VitessJDBCExample" \ -Dexec.args="localhost:15991$optional_tls_args" diff --git a/test/client_test.sh b/test/client_test.sh index 5465d6f45ab..aa738efbb04 100755 --- a/test/client_test.sh +++ b/test/client_test.sh @@ -58,6 +58,6 @@ for i in 100 101 102; do CELL=test TABLET_UID=$i ./scripts/mysqlctl-down.sh done -./scripts/tctld-down.sh +./scripts/vtctld-down.sh CELL=test ./scripts/etcd-down.sh From 4a3a98bfeab4fa4388fbc9d7f9b955f13519d248 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 9 Feb 2020 01:07:15 -0800 Subject: [PATCH 078/825] stats: refactor counters for drop dimensions Also, the old code was badly written. Signed-off-by: Sugu Sougoumarane --- go/stats/counters.go | 144 +++++++++++++++---------------------------- 1 file changed, 50 insertions(+), 94 deletions(-) diff --git a/go/stats/counters.go b/go/stats/counters.go index 6307cf10f2b..1f7cc416b07 100644 --- a/go/stats/counters.go +++ b/go/stats/counters.go @@ -19,104 +19,70 @@ package stats import ( "bytes" "fmt" + "strings" "sync" - "sync/atomic" ) // counters is similar to expvar.Map, except that it doesn't allow floats. // It is used to build CountersWithSingleLabel and GaugesWithSingleLabel. type counters struct { - // mu only protects adding and retrieving the value (*int64) from the - // map. - // The modification to the actual number (int64) must be done with - // atomic funcs. - // If a value for a given name already exists in the map, we only have - // to use a read-lock to retrieve it. This is an important performance - // optimizations because it allows to concurrently increment a counter. - mu sync.RWMutex - counts map[string]*int64 - help string + mu sync.Mutex + counts map[string]int64 + + help string } -// String implements the expvar.Var interface. func (c *counters) String() string { - b := bytes.NewBuffer(make([]byte, 0, 4096)) - - c.mu.RLock() - defer c.mu.RUnlock() + c.mu.Lock() + defer c.mu.Unlock() + b := &strings.Builder{} fmt.Fprintf(b, "{") - firstValue := true - for k, a := range c.counts { - if firstValue { - firstValue = false - } else { - fmt.Fprintf(b, ", ") - } - fmt.Fprintf(b, "%q: %v", k, atomic.LoadInt64(a)) + prefix := "" + for k, v := range c.counts { + fmt.Fprintf(b, "%s%q: %v", prefix, k, v) + prefix = ", " } fmt.Fprintf(b, "}") return b.String() } -func (c *counters) getValueAddr(name string) *int64 { - c.mu.RLock() - a, ok := c.counts[name] - c.mu.RUnlock() - - if ok { - return a - } - +func (c *counters) add(name string, value int64) { c.mu.Lock() defer c.mu.Unlock() - // we need to check the existence again - // as it may be created by other goroutine. - a, ok = c.counts[name] - if ok { - return a - } - a = new(int64) - c.counts[name] = a - return a + c.counts[name] = c.counts[name] + value } -// Add adds a value to a named counter. -func (c *counters) Add(name string, value int64) { - a := c.getValueAddr(name) - atomic.AddInt64(a, value) +func (c *counters) set(name string, value int64) { + c.mu.Lock() + defer c.mu.Unlock() + c.counts[name] = value } -// ResetAll resets all counter values and clears all keys. -func (c *counters) ResetAll() { +func (c *counters) reset() { c.mu.Lock() defer c.mu.Unlock() - c.counts = make(map[string]*int64) + c.counts = make(map[string]int64) } -// ZeroAll resets all counter values to zero +// ZeroAll zeroes out all values func (c *counters) ZeroAll() { c.mu.Lock() defer c.mu.Unlock() - for _, a := range c.counts { - atomic.StoreInt64(a, int64(0)) - } -} -// Reset resets a specific counter value to 0. -func (c *counters) Reset(name string) { - a := c.getValueAddr(name) - atomic.StoreInt64(a, int64(0)) + for k := range c.counts { + c.counts[k] = 0 + } } // Counts returns a copy of the Counters' map. func (c *counters) Counts() map[string]int64 { - c.mu.RLock() - defer c.mu.RUnlock() + c.mu.Lock() + defer c.mu.Unlock() counts := make(map[string]int64, len(c.counts)) - for k, a := range c.counts { - counts[k] = atomic.LoadInt64(a) + for k, v := range c.counts { + counts[k] = v } return counts } @@ -143,14 +109,14 @@ type CountersWithSingleLabel struct { func NewCountersWithSingleLabel(name, help, label string, tags ...string) *CountersWithSingleLabel { c := &CountersWithSingleLabel{ counters: counters{ - counts: make(map[string]*int64), + counts: make(map[string]int64), help: help, }, label: label, } for _, tag := range tags { - c.counts[tag] = new(int64) + c.counts[tag] = 0 } if name != "" { publish(name, c) @@ -168,13 +134,17 @@ func (c *CountersWithSingleLabel) Add(name string, value int64) { if value < 0 { logCounterNegative.Warningf("Adding a negative value to a counter, %v should be a gauge instead", c) } - a := c.getValueAddr(name) - atomic.AddInt64(a, value) + c.counters.add(name, value) +} + +// Reset resets the value for the name. +func (c *CountersWithSingleLabel) Reset(name string) { + c.counters.set(name, 0) } // ResetAll clears the counters func (c *CountersWithSingleLabel) ResetAll() { - c.counters.ResetAll() + c.counters.reset() } // CountersWithMultiLabels is a multidimensional counters implementation. @@ -190,7 +160,7 @@ type CountersWithMultiLabels struct { func NewCountersWithMultiLabels(name, help string, labels []string) *CountersWithMultiLabels { t := &CountersWithMultiLabels{ counters: counters{ - counts: make(map[string]*int64), + counts: make(map[string]int64), help: help}, labels: labels, } @@ -215,8 +185,7 @@ func (mc *CountersWithMultiLabels) Add(names []string, value int64) { if value < 0 { logCounterNegative.Warningf("Adding a negative value to a counter, %v should be a gauge instead", mc) } - - mc.counters.Add(safeJoinLabels(names), value) + mc.counters.add(safeJoinLabels(names), value) } // Reset resets the value of a named counter back to 0. @@ -226,7 +195,12 @@ func (mc *CountersWithMultiLabels) Reset(names []string) { panic("CountersWithMultiLabels: wrong number of values in Reset") } - mc.counters.Reset(safeJoinLabels(names)) + mc.counters.set(safeJoinLabels(names), 0) +} + +// ResetAll clears the counters +func (mc *CountersWithMultiLabels) ResetAll() { + mc.counters.reset() } // Counts returns a copy of the Counters' map. @@ -317,7 +291,7 @@ func NewGaugesWithSingleLabel(name, help, label string, tags ...string) *GaugesW g := &GaugesWithSingleLabel{ CountersWithSingleLabel: CountersWithSingleLabel{ counters: counters{ - counts: make(map[string]*int64), + counts: make(map[string]int64), help: help, }, label: label, @@ -325,7 +299,7 @@ func NewGaugesWithSingleLabel(name, help, label string, tags ...string) *GaugesW } for _, tag := range tags { - g.counts[tag] = new(int64) + g.counts[tag] = 0 } if name != "" { publish(name, g) @@ -335,14 +309,7 @@ func NewGaugesWithSingleLabel(name, help, label string, tags ...string) *GaugesW // Set sets the value of a named gauge. func (g *GaugesWithSingleLabel) Set(name string, value int64) { - a := g.getValueAddr(name) - atomic.StoreInt64(a, value) -} - -// Add adds a value to a named gauge. -func (g *GaugesWithSingleLabel) Add(name string, value int64) { - a := g.getValueAddr(name) - atomic.AddInt64(a, value) + g.counters.set(name, value) } // GaugesWithMultiLabels is a CountersWithMultiLabels implementation where @@ -357,7 +324,7 @@ func NewGaugesWithMultiLabels(name, help string, labels []string) *GaugesWithMul t := &GaugesWithMultiLabels{ CountersWithMultiLabels: CountersWithMultiLabels{ counters: counters{ - counts: make(map[string]*int64), + counts: make(map[string]int64), help: help, }, labels: labels, @@ -375,18 +342,7 @@ func (mg *GaugesWithMultiLabels) Set(names []string, value int64) { if len(names) != len(mg.CountersWithMultiLabels.labels) { panic("GaugesWithMultiLabels: wrong number of values in Set") } - a := mg.getValueAddr(safeJoinLabels(names)) - atomic.StoreInt64(a, value) -} - -// Add adds a value to a named gauge. -// len(names) must be equal to len(Labels). -func (mg *GaugesWithMultiLabels) Add(names []string, value int64) { - if len(names) != len(mg.labels) { - panic("CountersWithMultiLabels: wrong number of values in Add") - } - - mg.counters.Add(safeJoinLabels(names), value) + mg.counters.set(safeJoinLabels(names), value) } // GaugesFuncWithMultiLabels is a wrapper around CountersFuncWithMultiLabels From b5066e3f847fcace049ed6eab0bb7a37db9a157a Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 9 Feb 2020 02:18:41 -0800 Subject: [PATCH 079/825] stats: drop dimension This feature implements the drop dimension part of #5791. The dropping of dimensions is not applicable to all stats vars. We drop dimensions for counters and timings, but not gauges. This also doesn't work for func based vars, but those are now unused for counters and timings. The feature doesn't actually drop the dimension. Instead, it consolidates all the different values into a single "all" category. This approach is more backward compatible and allows for going back and forth, because the label itself continues to exist in both scenarios. Signed-off-by: Sugu Sougoumarane --- go/stats/counters.go | 27 +++++++++++++++------ go/stats/counters_test.go | 19 +++++++++++++++ go/stats/export.go | 44 +++++++++++++++++++++++++++++++++ go/stats/export_test.go | 2 ++ go/stats/safe_label.go | 27 --------------------- go/stats/timings.go | 51 ++++++++++++++++++--------------------- go/stats/timings_test.go | 22 +++++++++++++++++ 7 files changed, 130 insertions(+), 62 deletions(-) delete mode 100644 go/stats/safe_label.go diff --git a/go/stats/counters.go b/go/stats/counters.go index 1f7cc416b07..d545b75c10c 100644 --- a/go/stats/counters.go +++ b/go/stats/counters.go @@ -97,7 +97,8 @@ func (c *counters) Help() string { // It provides a Counts method which can be used for tracking rates. type CountersWithSingleLabel struct { counters - label string + label string + labelDropped bool } // NewCountersWithSingleLabel create a new Counters instance. @@ -112,7 +113,8 @@ func NewCountersWithSingleLabel(name, help, label string, tags ...string) *Count counts: make(map[string]int64), help: help, }, - label: label, + label: label, + labelDropped: IsDimensionDropped(label), } for _, tag := range tags { @@ -134,11 +136,17 @@ func (c *CountersWithSingleLabel) Add(name string, value int64) { if value < 0 { logCounterNegative.Warningf("Adding a negative value to a counter, %v should be a gauge instead", c) } + if c.labelDropped { + name = StatsAllStr + } c.counters.add(name, value) } // Reset resets the value for the name. func (c *CountersWithSingleLabel) Reset(name string) { + if c.labelDropped { + name = StatsAllStr + } c.counters.set(name, 0) } @@ -152,7 +160,8 @@ func (c *CountersWithSingleLabel) ResetAll() { // label value where all label values are joined with ".". type CountersWithMultiLabels struct { counters - labels []string + labels []string + droppedLabels []bool } // NewCountersWithMultiLabels creates a new CountersWithMultiLabels @@ -162,7 +171,11 @@ func NewCountersWithMultiLabels(name, help string, labels []string) *CountersWit counters: counters{ counts: make(map[string]int64), help: help}, - labels: labels, + labels: labels, + droppedLabels: make([]bool, len(labels)), + } + for i, label := range labels { + t.droppedLabels[i] = IsDimensionDropped(label) } if name != "" { publish(name, t) @@ -185,7 +198,7 @@ func (mc *CountersWithMultiLabels) Add(names []string, value int64) { if value < 0 { logCounterNegative.Warningf("Adding a negative value to a counter, %v should be a gauge instead", mc) } - mc.counters.add(safeJoinLabels(names), value) + mc.counters.add(safeJoinLabels(names, mc.droppedLabels), value) } // Reset resets the value of a named counter back to 0. @@ -195,7 +208,7 @@ func (mc *CountersWithMultiLabels) Reset(names []string) { panic("CountersWithMultiLabels: wrong number of values in Reset") } - mc.counters.set(safeJoinLabels(names), 0) + mc.counters.set(safeJoinLabels(names, mc.droppedLabels), 0) } // ResetAll clears the counters @@ -342,7 +355,7 @@ func (mg *GaugesWithMultiLabels) Set(names []string, value int64) { if len(names) != len(mg.CountersWithMultiLabels.labels) { panic("GaugesWithMultiLabels: wrong number of values in Set") } - mg.counters.set(safeJoinLabels(names), value) + mg.counters.set(safeJoinLabels(names, nil), value) } // GaugesFuncWithMultiLabels is a wrapper around CountersFuncWithMultiLabels diff --git a/go/stats/counters_test.go b/go/stats/counters_test.go index a9ea7485604..801447d9057 100644 --- a/go/stats/counters_test.go +++ b/go/stats/counters_test.go @@ -24,6 +24,8 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/assert" ) func TestCounters(t *testing.T) { @@ -240,3 +242,20 @@ func TestCountersFuncWithMultiLabels_Hook(t *testing.T) { t.Errorf("want %#v, got %#v", v, gotv) } } + +func TestCountersDropDimension(t *testing.T) { + clear() + *dropDimensions = "a,c" + + c1 := NewCountersWithSingleLabel("counter_dropdim1", "help", "label") + c1.Add("c1", 1) + assert.Equal(t, `{"c1": 1}`, c1.String()) + + c2 := NewCountersWithSingleLabel("counter_dropdim2", "help", "a") + c2.Add("c1", 1) + assert.Equal(t, `{"all": 1}`, c2.String()) + + c3 := NewCountersWithMultiLabels("counter_dropdim3", "help", []string{"a", "b", "c"}) + c3.Add([]string{"c1", "c2", "c3"}, 1) + assert.Equal(t, `{"all.c2.all": 1}`, c3.String()) +} diff --git a/go/stats/export.go b/go/stats/export.go index 54aa38d94d7..f26cc7a5f92 100644 --- a/go/stats/export.go +++ b/go/stats/export.go @@ -33,6 +33,7 @@ import ( "flag" "fmt" "strconv" + "strings" "sync" "time" @@ -42,6 +43,10 @@ import ( var emitStats = flag.Bool("emit_stats", false, "true iff we should emit stats to push-based monitoring/stats backends") var statsEmitPeriod = flag.Duration("stats_emit_period", time.Duration(60*time.Second), "Interval between emitting stats to all registered backends") var statsBackend = flag.String("stats_backend", "", "The name of the registered push-based monitoring/stats backend to use") +var dropDimensions = flag.String("drop_dimension", "", "List of dimensions to be dropped from stats vars") + +// StatsAllStr is the consolidated name if a dimension gets dropped. +const StatsAllStr = "all" // NewVarHook is the type of a hook to export variables in a different way type NewVarHook func(name string, v expvar.Var) @@ -247,3 +252,42 @@ func stringMapToString(m map[string]string) string { fmt.Fprintf(b, "}") return b.String() } + +var ( + dimMu sync.Mutex + droppedDimensions map[string]bool +) + +// IsDimensionDropped returns true if the specified dimension is dropped. +func IsDimensionDropped(name string) bool { + dimMu.Lock() + defer dimMu.Unlock() + + if droppedDimensions == nil { + dims := strings.Split(*dropDimensions, ",") + droppedDimensions = make(map[string]bool, len(dims)) + for _, dim := range dims { + droppedDimensions[dim] = true + } + } + return droppedDimensions[name] +} + +// safeJoinLabels joins the label values with ".", but first replaces any existing +// "." characters in the labels with the proper replacement, to avoid issues parsing +// them apart later. +func safeJoinLabels(labels []string, droppedLabels []bool) string { + sanitizedLabels := make([]string, len(labels)) + for idx, label := range labels { + if droppedLabels != nil && droppedLabels[idx] { + sanitizedLabels[idx] = StatsAllStr + } else { + sanitizedLabels[idx] = safeLabel(label) + } + } + return strings.Join(sanitizedLabels, ".") +} + +func safeLabel(label string) string { + return strings.Replace(label, ".", "_", -1) +} diff --git a/go/stats/export_test.go b/go/stats/export_test.go index 86c07064a7d..8aa5f07f4c6 100644 --- a/go/stats/export_test.go +++ b/go/stats/export_test.go @@ -24,6 +24,8 @@ import ( func clear() { defaultVarGroup.vars = make(map[string]expvar.Var) defaultVarGroup.newVarHook = nil + *dropDimensions = "" + droppedDimensions = nil } func TestNoHook(t *testing.T) { diff --git a/go/stats/safe_label.go b/go/stats/safe_label.go deleted file mode 100644 index a9ca0720f26..00000000000 --- a/go/stats/safe_label.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package stats - -import ( - "strings" -) - -// safeLabel turns a label into a safe label for stats export. -// It is in its own file so it can be customized. -func safeLabel(label string) string { - return strings.Replace(label, ".", "_", -1) -} diff --git a/go/stats/timings.go b/go/stats/timings.go index 8d32ad94bea..d9195176d31 100644 --- a/go/stats/timings.go +++ b/go/stats/timings.go @@ -19,7 +19,6 @@ package stats import ( "encoding/json" "fmt" - "strings" "sync" "time" @@ -32,13 +31,12 @@ type Timings struct { totalCount sync2.AtomicInt64 totalTime sync2.AtomicInt64 - // mu protects get and set of hook and the map. - // Modification to the value in the map is not protected. mu sync.RWMutex histograms map[string]*Histogram - hook func(string, time.Duration) - help string - label string + + help string + label string + labelDropped bool } // NewTimings creates a new Timings object, and publishes it if name is set. @@ -47,9 +45,10 @@ type Timings struct { // first time they are updated. func NewTimings(name, help, label string, categories ...string) *Timings { t := &Timings{ - histograms: make(map[string]*Histogram), - help: help, - label: label, + histograms: make(map[string]*Histogram), + help: help, + label: label, + labelDropped: IsDimensionDropped(label), } for _, cat := range categories { t.histograms[cat] = NewGenericHistogram("", "", bucketCutoffs, bucketLabels, "Count", "Time") @@ -63,10 +62,12 @@ func NewTimings(name, help, label string, categories ...string) *Timings { // Add will add a new value to the named histogram. func (t *Timings) Add(name string, elapsed time.Duration) { + if t.labelDropped { + name = StatsAllStr + } // Get existing Histogram. t.mu.RLock() hist, ok := t.histograms[name] - hook := t.hook t.mu.RUnlock() // Create Histogram if it does not exist. @@ -84,14 +85,14 @@ func (t *Timings) Add(name string, elapsed time.Duration) { hist.Add(elapsedNs) t.totalCount.Add(1) t.totalTime.Add(elapsedNs) - if hook != nil { - hook(name, elapsed) - } } // Record is a convenience function that records completion // timing data based on the provided start time of an event. func (t *Timings) Record(name string, startTime time.Time) { + if t.labelDropped { + name = StatsAllStr + } t.Add(name, time.Since(startTime)) } @@ -184,7 +185,8 @@ func init() { // with joining multiple strings with '.'. type MultiTimings struct { Timings - labels []string + labels []string + droppedLabels []bool } // NewMultiTimings creates a new MultiTimings object. @@ -194,7 +196,11 @@ func NewMultiTimings(name string, help string, labels []string) *MultiTimings { histograms: make(map[string]*Histogram), help: help, }, - labels: labels, + labels: labels, + droppedLabels: make([]bool, len(labels)), + } + for i, label := range labels { + t.droppedLabels[i] = IsDimensionDropped(label) } if name != "" { publish(name, t) @@ -208,23 +214,12 @@ func (mt *MultiTimings) Labels() []string { return mt.labels } -// safeJoinLabels joins the label values with ".", but first replaces any existing -// "." characters in the labels with the proper replacement, to avoid issues parsing -// them apart later. -func safeJoinLabels(labels []string) string { - sanitizedLabels := make([]string, len(labels)) - for idx, label := range labels { - sanitizedLabels[idx] = safeLabel(label) - } - return strings.Join(sanitizedLabels, ".") -} - // Add will add a new value to the named histogram. func (mt *MultiTimings) Add(names []string, elapsed time.Duration) { if len(names) != len(mt.labels) { panic("MultiTimings: wrong number of values in Add") } - mt.Timings.Add(safeJoinLabels(names), elapsed) + mt.Timings.Add(safeJoinLabels(names, mt.droppedLabels), elapsed) } // Record is a convenience function that records completion @@ -233,7 +228,7 @@ func (mt *MultiTimings) Record(names []string, startTime time.Time) { if len(names) != len(mt.labels) { panic("MultiTimings: wrong number of values in Record") } - mt.Timings.Record(safeJoinLabels(names), startTime) + mt.Timings.Record(safeJoinLabels(names, mt.droppedLabels), startTime) } // Cutoffs returns the cutoffs used in the component histograms. diff --git a/go/stats/timings_test.go b/go/stats/timings_test.go index 808d1aa15b5..6ee89a312b5 100644 --- a/go/stats/timings_test.go +++ b/go/stats/timings_test.go @@ -21,6 +21,8 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/assert" ) func TestTimings(t *testing.T) { @@ -77,3 +79,23 @@ func TestTimingsHook(t *testing.T) { t.Errorf("got %#v, want %#v", gotv, v) } } + +func TestTimingsDropDimension(t *testing.T) { + clear() + *dropDimensions = "a,c" + + t1 := NewTimings("timing_dropdim1", "help", "label") + t1.Add("t1", 1*time.Nanosecond) + want := `{"TotalCount":1,"TotalTime":1,"Histograms":{"t1":{"500000":1,"1000000":1,"5000000":1,"10000000":1,"50000000":1,"100000000":1,"500000000":1,"1000000000":1,"5000000000":1,"10000000000":1,"inf":1,"Count":1,"Time":1}}}` + assert.Equal(t, want, t1.String()) + + t2 := NewTimings("timing_dropdim2", "help", "a") + t2.Add("t1", 1) + want = `{"TotalCount":1,"TotalTime":1,"Histograms":{"all":{"500000":1,"1000000":1,"5000000":1,"10000000":1,"50000000":1,"100000000":1,"500000000":1,"1000000000":1,"5000000000":1,"10000000000":1,"inf":1,"Count":1,"Time":1}}}` + assert.Equal(t, want, t2.String()) + + t3 := NewMultiTimings("timing_dropdim3", "help", []string{"a", "b", "c"}) + t3.Add([]string{"c1", "c2", "c3"}, 1) + want = `{"TotalCount":1,"TotalTime":1,"Histograms":{"all.c2.all":{"500000":1,"1000000":1,"5000000":1,"10000000":1,"50000000":1,"100000000":1,"500000000":1,"1000000000":1,"5000000000":1,"10000000000":1,"inf":1,"Count":1,"Time":1}}}` + assert.Equal(t, want, t3.String()) +} From 851a851f36e694fdb051013eefceb175ade54945 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 09:28:54 +0530 Subject: [PATCH 080/825] trying after removing the path Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 76b50600e3f..46b7566630d 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -34,7 +34,6 @@ jobs: if [ ${{matrix.name}} = 18 ]; then wget https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_linux_amd64.zip unzip consul_1.4.0_linux_amd64.zip - export PATH="$PATH:$PWD" fi - name: sharded cluster_endtoend From 52404e39a80e96ede392546944e2a10d34a19fb5 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 09:37:01 +0530 Subject: [PATCH 081/825] trying with make tools Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 46b7566630d..c12d1850170 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -31,10 +31,10 @@ jobs: sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb sudo apt-get update sudo apt-get install percona-xtrabackup-24 - if [ ${{matrix.name}} = 18 ]; then - wget https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_linux_amd64.zip - unzip consul_1.4.0_linux_amd64.zip - fi + + - name: Installing dependencies + run: | + make tools - name: sharded cluster_endtoend run: | From ab1b89e3790b08f50ddf4c5441db192c8bbcb125 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 09:40:37 +0530 Subject: [PATCH 082/825] fir indentation and trying with minimaltools Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 6 +++--- Makefile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index c12d1850170..cd4b6c4110e 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -32,9 +32,9 @@ jobs: sudo apt-get update sudo apt-get install percona-xtrabackup-24 - - name: Installing dependencies - run: | - make tools + - name: Installing minimal dependencies + run: | + make minimaltools - name: sharded cluster_endtoend run: | diff --git a/Makefile b/Makefile index d8b9f9a4c50..8be291a2c84 100644 --- a/Makefile +++ b/Makefile @@ -331,7 +331,7 @@ tools: minimaltools: echo $$(date): Installing minimal dependencies - BUILD_PYTHON=0 BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh + BUILD_PYTHON=0 BUILD_JAVA=0 ./bootstrap.sh dependency_check: ./tools/dependency_check.sh From aaf4741ecd8bd0d21f41e56570266cebf5265814 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 09:46:47 +0530 Subject: [PATCH 083/825] added source build.env back Signed-off-by: Ajeet jain --- tools/e2e_go_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/e2e_go_test.sh b/tools/e2e_go_test.sh index 37c5191eec5..9148d80e156 100755 --- a/tools/e2e_go_test.sh +++ b/tools/e2e_go_test.sh @@ -1,4 +1,4 @@ #!/bin/bash -# source build.env +source build.env echo "running tests for $@ " go test -v $@ -count=1 \ No newline at end of file From 3b3c6068c21129c032410fc1723ae160bbc26768 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 10:05:08 +0530 Subject: [PATCH 084/825] added zookeeper and removed flaky test Signed-off-by: Ajeet jain --- Makefile | 2 +- test/config.json | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 8be291a2c84..2275d71e1ad 100644 --- a/Makefile +++ b/Makefile @@ -331,7 +331,7 @@ tools: minimaltools: echo $$(date): Installing minimal dependencies - BUILD_PYTHON=0 BUILD_JAVA=0 ./bootstrap.sh + BUILD_PYTHON=0 ./bootstrap.sh dependency_check: ./tools/dependency_check.sh diff --git a/test/config.json b/test/config.json index da2ba9add77..a3e67bca5e6 100644 --- a/test/config.json +++ b/test/config.json @@ -420,17 +420,6 @@ "site_test" ] }, - "tabletmanager_master": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/tabletmanager/master"], - "Command": [], - "Manual": false, - "Shard": 18, - "RetryMax": 0, - "Tags": [ - "site_test" - ] - }, "tabletmanager_consul": { "File": "unused.go", "Args": [ From 1b3876191956921a5222b59c13c54191c408d265 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 11:08:22 +0530 Subject: [PATCH 085/825] Fixing test framework for zookeeper Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 6 ++++-- Makefile | 2 +- go/test/endtoend/cluster/topo_process.go | 7 ++++++- tools/e2e_go_test.sh | 1 - 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index cd4b6c4110e..40425dc9295 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -32,9 +32,11 @@ jobs: sudo apt-get update sudo apt-get install percona-xtrabackup-24 - - name: Installing minimal dependencies + - name: Installing zookeeper and consul run: | - make minimaltools + if [ ${{matrix.name}} = 18 ]; then + make tools + fi - name: sharded cluster_endtoend run: | diff --git a/Makefile b/Makefile index 2275d71e1ad..d8b9f9a4c50 100644 --- a/Makefile +++ b/Makefile @@ -331,7 +331,7 @@ tools: minimaltools: echo $$(date): Installing minimal dependencies - BUILD_PYTHON=0 ./bootstrap.sh + BUILD_PYTHON=0 BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh dependency_check: ./tools/dependency_check.sh diff --git a/go/test/endtoend/cluster/topo_process.go b/go/test/endtoend/cluster/topo_process.go index 21e62857917..4a742646952 100644 --- a/go/test/endtoend/cluster/topo_process.go +++ b/go/test/endtoend/cluster/topo_process.go @@ -112,12 +112,17 @@ func (topo *TopoProcess) SetupEtcd() (err error) { // The service is kept running in the background until TearDown() is called. func (topo *TopoProcess) SetupZookeeper(cluster *LocalProcessCluster) (err error) { + host, err := os.Hostname() + if err != nil { + return + } + topo.ZKPorts = fmt.Sprintf("%d:%d:%d", cluster.GetAndReservePort(), cluster.GetAndReservePort(), topo.Port) topo.proc = exec.Command( topo.Binary, "-log_dir", topo.LogDirectory, - "-zk.cfg", fmt.Sprintf("1@%v:%s", topo.Host, topo.ZKPorts), + "-zk.cfg", fmt.Sprintf("1@%v:%s", host, topo.ZKPorts), "init", ) diff --git a/tools/e2e_go_test.sh b/tools/e2e_go_test.sh index 9148d80e156..34904eb3ade 100755 --- a/tools/e2e_go_test.sh +++ b/tools/e2e_go_test.sh @@ -1,4 +1,3 @@ #!/bin/bash -source build.env echo "running tests for $@ " go test -v $@ -count=1 \ No newline at end of file From 2c00f4eac64c0b7fc12dd484d31ed8c93a97a411 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 11:20:05 +0530 Subject: [PATCH 086/825] removing if condition from make tools Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 40425dc9295..ac631340898 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -34,9 +34,7 @@ jobs: - name: Installing zookeeper and consul run: | - if [ ${{matrix.name}} = 18 ]; then make tools - fi - name: sharded cluster_endtoend run: | From 4ff177644c030babb52dc36c35a802783210d5f3 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 11:30:15 +0530 Subject: [PATCH 087/825] adding source back in the sh file Signed-off-by: Ajeet jain --- tools/e2e_go_test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/e2e_go_test.sh b/tools/e2e_go_test.sh index 34904eb3ade..9148d80e156 100755 --- a/tools/e2e_go_test.sh +++ b/tools/e2e_go_test.sh @@ -1,3 +1,4 @@ #!/bin/bash +source build.env echo "running tests for $@ " go test -v $@ -count=1 \ No newline at end of file From d1e13265bb9654230400c503796996e6fadf427e Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 11:56:33 +0530 Subject: [PATCH 088/825] adding if condition for make tool Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index ac631340898..40425dc9295 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -34,7 +34,9 @@ jobs: - name: Installing zookeeper and consul run: | + if [ ${{matrix.name}} = 18 ]; then make tools + fi - name: sharded cluster_endtoend run: | From 372ff26cbb959f31a62a94ccffdb103963c80d88 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 12:19:55 +0530 Subject: [PATCH 089/825] added tabletmanager master test back Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 4 ++-- test/config.json | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 40425dc9295..3dd07040370 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24] steps: - name: Set up Go @@ -34,7 +34,7 @@ jobs: - name: Installing zookeeper and consul run: | - if [ ${{matrix.name}} = 18 ]; then + if [ ${{matrix.name}} = 18 || ${{matrix.name}} = 24 ]; then make tools fi diff --git a/test/config.json b/test/config.json index a3e67bca5e6..8357906585b 100644 --- a/test/config.json +++ b/test/config.json @@ -433,6 +433,17 @@ "site_test" ] }, + "tabletmanager_master": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/tabletmanager/master"], + "Command": [], + "Manual": false, + "Shard": 24, + "RetryMax": 0, + "Tags": [ + "site_test" + ] + }, "tabletmanager_zk2": { "File": "unused.go", "Args": [ @@ -440,7 +451,7 @@ ], "Command": [], "Manual": false, - "Shard": 18, + "Shard": 24, "RetryMax": 0, "Tags": [ "site_test" From 468ead013f00cde4404f34f0d8483cc66e3f24e2 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 9 Feb 2020 23:02:42 -0800 Subject: [PATCH 090/825] stats: dropped dimension: fix empty label bug Signed-off-by: Sugu Sougoumarane --- go/stats/counters_test.go | 6 ++++++ go/stats/export.go | 3 +++ 2 files changed, 9 insertions(+) diff --git a/go/stats/counters_test.go b/go/stats/counters_test.go index 801447d9057..21267ec026e 100644 --- a/go/stats/counters_test.go +++ b/go/stats/counters_test.go @@ -244,6 +244,12 @@ func TestCountersFuncWithMultiLabels_Hook(t *testing.T) { } func TestCountersDropDimension(t *testing.T) { + clear() + // Empty labels shouldn't be dropped. + c4 := NewCountersWithSingleLabel("counter_dropdim4", "help", "") + c4.Add("c1", 1) + assert.Equal(t, `{"c1": 1}`, c4.String()) + clear() *dropDimensions = "a,c" diff --git a/go/stats/export.go b/go/stats/export.go index f26cc7a5f92..0a9296b7511 100644 --- a/go/stats/export.go +++ b/go/stats/export.go @@ -267,6 +267,9 @@ func IsDimensionDropped(name string) bool { dims := strings.Split(*dropDimensions, ",") droppedDimensions = make(map[string]bool, len(dims)) for _, dim := range dims { + if dim == "" { + continue + } droppedDimensions[dim] = true } } From fc8896d48110796b5d7509474fc256a98e8f3bc3 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 12:38:11 +0530 Subject: [PATCH 091/825] updated if condition Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 3dd07040370..8d9d719071e 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -34,7 +34,8 @@ jobs: - name: Installing zookeeper and consul run: | - if [ ${{matrix.name}} = 18 || ${{matrix.name}} = 24 ]; then + # Only running for shard 18 and 24 where we need to install consul and zookeeper + if [ ${{matrix.name}} == 18 || ${{matrix.name}} == 24 ]; then make tools fi From cb2f6aaf997d3cc111141d894c3d7145e0eef221 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 9 Feb 2020 23:27:57 -0800 Subject: [PATCH 092/825] stats: drop dimension: address review comments Signed-off-by: Sugu Sougoumarane --- go/stats/counters.go | 34 ++++++++++++--------- go/stats/counters_test.go | 25 ++++++++------- go/stats/export.go | 29 +++++++++--------- go/stats/export_test.go | 4 +-- go/stats/timings.go | 32 +++++++++---------- go/stats/timings_test.go | 10 +++--- go/vt/vttablet/tabletserver/query_engine.go | 3 +- 7 files changed, 72 insertions(+), 65 deletions(-) diff --git a/go/stats/counters.go b/go/stats/counters.go index d545b75c10c..46e9b8f904c 100644 --- a/go/stats/counters.go +++ b/go/stats/counters.go @@ -97,8 +97,8 @@ func (c *counters) Help() string { // It provides a Counts method which can be used for tracking rates. type CountersWithSingleLabel struct { counters - label string - labelDropped bool + label string + labelCombined bool } // NewCountersWithSingleLabel create a new Counters instance. @@ -113,12 +113,16 @@ func NewCountersWithSingleLabel(name, help, label string, tags ...string) *Count counts: make(map[string]int64), help: help, }, - label: label, - labelDropped: IsDimensionDropped(label), + label: label, + labelCombined: IsDimensionCombined(label), } - for _, tag := range tags { - c.counts[tag] = 0 + if c.labelCombined { + c.counts[StatsAllStr] = 0 + } else { + for _, tag := range tags { + c.counts[tag] = 0 + } } if name != "" { publish(name, c) @@ -136,7 +140,7 @@ func (c *CountersWithSingleLabel) Add(name string, value int64) { if value < 0 { logCounterNegative.Warningf("Adding a negative value to a counter, %v should be a gauge instead", c) } - if c.labelDropped { + if c.labelCombined { name = StatsAllStr } c.counters.add(name, value) @@ -144,7 +148,7 @@ func (c *CountersWithSingleLabel) Add(name string, value int64) { // Reset resets the value for the name. func (c *CountersWithSingleLabel) Reset(name string) { - if c.labelDropped { + if c.labelCombined { name = StatsAllStr } c.counters.set(name, 0) @@ -160,8 +164,8 @@ func (c *CountersWithSingleLabel) ResetAll() { // label value where all label values are joined with ".". type CountersWithMultiLabels struct { counters - labels []string - droppedLabels []bool + labels []string + combinedLabels []bool } // NewCountersWithMultiLabels creates a new CountersWithMultiLabels @@ -171,11 +175,11 @@ func NewCountersWithMultiLabels(name, help string, labels []string) *CountersWit counters: counters{ counts: make(map[string]int64), help: help}, - labels: labels, - droppedLabels: make([]bool, len(labels)), + labels: labels, + combinedLabels: make([]bool, len(labels)), } for i, label := range labels { - t.droppedLabels[i] = IsDimensionDropped(label) + t.combinedLabels[i] = IsDimensionCombined(label) } if name != "" { publish(name, t) @@ -198,7 +202,7 @@ func (mc *CountersWithMultiLabels) Add(names []string, value int64) { if value < 0 { logCounterNegative.Warningf("Adding a negative value to a counter, %v should be a gauge instead", mc) } - mc.counters.add(safeJoinLabels(names, mc.droppedLabels), value) + mc.counters.add(safeJoinLabels(names, mc.combinedLabels), value) } // Reset resets the value of a named counter back to 0. @@ -208,7 +212,7 @@ func (mc *CountersWithMultiLabels) Reset(names []string) { panic("CountersWithMultiLabels: wrong number of values in Reset") } - mc.counters.set(safeJoinLabels(names, mc.droppedLabels), 0) + mc.counters.set(safeJoinLabels(names, mc.combinedLabels), 0) } // ResetAll clears the counters diff --git a/go/stats/counters_test.go b/go/stats/counters_test.go index 21267ec026e..2fd9bc0fea1 100644 --- a/go/stats/counters_test.go +++ b/go/stats/counters_test.go @@ -243,25 +243,28 @@ func TestCountersFuncWithMultiLabels_Hook(t *testing.T) { } } -func TestCountersDropDimension(t *testing.T) { +func TestCountersCombineDimension(t *testing.T) { clear() - // Empty labels shouldn't be dropped. - c4 := NewCountersWithSingleLabel("counter_dropdim4", "help", "") - c4.Add("c1", 1) - assert.Equal(t, `{"c1": 1}`, c4.String()) + // Empty labels shouldn't be combined. + c0 := NewCountersWithSingleLabel("counter_combine_dim0", "help", "") + c0.Add("c1", 1) + assert.Equal(t, `{"c1": 1}`, c0.String()) clear() - *dropDimensions = "a,c" + *combineDimensions = "a,c" - c1 := NewCountersWithSingleLabel("counter_dropdim1", "help", "label") + c1 := NewCountersWithSingleLabel("counter_combine_dim1", "help", "label") c1.Add("c1", 1) assert.Equal(t, `{"c1": 1}`, c1.String()) - c2 := NewCountersWithSingleLabel("counter_dropdim2", "help", "a") + c2 := NewCountersWithSingleLabel("counter_combine_dim2", "help", "a") c2.Add("c1", 1) assert.Equal(t, `{"all": 1}`, c2.String()) - c3 := NewCountersWithMultiLabels("counter_dropdim3", "help", []string{"a", "b", "c"}) - c3.Add([]string{"c1", "c2", "c3"}, 1) - assert.Equal(t, `{"all.c2.all": 1}`, c3.String()) + c3 := NewCountersWithSingleLabel("counter_combine_dim3", "help", "a") + assert.Equal(t, `{"all": 0}`, c3.String()) + + c4 := NewCountersWithMultiLabels("counter_combine_dim4", "help", []string{"a", "b", "c"}) + c4.Add([]string{"c1", "c2", "c3"}, 1) + assert.Equal(t, `{"all.c2.all": 1}`, c4.String()) } diff --git a/go/stats/export.go b/go/stats/export.go index 0a9296b7511..ac4416adf58 100644 --- a/go/stats/export.go +++ b/go/stats/export.go @@ -43,9 +43,9 @@ import ( var emitStats = flag.Bool("emit_stats", false, "true iff we should emit stats to push-based monitoring/stats backends") var statsEmitPeriod = flag.Duration("stats_emit_period", time.Duration(60*time.Second), "Interval between emitting stats to all registered backends") var statsBackend = flag.String("stats_backend", "", "The name of the registered push-based monitoring/stats backend to use") -var dropDimensions = flag.String("drop_dimension", "", "List of dimensions to be dropped from stats vars") +var combineDimensions = flag.String("stats_combine_dimensions", "", `List of dimensions to be combined into a single "all" value in exported stats vars`) -// StatsAllStr is the consolidated name if a dimension gets dropped. +// StatsAllStr is the consolidated name if a dimension gets combined. const StatsAllStr = "all" // NewVarHook is the type of a hook to export variables in a different way @@ -254,35 +254,36 @@ func stringMapToString(m map[string]string) string { } var ( - dimMu sync.Mutex - droppedDimensions map[string]bool + dimMu sync.Mutex + combinedDimensions map[string]bool ) -// IsDimensionDropped returns true if the specified dimension is dropped. -func IsDimensionDropped(name string) bool { +// IsDimensionCombined returns true if the specified dimension should be combined. +func IsDimensionCombined(name string) bool { dimMu.Lock() defer dimMu.Unlock() - if droppedDimensions == nil { - dims := strings.Split(*dropDimensions, ",") - droppedDimensions = make(map[string]bool, len(dims)) + if combinedDimensions == nil { + dims := strings.Split(*combineDimensions, ",") + combinedDimensions = make(map[string]bool, len(dims)) for _, dim := range dims { if dim == "" { continue } - droppedDimensions[dim] = true + combinedDimensions[dim] = true } } - return droppedDimensions[name] + return combinedDimensions[name] } // safeJoinLabels joins the label values with ".", but first replaces any existing // "." characters in the labels with the proper replacement, to avoid issues parsing -// them apart later. -func safeJoinLabels(labels []string, droppedLabels []bool) string { +// them apart later. The function also replaces specific label values with "all" +// if a dimenstion is marked as true in combinedLabels. +func safeJoinLabels(labels []string, combinedLabels []bool) string { sanitizedLabels := make([]string, len(labels)) for idx, label := range labels { - if droppedLabels != nil && droppedLabels[idx] { + if combinedLabels != nil && combinedLabels[idx] { sanitizedLabels[idx] = StatsAllStr } else { sanitizedLabels[idx] = safeLabel(label) diff --git a/go/stats/export_test.go b/go/stats/export_test.go index 8aa5f07f4c6..b12939cd5c7 100644 --- a/go/stats/export_test.go +++ b/go/stats/export_test.go @@ -24,8 +24,8 @@ import ( func clear() { defaultVarGroup.vars = make(map[string]expvar.Var) defaultVarGroup.newVarHook = nil - *dropDimensions = "" - droppedDimensions = nil + *combineDimensions = "" + combinedDimensions = nil } func TestNoHook(t *testing.T) { diff --git a/go/stats/timings.go b/go/stats/timings.go index d9195176d31..697963c4773 100644 --- a/go/stats/timings.go +++ b/go/stats/timings.go @@ -34,9 +34,9 @@ type Timings struct { mu sync.RWMutex histograms map[string]*Histogram - help string - label string - labelDropped bool + help string + label string + labelCombined bool } // NewTimings creates a new Timings object, and publishes it if name is set. @@ -45,10 +45,10 @@ type Timings struct { // first time they are updated. func NewTimings(name, help, label string, categories ...string) *Timings { t := &Timings{ - histograms: make(map[string]*Histogram), - help: help, - label: label, - labelDropped: IsDimensionDropped(label), + histograms: make(map[string]*Histogram), + help: help, + label: label, + labelCombined: IsDimensionCombined(label), } for _, cat := range categories { t.histograms[cat] = NewGenericHistogram("", "", bucketCutoffs, bucketLabels, "Count", "Time") @@ -62,7 +62,7 @@ func NewTimings(name, help, label string, categories ...string) *Timings { // Add will add a new value to the named histogram. func (t *Timings) Add(name string, elapsed time.Duration) { - if t.labelDropped { + if t.labelCombined { name = StatsAllStr } // Get existing Histogram. @@ -90,7 +90,7 @@ func (t *Timings) Add(name string, elapsed time.Duration) { // Record is a convenience function that records completion // timing data based on the provided start time of an event. func (t *Timings) Record(name string, startTime time.Time) { - if t.labelDropped { + if t.labelCombined { name = StatsAllStr } t.Add(name, time.Since(startTime)) @@ -185,8 +185,8 @@ func init() { // with joining multiple strings with '.'. type MultiTimings struct { Timings - labels []string - droppedLabels []bool + labels []string + combinedLabels []bool } // NewMultiTimings creates a new MultiTimings object. @@ -196,11 +196,11 @@ func NewMultiTimings(name string, help string, labels []string) *MultiTimings { histograms: make(map[string]*Histogram), help: help, }, - labels: labels, - droppedLabels: make([]bool, len(labels)), + labels: labels, + combinedLabels: make([]bool, len(labels)), } for i, label := range labels { - t.droppedLabels[i] = IsDimensionDropped(label) + t.combinedLabels[i] = IsDimensionCombined(label) } if name != "" { publish(name, t) @@ -219,7 +219,7 @@ func (mt *MultiTimings) Add(names []string, elapsed time.Duration) { if len(names) != len(mt.labels) { panic("MultiTimings: wrong number of values in Add") } - mt.Timings.Add(safeJoinLabels(names, mt.droppedLabels), elapsed) + mt.Timings.Add(safeJoinLabels(names, mt.combinedLabels), elapsed) } // Record is a convenience function that records completion @@ -228,7 +228,7 @@ func (mt *MultiTimings) Record(names []string, startTime time.Time) { if len(names) != len(mt.labels) { panic("MultiTimings: wrong number of values in Record") } - mt.Timings.Record(safeJoinLabels(names, mt.droppedLabels), startTime) + mt.Timings.Record(safeJoinLabels(names, mt.combinedLabels), startTime) } // Cutoffs returns the cutoffs used in the component histograms. diff --git a/go/stats/timings_test.go b/go/stats/timings_test.go index 6ee89a312b5..435f5106ba2 100644 --- a/go/stats/timings_test.go +++ b/go/stats/timings_test.go @@ -80,21 +80,21 @@ func TestTimingsHook(t *testing.T) { } } -func TestTimingsDropDimension(t *testing.T) { +func TestTimingsCombineDimension(t *testing.T) { clear() - *dropDimensions = "a,c" + *combineDimensions = "a,c" - t1 := NewTimings("timing_dropdim1", "help", "label") + t1 := NewTimings("timing_combine_dim1", "help", "label") t1.Add("t1", 1*time.Nanosecond) want := `{"TotalCount":1,"TotalTime":1,"Histograms":{"t1":{"500000":1,"1000000":1,"5000000":1,"10000000":1,"50000000":1,"100000000":1,"500000000":1,"1000000000":1,"5000000000":1,"10000000000":1,"inf":1,"Count":1,"Time":1}}}` assert.Equal(t, want, t1.String()) - t2 := NewTimings("timing_dropdim2", "help", "a") + t2 := NewTimings("timing_combine_dim2", "help", "a") t2.Add("t1", 1) want = `{"TotalCount":1,"TotalTime":1,"Histograms":{"all":{"500000":1,"1000000":1,"5000000":1,"10000000":1,"50000000":1,"100000000":1,"500000000":1,"1000000000":1,"5000000000":1,"10000000000":1,"inf":1,"Count":1,"Time":1}}}` assert.Equal(t, want, t2.String()) - t3 := NewMultiTimings("timing_dropdim3", "help", []string{"a", "b", "c"}) + t3 := NewMultiTimings("timing_combine_dim3", "help", []string{"a", "b", "c"}) t3.Add([]string{"c1", "c2", "c3"}, 1) want = `{"TotalCount":1,"TotalTime":1,"Histograms":{"all.c2.all":{"500000":1,"1000000":1,"5000000":1,"10000000":1,"50000000":1,"100000000":1,"500000000":1,"1000000000":1,"5000000000":1,"10000000000":1,"inf":1,"Count":1,"Time":1}}}` assert.Equal(t, want, t3.String()) diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index be7e121ec0c..862f9950bd5 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "net/http" - "strings" "sync" "time" @@ -488,7 +487,7 @@ func (qe *QueryEngine) QueryPlanCacheCap() int { // AddStats adds the given stats for the planName.tableName func (qe *QueryEngine) AddStats(planName, tableName string, queryCount int64, duration, mysqlTime time.Duration, rowCount, errorCount int64) { // table names can contain "." characters, replace them! - keys := []string{strings.Replace(tableName, ".", "_", -1), planName} + keys := []string{tableName, planName} queryCounts.Add(keys, queryCount) queryTimes.Add(keys, int64(duration)) queryRowCounts.Add(keys, rowCount) From f6c8901a0fe580a0c45576b5c2328a80491be677 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 12:41:45 +0530 Subject: [PATCH 093/825] updating syntax Signed-off-by: Ajeet jain t push -f origin test_config_changes --- .github/workflows/cluster_endtoend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 8d9d719071e..f1e4dbe88d9 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -35,7 +35,7 @@ jobs: - name: Installing zookeeper and consul run: | # Only running for shard 18 and 24 where we need to install consul and zookeeper - if [ ${{matrix.name}} == 18 || ${{matrix.name}} == 24 ]; then + if [[ ${{matrix.name}} == 18 || ${{matrix.name}} == 24 ]]; then make tools fi From 0ee5f0b9df0105b0334d88ecf1b2efad5375c1d7 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Mon, 10 Feb 2020 16:33:38 +0530 Subject: [PATCH 094/825] removed flaky test Signed-off-by: Ajeet jain --- test/config.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/config.json b/test/config.json index 8357906585b..aca38ffa4ee 100644 --- a/test/config.json +++ b/test/config.json @@ -433,17 +433,6 @@ "site_test" ] }, - "tabletmanager_master": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/tabletmanager/master"], - "Command": [], - "Manual": false, - "Shard": 24, - "RetryMax": 0, - "Tags": [ - "site_test" - ] - }, "tabletmanager_zk2": { "File": "unused.go", "Args": [ From 5579aedcac17762712ca629db0f3d7b6886b4ee5 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 10:32:19 -0500 Subject: [PATCH 095/825] Added (broken) region Vindex from https://github.com/planetscale/vitess/tree/ds-kcn19-demo Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 120 +++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 go/vt/vtgate/vindexes/region_vindex.go diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go new file mode 100644 index 00000000000..0735f55a90e --- /dev/null +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vindexes + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "io/ioutil" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/log" +) + +var ( + _ Vindex = (*RegionVindex)(nil) + _ Lookup = (*RegionVindex)(nil) + _ WantOwnerInfo = (*RegionVindex)(nil) + _ MultiColumn = (*RegionVindex)(nil) +) + +func init() { + Register("region_vindex", NewRegionVindex) +} + +// RegionMap is used to store mapping of country to region +type RegionMap map[string]uint64 + +// RegionVindex defines a vindex that uses a lookup table. +// The table is expected to define the id column as unique. It's +// Unique and a Lookup. +type RegionVindex struct { + regionMap RegionMap + *RegionExperimental +} + +// NewRegionVindex creates a RegionVindex vindex. +// The supplied map requires all the fields of "region_experimental". +// Additionally, it requires a region_map argument representing the path to a json file +// containing a map of country to region. +func NewRegionVindex(name string, m map[string]string) (Vindex, error) { + rmPath := m["region_map"] + rmap := make(map[string]uint64) + data, err := ioutil.ReadFile(rmPath) + if err != nil { + return nil, err + } + log.Infof("Loaded Region map from: %s", rmPath) + err = json.Unmarshal(data, &rmap) + if err != nil { + return nil, err + } + + vindex, err := NewRegionExperimental(name, m) + if err != nil { + // Unreachable. + return nil, err + } + re := vindex.(*RegionExperimental) + if len(re.ConsistentLookupUnique.lkp.FromColumns) != 2 { + return nil, fmt.Errorf("two columns are required for region_experimental: %v", re.ConsistentLookupUnique.lkp.FromColumns) + } + return &RegionVindex{ + regionMap: rmap, + RegionExperimental: re, + }, nil +} + +// MapMulti satisfies MultiColumn. +func (rv *RegionVindex) MapMulti(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { + destinations := make([]key.Destination, 0, len(rowsColValues)) + for _, row := range rowsColValues { + if len(row) != 2 { + destinations = append(destinations, key.DestinationNone{}) + continue + } + // Compute hash. + hn, err := sqltypes.ToUint64(row[0]) + if err != nil { + destinations = append(destinations, key.DestinationNone{}) + continue + } + h := vhash(hn) + + log.Infof("Region map: %v", rv.regionMap) + // Compute region prefix. + log.Infof("Country: %v", row[1].ToString()) + rn, ok := rv.regionMap[row[1].ToString()] + log.Infof("Found region %v, true/false: %v", rn, ok) + if !ok { + destinations = append(destinations, key.DestinationNone{}) + continue + } + r := make([]byte, 2) + binary.BigEndian.PutUint16(r, uint16(rn)) + + // Concatenate and add to destinations. + if rv.regionBytes == 1 { + r = r[1:] + } + dest := append(r, h...) + destinations = append(destinations, key.DestinationKeyspaceID(dest)) + } + return destinations, nil +} \ No newline at end of file From 8266a34be9d533189b9d8d9c74757ab039fb77cb Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 10:45:22 -0500 Subject: [PATCH 096/825] Implemented changes similar to https://github.com/vitessio/vitess/commit/a0913915519859c75e24c161571013cf86dac2eb#diff-8ddd8af3d810b9a1c2bf9650ebed76e7 Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 39 ++++++++++++++------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index 0735f55a90e..b65b93dfe84 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -28,9 +28,6 @@ import ( ) var ( - _ Vindex = (*RegionVindex)(nil) - _ Lookup = (*RegionVindex)(nil) - _ WantOwnerInfo = (*RegionVindex)(nil) _ MultiColumn = (*RegionVindex)(nil) ) @@ -45,8 +42,8 @@ type RegionMap map[string]uint64 // The table is expected to define the id column as unique. It's // Unique and a Lookup. type RegionVindex struct { - regionMap RegionMap - *RegionExperimental + name string + regionMap RegionMap } // NewRegionVindex creates a RegionVindex vindex. @@ -66,23 +63,29 @@ func NewRegionVindex(name string, m map[string]string) (Vindex, error) { return nil, err } - vindex, err := NewRegionExperimental(name, m) - if err != nil { - // Unreachable. - return nil, err - } - re := vindex.(*RegionExperimental) - if len(re.ConsistentLookupUnique.lkp.FromColumns) != 2 { - return nil, fmt.Errorf("two columns are required for region_experimental: %v", re.ConsistentLookupUnique.lkp.FromColumns) - } return &RegionVindex{ - regionMap: rmap, - RegionExperimental: re, + name: name, + regionMap: rmap, }, nil } -// MapMulti satisfies MultiColumn. -func (rv *RegionVindex) MapMulti(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { +// String returns the name of the vindex. +func (rv *RegionVindex) String() string { + return rv.name +} + +// Cost returns the cost of this index as 1. +func (rv *RegionVindex) Cost() int { + return 1 +} + +// IsUnique returns true since the Vindex is unique. +func (rv *RegionVindex) IsUnique() bool { + return true; +} + +// Map satisfies MultiColumn. +func (rv *RegionVindex) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { destinations := make([]key.Destination, 0, len(rowsColValues)) for _, row := range rowsColValues { if len(row) != 2 { From c9369bc09a5593c0d0650992c07244e40671e4f9 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 10:52:08 -0500 Subject: [PATCH 097/825] Implemented changes similar to https://github.com/vitessio/vitess/commit/c8ccf37e3d77fa4df761c92e4ec8b1d17ac2c726#diff-8ddd8af3d810b9a1c2bf9650ebed76e7 Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index b65b93dfe84..5e1fa8355cc 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -120,4 +120,9 @@ func (rv *RegionVindex) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ( destinations = append(destinations, key.DestinationKeyspaceID(dest)) } return destinations, nil +} + +// NeedVCursor staisfies the Vindex interface. +func (rv *RegionVindex) NeedVCursor() bool { + return false } \ No newline at end of file From 27dab6bdfda57acae534bbde3659607b72d4ad41 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 10:54:39 -0500 Subject: [PATCH 098/825] Added regionBytes field. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index 5e1fa8355cc..08eec90faa3 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -44,6 +44,7 @@ type RegionMap map[string]uint64 type RegionVindex struct { name string regionMap RegionMap + regionBytes int } // NewRegionVindex creates a RegionVindex vindex. From d834206de04fc9f8c92f4930a760d1cdec8224dc Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 10:59:22 -0500 Subject: [PATCH 099/825] Removed extra import. NeedVCursor -> NeedsVCursor Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index 08eec90faa3..c54b053d750 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -19,7 +19,6 @@ package vindexes import ( "encoding/binary" "encoding/json" - "fmt" "io/ioutil" "vitess.io/vitess/go/sqltypes" @@ -124,6 +123,6 @@ func (rv *RegionVindex) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ( } // NeedVCursor staisfies the Vindex interface. -func (rv *RegionVindex) NeedVCursor() bool { +func (rv *RegionVindex) NeedsVCursor() bool { return false } \ No newline at end of file From 65716054fb98cb49085f94b42d7d9025a7eba8b8 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 11:05:34 -0500 Subject: [PATCH 100/825] Copied 'Verify' from https://github.com/vitessio/vitess/commit/a0913915519859c75e24c161571013cf86dac2eb#diff-8ddd8af3d810b9a1c2bf9650ebed76e7 Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index c54b053d750..413d641c28b 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -122,6 +122,20 @@ func (rv *RegionVindex) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ( return destinations, nil } +// Verify satisfies MultiColumn +func (rv *RegionVindex) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]bytes) ([]bool, error) { + result := make([]bool, len(rowsColValues)) + destinations, _ := rv.Map(vcursor, rowsColValues) + for i, dest := range destinations { + destksid, ok := dest.(key.DestinationKeyspaceID) + if !ok { + continue + } + result[i] = bytes.Equal([]byte(destksid), ksids[i]) + } + return result, nil +} + // NeedVCursor staisfies the Vindex interface. func (rv *RegionVindex) NeedsVCursor() bool { return false From dde3a3fac9c8d995939aea73df0440624d2c6d39 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 11:20:18 -0500 Subject: [PATCH 101/825] Import bytes. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index 413d641c28b..61bceacd098 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -17,6 +17,7 @@ limitations under the License. package vindexes import ( + "bytes" "encoding/binary" "encoding/json" "io/ioutil" From 99c3b2b7ac1c0a809f55575ce0f3ea1dee08a038 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Wed, 5 Feb 2020 11:24:02 -0500 Subject: [PATCH 102/825] bytes -> byte Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index 61bceacd098..d827abd65d3 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -124,7 +124,7 @@ func (rv *RegionVindex) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ( } // Verify satisfies MultiColumn -func (rv *RegionVindex) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]bytes) ([]bool, error) { +func (rv *RegionVindex) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { result := make([]bool, len(rowsColValues)) destinations, _ := rv.Map(vcursor, rowsColValues) for i, dest := range destinations { From d9fe561a5106008212cf0240da8fda38e610512d Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Thu, 6 Feb 2020 16:34:04 -0500 Subject: [PATCH 103/825] Updated formatting. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_vindex.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_vindex.go index d827abd65d3..c1cb360dc06 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_vindex.go @@ -28,7 +28,7 @@ import ( ) var ( - _ MultiColumn = (*RegionVindex)(nil) + _ MultiColumn = (*RegionVindex)(nil) ) func init() { @@ -82,7 +82,7 @@ func (rv *RegionVindex) Cost() int { // IsUnique returns true since the Vindex is unique. func (rv *RegionVindex) IsUnique() bool { - return true; + return true } // Map satisfies MultiColumn. @@ -140,4 +140,4 @@ func (rv *RegionVindex) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value // NeedVCursor staisfies the Vindex interface. func (rv *RegionVindex) NeedsVCursor() bool { return false -} \ No newline at end of file +} From c73e8d4a9691f25840d3dd710e8f1b0e5d84b44e Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Fri, 7 Feb 2020 12:53:35 -0500 Subject: [PATCH 104/825] Renamed 'region_vindex' -> 'region_json'. Signed-off-by: Ryan Leonard --- .../{region_vindex.go => region_json.go} | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) rename go/vt/vtgate/vindexes/{region_vindex.go => region_json.go} (81%) diff --git a/go/vt/vtgate/vindexes/region_vindex.go b/go/vt/vtgate/vindexes/region_json.go similarity index 81% rename from go/vt/vtgate/vindexes/region_vindex.go rename to go/vt/vtgate/vindexes/region_json.go index c1cb360dc06..d6879d0dce7 100644 --- a/go/vt/vtgate/vindexes/region_vindex.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -28,30 +28,30 @@ import ( ) var ( - _ MultiColumn = (*RegionVindex)(nil) + _ MultiColumn = (*RegionJson)(nil) ) func init() { - Register("region_vindex", NewRegionVindex) + Register("region_json", NewRegionJson) } // RegionMap is used to store mapping of country to region type RegionMap map[string]uint64 -// RegionVindex defines a vindex that uses a lookup table. +// RegionJson defines a vindex that uses a lookup table. // The table is expected to define the id column as unique. It's // Unique and a Lookup. -type RegionVindex struct { +type RegionJson struct { name string regionMap RegionMap regionBytes int } -// NewRegionVindex creates a RegionVindex vindex. +// NewRegionJson creates a RegionJson vindex. // The supplied map requires all the fields of "region_experimental". // Additionally, it requires a region_map argument representing the path to a json file // containing a map of country to region. -func NewRegionVindex(name string, m map[string]string) (Vindex, error) { +func NewRegionJson(name string, m map[string]string) (Vindex, error) { rmPath := m["region_map"] rmap := make(map[string]uint64) data, err := ioutil.ReadFile(rmPath) @@ -64,29 +64,29 @@ func NewRegionVindex(name string, m map[string]string) (Vindex, error) { return nil, err } - return &RegionVindex{ + return &RegionJson{ name: name, regionMap: rmap, }, nil } // String returns the name of the vindex. -func (rv *RegionVindex) String() string { +func (rv *RegionJson) String() string { return rv.name } // Cost returns the cost of this index as 1. -func (rv *RegionVindex) Cost() int { +func (rv *RegionJson) Cost() int { return 1 } // IsUnique returns true since the Vindex is unique. -func (rv *RegionVindex) IsUnique() bool { +func (rv *RegionJson) IsUnique() bool { return true } // Map satisfies MultiColumn. -func (rv *RegionVindex) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { +func (rv *RegionJson) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { destinations := make([]key.Destination, 0, len(rowsColValues)) for _, row := range rowsColValues { if len(row) != 2 { @@ -124,7 +124,7 @@ func (rv *RegionVindex) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ( } // Verify satisfies MultiColumn -func (rv *RegionVindex) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { +func (rv *RegionJson) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { result := make([]bool, len(rowsColValues)) destinations, _ := rv.Map(vcursor, rowsColValues) for i, dest := range destinations { @@ -138,6 +138,6 @@ func (rv *RegionVindex) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value } // NeedVCursor staisfies the Vindex interface. -func (rv *RegionVindex) NeedsVCursor() bool { +func (rv *RegionJson) NeedsVCursor() bool { return false } From 57a37ef4ae5edcd9779522c7e2505b1b462cdff3 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Fri, 7 Feb 2020 13:44:07 -0500 Subject: [PATCH 105/825] Updated copyright, removed log messages. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index d6879d0dce7..9a64c4aa599 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Vitess Authors. +Copyright 2020 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -101,11 +101,6 @@ func (rv *RegionJson) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([] } h := vhash(hn) - log.Infof("Region map: %v", rv.regionMap) - // Compute region prefix. - log.Infof("Country: %v", row[1].ToString()) - rn, ok := rv.regionMap[row[1].ToString()] - log.Infof("Found region %v, true/false: %v", rn, ok) if !ok { destinations = append(destinations, key.DestinationNone{}) continue From 54189ea2bf56a5b7b1e0e5f1cc9e8b2e93f32424 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Fri, 7 Feb 2020 13:46:52 -0500 Subject: [PATCH 106/825] Added line of code accidentally deleted with logs. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index 9a64c4aa599..ac68325435a 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -101,6 +101,7 @@ func (rv *RegionJson) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([] } h := vhash(hn) + rn, ok := rv.regionMap[row[1].ToString()] if !ok { destinations = append(destinations, key.DestinationNone{}) continue From c8e7adc2f3524b23dec40a8f38ba28724de9089d Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Fri, 7 Feb 2020 14:26:36 -0500 Subject: [PATCH 107/825] Typo staisfies -> satisfies, nit: region_experimental -> RegionExperimental Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index ac68325435a..0a3e3e8854a 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -48,7 +48,7 @@ type RegionJson struct { } // NewRegionJson creates a RegionJson vindex. -// The supplied map requires all the fields of "region_experimental". +// The supplied map requires all the fields of "RegionExperimental". // Additionally, it requires a region_map argument representing the path to a json file // containing a map of country to region. func NewRegionJson(name string, m map[string]string) (Vindex, error) { @@ -133,7 +133,7 @@ func (rv *RegionJson) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, return result, nil } -// NeedVCursor staisfies the Vindex interface. +// NeedVCursor satisfies the Vindex interface. func (rv *RegionJson) NeedsVCursor() bool { return false } From df13dcbf8f2747761a03889ca3514aafb130a320 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Fri, 7 Feb 2020 15:13:33 -0500 Subject: [PATCH 108/825] Added 'RegionExperimental' wrapping back. Removed 'Verify' function. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 30 +++++++++++----------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index 0a3e3e8854a..f7dbba578db 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -42,9 +42,8 @@ type RegionMap map[string]uint64 // The table is expected to define the id column as unique. It's // Unique and a Lookup. type RegionJson struct { - name string regionMap RegionMap - regionBytes int + *RegionExperimental } // NewRegionJson creates a RegionJson vindex. @@ -64,9 +63,18 @@ func NewRegionJson(name string, m map[string]string) (Vindex, error) { return nil, err } + vindex, err := NewRegionExperimental(name, m) + if err != nil { + // Unreachable. + return nil, err + } + re := vindex.(*RegionExperimental) + if len(re.ConsistentLookupUnique.lkp.FromColumns) != 2 { + return nil, fmt.Errorf("two columns are required for region_experimental: %v", re.ConsistentLookupUnique.lkp.FromColumns) + } return &RegionJson{ - name: name, - regionMap: rmap, + regionMap: rmap, + RegionExperimental: re, }, nil } @@ -119,20 +127,6 @@ func (rv *RegionJson) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([] return destinations, nil } -// Verify satisfies MultiColumn -func (rv *RegionJson) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { - result := make([]bool, len(rowsColValues)) - destinations, _ := rv.Map(vcursor, rowsColValues) - for i, dest := range destinations { - destksid, ok := dest.(key.DestinationKeyspaceID) - if !ok { - continue - } - result[i] = bytes.Equal([]byte(destksid), ksids[i]) - } - return result, nil -} - // NeedVCursor satisfies the Vindex interface. func (rv *RegionJson) NeedsVCursor() bool { return false From 392c92a59b803426f55c77ff6a402580b1d41686 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Fri, 7 Feb 2020 15:16:04 -0500 Subject: [PATCH 109/825] Fixed formatting. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index f7dbba578db..f5969394344 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -42,7 +42,7 @@ type RegionMap map[string]uint64 // The table is expected to define the id column as unique. It's // Unique and a Lookup. type RegionJson struct { - regionMap RegionMap + regionMap RegionMap *RegionExperimental } From 57a2badfaf2aff5dbb23f1dcee1c29877566fcc7 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Fri, 7 Feb 2020 15:16:59 -0500 Subject: [PATCH 110/825] Removed unused import. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 1 - 1 file changed, 1 deletion(-) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index f5969394344..b9cc1668730 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -17,7 +17,6 @@ limitations under the License. package vindexes import ( - "bytes" "encoding/binary" "encoding/json" "io/ioutil" From 4fb769735c7d63aa3b05785a55ddfef48c8cb9f3 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Mon, 10 Feb 2020 11:12:38 -0500 Subject: [PATCH 111/825] Revert "Added 'RegionExperimental' wrapping back. Removed 'Verify' function." This reverts commit da1853d9a1ac0da9aec2c47884b80b8d57a53c2d. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index b9cc1668730..d73b8786584 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -41,8 +41,9 @@ type RegionMap map[string]uint64 // The table is expected to define the id column as unique. It's // Unique and a Lookup. type RegionJson struct { - regionMap RegionMap - *RegionExperimental + name string + regionMap RegionMap + regionBytes int } // NewRegionJson creates a RegionJson vindex. @@ -62,18 +63,9 @@ func NewRegionJson(name string, m map[string]string) (Vindex, error) { return nil, err } - vindex, err := NewRegionExperimental(name, m) - if err != nil { - // Unreachable. - return nil, err - } - re := vindex.(*RegionExperimental) - if len(re.ConsistentLookupUnique.lkp.FromColumns) != 2 { - return nil, fmt.Errorf("two columns are required for region_experimental: %v", re.ConsistentLookupUnique.lkp.FromColumns) - } return &RegionJson{ - regionMap: rmap, - RegionExperimental: re, + name: name, + regionMap: rmap, }, nil } @@ -126,6 +118,20 @@ func (rv *RegionJson) Map(vcursor VCursor, rowsColValues [][]sqltypes.Value) ([] return destinations, nil } +// Verify satisfies MultiColumn +func (rv *RegionJson) Verify(vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { + result := make([]bool, len(rowsColValues)) + destinations, _ := rv.Map(vcursor, rowsColValues) + for i, dest := range destinations { + destksid, ok := dest.(key.DestinationKeyspaceID) + if !ok { + continue + } + result[i] = bytes.Equal([]byte(destksid), ksids[i]) + } + return result, nil +} + // NeedVCursor satisfies the Vindex interface. func (rv *RegionJson) NeedsVCursor() bool { return false From 23fe9635f169f648bd47bf62f832b7bccca862b9 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Mon, 10 Feb 2020 11:45:56 -0500 Subject: [PATCH 112/825] Added bytes back in. Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index d73b8786584..0a3e3e8854a 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -17,6 +17,7 @@ limitations under the License. package vindexes import ( + "bytes" "encoding/binary" "encoding/json" "io/ioutil" From 980492615b1f05bfe59b2132acfe5bb56e380c49 Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Mon, 10 Feb 2020 11:32:58 -0800 Subject: [PATCH 113/825] Fix IndexHints node formatter Signed-off-by: Saif Alharthi --- go/vt/sqlparser/ast.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 7f30c6a1984..31750b0a5fb 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -1372,12 +1372,16 @@ func (node *JoinTableExpr) Format(buf *TrackedBuffer) { // Format formats the node. func (node *IndexHints) Format(buf *TrackedBuffer) { buf.Myprintf(" %sindex ", node.Type) - prefix := "(" - for _, n := range node.Indexes { - buf.Myprintf("%s%v", prefix, n) - prefix = ", " + if len(node.Indexes) == 0 { + buf.Myprintf("()") + } else { + prefix := "(" + for _, n := range node.Indexes { + buf.Myprintf("%s%v", prefix, n) + prefix = ", " + } + buf.Myprintf(")") } - buf.Myprintf(")") } // Format formats the node. From 40a835632de8208a7f8a76f98337cda3a11c3515 Mon Sep 17 00:00:00 2001 From: Anthony Yeh Date: Wed, 12 Feb 2020 16:11:07 -0800 Subject: [PATCH 114/825] docker: Add sysstat and strace to vitess/lite. These are useful for diagnostics on running containers. Signed-off-by: Anthony Yeh --- docker/lite/install_dependencies.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker/lite/install_dependencies.sh b/docker/lite/install_dependencies.sh index 60c66642bdd..986d80df05d 100755 --- a/docker/lite/install_dependencies.sh +++ b/docker/lite/install_dependencies.sh @@ -31,6 +31,8 @@ BASE_PACKAGES=( libtcmalloc-minimal4 procps rsync + strace + sysstat wget ) @@ -172,4 +174,4 @@ apt-get install -y --no-install-recommends "${PACKAGES[@]}" # Clean up files we won't need in the final image. rm -rf /var/lib/apt/lists/* - rm -rf /var/lib/mysql/ \ No newline at end of file + rm -rf /var/lib/mysql/ From 5d172c4c2dc9a670ccc77430a4564236d5288ff9 Mon Sep 17 00:00:00 2001 From: Lloyd Cabancla Date: Thu, 16 Jan 2020 16:45:24 -0500 Subject: [PATCH 115/825] Added resource pool wait time histogram metrics Signed-off-by: Lloyd Cabancla --- go/pools/resource_pool.go | 11 ++++- go/pools/resource_pool_flaky_test.go | 29 ++++++++------ go/vt/dbconnpool/connection_pool.go | 5 ++- go/vt/vttablet/endtoend/misc_test.go | 4 -- go/vt/vttablet/endtoend/transaction_test.go | 42 ++++++++++++++++++++ go/vt/vttablet/tabletserver/connpool/pool.go | 2 +- 6 files changed, 72 insertions(+), 21 deletions(-) diff --git a/go/pools/resource_pool.go b/go/pools/resource_pool.go index 63b8309137b..60ee91bf993 100644 --- a/go/pools/resource_pool.go +++ b/go/pools/resource_pool.go @@ -24,6 +24,8 @@ import ( "sync" "time" + "vitess.io/vitess/go/stats" + "golang.org/x/net/context" "vitess.io/vitess/go/sync2" @@ -71,6 +73,8 @@ type ResourcePool struct { resources chan resourceWrapper factory Factory idleTimer *timer.Timer + waitStats *stats.Timings + name string } type resourceWrapper struct { @@ -89,16 +93,18 @@ type resourceWrapper struct { // An idleTimeout of 0 means that there is no timeout. // A non-zero value of prefillParallelism causes the pool to be pre-filled. // The value specifies how many resources can be opened in parallel. -func NewResourcePool(factory Factory, capacity, maxCap int, idleTimeout time.Duration, prefillParallelism int) *ResourcePool { +func NewResourcePool(name string, factory Factory, capacity, maxCap int, idleTimeout time.Duration, prefillParallelism int, waitStats *stats.Timings) *ResourcePool { if capacity <= 0 || maxCap <= 0 || capacity > maxCap { panic(errors.New("invalid/out of range capacity")) } rp := &ResourcePool{ + name: name, resources: make(chan resourceWrapper, maxCap), factory: factory, available: sync2.NewAtomicInt64(int64(capacity)), capacity: sync2.NewAtomicInt64(int64(capacity)), idleTimeout: sync2.NewAtomicDuration(idleTimeout), + waitStats: waitStats, } for i := 0; i < capacity; i++ { rp.resources <- resourceWrapper{} @@ -325,6 +331,9 @@ func (rp *ResourcePool) SetCapacity(capacity int) error { func (rp *ResourcePool) recordWait(start time.Time) { rp.waitCount.Add(1) rp.waitTime.Add(time.Since(start)) + if rp.name != "" { + rp.waitStats.Record(rp.name+"ResourceWaitTime", start) + } } // SetIdleTimeout sets the idle timeout. It can only be used if there was an diff --git a/go/pools/resource_pool_flaky_test.go b/go/pools/resource_pool_flaky_test.go index f3950e5e23e..851ae5633a3 100644 --- a/go/pools/resource_pool_flaky_test.go +++ b/go/pools/resource_pool_flaky_test.go @@ -21,11 +21,14 @@ import ( "testing" "time" + "vitess.io/vitess/go/stats" + "golang.org/x/net/context" "vitess.io/vitess/go/sync2" ) var lastID, count sync2.AtomicInt64 +var waitTimes = stats.NewTimings("", "", "") type TestResource struct { num int64 @@ -57,7 +60,7 @@ func TestOpen(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 6, 6, time.Second, 0) + p := NewResourcePool("", PoolFactory, 6, 6, time.Second, 0, waitTimes) p.SetCapacity(5) var resources [10]Resource @@ -198,12 +201,12 @@ func TestOpen(t *testing.T) { func TestPrefill(t *testing.T) { lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 5, 5, time.Second, 1) + p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 1, waitTimes) defer p.Close() if p.Active() != 5 { t.Errorf("p.Active(): %d, want 5", p.Active()) } - p = NewResourcePool(FailFactory, 5, 5, time.Second, 1) + p = NewResourcePool("", FailFactory, 5, 5, time.Second, 1, waitTimes) defer p.Close() if p.Active() != 0 { t.Errorf("p.Active(): %d, want 0", p.Active()) @@ -218,7 +221,7 @@ func TestPrefillTimeout(t *testing.T) { defer func() { prefillTimeout = saveTimeout }() start := time.Now() - p := NewResourcePool(SlowFailFactory, 5, 5, time.Second, 1) + p := NewResourcePool("", SlowFailFactory, 5, 5, time.Second, 1, waitTimes) defer p.Close() if elapsed := time.Since(start); elapsed > 20*time.Millisecond { t.Errorf("elapsed: %v, should be around 10ms", elapsed) @@ -232,7 +235,7 @@ func TestShrinking(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0) + p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 0, waitTimes) var resources [10]Resource // Leave one empty slot in the pool for i := 0; i < 4; i++ { @@ -371,7 +374,7 @@ func TestClosing(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0) + p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 0, waitTimes) var resources [10]Resource for i := 0; i < 5; i++ { r, err := p.Get(ctx) @@ -425,7 +428,7 @@ func TestIdleTimeout(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0) + p := NewResourcePool("", PoolFactory, 1, 1, 10*time.Millisecond, 0, waitTimes) defer p.Close() r, err := p.Get(ctx) @@ -536,7 +539,7 @@ func TestIdleTimeoutCreateFail(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0) + p := NewResourcePool("", PoolFactory, 1, 1, 10*time.Millisecond, 0, waitTimes) defer p.Close() r, err := p.Get(ctx) if err != nil { @@ -557,7 +560,7 @@ func TestCreateFail(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(FailFactory, 5, 5, time.Second, 0) + p := NewResourcePool("", FailFactory, 5, 5, time.Second, 0, waitTimes) defer p.Close() if _, err := p.Get(ctx); err.Error() != "Failed" { t.Errorf("Expecting Failed, received %v", err) @@ -573,7 +576,7 @@ func TestCreateFailOnPut(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0) + p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 0, waitTimes) defer p.Close() _, err := p.Get(ctx) if err != nil { @@ -590,7 +593,7 @@ func TestSlowCreateFail(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(SlowFailFactory, 2, 2, time.Second, 0) + p := NewResourcePool("", SlowFailFactory, 2, 2, time.Second, 0, waitTimes) defer p.Close() ch := make(chan bool) // The third Get should not wait indefinitely @@ -612,7 +615,7 @@ func TestTimeout(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0) + p := NewResourcePool("", PoolFactory, 1, 1, time.Second, 0, waitTimes) defer p.Close() r, err := p.Get(ctx) if err != nil { @@ -631,7 +634,7 @@ func TestTimeout(t *testing.T) { func TestExpired(t *testing.T) { lastID.Set(0) count.Set(0) - p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0) + p := NewResourcePool("", PoolFactory, 1, 1, time.Second, 0, waitTimes) defer p.Close() ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second)) r, err := p.Get(ctx) diff --git a/go/vt/dbconnpool/connection_pool.go b/go/vt/dbconnpool/connection_pool.go index 472f4942cde..876694571a9 100644 --- a/go/vt/dbconnpool/connection_pool.go +++ b/go/vt/dbconnpool/connection_pool.go @@ -65,12 +65,13 @@ type ConnectionPool struct { hostIsNotIP bool mysqlStats *stats.Timings + name string } // NewConnectionPool creates a new ConnectionPool. The name is used // to publish stats only. func NewConnectionPool(name string, capacity int, idleTimeout time.Duration, dnsResolutionFrequency time.Duration) *ConnectionPool { - cp := &ConnectionPool{capacity: capacity, idleTimeout: idleTimeout, resolutionFrequency: dnsResolutionFrequency} + cp := &ConnectionPool{name: name, capacity: capacity, idleTimeout: idleTimeout, resolutionFrequency: dnsResolutionFrequency} if name == "" || usedNames[name] { return cp } @@ -146,7 +147,7 @@ func (cp *ConnectionPool) Open(info *mysql.ConnParams, mysqlStats *stats.Timings defer cp.mu.Unlock() cp.info = info cp.mysqlStats = mysqlStats - cp.connections = pools.NewResourcePool(cp.connect, cp.capacity, cp.capacity, cp.idleTimeout, 0) + cp.connections = pools.NewResourcePool(cp.name, cp.connect, cp.capacity, cp.capacity, cp.idleTimeout, 0, mysqlStats) // Check if we need to resolve a hostname (The Host is not just an IP address). if cp.resolutionFrequency > 0 && net.ParseIP(info.Host) == nil { cp.hostIsNotIP = true diff --git a/go/vt/vttablet/endtoend/misc_test.go b/go/vt/vttablet/endtoend/misc_test.go index eac102015d5..f463514f150 100644 --- a/go/vt/vttablet/endtoend/misc_test.go +++ b/go/vt/vttablet/endtoend/misc_test.go @@ -296,10 +296,6 @@ func TestConsolidation(t *testing.T) { wg.Wait() vend := framework.DebugVars() - if err := compareIntDiff(vend, "Waits/TotalCount", vstart, 1); err != nil { - t.Logf("DebugVars Waits/TotalCount not incremented with sleep=%v", sleep) - continue - } if err := compareIntDiff(vend, "Waits/Histograms/Consolidations/Count", vstart, 1); err != nil { t.Logf("DebugVars Waits/Histograms/Consolidations/Count not incremented with sleep=%v", sleep) continue diff --git a/go/vt/vttablet/endtoend/transaction_test.go b/go/vt/vttablet/endtoend/transaction_test.go index eecb7d74526..63e4444edd1 100644 --- a/go/vt/vttablet/endtoend/transaction_test.go +++ b/go/vt/vttablet/endtoend/transaction_test.go @@ -20,6 +20,7 @@ import ( "fmt" "reflect" "strings" + "sync" "testing" "time" @@ -823,3 +824,44 @@ func TestManualTwopcz(t *testing.T) { fmt.Print("Sleeping for 30 seconds\n") time.Sleep(30 * time.Second) } + +func TestTransactionPoolResourceWaitTime(t *testing.T) { + defer framework.Server.SetPoolSize(framework.Server.TxPoolSize()) + defer framework.Server.SetTxPoolTimeout(framework.Server.TxPoolTimeout()) + framework.Server.SetTxPoolSize(1) + framework.Server.SetTxPoolTimeout(10 * time.Second) + debugVarPath := "Waits/Histograms/TransactionPoolResourceWaitTime/Count" + + for sleep := 0.1; sleep < 10.0; sleep *= 2 { + vstart := framework.DebugVars() + var wg sync.WaitGroup + wg.Add(2) + + transactionFunc := func() { + client := framework.NewClient() + + bv := map[string]*querypb.BindVariable{} + query := fmt.Sprintf("select sleep(%v) from dual", sleep) + if _, err := client.BeginExecute(query, bv); err != nil { + t.Error(err) + return + } + if err := client.Rollback(); err != nil { + t.Error(err) + return + } + wg.Done() + } + go transactionFunc() + go transactionFunc() + wg.Wait() + vend := framework.DebugVars() + if err := compareIntDiff(vend, debugVarPath, vstart, 1); err != nil { + t.Logf("DebugVars %v not incremented with sleep=%v", debugVarPath, sleep) + continue + } + t.Logf("DebugVars %v properly incremented with sleep=%v", debugVarPath, sleep) + return + } + t.Errorf("DebugVars %v not incremented", debugVarPath) +} diff --git a/go/vt/vttablet/tabletserver/connpool/pool.go b/go/vt/vttablet/tabletserver/connpool/pool.go index 1c32929e91d..eedb63531f3 100644 --- a/go/vt/vttablet/tabletserver/connpool/pool.go +++ b/go/vt/vttablet/tabletserver/connpool/pool.go @@ -122,7 +122,7 @@ func (cp *Pool) Open(appParams, dbaParams, appDebugParams *mysql.ConnParams) { f := func() (pools.Resource, error) { return NewDBConn(cp, appParams) } - cp.connections = pools.NewResourcePool(f, cp.capacity, cp.capacity, cp.idleTimeout, cp.prefillParallelism) + cp.connections = pools.NewResourcePool(cp.name, f, cp.capacity, cp.capacity, cp.idleTimeout, cp.prefillParallelism, tabletenv.WaitStats) cp.appDebugParams = appDebugParams cp.dbaPool.Open(dbaParams, tabletenv.MySQLStats) From 6b8b6b460e61c2a8ca07e9cb56b3c2461df5e676 Mon Sep 17 00:00:00 2001 From: Lloyd Cabancla Date: Thu, 13 Feb 2020 14:22:22 -0500 Subject: [PATCH 116/825] Used callback to log resource pool wait time Signed-off-by: Lloyd Cabancla --- go/pools/resource_pool.go | 14 ++---- go/pools/resource_pool_flaky_test.go | 53 ++++++++++++++------ go/vt/dbconnpool/connection_pool.go | 11 +++- go/vt/vttablet/tabletserver/connpool/pool.go | 11 +++- 4 files changed, 61 insertions(+), 28 deletions(-) diff --git a/go/pools/resource_pool.go b/go/pools/resource_pool.go index 60ee91bf993..d3459b06d53 100644 --- a/go/pools/resource_pool.go +++ b/go/pools/resource_pool.go @@ -24,8 +24,6 @@ import ( "sync" "time" - "vitess.io/vitess/go/stats" - "golang.org/x/net/context" "vitess.io/vitess/go/sync2" @@ -73,8 +71,7 @@ type ResourcePool struct { resources chan resourceWrapper factory Factory idleTimer *timer.Timer - waitStats *stats.Timings - name string + logWait func(time.Time) } type resourceWrapper struct { @@ -93,18 +90,17 @@ type resourceWrapper struct { // An idleTimeout of 0 means that there is no timeout. // A non-zero value of prefillParallelism causes the pool to be pre-filled. // The value specifies how many resources can be opened in parallel. -func NewResourcePool(name string, factory Factory, capacity, maxCap int, idleTimeout time.Duration, prefillParallelism int, waitStats *stats.Timings) *ResourcePool { +func NewResourcePool(factory Factory, capacity, maxCap int, idleTimeout time.Duration, prefillParallelism int, logWait func(time.Time)) *ResourcePool { if capacity <= 0 || maxCap <= 0 || capacity > maxCap { panic(errors.New("invalid/out of range capacity")) } rp := &ResourcePool{ - name: name, resources: make(chan resourceWrapper, maxCap), factory: factory, available: sync2.NewAtomicInt64(int64(capacity)), capacity: sync2.NewAtomicInt64(int64(capacity)), idleTimeout: sync2.NewAtomicDuration(idleTimeout), - waitStats: waitStats, + logWait: logWait, } for i := 0; i < capacity; i++ { rp.resources <- resourceWrapper{} @@ -331,9 +327,7 @@ func (rp *ResourcePool) SetCapacity(capacity int) error { func (rp *ResourcePool) recordWait(start time.Time) { rp.waitCount.Add(1) rp.waitTime.Add(time.Since(start)) - if rp.name != "" { - rp.waitStats.Record(rp.name+"ResourceWaitTime", start) - } + rp.logWait(start) } // SetIdleTimeout sets the idle timeout. It can only be used if there was an diff --git a/go/pools/resource_pool_flaky_test.go b/go/pools/resource_pool_flaky_test.go index 851ae5633a3..eeb3f8eda9a 100644 --- a/go/pools/resource_pool_flaky_test.go +++ b/go/pools/resource_pool_flaky_test.go @@ -21,14 +21,12 @@ import ( "testing" "time" - "vitess.io/vitess/go/stats" - "golang.org/x/net/context" "vitess.io/vitess/go/sync2" ) var lastID, count sync2.AtomicInt64 -var waitTimes = stats.NewTimings("", "", "") +var waitStarts []time.Time type TestResource struct { num int64 @@ -42,6 +40,10 @@ func (tr *TestResource) Close() { } } +func logWait(start time.Time) { + waitStarts = append(waitStarts, start) +} + func PoolFactory() (Resource, error) { count.Add(1) return &TestResource{lastID.Add(1), false}, nil @@ -60,7 +62,9 @@ func TestOpen(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 6, 6, time.Second, 0, waitTimes) + waitStarts = waitStarts[:0] + + p := NewResourcePool(PoolFactory, 6, 6, time.Second, 0, logWait) p.SetCapacity(5) var resources [10]Resource @@ -77,6 +81,9 @@ func TestOpen(t *testing.T) { if p.WaitCount() != 0 { t.Errorf("expecting 0, received %d", p.WaitCount()) } + if len(waitStarts) != 0 { + t.Errorf("expecting 0, received %d", len(waitStarts)) + } if p.WaitTime() != 0 { t.Errorf("expecting 0, received %d", p.WaitTime()) } @@ -112,6 +119,15 @@ func TestOpen(t *testing.T) { if p.WaitCount() != 5 { t.Errorf("Expecting 5, received %d", p.WaitCount()) } + if int64(len(waitStarts)) != p.WaitCount() { + t.Errorf("expecting %d, received %d", p.WaitCount(), len(waitStarts)) + } + // verify start times are monotonic increasing + for i := 1; i < len(waitStarts); i++ { + if waitStarts[i].Before(waitStarts[i-1]) { + t.Errorf("Expecting monotonic increasing start times") + } + } if p.WaitTime() == 0 { t.Errorf("Expecting non-zero") } @@ -201,12 +217,12 @@ func TestOpen(t *testing.T) { func TestPrefill(t *testing.T) { lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 1, waitTimes) + p := NewResourcePool(PoolFactory, 5, 5, time.Second, 1, logWait) defer p.Close() if p.Active() != 5 { t.Errorf("p.Active(): %d, want 5", p.Active()) } - p = NewResourcePool("", FailFactory, 5, 5, time.Second, 1, waitTimes) + p = NewResourcePool(FailFactory, 5, 5, time.Second, 1, logWait) defer p.Close() if p.Active() != 0 { t.Errorf("p.Active(): %d, want 0", p.Active()) @@ -221,7 +237,7 @@ func TestPrefillTimeout(t *testing.T) { defer func() { prefillTimeout = saveTimeout }() start := time.Now() - p := NewResourcePool("", SlowFailFactory, 5, 5, time.Second, 1, waitTimes) + p := NewResourcePool(SlowFailFactory, 5, 5, time.Second, 1, logWait) defer p.Close() if elapsed := time.Since(start); elapsed > 20*time.Millisecond { t.Errorf("elapsed: %v, should be around 10ms", elapsed) @@ -235,7 +251,9 @@ func TestShrinking(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 0, waitTimes) + waitStarts = waitStarts[:0] + + p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait) var resources [10]Resource // Leave one empty slot in the pool for i := 0; i < 4; i++ { @@ -318,6 +336,9 @@ func TestShrinking(t *testing.T) { if p.WaitCount() != 1 { t.Errorf("Expecting 1, received %d", p.WaitCount()) } + if int64(len(waitStarts)) != p.WaitCount() { + t.Errorf("Expecting %d, received %d", p.WaitCount(), len(waitStarts)) + } if count.Get() != 2 { t.Errorf("Expecting 2, received %d", count.Get()) } @@ -374,7 +395,7 @@ func TestClosing(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 0, waitTimes) + p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait) var resources [10]Resource for i := 0; i < 5; i++ { r, err := p.Get(ctx) @@ -428,7 +449,7 @@ func TestIdleTimeout(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 1, 1, 10*time.Millisecond, 0, waitTimes) + p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0, logWait) defer p.Close() r, err := p.Get(ctx) @@ -539,7 +560,7 @@ func TestIdleTimeoutCreateFail(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 1, 1, 10*time.Millisecond, 0, waitTimes) + p := NewResourcePool(PoolFactory, 1, 1, 10*time.Millisecond, 0, logWait) defer p.Close() r, err := p.Get(ctx) if err != nil { @@ -560,7 +581,7 @@ func TestCreateFail(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", FailFactory, 5, 5, time.Second, 0, waitTimes) + p := NewResourcePool(FailFactory, 5, 5, time.Second, 0, logWait) defer p.Close() if _, err := p.Get(ctx); err.Error() != "Failed" { t.Errorf("Expecting Failed, received %v", err) @@ -576,7 +597,7 @@ func TestCreateFailOnPut(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 5, 5, time.Second, 0, waitTimes) + p := NewResourcePool(PoolFactory, 5, 5, time.Second, 0, logWait) defer p.Close() _, err := p.Get(ctx) if err != nil { @@ -593,7 +614,7 @@ func TestSlowCreateFail(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", SlowFailFactory, 2, 2, time.Second, 0, waitTimes) + p := NewResourcePool(SlowFailFactory, 2, 2, time.Second, 0, logWait) defer p.Close() ch := make(chan bool) // The third Get should not wait indefinitely @@ -615,7 +636,7 @@ func TestTimeout(t *testing.T) { ctx := context.Background() lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 1, 1, time.Second, 0, waitTimes) + p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0, logWait) defer p.Close() r, err := p.Get(ctx) if err != nil { @@ -634,7 +655,7 @@ func TestTimeout(t *testing.T) { func TestExpired(t *testing.T) { lastID.Set(0) count.Set(0) - p := NewResourcePool("", PoolFactory, 1, 1, time.Second, 0, waitTimes) + p := NewResourcePool(PoolFactory, 1, 1, time.Second, 0, logWait) defer p.Close() ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second)) r, err := p.Get(ctx) diff --git a/go/vt/dbconnpool/connection_pool.go b/go/vt/dbconnpool/connection_pool.go index 876694571a9..6a22e561d6e 100644 --- a/go/vt/dbconnpool/connection_pool.go +++ b/go/vt/dbconnpool/connection_pool.go @@ -147,7 +147,7 @@ func (cp *ConnectionPool) Open(info *mysql.ConnParams, mysqlStats *stats.Timings defer cp.mu.Unlock() cp.info = info cp.mysqlStats = mysqlStats - cp.connections = pools.NewResourcePool(cp.name, cp.connect, cp.capacity, cp.capacity, cp.idleTimeout, 0, mysqlStats) + cp.connections = pools.NewResourcePool(cp.connect, cp.capacity, cp.capacity, cp.idleTimeout, 0, cp.getLogWaitCallback()) // Check if we need to resolve a hostname (The Host is not just an IP address). if cp.resolutionFrequency > 0 && net.ParseIP(info.Host) == nil { cp.hostIsNotIP = true @@ -169,6 +169,15 @@ func (cp *ConnectionPool) Open(info *mysql.ConnParams, mysqlStats *stats.Timings } } +func (cp *ConnectionPool) getLogWaitCallback() func(time.Time) { + if cp.name == "" { + return func(start time.Time) {} // no op + } + return func(start time.Time) { + cp.mysqlStats.Record(cp.name+"ResourceWaitTime", start) + } +} + // connect is used by the resource pool to create a new Resource. func (cp *ConnectionPool) connect() (pools.Resource, error) { c, err := NewDBConnection(cp.info, cp.mysqlStats) diff --git a/go/vt/vttablet/tabletserver/connpool/pool.go b/go/vt/vttablet/tabletserver/connpool/pool.go index eedb63531f3..516bf09b8e8 100644 --- a/go/vt/vttablet/tabletserver/connpool/pool.go +++ b/go/vt/vttablet/tabletserver/connpool/pool.go @@ -122,12 +122,21 @@ func (cp *Pool) Open(appParams, dbaParams, appDebugParams *mysql.ConnParams) { f := func() (pools.Resource, error) { return NewDBConn(cp, appParams) } - cp.connections = pools.NewResourcePool(cp.name, f, cp.capacity, cp.capacity, cp.idleTimeout, cp.prefillParallelism, tabletenv.WaitStats) + cp.connections = pools.NewResourcePool(f, cp.capacity, cp.capacity, cp.idleTimeout, cp.prefillParallelism, cp.getLogWaitCallback()) cp.appDebugParams = appDebugParams cp.dbaPool.Open(dbaParams, tabletenv.MySQLStats) } +func (cp *Pool) getLogWaitCallback() func(time.Time) { + if cp.name == "" { + return func(start time.Time) {} // no op + } + return func(start time.Time) { + tabletenv.WaitStats.Record(cp.name+"ResourceWaitTime", start) + } +} + // Close will close the pool and wait for connections to be returned before // exiting. func (cp *Pool) Close() { From edad6f8e0049674dc88aa8c1488b7502de7e8614 Mon Sep 17 00:00:00 2001 From: Jacques Grove Date: Thu, 13 Feb 2020 23:24:27 -0800 Subject: [PATCH 117/825] Add credentials plugin lookup for vstreamer master position lookup. Signed-off-by: Jacques Grove --- go/vt/vttablet/tabletserver/vstreamer/vstreamer.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index cc482dc5e21..3f6d6ede2c1 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/sync2" "vitess.io/vitess/go/vt/binlog" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" @@ -163,7 +164,11 @@ func (vs *vstreamer) Stream() error { } func (vs *vstreamer) currentPosition() (mysql.Position, error) { - conn, err := mysql.Connect(vs.ctx, vs.cp) + cp, err := dbconfigs.WithCredentials(vs.cp) + if err != nil { + return mysql.Position{}, err + } + conn, err := mysql.Connect(vs.ctx, cp) if err != nil { return mysql.Position{}, err } From 308b4eca7c5f0fc944cec01a09a08ca4c60f60ff Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 14 Feb 2020 05:46:06 -0800 Subject: [PATCH 118/825] stats: drop variables: ability to drop variables It turns out that combining dimensions doesn't address all use cases. Gauges are also susceptible to cause huge /debug/vars. This additional feature allows you to drop specific variables from being exported. Ideally, they should also not be tracked. But we can iterate on this improvement later. Signed-off-by: Sugu Sougoumarane --- go/stats/export.go | 28 +++++++++++++++++++++++++--- go/stats/export_test.go | 11 +++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/go/stats/export.go b/go/stats/export.go index ac4416adf58..b61c7278118 100644 --- a/go/stats/export.go +++ b/go/stats/export.go @@ -44,6 +44,7 @@ var emitStats = flag.Bool("emit_stats", false, "true iff we should emit stats to var statsEmitPeriod = flag.Duration("stats_emit_period", time.Duration(60*time.Second), "Interval between emitting stats to all registered backends") var statsBackend = flag.String("stats_backend", "", "The name of the registered push-based monitoring/stats backend to use") var combineDimensions = flag.String("stats_combine_dimensions", "", `List of dimensions to be combined into a single "all" value in exported stats vars`) +var dropVariables = flag.String("stats_drop_variables", "", `Variables to be dropped from the list of exported variables.`) // StatsAllStr is the consolidated name if a dimension gets combined. const StatsAllStr = "all" @@ -76,6 +77,9 @@ func (vg *varGroup) register(nvh NewVarHook) { } func (vg *varGroup) publish(name string, v expvar.Var) { + if isVarDropped(name) { + return + } vg.Lock() defer vg.Unlock() @@ -254,14 +258,15 @@ func stringMapToString(m map[string]string) string { } var ( - dimMu sync.Mutex + varsMu sync.Mutex combinedDimensions map[string]bool + droppedVars map[string]bool ) // IsDimensionCombined returns true if the specified dimension should be combined. func IsDimensionCombined(name string) bool { - dimMu.Lock() - defer dimMu.Unlock() + varsMu.Lock() + defer varsMu.Unlock() if combinedDimensions == nil { dims := strings.Split(*combineDimensions, ",") @@ -295,3 +300,20 @@ func safeJoinLabels(labels []string, combinedLabels []bool) string { func safeLabel(label string) string { return strings.Replace(label, ".", "_", -1) } + +func isVarDropped(name string) bool { + varsMu.Lock() + defer varsMu.Unlock() + + if droppedVars == nil { + dims := strings.Split(*dropVariables, ",") + droppedVars = make(map[string]bool, len(dims)) + for _, dim := range dims { + if dim == "" { + continue + } + droppedVars[dim] = true + } + } + return droppedVars[name] +} diff --git a/go/stats/export_test.go b/go/stats/export_test.go index b12939cd5c7..a765af1ee50 100644 --- a/go/stats/export_test.go +++ b/go/stats/export_test.go @@ -25,7 +25,9 @@ func clear() { defaultVarGroup.vars = make(map[string]expvar.Var) defaultVarGroup.newVarHook = nil *combineDimensions = "" + *dropVariables = "" combinedDimensions = nil + droppedVars = nil } func TestNoHook(t *testing.T) { @@ -118,3 +120,12 @@ func TestPublishFunc(t *testing.T) { t.Errorf("want %v, got %#v", f(), gotv()) } } + +func TestDropVariable(t *testing.T) { + clear() + *dropVariables = "dropTest" + + // This should not panic. + _ = NewGaugesWithSingleLabel("dropTest", "help", "label") + _ = NewGaugesWithSingleLabel("dropTest", "help", "label") +} From 64437c87aa6aeea39db162606528f33d47a39018 Mon Sep 17 00:00:00 2001 From: Kim Bao Long Date: Tue, 18 Feb 2020 10:00:02 +0700 Subject: [PATCH 119/825] Replacing 'HTTP' by 'HTTPS' for securing links Currently, when we access github.com with HTTP, it is redirected to HTTPS automatically. So this commit aims to replace http://github.com by https://github.com for security. Signed-off-by: Kim Bao Long --- go/vt/mysqlctl/metadata_tables.go | 2 +- java/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/mysqlctl/metadata_tables.go b/go/vt/mysqlctl/metadata_tables.go index 4df32691eac..699bc57ee4e 100644 --- a/go/vt/mysqlctl/metadata_tables.go +++ b/go/vt/mysqlctl/metadata_tables.go @@ -61,7 +61,7 @@ var ( // a per-tablet table that is never replicated. This allows queries // against local_metadata to return different values on different tablets, // which is used for communicating between Vitess and MySQL-level tools like -// Orchestrator (http://github.com/github/orchestrator). +// Orchestrator (https://github.com/github/orchestrator). // _vt.shard_metadata is a replicated table with per-shard information, but it's // created here to make it easier to create it on databases that were running // old version of Vitess, or databases that are getting converted to run under diff --git a/java/pom.xml b/java/pom.xml index e580d47bfc5..6e397c03999 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -52,7 +52,7 @@ scm:git:git@github.com:vitessio/vitess.git scm:git:git@github.com:vitessio/vitess.git - http://github.com/vitessio/vitess/tree/master + https://github.com/vitessio/vitess/tree/master GitHub From cc4617d10a90ed56c9948985d75763cd28e2da49 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Mon, 17 Feb 2020 21:45:33 +0530 Subject: [PATCH 120/825] Initial changes to update in plan and engine Signed-off-by: Harshit Gangal --- go/vt/vtgate/engine/route.go | 10 +- go/vt/vtgate/engine/update.go | 33 + go/vt/vtgate/planbuilder/plan_test.go | 20 +- .../vtgate/planbuilder/testdata/dml_cases.txt | 1826 +---------------- go/vt/vtgate/planbuilder/update.go | 20 +- 5 files changed, 73 insertions(+), 1836 deletions(-) diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go index 9d5b45471b2..f9d2ce5ebd0 100644 --- a/go/vt/vtgate/engine/route.go +++ b/go/vt/vtgate/engine/route.go @@ -385,7 +385,7 @@ func (route *Route) paramsSelectEqual(vcursor VCursor, bindVars map[string]*quer if err != nil { return nil, nil, vterrors.Wrap(err, "paramsSelectEqual") } - rss, _, err := route.resolveShards(vcursor, []sqltypes.Value{key}) + rss, _, err := resolveShards(vcursor, route.Vindex, route.Keyspace, []sqltypes.Value{key}) if err != nil { return nil, nil, vterrors.Wrap(err, "paramsSelectEqual") } @@ -401,14 +401,14 @@ func (route *Route) paramsSelectIn(vcursor VCursor, bindVars map[string]*querypb if err != nil { return nil, nil, vterrors.Wrap(err, "paramsSelectIn") } - rss, values, err := route.resolveShards(vcursor, keys) + rss, values, err := resolveShards(vcursor, route.Vindex, route.Keyspace, keys) if err != nil { return nil, nil, vterrors.Wrap(err, "paramsSelectIn") } return rss, shardVars(bindVars, values), nil } -func (route *Route) resolveShards(vcursor VCursor, vindexKeys []sqltypes.Value) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) { +func resolveShards(vcursor VCursor, vindex vindexes.SingleColumn, keyspace *vindexes.Keyspace, vindexKeys []sqltypes.Value) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) { // Convert vindexKeys to []*querypb.Value ids := make([]*querypb.Value, len(vindexKeys)) for i, vik := range vindexKeys { @@ -416,13 +416,13 @@ func (route *Route) resolveShards(vcursor VCursor, vindexKeys []sqltypes.Value) } // Map using the Vindex - destinations, err := route.Vindex.Map(vcursor, vindexKeys) + destinations, err := vindex.Map(vcursor, vindexKeys) if err != nil { return nil, nil, err } // And use the Resolver to map to ResolvedShards. - return vcursor.ResolveDestinations(route.Keyspace.Name, ids, destinations) + return vcursor.ResolveDestinations(keyspace.Name, ids, destinations) } func (route *Route) sort(in *sqltypes.Result) (*sqltypes.Result, error) { diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index 49ee8324b9a..ede8754b89f 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -132,6 +132,9 @@ const ( // in the clause: // e.g: UPDATE `keyspace[-]`.x1 SET foo=1 UpdateByDestination + // UpdateIn is for routing an update statement with in where clause + // Requires: A Vindex and multiple values. + UpdateIn ) var updName = map[UpdateOpcode]string{ @@ -139,6 +142,7 @@ var updName = map[UpdateOpcode]string{ UpdateEqual: "UpdateEqual", UpdateScatter: "UpdateScatter", UpdateByDestination: "UpdateByDestination", + UpdateIn: "UpdateIn", } // MarshalJSON serializes the UpdateOpcode as a JSON string. @@ -181,6 +185,8 @@ func (upd *Update) Execute(vcursor VCursor, bindVars map[string]*querypb.BindVar return upd.execUpdateByDestination(vcursor, bindVars, key.DestinationAllShards{}) case UpdateByDestination: return upd.execUpdateByDestination(vcursor, bindVars, upd.TargetDestination) + case UpdateIn: + return upd.execUpdateIn(vcursor, bindVars) default: // Unreachable. return nil, fmt.Errorf("unsupported opcode: %v", upd) @@ -292,3 +298,30 @@ func (upd *Update) execUpdateByDestination(vcursor VCursor, bindVars map[string] result, errs := vcursor.ExecuteMultiShard(rss, queries, true /* isDML */, autocommit) return result, vterrors.Aggregate(errs) } + +func (upd *Update) execUpdateIn(vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + keys, err := upd.Values[0].ResolveList(bindVars) + if err != nil { + return nil, vterrors.Wrap(err, "execUpdateIn") + } + _, _, err = resolveShards(vcursor, upd.Vindex, upd.Keyspace, keys) + if err != nil { + return nil, vterrors.Wrap(err, "execUpdateIn") + } + // just to have code compile returning blank. + return &sqltypes.Result{}, nil + /* + TODO: Think of, How to go about updating lookup vindex. Will it be a loop or should resolve shards be performed on it. + + if len(ksids) == 0 { + return &sqltypes.Result{}, nil + } + + if len(upd.ChangedVindexValues) != 0 { + if err := upd.updateVindexEntries(vcursor, upd.OwnedVindexQuery, bindVars, rs, ksid); err != nil { + return nil, vterrors.Wrap(err, "execUpdateEqual") + } + } + rewritten := sqlannotation.AddKeyspaceIDs(upd.Query, [][]byte{ksid}, "")*/ + //return execShard(vcursor, rewritten, bindVars, rs, true /* isDML */, true /* canAutocommit */) +} diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 885499d87a2..41e115cbec0 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -147,17 +147,17 @@ func TestPlan(t *testing.T) { // the column is named as Id. This is to make sure that // column names are case-preserved, but treated as // case-insensitive even if they come from the vschema. - testFile(t, "aggr_cases.txt", vschema) + //testFile(t, "aggr_cases.txt", vschema) testFile(t, "dml_cases.txt", vschema) - testFile(t, "from_cases.txt", vschema) - testFile(t, "filter_cases.txt", vschema) - testFile(t, "postprocess_cases.txt", vschema) - testFile(t, "select_cases.txt", vschema) - testFile(t, "symtab_cases.txt", vschema) - testFile(t, "unsupported_cases.txt", vschema) - testFile(t, "vindex_func_cases.txt", vschema) - testFile(t, "wireup_cases.txt", vschema) - testFile(t, "memory_sort_cases.txt", vschema) + //testFile(t, "from_cases.txt", vschema) + //testFile(t, "filter_cases.txt", vschema) + //testFile(t, "postprocess_cases.txt", vschema) + //testFile(t, "select_cases.txt", vschema) + //testFile(t, "symtab_cases.txt", vschema) + //testFile(t, "unsupported_cases.txt", vschema) + //testFile(t, "vindex_func_cases.txt", vschema) + //testFile(t, "wireup_cases.txt", vschema) + //testFile(t, "memory_sort_cases.txt", vschema) } func TestOne(t *testing.T) { diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt index d59e99cdbf5..f7238d578cc 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt @@ -1,1816 +1,24 @@ -# update table not found -"update nouser set val = 1" -"table nouser not found" - -# delete table not found -"delete from nouser" -"table nouser not found" - -# explicit keyspace reference -"update main.m1 set val = 1" -{ - "Original": "update main.m1 set val = 1", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update m1 set val = 1" - } -} - -# update unsharded -"update unsharded set val = 1" -{ - "Original": "update unsharded set val = 1", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded set val = 1" - } -} - -# subqueries in unsharded update -"update unsharded set col = (select col from unsharded limit 1)" -{ - "Original": "update unsharded set col = (select col from unsharded limit 1)", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded set col = (select col from unsharded limit 1)" - } -} - -# unsharded union in subquery of unsharded update -"update unsharded set col = (select id from unsharded union select id from unsharded)" -{ - "Original": "update unsharded set col = (select id from unsharded union select id from unsharded)", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded set col = (select id from unsharded union select id from unsharded)" - } -} - -# unsharded join in subquery of unsharded update -"update unsharded set col = (select id from unsharded a join unsharded b on a.id = b.id)" -{ - "Original": "update unsharded set col = (select id from unsharded a join unsharded b on a.id = b.id)", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded set col = (select id from unsharded as a join unsharded as b on a.id = b.id)" - } -} - -# update with join subquery -"update unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col < 1000" -{ - "Original": "update unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col \u003c 1000", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col \u003c 1000" - } -} - -# routing rules: updated of a routed table -"update route1 set a=1 where id=1" -{ - "Original": "update route1 set a=1 where id=1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user as route1 set a = 1 where id = 1", - "Vindex": "user_index", - "Values": [ - 1 - ], - "Table": "user" - } -} - -# routing rules: subquery chooses unsharded route for update -"update unsharded_a set a=(select a from route2)" -{ - "Original": "update unsharded_a set a=(select a from route2)", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded_a set a = (select a from unsharded as route2)" - } -} - -# delete unsharded -"delete from unsharded" -{ - "Original": "delete from unsharded", - "Instructions": { - "Opcode": "DeleteUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "delete from unsharded" - } -} - -# update by primary keyspace id -"update user set val = 1 where id = 1" -{ - "Original": "update user set val = 1 where id = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user set val = 1 where id = 1", - "Vindex": "user_index", - "Values": [1], - "Table": "user" - } -} - - -# update by primary keyspace id with alias -"update user as user_alias set val = 1 where user_alias.id = 1" -{ - "Original": "update user as user_alias set val = 1 where user_alias.id = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user as user_alias set val = 1 where user_alias.id = 1", - "Vindex": "user_index", - "Values": [1], - "Table": "user" - } -} - -# update by primary keyspace id with parenthesized expression -"update user set val = 1 where (id = 1)" -{ - "Original": "update user set val = 1 where (id = 1)", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user set val = 1 where (id = 1)", - "Vindex": "user_index", - "Values": [ - 1 - ], - "Table": "user" - } -} - -# update by primary keyspace id with multi-part where clause with parens -"update user set val = 1 where (name = 'foo' and id = 1)" -{ - "Original": "update user set val = 1 where (name = 'foo' and id = 1)", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user set val = 1 where (name = 'foo' and id = 1)", - "Vindex": "user_index", - "Values": [1], - "Table": "user" - } -} - -# update by primary keyspace id, changing one vindex column -"update user_metadata set email = 'juan@vitess.io' where user_id = 1" -{ - "Original": "update user_metadata set email = 'juan@vitess.io' where user_id = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_metadata set email = 'juan@vitess.io' where user_id = 1", - "Vindex": "user_index", - "Values": [1], - "ChangedVindexValues": { - "email_user_map": ["juan@vitess.io"] - }, - "Table": "user_metadata", - "OwnedVindexQuery": "select email, address from user_metadata where user_id = 1 for update" - } -} - -# update by primary keyspace id, changing same vindex twice -"update user_metadata set email = 'a', email = 'b' where user_id = 1" -"column has duplicate set values: 'email'" - -# update by primary keyspace id, changing multiple vindex columns -"update user_metadata set email = 'juan@vitess.io', address = '155 5th street' where user_id = 1" -{ - "Original": "update user_metadata set email = 'juan@vitess.io', address = '155 5th street' where user_id = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_metadata set email = 'juan@vitess.io', address = '155 5th street' where user_id = 1", - "Vindex": "user_index", - "Values": [1], - "ChangedVindexValues": { - "address_user_map": ["155 5th street"], - "email_user_map": ["juan@vitess.io"] - }, - "Table": "user_metadata", - "OwnedVindexQuery": "select email, address from user_metadata where user_id = 1 for update" - } -} - -# update by primary keyspace id, changing one vindex column, using order by and limit -"update user_metadata set email = 'juan@vitess.io' where user_id = 1 order by user_id asc limit 10" -{ - "Original": "update user_metadata set email = 'juan@vitess.io' where user_id = 1 order by user_id asc limit 10", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_metadata set email = 'juan@vitess.io' where user_id = 1 order by user_id asc limit 10", - "Vindex": "user_index", - "Values": [ - 1 - ], - "ChangedVindexValues": { - "email_user_map": ["juan@vitess.io"] - }, - "Table": "user_metadata", - "OwnedVindexQuery": "select email, address from user_metadata where user_id = 1 order by user_id asc limit 10 for update" - } -} - -# update by primary keyspace id, stray where clause -"update user set val = 1 where id = id2 and id = 1" -{ - "Original": "update user set val = 1 where id = id2 and id = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user set val = 1 where id = id2 and id = 1", - "Vindex": "user_index", - "Values": [1], - "Table": "user" - } -} - -# update by primary keyspace id, stray where clause with conversion error -"update user set val = 1 where id = 18446744073709551616 and id = 1" -{ - "Original": "update user set val = 1 where id = 18446744073709551616 and id = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user set val = 1 where id = 18446744073709551616 and id = 1", - "Vindex": "user_index", - "Values": [1], - "Table": "user" - } -} - -# delete from by primary keyspace id -"delete from user where id = 1" -{ - "Original": "delete from user where id = 1", - "Instructions": { - "Opcode": "DeleteEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from user where id = 1", - "Vindex": "user_index", - "Values": [1], - "Table": "user", - "OwnedVindexQuery": "select Name, Costly from user where id = 1 for update" - } -} - -# multi-table delete with comma join -"delete a from unsharded_a a, unsharded_b b where a.id = b.id and b.val = 1" -{ - "Original": "delete a from unsharded_a a, unsharded_b b where a.id = b.id and b.val = 1", - "Instructions": { - "Opcode": "DeleteUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "delete a from unsharded_a as a, unsharded_b as b where a.id = b.id and b.val = 1" - } -} - -# multi-table delete with ansi join -"delete a from unsharded_a a join unsharded_b b on a.id = b.id where b.val = 1" -{ - "Original": "delete a from unsharded_a a join unsharded_b b on a.id = b.id where b.val = 1", - "Instructions": { - "Opcode": "DeleteUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "delete a from unsharded_a as a join unsharded_b as b on a.id = b.id where b.val = 1" - } -} - -#delete with join from subquery -"delete foo from unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000" -{ - "Original": "delete foo from unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col \u003c 1000", - "Instructions": { - "Opcode": "DeleteUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "delete foo from unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col \u003c 1000" - } -} - -# routing rules: deleted from a routed table -"delete from route1 where id = 1" -{ - "Original": "delete from route1 where id = 1", - "Instructions": { - "Opcode": "DeleteEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from user as route1 where id = 1", - "Vindex": "user_index", - "Values": [ - 1 - ], - "Table": "user", - "OwnedVindexQuery": "select Name, Costly from user where id = 1 for update" - } -} - -# routing rules: subquery chooses unsharded route for delete -"delete from unsharded_a where a=(select a from route2)" -{ - "Original": "delete from unsharded_a where a=(select a from route2)", - "Instructions": { - "Opcode": "DeleteUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "delete from unsharded_a where a = (select a from unsharded as route2)" - } -} - -# update by lookup -"update music set val = 1 where id = 1" -{ - "Original": "update music set val = 1 where id = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update music set val = 1 where id = 1", - "Vindex": "music_user_map", - "Values": [1], - "Table": "music" - } -} - -# update multi-table ansi join -"update unsharded_a a join unsharded_b b on a.id = b.id set a.val = 'foo' where b.val = 1" -{ - "Original": "update unsharded_a a join unsharded_b b on a.id = b.id set a.val = 'foo' where b.val = 1", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded_a as a join unsharded_b as b on a.id = b.id set a.val = 'foo' where b.val = 1" - } -} - -# update multi-table comma join -"update unsharded_a a, unsharded_b b set a.val = 'foo' where a.id = b.id and b.val = 1" -{ - "Original": "update unsharded_a a, unsharded_b b set a.val = 'foo' where a.id = b.id and b.val = 1", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "update unsharded_a as a, unsharded_b as b set a.val = 'foo' where a.id = b.id and b.val = 1" - } -} - -# delete from by lookup -"delete from music where id = 1" -{ - "Original": "delete from music where id = 1", - "Instructions": { - "Opcode": "DeleteEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from music where id = 1", - "Vindex": "music_user_map", - "Values": [1], - "Table": "music", - "OwnedVindexQuery": "select id from music where id = 1 for update" - } -} - -# delete from, no owned vindexes -"delete from music_extra where user_id = 1" -{ - "Original": "delete from music_extra where user_id = 1", - "Instructions": { - "Opcode": "DeleteEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from music_extra where user_id = 1", - "Vindex": "user_index", - "Values": [1], - "Table": "music_extra" - } -} - -# simple insert, no values -"insert into unsharded values()" -{ - "Original": "insert into unsharded values()", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded values ()", - "Table": "unsharded" - } -} - -# simple insert unsharded -"insert into unsharded values(1, 2)" -{ - "Original": "insert into unsharded values(1, 2)", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded values (1, 2)", - "Table": "unsharded" - } -} - -# simple upsert unsharded -"insert into unsharded values(1, 2) on duplicate key update x = 3" -{ - "Original": "insert into unsharded values(1, 2) on duplicate key update x = 3", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded values (1, 2) on duplicate key update x = 3", - "Table": "unsharded" - } -} - -# unsharded insert, no col list with auto-inc and authoritative column list -"insert into unsharded_authoritative values(1,1)" -{ - "Original": "insert into unsharded_authoritative values(1,1)", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded_authoritative(col1, col2) values (:__seq0, 1)", - "Table": "unsharded_authoritative", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - 1 - ] - } - } -} - -# sharded upsert with sharding key set to vindex column -"insert into music(user_id, id) values(1, 2) on duplicate key update user_id = values(user_id)" -{ - "Original": "insert into music(user_id, id) values(1, 2) on duplicate key update user_id = values(user_id)", - "Instructions": { - "Opcode": "InsertShardedIgnore", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into music(user_id, id) values (:_user_id0, :_id0) on duplicate key update user_id = values(user_id)", - "Values": [ - [ - [ - 1 - ] - ], - [ - [ - 2 - ] - ] - ], - "Table": "music", - "Prefix": "insert into music(user_id, id) values ", - "Mid": [ - "(:_user_id0, :_id0)" - ], - "Suffix": " on duplicate key update user_id = values(user_id)" - } -} - -# sharded bulk upsert with sharding key set to vindex column -"insert into music(user_id, id) values (1, 2), (3,4) on duplicate key update user_id = values(user_id)" -{ - "Original": "insert into music(user_id, id) values (1, 2), (3,4) on duplicate key update user_id = values(user_id)", - "Instructions": { - "Opcode": "InsertShardedIgnore", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into music(user_id, id) values (:_user_id0, :_id0), (:_user_id1, :_id1) on duplicate key update user_id = values(user_id)", - "Values": [ - [ - [ - 1, - 3 - ] - ], - [ - [ - 2, - 4 - ] - ] - ], - "Table": "music", - "Prefix": "insert into music(user_id, id) values ", - "Mid": [ - "(:_user_id0, :_id0)", - "(:_user_id1, :_id1)" - ], - "Suffix": " on duplicate key update user_id = values(user_id)" - } -} - -# insert unsharded with select -"insert into unsharded select id from unsharded_auto" -{ - "Original": "insert into unsharded select id from unsharded_auto", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded select id from unsharded_auto", - "Table": "unsharded" - } -} - -# insert unsharded with select with join -"insert into unsharded select id from unsharded join unsharded_auto" -{ - "Original": "insert into unsharded select id from unsharded join unsharded_auto", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded select id from unsharded join unsharded_auto", - "Table": "unsharded" - } -} - -# insert unsharded, invalid value for auto-inc -"insert into unsharded_auto(id, val) values(18446744073709551616, 'aa')" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" - -# insert unsharded, column present -"insert into unsharded_auto(id, val) values(1, 'aa')" -{ - "Original": "insert into unsharded_auto(id, val) values(1, 'aa')", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded_auto(id, val) values (:__seq0, 'aa')", - "Table": "unsharded_auto", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - 1 - ] - } - } -} - -# insert unsharded, column absent -"insert into unsharded_auto(val) values('aa')" -{ - "Original": "insert into unsharded_auto(val) values('aa')", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded_auto(val, id) values ('aa', :__seq0)", - "Table": "unsharded_auto", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - null - ] - } - } -} - -# insert unsharded, column absent -"insert into unsharded_auto(val) values(false)" -{ - "Original": "insert into unsharded_auto(val) values(false)", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded_auto(val, id) values (false, :__seq0)", - "Table": "unsharded_auto", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - null - ] - } - } -} - -# insert unsharded, multi-val -"insert into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')" -{ - "Original": "insert into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded_auto(id, val) values (:__seq0, 'aa'), (:__seq1, 'bb')", - "Table": "unsharded_auto", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - 1, - null - ] - } - } -} - - -# unsharded insert subquery in insert value -"insert into unsharded values((select 1 from dual), 1)" -{ - "Original": "insert into unsharded values((select 1 from dual), 1)", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded values ((select 1 from dual), 1)", - "Table": "unsharded" - } -} - -# insert into a routed table -"insert into route1(id) values (1)" -{ - "Original": "insert into route1(id) values (1)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", - "Values": [ - [ - [ - ":__seq0" - ] - ], - [ - [ - null - ] - ], - [ - [ - null - ] - ] - ], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - 1 - ] - }, - "Prefix": "insert into user(id, Name, Costly) values ", - "Mid": [ - "(:_Id0, :_Name0, :_Costly0)" - ] - } -} - -# insert with mimatched column list -"insert into user(id) values (1, 2)" -"column list doesn't match values" - -# insert no column list for sharded authoritative table -"insert into authoritative values(1, 2, 3)" -{ - "Original": "insert into authoritative values(1, 2, 3)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into authoritative(user_id, col1, col2) values (:_user_id0, 2, 3)", - "Values": [ - [ - [ - 1 - ] - ] - ], - "Table": "authoritative", - "Prefix": "insert into authoritative(user_id, col1, col2) values ", - "Mid": [ - "(:_user_id0, 2, 3)" - ] - } -} - -# insert sharded, no values -"insert into user values()" -"no column list" - -# insert with one vindex -"insert into user(id) values (1)" -{ - "Original": "insert into user(id) values (1)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [1] - }, - "Prefix": "insert into user(id, Name, Costly) values ", - "Mid":["(:_Id0, :_Name0, :_Costly0)"] - } -} - -# insert ignore sharded -"insert ignore into user(id) values (1)" -{ - "Original": "insert ignore into user(id) values (1)", - "Instructions": { - "Opcode": "InsertShardedIgnore", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert ignore into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [1] - }, - "Prefix": "insert ignore into user(id, Name, Costly) values ", - "Mid":["(:_Id0, :_Name0, :_Costly0)"] - } -} - -# insert on duplicate key -"insert into user(id) values(1) on duplicate key update col = 2" -{ - "Original": "insert into user(id) values(1) on duplicate key update col = 2", - "Instructions": { - "Opcode": "InsertShardedIgnore", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0) on duplicate key update col = 2", - "Values": [[[":__seq0"]],[[null]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - 1 - ] - }, - "Prefix": "insert into user(id, Name, Costly) values ", - "Mid": [ - "(:_Id0, :_Name0, :_Costly0)" - ], - "Suffix": " on duplicate key update col = 2" - } -} - -# insert with one vindex and bind var -"insert into user(id) values (:aa)" -{ - "Original": "insert into user(id) values (:aa)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - ":aa" - ] - }, - "Prefix": "insert into user(id, Name, Costly) values ", - "Mid": [ - "(:_Id0, :_Name0, :_Costly0)" - ] - } -} - -# insert with non vindex -"insert into user(nonid) values (2)" -{ - "Original": "insert into user(nonid) values (2)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(nonid, id, Name, Costly) values (2, :_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [null] - }, - "Prefix": "insert into user(nonid, id, Name, Costly) values ", - "Mid": ["(2, :_Id0, :_Name0, :_Costly0)"] - } -} - -# insert with default seq -"insert into user(id, nonid) values (default, 2)" -{ - "Original": "insert into user(id, nonid) values (default, 2)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(id, nonid, Name, Costly) values (:_Id0, 2, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [null] - }, - "Prefix": "insert into user(id, nonid, Name, Costly) values ", - "Mid": ["(:_Id0, 2, :_Name0, :_Costly0)"] - } -} - -# insert with non vindex bool value -"insert into user(nonid) values (true)" -{ - "Original": "insert into user(nonid) values (true)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(nonid, id, Name, Costly) values (true, :_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [null] - }, - "Prefix": "insert into user(nonid, id, Name, Costly) values ", - "Mid": ["(true, :_Id0, :_Name0, :_Costly0)"] - } -} - -# insert with all vindexes supplied -"insert into user(nonid, name, id) values (2, 'foo', 1)" -{ - "Original": "insert into user(nonid, name, id) values (2, 'foo', 1)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(nonid, name, id, Costly) values (2, :_Name0, :_Id0, :_Costly0)", - "Values": [[[":__seq0"]],[["foo"]],[[null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [1] - }, - "Prefix": "insert into user(nonid, name, id, Costly) values ", - "Mid": ["(2, :_Name0, :_Id0, :_Costly0)"] - } -} - -# insert for non-vindex autoinc -"insert into user_extra(nonid) values (2)" -{ - "Original": "insert into user_extra(nonid) values (2)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user_extra(nonid, extra_id, user_id) values (2, :__seq0, :_user_id0)", - "Values": [[[null]]], - "Table": "user_extra", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [null] - }, - "Prefix": "insert into user_extra(nonid, extra_id, user_id) values ", - "Mid": ["(2, :__seq0, :_user_id0)"] - } -} - -# insert for non-compliant names -"insert into `weird``name`(`a``b*c`, `b*c`) values(1, 2)" -{ - "Original": "insert into `weird``name`(`a``b*c`, `b*c`) values(1, 2)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into `weird``name`(`a``b*c`, `b*c`) values (:_a_b_c0, 2)", - "Values": [[[1]]], - "Table": "weird`name", - "Prefix": "insert into `weird``name`(`a``b*c`, `b*c`) values ", - "Mid": [ - "(:_a_b_c0, 2)" - ] - } -} - -# unsharded insert from union -"insert into unsharded select 1 from dual union select 1 from dual" -{ - "Original": "insert into unsharded select 1 from dual union select 1 from dual", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded select 1 from dual union select 1 from dual", - "Table": "unsharded" - } -} - -# insert for non-vindex autoinc, invalid value -"insert into user_extra(nonid, extra_id) values (2, 18446744073709551616)" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" - -# insert invalid index value -"insert into music_extra(music_id, user_id) values(1, 18446744073709551616)" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" - -# insert invalid index value -"insert into music_extra(music_id, user_id) values(1, id)" -"could not compute value for vindex or auto-inc column: expression is too complex 'id'" - -# insert invalid table -"insert into noexist(music_id, user_id) values(1, 18446744073709551616)" -"table noexist not found" - -# insert with multiple rows -"insert into user(id) values (1), (2)" -{ - "Original": "insert into user(id) values (1), (2)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", - "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [1,2] - }, - "Prefix": "insert into user(id, Name, Costly) values ", - "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"] - } -} - -# insert with query timeout -"insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id) values (1), (2)" -{ - "Original": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id) values (1), (2)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", - "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [1,2] - }, - "Prefix": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id, Name, Costly) values ", - "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"], - "QueryTimeout": 1 - } -} - -# insert with multiple rows - multi-shard autocommit -"insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id) values (1), (2)" -{ - "Original": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id) values (1), (2)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", - "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], - "Table": "user", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [1,2] - }, - "Prefix": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id, Name, Costly) values ", - "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"], - "MultiShardAutocommit": true - } -} - -# insert into a vindex not allowed -"insert into user_index(id) values(1)" -"unsupported: multi-shard or vindex write statement" - -# simple replace unsharded -"replace into unsharded values(1, 2)" -{ - "Original": "replace into unsharded values(1, 2)", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "replace into unsharded values (1, 2)", - "Table": "unsharded" - } -} - -# replace unsharded with select -"replace into unsharded select id from unsharded_auto" -{ - "Original": "replace into unsharded select id from unsharded_auto", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "replace into unsharded select id from unsharded_auto", - "Table": "unsharded" - } -} - -# replace unsharded, invalid value for auto-inc -"replace into unsharded_auto(id, val) values(18446744073709551616, 'aa')" -"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" - -# replace unsharded, column present -"replace into unsharded_auto(id, val) values(1, 'aa')" -{ - "Original": "replace into unsharded_auto(id, val) values(1, 'aa')", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "replace into unsharded_auto(id, val) values (:__seq0, 'aa')", - "Table": "unsharded_auto", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - 1 - ] - } - } -} - -# replace unsharded, column absent -"replace into unsharded_auto(val) values('aa')" -{ - "Original": "replace into unsharded_auto(val) values('aa')", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "replace into unsharded_auto(val, id) values ('aa', :__seq0)", - "Table": "unsharded_auto", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - null - ] - } - } -} - -# replace unsharded, multi-val -"replace into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')" -{ - "Original": "replace into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "replace into unsharded_auto(id, val) values (:__seq0, 'aa'), (:__seq1, 'bb')", - "Table": "unsharded_auto", - "Generate": { - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "select next :n values from seq", - "Values": [ - 1, - null - ] - } - } -} - -# replace invalid table -"replace into noexist(music_id, user_id) values(1, 18446744073709551616)" -"table noexist not found" - -# insert a row in a multi column vindex table -"insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4)" -{ - "Original": "insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into multicolvin(column_a, column_b, column_c, kid) values (:_column_a0, :_column_b0, :_column_c0, :_kid0)", - "Values": [ - [ - [ - 4 - ] - ], - [ - [ - 1 - ] - ], - [ - [ - 2 - ], - [ - 3 - ] - ] - ], - "Table": "multicolvin", - "Prefix": "insert into multicolvin(column_a, column_b, column_c, kid) values ", - "Mid": [ - "(:_column_a0, :_column_b0, :_column_c0, :_kid0)" - ] - } -} - -# insert for overlapped vindex columns -"insert overlap_vindex (kid, column_a, column_b) VALUES (1,2,3)" -{ - "Original": "insert overlap_vindex (kid, column_a, column_b) VALUES (1,2,3)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into overlap_vindex(kid, column_a, column_b) values (:_kid0, :_column_a0, 3)", - "Values": [ - [ - [ - 1 - ] - ], - [ - [ - 2 - ], - [ - 1 - ] - ] - ], - "Table": "overlap_vindex", - "Prefix": "insert into overlap_vindex(kid, column_a, column_b) values ", - "Mid": [ - "(:_kid0, :_column_a0, 3)" - ] - } -} - - -# insert multiple rows in a multi column vindex table -"insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4), (5,6,7,8)" -{ - "Original": "insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4), (5,6,7,8)", - "Instructions": { - "Opcode": "InsertSharded", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "insert into multicolvin(column_a, column_b, column_c, kid) values (:_column_a0, :_column_b0, :_column_c0, :_kid0), (:_column_a1, :_column_b1, :_column_c1, :_kid1)", - "Values": [ - [ - [ - 4, - 8 - ] - ], - [ - [ - 1, - 5 - ] - ], - [ - [ - 2, - 6 - ], - [ - 3, - 7 - ] - ] - ], - "Table": "multicolvin", - "Prefix": "insert into multicolvin(column_a, column_b, column_c, kid) values ", - "Mid": [ - "(:_column_a0, :_column_b0, :_column_c0, :_kid0)", - "(:_column_a1, :_column_b1, :_column_c1, :_kid1)" - ] - } -} - -# delete row in a multi column vindex table -"delete from multicolvin where kid=1" -{ - "Original": "delete from multicolvin where kid=1", - "Instructions": { - "Opcode": "DeleteEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from multicolvin where kid = 1", - "Vindex": "kid_index", - "Values": [ - 1 - ], - "Table": "multicolvin", - "OwnedVindexQuery": "select column_a, column_b, column_c from multicolvin where kid = 1 for update" - } -} - -# update columns of multi column vindex -"update multicolvin set column_b = 1, column_c = 2 where kid = 1" -{ - "Original": "update multicolvin set column_b = 1, column_c = 2 where kid = 1", - "Instructions": { - "Opcode": "UpdateEqual", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update multicolvin set column_b = 1, column_c = 2 where kid = 1", - "Vindex": "kid_index", - "Values": [ - 1 - ], - "ChangedVindexValues": { - "colb_colc_map": [ - 1, - 2 - ] - }, - "Table": "multicolvin", - "OwnedVindexQuery": "select column_a, column_b, column_c from multicolvin where kid = 1 for update" - } -} - -# update multiple vindexes, with multi column vindex -"update multicolvin set column_a = 0, column_b = 1, column_c = 2 where kid = 1" +# update multiple vindex value to null +"update user set name = null where id in (1, 2, 3)" { - "Original": "update multicolvin set column_a = 0, column_b = 1, column_c = 2 where kid = 1", + "Original": "update user set name = null where id in (1, 2, 3)", "Instructions": { - "Opcode": "UpdateEqual", + "Opcode": "UpdateIn", "Keyspace": { "Name": "user", "Sharded": true }, - "Query": "update multicolvin set column_a = 0, column_b = 1, column_c = 2 where kid = 1", - "Vindex": "kid_index", - "Values": [ - 1 - ], + "Query": "update user set name = null where id in (1, 2, 3)", + "Vindex": "user_index", + "Values": [[1, 2, 3]], "ChangedVindexValues": { - "cola_map": [ - 0 - ], - "colb_colc_map": [ - 1, - 2 - ] - }, - "Table": "multicolvin", - "OwnedVindexQuery": "select column_a, column_b, column_c from multicolvin where kid = 1 for update" - } -} - -# update with no primary vindex on where clause (scatter update) -"update user_extra set val = 1" -{ - "Original": "update user_extra set val = 1", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_extra set val = 1", - "Table": "user_extra" - } -} - -# update with target destination -"update `user[-]`.user_extra set val = 1" -{ - "Original": "update `user[-]`.user_extra set val = 1", - "Instructions": { - "Opcode": "UpdateByDestination", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_extra set val = 1", - "Table": "user_extra" - } -} - -# update with no primary vindex on where clause (scatter update) - multi shard autocommit -"update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ user_extra set val = 1" -{ - "Original": "update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ user_extra set val = 1", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ user_extra set val = 1", - "Table": "user_extra", - "MultiShardAutocommit": true - } -} - -# update with no primary vindex on where clause (scatter update) - query timeout -"update /*vt+ QUERY_TIMEOUT_MS=1 */ user_extra set val = 1" -{ - "Original": "update /*vt+ QUERY_TIMEOUT_MS=1 */ user_extra set val = 1", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update /*vt+ QUERY_TIMEOUT_MS=1 */ user_extra set val = 1", - "Table": "user_extra", - "QueryTimeout": 1 - } -} - -# update with non-comparison expr -"update user_extra set val = 1 where id between 1 and 2" -{ - "Original": "update user_extra set val = 1 where id between 1 and 2", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_extra set val = 1 where id between 1 and 2", - "Table": "user_extra" - } -} - -# update with primary id through IN clause -"update user_extra set val = 1 where user_id in (1, 2)" -{ - "Original": "update user_extra set val = 1 where user_id in (1, 2)", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_extra set val = 1 where user_id in (1, 2)", - "Table": "user_extra" - } -} - - -# update with non-unique key -"update user_extra set val = 1 where name = 'foo'" -{ - "Original": "update user_extra set val = 1 where name = 'foo'", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_extra set val = 1 where name = 'foo'", - "Table": "user_extra" - } -} - -# update by lookup with IN clause -"update user_extra set val = 1 where id in (1, 2)" -{ - "Original": "update user_extra set val = 1 where id in (1, 2)", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_extra set val = 1 where id in (1, 2)", - "Table": "user_extra" - } -} - -# update with where clause with parens -"update user_extra set val = 1 where (name = 'foo' or id = 1)" -{ - "Original": "update user_extra set val = 1 where (name = 'foo' or id = 1)", - "Instructions": { - "Opcode": "UpdateScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "update user_extra set val = 1 where (name = 'foo' or id = 1)", - "Table": "user_extra" - } -} - -# delete from with no where clause -"delete from user_extra" -{ - "Original": "delete from user_extra", - "Instructions": { - "Opcode": "DeleteScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from user_extra", - "Table": "user_extra" - } -} - -# delete with target destination -"delete from `user[-]`.user_extra" -{ - "Original": "delete from `user[-]`.user_extra", - "Instructions": { - "Opcode": "DeleteByDestination", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from user_extra", - "Table": "user_extra" - } -} - -# delete with non-comparison expr -"delete from user_extra where user_id between 1 and 2" -{ - "Original": "delete from user_extra where user_id between 1 and 2", - "Instructions": { - "Opcode": "DeleteScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from user_extra where user_id between 1 and 2", - "Table": "user_extra" - } -} - -# delete from with no index match -"delete from user_extra where name = 'jose'" -{ - "Original": "delete from user_extra where name = 'jose'", - "Instructions": { - "Opcode": "DeleteScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from user_extra where name = 'jose'", - "Table": "user_extra" - } -} - -# delete from with no index match - multi shard autocommit -"delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where name = 'jose'" -{ - "Original": "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where name = 'jose'", - "Instructions": { - "Opcode": "DeleteScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where name = 'jose'", - "Table": "user_extra", - "MultiShardAutocommit": true - } -} - -# delete from with no index match - query timeout -"delete /*vt+ QUERY_TIMEOUT_MS=1 */ from user_extra where name = 'jose'" -{ - "Original": "delete /*vt+ QUERY_TIMEOUT_MS=1 */ from user_extra where name = 'jose'", - "Instructions": { - "Opcode": "DeleteScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete /*vt+ QUERY_TIMEOUT_MS=1 */ from user_extra where name = 'jose'", - "Table": "user_extra", - "QueryTimeout": 1 - } -} - -# delete from with primary id in through IN clause -"delete from user_extra where user_id in (1, 2)" -{ - "Original": "delete from user_extra where user_id in (1, 2)", - "Instructions": { - "Opcode": "DeleteScatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "Query": "delete from user_extra where user_id in (1, 2)", - "Table": "user_extra" - } -} - -# unsharded update where inner query references outer query -"update unsharded set col = (select id from unsharded_a where id = unsharded.col) where col = (select id from unsharded_b)" -{ - "Original": "update unsharded set col = (select id from unsharded_a where id = unsharded.col) where col = (select id from unsharded_b)", - "Instructions": { - "Opcode": "UpdateUnsharded", - "Keyspace": { - "Name":"main", - "Sharded":false + "name_user_map": [null] }, - "Query": "update unsharded set col = (select id from unsharded_a where id = unsharded.col) where col = (select id from unsharded_b)" + "Table": "user", + "OwnedVindexQuery": "select Name, Costly from user where id in (1, 2, 3) for update" } } -# unsharded delete where inner query references outer query -"delete from unsharded where col = (select id from unsharded_a where id = unsharded.col)" -{ - "Original": "delete from unsharded where col = (select id from unsharded_a where id = unsharded.col)", - "Instructions": { - "Opcode": "DeleteUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "delete from unsharded where col = (select id from unsharded_a where id = unsharded.col)" - } -} # update vindex value to null "update user set name = null where id = 1" @@ -1833,17 +41,3 @@ } } -# insert using last_insert_id -"insert into unsharded values(last_insert_id(), 2)" -{ - "Original": "insert into unsharded values(last_insert_id(), 2)", - "Instructions": { - "Opcode": "InsertUnsharded", - "Keyspace": { - "Name": "main", - "Sharded": false - }, - "Query": "insert into unsharded values (:__lastInsertId, 2)", - "Table": "unsharded" - } -} diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index 79bd4df5c36..2b8befcb10b 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -87,7 +87,11 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Upd if err != nil { eupd.Opcode = engine.UpdateScatter } else { - eupd.Opcode = engine.UpdateEqual + if eupd.Values[0].IsList() { + eupd.Opcode = engine.UpdateIn + } else { + eupd.Opcode = engine.UpdateEqual + } } if eupd.Opcode == engine.UpdateScatter { @@ -240,13 +244,19 @@ func getMatch(node sqlparser.Expr, col sqlparser.ColIdent) (pv sqltypes.PlanValu if !ok { continue } - if comparison.Operator != sqlparser.EqualStr { - continue - } if !nameMatch(comparison.Left, col) { continue } - if !sqlparser.IsValue(comparison.Right) { + switch comparison.Operator { + case sqlparser.EqualStr: + if !sqlparser.IsValue(comparison.Right) { + continue + } + case sqlparser.InStr: + if !sqlparser.IsSimpleTuple(comparison.Right) { + continue + } + default: continue } pv, err := sqlparser.NewPlanValue(comparison.Right) From 54afe78a1034e09cf760e65f8e12671a8f7fd302 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Tue, 18 Feb 2020 14:32:34 +0530 Subject: [PATCH 121/825] vtctld web test . Signed-off-by: pradip parmar --- go.mod | 3 +- go.sum | 11 + .../vtctldweb/vtctld_web_main_test.go | 596 ++++++++++++++++++ go/test/endtoend/vtctldweb/vtctld_web_test.go | 244 +++++++ go/vt/vtgate/executor.go | 28 +- go/vt/vtgate/executor_test.go | 13 +- 6 files changed, 874 insertions(+), 21 deletions(-) create mode 100644 go/test/endtoend/vtctldweb/vtctld_web_main_test.go create mode 100644 go/test/endtoend/vtctldweb/vtctld_web_test.go diff --git a/go.mod b/go.mod index 25077ce841a..97feafc3c11 100644 --- a/go.mod +++ b/go.mod @@ -52,8 +52,6 @@ require ( github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 @@ -74,6 +72,7 @@ require ( github.com/spf13/viper v1.6.1 // indirect github.com/stretchr/testify v1.4.0 github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b + github.com/tebeka/selenium v0.9.9 github.com/tinylib/msgp v1.1.1 // indirect github.com/uber-go/atomic v1.4.0 // indirect github.com/uber/jaeger-client-go v2.16.0+incompatible diff --git a/go.sum b/go.sum index 12ffff34b90..71ccc5ab9d3 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= @@ -12,6 +13,7 @@ github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= @@ -36,6 +38,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a h1:Ed33uJE74ksDaYfdY72gK7Cg//o2FgsqlqUfBW079T8= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= github.com/aws/aws-sdk-go v1.28.8 h1:kPGnElMdW0GDc54Giy1lcE/3gAr2Gzl6cMjYKoBNFhw= @@ -45,6 +48,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bombsimon/wsl v1.2.5 h1:9gTOkIwVtoDZywvX802SDHokeX4kW1cKnV8ZTVAPkRs= @@ -203,6 +208,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -488,6 +495,8 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b h1:i3lm+BZX5fAaH95wJavMgsSYU95LhSxdNCMa8nLv2gk= github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tebeka/selenium v0.9.9 h1:cNziB+etNgyH/7KlNI7RMC1ua5aH1+5wUlFQyzeMh+w= +github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tinylib/msgp v1.1.1 h1:TnCZ3FIuKeaIy+F45+Cnp+caqdXGy4z74HvwXN+570Y= @@ -644,6 +653,7 @@ golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190830154057-c17b040389b9 h1:5/jaG/gKlo3xxvUn85ReNyTlN7BvlPPsxC6sHZKjGEE= @@ -677,6 +687,7 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go new file mode 100644 index 00000000000..6f4834a34dd --- /dev/null +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -0,0 +1,596 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtctldweb + +import ( + "flag" + "fmt" + "os" + "os/exec" + "strings" + "syscall" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tebeka/selenium" + "github.com/tebeka/selenium/chrome" + vttestpb "vitess.io/vitess/go/vt/proto/vttest" + "vitess.io/vitess/go/vt/vttest" +) + +var ( + localCluster *vttest.LocalCluster + cell = "zone1" + hostname = "localhost" + wd selenium.WebDriver + seleniumService *selenium.Service + vtctldAddr string + ks1 = "test_keyspace" + ks2 = "test_keyspace2" + sqlSchema = "CREATE TABLE test_table (\n" + + " `id` BIGINT(20) UNSIGNED NOT NULL,\n" + + " `msg` VARCHAR(64),\n" + + " `keyspace_id` BIGINT(20) UNSIGNED NOT NULL,\n" + + " PRIMARY KEY (id)\n" + + ") ENGINE=InnoDB" +) + +func TestMain(m *testing.M) { + flag.Parse() + + exitcode, err := func() (int, error) { + + tearDownXvfb, err := RunXvfb() + if err != nil { + return 1, err + } + defer tearDownXvfb() + + // cluster setup + topology := new(vttestpb.VTTestTopology) + topology.Cells = []string{"test", "test2"} + topology.Keyspaces = []*vttestpb.Keyspace{ + { + Name: ks1, + Shards: []*vttestpb.Shard{ + {Name: "-80"}, + {Name: "80-"}, + }, + RdonlyCount: 2, + ReplicaCount: 2, + }, + { + Name: ks2, + Shards: []*vttestpb.Shard{ + {Name: "0"}, + }, + RdonlyCount: 2, + ReplicaCount: 1, + }, + } + + // create driver here + err = CreateWebDriver() + if err != nil { + return 1, err + } + defer TeardownWebDriver() + + // var cfg vttest.Config + var cfg vttest.Config + cfg.Topology = topology + cfg.SchemaDir = os.Getenv("VTROOT") + "/test/vttest_schema" + cfg.DefaultSchemaDir = os.Getenv("VTROOT") + "/test/vttest_schema/default" + + localCluster = &vttest.LocalCluster{ + Config: cfg, + } + + err = localCluster.Setup() + defer localCluster.TearDown() + vtctldAddr = fmt.Sprintf("http://localhost:%d", localCluster.Env.PortForProtocol("vtcombo", "port")) + if err != nil { + return 1, err + } + + return m.Run(), nil + }() + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } else { + os.Exit(exitcode) + } +} + +// RunXvfb runs Xvfb command in background and returns the teardown function. +func RunXvfb() (func() error, error) { + + tmpProcess := exec.Command("Xvfb", ":15", "-ac") + + err := tmpProcess.Start() + if err != nil { + return nil, err + } + + exit := make(chan error) + go func() { + exit <- tmpProcess.Wait() + }() + + teardownFunc := func() error { + tmpProcess.Process.Signal(syscall.SIGTERM) + select { + case <-exit: + return nil + case <-time.After(10 * time.Second): + tmpProcess.Process.Kill() + return <-exit + } + } + + os.Setenv("DISPLAY", ":15") + + return teardownFunc, nil +} + +// CreateWebDriver Creates a webdriver object (local or remote for Travis). +func CreateWebDriver() error { + selenium.SetDebug(true) + + options := selenium.ChromeDriver(os.Getenv("VTROOT") + "/dist") + + if os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true" { + + capabilities := selenium.Capabilities{} + capabilities["tunnel-identifier"] = os.Getenv("TRAVIS_JOB_NUMBER") + capabilities["build"] = os.Getenv("TRAVIS_BUILD_NUMBER") + capabilities["platform"] = "Linux" + capabilities["browserName"] = "chrome" + capabilities["chromeOptions"] = options + + var err error + wd, err = selenium.NewRemote(capabilities, fmt.Sprintf("%s:%s@localhost:4445/wd/hub", os.Getenv("SAUCE_USERNAME"), os.Getenv("SAUCE_ACCESS_KEY"))) + return err + } + + cc := selenium.Capabilities{ + "browserName": "chrome", + } + cc.AddChrome(chrome.Capabilities{ + Args: []string{ + "--disable-gpu", + "--no-sandbox", + "--headless", + }, + }) + + os.Setenv("webdriver.chrome.driver", os.Getenv("VTROOT")+"/dist") + + var err error + seleniumService, err = selenium.NewChromeDriverService(os.Getenv("VTROOT")+"/dist/chromedriver/chromedriver", 9515, options) + if err != nil { + return err + } + + wd, err = selenium.NewRemote(cc, "http://localhost:9515/wd/hub") + if err != nil { + return err + } + name, err := wd.CurrentWindowHandle() + return wd.ResizeWindow(name, 1280, 1024) + // return err +} + +func TeardownWebDriver() { + wd.Quit() + if seleniumService != nil { + seleniumService.Stop() + + } +} + +func GetKeyspaces() ([]string, error) { + element, err := wd.FindElement(selenium.ByID, "content") + if err != nil { + return nil, err + } + + keyspace, err := element.FindElements(selenium.ByTagName, "md-card") + if err != nil { + return nil, err + } + + var keyspaceNames []string + for _, ks := range keyspace { + e, err := ks.FindElement(selenium.ByTagName, "h2") + if err != nil { + return nil, err + } + + k, err := e.Text() + if err != nil { + return nil, err + } + + keyspaceNames = append(keyspaceNames, k) + } + + return keyspaceNames, nil +} + +func GetKeyspaceElem(ks string) (selenium.WebElement, error) { + var ksElem selenium.WebElement + err := wd.WaitWithTimeout(func(xwd selenium.WebDriver) (bool, error) { + elem, err := xwd.FindElement(selenium.ByID, ks+"-card") + if err != nil { + return false, err + } + + ksElem = elem + return true, nil + + }, selenium.DefaultWaitTimeout) + + return ksElem, err +} + +func GetShards(ks string) ([]string, error) { + return getList(ks + "-shards") +} +func GetServingShards(ks string) ([]string, error) { + return getList(ks + "-serving") +} +func GetInactiveShards(ks string) ([]string, error) { + return getList(ks + "-inactive") +} + +func getList(pre string) ([]string, error) { + shard, err := wd.FindElement(selenium.ByID, pre+"-list") + if err != nil { + return nil, err + } + + shardNames, err := shard.Text() + return strings.Split(shardNames, "\n"), err +} + +func GetShardElem(ks, shard string) (selenium.WebElement, error) { + + ksElem, err := GetKeyspaceElem(ks) + if err != nil { + return nil, err + } + + return ksElem.FindElement(selenium.ByLinkText, shard) +} + +func GetTabletNames() (map[string]string, error) { + ele, err := wd.FindElement(selenium.ByID, "tablets") + if err != nil { + return nil, err + } + + mdCards, err := ele.FindElements(selenium.ByTagName, "md-card") + if err != nil { + return nil, err + } + + var tabletTitles []string + for _, e := range mdCards { + toolBar, err := e.FindElement(selenium.ByTagName, "md-toolbar") + if err != nil { + return nil, err + } + t, err := toolBar.Text() + if err != nil { + return nil, err + } + + tabletTitles = append(tabletTitles, strings.Split(t, "\n")[0]) + } + + out := make(map[string]string) + + for _, tabletTitle := range tabletTitles { + tmp := strings.Split(tabletTitle, " ") + out[tmp[0]] = tmp[1][1 : len(tmp)-1] + } + + return out, nil +} + +func checkNewView(t *testing.T, keyspaces, cells, types, metrics []string, selectedKs, selectedCell, selectedType, selectedMetric string) { + checkDropdowns(t, keyspaces, cells, types, metrics, selectedKs, selectedCell, selectedType, selectedMetric) + checkHeatMaps(t, selectedKs) +} + +func checkHeatMaps(t *testing.T, selectedKs string) { + elem, err := wd.FindElement(selenium.ByTagName, "vt-status") + require.Nil(t, err) + + elems, err := elem.FindElements(selenium.ByTagName, "vt-heatmap") + require.Nil(t, err) + + if selectedKs == "all" { + availableKs := getDropdownOptions(t, "keyspace") + assert.Equal(t, len(elems), len(availableKs)-1) + for _, elem := range elems { + heading, err := elem.FindElement(selenium.ByID, "keyspaceName") + require.Nil(t, err) + + headingTxt, err := heading.Text() + require.Nil(t, err) + + _, err = elem.FindElement(selenium.ByID, headingTxt) + require.Nil(t, err) + + assert.Contains(t, availableKs, headingTxt) + } + return + } + + assert.Equal(t, 1, len(elems)) + heading, err := elems[0].FindElement(selenium.ByID, "keyspaceName") + require.Nil(t, err) + + headingTxt, err := heading.Text() + require.Nil(t, err) + + _, err = elem.FindElement(selenium.ByID, headingTxt) + require.Nil(t, err) + + assert.Equal(t, selectedKs, headingTxt) +} + +func changeDropdownOptions(t *testing.T, dropdownID, dropdownValue string) { + statusContent, err := wd.FindElement(selenium.ByTagName, "vt-status") + require.Nil(t, err) + + dropdown, err := statusContent.FindElement(selenium.ByID, dropdownID) + require.Nil(t, err) + + err = dropdown.Click() + require.Nil(t, err) + options, err := dropdown.FindElements(selenium.ByTagName, "li") + require.Nil(t, err) + + for _, op := range options { + opTxt, err := op.Text() + require.Nil(t, err) + if opTxt == dropdownValue { + err = op.Click() + require.Nil(t, err) + break + } + } +} + +func checkDropdowns(t *testing.T, keyspaces, cells, types, metrics []string, selectedKs, selectedCell, selectedType, selectedMetric string) { + + Options := getDropdownOptions(t, "keyspace") + Selected := getDropdownSelection(t, "keyspace") + + assert.Equal(t, keyspaces, Options) + assert.Equal(t, selectedKs, Selected) + + Options = getDropdownOptions(t, "cell") + Selected = getDropdownSelection(t, "cell") + + assert.Equal(t, cells, Options) + assert.Equal(t, selectedCell, Selected) + + Options = getDropdownOptions(t, "type") + Selected = getDropdownSelection(t, "type") + + assert.Equal(t, types, Options) + assert.Equal(t, selectedType, Selected) + + Options = getDropdownOptions(t, "metric") + Selected = getDropdownSelection(t, "metric") + + assert.Equal(t, metrics, Options) + assert.Equal(t, selectedMetric, Selected) + +} + +func getDropdownSelection(t *testing.T, group string) string { + elem, err := wd.FindElement(selenium.ByTagName, "vt-status") + require.Nil(t, err) + elem, err = elem.FindElement(selenium.ByID, group) + require.Nil(t, err) + elem, err = elem.FindElement(selenium.ByTagName, "label") + require.Nil(t, err) + + tx, err := elem.Text() + require.Nil(t, err) + return tx +} + +func getDropdownOptions(t *testing.T, group string) []string { + elem, err := wd.FindElement(selenium.ByTagName, "vt-status") + require.Nil(t, err) + elem, err = elem.FindElement(selenium.ByID, group) + require.Nil(t, err) + elems, err := elem.FindElements(selenium.ByTagName, "option") + require.Nil(t, err) + + var out []string + for _, elem = range elems { + tx, err := elem.Text() + require.Nil(t, err) + out = append(out, tx) + } + + return out +} + +func getDashboardKeyspaces(t *testing.T) []string { + wait(t, selenium.ByTagName, "vt-dashboard") + + dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") + require.Nil(t, err) + + ksCards, err := dashboardContent.FindElements(selenium.ByClassName, "vt-keyspace-card") + var out []string + for _, ks := range ksCards { + txt, err := ks.Text() + require.Nil(t, err) + out = append(out, txt) + } + return out +} + +func getDashboardShards(t *testing.T) []string { + wait(t, selenium.ByTagName, "vt-dashboard") + + dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") + require.Nil(t, err) + + ksCards, err := dashboardContent.FindElements(selenium.ByClassName, "vt-shard-stats") + var out []string + for _, ks := range ksCards { + txt, err := ks.Text() + require.Nil(t, err) + out = append(out, txt) + } + return out +} + +func getKeyspaceShard(t *testing.T) []string { + wait(t, selenium.ByTagName, "vt-keyspace-view") + + ksContent, err := wd.FindElement(selenium.ByTagName, "vt-keyspace-view") + require.Nil(t, err) + + shards, err := ksContent.FindElements(selenium.ByClassName, "vt-serving-shard") + require.Nil(t, err) + var out []string + for _, s := range shards { + txt, err := s.Text() + require.Nil(t, err) + + out = append(out, txt) + } + return out +} + +func getShardTablets(t *testing.T) ([]string, []string) { + wait(t, selenium.ByTagName, "vt-shard-view") + shardContent, err := wd.FindElement(selenium.ByTagName, "vt-shard-view") + require.Nil(t, err) + + tableRows, err := shardContent.FindElements(selenium.ByTagName, "tr") + tableRows = tableRows[1:] + + var tabletTypes, tabletUIDs []string + for _, row := range tableRows { + columns, err := row.FindElements(selenium.ByTagName, "td") + require.Nil(t, err) + + typ, err := columns[1].FindElement(selenium.ByClassName, "ui-cell-data") + require.Nil(t, err) + + typTxt, err := typ.Text() + require.Nil(t, err) + + tabletTypes = append(tabletTypes, typTxt) + + uid, err := columns[3].FindElement(selenium.ByClassName, "ui-cell-data") + require.Nil(t, err) + + uidTxt, err := uid.Text() + require.Nil(t, err) + + tabletUIDs = append(tabletUIDs, uidTxt) + } + + return tabletTypes, tabletUIDs +} + +// navigation +func navigateToDashBoard(t *testing.T) { + err := wd.Get(vtctldAddr + "/app2") + require.Nil(t, err) + + wait(t, selenium.ByID, "test_keyspace") +} + +func navigateToKeyspaceView(t *testing.T) { + navigateToDashBoard(t) + dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") + require.Nil(t, err) + ksCard, err := dashboardContent.FindElements(selenium.ByClassName, "vt-card") + require.Nil(t, err) + require.Equal(t, 2, len(ksCard)) + + shardStarts, err := ksCard[0].FindElement(selenium.ByTagName, "md-list") + require.Nil(t, err) + + err = shardStarts.Click() + require.Nil(t, err) + + wait(t, selenium.ByClassName, "vt-card") +} + +func navigateToShardView(t *testing.T) { + navigateToKeyspaceView(t) + ksContent, err := wd.FindElement(selenium.ByTagName, "vt-keyspace-view") + require.Nil(t, err) + + shardCards, err := ksContent.FindElements(selenium.ByClassName, "vt-serving-shard") + require.Nil(t, err) + require.Equal(t, 2, len(shardCards)) + + err = shardCards[0].Click() + require.Nil(t, err) + + wait(t, selenium.ByID, "1") +} + +func wait(t *testing.T, by, val string) { + err := wd.WaitWithTimeout(func(xwd selenium.WebDriver) (bool, error) { + _, err := xwd.FindElement(by, val) + return err == nil, nil + }, selenium.DefaultWaitTimeout) + require.Nil(t, err) +} + +func assertDialogCommand(t *testing.T, dialog selenium.WebElement, cmds []string) { + elms, err := dialog.FindElements(selenium.ByClassName, "vt-sheet") + require.Nil(t, err) + + var tmpCmd []string + for _, elm := range elms { + txt, err := elm.Text() + require.Nil(t, err) + tmpCmd = append(tmpCmd, txt) + } + + assert.ElementsMatch(t, cmds, tmpCmd) +} + +func screenshot(t *testing.T, name string) { + ss, err := wd.Screenshot() + require.Nil(t, err) + f, err := os.Create("./" + name) + require.Nil(t, err) + _, err = f.Write(ss) + require.Nil(t, err) +} diff --git a/go/test/endtoend/vtctldweb/vtctld_web_test.go b/go/test/endtoend/vtctldweb/vtctld_web_test.go new file mode 100644 index 00000000000..359d2b6c9bc --- /dev/null +++ b/go/test/endtoend/vtctldweb/vtctld_web_test.go @@ -0,0 +1,244 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtctldweb + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tebeka/selenium" +) + +func TestRealtimeStats(t *testing.T) { + err := wd.Get(vtctldAddr + "/app2") + require.Nil(t, err) + + statusBtn, err := wd.FindElement(selenium.ByPartialLinkText, "Status") + require.Nil(t, err) + + err = statusBtn.Click() + require.Nil(t, err) + + wait(t, selenium.ByTagName, "vt-status") + + testCases := [8][5]string{ + {"", "", "all", "all", "all"}, + {"type", "REPLICA", "all", "all", "REPLICA"}, + {"cell", "test2", "all", "test2", "REPLICA"}, + {"keyspace", "test_keyspace", "test_keyspace", "test2", "REPLICA"}, + {"cell", "all", "test_keyspace", "all", "REPLICA"}, + {"type", "all", "test_keyspace", "all", "all"}, + {"cell", "test2", "test_keyspace", "test2", "all"}, + {"keyspace", "all", "all", "test2", "all"}, + } + for _, k := range testCases { + if k[0] != "" && k[1] != "" { + changeDropdownOptions(t, k[0], k[1]) + } + + tabletOption := []string{"all", "MASTER", "REPLICA", "RDONLY"} + if k[3] == "test2" { + tabletOption = []string{"all", "REPLICA", "RDONLY"} + } + + checkNewView(t, []string{"all", ks1, ks2}, []string{"all", "test", "test2"}, tabletOption, []string{"lag", "qps", "health"}, k[2], k[3], k[4], "health") + } +} + +func TestShardView(t *testing.T) { + navigateToShardView(t) + + tabletTypes, tabletUIDs := getShardTablets(t) + + assert.ElementsMatch(t, []string{"master", "replica", "rdonly", "rdonly", "replica", "replica", "rdonly", "rdonly"}, tabletTypes) + assert.ElementsMatch(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, tabletUIDs) +} + +func TestKsView(t *testing.T) { + navigateToKeyspaceView(t) + shards := getKeyspaceShard(t) + assert.ElementsMatch(t, []string{"-80", "80-"}, shards) +} + +func TestCreateKs(t *testing.T) { + navigateToDashBoard(t) + + dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") + require.Nil(t, err) + + dialog, err := dashboardContent.FindElement(selenium.ByTagName, "vt-dialog") + require.Nil(t, err) + + dashboardMenu, err := dashboardContent.FindElement(selenium.ByClassName, "vt-menu") + require.Nil(t, err) + + err = dashboardMenu.Click() + require.Nil(t, err) + + dashboardOptions, err := dashboardContent.FindElements(selenium.ByClassName, "ui-menuitem-text") + require.Nil(t, err) + + for _, v := range dashboardOptions { + txt, err := v.Text() + require.Nil(t, err) + + if txt == "New" { + err := v.Click() + require.Nil(t, err) + break + } + } + + inputFields, err := dialog.FindElements(selenium.ByTagName, "md-input") + require.Nil(t, err) + + for i, input := range inputFields { + ele, err := input.FindElement(selenium.ByTagName, "input") + require.Nil(t, err) + switch i { + case 0: + err := ele.SendKeys("test_keyspace3") + require.Nil(t, err) + assertDialogCommand(t, dialog, []string{"CreateKeyspace", "-force=false", "test_keyspace3"}) + + case 1: + err := ele.SendKeys("test_id") + require.Nil(t, err) + assertDialogCommand(t, dialog, []string{"CreateKeyspace", "-sharding_column_name=test_id", "-sharding_column_type=UINT64", "-force=false", "test_keyspace3"}) + } + } + + dropdown, err := dialog.FindElement(selenium.ByTagName, "p-dropdown") + require.Nil(t, err) + + err = dropdown.Click() + require.Nil(t, err) + + options, err := dropdown.FindElements(selenium.ByTagName, "li") + require.Nil(t, err) + + err = options[1].Click() + require.Nil(t, err) + + assertDialogCommand(t, dialog, []string{"CreateKeyspace", "-sharding_column_name=test_id", "-sharding_column_type=BYTES", "-force=false", "test_keyspace3"}) + + create, err := dialog.FindElement(selenium.ByID, "vt-action") + require.Nil(t, err) + err = create.Click() + require.Nil(t, err) + + dismiss, err := dialog.FindElement(selenium.ByID, "vt-dismiss") + require.Nil(t, err) + err = dismiss.Click() + require.Nil(t, err) + + ksNames := getDashboardKeyspaces(t) + assert.ElementsMatch(t, []string{"test_keyspace", "test_keyspace2", "test_keyspace3"}, ksNames) + + testKs, err := dashboardContent.FindElements(selenium.ByClassName, "vt-card") + require.Nil(t, err) + menu, err := testKs[2].FindElement(selenium.ByClassName, "vt-menu") + require.Nil(t, err) + err = menu.Click() + require.Nil(t, err) + + options, err = testKs[2].FindElements(selenium.ByTagName, "li") + require.Nil(t, err) + for _, v := range options { + txt, err := v.Text() + require.Nil(t, err) + + if txt == "Delete" { + err := v.Click() + require.Nil(t, err) + break + } + } + + delete, err := dialog.FindElement(selenium.ByID, "vt-action") + require.Nil(t, err) + err = delete.Click() + require.Nil(t, err) + + dismiss, err = dialog.FindElement(selenium.ByID, "vt-dismiss") + require.Nil(t, err) + err = dismiss.Click() + require.Nil(t, err) + + ksNames = getDashboardKeyspaces(t) + assert.ElementsMatch(t, []string{"test_keyspace", "test_keyspace2"}, ksNames) +} + +func TestDashboard(t *testing.T) { + navigateToDashBoard(t) + ksNames := getDashboardKeyspaces(t) + assert.ElementsMatch(t, []string{"test_keyspace", "test_keyspace2"}, ksNames) + shardNames := getDashboardShards(t) + assert.ElementsMatch(t, []string{"2 Shards", "1 Shards"}, shardNames) +} + +func TestDashboardValidate(t *testing.T) { + navigateToDashBoard(t) + dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") + require.Nil(t, err) + + menu, err := dashboardContent.FindElement(selenium.ByClassName, "vt-menu") + require.Nil(t, err) + err = menu.Click() + require.Nil(t, err) + + firstOption, err := dashboardContent.FindElement(selenium.ByClassName, "ui-menuitem-text") + require.Nil(t, err) + screenshot(t, "first_option") + txt, err := firstOption.Text() + require.Nil(t, err) + assert.Equal(t, "Validate", txt) + + err = firstOption.Click() + require.Nil(t, err) + + dialog, err := dashboardContent.FindElement(selenium.ByTagName, "vt-dialog") + require.Nil(t, err) + + assertDialogCommand(t, dialog, []string{"Validate", "-ping-tablets=false"}) + + checkBoxes, err := dialog.FindElements(selenium.ByClassName, "md-checkbox-inner-container") + require.Nil(t, err) + + err = checkBoxes[0].Click() + require.Nil(t, err) + + assertDialogCommand(t, dialog, []string{"Validate", "-ping-tablets"}) + + validate, err := dialog.FindElement(selenium.ByID, "vt-action") + require.Nil(t, err) + err = validate.Click() + require.Nil(t, err) + validateResp, err := dialog.FindElement(selenium.ByClassName, "vt-resp") + require.Nil(t, err) + txt, err = validateResp.Text() + require.Nil(t, err) + + fmt.Printf("Validate command response: %s\n", txt) + + dismiss, err := dialog.FindElement(selenium.ByID, "vt-dismiss") + require.Nil(t, err) + err = dismiss.Click() + require.Nil(t, err) +} diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 45e126c6f2f..e2ac0b2dca8 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -785,7 +785,6 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql } execStart := time.Now() defer func() { logStats.ExecuteTime = time.Since(execStart) }() - switch strings.ToLower(show.Type) { case sqlparser.KeywordString(sqlparser.COLLATION), sqlparser.KeywordString(sqlparser.VARIABLES): if show.Scope == sqlparser.VitessMetadataStr { @@ -860,24 +859,19 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql RowsAffected: rowsAffected, }, err case "create table": - if destKeyspace == "" && show.HasTable() { - // For "show create table", if there isn't a targeted keyspace already - // we can either get a keyspace from the statement or potentially from - // the vschema. - - if !show.Table.Qualifier.IsEmpty() { - // Explicit keyspace was passed. Use that for targeting but remove from the query itself. - destKeyspace = show.Table.Qualifier.String() - show.Table.Qualifier = sqlparser.NewTableIdent("") - sql = sqlparser.String(show) - } else { - // No keyspace was indicated. Try to find one using the vschema. - tbl, err := e.VSchema().FindTable("", show.Table.Name.String()) - if err == nil { - destKeyspace = tbl.Keyspace.Name - } + if !show.Table.Qualifier.IsEmpty() { + // Explicit keyspace was passed. Use that for targeting but remove from the query itself. + destKeyspace = show.Table.Qualifier.String() + show.Table.Qualifier = sqlparser.NewTableIdent("") + } else { + // No keyspace was indicated. Try to find one using the vschema. + tbl, err := e.VSchema().FindTable(destKeyspace, show.Table.Name.String()) + if err != nil { + return nil, err } + destKeyspace = tbl.Keyspace.Name } + sql = sqlparser.String(show) case sqlparser.KeywordString(sqlparser.COLUMNS): if !show.OnTable.Qualifier.IsEmpty() { destKeyspace = show.OnTable.Qualifier.String() diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 115bf89da7a..e6da29eba0d 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -802,9 +802,10 @@ func TestExecutorShow(t *testing.T) { t.Errorf("%v:\n%+v, want\n%+v", query, qr, wantqr) } + wantErrNoTable := "table unknown_table not found" _, err = executor.Execute(context.Background(), "TestExecute", session, "show create table unknown_table", nil) - if err != errNoKeyspace { - t.Errorf("Got: %v. Want: %v", err, errNoKeyspace) + if err.Error() != wantErrNoTable { + t.Errorf("Got: %v. Want: %v", err, wantErrNoTable) } // SHOW CREATE table using vschema to find keyspace. @@ -830,6 +831,14 @@ func TestExecutorShow(t *testing.T) { t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) } + // Set desitation keyspace in session + session.TargetString = KsTestUnsharded + _, err = executor.Execute(context.Background(), "TestExecute", session, "show create table unknown", nil) + require.NoError(t, err) + // Reset target string so other tests dont fail. + session.TargetString = "@master" + _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show full columns from unknown from %v", KsTestUnsharded), nil) + require.NoError(t, err) for _, query := range []string{"show charset", "show character set"} { qr, err := executor.Execute(context.Background(), "TestExecute", session, query, nil) require.NoError(t, err) From c2c375b007e9c7b3ec7bf7e9665b53a78a4f618e Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Tue, 18 Feb 2020 14:37:58 +0530 Subject: [PATCH 122/825] testcase added in config. Signed-off-by: pradip parmar --- test/config.json | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/config.json b/test/config.json index 7f4acf8caa6..880a45b7331 100644 --- a/test/config.json +++ b/test/config.json @@ -94,17 +94,6 @@ "RetryMax": 0, "Tags": [] }, - "vtctld_web": { - "File": "vtctld_web_test.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 1, - "RetryMax": 0, - "Tags": [ - "webdriver" - ] - }, "vttest_sample": { "File": "vttest_sample_test.py", "Args": [], @@ -510,6 +499,15 @@ "Shard": 17, "RetryMax": 0, "Tags": [] + }, + "web_test": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vtctldweb"], + "Command": [], + "Manual": false, + "Shard": 21, + "RetryMax": 0, + "Tags": [] } } } \ No newline at end of file From 6c71bb2d082b36f86c9f7a51d0f796bca107ce44 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Tue, 18 Feb 2020 14:40:04 +0530 Subject: [PATCH 123/825] removed unsed functions. Signed-off-by: pradip parmar --- go.sum | 3 + .../vtctldweb/vtctld_web_main_test.go | 111 ------------------ 2 files changed, 3 insertions(+), 111 deletions(-) diff --git a/go.sum b/go.sum index 71ccc5ab9d3..d1c40c8a241 100644 --- a/go.sum +++ b/go.sum @@ -12,7 +12,9 @@ github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3Dw github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA= github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg= @@ -38,6 +40,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a h1:Ed33uJE74ksDaYfdY72gK7Cg//o2FgsqlqUfBW079T8= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go index 6f4834a34dd..f83383649d1 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -21,7 +21,6 @@ import ( "fmt" "os" "os/exec" - "strings" "syscall" "testing" "time" @@ -206,116 +205,6 @@ func TeardownWebDriver() { } } -func GetKeyspaces() ([]string, error) { - element, err := wd.FindElement(selenium.ByID, "content") - if err != nil { - return nil, err - } - - keyspace, err := element.FindElements(selenium.ByTagName, "md-card") - if err != nil { - return nil, err - } - - var keyspaceNames []string - for _, ks := range keyspace { - e, err := ks.FindElement(selenium.ByTagName, "h2") - if err != nil { - return nil, err - } - - k, err := e.Text() - if err != nil { - return nil, err - } - - keyspaceNames = append(keyspaceNames, k) - } - - return keyspaceNames, nil -} - -func GetKeyspaceElem(ks string) (selenium.WebElement, error) { - var ksElem selenium.WebElement - err := wd.WaitWithTimeout(func(xwd selenium.WebDriver) (bool, error) { - elem, err := xwd.FindElement(selenium.ByID, ks+"-card") - if err != nil { - return false, err - } - - ksElem = elem - return true, nil - - }, selenium.DefaultWaitTimeout) - - return ksElem, err -} - -func GetShards(ks string) ([]string, error) { - return getList(ks + "-shards") -} -func GetServingShards(ks string) ([]string, error) { - return getList(ks + "-serving") -} -func GetInactiveShards(ks string) ([]string, error) { - return getList(ks + "-inactive") -} - -func getList(pre string) ([]string, error) { - shard, err := wd.FindElement(selenium.ByID, pre+"-list") - if err != nil { - return nil, err - } - - shardNames, err := shard.Text() - return strings.Split(shardNames, "\n"), err -} - -func GetShardElem(ks, shard string) (selenium.WebElement, error) { - - ksElem, err := GetKeyspaceElem(ks) - if err != nil { - return nil, err - } - - return ksElem.FindElement(selenium.ByLinkText, shard) -} - -func GetTabletNames() (map[string]string, error) { - ele, err := wd.FindElement(selenium.ByID, "tablets") - if err != nil { - return nil, err - } - - mdCards, err := ele.FindElements(selenium.ByTagName, "md-card") - if err != nil { - return nil, err - } - - var tabletTitles []string - for _, e := range mdCards { - toolBar, err := e.FindElement(selenium.ByTagName, "md-toolbar") - if err != nil { - return nil, err - } - t, err := toolBar.Text() - if err != nil { - return nil, err - } - - tabletTitles = append(tabletTitles, strings.Split(t, "\n")[0]) - } - - out := make(map[string]string) - - for _, tabletTitle := range tabletTitles { - tmp := strings.Split(tabletTitle, " ") - out[tmp[0]] = tmp[1][1 : len(tmp)-1] - } - - return out, nil -} - func checkNewView(t *testing.T, keyspaces, cells, types, metrics []string, selectedKs, selectedCell, selectedType, selectedMetric string) { checkDropdowns(t, keyspaces, cells, types, metrics, selectedKs, selectedCell, selectedType, selectedMetric) checkHeatMaps(t, selectedKs) From 9b835bff4ddc63477c3527f40247280973b51102 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Tue, 18 Feb 2020 16:24:25 +0530 Subject: [PATCH 124/825] comments added for the functions. Signed-off-by: pradip parmar --- .../vtctldweb/vtctld_web_main_test.go | 44 ++++++++++++------- go/test/endtoend/vtctldweb/vtctld_web_test.go | 7 ++- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go index f83383649d1..2e0669ea7fb 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -55,13 +55,14 @@ func TestMain(m *testing.M) { exitcode, err := func() (int, error) { + // runs Xvfb in background tearDownXvfb, err := RunXvfb() if err != nil { return 1, err } defer tearDownXvfb() - // cluster setup + // cluster setup using vtcombo topology := new(vttestpb.VTTestTopology) topology.Cells = []string{"test", "test2"} topology.Keyspaces = []*vttestpb.Keyspace{ @@ -91,7 +92,6 @@ func TestMain(m *testing.M) { } defer TeardownWebDriver() - // var cfg vttest.Config var cfg vttest.Config cfg.Topology = topology cfg.SchemaDir = os.Getenv("VTROOT") + "/test/vttest_schema" @@ -103,6 +103,7 @@ func TestMain(m *testing.M) { err = localCluster.Setup() defer localCluster.TearDown() + vtctldAddr = fmt.Sprintf("http://localhost:%d", localCluster.Env.PortForProtocol("vtcombo", "port")) if err != nil { return 1, err @@ -153,6 +154,7 @@ func RunXvfb() (func() error, error) { func CreateWebDriver() error { selenium.SetDebug(true) + // Set common Options options := selenium.ChromeDriver(os.Getenv("VTROOT") + "/dist") if os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true" { @@ -166,12 +168,16 @@ func CreateWebDriver() error { var err error wd, err = selenium.NewRemote(capabilities, fmt.Sprintf("%s:%s@localhost:4445/wd/hub", os.Getenv("SAUCE_USERNAME"), os.Getenv("SAUCE_ACCESS_KEY"))) - return err - } + if err != nil { + return err + } - cc := selenium.Capabilities{ - "browserName": "chrome", + name, err := wd.CurrentWindowHandle() + return wd.ResizeWindow(name, 1280, 1024) } + + // Only testing against Chrome for now + cc := selenium.Capabilities{"browserName": "chrome"} cc.AddChrome(chrome.Capabilities{ Args: []string{ "--disable-gpu", @@ -194,7 +200,6 @@ func CreateWebDriver() error { } name, err := wd.CurrentWindowHandle() return wd.ResizeWindow(name, 1280, 1024) - // return err } func TeardownWebDriver() { @@ -248,6 +253,7 @@ func checkHeatMaps(t *testing.T, selectedKs string) { assert.Equal(t, selectedKs, headingTxt) } +// changeDropdownOptions changes the selected value of dropdown. func changeDropdownOptions(t *testing.T, dropdownID, dropdownValue string) { statusContent, err := wd.FindElement(selenium.ByTagName, "vt-status") require.Nil(t, err) @@ -271,6 +277,7 @@ func changeDropdownOptions(t *testing.T, dropdownID, dropdownValue string) { } } +// checkDropdowns validates the dropdown values and selected value. func checkDropdowns(t *testing.T, keyspaces, cells, types, metrics []string, selectedKs, selectedCell, selectedType, selectedMetric string) { Options := getDropdownOptions(t, "keyspace") @@ -299,6 +306,8 @@ func checkDropdowns(t *testing.T, keyspaces, cells, types, metrics []string, sel } +// get element functions +// getDropdownSelection fetchs selected value for corresponding group. func getDropdownSelection(t *testing.T, group string) string { elem, err := wd.FindElement(selenium.ByTagName, "vt-status") require.Nil(t, err) @@ -312,6 +321,7 @@ func getDropdownSelection(t *testing.T, group string) string { return tx } +// getDropdownOptions fetchs list of option available for corresponding group. func getDropdownOptions(t *testing.T, group string) []string { elem, err := wd.FindElement(selenium.ByTagName, "vt-status") require.Nil(t, err) @@ -330,6 +340,7 @@ func getDropdownOptions(t *testing.T, group string) []string { return out } +// getDashboardKeyspaces fetches keyspaces from the dashboard. func getDashboardKeyspaces(t *testing.T) []string { wait(t, selenium.ByTagName, "vt-dashboard") @@ -346,6 +357,7 @@ func getDashboardKeyspaces(t *testing.T) []string { return out } +// getDashboardShards fetches shards from the dashboard. func getDashboardShards(t *testing.T) []string { wait(t, selenium.ByTagName, "vt-dashboard") @@ -380,6 +392,7 @@ func getKeyspaceShard(t *testing.T) []string { return out } +// getShardTablets gives list of tablet type and uid. func getShardTablets(t *testing.T) ([]string, []string) { wait(t, selenium.ByTagName, "vt-shard-view") shardContent, err := wd.FindElement(selenium.ByTagName, "vt-shard-view") @@ -413,7 +426,8 @@ func getShardTablets(t *testing.T) ([]string, []string) { return tabletTypes, tabletUIDs } -// navigation +// navigation functions +// navigateToDashBoard navigates chrome screen to dashboard of vitess. func navigateToDashBoard(t *testing.T) { err := wd.Get(vtctldAddr + "/app2") require.Nil(t, err) @@ -421,6 +435,7 @@ func navigateToDashBoard(t *testing.T) { wait(t, selenium.ByID, "test_keyspace") } +// navigateToKeyspaceView navigates chrome screen to first keyspace. func navigateToKeyspaceView(t *testing.T) { navigateToDashBoard(t) dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") @@ -438,6 +453,7 @@ func navigateToKeyspaceView(t *testing.T) { wait(t, selenium.ByClassName, "vt-card") } +// navigateToShardView navigates chrome screen to the first shard of first keyspace. func navigateToShardView(t *testing.T) { navigateToKeyspaceView(t) ksContent, err := wd.FindElement(selenium.ByTagName, "vt-keyspace-view") @@ -453,6 +469,8 @@ func navigateToShardView(t *testing.T) { wait(t, selenium.ByID, "1") } +// other utility +// wait waits for the given element to be discoverable. func wait(t *testing.T, by, val string) { err := wd.WaitWithTimeout(func(xwd selenium.WebDriver) (bool, error) { _, err := xwd.FindElement(by, val) @@ -461,6 +479,7 @@ func wait(t *testing.T, by, val string) { require.Nil(t, err) } +// assertDialogCommand validates the command in dialog. func assertDialogCommand(t *testing.T, dialog selenium.WebElement, cmds []string) { elms, err := dialog.FindElements(selenium.ByClassName, "vt-sheet") require.Nil(t, err) @@ -474,12 +493,3 @@ func assertDialogCommand(t *testing.T, dialog selenium.WebElement, cmds []string assert.ElementsMatch(t, cmds, tmpCmd) } - -func screenshot(t *testing.T, name string) { - ss, err := wd.Screenshot() - require.Nil(t, err) - f, err := os.Create("./" + name) - require.Nil(t, err) - _, err = f.Write(ss) - require.Nil(t, err) -} diff --git a/go/test/endtoend/vtctldweb/vtctld_web_test.go b/go/test/endtoend/vtctldweb/vtctld_web_test.go index 359d2b6c9bc..5e06a49df9c 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_test.go @@ -25,6 +25,7 @@ import ( "github.com/tebeka/selenium" ) +// TestRealtimeStats checks the status by changing dropdown values. func TestRealtimeStats(t *testing.T) { err := wd.Get(vtctldAddr + "/app2") require.Nil(t, err) @@ -61,6 +62,7 @@ func TestRealtimeStats(t *testing.T) { } } +// TestShardView validates tablet type and uids. func TestShardView(t *testing.T) { navigateToShardView(t) @@ -70,12 +72,14 @@ func TestShardView(t *testing.T) { assert.ElementsMatch(t, []string{"1", "2", "3", "4", "5", "6", "7", "8"}, tabletUIDs) } +// TestKsView validates the shard names for keyspace func TestKsView(t *testing.T) { navigateToKeyspaceView(t) shards := getKeyspaceShard(t) assert.ElementsMatch(t, []string{"-80", "80-"}, shards) } +// TestCreateKs validates the keyspace creation using ui. func TestCreateKs(t *testing.T) { navigateToDashBoard(t) @@ -185,6 +189,7 @@ func TestCreateKs(t *testing.T) { assert.ElementsMatch(t, []string{"test_keyspace", "test_keyspace2"}, ksNames) } +// TestDashboard validate the keyspaces and shard in dashboard. func TestDashboard(t *testing.T) { navigateToDashBoard(t) ksNames := getDashboardKeyspaces(t) @@ -193,6 +198,7 @@ func TestDashboard(t *testing.T) { assert.ElementsMatch(t, []string{"2 Shards", "1 Shards"}, shardNames) } +// TestDashboardValidate validates the validate command from the ui. func TestDashboardValidate(t *testing.T) { navigateToDashBoard(t) dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") @@ -205,7 +211,6 @@ func TestDashboardValidate(t *testing.T) { firstOption, err := dashboardContent.FindElement(selenium.ByClassName, "ui-menuitem-text") require.Nil(t, err) - screenshot(t, "first_option") txt, err := firstOption.Text() require.Nil(t, err) assert.Equal(t, "Validate", txt) From 4b401297dc4d48d0c6f7dee952b87a8e7c3706a7 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 18 Feb 2020 14:04:41 +0100 Subject: [PATCH 125/825] Plan IN updates with scatter for now Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/delete.go | 16 +- .../vtgate/planbuilder/testdata/dml_cases.txt | 1846 ++++++++++++++++- go/vt/vtgate/planbuilder/update.go | 67 +- 3 files changed, 1878 insertions(+), 51 deletions(-) diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index 5dc44b3d6d6..cb7974c5ab8 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -74,22 +74,20 @@ func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Del edel.TargetDestination = ro.eroute.TargetDestination return edel, nil } - edel.Vindex, edel.Values, err = getDMLRouting(del.Where, edel.Table) - // We couldn't generate a route for a single shard - // Execute a delete sharded - if err != nil { - edel.Opcode = engine.DeleteScatter - } else { - edel.Opcode = engine.DeleteEqual - } + routingType, vindex, _, values := getDMLRouting(del.Where, edel.Table) - if edel.Opcode == engine.DeleteScatter { + if routingType == scatter { + edel.Opcode = engine.DeleteScatter if len(edel.Table.Owned) != 0 { return nil, errors.New("unsupported: multi shard delete on a table with owned lookup vindexes") } if del.Limit != nil { return nil, errors.New("unsupported: multi shard delete with limit") } + } else { + edel.Values = values + edel.Vindex = vindex + edel.Opcode = engine.DeleteEqual } edel.OwnedVindexQuery = generateDeleteSubquery(del, edel.Table) diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt index f7238d578cc..e51d7d9b20d 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt @@ -1,24 +1,1815 @@ -# update multiple vindex value to null -"update user set name = null where id in (1, 2, 3)" +# update table not found +"update nouser set val = 1" +"table nouser not found" + +# delete table not found +"delete from nouser" +"table nouser not found" + +# explicit keyspace reference +"update main.m1 set val = 1" +{ + "Original": "update main.m1 set val = 1", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update m1 set val = 1" + } +} + +# update unsharded +"update unsharded set val = 1" +{ + "Original": "update unsharded set val = 1", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded set val = 1" + } +} + +# subqueries in unsharded update +"update unsharded set col = (select col from unsharded limit 1)" +{ + "Original": "update unsharded set col = (select col from unsharded limit 1)", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded set col = (select col from unsharded limit 1)" + } +} + +# unsharded union in subquery of unsharded update +"update unsharded set col = (select id from unsharded union select id from unsharded)" +{ + "Original": "update unsharded set col = (select id from unsharded union select id from unsharded)", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded set col = (select id from unsharded union select id from unsharded)" + } +} + +# unsharded join in subquery of unsharded update +"update unsharded set col = (select id from unsharded a join unsharded b on a.id = b.id)" +{ + "Original": "update unsharded set col = (select id from unsharded a join unsharded b on a.id = b.id)", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded set col = (select id from unsharded as a join unsharded as b on a.id = b.id)" + } +} + +# update with join subquery +"update unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col < 1000" +{ + "Original": "update unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col \u003c 1000", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col \u003c 1000" + } +} + +# routing rules: updated of a routed table +"update route1 set a=1 where id=1" +{ + "Original": "update route1 set a=1 where id=1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user as route1 set a = 1 where id = 1", + "Vindex": "user_index", + "Values": [ + 1 + ], + "Table": "user" + } +} + +# routing rules: subquery chooses unsharded route for update +"update unsharded_a set a=(select a from route2)" +{ + "Original": "update unsharded_a set a=(select a from route2)", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded_a set a = (select a from unsharded as route2)" + } +} + +# delete unsharded +"delete from unsharded" +{ + "Original": "delete from unsharded", + "Instructions": { + "Opcode": "DeleteUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "delete from unsharded" + } +} + +# update by primary keyspace id +"update user set val = 1 where id = 1" +{ + "Original": "update user set val = 1 where id = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set val = 1 where id = 1", + "Vindex": "user_index", + "Values": [1], + "Table": "user" + } +} + + +# update by primary keyspace id with alias +"update user as user_alias set val = 1 where user_alias.id = 1" +{ + "Original": "update user as user_alias set val = 1 where user_alias.id = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user as user_alias set val = 1 where user_alias.id = 1", + "Vindex": "user_index", + "Values": [1], + "Table": "user" + } +} + +# update by primary keyspace id with parenthesized expression +"update user set val = 1 where (id = 1)" +{ + "Original": "update user set val = 1 where (id = 1)", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set val = 1 where (id = 1)", + "Vindex": "user_index", + "Values": [ + 1 + ], + "Table": "user" + } +} + +# update by primary keyspace id with multi-part where clause with parens +"update user set val = 1 where (name = 'foo' and id = 1)" +{ + "Original": "update user set val = 1 where (name = 'foo' and id = 1)", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set val = 1 where (name = 'foo' and id = 1)", + "Vindex": "user_index", + "Values": [1], + "Table": "user" + } +} + +# update by primary keyspace id, changing one vindex column +"update user_metadata set email = 'juan@vitess.io' where user_id = 1" +{ + "Original": "update user_metadata set email = 'juan@vitess.io' where user_id = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_metadata set email = 'juan@vitess.io' where user_id = 1", + "Vindex": "user_index", + "Values": [1], + "ChangedVindexValues": { + "email_user_map": ["juan@vitess.io"] + }, + "Table": "user_metadata", + "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update" + } +} + +# update by primary keyspace id, changing same vindex twice +"update user_metadata set email = 'a', email = 'b' where user_id = 1" +"column has duplicate set values: 'email'" + +# update by primary keyspace id, changing multiple vindex columns +"update user_metadata set email = 'juan@vitess.io', address = '155 5th street' where user_id = 1" +{ + "Original": "update user_metadata set email = 'juan@vitess.io', address = '155 5th street' where user_id = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_metadata set email = 'juan@vitess.io', address = '155 5th street' where user_id = 1", + "Vindex": "user_index", + "Values": [1], + "ChangedVindexValues": { + "address_user_map": ["155 5th street"], + "email_user_map": ["juan@vitess.io"] + }, + "Table": "user_metadata", + "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update" + } +} + +# update by primary keyspace id, changing one vindex column, using order by and limit +"update user_metadata set email = 'juan@vitess.io' where user_id = 1 order by user_id asc limit 10" +{ + "Original": "update user_metadata set email = 'juan@vitess.io' where user_id = 1 order by user_id asc limit 10", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_metadata set email = 'juan@vitess.io' where user_id = 1 order by user_id asc limit 10", + "Vindex": "user_index", + "Values": [ + 1 + ], + "ChangedVindexValues": { + "email_user_map": ["juan@vitess.io"] + }, + "Table": "user_metadata", + "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 order by user_id asc limit 10 for update" + } +} + +# update by primary keyspace id, stray where clause +"update user set val = 1 where id = id2 and id = 1" +{ + "Original": "update user set val = 1 where id = id2 and id = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set val = 1 where id = id2 and id = 1", + "Vindex": "user_index", + "Values": [1], + "Table": "user" + } +} + +# update by primary keyspace id, stray where clause with conversion error +"update user set val = 1 where id = 18446744073709551616 and id = 1" +{ + "Original": "update user set val = 1 where id = 18446744073709551616 and id = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set val = 1 where id = 18446744073709551616 and id = 1", + "Vindex": "user_index", + "Values": [1], + "Table": "user" + } +} + +# delete from by primary keyspace id +"delete from user where id = 1" +{ + "Original": "delete from user where id = 1", + "Instructions": { + "Opcode": "DeleteEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user where id = 1", + "Vindex": "user_index", + "Values": [1], + "Table": "user", + "OwnedVindexQuery": "select Name, Costly from user where id = 1 for update" + } +} + +# multi-table delete with comma join +"delete a from unsharded_a a, unsharded_b b where a.id = b.id and b.val = 1" +{ + "Original": "delete a from unsharded_a a, unsharded_b b where a.id = b.id and b.val = 1", + "Instructions": { + "Opcode": "DeleteUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "delete a from unsharded_a as a, unsharded_b as b where a.id = b.id and b.val = 1" + } +} + +# multi-table delete with ansi join +"delete a from unsharded_a a join unsharded_b b on a.id = b.id where b.val = 1" +{ + "Original": "delete a from unsharded_a a join unsharded_b b on a.id = b.id where b.val = 1", + "Instructions": { + "Opcode": "DeleteUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "delete a from unsharded_a as a join unsharded_b as b on a.id = b.id where b.val = 1" + } +} + +#delete with join from subquery +"delete foo from unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000" +{ + "Original": "delete foo from unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col \u003c 1000", + "Instructions": { + "Opcode": "DeleteUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "delete foo from unsharded as foo left join (select id from unsharded where col is not null order by col desc limit 10) as keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col \u003c 1000" + } +} + +# routing rules: deleted from a routed table +"delete from route1 where id = 1" +{ + "Original": "delete from route1 where id = 1", + "Instructions": { + "Opcode": "DeleteEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user as route1 where id = 1", + "Vindex": "user_index", + "Values": [ + 1 + ], + "Table": "user", + "OwnedVindexQuery": "select Name, Costly from user where id = 1 for update" + } +} + +# routing rules: subquery chooses unsharded route for delete +"delete from unsharded_a where a=(select a from route2)" +{ + "Original": "delete from unsharded_a where a=(select a from route2)", + "Instructions": { + "Opcode": "DeleteUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "delete from unsharded_a where a = (select a from unsharded as route2)" + } +} + +# update by lookup +"update music set val = 1 where id = 1" +{ + "Original": "update music set val = 1 where id = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update music set val = 1 where id = 1", + "Vindex": "music_user_map", + "Values": [1], + "Table": "music" + } +} + +# update multi-table ansi join +"update unsharded_a a join unsharded_b b on a.id = b.id set a.val = 'foo' where b.val = 1" +{ + "Original": "update unsharded_a a join unsharded_b b on a.id = b.id set a.val = 'foo' where b.val = 1", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded_a as a join unsharded_b as b on a.id = b.id set a.val = 'foo' where b.val = 1" + } +} + +# update multi-table comma join +"update unsharded_a a, unsharded_b b set a.val = 'foo' where a.id = b.id and b.val = 1" +{ + "Original": "update unsharded_a a, unsharded_b b set a.val = 'foo' where a.id = b.id and b.val = 1", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "update unsharded_a as a, unsharded_b as b set a.val = 'foo' where a.id = b.id and b.val = 1" + } +} + +# delete from by lookup +"delete from music where id = 1" +{ + "Original": "delete from music where id = 1", + "Instructions": { + "Opcode": "DeleteEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from music where id = 1", + "Vindex": "music_user_map", + "Values": [1], + "Table": "music", + "OwnedVindexQuery": "select id from music where id = 1 for update" + } +} + +# delete from, no owned vindexes +"delete from music_extra where user_id = 1" +{ + "Original": "delete from music_extra where user_id = 1", + "Instructions": { + "Opcode": "DeleteEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from music_extra where user_id = 1", + "Vindex": "user_index", + "Values": [1], + "Table": "music_extra" + } +} + +# simple insert, no values +"insert into unsharded values()" +{ + "Original": "insert into unsharded values()", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded values ()", + "Table": "unsharded" + } +} + +# simple insert unsharded +"insert into unsharded values(1, 2)" +{ + "Original": "insert into unsharded values(1, 2)", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded values (1, 2)", + "Table": "unsharded" + } +} + +# simple upsert unsharded +"insert into unsharded values(1, 2) on duplicate key update x = 3" +{ + "Original": "insert into unsharded values(1, 2) on duplicate key update x = 3", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded values (1, 2) on duplicate key update x = 3", + "Table": "unsharded" + } +} + +# unsharded insert, no col list with auto-inc and authoritative column list +"insert into unsharded_authoritative values(1,1)" +{ + "Original": "insert into unsharded_authoritative values(1,1)", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded_authoritative(col1, col2) values (:__seq0, 1)", + "Table": "unsharded_authoritative", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + 1 + ] + } + } +} + +# sharded upsert with sharding key set to vindex column +"insert into music(user_id, id) values(1, 2) on duplicate key update user_id = values(user_id)" +{ + "Original": "insert into music(user_id, id) values(1, 2) on duplicate key update user_id = values(user_id)", + "Instructions": { + "Opcode": "InsertShardedIgnore", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into music(user_id, id) values (:_user_id0, :_id0) on duplicate key update user_id = values(user_id)", + "Values": [ + [ + [ + 1 + ] + ], + [ + [ + 2 + ] + ] + ], + "Table": "music", + "Prefix": "insert into music(user_id, id) values ", + "Mid": [ + "(:_user_id0, :_id0)" + ], + "Suffix": " on duplicate key update user_id = values(user_id)" + } +} + +# sharded bulk upsert with sharding key set to vindex column +"insert into music(user_id, id) values (1, 2), (3,4) on duplicate key update user_id = values(user_id)" +{ + "Original": "insert into music(user_id, id) values (1, 2), (3,4) on duplicate key update user_id = values(user_id)", + "Instructions": { + "Opcode": "InsertShardedIgnore", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into music(user_id, id) values (:_user_id0, :_id0), (:_user_id1, :_id1) on duplicate key update user_id = values(user_id)", + "Values": [ + [ + [ + 1, + 3 + ] + ], + [ + [ + 2, + 4 + ] + ] + ], + "Table": "music", + "Prefix": "insert into music(user_id, id) values ", + "Mid": [ + "(:_user_id0, :_id0)", + "(:_user_id1, :_id1)" + ], + "Suffix": " on duplicate key update user_id = values(user_id)" + } +} + +# insert unsharded with select +"insert into unsharded select id from unsharded_auto" +{ + "Original": "insert into unsharded select id from unsharded_auto", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded select id from unsharded_auto", + "Table": "unsharded" + } +} + +# insert unsharded with select with join +"insert into unsharded select id from unsharded join unsharded_auto" +{ + "Original": "insert into unsharded select id from unsharded join unsharded_auto", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded select id from unsharded join unsharded_auto", + "Table": "unsharded" + } +} + +# insert unsharded, invalid value for auto-inc +"insert into unsharded_auto(id, val) values(18446744073709551616, 'aa')" +"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" + +# insert unsharded, column present +"insert into unsharded_auto(id, val) values(1, 'aa')" +{ + "Original": "insert into unsharded_auto(id, val) values(1, 'aa')", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded_auto(id, val) values (:__seq0, 'aa')", + "Table": "unsharded_auto", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + 1 + ] + } + } +} + +# insert unsharded, column absent +"insert into unsharded_auto(val) values('aa')" +{ + "Original": "insert into unsharded_auto(val) values('aa')", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded_auto(val, id) values ('aa', :__seq0)", + "Table": "unsharded_auto", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + null + ] + } + } +} + +# insert unsharded, column absent +"insert into unsharded_auto(val) values(false)" +{ + "Original": "insert into unsharded_auto(val) values(false)", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded_auto(val, id) values (false, :__seq0)", + "Table": "unsharded_auto", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + null + ] + } + } +} + +# insert unsharded, multi-val +"insert into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')" +{ + "Original": "insert into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded_auto(id, val) values (:__seq0, 'aa'), (:__seq1, 'bb')", + "Table": "unsharded_auto", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + 1, + null + ] + } + } +} + + +# unsharded insert subquery in insert value +"insert into unsharded values((select 1 from dual), 1)" +{ + "Original": "insert into unsharded values((select 1 from dual), 1)", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded values ((select 1 from dual), 1)", + "Table": "unsharded" + } +} + +# insert into a routed table +"insert into route1(id) values (1)" +{ + "Original": "insert into route1(id) values (1)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + 1 + ] + }, + "Prefix": "insert into user(id, Name, Costly) values ", + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)" + ] + } +} + +# insert with mimatched column list +"insert into user(id) values (1, 2)" +"column list doesn't match values" + +# insert no column list for sharded authoritative table +"insert into authoritative values(1, 2, 3)" +{ + "Original": "insert into authoritative values(1, 2, 3)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into authoritative(user_id, col1, col2) values (:_user_id0, 2, 3)", + "Values": [ + [ + [ + 1 + ] + ] + ], + "Table": "authoritative", + "Prefix": "insert into authoritative(user_id, col1, col2) values ", + "Mid": [ + "(:_user_id0, 2, 3)" + ] + } +} + +# insert sharded, no values +"insert into user values()" +"no column list" + +# insert with one vindex +"insert into user(id) values (1)" +{ + "Original": "insert into user(id) values (1)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", + "Values": [[[":__seq0"]],[[null]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [1] + }, + "Prefix": "insert into user(id, Name, Costly) values ", + "Mid":["(:_Id0, :_Name0, :_Costly0)"] + } +} + +# insert ignore sharded +"insert ignore into user(id) values (1)" +{ + "Original": "insert ignore into user(id) values (1)", + "Instructions": { + "Opcode": "InsertShardedIgnore", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert ignore into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", + "Values": [[[":__seq0"]],[[null]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [1] + }, + "Prefix": "insert ignore into user(id, Name, Costly) values ", + "Mid":["(:_Id0, :_Name0, :_Costly0)"] + } +} + +# insert on duplicate key +"insert into user(id) values(1) on duplicate key update col = 2" +{ + "Original": "insert into user(id) values(1) on duplicate key update col = 2", + "Instructions": { + "Opcode": "InsertShardedIgnore", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0) on duplicate key update col = 2", + "Values": [[[":__seq0"]],[[null]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + 1 + ] + }, + "Prefix": "insert into user(id, Name, Costly) values ", + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)" + ], + "Suffix": " on duplicate key update col = 2" + } +} + +# insert with one vindex and bind var +"insert into user(id) values (:aa)" +{ + "Original": "insert into user(id) values (:aa)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", + "Values": [[[":__seq0"]],[[null]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + ":aa" + ] + }, + "Prefix": "insert into user(id, Name, Costly) values ", + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)" + ] + } +} + +# insert with non vindex +"insert into user(nonid) values (2)" +{ + "Original": "insert into user(nonid) values (2)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(nonid, id, Name, Costly) values (2, :_Id0, :_Name0, :_Costly0)", + "Values": [[[":__seq0"]],[[null]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [null] + }, + "Prefix": "insert into user(nonid, id, Name, Costly) values ", + "Mid": ["(2, :_Id0, :_Name0, :_Costly0)"] + } +} + +# insert with default seq +"insert into user(id, nonid) values (default, 2)" +{ + "Original": "insert into user(id, nonid) values (default, 2)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(id, nonid, Name, Costly) values (:_Id0, 2, :_Name0, :_Costly0)", + "Values": [[[":__seq0"]],[[null]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [null] + }, + "Prefix": "insert into user(id, nonid, Name, Costly) values ", + "Mid": ["(:_Id0, 2, :_Name0, :_Costly0)"] + } +} + +# insert with non vindex bool value +"insert into user(nonid) values (true)" +{ + "Original": "insert into user(nonid) values (true)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(nonid, id, Name, Costly) values (true, :_Id0, :_Name0, :_Costly0)", + "Values": [[[":__seq0"]],[[null]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [null] + }, + "Prefix": "insert into user(nonid, id, Name, Costly) values ", + "Mid": ["(true, :_Id0, :_Name0, :_Costly0)"] + } +} + +# insert with all vindexes supplied +"insert into user(nonid, name, id) values (2, 'foo', 1)" +{ + "Original": "insert into user(nonid, name, id) values (2, 'foo', 1)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(nonid, name, id, Costly) values (2, :_Name0, :_Id0, :_Costly0)", + "Values": [[[":__seq0"]],[["foo"]],[[null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [1] + }, + "Prefix": "insert into user(nonid, name, id, Costly) values ", + "Mid": ["(2, :_Name0, :_Id0, :_Costly0)"] + } +} + +# insert for non-vindex autoinc +"insert into user_extra(nonid) values (2)" +{ + "Original": "insert into user_extra(nonid) values (2)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user_extra(nonid, extra_id, user_id) values (2, :__seq0, :_user_id0)", + "Values": [[[null]]], + "Table": "user_extra", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [null] + }, + "Prefix": "insert into user_extra(nonid, extra_id, user_id) values ", + "Mid": ["(2, :__seq0, :_user_id0)"] + } +} + +# insert for non-compliant names +"insert into `weird``name`(`a``b*c`, `b*c`) values(1, 2)" +{ + "Original": "insert into `weird``name`(`a``b*c`, `b*c`) values(1, 2)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into `weird``name`(`a``b*c`, `b*c`) values (:_a_b_c0, 2)", + "Values": [[[1]]], + "Table": "weird`name", + "Prefix": "insert into `weird``name`(`a``b*c`, `b*c`) values ", + "Mid": [ + "(:_a_b_c0, 2)" + ] + } +} + +# unsharded insert from union +"insert into unsharded select 1 from dual union select 1 from dual" +{ + "Original": "insert into unsharded select 1 from dual union select 1 from dual", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded select 1 from dual union select 1 from dual", + "Table": "unsharded" + } +} + +# insert for non-vindex autoinc, invalid value +"insert into user_extra(nonid, extra_id) values (2, 18446744073709551616)" +"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" + +# insert invalid index value +"insert into music_extra(music_id, user_id) values(1, 18446744073709551616)" +"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" + +# insert invalid index value +"insert into music_extra(music_id, user_id) values(1, id)" +"could not compute value for vindex or auto-inc column: expression is too complex 'id'" + +# insert invalid table +"insert into noexist(music_id, user_id) values(1, 18446744073709551616)" +"table noexist not found" + +# insert with multiple rows +"insert into user(id) values (1), (2)" +{ + "Original": "insert into user(id) values (1), (2)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", + "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [1,2] + }, + "Prefix": "insert into user(id, Name, Costly) values ", + "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"] + } +} + +# insert with query timeout +"insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id) values (1), (2)" +{ + "Original": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id) values (1), (2)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", + "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [1,2] + }, + "Prefix": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id, Name, Costly) values ", + "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"], + "QueryTimeout": 1 + } +} + +# insert with multiple rows - multi-shard autocommit +"insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id) values (1), (2)" +{ + "Original": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id) values (1), (2)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", + "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], + "Table": "user", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [1,2] + }, + "Prefix": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id, Name, Costly) values ", + "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"], + "MultiShardAutocommit": true + } +} + +# insert into a vindex not allowed +"insert into user_index(id) values(1)" +"unsupported: multi-shard or vindex write statement" + +# simple replace unsharded +"replace into unsharded values(1, 2)" +{ + "Original": "replace into unsharded values(1, 2)", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "replace into unsharded values (1, 2)", + "Table": "unsharded" + } +} + +# replace unsharded with select +"replace into unsharded select id from unsharded_auto" +{ + "Original": "replace into unsharded select id from unsharded_auto", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "replace into unsharded select id from unsharded_auto", + "Table": "unsharded" + } +} + +# replace unsharded, invalid value for auto-inc +"replace into unsharded_auto(id, val) values(18446744073709551616, 'aa')" +"could not compute value for vindex or auto-inc column: strconv.ParseUint: parsing "18446744073709551616": value out of range" + +# replace unsharded, column present +"replace into unsharded_auto(id, val) values(1, 'aa')" +{ + "Original": "replace into unsharded_auto(id, val) values(1, 'aa')", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "replace into unsharded_auto(id, val) values (:__seq0, 'aa')", + "Table": "unsharded_auto", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + 1 + ] + } + } +} + +# replace unsharded, column absent +"replace into unsharded_auto(val) values('aa')" { - "Original": "update user set name = null where id in (1, 2, 3)", + "Original": "replace into unsharded_auto(val) values('aa')", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "replace into unsharded_auto(val, id) values ('aa', :__seq0)", + "Table": "unsharded_auto", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + null + ] + } + } +} + +# replace unsharded, multi-val +"replace into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')" +{ + "Original": "replace into unsharded_auto(id, val) values(1, 'aa'), (null, 'bb')", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "replace into unsharded_auto(id, val) values (:__seq0, 'aa'), (:__seq1, 'bb')", + "Table": "unsharded_auto", + "Generate": { + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "select next :n values from seq", + "Values": [ + 1, + null + ] + } + } +} + +# replace invalid table +"replace into noexist(music_id, user_id) values(1, 18446744073709551616)" +"table noexist not found" + +# insert a row in a multi column vindex table +"insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4)" +{ + "Original": "insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4)", "Instructions": { - "Opcode": "UpdateIn", + "Opcode": "InsertSharded", "Keyspace": { "Name": "user", "Sharded": true }, - "Query": "update user set name = null where id in (1, 2, 3)", - "Vindex": "user_index", - "Values": [[1, 2, 3]], + "Query": "insert into multicolvin(column_a, column_b, column_c, kid) values (:_column_a0, :_column_b0, :_column_c0, :_kid0)", + "Values": [ + [ + [ + 4 + ] + ], + [ + [ + 1 + ] + ], + [ + [ + 2 + ], + [ + 3 + ] + ] + ], + "Table": "multicolvin", + "Prefix": "insert into multicolvin(column_a, column_b, column_c, kid) values ", + "Mid": [ + "(:_column_a0, :_column_b0, :_column_c0, :_kid0)" + ] + } +} + +# insert for overlapped vindex columns +"insert overlap_vindex (kid, column_a, column_b) VALUES (1,2,3)" +{ + "Original": "insert overlap_vindex (kid, column_a, column_b) VALUES (1,2,3)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into overlap_vindex(kid, column_a, column_b) values (:_kid0, :_column_a0, 3)", + "Values": [ + [ + [ + 1 + ] + ], + [ + [ + 2 + ], + [ + 1 + ] + ] + ], + "Table": "overlap_vindex", + "Prefix": "insert into overlap_vindex(kid, column_a, column_b) values ", + "Mid": [ + "(:_kid0, :_column_a0, 3)" + ] + } +} + + +# insert multiple rows in a multi column vindex table +"insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4), (5,6,7,8)" +{ + "Original": "insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4), (5,6,7,8)", + "Instructions": { + "Opcode": "InsertSharded", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "insert into multicolvin(column_a, column_b, column_c, kid) values (:_column_a0, :_column_b0, :_column_c0, :_kid0), (:_column_a1, :_column_b1, :_column_c1, :_kid1)", + "Values": [ + [ + [ + 4, + 8 + ] + ], + [ + [ + 1, + 5 + ] + ], + [ + [ + 2, + 6 + ], + [ + 3, + 7 + ] + ] + ], + "Table": "multicolvin", + "Prefix": "insert into multicolvin(column_a, column_b, column_c, kid) values ", + "Mid": [ + "(:_column_a0, :_column_b0, :_column_c0, :_kid0)", + "(:_column_a1, :_column_b1, :_column_c1, :_kid1)" + ] + } +} + +# delete row in a multi column vindex table +"delete from multicolvin where kid=1" +{ + "Original": "delete from multicolvin where kid=1", + "Instructions": { + "Opcode": "DeleteEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from multicolvin where kid = 1", + "Vindex": "kid_index", + "Values": [ + 1 + ], + "Table": "multicolvin", + "OwnedVindexQuery": "select column_a, column_b, column_c from multicolvin where kid = 1 for update" + } +} + +# update columns of multi column vindex +"update multicolvin set column_b = 1, column_c = 2 where kid = 1" +{ + "Original": "update multicolvin set column_b = 1, column_c = 2 where kid = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update multicolvin set column_b = 1, column_c = 2 where kid = 1", + "Vindex": "kid_index", + "Values": [ + 1 + ], "ChangedVindexValues": { - "name_user_map": [null] + "colb_colc_map": [ + 1, + 2 + ] }, - "Table": "user", - "OwnedVindexQuery": "select Name, Costly from user where id in (1, 2, 3) for update" + "Table": "multicolvin", + "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update" + } +} + +# update multiple vindexes, with multi column vindex +"update multicolvin set column_a = 0, column_b = 1, column_c = 2 where kid = 1" +{ + "Original": "update multicolvin set column_a = 0, column_b = 1, column_c = 2 where kid = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update multicolvin set column_a = 0, column_b = 1, column_c = 2 where kid = 1", + "Vindex": "kid_index", + "Values": [ + 1 + ], + "ChangedVindexValues": { + "cola_map": [ + 0 + ], + "colb_colc_map": [ + 1, + 2 + ] + }, + "Table": "multicolvin", + "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update" + } +} + +# update with no primary vindex on where clause (scatter update) +"update user_extra set val = 1" +{ + "Original": "update user_extra set val = 1", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_extra set val = 1", + "Table": "user_extra" + } +} + +# update with target destination +"update `user[-]`.user_extra set val = 1" +{ + "Original": "update `user[-]`.user_extra set val = 1", + "Instructions": { + "Opcode": "UpdateByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_extra set val = 1", + "Table": "user_extra" + } +} + +# update with no primary vindex on where clause (scatter update) - multi shard autocommit +"update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ user_extra set val = 1" +{ + "Original": "update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ user_extra set val = 1", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ user_extra set val = 1", + "Table": "user_extra", + "MultiShardAutocommit": true + } +} + +# update with no primary vindex on where clause (scatter update) - query timeout +"update /*vt+ QUERY_TIMEOUT_MS=1 */ user_extra set val = 1" +{ + "Original": "update /*vt+ QUERY_TIMEOUT_MS=1 */ user_extra set val = 1", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update /*vt+ QUERY_TIMEOUT_MS=1 */ user_extra set val = 1", + "Table": "user_extra", + "QueryTimeout": 1 + } +} + +# update with non-comparison expr +"update user_extra set val = 1 where id between 1 and 2" +{ + "Original": "update user_extra set val = 1 where id between 1 and 2", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_extra set val = 1 where id between 1 and 2", + "Table": "user_extra" + } +} + +# update with primary id through IN clause +"update user_extra set val = 1 where user_id in (1, 2)" +{ + "Original": "update user_extra set val = 1 where user_id in (1, 2)", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_extra set val = 1 where user_id in (1, 2)", + "Table": "user_extra" + } +} + +# update with non-unique key +"update user_extra set val = 1 where name = 'foo'" +{ + "Original": "update user_extra set val = 1 where name = 'foo'", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_extra set val = 1 where name = 'foo'", + "Table": "user_extra" + } +} + +# update by lookup with IN clause +"update user_extra set val = 1 where id in (1, 2)" +{ + "Original": "update user_extra set val = 1 where id in (1, 2)", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_extra set val = 1 where id in (1, 2)", + "Table": "user_extra" + } +} + +# update with where clause with parens +"update user_extra set val = 1 where (name = 'foo' or id = 1)" +{ + "Original": "update user_extra set val = 1 where (name = 'foo' or id = 1)", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user_extra set val = 1 where (name = 'foo' or id = 1)", + "Table": "user_extra" + } +} + +# delete from with no where clause +"delete from user_extra" +{ + "Original": "delete from user_extra", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user_extra", + "Table": "user_extra" + } +} + +# delete with target destination +"delete from `user[-]`.user_extra" +{ + "Original": "delete from `user[-]`.user_extra", + "Instructions": { + "Opcode": "DeleteByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user_extra", + "Table": "user_extra" + } +} + +# delete with non-comparison expr +"delete from user_extra where user_id between 1 and 2" +{ + "Original": "delete from user_extra where user_id between 1 and 2", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user_extra where user_id between 1 and 2", + "Table": "user_extra" + } +} + +# delete from with no index match +"delete from user_extra where name = 'jose'" +{ + "Original": "delete from user_extra where name = 'jose'", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user_extra where name = 'jose'", + "Table": "user_extra" } } +# delete from with no index match - multi shard autocommit +"delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where name = 'jose'" +{ + "Original": "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where name = 'jose'", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where name = 'jose'", + "Table": "user_extra", + "MultiShardAutocommit": true + } +} + +# delete from with no index match - query timeout +"delete /*vt+ QUERY_TIMEOUT_MS=1 */ from user_extra where name = 'jose'" +{ + "Original": "delete /*vt+ QUERY_TIMEOUT_MS=1 */ from user_extra where name = 'jose'", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete /*vt+ QUERY_TIMEOUT_MS=1 */ from user_extra where name = 'jose'", + "Table": "user_extra", + "QueryTimeout": 1 + } +} + +# delete from with primary id in through IN clause +"delete from user_extra where user_id in (1, 2)" +{ + "Original": "delete from user_extra where user_id in (1, 2)", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user_extra where user_id in (1, 2)", + "Table": "user_extra" + } +} + +# unsharded update where inner query references outer query +"update unsharded set col = (select id from unsharded_a where id = unsharded.col) where col = (select id from unsharded_b)" +{ + "Original": "update unsharded set col = (select id from unsharded_a where id = unsharded.col) where col = (select id from unsharded_b)", + "Instructions": { + "Opcode": "UpdateUnsharded", + "Keyspace": { + "Name":"main", + "Sharded":false + }, + "Query": "update unsharded set col = (select id from unsharded_a where id = unsharded.col) where col = (select id from unsharded_b)" + } +} + +# unsharded delete where inner query references outer query +"delete from unsharded where col = (select id from unsharded_a where id = unsharded.col)" +{ + "Original": "delete from unsharded where col = (select id from unsharded_a where id = unsharded.col)", + "Instructions": { + "Opcode": "DeleteUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "delete from unsharded where col = (select id from unsharded_a where id = unsharded.col)" + } +} # update vindex value to null "update user set name = null where id = 1" @@ -37,7 +1828,40 @@ "name_user_map": [null] }, "Table": "user", - "OwnedVindexQuery": "select Name, Costly from user where id = 1 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update" + } +} + +# insert using last_insert_id +"insert into unsharded values(last_insert_id(), 2)" +{ + "Original": "insert into unsharded values(last_insert_id(), 2)", + "Instructions": { + "Opcode": "InsertUnsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "Query": "insert into unsharded values (:__lastInsertId, 2)", + "Table": "unsharded" } } +# update multiple vindex value to null +"update user set name = null where id in (1, 2, 3)" +{ + "Original": "update user set name = null where id in (1, 2, 3)", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set name = null where id in (1, 2, 3)", + "ChangedVindexValues": { + "name_user_map": [null] + }, + "Table": "user", + "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update" + } +} diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index 2b8befcb10b..b49d27a12ab 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -83,31 +83,24 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Upd return eupd, nil } - eupd.Vindex, eupd.Values, err = getDMLRouting(upd.Where, eupd.Table) - if err != nil { - eupd.Opcode = engine.UpdateScatter - } else { - if eupd.Values[0].IsList() { - eupd.Opcode = engine.UpdateIn - } else { - eupd.Opcode = engine.UpdateEqual - } - } + routingType, vindex, vindexCol, values := getDMLRouting(upd.Where, eupd.Table) - if eupd.Opcode == engine.UpdateScatter { - if len(eupd.Table.Owned) != 0 { - return nil, errors.New("unsupported: multi shard update on a table with owned lookup vindexes") - } + if routingType == scatter { if upd.Limit != nil { return nil, errors.New("unsupported: multi shard update with limit") } + eupd.Opcode = engine.UpdateScatter + } else { + eupd.Opcode = engine.UpdateEqual + eupd.Values = values + eupd.Vindex = vindex } - if eupd.ChangedVindexValues, err = buildChangedVindexesValues(eupd, upd, eupd.Table.ColumnVindexes); err != nil { + if eupd.ChangedVindexValues, err = buildChangedVindexesValues(upd, eupd.Table.ColumnVindexes); err != nil { return nil, err } if len(eupd.ChangedVindexValues) != 0 { - eupd.OwnedVindexQuery = generateUpdateSubquery(upd, eupd.Table) + eupd.OwnedVindexQuery = generateUpdateSubquery(upd, eupd.Table, vindexCol) } return eupd, nil } @@ -115,7 +108,7 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Upd // buildChangedVindexesValues adds to the plan all the lookup vindexes that are changing. // Updates can only be performed to secondary lookup vindexes with no complex expressions // in the set clause. -func buildChangedVindexesValues(eupd *engine.Update, update *sqlparser.Update, colVindexes []*vindexes.ColumnVindex) (map[string][]sqltypes.PlanValue, error) { +func buildChangedVindexesValues(update *sqlparser.Update, colVindexes []*vindexes.ColumnVindex) (map[string][]sqltypes.PlanValue, error) { changedVindexes := make(map[string][]sqltypes.PlanValue) for i, vindex := range colVindexes { var vindexValues []sqltypes.PlanValue @@ -163,16 +156,13 @@ func buildChangedVindexesValues(eupd *engine.Update, update *sqlparser.Update, c return changedVindexes, nil } -func generateUpdateSubquery(upd *sqlparser.Update, table *vindexes.Table) string { +func generateUpdateSubquery(upd *sqlparser.Update, table *vindexes.Table, vindex string) string { buf := sqlparser.NewTrackedBuffer(nil) buf.WriteString("select ") - for vIdx, cv := range table.Owned { - for cIdx, column := range cv.Columns { - if cIdx == 0 && vIdx == 0 { - buf.Myprintf("%v", column) - } else { - buf.Myprintf(", %v", column) - } + buf.Myprintf("%s", vindex) + for _, cv := range table.Owned { + for _, column := range cv.Columns { + buf.Myprintf(", %v", column) } } buf.Myprintf(" from %v%v%v%v for update", table.Name, upd.Where, upd.OrderBy, upd.Limit) @@ -206,12 +196,17 @@ func generateQuery(statement sqlparser.Statement) string { return buf.String() } +type dmlRoutingType int + +const ( + scatter dmlRoutingType = iota + equal +) + // getDMLRouting returns the vindex and values for the DML, // If it cannot find a unique vindex match, it returns an error. -func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (vindexes.SingleColumn, []sqltypes.PlanValue, error) { - if where == nil { - return nil, nil, errors.New("unsupported: multi-shard where clause in DML") - } +func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (dmlRoutingType, vindexes.SingleColumn, string, []sqltypes.PlanValue) { + var keyColumn string for _, index := range table.Ordered { if !index.Vindex.IsUnique() { continue @@ -220,11 +215,21 @@ func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (vindexes.Sing if !ok { continue } + + keyColumn = sqlparser.String(index.Columns[0]) + if where == nil { + return scatter, nil, keyColumn, nil + } + if pv, ok := getMatch(where.Expr, index.Columns[0]); ok { - return single, []sqltypes.PlanValue{pv}, nil + if pv.IsList() { + return scatter, nil, keyColumn, nil + } + + return equal, single, keyColumn, []sqltypes.PlanValue{pv} } } - return nil, nil, errors.New("unsupported: multi-shard where clause in DML") + return scatter, nil, keyColumn, nil } // getMatch returns the matched value if there is an equality From 3892759aee3a91bdf9aded51b8c73ef6246e1bcc Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Tue, 18 Feb 2020 09:45:55 -0800 Subject: [PATCH 126/825] Add transaction_read_only keyword Signed-off-by: Saif Alharthi --- go/vt/sqlparser/analyzer_test.go | 8 ++++++++ go/vt/sqlparser/parse_test.go | 4 ++++ go/vt/vtgate/executor.go | 4 ++-- go/vt/vtgate/executor_test.go | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/go/vt/sqlparser/analyzer_test.go b/go/vt/sqlparser/analyzer_test.go index b140703849f..062a3e3b66b 100644 --- a/go/vt/sqlparser/analyzer_test.go +++ b/go/vt/sqlparser/analyzer_test.go @@ -567,6 +567,14 @@ func TestExtractSetValues(t *testing.T) { sql: "set session sql_safe_updates = 0", out: map[SetKey]interface{}{{Key: "sql_safe_updates", Scope: ImplicitStr}: int64(0)}, scope: SessionStr, + }, { + sql: "set session transaction_read_only = 0", + out: map[SetKey]interface{}{{Key: "transaction_read_only", Scope: ImplicitStr}: int64(0)}, + scope: SessionStr, + }, { + sql: "set session transaction_read_only = 1", + out: map[SetKey]interface{}{{Key: "transaction_read_only", Scope: ImplicitStr}: int64(1)}, + scope: SessionStr, }, { sql: "set session sql_safe_updates = 1", out: map[SetKey]interface{}{{Key: "sql_safe_updates", Scope: ImplicitStr}: int64(1)}, diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 586963cc122..f0c6ee66131 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -813,6 +813,10 @@ var ( input: "set tx_read_only = 1", }, { input: "set tx_read_only = 0", + }, { + input: "set transaction_read_only = 1", + }, { + input: "set transaction_read_only = 0", }, { input: "set tx_isolation = 'repeatable read'", }, { diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index e2ac0b2dca8..d8c9287cd55 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -602,7 +602,7 @@ func (e *Executor) handleSet(ctx context.Context, safeSession *SafeSession, sql default: return nil, fmt.Errorf("unexpected value for tx_isolation: %v", val) } - case "tx_read_only": + case "tx_read_only", "transaction_read_only": val, err := validateSetOnOff(v, k.Key) if err != nil { return nil, err @@ -611,7 +611,7 @@ func (e *Executor) handleSet(ctx context.Context, safeSession *SafeSession, sql case 0, 1: // TODO (4127): This is a dangerous NOP. default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for tx_read_only: %d", val) + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for %v: %d", k.Key, val) } case "workload": val, ok := v.(string) diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index f1709226c47..a68f5514710 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -476,6 +476,9 @@ func TestExecutorSet(t *testing.T) { }, { in: "set tx_read_only = 2", err: "unexpected value for tx_read_only: 2", + }, { + in: "set transaction_read_only = 2", + err: "unexpected value for transaction_read_only: 2", }, { in: "set tx_isolation = 'invalid'", err: "unexpected value for tx_isolation: invalid", From 4236cb3f172b22062112a08b606c26fd035a6cb2 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 18 Feb 2020 23:29:13 +0100 Subject: [PATCH 127/825] wip - some tests still red Signed-off-by: Andres Taylor --- go/vt/vtgate/autocommit_test.go | 27 ++++--- go/vt/vtgate/engine/fake_vcursor_test.go | 6 +- go/vt/vtgate/engine/update.go | 90 ++++++++++++++++++++---- go/vt/vtgate/engine/update_test.go | 86 +++++++++++++++------- 4 files changed, 160 insertions(+), 49 deletions(-) diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index 0ec8fffd862..b37582e7d04 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -20,6 +20,8 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" @@ -79,11 +81,20 @@ func TestAutocommitUpdateLookup(t *testing.T) { // TestAutocommitUpdateVindexChange: transaction: select & update before final update. func TestAutocommitUpdateVindexChange(t *testing.T) { - executor, sbc1, _, sbclookup := createExecutorEnv() + executor, sbc, _, sbclookup := createExecutorEnv() + sbc.SetResults([]*sqltypes.Result{{ + Fields: []*querypb.Field{ + {Name: "name", Type: sqltypes.VarChar}, + }, + RowsAffected: 1, + InsertID: 0, + Rows: [][]sqltypes.Value{{ + //sqltypes.NewVarChar("myname"), + }}, + }}) - if _, err := autocommitExec(executor, "update user2 set name='myname', lastname='mylastname' where id = 1"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "update user2 set name='myname', lastname='mylastname' where id = 1") + require.NoError(t, err) testQueries(t, "sbclookup", sbclookup, []*querypb.BoundQuery{{ Sql: "delete from name_lastname_keyspace_id_map where name = :name and lastname = :lastname and keyspace_id = :keyspace_id", @@ -103,15 +114,15 @@ func TestAutocommitUpdateVindexChange(t *testing.T) { testAsTransactionCount(t, "sbclookup", sbclookup, 0) testCommitCount(t, "sbclookup", sbclookup, 1) - testQueries(t, "sbc1", sbc1, []*querypb.BoundQuery{{ - Sql: "select name, lastname from user2 where id = 1 for update", + testQueries(t, "sbc", sbc, []*querypb.BoundQuery{{ + Sql: "select id, name, lastname from user2 where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "update user2 set name = 'myname', lastname = 'mylastname' where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{}, }}) - testAsTransactionCount(t, "sbc1", sbc1, 0) - testCommitCount(t, "sbc1", sbc1, 1) + testAsTransactionCount(t, "sbc", sbc, 0) + testCommitCount(t, "sbc", sbc, 1) } // TestAutocommitDeleteSharded: instant-commit. diff --git a/go/vt/vtgate/engine/fake_vcursor_test.go b/go/vt/vtgate/engine/fake_vcursor_test.go index 28bd7b20d2c..f7545a1cfe3 100644 --- a/go/vt/vtgate/engine/fake_vcursor_test.go +++ b/go/vt/vtgate/engine/fake_vcursor_test.go @@ -25,6 +25,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" @@ -229,9 +231,7 @@ func (f *loggingVCursor) ResolveDestinations(keyspace string, ids []*querypb.Val func (f *loggingVCursor) ExpectLog(t *testing.T, want []string) { t.Helper() - if !reflect.DeepEqual(f.log, want) { - t.Errorf("vc.log:\n%v\nwant:\n%v", strings.Join(f.log, "\n"), strings.Join(want, "\n")) - } + require.Equal(t, strings.Join(f.log, "\n"), strings.Join(want, "\n")) } func (f *loggingVCursor) ExpectWarnings(t *testing.T, want []*querypb.QueryWarning) { diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index ede8754b89f..69bd58cea49 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -227,7 +227,7 @@ func (upd *Update) execUpdateEqual(vcursor VCursor, bindVars map[string]*querypb return &sqltypes.Result{}, nil } if len(upd.ChangedVindexValues) != 0 { - if err := upd.updateVindexEntries(vcursor, upd.OwnedVindexQuery, bindVars, rs, ksid); err != nil { + if err := upd.updateVindexEntries(vcursor, bindVars, rs, ksid); err != nil { return nil, vterrors.Wrap(err, "execUpdateEqual") } } @@ -241,7 +241,7 @@ func (upd *Update) execUpdateEqual(vcursor VCursor, bindVars map[string]*querypb // for DMLs to reuse existing transactions. // Note 2: While changes are being committed, the changing row could be // unreachable by either the new or old column values. -func (upd *Update) updateVindexEntries(vcursor VCursor, query string, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard, ksid []byte) error { +func (upd *Update) updateVindexEntries(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard, ksid []byte) error { subQueryResult, err := execShard(vcursor, upd.OwnedVindexQuery, bindVars, rs, false /* isDML */, false /* canAutocommit */) if err != nil { return err @@ -252,7 +252,7 @@ func (upd *Update) updateVindexEntries(vcursor VCursor, query string, bindVars m if len(subQueryResult.Rows) > 1 { return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: update changes multiple rows in the vindex") } - colnum := 0 + colnum := 1 for _, colVindex := range upd.Table.Owned { // Fetch the column values. colnum must keep incrementing. fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) @@ -263,7 +263,7 @@ func (upd *Update) updateVindexEntries(vcursor VCursor, query string, bindVars m // Update columns only if they're being changed. if colValues, ok := upd.ChangedVindexValues[colVindex.Name]; ok { - vindexColumnKeys := make([]sqltypes.Value, 0, len(colValues)) + var vindexColumnKeys []sqltypes.Value for _, colValue := range colValues { resolvedVal, err := colValue.ResolveValue(bindVars) if err != nil { @@ -280,6 +280,59 @@ func (upd *Update) updateVindexEntries(vcursor VCursor, query string, bindVars m return nil } +// updateVindexEntries performs an update when a vindex is being modified +// by the statement. +// Note: the commit order may be different from the DML order because it's possible +// for DMLs to reuse existing transactions. +// Note 2: While changes are being committed, the changing row could be +// unreachable by either the new or old column values. +func (upd *Update) updateVindexEntriesScatter(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) error { + queries := make([]*querypb.BoundQuery, len(rss)) + for i := range rss { + queries[i] = &querypb.BoundQuery{Sql: upd.OwnedVindexQuery, BindVariables: bindVars} + } + subQueryResult, errors := vcursor.ExecuteMultiShard(rss, queries, false, false) + for _, err := range errors { + if err != nil { + return vterrors.Wrap(err, "updateVindexEntriesScatter") + } + } + + if len(subQueryResult.Rows) == 0 { + return nil + } + + for _, row := range subQueryResult.Rows { + colnum := 1 // we start from the first non-vindex col + ksid := row[0].Raw() + for _, colVindex := range upd.Table.Owned { + // Fetch the column values. colnum must keep incrementing. + fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) + for range colVindex.Columns { + fromIds = append(fromIds, row[colnum]) + colnum++ + } + + // Update columns only if they're being changed. + if colValues, ok := upd.ChangedVindexValues[colVindex.Name]; ok { + var vindexColumnKeys []sqltypes.Value + for _, colValue := range colValues { + resolvedVal, err := colValue.ResolveValue(bindVars) + if err != nil { + return err + } + vindexColumnKeys = append(vindexColumnKeys, resolvedVal) + } + + if err := colVindex.Vindex.(vindexes.Lookup).Update(vcursor, fromIds, ksid, vindexColumnKeys); err != nil { + return err + } + } + } + } + return nil +} + func (upd *Update) execUpdateByDestination(vcursor VCursor, bindVars map[string]*querypb.BindVariable, dest key.Destination) (*sqltypes.Result, error) { rss, _, err := vcursor.ResolveDestinations(upd.Keyspace.Name, nil, []key.Destination{dest}) if err != nil { @@ -294,22 +347,31 @@ func (upd *Update) execUpdateByDestination(vcursor VCursor, bindVars map[string] BindVariables: bindVars, } } + + // update any owned vindexes + if len(upd.ChangedVindexValues) != 0 { + if err := upd.updateVindexEntriesScatter(vcursor, bindVars, rss); err != nil { + return nil, vterrors.Wrap(err, "execUpdateByDestination") + } + } + autocommit := (len(rss) == 1 || upd.MultiShardAutocommit) && vcursor.AutocommitApproval() result, errs := vcursor.ExecuteMultiShard(rss, queries, true /* isDML */, autocommit) return result, vterrors.Aggregate(errs) } func (upd *Update) execUpdateIn(vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - keys, err := upd.Values[0].ResolveList(bindVars) - if err != nil { - return nil, vterrors.Wrap(err, "execUpdateIn") - } - _, _, err = resolveShards(vcursor, upd.Vindex, upd.Keyspace, keys) - if err != nil { - return nil, vterrors.Wrap(err, "execUpdateIn") - } - // just to have code compile returning blank. - return &sqltypes.Result{}, nil + panic("implement me!") + //keys, err := upd.Values[0].ResolveList(bindVars) + //if err != nil { + // return nil, vterrors.Wrap(err, "execUpdateIn") + //} + //_, _, err = resolveShards(vcursor, upd.Vindex, upd.Keyspace, keys) + //if err != nil { + // return nil, vterrors.Wrap(err, "execUpdateIn") + //} + //just to have code compile returning blank. + //return &sqltypes.Result{}, nil /* TODO: Think of, How to go about updating lookup vindex. Will it be a loop or should resolve shards be performed on it. diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index d21f62990f7..af0a2159599 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -20,6 +20,8 @@ import ( "errors" "testing" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -39,9 +41,7 @@ func TestUpdateUnsharded(t *testing.T) { vc := &loggingVCursor{shards: []string{"0"}} _, err := upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, `ExecuteMultiShard ks.0: dummy_update {} true true`, @@ -72,9 +72,7 @@ func TestUpdateEqual(t *testing.T) { vc := &loggingVCursor{shards: []string{"-20", "20-"}} _, err := upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, `ExecuteMultiShard ks.-20: dummy_update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ {} true true`, @@ -101,9 +99,7 @@ func TestUpdateScatter(t *testing.T) { vc := &loggingVCursor{shards: []string{"-20", "20-"}} _, err := upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, @@ -125,9 +121,7 @@ func TestUpdateScatter(t *testing.T) { vc = &loggingVCursor{shards: []string{"-20", "20-"}} _, err = upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations ks [] Destinations:DestinationAllShards()`, @@ -154,9 +148,7 @@ func TestUpdateEqualNoRoute(t *testing.T) { vc := &loggingVCursor{shards: []string{"0"}} _, err := upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) vc.ExpectLog(t, []string{ // This lookup query will return no rows. So, the DML will not be sent anywhere. `Execute select toc from lkp where from = :from from: type:INT64 value:"1" false`, @@ -210,10 +202,10 @@ func TestUpdateEqualChangedVindex(t *testing.T) { results := []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( - "c1|c2|c3", - "int64|int64|int64", + "id|c1|c2|c3", + "varbinary|int64|int64|int64", ), - "4|5|6", + "\026k@\264J\272K\326|4|5|6", )} vc := &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -221,9 +213,7 @@ func TestUpdateEqualChangedVindex(t *testing.T) { } _, err := upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations sharded [] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, // ResolveDestinations is hard-coded to return -20. @@ -245,9 +235,7 @@ func TestUpdateEqualChangedVindex(t *testing.T) { shards: []string{"-20", "20-"}, } _, err = upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) vc.ExpectLog(t, []string{ `ResolveDestinations sharded [] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, // ResolveDestinations is hard-coded to return -20. @@ -274,6 +262,56 @@ func TestUpdateEqualChangedVindex(t *testing.T) { expectError(t, "Execute", err, "execUpdateEqual: unsupported: update changes multiple rows in the vindex") } +func TestUpdateScatterChangedVindex(t *testing.T) { + // update t1 set c1 = 1, c2 = 2, c3 = 3 + ks := buildTestVSchema().Keyspaces["sharded"] + upd := &Update{ + Opcode: UpdateScatter, + Keyspace: ks.Keyspace, + Query: "dummy_update", + ChangedVindexValues: map[string][]sqltypes.PlanValue{ + "twocol": {{ + Value: sqltypes.NewInt64(1), + }, { + Value: sqltypes.NewInt64(2), + }}, + "onecol": {{ + Value: sqltypes.NewInt64(3), + }}, + }, + Table: ks.Tables["t1"], + OwnedVindexQuery: "dummy_subquery", + } + + results := []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|c1|c2|c3", + "varbinary|int64|int64|int64", + ), + "\026k@\264J\272K\326|4|5|6", + )} + vc := &loggingVCursor{ + shards: []string{"-20", "20-"}, + results: results, + } + + _, err := upd.Execute(vc, map[string]*querypb.BindVariable{}, false) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, + // Those values are returned as 4,5 for twocol and 6 for onecol. + // 4,5 have to be replaced by 1,2 (the new values). + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp2(from1, from2, toc) values(:from10, :from20, :toc0) from10: type:INT64 value:"1" from20: type:INT64 value:"2" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // 6 has to be replaced by 3. + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp1(from, toc) values(:from0, :toc0) from0: type:INT64 value:"3" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // Finally, the actual update, which is also sent to -20, same route as the subquery. + `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, + }) +} + func TestUpdateNoStream(t *testing.T) { upd := &Update{} err := upd.StreamExecute(nil, nil, false, nil) From 9622e083d4275c9b7305087566cc1cd8667a3a0b Mon Sep 17 00:00:00 2001 From: saurabh408 Date: Wed, 19 Feb 2020 07:01:49 +0530 Subject: [PATCH 128/825] vtworker tests migrated in go (#5794) * first commit worker tests migrated in go Signed-off-by: saurabh * added testcase for worker test when mysql down Signed-off-by: saurabh * added vtworker webinterface tests Signed-off-by: saurabh * code clean-up moved common code to utils Signed-off-by: saurabh * modifying config.json Signed-off-by: saurabh * update config.json Signed-off-by: saurabh * fixing throttling service assertions Signed-off-by: saurabh * fixing throttling assertions failures and addressed review comments Signed-off-by: saurabh * fixing throttling assertions failures and addressed review comments Signed-off-by: saurabh * adding 2 minutes max waittime for throttler registration Signed-off-by: saurabh * validating worker python testcase in github workflow Signed-off-by: saurabh * modifying configurations Signed-off-by: saurabh --- .github/workflows/cluster_endtoend.yml | 2 +- go/test/endtoend/cluster/cluster_util.go | 32 + go/test/endtoend/cluster/topo_process.go | 11 +- go/test/endtoend/cluster/vttablet_process.go | 7 +- go/test/endtoend/cluster/vtworker_process.go | 31 + go/test/endtoend/sharding/base_sharding.go | 16 +- go/test/endtoend/vtgate/buffer/buffer_test.go | 33 +- go/test/endtoend/worker/worker_test.go | 603 ++++++++++++++++++ test/config.json | 22 +- 9 files changed, 702 insertions(+), 55 deletions(-) create mode 100644 go/test/endtoend/worker/worker_test.go diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index f1e4dbe88d9..27c73378c32 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] steps: - name: Set up Go diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 5237f2e323f..e382335d704 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -149,6 +149,38 @@ func getTablet(tabletGrpcPort int, hostname string) *tabletpb.Tablet { return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap} } +// WaitForReplicationPos will wait for replication position to catch-up +func WaitForReplicationPos(t *testing.T, tabletA *Vttablet, tabletB *Vttablet, hostname string, timeout float64) { + replicationPosA, _ := GetMasterPosition(t, *tabletA, hostname) + for { + replicationPosB, _ := GetMasterPosition(t, *tabletB, hostname) + if positionAtLeast(t, tabletA, replicationPosB, replicationPosA) { + break + } + msg := fmt.Sprintf("%s's replication position to catch up to %s's;currently at: %s, waiting to catch up to: %s", tabletB.Alias, tabletA.Alias, replicationPosB, replicationPosA) + waitStep(t, msg, timeout, 0.01) + } +} + +func waitStep(t *testing.T, msg string, timeout float64, sleepTime float64) float64 { + timeout = timeout - sleepTime + if timeout < 0.0 { + t.Errorf("timeout waiting for condition '%s'", msg) + } + time.Sleep(time.Duration(sleepTime) * time.Second) + return timeout +} + +func positionAtLeast(t *testing.T, tablet *Vttablet, a string, b string) bool { + isAtleast := false + val, err := tablet.MysqlctlProcess.ExecuteCommandWithOutput("position", "at_least", a, b) + require.NoError(t, err) + if strings.Contains(val, "true") { + isAtleast = true + } + return isAtleast +} + // ExecuteQueriesUsingVtgate sends query to vtgate using vtgate session. func ExecuteQueriesUsingVtgate(t *testing.T, session *vtgateconn.VTGateSession, query string) { _, err := session.Execute(context.Background(), query, nil) diff --git a/go/test/endtoend/cluster/topo_process.go b/go/test/endtoend/cluster/topo_process.go index 4a742646952..2691497e42f 100644 --- a/go/test/endtoend/cluster/topo_process.go +++ b/go/test/endtoend/cluster/topo_process.go @@ -223,6 +223,12 @@ func (topo *TopoProcess) TearDown(Cell string, originalVtRoot string, currentRoo // Attempt graceful shutdown with SIGTERM first _ = topo.proc.Process.Signal(syscall.SIGTERM) + if !*keepData { + _ = os.RemoveAll(topo.DataDirectory) + _ = os.RemoveAll(currentRoot) + } + _ = os.Setenv("VTDATAROOT", originalVtRoot) + select { case <-topo.exit: topo.proc = nil @@ -235,11 +241,6 @@ func (topo *TopoProcess) TearDown(Cell string, originalVtRoot string, currentRoo } } - if !*keepData { - _ = os.RemoveAll(topo.DataDirectory) - _ = os.RemoveAll(currentRoot) - } - _ = os.Setenv("VTDATAROOT", originalVtRoot) return nil } diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index c919f97555a..1452d43acb6 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -33,7 +33,6 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/log" ) @@ -322,7 +321,7 @@ func executeQuery(dbParams mysql.ConnParams, query string) (*sqltypes.Result, er return nil, err } defer dbConn.Close() - return dbConn.ExecuteFetch(query, 1000, true) + return dbConn.ExecuteFetch(query, 10000, true) } // GetDBVar returns first matching database variable's value @@ -365,8 +364,6 @@ func VttabletProcessInstance(port int, grpcPort int, tabletUID int, cell string, TabletType: "replica", CommonArg: *vtctl, HealthCheckInterval: 5, - BackupStorageImplementation: "file", - FileBackupStorageRoot: path.Join(os.Getenv("VTDATAROOT"), "/backups"), Port: port, GrpcPort: grpcPort, PidFile: path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/vt_%010d/vttablet.pid", tabletUID)), @@ -375,6 +372,8 @@ func VttabletProcessInstance(port int, grpcPort int, tabletUID int, cell string, EnableSemiSync: enableSemiSync, SupportsBackup: true, ServingStatus: "NOT_SERVING", + BackupStorageImplementation: "file", + FileBackupStorageRoot: path.Join(os.Getenv("VTDATAROOT"), "/backups"), } if tabletType == "rdonly" { diff --git a/go/test/endtoend/cluster/vtworker_process.go b/go/test/endtoend/cluster/vtworker_process.go index 46ab0fb715b..64cccbc88c4 100644 --- a/go/test/endtoend/cluster/vtworker_process.go +++ b/go/test/endtoend/cluster/vtworker_process.go @@ -17,7 +17,9 @@ limitations under the License. package cluster import ( + "encoding/json" "fmt" + "io/ioutil" "net/http" "os" "os/exec" @@ -148,6 +150,17 @@ func (vtworker *VtworkerProcess) ExecuteCommand(args ...string) (err error) { return tmpProcess.Run() } +func (vtworker *VtworkerProcess) ExecuteCommandInBg(args ...string) (*exec.Cmd, error) { + args = append([]string{"-vtworker_client_protocol", "grpc", + "-server", vtworker.Server, "-log_dir", vtworker.LogDir, "-stderrthreshold", "info"}, args...) + tmpProcess := exec.Command( + "vtworkerclient", + args..., + ) + log.Info(fmt.Sprintf("Executing vtworkerclient with arguments %v", strings.Join(tmpProcess.Args, " "))) + return tmpProcess, tmpProcess.Start() +} + // ExecuteVtworkerCommand executes any vtworker command func (vtworker *VtworkerProcess) ExecuteVtworkerCommand(port int, grpcPort int, args ...string) (err error) { args = append([]string{ @@ -191,3 +204,21 @@ func VtworkerProcessInstance(httpPort int, grpcPort int, topoPort int, hostname vtworker.VerifyURL = fmt.Sprintf("http://%s:%d/debug/vars", hostname, vtworker.Port) return vtworker } + +// GetVars returns map of vars +func (vtworker *VtworkerProcess) GetVars() (map[string]interface{}, error) { + resultMap := make(map[string]interface{}) + resp, err := http.Get(vtworker.VerifyURL) + if err != nil { + return nil, fmt.Errorf("error getting response from %s", vtworker.VerifyURL) + } + if resp.StatusCode == 200 { + respByte, _ := ioutil.ReadAll(resp.Body) + err := json.Unmarshal(respByte, &resultMap) + if err != nil { + return nil, fmt.Errorf("not able to parse response body") + } + return resultMap, nil + } + return nil, fmt.Errorf("unsuccessful response") +} diff --git a/go/test/endtoend/sharding/base_sharding.go b/go/test/endtoend/sharding/base_sharding.go index 6794045a42b..c8d27a2eae0 100644 --- a/go/test/endtoend/sharding/base_sharding.go +++ b/go/test/endtoend/sharding/base_sharding.go @@ -24,9 +24,12 @@ import ( "net/http" "reflect" "strconv" + "strings" "testing" "time" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/json2" "vitess.io/vitess/go/test/endtoend/cluster" querypb "vitess.io/vitess/go/vt/proto/query" @@ -444,9 +447,18 @@ func GetShardInfo(t *testing.T, shard1Ks string, ci cluster.LocalProcessCluster) func checkThrottlerServiceMaxRates(t *testing.T, server string, names []string, rate int, ci cluster.LocalProcessCluster) { // Avoid flakes by waiting for all throttlers. (Necessary because filtered // replication on vttablet will register the throttler asynchronously.) - output, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("ThrottlerMaxRates", "--server", server) - assert.Nil(t, err) + var output string + var err error + startTime := time.Now() msg := fmt.Sprintf("%d active throttler(s)", len(names)) + for { + output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ThrottlerMaxRates", "--server", server) + require.Nil(t, err) + if strings.Contains(output, msg) || (time.Now().After(startTime.Add(2 * time.Minute))) { + break + } + time.Sleep(2 * time.Second) + } assert.Contains(t, output, msg) for _, name := range names { diff --git a/go/test/endtoend/vtgate/buffer/buffer_test.go b/go/test/endtoend/vtgate/buffer/buffer_test.go index ff796dc7b90..8340eec113b 100644 --- a/go/test/endtoend/vtgate/buffer/buffer_test.go +++ b/go/test/endtoend/vtgate/buffer/buffer_test.go @@ -355,7 +355,7 @@ func externalReparenting(ctx context.Context, t *testing.T, clusterInstance *clu } // Wait for replica to catch up to master. - waitForReplicationPos(ctx, t, master, replica, 60.0) + cluster.WaitForReplicationPos(t, master, replica, "localhost", 60.0) duration := time.Since(start) minUnavailabilityInS := 1.0 @@ -384,34 +384,3 @@ func externalReparenting(ctx context.Context, t *testing.T, clusterInstance *clu // Notify the new vttablet master about the reparent. clusterInstance.VtctlclientProcess.ExecuteCommand("TabletExternallyReparented", newMaster.Alias) } - -func waitForReplicationPos(ctx context.Context, t *testing.T, tabletA *cluster.Vttablet, tabletB *cluster.Vttablet, timeout float64) { - replicationPosA, _ := cluster.GetMasterPosition(t, *tabletA, hostname) - for { - replicationPosB, _ := cluster.GetMasterPosition(t, *tabletB, hostname) - if positionAtLeast(t, tabletA, replicationPosB, replicationPosA) { - break - } - msg := fmt.Sprintf("%s's replication position to catch up to %s's;currently at: %s, waiting to catch up to: %s", tabletB.Alias, tabletA.Alias, replicationPosB, replicationPosA) - waitStep(t, msg, timeout, 0.01) - } -} - -func positionAtLeast(t *testing.T, tablet *cluster.Vttablet, a string, b string) bool { - isAtleast := false - val, err := tablet.MysqlctlProcess.ExecuteCommandWithOutput("position", "at_least", a, b) - require.NoError(t, err) - if strings.Contains(val, "true") { - isAtleast = true - } - return isAtleast -} - -func waitStep(t *testing.T, msg string, timeout float64, sleepTime float64) float64 { - timeout = timeout - sleepTime - if timeout < 0.0 { - t.Errorf("timeout waiting for condition '%s'", msg) - } - time.Sleep(time.Duration(sleepTime) * time.Second) - return timeout -} diff --git a/go/test/endtoend/worker/worker_test.go b/go/test/endtoend/worker/worker_test.go new file mode 100644 index 00000000000..2278fe6071f --- /dev/null +++ b/go/test/endtoend/worker/worker_test.go @@ -0,0 +1,603 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Tests the robustness and resiliency of vtworkers. + +package worker + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/url" + "os/exec" + "reflect" + "strconv" + "strings" + "testing" + "time" + + "vitess.io/vitess/go/test/endtoend/sharding" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/json2" + "vitess.io/vitess/go/test/endtoend/cluster" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" +) + +var ( + master *cluster.Vttablet + replica1 *cluster.Vttablet + rdOnly1 *cluster.Vttablet + + shard0Master *cluster.Vttablet + shard0Replica *cluster.Vttablet + shard0RdOnly1 *cluster.Vttablet + + shard1Master *cluster.Vttablet + shard1Replica *cluster.Vttablet + shard1RdOnly1 *cluster.Vttablet + + localCluster *cluster.LocalProcessCluster + cell = cluster.DefaultCell + hostname = "localhost" + keyspaceName = "test_keyspace" + shardName = "0" + keyspaceIDType = "uint64" + shardTablets []*cluster.Vttablet + shard0Tablets []*cluster.Vttablet + shard1Tablets []*cluster.Vttablet + workerTestOffset = 0 + commonTabletArg = []string{ + "-binlog_use_v3_resharding_mode=true"} + vtWorkerTest = ` + create table worker_test ( + id bigint unsigned, + sid int unsigned, + msg varchar(64), + primary key (id), + index by_msg (msg) + ) Engine=InnoDB` + + vSchema = ` + { + "sharded": true, + "vindexes": { + "hash": { + "type": "hash" + } + }, + "tables": { + "worker_test": { + "column_vindexes": [ + { + "column": "sid", + "name": "hash" + } + ] + } + } +}` +) + +func TestReparentDuringWorkerCopy(t *testing.T) { + _, err := initializeCluster(t, false) + defer localCluster.Teardown() + require.Nil(t, err) + initialSetup(t) + verifySuccessfulWorkerCopyWithReparent(t, false) +} + +func TestReparentDuringWorkerCopyMysqlDown(t *testing.T) { + _, err := initializeCluster(t, false) + defer localCluster.Teardown() + require.Nil(t, err) + initialSetup(t) + verifySuccessfulWorkerCopyWithReparent(t, true) +} + +func TestWebInterface(t *testing.T) { + _, err := initializeCluster(t, true) + defer localCluster.Teardown() + err = localCluster.StartVtworker(cell, "--use_v3_resharding_mode=true") + assert.Nil(t, err) + baseURL := fmt.Sprintf("http://localhost:%d", localCluster.VtworkerProcess.Port) + + // Wait for /status to become available. + startTime := time.Now() + for { + resp, err := http.Get(baseURL + "/status") + if err != nil && !time.Now().After(startTime.Add(10*time.Second)) { + time.Sleep(10 * time.Millisecond) + continue + } + if resp.StatusCode == 200 || time.Now().After(startTime.Add(10*time.Second)) { + break + } + } + + // Run the command twice to make sure it's idempotent. + i := 0 + for i < 2 { + data := url.Values{"message": {"pong"}} + http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + resp, err := http.Post(baseURL+"/Debugging/Ping", "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) + assert.Nil(t, err) + assert.Equal(t, 307, resp.StatusCode) + + // Wait for the Ping command to finish. + pollForVars(t, "done") + // Verify that the command logged something and it's available at /status. + resp, err = http.Get(baseURL + "/status") + assert.Nil(t, err) + if resp.StatusCode == 200 { + respByte, _ := ioutil.ReadAll(resp.Body) + respStr := string(respByte) + assert.Contains(t, respStr, "Ping command was called with message: 'pong'", fmt.Sprintf("Command did not log output to /status: %s", respStr)) + } + + // Reset the job. + _, err = http.Get(baseURL + "/reset") + assert.Nil(t, err) + resp, err = http.Get(baseURL + "/status") + assert.Nil(t, err) + if resp.StatusCode == 200 { + respByte, _ := ioutil.ReadAll(resp.Body) + statusAfterReset := string(respByte) + assert.Contains(t, statusAfterReset, "This worker is idle.", "/status does not indicate that the reset was successful") + } + i++ + } + + err = localCluster.VtworkerProcess.TearDown() + assert.Nil(t, err) + +} + +func initialSetup(t *testing.T) { + + runShardTablets(t, "0", shardTablets, true) + + // create the split shards + runShardTablets(t, "-80", shard0Tablets, false) + runShardTablets(t, "80-", shard1Tablets, false) + + // insert values + insertValues(master, "shard-0", 1, 2000, 0) + insertValues(master, "shard-1", 4, 2000, 1) + + // wait for replication position + cluster.WaitForReplicationPos(t, master, rdOnly1, "localhost", 60) + + copySchemaToDestinationShard(t) +} + +func verifySuccessfulWorkerCopyWithReparent(t *testing.T, isMysqlDown bool) { + + // Verifies that vtworker can successfully copy data for a SplitClone. + + // Order of operations: + // 1. Run a background vtworker + // 2. Wait until the worker successfully resolves the destination masters. + // 3. Reparent the destination tablets + // 4. Wait until the vtworker copy is finished + // 5. Verify that the worker was forced to reresolve topology and retry writes + // due to the reparent. + // 6. Verify that the data was copied successfully to both new shards + + err := localCluster.StartVtworker(cell, "--use_v3_resharding_mode=true") + assert.Nil(t, err) + + // --max_tps is only specified to enable the throttler and ensure that the + // code is executed. But the intent here is not to throttle the test, hence + // the rate limit is set very high. + + var args []string + + args = append(args, "SplitClone", "--offline=false", + "--destination_writer_count", "1", + "--min_healthy_rdonly_tablets", "1", + "--max_tps", "9999") + + // --chunk_count is 2 because rows are currently ordered by primary key such + // that all rows of the first shard come first and then the second shard. + // Make the clone as slow as necessary such that there is enough time to + // run PlannedReparent in the meantime. + + args = append(args, "--source_reader_count", "2", + "--chunk_count", "2", + "--min_rows_per_chunk", "1", + "--write_query_max_rows", "1", + "test_keyspace/0") + + proc, err := localCluster.VtworkerProcess.ExecuteCommandInBg(args...) + assert.Nil(t, err) + + if isMysqlDown { + // vtworker is blocked at this point. This is a good time to test that its + // throttler server is reacting to RPCs. + sharding.CheckThrottlerService(t, fmt.Sprintf("%s:%d", hostname, localCluster.VtworkerProcess.GrpcPort), + []string{"test_keyspace/-80", "test_keyspace/80-"}, 9999, *localCluster) + + pollForVars(t, "cloning the data (online)") + + // Stop MySql + var mysqlCtlProcessList []*exec.Cmd + + for _, tablet := range []*cluster.Vttablet{shard0Master, shard1Master} { + tablet.MysqlctlProcess.InitMysql = false + sqlProc, err := tablet.MysqlctlProcess.StopProcess() + assert.Nil(t, err) + mysqlCtlProcessList = append(mysqlCtlProcessList, sqlProc) + } + + // Wait for mysql processes to stop + for _, sqlProc := range mysqlCtlProcessList { + if err := sqlProc.Wait(); err != nil { + t.Fatal(err) + } + } + + // If MySQL is down, we wait until vtworker retried at least once to make + // sure it reached the point where a write failed due to MySQL being down. + // There should be two retries at least, one for each destination shard. + pollForVarsWorkerRetryCount(t, 1) + + // Bring back masters. Since we test with semi-sync now, we need at least + // one replica for the new master. This test is already quite expensive, + // so we bring back the old master and then let it be converted to a + // replica by PRS, rather than leaving the old master down and having a + // third replica up the whole time. + + // start mysql + var mysqlCtlProcessStartList []*exec.Cmd + + for _, tablet := range []*cluster.Vttablet{shard0Master, shard1Master} { + tablet.MysqlctlProcess.InitMysql = false + sqlProc, err := tablet.MysqlctlProcess.StartProcess() + assert.Nil(t, err) + mysqlCtlProcessStartList = append(mysqlCtlProcessStartList, sqlProc) + } + + // Wait for mysql processes to start + for _, sqlProc := range mysqlCtlProcessStartList { + if err := sqlProc.Wait(); err != nil { + t.Fatal(err) + } + } + } else { + + // NOTE: There is a race condition around this: + // It's possible that the SplitClone vtworker command finishes before the + // PlannedReparentShard vtctl command, which we start below, succeeds. + // Then the test would fail because vtworker did not have to retry. + // + // To workaround this, the test takes a parameter to increase the number of + // rows that the worker has to copy (with the idea being to slow the worker + // down). + // You should choose a value for num_insert_rows, such that this test + // passes for your environment (trial-and-error...) + // Make sure that vtworker got past the point where it picked a master + // for each destination shard ("finding targets" state). + pollForVars(t, "cloning the data (online)") + + } + + // Reparent away from the old masters. + localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "-keyspace_shard", + "test_keyspace/-80", "-new_master", shard0Replica.Alias) + + localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "-keyspace_shard", + "test_keyspace/80-", "-new_master", shard1Replica.Alias) + + proc.Wait() + + // Verify that we were forced to re-resolve and retry. + pollForVarsWorkerRetryCount(t, 1) + + err = localCluster.VtworkerProcess.TearDown() + assert.Nil(t, err) + + cluster.WaitForReplicationPos(t, shard0Replica, shard0RdOnly1, "localhost", 60) + cluster.WaitForReplicationPos(t, shard1Replica, shard1RdOnly1, "localhost", 60) + + err = localCluster.VtworkerProcess.ExecuteVtworkerCommand(localCluster.GetAndReservePort(), localCluster.GetAndReservePort(), "-cell", cell, + "--use_v3_resharding_mode=true", + "SplitClone", + "--online=false", + "--min_healthy_rdonly_tablets", "1", + "test_keyspace/0") + assert.Nil(t, err) + + // Make sure that everything is caught up to the same replication point + runSplitDiff(t, "test_keyspace/-80") + runSplitDiff(t, "test_keyspace/80-") + assertShardDataEqual(t, "0", master, shard0Replica) + assertShardDataEqual(t, "1", master, shard1Replica) +} + +func assertShardDataEqual(t *testing.T, shardNum string, sourceTablet *cluster.Vttablet, destinationTablet *cluster.Vttablet) { + messageStr := fmt.Sprintf("shard-%s", shardNum) + selectQuery := "select id,sid,msg from worker_test where msg = '" + messageStr + "' order by id asc" + qrSource, err := sourceTablet.VttabletProcess.QueryTablet(selectQuery, keyspaceName, true) + assert.Nil(t, err) + + // Make sure all the right rows made it from the source to the destination + qrDestination, err := destinationTablet.VttabletProcess.QueryTablet(selectQuery, keyspaceName, true) + assert.Nil(t, err) + assert.Equal(t, len(qrSource.Rows), len(qrDestination.Rows)) + + assert.Equal(t, fmt.Sprint(qrSource.Rows), fmt.Sprint(qrDestination.Rows)) + + // Make sure that there are no extra rows on the destination + countQuery := "select count(*) from worker_test" + qrDestinationCount, err := destinationTablet.VttabletProcess.QueryTablet(countQuery, keyspaceName, true) + assert.Nil(t, err) + assert.Equal(t, fmt.Sprintf("%d", len(qrDestination.Rows)), fmt.Sprintf("%s", qrDestinationCount.Rows[0][0].ToBytes())) + +} + +// Runs a vtworker SplitDiff on the given keyspace/shard. +func runSplitDiff(t *testing.T, keyspaceShard string) { + + err := localCluster.VtworkerProcess.ExecuteVtworkerCommand(localCluster.GetAndReservePort(), + localCluster.GetAndReservePort(), + "-cell", cell, + "--use_v3_resharding_mode=true", + "SplitDiff", + "--min_healthy_rdonly_tablets", "1", + keyspaceShard) + assert.Nil(t, err) + +} + +func pollForVars(t *testing.T, mssg string) { + startTime := time.Now() + var resultMap map[string]interface{} + var err error + var workerState string + for { + resultMap, err = localCluster.VtworkerProcess.GetVars() + assert.Nil(t, err) + workerState = fmt.Sprintf("%v", reflect.ValueOf(resultMap["WorkerState"])) + if strings.Contains(workerState, mssg) || (time.Now().After(startTime.Add(60 * time.Second))) { + break + } + continue + } + assert.Contains(t, workerState, mssg) +} + +func pollForVarsWorkerRetryCount(t *testing.T, count int) { + startTime := time.Now() + var resultMap map[string]interface{} + var err error + var workerRetryCountInt int + for { + resultMap, err = localCluster.VtworkerProcess.GetVars() + if err != nil { + continue + } + workerRetryCount := fmt.Sprintf("%v", reflect.ValueOf(resultMap["WorkerRetryCount"])) + workerRetryCountInt, err = strconv.Atoi(workerRetryCount) + assert.Nil(t, err) + if workerRetryCountInt > count || (time.Now().After(startTime.Add(60 * time.Second))) { + break + } + continue + } + assert.Greater(t, workerRetryCountInt, count) +} + +// vttablet: the Tablet instance to modify. +// msg: the value of `msg` column. +// numValues: number of rows to be inserted. +func insertValues(tablet *cluster.Vttablet, msg string, sid int, numValues int, initialVal int) { + + // For maximum performance, multiple values are inserted in one statement. + // However, when the statements are too long, queries will timeout and + // vttablet will kill them. Therefore, we chunk it into multiple statements. + maxChunkSize := 100 * 1000 + + var fullList []int + for i := 1; i <= numValues; i++ { + fullList = append(fullList, i) + } + m := getChunkArr(fullList, maxChunkSize) + + for i := 0; i < len(m); i++ { + valueStr := "" + for j := 0; j < len(m[i]); j++ { + if m[i][j] != m[i][0] { + valueStr += "," + } + rowID := j*2 + initialVal + valueStr = valueStr + fmt.Sprintf("(%d, %d, '%s')", rowID, sid, msg) + } + workerTestOffset += len(m[i]) + tablet.VttabletProcess.QueryTablet(fmt.Sprintf("insert into worker_test(id, sid, msg) values %s", valueStr), keyspaceName, true) + } +} + +func getChunkArr(fullList []int, chunkSize int) [][]int { + var m [][]int + for i := 0; i < len(fullList); i += chunkSize { + end := i + chunkSize + if end > len(fullList) { + end = len(fullList) + } + m = append(m, fullList[i:end]) + } + return m +} + +// shardName: the name of the shard to start tablets in +// tabletArr: an instance of ShardTablets for the given shard +// createTable: boolean, True iff we should create a table on the tablets +func runShardTablets(t *testing.T, shardName string, tabletArr []*cluster.Vttablet, createTable bool) error { + //Handles all the necessary work for initially running a shard's tablets. + + // This encompasses the following steps: + // 1. (optional) Create db + // 2. Starting vttablets and let themselves init them + // 3. Waiting for the appropriate vttablet state + // 4. Force reparent to the master tablet + // 5. RebuildKeyspaceGraph + // 7. (optional) Running initial schema setup + + // Start tablets. + for _, tablet := range tabletArr { + if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { + return err + } + tablet.VttabletProcess.ServiceMap = "grpc-queryservice,grpc-tabletmanager,grpc-updatestream,grpc-throttler" + err := tablet.VttabletProcess.Setup() + require.Nil(t, err) + } + + // Reparent to choose an initial master and enable replication. + err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, tabletArr[0].TabletUID) + require.Nil(t, err) + + for { + result, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", fmt.Sprintf("test_keyspace/%s", shardName)) + assert.Nil(t, err) + + var shardInfo topodatapb.Shard + err = json2.Unmarshal([]byte(result), &shardInfo) + assert.Nil(t, err) + + if int(shardInfo.MasterAlias.Uid) == tabletArr[0].TabletUID { + break + } + time.Sleep(10 * time.Second) + continue + } + + err = localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "test_keyspace") + assert.Nil(t, err) + + // Enforce a health check instead of waiting for the next periodic one. + // (saves up to 1 second execution time on average) + for _, tablet := range []*cluster.Vttablet{tabletArr[1], tabletArr[2]} { + err = localCluster.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) + assert.Nil(t, err) + } + + // Wait for tablet state to change after starting all tablets. This allows + // us to start all tablets at once, instead of sequentially waiting. + // NOTE: Replication has to be enabled first or the health check will + // set a replica or rdonly tablet back to NOT_SERVING. + + for _, tablet := range tabletArr { + err = tablet.VttabletProcess.WaitForTabletType("SERVING") + require.Nil(t, err) + } + + if createTable { + err = localCluster.VtctlclientProcess.ApplySchema(keyspaceName, vtWorkerTest) + assert.Nil(t, err) + + err = localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) + assert.Nil(t, err) + } + + return err +} + +func copySchemaToDestinationShard(t *testing.T) { + for _, keyspaceShard := range []string{"test_keyspace/-80", "test_keyspace/80-"} { + err := localCluster.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--exclude_tables", "unrelated", "test_keyspace/0", keyspaceShard) + assert.Nil(t, err) + } +} + +func initializeCluster(t *testing.T, onlyTopo bool) (int, error) { + + localCluster = cluster.NewCluster(cell, hostname) + + // Start topo server + err := localCluster.StartTopo() + if err != nil { + return 1, err + } + + if onlyTopo { + return 0, nil + } + // Start keyspace + keyspace := &cluster.Keyspace{ + Name: keyspaceName, + } + localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) + + shard := &cluster.Shard{ + Name: shardName, + } + shard0 := &cluster.Shard{ + Name: "-80", + } + shard1 := &cluster.Shard{ + Name: "80-", + } + + // Defining all the tablets + master = localCluster.GetVttabletInstance("replica", 0, "") + replica1 = localCluster.GetVttabletInstance("replica", 0, "") + rdOnly1 = localCluster.GetVttabletInstance("rdonly", 0, "") + shard0Master = localCluster.GetVttabletInstance("replica", 0, "") + shard0Replica = localCluster.GetVttabletInstance("replica", 0, "") + shard0RdOnly1 = localCluster.GetVttabletInstance("rdonly", 0, "") + shard1Master = localCluster.GetVttabletInstance("replica", 0, "") + shard1Replica = localCluster.GetVttabletInstance("replica", 0, "") + shard1RdOnly1 = localCluster.GetVttabletInstance("rdonly", 0, "") + + shard.Vttablets = []*cluster.Vttablet{master, replica1, rdOnly1} + shard0.Vttablets = []*cluster.Vttablet{shard0Master, shard0Replica, shard0RdOnly1} + shard1.Vttablets = []*cluster.Vttablet{shard1Master, shard1Replica, shard1RdOnly1} + + localCluster.VtTabletExtraArgs = append(localCluster.VtTabletExtraArgs, commonTabletArg...) + + err = localCluster.LaunchCluster(keyspace, []cluster.Shard{*shard, *shard0, *shard1}) + + // Start MySql + var mysqlCtlProcessList []*exec.Cmd + for _, shard := range localCluster.Keyspaces[0].Shards { + for _, tablet := range shard.Vttablets { + if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { + t.Fatal(err) + } else { + mysqlCtlProcessList = append(mysqlCtlProcessList, proc) + } + } + } + + // Wait for mysql processes to start + for _, proc := range mysqlCtlProcessList { + if err := proc.Wait(); err != nil { + t.Fatal(err) + } + } + + shardTablets = []*cluster.Vttablet{master, replica1, rdOnly1} + shard0Tablets = []*cluster.Vttablet{shard0Master, shard0Replica, shard0RdOnly1} + shard1Tablets = []*cluster.Vttablet{shard1Master, shard1Replica, shard1RdOnly1} + + return 0, nil +} diff --git a/test/config.json b/test/config.json index 7f4acf8caa6..c9a9f4b5d9e 100644 --- a/test/config.json +++ b/test/config.json @@ -114,17 +114,6 @@ "RetryMax": 0, "Tags": [] }, - "worker": { - "File": "worker.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 5, - "RetryMax": 0, - "Tags": [ - "worker_test" - ] - }, "xb_recovery": { "File": "xb_recovery.py", "Args": [], @@ -510,6 +499,17 @@ "Shard": 17, "RetryMax": 0, "Tags": [] + }, + "worker": { + "File": "worker_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/worker"], + "Command": [], + "Manual": false, + "Shard": 23, + "RetryMax": 0, + "Tags": [ + "worker_test" + ] } } } \ No newline at end of file From 3955b46d9eabdfa609dc576151dd72460616d4d5 Mon Sep 17 00:00:00 2001 From: Pham Duc Hanh Date: Wed, 19 Feb 2020 09:58:41 +0700 Subject: [PATCH 129/825] trivial fix typo Signed-off-by: Pham Duc Hanh --- doc/V3HighLevelDesign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/V3HighLevelDesign.md b/doc/V3HighLevelDesign.md index 5becdb0d46b..72fc15d43c4 100644 --- a/doc/V3HighLevelDesign.md +++ b/doc/V3HighLevelDesign.md @@ -641,7 +641,7 @@ All other operations are considered ‘cheap’ because they can be applied as t ## Preserving the original representation -In the case of VTGate, the ‘Route’ operation is capable of performing all 9 functions, as long as all the rows needed to fulfil the query reside in a single database. However, those functions have to be expressed as an SQL query. Most often, the original query could just be passed-through to a single shard. So, the one important question is: If we converted a query to its relational representation, can we then convert it back to the original query? The answer is no, or at least not trivially; The relational operators don’t always map one-to-one with the constructs of an SQL statement. Here’s a specific example: +In the case of VTGate, the ‘Route’ operation is capable of performing all 9 functions, as long as all the rows needed to fulfill the query reside in a single database. However, those functions have to be expressed as an SQL query. Most often, the original query could just be passed-through to a single shard. So, the one important question is: If we converted a query to its relational representation, can we then convert it back to the original query? The answer is no, or at least not trivially; The relational operators don’t always map one-to-one with the constructs of an SQL statement. Here’s a specific example: `select count(*), a+1, b from t group by a+1, b` From 8412804573b17109929501185947f8ed7d742dee Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Wed, 19 Feb 2020 11:07:12 +0530 Subject: [PATCH 130/825] bootstrap.sh changed for install chromedriver. Signed-off-by: pradip parmar --- bootstrap.sh | 4 +--- test/config.json | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 1252e3717b0..feeb04e3fb2 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -283,9 +283,7 @@ function install_chromedriver() { rm chromedriver_linux64.zip fi } -if [ "$BUILD_PYTHON" == 1 ] ; then - install_dep "chromedriver" "73.0.3683.20" "$VTROOT/dist/chromedriver" install_chromedriver -fi +install_dep "chromedriver" "73.0.3683.20" "$VTROOT/dist/chromedriver" install_chromedriver if [ "$BUILD_PYTHON" == 1 ] ; then PYTHONPATH='' $PIP install mysql-connector-python diff --git a/test/config.json b/test/config.json index 37c46d1559f..4318ebaad3e 100644 --- a/test/config.json +++ b/test/config.json @@ -494,7 +494,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vtctldweb"], "Command": [], "Manual": false, - "Shard": 21, + "Shard": 24, "RetryMax": 0, "Tags": [] }, From e2242309169ef7a3aff247df8b53fa09b382d45d Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Wed, 19 Feb 2020 13:13:03 +0530 Subject: [PATCH 131/825] Fixed Update test cases Signed-off-by: Harshit Gangal --- go/vt/vtgate/autocommit_test.go | 15 +++++---------- go/vt/vtgate/executor_dml_test.go | 14 ++++++++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index b37582e7d04..397262ad5a0 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -82,16 +82,11 @@ func TestAutocommitUpdateLookup(t *testing.T) { // TestAutocommitUpdateVindexChange: transaction: select & update before final update. func TestAutocommitUpdateVindexChange(t *testing.T) { executor, sbc, _, sbclookup := createExecutorEnv() - sbc.SetResults([]*sqltypes.Result{{ - Fields: []*querypb.Field{ - {Name: "name", Type: sqltypes.VarChar}, - }, - RowsAffected: 1, - InsertID: 0, - Rows: [][]sqltypes.Value{{ - //sqltypes.NewVarChar("myname"), - }}, - }}) + sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), + "1|1|foo", + ), + }) _, err := autocommitExec(executor, "update user2 set name='myname', lastname='mylastname' where id = 1") require.NoError(t, err) diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 9d3bc5ba8e7..faa7ebf4633 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -95,11 +95,17 @@ func TestUpdateEqual(t *testing.T) { sbc1.Queries = nil sbc2.Queries = nil sbclookup.Queries = nil + sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), + "1|1|foo", + ), + }) + _, err = executorExec(executor, "update user2 set name='myname', lastname='mylastname' where id = 1", nil) require.NoError(t, err) wantQueries = []*querypb.BoundQuery{ { - Sql: "select name, lastname from user2 where id = 1 for update", + Sql: "select id, name, lastname from user2 where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { @@ -202,8 +208,8 @@ func TestUpdateMultiOwned(t *testing.T) { sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult( - sqltypes.MakeTestFields("a|b|c|d|e|f", "int64|int64|int64|int64|int64|int64"), - "10|20|30|40|50|60", + sqltypes.MakeTestFields("id|a|b|c|d|e|f", "int64|int64|int64|int64|int64|int64|int64"), + "1|10|20|30|40|50|60", ), }) _, err := executorExec(executor, "update user set a=1, b=2, f=4, e=3 where id=1", nil) @@ -211,7 +217,7 @@ func TestUpdateMultiOwned(t *testing.T) { t.Fatal(err) } wantQueries := []*querypb.BoundQuery{{ - Sql: "select a, b, c, d, e, f from user where id = 1 for update", + Sql: "select id, a, b, c, d, e, f from user where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "update user set a = 1, b = 2, f = 4, e = 3 where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", From f57c316f39abcefb8b7e0e18a86e43fbdb717d72 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Wed, 19 Feb 2020 15:12:06 +0530 Subject: [PATCH 132/825] empty text reading of the component fixed. Signed-off-by: pradip parmar --- .../vtctldweb/vtctld_web_main_test.go | 102 ++++++++++-------- go/test/endtoend/vtctldweb/vtctld_web_test.go | 66 ++++-------- 2 files changed, 78 insertions(+), 90 deletions(-) diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go index 2e0669ea7fb..61a3b291836 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -19,8 +19,10 @@ package vtctldweb import ( "flag" "fmt" + "math/rand" "os" "os/exec" + "strings" "syscall" "testing" "time" @@ -86,7 +88,7 @@ func TestMain(m *testing.M) { } // create driver here - err = CreateWebDriver() + err = CreateWebDriver(getPort()) if err != nil { return 1, err } @@ -151,8 +153,8 @@ func RunXvfb() (func() error, error) { } // CreateWebDriver Creates a webdriver object (local or remote for Travis). -func CreateWebDriver() error { - selenium.SetDebug(true) +func CreateWebDriver(port int) error { + // selenium.SetDebug(true) // Set common Options options := selenium.ChromeDriver(os.Getenv("VTROOT") + "/dist") @@ -189,12 +191,12 @@ func CreateWebDriver() error { os.Setenv("webdriver.chrome.driver", os.Getenv("VTROOT")+"/dist") var err error - seleniumService, err = selenium.NewChromeDriverService(os.Getenv("VTROOT")+"/dist/chromedriver/chromedriver", 9515, options) + seleniumService, err = selenium.NewChromeDriverService(os.Getenv("VTROOT")+"/dist/chromedriver/chromedriver", port, options) if err != nil { return err } - wd, err = selenium.NewRemote(cc, "http://localhost:9515/wd/hub") + wd, err = selenium.NewRemote(cc, fmt.Sprintf("http://localhost:%d/wd/hub", port)) if err != nil { return err } @@ -229,8 +231,7 @@ func checkHeatMaps(t *testing.T, selectedKs string) { heading, err := elem.FindElement(selenium.ByID, "keyspaceName") require.Nil(t, err) - headingTxt, err := heading.Text() - require.Nil(t, err) + headingTxt := text(t, heading) _, err = elem.FindElement(selenium.ByID, headingTxt) require.Nil(t, err) @@ -244,8 +245,7 @@ func checkHeatMaps(t *testing.T, selectedKs string) { heading, err := elems[0].FindElement(selenium.ByID, "keyspaceName") require.Nil(t, err) - headingTxt, err := heading.Text() - require.Nil(t, err) + headingTxt := text(t, heading) _, err = elem.FindElement(selenium.ByID, headingTxt) require.Nil(t, err) @@ -261,20 +261,22 @@ func changeDropdownOptions(t *testing.T, dropdownID, dropdownValue string) { dropdown, err := statusContent.FindElement(selenium.ByID, dropdownID) require.Nil(t, err) - err = dropdown.Click() - require.Nil(t, err) + click(t, dropdown) options, err := dropdown.FindElements(selenium.ByTagName, "li") require.Nil(t, err) + triedOption := []string{} for _, op := range options { - opTxt, err := op.Text() - require.Nil(t, err) + opTxt := text(t, op) if opTxt == dropdownValue { - err = op.Click() - require.Nil(t, err) - break + click(t, op) + return } + + triedOption = append(triedOption, opTxt) } + ss(t, "option_check") + t.Log("dropdown options change failed", strings.Join(triedOption, ","), dropdownValue) } // checkDropdowns validates the dropdown values and selected value. @@ -316,9 +318,7 @@ func getDropdownSelection(t *testing.T, group string) string { elem, err = elem.FindElement(selenium.ByTagName, "label") require.Nil(t, err) - tx, err := elem.Text() - require.Nil(t, err) - return tx + return text(t, elem) } // getDropdownOptions fetchs list of option available for corresponding group. @@ -332,9 +332,7 @@ func getDropdownOptions(t *testing.T, group string) []string { var out []string for _, elem = range elems { - tx, err := elem.Text() - require.Nil(t, err) - out = append(out, tx) + out = append(out, text(t, elem)) } return out @@ -350,9 +348,7 @@ func getDashboardKeyspaces(t *testing.T) []string { ksCards, err := dashboardContent.FindElements(selenium.ByClassName, "vt-keyspace-card") var out []string for _, ks := range ksCards { - txt, err := ks.Text() - require.Nil(t, err) - out = append(out, txt) + out = append(out, text(t, ks)) } return out } @@ -367,9 +363,7 @@ func getDashboardShards(t *testing.T) []string { ksCards, err := dashboardContent.FindElements(selenium.ByClassName, "vt-shard-stats") var out []string for _, ks := range ksCards { - txt, err := ks.Text() - require.Nil(t, err) - out = append(out, txt) + out = append(out, text(t, ks)) } return out } @@ -384,10 +378,7 @@ func getKeyspaceShard(t *testing.T) []string { require.Nil(t, err) var out []string for _, s := range shards { - txt, err := s.Text() - require.Nil(t, err) - - out = append(out, txt) + out = append(out, text(t, s)) } return out } @@ -409,17 +400,14 @@ func getShardTablets(t *testing.T) ([]string, []string) { typ, err := columns[1].FindElement(selenium.ByClassName, "ui-cell-data") require.Nil(t, err) - typTxt, err := typ.Text() - require.Nil(t, err) + typTxt := text(t, typ) tabletTypes = append(tabletTypes, typTxt) uid, err := columns[3].FindElement(selenium.ByClassName, "ui-cell-data") require.Nil(t, err) - uidTxt, err := uid.Text() - require.Nil(t, err) - + uidTxt := text(t, uid) tabletUIDs = append(tabletUIDs, uidTxt) } @@ -447,8 +435,7 @@ func navigateToKeyspaceView(t *testing.T) { shardStarts, err := ksCard[0].FindElement(selenium.ByTagName, "md-list") require.Nil(t, err) - err = shardStarts.Click() - require.Nil(t, err) + click(t, shardStarts) wait(t, selenium.ByClassName, "vt-card") } @@ -463,8 +450,7 @@ func navigateToShardView(t *testing.T) { require.Nil(t, err) require.Equal(t, 2, len(shardCards)) - err = shardCards[0].Click() - require.Nil(t, err) + click(t, shardCards[0]) wait(t, selenium.ByID, "1") } @@ -486,10 +472,38 @@ func assertDialogCommand(t *testing.T, dialog selenium.WebElement, cmds []string var tmpCmd []string for _, elm := range elms { - txt, err := elm.Text() - require.Nil(t, err) - tmpCmd = append(tmpCmd, txt) + tmpCmd = append(tmpCmd, text(t, elm)) } assert.ElementsMatch(t, cmds, tmpCmd) } + +func text(t *testing.T, elem selenium.WebElement) string { + for i := 0; i < 5; i++ { + opTxt, err := elem.Text() + require.Nil(t, err) + if opTxt != "" { + return opTxt + } + } + + return "" +} + +func click(t *testing.T, elem selenium.WebElement) { + require.Nil(t, elem.Click()) +} + +// ss takes screenshot of chrome, for debugging only. +func ss(t *testing.T, name string) { + b, err := wd.Screenshot() + require.Nil(t, err) + f, err := os.Create("./" + name) + require.Nil(t, err) + _, err = f.Write(b) + require.Nil(t, err) +} + +func getPort() int { + return 50000 + rand.Intn(10000) +} diff --git a/go/test/endtoend/vtctldweb/vtctld_web_test.go b/go/test/endtoend/vtctldweb/vtctld_web_test.go index ce742f86fae..70762344be8 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_test.go @@ -33,8 +33,7 @@ func TestRealtimeStats(t *testing.T) { statusBtn, err := wd.FindElement(selenium.ByPartialLinkText, "Status") require.Nil(t, err) - err = statusBtn.Click() - require.Nil(t, err) + click(t, statusBtn) wait(t, selenium.ByTagName, "vt-status") @@ -92,19 +91,14 @@ func TestCreateKs(t *testing.T) { dashboardMenu, err := dashboardContent.FindElement(selenium.ByClassName, "vt-menu") require.Nil(t, err) - err = dashboardMenu.Click() - require.Nil(t, err) + click(t, dashboardMenu) dashboardOptions, err := dashboardContent.FindElements(selenium.ByClassName, "ui-menuitem-text") require.Nil(t, err) for _, v := range dashboardOptions { - txt, err := v.Text() - require.Nil(t, err) - - if txt == "New" { - err := v.Click() - require.Nil(t, err) + if text(t, v) == "New" { + click(t, v) break } } @@ -131,26 +125,22 @@ func TestCreateKs(t *testing.T) { dropdown, err := dialog.FindElement(selenium.ByTagName, "p-dropdown") require.Nil(t, err) - err = dropdown.Click() - require.Nil(t, err) + click(t, dropdown) options, err := dropdown.FindElements(selenium.ByTagName, "li") require.Nil(t, err) - err = options[1].Click() - require.Nil(t, err) + click(t, options[1]) assertDialogCommand(t, dialog, []string{"CreateKeyspace", "-sharding_column_name=test_id", "-sharding_column_type=BYTES", "-force=false", "test_keyspace3"}) create, err := dialog.FindElement(selenium.ByID, "vt-action") require.Nil(t, err) - err = create.Click() - require.Nil(t, err) + click(t, create) dismiss, err := dialog.FindElement(selenium.ByID, "vt-dismiss") require.Nil(t, err) - err = dismiss.Click() - require.Nil(t, err) + click(t, dismiss) ksNames := getDashboardKeyspaces(t) assert.ElementsMatch(t, []string{"test_keyspace", "test_keyspace2", "test_keyspace3"}, ksNames) @@ -159,31 +149,24 @@ func TestCreateKs(t *testing.T) { require.Nil(t, err) menu, err := testKs[2].FindElement(selenium.ByClassName, "vt-menu") require.Nil(t, err) - err = menu.Click() - require.Nil(t, err) + click(t, menu) options, err = testKs[2].FindElements(selenium.ByTagName, "li") require.Nil(t, err) for _, v := range options { - txt, err := v.Text() - require.Nil(t, err) - - if txt == "Delete" { - err := v.Click() - require.Nil(t, err) + if text(t, v) == "Delete" { + click(t, v) break } } delete, err := dialog.FindElement(selenium.ByID, "vt-action") require.Nil(t, err) - err = delete.Click() - require.Nil(t, err) + click(t, delete) dismiss, err = dialog.FindElement(selenium.ByID, "vt-dismiss") require.Nil(t, err) - err = dismiss.Click() - require.Nil(t, err) + click(t, dismiss) ksNames = getDashboardKeyspaces(t) assert.ElementsMatch(t, []string{"test_keyspace", "test_keyspace2"}, ksNames) @@ -206,17 +189,13 @@ func TestDashboardValidate(t *testing.T) { menu, err := dashboardContent.FindElement(selenium.ByClassName, "vt-menu") require.Nil(t, err) - err = menu.Click() - require.Nil(t, err) + click(t, menu) firstOption, err := dashboardContent.FindElement(selenium.ByClassName, "ui-menuitem-text") require.Nil(t, err) - txt, err := firstOption.Text() - require.Nil(t, err) - assert.Equal(t, "Validate", txt) + assert.Equal(t, "Validate", text(t, firstOption)) - err = firstOption.Click() - require.Nil(t, err) + click(t, firstOption) dialog, err := dashboardContent.FindElement(selenium.ByTagName, "vt-dialog") require.Nil(t, err) @@ -226,24 +205,19 @@ func TestDashboardValidate(t *testing.T) { checkBoxes, err := dialog.FindElements(selenium.ByClassName, "md-checkbox-inner-container") require.Nil(t, err) - err = checkBoxes[0].Click() - require.Nil(t, err) + click(t, checkBoxes[0]) assertDialogCommand(t, dialog, []string{"Validate", "-ping-tablets"}) validate, err := dialog.FindElement(selenium.ByID, "vt-action") require.Nil(t, err) - err = validate.Click() - require.Nil(t, err) + click(t, validate) validateResp, err := dialog.FindElement(selenium.ByClassName, "vt-resp") require.Nil(t, err) - txt, err = validateResp.Text() - require.Nil(t, err) - fmt.Printf("Validate command response: %s\n", txt) + fmt.Printf("Validate command response: %s\n", text(t, validateResp)) dismiss, err := dialog.FindElement(selenium.ByID, "vt-dismiss") require.Nil(t, err) - err = dismiss.Click() - require.Nil(t, err) + click(t, dismiss) } From bc10b6b9e4a127dc37d57e4813cfb34a9d6f74d3 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Wed, 19 Feb 2020 15:49:51 +0530 Subject: [PATCH 133/825] port base changed. Signed-off-by: pradip parmar --- go/test/endtoend/vtctldweb/vtctld_web_main_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go index 61a3b291836..68a25245391 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -505,5 +505,5 @@ func ss(t *testing.T, name string) { } func getPort() int { - return 50000 + rand.Intn(10000) + return 2000 + rand.Intn(1000) } From fb21d72a61d2985979e9eedd9b759ba0b6659754 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Wed, 19 Feb 2020 16:43:32 +0530 Subject: [PATCH 134/825] port range changed. Signed-off-by: pradip parmar --- go/test/endtoend/vtctldweb/vtctld_web_main_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go index 68a25245391..6a689bad556 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -505,5 +505,5 @@ func ss(t *testing.T, name string) { } func getPort() int { - return 2000 + rand.Intn(1000) + return 20000 + rand.Intn(10000) } From f2dbd1116e62e07ed1ada27c3fb5ea54397dd5b1 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Wed, 19 Feb 2020 20:55:31 +0530 Subject: [PATCH 135/825] scatter delete support for complex where clause Signed-off-by: Harshit Gangal --- .../multi-output/updatesharded-output.txt | 4 +- go/vt/vtgate/planbuilder/delete.go | 23 ++--- .../vtgate/planbuilder/testdata/dml_cases.txt | 96 ++++++++++++++++++- go/vt/vtgate/planbuilder/update.go | 5 +- 4 files changed, 105 insertions(+), 23 deletions(-) diff --git a/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt b/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt index e999a0dcaa6..5bc555eefdc 100644 --- a/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt @@ -27,7 +27,7 @@ update user set pet='fido' where id=1 update user set name='alicia' where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: select name from user where id = 1 limit 10001 for update +1 ks_sharded/-40: select id, name from user where id = 1 limit 10001 for update 2 ks_sharded/80-c0: begin 2 ks_sharded/80-c0: delete from name_user_map where (name = 'name_val_1' and user_id = 1) /* vtgate:: keyspace_id:a6e89b54b129c33051b76db219595660 */ 3 ks_sharded/c0-: begin @@ -43,7 +43,7 @@ update user set name='alicia' where name='alice' 1 ks_sharded/40-80: begin 1 ks_sharded/40-80: select user_id from name_user_map where name = 'alice' limit 10001 2 ks_sharded/-40: begin -2 ks_sharded/-40: select name from user where name = 'alice' limit 10001 for update +2 ks_sharded/-40: select id, name from user where name = 'alice' limit 10001 for update 3 ks_sharded/80-c0: begin 3 ks_sharded/80-c0: delete from name_user_map where (name = 'name_val_1' and user_id = 1) /* vtgate:: keyspace_id:a6e89b54b129c33051b76db219595660 */ 4 ks_sharded/c0-: begin diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index cb7974c5ab8..ec177db2b47 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -74,15 +74,12 @@ func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Del edel.TargetDestination = ro.eroute.TargetDestination return edel, nil } - routingType, vindex, _, values := getDMLRouting(del.Where, edel.Table) + routingType, vindex, vindexCol, values := getDMLRouting(del.Where, edel.Table) if routingType == scatter { edel.Opcode = engine.DeleteScatter - if len(edel.Table.Owned) != 0 { - return nil, errors.New("unsupported: multi shard delete on a table with owned lookup vindexes") - } if del.Limit != nil { - return nil, errors.New("unsupported: multi shard delete with limit") + return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "unsupported: multi shard delete with limit") } } else { edel.Values = values @@ -90,26 +87,22 @@ func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Del edel.Opcode = engine.DeleteEqual } - edel.OwnedVindexQuery = generateDeleteSubquery(del, edel.Table) + edel.OwnedVindexQuery = generateDeleteSubquery(del, edel.Table, vindexCol) return edel, nil } // generateDeleteSubquery generates the query to fetch the rows // that will be deleted. This allows VTGate to clean up any // owned vindexes as needed. -func generateDeleteSubquery(del *sqlparser.Delete, table *vindexes.Table) string { +func generateDeleteSubquery(del *sqlparser.Delete, table *vindexes.Table, vindexCol string) string { if len(table.Owned) == 0 { return "" } buf := sqlparser.NewTrackedBuffer(nil) - buf.WriteString("select ") - for vIdx, cv := range table.Owned { - for cIdx, column := range cv.Columns { - if cIdx == 0 && vIdx == 0 { - buf.Myprintf("%v", column) - } else { - buf.Myprintf(", %v", column) - } + buf.Myprintf("select %s", vindexCol) + for _, cv := range table.Owned { + for _, column := range cv.Columns { + buf.Myprintf(", %v", column) } } buf.Myprintf(" from %v%v for update", table.Name, del.Where) diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt index e51d7d9b20d..c7137f3cef0 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt @@ -326,7 +326,7 @@ "Vindex": "user_index", "Values": [1], "Table": "user", - "OwnedVindexQuery": "select Name, Costly from user where id = 1 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update" } } @@ -388,7 +388,7 @@ 1 ], "Table": "user", - "OwnedVindexQuery": "select Name, Costly from user where id = 1 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update" } } @@ -465,7 +465,7 @@ "Vindex": "music_user_map", "Values": [1], "Table": "music", - "OwnedVindexQuery": "select id from music where id = 1 for update" + "OwnedVindexQuery": "select user_id, id from music where id = 1 for update" } } @@ -1480,7 +1480,7 @@ 1 ], "Table": "multicolvin", - "OwnedVindexQuery": "select column_a, column_b, column_c from multicolvin where kid = 1 for update" + "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update" } } @@ -1847,7 +1847,7 @@ } } -# update multiple vindex value to null +# update vindex value to null with multiple primary keyspace id "update user set name = null where id in (1, 2, 3)" { "Original": "update user set name = null where id in (1, 2, 3)", @@ -1865,3 +1865,89 @@ "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update" } } + +# update vindex value to null without a where clause +"update user set name = null" +{ + "Original": "update user set name = null", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set name = null", + "ChangedVindexValues": { + "name_user_map": [null] + }, + "Table": "user", + "OwnedVindexQuery": "select Id, Name, Costly from user for update" + } +} + +# update vindex value to null with complex where clause +"update user set name = null where id + 1 = 2" +{ + "Original": "update user set name = null where id + 1 = 2", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set name = null where id + 1 = 2", + "ChangedVindexValues": { + "name_user_map": [null] + }, + "Table": "user", + "OwnedVindexQuery": "select Id, Name, Costly from user where id + 1 = 2 for update" + } +} + +# delete from user by primary keyspace id with in clause +"delete from user where id in (1, 2, 3)" +{ + "Original": "delete from user where id in (1, 2, 3)", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user where id in (1, 2, 3)", + "Table": "user", + "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update" + } +} + +# delete from user by complex expression +"delete from user where id + 1 = 2" +{ + "Original": "delete from user where id + 1 = 2", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user where id + 1 = 2", + "Table": "user", + "OwnedVindexQuery": "select Id, Name, Costly from user where id + 1 = 2 for update" + } +} + +# delete from user without a where clause +"delete from user" +{ + "Original": "delete from user", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user", + "Table": "user", + "OwnedVindexQuery": "select Id, Name, Costly from user for update" + } +} diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index b49d27a12ab..c0809346674 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -216,7 +216,10 @@ func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (dmlRoutingTyp continue } - keyColumn = sqlparser.String(index.Columns[0]) + if keyColumn == "" { //TODO - check with sougou on how to deterministically get the primary vindex column + keyColumn = sqlparser.String(index.Columns[0]) + } + if where == nil { return scatter, nil, keyColumn, nil } From 5240335511494a8b13fc6503abec77fda1b483d0 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Wed, 19 Feb 2020 08:42:38 -0700 Subject: [PATCH 136/825] Fix consul dependency Signed-off-by: Morgan Tocker --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5310d66e0b1..e3d537a1fbe 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/gostaticanalysis/analysisutil v0.0.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/hashicorp/consul v1.4.0 + github.com/hashicorp/consul v1.5.1 github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect github.com/hashicorp/go-uuid v1.0.1 // indirect From 499168aa021635c6a95e1413190b6efd3334639c Mon Sep 17 00:00:00 2001 From: Ajeet Jain Date: Thu, 20 Feb 2020 10:44:38 +0530 Subject: [PATCH 137/825] trying out docker test for web vtctl Signed-off-by: Ajeet Jain Signed-off-by: --- .github/workflows/cluster_endtoend.yml | 11 ++++++++--- test.go | 2 +- test/config.json | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 27c73378c32..7699dd56eb3 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] steps: - name: Set up Go @@ -35,11 +35,16 @@ jobs: - name: Installing zookeeper and consul run: | # Only running for shard 18 and 24 where we need to install consul and zookeeper - if [[ ${{matrix.name}} == 18 || ${{matrix.name}} == 24 ]]; then + if [[ ${{matrix.name}} == 18 || ${{matrix.name}} == 24 || ${{matrix.name}} == 25 ]]; then make tools fi - name: sharded cluster_endtoend run: | source build.env - eatmydata -- go run test.go -docker=false -print-log -shard ${{matrix.name}} + if [[ ${{matrix.name}} == 25 ]]; then + eatmydata -- go run test.go -docker=true -print-log -shard ${{matrix.name}} + else + eatmydata -- go run test.go -docker=false -print-log -shard ${{matrix.name}} + fi + diff --git a/test.go b/test.go index b6be41a86e6..0a5adf25311 100755 --- a/test.go +++ b/test.go @@ -174,7 +174,7 @@ func (t *Test) run(dir, dataDir string) ([]byte, error) { testCmd = append(testCmd, extraArgs...) if *docker { // Teardown is unnecessary since Docker kills everything. - testCmd = append(testCmd, "--skip-teardown") + // testCmd = append(testCmd, "--skip-teardown") } } diff --git a/test/config.json b/test/config.json index 4318ebaad3e..4504d85c1eb 100644 --- a/test/config.json +++ b/test/config.json @@ -494,7 +494,7 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vtctldweb"], "Command": [], "Manual": false, - "Shard": 24, + "Shard": 25, "RetryMax": 0, "Tags": [] }, From 74f6b09c46516ed984903f0b70262fd7adcd90d8 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 20 Feb 2020 08:11:56 +0100 Subject: [PATCH 138/825] Use require in test Signed-off-by: Andres Taylor --- go/vt/vtgate/autocommit_test.go | 93 +++++++++++++++------------------ 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index 397262ad5a0..732b01931f5 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -17,7 +17,6 @@ limitations under the License. package vtgate import ( - "strings" "testing" "github.com/stretchr/testify/require" @@ -39,9 +38,9 @@ import ( func TestAutocommitUpdateSharded(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() - if _, err := autocommitExec(executor, "update user set a=2 where id = 1"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "update user set a=2 where id = 1") + require.NoError(t, err) + testBatchQuery(t, "sbc1", sbc1, &querypb.BoundQuery{ Sql: "update user set a = 2 where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{}, @@ -58,9 +57,8 @@ func TestAutocommitUpdateSharded(t *testing.T) { func TestAutocommitUpdateLookup(t *testing.T) { executor, sbc1, _, sbclookup := createExecutorEnv() - if _, err := autocommitExec(executor, "update music set a=2 where id = 2"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "update music set a=2 where id = 2") + require.NoError(t, err) testQueries(t, "sbclookup", sbclookup, []*querypb.BoundQuery{{ Sql: "select user_id from music_user_map where music_id = :music_id", @@ -124,9 +122,9 @@ func TestAutocommitUpdateVindexChange(t *testing.T) { func TestAutocommitDeleteSharded(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() - if _, err := autocommitExec(executor, "delete from user_extra where user_id = 1"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "delete from user_extra where user_id = 1") + require.NoError(t, err) + testBatchQuery(t, "sbc1", sbc1, &querypb.BoundQuery{ Sql: "delete from user_extra where user_id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{}, @@ -143,9 +141,8 @@ func TestAutocommitDeleteSharded(t *testing.T) { func TestAutocommitDeleteLookup(t *testing.T) { executor, sbc1, _, sbclookup := createExecutorEnv() - if _, err := autocommitExec(executor, "delete from music where id = 1"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "delete from music where id = 1") + require.NoError(t, err) testQueries(t, "sbclookup", sbclookup, []*querypb.BoundQuery{{ Sql: "select user_id from music_user_map where music_id = :music_id", @@ -177,9 +174,9 @@ func TestAutocommitDeleteLookup(t *testing.T) { func TestAutocommitDeleteMultiShard(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() - if _, err := autocommitExec(executor, "delete from user_extra where user_id in (1, 2)"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "delete from user_extra where user_id in (1, 2)") + require.NoError(t, err) + testQueries(t, "sbc1", sbc1, []*querypb.BoundQuery{{ Sql: "delete from user_extra where user_id in (1, 2)/* vtgate:: filtered_replication_unfriendly */", BindVariables: map[string]*querypb.BindVariable{}, @@ -201,9 +198,9 @@ func TestAutocommitDeleteMultiShard(t *testing.T) { func TestAutocommitDeleteMultiShardAutoCommit(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() - if _, err := autocommitExec(executor, "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where user_id in (1, 2)"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where user_id in (1, 2)") + require.NoError(t, err) + testBatchQuery(t, "sbc1", sbc1, &querypb.BoundQuery{ Sql: "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where user_id in (1, 2)/* vtgate:: filtered_replication_unfriendly */", BindVariables: map[string]*querypb.BindVariable{}, @@ -223,9 +220,9 @@ func TestAutocommitDeleteMultiShardAutoCommit(t *testing.T) { func TestAutocommitInsertSharded(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() - if _, err := autocommitExec(executor, "insert into user_extra(user_id, v) values (1, 2)"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "insert into user_extra(user_id, v) values (1, 2)") + require.NoError(t, err) + testBatchQuery(t, "sbc1", sbc1, &querypb.BoundQuery{ Sql: "insert into user_extra(user_id, v) values (:_user_id0, 2) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -244,9 +241,8 @@ func TestAutocommitInsertSharded(t *testing.T) { func TestAutocommitInsertLookup(t *testing.T) { executor, sbc1, _, sbclookup := createExecutorEnv() - if _, err := autocommitExec(executor, "insert into user(id, v, name) values (1, 2, 'myname')"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "insert into user(id, v, name) values (1, 2, 'myname')") + require.NoError(t, err) testQueries(t, "sbclookup", sbclookup, []*querypb.BoundQuery{{ Sql: "insert into name_user_map(name, user_id) values (:name0, :user_id0)", @@ -274,9 +270,9 @@ func TestAutocommitInsertLookup(t *testing.T) { func TestAutocommitInsertMultishardAutoCommit(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() - if _, err := autocommitExec(executor, "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (1, 2), (3, 4)"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (1, 2), (3, 4)") + require.NoError(t, err) + testBatchQuery(t, "sbc1", sbc1, &querypb.BoundQuery{ Sql: "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (:_user_id0, 2) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -300,13 +296,12 @@ func TestAutocommitInsertMultishardAutoCommit(t *testing.T) { executor, sbc1, sbc2, _ = createExecutorEnv() // Make the first shard fail - the second completes anyway sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1 - _, err := autocommitExec(executor, "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (1, 2), (3, 4)") - if err == nil || !strings.Contains(err.Error(), "INVALID_ARGUMENT") { - t.Errorf("expected invalid argument error, got %v", err) - } - if len(sbc1.Queries) != 0 || len(sbc1.BatchQueries) != 0 { - t.Errorf("expected no queries") - } + _, err = autocommitExec(executor, "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (1, 2), (3, 4)") + require.Error(t, err) + require.Contains(t, err.Error(), "INVALID_ARGUMENT", "expected invalid argument error") + require.Empty(t, sbc1.Queries) + require.Empty(t, sbc1.BatchQueries) + testAsTransactionCount(t, "sbc1", sbc1, 1) testCommitCount(t, "sbc1", sbc1, 0) @@ -325,9 +320,9 @@ func TestAutocommitInsertMultishardAutoCommit(t *testing.T) { func TestAutocommitInsertMultishard(t *testing.T) { executor, sbc1, sbc2, _ := createExecutorEnv() - if _, err := autocommitExec(executor, "insert into user_extra(user_id, v) values (1, 2), (3, 4)"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "insert into user_extra(user_id, v) values (1, 2), (3, 4)") + require.NoError(t, err) + testQueries(t, "sbc1", sbc1, []*querypb.BoundQuery{{ Sql: "insert into user_extra(user_id, v) values (:_user_id0, 2) /* vtgate:: keyspace_id:166b40b44aba4bd6 */", BindVariables: map[string]*querypb.BindVariable{ @@ -353,9 +348,8 @@ func TestAutocommitInsertMultishard(t *testing.T) { func TestAutocommitInsertAutoinc(t *testing.T) { executor, _, _, sbclookup := createExecutorEnv() - if _, err := autocommitExec(executor, "insert into main1(id, name) values (null, 'myname')"); err != nil { - t.Fatal(err) - } + _, err := autocommitExec(executor, "insert into main1(id, name) values (null, 'myname')") + require.NoError(t, err) testQueries(t, "sbclookup", sbclookup, []*querypb.BoundQuery{{ Sql: "select next :n values from user_seq", @@ -383,9 +377,8 @@ func TestAutocommitTransactionStarted(t *testing.T) { } sql := "update user set a=2 where id = 1" - if _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}); err != nil { - t.Fatal(err) - } + _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + require.NoError(t, err) testQueries(t, "sbc1", sbc1, []*querypb.BoundQuery{{ Sql: "update user set a = 2 where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", @@ -406,9 +399,9 @@ func TestAutocommitDirectTarget(t *testing.T) { } sql := "insert into simple(val) values ('val')" - if _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}); err != nil { - t.Error(err) - } + _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + require.NoError(t, err) + testQueries(t, "sbclookup", sbclookup, []*querypb.BoundQuery{{ Sql: sql + "/* vtgate:: filtered_replication_unfriendly */", BindVariables: map[string]*querypb.BindVariable{}, @@ -428,9 +421,9 @@ func TestAutocommitDirectRangeTarget(t *testing.T) { } sql := "DELETE FROM sharded_user_msgs LIMIT 1000" - if _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}); err != nil { - t.Error(err) - } + _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) + require.NoError(t, err) + testQueries(t, "sbc1", sbc1, []*querypb.BoundQuery{{ Sql: sql + "/* vtgate:: filtered_replication_unfriendly */", BindVariables: map[string]*querypb.BindVariable{}, From 68fba7d498a55722c5b1d919c85c8897d1047d28 Mon Sep 17 00:00:00 2001 From: saurabh Date: Thu, 20 Feb 2020 12:43:27 +0530 Subject: [PATCH 139/825] increase num of records inserted Signed-off-by: saurabh --- go/test/endtoend/worker/worker_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/go/test/endtoend/worker/worker_test.go b/go/test/endtoend/worker/worker_test.go index 2278fe6071f..04d8b70b4e8 100644 --- a/go/test/endtoend/worker/worker_test.go +++ b/go/test/endtoend/worker/worker_test.go @@ -57,7 +57,6 @@ var ( hostname = "localhost" keyspaceName = "test_keyspace" shardName = "0" - keyspaceIDType = "uint64" shardTablets []*cluster.Vttablet shard0Tablets []*cluster.Vttablet shard1Tablets []*cluster.Vttablet @@ -112,6 +111,7 @@ func TestReparentDuringWorkerCopyMysqlDown(t *testing.T) { func TestWebInterface(t *testing.T) { _, err := initializeCluster(t, true) + require.Nil(t, err) defer localCluster.Teardown() err = localCluster.StartVtworker(cell, "--use_v3_resharding_mode=true") assert.Nil(t, err) @@ -179,8 +179,8 @@ func initialSetup(t *testing.T) { runShardTablets(t, "80-", shard1Tablets, false) // insert values - insertValues(master, "shard-0", 1, 2000, 0) - insertValues(master, "shard-1", 4, 2000, 1) + insertValues(master, "shard-0", 1, 4000, 0) + insertValues(master, "shard-1", 4, 4000, 1) // wait for replication position cluster.WaitForReplicationPos(t, master, rdOnly1, "localhost", 60) @@ -575,6 +575,7 @@ func initializeCluster(t *testing.T, onlyTopo bool) (int, error) { localCluster.VtTabletExtraArgs = append(localCluster.VtTabletExtraArgs, commonTabletArg...) err = localCluster.LaunchCluster(keyspace, []cluster.Shard{*shard, *shard0, *shard1}) + assert.Nil(t, err) // Start MySql var mysqlCtlProcessList []*exec.Cmd From b3cadd96a204c5898754fa0101e80a71b52faa6f Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 20 Feb 2020 08:21:23 +0100 Subject: [PATCH 140/825] Update test assertions Signed-off-by: Andres Taylor --- go/vt/vtgate/autocommit_test.go | 2 +- go/vt/vtgate/executor_dml_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index 732b01931f5..a88490c89eb 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -160,7 +160,7 @@ func TestAutocommitDeleteLookup(t *testing.T) { testCommitCount(t, "sbclookup", sbclookup, 1) testQueries(t, "sbc1", sbc1, []*querypb.BoundQuery{{ - Sql: "select id from music where id = 1 for update", + Sql: "select user_id, id from music where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "delete from music where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index faa7ebf4633..bf10eccaff6 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -340,7 +340,7 @@ func TestDeleteEqual(t *testing.T) { _, err := executorExec(executor, "delete from user where id = 1", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ - Sql: "select name from user where id = 1 for update", + Sql: "select Id, name from user where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "delete from user where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", @@ -367,7 +367,7 @@ func TestDeleteEqual(t *testing.T) { _, err = executorExec(executor, "delete from user where id = 1", nil) require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ - Sql: "select name from user where id = 1 for update", + Sql: "select Id, name from user where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "delete from user where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */", @@ -420,7 +420,7 @@ func TestDeleteEqual(t *testing.T) { require.NoError(t, err) wantQueries = []*querypb.BoundQuery{ { - Sql: "select name, lastname from user2 where id = 1 for update", + Sql: "select id, name, lastname from user2 where id = 1 for update", BindVariables: map[string]*querypb.BindVariable{}, }, { @@ -516,7 +516,7 @@ func TestDeleteComments(t *testing.T) { _, err := executorExec(executor, "delete from user where id = 1 /* trailing */", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ - Sql: "select name from user where id = 1 for update /* trailing */", + Sql: "select Id, name from user where id = 1 for update /* trailing */", BindVariables: map[string]*querypb.BindVariable{}, }, { Sql: "delete from user where id = 1 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ /* trailing */", From ddc1fb440dd0d3c3e1d197abc3f3cbd326de0920 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Thu, 20 Feb 2020 13:16:25 +0530 Subject: [PATCH 141/825] vtworker insert value change. Signed-off-by: pradip parmar --- go/test/endtoend/worker/worker_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/test/endtoend/worker/worker_test.go b/go/test/endtoend/worker/worker_test.go index 2278fe6071f..5ab7762f017 100644 --- a/go/test/endtoend/worker/worker_test.go +++ b/go/test/endtoend/worker/worker_test.go @@ -179,8 +179,8 @@ func initialSetup(t *testing.T) { runShardTablets(t, "80-", shard1Tablets, false) // insert values - insertValues(master, "shard-0", 1, 2000, 0) - insertValues(master, "shard-1", 4, 2000, 1) + insertValues(master, "shard-0", 1, 4000, 0) + insertValues(master, "shard-1", 4, 4000, 1) // wait for replication position cluster.WaitForReplicationPos(t, master, rdOnly1, "localhost", 60) From 0ac244162d92318b49c3602ef5a6637031699ace Mon Sep 17 00:00:00 2001 From: Shaun Verch Date: Thu, 20 Feb 2020 17:02:02 -0500 Subject: [PATCH 142/825] Use go modules and fix build Now everything described in the README works with the new paths. Signed-off-by: Shaun Verch --- examples/are-you-alive/.gitignore | 2 + examples/are-you-alive/Gopkg.lock | 123 ------------------ examples/are-you-alive/Gopkg.toml | 29 ----- examples/are-you-alive/README.md | 4 +- examples/are-you-alive/build/dev/reflex.conf | 2 +- .../are-you-alive/build/release/Dockerfile | 4 +- .../are-you-alive/cmd/are-you-alive/main.go | 2 +- examples/are-you-alive/docker-compose.yml | 4 +- examples/are-you-alive/go.mod | 11 ++ examples/are-you-alive/go.sum | 92 +++++++++++++ 10 files changed, 112 insertions(+), 161 deletions(-) create mode 100644 examples/are-you-alive/.gitignore delete mode 100644 examples/are-you-alive/Gopkg.lock delete mode 100644 examples/are-you-alive/Gopkg.toml create mode 100644 examples/are-you-alive/go.mod create mode 100644 examples/are-you-alive/go.sum diff --git a/examples/are-you-alive/.gitignore b/examples/are-you-alive/.gitignore new file mode 100644 index 00000000000..a0ba6e25a73 --- /dev/null +++ b/examples/are-you-alive/.gitignore @@ -0,0 +1,2 @@ +vendor +are-you-alive diff --git a/examples/are-you-alive/Gopkg.lock b/examples/are-you-alive/Gopkg.lock deleted file mode 100644 index 06d76526790..00000000000 --- a/examples/are-you-alive/Gopkg.lock +++ /dev/null @@ -1,123 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" - name = "github.com/beorn7/perks" - packages = ["quantile"] - pruneopts = "UT" - revision = "4b2b341e8d7715fae06375aa633dbb6e91b3fb46" - version = "v1.0.0" - -[[projects]] - digest = "1:ec6f9bf5e274c833c911923c9193867f3f18788c461f76f05f62bb1510e0ae65" - name = "github.com/go-sql-driver/mysql" - packages = ["."] - pruneopts = "UT" - revision = "72cd26f257d44c1114970e19afddcd812016007e" - version = "v1.4.1" - -[[projects]] - digest = "1:318f1c959a8a740366fce4b1e1eb2fd914036b4af58fbd0a003349b305f118ad" - name = "github.com/golang/protobuf" - packages = ["proto"] - pruneopts = "UT" - revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30" - version = "v1.3.1" - -[[projects]] - digest = "1:31e761d97c76151dde79e9d28964a812c46efc5baee4085b86f68f0c654450de" - name = "github.com/konsorten/go-windows-terminal-sequences" - packages = ["."] - pruneopts = "UT" - revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e" - version = "v1.0.2" - -[[projects]] - digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - pruneopts = "UT" - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - version = "v1.0.1" - -[[projects]] - digest = "1:1db19cfa69213ff994b19451023750122862f39643a5f37728126e1b5180eaac" - name = "github.com/prometheus/client_golang" - packages = [ - "prometheus", - "prometheus/internal", - "prometheus/promauto", - "prometheus/promhttp", - ] - pruneopts = "UT" - revision = "50c4339db732beb2165735d2cde0bff78eb3c5a5" - version = "v0.9.3" - -[[projects]] - branch = "master" - digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" - name = "github.com/prometheus/client_model" - packages = ["go"] - pruneopts = "UT" - revision = "fd36f4220a901265f90734c3183c5f0c91daa0b8" - -[[projects]] - digest = "1:8dcedf2e8f06c7f94e48267dea0bc0be261fa97b377f3ae3e87843a92a549481" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model", - ] - pruneopts = "UT" - revision = "17f5ca1748182ddf24fc33a5a7caaaf790a52fcc" - version = "v0.4.1" - -[[projects]] - branch = "master" - digest = "1:77b841555ca2ce5a45d7ba8b3eea5bd6e2e50c139d979b9b10f57a30fbd1132c" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/fs", - ] - pruneopts = "UT" - revision = "a7aeb8df3389edaf53efcc319ad0063cb27c3960" - -[[projects]] - digest = "1:04457f9f6f3ffc5fea48e71d62f2ca256637dee0a04d710288e27e05c8b41976" - name = "github.com/sirupsen/logrus" - packages = ["."] - pruneopts = "UT" - revision = "839c75faf7f98a33d445d181f3018b5c3409a45e" - version = "v1.4.2" - -[[projects]] - branch = "master" - digest = "1:9ca267f99487ef450fff0c2a49eaf0788d9ff3562bbaeff19f564d380f84bc6d" - name = "golang.org/x/sys" - packages = ["unix"] - pruneopts = "UT" - revision = "dbbf3f1254d491605cf4a0034ce25d0dc71b0c58" - -[[projects]] - digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3" - name = "google.golang.org/appengine" - packages = ["cloudsql"] - pruneopts = "UT" - revision = "4c25cacc810c02874000e4f7071286a8e96b2515" - version = "v1.6.0" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/go-sql-driver/mysql", - "github.com/prometheus/client_golang/prometheus", - "github.com/prometheus/client_golang/prometheus/promauto", - "github.com/prometheus/client_golang/prometheus/promhttp", - "github.com/sirupsen/logrus", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/examples/are-you-alive/Gopkg.toml b/examples/are-you-alive/Gopkg.toml deleted file mode 100644 index 2765dc95c4e..00000000000 --- a/examples/are-you-alive/Gopkg.toml +++ /dev/null @@ -1,29 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - -[prune] - go-tests = true - unused-packages = true diff --git a/examples/are-you-alive/README.md b/examples/are-you-alive/README.md index 889ba4f0e01..e02e366ace9 100644 --- a/examples/are-you-alive/README.md +++ b/examples/are-you-alive/README.md @@ -52,8 +52,7 @@ and everything else will be set to defaults. ## Building ``` -dep ensure -v -go build github.com/planetscale/are-you-alive/cmd/are-you-alive +go build vitess.io/vitess/examples/are-you-alive/cmd/are-you-alive ``` ## Testing @@ -62,7 +61,6 @@ First, [install docker compose](https://docs.docker.com/compose/install/) and make sure it's working. Then run: ``` -dep ensure -v docker-compose build docker-compose up ``` diff --git a/examples/are-you-alive/build/dev/reflex.conf b/examples/are-you-alive/build/dev/reflex.conf index 5e368bf1f93..9e92ab1a7ca 100644 --- a/examples/are-you-alive/build/dev/reflex.conf +++ b/examples/are-you-alive/build/dev/reflex.conf @@ -1,2 +1,2 @@ # Rerun "go run" every time a ".go" file changes. --r '(\.go$)' -s -- go run github.com/planetscale/are-you-alive/cmd/are-you-alive --initialize +-r '(\.go$)' -s -- go run vitess.io/vitess/examples/are-you-alive/cmd/are-you-alive --initialize diff --git a/examples/are-you-alive/build/release/Dockerfile b/examples/are-you-alive/build/release/Dockerfile index 51a3fbc36cf..67310da895a 100644 --- a/examples/are-you-alive/build/release/Dockerfile +++ b/examples/are-you-alive/build/release/Dockerfile @@ -1,6 +1,6 @@ FROM golang:1.12.5 AS build -COPY . /go/src/github.com/planetscale/are-you-alive -RUN CGO_ENABLED=0 go install github.com/planetscale/are-you-alive/cmd/are-you-alive +COPY . /go/src/vitess.io/vitess/examples/are-you-alive +RUN CGO_ENABLED=0 go install vitess.io/vitess/examples/are-you-alive/cmd/are-you-alive FROM debian:stretch-slim AS runtime COPY --from=build /go/bin/are-you-alive /go/bin/are-you-alive diff --git a/examples/are-you-alive/cmd/are-you-alive/main.go b/examples/are-you-alive/cmd/are-you-alive/main.go index 5433f4d9bf5..fd747468558 100644 --- a/examples/are-you-alive/cmd/are-you-alive/main.go +++ b/examples/are-you-alive/cmd/are-you-alive/main.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "github.com/go-sql-driver/mysql" - "github.com/planetscale/are-you-alive/pkg/client" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -16,6 +15,7 @@ import ( "os/signal" "sync" "time" + "vitess.io/vitess/examples/are-you-alive/pkg/client" ) /* diff --git a/examples/are-you-alive/docker-compose.yml b/examples/are-you-alive/docker-compose.yml index a8203b09f56..603353dcb89 100644 --- a/examples/are-you-alive/docker-compose.yml +++ b/examples/are-you-alive/docker-compose.yml @@ -5,8 +5,8 @@ services: build: build/dev image: are-you-alive-dev volumes: - - .:/go/src/github.com/planetscale/are-you-alive - working_dir: /go/src/github.com/planetscale/are-you-alive + - .:/go/src/vitess.io/vitess/examples/are-you-alive + working_dir: /go/src/vitess.io/vitess/examples/are-you-alive environment: MYSQL_CONN_STRING: root:mysql@tcp(mysql)/testfixture depends_on: diff --git a/examples/are-you-alive/go.mod b/examples/are-you-alive/go.mod new file mode 100644 index 00000000000..5f3d04ddccd --- /dev/null +++ b/examples/are-you-alive/go.mod @@ -0,0 +1,11 @@ +module vitess.io/vitess/examples/are-you-alive + +go 1.12 + +require ( + github.com/go-sql-driver/mysql v1.5.0 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/prometheus/client_golang v1.4.1 + github.com/sirupsen/logrus v1.4.2 +) diff --git a/examples/are-you-alive/go.sum b/examples/are-you-alive/go.sum new file mode 100644 index 00000000000..7c05c83b99b --- /dev/null +++ b/examples/are-you-alive/go.sum @@ -0,0 +1,92 @@ +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8= +github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +vitess.io/vitess v2.1.1+incompatible h1:nuuGHiWYWpudD3gOCLeGzol2EJ25e/u5Wer2wV1O130= From 2e44ce13c944514aea68f95736f4eb226267a2d7 Mon Sep 17 00:00:00 2001 From: Gary Edgar Date: Thu, 20 Feb 2020 15:47:20 -0800 Subject: [PATCH 143/825] tools/make-release-packages.sh: install dir tweaks Signed-off-by: Gary Edgar --- tools/make-release-packages.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/make-release-packages.sh b/tools/make-release-packages.sh index 77d99aa4014..cfeed5dcd7f 100755 --- a/tools/make-release-packages.sh +++ b/tools/make-release-packages.sh @@ -41,13 +41,17 @@ done; # Copy remaining files, preserving date/permissions # But resolving symlinks -cp -rpfL examples "${RELEASE_DIR}/" +mkdir -p "${RELEASE_DIR}"/share/vitess/ +cp -rpfL examples "${RELEASE_DIR}"/share/vitess/ -echo "Follow the binary installation instructions at: https://vitess.io/docs/get-started/local/" > "${RELEASE_DIR}"/README.md +echo "Follow the binary installation instructions at: https://vitess.io/docs/get-started/local/" > "${RELEASE_DIR}"/share/vitess/examples/README.md cd "${RELEASE_DIR}/.." tar -czf "${TAR_FILE}" "${RELEASE_ID}" +cd "${RELEASE_DIR}" +PREFIX=${PREFIX:-/usr} + fpm \ --force \ --input-type dir \ @@ -56,10 +60,9 @@ fpm \ --url "https://vitess.io/" \ --description "${DESCRIPTION}" \ --license "Apache License - Version 2.0, January 2004" \ - --prefix "/vt" \ - --directories "/vt" \ - --before-install "$VTROOT/tools/preinstall.sh" \ + --prefix "$PREFIX" \ -C "${RELEASE_DIR}" \ + --before-install "$VTROOT/tools/preinstall.sh" \ --package "$(dirname "${RELEASE_DIR}")" \ --iteration "${SHORT_REV}" \ -t deb --deb-no-default-config-files @@ -72,14 +75,14 @@ fpm \ --url "https://vitess.io/" \ --description "${DESCRIPTION}" \ --license "Apache License - Version 2.0, January 2004" \ - --prefix "/vt" \ - --directories "/vt" \ - --before-install "$VTROOT/tools/preinstall.sh" \ + --prefix "$PREFIX" \ -C "${RELEASE_DIR}" \ + --before-install "$VTROOT/tools/preinstall.sh" \ --package "$(dirname "${RELEASE_DIR}")" \ --iteration "${SHORT_REV}" \ -t rpm +cd "${VTROOT}"/releases echo "" echo "Packages created as of $(date +"%m-%d-%y") at $(date +"%r %Z")" echo "" From 776536bf2225298b06042a4d4e975fa81a81411d Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 21 Feb 2020 09:35:14 +0530 Subject: [PATCH 144/825] made seperate githuv action file for web test Signed-off-by: Ajeet jain --- .github/workflows/cluster_endtoend.yml | 10 +++---- .github/workflows/cluster_vtctl_web.yml | 36 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/cluster_vtctl_web.yml diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 7699dd56eb3..1761630018b 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] steps: - name: Set up Go @@ -35,16 +35,12 @@ jobs: - name: Installing zookeeper and consul run: | # Only running for shard 18 and 24 where we need to install consul and zookeeper - if [[ ${{matrix.name}} == 18 || ${{matrix.name}} == 24 || ${{matrix.name}} == 25 ]]; then + if [[ ${{matrix.name}} == 18 || ${{matrix.name}} == 24 ]]; then make tools fi - name: sharded cluster_endtoend run: | source build.env - if [[ ${{matrix.name}} == 25 ]]; then - eatmydata -- go run test.go -docker=true -print-log -shard ${{matrix.name}} - else - eatmydata -- go run test.go -docker=false -print-log -shard ${{matrix.name}} - fi + eatmydata -- go run test.go -docker=false -print-log -shard ${{matrix.name}} diff --git a/.github/workflows/cluster_vtctl_web.yml b/.github/workflows/cluster_vtctl_web.yml new file mode 100644 index 00000000000..e71c28dceda --- /dev/null +++ b/.github/workflows/cluster_vtctl_web.yml @@ -0,0 +1,36 @@ +name: cluster_vtctl_web +on: [push, pull_request] +jobs: + + build: + name: cluster vtctl web + runs-on: ubuntu-latest + steps: + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.13 + + - name: Check out code + uses: actions/checkout@v2 + + - name: Get dependencies + run: | + sudo apt-get update + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata + sudo service mysql stop + sudo service etcd stop + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld + go mod download + + - name: Run make tools + run: | + make tools + + - name: Run vtctl web + run: | + # Running web test inside docker + eatmydata -- go run test.go -docker=true -print-log -shard ${{matrix.name}} + From 3342d5863b61f1b0df919a86f730e96eaf7881cf Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 21 Feb 2020 09:48:17 +0530 Subject: [PATCH 145/825] added the value of shard for web test Signed-off-by: Ajeet jain --- .github/workflows/cluster_vtctl_web.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cluster_vtctl_web.yml b/.github/workflows/cluster_vtctl_web.yml index e71c28dceda..e28143e4b67 100644 --- a/.github/workflows/cluster_vtctl_web.yml +++ b/.github/workflows/cluster_vtctl_web.yml @@ -32,5 +32,5 @@ jobs: - name: Run vtctl web run: | # Running web test inside docker - eatmydata -- go run test.go -docker=true -print-log -shard ${{matrix.name}} + eatmydata -- go run test.go -docker=true -print-log -shard 25 From 2a8b8fe4bad2f5c913289fe867aacfd2a60600c7 Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Fri, 21 Feb 2020 10:04:23 +0530 Subject: [PATCH 146/825] added comment for docker run inside test.go Signed-off-by: Ajeet jain --- test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test.go b/test.go index 0a5adf25311..a31c1ce153e 100755 --- a/test.go +++ b/test.go @@ -174,6 +174,7 @@ func (t *Test) run(dir, dataDir string) ([]byte, error) { testCmd = append(testCmd, extraArgs...) if *docker { // Teardown is unnecessary since Docker kills everything. + // Go cluster doesn't recognize 'skip-teardown' flag so commenting it out for now. // testCmd = append(testCmd, "--skip-teardown") } } From 2f2fa8c046f375f3545b1e719ac4278660b38aaa Mon Sep 17 00:00:00 2001 From: saurabh Date: Fri, 21 Feb 2020 11:30:35 +0530 Subject: [PATCH 147/825] removing service map Signed-off-by: saurabh --- go/test/endtoend/worker/worker_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/go/test/endtoend/worker/worker_test.go b/go/test/endtoend/worker/worker_test.go index 04d8b70b4e8..066bfc42a31 100644 --- a/go/test/endtoend/worker/worker_test.go +++ b/go/test/endtoend/worker/worker_test.go @@ -466,7 +466,6 @@ func runShardTablets(t *testing.T, shardName string, tabletArr []*cluster.Vttabl if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { return err } - tablet.VttabletProcess.ServiceMap = "grpc-queryservice,grpc-tabletmanager,grpc-updatestream,grpc-throttler" err := tablet.VttabletProcess.Setup() require.Nil(t, err) } From b758f36beaf9ad6d03338369752a6f39c1ea71f7 Mon Sep 17 00:00:00 2001 From: saurabh Date: Thu, 20 Feb 2020 12:43:27 +0530 Subject: [PATCH 148/825] increase num of records inserted Signed-off-by: saurabh Signed-off-by: pradip parmar --- go/test/endtoend/worker/worker_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/go/test/endtoend/worker/worker_test.go b/go/test/endtoend/worker/worker_test.go index 5ab7762f017..04d8b70b4e8 100644 --- a/go/test/endtoend/worker/worker_test.go +++ b/go/test/endtoend/worker/worker_test.go @@ -57,7 +57,6 @@ var ( hostname = "localhost" keyspaceName = "test_keyspace" shardName = "0" - keyspaceIDType = "uint64" shardTablets []*cluster.Vttablet shard0Tablets []*cluster.Vttablet shard1Tablets []*cluster.Vttablet @@ -112,6 +111,7 @@ func TestReparentDuringWorkerCopyMysqlDown(t *testing.T) { func TestWebInterface(t *testing.T) { _, err := initializeCluster(t, true) + require.Nil(t, err) defer localCluster.Teardown() err = localCluster.StartVtworker(cell, "--use_v3_resharding_mode=true") assert.Nil(t, err) @@ -575,6 +575,7 @@ func initializeCluster(t *testing.T, onlyTopo bool) (int, error) { localCluster.VtTabletExtraArgs = append(localCluster.VtTabletExtraArgs, commonTabletArg...) err = localCluster.LaunchCluster(keyspace, []cluster.Shard{*shard, *shard0, *shard1}) + assert.Nil(t, err) // Start MySql var mysqlCtlProcessList []*exec.Cmd From 6fc957a895018adeee9360a6bd86e95fcd18c623 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Fri, 21 Feb 2020 13:34:26 +0530 Subject: [PATCH 149/825] removed cell var (unused var). Signed-off-by: pradip parmar --- go/test/endtoend/vtctldweb/vtctld_web_main_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go index 6a689bad556..755730087be 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -37,7 +37,6 @@ import ( var ( localCluster *vttest.LocalCluster - cell = "zone1" hostname = "localhost" wd selenium.WebDriver seleniumService *selenium.Service From 60f5becda7d3b6011a220a59767f103ac7c83c4f Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 21 Feb 2020 11:56:23 +0100 Subject: [PATCH 150/825] Refactor - unify planning of UPDATE and DELETE Signed-off-by: Andres Taylor --- go/vt/vtgate/engine/delete.go | 84 ++-------- go/vt/vtgate/engine/dml.go | 81 ++++++++++ go/vt/vtgate/engine/update.go | 117 ++------------ go/vt/vtgate/engine/update_test.go | 16 +- go/vt/vtgate/planbuilder/delete.go | 79 +--------- go/vt/vtgate/planbuilder/dml.go | 214 ++++++++++++++++++++++++++ go/vt/vtgate/planbuilder/plan_test.go | 20 +-- go/vt/vtgate/planbuilder/update.go | 181 +--------------------- 8 files changed, 349 insertions(+), 443 deletions(-) create mode 100644 go/vt/vtgate/engine/dml.go create mode 100644 go/vt/vtgate/planbuilder/dml.go diff --git a/go/vt/vtgate/engine/delete.go b/go/vt/vtgate/engine/delete.go index b0f06174c69..07702164989 100644 --- a/go/vt/vtgate/engine/delete.go +++ b/go/vt/vtgate/engine/delete.go @@ -17,7 +17,6 @@ limitations under the License. package engine import ( - "encoding/json" "fmt" "time" @@ -37,36 +36,7 @@ var _ Primitive = (*Delete)(nil) // Delete represents the instructions to perform a delete. type Delete struct { - // Opcode is the execution opcode. - Opcode DeleteOpcode - - // Keyspace specifies the keyspace to send the query to. - Keyspace *vindexes.Keyspace - - // TargetDestination specifies the destination to send the query to. - TargetDestination key.Destination - - // Query specifies the query to be executed. - Query string - - // Vindex specifies the vindex to be used. - Vindex vindexes.SingleColumn - // Values specifies the vindex values to use for routing. - // For now, only one value is specified. - Values []sqltypes.PlanValue - - // Table specifies the table for the delete. - Table *vindexes.Table - - // OwnedVindexQuery is used for deleting lookup vindex entries. - OwnedVindexQuery string - - // Option to override the standard behavior and allow a multi-shard delete - // to use single round trip autocommit. - MultiShardAutocommit bool - - // QueryTimeout contains the optional timeout (in milliseconds) to apply to this query - QueryTimeout int + DML // Delete does not take inputs noInputs @@ -83,7 +53,7 @@ func (del *Delete) MarshalJSON() ([]byte, error) { vindexName = del.Vindex.String() } marshalDelete := struct { - Opcode DeleteOpcode + Opcode string Keyspace *vindexes.Keyspace `json:",omitempty"` Query string `json:",omitempty"` Vindex string `json:",omitempty"` @@ -93,7 +63,7 @@ func (del *Delete) MarshalJSON() ([]byte, error) { MultiShardAutocommit bool `json:",omitempty"` QueryTimeout int `json:",omitempty"` }{ - Opcode: del.Opcode, + Opcode: del.RouteType(), Keyspace: del.Keyspace, Query: del.Query, Vindex: vindexName, @@ -106,41 +76,11 @@ func (del *Delete) MarshalJSON() ([]byte, error) { return jsonutil.MarshalNoEscape(marshalDelete) } -// DeleteOpcode is a number representing the opcode -// for the Delete primitve. -type DeleteOpcode int - -// This is the list of DeleteOpcode values. -const ( - // DeleteUnsharded is for routing a delete statement - // to an unsharded keyspace. - DeleteUnsharded = DeleteOpcode(iota) - // DeleteEqual is for routing a delete statement - // to a single shard. Requires: A Vindex, a single - // Value, and an OwnedVindexQuery, which will be used to - // determine if lookup rows need to be deleted. - DeleteEqual - // DeleteScatter is for routing a scattered - // delete statement. - DeleteScatter - // DeleteByDestination is to route explicitly to a given - // target destination. Is used when the query explicitly sets a target destination: - // in the from clause: - // e.g: DELETE FROM `keyspace[-]`.x1 LIMIT 100 - DeleteByDestination -) - -var delName = map[DeleteOpcode]string{ - DeleteUnsharded: "DeleteUnsharded", - DeleteEqual: "DeleteEqual", - DeleteScatter: "DeleteScatter", - DeleteByDestination: "DeleteByDestination", -} - -// MarshalJSON serializes the DeleteOpcode as a JSON string. -// It's used for testing and diagnostics. -func (code DeleteOpcode) MarshalJSON() ([]byte, error) { - return json.Marshal(delName[code]) +var delName = map[DMLOpcode]string{ + Unsharded: "DeleteUnsharded", + Equal: "DeleteEqual", + Scatter: "DeleteScatter", + ByDestination: "DeleteByDestination", } // RouteType returns a description of the query routing type used by the primitive @@ -169,13 +109,13 @@ func (del *Delete) Execute(vcursor VCursor, bindVars map[string]*querypb.BindVar } switch del.Opcode { - case DeleteUnsharded: + case Unsharded: return del.execDeleteUnsharded(vcursor, bindVars) - case DeleteEqual: + case Equal: return del.execDeleteEqual(vcursor, bindVars) - case DeleteScatter: + case Scatter: return del.execDeleteByDestination(vcursor, bindVars, key.DestinationAllShards{}) - case DeleteByDestination: + case ByDestination: return del.execDeleteByDestination(vcursor, bindVars, del.TargetDestination) default: // Unreachable. diff --git a/go/vt/vtgate/engine/dml.go b/go/vt/vtgate/engine/dml.go new file mode 100644 index 00000000000..bc30dc8eb3b --- /dev/null +++ b/go/vt/vtgate/engine/dml.go @@ -0,0 +1,81 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/key" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +// DML contains the common elements between Update and Delete plans +type DML struct { + // Opcode is the execution opcode. + Opcode DMLOpcode + + // Keyspace specifies the keyspace to send the query to. + Keyspace *vindexes.Keyspace + + // TargetDestination specifies the destination to send the query to. + TargetDestination key.Destination + + // Query specifies the query to be executed. + Query string + + // Vindex specifies the vindex to be used. + Vindex vindexes.SingleColumn + + // Values specifies the vindex values to use for routing. + // For now, only one value is specified. + Values []sqltypes.PlanValue + + // Table specifies the table for the update. + Table *vindexes.Table + + // OwnedVindexQuery is used for updating changes in lookup vindexes. + OwnedVindexQuery string + + // Option to override the standard behavior and allow a multi-shard update + // to use single round trip autocommit. + MultiShardAutocommit bool + + // QueryTimeout contains the optional timeout (in milliseconds) to apply to this query + QueryTimeout int +} + +// DMLOpcode is a number representing the opcode +// for the Update or Delete primitve. +type DMLOpcode int + +// This is the list of UpdateOpcode values. +const ( + // UpdateUnsharded is for routing an update statement + // to an unsharded keyspace. + Unsharded = DMLOpcode(iota) + // UpdateEqual is for routing an update statement + // to a single shard: Requires: A Vindex, and + // a single Value. + Equal + // UpdateScatter is for routing a scattered + // update statement. + Scatter + // UpdateByDestination is to route explicitly to a given + // target destination. Is used when the query explicitly sets a target destination: + // in the clause: + // e.g: UPDATE `keyspace[-]`.x1 SET foo=1 + ByDestination +) diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index 69bd58cea49..d97a492bd65 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -17,7 +17,6 @@ limitations under the License. package engine import ( - "encoding/json" "fmt" "time" @@ -37,40 +36,11 @@ var _ Primitive = (*Update)(nil) // Update represents the instructions to perform an update. type Update struct { - // Opcode is the execution opcode. - Opcode UpdateOpcode - - // Keyspace specifies the keyspace to send the query to. - Keyspace *vindexes.Keyspace - - // TargetDestination specifies the destination to send the query to. - TargetDestination key.Destination - - // Query specifies the query to be executed. - Query string - - // Vindex specifies the vindex to be used. - Vindex vindexes.SingleColumn - // Values specifies the vindex values to use for routing. - // For now, only one value is specified. - Values []sqltypes.PlanValue + DML // ChangedVindexValues contains values for updated Vindexes during an update statement. ChangedVindexValues map[string][]sqltypes.PlanValue - // Table specifies the table for the update. - Table *vindexes.Table - - // OwnedVindexQuery is used for updating changes in lookup vindexes. - OwnedVindexQuery string - - // Option to override the standard behavior and allow a multi-shard update - // to use single round trip autocommit. - MultiShardAutocommit bool - - // QueryTimeout contains the optional timeout (in milliseconds) to apply to this query - QueryTimeout int - // Update does not take inputs noInputs } @@ -86,7 +56,7 @@ func (upd *Update) MarshalJSON() ([]byte, error) { vindexName = upd.Vindex.String() } marshalUpdate := struct { - Opcode UpdateOpcode + Opcode string Keyspace *vindexes.Keyspace `json:",omitempty"` Query string `json:",omitempty"` Vindex string `json:",omitempty"` @@ -97,7 +67,7 @@ func (upd *Update) MarshalJSON() ([]byte, error) { MultiShardAutocommit bool `json:",omitempty"` QueryTimeout int `json:",omitempty"` }{ - Opcode: upd.Opcode, + Opcode: upd.RouteType(), Keyspace: upd.Keyspace, Query: upd.Query, Vindex: vindexName, @@ -111,44 +81,11 @@ func (upd *Update) MarshalJSON() ([]byte, error) { return jsonutil.MarshalNoEscape(marshalUpdate) } -// UpdateOpcode is a number representing the opcode -// for the Update primitve. -type UpdateOpcode int - -// This is the list of UpdateOpcode values. -const ( - // UpdateUnsharded is for routing an update statement - // to an unsharded keyspace. - UpdateUnsharded = UpdateOpcode(iota) - // UpdateEqual is for routing an update statement - // to a single shard: Requires: A Vindex, and - // a single Value. - UpdateEqual - // UpdateScatter is for routing a scattered - // update statement. - UpdateScatter - // UpdateByDestination is to route explicitly to a given - // target destination. Is used when the query explicitly sets a target destination: - // in the clause: - // e.g: UPDATE `keyspace[-]`.x1 SET foo=1 - UpdateByDestination - // UpdateIn is for routing an update statement with in where clause - // Requires: A Vindex and multiple values. - UpdateIn -) - -var updName = map[UpdateOpcode]string{ - UpdateUnsharded: "UpdateUnsharded", - UpdateEqual: "UpdateEqual", - UpdateScatter: "UpdateScatter", - UpdateByDestination: "UpdateByDestination", - UpdateIn: "UpdateIn", -} - -// MarshalJSON serializes the UpdateOpcode as a JSON string. -// It's used for testing and diagnostics. -func (code UpdateOpcode) MarshalJSON() ([]byte, error) { - return json.Marshal(updName[code]) +var updName = map[DMLOpcode]string{ + Unsharded: "UpdateUnsharded", + Equal: "UpdateEqual", + Scatter: "UpdateScatter", + ByDestination: "UpdateByDestination", } // RouteType returns a description of the query routing type used by the primitive @@ -177,16 +114,14 @@ func (upd *Update) Execute(vcursor VCursor, bindVars map[string]*querypb.BindVar } switch upd.Opcode { - case UpdateUnsharded: + case Unsharded: return upd.execUpdateUnsharded(vcursor, bindVars) - case UpdateEqual: + case Equal: return upd.execUpdateEqual(vcursor, bindVars) - case UpdateScatter: + case Scatter: return upd.execUpdateByDestination(vcursor, bindVars, key.DestinationAllShards{}) - case UpdateByDestination: + case ByDestination: return upd.execUpdateByDestination(vcursor, bindVars, upd.TargetDestination) - case UpdateIn: - return upd.execUpdateIn(vcursor, bindVars) default: // Unreachable. return nil, fmt.Errorf("unsupported opcode: %v", upd) @@ -359,31 +294,3 @@ func (upd *Update) execUpdateByDestination(vcursor VCursor, bindVars map[string] result, errs := vcursor.ExecuteMultiShard(rss, queries, true /* isDML */, autocommit) return result, vterrors.Aggregate(errs) } - -func (upd *Update) execUpdateIn(vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - panic("implement me!") - //keys, err := upd.Values[0].ResolveList(bindVars) - //if err != nil { - // return nil, vterrors.Wrap(err, "execUpdateIn") - //} - //_, _, err = resolveShards(vcursor, upd.Vindex, upd.Keyspace, keys) - //if err != nil { - // return nil, vterrors.Wrap(err, "execUpdateIn") - //} - //just to have code compile returning blank. - //return &sqltypes.Result{}, nil - /* - TODO: Think of, How to go about updating lookup vindex. Will it be a loop or should resolve shards be performed on it. - - if len(ksids) == 0 { - return &sqltypes.Result{}, nil - } - - if len(upd.ChangedVindexValues) != 0 { - if err := upd.updateVindexEntries(vcursor, upd.OwnedVindexQuery, bindVars, rs, ksid); err != nil { - return nil, vterrors.Wrap(err, "execUpdateEqual") - } - } - rewritten := sqlannotation.AddKeyspaceIDs(upd.Query, [][]byte{ksid}, "")*/ - //return execShard(vcursor, rewritten, bindVars, rs, true /* isDML */, true /* canAutocommit */) -} diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index af0a2159599..a439d95fd7e 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -31,7 +31,7 @@ import ( func TestUpdateUnsharded(t *testing.T) { upd := &Update{ - Opcode: UpdateUnsharded, + Opcode: Unsharded, Keyspace: &vindexes.Keyspace{ Name: "ks", Sharded: false, @@ -60,7 +60,7 @@ func TestUpdateUnsharded(t *testing.T) { func TestUpdateEqual(t *testing.T) { vindex, _ := vindexes.NewHash("", nil) upd := &Update{ - Opcode: UpdateEqual, + Opcode: Equal, Keyspace: &vindexes.Keyspace{ Name: "ks", Sharded: true, @@ -87,7 +87,7 @@ func TestUpdateEqual(t *testing.T) { func TestUpdateScatter(t *testing.T) { vindex, _ := vindexes.NewHash("", nil) upd := &Update{ - Opcode: UpdateScatter, + Opcode: Scatter, Keyspace: &vindexes.Keyspace{ Name: "ks", Sharded: true, @@ -108,7 +108,7 @@ func TestUpdateScatter(t *testing.T) { // works with multishard autocommit upd = &Update{ - Opcode: UpdateScatter, + Opcode: Scatter, Keyspace: &vindexes.Keyspace{ Name: "ks", Sharded: true, @@ -136,7 +136,7 @@ func TestUpdateEqualNoRoute(t *testing.T) { "to": "toc", }) upd := &Update{ - Opcode: UpdateEqual, + Opcode: Equal, Keyspace: &vindexes.Keyspace{ Name: "ks", Sharded: true, @@ -163,7 +163,7 @@ func TestUpdateEqualNoScatter(t *testing.T) { "write_only": "true", }) upd := &Update{ - Opcode: UpdateEqual, + Opcode: Equal, Keyspace: &vindexes.Keyspace{ Name: "ks", Sharded: true, @@ -181,7 +181,7 @@ func TestUpdateEqualNoScatter(t *testing.T) { func TestUpdateEqualChangedVindex(t *testing.T) { ks := buildTestVSchema().Keyspaces["sharded"] upd := &Update{ - Opcode: UpdateEqual, + Opcode: Equal, Keyspace: ks.Keyspace, Query: "dummy_update", Vindex: ks.Vindexes["hash"].(vindexes.SingleColumn), @@ -266,7 +266,7 @@ func TestUpdateScatterChangedVindex(t *testing.T) { // update t1 set c1 = 1, c2 = 2, c3 = 3 ks := buildTestVSchema().Keyspaces["sharded"] upd := &Update{ - Opcode: UpdateScatter, + Opcode: Scatter, Keyspace: ks.Keyspace, Query: "dummy_update", ChangedVindexValues: map[string][]sqltypes.PlanValue{ diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index ec177db2b47..f9e54289287 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -17,94 +17,27 @@ limitations under the License. package planbuilder import ( - "errors" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" - "vitess.io/vitess/go/vt/vtgate/vindexes" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // buildDeletePlan builds the instructions for a DELETE statement. func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Delete, error) { - edel := &engine.Delete{} - pb := newPrimitiveBuilder(vschema, newJointab(sqlparser.GetBindvars(del))) - ro, err := pb.processDMLTable(del.TableExprs) + dml, vindexCol, err := buildDMLPlan(vschema, "delete", del, del.TableExprs, del.Where, del.OrderBy, del.Limit, del.Comments, del.Targets) if err != nil { return nil, err } - edel.Query = generateQuery(del) - edel.Keyspace = ro.eroute.Keyspace - if !edel.Keyspace.Sharded { - // We only validate non-table subexpressions because the previous analysis has already validated them. - if !pb.finalizeUnshardedDMLSubqueries(del.Targets, del.Where, del.OrderBy, del.Limit) { - return nil, errors.New("unsupported: sharded subqueries in DML") - } - edel.Opcode = engine.DeleteUnsharded - // Generate query after all the analysis. Otherwise table name substitutions for - // routed tables won't happen. - edel.Query = generateQuery(del) - return edel, nil - } - if del.Targets != nil || ro.vschemaTable == nil { - return nil, errors.New("unsupported: multi-table delete statement in sharded keyspace") - } - if hasSubquery(del) { - return nil, errors.New("unsupported: subqueries in sharded DML") + edel := &engine.Delete{ + DML: *dml, } - edel.Table = ro.vschemaTable - // Generate query after all the analysis. Otherwise table name substitutions for - // routed tables won't happen. - edel.Query = generateQuery(del) - directives := sqlparser.ExtractCommentDirectives(del.Comments) - if directives.IsSet(sqlparser.DirectiveMultiShardAutocommit) { - edel.MultiShardAutocommit = true - } - - edel.QueryTimeout = queryTimeout(directives) - if ro.eroute.TargetDestination != nil { - if ro.eroute.TargetTabletType != topodatapb.TabletType_MASTER { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported: DELETE statement with a replica target") - } - edel.Opcode = engine.DeleteByDestination - edel.TargetDestination = ro.eroute.TargetDestination + if dml.Opcode == engine.Unsharded { return edel, nil } - routingType, vindex, vindexCol, values := getDMLRouting(del.Where, edel.Table) - if routingType == scatter { - edel.Opcode = engine.DeleteScatter - if del.Limit != nil { - return nil, vterrors.New(vtrpcpb.Code_UNAVAILABLE, "unsupported: multi shard delete with limit") - } - } else { - edel.Values = values - edel.Vindex = vindex - edel.Opcode = engine.DeleteEqual + if len(edel.Table.Owned) > 0 { + edel.OwnedVindexQuery = generateDMLSubquery(del.Where, del.OrderBy, del.Limit, edel.Table, vindexCol) } - edel.OwnedVindexQuery = generateDeleteSubquery(del, edel.Table, vindexCol) return edel, nil } - -// generateDeleteSubquery generates the query to fetch the rows -// that will be deleted. This allows VTGate to clean up any -// owned vindexes as needed. -func generateDeleteSubquery(del *sqlparser.Delete, table *vindexes.Table, vindexCol string) string { - if len(table.Owned) == 0 { - return "" - } - buf := sqlparser.NewTrackedBuffer(nil) - buf.Myprintf("select %s", vindexCol) - for _, cv := range table.Owned { - for _, column := range cv.Columns { - buf.Myprintf(", %v", column) - } - } - buf.Myprintf(" from %v%v for update", table.Name, del.Where) - return buf.String() -} diff --git a/go/vt/vtgate/planbuilder/dml.go b/go/vt/vtgate/planbuilder/dml.go new file mode 100644 index 00000000000..4ad7e7f0c88 --- /dev/null +++ b/go/vt/vtgate/planbuilder/dml.go @@ -0,0 +1,214 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package planbuilder + +import ( + "vitess.io/vitess/go/sqltypes" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +// getDMLRouting returns the vindex and values for the DML, +// If it cannot find a unique vindex match, it returns an error. +func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (dmlRoutingType, vindexes.SingleColumn, string, []sqltypes.PlanValue) { + var keyColumn string + for _, index := range table.Ordered { + if !index.Vindex.IsUnique() { + continue + } + single, ok := index.Vindex.(vindexes.SingleColumn) + if !ok { + continue + } + + if keyColumn == "" { //TODO - check with sougou on how to deterministically get the primary vindex column + keyColumn = sqlparser.String(index.Columns[0]) + } + + if where == nil { + return scatter, nil, keyColumn, nil + } + + if pv, ok := getMatch(where.Expr, index.Columns[0]); ok { + if pv.IsList() { + return scatter, nil, keyColumn, nil + } + + return equal, single, keyColumn, []sqltypes.PlanValue{pv} + } + } + return scatter, nil, keyColumn, nil +} + +// getMatch returns the matched value if there is an equality +// constraint on the specified column that can be used to +// decide on a route. +func getMatch(node sqlparser.Expr, col sqlparser.ColIdent) (pv sqltypes.PlanValue, ok bool) { + filters := splitAndExpression(nil, node) + for _, filter := range filters { + filter = skipParenthesis(filter) + if parenthesis, ok := node.(*sqlparser.ParenExpr); ok { + if pv, ok := getMatch(parenthesis.Expr, col); ok { + return pv, ok + } + continue + } + comparison, ok := filter.(*sqlparser.ComparisonExpr) + if !ok { + continue + } + if !nameMatch(comparison.Left, col) { + continue + } + switch comparison.Operator { + case sqlparser.EqualStr: + if !sqlparser.IsValue(comparison.Right) { + continue + } + case sqlparser.InStr: + if !sqlparser.IsSimpleTuple(comparison.Right) { + continue + } + default: + continue + } + pv, err := sqlparser.NewPlanValue(comparison.Right) + if err != nil { + continue + } + return pv, true + } + return sqltypes.PlanValue{}, false +} + +func nameMatch(node sqlparser.Expr, col sqlparser.ColIdent) bool { + colname, ok := node.(*sqlparser.ColName) + return ok && colname.Name.Equal(col) +} + +func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Statement, tableExprs sqlparser.TableExprs, where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, comments sqlparser.Comments, nodes ...sqlparser.SQLNode) (*engine.DML, string, error) { + eupd := &engine.DML{} + pb := newPrimitiveBuilder(vschema, newJointab(sqlparser.GetBindvars(stmt))) + ro, err := pb.processDMLTable(tableExprs) + if err != nil { + return nil, "", err + } + eupd.Keyspace = ro.eroute.Keyspace + if !eupd.Keyspace.Sharded { + // We only validate non-table subexpressions because the previous analysis has already validated them. + subqueryArgs := []sqlparser.SQLNode{} + for _, n := range nodes { + subqueryArgs = append(subqueryArgs, n) + } + subqueryArgs = append(subqueryArgs, where, orderBy, limit) + if !pb.finalizeUnshardedDMLSubqueries(subqueryArgs...) { + return nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: sharded subqueries in DML") + } + eupd.Opcode = engine.Unsharded + // Generate query after all the analysis. Otherwise table name substitutions for + // routed tables won't happen. + eupd.Query = generateQuery(stmt) + return eupd, "", nil + } + + if hasSubquery(stmt) { + return nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: subqueries in sharded DML") + } + + if len(pb.st.tables) != 1 { + return nil, "", vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi-table %s statement in sharded keyspace", dmlType) + } + + // Generate query after all the analysis. Otherwise table name substitutions for + // routed tables won't happen. + eupd.Query = generateQuery(stmt) + + directives := sqlparser.ExtractCommentDirectives(comments) + if directives.IsSet(sqlparser.DirectiveMultiShardAutocommit) { + eupd.MultiShardAutocommit = true + } + + eupd.QueryTimeout = queryTimeout(directives) + eupd.Table = ro.vschemaTable + if eupd.Table == nil { + return nil, "", vterrors.New(vtrpcpb.Code_INTERNAL, "internal error: table.vindexTable is mysteriously nil") + } + + if ro.eroute.TargetDestination != nil { + if ro.eroute.TargetTabletType != topodatapb.TabletType_MASTER { + return nil, "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported: %s statement with a replica target", dmlType) + } + eupd.Opcode = engine.ByDestination + eupd.TargetDestination = ro.eroute.TargetDestination + return eupd, "", nil + } + + routingType, vindex, vindexCol, values := getDMLRouting(where, eupd.Table) + + if routingType == scatter { + if limit != nil { + return nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi shard update with limit") + } + eupd.Opcode = engine.Scatter + } else { + eupd.Opcode = engine.Equal + eupd.Values = values + eupd.Vindex = vindex + } + + return eupd, vindexCol, nil +} + +func generateDMLSubquery(where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, table *vindexes.Table, vindex string) string { + buf := sqlparser.NewTrackedBuffer(nil) + buf.WriteString("select ") + buf.Myprintf("%s", vindex) + for _, cv := range table.Owned { + for _, column := range cv.Columns { + buf.Myprintf(", %v", column) + } + } + buf.Myprintf(" from %v%v%v%v for update", table.Name, where, orderBy, limit) + return buf.String() +} + +func generateQuery(statement sqlparser.Statement) string { + buf := sqlparser.NewTrackedBuffer(dmlFormatter) + statement.Format(buf) + return buf.String() +} + +// dmlFormatter strips out keyspace name from dmls. +func dmlFormatter(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { + switch node := node.(type) { + case sqlparser.TableName: + node.Name.Format(buf) + return + } + node.Format(buf) +} + +type dmlRoutingType int + +const ( + scatter dmlRoutingType = iota + equal +) diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 41e115cbec0..885499d87a2 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -147,17 +147,17 @@ func TestPlan(t *testing.T) { // the column is named as Id. This is to make sure that // column names are case-preserved, but treated as // case-insensitive even if they come from the vschema. - //testFile(t, "aggr_cases.txt", vschema) + testFile(t, "aggr_cases.txt", vschema) testFile(t, "dml_cases.txt", vschema) - //testFile(t, "from_cases.txt", vschema) - //testFile(t, "filter_cases.txt", vschema) - //testFile(t, "postprocess_cases.txt", vschema) - //testFile(t, "select_cases.txt", vschema) - //testFile(t, "symtab_cases.txt", vschema) - //testFile(t, "unsupported_cases.txt", vschema) - //testFile(t, "vindex_func_cases.txt", vschema) - //testFile(t, "wireup_cases.txt", vschema) - //testFile(t, "memory_sort_cases.txt", vschema) + testFile(t, "from_cases.txt", vschema) + testFile(t, "filter_cases.txt", vschema) + testFile(t, "postprocess_cases.txt", vschema) + testFile(t, "select_cases.txt", vschema) + testFile(t, "symtab_cases.txt", vschema) + testFile(t, "unsupported_cases.txt", vschema) + testFile(t, "vindex_func_cases.txt", vschema) + testFile(t, "wireup_cases.txt", vschema) + testFile(t, "memory_sort_cases.txt", vschema) } func TestOne(t *testing.T) { diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index c0809346674..1bb3b9c9993 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -17,90 +17,35 @@ limitations under the License. package planbuilder import ( - "errors" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" "vitess.io/vitess/go/vt/vtgate/vindexes" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // buildUpdatePlan builds the instructions for an UPDATE statement. func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Update, error) { - eupd := &engine.Update{ - ChangedVindexValues: make(map[string][]sqltypes.PlanValue), - } - pb := newPrimitiveBuilder(vschema, newJointab(sqlparser.GetBindvars(upd))) - ro, err := pb.processDMLTable(upd.TableExprs) + dml, vindexCol, err := buildDMLPlan(vschema, "update", upd, upd.TableExprs, upd.Where, upd.OrderBy, upd.Limit, upd.Comments, upd.Exprs) if err != nil { return nil, err } - eupd.Keyspace = ro.eroute.Keyspace - if !eupd.Keyspace.Sharded { - // We only validate non-table subexpressions because the previous analysis has already validated them. - if !pb.finalizeUnshardedDMLSubqueries(upd.Exprs, upd.Where, upd.OrderBy, upd.Limit) { - return nil, errors.New("unsupported: sharded subqueries in DML") - } - eupd.Opcode = engine.UpdateUnsharded - // Generate query after all the analysis. Otherwise table name substitutions for - // routed tables won't happen. - eupd.Query = generateQuery(upd) - return eupd, nil - } - - if hasSubquery(upd) { - return nil, errors.New("unsupported: subqueries in sharded DML") - } - if len(pb.st.tables) != 1 { - return nil, errors.New("unsupported: multi-table update statement in sharded keyspace") - } - - // Generate query after all the analysis. Otherwise table name substitutions for - // routed tables won't happen. - eupd.Query = generateQuery(upd) - - directives := sqlparser.ExtractCommentDirectives(upd.Comments) - if directives.IsSet(sqlparser.DirectiveMultiShardAutocommit) { - eupd.MultiShardAutocommit = true - } - - eupd.QueryTimeout = queryTimeout(directives) - eupd.Table = ro.vschemaTable - if eupd.Table == nil { - return nil, errors.New("internal error: table.vindexTable is mysteriously nil") + eupd := &engine.Update{ + DML: *dml, + ChangedVindexValues: make(map[string][]sqltypes.PlanValue), } - if ro.eroute.TargetDestination != nil { - if ro.eroute.TargetTabletType != topodatapb.TabletType_MASTER { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported: UPDATE statement with a replica target") - } - eupd.Opcode = engine.UpdateByDestination - eupd.TargetDestination = ro.eroute.TargetDestination + if dml.Opcode == engine.Unsharded { return eupd, nil } - routingType, vindex, vindexCol, values := getDMLRouting(upd.Where, eupd.Table) - - if routingType == scatter { - if upd.Limit != nil { - return nil, errors.New("unsupported: multi shard update with limit") - } - eupd.Opcode = engine.UpdateScatter - } else { - eupd.Opcode = engine.UpdateEqual - eupd.Values = values - eupd.Vindex = vindex - } - if eupd.ChangedVindexValues, err = buildChangedVindexesValues(upd, eupd.Table.ColumnVindexes); err != nil { return nil, err } if len(eupd.ChangedVindexValues) != 0 { - eupd.OwnedVindexQuery = generateUpdateSubquery(upd, eupd.Table, vindexCol) + eupd.OwnedVindexQuery = generateDMLSubquery(upd.Where, upd.OrderBy, upd.Limit, eupd.Table, vindexCol) } return eupd, nil } @@ -156,19 +101,6 @@ func buildChangedVindexesValues(update *sqlparser.Update, colVindexes []*vindexe return changedVindexes, nil } -func generateUpdateSubquery(upd *sqlparser.Update, table *vindexes.Table, vindex string) string { - buf := sqlparser.NewTrackedBuffer(nil) - buf.WriteString("select ") - buf.Myprintf("%s", vindex) - for _, cv := range table.Owned { - for _, column := range cv.Columns { - buf.Myprintf(", %v", column) - } - } - buf.Myprintf(" from %v%v%v%v for update", table.Name, upd.Where, upd.OrderBy, upd.Limit) - return buf.String() -} - // extractValueFromUpdate given an UpdateExpr attempts to extracts the Value // it's holding. At the moment it only supports: StrVal, HexVal, IntVal, ValArg. // If a complex expression is provided (e.g set name = name + 1), the update will be rejected. @@ -179,104 +111,3 @@ func extractValueFromUpdate(upd *sqlparser.UpdateExpr) (pv sqltypes.PlanValue, e } return sqlparser.NewPlanValue(upd.Expr) } - -// dmlFormatter strips out keyspace name from dmls. -func dmlFormatter(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { - switch node := node.(type) { - case sqlparser.TableName: - node.Name.Format(buf) - return - } - node.Format(buf) -} - -func generateQuery(statement sqlparser.Statement) string { - buf := sqlparser.NewTrackedBuffer(dmlFormatter) - statement.Format(buf) - return buf.String() -} - -type dmlRoutingType int - -const ( - scatter dmlRoutingType = iota - equal -) - -// getDMLRouting returns the vindex and values for the DML, -// If it cannot find a unique vindex match, it returns an error. -func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (dmlRoutingType, vindexes.SingleColumn, string, []sqltypes.PlanValue) { - var keyColumn string - for _, index := range table.Ordered { - if !index.Vindex.IsUnique() { - continue - } - single, ok := index.Vindex.(vindexes.SingleColumn) - if !ok { - continue - } - - if keyColumn == "" { //TODO - check with sougou on how to deterministically get the primary vindex column - keyColumn = sqlparser.String(index.Columns[0]) - } - - if where == nil { - return scatter, nil, keyColumn, nil - } - - if pv, ok := getMatch(where.Expr, index.Columns[0]); ok { - if pv.IsList() { - return scatter, nil, keyColumn, nil - } - - return equal, single, keyColumn, []sqltypes.PlanValue{pv} - } - } - return scatter, nil, keyColumn, nil -} - -// getMatch returns the matched value if there is an equality -// constraint on the specified column that can be used to -// decide on a route. -func getMatch(node sqlparser.Expr, col sqlparser.ColIdent) (pv sqltypes.PlanValue, ok bool) { - filters := splitAndExpression(nil, node) - for _, filter := range filters { - filter = skipParenthesis(filter) - if parenthesis, ok := node.(*sqlparser.ParenExpr); ok { - if pv, ok := getMatch(parenthesis.Expr, col); ok { - return pv, ok - } - continue - } - comparison, ok := filter.(*sqlparser.ComparisonExpr) - if !ok { - continue - } - if !nameMatch(comparison.Left, col) { - continue - } - switch comparison.Operator { - case sqlparser.EqualStr: - if !sqlparser.IsValue(comparison.Right) { - continue - } - case sqlparser.InStr: - if !sqlparser.IsSimpleTuple(comparison.Right) { - continue - } - default: - continue - } - pv, err := sqlparser.NewPlanValue(comparison.Right) - if err != nil { - continue - } - return pv, true - } - return sqltypes.PlanValue{}, false -} - -func nameMatch(node sqlparser.Expr, col sqlparser.ColIdent) bool { - colname, ok := node.(*sqlparser.ColName) - return ok && colname.Name.Equal(col) -} From d862ca3d4448367e32990b47a2940a144bda7e09 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Fri, 21 Feb 2020 19:33:42 +0530 Subject: [PATCH 151/825] added and fixed tests Signed-off-by: Harshit Gangal --- go/vt/vtgate/engine/delete_test.go | 88 +++++++------ go/vt/vtgate/engine/update_test.go | 122 ++++++++++-------- go/vt/vtgate/planbuilder/delete.go | 10 ++ go/vt/vtgate/planbuilder/dml.go | 2 +- .../vtgate/planbuilder/testdata/dml_cases.txt | 51 ++++++++ .../testdata/unsupported_cases.txt | 28 ++-- 6 files changed, 193 insertions(+), 108 deletions(-) diff --git a/go/vt/vtgate/engine/delete_test.go b/go/vt/vtgate/engine/delete_test.go index d2b7c9173af..86dd8e37905 100644 --- a/go/vt/vtgate/engine/delete_test.go +++ b/go/vt/vtgate/engine/delete_test.go @@ -28,12 +28,14 @@ import ( func TestDeleteUnsharded(t *testing.T) { del := &Delete{ - Opcode: DeleteUnsharded, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: false, + DML: DML{ + Opcode: Unsharded, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: false, + }, + Query: "dummy_delete", }, - Query: "dummy_delete", } vc := &loggingVCursor{shards: []string{"0"}} @@ -59,14 +61,16 @@ func TestDeleteUnsharded(t *testing.T) { func TestDeleteEqual(t *testing.T) { vindex, _ := vindexes.NewHash("", nil) del := &Delete{ - Opcode: DeleteEqual, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Equal, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_delete", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, }, - Query: "dummy_delete", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, } vc := &loggingVCursor{shards: []string{"-20", "20-"}} @@ -92,14 +96,16 @@ func TestDeleteEqualNoRoute(t *testing.T) { "to": "toc", }) del := &Delete{ - Opcode: DeleteEqual, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Equal, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_delete", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, }, - Query: "dummy_delete", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, } vc := &loggingVCursor{shards: []string{"0"}} @@ -121,14 +127,16 @@ func TestDeleteEqualNoScatter(t *testing.T) { "write_only": "true", }) del := &Delete{ - Opcode: DeleteEqual, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Equal, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_delete", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, }, - Query: "dummy_delete", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, } vc := &loggingVCursor{shards: []string{"0"}} @@ -139,13 +147,15 @@ func TestDeleteEqualNoScatter(t *testing.T) { func TestDeleteOwnedVindex(t *testing.T) { ks := buildTestVSchema().Keyspaces["sharded"] del := &Delete{ - Opcode: DeleteEqual, - Keyspace: ks.Keyspace, - Query: "dummy_delete", - Vindex: ks.Vindexes["hash"].(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, - Table: ks.Tables["t1"], - OwnedVindexQuery: "dummy_subquery", + DML: DML{ + Opcode: Equal, + Keyspace: ks.Keyspace, + Query: "dummy_delete", + Vindex: ks.Vindexes["hash"].(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, + Table: ks.Tables["t1"], + OwnedVindexQuery: "dummy_subquery", + }, } results := []*sqltypes.Result{sqltypes.MakeTestResult( @@ -228,12 +238,14 @@ func TestDeleteOwnedVindex(t *testing.T) { func TestDeleteSharded(t *testing.T) { del := &Delete{ - Opcode: DeleteScatter, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Scatter, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_delete", }, - Query: "dummy_delete", } vc := &loggingVCursor{shards: []string{"-20", "20-"}} diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index a439d95fd7e..9f665c93d4f 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -31,12 +31,14 @@ import ( func TestUpdateUnsharded(t *testing.T) { upd := &Update{ - Opcode: Unsharded, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: false, + DML: DML{ + Opcode: Unsharded, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: false, + }, + Query: "dummy_update", }, - Query: "dummy_update", } vc := &loggingVCursor{shards: []string{"0"}} @@ -60,14 +62,16 @@ func TestUpdateUnsharded(t *testing.T) { func TestUpdateEqual(t *testing.T) { vindex, _ := vindexes.NewHash("", nil) upd := &Update{ - Opcode: Equal, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Equal, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_update", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, }, - Query: "dummy_update", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, } vc := &loggingVCursor{shards: []string{"-20", "20-"}} @@ -87,14 +91,16 @@ func TestUpdateEqual(t *testing.T) { func TestUpdateScatter(t *testing.T) { vindex, _ := vindexes.NewHash("", nil) upd := &Update{ - Opcode: Scatter, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Scatter, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_update", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, }, - Query: "dummy_update", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, } vc := &loggingVCursor{shards: []string{"-20", "20-"}} @@ -108,15 +114,17 @@ func TestUpdateScatter(t *testing.T) { // works with multishard autocommit upd = &Update{ - Opcode: Scatter, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Scatter, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_update", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, + MultiShardAutocommit: true, }, - Query: "dummy_update", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, - MultiShardAutocommit: true, } vc = &loggingVCursor{shards: []string{"-20", "20-"}} @@ -136,14 +144,16 @@ func TestUpdateEqualNoRoute(t *testing.T) { "to": "toc", }) upd := &Update{ - Opcode: Equal, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Equal, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_update", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, }, - Query: "dummy_update", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, } vc := &loggingVCursor{shards: []string{"0"}} @@ -163,14 +173,16 @@ func TestUpdateEqualNoScatter(t *testing.T) { "write_only": "true", }) upd := &Update{ - Opcode: Equal, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, + DML: DML{ + Opcode: Equal, + Keyspace: &vindexes.Keyspace{ + Name: "ks", + Sharded: true, + }, + Query: "dummy_update", + Vindex: vindex.(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, }, - Query: "dummy_update", - Vindex: vindex.(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, } vc := &loggingVCursor{shards: []string{"0"}} @@ -181,11 +193,15 @@ func TestUpdateEqualNoScatter(t *testing.T) { func TestUpdateEqualChangedVindex(t *testing.T) { ks := buildTestVSchema().Keyspaces["sharded"] upd := &Update{ - Opcode: Equal, - Keyspace: ks.Keyspace, - Query: "dummy_update", - Vindex: ks.Vindexes["hash"].(vindexes.SingleColumn), - Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, + DML: DML{ + Opcode: Equal, + Keyspace: ks.Keyspace, + Query: "dummy_update", + Vindex: ks.Vindexes["hash"].(vindexes.SingleColumn), + Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, + Table: ks.Tables["t1"], + OwnedVindexQuery: "dummy_subquery", + }, ChangedVindexValues: map[string][]sqltypes.PlanValue{ "twocol": {{ Value: sqltypes.NewInt64(1), @@ -196,8 +212,6 @@ func TestUpdateEqualChangedVindex(t *testing.T) { Value: sqltypes.NewInt64(3), }}, }, - Table: ks.Tables["t1"], - OwnedVindexQuery: "dummy_subquery", } results := []*sqltypes.Result{sqltypes.MakeTestResult( @@ -266,9 +280,13 @@ func TestUpdateScatterChangedVindex(t *testing.T) { // update t1 set c1 = 1, c2 = 2, c3 = 3 ks := buildTestVSchema().Keyspaces["sharded"] upd := &Update{ - Opcode: Scatter, - Keyspace: ks.Keyspace, - Query: "dummy_update", + DML: DML{ + Opcode: Scatter, + Keyspace: ks.Keyspace, + Query: "dummy_update", + Table: ks.Tables["t1"], + OwnedVindexQuery: "dummy_subquery", + }, ChangedVindexValues: map[string][]sqltypes.PlanValue{ "twocol": {{ Value: sqltypes.NewInt64(1), @@ -279,8 +297,6 @@ func TestUpdateScatterChangedVindex(t *testing.T) { Value: sqltypes.NewInt64(3), }}, }, - Table: ks.Tables["t1"], - OwnedVindexQuery: "dummy_subquery", } results := []*sqltypes.Result{sqltypes.MakeTestResult( diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index f9e54289287..664a8e1a5d5 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -17,7 +17,9 @@ limitations under the License. package planbuilder import ( + "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" ) @@ -35,6 +37,14 @@ func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Del return edel, nil } + if len(del.Targets) > 1 { + return nil, vterrors.New(vtrpc.Code_UNIMPLEMENTED, "unsupported: multi-table delete statement in sharded keyspace") + } + + if len(del.Targets) == 1 && del.Targets[0].Name != edel.Table.Name { + return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "Unknown table '%s' in MULTI DELETE", del.Targets[0].Name.String()) + } + if len(edel.Table.Owned) > 0 { edel.OwnedVindexQuery = generateDMLSubquery(del.Where, del.OrderBy, del.Limit, edel.Table, vindexCol) } diff --git a/go/vt/vtgate/planbuilder/dml.go b/go/vt/vtgate/planbuilder/dml.go index 4ad7e7f0c88..6cf10c9fd4a 100644 --- a/go/vt/vtgate/planbuilder/dml.go +++ b/go/vt/vtgate/planbuilder/dml.go @@ -165,7 +165,7 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme if routingType == scatter { if limit != nil { - return nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi shard update with limit") + return nil, "", vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi shard %s with limit", dmlType) } eupd.Opcode = engine.Scatter } else { diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt index c7137f3cef0..05654261041 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt @@ -1951,3 +1951,54 @@ "OwnedVindexQuery": "select Id, Name, Costly from user for update" } } + +# delete with single table targets +"delete music from music where id = 1" +{ + "Original": "delete music from music where id = 1", + "Instructions": { + "Opcode": "DeleteEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete music from music where id = 1", + "Vindex": "music_user_map", + "Values": [ + 1 + ], + "Table": "music", + "OwnedVindexQuery": "select user_id, id from music where id = 1 for update" + } +} + +# scatter update table with owned vindexes without changing lookup vindex +"update user set val = 1" +{ + "Original": "update user set val = 1", + "Instructions": { + "Opcode": "UpdateScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update user set val = 1", + "Table": "user" + } +} + +# scatter delete with owned lookup vindex +"delete from user" +{ + "Original": "delete from user", + "Instructions": { + "Opcode": "DeleteScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "delete from user", + "Table": "user", + "OwnedVindexQuery": "select Id, Name, Costly from user for update" + } +} diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt index f038cb9095a..0bf10af7c87 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt @@ -43,13 +43,13 @@ "insert into `user[-]`.user_metadata (a, b) values (1,2)" "unsupported: INSERT with a target destination" -# Unsupported DELETE statement with a replica target destination +# Unsupported delete statement with a replica target destination "DELETE FROM `user[-]@replica`.user_metadata limit 1" -"unsupported: DELETE statement with a replica target" +"unsupported: delete statement with a replica target" -# Unsupported UPDATE statement with a replica target destination +# Unsupported update statement with a replica target destination "update `user[-]@replica`.user_metadata set id=2" -"unsupported: UPDATE statement with a replica target" +"unsupported: update statement with a replica target" # scatter order by with * expression "select * from user order by id" @@ -235,22 +235,10 @@ "update user_extra set val = 1 where (name = 'foo' or id = 1) limit 1" "unsupported: multi shard update with limit" -# scatter update table with owned vindexes -"update user set val = 1" -"unsupported: multi shard update on a table with owned lookup vindexes" - -# delete with multi-table targets -"delete music from music where id = 1" -"unsupported: multi-table delete statement in sharded keyspace" - # multi delete multi table "delete user from user join user_extra on user.id = user_extra.id where user.name = 'foo'" "unsupported: multi-shard or vindex write statement" -# scatter delete with owned lookup vindex -"delete from user" -"unsupported: multi shard delete on a table with owned lookup vindexes" - # update changes primary vindex column "update user set id = 1 where id = 1" "unsupported: You can't update primary vindex columns. Invalid update on vindex: user_index" @@ -428,3 +416,11 @@ "select func(keyspace_id) from user_index where id = :id" "unsupported: expression on results of a vindex function" + +# delete with unknown reference +"delete music from user where id = 1" +"Unknown table 'music' in MULTI DELETE" + +# delete with multi-table targets +"delete music,user from music inner join user where music.id = user.id" +"unsupported: multi-shard or vindex write statement" From 6ffef463df15657cd93bacf99b4516d03e7e9f03 Mon Sep 17 00:00:00 2001 From: prince Date: Fri, 21 Feb 2020 21:02:48 +0530 Subject: [PATCH 152/825] migrating xb-recovery python testcase to go (#5799) * xb-recovery: xtra backup recovery for sharded and unshared cluster. Signed-off-by: pradip parmar * vtworker fix. Signed-off-by: pradip parmar * increase num of records inserted Signed-off-by: saurabh Co-authored-by: saurabh408 --- go/test/endtoend/recovery/recovery_util.go | 18 +- .../recovery/unshardedrecovery/main_test.go | 152 -------- .../recovery/unshardedrecovery/recovery.go | 345 ++++++++++++++++++ .../unshardedrecovery/recovery_test.go | 183 +--------- .../recovery/xtrabackup/recovery_test.go | 34 ++ test/config.json | 18 +- 6 files changed, 410 insertions(+), 340 deletions(-) delete mode 100644 go/test/endtoend/recovery/unshardedrecovery/main_test.go create mode 100644 go/test/endtoend/recovery/unshardedrecovery/recovery.go create mode 100644 go/test/endtoend/recovery/xtrabackup/recovery_test.go diff --git a/go/test/endtoend/recovery/recovery_util.go b/go/test/endtoend/recovery/recovery_util.go index 1c09c235f49..339072a98d0 100644 --- a/go/test/endtoend/recovery/recovery_util.go +++ b/go/test/endtoend/recovery/recovery_util.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Vitess Authors. +Copyright 2020 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,6 +27,19 @@ import ( "vitess.io/vitess/go/vt/vtgate/vtgateconn" ) +var ( + dbPassword = "VtDbaPass" + + // UseXb flag to use extra backup for recovery teseting. + UseXb = false + XbArgs = []string{ + "-backup_engine_implementation", "xtrabackup", + "-xtrabackup_stream_mode=xbstream", + "-xtrabackup_user=vt_dba", + "-xtrabackup_backup_flags", fmt.Sprintf("--password=%s", dbPassword), + } +) + func VerifyQueriesUsingVtgate(t *testing.T, session *vtgateconn.VTGateSession, query string, value string) { qr, err := session.Execute(context.Background(), query, nil) assert.Nil(t, err) @@ -43,6 +56,9 @@ func RestoreTablet(t *testing.T, localCluster *cluster.LocalProcessCluster, tabl assert.Nil(t, err) replicaTabletArgs := commonTabletArg + if UseXb { + replicaTabletArgs = append(replicaTabletArgs, XbArgs...) + } replicaTabletArgs = append(replicaTabletArgs, "-disable_active_reparents", "-enable_replication_reporter=false", "-init_tablet_type", "replica", diff --git a/go/test/endtoend/recovery/unshardedrecovery/main_test.go b/go/test/endtoend/recovery/unshardedrecovery/main_test.go deleted file mode 100644 index f92e51fa914..00000000000 --- a/go/test/endtoend/recovery/unshardedrecovery/main_test.go +++ /dev/null @@ -1,152 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package unshardedrecovery - -import ( - "flag" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path" - "testing" - - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" - "vitess.io/vitess/go/vt/log" -) - -var ( - master *cluster.Vttablet - replica1 *cluster.Vttablet - replica2 *cluster.Vttablet - replica3 *cluster.Vttablet - localCluster *cluster.LocalProcessCluster - newInitDBFile string - cell = cluster.DefaultCell - hostname = "localhost" - keyspaceName = "ks" - dbPassword = "VtDbaPass" - shardKsName = fmt.Sprintf("%s/%s", keyspaceName, shardName) - dbCredentialFile string - shardName = "0" - commonTabletArg = []string{ - "-vreplication_healthcheck_topology_refresh", "1s", - "-vreplication_healthcheck_retry_delay", "1s", - "-vreplication_retry_delay", "1s", - "-degraded_threshold", "5s", - "-lock_tables_timeout", "5s", - "-watch_replication_stream", - "-serving_state_grace_period", "1s"} -) - -func TestMain(m *testing.M) { - flag.Parse() - - exitCode, err := func() (int, error) { - localCluster = cluster.NewCluster(cell, hostname) - defer localCluster.Teardown() - - // Start topo server - err := localCluster.StartTopo() - if err != nil { - return 1, err - } - - // Start keyspace - keyspace := &cluster.Keyspace{ - Name: keyspaceName, - } - localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) - - dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) - initDb, _ := ioutil.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) - sql := string(initDb) - newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") - sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) - ioutil.WriteFile(newInitDBFile, []byte(sql), 0666) - - extraArgs := []string{"-db-credentials-file", dbCredentialFile} - commonTabletArg = append(commonTabletArg, "-db-credentials-file", dbCredentialFile) - - shard := cluster.Shard{ - Name: shardName, - } - - var mysqlProcs []*exec.Cmd - for i := 0; i < 4; i++ { - tabletType := "replica" - if i == 0 { - tabletType = "master" - } - tablet := localCluster.GetVttabletInstance(tabletType, 0, cell) - tablet.VttabletProcess = localCluster.GetVtprocessInstanceFromVttablet(tablet, shard.Name, keyspaceName) - tablet.VttabletProcess.DbPassword = dbPassword - tablet.VttabletProcess.ExtraArgs = commonTabletArg - tablet.VttabletProcess.SupportsBackup = true - tablet.VttabletProcess.EnableSemiSync = true - - tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) - tablet.MysqlctlProcess.InitDBFile = newInitDBFile - tablet.MysqlctlProcess.ExtraArgs = extraArgs - if proc, err := tablet.MysqlctlProcess.StartProcess(); err != nil { - return 1, err - } else { - mysqlProcs = append(mysqlProcs, proc) - } - shard.Vttablets = append(shard.Vttablets, tablet) - } - for _, proc := range mysqlProcs { - if err := proc.Wait(); err != nil { - return 1, err - } - } - master = shard.Vttablets[0] - replica1 = shard.Vttablets[1] - replica2 = shard.Vttablets[2] - replica3 = shard.Vttablets[3] - - if err := localCluster.VtctlclientProcess.InitTablet(master, cell, keyspaceName, hostname, shard.Name); err != nil { - return 1, err - } - if err := localCluster.VtctlclientProcess.InitTablet(replica1, cell, keyspaceName, hostname, shard.Name); err != nil { - return 1, err - } - - for _, tablet := range []cluster.Vttablet{*master, *replica1} { - if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { - return 1, err - } - if err := tablet.VttabletProcess.Setup(); err != nil { - return 1, err - } - } - - if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard.Name, cell, master.TabletUID); err != nil { - return 1, err - } - return m.Run(), nil - }() - - if err != nil { - log.Error(err.Error()) - os.Exit(1) - } else { - os.Exit(exitCode) - } - -} diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery.go b/go/test/endtoend/recovery/unshardedrecovery/recovery.go new file mode 100644 index 00000000000..e17dd76f82a --- /dev/null +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery.go @@ -0,0 +1,345 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package unshardedrecovery + +import ( + "context" + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "testing" + + "vitess.io/vitess/go/test/endtoend/recovery" + "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/vtgate/vtgateconn" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + master *cluster.Vttablet + replica1 *cluster.Vttablet + replica2 *cluster.Vttablet + replica3 *cluster.Vttablet + localCluster *cluster.LocalProcessCluster + newInitDBFile string + cell = cluster.DefaultCell + hostname = "localhost" + keyspaceName = "ks" + dbPassword = "VtDbaPass" + shardKsName = fmt.Sprintf("%s/%s", keyspaceName, shardName) + dbCredentialFile string + shardName = "0" + commonTabletArg = []string{ + "-vreplication_healthcheck_topology_refresh", "1s", + "-vreplication_healthcheck_retry_delay", "1s", + "-vreplication_retry_delay", "1s", + "-degraded_threshold", "5s", + "-lock_tables_timeout", "5s", + "-watch_replication_stream", + "-serving_state_grace_period", "1s"} + recoveryKS1 = "recovery_ks1" + recoveryKS2 = "recovery_ks2" + vtInsertTest = `create table vt_insert_test ( + id bigint auto_increment, + msg varchar(64), + primary key (id) + ) Engine=InnoDB` + vSchema = `{ + "tables": { + "vt_insert_test": {} + } +}` +) + +// TestMainImpl creates cluster for unsharded recovery testing. +func TestMainImpl(m *testing.M) { + flag.Parse() + + exitCode, err := func() (int, error) { + localCluster = cluster.NewCluster(cell, hostname) + defer localCluster.Teardown() + + // Start topo server + err := localCluster.StartTopo() + if err != nil { + return 1, err + } + + // Start keyspace + keyspace := &cluster.Keyspace{ + Name: keyspaceName, + } + localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) + + dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) + initDb, _ := ioutil.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) + sql := string(initDb) + newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql") + sql = sql + initialsharding.GetPasswordUpdateSQL(localCluster) + ioutil.WriteFile(newInitDBFile, []byte(sql), 0666) + + extraArgs := []string{"-db-credentials-file", dbCredentialFile} + commonTabletArg = append(commonTabletArg, "-db-credentials-file", dbCredentialFile) + + shard := cluster.Shard{ + Name: shardName, + } + + var mysqlProcs []*exec.Cmd + for i := 0; i < 4; i++ { + tabletType := "replica" + if i == 0 { + tabletType = "master" + } + tablet := localCluster.GetVttabletInstance(tabletType, 0, cell) + tablet.VttabletProcess = localCluster.GetVtprocessInstanceFromVttablet(tablet, shard.Name, keyspaceName) + tablet.VttabletProcess.DbPassword = dbPassword + tablet.VttabletProcess.ExtraArgs = commonTabletArg + if recovery.UseXb { + tablet.VttabletProcess.ExtraArgs = append(tablet.VttabletProcess.ExtraArgs, recovery.XbArgs...) + } + tablet.VttabletProcess.SupportsBackup = true + tablet.VttabletProcess.EnableSemiSync = true + + tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) + tablet.MysqlctlProcess.InitDBFile = newInitDBFile + tablet.MysqlctlProcess.ExtraArgs = extraArgs + proc, err := tablet.MysqlctlProcess.StartProcess() + if err != nil { + return 1, err + } + mysqlProcs = append(mysqlProcs, proc) + + shard.Vttablets = append(shard.Vttablets, tablet) + } + for _, proc := range mysqlProcs { + if err := proc.Wait(); err != nil { + return 1, err + } + } + master = shard.Vttablets[0] + replica1 = shard.Vttablets[1] + replica2 = shard.Vttablets[2] + replica3 = shard.Vttablets[3] + + if err := localCluster.VtctlclientProcess.InitTablet(master, cell, keyspaceName, hostname, shard.Name); err != nil { + return 1, err + } + if err := localCluster.VtctlclientProcess.InitTablet(replica1, cell, keyspaceName, hostname, shard.Name); err != nil { + return 1, err + } + + for _, tablet := range []cluster.Vttablet{*master, *replica1} { + if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil { + return 1, err + } + if err := tablet.VttabletProcess.Setup(); err != nil { + return 1, err + } + } + + if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard.Name, cell, master.TabletUID); err != nil { + return 1, err + } + return m.Run(), nil + }() + + if err != nil { + log.Error(err.Error()) + os.Exit(1) + } else { + os.Exit(exitCode) + } + +} + +// TestRecoveryImpl does following +// - create a shard with master and replica1 only +// - run InitShardMaster +// - insert some data +// - take a backup +// - insert more data on the master +// - take another backup +// - create a recovery keyspace after first backup +// - bring up tablet_replica2 in the new keyspace +// - check that new tablet does not have data created after backup1 +// - create second recovery keyspace after second backup +// - bring up tablet_replica3 in second keyspace +// - check that new tablet has data created after backup1 but not data created after backup2 +// - check that vtgate queries work correctly +func TestRecoveryImpl(t *testing.T) { + defer tabletsTeardown() + verifyInitialReplication(t) + + err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + assert.Nil(t, err) + + backups := listBackups(t) + require.Equal(t, len(backups), 1) + assert.Contains(t, backups[0], replica1.Alias) + + _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) + assert.Nil(t, err) + cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 2) + + err = localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) + assert.Nil(t, err) + + output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", keyspaceName) + assert.Nil(t, err) + assert.Contains(t, output, "vt_insert_test") + + recovery.RestoreTablet(t, localCluster, replica2, recoveryKS1, "0", keyspaceName, commonTabletArg) + + output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvVSchema", cell) + assert.Nil(t, err) + assert.Contains(t, output, keyspaceName) + assert.Contains(t, output, recoveryKS1) + + err = localCluster.VtctlclientProcess.ExecuteCommand("GetSrvKeyspace", cell, keyspaceName) + assert.Nil(t, err) + + output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS1) + assert.Nil(t, err) + assert.Contains(t, output, "vt_insert_test") + + cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 1) + + cluster.VerifyLocalMetadata(t, replica2, recoveryKS1, shardName, cell) + + // update the original row in master + _, err = master.VttabletProcess.QueryTablet("update vt_insert_test set msg = 'msgx1' where id = 1", keyspaceName, true) + assert.Nil(t, err) + + //verify that master has new value + qr, err := master.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) + assert.Nil(t, err) + assert.Equal(t, "msgx1", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) + + //verify that restored replica has old value + qr, err = replica2.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) + assert.Nil(t, err) + assert.Equal(t, "test1", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) + + err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) + assert.Nil(t, err) + + _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test3')", keyspaceName, true) + assert.Nil(t, err) + cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 3) + + recovery.RestoreTablet(t, localCluster, replica3, recoveryKS2, "0", keyspaceName, commonTabletArg) + + output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS2) + assert.Nil(t, err) + assert.Contains(t, output, "vt_insert_test") + + cluster.VerifyRowsInTablet(t, replica3, keyspaceName, 2) + + // update the original row in master + _, err = master.VttabletProcess.QueryTablet("update vt_insert_test set msg = 'msgx2' where id = 1", keyspaceName, true) + assert.Nil(t, err) + + //verify that master has new value + qr, err = master.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) + assert.Nil(t, err) + assert.Equal(t, "msgx2", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) + + //verify that restored replica has old value + qr, err = replica3.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) + assert.Nil(t, err) + assert.Equal(t, "msgx1", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) + + vtgateInstance := localCluster.GetVtgateInstance() + vtgateInstance.TabletTypesToWait = "REPLICA" + err = vtgateInstance.Setup() + localCluster.VtgateGrpcPort = vtgateInstance.GrpcPort + assert.Nil(t, err) + defer vtgateInstance.TearDown() + err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", keyspaceName, shardName), 1) + assert.Nil(t, err) + err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shardName), 1) + assert.Nil(t, err) + err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", recoveryKS1, shardName), 1) + assert.Nil(t, err) + err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", recoveryKS2, shardName), 1) + assert.Nil(t, err) + + // Build vtgate grpc connection + grpcAddress := fmt.Sprintf("%s:%d", localCluster.Hostname, localCluster.VtgateGrpcPort) + vtgateConn, err := vtgateconn.Dial(context.Background(), grpcAddress) + assert.Nil(t, err) + defer vtgateConn.Close() + session := vtgateConn.Session("@replica", nil) + + //check that vtgate doesn't route queries to new tablet + recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(3)") + recovery.VerifyQueriesUsingVtgate(t, session, "select msg from vt_insert_test where id = 1", `VARCHAR("msgx2")`) + recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select count(*) from %s.vt_insert_test", recoveryKS1), "INT64(1)") + recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select msg from %s.vt_insert_test where id = 1", recoveryKS1), `VARCHAR("test1")`) + recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select count(*) from %s.vt_insert_test", recoveryKS2), "INT64(2)") + recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select msg from %s.vt_insert_test where id = 1", recoveryKS2), `VARCHAR("msgx1")`) + + // check that new keyspace is accessible with 'use ks' + cluster.ExecuteQueriesUsingVtgate(t, session, "use "+recoveryKS1+"@replica") + recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(1)") + + cluster.ExecuteQueriesUsingVtgate(t, session, "use "+recoveryKS2+"@replica") + recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") + + // check that new tablet is accessible with use `ks:shard` + cluster.ExecuteQueriesUsingVtgate(t, session, "use `"+recoveryKS1+":0@replica`") + recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(1)") + + cluster.ExecuteQueriesUsingVtgate(t, session, "use `"+recoveryKS2+":0@replica`") + recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(2)") +} + +// verifyInitialReplication will create schema in master, insert some data to master and verify the same data in replica. +func verifyInitialReplication(t *testing.T) { + _, err := master.VttabletProcess.QueryTablet(vtInsertTest, keyspaceName, true) + assert.Nil(t, err) + _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test1')", keyspaceName, true) + assert.Nil(t, err) + cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 1) +} + +func listBackups(t *testing.T) []string { + output, err := localCluster.ListBackups(shardKsName) + assert.Nil(t, err) + return output +} + +func tabletsTeardown() { + var mysqlProcs []*exec.Cmd + for _, tablet := range []*cluster.Vttablet{master, replica1, replica2, replica3} { + proc, _ := tablet.MysqlctlProcess.StopProcess() + mysqlProcs = append(mysqlProcs, proc) + tablet.VttabletProcess.TearDown() + } + for _, proc := range mysqlProcs { + proc.Wait() + } +} diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go b/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go index 6cfaee4ff4e..86079a6eb54 100644 --- a/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Vitess Authors. +Copyright 2020 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,188 +17,15 @@ limitations under the License. package unshardedrecovery import ( - "context" - "fmt" - "os/exec" "testing" - "vitess.io/vitess/go/test/endtoend/recovery" - - "vitess.io/vitess/go/vt/vtgate/vtgateconn" - - "github.com/stretchr/testify/assert" - "vitess.io/vitess/go/test/endtoend/cluster" _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" ) -var ( - recoveryKS1 = "recovery_ks1" - recoveryKS2 = "recovery_ks2" - vtInsertTest = `create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB` - vSchema = `{ - "tables": { - "vt_insert_test": {} - } -}` -) - -//TestRecovery does following -//- create a shard with master and replica1 only -//- run InitShardMaster -//- insert some data -//- take a backup -//- insert more data on the master -//- take another backup -//- create a recovery keyspace after first backup -//- bring up tablet_replica2 in the new keyspace -//- check that new tablet does not have data created after backup1 -//- create second recovery keyspace after second backup -//- bring up tablet_replica3 in second keyspace -//- check that new tablet has data created after backup1 but not data created after backup2 -//- check that vtgate queries work correctly - -func TestRecovery(t *testing.T) { - verifyInitialReplication(t) - - err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) - - backups := listBackups(t) - assert.Equal(t, len(backups), 1) - assert.Contains(t, backups[0], replica1.Alias) - - _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) - cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 2) - - err = localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - assert.Nil(t, err) - - output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", keyspaceName) - assert.Nil(t, err) - assert.Contains(t, output, "vt_insert_test") - - recovery.RestoreTablet(t, localCluster, replica2, recoveryKS1, "0", keyspaceName, commonTabletArg) - - output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvVSchema", cell) - assert.Nil(t, err) - assert.Contains(t, output, keyspaceName) - assert.Contains(t, output, recoveryKS1) - - err = localCluster.VtctlclientProcess.ExecuteCommand("GetSrvKeyspace", cell, keyspaceName) - assert.Nil(t, err) - - output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS1) - assert.Nil(t, err) - assert.Contains(t, output, "vt_insert_test") - - cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 1) - - cluster.VerifyLocalMetadata(t, replica2, recoveryKS1, shardName, cell) - - // update the original row in master - _, err = master.VttabletProcess.QueryTablet("update vt_insert_test set msg = 'msgx1' where id = 1", keyspaceName, true) - assert.Nil(t, err) - - //verify that master has new value - qr, err := master.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) - assert.Nil(t, err) - assert.Equal(t, "msgx1", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) - - //verify that restored replica has old value - qr, err = replica2.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) - assert.Nil(t, err) - assert.Equal(t, "test1", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) - - err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) - - _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test3')", keyspaceName, true) - assert.Nil(t, err) - cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 3) - - recovery.RestoreTablet(t, localCluster, replica3, recoveryKS2, "0", keyspaceName, commonTabletArg) - - output, err = localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetVSchema", recoveryKS2) - assert.Nil(t, err) - assert.Contains(t, output, "vt_insert_test") - - cluster.VerifyRowsInTablet(t, replica3, keyspaceName, 2) - - // update the original row in master - _, err = master.VttabletProcess.QueryTablet("update vt_insert_test set msg = 'msgx2' where id = 1", keyspaceName, true) - assert.Nil(t, err) - - //verify that master has new value - qr, err = master.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) - assert.Nil(t, err) - assert.Equal(t, "msgx2", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) - - //verify that restored replica has old value - qr, err = replica3.VttabletProcess.QueryTablet("select msg from vt_insert_test where id = 1", keyspaceName, true) - assert.Nil(t, err) - assert.Equal(t, "msgx1", fmt.Sprintf("%s", qr.Rows[0][0].ToBytes())) - - vtgateInstance := localCluster.GetVtgateInstance() - vtgateInstance.TabletTypesToWait = "REPLICA" - err = vtgateInstance.Setup() - localCluster.VtgateGrpcPort = vtgateInstance.GrpcPort - assert.Nil(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", keyspaceName, shardName), 1) - assert.Nil(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shardName), 1) - assert.Nil(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", recoveryKS1, shardName), 1) - assert.Nil(t, err) - err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", recoveryKS2, shardName), 1) - assert.Nil(t, err) - - // Build vtgate grpc connection - grpcAddress := fmt.Sprintf("%s:%d", localCluster.Hostname, localCluster.VtgateGrpcPort) - vtgateConn, err := vtgateconn.Dial(context.Background(), grpcAddress) - assert.Nil(t, err) - session := vtgateConn.Session("@replica", nil) - - //check that vtgate doesn't route queries to new tablet - recovery.VerifyQueriesUsingVtgate(t, session, "select count(*) from vt_insert_test", "INT64(3)") - recovery.VerifyQueriesUsingVtgate(t, session, "select msg from vt_insert_test where id = 1", `VARCHAR("msgx2")`) - recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select count(*) from %s.vt_insert_test", recoveryKS1), "INT64(1)") - recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select msg from %s.vt_insert_test where id = 1", recoveryKS1), `VARCHAR("test1")`) - recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select count(*) from %s.vt_insert_test", recoveryKS2), "INT64(2)") - recovery.VerifyQueriesUsingVtgate(t, session, fmt.Sprintf("select msg from %s.vt_insert_test where id = 1", recoveryKS2), `VARCHAR("msgx1")`) - - vtgateConn.Close() - vtgateInstance.TearDown() - tabletsTeardown() +func TestMain(m *testing.M) { + TestMainImpl(m) } -// This will create schema in master, insert some data to master and verify the same data in replica -func verifyInitialReplication(t *testing.T) { - _, err := master.VttabletProcess.QueryTablet(vtInsertTest, keyspaceName, true) - assert.Nil(t, err) - _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test1')", keyspaceName, true) - assert.Nil(t, err) - cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 1) -} - -func listBackups(t *testing.T) []string { - output, err := localCluster.ListBackups(shardKsName) - assert.Nil(t, err) - return output -} - -func tabletsTeardown() { - var mysqlProcs []*exec.Cmd - for _, tablet := range []*cluster.Vttablet{master, replica1, replica2, replica3} { - proc, _ := tablet.MysqlctlProcess.StopProcess() - mysqlProcs = append(mysqlProcs, proc) - tablet.VttabletProcess.TearDown() - } - for _, proc := range mysqlProcs { - proc.Wait() - } +func TestRecovery(t *testing.T) { + TestRecoveryImpl(t) } diff --git a/go/test/endtoend/recovery/xtrabackup/recovery_test.go b/go/test/endtoend/recovery/xtrabackup/recovery_test.go new file mode 100644 index 00000000000..940fc5ad7e2 --- /dev/null +++ b/go/test/endtoend/recovery/xtrabackup/recovery_test.go @@ -0,0 +1,34 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package xtrabackup + +import ( + "testing" + + "vitess.io/vitess/go/test/endtoend/recovery" + "vitess.io/vitess/go/test/endtoend/recovery/unshardedrecovery" + _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" +) + +func TestMain(m *testing.M) { + recovery.UseXb = true + unshardedrecovery.TestMainImpl(m) +} + +func TestRecovery(t *testing.T) { + unshardedrecovery.TestRecoveryImpl(t) +} diff --git a/test/config.json b/test/config.json index c9a9f4b5d9e..9efbc5a5df5 100644 --- a/test/config.json +++ b/test/config.json @@ -114,15 +114,6 @@ "RetryMax": 0, "Tags": [] }, - "xb_recovery": { - "File": "xb_recovery.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 2, - "RetryMax": 0, - "Tags": [] - }, "backup": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtctlbackup"], @@ -500,6 +491,15 @@ "RetryMax": 0, "Tags": [] }, + "xb_recovery": { + "File": "recovery_test.go", + "Args": ["vitess.io/vitess/go/test/endtoend/recovery/xtrabackup"], + "Command": [], + "Manual": false, + "Shard": 17, + "RetryMax": 0, + "Tags": [] + }, "worker": { "File": "worker_test.go", "Args": ["vitess.io/vitess/go/test/endtoend/worker"], From 47d7b2b20bb20b1c4f85fce14fba17e21faa6ec3 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Fri, 21 Feb 2020 21:19:05 +0530 Subject: [PATCH 153/825] Support Delete Scatter for owned vindex Signed-off-by: Harshit Gangal --- go/vt/vtgate/engine/delete.go | 44 ++++++++- go/vt/vtgate/engine/delete_test.go | 119 ++++++++++++++++++++--- go/vt/vtgate/engine/fake_vcursor_test.go | 2 +- go/vt/vtgate/engine/update.go | 2 +- go/vt/vtgate/engine/update_test.go | 6 ++ 5 files changed, 155 insertions(+), 18 deletions(-) diff --git a/go/vt/vtgate/engine/delete.go b/go/vt/vtgate/engine/delete.go index 07702164989..427fd0362a4 100644 --- a/go/vt/vtgate/engine/delete.go +++ b/go/vt/vtgate/engine/delete.go @@ -174,7 +174,7 @@ func (del *Delete) deleteVindexEntries(vcursor VCursor, bindVars map[string]*que if len(result.Rows) == 0 { return nil } - colnum := 0 + colnum := 1 // we start from the first lookup vindex col for _, colVindex := range del.Table.Owned { ids := make([][]sqltypes.Value, len(result.Rows)) for range colVindex.Columns { @@ -190,6 +190,42 @@ func (del *Delete) deleteVindexEntries(vcursor VCursor, bindVars map[string]*que return nil } +func (del *Delete) deleteVindexEntriesScatter(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) error { + queries := make([]*querypb.BoundQuery, len(rss)) + for i := range rss { + queries[i] = &querypb.BoundQuery{Sql: del.OwnedVindexQuery, BindVariables: bindVars} + } + subQueryResults, errors := vcursor.ExecuteMultiShard(rss, queries, false, false) + for _, err := range errors { + if err != nil { + return vterrors.Wrap(err, "deleteVindexEntriesScatter") + } + } + + if len(subQueryResults.Rows) == 0 { + return nil + } + + for _, row := range subQueryResults.Rows { + ksid := row[0].Raw() + colnum := 1 + for _, colVindex := range del.Table.Owned { + // Fetch the column values. colnum must keep incrementing. + fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) + for range colVindex.Columns { + fromIds = append(fromIds, row[colnum]) + colnum++ + } + if err := colVindex.Vindex.(vindexes.Lookup).Delete(vcursor, [][]sqltypes.Value{fromIds}, ksid); err != nil { + return err + } + } + + } + + return nil +} + func (del *Delete) execDeleteByDestination(vcursor VCursor, bindVars map[string]*querypb.BindVariable, dest key.Destination) (*sqltypes.Result, error) { rss, _, err := vcursor.ResolveDestinations(del.Keyspace.Name, nil, []key.Destination{dest}) if err != nil { @@ -204,6 +240,12 @@ func (del *Delete) execDeleteByDestination(vcursor VCursor, bindVars map[string] BindVariables: bindVars, } } + if len(del.Table.Owned) > 0 { + err = del.deleteVindexEntriesScatter(vcursor, bindVars, rss) + if err != nil { + return nil, err + } + } autocommit := (len(rss) == 1 || del.MultiShardAutocommit) && vcursor.AutocommitApproval() res, errs := vcursor.ExecuteMultiShard(rss, queries, true /* isDML */, autocommit) return res, vterrors.Aggregate(errs) diff --git a/go/vt/vtgate/engine/delete_test.go b/go/vt/vtgate/engine/delete_test.go index 86dd8e37905..e2568037736 100644 --- a/go/vt/vtgate/engine/delete_test.go +++ b/go/vt/vtgate/engine/delete_test.go @@ -160,10 +160,10 @@ func TestDeleteOwnedVindex(t *testing.T) { results := []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( - "c1|c2|c3", - "int64|int64|int64", + "id|c1|c2|c3", + "varbinary|int64|int64|int64", ), - "4|5|6", + "\026k@\264J\272K\326|4|5|6", )} vc := &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -206,11 +206,11 @@ func TestDeleteOwnedVindex(t *testing.T) { // Delete can affect multiple rows results = []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( - "c1|c2|c3", - "int64|int64|int64", + "id|c1|c2|c3", + "varbinary|int64|int64|int64", ), - "4|5|6", - "7|8|9", + "\026k@\264J\272K\326|4|5|6", + "\026k@\264J\272K\326|7|8|9", )} vc = &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -237,14 +237,13 @@ func TestDeleteOwnedVindex(t *testing.T) { } func TestDeleteSharded(t *testing.T) { + ks := buildTestVSchema().Keyspaces["sharded"] del := &Delete{ DML: DML{ - Opcode: Scatter, - Keyspace: &vindexes.Keyspace{ - Name: "ks", - Sharded: true, - }, - Query: "dummy_delete", + Opcode: Scatter, + Keyspace: ks.Keyspace, + Query: "dummy_delete", + Table: ks.Tables["t2"], }, } @@ -254,8 +253,8 @@ func TestDeleteSharded(t *testing.T) { t.Fatal(err) } vc.ExpectLog(t, []string{ - `ResolveDestinations ks [] Destinations:DestinationAllShards()`, - `ExecuteMultiShard ks.-20: dummy_delete {} ks.20-: dummy_delete {} true false`, + `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, + `ExecuteMultiShard sharded.-20: dummy_delete {} sharded.20-: dummy_delete {} true false`, }) // Failure case @@ -269,3 +268,93 @@ func TestDeleteNoStream(t *testing.T) { err := del.StreamExecute(nil, nil, false, nil) expectError(t, "StreamExecute", err, `query "" cannot be used for streaming`) } + +func TestDeleteScatterOwnedVindex(t *testing.T) { + ks := buildTestVSchema().Keyspaces["sharded"] + del := &Delete{ + DML: DML{ + Opcode: Scatter, + Keyspace: ks.Keyspace, + Query: "dummy_delete", + Table: ks.Tables["t1"], + OwnedVindexQuery: "dummy_subquery", + }, + } + + results := []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|c1|c2|c3", + "varbinary|int64|int64|int64", + ), + "\026k@\264J\272K\326|4|5|6", + )} + vc := &loggingVCursor{ + shards: []string{"-20", "20-"}, + results: results, + } + + _, err := del.Execute(vc, map[string]*querypb.BindVariable{}, false) + if err != nil { + t.Fatal(err) + } + vc.ExpectLog(t, []string{ + `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, + // ResolveDestinations is hard-coded to return -20. + // It gets used to perform the subquery to fetch the changing column values. + `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, + // Those values are returned as 4,5 for twocol and 6 for onecol. + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // Finally, the actual delete, which is also sent to -20, same route as the subquery. + `ExecuteMultiShard sharded.-20: dummy_delete {} sharded.20-: dummy_delete {} true false`, + }) + + // No rows changing + vc = &loggingVCursor{ + shards: []string{"-20", "20-"}, + } + _, err = del.Execute(vc, map[string]*querypb.BindVariable{}, false) + if err != nil { + t.Fatal(err) + } + vc.ExpectLog(t, []string{ + `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, + // ResolveDestinations is hard-coded to return -20. + // It gets used to perform the subquery to fetch the changing column values. + `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, + // Subquery returns no rows. So, no vindexes are deleted. We still pass-through the original delete. + `ExecuteMultiShard sharded.-20: dummy_delete {} sharded.20-: dummy_delete {} true false`, + }) + + // Delete can affect multiple rows + results = []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|c1|c2|c3", + "varbinary|int64|int64|int64", + ), + "\026k@\264J\272K\326|4|5|6", + "\026k@\264J\272K\326|7|8|9", + )} + vc = &loggingVCursor{ + shards: []string{"-20", "20-"}, + results: results, + } + _, err = del.Execute(vc, map[string]*querypb.BindVariable{}, false) + if err != nil { + t.Fatal(err) + } + vc.ExpectLog(t, []string{ + `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, + // ResolveDestinations is hard-coded to return -20. + // It gets used to perform the subquery to fetch the changing column values. + `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, + // Delete 4,5 and 7,8 from lkp2. + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // Delete 6 and 8 from lkp1. + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // Send the DML. + `ExecuteMultiShard sharded.-20: dummy_delete {} sharded.20-: dummy_delete {} true false`, + }) +} diff --git a/go/vt/vtgate/engine/fake_vcursor_test.go b/go/vt/vtgate/engine/fake_vcursor_test.go index f7545a1cfe3..7ff09281eb8 100644 --- a/go/vt/vtgate/engine/fake_vcursor_test.go +++ b/go/vt/vtgate/engine/fake_vcursor_test.go @@ -231,7 +231,7 @@ func (f *loggingVCursor) ResolveDestinations(keyspace string, ids []*querypb.Val func (f *loggingVCursor) ExpectLog(t *testing.T, want []string) { t.Helper() - require.Equal(t, strings.Join(f.log, "\n"), strings.Join(want, "\n")) + require.Equal(t, strings.Join(want, "\n"), strings.Join(f.log, "\n")) } func (f *loggingVCursor) ExpectWarnings(t *testing.T, want []*querypb.QueryWarning) { diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index d97a492bd65..ca5359e54bd 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -187,7 +187,7 @@ func (upd *Update) updateVindexEntries(vcursor VCursor, bindVars map[string]*que if len(subQueryResult.Rows) > 1 { return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: update changes multiple rows in the vindex") } - colnum := 1 + colnum := 1 // we start from the first lookup vindex col for _, colVindex := range upd.Table.Owned { // Fetch the column values. colnum must keep incrementing. fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index 9f665c93d4f..786de84e9ec 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -375,6 +375,12 @@ func buildTestVSchema() *vindexes.VSchema { Columns: []string{"c3"}, }}, }, + "t2": { + ColumnVindexes: []*vschemapb.ColumnVindex{{ + Name: "hash", + Columns: []string{"id"}, + }}, + }, }, }, }, From a4055f11753b1666e0cf5b5af6895b426201ee57 Mon Sep 17 00:00:00 2001 From: Serry Park Date: Tue, 21 Jan 2020 16:00:37 -0800 Subject: [PATCH 154/825] limit healthchecking when we pass in keyspaces_to_watch Signed-off-by: Serry Park Signed-off-by: Serry Park --- go/vt/discovery/topology_watcher.go | 56 ++++++++++++++ go/vt/discovery/topology_watcher_test.go | 95 ++++++++++++++++++++++++ go/vt/vtgate/gateway/discoverygateway.go | 2 + 3 files changed, 153 insertions(+) diff --git a/go/vt/discovery/topology_watcher.go b/go/vt/discovery/topology_watcher.go index fdf14f11a19..b623457663c 100644 --- a/go/vt/discovery/topology_watcher.go +++ b/go/vt/discovery/topology_watcher.go @@ -437,3 +437,59 @@ func (fbs *FilterByShard) isIncluded(tablet *topodatapb.Tablet) bool { } return false } + +// FilterByKeyspace is a TabletRecorder filter that filters tablets by +// keyspace +type FilterByKeyspace struct { + tr TabletRecorder + + keyspaces map[string]bool +} + +// NewFilterByKeyspace creates a new FilterByKeyspace on top of an existing +// TabletRecorder. Each filter is a keyspace entry. All tablets that match +// a keyspace will be forwarded to the underlying TabletRecorder. +func NewFilterByKeyspace(tr TabletRecorder, selectedKeyspaces []string) *FilterByKeyspace { + m := make(map[string]bool) + for _, keyspace := range selectedKeyspaces { + m[keyspace] = true + } + + return &FilterByKeyspace{ + tr: tr, + keyspaces: m, + } +} + +// AddTablet is part of the TabletRecorder interface. +func (fbk *FilterByKeyspace) AddTablet(tablet *topodatapb.Tablet, name string) { + if fbk.isIncluded(tablet) { + fbk.tr.AddTablet(tablet, name) + } +} + +// RemoveTablet is part of the TabletRecorder interface. +func (fbk *FilterByKeyspace) RemoveTablet(tablet *topodatapb.Tablet) { + if fbk.isIncluded(tablet) { + fbk.tr.RemoveTablet(tablet) + } +} + +// ReplaceTablet is part of the TabletRecorder interface. +func (fbk *FilterByKeyspace) ReplaceTablet(old *topodatapb.Tablet, new *topodatapb.Tablet, name string) { + if old.Keyspace != new.Keyspace { + log.Errorf("Error replacing old tablet in %v with new tablet in %v", old.Keyspace, new.Keyspace) + return + } + + if fbk.isIncluded(new) { + fbk.tr.ReplaceTablet(old, new, name) + } +} + +// isIncluded returns true if the tablet's keyspace should be +// forwarded to the underlying TabletRecorder. +func (fbk *FilterByKeyspace) isIncluded(tablet *topodatapb.Tablet) bool { + _, exist := fbk.keyspaces[tablet.Keyspace] + return exist +} diff --git a/go/vt/discovery/topology_watcher_test.go b/go/vt/discovery/topology_watcher_test.go index c3cd553699a..02c664fa15b 100644 --- a/go/vt/discovery/topology_watcher_test.go +++ b/go/vt/discovery/topology_watcher_test.go @@ -17,6 +17,7 @@ limitations under the License. package discovery import ( + "math/rand" "testing" "time" @@ -410,3 +411,97 @@ func TestFilterByShard(t *testing.T) { } } } + +var ( + testFilterByKeyspace = []struct { + keyspace string + expected bool + }{ + {"ks1", true}, + {"ks2", true}, + {"ks3", false}, + {"ks4", true}, + {"ks5", true}, + {"ks6", false}, + {"ks7", false}, + } + testKeyspacesToWatch = []string{"ks1", "ks2", "ks4", "ks5"} + testCell = "testCell" + testShard = "testShard" + testHostName = "testHostName" +) + +func TestFilterByKeyspace(t *testing.T) { + hc := NewFakeHealthCheck() + tr := NewFilterByKeyspace(hc, testKeyspacesToWatch) + ts := memorytopo.NewServer(testCell) + tw := NewCellTabletsWatcher(context.Background(), ts, tr, testCell, 10*time.Minute, true, 5) + + for _, test := range testFilterByKeyspace { + // Add a new tablet to the topology. + port := rand.Int31n(1000) + tablet := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: testCell, + Uid: rand.Uint32(), + }, + Hostname: testHostName, + PortMap: map[string]int32{ + "vt": port, + }, + Keyspace: test.keyspace, + Shard: testShard, + } + + got := tr.isIncluded(tablet) + if got != test.expected { + t.Errorf("isIncluded(%v) for keyspace %v returned %v but expected %v", test.keyspace, test.keyspace, got, test.expected) + } + + if err := ts.CreateTablet(context.Background(), tablet); err != nil { + t.Errorf("CreateTablet failed: %v", err) + } + + tw.loadTablets() + key := TabletToMapKey(tablet) + allTablets := hc.GetAllTablets() + + if _, ok := allTablets[key]; ok != test.expected && proto.Equal(allTablets[key], tablet) != test.expected { + t.Errorf("Error adding tablet - got %v; want %v", ok, test.expected) + } + + // Replace the tablet we added above + tabletReplacement := &topodatapb.Tablet{ + Alias: &topodatapb.TabletAlias{ + Cell: testCell, + Uid: rand.Uint32(), + }, + Hostname: testHostName, + PortMap: map[string]int32{ + "vt": port, + }, + Keyspace: test.keyspace, + Shard: testShard, + } + got = tr.isIncluded(tabletReplacement) + if got != test.expected { + t.Errorf("isIncluded(%v) for keyspace %v returned %v but expected %v", test.keyspace, test.keyspace, got, test.expected) + } + if err := ts.CreateTablet(context.Background(), tabletReplacement); err != nil { + t.Errorf("CreateTablet failed: %v", err) + } + + tw.loadTablets() + key = TabletToMapKey(tabletReplacement) + allTablets = hc.GetAllTablets() + + if _, ok := allTablets[key]; ok != test.expected && proto.Equal(allTablets[key], tabletReplacement) != test.expected { + t.Errorf("Error replacing tablet - got %v; want %v", ok, test.expected) + } + + // Delete the tablet + if err := ts.DeleteTablet(context.Background(), tabletReplacement.Alias); err != nil { + t.Fatalf("DeleteTablet failed: %v", err) + } + } +} diff --git a/go/vt/vtgate/gateway/discoverygateway.go b/go/vt/vtgate/gateway/discoverygateway.go index feb1792a9a0..e8f2240ee40 100644 --- a/go/vt/vtgate/gateway/discoverygateway.go +++ b/go/vt/vtgate/gateway/discoverygateway.go @@ -125,6 +125,8 @@ func createDiscoveryGateway(ctx context.Context, hc discovery.HealthCheck, serv log.Exitf("Cannot parse tablet_filters parameter: %v", err) } tr = fbs + } else if len(KeyspacesToWatch) > 0 { + tr = discovery.NewFilterByKeyspace(dg.hc, KeyspacesToWatch) } ctw := discovery.NewCellTabletsWatcher(ctx, topoServer, tr, c, *refreshInterval, *refreshKnownTablets, *topoReadConcurrency) From c051a7051ffda0cfaf3a741d8e36d01390c3d9af Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 21 Feb 2020 12:07:40 -0800 Subject: [PATCH 155/825] vrepl: vplayer must rollback on exit There were code paths where vplayer had an open transaction on its dbclient connection, which got accidentally continued by the copier. This caused production issues with some users. The specific use case that was observed is: * vreplication performs catchup. * While it's in the middle of an apply, the context is canceled. * The copier has no work. * vreplication is asked to resume from the last saved position. * It replays the same statements already in the transaction. * This leads to dup key errors. This fix conservatively rolls back dbclient before exiting. The dbclient itself skips rollback if it's not in a transaction. This change leads to relieable rollbacks where needed. At the same time, there are no spurious rollbacks if we didn't start a transaction. As safety, I've added an extra rollback in the vreplicator loop just in case the underlying functions accidentally leave an incomplete transaction open. Signed-off-by: Sugu Sougoumarane --- go.mod | 11 +- go.sum | 156 ++++++++++++++++++ .../vreplication/vcopier_flaky_test.go | 21 --- .../tabletmanager/vreplication/vdbclient.go | 3 + .../tabletmanager/vreplication/vplayer.go | 3 +- .../vreplication/vplayer_test.go | 1 + .../tabletmanager/vreplication/vreplicator.go | 4 + 7 files changed, 168 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index e3d537a1fbe..da7f1292dc9 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/GeertJohan/go.rice v1.0.0 github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect github.com/aws/aws-sdk-go v1.28.8 - github.com/boltdb/bolt v1.3.1 // indirect github.com/bombsimon/wsl v1.2.8 // indirect github.com/cespare/xxhash/v2 v2.1.1 github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 // indirect @@ -20,14 +19,13 @@ require ( github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect github.com/evanphx/json-patch v4.5.0+incompatible github.com/go-critic/go-critic v0.4.0 // indirect - github.com/go-ini/ini v1.12.0 // indirect github.com/go-sql-driver/mysql v1.5.0 github.com/gogo/protobuf v1.3.1 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.2 - github.com/golang/snappy v0.0.0-20170215233205-553a64147049 + github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect github.com/golangci/golangci-lint v1.21.0 // indirect github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect @@ -37,12 +35,9 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/hashicorp/consul v1.5.1 + github.com/hashicorp/consul/api v1.1.0 github.com/hashicorp/go-msgpack v0.5.5 // indirect - github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect - github.com/hashicorp/go-uuid v1.0.1 // indirect github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/hashicorp/memberlist v0.1.4 // indirect - github.com/hashicorp/serf v0.0.0-20161207011743-d3a67ab21bc8 // indirect github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 github.com/klauspost/crc32 v1.2.0 // indirect github.com/klauspost/pgzip v1.2.0 @@ -51,7 +46,6 @@ require ( github.com/mattn/go-isatty v0.0.11 // indirect github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 - github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 @@ -91,7 +85,6 @@ require ( google.golang.org/genproto v0.0.0-20190926190326-7ee9db18f195 // indirect google.golang.org/grpc v1.24.0 gopkg.in/DataDog/dd-trace-go.v1 v1.17.0 - gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect gopkg.in/ldap.v2 v2.5.0 honnef.co/go/tools v0.0.1-2019.2.3 mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2 // indirect diff --git a/go.sum b/go.sum index 12ffff34b90..bd4be8ca64b 100644 --- a/go.sum +++ b/go.sum @@ -7,23 +7,35 @@ cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM= github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v0.0.0-20160329135253-cc2f4770f4d6/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= +github.com/Jeffail/gabs v1.1.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0= +github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -32,12 +44,16 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a h1:Ed33uJE74ksDaYfdY72gK7Cg//o2FgsqlqUfBW079T8= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= +github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.28.8 h1:kPGnElMdW0GDc54Giy1lcE/3gAr2Gzl6cMjYKoBNFhw= github.com/aws/aws-sdk-go v1.28.8/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= @@ -45,17 +61,23 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bombsimon/wsl v1.2.5 h1:9gTOkIwVtoDZywvX802SDHokeX4kW1cKnV8ZTVAPkRs= github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= github.com/bombsimon/wsl v1.2.8 h1:b+E/W7koicKBZDU+vEsw/hnQTN8026Gv1eMZDLUU/Wc= github.com/bombsimon/wsl v1.2.8/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/circonus-labs/circonus-gometrics v0.0.0-20161109192337-d17a8420c36e/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.0.0-20161110002650-365d370cc145/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 h1:dzj1/xcivGjNPwwifh/dWTczkwcuqsXXFHY1X/TZMtw= @@ -63,6 +85,8 @@ github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292/go.mod h1:qRiX68m github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v0.0.0-20170626015032-703663d1f6ed h1:uycR38QXnpc8YtCCTsNnQfeq6nPQ55F4ld6/WtGAIlM= github.com/coreos/etcd v0.0.0-20170626015032-703663d1f6ed/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -88,15 +112,25 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA= github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU= +github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= +github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/envoyproxy/go-control-plane v0.0.0-20180919002855-2137d9196328/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v0.0.0-20161207003320-04f313413ffd h1:U3yHrYB7NWH2o3UFzJ1J+TknZqM9QQtF8KVIE6Qzrfs= @@ -108,16 +142,20 @@ github.com/go-critic/go-critic v0.4.0 h1:sXD3pix0wDemuPuSlrXpJNNYXlUiKiysLrtPVQm github.com/go-critic/go-critic v0.4.0/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g= github.com/go-ini/ini v1.12.0 h1:K324HQuOp7fYRWIW84d39Y7MqlH/0JU9fImSXUJ2TWk= github.com/go-ini/ini v1.12.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= @@ -140,8 +178,10 @@ github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2X github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b h1:ekuhfTjngPhisSjOJ0QWKpPQE8/rbknHaes6WVJj5Hw= github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -153,6 +193,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= @@ -163,6 +204,8 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= @@ -203,6 +246,9 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -216,6 +262,9 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -227,6 +276,8 @@ github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:J github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.3 h1:iwp+5/UAyzQSFgQ4uR2sni99sJ8Eo9DEacKWM5pekIg= github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= @@ -235,42 +286,81 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v0.0.0-20161128002007-199c40a060d1 h1:HpW72214zD3cWQsV9DW4Sd9piuuw8rPmE4/TAjTgYIE= github.com/grpc-ecosystem/grpc-gateway v0.0.0-20161128002007-199c40a060d1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw= github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= +github.com/hashicorp/consul v1.5.1 h1:p7tRmQ4m3ZMYkGQkuyjLXKbdU1weeumgZFqZOvw7o4c= +github.com/hashicorp/consul v1.5.1/go.mod h1:QsmgXh2YA9Njv6y3/FHXqHYhsMye++3oBoAZ6SR8R8I= +github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.0/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU= +github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4= github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd/go.mod h1:ueUgD9BeIocT7QNuvxSyJyPAM9dfifBcaWmeybb67OY= +github.com/hashicorp/go-hclog v0.0.0-20180402200405-69ff559dc25f/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71/go.mod h1:kbfItVoBJwCfKXDXN4YoAXjxcFVZ7MRrJzyTX6H4giE= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ= +github.com/hashicorp/go-retryablehttp v0.0.0-20180531211321-3b087ef2d313/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E= github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs= github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q= +github.com/hashicorp/raft v1.0.1-0.20190409200437-d9fe23f7d472/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI= +github.com/hashicorp/raft-boltdb v0.0.0-20150201200839-d1e82c1ec3f1/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hashicorp/serf v0.0.0-20161207011743-d3a67ab21bc8 h1:Vd9tjFEMH3X1AMV1BzVAZRwnjy9MoxOsOl+1pqpCVQs= github.com/hashicorp/serf v0.0.0-20161207011743-d3a67ab21bc8/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/vault v0.10.3/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= +github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0= +github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= +github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= @@ -283,6 +373,8 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -290,6 +382,7 @@ github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpR github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= @@ -319,17 +412,21 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/krishicks/yaml-patch v0.0.10 h1:H4FcHpnNwVmw8u0MjPRjWyIXtco6zM2F78t+57oNM3E= github.com/krishicks/yaml-patch v0.0.10/go.mod h1:Sm5TchwZS6sm7RJoyg87tzxm2ZcKzdRE4Q7TjNhPrME= +github.com/lib/pq v0.0.0-20180523175426-90697d60dd84/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lyft/protoc-gen-validate v0.0.0-20180911180927-64fcb82c878e/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE= github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= @@ -344,13 +441,21 @@ github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 h1:jw16EimP5oAEM/2wt+SiEUov/YDyTCTDuPtIKgQIvk0= github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1/go.mod h1:vuvdOZLJuf5HmJAJrKV64MmozrSsk+or0PB5dzdfspg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v0.0.0-20160804032330-cdac8253d00f/go.mod h1:eOsF2yLPlBBJPvD+nhl5QMTBSOBbOph6N7j/IDUw7PY= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -360,23 +465,33 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= +github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= +github.com/oklog/run v0.0.0-20180308005104-6934b124db28/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 h1:c9gVtoY8wPlhJIN2V2I1V+Fn9UcXM8mDG8IHv/1c3r8= github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 h1:0R5mDLI66Qw13qN80TRz85zthQ2nf2+uDyiV23w6c3Q= github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v0.0.0-20160824210600-b984ec7fa9ff h1:pTiDfW+iOjIxjZeCm88gKn/AmR09UGZYZdqif2yPRrM= github.com/pborman/uuid v0.0.0-20160824210600-b984ec7fa9ff/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= @@ -385,6 +500,7 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pires/go-proxyproto v0.0.0-20191211124218-517ecdf5bb2b h1:JPLdtNmpXbWytipbGwYz7zXZzlQNASEiFw5aGAM75us= @@ -394,6 +510,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.0.0-20180328130430-f504d69affe1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= @@ -401,11 +519,13 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180326160409-38c53a9f4bfc/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -414,6 +534,7 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/procfs v0.0.0-20180408092902-8b1c2da0d56d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -424,10 +545,13 @@ github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQl github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= +github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v0.0.0-20160713180306-0aa62d5ddceb h1:1r/p6yT1FfHR1+qBm7UYBPgfqCmzz/8mpNvfc+iKlfU= github.com/satori/go.uuid v0.0.0-20160713180306-0aa62d5ddceb/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= @@ -436,19 +560,24 @@ github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d h1:BzRvVq1EHuIjxpij github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do= github.com/securego/gosec v0.0.0-20191217083152-cb4f343eaff1 h1:p7IOnYri8VyitvXJfgXw7yt2G/teasqQHQ6f/u1RQvc= github.com/securego/gosec v0.0.0-20191217083152-cb4f343eaff1/go.mod h1:sM2KJ/O9PKom+0jAmXpblJ8PWrLbGAk6F2Lzwj4H6wg= +github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs= github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= @@ -467,6 +596,7 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -488,6 +618,7 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b h1:i3lm+BZX5fAaH95wJavMgsSYU95LhSxdNCMa8nLv2gk= github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tinylib/msgp v1.1.1 h1:TnCZ3FIuKeaIy+F45+Cnp+caqdXGy4z74HvwXN+570Y= @@ -521,6 +652,7 @@ github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8W github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -552,6 +684,7 @@ golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -573,6 +706,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -583,6 +717,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190926025831-c00fd9afed17 h1:qPnAdmjNA41t3QBTx2mFGf/SD1IoslhYu7AmdsVzCcs= golang.org/x/net v0.0.0-20190926025831-c00fd9afed17/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -593,6 +728,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -604,6 +740,7 @@ golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -621,11 +758,13 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -661,6 +800,7 @@ golang.org/x/tools v0.0.0-20191219041853-979b82bfef62/go.mod h1:TB2adYChydJhpapK golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -683,6 +823,7 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2El google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190926190326-7ee9db18f195 h1:dWzgMaXfaHsnkRKZ1l3iJLDmTEB40JMl/dqRbJX4D/o= google.golang.org/genproto v0.0.0-20190926190326-7ee9db18f195/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/grpc v0.0.0-20180920234847-8997b5fa0873/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -692,23 +833,31 @@ google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= gopkg.in/DataDog/dd-trace-go.v1 v1.17.0 h1:j9vAp9Re9bbtA/QFehkJpNba/6W2IbJtNuXZophCa54= gopkg.in/DataDog/dd-trace-go.v1 v1.17.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8KqxhFwKci30UxHy3HXPTyQ= gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE= gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ldap.v2 v2.5.0 h1:1rO3ojzsHUk+gq4ZYhC4Pg+EzWaaKIV8+DJwExS5/QQ= gopkg.in/ldap.v2 v2.5.0/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= +gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -718,6 +867,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a h1:LJwr7TCTghdatWv40WobzlKXc9c4s8oGa7QKJUtHhWA= @@ -726,6 +877,11 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069z honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +k8s.io/api v0.0.0-20180806132203-61b11ee65332/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/api v0.0.0-20190325185214-7544f9db76f6/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/apimachinery v0.0.0-20180821005732-488889b0007f/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go index ca02ec3b37f..837054ed1d4 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go @@ -84,7 +84,6 @@ func TestPlayerCopyTables(t *testing.T) { "/insert into _vt.copy_state", "/update _vt.vreplication set state='Copying'", "commit", - "rollback", // The first fast-forward has no starting point. So, it just saves the current position. "/update _vt.vreplication set pos=", "begin", @@ -93,14 +92,12 @@ func TestPlayerCopyTables(t *testing.T) { "commit", // copy of dst1 is done: delete from copy_state. "/delete from _vt.copy_state.*dst1", - "rollback", // The next FF executes and updates the position before copying. "begin", "/update _vt.vreplication set pos=", "commit", // Nothing to copy from yes. Delete from copy_state. "/delete from _vt.copy_state.*yes", - "rollback", // All tables copied. Final catch up followed by Running state. "/update _vt.vreplication set state='Running'", }) @@ -200,14 +197,12 @@ func TestPlayerCopyBigTable(t *testing.T) { "/insert into _vt.copy_state", "/update _vt.vreplication set state='Copying'", "commit", - "rollback", // The first fast-forward has no starting point. So, it just saves the current position. "/update _vt.vreplication set pos=", "begin", "insert into dst(id,val) values (1,'aaa')", `/update _vt.copy_state set lastpk='fields: rows: ' where vrepl_id=.*`, "commit", - "rollback", // The next catchup executes the new row insert, but will be a no-op. "begin", "insert into dst(id,val) select 3, 'ccc' from dual where (3) <= (1)", @@ -228,8 +223,6 @@ func TestPlayerCopyBigTable(t *testing.T) { `/update _vt.copy_state set lastpk='fields: rows: ' where vrepl_id=.*`, "commit", "/delete from _vt.copy_state.*dst", - // rollback is a no-op because the delete is autocommitted. - "rollback", // Copy is done. Go into running state. "/update _vt.vreplication set state='Running'", // All tables copied. Final catch up followed by Running state. @@ -331,14 +324,12 @@ func TestPlayerCopyWildcardRule(t *testing.T) { "/insert into _vt.copy_state", "/update _vt.vreplication set state='Copying'", "commit", - "rollback", // The first fast-forward has no starting point. So, it just saves the current position. "/update _vt.vreplication set pos=", "begin", "insert into src(id,val) values (1,'aaa')", `/update _vt.copy_state set lastpk='fields: rows: ' where vrepl_id=.*`, "commit", - "rollback", // The next catchup executes the new row insert, but will be a no-op. "begin", "insert into src(id,val) select 3, 'ccc' from dual where (3) <= (1)", @@ -359,8 +350,6 @@ func TestPlayerCopyWildcardRule(t *testing.T) { `/update _vt.copy_state set lastpk='fields: rows: ' where vrepl_id=.*`, "commit", "/delete from _vt.copy_state.*src", - // rollback is a no-op because the delete is autocommitted. - "rollback", // Copy is done. Go into running state. "/update _vt.vreplication set state='Running'", // All tables copied. Final catch up followed by Running state. @@ -505,12 +494,10 @@ func TestPlayerCopyTableContinuation(t *testing.T) { "insert into dst1(id,val) values (7,'insert out'), (8,'no change'), (10,'updated'), (12,'move out')", `/update _vt.copy_state set lastpk='fields: fields: rows: ' where vrepl_id=.*`, "/delete from _vt.copy_state.*dst1", - "rollback", // Copy again. There should be no events for catchup. "insert into not_copied(id,val) values (1,'bbb')", `/update _vt.copy_state set lastpk='fields: rows: ' where vrepl_id=.*`, "/delete from _vt.copy_state.*not_copied", - "rollback", }) // Explicitly eat the Running state query. You can't make expectNontxQueries // wait for it because it ignores _vt.vreplication events. @@ -606,7 +593,6 @@ func TestPlayerCopyWildcardTableContinuation(t *testing.T) { "insert into dst(id,val) values (3,'uncopied'), (4,'new')", `/update _vt.copy_state set lastpk.*`, "/delete from _vt.copy_state.*dst", - "rollback", }) // Explicitly eat the Running state query. You can't make expectNontxQueries // wait for it because it ignores _vt.vreplication events. @@ -654,7 +640,6 @@ func TestPlayerCopyTablesNone(t *testing.T) { "begin", "/update _vt.vreplication set state='Stopped'", "commit", - "rollback", }) } @@ -706,7 +691,6 @@ func TestPlayerCopyTablesStopAfterCopy(t *testing.T) { "/insert into _vt.copy_state", "/update _vt.vreplication set state='Copying'", "commit", - "rollback", // The first fast-forward has no starting point. So, it just saves the current position. "/update _vt.vreplication set pos=", "begin", @@ -715,7 +699,6 @@ func TestPlayerCopyTablesStopAfterCopy(t *testing.T) { "commit", // copy of dst1 is done: delete from copy_state. "/delete from _vt.copy_state.*dst1", - "rollback", // All tables copied. Stop vreplication because we requested it. "/update _vt.vreplication set state='Stopped'", }) @@ -784,10 +767,7 @@ func TestPlayerCopyTableCancel(t *testing.T) { "/insert into _vt.copy_state", "/update _vt.vreplication set state='Copying'", "commit", - "rollback", // The first copy will do nothing because we set the timeout to be too low. - // We should expect it to do an empty rollback. - "rollback", // The next copy should proceed as planned because we've made the timeout high again. // The first fast-forward has no starting point. So, it just saves the current position. "/update _vt.vreplication set pos=", @@ -797,7 +777,6 @@ func TestPlayerCopyTableCancel(t *testing.T) { "commit", // copy of dst1 is done: delete from copy_state. "/delete from _vt.copy_state.*dst1", - "rollback", // All tables copied. Go into running state. "/update _vt.vreplication set state='Running'", }) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go index 8be8ac068a2..9b78a7d254f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vdbclient.go @@ -68,6 +68,9 @@ func (vc *vdbClient) Commit() error { } func (vc *vdbClient) Rollback() error { + if !vc.InTransaction { + return nil + } if err := vc.DBClient.Rollback(); err != nil { return err } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index b3b551d452c..464a4fe46cb 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -204,7 +204,6 @@ func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.Row func (vp *vplayer) updatePos(ts int64) (posReached bool, err error) { update := binlogplayer.GenerateUpdatePos(vp.vr.id, vp.pos, time.Now().Unix(), ts) if _, err := vp.vr.dbClient.Execute(update); err != nil { - vp.vr.dbClient.Rollback() return false, fmt.Errorf("error %v updating position", err) } vp.unsavedEvent = nil @@ -223,6 +222,8 @@ func (vp *vplayer) updatePos(ts int64) (posReached bool, err error) { } func (vp *vplayer) applyEvents(ctx context.Context, relay *relayLog) error { + defer vp.vr.dbClient.Rollback() + // If we're not running, set SecondsBehindMaster to be very high. // TODO(sougou): if we also stored the time of the last event, we // can estimate this value more accurately. diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go index 777e6177cc6..ab876697fa0 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go @@ -67,6 +67,7 @@ func TestPlayerStatementModeWithFilter(t *testing.T) { // It does not work when filter is enabled output := []string{ "begin", + "rollback", "/update _vt.vreplication set message='filter rules are not supported for SBR", } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index fec2ab7b44c..0e16bccdcce 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -81,6 +81,10 @@ func (vr *vreplicator) Replicate(ctx context.Context) error { vr.tableKeys = tableKeys for { + // This rollback is a no-op. It's here for safety + // in case the functions below leave transactions open. + vr.dbClient.Rollback() + settings, numTablesToCopy, err := vr.readSettings(ctx) if err != nil { return err From dae31b76b604ec0e63f4c8875dca816777b247ef Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Sat, 22 Feb 2020 09:17:18 +0530 Subject: [PATCH 156/825] fix autocommit and executor test Signed-off-by: Harshit Gangal --- go/vt/vtgate/autocommit_test.go | 5 +++++ go/vt/vtgate/executor_dml_test.go | 9 +++++++++ go/vt/vtgate/planbuilder/dml.go | 3 +-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index a88490c89eb..f7178f4f372 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -140,6 +140,11 @@ func TestAutocommitDeleteSharded(t *testing.T) { // TestAutocommitDeleteLookup: transaction: select before update. func TestAutocommitDeleteLookup(t *testing.T) { executor, sbc1, _, sbclookup := createExecutorEnv() + sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), + "1|1|foo", + ), + }) _, err := autocommitExec(executor, "delete from music where id = 1") require.NoError(t, err) diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index bf10eccaff6..4e53c7b152f 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -329,11 +329,13 @@ func TestDeleteEqual(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ + {Name: "Id", Type: sqltypes.Int64}, {Name: "name", Type: sqltypes.VarChar}, }, RowsAffected: 1, InsertID: 0, Rows: [][]sqltypes.Value{{ + sqltypes.NewInt64(1), sqltypes.NewVarChar("myname"), }}, }}) @@ -416,6 +418,11 @@ func TestDeleteEqual(t *testing.T) { sbc.Queries = nil sbclookup.Queries = nil + sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), + "1|1|foo", + ), + }) _, err = executorExec(executor, "delete from user2 where id = 1", nil) require.NoError(t, err) wantQueries = []*querypb.BoundQuery{ @@ -505,11 +512,13 @@ func TestDeleteComments(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ + {Name: "Id", Type: sqltypes.Int64}, {Name: "name", Type: sqltypes.VarChar}, }, RowsAffected: 1, InsertID: 0, Rows: [][]sqltypes.Value{{ + sqltypes.NewInt64(1), sqltypes.NewVarChar("myname"), }}, }}) diff --git a/go/vt/vtgate/planbuilder/dml.go b/go/vt/vtgate/planbuilder/dml.go index 6cf10c9fd4a..086d7b90501 100644 --- a/go/vt/vtgate/planbuilder/dml.go +++ b/go/vt/vtgate/planbuilder/dml.go @@ -179,8 +179,7 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme func generateDMLSubquery(where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, table *vindexes.Table, vindex string) string { buf := sqlparser.NewTrackedBuffer(nil) - buf.WriteString("select ") - buf.Myprintf("%s", vindex) + buf.Myprintf("select %s", vindex) for _, cv := range table.Owned { for _, column := range cv.Columns { buf.Myprintf(", %v", column) From 4dd79d353c95cffbc65e238881f27a89ce2147d1 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Sat, 22 Feb 2020 11:15:14 +0100 Subject: [PATCH 157/825] Simplify DML planning Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/dml.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/go/vt/vtgate/planbuilder/dml.go b/go/vt/vtgate/planbuilder/dml.go index 086d7b90501..6845d94cf03 100644 --- a/go/vt/vtgate/planbuilder/dml.go +++ b/go/vt/vtgate/planbuilder/dml.go @@ -28,7 +28,7 @@ import ( // getDMLRouting returns the vindex and values for the DML, // If it cannot find a unique vindex match, it returns an error. -func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (dmlRoutingType, vindexes.SingleColumn, string, []sqltypes.PlanValue) { +func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (engine.DMLOpcode, vindexes.SingleColumn, string, []sqltypes.PlanValue) { var keyColumn string for _, index := range table.Ordered { if !index.Vindex.IsUnique() { @@ -44,18 +44,18 @@ func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (dmlRoutingTyp } if where == nil { - return scatter, nil, keyColumn, nil + return engine.Scatter, nil, keyColumn, nil } if pv, ok := getMatch(where.Expr, index.Columns[0]); ok { if pv.IsList() { - return scatter, nil, keyColumn, nil + return engine.Scatter, nil, keyColumn, nil } - return equal, single, keyColumn, []sqltypes.PlanValue{pv} + return engine.Equal, single, keyColumn, []sqltypes.PlanValue{pv} } } - return scatter, nil, keyColumn, nil + return engine.Scatter, nil, keyColumn, nil } // getMatch returns the matched value if there is an equality @@ -162,14 +162,12 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme } routingType, vindex, vindexCol, values := getDMLRouting(where, eupd.Table) - - if routingType == scatter { + eupd.Opcode = routingType + if routingType == engine.Scatter { if limit != nil { return nil, "", vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi shard %s with limit", dmlType) } - eupd.Opcode = engine.Scatter } else { - eupd.Opcode = engine.Equal eupd.Values = values eupd.Vindex = vindex } @@ -204,10 +202,3 @@ func dmlFormatter(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { } node.Format(buf) } - -type dmlRoutingType int - -const ( - scatter dmlRoutingType = iota - equal -) From 2312f0e0ada76b3b411aa17e2472390b987263f5 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Sat, 22 Feb 2020 13:29:43 +0100 Subject: [PATCH 158/825] Test cleaning Signed-off-by: Andres Taylor --- go/vt/vtexplain/vtexplain_flaky_test.go | 166 +++++++++--------------- 1 file changed, 63 insertions(+), 103 deletions(-) diff --git a/go/vt/vtexplain/vtexplain_flaky_test.go b/go/vt/vtexplain/vtexplain_flaky_test.go index 05bd0c003f2..56b26aea57d 100644 --- a/go/vt/vtexplain/vtexplain_flaky_test.go +++ b/go/vt/vtexplain/vtexplain_flaky_test.go @@ -19,8 +19,9 @@ package vtexplain import ( "encoding/json" "fmt" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" "io/ioutil" - "os/exec" "path" "strings" "testing" @@ -39,20 +40,14 @@ func defaultTestOpts() *Options { func initTest(mode string, opts *Options, t *testing.T) { schema, err := ioutil.ReadFile("testdata/test-schema.sql") - if err != nil { - t.Fatalf("error: %v", err) - } + require.NoError(t, err) vSchema, err := ioutil.ReadFile("testdata/test-vschema.json") - if err != nil { - t.Fatalf("error: %v", err) - } + require.NoError(t, err) opts.ExecutionMode = mode err = Init(string(vSchema), string(schema), opts) - if err != nil { - t.Fatalf("vtexplain Init error: %v", err) - } + require.NoError(t, err, "vtexplain Init error") } func testExplain(testcase string, opts *Options, t *testing.T) { @@ -70,91 +65,67 @@ func testExplain(testcase string, opts *Options, t *testing.T) { } func runTestCase(testcase, mode string, opts *Options, t *testing.T) { - t.Logf("vtexplain test: %s mode: %s", testcase, mode) - initTest(mode, opts, t) + t.Run(testcase, func(t *testing.T) { + initTest(mode, opts, t) - sqlFile := fmt.Sprintf("testdata/%s-queries.sql", testcase) - sql, err := ioutil.ReadFile(sqlFile) - if err != nil { - t.Fatalf("vtexplain error: %v", err) - } + sqlFile := fmt.Sprintf("testdata/%s-queries.sql", testcase) + sql, err := ioutil.ReadFile(sqlFile) + require.NoError(t, err, "vtexplain error") - textOutFile := fmt.Sprintf("testdata/%s-output/%s-output.txt", mode, testcase) - textOut, _ := ioutil.ReadFile(textOutFile) + textOutFile := fmt.Sprintf("testdata/%s-output/%s-output.txt", mode, testcase) + expected, _ := ioutil.ReadFile(textOutFile) - explains, err := Run(string(sql)) - if err != nil { - t.Fatalf("vtexplain error: %v", err) - } - if explains == nil { - t.Fatalf("vtexplain error running %s: no explain", string(sql)) - } + explains, err := Run(string(sql)) + require.NoError(t, err, "vtexplain error") + require.NotNil(t, explains, "vtexplain error running %s: no explain", string(sql)) - explainText := ExplainsAsText(explains) - if strings.TrimSpace(string(explainText)) != strings.TrimSpace(string(textOut)) { - // Print the Text that was actually returned and also dump to a - // temp file to be able to diff the results. - t.Errorf("Text output did not match") + explainText := ExplainsAsText(explains) + if diff := cmp.Diff(strings.TrimSpace(string(expected)), strings.TrimSpace(explainText)); diff != "" { + // Print the Text that was actually returned and also dump to a + // temp file to be able to diff the results. + t.Errorf("Text output did not match (-want +got):\n%s", diff) - if testOutputTempDir == "" { - testOutputTempDir, err = ioutil.TempDir("", "vtexplain_output") - if err != nil { - t.Fatalf("error getting tempdir: %v", err) + if testOutputTempDir == "" { + testOutputTempDir, err = ioutil.TempDir("", "vtexplain_output") + require.NoError(t, err, "error getting tempdir") } - } - gotFile := fmt.Sprintf("%s/%s-output.txt", testOutputTempDir, testcase) - ioutil.WriteFile(gotFile, []byte(explainText), 0644) - - command := exec.Command("diff", "-u", textOutFile, gotFile) - out, _ := command.CombinedOutput() - t.Logf("diff:\n%s\n", out) - t.Logf("run the following command to update the expected output:") - t.Logf("cp %s/* %s", testOutputTempDir, path.Dir(textOutFile)) - } -} - -func TestUnsharded(t *testing.T) { - testExplain("unsharded", defaultTestOpts(), t) -} - -func TestSelectSharded(t *testing.T) { - testExplain("selectsharded", defaultTestOpts(), t) -} - -func TestInsertSharded(t *testing.T) { - testExplain("insertsharded", defaultTestOpts(), t) -} - -func TestUpdateSharded(t *testing.T) { - testExplain("updatesharded", defaultTestOpts(), t) -} + gotFile := fmt.Sprintf("%s/%s-output.txt", testOutputTempDir, testcase) + ioutil.WriteFile(gotFile, []byte(explainText), 0644) -func TestDeleteSharded(t *testing.T) { - testExplain("deletesharded", defaultTestOpts(), t) + t.Logf("run the following command to update the expected output:") + t.Logf("cp %s/* %s", testOutputTempDir, path.Dir(textOutFile)) + } + }) } -func TestOptions(t *testing.T) { - opts := &Options{ - ReplicationMode: "STATEMENT", - NumShards: 4, - Normalize: false, - } - - testExplain("options", opts, t) -} -func TestTarget(t *testing.T) { - opts := &Options{ - ReplicationMode: "ROW", - NumShards: 4, - Normalize: false, - Target: "ks_sharded/40-80", +func TestExplain(t *testing.T) { + type test struct { + name string + opts *Options + } + tests := []test{ + {"unsharded", defaultTestOpts()}, + {"selectsharded", defaultTestOpts()}, + {"insertsharded", defaultTestOpts()}, + {"updatesharded", defaultTestOpts()}, + {"deletesharded", defaultTestOpts()}, + {"comments", defaultTestOpts()}, + {"options", &Options{ + ReplicationMode: "STATEMENT", + NumShards: 4, + Normalize: false, + }}, + {"target", &Options{ + ReplicationMode: "ROW", + NumShards: 4, + Normalize: false, + Target: "ks_sharded/40-80", + }}, + } + + for _, tst := range tests { + testExplain(tst.name, tst.opts, t) } - - testExplain("target", opts, t) -} - -func TestComments(t *testing.T) { - testExplain("comments", defaultTestOpts(), t) } func TestErrors(t *testing.T) { @@ -188,9 +159,8 @@ func TestErrors(t *testing.T) { for _, test := range tests { t.Run(test.SQL, func(t *testing.T) { _, err := Run(test.SQL) - if err == nil || !strings.Contains(err.Error(), test.Err) { - t.Errorf(">> Query: %s\n>> Got: %v\nWant: %s", test.SQL, err, test.Err) - } + require.Error(t, err) + require.Contains(t, err.Error(), test.Err) }) } } @@ -198,20 +168,14 @@ func TestErrors(t *testing.T) { func TestJSONOutput(t *testing.T) { sql := "select 1 from user where id = 1" explains, err := Run(sql) - if err != nil { - t.Fatalf("vtexplain error: %v", err) - } - if explains == nil { - t.Fatalf("vtexplain error running %s: no explain", string(sql)) - } + require.NoError(t, err, "vtexplain error") + require.NotNil(t, explains, "vtexplain error running %s: no explain", string(sql)) explainJSON := ExplainsAsJSON(explains) var data interface{} err = json.Unmarshal([]byte(explainJSON), &data) - if err != nil { - t.Errorf("error unmarshaling json: %v", err) - } + require.NoError(t, err, "error unmarshaling json") array, ok := data.([]interface{}) if !ok || len(array) != 1 { @@ -238,9 +202,7 @@ func TestJSONOutput(t *testing.T) { } actionsJSON, err := json.MarshalIndent(actions, "", " ") - if err != nil { - t.Errorf("error in json marshal: %v", err) - } + require.NoError(t, err, "error in json marshal") wantJSON := `{ "ks_sharded/-40": { "MysqlQueries": [ @@ -261,7 +223,5 @@ func TestJSONOutput(t *testing.T) { ] } }` - if string(actionsJSON) != wantJSON { - t.Errorf("TabletActions mismatch: got:\n%v\nwant:\n%v\n", string(actionsJSON), wantJSON) - } + require.Equal(t, wantJSON, string(actionsJSON)) } From d74e4d168c46ae7755621465ac28d25a7067bb51 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 1 Jan 2020 10:23:32 -0800 Subject: [PATCH 159/825] vdiff: refactor MergeSort for readability MergeSort as a function that required a VCursor that performed scatter queries was hard to use from outside of the engine code. In the new change, MergeSort is a Primitive that directly drives other sub-Primitives. This greatly simplifies the vdiff code, removing the need for a mergeSorter and a resultStreamer. Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/engine/merge_sort.go | 70 +++++-- go/vt/vtgate/engine/merge_sort_test.go | 258 +++++++++++-------------- go/vt/vtgate/engine/route.go | 14 +- go/vt/vtgate/engine/shard_route.go | 38 ++++ go/vt/wrangler/vdiff.go | 177 +++++++---------- go/vt/wrangler/vdiff_test.go | 82 ++++---- 6 files changed, 331 insertions(+), 308 deletions(-) create mode 100644 go/vt/vtgate/engine/shard_route.go diff --git a/go/vt/vtgate/engine/merge_sort.go b/go/vt/vtgate/engine/merge_sort.go index e55f213b89d..61a5b4e4e1a 100644 --- a/go/vt/vtgate/engine/merge_sort.go +++ b/go/vt/vtgate/engine/merge_sort.go @@ -27,24 +27,61 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/srvtopo" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" ) -// MergeSort performs a merge-sort of rows returned by a streaming scatter query. -// Each shard of the scatter query is treated as a stream. One row from each stream -// is added to the merge-sorter heap. Every time a value is pulled out of the heap, +// StreamExecuter is a subset of Primitive that MergeSort +// requires its inputs to satisfy. +type StreamExecuter interface { + StreamExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantields bool, callback func(*sqltypes.Result) error) error +} + +var _ Primitive = (*MergeSort)(nil) + +// MergeSort performs a merge-sort of rows returned by each Input. This should +// only be used for StreamExecute. One row from each stream is added to the +// merge-sorter heap. Every time a value is pulled out of the heap, // a new value is added to it from the stream that was the source of the value that // was pulled out. Since the input streams are sorted the same way that the heap is // sorted, this guarantees that the merged stream will also be sorted the same way. -func MergeSort(vcursor VCursor, query string, orderBy []OrderbyParams, rss []*srvtopo.ResolvedShard, bvs []map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error) error { +// MergeSort is not functionally complete and should not be used as a Primitive +// in a plan. +type MergeSort struct { + Primitives []StreamExecuter + OrderBy []OrderbyParams + noInputs +} + +// RouteType satisfies Primitive. +func (ms *MergeSort) RouteType() string { return "MergeSort" } + +// GetKeyspaceName satisfies Primitive. +func (ms *MergeSort) GetKeyspaceName() string { return "" } + +// GetTableName satisfies Primitive. +func (ms *MergeSort) GetTableName() string { return "" } + +// Execute is not supported. +func (ms *MergeSort) Execute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { + return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Execute is not supported") +} + +// GetFields is not supported. +func (ms *MergeSort) GetFields(vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "GetFields is not supported") +} + +// StreamExecute performs a streaming exec. +func (ms *MergeSort) StreamExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { ctx, cancel := context.WithCancel(vcursor.Context()) defer cancel() - handles := make([]*streamHandle, len(rss)) - id := 0 - for i, rs := range rss { - handles[id] = runOneStream(ctx, vcursor, query, rs, bvs[i]) - id++ + handles := make([]*streamHandle, len(ms.Primitives)) + for i, input := range ms.Primitives { + handles[i] = runOneStream(vcursor, input, bindVars, wantfields) + // Need fields only from first handle, if wantfields was true. + wantfields = false } // Fetch field info from just one stream. @@ -59,7 +96,7 @@ func MergeSort(vcursor VCursor, query string, orderBy []OrderbyParams, rss []*sr sh := &scatterHeap{ rows: make([]streamRow, 0, len(handles)), - orderBy: orderBy, + orderBy: ms.OrderBy, } // Prime the heap. One element must be pulled from @@ -133,20 +170,21 @@ type streamHandle struct { } // runOnestream starts a streaming query on one shard, and returns a streamHandle for it. -func runOneStream(ctx context.Context, vcursor VCursor, query string, rs *srvtopo.ResolvedShard, vars map[string]*querypb.BindVariable) *streamHandle { +func runOneStream(vcursor VCursor, input StreamExecuter, bindVars map[string]*querypb.BindVariable, wantfields bool) *streamHandle { handle := &streamHandle{ fields: make(chan []*querypb.Field, 1), row: make(chan []sqltypes.Value, 10), } + ctx := vcursor.Context() go func() { defer close(handle.fields) defer close(handle.row) - handle.err = vcursor.StreamExecuteMulti( - query, - []*srvtopo.ResolvedShard{rs}, - []map[string]*querypb.BindVariable{vars}, + handle.err = input.StreamExecute( + vcursor, + bindVars, + wantfields, func(qr *sqltypes.Result) error { if len(qr.Fields) != 0 { select { diff --git a/go/vt/vtgate/engine/merge_sort_test.go b/go/vt/vtgate/engine/merge_sort_test.go index f8e0e34903c..d5bfc79384b 100644 --- a/go/vt/vtgate/engine/merge_sort_test.go +++ b/go/vt/vtgate/engine/merge_sort_test.go @@ -25,48 +25,41 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/srvtopo" ) // TestMergeSortNormal tests the normal flow of a merge // sort where all shards return ascending rows. func TestMergeSortNormal(t *testing.T) { idColFields := sqltypes.MakeTestFields("id|col", "int32|varchar") - vc := &streamVCursor{ - shardResults: map[string]*shardResult{ - "0": {results: sqltypes.MakeTestStreamingResults(idColFields, - "1|a", - "7|g", - )}, - "1": {results: sqltypes.MakeTestStreamingResults(idColFields, - "2|b", - "---", - "3|c", - )}, - "2": {results: sqltypes.MakeTestStreamingResults(idColFields, - "4|d", - "6|f", - )}, - "3": {results: sqltypes.MakeTestStreamingResults(idColFields, - "4|d", - "---", - "8|h", - )}, - }, - } + shardResults := []*shardResult{{ + results: sqltypes.MakeTestStreamingResults(idColFields, + "1|a", + "7|g", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "2|b", + "---", + "3|c", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "4|d", + "6|f", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "4|d", + "---", + "8|h", + ), + }} orderBy := []OrderbyParams{{ Col: 0, }} - rss := []*srvtopo.ResolvedShard{ - {Target: &querypb.Target{Shard: "0"}}, - {Target: &querypb.Target{Shard: "1"}}, - {Target: &querypb.Target{Shard: "2"}}, - {Target: &querypb.Target{Shard: "3"}}, - } - bvs := []map[string]*querypb.BindVariable{nil, nil, nil, nil} var results []*sqltypes.Result - err := MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { + err := testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { results = append(results, qr) return nil }) @@ -99,42 +92,36 @@ func TestMergeSortNormal(t *testing.T) { // sort where all shards return descending rows. func TestMergeSortDescending(t *testing.T) { idColFields := sqltypes.MakeTestFields("id|col", "int32|varchar") - vc := &streamVCursor{ - shardResults: map[string]*shardResult{ - "0": {results: sqltypes.MakeTestStreamingResults(idColFields, - "7|g", - "1|a", - )}, - "1": {results: sqltypes.MakeTestStreamingResults(idColFields, - "3|c", - "---", - "2|b", - )}, - "2": {results: sqltypes.MakeTestStreamingResults(idColFields, - "6|f", - "4|d", - )}, - "3": {results: sqltypes.MakeTestStreamingResults(idColFields, - "8|h", - "---", - "4|d", - )}, - }, - } + shardResults := []*shardResult{{ + results: sqltypes.MakeTestStreamingResults(idColFields, + "7|g", + "1|a", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "3|c", + "---", + "2|b", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "6|f", + "4|d", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "8|h", + "---", + "4|d", + ), + }} orderBy := []OrderbyParams{{ Col: 0, Desc: true, }} - rss := []*srvtopo.ResolvedShard{ - {Target: &querypb.Target{Shard: "0"}}, - {Target: &querypb.Target{Shard: "1"}}, - {Target: &querypb.Target{Shard: "2"}}, - {Target: &querypb.Target{Shard: "3"}}, - } - bvs := []map[string]*querypb.BindVariable{nil, nil, nil, nil} var results []*sqltypes.Result - err := MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { + err := testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { results = append(results, qr) return nil }) @@ -165,33 +152,27 @@ func TestMergeSortDescending(t *testing.T) { func TestMergeSortEmptyResults(t *testing.T) { idColFields := sqltypes.MakeTestFields("id|col", "int32|varchar") - vc := &streamVCursor{ - shardResults: map[string]*shardResult{ - "0": {results: sqltypes.MakeTestStreamingResults(idColFields, - "1|a", - "7|g", - )}, - "1": {results: sqltypes.MakeTestStreamingResults(idColFields)}, - "2": {results: sqltypes.MakeTestStreamingResults(idColFields, - "4|d", - "6|f", - )}, - "3": {results: sqltypes.MakeTestStreamingResults(idColFields)}, - }, - } + shardResults := []*shardResult{{ + results: sqltypes.MakeTestStreamingResults(idColFields, + "1|a", + "7|g", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "4|d", + "6|f", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields), + }} orderBy := []OrderbyParams{{ Col: 0, }} - rss := []*srvtopo.ResolvedShard{ - {Target: &querypb.Target{Shard: "0"}}, - {Target: &querypb.Target{Shard: "1"}}, - {Target: &querypb.Target{Shard: "2"}}, - {Target: &querypb.Target{Shard: "3"}}, - } - bvs := []map[string]*querypb.BindVariable{nil, nil, nil, nil} var results []*sqltypes.Result - err := MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { + err := testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { results = append(results, qr) return nil }) @@ -215,22 +196,15 @@ func TestMergeSortEmptyResults(t *testing.T) { // TestMergeSortResultFailures tests failures at various // stages of result return. func TestMergeSortResultFailures(t *testing.T) { - vc := &streamVCursor{ - shardResults: make(map[string]*shardResult), - } orderBy := []OrderbyParams{{ Col: 0, }} - rss := []*srvtopo.ResolvedShard{ - {Target: &querypb.Target{Shard: "0"}}, - } - bvs := []map[string]*querypb.BindVariable{nil} // Test early error. - vc.shardResults["0"] = &shardResult{ + shardResults := []*shardResult{{ sendErr: errors.New("early error"), - } - err := MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { return nil }) + }} + err := testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { return nil }) want := "early error" if err == nil || err.Error() != want { t.Errorf("MergeSort(): %v, want %v", err, want) @@ -238,22 +212,22 @@ func TestMergeSortResultFailures(t *testing.T) { // Test fail after fields. idFields := sqltypes.MakeTestFields("id", "int32") - vc.shardResults["0"] = &shardResult{ + shardResults = []*shardResult{{ results: sqltypes.MakeTestStreamingResults(idFields), sendErr: errors.New("fail after fields"), - } - err = MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { return nil }) + }} + err = testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { return nil }) want = "fail after fields" if err == nil || err.Error() != want { t.Errorf("MergeSort(): %v, want %v", err, want) } // Test fail after first row. - vc.shardResults["0"] = &shardResult{ + shardResults = []*shardResult{{ results: sqltypes.MakeTestStreamingResults(idFields, "1"), sendErr: errors.New("fail after first row"), - } - err = MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { return nil }) + }} + err = testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { return nil }) want = "fail after first row" if err == nil || err.Error() != want { t.Errorf("MergeSort(): %v, want %v", err, want) @@ -264,26 +238,20 @@ func TestMergeSortDataFailures(t *testing.T) { // The first row being bad fails in a different code path than // the case of subsequent rows. So, test the two cases separately. idColFields := sqltypes.MakeTestFields("id|col", "int32|varchar") - vc := &streamVCursor{ - shardResults: map[string]*shardResult{ - "0": {results: sqltypes.MakeTestStreamingResults(idColFields, - "1|a", - )}, - "1": {results: sqltypes.MakeTestStreamingResults(idColFields, - "2.1|b", - )}, - }, - } + shardResults := []*shardResult{{ + results: sqltypes.MakeTestStreamingResults(idColFields, + "1|a", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "2.1|b", + ), + }} orderBy := []OrderbyParams{{ Col: 0, }} - rss := []*srvtopo.ResolvedShard{ - {Target: &querypb.Target{Shard: "0"}}, - {Target: &querypb.Target{Shard: "1"}}, - } - bvs := []map[string]*querypb.BindVariable{nil, nil} - err := MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { return nil }) + err := testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { return nil }) want := `strconv.ParseInt: parsing "2.1": invalid syntax` if err == nil || err.Error() != want { t.Errorf("MergeSort(): %v, want %v", err, want) @@ -291,50 +259,48 @@ func TestMergeSortDataFailures(t *testing.T) { // Create a new VCursor because the previous MergeSort will still // have lingering goroutines that can cause data race. - vc = &streamVCursor{ - shardResults: map[string]*shardResult{ - "0": {results: sqltypes.MakeTestStreamingResults(idColFields, - "1|a", - "1.1|a", - )}, - "1": {results: sqltypes.MakeTestStreamingResults(idColFields, - "2|b", - )}, - }, - } - err = MergeSort(vc, "", orderBy, rss, bvs, func(qr *sqltypes.Result) error { return nil }) + shardResults = []*shardResult{{ + results: sqltypes.MakeTestStreamingResults(idColFields, + "1|a", + "1.1|a", + ), + }, { + results: sqltypes.MakeTestStreamingResults(idColFields, + "2|b", + ), + }} + err = testMergeSort(shardResults, orderBy, func(qr *sqltypes.Result) error { return nil }) want = `strconv.ParseInt: parsing "1.1": invalid syntax` if err == nil || err.Error() != want { t.Errorf("MergeSort(): %v, want %v", err, want) } } -type shardResult struct { - results []*sqltypes.Result - // sendErr is sent at the end of the stream if it's set. - sendErr error +func testMergeSort(shardResults []*shardResult, orderBy []OrderbyParams, callback func(qr *sqltypes.Result) error) error { + prims := make([]StreamExecuter, 0, len(shardResults)) + for _, sr := range shardResults { + prims = append(prims, sr) + } + ms := MergeSort{ + Primitives: prims, + OrderBy: orderBy, + } + return ms.StreamExecute(noopVCursor{}, nil, true, callback) } -// streamVCursor fakes a VCursor that supports -// a single-shard streaming query through StreamExecuteMulti. -type streamVCursor struct { - noopVCursor +type shardResult struct { + // shardRoute helps us avoid redefining the Primitive functions. + shardRoute - shardResults map[string]*shardResult + results []*sqltypes.Result + sendErr error } -// StreamExecuteMulti streams a result from the specified shard. -// The shard is specified by the only entry in shardVars. At the -// end of a stream, if sendErr is set, that error is returned. -func (t *streamVCursor) StreamExecuteMulti(query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, callback func(reply *sqltypes.Result) error) error { - shard := rss[0].Target.Shard - for _, r := range t.shardResults[shard].results { +func (sr *shardResult) StreamExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + for _, r := range sr.results { if err := callback(r); err != nil { return err } } - if t.shardResults[shard].sendErr != nil { - return t.shardResults[shard].sendErr - } - return nil + return sr.sendErr } diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go index 9d5b45471b2..f56cfc29fa0 100644 --- a/go/vt/vtgate/engine/route.go +++ b/go/vt/vtgate/engine/route.go @@ -333,8 +333,20 @@ func (route *Route) StreamExecute(vcursor VCursor, bindVars map[string]*querypb. return callback(qr.Truncate(route.TruncateColumnCount)) }) } + prims := make([]StreamExecuter, 0, len(rss)) + for i, rs := range rss { + prims = append(prims, &shardRoute{ + query: route.Query, + rs: rs, + bv: bvs[i], + }) + } + ms := MergeSort{ + Primitives: prims, + OrderBy: route.OrderBy, + } - return MergeSort(vcursor, route.Query, route.OrderBy, rss, bvs, func(qr *sqltypes.Result) error { + return ms.StreamExecute(vcursor, bindVars, wantfields, func(qr *sqltypes.Result) error { return callback(qr.Truncate(route.TruncateColumnCount)) }) } diff --git a/go/vt/vtgate/engine/shard_route.go b/go/vt/vtgate/engine/shard_route.go new file mode 100644 index 00000000000..b2b31c0fb33 --- /dev/null +++ b/go/vt/vtgate/engine/shard_route.go @@ -0,0 +1,38 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package engine + +import ( + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/srvtopo" +) + +var _ StreamExecuter = (*shardRoute)(nil) + +// shardRoute is an internal primitive used by Route +// for performing merge sorts. +type shardRoute struct { + query string + rs *srvtopo.ResolvedShard + bv map[string]*querypb.BindVariable +} + +// StreamExecute performs a streaming exec. +func (sr *shardRoute) StreamExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + return vcursor.StreamExecuteMulti(sr.query, []*srvtopo.ResolvedShard{sr.rs}, []map[string]*querypb.BindVariable{sr.bv}, callback) +} diff --git a/go/vt/wrangler/vdiff.go b/go/vt/wrangler/vdiff.go index 7b85ed2c653..05e1cafe8e8 100644 --- a/go/vt/wrangler/vdiff.go +++ b/go/vt/wrangler/vdiff.go @@ -35,7 +35,6 @@ import ( tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vterrors" @@ -59,8 +58,8 @@ type vdiff struct { targetCell string tabletTypesStr string differs map[string]*tableDiffer - sources map[string]*dfParams - targets map[string]*dfParams + sources map[string]*shardStreamer + targets map[string]*shardStreamer } type tableDiffer struct { @@ -73,7 +72,7 @@ type tableDiffer struct { targetPrimitive engine.Primitive } -type dfParams struct { +type shardStreamer struct { master *topo.TabletInfo tablet *topodatapb.Tablet position mysql.Position @@ -117,17 +116,17 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC sourceCell: sourceCell, targetCell: targetCell, tabletTypesStr: tabletTypesStr, - sources: make(map[string]*dfParams), - targets: make(map[string]*dfParams), + sources: make(map[string]*shardStreamer), + targets: make(map[string]*shardStreamer), } for shard, source := range mi.sources { - df.sources[shard] = &dfParams{ + df.sources[shard] = &shardStreamer{ master: source.master, } } var oneTarget *miTarget for shard, target := range mi.targets { - df.targets[shard] = &dfParams{ + df.targets[shard] = &shardStreamer{ master: target.master, } oneTarget = target @@ -141,8 +140,7 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC if err != nil { return nil, vterrors.Wrap(err, "GetSchema") } - df.differs, err = buildVDiffPlan(ctx, oneFilter, schm) - if err != nil { + if err = df.buildVDiffPlan(ctx, oneFilter, schm); err != nil { return nil, vterrors.Wrap(err, "buildVDiffPlan") } if err := df.selectTablets(ctx, healthcheckTopologyRefresh, healthcheckRetryDelay, healthcheckTimeout); err != nil { @@ -161,21 +159,19 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC if err := df.stopTargets(ctx); err != nil { return nil, vterrors.Wrap(err, "stopTargets") } - sourceReader, err := df.startQueryStreams(ctx, df.mi.sourceKeyspace, df.sources, td.sourceExpression, filteredReplicationWaitTime) - if err != nil { + if err := df.startQueryStreams(ctx, df.mi.sourceKeyspace, df.sources, td.sourceExpression, filteredReplicationWaitTime); err != nil { return nil, vterrors.Wrap(err, "startQueryStreams(sources)") } if err := df.syncTargets(ctx, filteredReplicationWaitTime); err != nil { return nil, vterrors.Wrap(err, "syncTargets") } - targetReader, err := df.startQueryStreams(ctx, df.mi.targetKeyspace, df.targets, td.targetExpression, filteredReplicationWaitTime) - if err != nil { + if err := df.startQueryStreams(ctx, df.mi.targetKeyspace, df.targets, td.targetExpression, filteredReplicationWaitTime); err != nil { return nil, vterrors.Wrap(err, "startQueryStreams(targets)") } if err := df.restartTargets(ctx); err != nil { return nil, vterrors.Wrap(err, "restartTargets") } - dr, err := td.diff(ctx, df.mi.wr, sourceReader, targetReader) + dr, err := td.diff(ctx, df.mi.wr) if err != nil { return nil, vterrors.Wrap(err, "diff") } @@ -185,12 +181,12 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC return diffReports, nil } -func buildVDiffPlan(ctx context.Context, filter *binlogdatapb.Filter, schm *tabletmanagerdatapb.SchemaDefinition) (map[string]*tableDiffer, error) { - differs := make(map[string]*tableDiffer) +func (df *vdiff) buildVDiffPlan(ctx context.Context, filter *binlogdatapb.Filter, schm *tabletmanagerdatapb.SchemaDefinition) error { + df.differs = make(map[string]*tableDiffer) for _, table := range schm.TableDefinitions { rule, err := vreplication.MatchTable(table.Name, filter) if err != nil { - return nil, err + return err } if rule == nil { continue @@ -201,15 +197,15 @@ func buildVDiffPlan(ctx context.Context, filter *binlogdatapb.Filter, schm *tabl buf.Myprintf("select * from %v", sqlparser.NewTableIdent(table.Name)) query = buf.String() } - differs[table.Name], err = buildDifferPlan(table, query) + df.differs[table.Name], err = df.buildTablePlan(table, query) if err != nil { - return nil, err + return err } } - return differs, nil + return nil } -func buildDifferPlan(table *tabletmanagerdatapb.TableDefinition, query string) (*tableDiffer, error) { +func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, query string) (*tableDiffer, error) { statement, err := sqlparser.Parse(query) if err != nil { return nil, err @@ -320,8 +316,8 @@ func buildDifferPlan(table *tabletmanagerdatapb.TableDefinition, query string) ( td.sourceExpression = sqlparser.String(sourceSelect) td.targetExpression = sqlparser.String(targetSelect) - td.sourcePrimitive = newMergeSorter(td.comparePKs) - td.targetPrimitive = newMergeSorter(td.comparePKs) + td.sourcePrimitive = newMergeSorter(df.sources, td.comparePKs) + td.targetPrimitive = newMergeSorter(df.targets, td.comparePKs) if len(aggregates) != 0 { td.sourcePrimitive = &engine.OrderedAggregate{ Aggregates: aggregates, @@ -333,6 +329,21 @@ func buildDifferPlan(table *tabletmanagerdatapb.TableDefinition, query string) ( return td, nil } +func newMergeSorter(participants map[string]*shardStreamer, comparePKs []int) *engine.MergeSort { + prims := make([]engine.StreamExecuter, 0, len(participants)) + for _, participant := range participants { + prims = append(prims, participant) + } + ob := make([]engine.OrderbyParams, 0, len(comparePKs)) + for _, cpk := range comparePKs { + ob = append(ob, engine.OrderbyParams{Col: cpk}) + } + return &engine.MergeSort{ + Primitives: prims, + OrderBy: ob, + } +} + func (df *vdiff) selectTablets(ctx context.Context, healthcheckTopologyRefresh, healthcheckRetryDelay, healthcheckTimeout time.Duration) error { var wg sync.WaitGroup var err1, err2 error @@ -341,7 +352,7 @@ func (df *vdiff) selectTablets(ctx context.Context, healthcheckTopologyRefresh, wg.Add(1) go func() { defer wg.Done() - err1 = df.forAll(df.sources, func(shard string, source *dfParams) error { + err1 = df.forAll(df.sources, func(shard string, source *shardStreamer) error { tp, err := discovery.NewTabletPicker(ctx, df.mi.wr.ts, df.sourceCell, df.mi.sourceKeyspace, shard, df.tabletTypesStr, healthcheckTopologyRefresh, healthcheckRetryDelay, healthcheckTimeout) if err != nil { return err @@ -360,7 +371,7 @@ func (df *vdiff) selectTablets(ctx context.Context, healthcheckTopologyRefresh, wg.Add(1) go func() { defer wg.Done() - err2 = df.forAll(df.targets, func(shard string, target *dfParams) error { + err2 = df.forAll(df.targets, func(shard string, target *shardStreamer) error { tp, err := discovery.NewTabletPicker(ctx, df.mi.wr.ts, df.targetCell, df.mi.targetKeyspace, shard, df.tabletTypesStr, healthcheckTopologyRefresh, healthcheckRetryDelay, healthcheckTimeout) if err != nil { return err @@ -386,7 +397,7 @@ func (df *vdiff) selectTablets(ctx context.Context, healthcheckTopologyRefresh, func (df *vdiff) stopTargets(ctx context.Context) error { var mu sync.Mutex - err := df.forAll(df.targets, func(shard string, target *dfParams) error { + err := df.forAll(df.targets, func(shard string, target *shardStreamer) error { query := fmt.Sprintf("update _vt.vreplication set state='Stopped', message='for vdiff' where db_name=%s and workflow=%s", encodeString(target.master.DbName()), encodeString(df.mi.workflow)) _, err := df.mi.wr.tmc.VReplicationExec(ctx, target.master.Tablet, query) if err != nil { @@ -431,10 +442,10 @@ func (df *vdiff) stopTargets(ctx context.Context) error { return nil } -func (df *vdiff) startQueryStreams(ctx context.Context, keyspace string, participants map[string]*dfParams, query string, filteredReplicationWaitTime time.Duration) (*resultReader, error) { +func (df *vdiff) startQueryStreams(ctx context.Context, keyspace string, participants map[string]*shardStreamer, query string, filteredReplicationWaitTime time.Duration) error { waitCtx, cancel := context.WithTimeout(ctx, filteredReplicationWaitTime) defer cancel() - err := df.forAll(participants, func(shard string, participant *dfParams) error { + return df.forAll(participants, func(shard string, participant *shardStreamer) error { // Iteration for each participant. if err := df.mi.wr.tmc.WaitForPosition(waitCtx, participant.tablet, mysql.EncodePosition(participant.position)); err != nil { return vterrors.Wrapf(err, "WaitForPosition for tablet %v", topoproto.TabletAliasString(participant.tablet.Alias)) @@ -455,10 +466,6 @@ func (df *vdiff) startQueryStreams(ctx context.Context, keyspace string, partici participant.snapshotPosition = gtid return nil }) - if err != nil { - return nil, err - } - return newResultReader(ctx, participants), nil } // streamOne is called as a goroutine, and communicates its results through channels. @@ -466,7 +473,7 @@ func (df *vdiff) startQueryStreams(ctx context.Context, keyspace string, partici // Then it streams results to participant.result. // Before returning, it sets participant.err, and closes all channels. // If any channel is closed, then participant.err can be checked if there was an error. -func (df *vdiff) streamOne(ctx context.Context, keyspace, shard string, participant *dfParams, query string, gtidch chan string) { +func (df *vdiff) streamOne(ctx context.Context, keyspace, shard string, participant *shardStreamer, query string, gtidch chan string) { defer close(participant.result) defer close(gtidch) @@ -485,7 +492,7 @@ func (df *vdiff) streamOne(ctx context.Context, keyspace, shard string, particip TabletType: participant.tablet.Type, } var fields []*querypb.Field - err = conn.VStreamResults(ctx, target, query, func(vrs *binlogdatapb.VStreamResultsResponse) error { + return conn.VStreamResults(ctx, target, query, func(vrs *binlogdatapb.VStreamResultsResponse) error { if vrs.Fields != nil { fields = vrs.Fields gtidch <- vrs.Gtid @@ -506,7 +513,6 @@ func (df *vdiff) streamOne(ctx context.Context, keyspace, shard string, particip } return nil }) - return err }() } @@ -529,7 +535,7 @@ func (df *vdiff) syncTargets(ctx context.Context, filteredReplicationWaitTime ti return err } - err = df.forAll(df.targets, func(shard string, target *dfParams) error { + err = df.forAll(df.targets, func(shard string, target *shardStreamer) error { pos, err := df.mi.wr.tmc.MasterPosition(ctx, target.master.Tablet) if err != nil { return err @@ -545,19 +551,19 @@ func (df *vdiff) syncTargets(ctx context.Context, filteredReplicationWaitTime ti } func (df *vdiff) restartTargets(ctx context.Context) error { - return df.forAll(df.targets, func(shard string, target *dfParams) error { + return df.forAll(df.targets, func(shard string, target *shardStreamer) error { query := fmt.Sprintf("update _vt.vreplication set state='Running', message='', stop_pos='' where db_name=%s and workflow=%s", encodeString(target.master.DbName()), encodeString(df.mi.workflow)) _, err := df.mi.wr.tmc.VReplicationExec(ctx, target.master.Tablet, query) return err }) } -func (df *vdiff) forAll(participants map[string]*dfParams, f func(string, *dfParams) error) error { +func (df *vdiff) forAll(participants map[string]*shardStreamer, f func(string, *shardStreamer) error) error { var wg sync.WaitGroup allErrors := &concurrency.AllErrorRecorder{} for shard, participant := range participants { wg.Add(1) - go func(shard string, participant *dfParams) { + go func(shard string, participant *shardStreamer) { defer wg.Done() if err := f(shard, participant); err != nil { @@ -579,11 +585,12 @@ type primitiveExecutor struct { err error } -func newPrimitiveExecutor(ctx context.Context, vcursor engine.VCursor, prim engine.Primitive) *primitiveExecutor { +func newPrimitiveExecutor(ctx context.Context, prim engine.Primitive) *primitiveExecutor { pe := &primitiveExecutor{ prim: prim, resultch: make(chan *sqltypes.Result, 1), } + vcursor := &contextVCursor{ctx: ctx} go func() { defer close(pe.resultch) pe.err = pe.prim.StreamExecute(vcursor, make(map[string]*querypb.BindVariable), false, func(qr *sqltypes.Result) error { @@ -627,80 +634,23 @@ func (pe *primitiveExecutor) drain(ctx context.Context) (int, error) { } //----------------------------------------------------------------- -// mergeSorter - -var _ engine.Primitive = (*mergeSorter)(nil) - -// mergeSorter performs a merge-sorted read from the participants. -type mergeSorter struct { - engine.Primitive - orderBy []engine.OrderbyParams -} - -func newMergeSorter(comparePKs []int) *mergeSorter { - ob := make([]engine.OrderbyParams, 0, len(comparePKs)) - for _, col := range comparePKs { - ob = append(ob, engine.OrderbyParams{Col: col}) - } - return &mergeSorter{ - orderBy: ob, - } -} +// shardStreamer -func (ms *mergeSorter) StreamExecute(vcursor engine.VCursor, bindVars map[string]*querypb.BindVariable, wantields bool, callback func(*sqltypes.Result) error) error { - rr, ok := vcursor.(*resultReader) - if !ok { - return fmt.Errorf("internal error: vcursor is not a resultReader: %T", vcursor) - } - rss := make([]*srvtopo.ResolvedShard, 0, len(rr.participants)) - bvs := make([]map[string]*querypb.BindVariable, 0, len(rr.participants)) - for shard := range rr.participants { - rss = append(rss, &srvtopo.ResolvedShard{ - Target: &querypb.Target{ - Shard: shard, - }, - }) - bvs = append(bvs, bindVars) - } - return engine.MergeSort(vcursor, "", ms.orderBy, rss, bvs, callback) -} - -//----------------------------------------------------------------- -// resultReader - -// resultReader acts as a VCursor for the wrapping primitives. -type resultReader struct { - engine.VCursor - ctx context.Context - participants map[string]*dfParams -} - -func newResultReader(ctx context.Context, participants map[string]*dfParams) *resultReader { - return &resultReader{ - ctx: ctx, - participants: participants, - } -} - -func (rr *resultReader) Context() context.Context { - return rr.ctx -} - -func (rr *resultReader) StreamExecuteMulti(query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, callback func(reply *sqltypes.Result) error) error { - for result := range rr.participants[rss[0].Target.Shard].result { +func (sm *shardStreamer) StreamExecute(vcursor engine.VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { + for result := range sm.result { if err := callback(result); err != nil { return err } } - return rr.participants[rss[0].Target.Shard].err + return sm.err } //----------------------------------------------------------------- // tableDiffer -func (td *tableDiffer) diff(ctx context.Context, wr *Wrangler, sourceReader, targetReader *resultReader) (*DiffReport, error) { - sourceExecutor := newPrimitiveExecutor(ctx, sourceReader, td.sourcePrimitive) - targetExecutor := newPrimitiveExecutor(ctx, targetReader, td.targetPrimitive) +func (td *tableDiffer) diff(ctx context.Context, wr *Wrangler) (*DiffReport, error) { + sourceExecutor := newPrimitiveExecutor(ctx, td.sourcePrimitive) + targetExecutor := newPrimitiveExecutor(ctx, td.targetPrimitive) dr := &DiffReport{} var sourceRow, targetRow []sqltypes.Value var err error @@ -807,6 +757,23 @@ func (td *tableDiffer) compare(sourceRow, targetRow []sqltypes.Value, cols []int return 0, nil } +//----------------------------------------------------------------- +// contextVCursor + +// contextVCursor satisfies VCursor, but only implements Context(). +// MergeSort only requires Context to be implemented. +type contextVCursor struct { + engine.VCursor + ctx context.Context +} + +func (vc *contextVCursor) Context() context.Context { + return vc.ctx +} + +//----------------------------------------------------------------- +// Utility functions + func removeKeyrange(where *sqlparser.Where) *sqlparser.Where { if where == nil { return nil diff --git a/go/vt/wrangler/vdiff_test.go b/go/vt/wrangler/vdiff_test.go index b2f92131c31..6a04f000562 100644 --- a/go/vt/wrangler/vdiff_test.go +++ b/go/vt/wrangler/vdiff_test.go @@ -74,8 +74,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { input: &binlogdatapb.Rule{ @@ -89,8 +89,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { input: &binlogdatapb.Rule{ @@ -104,8 +104,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { input: &binlogdatapb.Rule{ @@ -119,8 +119,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c2, c1 from t1 order by c1 asc", compareCols: []int{0, -1}, comparePKs: []int{1}, - sourcePrimitive: newMergeSorter([]int{1}), - targetPrimitive: newMergeSorter([]int{1}), + sourcePrimitive: newMergeSorter(nil, []int{1}), + targetPrimitive: newMergeSorter(nil, []int{1}), }, }, { input: &binlogdatapb.Rule{ @@ -134,8 +134,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // non-pk text column. @@ -150,8 +150,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, textcol, weight_string(textcol) from nonpktext order by c1 asc", compareCols: []int{-1, 2}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // non-pk text column, different order. @@ -166,8 +166,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select textcol, c1, weight_string(textcol) from nonpktext order by c1 asc", compareCols: []int{2, -1}, comparePKs: []int{1}, - sourcePrimitive: newMergeSorter([]int{1}), - targetPrimitive: newMergeSorter([]int{1}), + sourcePrimitive: newMergeSorter(nil, []int{1}), + targetPrimitive: newMergeSorter(nil, []int{1}), }, }, { // pk text column. @@ -182,8 +182,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select textcol, c2, weight_string(textcol) from pktext order by textcol asc", compareCols: []int{-1, 1}, comparePKs: []int{2}, - sourcePrimitive: newMergeSorter([]int{2}), - targetPrimitive: newMergeSorter([]int{2}), + sourcePrimitive: newMergeSorter(nil, []int{2}), + targetPrimitive: newMergeSorter(nil, []int{2}), }, }, { // pk text column, different order. @@ -198,8 +198,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c2, textcol, weight_string(textcol) from pktext order by textcol asc", compareCols: []int{0, -1}, comparePKs: []int{2}, - sourcePrimitive: newMergeSorter([]int{2}), - targetPrimitive: newMergeSorter([]int{2}), + sourcePrimitive: newMergeSorter(nil, []int{2}), + targetPrimitive: newMergeSorter(nil, []int{2}), }, }, { // text column as expression. @@ -214,8 +214,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c2, textcol, weight_string(textcol) from pktext order by textcol asc", compareCols: []int{0, -1}, comparePKs: []int{2}, - sourcePrimitive: newMergeSorter([]int{2}), - targetPrimitive: newMergeSorter([]int{2}), + sourcePrimitive: newMergeSorter(nil, []int{2}), + targetPrimitive: newMergeSorter(nil, []int{2}), }, }, { input: &binlogdatapb.Rule{ @@ -228,8 +228,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from multipk order by c1 asc, c2 asc", compareCols: []int{-1, -1}, comparePKs: []int{0, 1}, - sourcePrimitive: newMergeSorter([]int{0, 1}), - targetPrimitive: newMergeSorter([]int{0, 1}), + sourcePrimitive: newMergeSorter(nil, []int{0, 1}), + targetPrimitive: newMergeSorter(nil, []int{0, 1}), }, }, { // in_keyrange @@ -244,8 +244,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // in_keyrange on RHS of AND. @@ -261,8 +261,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // in_keyrange on LHS of AND. @@ -278,8 +278,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // in_keyrange on cascaded AND expression @@ -295,8 +295,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // in_keyrange parenthesized @@ -312,8 +312,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // group by @@ -328,8 +328,8 @@ func TestVDiffPlanSuccess(t *testing.T) { targetExpression: "select c1, c2 from t1 order by c1 asc", compareCols: []int{-1, 1}, comparePKs: []int{0}, - sourcePrimitive: newMergeSorter([]int{0}), - targetPrimitive: newMergeSorter([]int{0}), + sourcePrimitive: newMergeSorter(nil, []int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }, { // aggregations @@ -353,17 +353,18 @@ func TestVDiffPlanSuccess(t *testing.T) { Col: 3, }}, Keys: []int{0}, - Input: newMergeSorter([]int{0}), + Input: newMergeSorter(nil, []int{0}), }, - targetPrimitive: newMergeSorter([]int{0}), + targetPrimitive: newMergeSorter(nil, []int{0}), }, }} for _, tcase := range testcases { filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{tcase.input}} - differs, err := buildVDiffPlan(context.Background(), filter, schm) + df := &vdiff{} + err := df.buildVDiffPlan(context.Background(), filter, schm) require.NoError(t, err, tcase.input) - require.Equal(t, 1, len(differs), tcase.input) - assert.Equal(t, tcase.td, differs[tcase.table], tcase.input) + require.Equal(t, 1, len(df.differs), tcase.input) + assert.Equal(t, tcase.td, df.differs[tcase.table], tcase.input) } } @@ -413,7 +414,8 @@ func TestVDiffPlanFailure(t *testing.T) { }} for _, tcase := range testcases { filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{tcase.input}} - _, err := buildVDiffPlan(context.Background(), filter, schm) + df := &vdiff{} + err := df.buildVDiffPlan(context.Background(), filter, schm) assert.EqualError(t, err, tcase.err, tcase.input) } } From 44ddea8d518d70eeea9cd1269d45449e6e0c41be Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 1 Jan 2020 14:00:35 -0800 Subject: [PATCH 160/825] vdiff: document vdiff.go Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/engine/merge_sort.go | 2 - go/vt/vtgate/engine/route.go | 3 +- go/vt/wrangler/vdiff.go | 83 ++++++++++++++++++++++++++++--- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/go/vt/vtgate/engine/merge_sort.go b/go/vt/vtgate/engine/merge_sort.go index 61a5b4e4e1a..5c9dde9e3ff 100644 --- a/go/vt/vtgate/engine/merge_sort.go +++ b/go/vt/vtgate/engine/merge_sort.go @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -// This file has the logic for performing merge-sorts of scatter queries. - package engine import ( diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go index f56cfc29fa0..48aef3a2006 100644 --- a/go/vt/vtgate/engine/route.go +++ b/go/vt/vtgate/engine/route.go @@ -333,6 +333,8 @@ func (route *Route) StreamExecute(vcursor VCursor, bindVars map[string]*querypb. return callback(qr.Truncate(route.TruncateColumnCount)) }) } + + // There is an order by. We have to merge-sort. prims := make([]StreamExecuter, 0, len(rss)) for i, rs := range rss { prims = append(prims, &shardRoute{ @@ -345,7 +347,6 @@ func (route *Route) StreamExecute(vcursor VCursor, bindVars map[string]*querypb. Primitives: prims, OrderBy: route.OrderBy, } - return ms.StreamExecute(vcursor, bindVars, wantfields, func(qr *sqltypes.Result) error { return callback(qr.Truncate(route.TruncateColumnCount)) }) diff --git a/go/vt/wrangler/vdiff.go b/go/vt/wrangler/vdiff.go index 05e1cafe8e8..b5b9ed52198 100644 --- a/go/vt/wrangler/vdiff.go +++ b/go/vt/wrangler/vdiff.go @@ -52,26 +52,50 @@ type DiffReport struct { ExtraRowsTarget int } +// vdiff contains the metadata for performing vdiff for one workflow. type vdiff struct { mi *migrater sourceCell string targetCell string tabletTypesStr string - differs map[string]*tableDiffer - sources map[string]*shardStreamer - targets map[string]*shardStreamer + + // differs uses the target table name for its key. + differs map[string]*tableDiffer + + // The key for sources and targets is the shard name. + // The source and target keyspaces are pulled from mi. + sources map[string]*shardStreamer + targets map[string]*shardStreamer } +// tableDiffer performs a diff for one table in the workflow. type tableDiffer struct { - targetTable string + targetTable string + // sourceExpression and targetExpression are select queries. sourceExpression string targetExpression string - compareCols []int - comparePKs []int - sourcePrimitive engine.Primitive - targetPrimitive engine.Primitive + + // compareCols is the list of non-pk columns to compare. + // If the value is -1, it's a pk column and should not be + // compared. + compareCols []int + // comparePKs is the list of pk columns to compare. The logic + // for comparing pk columns is different from compareCols + comparePKs []int + + // source Primitive and targetPrimitive are used for streaming + // results from source and target. + sourcePrimitive engine.Primitive + targetPrimitive engine.Primitive } +// shardStreamer streams rows from one shard. This works for +// the source as well as the target. +// shardStreamer satisfies engine.StreamExecuter, and can be +// added to Primitives of engine.MergeSort. +// shardStreamer is a member of vdiff, and gets reused by +// every tableDiffer. A new result channel gets instantiated +// for every tableDiffer iteration. type shardStreamer struct { master *topo.TabletInfo tablet *topodatapb.Tablet @@ -84,6 +108,7 @@ type shardStreamer struct { // VDiff reports differences between the sources and targets of a vreplication workflow. func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceCell, targetCell, tabletTypesStr string, filteredReplicationWaitTime, healthcheckTopologyRefresh, healthcheckRetryDelay, healthcheckTimeout time.Duration) (map[string]*DiffReport, error) { + // Assign defaults to sourceCell and targetCell if not specified. if sourceCell == "" && targetCell == "" { cells, err := wr.ts.GetCellInfoNames(ctx) if err != nil { @@ -102,6 +127,8 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC if targetCell == "" { targetCell = sourceCell } + + // Reuse migrater code to fetch and validate initial metadata about the workflow. mi, err := wr.buildMigrater(ctx, targetKeyspace, workflow) if err != nil { wr.Logger().Errorf("buildMigrater: %v", err) @@ -111,6 +138,8 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC mi.wr.Logger().Errorf("validate: %v", err) return nil, err } + + // Initialize vdiff. df := &vdiff{ mi: mi, sourceCell: sourceCell, @@ -151,26 +180,37 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC wr.Logger().Errorf("Could not restart workflow %s: %v, please restart it manually", workflow, err) } }(ctx) + + // Perform the diffs. + // We need a cancelable context to abort all running streams + // if one stream returns an error. ctx, cancel := context.WithCancel(ctx) defer cancel() + // TODO(sougou): parallelize diffReports := make(map[string]*DiffReport) for table, td := range df.differs { + // Stop the targets and record their source positions. if err := df.stopTargets(ctx); err != nil { return nil, vterrors.Wrap(err, "stopTargets") } + // Make sure all sources are past the target's positions and start a query stream that records the current source positions. if err := df.startQueryStreams(ctx, df.mi.sourceKeyspace, df.sources, td.sourceExpression, filteredReplicationWaitTime); err != nil { return nil, vterrors.Wrap(err, "startQueryStreams(sources)") } + // Fast forward the targets to the newly recorded source positions. if err := df.syncTargets(ctx, filteredReplicationWaitTime); err != nil { return nil, vterrors.Wrap(err, "syncTargets") } + // Sources and targets are in sync. Start query streams on the targets. if err := df.startQueryStreams(ctx, df.mi.targetKeyspace, df.targets, td.targetExpression, filteredReplicationWaitTime); err != nil { return nil, vterrors.Wrap(err, "startQueryStreams(targets)") } + // Now that queries are running, target vreplication streams can be restarted. if err := df.restartTargets(ctx); err != nil { return nil, vterrors.Wrap(err, "restartTargets") } + // Perform the diff of source and target streams. dr, err := td.diff(ctx, df.mi.wr) if err != nil { return nil, vterrors.Wrap(err, "diff") @@ -181,6 +221,7 @@ func (wr *Wrangler) VDiff(ctx context.Context, targetKeyspace, workflow, sourceC return diffReports, nil } +// buildVDiffPlan builds all the differs. func (df *vdiff) buildVDiffPlan(ctx context.Context, filter *binlogdatapb.Filter, schm *tabletmanagerdatapb.SchemaDefinition) error { df.differs = make(map[string]*tableDiffer) for _, table := range schm.TableDefinitions { @@ -205,6 +246,7 @@ func (df *vdiff) buildVDiffPlan(ctx context.Context, filter *binlogdatapb.Filter return nil } +// buildTablePlan builds one tableDiffer. func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, query string) (*tableDiffer, error) { statement, err := sqlparser.Parse(query) if err != nil { @@ -219,10 +261,12 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer } sourceSelect := &sqlparser.Select{} targetSelect := &sqlparser.Select{} + // aggregates contains the list if Aggregate functions, if any. var aggregates []engine.AggregateParams for _, selExpr := range sel.SelectExprs { switch selExpr := selExpr.(type) { case *sqlparser.StarExpr: + // If it's a '*' expression, expand column list from the schema. for _, fld := range table.Fields { aliased := &sqlparser.AliasedExpr{Expr: &sqlparser.ColName{Name: sqlparser.NewColIdent(fld.Name)}} sourceSelect.SelectExprs = append(sourceSelect.SelectExprs, aliased) @@ -239,6 +283,7 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer return nil, fmt.Errorf("expression needs an alias: %v", sqlparser.String(selExpr)) } } + // If the input was "select a as b", then source will use "a" and target will use "b". sourceSelect.SelectExprs = append(sourceSelect.SelectExprs, selExpr) targetSelect.SelectExprs = append(targetSelect.SelectExprs, &sqlparser.AliasedExpr{Expr: targetCol}) @@ -261,6 +306,7 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer fields[strings.ToLower(field.Name)] = field.Type } + // Start with adding all columns for comparison. td.compareCols = make([]int, len(sourceSelect.SelectExprs)) for i := range td.compareCols { colname := targetSelect.SelectExprs[i].(*sqlparser.AliasedExpr).Expr.(*sqlparser.ColName).Name.Lowered() @@ -270,13 +316,17 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer } td.compareCols[i] = i if sqltypes.IsText(typ) { + // For text columns, we need to additionally pull their weight string values for lexical comparisons. sourceSelect.SelectExprs = append(sourceSelect.SelectExprs, wrapWeightString(sourceSelect.SelectExprs[i])) targetSelect.SelectExprs = append(targetSelect.SelectExprs, wrapWeightString(targetSelect.SelectExprs[i])) + // Update the column number to point at the weight_string column instead. td.compareCols[i] = len(sourceSelect.SelectExprs) - 1 } } sourceSelect.From = sel.From + // The target table name should the one that matched the rule. + // It can be different from the source table. targetSelect.From = sqlparser.TableExprs{ &sqlparser.AliasedTableExpr{ Expr: &sqlparser.TableName{ @@ -307,10 +357,13 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer Direction: sqlparser.AscScr, }) } + // Remove in_keyrange. It's not understood by mysql. sourceSelect.Where = removeKeyrange(sel.Where) + // The source should also perform the group by. sourceSelect.GroupBy = sel.GroupBy sourceSelect.OrderBy = orderby + // The target should perform the order by, but not the group by. targetSelect.OrderBy = orderby td.sourceExpression = sqlparser.String(sourceSelect) @@ -318,6 +371,8 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer td.sourcePrimitive = newMergeSorter(df.sources, td.comparePKs) td.targetPrimitive = newMergeSorter(df.targets, td.comparePKs) + // If there were aggregate expressions, we have to re-aggregate + // the results, which engine.OrderedAggregate can do. if len(aggregates) != 0 { td.sourcePrimitive = &engine.OrderedAggregate{ Aggregates: aggregates, @@ -329,6 +384,7 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer return td, nil } +// newMergeSorter creates an engine.MergeSort based on the shard streamers and pk columns. func newMergeSorter(participants map[string]*shardStreamer, comparePKs []int) *engine.MergeSort { prims := make([]engine.StreamExecuter, 0, len(participants)) for _, participant := range participants { @@ -344,6 +400,7 @@ func newMergeSorter(participants map[string]*shardStreamer, comparePKs []int) *e } } +// selectTablets selects the tablets that will be used for the diff. func (df *vdiff) selectTablets(ctx context.Context, healthcheckTopologyRefresh, healthcheckRetryDelay, healthcheckTimeout time.Duration) error { var wg sync.WaitGroup var err1, err2 error @@ -394,6 +451,7 @@ func (df *vdiff) selectTablets(ctx context.Context, healthcheckTopologyRefresh, return err2 } +// stopTargets stops all the targets and records their source positions. func (df *vdiff) stopTargets(ctx context.Context) error { var mu sync.Mutex @@ -442,6 +500,9 @@ func (df *vdiff) stopTargets(ctx context.Context) error { return nil } +// starQueryStreams makes sure the sources are past the target's positions, starts the query streams, +// and records the snapshot position of the query. It creates a result channel which StreamExecute +// will use to serve rows. func (df *vdiff) startQueryStreams(ctx context.Context, keyspace string, participants map[string]*shardStreamer, query string, filteredReplicationWaitTime time.Duration) error { waitCtx, cancel := context.WithTimeout(ctx, filteredReplicationWaitTime) defer cancel() @@ -473,6 +534,7 @@ func (df *vdiff) startQueryStreams(ctx context.Context, keyspace string, partici // Then it streams results to participant.result. // Before returning, it sets participant.err, and closes all channels. // If any channel is closed, then participant.err can be checked if there was an error. +// The shardStreamer's StreamExecute consumes the result channel. func (df *vdiff) streamOne(ctx context.Context, keyspace, shard string, participant *shardStreamer, query string, gtidch chan string) { defer close(participant.result) defer close(gtidch) @@ -516,6 +578,8 @@ func (df *vdiff) streamOne(ctx context.Context, keyspace, shard string, particip }() } +// syncTargets fast-forwards the vreplication to the source snapshot positons +// and waits for the selected tablets to catch up to that point. func (df *vdiff) syncTargets(ctx context.Context, filteredReplicationWaitTime time.Duration) error { waitCtx, cancel := context.WithTimeout(ctx, filteredReplicationWaitTime) defer cancel() @@ -550,6 +614,7 @@ func (df *vdiff) syncTargets(ctx context.Context, filteredReplicationWaitTime ti return err } +// restartTargets restarts the stopped target vreplication streams. func (df *vdiff) restartTargets(ctx context.Context) error { return df.forAll(df.targets, func(shard string, target *shardStreamer) error { query := fmt.Sprintf("update _vt.vreplication set state='Running', message='', stop_pos='' where db_name=%s and workflow=%s", encodeString(target.master.DbName()), encodeString(df.mi.workflow)) @@ -578,6 +643,8 @@ func (df *vdiff) forAll(participants map[string]*shardStreamer, f func(string, * //----------------------------------------------------------------- // primitiveExecutor +// primitiveExecutor starts execution on the top level primitive +// and provides convenience functions for row-by-row iteration. type primitiveExecutor struct { prim engine.Primitive rows [][]sqltypes.Value From 42c8f17672b16f53ff83145152e9be02f0f35d7c Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Sat, 22 Feb 2020 22:23:09 -0800 Subject: [PATCH 161/825] Add keyword support for unique_checks Signed-off-by: Saif Alharthi --- go/vt/vtgate/executor.go | 2 +- go/vt/vtgate/executor_test.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index e2ac0b2dca8..95938c9efa1 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -669,7 +669,7 @@ func (e *Executor) handleSet(ctx context.Context, safeSession *SafeSession, sql if !ok { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for wait_timeout: %T", v) } - case "sql_mode", "net_write_timeout", "net_read_timeout", "lc_messages", "collation_connection", "foreign_key_checks": + case "sql_mode", "net_write_timeout", "net_read_timeout", "lc_messages", "collation_connection", "foreign_key_checks", "unique_checks": log.Warningf("Ignored inapplicable SET %v = %v", k, v) warnings.Add("IgnoredSet", 1) case "charset", "names": diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index f1709226c47..714c061d3d3 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -461,6 +461,9 @@ func TestExecutorSet(t *testing.T) { }, { in: "set foreign_key_checks = 0", out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set unique_checks = 0", + out: &vtgatepb.Session{Autocommit: true}, }, { in: "set skip_query_plan_cache = 1", out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{SkipQueryPlanCache: true}}, From e31b245130a519dc63f9afff66a902bfe7e28c84 Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Mon, 24 Feb 2020 10:48:30 +0530 Subject: [PATCH 162/825] removed Get dependencies and eatmydata from cluster_vtctld_web.yaml . Signed-off-by: pradip parmar --- .github/workflows/cluster_vtctl_web.yml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.github/workflows/cluster_vtctl_web.yml b/.github/workflows/cluster_vtctl_web.yml index e28143e4b67..dc6204b71c2 100644 --- a/.github/workflows/cluster_vtctl_web.yml +++ b/.github/workflows/cluster_vtctl_web.yml @@ -15,22 +15,8 @@ jobs: - name: Check out code uses: actions/checkout@v2 - - name: Get dependencies - run: | - sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget eatmydata - sudo service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - - name: Run make tools - run: | - make tools - - name: Run vtctl web run: | # Running web test inside docker - eatmydata -- go run test.go -docker=true -print-log -shard 25 + go run test.go -docker=true -print-log -shard 25 From 154b86e043c497e694d7fdb682bc936d99e1c7c7 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Mon, 24 Feb 2020 12:25:56 +0530 Subject: [PATCH 163/825] bump netty-handler to 4.1.45.Final and tcnative-boring-ssl to 2.0.28.Final Signed-off-by: Harshit Gangal --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index e580d47bfc5..762e4034b49 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -70,8 +70,8 @@ 1.16.0 - 4.1.30.Final - 2.0.17.Final + 4.1.45.Final + 2.0.28.Final 3.6.1 3.6.1 From 8b257f896c97e9a84a329621ad5a1b64dcd99120 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Mon, 24 Feb 2020 15:36:29 +0530 Subject: [PATCH 164/825] Addressed Review Comments Signed-off-by: Harshit Gangal --- .../multi-output/deletesharded-output.txt | 16 ++-- .../multi-output/updatesharded-output.txt | 12 ++- go/vt/vtgate/autocommit_test.go | 4 +- go/vt/vtgate/engine/delete.go | 37 ++------ go/vt/vtgate/engine/delete_test.go | 6 +- go/vt/vtgate/engine/update.go | 55 ++---------- go/vt/vtgate/engine/update_test.go | 86 +++++++++++++++++-- go/vt/vtgate/executor.go | 3 + go/vt/vtgate/executor_dml_test.go | 16 ++-- go/vt/vtgate/executor_test.go | 1 + go/vt/vtgate/planbuilder/delete.go | 4 +- go/vt/vtgate/planbuilder/dml.go | 28 +++--- go/vt/vtgate/planbuilder/route.go | 10 --- 13 files changed, 139 insertions(+), 139 deletions(-) diff --git a/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt b/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt index 1ca8435a681..554453ec54a 100644 --- a/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt @@ -17,12 +17,12 @@ delete from music_extra where id=1 and extra='abc' delete from user where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: select name from user where id = 1 limit 10001 for update -2 ks_sharded/80-c0: begin -2 ks_sharded/80-c0: delete from name_user_map where (name = 'name_val_1' and user_id = 1) /* vtgate:: keyspace_id:a6e89b54b129c33051b76db219595660 */ +1 ks_sharded/-40: select id, name from user where id = 1 limit 10001 for update +2 ks_sharded/40-80: begin +2 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ 3 ks_sharded/-40: delete from user where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 4 ks_sharded/-40: commit -5 ks_sharded/80-c0: commit +5 ks_sharded/40-80: commit ---------------------------------------------------------------------- delete from user where name='billy' @@ -30,14 +30,14 @@ delete from user where name='billy' 1 ks_sharded/c0-: begin 1 ks_sharded/c0-: select user_id from name_user_map where name = 'billy' limit 10001 2 ks_sharded/-40: begin -2 ks_sharded/-40: select name from user where name = 'billy' limit 10001 for update -3 ks_sharded/80-c0: begin -3 ks_sharded/80-c0: delete from name_user_map where (name = 'name_val_1' and user_id = 1) /* vtgate:: keyspace_id:a6e89b54b129c33051b76db219595660 */ +2 ks_sharded/-40: select id, name from user where name = 'billy' limit 10001 for update +3 ks_sharded/40-80: begin +3 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ 4 ks_sharded/-40: select id from user where name = 'billy' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 4 ks_sharded/-40: delete from user where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 5 ks_sharded/c0-: commit 6 ks_sharded/-40: commit -7 ks_sharded/80-c0: commit +7 ks_sharded/40-80: commit ---------------------------------------------------------------------- delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra='abc' diff --git a/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt b/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt index 5bc555eefdc..7f143656dbd 100644 --- a/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt @@ -28,13 +28,13 @@ update user set name='alicia' where id=1 1 ks_sharded/-40: begin 1 ks_sharded/-40: select id, name from user where id = 1 limit 10001 for update -2 ks_sharded/80-c0: begin -2 ks_sharded/80-c0: delete from name_user_map where (name = 'name_val_1' and user_id = 1) /* vtgate:: keyspace_id:a6e89b54b129c33051b76db219595660 */ +2 ks_sharded/40-80: begin +2 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ 3 ks_sharded/c0-: begin 3 ks_sharded/c0-: insert into name_user_map(name, user_id) values ('alicia', 1) /* vtgate:: keyspace_id:e2821261367fbee90bb5cf72955146c6 */ 4 ks_sharded/-40: update user set name = 'alicia' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 5 ks_sharded/-40: commit -6 ks_sharded/80-c0: commit +6 ks_sharded/40-80: commit 7 ks_sharded/c0-: commit ---------------------------------------------------------------------- @@ -44,16 +44,14 @@ update user set name='alicia' where name='alice' 1 ks_sharded/40-80: select user_id from name_user_map where name = 'alice' limit 10001 2 ks_sharded/-40: begin 2 ks_sharded/-40: select id, name from user where name = 'alice' limit 10001 for update -3 ks_sharded/80-c0: begin -3 ks_sharded/80-c0: delete from name_user_map where (name = 'name_val_1' and user_id = 1) /* vtgate:: keyspace_id:a6e89b54b129c33051b76db219595660 */ +3 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ 4 ks_sharded/c0-: begin 4 ks_sharded/c0-: insert into name_user_map(name, user_id) values ('alicia', 1) /* vtgate:: keyspace_id:e2821261367fbee90bb5cf72955146c6 */ 5 ks_sharded/-40: select id from user where name = 'alice' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 5 ks_sharded/-40: update user set name = 'alicia' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 6 ks_sharded/40-80: commit 7 ks_sharded/-40: commit -8 ks_sharded/80-c0: commit -9 ks_sharded/c0-: commit +8 ks_sharded/c0-: commit ---------------------------------------------------------------------- update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname=1 where nickname != '' diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index f7178f4f372..f908ad57b6e 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -82,7 +82,7 @@ func TestAutocommitUpdateVindexChange(t *testing.T) { executor, sbc, _, sbclookup := createExecutorEnv() sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "1|1|foo", + "\026k@\264J\272K\326|1|foo", ), }) @@ -142,7 +142,7 @@ func TestAutocommitDeleteLookup(t *testing.T) { executor, sbc1, _, sbclookup := createExecutorEnv() sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "1|1|foo", + "\026k@\264J\272K\326|1|foo", ), }) diff --git a/go/vt/vtgate/engine/delete.go b/go/vt/vtgate/engine/delete.go index 427fd0362a4..399ab9f6f2c 100644 --- a/go/vt/vtgate/engine/delete.go +++ b/go/vt/vtgate/engine/delete.go @@ -157,7 +157,7 @@ func (del *Delete) execDeleteEqual(vcursor VCursor, bindVars map[string]*querypb return &sqltypes.Result{}, nil } if del.OwnedVindexQuery != "" { - err = del.deleteVindexEntries(vcursor, bindVars, rs, ksid) + err = del.deleteVindexEntries(vcursor, bindVars, []*srvtopo.ResolvedShard{rs}) if err != nil { return nil, vterrors.Wrap(err, "execDeleteEqual") } @@ -166,31 +166,10 @@ func (del *Delete) execDeleteEqual(vcursor VCursor, bindVars map[string]*querypb return execShard(vcursor, rewritten, bindVars, rs, true /* isDML */, true /* canAutocommit */) } -func (del *Delete) deleteVindexEntries(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard, ksid []byte) error { - result, err := execShard(vcursor, del.OwnedVindexQuery, bindVars, rs, false /* isDML */, false /* canAutocommit */) - if err != nil { - return err - } - if len(result.Rows) == 0 { - return nil - } - colnum := 1 // we start from the first lookup vindex col - for _, colVindex := range del.Table.Owned { - ids := make([][]sqltypes.Value, len(result.Rows)) - for range colVindex.Columns { - for rowIdx, row := range result.Rows { - ids[rowIdx] = append(ids[rowIdx], row[colnum]) - } - colnum++ - } - if err = colVindex.Vindex.(vindexes.Lookup).Delete(vcursor, ids, ksid); err != nil { - return err - } - } - return nil -} - -func (del *Delete) deleteVindexEntriesScatter(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) error { +// deleteVindexEntries performs an delete if table owns vindex. +// Note: the commit order may be different from the DML order because it's possible +// for DMLs to reuse existing transactions. +func (del *Delete) deleteVindexEntries(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) error { queries := make([]*querypb.BoundQuery, len(rss)) for i := range rss { queries[i] = &querypb.BoundQuery{Sql: del.OwnedVindexQuery, BindVariables: bindVars} @@ -198,7 +177,7 @@ func (del *Delete) deleteVindexEntriesScatter(vcursor VCursor, bindVars map[stri subQueryResults, errors := vcursor.ExecuteMultiShard(rss, queries, false, false) for _, err := range errors { if err != nil { - return vterrors.Wrap(err, "deleteVindexEntriesScatter") + return vterrors.Wrap(err, "deleteVindexEntries") } } @@ -207,7 +186,7 @@ func (del *Delete) deleteVindexEntriesScatter(vcursor VCursor, bindVars map[stri } for _, row := range subQueryResults.Rows { - ksid := row[0].Raw() + ksid := row[0].ToBytes() colnum := 1 for _, colVindex := range del.Table.Owned { // Fetch the column values. colnum must keep incrementing. @@ -241,7 +220,7 @@ func (del *Delete) execDeleteByDestination(vcursor VCursor, bindVars map[string] } } if len(del.Table.Owned) > 0 { - err = del.deleteVindexEntriesScatter(vcursor, bindVars, rss) + err = del.deleteVindexEntries(vcursor, bindVars, rss) if err != nil { return nil, err } diff --git a/go/vt/vtgate/engine/delete_test.go b/go/vt/vtgate/engine/delete_test.go index e2568037736..f199352f436 100644 --- a/go/vt/vtgate/engine/delete_test.go +++ b/go/vt/vtgate/engine/delete_test.go @@ -226,10 +226,10 @@ func TestDeleteOwnedVindex(t *testing.T) { // It gets used to perform the subquery to fetch the changing column values. `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, // Delete 4,5 and 7,8 from lkp2. - `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, - `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, // Delete 6 and 8 from lkp1. + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, // Send the DML. `ExecuteMultiShard sharded.-20: dummy_delete /* vtgate:: keyspace_id:166b40b44aba4bd6 */ {} true true`, @@ -349,8 +349,8 @@ func TestDeleteScatterOwnedVindex(t *testing.T) { // It gets used to perform the subquery to fetch the changing column values. `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, // Delete 4,5 and 7,8 from lkp2. - `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, // Delete 6 and 8 from lkp1. + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index ca5359e54bd..966626c5165 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -162,7 +162,7 @@ func (upd *Update) execUpdateEqual(vcursor VCursor, bindVars map[string]*querypb return &sqltypes.Result{}, nil } if len(upd.ChangedVindexValues) != 0 { - if err := upd.updateVindexEntries(vcursor, bindVars, rs, ksid); err != nil { + if err := upd.updateVindexEntries(vcursor, bindVars, []*srvtopo.ResolvedShard{rs}); err != nil { return nil, vterrors.Wrap(err, "execUpdateEqual") } } @@ -176,52 +176,7 @@ func (upd *Update) execUpdateEqual(vcursor VCursor, bindVars map[string]*querypb // for DMLs to reuse existing transactions. // Note 2: While changes are being committed, the changing row could be // unreachable by either the new or old column values. -func (upd *Update) updateVindexEntries(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard, ksid []byte) error { - subQueryResult, err := execShard(vcursor, upd.OwnedVindexQuery, bindVars, rs, false /* isDML */, false /* canAutocommit */) - if err != nil { - return err - } - if len(subQueryResult.Rows) == 0 { - return nil - } - if len(subQueryResult.Rows) > 1 { - return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: update changes multiple rows in the vindex") - } - colnum := 1 // we start from the first lookup vindex col - for _, colVindex := range upd.Table.Owned { - // Fetch the column values. colnum must keep incrementing. - fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) - for range colVindex.Columns { - fromIds = append(fromIds, subQueryResult.Rows[0][colnum]) - colnum++ - } - - // Update columns only if they're being changed. - if colValues, ok := upd.ChangedVindexValues[colVindex.Name]; ok { - var vindexColumnKeys []sqltypes.Value - for _, colValue := range colValues { - resolvedVal, err := colValue.ResolveValue(bindVars) - if err != nil { - return err - } - vindexColumnKeys = append(vindexColumnKeys, resolvedVal) - } - - if err := colVindex.Vindex.(vindexes.Lookup).Update(vcursor, fromIds, ksid, vindexColumnKeys); err != nil { - return err - } - } - } - return nil -} - -// updateVindexEntries performs an update when a vindex is being modified -// by the statement. -// Note: the commit order may be different from the DML order because it's possible -// for DMLs to reuse existing transactions. -// Note 2: While changes are being committed, the changing row could be -// unreachable by either the new or old column values. -func (upd *Update) updateVindexEntriesScatter(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) error { +func (upd *Update) updateVindexEntries(vcursor VCursor, bindVars map[string]*querypb.BindVariable, rss []*srvtopo.ResolvedShard) error { queries := make([]*querypb.BoundQuery, len(rss)) for i := range rss { queries[i] = &querypb.BoundQuery{Sql: upd.OwnedVindexQuery, BindVariables: bindVars} @@ -229,7 +184,7 @@ func (upd *Update) updateVindexEntriesScatter(vcursor VCursor, bindVars map[stri subQueryResult, errors := vcursor.ExecuteMultiShard(rss, queries, false, false) for _, err := range errors { if err != nil { - return vterrors.Wrap(err, "updateVindexEntriesScatter") + return vterrors.Wrap(err, "updateVindexEntries") } } @@ -239,7 +194,7 @@ func (upd *Update) updateVindexEntriesScatter(vcursor VCursor, bindVars map[stri for _, row := range subQueryResult.Rows { colnum := 1 // we start from the first non-vindex col - ksid := row[0].Raw() + ksid := row[0].ToBytes() for _, colVindex := range upd.Table.Owned { // Fetch the column values. colnum must keep incrementing. fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) @@ -285,7 +240,7 @@ func (upd *Update) execUpdateByDestination(vcursor VCursor, bindVars map[string] // update any owned vindexes if len(upd.ChangedVindexValues) != 0 { - if err := upd.updateVindexEntriesScatter(vcursor, bindVars, rss); err != nil { + if err := upd.updateVindexEntries(vcursor, bindVars, rss); err != nil { return nil, vterrors.Wrap(err, "execUpdateByDestination") } } diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index 786de84e9ec..2e2437b0970 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -262,18 +262,38 @@ func TestUpdateEqualChangedVindex(t *testing.T) { // Failure case: multiple rows changing. results = []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( - "c1|c2|c3", - "int64|int64|int64", + "id|c1|c2|c3", + "varbinary|int64|int64|int64", ), - "4|5|6", - "7|8|9", + "\026k@\264J\272K\326|4|5|6", + "\026k@\264J\272K\326|7|8|9", )} vc = &loggingVCursor{ shards: []string{"-20", "20-"}, results: results, } _, err = upd.Execute(vc, map[string]*querypb.BindVariable{}, false) - expectError(t, "Execute", err, "execUpdateEqual: unsupported: update changes multiple rows in the vindex") + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations sharded [] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, + // ResolveDestinations is hard-coded to return -20. + // It gets used to perform the subquery to fetch the changing column values. + `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, + // Those values are returned as 4,5 for twocol and 6 for onecol. + // 4,5 have to be replaced by 1,2 (the new values). + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp2(from1, from2, toc) values(:from10, :from20, :toc0) from10: type:INT64 value:"1" from20: type:INT64 value:"2" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // 6 has to be replaced by 3. + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp1(from, toc) values(:from0, :toc0) from0: type:INT64 value:"3" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp2(from1, from2, toc) values(:from10, :from20, :toc0) from10: type:INT64 value:"1" from20: type:INT64 value:"2" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp1(from, toc) values(:from0, :toc0) from0: type:INT64 value:"3" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // Finally, the actual update, which is also sent to -20, same route as the subquery. + `ExecuteMultiShard sharded.-20: dummy_update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ {} true true`, + }) + } func TestUpdateScatterChangedVindex(t *testing.T) { @@ -326,6 +346,62 @@ func TestUpdateScatterChangedVindex(t *testing.T) { // Finally, the actual update, which is also sent to -20, same route as the subquery. `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, }) + + // No rows changing + vc = &loggingVCursor{ + shards: []string{"-20", "20-"}, + } + _, err = upd.Execute(vc, map[string]*querypb.BindVariable{}, false) + if err != nil { + t.Fatal(err) + } + vc.ExpectLog(t, []string{ + `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, + // ResolveDestinations is hard-coded to return -20. + // It gets used to perform the subquery to fetch the changing column values. + `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, + // Subquery returns no rows. So, no vindexes are deleted. We still pass-through the original delete. + `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, + }) + + // Update can affect multiple rows + results = []*sqltypes.Result{sqltypes.MakeTestResult( + sqltypes.MakeTestFields( + "id|c1|c2|c3", + "varbinary|int64|int64|int64", + ), + "\026k@\264J\272K\326|4|5|6", + "\026k@\264J\272K\326|7|8|9", + )} + vc = &loggingVCursor{ + shards: []string{"-20", "20-"}, + results: results, + } + _, err = upd.Execute(vc, map[string]*querypb.BindVariable{}, false) + require.NoError(t, err) + vc.ExpectLog(t, []string{ + `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, + // ResolveDestinations is hard-coded to return -20. + // It gets used to perform the subquery to fetch the changing column values. + `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, + // Those values are returned as 4,5 for twocol and 6 for onecol. + // 4,5 have to be replaced by 1,2 (the new values). + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp2(from1, from2, toc) values(:from10, :from20, :toc0) from10: type:INT64 value:"1" from20: type:INT64 value:"2" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // 6 has to be replaced by 3. + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp1(from, toc) values(:from0, :toc0) from0: type:INT64 value:"3" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // Those values are returned as 7,8 for twocol and 9 for onecol. + // 7,8 have to be replaced by 1,2 (the new values). + `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp2(from1, from2, toc) values(:from10, :from20, :toc0) from10: type:INT64 value:"1" from20: type:INT64 value:"2" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // 9 has to be replaced by 3. + `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\026k@\264J\272K\326" true`, + `Execute insert into lkp1(from, toc) values(:from0, :toc0) from0: type:INT64 value:"3" toc0: type:VARBINARY value:"\026k@\264J\272K\326" true`, + // Finally, the actual update, which is also sent to -20, same route as the subquery. + `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, + }) + } func TestUpdateNoStream(t *testing.T) { diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index e2ac0b2dca8..73902a834be 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -1578,6 +1578,9 @@ func generateCharsetRows(showFilter *sqlparser.ShowFilter, colNames []string) ([ } case sqlparser.LikeStr: filteredColName, err = checkLikeOpt(rightString, colNames) + if err != nil { + return nil, err + } } } diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 4e53c7b152f..50c6885efa8 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -97,7 +97,7 @@ func TestUpdateEqual(t *testing.T) { sbclookup.Queries = nil sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "1|1|foo", + "\026k@\264J\272K\326|1|foo", ), }) @@ -208,8 +208,8 @@ func TestUpdateMultiOwned(t *testing.T) { sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|a|b|c|d|e|f", "int64|int64|int64|int64|int64|int64|int64"), - "1|10|20|30|40|50|60", + sqltypes.MakeTestFields("id|a|b|c|d|e|f", "varbinary|int64|int64|int64|int64|int64|int64"), + "\026k@\264J\272K\326|10|20|30|40|50|60", ), }) _, err := executorExec(executor, "update user set a=1, b=2, f=4, e=3 where id=1", nil) @@ -329,13 +329,13 @@ func TestDeleteEqual(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "Id", Type: sqltypes.Int64}, + {Name: "Id", Type: sqltypes.VarBinary}, {Name: "name", Type: sqltypes.VarChar}, }, RowsAffected: 1, InsertID: 0, Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), + sqltypes.NewVarBinary("\026k@\264J\272K\326"), sqltypes.NewVarChar("myname"), }}, }}) @@ -420,7 +420,7 @@ func TestDeleteEqual(t *testing.T) { sbclookup.Queries = nil sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "1|1|foo", + "\026k@\264J\272K\326|1|foo", ), }) _, err = executorExec(executor, "delete from user2 where id = 1", nil) @@ -512,13 +512,13 @@ func TestDeleteComments(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "Id", Type: sqltypes.Int64}, + {Name: "Id", Type: sqltypes.VarBinary}, {Name: "name", Type: sqltypes.VarChar}, }, RowsAffected: 1, InsertID: 0, Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), + sqltypes.NewVarBinary("\026k@\264J\272K\326"), sqltypes.NewVarChar("myname"), }}, }}) diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index f1709226c47..a7b28064c75 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -2632,6 +2632,7 @@ func TestGenerateCharsetRows(t *testing.T) { match := stmt.(*sqlparser.Show) filter := match.ShowTablesOpt.Filter actual, err := generateCharsetRows(filter, charsets) + require.NoError(t, err) require.Equal(t, tc.expected, actual) }) } diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index 664a8e1a5d5..2144e0e3763 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -25,7 +25,7 @@ import ( // buildDeletePlan builds the instructions for a DELETE statement. func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Delete, error) { - dml, vindexCol, err := buildDMLPlan(vschema, "delete", del, del.TableExprs, del.Where, del.OrderBy, del.Limit, del.Comments, del.Targets) + dml, ksidCol, err := buildDMLPlan(vschema, "delete", del, del.TableExprs, del.Where, del.OrderBy, del.Limit, del.Comments, del.Targets) if err != nil { return nil, err } @@ -46,7 +46,7 @@ func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Del } if len(edel.Table.Owned) > 0 { - edel.OwnedVindexQuery = generateDMLSubquery(del.Where, del.OrderBy, del.Limit, edel.Table, vindexCol) + edel.OwnedVindexQuery = generateDMLSubquery(del.Where, del.OrderBy, del.Limit, edel.Table, ksidCol) } return edel, nil diff --git a/go/vt/vtgate/planbuilder/dml.go b/go/vt/vtgate/planbuilder/dml.go index 6845d94cf03..107ebd5b541 100644 --- a/go/vt/vtgate/planbuilder/dml.go +++ b/go/vt/vtgate/planbuilder/dml.go @@ -29,7 +29,7 @@ import ( // getDMLRouting returns the vindex and values for the DML, // If it cannot find a unique vindex match, it returns an error. func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (engine.DMLOpcode, vindexes.SingleColumn, string, []sqltypes.PlanValue) { - var keyColumn string + var ksidCol string for _, index := range table.Ordered { if !index.Vindex.IsUnique() { continue @@ -39,23 +39,23 @@ func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (engine.DMLOpc continue } - if keyColumn == "" { //TODO - check with sougou on how to deterministically get the primary vindex column - keyColumn = sqlparser.String(index.Columns[0]) + if ksidCol == "" { + ksidCol = sqlparser.String(index.Columns[0]) } if where == nil { - return engine.Scatter, nil, keyColumn, nil + return engine.Scatter, nil, ksidCol, nil } if pv, ok := getMatch(where.Expr, index.Columns[0]); ok { if pv.IsList() { - return engine.Scatter, nil, keyColumn, nil + return engine.Scatter, nil, ksidCol, nil } - return engine.Equal, single, keyColumn, []sqltypes.PlanValue{pv} + return engine.Equal, single, ksidCol, []sqltypes.PlanValue{pv} } } - return engine.Scatter, nil, keyColumn, nil + return engine.Scatter, nil, ksidCol, nil } // getMatch returns the matched value if there is an equality @@ -114,10 +114,8 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme eupd.Keyspace = ro.eroute.Keyspace if !eupd.Keyspace.Sharded { // We only validate non-table subexpressions because the previous analysis has already validated them. - subqueryArgs := []sqlparser.SQLNode{} - for _, n := range nodes { - subqueryArgs = append(subqueryArgs, n) - } + var subqueryArgs []sqlparser.SQLNode + subqueryArgs = append(subqueryArgs, nodes...) subqueryArgs = append(subqueryArgs, where, orderBy, limit) if !pb.finalizeUnshardedDMLSubqueries(subqueryArgs...) { return nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: sharded subqueries in DML") @@ -161,7 +159,7 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme return eupd, "", nil } - routingType, vindex, vindexCol, values := getDMLRouting(where, eupd.Table) + routingType, vindex, ksidCol, values := getDMLRouting(where, eupd.Table) eupd.Opcode = routingType if routingType == engine.Scatter { if limit != nil { @@ -172,12 +170,12 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme eupd.Vindex = vindex } - return eupd, vindexCol, nil + return eupd, ksidCol, nil } -func generateDMLSubquery(where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, table *vindexes.Table, vindex string) string { +func generateDMLSubquery(where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, table *vindexes.Table, ksidCol string) string { buf := sqlparser.NewTrackedBuffer(nil) - buf.Myprintf("select %s", vindex) + buf.Myprintf("select %s", ksidCol) for _, cv := range table.Owned { for _, column := range cv.Columns { buf.Myprintf(", %v", column) diff --git a/go/vt/vtgate/planbuilder/route.go b/go/vt/vtgate/planbuilder/route.go index 1f58eadbda8..00954e475de 100644 --- a/go/vt/vtgate/planbuilder/route.go +++ b/go/vt/vtgate/planbuilder/route.go @@ -493,16 +493,6 @@ func (rb *route) removeMultishardOptions() bool { }) } -// removeShardedOptions removes all sharded options from the -// route. It returns false if no such options exist. -// This is used for constructs that are only supported for unsharded -// keyspaces like last_insert_id. -func (rb *route) removeShardedOptions() bool { - return rb.removeOptions(func(ro *routeOption) bool { - return ro.eroute.Opcode == engine.SelectUnsharded - }) -} - // removeOptionsWithUnmatchedKeyspace removes all options that don't match // the specified keyspace. It returns false if no such options exist. func (rb *route) removeOptionsWithUnmatchedKeyspace(keyspace string) bool { From e8b87f94694e391f6319a84e7711b8abc55877dc Mon Sep 17 00:00:00 2001 From: Adam Saponara Date: Wed, 19 Feb 2020 14:00:13 -0500 Subject: [PATCH 165/825] Add `-db_connect_timeout_ms` for timing out mysqld conns. While testing vttablet-mysqld over TCP, we noticed mysql.Connect sometimes blocks forever if the backend mysqld hit max conns. This patch adds a timeout to this codepath. It defaults to 0 (no timeout) for back compatibility. Signed-off-by: Adam Saponara --- go/mysql/conn_params.go | 11 ++++--- go/vt/dbconfigs/dbconfigs.go | 3 +- go/vt/dbconfigs/dbconfigs_test.go | 54 +++++++++++++++++++++++++++++++ go/vt/dbconnpool/connection.go | 5 +++ 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/go/mysql/conn_params.go b/go/mysql/conn_params.go index d3956346612..488cca10199 100644 --- a/go/mysql/conn_params.go +++ b/go/mysql/conn_params.go @@ -30,11 +30,12 @@ type ConnParams struct { // The following SSL flags are only used when flags |= 2048 // is set (CapabilityClientSSL). - SslCa string `json:"ssl_ca"` - SslCaPath string `json:"ssl_ca_path"` - SslCert string `json:"ssl_cert"` - SslKey string `json:"ssl_key"` - ServerName string `json:"server_name"` + SslCa string `json:"ssl_ca"` + SslCaPath string `json:"ssl_ca_path"` + SslCert string `json:"ssl_cert"` + SslKey string `json:"ssl_key"` + ServerName string `json:"server_name"` + ConnectTimeoutMs uint64 `json:"connect_timeout_ms"` // The following is only set when the deprecated "dbname" flags are // supplied and will be removed. diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index c9df1ab26c8..1c0ee4173b3 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -103,7 +103,7 @@ func registerBaseFlags() { flag.StringVar(&baseConfig.SslCert, "db_ssl_cert", "", "connection ssl certificate") flag.StringVar(&baseConfig.SslKey, "db_ssl_key", "", "connection ssl key") flag.StringVar(&baseConfig.ServerName, "db_server_name", "", "server name of the DB we are connecting to.") - + flag.Uint64Var(&baseConfig.ConnectTimeoutMs, "db_connect_timeout_ms", 0, "connection timeout to mysqld in milliseconds (0 for no timeout)") } // The flags will change the global singleton @@ -287,6 +287,7 @@ func Init(defaultSocketFile string) (*DBConfigs, error) { uc.param.SslKey = baseConfig.SslKey uc.param.ServerName = baseConfig.ServerName } + uc.param.ConnectTimeoutMs = baseConfig.ConnectTimeoutMs } // See if the CredentialsServer is working. We do not use the diff --git a/go/vt/dbconfigs/dbconfigs_test.go b/go/vt/dbconfigs/dbconfigs_test.go index d20b50539f8..1917b1b3758 100644 --- a/go/vt/dbconfigs/dbconfigs_test.go +++ b/go/vt/dbconfigs/dbconfigs_test.go @@ -261,6 +261,60 @@ func TestInit(t *testing.T) { } } +func TestInitTimeout(t *testing.T) { + f := saveDBConfigs() + defer f() + + baseConfig = mysql.ConnParams{ + Host: "a", + Port: 1, + Uname: "b", + Pass: "c", + DbName: "d", + UnixSocket: "e", + Charset: "f", + Flags: 2, + Flavor: "flavor", + ConnectTimeoutMs: 250, + } + dbConfigs = DBConfigs{ + userConfigs: map[string]*userConfig{ + App: { + param: mysql.ConnParams{ + Uname: "app", + Pass: "apppass", + }, + }, + }, + } + + dbc, err := Init("default") + if err != nil { + t.Fatal(err) + } + want := &DBConfigs{ + userConfigs: map[string]*userConfig{ + App: { + param: mysql.ConnParams{ + Host: "a", + Port: 1, + Uname: "app", + Pass: "apppass", + UnixSocket: "e", + Charset: "f", + Flags: 2, + Flavor: "flavor", + ConnectTimeoutMs: 250, + }, + }, + }, + } + + if !reflect.DeepEqual(dbc.userConfigs[App].param, want.userConfigs[App].param) { + t.Errorf("dbc: \n%#v, want \n%#v", dbc.userConfigs[App].param, want.userConfigs[App].param) + } +} + func TestAccessors(t *testing.T) { dbc := &DBConfigs{ userConfigs: map[string]*userConfig{ diff --git a/go/vt/dbconnpool/connection.go b/go/vt/dbconnpool/connection.go index 294a7319233..e1a2890a41a 100644 --- a/go/vt/dbconnpool/connection.go +++ b/go/vt/dbconnpool/connection.go @@ -124,6 +124,11 @@ func NewDBConnection(info *mysql.ConnParams, mysqlStats *stats.Timings) (*DBConn return nil, err } ctx := context.Background() + if info.ConnectTimeoutMs != 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, time.Duration(info.ConnectTimeoutMs)*time.Millisecond) + defer cancel() + } c, err := mysql.Connect(ctx, params) if err != nil { mysqlStats.Record("ConnectError", start) From 802e1bdb38f6928ec7f5e6643e9a6dc214b2c7d0 Mon Sep 17 00:00:00 2001 From: prince Date: Mon, 24 Feb 2020 22:43:30 +0530 Subject: [PATCH 166/825] Teardown fixes (#5837) * teardown fixes: fixed unlinked shard objects. replaced assert.Nil with require.NoError to handle teardown while error in setup or restart process. Signed-off-by: pradip parmar * replaced all assert.NotNil to require.Error. Signed-off-by: pradip parmar * revert noError to nil Signed-off-by: pradip parmar * removed unwanted space causing test case failure. Signed-off-by: pradip parmar * removed exit response wait from srop method of mysqlctld. Signed-off-by: pradip parmar --- .../transform/backup_transform_utils.go | 40 +-- .../backup/vtbackup/backup_only_test.go | 48 ++-- go/test/endtoend/backup/vtbackup/main_test.go | 17 +- .../backup/vtctlbackup/backup_utils.go | 109 +++++---- go/test/endtoend/binlog/binlog_test.go | 19 +- go/test/endtoend/cellalias/cell_alias_test.go | 25 +- go/test/endtoend/cluster/cluster_util.go | 10 +- go/test/endtoend/cluster/mysqlctld_process.go | 6 +- go/test/endtoend/clustertest/vtcltd_test.go | 17 +- go/test/endtoend/clustertest/vtgate_test.go | 6 +- .../encrypted_replication_test.go | 36 +-- .../encrypted_transport_test.go | 79 +++--- go/test/endtoend/keyspace/keyspace_test.go | 37 +-- go/test/endtoend/messaging/messaging_test.go | 14 +- go/test/endtoend/mysqlctl/mysqlctl_test.go | 12 +- go/test/endtoend/mysqlctld/mysqlctld_test.go | 12 +- .../endtoend/mysqlserver/mysql_server_test.go | 10 +- go/test/endtoend/preparestmt/main_test.go | 8 +- .../endtoend/preparestmt/stmt_methods_test.go | 2 +- go/test/endtoend/recovery/recovery_util.go | 11 +- .../shardedrecovery/sharded_recovery_test.go | 95 ++++---- go/test/endtoend/reparent/main_test.go | 6 +- .../reparent/reparent_range_based_test.go | 17 +- go/test/endtoend/reparent/reparent_test.go | 227 +++++++++--------- .../endtoend/sharded/sharded_keyspace_test.go | 35 +-- go/test/endtoend/sharding/base_sharding.go | 39 ++- .../sharding/initialsharding/sharding_util.go | 81 ++++--- .../mergesharding/mergesharding_base.go | 103 ++++---- .../sharding/resharding/resharding_base.go | 207 ++++++++-------- .../verticalsplit/vertical_split_test.go | 157 ++++++------ .../endtoend/tabletmanager/commands_test.go | 54 ++--- .../tabletmanager/custom_rule_topo_test.go | 24 +- .../dbnameoverride/tablet_master_test.go | 4 +- .../tabletmanager/lock_unlock_test.go | 34 +-- go/test/endtoend/tabletmanager/main_test.go | 2 +- .../master/tablet_master_test.go | 39 +-- go/test/endtoend/tabletmanager/qps_test.go | 8 +- .../tabletmanager/tablet_health_test.go | 118 ++++----- .../tablet_security_policy_test.go | 27 ++- go/test/endtoend/tabletmanager/tablet_test.go | 17 +- go/test/endtoend/vtgate/aggr_test.go | 2 +- go/test/endtoend/vtgate/buffer/buffer_test.go | 39 ++- go/test/endtoend/vtgate/lookup_test.go | 14 +- go/test/endtoend/vtgate/schema/schema_test.go | 35 +-- go/test/endtoend/vtgate/sequence/seq_test.go | 4 +- 45 files changed, 983 insertions(+), 923 deletions(-) diff --git a/go/test/endtoend/backup/transform/backup_transform_utils.go b/go/test/endtoend/backup/transform/backup_transform_utils.go index 8b1c43482f1..f1cd99e0f09 100644 --- a/go/test/endtoend/backup/transform/backup_transform_utils.go +++ b/go/test/endtoend/backup/transform/backup_transform_utils.go @@ -73,16 +73,17 @@ func TestMainSetup(m *testing.M, useMysqlctld bool) { } // Start keyspace - keyspace := &cluster.Keyspace{ - Name: keyspaceName, - Shards: []cluster.Shard{ - { - Name: shardName, + localCluster.Keyspaces = []cluster.Keyspace{ + { + Name: keyspaceName, + Shards: []cluster.Shard{ + { + Name: shardName, + }, }, }, } - localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) - shard := &keyspace.Shards[0] + shard := &localCluster.Keyspaces[0].Shards[0] // changing password for mysql user dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) initDb, _ := ioutil.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) @@ -195,15 +196,15 @@ func TestBackupTransformImpl(t *testing.T) { "-file_backup_storage_root", localCluster.VtctldProcess.FileBackupStorageRoot} replica1.VttabletProcess.ServingStatus = "SERVING" err := replica1.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) // take backup, it should not give any error err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // insert data in master _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // validate backup_list, expecting 1 backup available backups := localCluster.VerifyBackupCount(t, shardKsName, 1) @@ -234,7 +235,7 @@ func TestBackupTransformImpl(t *testing.T) { } err = localCluster.VtctlclientProcess.InitTablet(replica2, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) replica2.VttabletProcess.CreateDB(keyspaceName) replica2.VttabletProcess.ExtraArgs = []string{ "-db-credentials-file", dbCredentialFile, @@ -278,12 +279,12 @@ func TestBackupTransformErrorImpl(t *testing.T) { "-file_backup_storage_root", localCluster.VtctldProcess.FileBackupStorageRoot} replica1.VttabletProcess.ServingStatus = "SERVING" err = replica1.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) // create backup, it should fail out, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("Backup", replica1.Alias) require.NotNil(t, err) - assert.Containsf(t, out, "backup is not usable, aborting it", "unexpected error received %v", err) + require.Containsf(t, out, "backup is not usable, aborting it", "unexpected error received %v", err) // validate there is no backup left localCluster.VerifyBackupCount(t, shardKsName, 0) @@ -311,7 +312,10 @@ func validateManifestFile(t *testing.T, backupLocation string) { assert.Equalf(t, skipCompress, true, "invalid value of skipCompress") // validate backup files - for i := range manifest["FileEntries"].([]interface{}) { + fielEntries, _ := manifest["FileEntries"] + fileArr, ok := fielEntries.([]interface{}) + require.True(t, ok) + for i := range fileArr { f, err := os.Open(fmt.Sprintf("%s/%d", backupLocation, i)) require.Nilf(t, err, "error while opening backup_file %d: %v", i, err) var fileHeader string @@ -327,18 +331,18 @@ func validateManifestFile(t *testing.T, backupLocation string) { // verifyReplicationStatus validates the replication status in tablet. func verifyReplicationStatus(t *testing.T, vttablet *cluster.Vttablet, expectedStatus string) { status, err := vttablet.VttabletProcess.GetDBVar("rpl_semi_sync_slave_enabled", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, expectedStatus, status) status, err = vttablet.VttabletProcess.GetDBStatus("rpl_semi_sync_slave_status", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, expectedStatus, status) } // verifyInitialReplication creates schema in master, insert some data to master and verify the same data in replica func verifyInitialReplication(t *testing.T) { _, err := master.VttabletProcess.QueryTablet(vtInsertTest, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test1')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 1) } diff --git a/go/test/endtoend/backup/vtbackup/backup_only_test.go b/go/test/endtoend/backup/vtbackup/backup_only_test.go index 5a6de0d8a56..ee5239ac42e 100644 --- a/go/test/endtoend/backup/vtbackup/backup_only_test.go +++ b/go/test/endtoend/backup/vtbackup/backup_only_test.go @@ -64,7 +64,7 @@ func TestTabletInitialBackup(t *testing.T) { restore(t, master, "replica", "NOT_SERVING") err := localCluster.VtctlclientProcess.ExecuteCommand( "TabletExternallyReparented", master.Alias) - assert.Nil(t, err) + require.Nil(t, err) restore(t, replica1, "replica", "SERVING") // Run the entire backup test @@ -112,10 +112,10 @@ func firstBackupTest(t *testing.T, tabletType string) { // insert data on master, wait for slave to get it _, err = master.VttabletProcess.QueryTablet(vtInsertTest, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Add a single row with value 'test1' to the master tablet _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test1')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Check that the specified tablet has the expected number of rows cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 1) @@ -130,12 +130,12 @@ func firstBackupTest(t *testing.T, tabletType string) { // insert more data on the master _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 2) // now bring up the other slave, letting it restore from backup. err = localCluster.VtctlclientProcess.InitTablet(replica2, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) restore(t, replica2, "replica", "SERVING") // Replica2 takes time to serve. Sleeping for 5 sec. time.Sleep(5 * time.Second) @@ -144,7 +144,7 @@ func firstBackupTest(t *testing.T, tabletType string) { // check that the restored slave has the right local_metadata result, err := replica2.VttabletProcess.QueryTabletWithDB("select * from local_metadata", "_vt") - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, replica2.Alias, result.Rows[0][1].ToString(), "Alias") assert.Equal(t, "ks.0", result.Rows[1][1].ToString(), "ClusterAlias") assert.Equal(t, cell, result.Rows[2][1].ToString(), "DataCenter") @@ -164,12 +164,12 @@ func vtBackup(t *testing.T, initialBackup bool) { extraArgs := []string{"-allow_first_backup", "-db-credentials-file", dbCredentialFile} log.Info("starting backup tablet %s", time.Now()) err := localCluster.StartVtbackup(newInitDBFile, initialBackup, keyspaceName, shardName, cell, extraArgs...) - assert.Nil(t, err) + require.Nil(t, err) } func verifyBackupCount(t *testing.T, shardKsName string, expected int) []string { backups, err := listBackups(shardKsName) - assert.Nil(t, err) + require.Nil(t, err) assert.Equalf(t, expected, len(backups), "invalid number of backups") return backups } @@ -197,7 +197,7 @@ func listBackups(shardKsName string) ([]string, error) { func removeBackups(t *testing.T) { // Remove all the backups from the shard backups, err := listBackups(shardKsName) - assert.Nil(t, err) + require.Nil(t, err) for _, backup := range backups { _, err := localCluster.VtctlProcess.ExecuteCommandWithOutput( "-backup_storage_implementation", "file", @@ -205,7 +205,7 @@ func removeBackups(t *testing.T) { path.Join(os.Getenv("VTDATAROOT"), "tmp", "backupstorage"), "RemoveBackup", shardKsName, backup, ) - assert.Nil(t, err) + require.Nil(t, err) } } @@ -213,18 +213,18 @@ func initTablets(t *testing.T, startTablet bool, initShardMaster bool) { // Initialize tablets for _, tablet := range []cluster.Vttablet{*master, *replica1} { err := localCluster.VtctlclientProcess.InitTablet(&tablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) if startTablet { err = tablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) } } if initShardMaster { // choose master and start replication err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, master.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) } } @@ -235,7 +235,7 @@ func restore(t *testing.T, tablet *cluster.Vttablet, tabletType string, waitForS resetTabletDirectory(t, *tablet, true) err := tablet.VttabletProcess.CreateDB(keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // Start tablets tablet.VttabletProcess.ExtraArgs = []string{"-db-credentials-file", dbCredentialFile} @@ -243,7 +243,7 @@ func restore(t *testing.T, tablet *cluster.Vttablet, tabletType string, waitForS tablet.VttabletProcess.ServingStatus = waitForState tablet.VttabletProcess.SupportsBackup = true err = tablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) } func resetTabletDirectory(t *testing.T, tablet cluster.Vttablet, initMysql bool) { @@ -253,20 +253,20 @@ func resetTabletDirectory(t *testing.T, tablet cluster.Vttablet, initMysql bool) // Shutdown Mysql err := tablet.MysqlctlProcess.Stop() - assert.Nil(t, err) + require.Nil(t, err) // Teardown Tablet err = tablet.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) // Empty the dir err = os.RemoveAll(tablet.VttabletProcess.Directory) - assert.Nil(t, err) + require.Nil(t, err) if initMysql { // Init the Mysql tablet.MysqlctlProcess.InitDBFile = newInitDBFile err = tablet.MysqlctlProcess.Start() - assert.Nil(t, err) + require.Nil(t, err) } } @@ -277,12 +277,12 @@ func tearDown(t *testing.T, initMysql bool) { disableSemiSyncCommands := "SET GLOBAL rpl_semi_sync_master_enabled = false; SET GLOBAL rpl_semi_sync_slave_enabled = false" for _, tablet := range []cluster.Vttablet{*master, *replica1, *replica2} { _, err := tablet.VttabletProcess.QueryTablet(promoteSlaveCommands, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) _, err = tablet.VttabletProcess.QueryTablet(disableSemiSyncCommands, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) for _, db := range []string{"_vt", "vt_insert_test"} { _, err = tablet.VttabletProcess.QueryTablet(fmt.Sprintf("drop database if exists %s", db), keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) } } @@ -291,9 +291,9 @@ func tearDown(t *testing.T, initMysql bool) { for _, tablet := range []cluster.Vttablet{*master, *replica1, *replica2} { //Tear down Tablet //err := tablet.VttabletProcess.TearDown() - //assert.Nil(t, err) + //require.Nil(t, err) err := localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", "-allow_master", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) resetTabletDirectory(t, tablet, initMysql) } diff --git a/go/test/endtoend/backup/vtbackup/main_test.go b/go/test/endtoend/backup/vtbackup/main_test.go index 6a597371057..42078af68ed 100644 --- a/go/test/endtoend/backup/vtbackup/main_test.go +++ b/go/test/endtoend/backup/vtbackup/main_test.go @@ -68,10 +68,17 @@ func TestMain(m *testing.M) { } // Start keyspace - keyspace := &cluster.Keyspace{ - Name: keyspaceName, + localCluster.Keyspaces = []cluster.Keyspace{ + { + Name: keyspaceName, + Shards: []cluster.Shard{ + { + Name: shardName, + }, + }, + }, } - localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) + shard := &localCluster.Keyspaces[0].Shards[0] // Create a new init_db.sql file that sets up passwords for all users. // Then we use a db-credentials-file with the passwords. @@ -88,10 +95,6 @@ func TestMain(m *testing.M) { extraArgs := []string{"-db-credentials-file", dbCredentialFile} commonTabletArg = append(commonTabletArg, "-db-credentials-file", dbCredentialFile) - shard := cluster.Shard{ - Name: shardName, - } - master = localCluster.GetVttabletInstance("replica", 0, "") replica1 = localCluster.GetVttabletInstance("replica", 0, "") replica2 = localCluster.GetVttabletInstance("replica", 0, "") diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index 0fdc62e1796..b5503f9d1f7 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -88,10 +88,17 @@ func LaunchCluster(setupType int, streamMode string, stripes int) (int, error) { } // Start keyspace - keyspace := &cluster.Keyspace{ - Name: keyspaceName, + localCluster.Keyspaces = []cluster.Keyspace{ + { + Name: keyspaceName, + Shards: []cluster.Shard{ + { + Name: shardName, + }, + }, + }, } - localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace) + shard := &localCluster.Keyspaces[0].Shards[0] dbCredentialFile = initialsharding.WriteDbCredentialToTmp(localCluster.TmpDirectory) initDb, _ := ioutil.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql")) @@ -121,10 +128,6 @@ func LaunchCluster(setupType int, streamMode string, stripes int) (int, error) { commonTabletArg = append(commonTabletArg, xtrabackupArgs...) } - shard := cluster.Shard{ - Name: shardName, - } - var mysqlProcs []*exec.Cmd for i := 0; i < 3; i++ { tabletType := "replica" @@ -265,33 +268,33 @@ func masterBackup(t *testing.T) { verifyInitialReplication(t) output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("Backup", master.Alias) - assert.NotNil(t, err) + require.Error(t, err) assert.Contains(t, output, "type MASTER cannot take backup. if you really need to do this, rerun the backup command with -allow_master") localCluster.VerifyBackupCount(t, shardKsName, 0) err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", "-allow_master=true", master.Alias) - assert.Nil(t, err) + require.Nil(t, err) backups := localCluster.VerifyBackupCount(t, shardKsName, 1) assert.Contains(t, backups[0], master.Alias) _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) restoreWaitForBackup(t, "replica") err = replica2.VttabletProcess.WaitForTabletTypesForTimeout([]string{"SERVING"}, 25*time.Second) - assert.Nil(t, err) + require.Nil(t, err) cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) cluster.VerifyLocalMetadata(t, replica2, keyspaceName, shardName, cell) verifyAfterRemovingBackupNoBackupShouldBePresent(t, backups) err = replica2.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) _, err = master.VttabletProcess.QueryTablet("DROP TABLE vt_insert_test", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) } // Test a master and replica from the same backup. @@ -304,16 +307,16 @@ func masterReplicaSameBackup(t *testing.T) { // backup the replica err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // insert more data on the master _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // now bring up the other replica, letting it restore from backup. restoreWaitForBackup(t, "replica") err = replica2.VttabletProcess.WaitForTabletTypesForTimeout([]string{"SERVING"}, 25*time.Second) - assert.Nil(t, err) + require.Nil(t, err) // check the new replica has the data cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) @@ -322,11 +325,11 @@ func masterReplicaSameBackup(t *testing.T) { err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "-keyspace_shard", shardKsName, "-new_master", replica2.Alias) - assert.Nil(t, err) + require.Nil(t, err) // insert more data on replica2 (current master) _, err = replica2.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test3')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Force replica1 to restore from backup. verifyRestoreTablet(t, replica1, "SERVING") @@ -340,18 +343,18 @@ func masterReplicaSameBackup(t *testing.T) { // // Take another backup on the replica. err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // Insert more data on replica2 (current master). _, err = replica2.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test4')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Force replica1 to restore from backup. verifyRestoreTablet(t, replica1, "SERVING") cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 4) err = replica2.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) restartMasterReplica(t) } @@ -379,21 +382,21 @@ func testRestoreOldMaster(t *testing.T, method restoreMethod) { // backup the replica err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // insert more data on the master _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // reparent to replica1 err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "-keyspace_shard", shardKsName, "-new_master", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // insert more data to new master _, err = replica1.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test3')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // force the old master to restore at the latest backup. method(t, master) @@ -407,13 +410,13 @@ func testRestoreOldMaster(t *testing.T, method restoreMethod) { func restoreUsingRestart(t *testing.T, tablet *cluster.Vttablet) { err := tablet.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) verifyRestoreTablet(t, tablet, "SERVING") } func restoreInPlace(t *testing.T, tablet *cluster.Vttablet) { err := localCluster.VtctlclientProcess.ExecuteCommand("RestoreFromBackup", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } func restartMasterReplica(t *testing.T) { @@ -438,14 +441,14 @@ func restartMasterReplica(t *testing.T) { } for _, tablet := range []*cluster.Vttablet{master, replica1} { err := localCluster.VtctlclientProcess.InitTablet(tablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) err = tablet.VttabletProcess.CreateDB(keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) err = tablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) } err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, master.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) } func stopAllTablets() { @@ -475,33 +478,33 @@ func terminatedRestore(t *testing.T) { // backup the replica err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // insert more data on the master _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // reparent to replica1 err = localCluster.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "-keyspace_shard", shardKsName, "-new_master", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // insert more data to new master _, err = replica1.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test3')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) terminateRestore(t) err = localCluster.VtctlclientProcess.ExecuteCommand("RestoreFromBackup", master.Alias) - assert.Nil(t, err) + require.Nil(t, err) output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("GetTablet", master.Alias) - assert.Nil(t, err) + require.Nil(t, err) var tabletPB topodata.Tablet err = json.Unmarshal([]byte(output), &tabletPB) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, tabletPB.Type, topodata.TabletType_REPLICA) _, err = os.Stat(path.Join(master.VttabletProcess.Directory, "restore_in_progress")) @@ -531,36 +534,36 @@ func vtctlBackup(t *testing.T, tabletType string) { verifyInitialReplication(t) err := localCluster.VtctlclientProcess.ExecuteCommand("Backup", replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) backups := localCluster.VerifyBackupCount(t, shardKsName, 1) _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test2')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) err = replica2.VttabletProcess.WaitForTabletTypesForTimeout([]string{"SERVING"}, 25*time.Second) - assert.Nil(t, err) + require.Nil(t, err) cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) cluster.VerifyLocalMetadata(t, replica2, keyspaceName, shardName, cell) verifyAfterRemovingBackupNoBackupShouldBePresent(t, backups) err = replica2.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteTablet", replica2.Alias) - assert.Nil(t, err) + require.Nil(t, err) _, err = master.VttabletProcess.QueryTablet("DROP TABLE vt_insert_test", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) } // This will create schema in master, insert some data to master and verify the same data in replica func verifyInitialReplication(t *testing.T) { _, err := master.VttabletProcess.QueryTablet(vtInsertTest, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) _, err = master.VttabletProcess.QueryTablet("insert into vt_insert_test (msg) values ('test1')", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 1) } @@ -580,14 +583,14 @@ func restoreWaitForBackup(t *testing.T, tabletType string) { replica2.VttabletProcess.ExtraArgs = replicaTabletArgs replica2.VttabletProcess.ServingStatus = "" err := replica2.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) } func verifyAfterRemovingBackupNoBackupShouldBePresent(t *testing.T, backups []string) { // Remove the backup for _, backup := range backups { err := localCluster.VtctlclientProcess.ExecuteCommand("RemoveBackup", shardKsName, backup) - assert.Nil(t, err) + require.Nil(t, err) } // Now, there should not be no backup @@ -599,10 +602,10 @@ func verifyRestoreTablet(t *testing.T, tablet *cluster.Vttablet, status string) tablet.ValidateTabletRestart(t) tablet.VttabletProcess.ServingStatus = "" err := tablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) if status != "" { err = tablet.VttabletProcess.WaitForTabletTypesForTimeout([]string{status}, 25*time.Second) - assert.Nil(t, err) + require.Nil(t, err) } if tablet.Type == "replica" { @@ -614,10 +617,10 @@ func verifyRestoreTablet(t *testing.T, tablet *cluster.Vttablet, status string) func verifyReplicationStatus(t *testing.T, vttablet *cluster.Vttablet, expectedStatus string) { status, err := vttablet.VttabletProcess.GetDBVar("rpl_semi_sync_slave_enabled", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, status, expectedStatus) status, err = vttablet.VttabletProcess.GetDBStatus("rpl_semi_sync_slave_status", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, status, expectedStatus) } @@ -636,7 +639,7 @@ func terminateRestore(t *testing.T) { reader, _ := tmpProcess.StderrPipe() err := tmpProcess.Start() - assert.Nil(t, err) + require.Nil(t, err) found := false scanner := bufio.NewScanner(reader) diff --git a/go/test/endtoend/binlog/binlog_test.go b/go/test/endtoend/binlog/binlog_test.go index 8914405df1a..0ed46feb0ee 100644 --- a/go/test/endtoend/binlog/binlog_test.go +++ b/go/test/endtoend/binlog/binlog_test.go @@ -32,6 +32,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" @@ -261,7 +262,7 @@ func TestCharset(t *testing.T) { position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) _, err := queryTablet(t, *srcMaster, fmt.Sprintf(insertSql, tableName, 1, "Šṛ́rỏé"), "latin1") - assert.Nil(t, err) + require.Nil(t, err) println("Waiting to get rows in dest master tablet") waitForReplicaEvent(t, position, "1", *destReplica) @@ -273,11 +274,11 @@ func TestCharset(t *testing.T) { func TestChecksumEnabled(t *testing.T) { position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) _, err := queryTablet(t, *destReplica, "SET @@global.binlog_checksum=1", "") - assert.Nil(t, err) + require.Nil(t, err) // Insert something and make sure it comes through intact. _, err = queryTablet(t, *srcMaster, fmt.Sprintf(insertSql, tableName, 2, "value - 2"), "") - assert.Nil(t, err) + require.Nil(t, err) // Look for it using update stream to see if binlog streamer can talk to // dest_replica, which now has binlog_checksum enabled. @@ -292,11 +293,11 @@ func TestChecksumDisabled(t *testing.T) { position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) _, err := queryTablet(t, *destReplica, "SET @@global.binlog_checksum=0", "") - assert.Nil(t, err) + require.Nil(t, err) // Insert something and make sure it comes through intact. _, err = queryTablet(t, *srcMaster, fmt.Sprintf(insertSql, tableName, 3, "value - 3"), "") - assert.Nil(t, err) + require.Nil(t, err) // Look for it using update stream to see if binlog streamer can talk to // dest_replica, which now has binlog_checksum disabled. @@ -311,11 +312,11 @@ func waitForReplicaEvent(t *testing.T, position string, pkKey string, vttablet c for time.Now().Before(timeout) { println("fetching with position " + position) output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletUpdateStream", "-position", position, "-count", "1", vttablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var binlogStreamEvent query.StreamEvent err = json.Unmarshal([]byte(output), &binlogStreamEvent) - assert.Nil(t, err) + require.Nil(t, err) for _, statement := range binlogStreamEvent.Statements { if isCurrentRowPresent(*statement, pkKey) { return @@ -337,7 +338,7 @@ func isCurrentRowPresent(statement query.StreamEvent_Statement, pkKey string) bo func verifyData(t *testing.T, id uint64, charset string, expectedOutput string) { data, err := queryTablet(t, *destMaster, fmt.Sprintf("select id, msg from %s where id = %d", tableName, id), charset) - assert.Nil(t, err) + require.Nil(t, err) assert.NotNil(t, data.Rows) rowFound := assert.Equal(t, len(data.Rows), 1) assert.Equal(t, len(data.Fields), 2) @@ -358,7 +359,7 @@ func queryTablet(t *testing.T, vttablet cluster.Vttablet, query string, charset } ctx := context.Background() dbConn, err := mysql.Connect(ctx, &dbParams) - assert.Nil(t, err) + require.Nil(t, err) defer dbConn.Close() return dbConn.ExecuteFetch(query, 1000, true) } diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index c9d816271ad..a64df9e74df 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -30,6 +30,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/sharding" @@ -237,7 +238,7 @@ func TestMain(m *testing.M) { func TestAlias(t *testing.T) { insertInitialValues(t) err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) shard1 := localCluster.Keyspaces[0].Shards[0] shard2 := localCluster.Keyspaces[0].Shards[1] allCells := fmt.Sprintf("%s,%s", cell1, cell2) @@ -253,17 +254,17 @@ func TestAlias(t *testing.T) { err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias", "-cells", allCells, "region_east_coast") - assert.Nil(t, err) + require.Nil(t, err) err = localCluster.VtctlclientProcess.ExecuteCommand("UpdateCellsAlias", "-cells", allCells, "region_east_coast") - assert.Nil(t, err) + require.Nil(t, err) vtgateInstance := localCluster.GetVtgateInstance() vtgateInstance.CellsToWatch = allCells vtgateInstance.TabletTypesToWait = "MASTER,REPLICA" err = vtgateInstance.Setup() - assert.Nil(t, err) + require.Nil(t, err) waitTillAllTabletsAreHealthyInVtgate(t, *vtgateInstance, shard1.Name, shard2.Name) testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) @@ -273,13 +274,13 @@ func TestAlias(t *testing.T) { // now, delete the alias, so that if we run above assertions again, it will fail for replica,rdonly target type err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteCellsAlias", "region_east_coast") - assert.Nil(t, err) + require.Nil(t, err) // restarts the vtgate process _ = vtgateInstance.TearDown() vtgateInstance.TabletTypesToWait = "MASTER" err = vtgateInstance.Setup() - assert.Nil(t, err) + require.Nil(t, err) // since replica and rdonly tablets of all shards in cell2, the last 2 assertion is expected to fail testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) @@ -290,11 +291,11 @@ func TestAlias(t *testing.T) { func waitTillAllTabletsAreHealthyInVtgate(t *testing.T, vtgateInstance cluster.VtgateProcess, shards ...string) { for _, shard := range shards { err := vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", keyspaceName, shard), 1) - assert.Nil(t, err) + require.Nil(t, err) err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard), 1) - assert.Nil(t, err) + require.Nil(t, err) err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspaceName, shard), 1) - assert.Nil(t, err) + require.Nil(t, err) } } @@ -304,14 +305,14 @@ func testQueriesInDifferentTabletType(t *testing.T, tabletType string, vtgateGrp "-target", "@"+tabletType, fmt.Sprintf(`select * from %s`, tableName)) if shouldFail { - assert.NotNil(t, err) + require.Error(t, err) return } - assert.Nil(t, err) + require.Nil(t, err) var result sqltypes.Result err = json.Unmarshal([]byte(output), &result) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, len(result.Rows), 3) } diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 81d68440a6d..df6f9ce9fa5 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -68,7 +68,7 @@ func GetMasterPosition(t *testing.T, vttablet Vttablet, hostname string) (string ctx := context.Background() vtablet := getTablet(vttablet.GrpcPort, hostname) pos, err := tmClient.MasterPosition(ctx, vtablet) - require.NoError(t, err) + require.Nil(t, err) gtID := strings.SplitAfter(pos, "/")[1] return pos, gtID } @@ -90,7 +90,7 @@ func VerifyRowsInTablet(t *testing.T, vttablet *Vttablet, ksName string, expecte // VerifyLocalMetadata Verify Local Metadata of a tablet func VerifyLocalMetadata(t *testing.T, tablet *Vttablet, ksName string, shardName string, cell string) { qr, err := tablet.VttabletProcess.QueryTablet("select * from _vt.local_metadata", ksName, false) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, fmt.Sprintf("%v", qr.Rows[0][1]), fmt.Sprintf(`BLOB("%s")`, tablet.Alias)) assert.Equal(t, fmt.Sprintf("%v", qr.Rows[1][1]), fmt.Sprintf(`BLOB("%s.%s")`, ksName, shardName)) assert.Equal(t, fmt.Sprintf("%v", qr.Rows[2][1]), fmt.Sprintf(`BLOB("%s")`, cell)) @@ -120,7 +120,7 @@ func (cluster LocalProcessCluster) ListBackups(shardKsName string) ([]string, er // VerifyBackupCount compares the backup count with expected count. func (cluster LocalProcessCluster) VerifyBackupCount(t *testing.T, shardKsName string, expected int) []string { backups, err := cluster.ListBackups(shardKsName) - assert.Nil(t, err) + require.Nil(t, err) assert.Equalf(t, expected, len(backups), "invalid number of backups") return backups } @@ -128,7 +128,7 @@ func (cluster LocalProcessCluster) VerifyBackupCount(t *testing.T, shardKsName s // RemoveAllBackups removes all the backup corresponds to list backup. func (cluster LocalProcessCluster) RemoveAllBackups(t *testing.T, shardKsName string) { backups, err := cluster.ListBackups(shardKsName) - assert.Nil(t, err) + require.Nil(t, err) for _, backup := range backups { cluster.VtctlclientProcess.ExecuteCommand("RemoveBackup", shardKsName, backup) } @@ -202,7 +202,7 @@ func positionAtLeast(t *testing.T, tablet *Vttablet, a string, b string) bool { // ExecuteQueriesUsingVtgate sends query to vtgate using vtgate session. func ExecuteQueriesUsingVtgate(t *testing.T, session *vtgateconn.VTGateSession, query string) { _, err := session.Execute(context.Background(), query, nil) - assert.Nil(t, err) + require.Nil(t, err) } // NewConnParams creates ConnParams corresponds to given arguments. diff --git a/go/test/endtoend/cluster/mysqlctld_process.go b/go/test/endtoend/cluster/mysqlctld_process.go index 855315fbe7d..08abb7027d3 100644 --- a/go/test/endtoend/cluster/mysqlctld_process.go +++ b/go/test/endtoend/cluster/mysqlctld_process.go @@ -134,11 +134,7 @@ func (mysqlctld *MysqlctldProcess) Stop() error { ) tmpProcess.Args = append(tmpProcess.Args, mysqlctld.ExtraArgs...) tmpProcess.Args = append(tmpProcess.Args, "shutdown") - err := tmpProcess.Run() - if err != nil { - return err - } - return <-mysqlctld.exit + return tmpProcess.Run() } // CleanupFiles clean the mysql files to make sure we can start the same process again diff --git a/go/test/endtoend/clustertest/vtcltd_test.go b/go/test/endtoend/clustertest/vtcltd_test.go index 97a157f3458..3b35c383d6e 100644 --- a/go/test/endtoend/clustertest/vtcltd_test.go +++ b/go/test/endtoend/clustertest/vtcltd_test.go @@ -28,6 +28,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -57,13 +58,13 @@ func TestVtctldProcess(t *testing.T) { func testTopoDataAPI(t *testing.T, url string) { resp, err := http.Get(url) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, resp.StatusCode, 200) resultMap := make(map[string]interface{}) respByte, _ := ioutil.ReadAll(resp.Body) err = json.Unmarshal(respByte, &resultMap) - assert.Nil(t, err) + require.Nil(t, err) errorValue := reflect.ValueOf(resultMap["Error"]) assert.Empty(t, errorValue.String()) @@ -77,7 +78,7 @@ func testTopoDataAPI(t *testing.T, url string) { func testListAllTablets(t *testing.T) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ListAllTablets", clusterInstance.Cell) - assert.Nil(t, err) + require.Nil(t, err) tablets := getAllTablets() @@ -95,14 +96,14 @@ func testListAllTablets(t *testing.T) { func testTabletStatus(t *testing.T) { resp, err := http.Get(fmt.Sprintf("http://%s:%d", clusterInstance.Hostname, clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].HTTPPort)) - assert.Nil(t, err) + require.Nil(t, err) respByte, err := ioutil.ReadAll(resp.Body) - assert.Nil(t, err) + require.Nil(t, err) result := string(respByte) println(result) println(strings.Contains(result, "Polling health information from.")) matched, err := regexp.Match(`Polling health information from.+MySQLReplicationLag`, []byte(result)) - assert.Nil(t, err) + require.Nil(t, err) assert.True(t, matched) assert.True(t, strings.Contains(result, `Alias: rdonly below. err = clusterInstance.VtctlclientProcess.ExecuteCommand("StopSlave", rdonly1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // Check semi-sync on replicas. // The flag is only an indication of the value to use next time @@ -670,27 +671,27 @@ func TestChangeTypeSemiSync(t *testing.T) { // Change replica to rdonly while replicating, should turn off semi-sync, and restart replication. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", replica.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) checkDBvar(ctx, t, replica, "rpl_semi_sync_slave_enabled", "OFF") checkDBstatus(ctx, t, replica, "Rpl_semi_sync_slave_status", "OFF") // Change rdonly1 to replica, should turn on semi-sync, and not start replication. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", rdonly1.Alias, "replica") - assert.Nil(t, err) + require.Nil(t, err) checkDBvar(ctx, t, rdonly1, "rpl_semi_sync_slave_enabled", "ON") checkDBstatus(ctx, t, rdonly1, "Rpl_semi_sync_slave_status", "OFF") checkSlaveStatus(ctx, t, rdonly1) // Now change from replica back to rdonly, make sure replication is still not enabled. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", rdonly1.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) checkDBvar(ctx, t, rdonly1, "rpl_semi_sync_slave_enabled", "OFF") checkDBstatus(ctx, t, rdonly1, "Rpl_semi_sync_slave_status", "OFF") checkSlaveStatus(ctx, t, rdonly1) // Change rdonly2 to replica, should turn on semi-sync, and restart replication. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", rdonly2.Alias, "replica") - assert.Nil(t, err) + require.Nil(t, err) checkDBvar(ctx, t, rdonly2, "rpl_semi_sync_slave_enabled", "ON") checkDBstatus(ctx, t, rdonly2, "Rpl_semi_sync_slave_status", "ON") @@ -701,25 +702,25 @@ func TestReparentDoesntHangIfMasterFails(t *testing.T) { for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { // Create Database err := tablet.VttabletProcess.CreateDB(keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // Init Tablet err = clusterInstance.VtctlclientProcess.InitTablet(&tablet, tablet.Cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // Start the tablet err = tablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) } // Init Shard Master err := clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shardName), tablet62344.Alias) - assert.Nil(t, err) + require.Nil(t, err) for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { err := tablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) } validateTopology(t, true) @@ -728,7 +729,7 @@ func TestReparentDoesntHangIfMasterFails(t *testing.T) { // inserts into it will fail. That will make the master fail. _, err = tablet62344.VttabletProcess.QueryTabletWithDB( "ALTER TABLE reparent_journal DROP COLUMN replication_position", "_vt") - assert.Nil(t, err) + require.Nil(t, err) // Perform a planned reparent operation, the master will fail the // insert. The slaves should then abort right away. @@ -736,7 +737,7 @@ func TestReparentDoesntHangIfMasterFails(t *testing.T) { "PlannedReparentShard", "-keyspace_shard", keyspaceShard, "-new_master", tablet62044.Alias) - assert.NotNil(t, err) + require.Error(t, err) assert.Contains(t, out, "master failed to PopulateReparentJournal") killTablets(t) @@ -759,7 +760,7 @@ func waitForReplicationPosition(t *testing.T, tabletA *cluster.Vttablet, tabletB func positionAtLeast(t *testing.T, tablet *cluster.Vttablet, a string, b string) bool { isAtleast := false val, err := tablet.MysqlctlProcess.ExecuteCommandWithOutput("position", "at_least", a, b) - assert.Nil(t, err) + require.Nil(t, err) if strings.Contains(val, "true") { isAtleast = true } @@ -768,7 +769,7 @@ func positionAtLeast(t *testing.T, tablet *cluster.Vttablet, a string, b string) func checkReparentFromOutside(t *testing.T, tablet *cluster.Vttablet, downMaster bool, baseTime int64) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell1, keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") if !downMaster { assertNodeCount(t, result, int(3)) } else { @@ -782,16 +783,16 @@ func checkReparentFromOutside(t *testing.T, tablet *cluster.Vttablet, downMaster // make sure the master health stream says it's the master too // (health check is disabled on these servers, force it first) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) streamHealth, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( "VtTabletStreamHealth", "-count", "1", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthResponse querypb.StreamHealthResponse err = json.Unmarshal([]byte(streamHealth), &streamHealthResponse) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, streamHealthResponse.Target.TabletType, topodatapb.TabletType_MASTER) assert.True(t, streamHealthResponse.TabletExternallyReparentedTimestamp >= baseTime) @@ -800,7 +801,7 @@ func checkReparentFromOutside(t *testing.T, tablet *cluster.Vttablet, downMaster func assertNodeCount(t *testing.T, result string, want int) { resultMap := make(map[string]interface{}) err := json.Unmarshal([]byte(result), &resultMap) - assert.Nil(t, err) + require.Nil(t, err) nodes := reflect.ValueOf(resultMap["nodes"]) got := nodes.Len() @@ -810,7 +811,7 @@ func assertNodeCount(t *testing.T, result string, want int) { func checkDBvar(ctx context.Context, t *testing.T, tablet *cluster.Vttablet, variable string, status string) { tabletParams := getMysqlConnParam(tablet) conn, err := mysql.Connect(ctx, &tabletParams) - assert.Nil(t, err) + require.Nil(t, err) defer conn.Close() qr := execute(t, conn, fmt.Sprintf("show variables like '%s'", variable)) @@ -822,7 +823,7 @@ func checkDBvar(ctx context.Context, t *testing.T, tablet *cluster.Vttablet, var func checkDBstatus(ctx context.Context, t *testing.T, tablet *cluster.Vttablet, variable string, status string) { tabletParams := getMysqlConnParam(tablet) conn, err := mysql.Connect(ctx, &tabletParams) - assert.Nil(t, err) + require.Nil(t, err) defer conn.Close() qr := execute(t, conn, fmt.Sprintf("show status like '%s'", variable)) @@ -842,19 +843,19 @@ func checkSlaveStatus(ctx context.Context, t *testing.T, tablet *cluster.Vttable // Makes sure the tablet type is master, and its health check agrees. func checkMasterTablet(t *testing.T, tablet *cluster.Vttablet) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetTablet", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var tabletInfo topodatapb.Tablet err = json2.Unmarshal([]byte(result), &tabletInfo) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, topodatapb.TabletType_MASTER, tabletInfo.GetType()) // make sure the health stream is updated result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "-count", "1", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthResponse querypb.StreamHealthResponse err = json2.Unmarshal([]byte(result), &streamHealthResponse) - assert.Nil(t, err) + require.Nil(t, err) assert.True(t, streamHealthResponse.GetServing()) tabletType := streamHealthResponse.GetTarget().GetTabletType() @@ -879,10 +880,10 @@ func checkInsertedValues(ctx context.Context, t *testing.T, tablet *cluster.Vtta func validateTopology(t *testing.T, pingTablets bool) { if pingTablets { err := clusterInstance.VtctlclientProcess.ExecuteCommand("Validate", "-ping-tablets=true") - assert.Nil(t, err) + require.Nil(t, err) } else { err := clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") - assert.Nil(t, err) + require.Nil(t, err) } } @@ -890,7 +891,7 @@ func killTablets(t *testing.T) { for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { fmt.Println("Teardown tablet: ", tablet.Alias) err := tablet.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) // Reset status and type tablet.VttabletProcess.ServingStatus = "" diff --git a/go/test/endtoend/sharded/sharded_keyspace_test.go b/go/test/endtoend/sharded/sharded_keyspace_test.go index 85b122330a3..ef7bc979787 100644 --- a/go/test/endtoend/sharded/sharded_keyspace_test.go +++ b/go/test/endtoend/sharded/sharded_keyspace_test.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/test/endtoend/cluster" @@ -109,15 +110,15 @@ func TestShardedKeyspace(t *testing.T) { // apply the schema on the first shard through vtctl, so all tablets // are the same. _, err := shard1Master.VttabletProcess.QueryTablet(sqlSchema, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) _, err = shard1.Vttablets[1].VttabletProcess.QueryTablet(sqlSchema, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) //apply the schema on the second shard. _, err = shard2Master.VttabletProcess.QueryTablet(sqlSchemaReverse, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) _, err = shard2.Vttablets[1].VttabletProcess.QueryTablet(sqlSchemaReverse, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) if err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema); err != nil { log.Error(err.Error()) @@ -131,9 +132,9 @@ func TestShardedKeyspace(t *testing.T) { shard2.Vttablets[1].Alias) err = clusterInstance.VtctlclientProcess.InitShardMaster(keyspaceName, shard1.Name, cell, shard1Master.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.InitShardMaster(keyspaceName, shard2.Name, cell, shard2Master.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) _ = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", shard1Master.Alias) _ = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", shard2Master.Alias) @@ -142,38 +143,38 @@ func TestShardedKeyspace(t *testing.T) { _, _ = shard2Master.VttabletProcess.QueryTablet("insert into vt_select_test (id, msg) values (10, 'test 10')", keyspaceName, true) err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate", "-ping-tablets") - assert.Nil(t, err) + require.Nil(t, err) rows, err := shard1Master.VttabletProcess.QueryTablet("select id, msg from vt_select_test order by id", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, `[[INT64(1) VARCHAR("test 1")]]`, fmt.Sprintf("%v", rows.Rows)) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - assert.Nil(t, err) + require.Nil(t, err) output, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ValidateSchemaKeyspace", keyspaceName) - assert.NotNil(t, err) + require.Error(t, err) assert.True(t, strings.Contains(output, "schemas differ on table vt_select_test:\n"+shard1Master.Alias+": CREATE TABLE")) fmt.Println(output) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateVersionShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("GetPermissions", shard1.Vttablets[1].Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidatePermissionsShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidatePermissionsKeyspace", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) rows, err = shard1Master.VttabletProcess.QueryTablet("select id, msg from vt_select_test order by id", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, `[[INT64(1) VARCHAR("test 1")]]`, fmt.Sprintf("%v", rows.Rows)) rows, err = shard2Master.VttabletProcess.QueryTablet("select id, msg from vt_select_test order by id", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, `[[INT64(10) VARCHAR("test 10")]]`, fmt.Sprintf("%v", rows.Rows)) } diff --git a/go/test/endtoend/sharding/base_sharding.go b/go/test/endtoend/sharding/base_sharding.go index c8d27a2eae0..d3d01a6c30b 100644 --- a/go/test/endtoend/sharding/base_sharding.go +++ b/go/test/endtoend/sharding/base_sharding.go @@ -28,14 +28,13 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - "vitess.io/vitess/go/json2" "vitess.io/vitess/go/test/endtoend/cluster" querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/proto/topodata" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -70,11 +69,11 @@ func CheckSrvKeyspace(t *testing.T, cell string, ksname string, shardingCol stri // GetSrvKeyspace return the Srv Keyspace structure func GetSrvKeyspace(t *testing.T, cell string, ksname string, ci cluster.LocalProcessCluster) *topodata.SrvKeyspace { output, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvKeyspace", cell, ksname) - assert.Nil(t, err) + require.Nil(t, err) var srvKeyspace topodata.SrvKeyspace err = json2.Unmarshal([]byte(output), &srvKeyspace) - assert.Nil(t, err) + require.Nil(t, err) return &srvKeyspace } @@ -82,7 +81,7 @@ func GetSrvKeyspace(t *testing.T, cell string, ksname string, ci cluster.LocalPr func VerifyTabletHealth(t *testing.T, vttablet cluster.Vttablet, hostname string) { tabletURL := fmt.Sprintf("http://%s:%d/healthz", hostname, vttablet.HTTPPort) resp, err := http.Get(tabletURL) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, resp.StatusCode, 200) } @@ -90,13 +89,13 @@ func VerifyTabletHealth(t *testing.T, vttablet cluster.Vttablet, hostname string func VerifyReconciliationCounters(t *testing.T, vtworkerURL string, availabilityType string, table string, inserts int, updates int, deletes int, equals int) { resp, err := http.Get(vtworkerURL) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, resp.StatusCode, 200) resultMap := make(map[string]interface{}) respByte, _ := ioutil.ReadAll(resp.Body) err = json2.Unmarshal(respByte, &resultMap) - assert.Nil(t, err) + require.Nil(t, err) value := getValueFromJSON(resultMap, "Worker"+availabilityType+"InsertsCounters", table) if inserts == 0 { @@ -147,7 +146,7 @@ func CheckValues(t *testing.T, vttablet cluster.Vttablet, id uint64, msg string, } result, err := vttablet.VttabletProcess.QueryTablet(query, ks, true) - assert.Nil(t, err) + require.Nil(t, err) isFound := false if exists && len(result.Rows) > 0 { if keyType == querypb.Type_VARBINARY { @@ -227,11 +226,11 @@ func checkStreamHealthEqualsBinlogPlayerVars(t *testing.T, vttablet cluster.Vtta // tablets may not be started with it, or may not run it in time. _ = ci.VtctlclientProcess.ExecuteCommand("RunHealthCheck", vttablet.Alias) streamHealth, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "-count", "1", vttablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthResponse querypb.StreamHealthResponse err = json2.Unmarshal([]byte(streamHealth), &streamHealthResponse) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") assert.Equal(t, streamHealthResponse.Serving, false) assert.NotNil(t, streamHealthResponse.RealtimeStats) assert.Equal(t, streamHealthResponse.RealtimeStats.HealthError, "") @@ -282,9 +281,9 @@ func InsertToTablet(t *testing.T, query string, vttablet cluster.Vttablet, ks st _, _ = vttablet.VttabletProcess.QueryTablet("begin", ks, true) _, err := vttablet.VttabletProcess.QueryTablet(query, ks, true) if expectFail { - assert.NotNil(t, err) + require.Error(t, err) } else { - assert.Nil(t, err) + require.Nil(t, err) } _, _ = vttablet.VttabletProcess.QueryTablet("commit", ks, true) } @@ -436,10 +435,10 @@ func CheckShardQueryService(t *testing.T, ci cluster.LocalProcessCluster, cell s // GetShardInfo return the Shard information func GetShardInfo(t *testing.T, shard1Ks string, ci cluster.LocalProcessCluster) *topodata.Shard { output, err := ci.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) var shard topodata.Shard err = json2.Unmarshal([]byte(output), &shard) - assert.Nil(t, err) + require.Nil(t, err) return &shard } @@ -469,11 +468,11 @@ func checkThrottlerServiceMaxRates(t *testing.T, server string, names []string, // Check that it's possible to change the max rate on the throttler. newRate := "unlimited" output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ThrottlerSetMaxRate", "--server", server, newRate) - assert.Nil(t, err) + require.Nil(t, err) assert.Contains(t, output, msg) output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ThrottlerMaxRates", "--server", server) - assert.Nil(t, err) + require.Nil(t, err) for _, name := range names { str := fmt.Sprintf("| %s | %s |", name, newRate) assert.Contains(t, output, str) @@ -500,12 +499,12 @@ func checkThrottlerServiceConfiguration(t *testing.T, server string, names []str "bad_rate_increase:0.13 "+ "max_rate_approach_threshold: 0.9 ", ) - assert.Nil(t, err) + require.Nil(t, err) msg := fmt.Sprintf("%d active throttler(s)", len(names)) assert.Contains(t, output, msg) output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("GetThrottlerConfiguration", "--server", server) - assert.Nil(t, err) + require.Nil(t, err) for _, name := range names { str := fmt.Sprintf("| %s | target_replication_lag_sec:12345 ", name) assert.Contains(t, output, str) @@ -515,12 +514,12 @@ func checkThrottlerServiceConfiguration(t *testing.T, server string, names []str // Reset clears our configuration values. output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("ResetThrottlerConfiguration", "--server", server) - assert.Nil(t, err) + require.Nil(t, err) assert.Contains(t, output, msg) // Check that the reset configuration no longer has our values. output, err = ci.VtctlclientProcess.ExecuteCommandWithOutput("GetThrottlerConfiguration", "--server", server) - assert.Nil(t, err) + require.Nil(t, err) assert.NotContains(t, output, "target_replication_lag_sec:12345") assert.Contains(t, output, msg) diff --git a/go/test/endtoend/sharding/initialsharding/sharding_util.go b/go/test/endtoend/sharding/initialsharding/sharding_util.go index 51fb69fdcbf..666e69585d6 100644 --- a/go/test/endtoend/sharding/initialsharding/sharding_util.go +++ b/go/test/endtoend/sharding/initialsharding/sharding_util.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/test/endtoend/sharding" querypb "vitess.io/vitess/go/vt/proto/query" @@ -245,7 +246,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query shard1MasterTablet.VttabletProcess.ExtraArgs = append(shard1MasterTablet.VttabletProcess.ExtraArgs, commonTabletArg...) //var err error err := ClusterInstance.VtctlclientProcess.InitTablet(&shard1MasterTablet, cell, keyspaceName, hostname, shard1.Name) - assert.Nil(t, err) + require.Nil(t, err) shard1.Replica().VttabletProcess.ExtraArgs = append(shard1.Replica().VttabletProcess.ExtraArgs, commonTabletArg...) shard1.Rdonly().VttabletProcess.ExtraArgs = append(shard1.Rdonly().VttabletProcess.ExtraArgs, commonTabletArg...) @@ -253,48 +254,48 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query _ = tablet.VttabletProcess.CreateDB(keyspaceName) } err = shard1MasterTablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) // replica tablet init err = ClusterInstance.VtctlclientProcess.InitTablet(shard1.Replica(), cell, keyspaceName, hostname, shard1.Name) - assert.Nil(t, err) + require.Nil(t, err) // rdonly tablet start err = ClusterInstance.VtctlclientProcess.InitTablet(shard1.Rdonly(), cell, keyspaceName, hostname, shard1.Name) - assert.Nil(t, err) + require.Nil(t, err) if isExternal { shard1.Rdonly().VttabletProcess.ServingStatus = "SERVING" shard1.Replica().VttabletProcess.ServingStatus = "SERVING" } err = shard1.Rdonly().VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) if !isMulti { output, err := ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name), shard1MasterTablet.Alias) - assert.NotNil(t, err, "Should fail as no replica tablet is present.") + require.Error(t, err, "Should fail as no replica tablet is present.") assert.Contains(t, output, fmt.Sprintf("tablet %s ResetReplication failed", shard1.Replica().Alias)) } // start replica err = shard1.Replica().VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) // reparent to make the tablets work if !isExternal { // reparent to make the tablets work err = ClusterInstance.VtctlclientProcess.InitShardMaster(keyspace.Name, shard1.Name, cell, shard1MasterTablet.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) } else { err = shard1.Replica().VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) _, err = ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("TabletExternallyReparented", shard1MasterTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } err = shard1.Replica().VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard1.Rdonly().VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) for _, vttablet := range shard1.Vttablets { assert.Equal(t, vttablet.VttabletProcess.GetTabletStatus(), "SERVING") } @@ -304,16 +305,16 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query sqlSchemaToApply = createTabletTemplateByte } err = ClusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(sqlSchemaToApply, tableName)) - assert.Nil(t, err) + require.Nil(t, err) err = ClusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, fmt.Sprintf(vSchema, tableName, "id")) - assert.Nil(t, err) + require.Nil(t, err) _, err = shard1MasterTablet.VttabletProcess.QueryTablet(fmt.Sprintf(insertTabletTemplate, tableName, uint64(0x1000000000000000), "msg1"), keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) _, err = shard1MasterTablet.VttabletProcess.QueryTablet(fmt.Sprintf(insertTabletTemplate, tableName, uint64(0x9000000000000000), "msg2"), keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) _, err = shard1MasterTablet.VttabletProcess.QueryTablet(fmt.Sprintf(insertTabletTemplate, tableName, uint64(0xD000000000000000), "msg3"), keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // reload schema on all tablets so we can query them for _, vttablet := range shard1.Vttablets { @@ -325,7 +326,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query vtgateInstance.ExtraArgs = []string{"-retry-count", fmt.Sprintf("%d", 2), "-tablet_protocol", "grpc", "-normalize_queries", "-tablet_refresh_interval", "2s"} err = vtgateInstance.Setup() vtgateInstances = append(vtgateInstances, vtgateInstance) - assert.Nil(t, err) + require.Nil(t, err) for _, tabletType := range []string{"master", "replica", "rdonly"} { if err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.%s", keyspaceName, shard1.Name, tabletType), 1); err != nil { @@ -337,7 +338,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query // run a health check on source replica so it responds to discovery err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard1.Replica().Alias) - assert.Nil(t, err) + require.Nil(t, err) // create the split shards shard21 := keyspace.Shards[1] @@ -347,13 +348,13 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query for idx, vttablet := range shard.Vttablets { vttablet.VttabletProcess.ExtraArgs = append(vttablet.VttabletProcess.ExtraArgs, commonTabletArg...) err = ClusterInstance.VtctlclientProcess.InitTablet(vttablet, cell, keyspaceName, hostname, shard.Name) - assert.Nil(t, err) + require.Nil(t, err) _ = vttablet.VttabletProcess.CreateDB(keyspaceName) if isExternal { shard.Vttablets[idx].VttabletProcess.ServingStatus = "" } err = vttablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) } } if !isExternal { @@ -374,9 +375,9 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query } } else { _, err = ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("TabletExternallyReparented", shard21.MasterTablet().Alias) - assert.Nil(t, err) + require.Nil(t, err) _, err = ClusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("TabletExternallyReparented", shard22.MasterTablet().Alias) - assert.Nil(t, err) + require.Nil(t, err) } // must restart vtgate after tablets are up, or else wait until 1min refresh @@ -388,11 +389,11 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query // Wait for the endpoints, either local or remote. for _, shard := range []cluster.Shard{shard1, shard21, shard22} { err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", keyspaceName, shard.Name), 1) - assert.Nil(t, err) + require.Nil(t, err) err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspaceName, shard.Name), 1) - assert.Nil(t, err) + require.Nil(t, err) err = vtgateInstance.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspaceName, shard.Name), 1) - assert.Nil(t, err) + require.Nil(t, err) } // Check srv keyspace @@ -405,15 +406,15 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query err = ClusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--exclude_tables", "unrelated", shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard21.Name)) - assert.Nil(t, err) + require.Nil(t, err) err = ClusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--exclude_tables", "unrelated", shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard22.Name)) - assert.Nil(t, err) + require.Nil(t, err) err = ClusterInstance.StartVtworker(cell, "--use_v3_resharding_mode=true") - assert.Nil(t, err) + require.Nil(t, err) // Initial clone (online). _ = ClusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", @@ -472,7 +473,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query } err = ClusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // check the binlog players are running sharding.CheckDestinationMaster(t, *shard21.MasterTablet(), []string{shard1Ks}, *ClusterInstance) @@ -483,7 +484,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query for _, tablet := range []cluster.Vttablet{*shard21.Rdonly(), *shard22.Rdonly()} { err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } // testing filtered replication: insert a bunch of data on shard 1, @@ -499,9 +500,9 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query sharding.CheckBinlogServerVars(t, *shard1.Replica(), 1000, 1000, false) err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard21.Rdonly().Alias) - assert.Nil(t, err) + require.Nil(t, err) err = ClusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard22.Rdonly().Alias) - assert.Nil(t, err) + require.Nil(t, err) //use vtworker to compare the data ClusterInstance.VtworkerProcess.Cell = cell @@ -511,7 +512,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query "--use_v3_resharding_mode=true", "MultiSplitDiff", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)) - assert.Nil(t, err) + require.Nil(t, err) for _, shard := range []string{shard21.Name, shard22.Name} { err = ClusterInstance.VtworkerProcess.ExecuteVtworkerCommand(ClusterInstance.GetAndReservePort(), @@ -520,7 +521,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query "SplitDiff", "--min_healthy_rdonly_tablets", "1", fmt.Sprintf("%s/%s", keyspaceName, shard)) - assert.Nil(t, err) + require.Nil(t, err) } } @@ -535,11 +536,11 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query // check we can't migrate the master just yet err = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "master") - assert.NotNil(t, err) + require.Error(t, err) // now serve rdonly from the split shards err = ClusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard1.Name} expectedPartitions[topodata.TabletType_REPLICA] = []string{shard1.Name} @@ -598,13 +599,13 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query // check the binlog players are gone now err = shard21.MasterTablet().VttabletProcess.WaitForBinLogPlayerCount(0) - assert.Nil(t, err) + require.Nil(t, err) err = shard22.MasterTablet().VttabletProcess.WaitForBinLogPlayerCount(0) - assert.Nil(t, err) + require.Nil(t, err) // make sure we can't delete a shard with tablets err = ClusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard1Ks) - assert.NotNil(t, err) + require.Error(t, err) if !isMulti { KillTabletsInKeyspace(keyspace) KillVtgateInstances() diff --git a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go index 228452877c3..15aa9fb14bf 100644 --- a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go +++ b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go @@ -38,6 +38,7 @@ import ( "vitess.io/vitess/go/vt/proto/topodata" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -110,7 +111,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Start topo server err := clusterInstance.StartTopo() - assert.Nil(t, err) + require.Nil(t, err) // Defining all the tablets shard0Master := clusterInstance.GetVttabletInstance("replica", 0, "") @@ -157,7 +158,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Initialize Cluster err = clusterInstance.LaunchCluster(keyspace, []cluster.Shard{*shard0, *shard1, *shard2, *shard3}) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, len(clusterInstance.Keyspaces[0].Shards), 4) //Start MySql @@ -182,7 +183,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Rebuild keyspace Graph err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // Get Keyspace and verify the structure srvKeyspace := sharding.GetSrvKeyspace(t, cell, keyspaceName, *clusterInstance) @@ -193,45 +194,45 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { for _, tablet := range shard.Vttablets { // Init Tablet err := clusterInstance.VtctlclientProcess.InitTablet(tablet, tablet.Cell, keyspaceName, hostname, shard.Name) - assert.Nil(t, err) + require.Nil(t, err) // Start the tablet err = tablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) // Create Database _, err = tablet.VttabletProcess.QueryTablet(fmt.Sprintf("create database vt_%s", keyspace.Name), keyspace.Name, false) - assert.Nil(t, err) + require.Nil(t, err) } } // Init Shard Master err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard0.Name), shard0Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name), shard1Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard2.Name), shard2Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) // Init Shard Master on Merge Shard err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard3.Name), shard3Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) // Wait for tablets to come in Service state err = shard0Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard1Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard2Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard3Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) // keyspace/shard name fields shard0Ks := fmt.Sprintf("%s/%s", keyspaceName, shard0.Name) @@ -240,19 +241,19 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // check for shards result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("FindAllShardsInKeyspace", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) resultMap := make(map[string]interface{}) err = json.Unmarshal([]byte(result), &resultMap) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, 4, len(resultMap), "No of shards should be 4") // Apply Schema err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding1", shardingColumnType)) - assert.Nil(t, err) + require.Nil(t, err) // Apply VSchema err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - assert.Nil(t, err) + require.Nil(t, err) // Insert Data insertStartupValues(t) @@ -262,13 +263,13 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { for _, shard := range keyspace.Shards { for _, tablet := range shard.Vttablets { err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } } // Rebuild keyspace Graph err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // check srv keyspace expectedPartitions := map[topodata.TabletType][]string{} @@ -280,11 +281,11 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // we need to create the schema, and the worker will do data copying err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", shard0.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard3.Name)) - assert.Nil(t, err) + require.Nil(t, err) // Run vtworker as daemon for the following SplitClone commands. -use_v3_resharding_mode default is true err = clusterInstance.StartVtworker(cell, "--command_display_interval", "10ms") - assert.Nil(t, err) + require.Nil(t, err) // Initial clone (online). err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", @@ -294,7 +295,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--max_tps", "9999", shard3Ks) - assert.Nil(t, err) + require.Nil(t, err) // Check values in the merge shard checkValues(t, *shard3.MasterTablet(), []string{"INT64(86)", "INT64(1)", `VARCHAR("msg1")`, fmt.Sprintf("UINT64(%d)", key1)}, @@ -304,14 +305,14 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Reset vtworker such that we can run the next command. err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - assert.Nil(t, err) + require.Nil(t, err) // Delete row 2 (provokes an insert). _, err = shard3Master.VttabletProcess.QueryTablet("delete from resharding1 where id=2", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Update row 3 (provokes an update). _, err = shard3Master.VttabletProcess.QueryTablet("update resharding1 set msg='msg-not-1' where id=1", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Insert row 4 (provokes a delete). insertValue(t, shard3.MasterTablet(), keyspaceName, tableName, 4, "msg4", key3) @@ -323,28 +324,28 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--max_tps", "9999", shard3Ks) - assert.Nil(t, err) + require.Nil(t, err) // Change tablet, which was taken offline, back to rdonly. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard0Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // Terminate worker daemon because it is no longer needed. err = clusterInstance.VtworkerProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) // Check startup values checkStartupValues(t, shardingKeyType) // check the schema too err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // Verify vreplication table entries qr, err := shard3.MasterTablet().VttabletProcess.QueryTabletWithDB("select * from vreplication", "_vt") - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, 2, len(qr.Rows)) assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"-40\" key_range: "`) @@ -359,7 +360,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // accidentally modify data on the destination masters while they are not // migrated yet and the source shards are still the source of truth. err = shard3Master.VttabletProcess.WaitForTabletType("NOT_SERVING") - assert.Nil(t, err) + require.Nil(t, err) // check that binlog server exported the stats vars sharding.CheckBinlogServerVars(t, *shard0Replica, 0, 0, false) @@ -381,7 +382,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // use vtworker to compare the data (after health-checking the destination // rdonly tablets so discovery works) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard3Rdonly.Alias) - assert.Nil(t, err) + require.Nil(t, err) // use vtworker to compare the data clusterInstance.VtworkerProcess.Cell = cell @@ -396,12 +397,12 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--source_uid", "1", shard3Ks) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard0Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard3Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) log.Debug("Running vtworker SplitDiff on second half") @@ -413,12 +414,12 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--source_uid", "2", shard3Ks) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard3Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // get status for destination master tablets, make sure we have it all sharding.CheckRunningBinlogPlayer(t, *shard3Master, 300, 100) @@ -427,12 +428,12 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { streamHealth, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( "VtTabletStreamHealth", "-count", "1", shard3Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) log.Debug("Got health: ", streamHealth) var streamHealthResponse querypb.StreamHealthResponse err = json.Unmarshal([]byte(streamHealth), &streamHealthResponse) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, streamHealthResponse.Serving, false) assert.NotNil(t, streamHealthResponse.RealtimeStats) @@ -443,7 +444,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // now serve rdonly from the split shards, in cell1 only err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", shard3Ks, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // check srv keyspace expectedPartitions = map[topodata.TabletType][]string{} @@ -458,7 +459,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Now serve replica from the split shards err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", shard3Ks, "replica") - assert.Nil(t, err) + require.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name, shard2.Name} @@ -469,7 +470,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // now serve master from the split shards err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", shard3Ks, "master") - assert.Nil(t, err) + require.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard3.Name, shard2.Name} @@ -485,7 +486,7 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { // check the binlog players are gone now err = shard3Master.VttabletProcess.WaitForBinLogPlayerCount(0) - assert.Nil(t, err) + require.Nil(t, err) // delete the original tablets in the original shard for _, shard := range []cluster.Shard{*shard0, *shard1} { @@ -497,17 +498,17 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { for _, tablet := range []cluster.Vttablet{*shard0Replica, *shard1Replica, *shard0Rdonly, *shard1Rdonly} { err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } for _, tablet := range []cluster.Vttablet{*shard0Master, *shard1Master} { err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", "-allow_master", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } // rebuild the serving graph, all mentions of the old shards should be gone err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) } @@ -590,10 +591,10 @@ func checkValues(t *testing.T, vttablet cluster.Vttablet, values []string, id ui var err error if dbConn != nil { result, err = dbConn.ExecuteFetch(query, 1000, true) - assert.Nil(t, err) + require.Nil(t, err) } else { result, err = vttablet.VttabletProcess.QueryTablet(query, ks, true) - assert.Nil(t, err) + require.Nil(t, err) } isFound := false diff --git a/go/test/endtoend/sharding/resharding/resharding_base.go b/go/test/endtoend/sharding/resharding/resharding_base.go index 4068a3b39bd..b58eda4bfc3 100644 --- a/go/test/endtoend/sharding/resharding/resharding_base.go +++ b/go/test/endtoend/sharding/resharding/resharding_base.go @@ -38,6 +38,7 @@ import ( "vitess.io/vitess/go/vt/proto/topodata" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -190,13 +191,13 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Start topo server err := clusterInstance.StartTopo() - assert.Nil(t, err) + require.Nil(t, err) // Adding another cell in the same cluster err = clusterInstance.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+cell2) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlProcess.AddCellInfo(cell2) - assert.Nil(t, err) + require.Nil(t, err) // Defining all the tablets shard0Master := clusterInstance.GetVttabletInstance("replica", 0, "") @@ -246,7 +247,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Initialize Cluster err = clusterInstance.LaunchCluster(keyspace, []cluster.Shard{*shard0, *shard1, *shard2, *shard3}) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, len(clusterInstance.Keyspaces[0].Shards), 4) //Start MySql @@ -271,7 +272,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Rebuild keyspace Graph err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // Get Keyspace and verify the structure srvKeyspace := sharding.GetSrvKeyspace(t, cell1, keyspaceName, *clusterInstance) @@ -282,44 +283,44 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { for _, tablet := range shard.Vttablets { // Init Tablet err := clusterInstance.VtctlclientProcess.InitTablet(tablet, tablet.Cell, keyspaceName, hostname, shard.Name) - assert.Nil(t, err) + require.Nil(t, err) // Start the tablet err = tablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) // Create Database _, err = tablet.VttabletProcess.QueryTablet(fmt.Sprintf("create database vt_%s", keyspace.Name), keyspace.Name, false) - assert.Nil(t, err) + require.Nil(t, err) } } // Init Shard Master err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard0.Name), shard0Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name), shard1Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) // Init Shard Master on Split Shards err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard2.Name), shard2Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("InitShardMaster", "-force", fmt.Sprintf("%s/%s", keyspaceName, shard3.Name), shard3Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) // Wait for tablets to come in Service state err = shard0Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard1Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard2Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard3Master.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) // keyspace/shard name fields shard0Ks := fmt.Sprintf("%s/%s", keyspaceName, shard0.Name) @@ -329,31 +330,31 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // check for shards result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("FindAllShardsInKeyspace", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) resultMap := make(map[string]interface{}) err = json.Unmarshal([]byte(result), &resultMap) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, 4, len(resultMap), "No of shards should be 4") // Apply Schema err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding1", shardingColumnType)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTabletTemplate, "resharding2", shardingColumnType)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTableBindataTemplate, "resharding3", shardingColumnType)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createViewTemplate, "view1", "resharding3")) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createNoPkTable, shardingColumnType)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(createTimestampTable, shardingColumnType)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, createUnrelatedTable) - assert.Nil(t, err) + require.Nil(t, err) // Apply VSchema err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema) - assert.Nil(t, err) + require.Nil(t, err) // Insert Data insertStartupValues(t) @@ -363,13 +364,13 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { for _, shard := range keyspace.Shards { for _, tablet := range shard.Vttablets { err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } } // Rebuild keyspace Graph err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // check srv keyspace expectedPartitions := map[topodata.TabletType][]string{} @@ -380,22 +381,22 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // disable shard1Replica2, so we're sure filtered replication will go from shard1Replica1 err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Replica2.Alias, "spare") - assert.Nil(t, err) + require.Nil(t, err) err = shard1Replica2.VttabletProcess.WaitForTabletType("NOT_SERVING") - assert.Nil(t, err) + require.Nil(t, err) // we need to create the schema, and the worker will do data copying err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--exclude_tables", "unrelated", shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard2.Name)) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--exclude_tables", "unrelated", shard1.Rdonly().Alias, fmt.Sprintf("%s/%s", keyspaceName, shard3.Name)) - assert.Nil(t, err) + require.Nil(t, err) // Run vtworker as daemon for the following SplitClone commands. -use_v3_resharding_mode default is true err = clusterInstance.StartVtworker(cell1, "--command_display_interval", "10ms") - assert.Nil(t, err) + require.Nil(t, err) // Copy the data from the source to the destination shards. // --max_tps is only specified to enable the throttler and ensure that the @@ -411,7 +412,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--max_tps", "9999", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) // Check values in the split shard checkValues(t, *shard2.MasterTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, @@ -421,12 +422,12 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Reset vtworker such that we can run the next command. err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - assert.Nil(t, err) + require.Nil(t, err) // Test the correct handling of keyspace_id changes which happen after the first clone. sql := fmt.Sprintf("update resharding1 set custom_ksid_col=%d WHERE id=2", key3) _, err = shard1Master.VttabletProcess.QueryTablet(sql, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--offline=false", @@ -436,7 +437,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--max_tps", "9999", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) checkValues(t, *shard2.MasterTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key3)}, 2, false, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) @@ -444,12 +445,12 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - assert.Nil(t, err) + require.Nil(t, err) // Move row 2 back to shard 2 from shard 3 by changing the keyspace_id again sql = fmt.Sprintf("update resharding1 set custom_ksid_col=%d WHERE id=2", key2) _, err = shard1Master.VttabletProcess.QueryTablet(sql, keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--offline=false", @@ -459,7 +460,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--max_tps", "9999", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) checkValues(t, *shard2.MasterTablet(), []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, 2, true, tableName, fixedParentID, keyspaceName, shardingKeyType, nil) @@ -468,16 +469,16 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Reset vtworker such that we can run the next command. err = clusterInstance.VtworkerProcess.ExecuteCommand("Reset") - assert.Nil(t, err) + require.Nil(t, err) // Modify the destination shard. SplitClone will revert the changes. // Delete row 2 (provokes an insert). _, err = shard2Master.VttabletProcess.QueryTablet("delete from resharding1 where id=2", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Update row 3 (provokes an update). _, err = shard3Master.VttabletProcess.QueryTablet("update resharding1 set msg='msg-not-3' where id=3", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) // Insert row 4 and 5 (provokes a delete). insertValue(t, shard3.MasterTablet(), keyspaceName, tableName, 4, "msg4", key3) @@ -490,32 +491,32 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--min_healthy_rdonly_tablets", "1", "--max_tps", "9999", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) // Change tablet, which was taken offline, back to rdonly. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // Terminate worker daemon because it is no longer needed. err = clusterInstance.VtworkerProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) // Check startup values checkStartupValues(t, shardingKeyType) // check the schema too err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaKeyspace", "--exclude_tables=unrelated", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // Verify vreplication table entries qr, err := shard2Master.VttabletProcess.QueryTabletWithDB("select * from vreplication", "_vt") - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, 1, len(qr.Rows)) assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"80-\" key_range: "`) qr, err = shard3.MasterTablet().VttabletProcess.QueryTabletWithDB("select * from vreplication", "_vt") - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, 1, len(qr.Rows)) assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `"keyspace:\"ks\" shard:\"80-\" key_range: "`) @@ -530,9 +531,9 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // accidentally modify data on the destination masters while they are not // migrated yet and the source shards are still the source of truth. err = shard2Master.VttabletProcess.WaitForTabletType("NOT_SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard3Master.VttabletProcess.WaitForTabletType("NOT_SERVING") - assert.Nil(t, err) + require.Nil(t, err) // check that binlog server exported the stats vars sharding.CheckBinlogServerVars(t, *shard1Replica1, 0, 0, false) @@ -566,7 +567,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // use vtworker to compare the data (after health-checking the destination // rdonly tablets so discovery works) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard3Rdonly.Alias) - assert.Nil(t, err) + require.Nil(t, err) // use vtworker to compare the data clusterInstance.VtworkerProcess.Cell = cell1 @@ -580,7 +581,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--exclude_tables", "unrelated", "--min_healthy_rdonly_tablets", "1", shard3Ks) - assert.Nil(t, err) + require.Nil(t, err) // Compare using MultiSplitDiff log.Debug("Running vtworker MultiSplitDiff") @@ -590,12 +591,12 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "MultiSplitDiff", "--exclude_tables", "unrelated", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard3Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // get status for destination master tablets, make sure we have it all sharding.CheckRunningBinlogPlayer(t, *shard2Master, 436, 216) @@ -603,17 +604,17 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // tests a failover switching serving to a different replica err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Replica2.Alias, "replica") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Replica1.Alias, "spare") - assert.Nil(t, err) + require.Nil(t, err) err = shard1Replica2.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = shard1Replica1.VttabletProcess.WaitForTabletType("NOT_SERVING") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard1Replica2.Alias) - assert.Nil(t, err) + require.Nil(t, err) // test data goes through again log.Debug("Inserting lots of data on source shard") @@ -625,24 +626,24 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // check we can't migrate the master just yet err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "master") - assert.NotNil(t, err, "MigrateServedTypes should fail") + require.Error(t, err, "MigrateServedTypes should fail") // check query service is off on master 2 and master 3, as filtered replication is enabled. // Even health check that is enabled on master 3 should not interfere (we run it to be sure). err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", shard3Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) for _, master := range []cluster.Vttablet{*shard2Master, *shard3Master} { sharding.CheckTabletQueryService(t, master, "NOT_SERVING", false, *clusterInstance) streamHealth, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( "VtTabletStreamHealth", "-count", "1", master.Alias) - assert.Nil(t, err) + require.Nil(t, err) log.Debug("Got health: ", streamHealth) var streamHealthResponse querypb.StreamHealthResponse err = json.Unmarshal([]byte(streamHealth), &streamHealthResponse) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, streamHealthResponse.Serving, false) assert.NotNil(t, streamHealthResponse.RealtimeStats) @@ -656,7 +657,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", fmt.Sprintf("--cells=%s", cell1), shard1Ks, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // check srv keyspace expectedPartitions = map[topodata.TabletType][]string{} @@ -679,7 +680,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // Shouldn't be able to rebuild keyspace graph while migration is on going // (i.e there are records that have tablet controls set) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.NotNil(t, err, "Error expected") + require.Error(t, err, "Error expected") // rerun migrate to ensure it doesn't fail // skip refresh to make it go faster @@ -688,13 +689,13 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { fmt.Sprintf("--cells=%s", cell1), "-skip-refresh-state=true", shard1Ks, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // now serve rdonly from the split shards, everywhere err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", shard1Ks, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name} @@ -714,13 +715,13 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "MigrateServedTypes", "-skip-refresh-state=true", shard1Ks, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // then serve replica from the split shards destinationShards := []cluster.Shard{*shard2, *shard3} err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", shard1Ks, "replica") - assert.Nil(t, err) + require.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name} @@ -732,7 +733,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", "-reverse", shard1Ks, "replica") - assert.Nil(t, err) + require.Nil(t, err) // After a backwards migration, queryservice should be enabled on // source and disabled on destinations @@ -753,7 +754,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { err = clusterInstance.VtctlclientProcess.ExecuteCommand( "MigrateServedTypes", shard1Ks, "replica") - assert.Nil(t, err) + require.Nil(t, err) // After a forwards migration, queryservice should be disabled on // source and enabled on destinations sharding.CheckTabletQueryService(t, *shard1Replica2, "NOT_SERVING", true, *clusterInstance) @@ -773,7 +774,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // reparent shard2 to shard2Replica1, then insert more data and see it flow through still err = clusterInstance.VtctlclientProcess.ExecuteCommand("PlannedReparentShard", "-keyspace_shard", shard2Ks, "-new_master", shard2Replica1.Alias) - assert.Nil(t, err) + require.Nil(t, err) // update our test variables to point at the new master tmp := shard2Master @@ -794,7 +795,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "--exclude_tables", "unrelated", "--min_healthy_rdonly_tablets", "1", shard3Ks) - assert.Nil(t, err) + require.Nil(t, err) // Compare using MultiSplitDiff log.Debug("Running vtworker MultiSplitDiff") @@ -804,32 +805,32 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "MultiSplitDiff", "--exclude_tables", "unrelated", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard1Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", shard3Rdonly.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // going to migrate the master now // mock with the SourceShard records to test 'vtctl SourceShardDelete' and 'vtctl SourceShardAdd' err = clusterInstance.VtctlclientProcess.ExecuteCommand("SourceShardDelete", shard3Ks, "1") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("SourceShardAdd", "--key_range=80-", shard3Ks, "1", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) // CancelResharding should fail because migration has started. err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", shard1Ks, "1") - assert.NotNil(t, err) + require.Error(t, err) // do a Migrate that will fail waiting for replication // which should cause the Migrate to be canceled and the source // master to be serving again. err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "-filtered_replication_wait_time", "0s", shard1Ks, "master") - assert.NotNil(t, err) + require.Error(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard1.Name} @@ -843,11 +844,11 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "-blacklisted_tables=t", shard3Ks, "master") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "master") - assert.NotNil(t, err) + require.Error(t, err) // Query service is disabled in source shard as failure occurred after point of no return sharding.CheckTabletQueryService(t, *shard1Master, "NOT_SERVING", true, *clusterInstance) @@ -868,17 +869,17 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // because this failure is past the point of no return. err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "-blacklisted_tables=t", "-remove", shard3Ks, "master") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "-filtered_replication_wait_time", "0s", shard1Ks, "master") - assert.NotNil(t, err) + require.Error(t, err) sharding.CheckTabletQueryService(t, *shard1Master, "NOT_SERVING", true, *clusterInstance) // do the migration that's expected to succeed err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", shard1Ks, "master") - assert.Nil(t, err) + require.Nil(t, err) expectedPartitions = map[topodata.TabletType][]string{} expectedPartitions[topodata.TabletType_MASTER] = []string{shard0.Name, shard2.Name, shard3.Name} expectedPartitions[topodata.TabletType_RDONLY] = []string{shard0.Name, shard2.Name, shard3.Name} @@ -893,9 +894,9 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // check the binlog players are gone now err = shard2Master.VttabletProcess.WaitForBinLogPlayerCount(0) - assert.Nil(t, err) + require.Nil(t, err) err = shard3Master.VttabletProcess.WaitForBinLogPlayerCount(0) - assert.Nil(t, err) + require.Nil(t, err) // test reverse_replication // start with inserting a row in each destination shard @@ -911,7 +912,7 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { // repeat the migration with reverse_replication err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedTypes", "-reverse_replication=true", shard1Ks, "master") - assert.Nil(t, err) + require.Nil(t, err) // look for the rows in the original master after a short wait time.Sleep(1 * time.Second) checkValues(t, *shard1Master, []string{"INT64(86)", "INT64(2)", `VARCHAR("msg2")`, fmt.Sprintf("UINT64(%d)", key2)}, @@ -924,13 +925,13 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { "MigrateServedTypes", "-reverse_replication=true", shard1Ks, "master") - assert.NotNil(t, err) + require.Error(t, err) // CancelResharding should now succeed err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) err = shard1Master.VttabletProcess.WaitForBinLogPlayerCount(0) - assert.Nil(t, err) + require.Nil(t, err) // delete the original tablets in the original shard for _, tablet := range []cluster.Vttablet{*shard1Master, *shard1Replica1, *shard1Replica2, *shard1Rdonly, *shard1RdonlyZ2} { @@ -940,33 +941,33 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { for _, tablet := range []cluster.Vttablet{*shard1Replica1, *shard1Replica2, *shard1Rdonly, *shard1RdonlyZ2} { err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", "-allow_master", shard1Master.Alias) - assert.Nil(t, err) + require.Nil(t, err) // rebuild the serving graph, all mentions of the old shards should be gone err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // test RemoveShardCell err = clusterInstance.VtctlclientProcess.ExecuteCommand("RemoveShardCell", shard0Ks, cell1) - assert.NotNil(t, err) + require.Error(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RemoveShardCell", shard1Ks, cell1) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RemoveShardCell", shard1Ks, cell2) - assert.Nil(t, err) + require.Nil(t, err) shardInfo = sharding.GetShardInfo(t, shard1Ks, *clusterInstance) assert.Empty(t, shardInfo.GetTabletControls(), "cells not in shard ") // delete the original shard err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard1Ks) - assert.Nil(t, err) + require.Nil(t, err) // make sure we can't delete the destination shard now that it's serving err = clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteShard", shard2Ks) - assert.NotNil(t, err) + require.Error(t, err) } @@ -1200,10 +1201,10 @@ func checkValues(t *testing.T, vttablet cluster.Vttablet, values []string, id ui var err error if dbConn != nil { result, err = dbConn.ExecuteFetch(query, 1000, true) - assert.Nil(t, err) + require.Nil(t, err) } else { result, err = vttablet.VttabletProcess.QueryTablet(query, ks, true) - assert.Nil(t, err) + require.Nil(t, err) } isFound := false diff --git a/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go b/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go index 4fee29f532d..43fb10a0ea9 100644 --- a/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go +++ b/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go @@ -31,6 +31,7 @@ import ( "vitess.io/vitess/go/vt/vtgate/vtgateconn" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" @@ -79,15 +80,15 @@ func TestVerticalSplit(t *testing.T) { // Adding another cell in the same cluster err = clusterInstance.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+"test_ca") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlProcess.AddCellInfo("test_ca") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.TopoProcess.ManageTopoDir("mkdir", "/vitess/"+"test_ny") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlProcess.AddCellInfo("test_ny") - assert.Nil(t, err) + require.Nil(t, err) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // source keyspace, with 4 tables sourceShard := clusterInstance.Keyspaces[0].Shards[0] @@ -100,9 +101,9 @@ func TestVerticalSplit(t *testing.T) { // source tablets init for _, tablet := range []cluster.Vttablet{sourceMasterTablet, sourceReplicaTablet, sourceRdOnlyTablet1, sourceRdOnlyTablet2} { err = clusterInstance.VtctlclientProcess.InitTablet(&tablet, cellj, sourceKeyspace, hostname, sourceShard.Name) - assert.Nil(t, err) + require.Nil(t, err) err = tablet.VttabletProcess.CreateDB(sourceKeyspace) - assert.Nil(t, err) + require.Nil(t, err) } // destination keyspace, with just two tables @@ -115,39 +116,39 @@ func TestVerticalSplit(t *testing.T) { // destination tablets init for _, tablet := range []cluster.Vttablet{destinationMasterTablet, destinationReplicaTablet, destinationRdOnlyTablet1, destinationRdOnlyTablet2} { err = clusterInstance.VtctlclientProcess.InitTablet(&tablet, cellj, destinationKeyspace, hostname, destinationShard.Name) - assert.Nil(t, err) + require.Nil(t, err) err = tablet.VttabletProcess.CreateDB(destinationKeyspace) - assert.Nil(t, err) + require.Nil(t, err) } // RebuildKeyspaceGraph source keyspace err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", sourceKeyspace) - assert.Nil(t, err) + require.Nil(t, err) // RebuildKeyspaceGraph destination keyspace err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", destinationKeyspace) - assert.Nil(t, err) + require.Nil(t, err) // source schema for _, tablet := range []cluster.Vttablet{sourceMasterTablet, sourceReplicaTablet, sourceRdOnlyTablet1, sourceRdOnlyTablet2} { for _, tableName := range tableArr { _, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf(createTabletTemplate, tableName), sourceKeyspace, true) - assert.Nil(t, err) + require.Nil(t, err) } _, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf(createViewTemplate, "view1", "moving1"), sourceKeyspace, true) - assert.Nil(t, err) + require.Nil(t, err) // RBR (default behaviour) only because Vitess requires the primary key for query rewrites if // it is running with statement based replication. _, err = tablet.VttabletProcess.QueryTablet(createMoving3NoPkTable, sourceKeyspace, true) - assert.Nil(t, err) + require.Nil(t, err) } // destination schema // Insert data directly because vtgate would redirect us. for _, tablet := range []cluster.Vttablet{destinationMasterTablet, destinationReplicaTablet, destinationRdOnlyTablet1, destinationRdOnlyTablet2} { _, err = tablet.VttabletProcess.QueryTablet(fmt.Sprintf(createTabletTemplate, "extra1"), destinationKeyspace, true) - assert.Nil(t, err) + require.Nil(t, err) } // source and destination master and replica tablets will be started @@ -166,20 +167,20 @@ func TestVerticalSplit(t *testing.T) { // reparent to make the tablets work (we use health check, fix their types) err = clusterInstance.VtctlclientProcess.InitShardMaster(sourceKeyspace, shardName, cellj, sourceMasterTablet.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.InitShardMaster(destinationKeyspace, shardName, cellj, destinationMasterTablet.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) sourceMasterTablet.Type = "master" destinationMasterTablet.Type = "master" for _, tablet := range []cluster.Vttablet{sourceReplicaTablet, destinationReplicaTablet} { _ = tablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) } for _, tablet := range []cluster.Vttablet{sourceRdOnlyTablet1, sourceRdOnlyTablet2, destinationRdOnlyTablet1, destinationRdOnlyTablet2} { _ = tablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) } for _, tablet := range []cluster.Vttablet{sourceMasterTablet, destinationMasterTablet, sourceReplicaTablet, destinationReplicaTablet, sourceRdOnlyTablet1, sourceRdOnlyTablet2, destinationRdOnlyTablet1, destinationRdOnlyTablet2} { @@ -187,7 +188,7 @@ func TestVerticalSplit(t *testing.T) { } err = clusterInstance.StartVtgate() - assert.Nil(t, err) + require.Nil(t, err) vtParams := mysql.ConnParams{ Host: clusterInstance.Hostname, @@ -196,27 +197,27 @@ func TestVerticalSplit(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - assert.Nil(t, err) + require.Nil(t, err) defer conn.Close() err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", sourceKeyspace, shardName), 1) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", sourceKeyspace, shardName), 1) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", sourceKeyspace, shardName), 2) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", destinationKeyspace, shardName), 1) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", destinationKeyspace, shardName), 1) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", destinationKeyspace, shardName), 2) - assert.Nil(t, err) + require.Nil(t, err) // create the schema on the source keyspace, add some values insertInitialValues(t, conn, sourceMasterTablet, destinationMasterTablet) err = clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", "--tables", "/moving/,view1", sourceRdOnlyTablet1.Alias, "destination_keyspace/0") - assert.Nil(t, err, "CopySchemaShard failed") + require.Nil(t, err, "CopySchemaShard failed") // starting vtworker httpPort := clusterInstance.GetAndReservePort() @@ -237,13 +238,13 @@ func TestVerticalSplit(t *testing.T) { "--chunk_count", "10", "--min_rows_per_chunk", "1", "--min_healthy_tablets", "1", "destination_keyspace/0") - assert.Nil(t, err) + require.Nil(t, err) // test Cancel first err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", "destination_keyspace/0") - assert.Nil(t, err) + require.Nil(t, err) err = destinationMasterTablet.VttabletProcess.WaitForBinLogPlayerCount(0) - assert.Nil(t, err) + require.Nil(t, err) // master should be in serving state after cancel sharding.CheckTabletQueryServices(t, []cluster.Vttablet{destinationMasterTablet}, "SERVING", false, *clusterInstance) @@ -257,7 +258,7 @@ func TestVerticalSplit(t *testing.T) { "--chunk_count", "10", "--min_rows_per_chunk", "1", "--min_healthy_tablets", "1", "destination_keyspace/0") - assert.Nil(t, err) + require.Nil(t, err) // check values are present checkValues(t, &destinationMasterTablet, destinationKeyspace, "vt_destination_keyspace", "moving1", moving1First, 100) @@ -272,9 +273,9 @@ func TestVerticalSplit(t *testing.T) { } dbParams.DbName = "_vt" dbConn, err := mysql.Connect(ctx, &dbParams) - assert.Nil(t, err) + require.Nil(t, err) qr, err := dbConn.ExecuteFetch("select * from vreplication", 1000, true) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") assert.Equal(t, 1, len(qr.Rows)) assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "SplitClone") assert.Contains(t, fmt.Sprintf("%v", qr.Rows), `keyspace:\"source_keyspace\" shard:\"0\" tables:\"/moving/\" tables:\"view1\`) @@ -304,7 +305,7 @@ func TestVerticalSplit(t *testing.T) { "VerticalSplitDiff", "--min_healthy_rdonly_tablets", "1", "destination_keyspace/0") - assert.Nil(t, err) + require.Nil(t, err) // get status for destination master tablet, make sure we have it all sharding.CheckRunningBinlogPlayer(t, destinationMasterTablet, 700, 300) @@ -317,12 +318,12 @@ func TestVerticalSplit(t *testing.T) { // check we can't migrate the master just yet err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "master") - assert.NotNil(t, err) + require.Error(t, err) // migrate rdonly only in test_ny cell, make sure nothing is migrated // in test_nj err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "--cells=test_ny", "destination_keyspace/0", "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // check SrvKeyspace checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, ksServedFrom, *clusterInstance) @@ -334,30 +335,30 @@ func TestVerticalSplit(t *testing.T) { // migrate test_nj only, using command line manual fix command, // and restore it back. keyspaceJSON, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetKeyspace", "destination_keyspace") - assert.Nil(t, err) + require.Nil(t, err) validateKeyspaceJSON(t, keyspaceJSON, []string{"test_ca", "test_nj"}) err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetKeyspaceServedFrom", "-source=source_keyspace", "-remove", "-cells=test_nj,test_ca", "destination_keyspace", "rdonly") - assert.Nil(t, err) + require.Nil(t, err) // again validating keyspaceJSON keyspaceJSON, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetKeyspace", "destination_keyspace") - assert.Nil(t, err) + require.Nil(t, err) validateKeyspaceJSON(t, keyspaceJSON, nil) err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetKeyspaceServedFrom", "-source=source_keyspace", "destination_keyspace", "rdonly") - assert.Nil(t, err) + require.Nil(t, err) keyspaceJSON, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetKeyspace", "destination_keyspace") - assert.Nil(t, err) + require.Nil(t, err) validateKeyspaceJSON(t, keyspaceJSON, []string{}) // now serve rdonly from the destination shards err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "rdonly") - assert.Nil(t, err) + require.Nil(t, err) checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(master): source_keyspace\nServedFrom(replica): source_keyspace\n", *clusterInstance) checkBlacklistedTables(t, sourceMasterTablet, sourceKeyspace, nil) checkBlacklistedTables(t, sourceReplicaTablet, sourceKeyspace, nil) @@ -366,14 +367,14 @@ func TestVerticalSplit(t *testing.T) { grpcAddress := fmt.Sprintf("%s:%d", "localhost", clusterInstance.VtgateProcess.GrpcPort) gconn, err := vtgateconn.Dial(ctx, grpcAddress) - assert.Nil(t, err) + require.Nil(t, err) defer gconn.Close() checkClientConnRedirectionExecuteKeyrange(ctx, t, gconn, destinationKeyspace, []topodata.TabletType{topodata.TabletType_MASTER, topodata.TabletType_REPLICA}, []string{"moving1", "moving2"}) // then serve replica from the destination shards err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "replica") - assert.Nil(t, err) + require.Nil(t, err) checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(master): source_keyspace\n", *clusterInstance) checkBlacklistedTables(t, sourceMasterTablet, sourceKeyspace, nil) checkBlacklistedTables(t, sourceReplicaTablet, sourceKeyspace, []string{"/moving/", "view1"}) @@ -383,7 +384,7 @@ func TestVerticalSplit(t *testing.T) { // move replica back and forth err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "-reverse", "destination_keyspace/0", "replica") - assert.Nil(t, err) + require.Nil(t, err) checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(master): source_keyspace\nServedFrom(replica): source_keyspace\n", *clusterInstance) checkBlacklistedTables(t, sourceMasterTablet, sourceKeyspace, nil) checkBlacklistedTables(t, sourceReplicaTablet, sourceKeyspace, nil) @@ -391,7 +392,7 @@ func TestVerticalSplit(t *testing.T) { checkBlacklistedTables(t, sourceRdOnlyTablet2, sourceKeyspace, []string{"/moving/", "view1"}) err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "replica") - assert.Nil(t, err) + require.Nil(t, err) checkSrvKeyspaceServedFrom(t, cellj, destinationKeyspace, "ServedFrom(master): source_keyspace\n", *clusterInstance) checkBlacklistedTables(t, sourceMasterTablet, sourceKeyspace, nil) checkBlacklistedTables(t, sourceReplicaTablet, sourceKeyspace, []string{"/moving/", "view1"}) @@ -401,7 +402,7 @@ func TestVerticalSplit(t *testing.T) { // Cancel should fail now err = clusterInstance.VtctlclientProcess.ExecuteCommand("CancelResharding", "destination_keyspace/0") - assert.NotNil(t, err) + require.Error(t, err) // then serve master from the destination shards err = clusterInstance.VtctlclientProcess.ExecuteCommand("MigrateServedFrom", "destination_keyspace/0", "master") @@ -420,15 +421,15 @@ func TestVerticalSplit(t *testing.T) { // now remove the tables on the source shard. The blacklisted tables // in the source shard won't match any table, make sure that works. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ApplySchema", "-sql=drop view view1", "source_keyspace") - assert.Nil(t, err) + require.Nil(t, err) for _, table := range []string{"moving1", "moving2"} { err = clusterInstance.VtctlclientProcess.ExecuteCommand("ApplySchema", "--sql=drop table "+table, "source_keyspace") - assert.Nil(t, err) + require.Nil(t, err) } for _, tablet := range []cluster.Vttablet{sourceMasterTablet, sourceReplicaTablet, sourceRdOnlyTablet1, sourceRdOnlyTablet2} { err = clusterInstance.VtctlclientProcess.ExecuteCommand("ReloadSchema", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) } qr, _ = sourceMasterTablet.VttabletProcess.QueryTablet("select count(1) from staying1", sourceKeyspace, true) assert.Equal(t, 1, len(qr.Rows), fmt.Sprintf("cannot read staying1: got %d", len(qr.Rows))) @@ -441,21 +442,21 @@ func TestVerticalSplit(t *testing.T) { func verifyVtctlSetShardTabletControl(t *testing.T) { // clear the rdonly entry: err := clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--remove", "source_keyspace/0", "rdonly") - assert.Nil(t, err) + require.Nil(t, err) assertTabletControls(t, clusterInstance, []topodata.TabletType{topodata.TabletType_MASTER, topodata.TabletType_REPLICA}) // re-add rdonly: err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--blacklisted_tables=/moving/,view1", "source_keyspace/0", "rdonly") - assert.Nil(t, err) + require.Nil(t, err) assertTabletControls(t, clusterInstance, []topodata.TabletType{topodata.TabletType_MASTER, topodata.TabletType_REPLICA, topodata.TabletType_RDONLY}) //and then clear all entries: err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--remove", "source_keyspace/0", "rdonly") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--remove", "source_keyspace/0", "replica") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetShardTabletControl", "--remove", "source_keyspace/0", "master") - assert.Nil(t, err) + require.Nil(t, err) shardJSON, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", "source_keyspace/0") var shardJSONData topodata.Shard @@ -466,10 +467,10 @@ func verifyVtctlSetShardTabletControl(t *testing.T) { func assertTabletControls(t *testing.T, clusterInstance *cluster.LocalProcessCluster, aliases []topodata.TabletType) { shardJSON, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShard", "source_keyspace/0") - assert.Nil(t, err) + require.Nil(t, err) var shardJSONData topodata.Shard err = json2.Unmarshal([]byte(shardJSON), &shardJSONData) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, len(shardJSONData.TabletControls), len(aliases)) for _, tc := range shardJSONData.TabletControls { assert.Contains(t, aliases, tc.TabletType) @@ -480,23 +481,23 @@ func assertTabletControls(t *testing.T, clusterInstance *cluster.LocalProcessClu func checkStats(t *testing.T) { resultMap, err := clusterInstance.VtgateProcess.GetVars() - assert.Nil(t, err) - resultVtTabletCall := resultMap["VttabletCall"] - resultVtTabletCallMap := resultVtTabletCall.(map[string]interface{}) - resultHistograms := resultVtTabletCallMap["Histograms"] - resultHistogramsMap := resultHistograms.(map[string]interface{}) - resultTablet := resultHistogramsMap["Execute.source_keyspace.0.replica"] - resultTableMap := resultTablet.(map[string]interface{}) + require.Nil(t, err) + resultVtTabletCall, _ := resultMap["VttabletCall"] + resultVtTabletCallMap, _ := resultVtTabletCall.(map[string]interface{}) + resultHistograms, _ := resultVtTabletCallMap["Histograms"] + resultHistogramsMap, _ := resultHistograms.(map[string]interface{}) + resultTablet, _ := resultHistogramsMap["Execute.source_keyspace.0.replica"] + resultTableMap, _ := resultTablet.(map[string]interface{}) resultCountStr := fmt.Sprintf("%v", reflect.ValueOf(resultTableMap["Count"])) assert.Equal(t, "2", resultCountStr, fmt.Sprintf("unexpected value for VttabletCall(Execute.source_keyspace.0.replica) inside %s", resultCountStr)) // Verify master reads done by self._check_client_conn_redirection(). - resultVtgateAPI := resultMap["VtgateApi"] - resultVtgateAPIMap := resultVtgateAPI.(map[string]interface{}) - resultAPIHistograms := resultVtgateAPIMap["Histograms"] - resultAPIHistogramsMap := resultAPIHistograms.(map[string]interface{}) - resultTabletDestination := resultAPIHistogramsMap["ExecuteKeyRanges.destination_keyspace.master"] - resultTabletDestinationMap := resultTabletDestination.(map[string]interface{}) + resultVtgateAPI, _ := resultMap["VtgateApi"] + resultVtgateAPIMap, _ := resultVtgateAPI.(map[string]interface{}) + resultAPIHistograms, _ := resultVtgateAPIMap["Histograms"] + resultAPIHistogramsMap, _ := resultAPIHistograms.(map[string]interface{}) + resultTabletDestination, _ := resultAPIHistogramsMap["ExecuteKeyRanges.destination_keyspace.master"] + resultTabletDestinationMap, _ := resultTabletDestination.(map[string]interface{}) resultCountStrDestination := fmt.Sprintf("%v", reflect.ValueOf(resultTabletDestinationMap["Count"])) assert.Equal(t, "6", resultCountStrDestination, fmt.Sprintf("unexpected value for VtgateApi(ExecuteKeyRanges.destination_keyspace.master) inside %s)", resultCountStrDestination)) @@ -520,7 +521,7 @@ func insertInitialValues(t *testing.T, conn *mysql.Conn, sourceMasterTablet clus // Insert data directly because vtgate would redirect us. _, err := destinationMasterTablet.VttabletProcess.QueryTablet(fmt.Sprintf("insert into %s (id, msg) values(%d, 'value %d')", "extra1", 1, 1), destinationKeyspace, true) - assert.Nil(t, err) + require.Nil(t, err) checkValues(t, &destinationMasterTablet, destinationKeyspace, "vt_destination_keyspace", "extra1", 1, 1) } @@ -534,7 +535,7 @@ func checkClientConnRedirectionExecuteKeyrange(ctx context.Context, t *testing.T for _, tableType := range servedFromDbTypes { for _, table := range movedTables { _, err := conn.ExecuteKeyRanges(ctx, fmt.Sprintf("select * from %s", table), keyspace, keyRanges, nil, tableType, nil) - assert.Nil(t, err) + require.Nil(t, err) } } } @@ -556,7 +557,7 @@ func checkValuesTimeout(t *testing.T, tablet cluster.Vttablet, table string, fir for i := 0; i < timeoutInSec; i-- { qr, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("select id, msg from %s where id>=%d order by id limit %d", table, first, count), destinationKeyspace, true) if err != nil { - assert.Nil(t, err, "select failed on master tablet") + require.Nil(t, err, "select failed on master tablet") } if len(qr.Rows) == count { return true @@ -579,11 +580,11 @@ func checkBlacklistedTables(t *testing.T, tablet cluster.Vttablet, keyspace stri if expected != nil && strings.Contains(strings.Join(expected, " "), "moving") { // table is blacklisted, should get error err := clusterInstance.VtctlclientProcess.ExecuteCommand("VtTabletExecute", "-json", tablet.Alias, fmt.Sprintf("select count(1) from %s", table)) - assert.NotNil(t, err, "disallowed due to rule: enforce blacklisted tables") + require.Error(t, err, "disallowed due to rule: enforce blacklisted tables") } else { // table is not blacklisted, should just work _, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("select count(1) from %s", table), keyspace, true) - assert.Nil(t, err) + require.Nil(t, err) } } } @@ -609,7 +610,7 @@ func teardownCluster() { func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { t.Helper() qr, err := conn.ExecuteFetch(query, 1000, true) - assert.Nil(t, err) + require.Nil(t, err) return qr } @@ -713,7 +714,7 @@ func initializeCluster() (int, error) { func validateKeyspaceJSON(t *testing.T, keyspaceJSON string, cellsArr []string) { var keyspace topodata.Keyspace err := json2.Unmarshal([]byte(keyspaceJSON), &keyspace) - assert.Nil(t, err) + require.Nil(t, err) found := false for _, servedFrom := range keyspace.GetServedFroms() { if strings.ToLower(servedFrom.GetTabletType().String()) == "rdonly" { diff --git a/go/test/endtoend/tabletmanager/commands_test.go b/go/test/endtoend/tabletmanager/commands_test.go index ee5d440b666..43c3342cca8 100644 --- a/go/test/endtoend/tabletmanager/commands_test.go +++ b/go/test/endtoend/tabletmanager/commands_test.go @@ -35,11 +35,11 @@ func TestTabletCommands(t *testing.T) { ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer masterConn.Close() replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer replicaConn.Close() // Sanity Check @@ -57,65 +57,65 @@ func TestTabletCommands(t *testing.T) { sql, } result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput(args...) - assert.Nil(t, err) + require.Nil(t, err) assertExcludeFields(t, result) // make sure direct dba queries work sql = "select * from t1" result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ExecuteFetchAsDba", "-json", masterTablet.Alias, sql) - assert.Nil(t, err) + require.Nil(t, err) assertExecuteFetch(t, result) // check Ping / RefreshState / RefreshStateByShard err = clusterInstance.VtctlclientProcess.ExecuteCommand("Ping", masterTablet.Alias) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("RefreshState", masterTablet.Alias) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("RefreshStateByShard", keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("RefreshStateByShard", "--cells="+cell, keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // Check basic actions. err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadOnly", masterTablet.Alias) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") qr := exec(t, masterConn, "show variables like 'read_only'") got := fmt.Sprintf("%v", qr.Rows) want := "[[VARCHAR(\"read_only\") VARCHAR(\"ON\")]]" assert.Equal(t, want, got) err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", masterTablet.Alias) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") qr = exec(t, masterConn, "show variables like 'read_only'") got = fmt.Sprintf("%v", qr.Rows) want = "[[VARCHAR(\"read_only\") VARCHAR(\"OFF\")]]" assert.Equal(t, want, got) err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate", "-ping-tablets=true") - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateKeyspace", keyspaceName) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateKeyspace", "-ping-tablets=true", keyspaceName) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateShard", "-ping-tablets=false", keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateShard", "-ping-tablets=true", keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") } func assertExcludeFields(t *testing.T, qr string) { resultMap := make(map[string]interface{}) err := json.Unmarshal([]byte(qr), &resultMap) - require.NoError(t, err) + require.Nil(t, err) rowsAffected := resultMap["rows_affected"] want := float64(2) @@ -128,7 +128,7 @@ func assertExcludeFields(t *testing.T, qr string) { func assertExecuteFetch(t *testing.T, qr string) { resultMap := make(map[string]interface{}) err := json.Unmarshal([]byte(qr), &resultMap) - require.NoError(t, err) + require.Nil(t, err) rows := reflect.ValueOf(resultMap["rows"]) got := rows.Len() @@ -145,7 +145,7 @@ func assertExecuteFetch(t *testing.T, qr string) { func TestActionAndTimeout(t *testing.T) { err := clusterInstance.VtctlclientProcess.ExecuteCommand("Sleep", masterTablet.Alias, "5s") - assert.Nil(t, err) + require.Nil(t, err) time.Sleep(1 * time.Second) // try a frontend RefreshState that should timeout as the tablet is busy running the other one @@ -182,11 +182,11 @@ func runHookAndAssert(t *testing.T, params []string, expectedStatus string, expe if expectedError { assert.Error(t, err, "Expected error") } else { - require.NoError(t, err) + require.Nil(t, err) resultMap := make(map[string]interface{}) err = json.Unmarshal([]byte(hr), &resultMap) - require.NoError(t, err) + require.Nil(t, err) exitStatus := reflect.ValueOf(resultMap["ExitStatus"]).Float() status := fmt.Sprintf("%.0f", exitStatus) @@ -201,28 +201,28 @@ func runHookAndAssert(t *testing.T, params []string, expectedStatus string, expe func TestShardReplicationFix(t *testing.T) { // make sure the replica is in the replication graph, 2 nodes: 1 master, 1 replica result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell, keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") assertNodeCount(t, result, int(3)) // Manually add a bogus entry to the replication graph, and check it is removed by ShardReplicationFix err = clusterInstance.VtctlclientProcess.ExecuteCommand("ShardReplicationAdd", keyspaceShard, fmt.Sprintf("%s-9000", cell)) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell, keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") assertNodeCount(t, result, int(4)) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ShardReplicationFix", cell, keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell, keyspaceShard) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") assertNodeCount(t, result, int(3)) } func assertNodeCount(t *testing.T, result string, want int) { resultMap := make(map[string]interface{}) err := json.Unmarshal([]byte(result), &resultMap) - require.NoError(t, err) + require.Nil(t, err) nodes := reflect.ValueOf(resultMap["nodes"]) got := nodes.Len() diff --git a/go/test/endtoend/tabletmanager/custom_rule_topo_test.go b/go/test/endtoend/tabletmanager/custom_rule_topo_test.go index a2155ff33d0..51a66fea62a 100644 --- a/go/test/endtoend/tabletmanager/custom_rule_topo_test.go +++ b/go/test/endtoend/tabletmanager/custom_rule_topo_test.go @@ -33,10 +33,10 @@ func TestTopoCustomRule(t *testing.T) { ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer masterConn.Close() replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer replicaConn.Close() // Insert data for sanity checks @@ -49,11 +49,11 @@ func TestTopoCustomRule(t *testing.T) { topoCustomRulePath := "/keyspaces/ks/configs/CustomRules" data := []byte("[]\n") err = ioutil.WriteFile(topoCustomRuleFile, data, 0777) - require.NoError(t, err) + require.Nil(t, err) // Copy config file into topo. err = clusterInstance.VtctlclientProcess.ExecuteCommand("TopoCp", "-to_topo", topoCustomRuleFile, topoCustomRulePath) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // Set extra tablet args for topo custom rule clusterInstance.VtTabletExtraArgs = []string{ @@ -65,25 +65,25 @@ func TestTopoCustomRule(t *testing.T) { // Init Tablets err = clusterInstance.VtctlclientProcess.InitTablet(rTablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // Start Mysql Processes err = cluster.StartMySQL(ctx, rTablet, username, clusterInstance.TmpDirectory) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // Start Vttablet err = clusterInstance.StartVttablet(rTablet, "SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate") - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // Verify that query is working result, err := vtctlExec("select id, value from t1", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) resultMap := make(map[string]interface{}) err = json.Unmarshal([]byte(result), &resultMap) - require.NoError(t, err) + require.Nil(t, err) rowsAffected := resultMap["rows_affected"] want := float64(2) @@ -97,10 +97,10 @@ func TestTopoCustomRule(t *testing.T) { "Query" : "(select)|(SELECT)" }]`) err = ioutil.WriteFile(topoCustomRuleFile, data, 0777) - require.NoError(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("TopoCp", "-to_topo", topoCustomRuleFile, topoCustomRulePath) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // And wait until the query fails with the right error. timeout := time.Now().Add(10 * time.Second) diff --git a/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go index 6441a161e72..23a5f5edddd 100644 --- a/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go +++ b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go @@ -109,11 +109,11 @@ func TestMain(m *testing.M) { func TestDbNameOverride(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn.Close() qr, err := conn.ExecuteFetch("SELECT database() FROM information_schema.tables WHERE table_schema = database()", 1000, true) - require.NoError(t, err) + require.Nil(t, err) require.Equal(t, 1, len(qr.Rows), "did not get enough rows back") require.Equal(t, dbName, qr.Rows[0][0].ToString()) } diff --git a/go/test/endtoend/tabletmanager/lock_unlock_test.go b/go/test/endtoend/tabletmanager/lock_unlock_test.go index fa3771cc0fd..9f6f5dbfb3e 100644 --- a/go/test/endtoend/tabletmanager/lock_unlock_test.go +++ b/go/test/endtoend/tabletmanager/lock_unlock_test.go @@ -34,11 +34,11 @@ func TestLockAndUnlock(t *testing.T) { ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer masterConn.Close() replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer replicaConn.Close() // first make sure that our writes to the master make it to the replica @@ -48,14 +48,14 @@ func TestLockAndUnlock(t *testing.T) { // now lock the replica err = tmcLockTables(ctx, replicaTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) // make sure that writing to the master does not show up on the replica while locked exec(t, masterConn, "insert into t1(id, value) values(3,'c')") checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")]]`) // finally, make sure that unlocking the replica leads to the previous write showing up err = tmcUnlockTables(ctx, replicaTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")] [VARCHAR("c")]]`) // Unlocking when we do not have a valid lock should lead to an exception being raised @@ -74,28 +74,28 @@ func TestStartSlaveUntilAfter(t *testing.T) { ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer masterConn.Close() replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer replicaConn.Close() //first we stop replication to the replica, so we can move forward step by step. err = tmcStopSlave(ctx, replicaTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) exec(t, masterConn, "insert into t1(id, value) values(1,'a')") pos1, err := tmcMasterPosition(ctx, masterTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) exec(t, masterConn, "insert into t1(id, value) values(2,'b')") pos2, err := tmcMasterPosition(ctx, masterTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) exec(t, masterConn, "insert into t1(id, value) values(3,'c')") pos3, err := tmcMasterPosition(ctx, masterTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) // Now, we'll resume stepwise position by position and make sure that we see the expected data checkDataOnReplica(t, replicaConn, `[]`) @@ -103,21 +103,21 @@ func TestStartSlaveUntilAfter(t *testing.T) { // starts the mysql replication until timeout := 10 * time.Second err = tmcStartSlaveUntilAfter(ctx, replicaTablet.GrpcPort, pos1, timeout) - require.NoError(t, err) + require.Nil(t, err) // first row should be visible checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")]]`) err = tmcStartSlaveUntilAfter(ctx, replicaTablet.GrpcPort, pos2, timeout) - require.NoError(t, err) + require.Nil(t, err) checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")]]`) err = tmcStartSlaveUntilAfter(ctx, replicaTablet.GrpcPort, pos3, timeout) - require.NoError(t, err) + require.Nil(t, err) checkDataOnReplica(t, replicaConn, `[[VARCHAR("a")] [VARCHAR("b")] [VARCHAR("c")]]`) // Strat replication to the replica err = tmcStartSlave(ctx, replicaTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) // Clean the table for further testing exec(t, masterConn, "delete from t1") } @@ -127,11 +127,11 @@ func TestLockAndTimeout(t *testing.T) { ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer masterConn.Close() replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer replicaConn.Close() // first make sure that our writes to the master make it to the replica @@ -140,7 +140,7 @@ func TestLockAndTimeout(t *testing.T) { // now lock the replica err = tmcLockTables(ctx, replicaTablet.GrpcPort) - require.NoError(t, err) + require.Nil(t, err) // make sure that writing to the master does not show up on the replica while locked exec(t, masterConn, "insert into t1(id, value) values(2,'b')") diff --git a/go/test/endtoend/tabletmanager/main_test.go b/go/test/endtoend/tabletmanager/main_test.go index 8dd433a9afc..e312182bb02 100644 --- a/go/test/endtoend/tabletmanager/main_test.go +++ b/go/test/endtoend/tabletmanager/main_test.go @@ -153,7 +153,7 @@ func TestMain(m *testing.M) { func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { t.Helper() qr, err := conn.ExecuteFetch(query, 1000, true) - require.NoError(t, err) + require.Nil(t, err) return qr } diff --git a/go/test/endtoend/tabletmanager/master/tablet_master_test.go b/go/test/endtoend/tabletmanager/master/tablet_master_test.go index ddae64d229e..7727daccb54 100644 --- a/go/test/endtoend/tabletmanager/master/tablet_master_test.go +++ b/go/test/endtoend/tabletmanager/master/tablet_master_test.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/test/endtoend/cluster" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) @@ -122,16 +123,16 @@ func TestRepeatedInitShardMaster(t *testing.T) { // Make replica tablet as master err := clusterInstance.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, replicaTablet.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) // Run health check on both, make sure they are both healthy. // Also make sure the types are correct. err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", masterTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, masterTablet.HTTPPort, false) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", replicaTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, replicaTablet.HTTPPort, false) checkTabletType(t, masterTablet.Alias, "REPLICA") @@ -139,16 +140,16 @@ func TestRepeatedInitShardMaster(t *testing.T) { // Come back to the original tablet. err = clusterInstance.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, masterTablet.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) // Run health check on both, make sure they are both healthy. // Also make sure the types are correct. err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", masterTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, masterTablet.HTTPPort, false) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", replicaTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, replicaTablet.HTTPPort, false) checkTabletType(t, masterTablet.Alias, "MASTER") @@ -162,19 +163,19 @@ func TestMasterRestartSetsTERTimestamp(t *testing.T) { // Make replica as master err := clusterInstance.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, replicaTablet.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) err = replicaTablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) // Capture the current TER. result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( "VtTabletStreamHealth", "-count", "1", replicaTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthRes1 querypb.StreamHealthResponse err = json.Unmarshal([]byte(result), &streamHealthRes1) - assert.Nil(t, err) + require.Nil(t, err) actualType := streamHealthRes1.GetTarget().GetTabletType() tabletType := topodatapb.TabletType_value["MASTER"] got := fmt.Sprintf("%d", actualType) @@ -188,20 +189,20 @@ func TestMasterRestartSetsTERTimestamp(t *testing.T) { // kill the newly promoted master tablet err = replicaTablet.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) // Start Vttablet err = clusterInstance.StartVttablet(&replicaTablet, "SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // Make sure that the TER did not change result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput( "VtTabletStreamHealth", "-count", "1", replicaTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthRes2 querypb.StreamHealthResponse err = json.Unmarshal([]byte(result), &streamHealthRes2) - assert.Nil(t, err) + require.Nil(t, err) actualType = streamHealthRes2.GetTarget().GetTabletType() tabletType = topodatapb.TabletType_value["MASTER"] @@ -218,16 +219,16 @@ func TestMasterRestartSetsTERTimestamp(t *testing.T) { // Reset master err = clusterInstance.VtctlclientProcess.InitShardMaster(keyspaceName, shardName, cell, masterTablet.TabletUID) - assert.Nil(t, err) + require.Nil(t, err) err = masterTablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) } func checkHealth(t *testing.T, port int, shouldError bool) { url := fmt.Sprintf("http://localhost:%d/healthz", port) resp, err := http.Get(url) - assert.Nil(t, err) + require.Nil(t, err) if shouldError { assert.True(t, resp.StatusCode > 400) } else { @@ -237,11 +238,11 @@ func checkHealth(t *testing.T, port int, shouldError bool) { func checkTabletType(t *testing.T, tabletAlias string, typeWant string) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetTablet", tabletAlias) - assert.Nil(t, err) + require.Nil(t, err) var tablet topodatapb.Tablet err = json2.Unmarshal([]byte(result), &tablet) - assert.Nil(t, err) + require.Nil(t, err) actualType := tablet.GetType() got := fmt.Sprintf("%d", actualType) diff --git a/go/test/endtoend/tabletmanager/qps_test.go b/go/test/endtoend/tabletmanager/qps_test.go index 0949322a54e..b55b3bb227e 100644 --- a/go/test/endtoend/tabletmanager/qps_test.go +++ b/go/test/endtoend/tabletmanager/qps_test.go @@ -36,11 +36,11 @@ func TestQPS(t *testing.T) { Port: clusterInstance.VtgateMySQLPort, } vtGateConn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer vtGateConn.Close() replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer replicaConn.Close() // Sanity Check @@ -69,11 +69,11 @@ func TestQPS(t *testing.T) { timeout := time.Now().Add(12 * time.Second) for time.Now().Before(timeout) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "-count", "1", masterTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthResponse querypb.StreamHealthResponse err = json.Unmarshal([]byte(result), &streamHealthResponse) - require.NoError(t, err) + require.Nil(t, err) realTimeStats := streamHealthResponse.GetRealtimeStats() qps := realTimeStats.GetQps() diff --git a/go/test/endtoend/tabletmanager/tablet_health_test.go b/go/test/endtoend/tabletmanager/tablet_health_test.go index 82c0c9ba011..e3ae0b27a55 100644 --- a/go/test/endtoend/tabletmanager/tablet_health_test.go +++ b/go/test/endtoend/tabletmanager/tablet_health_test.go @@ -40,11 +40,11 @@ func TestTabletReshuffle(t *testing.T) { ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer masterConn.Close() replicaConn, err := mysql.Connect(ctx, &replicaTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer replicaConn.Close() // Sanity Check @@ -57,7 +57,7 @@ func TestTabletReshuffle(t *testing.T) { //Init Tablets err = clusterInstance.VtctlclientProcess.InitTablet(rTablet, cell, keyspaceName, hostname, shardName) - require.NoError(t, err) + require.Nil(t, err) // mycnf_server_id prevents vttablet from reading the mycnf // Pointing to masterTablet's socket file @@ -69,7 +69,7 @@ func TestTabletReshuffle(t *testing.T) { // SupportsBackup=False prevents vttablet from trying to restore // Start vttablet process err = clusterInstance.StartVttablet(rTablet, "SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) sql := "select value from t1" args := []string{ @@ -80,7 +80,7 @@ func TestTabletReshuffle(t *testing.T) { sql, } result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput(args...) - assert.Nil(t, err) + require.Nil(t, err) assertExcludeFields(t, result) err = clusterInstance.VtctlclientProcess.ExecuteCommand("Backup", rTablet.Alias) @@ -100,7 +100,7 @@ func TestHealthCheck(t *testing.T) { // Start Mysql Processes and return connection replicaConn, err := cluster.StartMySQLAndGetConnection(ctx, rTablet, username, clusterInstance.TmpDirectory) - assert.Nil(t, err) + require.Nil(t, err) defer replicaConn.Close() @@ -109,18 +109,18 @@ func TestHealthCheck(t *testing.T) { //Init Replica Tablet err = clusterInstance.VtctlclientProcess.InitTablet(rTablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // start vttablet process, should be in SERVING state as we already have a master err = clusterInstance.StartVttablet(rTablet, "SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) masterConn, err := mysql.Connect(ctx, &masterTabletParams) - require.NoError(t, err) + require.Nil(t, err) defer masterConn.Close() err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, rTablet.HTTPPort, false) // Make sure the master is still master @@ -129,25 +129,25 @@ func TestHealthCheck(t *testing.T) { // stop replication, make sure we don't go unhealthy. err = clusterInstance.VtctlclientProcess.ExecuteCommand("StopSlave", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) // make sure the health stream is updated result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "-count", "1", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) verifyStreamHealth(t, result) // then restart replication, make sure we stay healthy err = clusterInstance.VtctlclientProcess.ExecuteCommand("StopSlave", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, rTablet.HTTPPort, false) // now test VtTabletStreamHealth returns the right thing result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "-count", "2", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) scanner := bufio.NewScanner(strings.NewReader(result)) for scanner.Scan() { // fmt.Println() // Println will add back the final '\n' @@ -161,7 +161,7 @@ func TestHealthCheck(t *testing.T) { func checkHealth(t *testing.T, port int, shouldError bool) { url := fmt.Sprintf("http://localhost:%d/healthz", port) resp, err := http.Get(url) - assert.Nil(t, err) + require.Nil(t, err) if shouldError { assert.True(t, resp.StatusCode > 400) } else { @@ -171,11 +171,11 @@ func checkHealth(t *testing.T, port int, shouldError bool) { func checkTabletType(t *testing.T, tabletAlias string, typeWant string) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetTablet", tabletAlias) - assert.Nil(t, err) + require.Nil(t, err) var tablet topodatapb.Tablet err = json2.Unmarshal([]byte(result), &tablet) - assert.Nil(t, err) + require.Nil(t, err) actualType := tablet.GetType() got := fmt.Sprintf("%d", actualType) @@ -189,7 +189,7 @@ func checkTabletType(t *testing.T, tabletAlias string, typeWant string) { func verifyStreamHealth(t *testing.T, result string) { var streamHealthResponse querypb.StreamHealthResponse err := json2.Unmarshal([]byte(result), &streamHealthResponse) - require.NoError(t, err) + require.Nil(t, err) serving := streamHealthResponse.GetServing() UID := streamHealthResponse.GetTabletAlias().GetUid() realTimeStats := streamHealthResponse.GetRealtimeStats() @@ -208,7 +208,7 @@ func TestHealthCheckDrainedStateDoesNotShutdownQueryService(t *testing.T) { //Wait if tablet is not in service state err := rdonlyTablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) // Check tablet health checkHealth(t, rdonlyTablet.HTTPPort, false) @@ -219,30 +219,30 @@ func TestHealthCheckDrainedStateDoesNotShutdownQueryService(t *testing.T) { // implementation.) The tablet will stay healthy, and the // query service is still running. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", rdonlyTablet.Alias, "drained") - assert.Nil(t, err) + require.Nil(t, err) // Trying to drain the same tablet again, should error err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", rdonlyTablet.Alias, "drained") assert.Error(t, err, "already drained") err = clusterInstance.VtctlclientProcess.ExecuteCommand("StopSlave", rdonlyTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) // Trigger healthcheck explicitly to avoid waiting for the next interval. err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", rdonlyTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkTabletType(t, rdonlyTablet.Alias, "DRAINED") // Query service is still running. err = rdonlyTablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) // Restart replication. Tablet will become healthy again. err = clusterInstance.VtctlclientProcess.ExecuteCommand("ChangeSlaveType", rdonlyTablet.Alias, "rdonly") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("StartSlave", rdonlyTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", rdonlyTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, rdonlyTablet.HTTPPort, false) } @@ -261,7 +261,7 @@ func TestIgnoreHealthError(t *testing.T) { tablet := clusterInstance.GetVttabletInstance("replica", 0, "") tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory) err := tablet.MysqlctlProcess.Start() - assert.Nil(t, err) + require.Nil(t, err) // start vttablet process tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort, @@ -284,32 +284,32 @@ func TestIgnoreHealthError(t *testing.T) { // Init Tablet err = clusterInstance.VtctlclientProcess.InitTablet(tablet, cell, keyspaceName, hostname, newShard.Name) - assert.Nil(t, err) + require.Nil(t, err) // create database err = tablet.VttabletProcess.CreateDB(keyspaceName) - assert.Nil(t, err) + require.Nil(t, err) // Start Vttablet, it should be NOT_SERVING as there is no master err = clusterInstance.StartVttablet(tablet, "NOT_SERVING", false, cell, keyspaceName, hostname, newShard.Name) - assert.Nil(t, err) + require.Nil(t, err) // Force it healthy. err = clusterInstance.VtctlclientProcess.ExecuteCommand("IgnoreHealthError", tablet.Alias, ".*no slave status.*") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = tablet.VttabletProcess.WaitForTabletType("SERVING") - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, tablet.HTTPPort, false) // Turn off the force-healthy. err = clusterInstance.VtctlclientProcess.ExecuteCommand("IgnoreHealthError", tablet.Alias, "") - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) err = tablet.VttabletProcess.WaitForTabletType("NOT_SERVING") - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, tablet.HTTPPort, true) // Tear down custom processes @@ -327,11 +327,11 @@ func TestNoMysqlHealthCheck(t *testing.T) { // Start Mysql Processes and return connection masterConn, err := cluster.StartMySQLAndGetConnection(ctx, mTablet, username, clusterInstance.TmpDirectory) - assert.Nil(t, err) + require.Nil(t, err) defer masterConn.Close() replicaConn, err := cluster.StartMySQLAndGetConnection(ctx, rTablet, username, clusterInstance.TmpDirectory) - assert.Nil(t, err) + require.Nil(t, err) defer replicaConn.Close() // Create database in mysql @@ -352,21 +352,21 @@ func TestNoMysqlHealthCheck(t *testing.T) { // now shutdown all mysqld err = rTablet.MysqlctlProcess.Stop() - assert.Nil(t, err) + require.Nil(t, err) err = mTablet.MysqlctlProcess.Stop() - assert.Nil(t, err) + require.Nil(t, err) //Init Tablets err = clusterInstance.VtctlclientProcess.InitTablet(mTablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) err = clusterInstance.VtctlclientProcess.InitTablet(rTablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // Start vttablet process, should be in NOT_SERVING state as mysqld is not running err = clusterInstance.StartVttablet(mTablet, "NOT_SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") err = clusterInstance.StartVttablet(rTablet, "NOT_SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err, "error should be Nil") + require.Nil(t, err, "error should be Nil") // Check Health should fail as Mysqld is not found checkHealth(t, mTablet.HTTPPort, true) @@ -380,35 +380,35 @@ func TestNoMysqlHealthCheck(t *testing.T) { //The above notice to not fix replication should survive tablet restart. err = rTablet.VttabletProcess.TearDown() - assert.Nil(t, err) + require.Nil(t, err) err = rTablet.VttabletProcess.Setup() - assert.Nil(t, err) + require.Nil(t, err) // restart mysqld rTablet.MysqlctlProcess.InitMysql = false err = rTablet.MysqlctlProcess.Start() - assert.Nil(t, err) + require.Nil(t, err) mTablet.MysqlctlProcess.InitMysql = false err = mTablet.MysqlctlProcess.Start() - assert.Nil(t, err) + require.Nil(t, err) // the master should still be healthy err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", mTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) checkHealth(t, mTablet.HTTPPort, false) // the slave will now be healthy, but report a very high replication // lag, because it can't figure out what it exactly is. err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, "SERVING", rTablet.VttabletProcess.GetTabletStatus()) checkHealth(t, rTablet.HTTPPort, false) result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "-count", "1", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthResponse querypb.StreamHealthResponse err = json2.Unmarshal([]byte(result), &streamHealthResponse) - assert.Nil(t, err) + require.Nil(t, err) realTimeStats := streamHealthResponse.GetRealtimeStats() secondsBehindMaster := realTimeStats.GetSecondsBehindMaster() assert.True(t, secondsBehindMaster == 7200) @@ -416,15 +416,15 @@ func TestNoMysqlHealthCheck(t *testing.T) { // restart replication, wait until health check goes small // (a value of zero is default and won't be in structure) err = clusterInstance.VtctlclientProcess.ExecuteCommand("StartSlave", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) timeout := time.Now().Add(10 * time.Second) for time.Now().Before(timeout) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletStreamHealth", "-count", "1", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var streamHealthResponse querypb.StreamHealthResponse err = json2.Unmarshal([]byte(result), &streamHealthResponse) - assert.Nil(t, err) + require.Nil(t, err) realTimeStats := streamHealthResponse.GetRealtimeStats() secondsBehindMaster := realTimeStats.GetSecondsBehindMaster() if secondsBehindMaster < 30 { @@ -436,10 +436,10 @@ func TestNoMysqlHealthCheck(t *testing.T) { // wait for the tablet to fix its mysql port result, err = clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetTablet", rTablet.Alias) - assert.Nil(t, err) + require.Nil(t, err) var tablet topodatapb.Tablet err = json2.Unmarshal([]byte(result), &tablet) - assert.Nil(t, err) + require.Nil(t, err) portMap := tablet.GetPortMap() mysqlPort := int(portMap["mysql"]) assert.True(t, mysqlPort == rTablet.MySQLPort, "mysql port in tablet record") diff --git a/go/test/endtoend/tabletmanager/tablet_security_policy_test.go b/go/test/endtoend/tabletmanager/tablet_security_policy_test.go index 8b705121956..00b8df3b229 100644 --- a/go/test/endtoend/tabletmanager/tablet_security_policy_test.go +++ b/go/test/endtoend/tabletmanager/tablet_security_policy_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/cluster" ) @@ -32,16 +33,16 @@ func TestFallbackSecurityPolicy(t *testing.T) { //Init Tablets err := clusterInstance.VtctlclientProcess.InitTablet(mTablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // Start Mysql Processes err = cluster.StartMySQL(ctx, mTablet, username, clusterInstance.TmpDirectory) - assert.Nil(t, err) + require.Nil(t, err) // Requesting an unregistered security_policy should fallback to deny-all. clusterInstance.VtTabletExtraArgs = []string{"-security_policy", "bogus"} err = clusterInstance.StartVttablet(mTablet, "NOT_SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // It should deny ADMIN role. url := fmt.Sprintf("http://localhost:%d/streamqueryz/terminate", mTablet.HTTPPort) @@ -63,10 +64,10 @@ func TestFallbackSecurityPolicy(t *testing.T) { func assertNotAllowedURLTest(t *testing.T, url string) { resp, err := http.Get(url) - assert.Nil(t, err) + require.Nil(t, err) body, err := ioutil.ReadAll(resp.Body) - assert.Nil(t, err) + require.Nil(t, err) defer resp.Body.Close() assert.True(t, resp.StatusCode > 400) @@ -75,10 +76,10 @@ func assertNotAllowedURLTest(t *testing.T, url string) { func assertAllowedURLTest(t *testing.T, url string) { resp, err := http.Get(url) - assert.Nil(t, err) + require.Nil(t, err) body, err := ioutil.ReadAll(resp.Body) - assert.Nil(t, err) + require.Nil(t, err) defer resp.Body.Close() assert.NotContains(t, string(body), "Access denied: not allowed") @@ -90,16 +91,16 @@ func TestDenyAllSecurityPolicy(t *testing.T) { //Init Tablets err := clusterInstance.VtctlclientProcess.InitTablet(mTablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // Start Mysql Processes err = cluster.StartMySQL(ctx, mTablet, username, clusterInstance.TmpDirectory) - assert.Nil(t, err) + require.Nil(t, err) // Requesting a deny-all security_policy. clusterInstance.VtTabletExtraArgs = []string{"-security_policy", "deny-all"} err = clusterInstance.StartVttablet(mTablet, "NOT_SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // It should deny ADMIN role. url := fmt.Sprintf("http://localhost:%d/streamqueryz/terminate", mTablet.HTTPPort) @@ -125,16 +126,16 @@ func TestReadOnlySecurityPolicy(t *testing.T) { //Init Tablets err := clusterInstance.VtctlclientProcess.InitTablet(mTablet, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // Start Mysql Processes err = cluster.StartMySQL(ctx, mTablet, username, clusterInstance.TmpDirectory) - assert.Nil(t, err) + require.Nil(t, err) // Requesting a read-only security_policy. clusterInstance.VtTabletExtraArgs = []string{"-security_policy", "read-only"} err = clusterInstance.StartVttablet(mTablet, "NOT_SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // It should deny ADMIN role. url := fmt.Sprintf("http://localhost:%d/streamqueryz/terminate", mTablet.HTTPPort) diff --git a/go/test/endtoend/tabletmanager/tablet_test.go b/go/test/endtoend/tabletmanager/tablet_test.go index 4f7b7b03bc8..708cb5d711b 100644 --- a/go/test/endtoend/tabletmanager/tablet_test.go +++ b/go/test/endtoend/tabletmanager/tablet_test.go @@ -22,7 +22,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/stretchr/testify/assert" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" ) @@ -38,7 +37,7 @@ func TestLocalMetadata(t *testing.T) { // Init Tablet err := clusterInstance.VtctlclientProcess.InitTablet(rTablet, cell, keyspaceName, hostname, shardName) - require.NoError(t, err) + require.Nil(t, err) clusterInstance.VtTabletExtraArgs = []string{ "-lock_tables_timeout", "5s", @@ -46,13 +45,13 @@ func TestLocalMetadata(t *testing.T) { } rTablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(rTablet.TabletUID, rTablet.MySQLPort, clusterInstance.TmpDirectory) err = rTablet.MysqlctlProcess.Start() - assert.Nil(t, err) + require.Nil(t, err) log.Info(fmt.Sprintf("Started vttablet %v", rTablet)) // SupportsBackup=False prevents vttablet from trying to restore // Start vttablet process err = clusterInstance.StartVttablet(rTablet, "SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) cluster.VerifyLocalMetadata(t, rTablet, keyspaceName, shardName, cell) @@ -61,7 +60,7 @@ func TestLocalMetadata(t *testing.T) { // Init Tablet err = clusterInstance.VtctlclientProcess.InitTablet(rTablet2, cell, keyspaceName, hostname, shardName) - require.NoError(t, err) + require.Nil(t, err) // start with -init_populate_metadata false (default) clusterInstance.VtTabletExtraArgs = []string{ @@ -69,18 +68,18 @@ func TestLocalMetadata(t *testing.T) { } rTablet2.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(rTablet2.TabletUID, rTablet2.MySQLPort, clusterInstance.TmpDirectory) err = rTablet2.MysqlctlProcess.Start() - assert.Nil(t, err) + require.Nil(t, err) log.Info(fmt.Sprintf("Started vttablet %v", rTablet2)) // SupportsBackup=False prevents vttablet from trying to restore // Start vttablet process err = clusterInstance.StartVttablet(rTablet2, "SERVING", false, cell, keyspaceName, hostname, shardName) - assert.Nil(t, err) + require.Nil(t, err) // check that tablet did _not_ get populated qr, err := rTablet2.VttabletProcess.QueryTablet("select * from _vt.local_metadata", keyspaceName, false) - assert.Nil(t, err) - assert.Nil(t, qr.Rows) + require.Nil(t, err) + require.Nil(t, qr.Rows) // Reset the VtTabletExtraArgs and kill tablets clusterInstance.VtTabletExtraArgs = []string{} diff --git a/go/test/endtoend/vtgate/aggr_test.go b/go/test/endtoend/vtgate/aggr_test.go index 87acd6902c4..e661b30813a 100644 --- a/go/test/endtoend/vtgate/aggr_test.go +++ b/go/test/endtoend/vtgate/aggr_test.go @@ -29,7 +29,7 @@ import ( func TestAggregateTypes(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn.Close() exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'A',1), (3,'b',1), (4,'c',3), (5,'c',4)") diff --git a/go/test/endtoend/vtgate/buffer/buffer_test.go b/go/test/endtoend/vtgate/buffer/buffer_test.go index 8340eec113b..5f2455af6a5 100644 --- a/go/test/endtoend/vtgate/buffer/buffer_test.go +++ b/go/test/endtoend/vtgate/buffer/buffer_test.go @@ -217,7 +217,7 @@ func createCluster() (*cluster.LocalProcessCluster, int) { func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { t.Helper() qr, err := conn.ExecuteFetch(query, 1000, true) - require.NoError(t, err) + require.Nil(t, err) return qr } @@ -236,7 +236,7 @@ func testBufferBase(t *testing.T, isExternalParent bool) { } ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn.Close() // Insert two rows for the later threads (critical read, update). @@ -285,7 +285,7 @@ func testBufferBase(t *testing.T, isExternalParent bool) { //At least one thread should have been buffered. //This may fail if a failover is too fast. Add retries then. resp, err := http.Get(clusterInstance.VtgateProcess.VerifyURL) - require.NoError(t, err) + require.Nil(t, err) label := fmt.Sprintf("%s.%s", keyspaceUnshardedName, "0") inFlightMax := 0 masterPromotedCount := 0 @@ -334,7 +334,7 @@ func getVarFromVtgate(t *testing.T, label string, param string, resultMap map[st v := object.MapIndex(key) s := fmt.Sprintf("%v", v.Interface()) paramVal, err = strconv.Atoi(s) - require.NoError(t, err) + require.Nil(t, err) } } } @@ -384,3 +384,34 @@ func externalReparenting(ctx context.Context, t *testing.T, clusterInstance *clu // Notify the new vttablet master about the reparent. clusterInstance.VtctlclientProcess.ExecuteCommand("TabletExternallyReparented", newMaster.Alias) } + +func waitForReplicationPos(ctx context.Context, t *testing.T, tabletA *cluster.Vttablet, tabletB *cluster.Vttablet, timeout float64) { + replicationPosA, _ := cluster.GetMasterPosition(t, *tabletA, hostname) + for { + replicationPosB, _ := cluster.GetMasterPosition(t, *tabletB, hostname) + if positionAtLeast(t, tabletA, replicationPosB, replicationPosA) { + break + } + msg := fmt.Sprintf("%s's replication position to catch up to %s's;currently at: %s, waiting to catch up to: %s", tabletB.Alias, tabletA.Alias, replicationPosB, replicationPosA) + waitStep(t, msg, timeout, 0.01) + } +} + +func positionAtLeast(t *testing.T, tablet *cluster.Vttablet, a string, b string) bool { + isAtleast := false + val, err := tablet.MysqlctlProcess.ExecuteCommandWithOutput("position", "at_least", a, b) + require.Nil(t, err) + if strings.Contains(val, "true") { + isAtleast = true + } + return isAtleast +} + +func waitStep(t *testing.T, msg string, timeout float64, sleepTime float64) float64 { + timeout = timeout - sleepTime + if timeout < 0.0 { + t.Errorf("timeout waiting for condition '%s'", msg) + } + time.Sleep(time.Duration(sleepTime) * time.Second) + return timeout +} diff --git a/go/test/endtoend/vtgate/lookup_test.go b/go/test/endtoend/vtgate/lookup_test.go index 8c5c2c5a2c9..a7a81d51f94 100644 --- a/go/test/endtoend/vtgate/lookup_test.go +++ b/go/test/endtoend/vtgate/lookup_test.go @@ -31,11 +31,11 @@ import ( func TestConsistentLookup(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn.Close() // conn2 is for queries that target shards. conn2, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn2.Close() // Simple insert. @@ -164,11 +164,11 @@ func TestConsistentLookup(t *testing.T) { func TestConsistentLookupMultiInsert(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn.Close() // conn2 is for queries that target shards. conn2, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn2.Close() exec(t, conn, "begin") @@ -217,11 +217,11 @@ func TestConsistentLookupMultiInsert(t *testing.T) { func TestHashLookupMultiInsertIgnore(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn.Close() // conn2 is for queries that target shards. conn2, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn2.Close() // DB should start out clean @@ -253,6 +253,6 @@ func TestHashLookupMultiInsertIgnore(t *testing.T) { func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { t.Helper() qr, err := conn.ExecuteFetch(query, 1000, true) - require.NoError(t, err) + require.Nil(t, err) return qr } diff --git a/go/test/endtoend/vtgate/schema/schema_test.go b/go/test/endtoend/vtgate/schema/schema_test.go index 6e16e66e140..65d9239149a 100644 --- a/go/test/endtoend/vtgate/schema/schema_test.go +++ b/go/test/endtoend/vtgate/schema/schema_test.go @@ -29,6 +29,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/test/endtoend/cluster" ) @@ -116,7 +117,7 @@ func testWithInitialSchema(t *testing.T) { for i := 0; i < totalTableCount; i++ { sqlQuery = fmt.Sprintf(createTable, fmt.Sprintf("vt_select_test_%02d", i)) err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, sqlQuery) - assert.Nil(t, err) + require.Nil(t, err) } @@ -132,7 +133,7 @@ func testWithInitialSchema(t *testing.T) { func testWithAlterSchema(t *testing.T) { sqlQuery := fmt.Sprintf(alterTable, fmt.Sprintf("vt_select_test_%02d", 3), "msg") err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, sqlQuery) - assert.Nil(t, err) + require.Nil(t, err) matchSchema(t, clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].VttabletProcess.TabletPath, clusterInstance.Keyspaces[0].Shards[1].Vttablets[0].VttabletProcess.TabletPath) } @@ -140,7 +141,7 @@ func testWithAlterSchema(t *testing.T) { func testWithAlterDatabase(t *testing.T) { sql := "create database alter_database_test; alter database alter_database_test default character set = utf8mb4; drop database alter_database_test" err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, sql) - assert.NoError(t, err) + assert.Nil(t, err) } // testWithDropCreateSchema , we should be able to drop and create same schema @@ -154,7 +155,7 @@ func testWithAlterDatabase(t *testing.T) { func testWithDropCreateSchema(t *testing.T) { dropCreateTable := fmt.Sprintf("DROP TABLE vt_select_test_%02d ;", 2) + fmt.Sprintf(createTable, fmt.Sprintf("vt_select_test_%02d", 2)) err := clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, dropCreateTable) - assert.Nil(t, err) + require.Nil(t, err) checkTables(t, totalTableCount) } @@ -164,7 +165,7 @@ func testWithAutoSchemaFromChangeDir(t *testing.T) { _ = os.Mkdir(path.Join(schemaChangeDirectory, keyspaceName, "input"), 0700) sqlFile := path.Join(schemaChangeDirectory, keyspaceName, "input/create_test_table_x.sql") err := ioutil.WriteFile(sqlFile, []byte("create table test_table_x (id int)"), 0644) - assert.Nil(t, err) + require.Nil(t, err) timeout := time.Now().Add(10 * time.Second) matchFoundAfterAutoSchemaApply := false for time.Now().Before(timeout) { @@ -183,10 +184,10 @@ func testWithAutoSchemaFromChangeDir(t *testing.T) { // matchSchema schema for supplied tablets should match func matchSchema(t *testing.T, firstTablet string, secondTablet string) { firstShardSchema, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetSchema", firstTablet) - assert.Nil(t, err) + require.Nil(t, err) secondShardSchema, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetSchema", secondTablet) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, firstShardSchema, secondShardSchema) } @@ -196,7 +197,7 @@ func matchSchema(t *testing.T, firstTablet string, secondTablet string) { func testSchemaChangePreflightErrorPartially(t *testing.T) { createNewTable := fmt.Sprintf(createTable, fmt.Sprintf("vt_select_test_%02d", 5)) + fmt.Sprintf(createTable, fmt.Sprintf("vt_select_test_%02d", 2)) output, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ApplySchema", "-sql", createNewTable, keyspaceName) - assert.NotNil(t, err) + require.Error(t, err) assert.True(t, strings.Contains(output, "already exists")) checkTables(t, totalTableCount) @@ -211,12 +212,12 @@ func testSchemaChangePreflightErrorPartially(t *testing.T) { func testDropNonExistentTables(t *testing.T) { dropNonExistentTable := "DROP TABLE nonexistent_table;" output, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ApplySchema", "-sql", dropNonExistentTable, keyspaceName) - assert.NotNil(t, err) + require.Error(t, err) assert.True(t, strings.Contains(output, "Unknown table")) dropIfExists := "DROP TABLE IF EXISTS nonexistent_table;" err = clusterInstance.VtctlclientProcess.ApplySchema(keyspaceName, dropIfExists) - assert.Nil(t, err) + require.Nil(t, err) checkTables(t, totalTableCount) } @@ -230,7 +231,7 @@ func checkTables(t *testing.T, count int) { // checkTablesCount checks the number of tables in the given tablet func checkTablesCount(t *testing.T, tablet *cluster.Vttablet, count int) { queryResult, err := tablet.VttabletProcess.QueryTablet("show tables;", keyspaceName, true) - assert.Nil(t, err) + require.Nil(t, err) assert.Equal(t, len(queryResult.Rows), count) } @@ -243,7 +244,7 @@ func testCopySchemaShards(t *testing.T, source string, shard int) { // Run the command twice to make sure it's idempotent. for i := 0; i < 2; i++ { err := clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", source, fmt.Sprintf("%s/%d", keyspaceName, shard)) - assert.Nil(t, err) + require.Nil(t, err) } // shard_2_master should look the same as the replica we copied from checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0], totalTableCount) @@ -261,11 +262,11 @@ func testCopySchemaShardWithDifferentDB(t *testing.T, shard int) { masterTabletAlias := clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0].VttabletProcess.TabletPath schema, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetSchema", masterTabletAlias) - assert.Nil(t, err) + require.Nil(t, err) resultMap := make(map[string]interface{}) err = json.Unmarshal([]byte(schema), &resultMap) - assert.Nil(t, err) + require.Nil(t, err) dbSchema := reflect.ValueOf(resultMap["database_schema"]) assert.True(t, strings.Contains(dbSchema.String(), "utf8")) @@ -275,10 +276,10 @@ func testCopySchemaShardWithDifferentDB(t *testing.T, shard int) { // because we use "CREATE DATABASE IF NOT EXISTS" and this doesn't fail if // there are differences in the options e.g. the character set.) err = clusterInstance.VtctlclientProcess.ExecuteCommand("ExecuteFetchAsDba", "-json", masterTabletAlias, "ALTER DATABASE vt_ks CHARACTER SET latin1") - assert.Nil(t, err) + require.Nil(t, err) output, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("CopySchemaShard", source, fmt.Sprintf("%s/%d", keyspaceName, shard)) - assert.NotNil(t, err) + require.Error(t, err) assert.True(t, strings.Contains(output, "schemas are different")) // shard_2_master should have the same number of tables. Only the db @@ -292,5 +293,5 @@ func addNewShard(t *testing.T, shard int) { Name: keyspaceName, } err := clusterInstance.StartKeyspace(*keyspace, []string{fmt.Sprintf("%d", shard)}, 1, false) - assert.Nil(t, err) + require.Nil(t, err) } diff --git a/go/test/endtoend/vtgate/sequence/seq_test.go b/go/test/endtoend/vtgate/sequence/seq_test.go index 63eff2f43be..6f9ba004f66 100644 --- a/go/test/endtoend/vtgate/sequence/seq_test.go +++ b/go/test/endtoend/vtgate/sequence/seq_test.go @@ -115,7 +115,7 @@ func TestMain(m *testing.M) { func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { t.Helper() qr, err := conn.ExecuteFetch(query, 1000, true) - require.NoError(t, err) + require.Nil(t, err) return qr } @@ -126,7 +126,7 @@ func TestSeq(t *testing.T) { Port: clusterInstance.VtgateMySQLPort, } conn, err := mysql.Connect(ctx, &vtParams) - require.NoError(t, err) + require.Nil(t, err) defer conn.Close() //Initialize seq table From d94d74353039917ab6d0d50ba12e8decc9b1702c Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Tue, 25 Feb 2020 02:42:26 +0530 Subject: [PATCH 167/825] Added image in helm chart for configration Signed-off-by: Yuvraj --- helm/vitess/templates/_cron-jobs.tpl | 3 +- helm/vitess/templates/_helpers.tpl | 103 +++++++++++++++++++++++- helm/vitess/templates/_jobs.tpl | 6 +- helm/vitess/templates/_keyspace.tpl | 5 +- helm/vitess/templates/_orchestrator.tpl | 5 +- helm/vitess/templates/_pmm.tpl | 2 +- helm/vitess/templates/_shard.tpl | 5 +- helm/vitess/templates/_vtctld.tpl | 3 +- helm/vitess/templates/_vtgate.tpl | 8 +- helm/vitess/templates/_vttablet.tpl | 42 ++++++---- helm/vitess/templates/vitess.yaml | 18 ++--- helm/vitess/values.yaml | 12 +++ 12 files changed, 172 insertions(+), 40 deletions(-) diff --git a/helm/vitess/templates/_cron-jobs.tpl b/helm/vitess/templates/_cron-jobs.tpl index 3b4ffd13852..c36a68a3b4a 100644 --- a/helm/vitess/templates/_cron-jobs.tpl +++ b/helm/vitess/templates/_cron-jobs.tpl @@ -13,6 +13,7 @@ {{- $backup := index . 7 -}} {{- $namespace := index . 8 -}} {{- $defaultVtctlclient := index . 9 }} +{{- $repo := index . 10 }} {{ if $backup.enabled }} # create cron job for current shard @@ -55,7 +56,7 @@ spec: containers: - name: backup - image: "vitess/vtctlclient:{{$vitessTag}}" + image: "{{$repo}}/vtctlclient:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 14 }} diff --git a/helm/vitess/templates/_helpers.tpl b/helm/vitess/templates/_helpers.tpl index 695d198758f..a9e64060e9a 100644 --- a/helm/vitess/templates/_helpers.tpl +++ b/helm/vitess/templates/_helpers.tpl @@ -152,7 +152,7 @@ done -restore_from_backup {{ end }} --backup_storage_implementation=$VT_BACKUP_SERVICE + -backup_storage_implementation=$VT_BACKUP_SERVICE {{ if eq .backup_storage_implementation "gcs" }} -gcs_backup_storage_bucket=$VT_GCS_BACKUP_STORAGE_BUCKET @@ -168,6 +168,40 @@ done -ceph_backup_storage_config=$CEPH_CREDENTIALS_FILE {{ end }} + {{ if eq .backup_engine_implementation "xtrabackup" }} + -backup_engine_implementation=$VT_BACKUP_ENGINE_IMPLIMENTATION + + {{ if .xtrabackup_root_path }} + -xtrabackup_root_path=$VT_XTRABACKUP_ROOT_PATH + {{ end }} + + {{ if .xtrabackup_backup_flags }} + -xtrabackup_backup_flags=$VT_XTRABACKUP_BACKUP_FLAG + {{ end }} + + + {{ if .xbstream_restore_flags }} + -xbstream_restore_flags=$VT_XTRABACKUP_RESTORE_FLAGS + {{ end }} + + {{ if .xtrabackup_stream_mode }} + -xtrabackup_stream_mode=$VT_XTRABACKUP_STREAM_MODE + {{ end }} + + + {{ if .xtrabackup_user }} + -xtrabackup_user=$VT_XTRABACKUP_USER + {{ end }} + + {{ if .xtrabackup_stripes }} + -xtrabackup_stripes=$VT_XTRABACKUP_STRIPES + {{ end }} + + {{ if .xtrabackup_stripe_block_size }} + -xtrabackup_stripe_block_size=$VT_XTRABACKUP_STRIPE_BLOCK_SIZE + {{ end }} + + {{ end }} {{ end }} {{ end }} @@ -225,6 +259,73 @@ done {{ end }} +{{ if eq .backup_engine_implementation "xtrabackup" }} + +- name: VT_BACKUP_ENGINE_IMPLIMENTATION + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.backup_engine_implementation + + {{ if .xtrabackup_root_path }} +- name: VT_XTRABACKUP_ROOT_PATH + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.xtrabackup_root_path + {{ end }} + + {{ if .xtrabackup_backup_flags }} +- name: VT_XTRABACKUP_BACKUP_FLAG + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.xtrabackup_backup_flags + {{ end }} + + {{ if .xbstream_restore_flags }} +- name: VT_XTRABACKUP_RESTORE_FLAGS + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.xbstream_restore_flags + {{ end }} + + {{ if .xtrabackup_stream_mode }} +- name: VT_XTRABACKUP_STREAM_MODE + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.xtrabackup_stream_mode + {{ end }} + + {{ if .xtrabackup_user }} +- name: VT_XTRABACKUP_USER + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.xtrabackup_user + {{ end }} + + {{ if .xtrabackup_stripes }} +- name: VT_XTRABACKUP_STRIPES + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.xtrabackup_stripes + {{ end }} + + {{ if .xtrabackup_stripe_block_size }} +- name: VT_XTRABACKUP_STRIPE_BLOCK_SIZE + valueFrom: + configMapKeyRef: + name: vitess-cm + key: backup.xtrabackup_stripe_block_size + {{ end }} + + +{{ end }} + {{ end }} {{- end -}} diff --git a/helm/vitess/templates/_jobs.tpl b/helm/vitess/templates/_jobs.tpl index 45d389ff50e..c1c5c91f83a 100644 --- a/helm/vitess/templates/_jobs.tpl +++ b/helm/vitess/templates/_jobs.tpl @@ -6,6 +6,7 @@ {{- $job := index . 0 -}} {{- $defaultVtctlclient := index . 1 -}} {{- $namespace := index . 2 -}} +{{- $repo := index . 3 -}} {{- $vitessTag := $job.vitessTag | default $defaultVtctlclient.vitessTag -}} {{- $secrets := $job.secrets | default $defaultVtctlclient.secrets }} @@ -30,7 +31,7 @@ spec: restartPolicy: OnFailure containers: - name: vtjob - image: "vitess/vtctlclient:{{$vitessTag}}" + image: "{{$repo}}/vtctlclient:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} resources: @@ -54,6 +55,7 @@ spec: {{- $job := index . 0 -}} {{- $defaultVtworker := index . 1 -}} {{- $namespace := index . 2 -}} +{{- $repo := index . 3 -}} {{- $vitessTag := $job.vitessTag | default $defaultVtworker.vitessTag -}} {{- $secrets := $job.secrets | default $defaultVtworker.secrets }} @@ -79,7 +81,7 @@ spec: restartPolicy: OnFailure containers: - name: vtjob - image: "vitess/vtworker:{{$vitessTag}}" + image: "{{$repo}}/vtworker:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtworker.secrets | indent 10 }} resources: diff --git a/helm/vitess/templates/_keyspace.tpl b/helm/vitess/templates/_keyspace.tpl index 2ba9f6872c3..0061e15c83c 100644 --- a/helm/vitess/templates/_keyspace.tpl +++ b/helm/vitess/templates/_keyspace.tpl @@ -7,6 +7,7 @@ {{- $keyspace := index . 1 -}} {{- $defaultVtctlclient := index . 2 -}} {{- $namespace := index . 3 -}} +{{- $repo := index . 4 -}} # sanitize inputs for labels {{- $keyspaceClean := include "clean-label" $keyspace.name -}} @@ -33,7 +34,7 @@ spec: restartPolicy: OnFailure containers: - name: apply-schema - image: "vitess/vtctlclient:{{$vitessTag}}" + image: "{{$repo}}/vtctlclient:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} @@ -100,7 +101,7 @@ spec: restartPolicy: OnFailure containers: - name: apply-vschema - image: "vitess/vtctlclient:{{$vitessTag}}" + image: "{{$repo}}/vtctlclient:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} diff --git a/helm/vitess/templates/_orchestrator.tpl b/helm/vitess/templates/_orchestrator.tpl index 13ad07831aa..ba15ae4e4dc 100644 --- a/helm/vitess/templates/_orchestrator.tpl +++ b/helm/vitess/templates/_orchestrator.tpl @@ -5,6 +5,7 @@ # set tuple values to more recognizable variables {{- $orc := index . 0 -}} {{- $defaultVtctlclient := index . 1 }} +{{- $logtail := index . 2 }} apiVersion: v1 kind: Service @@ -123,7 +124,7 @@ spec: value: "15999" - name: recovery-log - image: vitess/logtail:helm-1.0.7-5 + image: "{{ $logtail.image }}:{{ $logtail.tag }}" imagePullPolicy: IfNotPresent env: - name: TAIL_FILEPATH @@ -133,7 +134,7 @@ spec: mountPath: /tmp - name: audit-log - image: vitess/logtail:helm-1.0.7-5 + image: "{{ $logtail.image }}:{{ $logtail.tag }}" imagePullPolicy: IfNotPresent env: - name: TAIL_FILEPATH diff --git a/helm/vitess/templates/_pmm.tpl b/helm/vitess/templates/_pmm.tpl index 9669f58298d..c5fec6a68e5 100644 --- a/helm/vitess/templates/_pmm.tpl +++ b/helm/vitess/templates/_pmm.tpl @@ -50,7 +50,7 @@ spec: spec: containers: - name: pmm - image: "percona/pmm-server:{{ $pmm.pmmTag }}" + image: "{{ $pmm.image}}:{{ $pmm.pmmTag }}" ports: - name: web diff --git a/helm/vitess/templates/_shard.tpl b/helm/vitess/templates/_shard.tpl index 3d075c24f85..d58cfadb53f 100644 --- a/helm/vitess/templates/_shard.tpl +++ b/helm/vitess/templates/_shard.tpl @@ -9,6 +9,7 @@ {{- $defaultVtctlclient := index . 3 -}} {{- $namespace := index . 4 -}} {{- $totalTabletCount := index . 5 -}} +{{- $repo := index . 6 -}} {{- $cellClean := include "clean-label" $cell.name -}} {{- $keyspaceClean := include "clean-label" $keyspace.name -}} @@ -42,7 +43,7 @@ spec: restartPolicy: OnFailure containers: - name: init-shard-master - image: "vitess/vtctlclient:{{$vitessTag}}" + image: "{{$repo}}/vtctlclient:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} @@ -145,7 +146,7 @@ spec: restartPolicy: OnFailure containers: - name: copy-schema - image: "vitess/vtctlclient:{{$vitessTag}}" + image: "{{$repo}}/vtctlclient:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} diff --git a/helm/vitess/templates/_vtctld.tpl b/helm/vitess/templates/_vtctld.tpl index af178035e99..911c3c230c9 100644 --- a/helm/vitess/templates/_vtctld.tpl +++ b/helm/vitess/templates/_vtctld.tpl @@ -8,6 +8,7 @@ {{- $defaultVtctld := index . 2 -}} {{- $namespace := index . 3 -}} {{- $config := index . 4 -}} +{{- $repo := index . 5 -}} {{- with $cell.vtctld -}} @@ -59,7 +60,7 @@ spec: {{ include "vtctld-affinity" (tuple $cellClean $cell.region) | indent 6 }} containers: - name: vtctld - image: vitess/vtctld:{{$vitessTag}} + image: {{$repo}}/vtctld:{{$vitessTag}} imagePullPolicy: IfNotPresent readinessProbe: httpGet: diff --git a/helm/vitess/templates/_vtgate.tpl b/helm/vitess/templates/_vtgate.tpl index 1455e9c47a2..580ab753cd1 100644 --- a/helm/vitess/templates/_vtgate.tpl +++ b/helm/vitess/templates/_vtgate.tpl @@ -7,6 +7,7 @@ {{- $cell := index . 1 -}} {{- $defaultVtgate := index . 2 -}} {{- $namespace := index . 3 -}} +{{- $repo := index . 4 -}} {{- with $cell.vtgate -}} @@ -68,13 +69,13 @@ spec: {{ if $cell.mysqlProtocol.enabled }} {{ if eq $cell.mysqlProtocol.authType "secret" }} initContainers: -{{ include "init-mysql-creds" (tuple $vitessTag $cell) | indent 8 }} +{{ include "init-mysql-creds" (tuple $vitessTag $cell $repo) | indent 8 }} {{ end }} {{ end }} containers: - name: vtgate - image: vitess/vtgate:{{$vitessTag}} + image: {{$repo}}/vtgate:{{$vitessTag}} imagePullPolicy: IfNotPresent readinessProbe: httpGet: @@ -219,11 +220,12 @@ affinity: {{ define "init-mysql-creds" -}} {{- $vitessTag := index . 0 -}} {{- $cell := index . 1 -}} +{{- $repo := index . 3 -}} {{- with $cell.mysqlProtocol }} - name: init-mysql-creds - image: "vitess/vtgate:{{$vitessTag}}" + image: "{{$repo}}/vtgate:{{$vitessTag}}" imagePullPolicy: IfNotPresent volumeMounts: - name: creds diff --git a/helm/vitess/templates/_vttablet.tpl b/helm/vitess/templates/_vttablet.tpl index 3053407af4d..b342eca2e84 100644 --- a/helm/vitess/templates/_vttablet.tpl +++ b/helm/vitess/templates/_vttablet.tpl @@ -48,6 +48,9 @@ spec: {{- $config := index . 8 -}} {{- $pmm := index . 9 -}} {{- $orc := index . 10 -}} +{{- $repo := index . 11 -}} +{{- $logrotate := index . 12 -}} +{{- $logtail := index . 13 -}} # sanitize inputs for labels {{- $cellClean := include "clean-label" $cell.name -}} @@ -101,17 +104,17 @@ spec: {{ include "vttablet-affinity" (tuple $cellClean $keyspaceClean $shardClean $cell.region) | indent 6 }} initContainers: -{{ include "init-mysql" (tuple $vitessTag $cellClean) | indent 8 }} -{{ include "init-vttablet" (tuple $vitessTag $cell $cellClean $namespace) | indent 8 }} +{{ include "init-mysql" (tuple $vitessTag $cellClean $repo) | indent 8 }} +{{ include "init-vttablet" (tuple $vitessTag $cell $cellClean $namespace $repo) | indent 8 }} containers: {{ include "cont-mysql" (tuple $topology $cell $keyspace $shard $tablet $defaultVttablet $uid) | indent 8 }} -{{ include "cont-vttablet" (tuple $topology $cell $keyspace $shard $tablet $defaultVttablet $defaultVtctlclient $vitessTag $uid $namespace $config $orc) | indent 8 }} -{{ include "cont-logrotate" . | indent 8 }} -{{ include "cont-mysql-generallog" . | indent 8 }} -{{ include "cont-mysql-errorlog" . | indent 8 }} -{{ include "cont-mysql-slowlog" . | indent 8 }} -{{ if $pmm.enabled }}{{ include "cont-pmm-client" (tuple $pmm $namespace $keyspace) | indent 8 }}{{ end }} +{{ include "cont-vttablet" (tuple $topology $cell $keyspace $shard $tablet $defaultVttablet $defaultVtctlclient $vitessTag $uid $namespace $config $orc $repo) | indent 8 }} +{{ include "cont-logrotate" (tuple $logrotate) | indent 8 }} +{{ include "cont-mysql-generallog" (tuple $logrotate) | indent 8 }} +{{ include "cont-mysql-errorlog" (tuple $logrotate) | indent 8 }} +{{ include "cont-mysql-slowlog" (tuple $logrotate) | indent 8 }} +{{ if $pmm.enabled }}{{ include "cont-pmm-client" (tuple $pmm $namespace $keyspace $repo $logtail) | indent 8 }}{{ end }} volumes: - name: vt @@ -153,7 +156,7 @@ spec: type: {{ $tablet.type | quote }} # conditionally add cron job -{{ include "vttablet-backup-cron" (tuple $cellClean $keyspaceClean $shardClean $shardName $keyspace $shard $vitessTag $config.backup $namespace $defaultVtctlclient) }} +{{ include "vttablet-backup-cron" (tuple $cellClean $keyspaceClean $shardClean $shardName $keyspace $shard $vitessTag $config.backup $namespace $defaultVtctlclient $repo) }} {{- end -}} {{- end -}} @@ -164,9 +167,10 @@ spec: {{ define "init-mysql" -}} {{- $vitessTag := index . 0 -}} {{- $cellClean := index . 1 }} +{{- $repo := index . 2 }} - name: "init-mysql" - image: "vitess/mysqlctld:{{$vitessTag}}" + image: "{{$repo}}/mysqlctld:{{$vitessTag}}" imagePullPolicy: IfNotPresent volumeMounts: - name: vtdataroot @@ -209,9 +213,10 @@ spec: {{- $cell := index . 1 -}} {{- $cellClean := index . 2 -}} {{- $namespace := index . 3 }} +{{- $repo := index . 4 }} - name: init-vttablet - image: "vitess/vtctl:{{$vitessTag}}" + image: "{{$repo}}/vtctl:{{$vitessTag}}" imagePullPolicy: IfNotPresent volumeMounts: - name: vtdataroot @@ -271,12 +276,13 @@ spec: {{- $namespace := index . 9 -}} {{- $config := index . 10 -}} {{- $orc := index . 11 -}} +{{- $repo := index . 12 -}} {{- $cellClean := include "clean-label" $cell.name -}} {{- with $tablet.vttablet }} - name: vttablet - image: "vitess/vttablet:{{$vitessTag}}" + image: "{{$repo}}/vttablet:{{$vitessTag}}" imagePullPolicy: IfNotPresent readinessProbe: httpGet: @@ -532,9 +538,10 @@ spec: # run logrotate for all log files in /vtdataroot/tabletdata ########################## {{ define "cont-logrotate" }} +{{- $logrotate := index . 0 }} - name: logrotate - image: vitess/logrotate:helm-1.0.7-5 + image: {{ $logrotate.image }}:{{ $logrotate.tag }} imagePullPolicy: IfNotPresent volumeMounts: - name: vtdataroot @@ -546,9 +553,10 @@ spec: # redirect the error log file to stdout ########################## {{ define "cont-mysql-errorlog" }} +{{- $logtail := index . 0 -}} - name: error-log - image: vitess/logtail:helm-1.0.7-5 + image: {{ $logtail.image }}:{{ $logtail.tag }} imagePullPolicy: IfNotPresent env: @@ -564,9 +572,10 @@ spec: # redirect the slow log file to stdout ########################## {{ define "cont-mysql-slowlog" }} +{{- $logtail := index . 0 -}} - name: slow-log - image: vitess/logtail:helm-1.0.7-5 + image: {{ $logtail.image }}:{{ $logtail.tag }} imagePullPolicy: IfNotPresent env: @@ -582,9 +591,10 @@ spec: # redirect the general log file to stdout ########################## {{ define "cont-mysql-generallog" }} +{{- $logtail := index . 0 -}} - name: general-log - image: vitess/logtail:helm-1.0.7-5 + image: {{ $logtail.image }}:{{ $logtail.tag }} imagePullPolicy: IfNotPresent env: diff --git a/helm/vitess/templates/vitess.yaml b/helm/vitess/templates/vitess.yaml index 7c3d36aac25..1ae18cf2c80 100644 --- a/helm/vitess/templates/vitess.yaml +++ b/helm/vitess/templates/vitess.yaml @@ -5,14 +5,14 @@ --- {{ if $.Values.pmm.enabled }} # create the pmm service and stateful set -{{ include "pmm" (tuple $.Values.pmm $.Release.Namespace) }} +{{ include "pmm" (tuple $.Values.pmm $.Release.Namespace $.Values.repo) }} --- {{ end }} {{ if $.Values.orchestrator.enabled }} # create orchestrator global services and StatefulSet -{{ include "orchestrator" (tuple $.Values.orchestrator $.Values.vtctlclient) }} +{{ include "orchestrator" (tuple $.Values.orchestrator $.Values.vtctlclient $.Values.logtail) }} --- # create orchestrator config map {{ include "orchestrator-config" (tuple $.Values.orchestrator $.Release.Namespace $.Values.vttablet.enableHeartbeat $.Values.vtctlclient) }} @@ -47,26 +47,26 @@ {{ include "etcd" (tuple $cellClean $replicas $version $resources $clusterWide) }} --- # create one controller per cell -{{ include "vtctld" (tuple $.Values.topology $cell $.Values.vtctld $.Release.Namespace $.Values.config) }} +{{ include "vtctld" (tuple $.Values.topology $cell $.Values.vtctld $.Release.Namespace $.Values.config $.Values.repo) }} --- # create a pool of vtgates per cell -{{ include "vtgate" (tuple $.Values.topology $cell $.Values.vtgate $.Release.Namespace) }} +{{ include "vtgate" (tuple $.Values.topology $cell $.Values.vtgate $.Release.Namespace $.Values.repo) }} # Tablets for keyspaces {{ range $keyspace := $cell.keyspaces }} # Keyspace initializations - {{ include "keyspace" (tuple $cell $keyspace $.Values.vtctlclient $.Release.Namespace) }} + {{ include "keyspace" (tuple $cell $keyspace $.Values.vtctlclient $.Release.Namespace $.Values.repo) }} {{ range $shard := $keyspace.shards }} {{ $totalTabletCount := len (include "tablet-count" $shard.tablets) }} # Shard initializations - {{ include "shard" (tuple $cell $keyspace $shard $.Values.vtctlclient $.Release.Namespace $totalTabletCount) }} + {{ include "shard" (tuple $cell $keyspace $shard $.Values.vtctlclient $.Release.Namespace $totalTabletCount $.Values.repo) }} # Tablet initializations {{ range $tablet := $shard.tablets }} - {{ include "vttablet" (tuple $.Values.topology $cell $keyspace $shard $tablet $.Values.vttablet $.Values.vtctlclient $.Release.Namespace $.Values.config $.Values.pmm $.Values.orchestrator) }} + {{ include "vttablet" (tuple $.Values.topology $cell $keyspace $shard $tablet $.Values.vttablet $.Values.vtctlclient $.Release.Namespace $.Values.config $.Values.pmm $.Values.orchestrator $.Values.repo $.Values.logrotate $.Values.logtail) }} {{ end }} # range $tablet {{ end }} # range $shard {{ end }} # range $keyspace @@ -75,9 +75,9 @@ {{ range $job := $.Values.jobs }} {{ if eq $job.kind "vtctlclient" }} - {{ include "vtctlclient-job" (tuple $job $.Values.vtctlclient $.Release.Namespace) }} + {{ include "vtctlclient-job" (tuple $job $.Values.vtctlclient $.Release.Namespace $.Values.repo) }} {{ else }} - {{ include "vtworker-job" (tuple $job $.Values.vtworker $.Release.Namespace) }} + {{ include "vtworker-job" (tuple $job $.Values.vtworker $.Release.Namespace $.Values.repo) }} {{ end }} {{ end }} --- diff --git a/helm/vitess/values.yaml b/helm/vitess/values.yaml index 96ef944051c..c463903b815 100644 --- a/helm/vitess/values.yaml +++ b/helm/vitess/values.yaml @@ -14,6 +14,8 @@ # will be taken from defaults defined below. # topology: +# Name of vitess base image +repo : vitess # config will be stored as a ConfigMap and mounted where appropriate config: # Backup flags will be applied to components that need them. @@ -325,6 +327,7 @@ vttablet: pmm: enabled: false pmmTag: 1.17.0 + image : percona/pmm-server client: resources: requests: @@ -385,3 +388,12 @@ orchestrator: requests: cpu: 50m memory: 350Mi + +logtail: + image: vitess/logtail + tag: helm-1.0.6 + +logrotate: + image: vitess/logrotate + tag: helm-1.0.6 + From c20d777d185ff9790227993d66fb33a6497d563f Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Tue, 25 Feb 2020 02:47:18 +0530 Subject: [PATCH 168/825] revert helper changes Signed-off-by: Yuvraj --- helm/vitess/templates/_helpers.tpl | 439 ++++++++++++++++++++++++----- 1 file changed, 365 insertions(+), 74 deletions(-) diff --git a/helm/vitess/templates/_helpers.tpl b/helm/vitess/templates/_helpers.tpl index a9e64060e9a..1e5e0c86956 100644 --- a/helm/vitess/templates/_helpers.tpl +++ b/helm/vitess/templates/_helpers.tpl @@ -152,7 +152,7 @@ done -restore_from_backup {{ end }} - -backup_storage_implementation=$VT_BACKUP_SERVICE +-backup_storage_implementation=$VT_BACKUP_SERVICE {{ if eq .backup_storage_implementation "gcs" }} -gcs_backup_storage_bucket=$VT_GCS_BACKUP_STORAGE_BUCKET @@ -168,40 +168,6 @@ done -ceph_backup_storage_config=$CEPH_CREDENTIALS_FILE {{ end }} - {{ if eq .backup_engine_implementation "xtrabackup" }} - -backup_engine_implementation=$VT_BACKUP_ENGINE_IMPLIMENTATION - - {{ if .xtrabackup_root_path }} - -xtrabackup_root_path=$VT_XTRABACKUP_ROOT_PATH - {{ end }} - - {{ if .xtrabackup_backup_flags }} - -xtrabackup_backup_flags=$VT_XTRABACKUP_BACKUP_FLAG - {{ end }} - - - {{ if .xbstream_restore_flags }} - -xbstream_restore_flags=$VT_XTRABACKUP_RESTORE_FLAGS - {{ end }} - - {{ if .xtrabackup_stream_mode }} - -xtrabackup_stream_mode=$VT_XTRABACKUP_STREAM_MODE - {{ end }} - - - {{ if .xtrabackup_user }} - -xtrabackup_user=$VT_XTRABACKUP_USER - {{ end }} - - {{ if .xtrabackup_stripes }} - -xtrabackup_stripes=$VT_XTRABACKUP_STRIPES - {{ end }} - - {{ if .xtrabackup_stripe_block_size }} - -xtrabackup_stripe_block_size=$VT_XTRABACKUP_STRIPE_BLOCK_SIZE - {{ end }} - - {{ end }} {{ end }} {{ end }} @@ -259,70 +225,395 @@ done {{ end }} -{{ if eq .backup_engine_implementation "xtrabackup" }} +{{ end }} + +{{- end -}} -- name: VT_BACKUP_ENGINE_IMPLIMENTATION - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.backup_engine_implementation +############################# +# backup volume - expects config.backup +############################# +{{- define "backup-volume" -}} + +{{ if .enabled }} + + {{ if eq .backup_storage_implementation "gcs" }} + + {{ if .gcsSecret }} +- name: backup-creds + secret: + secretName: {{ .gcsSecret }} + {{ end }} + + {{ else if eq .backup_storage_implementation "s3" }} + + {{ if .s3Secret }} +- name: backup-creds + secret: + secretName: {{ .s3Secret }} + {{ end }} + + {{ else if eq .backup_storage_implementation "ceph" }} + +- name: backup-creds + secret: + secretName: {{required ".cephSecret necessary to use backup_storage_implementation: ceph!" .cephSecret }} + + {{ end }} + +{{ end }} + +{{- end -}} + +############################# +# backup volumeMount - expects config.backup +############################# +{{- define "backup-volumeMount" -}} + +{{ if .enabled }} + + {{ if eq .backup_storage_implementation "gcs" }} + + {{ if .gcsSecret }} +- name: backup-creds + mountPath: /etc/secrets/creds + {{ end }} + + {{ else if eq .backup_storage_implementation "s3" }} + + {{ if .s3Secret }} +- name: backup-creds + mountPath: /etc/secrets/creds + {{ end }} + + {{ else if eq .backup_storage_implementation "ceph" }} + +- name: backup-creds + mountPath: /etc/secrets/creds + + {{ end }} + +{{ end }} + +{{- end -}} + +############################# +# backup exec +############################# +{{- define "backup-exec" -}} + +{{ if .enabled }} + + {{ if eq .backup_storage_implementation "gcs" }} + + {{ if .gcsSecret }} +credsPath=/etc/secrets/creds/$(ls /etc/secrets/creds/ | head -1) + +export GOOGLE_APPLICATION_CREDENTIALS=$credsPath +cat $GOOGLE_APPLICATION_CREDENTIALS + {{ end }} + + {{ else if eq .backup_storage_implementation "s3" }} + + {{ if .s3Secret }} +credsPath=/etc/secrets/creds/$(ls /etc/secrets/creds/ | head -1) + +export AWS_SHARED_CREDENTIALS_FILE=$credsPath +cat $AWS_SHARED_CREDENTIALS_FILE + {{ end }} + + {{ else if eq .backup_storage_implementation "ceph" }} + +credsPath=/etc/secrets/creds/$(ls /etc/secrets/creds/ | head -1) +export CEPH_CREDENTIALS_FILE=$credsPath +cat $CEPH_CREDENTIALS_FILE + + {{ end }} + +{{ end }} + +{{- end -}} + +############################# +# user config volume - expects config map name +############################# +{{- define "user-config-volume" -}} + +{{ if . }} + +- name: user-config + configMap: + name: {{ . }} + +{{ end }} + +{{- end -}} + +############################# +# user config volumeMount - expects config map name +############################# +{{- define "user-config-volumeMount" -}} + +{{ if . }} + +- name: user-config + mountPath: /vt/userconfig + +{{ end }} + +{{- end -}} + +############################# +# user secret volumes - expects list of secret names +############################# +{{- define "user-secret-volumes" -}} + +{{ if . }} +{{- range . }} +- name: user-secret-{{ . }} + secret: + secretName: {{ . }} +{{- end }} +{{ end }} + +{{- end -}} + +############################# +# user secret volumeMounts - expects list of secret names +############################# +{{- define "user-secret-volumeMounts" -}} + +{{ if . }} +{{- range . }} +- name: user-secret-{{ . }} + mountPath: /vt/usersecrets/{{ . }} +{{- end }} +{{ end }} + +{{- end -}} +# Helper templates + +############################# +# Format a flag map into a command line, +# as expected by the golang 'flag' package. +# Boolean flags must be given a value, such as "true" or "false". +############################# +{{- define "format-flags" -}} +{{- range $key, $value := . -}} +-{{$key}}={{$value | quote}} +{{end -}} +{{- end -}} + +############################ +# Format a flag map into a command line (inline), +# as expected by the golang 'flag' package. +# Boolean flags must be given a value, such as "true" or "false". +############################# +{{- define "format-flags-inline" -}} +{{- range $key, $value := . -}} +-{{$key}}={{$value | quote}}{{" "}} +{{- end -}} +{{- end -}} + +############################# +# Repeat a string N times, where N is the total number +# of replicas. Len must be used on the calling end to +# get an int +############################# +{{- define "tablet-count" -}} +{{- range . -}} +{{- repeat (int .vttablet.replicas) "x" -}} +{{- end -}} +{{- end -}} + +############################# +# Format a list of flag maps into a command line. +############################# +{{- define "format-flags-all" -}} +{{- range . }}{{template "format-flags" .}}{{end -}} +{{- end -}} + +############################# +# Clean labels, making sure it starts and ends with [A-Za-z0-9]. +# This is especially important for shard names, which can start or end with +# '-' (like -80 or 80-), which would be an invalid kubernetes label. +############################# +{{- define "clean-label" -}} +{{- $replaced_label := . | replace "_" "-"}} +{{- if hasPrefix "-" . -}} +x{{$replaced_label}} +{{- else if hasSuffix "-" . -}} +{{$replaced_label}}x +{{- else -}} +{{$replaced_label}} +{{- end -}} +{{- end -}} + +############################# +# injects default vitess environment variables +############################# +{{- define "vitess-env" -}} +- name: VTROOT + value: "/vt" +- name: VTDATAROOT + value: "/vtdataroot" +- name: GOBIN + value: "/vt/bin" +- name: VT_MYSQL_ROOT + value: "/usr" +- name: PKG_CONFIG_PATH + value: "/vt/lib" +{{- end -}} + +############################# +# inject default pod security +############################# +{{- define "pod-security" -}} +securityContext: + runAsUser: 1000 + fsGroup: 2000 +{{- end -}} + +############################# +# support region nodeAffinity if defined +############################# +{{- define "node-affinity" -}} +{{- $region := . -}} +{{ with $region }} +nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: "failure-domain.beta.kubernetes.io/region" + operator: In + values: [{{ $region | quote }}] +{{- end -}} +{{- end -}} + +############################# +# mycnf exec - expects extraMyCnf config map name +############################# +{{- define "mycnf-exec" -}} + +if [ "$VT_DB_FLAVOR" = "percona" ]; then + MYSQL_FLAVOR=Percona + +elif [ "$VT_DB_FLAVOR" = "mysql" ]; then + MYSQL_FLAVOR=MySQL56 + +elif [ "$VT_DB_FLAVOR" = "mysql56" ]; then + MYSQL_FLAVOR=MySQL56 - {{ if .xtrabackup_root_path }} -- name: VT_XTRABACKUP_ROOT_PATH +elif [ "$VT_DB_FLAVOR" = "maria" ]; then + MYSQL_FLAVOR=MariaDB + +elif [ "$VT_DB_FLAVOR" = "mariadb" ]; then + MYSQL_FLAVOR=MariaDB + +elif [ "$VT_DB_FLAVOR" = "mariadb103" ]; then + MYSQL_FLAVOR=MariaDB103 + +fi + +export MYSQL_FLAVOR + +{{ if . }} +for filename in /vt/userconfig/*.cnf; do + export EXTRA_MY_CNF="$EXTRA_MY_CNF:$filename" +done +{{ end }} + +{{- end -}} + +############################# +# +# all backup helpers below +# +############################# + +############################# +# backup flags - expects config.backup +############################# +{{- define "backup-flags" -}} +{{- $backup := index . 0 -}} +{{- $caller := index . 1 -}} + +{{ with $backup }} + + {{ if .enabled }} + {{ if eq $caller "vttablet" }} +-restore_from_backup + {{ end }} + +-backup_storage_implementation=$VT_BACKUP_SERVICE + + {{ if eq .backup_storage_implementation "gcs" }} +-gcs_backup_storage_bucket=$VT_GCS_BACKUP_STORAGE_BUCKET +-gcs_backup_storage_root=$VT_GCS_BACKUP_STORAGE_ROOT + + {{ else if eq .backup_storage_implementation "s3" }} +-s3_backup_aws_region=$VT_S3_BACKUP_AWS_REGION +-s3_backup_storage_bucket=$VT_S3_BACKUP_STORAGE_BUCKET +-s3_backup_storage_root=$VT_S3_BACKUP_STORAGE_ROOT +-s3_backup_server_side_encryption=$VT_S3_BACKUP_SERVER_SIDE_ENCRYPTION + + {{ else if eq .backup_storage_implementation "ceph" }} +-ceph_backup_storage_config=$CEPH_CREDENTIALS_FILE + {{ end }} + + {{ end }} + +{{ end }} + +{{- end -}} + +############################# +# backup env - expects config.backup +############################# +{{- define "backup-env" -}} + +{{ if .enabled }} + +- name: VT_BACKUP_SERVICE valueFrom: configMapKeyRef: name: vitess-cm - key: backup.xtrabackup_root_path - {{ end }} + key: backup.backup_storage_implementation - {{ if .xtrabackup_backup_flags }} -- name: VT_XTRABACKUP_BACKUP_FLAG +{{ if eq .backup_storage_implementation "gcs" }} + +- name: VT_GCS_BACKUP_STORAGE_BUCKET valueFrom: configMapKeyRef: name: vitess-cm - key: backup.xtrabackup_backup_flags - {{ end }} - - {{ if .xbstream_restore_flags }} -- name: VT_XTRABACKUP_RESTORE_FLAGS + key: backup.gcs_backup_storage_bucket +- name: VT_GCS_BACKUP_STORAGE_ROOT valueFrom: configMapKeyRef: name: vitess-cm - key: backup.xbstream_restore_flags - {{ end }} + key: backup.gcs_backup_storage_root - {{ if .xtrabackup_stream_mode }} -- name: VT_XTRABACKUP_STREAM_MODE +{{ else if eq .backup_storage_implementation "s3" }} + +- name: VT_S3_BACKUP_AWS_REGION valueFrom: configMapKeyRef: name: vitess-cm - key: backup.xtrabackup_stream_mode - {{ end }} - - {{ if .xtrabackup_user }} -- name: VT_XTRABACKUP_USER + key: backup.s3_backup_aws_region +- name: VT_S3_BACKUP_STORAGE_BUCKET valueFrom: configMapKeyRef: name: vitess-cm - key: backup.xtrabackup_user - {{ end }} - - {{ if .xtrabackup_stripes }} -- name: VT_XTRABACKUP_STRIPES + key: backup.s3_backup_storage_bucket +- name: VT_S3_BACKUP_STORAGE_ROOT valueFrom: configMapKeyRef: name: vitess-cm - key: backup.xtrabackup_stripes - {{ end }} - - {{ if .xtrabackup_stripe_block_size }} -- name: VT_XTRABACKUP_STRIPE_BLOCK_SIZE + key: backup.s3_backup_storage_root +- name: VT_S3_BACKUP_SERVER_SIDE_ENCRYPTION valueFrom: configMapKeyRef: name: vitess-cm - key: backup.xtrabackup_stripe_block_size - {{ end }} - + key: backup.s3_backup_server_side_encryption {{ end }} From 5c1439cd95199f0f1fbf80a63284febe9cc5ae87 Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Tue, 25 Feb 2020 02:48:46 +0530 Subject: [PATCH 169/825] revert helper changes Signed-off-by: Yuvraj --- helm/vitess/templates/_helpers.tpl | 392 ----------------------------- 1 file changed, 392 deletions(-) diff --git a/helm/vitess/templates/_helpers.tpl b/helm/vitess/templates/_helpers.tpl index 1e5e0c86956..695d198758f 100644 --- a/helm/vitess/templates/_helpers.tpl +++ b/helm/vitess/templates/_helpers.tpl @@ -390,395 +390,3 @@ cat $CEPH_CREDENTIALS_FILE {{ end }} {{- end -}} -# Helper templates - -############################# -# Format a flag map into a command line, -# as expected by the golang 'flag' package. -# Boolean flags must be given a value, such as "true" or "false". -############################# -{{- define "format-flags" -}} -{{- range $key, $value := . -}} --{{$key}}={{$value | quote}} -{{end -}} -{{- end -}} - -############################ -# Format a flag map into a command line (inline), -# as expected by the golang 'flag' package. -# Boolean flags must be given a value, such as "true" or "false". -############################# -{{- define "format-flags-inline" -}} -{{- range $key, $value := . -}} --{{$key}}={{$value | quote}}{{" "}} -{{- end -}} -{{- end -}} - -############################# -# Repeat a string N times, where N is the total number -# of replicas. Len must be used on the calling end to -# get an int -############################# -{{- define "tablet-count" -}} -{{- range . -}} -{{- repeat (int .vttablet.replicas) "x" -}} -{{- end -}} -{{- end -}} - -############################# -# Format a list of flag maps into a command line. -############################# -{{- define "format-flags-all" -}} -{{- range . }}{{template "format-flags" .}}{{end -}} -{{- end -}} - -############################# -# Clean labels, making sure it starts and ends with [A-Za-z0-9]. -# This is especially important for shard names, which can start or end with -# '-' (like -80 or 80-), which would be an invalid kubernetes label. -############################# -{{- define "clean-label" -}} -{{- $replaced_label := . | replace "_" "-"}} -{{- if hasPrefix "-" . -}} -x{{$replaced_label}} -{{- else if hasSuffix "-" . -}} -{{$replaced_label}}x -{{- else -}} -{{$replaced_label}} -{{- end -}} -{{- end -}} - -############################# -# injects default vitess environment variables -############################# -{{- define "vitess-env" -}} -- name: VTROOT - value: "/vt" -- name: VTDATAROOT - value: "/vtdataroot" -- name: GOBIN - value: "/vt/bin" -- name: VT_MYSQL_ROOT - value: "/usr" -- name: PKG_CONFIG_PATH - value: "/vt/lib" -{{- end -}} - -############################# -# inject default pod security -############################# -{{- define "pod-security" -}} -securityContext: - runAsUser: 1000 - fsGroup: 2000 -{{- end -}} - -############################# -# support region nodeAffinity if defined -############################# -{{- define "node-affinity" -}} -{{- $region := . -}} -{{ with $region }} -nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: "failure-domain.beta.kubernetes.io/region" - operator: In - values: [{{ $region | quote }}] -{{- end -}} -{{- end -}} - -############################# -# mycnf exec - expects extraMyCnf config map name -############################# -{{- define "mycnf-exec" -}} - -if [ "$VT_DB_FLAVOR" = "percona" ]; then - MYSQL_FLAVOR=Percona - -elif [ "$VT_DB_FLAVOR" = "mysql" ]; then - MYSQL_FLAVOR=MySQL56 - -elif [ "$VT_DB_FLAVOR" = "mysql56" ]; then - MYSQL_FLAVOR=MySQL56 - -elif [ "$VT_DB_FLAVOR" = "maria" ]; then - MYSQL_FLAVOR=MariaDB - -elif [ "$VT_DB_FLAVOR" = "mariadb" ]; then - MYSQL_FLAVOR=MariaDB - -elif [ "$VT_DB_FLAVOR" = "mariadb103" ]; then - MYSQL_FLAVOR=MariaDB103 - -fi - -export MYSQL_FLAVOR - -{{ if . }} -for filename in /vt/userconfig/*.cnf; do - export EXTRA_MY_CNF="$EXTRA_MY_CNF:$filename" -done -{{ end }} - -{{- end -}} - -############################# -# -# all backup helpers below -# -############################# - -############################# -# backup flags - expects config.backup -############################# -{{- define "backup-flags" -}} -{{- $backup := index . 0 -}} -{{- $caller := index . 1 -}} - -{{ with $backup }} - - {{ if .enabled }} - {{ if eq $caller "vttablet" }} --restore_from_backup - {{ end }} - --backup_storage_implementation=$VT_BACKUP_SERVICE - - {{ if eq .backup_storage_implementation "gcs" }} --gcs_backup_storage_bucket=$VT_GCS_BACKUP_STORAGE_BUCKET --gcs_backup_storage_root=$VT_GCS_BACKUP_STORAGE_ROOT - - {{ else if eq .backup_storage_implementation "s3" }} --s3_backup_aws_region=$VT_S3_BACKUP_AWS_REGION --s3_backup_storage_bucket=$VT_S3_BACKUP_STORAGE_BUCKET --s3_backup_storage_root=$VT_S3_BACKUP_STORAGE_ROOT --s3_backup_server_side_encryption=$VT_S3_BACKUP_SERVER_SIDE_ENCRYPTION - - {{ else if eq .backup_storage_implementation "ceph" }} --ceph_backup_storage_config=$CEPH_CREDENTIALS_FILE - {{ end }} - - {{ end }} - -{{ end }} - -{{- end -}} - -############################# -# backup env - expects config.backup -############################# -{{- define "backup-env" -}} - -{{ if .enabled }} - -- name: VT_BACKUP_SERVICE - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.backup_storage_implementation - -{{ if eq .backup_storage_implementation "gcs" }} - -- name: VT_GCS_BACKUP_STORAGE_BUCKET - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.gcs_backup_storage_bucket -- name: VT_GCS_BACKUP_STORAGE_ROOT - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.gcs_backup_storage_root - -{{ else if eq .backup_storage_implementation "s3" }} - -- name: VT_S3_BACKUP_AWS_REGION - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.s3_backup_aws_region -- name: VT_S3_BACKUP_STORAGE_BUCKET - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.s3_backup_storage_bucket -- name: VT_S3_BACKUP_STORAGE_ROOT - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.s3_backup_storage_root -- name: VT_S3_BACKUP_SERVER_SIDE_ENCRYPTION - valueFrom: - configMapKeyRef: - name: vitess-cm - key: backup.s3_backup_server_side_encryption - -{{ end }} - -{{ end }} - -{{- end -}} - -############################# -# backup volume - expects config.backup -############################# -{{- define "backup-volume" -}} - -{{ if .enabled }} - - {{ if eq .backup_storage_implementation "gcs" }} - - {{ if .gcsSecret }} -- name: backup-creds - secret: - secretName: {{ .gcsSecret }} - {{ end }} - - {{ else if eq .backup_storage_implementation "s3" }} - - {{ if .s3Secret }} -- name: backup-creds - secret: - secretName: {{ .s3Secret }} - {{ end }} - - {{ else if eq .backup_storage_implementation "ceph" }} - -- name: backup-creds - secret: - secretName: {{required ".cephSecret necessary to use backup_storage_implementation: ceph!" .cephSecret }} - - {{ end }} - -{{ end }} - -{{- end -}} - -############################# -# backup volumeMount - expects config.backup -############################# -{{- define "backup-volumeMount" -}} - -{{ if .enabled }} - - {{ if eq .backup_storage_implementation "gcs" }} - - {{ if .gcsSecret }} -- name: backup-creds - mountPath: /etc/secrets/creds - {{ end }} - - {{ else if eq .backup_storage_implementation "s3" }} - - {{ if .s3Secret }} -- name: backup-creds - mountPath: /etc/secrets/creds - {{ end }} - - {{ else if eq .backup_storage_implementation "ceph" }} - -- name: backup-creds - mountPath: /etc/secrets/creds - - {{ end }} - -{{ end }} - -{{- end -}} - -############################# -# backup exec -############################# -{{- define "backup-exec" -}} - -{{ if .enabled }} - - {{ if eq .backup_storage_implementation "gcs" }} - - {{ if .gcsSecret }} -credsPath=/etc/secrets/creds/$(ls /etc/secrets/creds/ | head -1) - -export GOOGLE_APPLICATION_CREDENTIALS=$credsPath -cat $GOOGLE_APPLICATION_CREDENTIALS - {{ end }} - - {{ else if eq .backup_storage_implementation "s3" }} - - {{ if .s3Secret }} -credsPath=/etc/secrets/creds/$(ls /etc/secrets/creds/ | head -1) - -export AWS_SHARED_CREDENTIALS_FILE=$credsPath -cat $AWS_SHARED_CREDENTIALS_FILE - {{ end }} - - {{ else if eq .backup_storage_implementation "ceph" }} - -credsPath=/etc/secrets/creds/$(ls /etc/secrets/creds/ | head -1) -export CEPH_CREDENTIALS_FILE=$credsPath -cat $CEPH_CREDENTIALS_FILE - - {{ end }} - -{{ end }} - -{{- end -}} - -############################# -# user config volume - expects config map name -############################# -{{- define "user-config-volume" -}} - -{{ if . }} - -- name: user-config - configMap: - name: {{ . }} - -{{ end }} - -{{- end -}} - -############################# -# user config volumeMount - expects config map name -############################# -{{- define "user-config-volumeMount" -}} - -{{ if . }} - -- name: user-config - mountPath: /vt/userconfig - -{{ end }} - -{{- end -}} - -############################# -# user secret volumes - expects list of secret names -############################# -{{- define "user-secret-volumes" -}} - -{{ if . }} -{{- range . }} -- name: user-secret-{{ . }} - secret: - secretName: {{ . }} -{{- end }} -{{ end }} - -{{- end -}} - -############################# -# user secret volumeMounts - expects list of secret names -############################# -{{- define "user-secret-volumeMounts" -}} - -{{ if . }} -{{- range . }} -- name: user-secret-{{ . }} - mountPath: /vt/usersecrets/{{ . }} -{{- end }} -{{ end }} - -{{- end -}} From 056b18f5bac958fcc656aac3a994849d4adf1c91 Mon Sep 17 00:00:00 2001 From: deepthi Date: Fri, 21 Feb 2020 12:06:47 -0800 Subject: [PATCH 170/825] PlannedReparentShard: always create a new context for calling UndoDemoteMaster Signed-off-by: deepthi --- go/vt/wrangler/reparent.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/go/vt/wrangler/reparent.go b/go/vt/wrangler/reparent.go index 1d63aa748a3..a36f7a6cf96 100644 --- a/go/vt/wrangler/reparent.go +++ b/go/vt/wrangler/reparent.go @@ -606,7 +606,12 @@ func (wr *Wrangler) plannedReparentShardLocked(ctx context.Context, ev *events.R if err != nil || (ctx.Err() != nil && ctx.Err() == context.DeadlineExceeded) { // If we fail to promote the new master, try to roll back to the // original master before aborting. - undoCtx, undoCancel := context.WithTimeout(ctx, *topo.RemoteOperationTimeout) + // It is possible that we have used up the original context, or that + // not enough time is left on it before it times out. + // But at this point we really need to be able to Undo so as not to + // leave the cluster in a bad state. + // So we create a fresh context based on context.Background(). + undoCtx, undoCancel := context.WithTimeout(context.Background(), *topo.RemoteOperationTimeout) defer undoCancel() if err1 := wr.tmc.UndoDemoteMaster(undoCtx, oldMasterTabletInfo.Tablet); err1 != nil { log.Warningf("Encountered error %v while trying to undo DemoteMaster", err1) From deb5ea37ce4857da47b6c8a61c2c0ac1bf51880e Mon Sep 17 00:00:00 2001 From: Derrick Laird Date: Fri, 14 Feb 2020 09:47:33 -0700 Subject: [PATCH 171/825] vitessdriver: add ability to set custom gRPC dial options Signed-off-by: Derrick Laird --- go/vt/vitessdriver/driver.go | 14 ++++++++++++ go/vt/vtgate/grpcvtgateconn/conn.go | 35 +++++++++++++++++++---------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/go/vt/vitessdriver/driver.go b/go/vt/vitessdriver/driver.go index 720f13f5bc6..c9f5b60ad21 100644 --- a/go/vt/vitessdriver/driver.go +++ b/go/vt/vitessdriver/driver.go @@ -23,6 +23,8 @@ import ( "encoding/json" "errors" + "google.golang.org/grpc" + "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" "vitess.io/vitess/go/vt/vtgate/vtgateconn" ) @@ -76,6 +78,11 @@ func OpenWithConfiguration(c Configuration) (*sql.DB, error) { if err != nil { return nil, err } + + if len(c.GRPCDialOptions) != 0 { + vtgateconn.RegisterDialer(c.Protocol, grpcvtgateconn.DialWithOpts(context.TODO(), c.GRPCDialOptions...)) + } + return sql.Open("vitess", json) } @@ -139,6 +146,13 @@ type Configuration struct { // This setting has no effect if ConvertDatetime is not set. // Default: UTC DefaultLocation string + + // GRPCDialOptions registers a new vtgateconn dialer with these dial options using the + // protocol as the key. This may overwrite the default grpcvtgateconn dial option + // if a custom one hasn't been specified in the config. + // + // Default: none + GRPCDialOptions []grpc.DialOption `json:"-"` } // toJSON converts Configuration to the JSON string which is required by the diff --git a/go/vt/vtgate/grpcvtgateconn/conn.go b/go/vt/vtgate/grpcvtgateconn/conn.go index 27a238d88c7..79fc977d683 100644 --- a/go/vt/vtgate/grpcvtgateconn/conn.go +++ b/go/vt/vtgate/grpcvtgateconn/conn.go @@ -54,19 +54,30 @@ type vtgateConn struct { } func dial(ctx context.Context, addr string) (vtgateconn.Impl, error) { - opt, err := grpcclient.SecureDialOption(*cert, *key, *ca, *name) - if err != nil { - return nil, err - } - cc, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt) - if err != nil { - return nil, err + return DialWithOpts(ctx)(ctx, addr) +} + +// DialWithOpts allows for custom dial options to be set on a vtgateConn. +func DialWithOpts(ctx context.Context, opts ...grpc.DialOption) vtgateconn.DialerFunc { + return func(ctx context.Context, address string) (vtgateconn.Impl, error) { + opt, err := grpcclient.SecureDialOption(*cert, *key, *ca, *name) + if err != nil { + return nil, err + } + + opts = append(opts, opt) + + cc, err := grpcclient.Dial(address, grpcclient.FailFast(false), opts...) + if err != nil { + return nil, err + } + + c := vtgateservicepb.NewVitessClient(cc) + return &vtgateConn{ + cc: cc, + c: c, + }, nil } - c := vtgateservicepb.NewVitessClient(cc) - return &vtgateConn{ - cc: cc, - c: c, - }, nil } func (conn *vtgateConn) Execute(ctx context.Context, session *vtgatepb.Session, query string, bindVars map[string]*querypb.BindVariable) (*vtgatepb.Session, *sqltypes.Result, error) { From 9bd118b574fe06fa6687213e26ef5cfc2df81b4f Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Tue, 25 Feb 2020 05:43:59 +0530 Subject: [PATCH 172/825] change variable comment Signed-off-by: Yuvraj --- helm/vitess/values.yaml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/helm/vitess/values.yaml b/helm/vitess/values.yaml index c463903b815..2cf97c23edd 100644 --- a/helm/vitess/values.yaml +++ b/helm/vitess/values.yaml @@ -14,8 +14,10 @@ # will be taken from defaults defined below. # topology: -# Name of vitess base image +# Repository for vitess images. Defaults to vitess repo on dockerhub. +# Override by specifying the full path of alternate repository, e.g. repo : vitess + # config will be stored as a ConfigMap and mounted where appropriate config: # Backup flags will be applied to components that need them. @@ -389,11 +391,3 @@ orchestrator: cpu: 50m memory: 350Mi -logtail: - image: vitess/logtail - tag: helm-1.0.6 - -logrotate: - image: vitess/logrotate - tag: helm-1.0.6 - From a9928e03aab5071be6752462704dda710420d167 Mon Sep 17 00:00:00 2001 From: Derrick Laird Date: Mon, 24 Feb 2020 15:56:45 -0700 Subject: [PATCH 173/825] vitessdriver: change how defaults are set Move setDefaults() on the configuration so the driver is always dialing with protocol so the flag in vtgateconn doesn't affect it. Signed-off-by: Derrick Laird --- go/vt/vitessdriver/driver.go | 16 ++++++++++------ go/vt/vitessdriver/driver_test.go | 8 ++++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/go/vt/vitessdriver/driver.go b/go/vt/vitessdriver/driver.go index c9f5b60ad21..92b89756d76 100644 --- a/go/vt/vitessdriver/driver.go +++ b/go/vt/vitessdriver/driver.go @@ -74,6 +74,8 @@ func OpenForStreaming(address, target string) (*sql.DB, error) { // It allows to pass in a Configuration struct to control all possible // settings of the Vitess Go SQL driver. func OpenWithConfiguration(c Configuration) (*sql.DB, error) { + c.setDefaults() + json, err := c.toJSON() if err != nil { return nil, err @@ -109,12 +111,17 @@ func (d drv) Open(name string) (driver.Conn, error) { if err != nil { return nil, err } + + c.setDefaults() + if c.convert, err = newConverter(&c.Configuration); err != nil { return nil, err } + if err = c.dial(); err != nil { return nil, err } + return c, nil } @@ -158,7 +165,6 @@ type Configuration struct { // toJSON converts Configuration to the JSON string which is required by the // Vitess driver. Default values for empty fields will be set. func (c Configuration) toJSON() (string, error) { - c.setDefaults() jsonBytes, err := json.Marshal(c) if err != nil { return "", err @@ -168,6 +174,8 @@ func (c Configuration) toJSON() (string, error) { // setDefaults sets the default values for empty fields. func (c *Configuration) setDefaults() { + // if no protocol is provided default to grpc so the driver is in control + // of the connection protocol and not the flag vtgateconn.VtgateProtocol if c.Protocol == "" { c.Protocol = "grpc" } @@ -182,11 +190,7 @@ type conn struct { func (c *conn) dial() error { var err error - if c.Protocol == "" { - c.conn, err = vtgateconn.Dial(context.Background(), c.Address) - } else { - c.conn, err = vtgateconn.DialProtocol(context.Background(), c.Protocol, c.Address) - } + c.conn, err = vtgateconn.DialProtocol(context.Background(), c.Protocol, c.Address) if err != nil { return err } diff --git a/go/vt/vitessdriver/driver_test.go b/go/vt/vitessdriver/driver_test.go index 1c0e78cd284..f1bbbfd8763 100644 --- a/go/vt/vitessdriver/driver_test.go +++ b/go/vt/vitessdriver/driver_test.go @@ -79,7 +79,8 @@ func TestOpen(t *testing.T) { connStr: fmt.Sprintf(`{"address": "%s", "target": "@replica", "timeout": %d}`, testAddress, int64(30*time.Second)), conn: &conn{ Configuration: Configuration{ - Target: "@replica", + Protocol: "grpc", + Target: "@replica", }, convert: &converter{ location: time.UTC, @@ -90,7 +91,9 @@ func TestOpen(t *testing.T) { desc: "Open() (defaults omitted)", connStr: fmt.Sprintf(`{"address": "%s", "timeout": %d}`, testAddress, int64(30*time.Second)), conn: &conn{ - Configuration: Configuration{}, + Configuration: Configuration{ + Protocol: "grpc", + }, convert: &converter{ location: time.UTC, }, @@ -116,6 +119,7 @@ func TestOpen(t *testing.T) { testAddress, int64(30*time.Second)), conn: &conn{ Configuration: Configuration{ + Protocol: "grpc", DefaultLocation: "America/Los_Angeles", }, convert: &converter{ From c040c88713f622a6636d05f4506b7a1d9f79d374 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Tue, 25 Feb 2020 11:39:07 +0530 Subject: [PATCH 174/825] updated sonar github workflow Signed-off-by: Arindam Nayak --- .github/workflows/sonar_analysis.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/sonar_analysis.yml b/.github/workflows/sonar_analysis.yml index 6c91f4b673b..f901499a853 100644 --- a/.github/workflows/sonar_analysis.yml +++ b/.github/workflows/sonar_analysis.yml @@ -37,24 +37,17 @@ jobs: run: | export SONAR_SCANNER_VERSION=4.2.0.1873 export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux - rm -rf $SONAR_SCANNER_HOME - mkdir -p $SONAR_SCANNER_HOME - curl -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip - unzip $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ - rm $HOME/.sonar/sonar-scanner.zip + curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip + unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ export PATH=$SONAR_SCANNER_HOME/bin:$PATH export SONAR_SCANNER_OPTS="-server" - curl -LsS https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip > build-wrapper-linux-x86.zip - unzip build-wrapper-linux-x86.zip - build-wrapper-linux-x86-64 --out-dir bw-output make clean all sonar-scanner \ -Dsonar.projectKey=vitessio \ -Dsonar.organization=vitess \ -Dsonar.host.url=https://sonarcloud.io \ -Dsonar.login=${SONAR_TOKEN} \ -Dsonar.go.coverage.reportPaths=report/*.out - -Dsonar.cfamily.build-wrapper-output=bw-output env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 21d4a14de0cf605e225fa8a1f8b28a8fd12308b0 Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Tue, 25 Feb 2020 03:50:05 -0800 Subject: [PATCH 175/825] Pass SQL_QUOTE_SHOW_CREATE keyword Signed-off-by: Saif Alharthi --- go/vt/vtgate/executor.go | 2 +- go/vt/vtgate/executor_test.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index e2ac0b2dca8..c4562f17ba8 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -669,7 +669,7 @@ func (e *Executor) handleSet(ctx context.Context, safeSession *SafeSession, sql if !ok { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for wait_timeout: %T", v) } - case "sql_mode", "net_write_timeout", "net_read_timeout", "lc_messages", "collation_connection", "foreign_key_checks": + case "sql_mode", "net_write_timeout", "net_read_timeout", "lc_messages", "collation_connection", "foreign_key_checks", "sql_quote_show_create": log.Warningf("Ignored inapplicable SET %v = %v", k, v) warnings.Add("IgnoredSet", 1) case "charset", "names": diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index f1709226c47..d93c428ca8a 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -458,6 +458,9 @@ func TestExecutorSet(t *testing.T) { }, { in: "set net_read_timeout = 600", out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set sql_quote_show_create = 1", + out: &vtgatepb.Session{Autocommit: true}, }, { in: "set foreign_key_checks = 0", out: &vtgatepb.Session{Autocommit: true}, From beb381ee52e39750e09abb37048ec988b76790d7 Mon Sep 17 00:00:00 2001 From: prince Date: Tue, 25 Feb 2020 21:48:08 +0530 Subject: [PATCH 176/825] migrating vttest_sample python to go (#5838) * xb-recovery: xtra backup recovery for sharded and unshared cluster. Signed-off-by: pradip parmar * vtworker fix. Signed-off-by: pradip parmar * increase num of records inserted Signed-off-by: saurabh * vttext-sample test case migrating to golang. Signed-off-by: pradip parmar * shard 25 removed from matrix. Signed-off-by: pradip parmar * timeout increased. Signed-off-by: pradip parmar * timeout set to 10 min. Signed-off-by: pradip parmar * renamed shard 25 to shard 10 and shard 26 to shard 25. Signed-off-by: pradip parmar Co-authored-by: saurabh408 --- .github/workflows/cluster_endtoend.yml | 2 +- .github/workflows/cluster_vtctl_web.yml | 2 +- .../endtoend/vtcombo/vttest_sample_test.go | 213 ++++++++++++++++++ test/config.json | 20 +- 4 files changed, 225 insertions(+), 12 deletions(-) create mode 100644 go/test/endtoend/vtcombo/vttest_sample_test.go diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 1761630018b..48d2dca64b1 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] steps: - name: Set up Go diff --git a/.github/workflows/cluster_vtctl_web.yml b/.github/workflows/cluster_vtctl_web.yml index dc6204b71c2..82597559b0e 100644 --- a/.github/workflows/cluster_vtctl_web.yml +++ b/.github/workflows/cluster_vtctl_web.yml @@ -18,5 +18,5 @@ jobs: - name: Run vtctl web run: | # Running web test inside docker - go run test.go -docker=true -print-log -shard 25 + go run test.go -docker=true -print-log -shard 10 diff --git a/go/test/endtoend/vtcombo/vttest_sample_test.go b/go/test/endtoend/vtcombo/vttest_sample_test.go new file mode 100644 index 00000000000..b6e41774cee --- /dev/null +++ b/go/test/endtoend/vtcombo/vttest_sample_test.go @@ -0,0 +1,213 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtcombo + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/exec" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + querypb "vitess.io/vitess/go/vt/proto/query" + vttestpb "vitess.io/vitess/go/vt/proto/vttest" + "vitess.io/vitess/go/vt/vtgate/vtgateconn" + "vitess.io/vitess/go/vt/vttest" +) + +var ( + localCluster *vttest.LocalCluster + grpcAddress string + vtctldAddr string + hostname = "localhost" + ks1 = "test_keyspace" + redirected = "redirected" +) + +func TestMain(m *testing.M) { + flag.Parse() + + exitcode, err := func() (int, error) { + + topology := new(vttestpb.VTTestTopology) + topology.Keyspaces = []*vttestpb.Keyspace{ + { + Name: ks1, + Shards: []*vttestpb.Shard{ + {Name: "-80"}, + {Name: "80-"}, + }, + RdonlyCount: 1, + ReplicaCount: 2, + }, + { + Name: redirected, + ServedFrom: ks1, + }, + } + + var cfg vttest.Config + cfg.Topology = topology + cfg.SchemaDir = os.Getenv("VTROOT") + "/test/vttest_schema" + cfg.DefaultSchemaDir = os.Getenv("VTROOT") + "/test/vttest_schema/default" + + localCluster = &vttest.LocalCluster{ + Config: cfg, + } + + err := localCluster.Setup() + defer localCluster.TearDown() + if err != nil { + return 1, err + } + + grpcAddress = fmt.Sprintf("localhost:%d", localCluster.Env.PortForProtocol("vtcombo", "grpc")) + vtctldAddr = fmt.Sprintf("localhost:%d", localCluster.Env.PortForProtocol("vtcombo", "port")) + + return m.Run(), nil + }() + if err != nil { + fmt.Printf("%v\n", err) + os.Exit(1) + } else { + os.Exit(exitcode) + } +} + +func TestStandalone(t *testing.T) { + // validate debug vars + resp, err := http.Get(fmt.Sprintf("http://%s/debug/vars", vtctldAddr)) + require.Nil(t, err) + require.Equal(t, 200, resp.StatusCode) + resultMap := make(map[string]interface{}) + respByte, _ := ioutil.ReadAll(resp.Body) + err = json.Unmarshal(respByte, &resultMap) + require.Nil(t, err) + cmd, _ := resultMap["cmdline"] + require.NotNil(t, cmd, "cmdline is not available in debug vars") + tmp, _ := cmd.([]interface{}) + require.Contains(t, tmp[0], "vtcombo") + + ctx := context.Background() + conn, err := vtgateconn.Dial(ctx, grpcAddress) + require.Nil(t, err) + defer conn.Close() + cur := conn.Session(ks1+":-80@master", nil) + + idStart, rowCount := 1000, 500 + query := "insert into test_table (id, msg, keyspace_id) values (:id, :msg, :keyspace_id)" + _, err = cur.Execute(ctx, "begin", nil) + require.Nil(t, err) + + for i := idStart; i < idStart+rowCount; i++ { + bindVariables := map[string]*querypb.BindVariable{ + "id": {Type: querypb.Type_UINT64, Value: []byte(fmt.Sprint(i))}, + "msg": {Type: querypb.Type_VARCHAR, Value: []byte(fmt.Sprint("test", i))}, + "keyspace_id": {Type: querypb.Type_UINT64, Value: []byte(fmt.Sprint(i))}, + } + _, err = cur.Execute(ctx, query, bindVariables) + require.Nil(t, err) + } + + _, err = cur.Execute(ctx, "commit", nil) + require.Nil(t, err) + + cur = conn.Session(ks1+":-80@rdonly", nil) + bindVariables := map[string]*querypb.BindVariable{ + "id_start": {Type: querypb.Type_UINT64, Value: []byte(fmt.Sprint(idStart))}, + } + res, err := cur.Execute(ctx, "select * from test_table where id >= :id_start", bindVariables) + require.Nil(t, err) + + assert.Equal(t, rowCount, len(res.Rows)) + + cur = conn.Session(redirected+":-80@replica", nil) + bindVariables = map[string]*querypb.BindVariable{ + "id_start": {Type: querypb.Type_UINT64, Value: []byte(fmt.Sprint(idStart))}, + } + res, err = cur.Execute(ctx, "select * from test_table where id = :id_start", bindVariables) + require.Nil(t, err) + require.Equal(t, 1, len(res.Rows)) + assert.Equal(t, "VARCHAR(\"test1000\")", res.Rows[0][1].String()) + + cur = conn.Session(ks1+":80-@master", nil) + _, err = cur.Execute(ctx, "begin", nil) + require.Nil(t, err) + + i := 0x810000000000000 + bindVariables = map[string]*querypb.BindVariable{ + "id": {Type: querypb.Type_UINT64, Value: []byte(fmt.Sprint(i))}, + "msg": {Type: querypb.Type_VARCHAR, Value: []byte(fmt.Sprint("test", i))}, + "keyspace_id": {Type: querypb.Type_UINT64, Value: []byte(fmt.Sprint(i))}, + } + _, err = cur.Execute(ctx, query, bindVariables) + require.Nil(t, err) + + _, err = cur.Execute(ctx, "commit", nil) + require.Nil(t, err) + + tmpCmd := exec.Command("vtctlclient", "-vtctl_client_protocol", "grpc", "-server", grpcAddress, "-stderrthreshold", "0", "ListAllTablets", "test") + + fmt.Println(tmpCmd.Args) + + output, err := tmpCmd.CombinedOutput() + require.Nil(t, err) + + numMaster, numReplica, numRdonly, numDash80, num80Dash := 0, 0, 0, 0, 0 + lines := strings.Split(string(output), "\n") + for _, line := range lines { + if !strings.HasPrefix(line, "test-") { + continue + } + parts := strings.Split(line, " ") + assert.Equal(t, "test_keyspace", parts[1]) + + switch parts[3] { + case "master": + numMaster++ + case "replica": + numReplica++ + case "rdonly": + numRdonly++ + default: + t.Logf("invalid tablet type %s", parts[3]) + } + + switch parts[2] { + case "-80": + numDash80++ + case "80-": + num80Dash++ + default: + t.Logf("invalid shard %s", parts[2]) + } + + } + + assert.Equal(t, 2, numMaster) + assert.Equal(t, 2, numReplica) + assert.Equal(t, 2, numRdonly) + assert.Equal(t, 3, numDash80) + assert.Equal(t, 3, num80Dash) +} diff --git a/test/config.json b/test/config.json index 3010f212998..aa5db0dc9b0 100644 --- a/test/config.json +++ b/test/config.json @@ -94,15 +94,6 @@ "RetryMax": 0, "Tags": [] }, - "vttest_sample": { - "File": "vttest_sample_test.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 0, - "RetryMax": 0, - "Tags": [] - }, "backup": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtctlbackup"], @@ -112,7 +103,7 @@ "RetryMax": 0, "Tags": [] }, - "backup_mysqlctld": { + "backup_mysqlctld": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/mysqlctld"], "Command": [], @@ -485,6 +476,15 @@ "Args": ["vitess.io/vitess/go/test/endtoend/vtctldweb"], "Command": [], "Manual": false, + "Shard": 10, + "RetryMax": 0, + "Tags": [] + }, + "vttest_sample": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vtcombo"], + "Command": [], + "Manual": false, "Shard": 25, "RetryMax": 0, "Tags": [] From 4e30a42ff50df0fa610c539059f46c44c1ff859f Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Wed, 26 Feb 2020 00:49:23 +0530 Subject: [PATCH 177/825] Added ksidVindex in the update and delete plan for owned lookup table Signed-off-by: Harshit Gangal --- go/vt/vtgate/autocommit_test.go | 8 +- go/vt/vtgate/engine/delete.go | 12 +- go/vt/vtgate/engine/delete_test.go | 22 +- go/vt/vtgate/engine/dml.go | 3 + go/vt/vtgate/engine/route.go | 15 + go/vt/vtgate/engine/update.go | 12 +- go/vt/vtgate/engine/update_test.go | 22 +- go/vt/vtgate/executor_dml_test.go | 20 +- go/vt/vtgate/planbuilder/delete.go | 3 +- go/vt/vtgate/planbuilder/dml.go | 52 +- .../vtgate/planbuilder/testdata/dml_cases.txt | 458 +++++++++++++++--- go/vt/vtgate/planbuilder/update.go | 5 +- 12 files changed, 484 insertions(+), 148 deletions(-) diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index f908ad57b6e..ab066c432bb 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -81,8 +81,8 @@ func TestAutocommitUpdateLookup(t *testing.T) { func TestAutocommitUpdateVindexChange(t *testing.T) { executor, sbc, _, sbclookup := createExecutorEnv() sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "\026k@\264J\272K\326|1|foo", + sqltypes.MakeTestFields("id|name|lastname", "int64|int32|varchar"), + "1|1|foo", ), }) @@ -141,8 +141,8 @@ func TestAutocommitDeleteSharded(t *testing.T) { func TestAutocommitDeleteLookup(t *testing.T) { executor, sbc1, _, sbclookup := createExecutorEnv() sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "\026k@\264J\272K\326|1|foo", + sqltypes.MakeTestFields("id|name|lastname", "int64|int32|varchar"), + "1|1|foo", ), }) diff --git a/go/vt/vtgate/engine/delete.go b/go/vt/vtgate/engine/delete.go index 399ab9f6f2c..663e0a9c362 100644 --- a/go/vt/vtgate/engine/delete.go +++ b/go/vt/vtgate/engine/delete.go @@ -45,13 +45,16 @@ type Delete struct { // MarshalJSON serializes the Delete into a JSON representation. // It's used for testing and diagnostics. func (del *Delete) MarshalJSON() ([]byte, error) { - var tname, vindexName string + var tname, vindexName, ksidVindexName string if del.Table != nil { tname = del.Table.Name.String() } if del.Vindex != nil { vindexName = del.Vindex.String() } + if del.KsidVindex != nil { + ksidVindexName = del.KsidVindex.String() + } marshalDelete := struct { Opcode string Keyspace *vindexes.Keyspace `json:",omitempty"` @@ -60,6 +63,7 @@ func (del *Delete) MarshalJSON() ([]byte, error) { Values []sqltypes.PlanValue `json:",omitempty"` Table string `json:",omitempty"` OwnedVindexQuery string `json:",omitempty"` + KsidVindex string `json:",omitempty"` MultiShardAutocommit bool `json:",omitempty"` QueryTimeout int `json:",omitempty"` }{ @@ -70,6 +74,7 @@ func (del *Delete) MarshalJSON() ([]byte, error) { Values: del.Values, Table: tname, OwnedVindexQuery: del.OwnedVindexQuery, + KsidVindex: ksidVindexName, MultiShardAutocommit: del.MultiShardAutocommit, QueryTimeout: del.QueryTimeout, } @@ -186,8 +191,11 @@ func (del *Delete) deleteVindexEntries(vcursor VCursor, bindVars map[string]*que } for _, row := range subQueryResults.Rows { - ksid := row[0].ToBytes() colnum := 1 + ksid, err := resolveKeyspaceID(vcursor, del.KsidVindex, row[0]) + if err != nil { + return err + } for _, colVindex := range del.Table.Owned { // Fetch the column values. colnum must keep incrementing. fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) diff --git a/go/vt/vtgate/engine/delete_test.go b/go/vt/vtgate/engine/delete_test.go index f199352f436..8daa679da03 100644 --- a/go/vt/vtgate/engine/delete_test.go +++ b/go/vt/vtgate/engine/delete_test.go @@ -155,15 +155,16 @@ func TestDeleteOwnedVindex(t *testing.T) { Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, Table: ks.Tables["t1"], OwnedVindexQuery: "dummy_subquery", + KsidVindex: ks.Vindexes["hash"].(vindexes.SingleColumn), }, } results := []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", + "1|4|5|6", )} vc := &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -207,10 +208,10 @@ func TestDeleteOwnedVindex(t *testing.T) { results = []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", - "\026k@\264J\272K\326|7|8|9", + "1|4|5|6", + "1|7|8|9", )} vc = &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -278,15 +279,16 @@ func TestDeleteScatterOwnedVindex(t *testing.T) { Query: "dummy_delete", Table: ks.Tables["t1"], OwnedVindexQuery: "dummy_subquery", + KsidVindex: ks.Vindexes["hash"].(vindexes.SingleColumn), }, } results := []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", + "1|4|5|6", )} vc := &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -330,10 +332,10 @@ func TestDeleteScatterOwnedVindex(t *testing.T) { results = []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", - "\026k@\264J\272K\326|7|8|9", + "1|4|5|6", + "1|7|8|9", )} vc = &loggingVCursor{ shards: []string{"-20", "20-"}, diff --git a/go/vt/vtgate/engine/dml.go b/go/vt/vtgate/engine/dml.go index bc30dc8eb3b..a82e4290773 100644 --- a/go/vt/vtgate/engine/dml.go +++ b/go/vt/vtgate/engine/dml.go @@ -43,6 +43,9 @@ type DML struct { // For now, only one value is specified. Values []sqltypes.PlanValue + // Keyspace Id Vindex + KsidVindex vindexes.SingleColumn + // Table specifies the table for the update. Table *vindexes.Table diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go index f9d2ce5ebd0..f39a82c86bd 100644 --- a/go/vt/vtgate/engine/route.go +++ b/go/vt/vtgate/engine/route.go @@ -490,6 +490,21 @@ func resolveSingleShard(vcursor VCursor, vindex vindexes.SingleColumn, keyspace return rss[0], ksid, nil } +func resolveKeyspaceID(vcursor VCursor, vindex vindexes.SingleColumn, vindexKey sqltypes.Value) ([]byte, error) { + destinations, err := vindex.Map(vcursor, []sqltypes.Value{vindexKey}) + if err != nil { + return nil, err + } + switch ksid := destinations[0].(type) { + case key.DestinationKeyspaceID: + return ksid, nil + case key.DestinationNone: + return nil, nil + default: + return nil, fmt.Errorf("cannot map vindex to unique keyspace id: %v", destinations[0]) + } +} + func execShard(vcursor VCursor, query string, bindVars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard, isDML, canAutocommit bool) (*sqltypes.Result, error) { autocommit := canAutocommit && vcursor.AutocommitApproval() result, errs := vcursor.ExecuteMultiShard([]*srvtopo.ResolvedShard{rs}, []*querypb.BoundQuery{ diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index 966626c5165..4fb54b0d841 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -48,13 +48,16 @@ type Update struct { // MarshalJSON serializes the Update into a JSON representation. // It's used for testing and diagnostics. func (upd *Update) MarshalJSON() ([]byte, error) { - var tname, vindexName string + var tname, vindexName, ksidVindexName string if upd.Table != nil { tname = upd.Table.Name.String() } if upd.Vindex != nil { vindexName = upd.Vindex.String() } + if upd.KsidVindex != nil { + ksidVindexName = upd.KsidVindex.String() + } marshalUpdate := struct { Opcode string Keyspace *vindexes.Keyspace `json:",omitempty"` @@ -64,6 +67,7 @@ func (upd *Update) MarshalJSON() ([]byte, error) { ChangedVindexValues map[string][]sqltypes.PlanValue `json:",omitempty"` Table string `json:",omitempty"` OwnedVindexQuery string `json:",omitempty"` + KsidVindex string `json:",omitempty"` MultiShardAutocommit bool `json:",omitempty"` QueryTimeout int `json:",omitempty"` }{ @@ -75,6 +79,7 @@ func (upd *Update) MarshalJSON() ([]byte, error) { ChangedVindexValues: upd.ChangedVindexValues, Table: tname, OwnedVindexQuery: upd.OwnedVindexQuery, + KsidVindex: ksidVindexName, MultiShardAutocommit: upd.MultiShardAutocommit, QueryTimeout: upd.QueryTimeout, } @@ -194,7 +199,10 @@ func (upd *Update) updateVindexEntries(vcursor VCursor, bindVars map[string]*que for _, row := range subQueryResult.Rows { colnum := 1 // we start from the first non-vindex col - ksid := row[0].ToBytes() + ksid, err := resolveKeyspaceID(vcursor, upd.KsidVindex, row[0]) + if err != nil { + return err + } for _, colVindex := range upd.Table.Owned { // Fetch the column values. colnum must keep incrementing. fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index 2e2437b0970..b73590a7ca3 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -201,6 +201,7 @@ func TestUpdateEqualChangedVindex(t *testing.T) { Values: []sqltypes.PlanValue{{Value: sqltypes.NewInt64(1)}}, Table: ks.Tables["t1"], OwnedVindexQuery: "dummy_subquery", + KsidVindex: ks.Vindexes["hash"].(vindexes.SingleColumn), }, ChangedVindexValues: map[string][]sqltypes.PlanValue{ "twocol": {{ @@ -217,9 +218,9 @@ func TestUpdateEqualChangedVindex(t *testing.T) { results := []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", + "1|4|5|6", )} vc := &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -263,10 +264,10 @@ func TestUpdateEqualChangedVindex(t *testing.T) { results = []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", - "\026k@\264J\272K\326|7|8|9", + "1|4|5|6", + "1|7|8|9", )} vc = &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -306,6 +307,7 @@ func TestUpdateScatterChangedVindex(t *testing.T) { Query: "dummy_update", Table: ks.Tables["t1"], OwnedVindexQuery: "dummy_subquery", + KsidVindex: ks.Vindexes["hash"].(vindexes.SingleColumn), }, ChangedVindexValues: map[string][]sqltypes.PlanValue{ "twocol": {{ @@ -322,9 +324,9 @@ func TestUpdateScatterChangedVindex(t *testing.T) { results := []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", + "1|4|5|6", )} vc := &loggingVCursor{ shards: []string{"-20", "20-"}, @@ -368,10 +370,10 @@ func TestUpdateScatterChangedVindex(t *testing.T) { results = []*sqltypes.Result{sqltypes.MakeTestResult( sqltypes.MakeTestFields( "id|c1|c2|c3", - "varbinary|int64|int64|int64", + "int64|int64|int64|int64", ), - "\026k@\264J\272K\326|4|5|6", - "\026k@\264J\272K\326|7|8|9", + "1|4|5|6", + "1|7|8|9", )} vc = &loggingVCursor{ shards: []string{"-20", "20-"}, diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 50c6885efa8..80ead36aac8 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -96,8 +96,8 @@ func TestUpdateEqual(t *testing.T) { sbc2.Queries = nil sbclookup.Queries = nil sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "\026k@\264J\272K\326|1|foo", + sqltypes.MakeTestFields("id|name|lastname", "int64|int32|varchar"), + "1|1|foo", ), }) @@ -208,8 +208,8 @@ func TestUpdateMultiOwned(t *testing.T) { sbc1.SetResults([]*sqltypes.Result{ sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|a|b|c|d|e|f", "varbinary|int64|int64|int64|int64|int64|int64"), - "\026k@\264J\272K\326|10|20|30|40|50|60", + sqltypes.MakeTestFields("id|a|b|c|d|e|f", "int64|int64|int64|int64|int64|int64|int64"), + "1|10|20|30|40|50|60", ), }) _, err := executorExec(executor, "update user set a=1, b=2, f=4, e=3 where id=1", nil) @@ -329,13 +329,13 @@ func TestDeleteEqual(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "Id", Type: sqltypes.VarBinary}, + {Name: "Id", Type: sqltypes.Int64}, {Name: "name", Type: sqltypes.VarChar}, }, RowsAffected: 1, InsertID: 0, Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("\026k@\264J\272K\326"), + sqltypes.NewInt64(1), sqltypes.NewVarChar("myname"), }}, }}) @@ -419,8 +419,8 @@ func TestDeleteEqual(t *testing.T) { sbc.Queries = nil sbclookup.Queries = nil sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult( - sqltypes.MakeTestFields("id|name|lastname", "varbinary|int32|varchar"), - "\026k@\264J\272K\326|1|foo", + sqltypes.MakeTestFields("id|name|lastname", "int64|int32|varchar"), + "1|1|foo", ), }) _, err = executorExec(executor, "delete from user2 where id = 1", nil) @@ -512,13 +512,13 @@ func TestDeleteComments(t *testing.T) { sbc.SetResults([]*sqltypes.Result{{ Fields: []*querypb.Field{ - {Name: "Id", Type: sqltypes.VarBinary}, + {Name: "Id", Type: sqltypes.Int64}, {Name: "name", Type: sqltypes.VarChar}, }, RowsAffected: 1, InsertID: 0, Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("\026k@\264J\272K\326"), + sqltypes.NewInt64(1), sqltypes.NewVarChar("myname"), }}, }}) diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index 2144e0e3763..5524b30d1a7 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -25,7 +25,7 @@ import ( // buildDeletePlan builds the instructions for a DELETE statement. func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Delete, error) { - dml, ksidCol, err := buildDMLPlan(vschema, "delete", del, del.TableExprs, del.Where, del.OrderBy, del.Limit, del.Comments, del.Targets) + dml, ksidVindex, ksidCol, err := buildDMLPlan(vschema, "delete", del, del.TableExprs, del.Where, del.OrderBy, del.Limit, del.Comments, del.Targets) if err != nil { return nil, err } @@ -47,6 +47,7 @@ func buildDeletePlan(del *sqlparser.Delete, vschema ContextVSchema) (*engine.Del if len(edel.Table.Owned) > 0 { edel.OwnedVindexQuery = generateDMLSubquery(del.Where, del.OrderBy, del.Limit, edel.Table, ksidCol) + edel.KsidVindex = ksidVindex } return edel, nil diff --git a/go/vt/vtgate/planbuilder/dml.go b/go/vt/vtgate/planbuilder/dml.go index 107ebd5b541..1d3f72dfb04 100644 --- a/go/vt/vtgate/planbuilder/dml.go +++ b/go/vt/vtgate/planbuilder/dml.go @@ -28,7 +28,8 @@ import ( // getDMLRouting returns the vindex and values for the DML, // If it cannot find a unique vindex match, it returns an error. -func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (engine.DMLOpcode, vindexes.SingleColumn, string, []sqltypes.PlanValue) { +func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (engine.DMLOpcode, vindexes.SingleColumn, string, vindexes.SingleColumn, []sqltypes.PlanValue, error) { + var ksidVindex vindexes.SingleColumn var ksidCol string for _, index := range table.Ordered { if !index.Vindex.IsUnique() { @@ -38,24 +39,22 @@ func getDMLRouting(where *sqlparser.Where, table *vindexes.Table) (engine.DMLOpc if !ok { continue } - if ksidCol == "" { ksidCol = sqlparser.String(index.Columns[0]) + ksidVindex = single } - if where == nil { - return engine.Scatter, nil, ksidCol, nil + return engine.Scatter, ksidVindex, ksidCol, nil, nil, nil } if pv, ok := getMatch(where.Expr, index.Columns[0]); ok { - if pv.IsList() { - return engine.Scatter, nil, ksidCol, nil - } - - return engine.Equal, single, ksidCol, []sqltypes.PlanValue{pv} + return engine.Equal, ksidVindex, ksidCol, single, []sqltypes.PlanValue{pv}, nil } } - return engine.Scatter, nil, ksidCol, nil + if ksidVindex == nil { + return engine.Scatter, nil, "", nil, nil, vterrors.New(vtrpcpb.Code_INTERNAL, "table without a primary vindex is not expected") + } + return engine.Scatter, ksidVindex, ksidCol, nil, nil, nil } // getMatch returns the matched value if there is an equality @@ -83,10 +82,6 @@ func getMatch(node sqlparser.Expr, col sqlparser.ColIdent) (pv sqltypes.PlanValu if !sqlparser.IsValue(comparison.Right) { continue } - case sqlparser.InStr: - if !sqlparser.IsSimpleTuple(comparison.Right) { - continue - } default: continue } @@ -104,12 +99,12 @@ func nameMatch(node sqlparser.Expr, col sqlparser.ColIdent) bool { return ok && colname.Name.Equal(col) } -func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Statement, tableExprs sqlparser.TableExprs, where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, comments sqlparser.Comments, nodes ...sqlparser.SQLNode) (*engine.DML, string, error) { +func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Statement, tableExprs sqlparser.TableExprs, where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, comments sqlparser.Comments, nodes ...sqlparser.SQLNode) (*engine.DML, vindexes.SingleColumn, string, error) { eupd := &engine.DML{} pb := newPrimitiveBuilder(vschema, newJointab(sqlparser.GetBindvars(stmt))) ro, err := pb.processDMLTable(tableExprs) if err != nil { - return nil, "", err + return nil, nil, "", err } eupd.Keyspace = ro.eroute.Keyspace if !eupd.Keyspace.Sharded { @@ -118,21 +113,21 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme subqueryArgs = append(subqueryArgs, nodes...) subqueryArgs = append(subqueryArgs, where, orderBy, limit) if !pb.finalizeUnshardedDMLSubqueries(subqueryArgs...) { - return nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: sharded subqueries in DML") + return nil, nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: sharded subqueries in DML") } eupd.Opcode = engine.Unsharded // Generate query after all the analysis. Otherwise table name substitutions for // routed tables won't happen. eupd.Query = generateQuery(stmt) - return eupd, "", nil + return eupd, nil, "", nil } if hasSubquery(stmt) { - return nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: subqueries in sharded DML") + return nil, nil, "", vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: subqueries in sharded DML") } if len(pb.st.tables) != 1 { - return nil, "", vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi-table %s statement in sharded keyspace", dmlType) + return nil, nil, "", vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi-table %s statement in sharded keyspace", dmlType) } // Generate query after all the analysis. Otherwise table name substitutions for @@ -147,30 +142,33 @@ func buildDMLPlan(vschema ContextVSchema, dmlType string, stmt sqlparser.Stateme eupd.QueryTimeout = queryTimeout(directives) eupd.Table = ro.vschemaTable if eupd.Table == nil { - return nil, "", vterrors.New(vtrpcpb.Code_INTERNAL, "internal error: table.vindexTable is mysteriously nil") + return nil, nil, "", vterrors.New(vtrpcpb.Code_INTERNAL, "internal error: table.vindexTable is mysteriously nil") } if ro.eroute.TargetDestination != nil { if ro.eroute.TargetTabletType != topodatapb.TabletType_MASTER { - return nil, "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported: %s statement with a replica target", dmlType) + return nil, nil, "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported: %s statement with a replica target", dmlType) } eupd.Opcode = engine.ByDestination eupd.TargetDestination = ro.eroute.TargetDestination - return eupd, "", nil + return eupd, nil, "", nil } - routingType, vindex, ksidCol, values := getDMLRouting(where, eupd.Table) + routingType, ksidVindex, ksidCol, vindex, values, err := getDMLRouting(where, eupd.Table) + if err != nil { + return nil, nil, "", err + } eupd.Opcode = routingType if routingType == engine.Scatter { if limit != nil { - return nil, "", vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi shard %s with limit", dmlType) + return nil, nil, "", vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi shard %s with limit", dmlType) } } else { - eupd.Values = values eupd.Vindex = vindex + eupd.Values = values } - return eupd, ksidCol, nil + return eupd, ksidVindex, ksidCol, nil } func generateDMLSubquery(where *sqlparser.Where, orderBy sqlparser.OrderBy, limit *sqlparser.Limit, table *vindexes.Table, ksidCol string) string { diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt index 05654261041..1de0c561b2d 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt @@ -149,12 +149,13 @@ }, "Query": "update user set val = 1 where id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } - # update by primary keyspace id with alias "update user as user_alias set val = 1 where user_alias.id = 1" { @@ -167,7 +168,9 @@ }, "Query": "update user as user_alias set val = 1 where user_alias.id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -203,7 +206,9 @@ }, "Query": "update user set val = 1 where (name = 'foo' and id = 1)", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -220,12 +225,17 @@ }, "Query": "update user_metadata set email = 'juan@vitess.io' where user_id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "ChangedVindexValues": { - "email_user_map": ["juan@vitess.io"] + "email_user_map": [ + "juan@vitess.io" + ] }, "Table": "user_metadata", - "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update" + "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update", + "KsidVindex": "user_index" } } @@ -245,13 +255,20 @@ }, "Query": "update user_metadata set email = 'juan@vitess.io', address = '155 5th street' where user_id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "ChangedVindexValues": { - "address_user_map": ["155 5th street"], - "email_user_map": ["juan@vitess.io"] + "address_user_map": [ + "155 5th street" + ], + "email_user_map": [ + "juan@vitess.io" + ] }, "Table": "user_metadata", - "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update" + "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update", + "KsidVindex": "user_index" } } @@ -271,10 +288,13 @@ 1 ], "ChangedVindexValues": { - "email_user_map": ["juan@vitess.io"] + "email_user_map": [ + "juan@vitess.io" + ] }, "Table": "user_metadata", - "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 order by user_id asc limit 10 for update" + "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 order by user_id asc limit 10 for update", + "KsidVindex": "user_index" } } @@ -290,7 +310,9 @@ }, "Query": "update user set val = 1 where id = id2 and id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -307,7 +329,9 @@ }, "Query": "update user set val = 1 where id = 18446744073709551616 and id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -324,9 +348,12 @@ }, "Query": "delete from user where id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update", + "KsidVindex": "user_index" } } @@ -388,7 +415,8 @@ 1 ], "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update", + "KsidVindex": "user_index" } } @@ -418,7 +446,9 @@ }, "Query": "update music set val = 1 where id = 1", "Vindex": "music_user_map", - "Values": [1], + "Values": [ + 1 + ], "Table": "music" } } @@ -430,9 +460,9 @@ "Instructions": { "Opcode": "UpdateUnsharded", "Keyspace": { - "Name": "main", - "Sharded": false - }, + "Name": "main", + "Sharded": false + }, "Query": "update unsharded_a as a join unsharded_b as b on a.id = b.id set a.val = 'foo' where b.val = 1" } } @@ -463,9 +493,12 @@ }, "Query": "delete from music where id = 1", "Vindex": "music_user_map", - "Values": [1], + "Values": [ + 1 + ], "Table": "music", - "OwnedVindexQuery": "select user_id, id from music where id = 1 for update" + "OwnedVindexQuery": "select user_id, id from music where id = 1 for update", + "KsidVindex": "user_index" } } @@ -481,7 +514,9 @@ }, "Query": "delete from music_extra where user_id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "music_extra" } } @@ -758,7 +793,6 @@ } } - # unsharded insert subquery in insert value "insert into unsharded values((select 1 from dual), 1)" { @@ -865,7 +899,23 @@ "Sharded": true }, "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -873,10 +923,14 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [1] + "Values": [ + 1 + ] }, "Prefix": "insert into user(id, Name, Costly) values ", - "Mid":["(:_Id0, :_Name0, :_Costly0)"] + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)" + ] } } @@ -891,7 +945,23 @@ "Sharded": true }, "Query": "insert ignore into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -899,10 +969,14 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [1] + "Values": [ + 1 + ] }, "Prefix": "insert ignore into user(id, Name, Costly) values ", - "Mid":["(:_Id0, :_Name0, :_Costly0)"] + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)" + ] } } @@ -917,7 +991,23 @@ "Sharded": true }, "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0) on duplicate key update col = 2", - "Values": [[[":__seq0"]],[[null]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -948,7 +1038,23 @@ "Sharded": true }, "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -978,7 +1084,23 @@ "Sharded": true }, "Query": "insert into user(nonid, id, Name, Costly) values (2, :_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -986,10 +1108,14 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [null] + "Values": [ + null + ] }, "Prefix": "insert into user(nonid, id, Name, Costly) values ", - "Mid": ["(2, :_Id0, :_Name0, :_Costly0)"] + "Mid": [ + "(2, :_Id0, :_Name0, :_Costly0)" + ] } } @@ -1004,7 +1130,23 @@ "Sharded": true }, "Query": "insert into user(id, nonid, Name, Costly) values (:_Id0, 2, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -1012,10 +1154,14 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [null] + "Values": [ + null + ] }, "Prefix": "insert into user(id, nonid, Name, Costly) values ", - "Mid": ["(:_Id0, 2, :_Name0, :_Costly0)"] + "Mid": [ + "(:_Id0, 2, :_Name0, :_Costly0)" + ] } } @@ -1030,7 +1176,23 @@ "Sharded": true }, "Query": "insert into user(nonid, id, Name, Costly) values (true, :_Id0, :_Name0, :_Costly0)", - "Values": [[[":__seq0"]],[[null]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + null + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -1038,10 +1200,14 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [null] + "Values": [ + null + ] }, "Prefix": "insert into user(nonid, id, Name, Costly) values ", - "Mid": ["(true, :_Id0, :_Name0, :_Costly0)"] + "Mid": [ + "(true, :_Id0, :_Name0, :_Costly0)" + ] } } @@ -1056,7 +1222,23 @@ "Sharded": true }, "Query": "insert into user(nonid, name, id, Costly) values (2, :_Name0, :_Id0, :_Costly0)", - "Values": [[[":__seq0"]],[["foo"]],[[null]]], + "Values": [ + [ + [ + ":__seq0" + ] + ], + [ + [ + "foo" + ] + ], + [ + [ + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -1064,10 +1246,14 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [1] + "Values": [ + 1 + ] }, "Prefix": "insert into user(nonid, name, id, Costly) values ", - "Mid": ["(2, :_Name0, :_Id0, :_Costly0)"] + "Mid": [ + "(2, :_Name0, :_Id0, :_Costly0)" + ] } } @@ -1082,7 +1268,13 @@ "Sharded": true }, "Query": "insert into user_extra(nonid, extra_id, user_id) values (2, :__seq0, :_user_id0)", - "Values": [[[null]]], + "Values": [ + [ + [ + null + ] + ] + ], "Table": "user_extra", "Generate": { "Keyspace": { @@ -1090,10 +1282,14 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [null] + "Values": [ + null + ] }, "Prefix": "insert into user_extra(nonid, extra_id, user_id) values ", - "Mid": ["(2, :__seq0, :_user_id0)"] + "Mid": [ + "(2, :__seq0, :_user_id0)" + ] } } @@ -1108,7 +1304,13 @@ "Sharded": true }, "Query": "insert into `weird``name`(`a``b*c`, `b*c`) values (:_a_b_c0, 2)", - "Values": [[[1]]], + "Values": [ + [ + [ + 1 + ] + ] + ], "Table": "weird`name", "Prefix": "insert into `weird``name`(`a``b*c`, `b*c`) values ", "Mid": [ @@ -1159,7 +1361,26 @@ "Sharded": true }, "Query": "insert into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", - "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], + "Values": [ + [ + [ + ":__seq0", + ":__seq1" + ] + ], + [ + [ + null, + null + ] + ], + [ + [ + null, + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -1167,10 +1388,16 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [1,2] + "Values": [ + 1, + 2 + ] }, "Prefix": "insert into user(id, Name, Costly) values ", - "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"] + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)", + "(:_Id1, :_Name1, :_Costly1)" + ] } } @@ -1185,7 +1412,26 @@ "Sharded": true }, "Query": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", - "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], + "Values": [ + [ + [ + ":__seq0", + ":__seq1" + ] + ], + [ + [ + null, + null + ] + ], + [ + [ + null, + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -1193,10 +1439,16 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [1,2] + "Values": [ + 1, + 2 + ] }, "Prefix": "insert /*vt+ QUERY_TIMEOUT_MS=1 */ into user(id, Name, Costly) values ", - "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"], + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)", + "(:_Id1, :_Name1, :_Costly1)" + ], "QueryTimeout": 1 } } @@ -1212,7 +1464,26 @@ "Sharded": true }, "Query": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id, Name, Costly) values (:_Id0, :_Name0, :_Costly0), (:_Id1, :_Name1, :_Costly1)", - "Values": [[[":__seq0",":__seq1"]],[[null,null]],[[null,null]]], + "Values": [ + [ + [ + ":__seq0", + ":__seq1" + ] + ], + [ + [ + null, + null + ] + ], + [ + [ + null, + null + ] + ] + ], "Table": "user", "Generate": { "Keyspace": { @@ -1220,10 +1491,16 @@ "Sharded": false }, "Query": "select next :n values from seq", - "Values": [1,2] + "Values": [ + 1, + 2 + ] }, "Prefix": "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user(id, Name, Costly) values ", - "Mid": ["(:_Id0, :_Name0, :_Costly0)","(:_Id1, :_Name1, :_Costly1)"], + "Mid": [ + "(:_Id0, :_Name0, :_Costly0)", + "(:_Id1, :_Name1, :_Costly1)" + ], "MultiShardAutocommit": true } } @@ -1419,7 +1696,6 @@ } } - # insert multiple rows in a multi column vindex table "insert multicolvin (column_a, column_b, column_c, kid) VALUES (1,2,3,4), (5,6,7,8)" { @@ -1480,7 +1756,8 @@ 1 ], "Table": "multicolvin", - "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update" + "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update", + "KsidVindex": "kid_index" } } @@ -1506,7 +1783,8 @@ ] }, "Table": "multicolvin", - "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update" + "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update", + "KsidVindex": "kid_index" } } @@ -1535,7 +1813,8 @@ ] }, "Table": "multicolvin", - "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update" + "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update", + "KsidVindex": "kid_index" } } @@ -1581,7 +1860,7 @@ }, "Query": "update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ user_extra set val = 1", "Table": "user_extra", - "MultiShardAutocommit": true + "MultiShardAutocommit": true } } @@ -1790,8 +2069,8 @@ "Instructions": { "Opcode": "UpdateUnsharded", "Keyspace": { - "Name":"main", - "Sharded":false + "Name": "main", + "Sharded": false }, "Query": "update unsharded set col = (select id from unsharded_a where id = unsharded.col) where col = (select id from unsharded_b)" } @@ -1823,12 +2102,17 @@ }, "Query": "update user set name = null where id = 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "ChangedVindexValues": { - "name_user_map": [null] + "name_user_map": [ + null + ] }, "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update", + "KsidVindex": "user_index" } } @@ -1859,10 +2143,13 @@ }, "Query": "update user set name = null where id in (1, 2, 3)", "ChangedVindexValues": { - "name_user_map": [null] + "name_user_map": [ + null + ] }, "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update", + "KsidVindex": "user_index" } } @@ -1878,10 +2165,13 @@ }, "Query": "update user set name = null", "ChangedVindexValues": { - "name_user_map": [null] + "name_user_map": [ + null + ] }, "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user for update" + "OwnedVindexQuery": "select Id, Name, Costly from user for update", + "KsidVindex": "user_index" } } @@ -1897,10 +2187,13 @@ }, "Query": "update user set name = null where id + 1 = 2", "ChangedVindexValues": { - "name_user_map": [null] + "name_user_map": [ + null + ] }, "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user where id + 1 = 2 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id + 1 = 2 for update", + "KsidVindex": "user_index" } } @@ -1916,7 +2209,8 @@ }, "Query": "delete from user where id in (1, 2, 3)", "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update", + "KsidVindex": "user_index" } } @@ -1932,7 +2226,8 @@ }, "Query": "delete from user where id + 1 = 2", "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user where id + 1 = 2 for update" + "OwnedVindexQuery": "select Id, Name, Costly from user where id + 1 = 2 for update", + "KsidVindex": "user_index" } } @@ -1948,7 +2243,8 @@ }, "Query": "delete from user", "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user for update" + "OwnedVindexQuery": "select Id, Name, Costly from user for update", + "KsidVindex": "user_index" } } @@ -1968,7 +2264,8 @@ 1 ], "Table": "music", - "OwnedVindexQuery": "select user_id, id from music where id = 1 for update" + "OwnedVindexQuery": "select user_id, id from music where id = 1 for update", + "KsidVindex": "user_index" } } @@ -1999,6 +2296,7 @@ }, "Query": "delete from user", "Table": "user", - "OwnedVindexQuery": "select Id, Name, Costly from user for update" + "OwnedVindexQuery": "select Id, Name, Costly from user for update", + "KsidVindex": "user_index" } } diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index 1bb3b9c9993..fc343014d71 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -28,7 +28,7 @@ import ( // buildUpdatePlan builds the instructions for an UPDATE statement. func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Update, error) { - dml, vindexCol, err := buildDMLPlan(vschema, "update", upd, upd.TableExprs, upd.Where, upd.OrderBy, upd.Limit, upd.Comments, upd.Exprs) + dml, ksidVindex, ksidCol, err := buildDMLPlan(vschema, "update", upd, upd.TableExprs, upd.Where, upd.OrderBy, upd.Limit, upd.Comments, upd.Exprs) if err != nil { return nil, err } @@ -45,7 +45,8 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Upd return nil, err } if len(eupd.ChangedVindexValues) != 0 { - eupd.OwnedVindexQuery = generateDMLSubquery(upd.Where, upd.OrderBy, upd.Limit, eupd.Table, vindexCol) + eupd.OwnedVindexQuery = generateDMLSubquery(upd.Where, upd.OrderBy, upd.Limit, eupd.Table, ksidCol) + eupd.KsidVindex = ksidVindex } return eupd, nil } From 8056e74d7679afaf3b8daebdf00547252f551f49 Mon Sep 17 00:00:00 2001 From: David Weitzman Date: Wed, 26 Feb 2020 05:18:49 -0800 Subject: [PATCH 178/825] tablet query engine: introduce -enable-consolidator-replicas This diff creates the possibility to start a tablet with "-enable-consolidator=false -enable-consolidator-replicas" so that query consolidation is enabled only for non-master instance types. This allows using the consolidator while keeping the agreement that OLTP reads from master have read-committed consistency while replicas have inconsistent reads. See #4058 Signed-off-by: David Weitzman --- go/vt/vttablet/endtoend/config_test.go | 74 +++++++++++++++++++ go/vt/vttablet/endtoend/framework/client.go | 15 ++++ go/vt/vttablet/tabletserver/query_engine.go | 2 + go/vt/vttablet/tabletserver/query_executor.go | 5 +- .../vttablet/tabletserver/tabletenv/config.go | 3 + go/vt/vttablet/tabletserver/tabletserver.go | 19 +++-- 6 files changed, 112 insertions(+), 6 deletions(-) diff --git a/go/vt/vttablet/endtoend/config_test.go b/go/vt/vttablet/endtoend/config_test.go index 9c24bba2cb0..507ae7ea2b6 100644 --- a/go/vt/vttablet/endtoend/config_test.go +++ b/go/vt/vttablet/endtoend/config_test.go @@ -25,6 +25,7 @@ import ( "vitess.io/vitess/go/sqltypes" querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/endtoend/framework" @@ -194,6 +195,79 @@ func TestDisableConsolidator(t *testing.T) { } } +func TestConsolidatorReplicasOnly(t *testing.T) { + totalConsolidationsTag := "Waits/Histograms/Consolidations/inf" + initial := framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) + var wg sync.WaitGroup + wg.Add(2) + go func() { + framework.NewClient().Execute("select sleep(0.5) from dual", nil) + wg.Done() + }() + go func() { + framework.NewClient().Execute("select sleep(0.5) from dual", nil) + wg.Done() + }() + wg.Wait() + afterOne := framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) + if initial+1 != afterOne { + t.Errorf("expected one consolidation, but got: before consolidation count: %v; after consolidation count: %v", initial, afterOne) + } + + framework.Server.SetConsolidatorEnabled(false) + defer framework.Server.SetConsolidatorEnabled(true) + framework.Server.SetConsolidatorReplicasEnabled(true) + defer framework.Server.SetConsolidatorReplicasEnabled(false) + + // master should not do query consolidation + var wg2 sync.WaitGroup + wg2.Add(2) + go func() { + framework.NewClient().Execute("select sleep(0.5) from dual", nil) + wg2.Done() + }() + go func() { + framework.NewClient().Execute("select sleep(0.5) from dual", nil) + wg2.Done() + }() + wg2.Wait() + noNewConsolidations := framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) + if afterOne != noNewConsolidations { + t.Errorf("expected no new consolidations, but got: before consolidation count: %v; after consolidation count: %v", afterOne, noNewConsolidations) + } + + // become a replica, where query consolidation should happen + client := framework.NewClientWithTabletType(topodatapb.TabletType_REPLICA) + + err := client.SetServingType(topodatapb.TabletType_REPLICA) + if err != nil { + t.Fatal(err) + } + defer func() { + err = client.SetServingType(topodatapb.TabletType_MASTER) + if err != nil { + t.Fatal(err) + } + }() + + initial = framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) + var wg3 sync.WaitGroup + wg3.Add(2) + go func() { + client.Execute("select sleep(0.5) from dual", nil) + wg3.Done() + }() + go func() { + client.Execute("select sleep(0.5) from dual", nil) + wg3.Done() + }() + wg3.Wait() + afterOne = framework.FetchInt(framework.DebugVars(), totalConsolidationsTag) + if initial+1 != afterOne { + t.Errorf("expected another consolidation, but got: before consolidation count: %v; after consolidation count: %v", initial, afterOne) + } +} + func TestQueryPlanCache(t *testing.T) { defer framework.Server.SetQueryPlanCacheCap(framework.Server.QueryPlanCacheCap()) framework.Server.SetQueryPlanCacheCap(1) diff --git a/go/vt/vttablet/endtoend/framework/client.go b/go/vt/vttablet/endtoend/framework/client.go index bb494440a80..279344beea9 100644 --- a/go/vt/vttablet/endtoend/framework/client.go +++ b/go/vt/vttablet/endtoend/framework/client.go @@ -52,6 +52,21 @@ func NewClient() *QueryClient { } } +// NewClientWithTabletType creates a new client for Server with the provided tablet type. +func NewClientWithTabletType(tabletType topodatapb.TabletType) *QueryClient { + targetCopy := Target + targetCopy.TabletType = tabletType + return &QueryClient{ + ctx: callerid.NewContext( + context.Background(), + &vtrpcpb.CallerID{}, + &querypb.VTGateCallerID{Username: "dev"}, + ), + target: targetCopy, + server: Server, + } +} + // NewClientWithContext creates a new client for Server with the provided context. func NewClientWithContext(ctx context.Context) *QueryClient { return &QueryClient{ diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index b182b7ba12a..332b97e5067 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -166,6 +166,7 @@ type QueryEngine struct { strictTransTables bool enableConsolidator bool + enableConsolidatorReplicas bool enableQueryPlanFieldCaching bool // Loggers @@ -206,6 +207,7 @@ func NewQueryEngine(checker connpool.MySQLChecker, se *schema.Engine, config tab checker, ) qe.enableConsolidator = config.EnableConsolidator + qe.enableConsolidatorReplicas = config.EnableConsolidatorReplicas qe.enableQueryPlanFieldCaching = config.EnableQueryPlanFieldCaching qe.consolidator = sync2.NewConsolidator() qe.txSerializer = txserializer.New(config.EnableHotRowProtectionDryRun, diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 3925207122b..02bc2752e67 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -41,6 +41,7 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/proto/topodata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) @@ -55,6 +56,7 @@ type QueryExecutor struct { ctx context.Context logStats *tabletenv.LogStats tsv *TabletServer + tabletType topodata.TabletType } var sequenceFields = []*querypb.Field{ @@ -829,7 +831,8 @@ func (qre *QueryExecutor) qFetch(logStats *tabletenv.LogStats, parsedQuery *sqlp if err != nil { return nil, err } - if qre.tsv.qe.enableConsolidator { + // Check tablet type. + if qre.tsv.qe.enableConsolidator || (qre.tsv.qe.enableConsolidatorReplicas && qre.tabletType != topodata.TabletType_MASTER) { q, original := qre.tsv.qe.consolidator.Create(string(sqlWithoutComments)) if original { defer q.Broadcast() diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index ed5aa7fcfc6..0d9a35a9f54 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -105,6 +105,7 @@ func init() { flag.BoolVar(&Config.EnforceStrictTransTables, "enforce_strict_trans_tables", DefaultQsConfig.EnforceStrictTransTables, "If true, vttablet requires MySQL to run with STRICT_TRANS_TABLES or STRICT_ALL_TABLES on. It is recommended to not turn this flag off. Otherwise MySQL may alter your supplied values before saving them to the database.") flag.BoolVar(&Config.EnableConsolidator, "enable-consolidator", DefaultQsConfig.EnableConsolidator, "This option enables the query consolidator.") + flag.BoolVar(&Config.EnableConsolidatorReplicas, "enable-consolidator-replicas", DefaultQsConfig.EnableConsolidatorReplicas, "This option enables the query consolidator only on replicas.") flag.BoolVar(&Config.EnableQueryPlanFieldCaching, "enable-query-plan-field-caching", DefaultQsConfig.EnableQueryPlanFieldCaching, "This option fetches & caches fields (columns) when storing query plans") } @@ -182,6 +183,7 @@ type TabletConfig struct { EnforceStrictTransTables bool EnableConsolidator bool + EnableConsolidatorReplicas bool EnableQueryPlanFieldCaching bool } @@ -262,6 +264,7 @@ var DefaultQsConfig = TabletConfig{ EnforceStrictTransTables: true, EnableConsolidator: true, + EnableConsolidatorReplicas: false, EnableQueryPlanFieldCaching: true, } diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 8048a2cebcd..3444a0ffc50 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -46,6 +46,7 @@ import ( "vitess.io/vitess/go/vt/logutil" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/proto/topodata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" @@ -1008,9 +1009,9 @@ func (tsv *TabletServer) Execute(ctx context.Context, target *querypb.Target, sq return err } if plan.PlanID == planbuilder.PlanInsertTopic { - result, err = tsv.topicExecute(ctx, query, comments, bindVariables, transactionID, options, plan, logStats) + result, err = tsv.topicExecute(ctx, query, comments, bindVariables, transactionID, options, plan, logStats, target.GetTabletType()) } else { - result, err = tsv.qreExecute(ctx, query, comments, bindVariables, transactionID, options, plan, logStats) + result, err = tsv.qreExecute(ctx, query, comments, bindVariables, transactionID, options, plan, logStats, target.GetTabletType()) } return err @@ -1019,7 +1020,7 @@ func (tsv *TabletServer) Execute(ctx context.Context, target *querypb.Target, sq return result, err } -func (tsv *TabletServer) topicExecute(ctx context.Context, query string, comments sqlparser.MarginComments, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions, plan *TabletPlan, logStats *tabletenv.LogStats) (result *sqltypes.Result, err error) { +func (tsv *TabletServer) topicExecute(ctx context.Context, query string, comments sqlparser.MarginComments, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions, plan *TabletPlan, logStats *tabletenv.LogStats, tabletType topodata.TabletType) (result *sqltypes.Result, err error) { for _, subscriber := range plan.Table.TopicInfo.Subscribers { // replace the topic name with the subscribed message table name newQuery := strings.Replace(query, plan.Table.Name.String(), subscriber.Name.String(), -1) @@ -1031,12 +1032,12 @@ func (tsv *TabletServer) topicExecute(ctx context.Context, query string, comment // because there isn't an option to return multiple results, only the last // message table result is returned - result, err = tsv.qreExecute(ctx, newQuery, comments, bindVariables, transactionID, options, newPlan, logStats) + result, err = tsv.qreExecute(ctx, newQuery, comments, bindVariables, transactionID, options, newPlan, logStats, tabletType) } return result, err } -func (tsv *TabletServer) qreExecute(ctx context.Context, query string, comments sqlparser.MarginComments, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions, plan *TabletPlan, logStats *tabletenv.LogStats) (result *sqltypes.Result, err error) { +func (tsv *TabletServer) qreExecute(ctx context.Context, query string, comments sqlparser.MarginComments, bindVariables map[string]*querypb.BindVariable, transactionID int64, options *querypb.ExecuteOptions, plan *TabletPlan, logStats *tabletenv.LogStats, tabletType topodata.TabletType) (result *sqltypes.Result, err error) { qre := &QueryExecutor{ query: query, marginComments: comments, @@ -1047,6 +1048,7 @@ func (tsv *TabletServer) qreExecute(ctx context.Context, query string, comments ctx: ctx, logStats: logStats, tsv: tsv, + tabletType: tabletType, } extras := tsv.watcher.ComputeExtras(options) result, err = qre.Execute() @@ -2308,6 +2310,13 @@ func (tsv *TabletServer) SetConsolidatorEnabled(enabled bool) { tsv.qe.enableConsolidator = enabled } +// SetConsolidatorReplicasEnabled (true) will enable the query consolidator for replicas. +// SetConsolidatorReplicasEnabled (false) will disable the query consolidator for replicas. +// This function should only be used for testing. +func (tsv *TabletServer) SetConsolidatorReplicasEnabled(enabled bool) { + tsv.qe.enableConsolidatorReplicas = enabled +} + // queryAsString returns a readable version of query+bind variables. func queryAsString(sql string, bindVariables map[string]*querypb.BindVariable) string { buf := &bytes.Buffer{} From 2f20bbcfe6499cbc4d8ed9d031ba846f5ae83bed Mon Sep 17 00:00:00 2001 From: deepthi Date: Wed, 26 Feb 2020 12:00:16 -0800 Subject: [PATCH 179/825] Use a timeout to update topo during vttablet shutdown Signed-off-by: deepthi --- go/vt/vttablet/tabletmanager/action_agent.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index 336ab496d97..8ccf55c79bb 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -770,7 +770,11 @@ func (agent *ActionAgent) Close() { tablet.PortMap = nil return nil } - if _, err := agent.TopoServer.UpdateTabletFields(context.Background(), agent.TabletAlias, f); err != nil { + + updateCtx, updateCancel := context.WithTimeout(context.Background(), *topo.RemoteOperationTimeout) + defer updateCancel() + + if _, err := agent.TopoServer.UpdateTabletFields(updateCtx, agent.TabletAlias, f); err != nil { log.Warningf("Failed to update tablet record, may contain stale identifiers: %v", err) } } From 40ca5cd4dec1dab53f991c2d55f8ec5741c6eacd Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 27 Feb 2020 10:50:24 +0100 Subject: [PATCH 180/825] Added end-to-end tests Signed-off-by: Andres Taylor --- go/test/endtoend/vtgate/lookup_test.go | 90 ++++++++++++++++++++++++++ go/test/endtoend/vtgate/main_test.go | 46 ++++++++++++- 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/go/test/endtoend/vtgate/lookup_test.go b/go/test/endtoend/vtgate/lookup_test.go index 8c5c2c5a2c9..4966dccb198 100644 --- a/go/test/endtoend/vtgate/lookup_test.go +++ b/go/test/endtoend/vtgate/lookup_test.go @@ -161,6 +161,96 @@ func TestConsistentLookup(t *testing.T) { exec(t, conn, "delete from t1 where id1=1") } +func TestDMLScatter(t *testing.T) { + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + /* Simple insert. after this dml, the tables will contain the following: + t3 (id5, id6, id7): + 1 2 3 + 2 2 3 + 3 4 3 + 4 5 4 + + t3_id7_idx (id7, keyspace_id:id6): + 3 2 + 3 2 + 3 4 + 4 5 + */ + exec(t, conn, "begin") + exec(t, conn, "insert into t3(id5, id6, id7) values(1, 2, 3), (2, 2, 3), (3, 4, 3), (4, 5, 4)") + exec(t, conn, "commit") + qr := exec(t, conn, "select id5, id6, id7 from t3 order by id5") + if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(1) INT64(2) INT64(3)] [INT64(2) INT64(2) INT64(3)] [INT64(3) INT64(4) INT64(3)] [INT64(4) INT64(5) INT64(4)]]"; got != want { + t.Errorf("select:\n%v want\n%v", got, want) + } + + /* Updating a non lookup column. after this dml, the tables will contain the following: + t3 (id5, id6, id7): + 42 2 3 + 2 2 3 + 3 4 3 + 4 5 4 + + t3_id7_idx (id7, keyspace_id:id6): + 3 2 + 3 2 + 3 4 + 4 5 + */ + exec(t, conn, "update t3 set id5 = 42 where id5 = 1") + qr = exec(t, conn, "select id5, id6, id7 from t3 order by id5") + if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(2) INT64(2) INT64(3)] [INT64(3) INT64(4) INT64(3)] [INT64(4) INT64(5) INT64(4)] [INT64(42) INT64(2) INT64(3)]]"; got != want { + t.Errorf("select:\n%v want\n%v", got, want) + } + + /* Updating a lookup column. after this dml, the tables will contain the following: + t3 (id5, id6, id7): + 42 2 42 + 2 2 42 + 3 4 3 + 4 5 4 + + t3_id7_idx (id7, keyspace_id:id6): + 42 2 + 42 2 + 3 4 + 4 5 + */ + exec(t, conn, "begin") + exec(t, conn, "update t3 set id7 = 42 where id6 = 2") + exec(t, conn, "commit") + qr = exec(t, conn, "select id5, id6, id7 from t3 order by id5") + if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(2) INT64(2) INT64(42)] [INT64(3) INT64(4) INT64(3)] [INT64(4) INT64(5) INT64(4)] [INT64(42) INT64(2) INT64(42)]]"; got != want { + t.Errorf("select:\n%v want\n%v", got, want) + } + + /* delete one specific keyspace id. after this dml, the tables will contain the following: + t3 (id5, id6, id7): + 3 4 3 + 4 5 4 + + t3_id7_idx (id7, keyspace_id:id6): + 3 4 + 4 5 + */ + exec(t, conn, "delete from t3 where id6 = 2") + qr = exec(t, conn, "select * from t3 where id6 = 2") + require.Empty(t, qr.Rows) + qr = exec(t, conn, "select * from t3_id7_idx where id6 = 2") + require.Empty(t, qr.Rows) + + // delete all the rows. + exec(t, conn, "delete from t3") + qr = exec(t, conn, "select * from t3") + require.Empty(t, qr.Rows) + qr = exec(t, conn, "select * from t3_id7_idx") + require.Empty(t, qr.Rows) +} + func TestConsistentLookupMultiInsert(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) diff --git a/go/test/endtoend/vtgate/main_test.go b/go/test/endtoend/vtgate/main_test.go index 9d506ee422b..550e0618cac 100644 --- a/go/test/endtoend/vtgate/main_test.go +++ b/go/test/endtoend/vtgate/main_test.go @@ -68,10 +68,23 @@ create table t2_id4_idx( primary key(id), key idx_id4(id4) ) Engine=InnoDB; -` + +create table t3( + id5 bigint, + id6 bigint, + id7 bigint, + primary key(id5) +) Engine=InnoDB; + +create table t3_id7_idx( + id bigint not null auto_increment, + id7 bigint, + id6 bigint, + primary key(id) +) Engine=InnoDB;` VSchema = ` - { +{ "sharded": true, "vindexes": { "hash": { @@ -95,6 +108,15 @@ create table t2_id4_idx( "autocommit": "true" }, "owner": "t2" + }, + "t3_id7_vdx": { + "type": "lookup_hash", + "params": { + "table": "t3_id7_idx", + "from": "id7", + "to": "id6" + }, + "owner": "t3" } }, "tables": { @@ -138,6 +160,26 @@ create table t2_id4_idx( } ] }, + "t3": { + "column_vindexes": [ + { + "column": "id6", + "name": "hash" + }, + { + "column": "id7", + "name": "t3_id7_vdx" + } + ] + }, + "t3_id7_idx": { + "column_vindexes": [ + { + "column": "id7", + "name": "hash" + } + ] + }, "vstream_test": { "column_vindexes": [ { From 50bed0a84dd6b33022b2120bdc8ff2fc301b15d6 Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Thu, 27 Feb 2020 22:18:37 +0000 Subject: [PATCH 181/825] Remove kubernetes template from example Signed-off-by: Yuvraj --- examples/kubernetes/README.md | 6 - examples/kubernetes/advanced.md | 61 ----- examples/kubernetes/cluster-down.sh | 30 --- examples/kubernetes/cluster-up.sh | 83 ------ examples/kubernetes/configure.sh | 69 ----- examples/kubernetes/create_test_table.sql | 7 - examples/kubernetes/env.sh | 94 ------- examples/kubernetes/etcd-down.sh | 34 --- .../kubernetes/etcd-service-template.yaml | 14 -- examples/kubernetes/etcd-up.sh | 61 ----- .../guestbook-controller-template.yaml | 29 --- examples/kubernetes/guestbook-down.sh | 26 -- examples/kubernetes/guestbook-service.yaml | 16 -- examples/kubernetes/guestbook-up.sh | 38 --- examples/kubernetes/guestbook/Dockerfile | 43 ---- examples/kubernetes/guestbook/README.md | 14 -- examples/kubernetes/guestbook/build.sh | 34 --- examples/kubernetes/guestbook/extract.sh | 23 -- examples/kubernetes/guestbook/main.py | 115 --------- .../kubernetes/guestbook/requirements.txt | 3 - .../kubernetes/guestbook/static/index.html | 33 --- .../kubernetes/guestbook/static/script.js | 60 ----- .../kubernetes/guestbook/static/style.css | 61 ----- examples/kubernetes/kvtctl.sh | 30 --- examples/kubernetes/namespace-down.sh | 27 -- examples/kubernetes/namespace-template.yaml | 4 - examples/kubernetes/namespace-up.sh | 32 --- examples/kubernetes/newrelic.sh | 37 --- examples/kubernetes/newrelic_start_agent.sh | 24 -- .../kubernetes/newrelic_start_mysql_plugin.sh | 58 ----- examples/kubernetes/orchestrator-down.sh | 31 --- .../kubernetes/orchestrator-template.yaml | 56 ----- examples/kubernetes/orchestrator-up.sh | 38 --- examples/kubernetes/sharded-vttablet-down.sh | 23 -- examples/kubernetes/sharded-vttablet-up.sh | 23 -- examples/kubernetes/sharded-vtworker.sh | 64 ----- examples/kubernetes/vitess-down.sh | 38 --- examples/kubernetes/vitess-up.sh | 236 ------------------ examples/kubernetes/vschema.json | 18 -- .../vtctld-controller-template.yaml | 67 ----- examples/kubernetes/vtctld-down.sh | 28 --- .../kubernetes/vtctld-service-template.yaml | 17 -- examples/kubernetes/vtctld-up.sh | 50 ---- ...gate-controller-benchmarking-template.yaml | 58 ----- .../vtgate-controller-template.yaml | 58 ----- examples/kubernetes/vtgate-down.sh | 33 --- .../kubernetes/vtgate-service-template.yaml | 21 -- examples/kubernetes/vtgate-up.sh | 57 ----- examples/kubernetes/vttablet-down.sh | 54 ---- .../vttablet-pod-benchmarking-template.yaml | 95 ------- .../kubernetes/vttablet-pod-template.yaml | 109 -------- examples/kubernetes/vttablet-up.sh | 77 ------ ...orker-controller-interactive-template.yaml | 42 ---- examples/kubernetes/vtworker-down.sh | 26 -- .../kubernetes/vtworker-pod-template.yaml | 39 --- .../kubernetes/vtworker-service-template.yaml | 17 -- examples/kubernetes/vtworker-up.sh | 42 ---- 57 files changed, 2583 deletions(-) delete mode 100644 examples/kubernetes/README.md delete mode 100644 examples/kubernetes/advanced.md delete mode 100755 examples/kubernetes/cluster-down.sh delete mode 100755 examples/kubernetes/cluster-up.sh delete mode 100755 examples/kubernetes/configure.sh delete mode 100644 examples/kubernetes/create_test_table.sql delete mode 100644 examples/kubernetes/env.sh delete mode 100644 examples/kubernetes/etcd-down.sh delete mode 100644 examples/kubernetes/etcd-service-template.yaml delete mode 100755 examples/kubernetes/etcd-up.sh delete mode 100644 examples/kubernetes/guestbook-controller-template.yaml delete mode 100755 examples/kubernetes/guestbook-down.sh delete mode 100644 examples/kubernetes/guestbook-service.yaml delete mode 100755 examples/kubernetes/guestbook-up.sh delete mode 100644 examples/kubernetes/guestbook/Dockerfile delete mode 100644 examples/kubernetes/guestbook/README.md delete mode 100755 examples/kubernetes/guestbook/build.sh delete mode 100644 examples/kubernetes/guestbook/extract.sh delete mode 100644 examples/kubernetes/guestbook/main.py delete mode 100644 examples/kubernetes/guestbook/requirements.txt delete mode 100644 examples/kubernetes/guestbook/static/index.html delete mode 100644 examples/kubernetes/guestbook/static/script.js delete mode 100644 examples/kubernetes/guestbook/static/style.css delete mode 100755 examples/kubernetes/kvtctl.sh delete mode 100755 examples/kubernetes/namespace-down.sh delete mode 100644 examples/kubernetes/namespace-template.yaml delete mode 100755 examples/kubernetes/namespace-up.sh delete mode 100755 examples/kubernetes/newrelic.sh delete mode 100755 examples/kubernetes/newrelic_start_agent.sh delete mode 100755 examples/kubernetes/newrelic_start_mysql_plugin.sh delete mode 100755 examples/kubernetes/orchestrator-down.sh delete mode 100644 examples/kubernetes/orchestrator-template.yaml delete mode 100755 examples/kubernetes/orchestrator-up.sh delete mode 100755 examples/kubernetes/sharded-vttablet-down.sh delete mode 100755 examples/kubernetes/sharded-vttablet-up.sh delete mode 100755 examples/kubernetes/sharded-vtworker.sh delete mode 100755 examples/kubernetes/vitess-down.sh delete mode 100755 examples/kubernetes/vitess-up.sh delete mode 100644 examples/kubernetes/vschema.json delete mode 100644 examples/kubernetes/vtctld-controller-template.yaml delete mode 100755 examples/kubernetes/vtctld-down.sh delete mode 100644 examples/kubernetes/vtctld-service-template.yaml delete mode 100755 examples/kubernetes/vtctld-up.sh delete mode 100644 examples/kubernetes/vtgate-controller-benchmarking-template.yaml delete mode 100644 examples/kubernetes/vtgate-controller-template.yaml delete mode 100755 examples/kubernetes/vtgate-down.sh delete mode 100644 examples/kubernetes/vtgate-service-template.yaml delete mode 100755 examples/kubernetes/vtgate-up.sh delete mode 100755 examples/kubernetes/vttablet-down.sh delete mode 100644 examples/kubernetes/vttablet-pod-benchmarking-template.yaml delete mode 100644 examples/kubernetes/vttablet-pod-template.yaml delete mode 100755 examples/kubernetes/vttablet-up.sh delete mode 100644 examples/kubernetes/vtworker-controller-interactive-template.yaml delete mode 100755 examples/kubernetes/vtworker-down.sh delete mode 100644 examples/kubernetes/vtworker-pod-template.yaml delete mode 100644 examples/kubernetes/vtworker-service-template.yaml delete mode 100755 examples/kubernetes/vtworker-up.sh diff --git a/examples/kubernetes/README.md b/examples/kubernetes/README.md deleted file mode 100644 index a4ea12366cd..00000000000 --- a/examples/kubernetes/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Vitess on Kubernetes - -This directory contains an example configuration for running Vitess on -[Kubernetes](https://kubernetes.io/). - -See the [Vitess on Kubernetes](https://vitess.io/docs/tutorials/kubernetes/) \ No newline at end of file diff --git a/examples/kubernetes/advanced.md b/examples/kubernetes/advanced.md deleted file mode 100644 index 1b4e4f260f3..00000000000 --- a/examples/kubernetes/advanced.md +++ /dev/null @@ -1,61 +0,0 @@ -# Advanced Vitess on Kubernetes - -## Automatically run Vitess on Container Engine - -The following commands will create a Google Container Engine cluster and bring -up Vitess with two shards and three tablets per shard: (Note that it does not -bring up the Guestbook example) - -``` - -vitess/examples/kubernetes$ export SHARDS=-80,80- -vitess/examples/kubernetes$ export GKE_ZONE=us-central1-b -vitess/examples/kubernetes$ export GKE_NUM_NODES=10 -vitess/examples/kubernetes$ export GKE_MACHINE_TYPE=n1-standard-8 -vitess/examples/kubernetes$ ./cluster-up.sh -vitess/examples/kubernetes$ ./vitess-up.sh -``` - -Run the following to tear down the entire Vitess + container engine cluster: - -``` -vitess/examples/kubernetes$ ./vitess-down.sh -vitess/examples/kubernetes$ ./cluster-down.sh -``` - -## Parameterizing configs - -The vitess and cluster scripts both support parameterization via exporting -environment variables. - -### Parameterizing cluster scripts - -Common environment variables: - -* GKE_ZONE - Zone to use for Container Engine (default us-central1-b) -* GKE_CLUSTER_NAME - Name to use when creating a cluster (default example). -* SHARDS - Comma delimited keyranges for shards (default -80,80- for 2 shards). -Use 0 for an unsharded keyspace. - -The cluster-up.sh script supports the following environment variables: - -* GKE_MACHINE_TYPE - Container Engine machine type (default n1-standard-1) -* GKE_NUM_NODES - Number of nodes to use for the cluster (required). -* GKE_SSD_SIZE_GB - SSD size (in GB) to use (default 0 for no SSD). - -The vitess-up.sh script supports the following environment variables: - -* TABLETS_PER_SHARD - Number of tablets per shard (default 3). -* RDONLY_COUNT - Number of tablets per shard that are rdonly (default 0). -* VTGATE_COUNT - Number of vtgates (default 25% of total vttablet count, -with a minimum of 3). - -For example, to create an unsharded keyspace with 5 tablets, use the following: - -``` -export SHARDS=0 -export TABLETS_PER_SHARD=5 -vitess/examples/kubernetes$ ./cluster-up.sh -vitess/examples/kubernetes$ ./vitess-up.sh -``` - diff --git a/examples/kubernetes/cluster-down.sh b/examples/kubernetes/cluster-down.sh deleted file mode 100755 index 33ea5891ae5..00000000000 --- a/examples/kubernetes/cluster-down.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Tears down the container engine cluster, removes rules/pools - -GKE_ZONE=${GKE_ZONE:-'us-central1-b'} -GKE_CLUSTER_NAME=${GKE_CLUSTER_NAME:-'example'} - -base_ssd_name="$GKE_CLUSTER_NAME-vt-ssd-" - -gcloud container clusters delete $GKE_CLUSTER_NAME -z $GKE_ZONE -q - -num_ssds=`gcloud compute disks list | awk -v name="$base_ssd_name" -v zone=$GKE_ZONE '$1~name && $2==zone' | wc -l` -for i in `seq 1 $num_ssds`; do - gcloud compute disks delete $base_ssd_name$i --zone $GKE_ZONE -q -done - diff --git a/examples/kubernetes/cluster-up.sh b/examples/kubernetes/cluster-up.sh deleted file mode 100755 index 7e4da58946c..00000000000 --- a/examples/kubernetes/cluster-up.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a fully functional vitess cluster. -# It performs the following steps: -# 1. Create a container engine cluster -# 2. Create etcd clusters -# 3. Create vtctld clusters -# 4. Forward vtctld port -# 5. Create vttablet clusters -# 6. Perform vtctl initialization: -# SetKeyspaceShardingInfo, Rebuild Keyspace, Reparent Shard, Apply Schema -# 7. Create vtgate clusters -# 8. Forward vtgate port - -# Customizable parameters -GKE_ZONE=${GKE_ZONE:-'us-central1-b'} -GKE_MACHINE_TYPE=${GKE_MACHINE_TYPE:-'n1-standard-4'} -GKE_CLUSTER_NAME=${GKE_CLUSTER_NAME:-'example'} -GKE_SSD_SIZE_GB=${GKE_SSD_SIZE_GB:-0} -GKE_NUM_NODES=${GKE_NUM_NODES:-0} -VTDATAROOT_VOLUME=${VTDATAROOT_VOLUME:-'/ssd'} - -# Get region from zone (everything to last dash) -gke_region=`echo $GKE_ZONE | sed "s/-[^-]*$//"` - -export KUBECTL='kubectl' -gcloud config set compute/zone $GKE_ZONE -project_id=`gcloud config list project | sed -n 2p | cut -d " " -f 3` - -echo "****************************" -echo "*Creating cluster:" -echo "* Zone: $GKE_ZONE" -echo "* Machine type: $GKE_MACHINE_TYPE" -echo "* Num nodes: $GKE_NUM_NODES" -echo "* SSD Size: $GKE_SSD_SIZE_GB" -echo "* Cluster name: $GKE_CLUSTER_NAME" -echo "* Project ID: $project_id" -echo "****************************" -gcloud container clusters create $GKE_CLUSTER_NAME --machine-type $GKE_MACHINE_TYPE --num-nodes $GKE_NUM_NODES --scopes storage-rw -gcloud config set container/cluster $GKE_CLUSTER_NAME - -if [ $GKE_SSD_SIZE_GB -gt 0 ] -then - echo Creating SSDs and attaching to container engine nodes - i=1 - for nodename in `$KUBECTL get nodes --no-headers | awk '{print $1}'`; do - diskname=$GKE_CLUSTER_NAME-vt-ssd-$i - gcloud compute disks create $diskname --type=pd-ssd --size=${GKE_SSD_SIZE_GB}GB - gcloud compute instances attach-disk $nodename --disk $diskname - gcloud compute ssh $nodename --zone=$GKE_ZONE --command "sudo mkdir ${VTDATAROOT_VOLUME}; sudo /usr/share/google/safe_format_and_mount -m \"mkfs.ext4 -o noatime -F\" /dev/disk/by-id/google-persistent-disk-1 ${VTDATAROOT_VOLUME} &" - gcloud compute ssh $nodename --zone=$GKE_ZONE --command "echo '/dev/disk/by-id/google-persistent-disk-1 /ssd ext4 defaults,noatime 0 0' | sudo tee --append /etc/fstab > /dev/null" - let i=i+1 - done -fi - -if [ -n "$NEWRELIC_LICENSE_KEY" -a $GKE_SSD_SIZE_GB -gt 0 ]; then - i=1 - for nodename in `$KUBECTL get nodes --no-header | awk '{print $1}'`; do - gcloud compute copy-files newrelic.sh $nodename:~/ - gcloud compute copy-files newrelic_start_agent.sh $nodename:~/ - gcloud compute copy-files newrelic_start_mysql_plugin.sh $nodename:~/ - gcloud compute ssh $nodename --command "bash -c '~/newrelic.sh ${NEWRELIC_LICENSE_KEY}'" - let i=i+1 - done -fi - -echo "****************************" -echo "* Complete!" -echo "****************************" diff --git a/examples/kubernetes/configure.sh b/examples/kubernetes/configure.sh deleted file mode 100755 index 8ed12fd9587..00000000000 --- a/examples/kubernetes/configure.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script generates config.sh, which is a site-local config file that is not -# checked into source control. - -# Erase any existing file since we append from here on. -> config.sh - -# Custom Docker image. -read -p "Vitess Docker image (leave empty for default) []: " -echo "vitess_image=\"$REPLY\"" >> config.sh - -# Select and configure Backup Storage Implementation. -storage=gcs -read -p "Backup Storage (file, gcs) [gcs]: " -if [ -n "$REPLY" ]; then storage="$REPLY"; fi - -case "$storage" in -gcs) - # Google Cloud Storage - read -p "Google Cloud Storage bucket for Vitess backups: " bucket - if [ -z "$bucket" ]; then - echo "ERROR: Bucket name must not be empty." - exit 1 - fi - echo - echo "NOTE: If you haven't already created this bucket, you can do so by running:" - echo " gsutil mb gs://$bucket" - echo - - backup_flags=$(echo -backup_storage_implementation gcs \ - -gcs_backup_storage_bucket "'$bucket'") - ;; -file) - # Mounted volume (e.g. NFS) - read -p "Root directory for backups (usually an NFS mount): " file_root - if [ -z "$file_root" ]; then - echo "ERROR: Root directory must not be empty." - exit 1 - fi - echo - echo "NOTE: You must add your NFS mount to the vtctld-controller-template" - echo " and vttablet-pod-template as described in the Kubernetes docs:" - echo " https://kubernetes.io/docs/concepts/storage/volumes#nfs" - echo - - backup_flags=$(echo -backup_storage_implementation file \ - -file_backup_storage_root "'$file_root'") - ;; -*) - echo "ERROR: Unsupported backup storage implementation: $storage" - exit 1 -esac -echo "backup_flags=\"$backup_flags\"" >> config.sh - diff --git a/examples/kubernetes/create_test_table.sql b/examples/kubernetes/create_test_table.sql deleted file mode 100644 index bfb24b766ad..00000000000 --- a/examples/kubernetes/create_test_table.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE messages ( - page BIGINT(20) UNSIGNED, - time_created_ns BIGINT(20) UNSIGNED, - message VARCHAR(10000), - PRIMARY KEY (page, time_created_ns) -) ENGINE=InnoDB - diff --git a/examples/kubernetes/env.sh b/examples/kubernetes/env.sh deleted file mode 100644 index c644b4de8be..00000000000 --- a/examples/kubernetes/env.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an include file used by the other scripts in this directory. - -# Most clusters will just be accessed with 'kubectl' on $PATH. -# However, some might require a different command. For example, GKE required -# KUBECTL='gcloud container kubectl' for a while. Now that most of our -# use cases just need KUBECTL=kubectl, we'll make that the default. -KUBECTL=${KUBECTL:-kubectl} - -# Kubernetes API address for $KUBECTL. When the Kubernetes API server is not -# local, We can easily access the API by editing KUBERNETES_API_SERVER's value -KUBERNETES_API_SERVER=${KUBERNETES_API_SERVER:-""} - -# Kubernetes namespace for Vitess and components. -VITESS_NAME=${VITESS_NAME:-'default'} - -# Kubernetes options config -KUBECTL_OPTIONS="--namespace=$VITESS_NAME" -if [[ -n "$KUBERNETES_API_SERVER" ]]; then - KUBECTL_OPTIONS+=" --server=$KUBERNETES_API_SERVER" -fi - -# CELLS should be a comma separated list of cells -# the first cell listed will become local to vtctld. -CELLS=${CELLS:-'test'} - -# This should match the nodePort in vtctld-service.yaml -VTCTLD_PORT=${VTCTLD_PORT:-30001} - -# Get the ExternalIP of any node. -get_node_ip() { - $KUBECTL $KUBECTL_OPTIONS get -o template --template '{{range (index .items 0).status.addresses}}{{if eq .type "ExternalIP" "LegacyHostIP"}}{{.address}}{{end}}{{end}}' nodes -} - -# Try to find vtctld address if not provided. -get_vtctld_addr() { - if [ -z "$VTCTLD_ADDR" ]; then - VTCTLD_HOST=$(get_node_ip) - if [ -n "$VTCTLD_HOST" ]; then - VTCTLD_ADDR="$VTCTLD_HOST:$VTCTLD_PORT" - fi - fi -} - -# Find the name of a vtctld pod. -get_vtctld_pod() { - $KUBECTL $KUBECTL_OPTIONS get -o template --template "{{if ge (len .items) 1 }}{{(index .items 0).metadata.name}}{{end}}" -l 'app=vitess,component=vtctld' pods -} - -start_vtctld_forward() { - pod=`get_vtctld_pod` - if [ -z "$pod" ]; then - >&2 echo "ERROR: Can't get vtctld pod name. Is vtctld running?" - return 1 - fi - - tmpfile=`mktemp` - $KUBECTL $KUBECTL_OPTIONS port-forward $pod 0:15999 &> $tmpfile & - vtctld_forward_pid=$! - - until [[ `cat $tmpfile` =~ :([0-9]+)\ -\> ]]; do :; done - vtctld_forward_port=${BASH_REMATCH[1]} - rm $tmpfile -} - -stop_vtctld_forward() { - kill $vtctld_forward_pid -} - -config_file=`dirname "${BASH_SOURCE}"`/config.sh -if [ ! -f $config_file ]; then - echo "Please run ./configure.sh first to generate config.sh file." - exit 1 -fi - -source $config_file - -# Fill in defaults for new variables, so old config.sh files still work. -vitess_image=${vitess_image:-vitess/lite} - diff --git a/examples/kubernetes/etcd-down.sh b/examples/kubernetes/etcd-down.sh deleted file mode 100644 index 3aef6636bd7..00000000000 --- a/examples/kubernetes/etcd-down.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that tears down the etcd servers started by -# etcd-up.sh. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -replicas=${ETCD_REPLICAS:-3} -cells=`echo $CELLS | tr ',' ' '` - -# Delete etcd clusters -for cell in 'global' $cells; do - echo "Stopping etcd cluster for $cell cell..." - sed -e "s/{{cell}}/$cell/g" -e "s/{{replicas}}/$replicas/g" \ - etcd-service-template.yaml | \ - $KUBECTL $KUBECTL_OPTIONS delete -f - -done diff --git a/examples/kubernetes/etcd-service-template.yaml b/examples/kubernetes/etcd-service-template.yaml deleted file mode 100644 index 9aceaec4b33..00000000000 --- a/examples/kubernetes/etcd-service-template.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: "etcd.database.coreos.com/v1beta2" -kind: "EtcdCluster" -metadata: - name: "etcd-{{cell}}" -spec: - size: {{replicas}} - version: "3.1.8" - repository: "quay.io/coreos/etcd" - pod: - labels: - component: etcd - cell: {{cell}} - application: vitess - busyboxImage: "busybox:1.28.0-glibc" diff --git a/examples/kubernetes/etcd-up.sh b/examples/kubernetes/etcd-up.sh deleted file mode 100755 index f4d4d6118bc..00000000000 --- a/examples/kubernetes/etcd-up.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates etcd clusters. -# Vitess requires a global cluster, as well as one for each cell. -# -# For automatic discovery, an etcd cluster can be bootstrapped from an -# existing cluster. In this example, we use an externally-run discovery -# service, but you can use your own. See the etcd docs for more: -# https://github.com/coreos/etcd/blob/v2.0.13/Documentation/clustering.md - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -replicas=${ETCD_REPLICAS:-3} -cells=`echo $CELLS | tr ',' ' '` - -# Check the installation for etcd-operator has been done. -# To make sure the EtcdClusters CRD not only exists but is ready to serve, -# we try to list EtcdClusters. This will succeed even if the list is empty. -if ! kubectl get etcdclusters.etcd.database.coreos.com &> /dev/null ; then - # etcd-operator is a shared resource, and installing it on a secure - # cluster requires granting RBAC permissions. - # We therefore require the user to do this before running Vitess. - echo - echo "Please install etcd-operator in the same namespace as Vitess" - echo "before running etcd-up.sh. See the instructions here:" - echo - echo " https://github.com/coreos/etcd-operator/blob/master/doc/user/install_guide.md" - echo - echo "If you already installed etcd-operator and still get this prompt," - echo "check for any errors in etcd-operator logs:" - echo - echo " kubectl logs -l name=etcd-operator" - echo - exit 1 -fi - -for cell in 'global' $cells; do - # Create the etcd cluster using etcd-operator - echo "Creating etcd service for '$cell' cell..." - sed -e "s/{{cell}}/$cell/g" -e "s/{{replicas}}/$replicas/g" \ - etcd-service-template.yaml | \ - $KUBECTL $KUBECTL_OPTIONS create -f - -done - diff --git a/examples/kubernetes/guestbook-controller-template.yaml b/examples/kubernetes/guestbook-controller-template.yaml deleted file mode 100644 index 2897b3f5929..00000000000 --- a/examples/kubernetes/guestbook-controller-template.yaml +++ /dev/null @@ -1,29 +0,0 @@ -kind: ReplicationController -apiVersion: v1 -metadata: - name: guestbook -spec: - replicas: 3 - template: - metadata: - labels: - component: frontend - app: guestbook - spec: - containers: - - name: guestbook - image: vitess/guestbook - livenessProbe: - httpGet: - path: /env - port: 8080 - initialDelaySeconds: 30 - timeoutSeconds: 5 - ports: - - name: http-server - containerPort: 8080 - resources: - limits: - memory: "128Mi" - cpu: "100m" - args: ["--port", "{{port}}", "--cell", "{{cell}}", "--keyspace", "{{keyspace}}", "--vtgate_port", "{{vtgate_port}}"] diff --git a/examples/kubernetes/guestbook-down.sh b/examples/kubernetes/guestbook-down.sh deleted file mode 100755 index 35186041553..00000000000 --- a/examples/kubernetes/guestbook-down.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops guestbook. - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Stopping guestbook replicationcontroller..." -$KUBECTL $KUBECTL_OPTIONS delete replicationcontroller guestbook - -echo "Deleting guestbook service..." -$KUBECTL $KUBECTL_OPTIONS delete service guestbook diff --git a/examples/kubernetes/guestbook-service.yaml b/examples/kubernetes/guestbook-service.yaml deleted file mode 100644 index 341c3ee0314..00000000000 --- a/examples/kubernetes/guestbook-service.yaml +++ /dev/null @@ -1,16 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: guestbook - labels: - component: frontend - app: guestbook -spec: - ports: - - port: 80 - targetPort: http-server - selector: - component: frontend - app: guestbook - type: LoadBalancer - diff --git a/examples/kubernetes/guestbook-up.sh b/examples/kubernetes/guestbook-up.sh deleted file mode 100755 index f246a72e2ec..00000000000 --- a/examples/kubernetes/guestbook-up.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that starts a guestbook replicationcontroller. - -set -e - -port=${GUESTBOOK_PORT:-8080} -cell=${GUESTBOOK_CELL:-"test"} -keyspace=${GUESTBOOK_KEYSPACE:-"test_keyspace"} -vtgate_port=${VTGATE_PORT:-15991} - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Creating guestbook service..." -$KUBECTL $KUBECTL_OPTIONS create -f guestbook-service.yaml - -sed_script="" -for var in port cell keyspace vtgate_port; do - sed_script+="s,{{$var}},${!var},g;" -done - -echo "Creating guestbook replicationcontroller..." -sed -e "$sed_script" guestbook-controller-template.yaml | $KUBECTL $KUBECTL_OPTIONS create -f - diff --git a/examples/kubernetes/guestbook/Dockerfile b/examples/kubernetes/guestbook/Dockerfile deleted file mode 100644 index afdd85b4f2e..00000000000 --- a/examples/kubernetes/guestbook/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This Dockerfile should be built from within the accompanying build.sh script. -FROM debian:jessie - -RUN apt-get update -y \ - && apt-get install --no-install-recommends -y -q \ - build-essential \ - python2.7 \ - python2.7-dev \ - python-pip \ - git \ - && pip install -U pip \ - && pip install virtualenv - -WORKDIR /app -RUN virtualenv /env -ADD requirements.txt /app/requirements.txt -RUN /env/bin/pip install -r /app/requirements.txt -ADD main.py requirements.txt /app/ -ADD static /app/static - -EXPOSE 8080 -CMD [] -ENTRYPOINT ["/env/bin/python", "main.py"] - -ADD tmp/pkg /app/pkg -ADD tmp/lib /app/lib -ENV LD_LIBRARY_PATH /app/lib -ENV PYTHONPATH /app/pkg/py-vtdb:/app/pkg/py-mock-1.0.1/lib/python2.7/site-packages:/app/pkg/dist-packages - diff --git a/examples/kubernetes/guestbook/README.md b/examples/kubernetes/guestbook/README.md deleted file mode 100644 index 2ff41a9217c..00000000000 --- a/examples/kubernetes/guestbook/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# vitess/guestbook - -This is a Docker image for a sample guestbook app that uses Vitess. - -It is essentially a port of the -[kubernetes/guestbook-go](https://github.com/kubernetes/examples/tree/master/guestbook-go) -example, but using Python instead of Go for the app server, -and Vitess instead of Redis for the storage engine. - -It has also been modified to support multiple Guestbook pages, -to better demonstrate sharding support in Vitess. - -Note that the Dockerfile should be built with the accompanying build.sh script. -See the comments in the script for more information. diff --git a/examples/kubernetes/guestbook/build.sh b/examples/kubernetes/guestbook/build.sh deleted file mode 100755 index 712bc8bf123..00000000000 --- a/examples/kubernetes/guestbook/build.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is a script to build the vitess/guestbook Docker image. -# It must be run from within a bootstrapped vitess tree, after dev.env. - -set -e - -mkdir tmp tmp/pkg tmp/lib -cp extract.sh tmp/ -chmod -R 777 tmp - -# We also need the grpc library. -docker run --rm -v $PWD/tmp:/out vitess/base bash /out/extract.sh - -# Build the Docker image. -docker build -t vitess/guestbook . - -# Clean up. -docker run --rm -v $PWD/tmp:/out vitess/base bash -c 'rm -rf /out/pkg/* /out/lib/*' -rm -rf tmp diff --git a/examples/kubernetes/guestbook/extract.sh b/examples/kubernetes/guestbook/extract.sh deleted file mode 100644 index 04499c87397..00000000000 --- a/examples/kubernetes/guestbook/extract.sh +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# Don't run this. It is only used as part of build.sh. - -set -e - -# Collect all the local Python libs we need. -mkdir -p /out/pkg/py-vtdb -cp -R $VTROOT/py/* /out/pkg/py-vtdb/ -cp -R /usr/local/lib/python2.7/dist-packages /out/pkg/ -cp -R /vt/dist/py-* /out/pkg/ - diff --git a/examples/kubernetes/guestbook/main.py b/examples/kubernetes/guestbook/main.py deleted file mode 100644 index 9814990b915..00000000000 --- a/examples/kubernetes/guestbook/main.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Main python file.""" - -import argparse -import json -import os -import time - -from flask import Flask - -from vtdb import vtgate_client - -# Register gRPC protocol. -from vtdb import grpc_vtgate_client # pylint: disable=unused-import - -app = Flask(__name__) - -# conn is the connection to vtgate. -conn = None -keyspace = None - - -@app.route('/') -def index(): - return app.send_static_file('index.html') - - -@app.route('/page/') -def view(page): - _ = page - return app.send_static_file('index.html') - - -@app.route('/lrange/guestbook/') -def list_guestbook(page): - """Read the list from a replica.""" - cursor = conn.cursor(tablet_type='replica', keyspace=keyspace) - - cursor.execute( - 'SELECT message, time_created_ns FROM messages WHERE page=:page' - ' ORDER BY time_created_ns', - {'page': page}) - entries = [row[0] for row in cursor.fetchall()] - cursor.close() - - return json.dumps(entries) - - -@app.route('/rpush/guestbook//') -def add_entry(page, value): - """Insert a row on the master.""" - cursor = conn.cursor(tablet_type='master', keyspace=keyspace, writable=True) - - cursor.begin() - cursor.execute( - 'INSERT INTO messages (page, time_created_ns, message)' - ' VALUES (:page, :time_created_ns, :message)', - { - 'page': page, - 'time_created_ns': int(time.time() * 1e9), - 'message': value, - }) - cursor.commit() - - # Read the list back from master (critical read) because it's - # important that the user sees their own addition immediately. - cursor.execute( - 'SELECT message, time_created_ns FROM messages WHERE page=:page' - ' ORDER BY time_created_ns', - {'page': page}) - entries = [row[0] for row in cursor.fetchall()] - cursor.close() - - return json.dumps(entries) - - -@app.route('/env') -def env(): - return json.dumps(dict(os.environ)) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Run guestbook app') - parser.add_argument('--port', help='Port', default=8080, type=int) - parser.add_argument('--cell', help='Cell', default='test', type=str) - parser.add_argument( - '--keyspace', help='Keyspace', default='test_keyspace', type=str) - parser.add_argument( - '--timeout', help='Connect timeout (s)', default=10, type=int) - parser.add_argument( - '--vtgate_port', help='Vtgate Port', default=15991, type=int) - guestbook_args = parser.parse_args() - - # Get vtgate service address from Kubernetes DNS. - addr = 'vtgate-%s:%d' % (guestbook_args.cell, guestbook_args.vtgate_port) - - # Connect to vtgate. - conn = vtgate_client.connect('grpc', addr, guestbook_args.timeout) - - keyspace = guestbook_args.keyspace - - app.run(host='0.0.0.0', port=guestbook_args.port, debug=True) diff --git a/examples/kubernetes/guestbook/requirements.txt b/examples/kubernetes/guestbook/requirements.txt deleted file mode 100644 index eb913c5c387..00000000000 --- a/examples/kubernetes/guestbook/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -Flask==1.0 -grpcio==1.12.0 -grpcio-tools==1.12.0 diff --git a/examples/kubernetes/guestbook/static/index.html b/examples/kubernetes/guestbook/static/index.html deleted file mode 100644 index 946a2b3d986..00000000000 --- a/examples/kubernetes/guestbook/static/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - Guestbook - - - - -
-

Waiting for database connection...

-
- -
- -
-

-

/env -

- - - - diff --git a/examples/kubernetes/guestbook/static/script.js b/examples/kubernetes/guestbook/static/script.js deleted file mode 100644 index 0240307fcd9..00000000000 --- a/examples/kubernetes/guestbook/static/script.js +++ /dev/null @@ -1,60 +0,0 @@ -$(document).ready(function() { - var headerTitleElement = $("#header h1"); - var entriesElement = $("#guestbook-entries"); - var formElement = $("#guestbook-form"); - var submitElement = $("#guestbook-submit"); - var entryContentElement = $("#guestbook-entry-content"); - var hostAddressElement = $("#guestbook-host-address"); - - // Look for page number in the URL, if any. - var pageNum = -1; - var match = window.location.href.match(/\/page\/(\d+)$/); - if (match) { - pageNum = parseInt(match[1]); - headerTitleElement.text('Guestbook Page ' + pageNum); - } else { - // No page number provided. Link to a random one. - pageNum = Math.floor(Math.random() * 100); - entriesElement.html('

Pick a page to view by navigating to /page/### or go to a random page.

'); - formElement.remove(); - return; - } - - var appendGuestbookEntries = function(data) { - entriesElement.empty(); - $.each(data, function(key, val) { - entriesElement.append("

" + val + "

"); - }); - } - - var handleSubmission = function(e) { - e.preventDefault(); - var entryValue = entryContentElement.val() - if (entryValue.length > 0) { - entriesElement.append("

...

"); - $.getJSON("/rpush/guestbook/" + pageNum + "/" + entryValue, appendGuestbookEntries); - } - return false; - } - - // colors = purple, blue, red, green, yellow - var colors = ["#549", "#18d", "#d31", "#2a4", "#db1"]; - var randomColor = colors[Math.floor(5 * Math.random())]; - (function setElementsColor(color) { - headerTitleElement.css("color", color); - entryContentElement.css("box-shadow", "inset 0 0 0 2px " + color); - submitElement.css("background-color", color); - })(randomColor); - - submitElement.click(handleSubmission); - formElement.submit(handleSubmission); - hostAddressElement.append(document.URL); - - // Poll every second. - (function fetchGuestbook() { - $.getJSON("/lrange/guestbook/" + pageNum).done(appendGuestbookEntries).always( - function() { - setTimeout(fetchGuestbook, 1000); - }); - })(); -}); diff --git a/examples/kubernetes/guestbook/static/style.css b/examples/kubernetes/guestbook/static/style.css deleted file mode 100644 index fd1c393fb08..00000000000 --- a/examples/kubernetes/guestbook/static/style.css +++ /dev/null @@ -1,61 +0,0 @@ -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/examples/kubernetes/kvtctl.sh b/examples/kubernetes/kvtctl.sh deleted file mode 100755 index 5dcb6dc3730..00000000000 --- a/examples/kubernetes/kvtctl.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is a script that uses kubectl to figure out the address for vtctld, -# and then runs vtctlclient with that address. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Starting port forwarding to vtctld..." -start_vtctld_forward -trap stop_vtctld_forward EXIT - -vtctlclient -server 127.0.0.1:$vtctld_forward_port "$@" - diff --git a/examples/kubernetes/namespace-down.sh b/examples/kubernetes/namespace-down.sh deleted file mode 100755 index e5345f412e6..00000000000 --- a/examples/kubernetes/namespace-down.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that deletes a namespace. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -namespace=${VITESS_NAME:-'vitess'} - -echo "Deleting namespace $namespace..." -$KUBECTL $KUBECTL_OPTIONS delete namespace $namespace diff --git a/examples/kubernetes/namespace-template.yaml b/examples/kubernetes/namespace-template.yaml deleted file mode 100644 index f0030d5d254..00000000000 --- a/examples/kubernetes/namespace-template.yaml +++ /dev/null @@ -1,4 +0,0 @@ -kind: Namespace -apiVersion: v1 -metadata: - name: {{namespace}} diff --git a/examples/kubernetes/namespace-up.sh b/examples/kubernetes/namespace-up.sh deleted file mode 100755 index d5c6e53f030..00000000000 --- a/examples/kubernetes/namespace-up.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a namespace. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -namespace=${VITESS_NAME:-'vitess'} - -echo "Creating namespace $namespace..." -sed_script="" -for var in namespace; do - sed_script+="s,{{$var}},${!var},g;" -done -cat namespace-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - diff --git a/examples/kubernetes/newrelic.sh b/examples/kubernetes/newrelic.sh deleted file mode 100755 index 9cc2a2d0b96..00000000000 --- a/examples/kubernetes/newrelic.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -NEWRELIC_LICENSE_KEY=$1 -VTDATAROOT=$2 - -./newrelic_start_agent.sh $NEWRELIC_LICENSE_KEY -if [ -n "$VTDATAROOT" ]; then - sudo cp newrelic_start*.sh $VTDATAROOT -fi - -mysql_docker_image=`sudo docker ps | awk '$NF~/^k8s_mysql/ {print $1}'` -vttablet_docker_image=`sudo docker ps | awk '$NF~/^k8s_vttablet/ {print $1}'` -vtgate_docker_image=`sudo docker ps | awk '$NF~/^k8s_vtgate/ {print $1}'` -for image in `echo -e "$mysql_docker_image\n$vttablet_docker_image\n$vtgate_docker_image"`; do - if [ -z "$VTDATAROOT" ]; then - vtdataroot=`sudo docker inspect -f '{{index .Volumes "/vt/vtdataroot"}}' $image` - sudo cp newrelic_start*.sh $vtdataroot - fi - sudo docker exec $image apt-get update - sudo docker exec $image apt-get install sudo -y - sudo docker exec $image apt-get install procps -y - sudo docker exec $image bash /vt/vtdataroot/newrelic_start_agent.sh $NEWRELIC_LICENSE_KEY -done diff --git a/examples/kubernetes/newrelic_start_agent.sh b/examples/kubernetes/newrelic_start_agent.sh deleted file mode 100755 index 99b28d1f77f..00000000000 --- a/examples/kubernetes/newrelic_start_agent.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -NEWRELIC_LICENSE_KEY=$1 - -sudo sh -c 'echo deb http://apt.newrelic.com/debian/ newrelic non-free >> /etc/apt/sources.list.d/newrelic.list' -wget -O- https://download.newrelic.com/548C16BF.gpg | sudo apt-key add - -sudo apt-get update -sudo apt-get install newrelic-sysmond -sudo nrsysmond-config --set license_key=$NEWRELIC_LICENSE_KEY -sudo /etc/init.d/newrelic-sysmond start diff --git a/examples/kubernetes/newrelic_start_mysql_plugin.sh b/examples/kubernetes/newrelic_start_mysql_plugin.sh deleted file mode 100755 index 30ab0337401..00000000000 --- a/examples/kubernetes/newrelic_start_mysql_plugin.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -NEWRELIC_LICENSE_KEY=$1 -AGENT_NAME=$2 - -export TERM=screen - -apt-get install openjdk-7-jdk -y - -yes | LICENSE_KEY=$NEWRELIC_LICENSE_KEY bash -c "$(curl -sSL https://download.newrelic.com/npi/release/install-npi-linux-debian-x64.sh)" - -vttablet_dir=`ls /vt/vtdataroot | grep -o vt_.* | tr '\r' ' ' | xargs` -mysql_sock=/vt/vtdataroot/${vttablet_dir}/mysql.sock -mysql -S $mysql_sock -u vt_dba -e "UPDATE mysql.user SET Password=PASSWORD('password') WHERE User='root'; FLUSH PRIVILEGES;" -mysql -S $mysql_sock -u vt_dba -e "create user newrelic@localhost identified by 'password'" -mysql -S $mysql_sock -u vt_dba -e "create user newrelic@127.0.0.1 identified by 'password'" -mysql -S $mysql_sock -u root --password=password -e "grant all on vt_test_keyspace.* to newrelic@localhost" -mysql -S $mysql_sock -u root --password=password -e "grant all on vt_test_keyspace.* to newrelic@127.0.0.1" -mysql -S $mysql_sock -u vt_dba -e "flush privileges;" - - -cd ~/newrelic-npi -./npi set user root -./npi config set license_key=$NEWRELIC_LICENSE_KEY -./npi fetch com.newrelic.plugins.mysql.instance -y -./npi prepare com.newrelic.plugins.mysql.instance -n - -echo "{ - \"agents\": [ - { - \"name\" : \"$AGENT_NAME\", - \"host\" : \"localhost\", - \"metrics\" : \"status,newrelic\", - \"user\" : \"root\", - \"passwd\" : \"password\", - \"properties\": \"mysql?socket=$mysql_sock\" - } - ] -}" > ~/newrelic-npi/plugins/com.newrelic.plugins.mysql.instance/newrelic_mysql_plugin-2.0.0/config/plugin.json - -sleep 10 -./npi add-service com.newrelic.plugins.mysql.instance --start -sleep 10 -./npi start com.newrelic.plugins.mysql.instance diff --git a/examples/kubernetes/orchestrator-down.sh b/examples/kubernetes/orchestrator-down.sh deleted file mode 100755 index 7648583d646..00000000000 --- a/examples/kubernetes/orchestrator-down.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops orchestrator. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Stopping orchestrator replicationcontroller..." -$KUBECTL $KUBECTL_OPTIONS delete replicationcontroller orchestrator - -echo "Deleting orchestrator service..." -$KUBECTL $KUBECTL_OPTIONS delete service orchestrator - -echo "Deleting orchestrator configmap..." -$KUBECTL $KUBECTL_OPTIONS delete configmap orchestrator-conf diff --git a/examples/kubernetes/orchestrator-template.yaml b/examples/kubernetes/orchestrator-template.yaml deleted file mode 100644 index 918aaaafffe..00000000000 --- a/examples/kubernetes/orchestrator-template.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# Orchestrator service -apiVersion: v1 -kind: Service -metadata: - name: orchestrator - labels: - component: orchestrator - app: vitess -spec: - ports: - - port: 80 - targetPort: 3000 - selector: - component: orchestrator - app: vitess ---- -# Orchestrator replication controller -apiVersion: v1 -kind: ReplicationController -metadata: - name: orchestrator -spec: - replicas: 1 - template: - metadata: - labels: - component: orchestrator - app: vitess - spec: - containers: - - name: orchestrator - image: vitess/orchestrator - livenessProbe: - httpGet: - path: / - port: 3000 - initialDelaySeconds: 300 - timeoutSeconds: 30 - resources: - limits: - memory: "512Mi" - cpu: "100m" - volumeMounts: - - mountPath: /orc/conf - name: config - - name: mysql - image: vitess/orchestrator - resources: - limits: - memory: "512Mi" - cpu: "100m" - command: ["mysqld"] - volumes: - - name: config - configMap: - name: orchestrator-conf diff --git a/examples/kubernetes/orchestrator-up.sh b/examples/kubernetes/orchestrator-up.sh deleted file mode 100755 index af9544bc12c..00000000000 --- a/examples/kubernetes/orchestrator-up.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that starts orchestrator. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Creating orchestrator service, configmap, and replicationcontroller..." -sed_script="" -for var in service_type; do - sed_script+="s,{{$var}},${!var},g;" -done - -# Create configmap from orchestrator docker config file -$KUBECTL $KUBECTL_OPTIONS create configmap orchestrator-conf --from-file="${script_root}/../../docker/orchestrator/orchestrator.conf.json" - -cat orchestrator-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - -echo -echo "To access orchestrator web UI, start kubectl proxy in another terminal:" -echo " kubectl proxy --port=8001" -echo "Then visit http://localhost:8001/api/v1/proxy/namespaces/$VITESS_NAME/services/orchestrator/" diff --git a/examples/kubernetes/sharded-vttablet-down.sh b/examples/kubernetes/sharded-vttablet-down.sh deleted file mode 100755 index 9c921dfcb85..00000000000 --- a/examples/kubernetes/sharded-vttablet-down.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that tears down the vttablet deployment -# created by sharded-vttablet-up.sh. - -SHARDS='-80,80-' -UID_BASE=200 - -source ./vttablet-down.sh diff --git a/examples/kubernetes/sharded-vttablet-up.sh b/examples/kubernetes/sharded-vttablet-up.sh deleted file mode 100755 index 507abfd37bb..00000000000 --- a/examples/kubernetes/sharded-vttablet-up.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that uses vttablet-up.sh with extra params -# to create a vttablet deployment with 2 shards. - -SHARDS='-80,80-' -UID_BASE=200 - -source ./vttablet-up.sh diff --git a/examples/kubernetes/sharded-vtworker.sh b/examples/kubernetes/sharded-vtworker.sh deleted file mode 100755 index 782c8e1c0cb..00000000000 --- a/examples/kubernetes/sharded-vtworker.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that runs vtworker. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -cell=test -port=15003 -vtworker_command="$*" - -# Expand template variables -sed_script="" -for var in vitess_image cell port vtworker_command; do - sed_script+="s,{{$var}},${!var},g;" -done - -# Instantiate template and send to kubectl. -echo "Creating vtworker pod in cell $cell..." -cat vtworker-pod-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - -set +e - -# Wait for vtworker pod to show up. -until [ $($KUBECTL $KUBECTL_OPTIONS get pod -o template --template '{{.status.phase}}' vtworker 2> /dev/null) = "Running" ]; do - echo "Waiting for vtworker pod to be created..." - sleep 1 -done - -echo "Following vtworker logs until termination..." -$KUBECTL $KUBECTL_OPTIONS logs -f vtworker - -# Get vtworker exit code. Wait for complete shutdown. -# (Although logs -f exited, the pod isn't fully shutdown yet and the exit code is not available yet.) -until [ $($KUBECTL $KUBECTL_OPTIONS get pod -o template --template '{{.status.phase}}' vtworker 2> /dev/null) != "Running" ]; do - echo "Waiting for vtworker pod to shutdown completely..." - sleep 1 -done -exit_code=$($KUBECTL $KUBECTL_OPTIONS get -o template --template '{{(index .status.containerStatuses 0).state.terminated.exitCode}}' pods vtworker) - -echo "Deleting vtworker pod..." -$KUBECTL $KUBECTL_OPTIONS delete pod vtworker - -if [ "$exit_code" != "0" ]; then - echo - echo "ERROR: vtworker did not run successfully (return value: $exit_code). Please check the error log above and try to run it again." - exit 1 -fi diff --git a/examples/kubernetes/vitess-down.sh b/examples/kubernetes/vitess-down.sh deleted file mode 100755 index 5e26c931234..00000000000 --- a/examples/kubernetes/vitess-down.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -SHARDS=${SHARDS:-'-80,80-'} -TABLETS_PER_SHARD=${TABLETS_PER_SHARD:-3} -CELLS=${CELLS:-'test'} -TEST_MODE=${TEST_MODE:-'0'} -VITESS_NAME=${VITESS_NAME:-'vitess'} - -export VITESS_NAME=$VITESS_NAME - -./vtgate-down.sh -SHARDS=$SHARDS CELLS=$CELLS TABLETS_PER_SHARD=$TABLETS_PER_SHARD ./vttablet-down.sh -./vtctld-down.sh -./etcd-down.sh - -if [ $TEST_MODE -gt 0 ]; then - gcloud compute firewall-rules delete ${VITESS_NAME}-vtctld -q -fi - -for cell in `echo $CELLS | tr ',' ' '`; do - gcloud compute firewall-rules delete ${VITESS_NAME}-vtgate-$cell -q -done - -./namespace-down.sh diff --git a/examples/kubernetes/vitess-up.sh b/examples/kubernetes/vitess-up.sh deleted file mode 100755 index 58db653fc8b..00000000000 --- a/examples/kubernetes/vitess-up.sh +++ /dev/null @@ -1,236 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a fully functional vitess cluster. -# It performs the following steps: -# 1. Create etcd clusters -# 2. Create vtctld clusters -# 3. Forward vtctld port -# 4. Create vttablet clusters -# 5. Perform vtctl initialization: -# SetKeyspaceShardingInfo, Rebuild Keyspace, Reparent Shard, Apply Schema -# 6. Create vtgate clusters -# 7. Forward vtgate port - -# Customizable parameters -VITESS_NAME=${VITESS_NAME:-'vitess'} -SHARDS=${SHARDS:-'-80,80-'} -TABLETS_PER_SHARD=${TABLETS_PER_SHARD:-3} -RDONLY_COUNT=${RDONLY_COUNT:-0} -MAX_TASK_WAIT_RETRIES=${MAX_TASK_WAIT_RETRIES:-300} -MAX_VTTABLET_TOPO_WAIT_RETRIES=${MAX_VTTABLET_TOPO_WAIT_RETRIES:-180} -VTTABLET_TEMPLATE=${VTTABLET_TEMPLATE:-'vttablet-pod-benchmarking-template.yaml'} -VTGATE_TEMPLATE=${VTGATE_TEMPLATE:-'vtgate-controller-benchmarking-template.yaml'} -VTGATE_COUNT=${VTGATE_COUNT:-0} -VTDATAROOT_VOLUME=${VTDATAROOT_VOLUME:-''} -CELLS=${CELLS:-'test'} -KEYSPACE=${KEYSPACE:-'test_keyspace'} -TEST_MODE=${TEST_MODE:-'0'} - -cells=`echo $CELLS | tr ',' ' '` -num_cells=`echo $cells | wc -w` - -num_shards=`echo $SHARDS | tr "," " " | wc -w` -total_tablet_count=$(($num_shards*$TABLETS_PER_SHARD*$num_cells)) -vtgate_count=$VTGATE_COUNT -if [ $vtgate_count -eq 0 ]; then - vtgate_count=$(($total_tablet_count/$num_cells/4>3?$total_tablet_count/$num_cells/4:3)) -fi - -VTCTLD_SERVICE_TYPE=`[[ $TEST_MODE -gt 0 ]] && echo 'LoadBalancer' || echo 'ClusterIP'` - -# export for other scripts -export SHARDS=$SHARDS -export TABLETS_PER_SHARD=$TABLETS_PER_SHARD -export RDONLY_COUNT=$RDONLY_COUNT -export VTDATAROOT_VOLUME=$VTDATAROOT_VOLUME -export VTGATE_TEMPLATE=$VTGATE_TEMPLATE -export VTTABLET_TEMPLATE=$VTTABLET_TEMPLATE -export VTGATE_REPLICAS=$vtgate_count -export VTCTLD_SERVICE_TYPE=$VTCTLD_SERVICE_TYPE -export VITESS_NAME=$VITESS_NAME - -function update_spinner_value () { - spinner='-\|/' - cur_spinner=${spinner:$(($1%${#spinner})):1} -} - -function wait_for_running_tasks () { - # This function waits for pods to be in the "Running" state - # 1. task_name: Name that the desired task begins with - # 2. num_tasks: Number of tasks to wait for - # Returns: - # 0 if successful, -1 if timed out - task_name=$1 - num_tasks=$2 - counter=0 - - echo "Waiting for ${num_tasks}x $task_name to enter state Running" - - while [ $counter -lt $MAX_TASK_WAIT_RETRIES ]; do - # Get status column of pods with name starting with $task_name, - # count how many are in state Running - num_running=`$KUBECTL get pods --namespace=$VITESS_NAME | grep ^$task_name | grep Running | wc -l` - - echo -en "\r$task_name: $num_running out of $num_tasks in state Running..." - if [ $num_running -eq $num_tasks ] - then - echo Complete - return 0 - fi - update_spinner_value $counter - echo -n $cur_spinner - let counter=counter+1 - sleep 1 - done - echo Timed out - return -1 -} - -if [ -z "$GOPATH" ]; then - echo "ERROR: GOPATH undefined, can't obtain vtctlclient" - exit -1 -fi - -export KUBECTL='kubectl' -go get vitess.io/vitess/go/cmd/vtctlclient - -echo "****************************" -echo "*Creating vitess cluster: $VITESS_NAME" -echo "* Shards: $SHARDS" -echo "* Tablets per shard: $TABLETS_PER_SHARD" -echo "* Rdonly per shard: $RDONLY_COUNT" -echo "* VTGate count: $vtgate_count" -echo "* Cells: $cells" -echo "****************************" - -echo 'Running namespace-up.sh' && ./namespace-up.sh - -echo 'Running etcd-up.sh' && CELLS=$CELLS ./etcd-up.sh -wait_for_running_tasks etcd-global 3 -for cell in $cells; do - wait_for_running_tasks etcd-$cell 3 -done - -echo 'Running vtctld-up.sh' && TEST_MODE=$TEST_MODE ./vtctld-up.sh - -wait_for_running_tasks vtctld 1 -kvtctl="./kvtctl.sh" - -if [ $num_shards -gt 0 ] -then - echo Calling CreateKeyspace and SetKeyspaceShardingInfo - $kvtctl CreateKeyspace -force $KEYSPACE - $kvtctl SetKeyspaceShardingInfo -force $KEYSPACE keyspace_id uint64 -fi - -echo 'Running vttablet-up.sh' && CELLS=$CELLS ./vttablet-up.sh -echo 'Running vtgate-up.sh' && ./vtgate-up.sh -wait_for_running_tasks vttablet $total_tablet_count -wait_for_running_tasks vtgate $(($vtgate_count*$num_cells)) - - -echo Waiting for tablets to be visible in the topology -counter=0 -while [ $counter -lt $MAX_VTTABLET_TOPO_WAIT_RETRIES ]; do - num_tablets=0 - for cell in $cells; do - num_tablets=$(($num_tablets+`$kvtctl ListAllTablets $cell | grep $KEYSPACE | wc -l`)) - done - echo -en "\r$num_tablets out of $total_tablet_count in topology..." - if [ $num_tablets -eq $total_tablet_count ] - then - echo Complete - break - fi - update_spinner_value $counter - echo -n $cur_spinner - let counter=counter+1 - sleep 1 - if [ $counter -eq $MAX_VTTABLET_TOPO_WAIT_RETRIES ] - then - echo Timed out - fi -done - -echo -n Setting Keyspace Sharding Info... -$kvtctl SetKeyspaceShardingInfo -force $KEYSPACE keyspace_id uint64 -echo Done -echo -n Rebuilding Keyspace Graph... -$kvtctl RebuildKeyspaceGraph $KEYSPACE -echo Done -echo -n Reparenting... -shard_num=1 -master_cell=`echo $cells | awk '{print $1}'` -for shard in $(echo $SHARDS | tr "," " "); do - [[ $num_cells -gt 1 ]] && cell_id=01 || cell_id=00 - printf -v master_tablet_id '%s-%02d0000%02d00' $master_cell $cell_id $shard_num - $kvtctl InitShardMaster -force $KEYSPACE/$shard $master_tablet_id - let shard_num=shard_num+1 -done -echo Done -echo -n Applying Schema... -$kvtctl ApplySchema -sql "$(cat create_test_table.sql)" $KEYSPACE -echo Done - -if [ $TEST_MODE -gt 0 ]; then - echo Creating firewall rule for vtctld - vtctld_port=15000 - gcloud compute firewall-rules create ${VITESS_NAME}-vtctld --allow tcp:$vtctld_port - vtctld_ip='' - until [ $vtctld_ip ]; do - vtctld_ip=`$KUBECTL get -o template --template '{{if ge (len .status.loadBalancer) 1}}{{index (index .status.loadBalancer.ingress 0) "ip"}}{{end}}' service vtctld --namespace=$VITESS_NAME` - sleep 1 - done - vtctld_server="$vtctld_ip:$vtctld_port" -fi - -vtgate_servers='' -for cell in $cells; do - echo Creating firewall rule for vtgate in cell $cell - vtgate_port=15001 - gcloud compute firewall-rules create ${VITESS_NAME}-vtgate-$cell --allow tcp:$vtgate_port - vtgate_ip='' - until [ $vtgate_ip ]; do - vtgate_ip=`$KUBECTL get -o template --template '{{if ge (len .status.loadBalancer) 1}}{{index (index .status.loadBalancer.ingress 0) "ip"}}{{end}}' service vtgate-$cell --namespace=$VITESS_NAME` - sleep 1 - done - vtgate_servers+="vtgate-$cell: $vtgate_ip:$vtgate_port," -done - -if [ -n "$NEWRELIC_LICENSE_KEY" ]; then - echo Setting up Newrelic monitoring - i=1 - for nodename in `$KUBECTL get nodes --no-headers --namespace=$VITESS_NAME | awk '{print $1}'`; do - gcloud compute copy-files newrelic.sh $nodename:~/ - gcloud compute copy-files newrelic_start_agent.sh $nodename:~/ - gcloud compute copy-files newrelic_start_mysql_plugin.sh $nodename:~/ - gcloud compute ssh $nodename --command "bash -c '~/newrelic.sh ${NEWRELIC_LICENSE_KEY} ${VTDATAROOT}'" - let i=i+1 - done -fi - -echo "****************************" -echo "* Complete!" -if [ $TEST_MODE -gt 0 ]; then - echo "* vtctld: $vtctld_server" -else - echo "* Access the vtctld web UI by performing the following steps:" - echo "* $ kubectl proxy --port=8001" - echo "* Visit http://localhost:8001/api/v1/proxy/namespaces/default/services/vtctld:web/" -fi -echo $vtgate_servers | awk -F',' '{for(i=1;i- - mkdir -p $VTDATAROOT/tmp && - chown -R vitess /vt && - su -p -c "/vt/bin/vtctld - -cell {{cell}} - -workflow_manager_init - -workflow_manager_use_election - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -port 15000 - -grpc_port 15999 - -service_map 'grpc-vtctl' - -topo_implementation etcd2 - -topo_global_server_address http://etcd-global-client:2379 - -topo_global_root /global - {{backup_flags}} {{test_flags}}" vitess - volumes: - - name: syslog - hostPath: {path: /dev/log} - - name: vtdataroot - emptyDir: {} - - name: certs - # Uncomment one of the following lines to configure the location - # of the root certificates file on your host OS. We need this so - # we can import it into the container OS. - # If your host OS is Fedora/RHEL: - #hostPath: {path: /etc/pki/tls/certs/ca-bundle.crt} - # If your host OS is Debian/Ubuntu/Gentoo: - hostPath: {path: /etc/ssl/certs/ca-certificates.crt} diff --git a/examples/kubernetes/vtctld-down.sh b/examples/kubernetes/vtctld-down.sh deleted file mode 100755 index a48e803aaf2..00000000000 --- a/examples/kubernetes/vtctld-down.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops vtctld. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Stopping vtctld replicationcontroller..." -$KUBECTL $KUBECTL_OPTIONS delete replicationcontroller vtctld - -echo "Deleting vtctld service..." -$KUBECTL $KUBECTL_OPTIONS delete service vtctld diff --git a/examples/kubernetes/vtctld-service-template.yaml b/examples/kubernetes/vtctld-service-template.yaml deleted file mode 100644 index 59327cb7d1a..00000000000 --- a/examples/kubernetes/vtctld-service-template.yaml +++ /dev/null @@ -1,17 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: vtctld - labels: - component: vtctld - app: vitess -spec: - ports: - - name: web - port: 15000 - - name: grpc - port: 15999 - selector: - component: vtctld - app: vitess - type: {{service_type}} diff --git a/examples/kubernetes/vtctld-up.sh b/examples/kubernetes/vtctld-up.sh deleted file mode 100755 index 1e65c6f587d..00000000000 --- a/examples/kubernetes/vtctld-up.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that starts vtctld. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -service_type=${VTCTLD_SERVICE_TYPE:-'ClusterIP'} -cell=(`echo $CELLS | tr ',' ' '`) # ref to cell will get first element -TEST_MODE=${TEST_MODE:-'0'} - -test_flags=`[[ $TEST_MODE -gt 0 ]] && echo '-enable_queries' || echo ''` - -echo "Creating vtctld $service_type service..." -sed_script="" -for var in service_type; do - sed_script+="s,{{$var}},${!var},g;" -done -cat vtctld-service-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - -echo "Creating vtctld replicationcontroller..." -# Expand template variables -sed_script="" -for var in vitess_image backup_flags test_flags cell; do - sed_script+="s,{{$var}},${!var},g;" -done - -# Instantiate template and send to kubectl. -cat vtctld-controller-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - -echo -echo "To access vtctld web UI, start kubectl proxy in another terminal:" -echo " kubectl proxy --port=8001" -echo "Then visit http://localhost:8001/api/v1/namespaces/$VITESS_NAME/services/vtctld:web/proxy" \ No newline at end of file diff --git a/examples/kubernetes/vtgate-controller-benchmarking-template.yaml b/examples/kubernetes/vtgate-controller-benchmarking-template.yaml deleted file mode 100644 index a5db7c585e8..00000000000 --- a/examples/kubernetes/vtgate-controller-benchmarking-template.yaml +++ /dev/null @@ -1,58 +0,0 @@ -kind: ReplicationController -apiVersion: v1 -metadata: - name: vtgate-{{cell}} -spec: - replicas: {{replicas}} - template: - metadata: - labels: - component: vtgate - cell: {{cell}} - app: vitess - spec: - containers: - - name: vtgate - image: vitess/root - livenessProbe: - httpGet: - path: /debug/vars - port: 15001 - initialDelaySeconds: 30 - timeoutSeconds: 5 - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - resources: - limits: - memory: "4Gi" - cpu: "6" - command: - - sh - - "-c" - - >- - mkdir -p $VTDATAROOT/tmp && - chown -R vitess /vt && - su -p -c "/vt/bin/vtgate - -topo_implementation etcd2 - -topo_global_server_address http://etcd-global-client:2379 - -topo_global_root /global - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -port 15001 - -grpc_port 15991 - -service_map 'grpc-vtgateservice' - -cells_to_watch {{cell}} - -tablet_types_to_wait MASTER,REPLICA - -gateway_implementation discoverygateway - -cell {{cell}}" vitess - env: - - name: GOMAXPROCS - value: "16" - volumes: - - name: syslog - hostPath: {path: /dev/log} - - name: vtdataroot - {{vtdataroot_volume}} diff --git a/examples/kubernetes/vtgate-controller-template.yaml b/examples/kubernetes/vtgate-controller-template.yaml deleted file mode 100644 index bbaac915b98..00000000000 --- a/examples/kubernetes/vtgate-controller-template.yaml +++ /dev/null @@ -1,58 +0,0 @@ -kind: ReplicationController -apiVersion: v1 -metadata: - name: vtgate-{{cell}} -spec: - replicas: {{replicas}} - template: - metadata: - labels: - component: vtgate - cell: {{cell}} - app: vitess - spec: - containers: - - name: vtgate - image: {{vitess_image}} - livenessProbe: - httpGet: - path: /debug/vars - port: 15001 - initialDelaySeconds: 30 - timeoutSeconds: 5 - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - resources: - limits: - memory: "512Mi" - cpu: "500m" - command: - - sh - - "-c" - - >- - mkdir -p $VTDATAROOT/tmp && - chown -R vitess /vt && - su -p -c "/vt/bin/vtgate - -topo_implementation etcd2 - -topo_global_server_address http://etcd-global-client:2379 - -topo_global_root /global - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -port 15001 - -grpc_port 15991 - -mysql_server_port {{mysql_server_port}} - -mysql_auth_server_static_string '{\"mysql_user\":{\"Password\":\"mysql_password\"}}' - -service_map 'grpc-vtgateservice' - -cells_to_watch {{cell}} - -tablet_types_to_wait MASTER,REPLICA - -gateway_implementation discoverygateway - -cell {{cell}}" vitess - volumes: - - name: syslog - hostPath: {path: /dev/log} - - name: vtdataroot - emptyDir: {} - diff --git a/examples/kubernetes/vtgate-down.sh b/examples/kubernetes/vtgate-down.sh deleted file mode 100755 index 96c29c9c33b..00000000000 --- a/examples/kubernetes/vtgate-down.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops vtgate. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -cells=`echo $CELLS | tr ',' ' '` - -for cell in $cells; do - echo "Stopping vtgate replicationcontroller in cell $cell..." - $KUBECTL $KUBECTL_OPTIONS delete replicationcontroller vtgate-$cell - - echo "Deleting vtgate service in cell $cell..." - $KUBECTL $KUBECTL_OPTIONS delete service vtgate-$cell -done - diff --git a/examples/kubernetes/vtgate-service-template.yaml b/examples/kubernetes/vtgate-service-template.yaml deleted file mode 100644 index 8a9f7450c99..00000000000 --- a/examples/kubernetes/vtgate-service-template.yaml +++ /dev/null @@ -1,21 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: vtgate-{{cell}} - labels: - component: vtgate - cell: {{cell}} - app: vitess -spec: - ports: - - name: web - port: 15001 - - name: grpc - port: 15991 - - name: mysql - port: {{mysql_server_port}} - selector: - component: vtgate - cell: {{cell}} - app: vitess - type: LoadBalancer diff --git a/examples/kubernetes/vtgate-up.sh b/examples/kubernetes/vtgate-up.sh deleted file mode 100755 index 66ef0667024..00000000000 --- a/examples/kubernetes/vtgate-up.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that starts a vtgate replicationcontroller. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -# NOTE: mysql server support is currently in beta -mysql_server_port=${MYSQL_SERVER_PORT:-3306} - -VTGATE_REPLICAS=${VTGATE_REPLICAS:-3} -VTDATAROOT_VOLUME=${VTDATAROOT_VOLUME:-''} -VTGATE_TEMPLATE=${VTGATE_TEMPLATE:-'vtgate-controller-template.yaml'} - -vtdataroot_volume='emptyDir: {}' -if [ -n "$VTDATAROOT_VOLUME" ]; then - vtdataroot_volume="hostPath: {path: ${VTDATAROOT_VOLUME}}" -fi - -replicas=$VTGATE_REPLICAS - -cells=`echo $CELLS | tr ',' ' '` -for cell in $cells; do - sed_script="" - for var in cell; do - sed_script+="s,{{$var}},${!var},g;" - done - - sed_script+="s,{{mysql_server_port}},$mysql_server_port,g;" - - echo "Creating vtgate service in cell $cell..." - cat vtgate-service-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - - sed_script="" - for var in vitess_image replicas vtdataroot_volume cell mysql_server_port; do - sed_script+="s,{{$var}},${!var},g;" - done - - echo "Creating vtgate replicationcontroller in cell $cell..." - cat $VTGATE_TEMPLATE | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - -done diff --git a/examples/kubernetes/vttablet-down.sh b/examples/kubernetes/vttablet-down.sh deleted file mode 100755 index e3fe9a67b03..00000000000 --- a/examples/kubernetes/vttablet-down.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that tears down the vttablet pods started by -# vttablet-up.sh. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Starting port forwarding to vtctld..." -start_vtctld_forward -trap stop_vtctld_forward EXIT -VTCTLD_ADDR="localhost:$vtctld_forward_port" - -# Delete the pods for all shards -keyspace='test_keyspace' -SHARDS=${SHARDS:-'0'} -TABLETS_PER_SHARD=${TABLETS_PER_SHARD:-5} -UID_BASE=${UID_BASE:-100} - -num_shards=`echo $SHARDS | tr "," " " | wc -w` -uid_base=$UID_BASE -cells=`echo $CELLS | tr ',' ' '` -num_cells=`echo $cells | wc -w` - -for shard in `seq 1 $num_shards`; do - [[ $num_cells -gt 1 ]] && cell_index=100000000 || cell_index=0 - for cell in $cells; do - for uid_index in `seq 0 $(($TABLETS_PER_SHARD-1))`; do - uid=$[$uid_base + $uid_index + $cell_index] - printf -v alias '%s-%010d' $cell $uid - - echo "Deleting pod for tablet $alias..." - $KUBECTL $KUBECTL_OPTIONS delete pod vttablet-$uid - done - let cell_index=cell_index+100000000 - done - let uid_base=uid_base+100 -done diff --git a/examples/kubernetes/vttablet-pod-benchmarking-template.yaml b/examples/kubernetes/vttablet-pod-benchmarking-template.yaml deleted file mode 100644 index f1cc59e7101..00000000000 --- a/examples/kubernetes/vttablet-pod-benchmarking-template.yaml +++ /dev/null @@ -1,95 +0,0 @@ -kind: Pod -apiVersion: v1 -metadata: - name: vttablet-{{uid}} - labels: - component: vttablet - keyspace: "{{keyspace}}" - shard: "{{shard_label}}" - tablet: "{{alias}}" - app: vitess -spec: - containers: - - name: vttablet - image: vitess/root - resources: - limits: - memory: "4Gi" - cpu: "2" - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - command: - - bash - - "-c" - - >- - set -e - - mysql_socket="$VTDATAROOT/{{tablet_subdir}}/mysql.sock" - - mkdir -p $VTDATAROOT/tmp - - chown -R vitess /vt - - while [ ! -e $mysql_socket ]; do - echo "[$(date)] waiting for $mysql_socket" ; - sleep 1 ; - done - - su -p -s /bin/bash -c "mysql -u vt_dba -S $mysql_socket - -e 'CREATE DATABASE IF NOT EXISTS vt_{{keyspace}}'" vitess - - su -p -s /bin/bash -c "/vt/bin/vttablet - -topo_implementation etcd2 - -topo_global_server_address http://etcd-global-client:2379 - -topo_global_root /global - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -port {{port}} - -grpc_port {{grpc_port}} - -service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' - -tablet-path {{alias}} - -tablet_hostname $(hostname -i) - -init_keyspace {{keyspace}} - -init_shard {{shard}} - -init_tablet_type {{tablet_type}} - -mysqlctl_socket $VTDATAROOT/mysqlctl.sock - -queryserver-config-transaction-cap 300 - -queryserver-config-schema-reload-time 1 - -queryserver-config-pool-size 100 - -enable_replication_reporter" vitess - env: - - name: GOMAXPROCS - value: "16" - - name: mysql - image: vitess/root - resources: - limits: - memory: "4Gi" - cpu: "2" - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - command: - - sh - - "-c" - - >- - mkdir -p $VTDATAROOT/tmp && - chown -R vitess /vt - - su -p -c "/vt/bin/mysqlctld - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -tablet_uid {{uid}} - -socket_file $VTDATAROOT/mysqlctl.sock - -init_db_sql_file $VTROOT/config/init_db.sql" vitess - volumes: - - name: syslog - hostPath: {path: /dev/log} - - name: vtdataroot - {{vtdataroot_volume}} - diff --git a/examples/kubernetes/vttablet-pod-template.yaml b/examples/kubernetes/vttablet-pod-template.yaml deleted file mode 100644 index 6be80409cae..00000000000 --- a/examples/kubernetes/vttablet-pod-template.yaml +++ /dev/null @@ -1,109 +0,0 @@ -kind: Pod -apiVersion: v1 -metadata: - name: vttablet-{{uid}} - labels: - component: vttablet - keyspace: "{{keyspace}}" - shard: "{{shard_label}}" - tablet: "{{alias}}" - app: vitess -spec: - containers: - - name: vttablet - image: {{vitess_image}} - livenessProbe: - httpGet: - path: /debug/vars - port: {{port}} - initialDelaySeconds: 60 - timeoutSeconds: 10 - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - - name: certs - readOnly: true - # Mount root certs from the host OS into the location - # expected for our container OS (Debian): - mountPath: /etc/ssl/certs/ca-certificates.crt - resources: - limits: - memory: "1Gi" - cpu: "500m" - ports: - - name: web - containerPort: {{port}} - - name: grpc - containerPort: {{grpc_port}} - command: - - bash - - "-c" - - >- - set -e - - mkdir -p $VTDATAROOT/tmp - - chown -R vitess /vt - - su -p -s /bin/bash -c "/vt/bin/vttablet - -binlog_use_v3_resharding_mode - -topo_implementation etcd2 - -topo_global_server_address http://etcd-global-client:2379 - -topo_global_root /global - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -port {{port}} - -grpc_port {{grpc_port}} - -service_map 'grpc-queryservice,grpc-tabletmanager,grpc-updatestream' - -tablet-path {{alias}} - -tablet_hostname $(hostname -i) - -init_keyspace {{keyspace}} - -init_shard {{shard}} - -init_tablet_type {{tablet_type}} - -health_check_interval 5s - -mysqlctl_socket $VTDATAROOT/mysqlctl.sock - -enable_semi_sync - -enable_replication_reporter - -orc_api_url http://orchestrator/api - -orc_discover_interval 5m - -restore_from_backup {{backup_flags}}" vitess - - name: mysql - image: {{vitess_image}} - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - resources: - limits: - memory: "1Gi" - cpu: "500m" - command: - - sh - - "-c" - - >- - mkdir -p $VTDATAROOT/tmp && - chown -R vitess /vt - - su -p -c "/vt/bin/mysqlctld - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -tablet_uid {{uid}} - -socket_file $VTDATAROOT/mysqlctl.sock - -init_db_sql_file $VTROOT/config/init_db.sql" vitess - volumes: - - name: syslog - hostPath: {path: /dev/log} - - name: vtdataroot - {{vtdataroot_volume}} - - name: certs - # Uncomment one of the following lines to configure the location - # of the root certificates file on your host OS. We need this so - # we can import it into the container OS. - # If your host OS is Fedora/RHEL: - #hostPath: {path: /etc/pki/tls/certs/ca-bundle.crt} - # If your host OS is Debian/Ubuntu/Gentoo: - hostPath: {path: /etc/ssl/certs/ca-certificates.crt} - diff --git a/examples/kubernetes/vttablet-up.sh b/examples/kubernetes/vttablet-up.sh deleted file mode 100755 index 8fbdcfc5ff3..00000000000 --- a/examples/kubernetes/vttablet-up.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that creates a vttablet deployment. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -# Create the pods for shard-0 -keyspace=${KEYSPACE:-'test_keyspace'} -SHARDS=${SHARDS:-'0'} -TABLETS_PER_SHARD=${TABLETS_PER_SHARD:-5} -port=15002 -grpc_port=16002 -UID_BASE=${UID_BASE:-100} -VTTABLET_TEMPLATE=${VTTABLET_TEMPLATE:-'vttablet-pod-template.yaml'} -VTDATAROOT_VOLUME=${VTDATAROOT_VOLUME:-''} -RDONLY_COUNT=${RDONLY_COUNT:-2} - -vtdataroot_volume='emptyDir: {}' -if [ -n "$VTDATAROOT_VOLUME" ]; then - vtdataroot_volume="hostPath: {path: ${VTDATAROOT_VOLUME}}" -fi - -uid_base=$UID_BASE -indices=${TASKS:-`seq 0 $(($TABLETS_PER_SHARD-1))`} -cells=`echo $CELLS | tr ',' ' '` -num_cells=`echo $cells | wc -w` - -for shard in $(echo $SHARDS | tr "," " "); do - [[ $num_cells -gt 1 ]] && cell_index=100000000 || cell_index=0 - for cell in $cells; do - echo "Creating $keyspace.shard-$shard pods in cell $cell..." - for uid_index in $indices; do - uid=$[$uid_base + $uid_index + $cell_index] - printf -v alias '%s-%010d' $cell $uid - printf -v tablet_subdir 'vt_%010d' $uid - - echo "Creating pod for tablet $alias..." - - # Add xx to beginning or end if there is a dash. K8s does not allow for - # leading or trailing dashes for labels - shard_label=`echo $shard | sed s'/[-]$/-xx/' | sed s'/^-/xx-/'` - - tablet_type=replica - if [ $uid_index -gt $(($TABLETS_PER_SHARD-$RDONLY_COUNT-1)) ]; then - tablet_type=rdonly - fi - - # Expand template variables - sed_script="" - for var in vitess_image alias cell uid keyspace shard shard_label port grpc_port tablet_subdir vtdataroot_volume tablet_type backup_flags; do - sed_script+="s,{{$var}},${!var},g;" - done - - # Instantiate template and send to kubectl. - cat $VTTABLET_TEMPLATE | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - done - let cell_index=cell_index+100000000 - done - let uid_base=uid_base+100 -done diff --git a/examples/kubernetes/vtworker-controller-interactive-template.yaml b/examples/kubernetes/vtworker-controller-interactive-template.yaml deleted file mode 100644 index b3f86ae191e..00000000000 --- a/examples/kubernetes/vtworker-controller-interactive-template.yaml +++ /dev/null @@ -1,42 +0,0 @@ -kind: ReplicationController -apiVersion: v1 -metadata: - name: vtworker -spec: - replicas: 1 - template: - metadata: - labels: - component: vtworker - app: vitess - spec: - containers: - - name: vtworker - image: {{vitess_image}} - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - command: - - sh - - "-c" - - >- - mkdir -p $VTDATAROOT/tmp && - chown -R vitess /vt && - su -p -c "/vt/bin/vtworker - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -port {{port}} - -grpc_port {{grpc_port}} - -service_map \"grpc-vtworker\" - -topo_implementation etcd2 - -topo_global_server_address http://etcd-global-client:2379 - -topo_global_root /global - -cell {{cell}} - -use_v3_resharding_mode" vitess - volumes: - - name: syslog - hostPath: {path: /dev/log} - - name: vtdataroot - emptyDir: {} diff --git a/examples/kubernetes/vtworker-down.sh b/examples/kubernetes/vtworker-down.sh deleted file mode 100755 index d1ab7330b49..00000000000 --- a/examples/kubernetes/vtworker-down.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is an example script that stops vtworker. - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -echo "Stopping vtworker replicationcontroller..." -$KUBECTL $KUBECTL_OPTIONS delete replicationcontroller vtworker - -echo "Deleting vtworker service..." -$KUBECTL $KUBECTL_OPTIONS delete service vtworker diff --git a/examples/kubernetes/vtworker-pod-template.yaml b/examples/kubernetes/vtworker-pod-template.yaml deleted file mode 100644 index 08ab4d68d36..00000000000 --- a/examples/kubernetes/vtworker-pod-template.yaml +++ /dev/null @@ -1,39 +0,0 @@ -kind: Pod -apiVersion: v1 -metadata: - name: vtworker - labels: - component: vtworker - app: vitess -spec: - containers: - - name: vtworker - image: {{vitess_image}} - volumeMounts: - - name: syslog - mountPath: /dev/log - - name: vtdataroot - mountPath: /vt/vtdataroot - command: - - sh - - "-c" - - >- - mkdir -p $VTDATAROOT/tmp && - chown -R vitess /vt && - su -p -c "/vt/bin/vtworker - -log_dir $VTDATAROOT/tmp - -alsologtostderr - -port {{port}} - -topo_implementation etcd2 - -topo_global_server_address http://etcd-global-client:2379 - -topo_global_root /global - -cell {{cell}} - -use_v3_resharding_mode - {{vtworker_command}}" vitess - restartPolicy: Never - volumes: - - name: syslog - hostPath: {path: /dev/log} - - name: vtdataroot - emptyDir: {} - diff --git a/examples/kubernetes/vtworker-service-template.yaml b/examples/kubernetes/vtworker-service-template.yaml deleted file mode 100644 index 1fbb7403c44..00000000000 --- a/examples/kubernetes/vtworker-service-template.yaml +++ /dev/null @@ -1,17 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: vtworker - labels: - component: vtworker - app: vitess -spec: - ports: - - name: web - port: {{port}} - - name: grpc - port: {{grpc_port}} - selector: - component: vtworker - app: vitess - type: {{service_type}} diff --git a/examples/kubernetes/vtworker-up.sh b/examples/kubernetes/vtworker-up.sh deleted file mode 100755 index d5cc3e32ccb..00000000000 --- a/examples/kubernetes/vtworker-up.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -script_root=`dirname "${BASH_SOURCE}"` -source $script_root/env.sh - -cell=(`echo $CELLS | tr ',' ' '`) # ref to cell will get first element -port=15032 -grpc_port=15033 - -sed_script="" -for var in vitess_image cell port grpc_port; do - sed_script+="s,{{$var}},${!var},g;" -done - -echo "Creating vtworker pod in cell $cell..." -cat vtworker-controller-interactive-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - - -set +e - -service_type='LoadBalancer' -echo "Creating vtworker $service_type service..." -sed_script="" -for var in service_type port grpc_port; do - sed_script+="s,{{$var}},${!var},g;" -done -cat vtworker-service-template.yaml | sed -e "$sed_script" | $KUBECTL $KUBECTL_OPTIONS create -f - From a7b828cb2791a89c8f3e77b5c544063b74de9a8a Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 27 Feb 2020 19:04:09 -0800 Subject: [PATCH 182/825] vdiff: address review comments Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/engine/merge_sort.go | 13 +++++++------ go/vt/vtgate/engine/merge_sort_test.go | 2 +- go/vt/vtgate/engine/route.go | 2 +- go/vt/vtgate/engine/shard_route.go | 2 +- go/vt/wrangler/vdiff.go | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/go/vt/vtgate/engine/merge_sort.go b/go/vt/vtgate/engine/merge_sort.go index 5c9dde9e3ff..b95190cc7e8 100644 --- a/go/vt/vtgate/engine/merge_sort.go +++ b/go/vt/vtgate/engine/merge_sort.go @@ -29,9 +29,9 @@ import ( "vitess.io/vitess/go/vt/vterrors" ) -// StreamExecuter is a subset of Primitive that MergeSort +// StreamExecutor is a subset of Primitive that MergeSort // requires its inputs to satisfy. -type StreamExecuter interface { +type StreamExecutor interface { StreamExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantields bool, callback func(*sqltypes.Result) error) error } @@ -43,10 +43,11 @@ var _ Primitive = (*MergeSort)(nil) // a new value is added to it from the stream that was the source of the value that // was pulled out. Since the input streams are sorted the same way that the heap is // sorted, this guarantees that the merged stream will also be sorted the same way. -// MergeSort is not functionally complete and should not be used as a Primitive -// in a plan. +// MergeSort only supports the StreamExecute function of a Primitive. So, it cannot +// be used like other Primitives in VTGate. However, it satisfies the Primitive API +// so that vdiff can use it. In that situation, only StreamExecute is used. type MergeSort struct { - Primitives []StreamExecuter + Primitives []StreamExecutor OrderBy []OrderbyParams noInputs } @@ -168,7 +169,7 @@ type streamHandle struct { } // runOnestream starts a streaming query on one shard, and returns a streamHandle for it. -func runOneStream(vcursor VCursor, input StreamExecuter, bindVars map[string]*querypb.BindVariable, wantfields bool) *streamHandle { +func runOneStream(vcursor VCursor, input StreamExecutor, bindVars map[string]*querypb.BindVariable, wantfields bool) *streamHandle { handle := &streamHandle{ fields: make(chan []*querypb.Field, 1), row: make(chan []sqltypes.Value, 10), diff --git a/go/vt/vtgate/engine/merge_sort_test.go b/go/vt/vtgate/engine/merge_sort_test.go index d5bfc79384b..0b35a1e1a67 100644 --- a/go/vt/vtgate/engine/merge_sort_test.go +++ b/go/vt/vtgate/engine/merge_sort_test.go @@ -277,7 +277,7 @@ func TestMergeSortDataFailures(t *testing.T) { } func testMergeSort(shardResults []*shardResult, orderBy []OrderbyParams, callback func(qr *sqltypes.Result) error) error { - prims := make([]StreamExecuter, 0, len(shardResults)) + prims := make([]StreamExecutor, 0, len(shardResults)) for _, sr := range shardResults { prims = append(prims, sr) } diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go index 48aef3a2006..4a849293e32 100644 --- a/go/vt/vtgate/engine/route.go +++ b/go/vt/vtgate/engine/route.go @@ -335,7 +335,7 @@ func (route *Route) StreamExecute(vcursor VCursor, bindVars map[string]*querypb. } // There is an order by. We have to merge-sort. - prims := make([]StreamExecuter, 0, len(rss)) + prims := make([]StreamExecutor, 0, len(rss)) for i, rs := range rss { prims = append(prims, &shardRoute{ query: route.Query, diff --git a/go/vt/vtgate/engine/shard_route.go b/go/vt/vtgate/engine/shard_route.go index b2b31c0fb33..1d695489c03 100644 --- a/go/vt/vtgate/engine/shard_route.go +++ b/go/vt/vtgate/engine/shard_route.go @@ -22,7 +22,7 @@ import ( "vitess.io/vitess/go/vt/srvtopo" ) -var _ StreamExecuter = (*shardRoute)(nil) +var _ StreamExecutor = (*shardRoute)(nil) // shardRoute is an internal primitive used by Route // for performing merge sorts. diff --git a/go/vt/wrangler/vdiff.go b/go/vt/wrangler/vdiff.go index b5b9ed52198..a9f2a54a2b0 100644 --- a/go/vt/wrangler/vdiff.go +++ b/go/vt/wrangler/vdiff.go @@ -91,7 +91,7 @@ type tableDiffer struct { // shardStreamer streams rows from one shard. This works for // the source as well as the target. -// shardStreamer satisfies engine.StreamExecuter, and can be +// shardStreamer satisfies engine.StreamExecutor, and can be // added to Primitives of engine.MergeSort. // shardStreamer is a member of vdiff, and gets reused by // every tableDiffer. A new result channel gets instantiated @@ -386,7 +386,7 @@ func (df *vdiff) buildTablePlan(table *tabletmanagerdatapb.TableDefinition, quer // newMergeSorter creates an engine.MergeSort based on the shard streamers and pk columns. func newMergeSorter(participants map[string]*shardStreamer, comparePKs []int) *engine.MergeSort { - prims := make([]engine.StreamExecuter, 0, len(participants)) + prims := make([]engine.StreamExecutor, 0, len(participants)) for _, participant := range participants { prims = append(prims, participant) } From 202024d1ba6152fbf008ff28cf4f60a1708de39c Mon Sep 17 00:00:00 2001 From: Alex Sladkov Date: Thu, 27 Feb 2020 14:33:51 +0300 Subject: [PATCH 183/825] Support for LIMIT clause in GROUP_CONCAT() Introduced in MariaDB 10.3 https://jira.mariadb.org/browse/MDEV-11297 Signed-off-by: Alex Sladkov --- go/vt/sqlparser/ast.go | 3 +- go/vt/sqlparser/parse_test.go | 4 + go/vt/sqlparser/sql.go | 2925 +++++++++++++++++---------------- go/vt/sqlparser/sql.y | 4 +- 4 files changed, 1472 insertions(+), 1464 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 31750b0a5fb..0777d9c3a4f 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -638,6 +638,7 @@ type ( Exprs SelectExprs OrderBy OrderBy Separator string + Limit *Limit } // ValuesFuncExpr represents a function call. @@ -1550,7 +1551,7 @@ func (node *FuncExpr) Format(buf *TrackedBuffer) { // Format formats the node func (node *GroupConcatExpr) Format(buf *TrackedBuffer) { - buf.Myprintf("group_concat(%s%v%v%s)", node.Distinct, node.Exprs, node.OrderBy, node.Separator) + buf.Myprintf("group_concat(%s%v%v%s%v)", node.Distinct, node.Exprs, node.OrderBy, node.Separator, node.Limit) } // Format formats the node. diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 586963cc122..b9cc77fd443 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1453,6 +1453,10 @@ var ( input: "select name, group_concat(score) from t group by name", }, { input: "select name, group_concat(distinct id, score order by id desc separator ':') from t group by name", + }, { + input: "select name, group_concat(distinct id, score order by id desc separator ':' limit 1) from t group by name", + }, { + input: "select name, group_concat(distinct id, score order by id desc separator ':' limit 10, 2) from t group by name", }, { input: "select * from t partition (p0)", }, { diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index 4dd681a9d43..a7d1ea2a5db 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -864,488 +864,319 @@ var yyExca = [...]int{ -1, 1408, 5, 29, -2, 584, - -1, 1487, + -1, 1488, 5, 30, -2, 585, } const yyPrivate = 57344 -const yyLast = 16957 +const yyLast = 16982 var yyAct = [...]int{ - 323, 1521, 1511, 1317, 1475, 1206, 1114, 1375, 653, 966, - 1421, 353, 1388, 1132, 1257, 340, 939, 301, 1258, 1291, - 57, 962, 1115, 1254, 1009, 975, 1159, 965, 1264, 1270, - 1229, 1072, 81, 989, 937, 562, 266, 396, 551, 266, - 884, 815, 877, 888, 329, 799, 979, 1185, 713, 1176, - 327, 941, 926, 854, 906, 1138, 584, 590, 712, 919, - 652, 3, 390, 385, 694, 1005, 596, 266, 81, 693, - 520, 325, 266, 605, 266, 292, 310, 382, 667, 702, - 56, 1514, 1498, 1509, 387, 668, 1028, 1485, 540, 61, - 1506, 995, 1318, 1497, 1484, 1246, 1347, 525, 1285, 253, - 1027, 956, 251, 393, 255, 555, 298, 314, 1147, 354, - 51, 1146, 300, 578, 1148, 63, 64, 65, 66, 67, - 293, 294, 295, 296, 1286, 1287, 299, 957, 958, 1032, - 297, 1167, 261, 257, 258, 259, 1378, 1230, 1026, 1450, - 618, 617, 627, 628, 620, 621, 622, 623, 624, 625, - 626, 619, 988, 714, 629, 715, 996, 1208, 276, 1395, - 365, 51, 371, 372, 369, 370, 368, 367, 366, 306, - 1338, 557, 577, 559, 573, 1232, 373, 374, 574, 571, - 572, 1336, 291, 286, 566, 567, 576, 788, 1023, 1020, - 1021, 1210, 1019, 787, 785, 1508, 1505, 254, 1476, 1525, - 1468, 1205, 920, 541, 556, 558, 980, 1529, 527, 1234, - 1209, 1238, 255, 1233, 1211, 1231, 982, 1422, 252, 792, - 1236, 1430, 1133, 1135, 1030, 1033, 911, 789, 786, 1235, - 1424, 1202, 982, 982, 269, 776, 1280, 1204, 1279, 1278, - 523, 272, 1237, 1239, 530, 268, 521, 256, 963, 280, - 275, 260, 266, 1040, 1090, 1457, 1039, 266, 1087, 641, - 642, 1025, 1160, 266, 1358, 1215, 1143, 1100, 1066, 266, - 537, 826, 708, 609, 81, 547, 81, 81, 629, 81, - 1303, 81, 278, 1024, 952, 603, 602, 81, 285, 554, - 619, 996, 1250, 629, 816, 823, 1193, 820, 1423, 1134, - 352, 604, 604, 603, 602, 1483, 521, 1523, 553, 1451, - 1524, 1466, 1522, 981, 1439, 270, 1268, 81, 978, 976, - 604, 977, 1029, 1431, 1429, 592, 1191, 974, 980, 981, - 981, 1304, 79, 534, 1203, 535, 1201, 1031, 536, 519, - 716, 1248, 282, 273, 602, 283, 284, 289, 543, 544, - 545, 274, 277, 907, 271, 288, 287, 641, 642, 985, - 604, 641, 642, 861, 907, 986, 1097, 593, 395, 639, - 829, 830, 778, 580, 581, 1086, 817, 859, 860, 858, - 266, 266, 266, 561, 1165, 561, 561, 552, 561, 81, - 561, 1471, 70, 1192, 599, 81, 561, 594, 1197, 1194, - 1187, 1195, 1190, 1085, 1186, 1084, 1489, 1188, 1189, 622, - 623, 624, 625, 626, 619, 692, 51, 629, 1384, 603, - 602, 1196, 603, 602, 393, 697, 603, 602, 71, 1383, - 1180, 638, 1344, 878, 640, 879, 604, 1179, 250, 604, - 1063, 1064, 1065, 604, 54, 670, 672, 674, 676, 678, - 680, 681, 671, 673, 857, 677, 679, 1168, 682, 701, - 1491, 1467, 651, 706, 655, 656, 657, 658, 659, 660, - 661, 662, 663, 710, 666, 669, 669, 669, 675, 669, - 669, 675, 669, 683, 684, 685, 686, 687, 688, 1530, - 698, 618, 617, 627, 628, 620, 621, 622, 623, 624, - 625, 626, 619, 379, 380, 629, 1402, 618, 617, 627, - 628, 620, 621, 622, 623, 624, 625, 626, 619, 1381, - 266, 629, 1149, 1177, 1150, 81, 526, 582, 825, 1531, - 266, 266, 81, 81, 81, 1049, 804, 22, 266, 1427, - 1507, 266, 1493, 583, 266, 1073, 1427, 1479, 266, 1464, - 81, 1427, 583, 1427, 1458, 81, 81, 81, 266, 81, - 81, 1427, 1426, 844, 846, 847, 824, 81, 81, 845, - 1373, 1372, 1360, 583, 395, 1320, 395, 395, 1160, 395, - 1155, 395, 880, 603, 602, 801, 798, 395, 797, 803, - 1357, 583, 1310, 1309, 1306, 1307, 81, 305, 1306, 1305, - 604, 266, 779, 528, 529, 1079, 583, 81, 923, 583, - 831, 890, 583, 704, 777, 774, 549, 607, 793, 723, - 722, 320, 542, 533, 855, 620, 621, 622, 623, 624, - 625, 626, 619, 532, 561, 629, 704, 583, 24, 1436, - 1435, 561, 561, 561, 24, 851, 1255, 852, 1300, 1267, - 1267, 81, 1139, 1139, 983, 850, 705, 856, 707, 561, - 58, 1218, 1109, 890, 561, 561, 561, 1110, 561, 561, - 946, 833, 703, 1407, 1353, 1438, 561, 561, 923, 705, - 848, 703, 24, 1308, 81, 81, 1151, 54, 54, 395, - 955, 266, 1103, 54, 1499, 718, 923, 1267, 1102, 266, - 266, 1079, 1079, 266, 266, 703, 1079, 266, 266, 266, - 81, 897, 900, 881, 882, 709, 892, 908, 928, 931, - 932, 933, 929, 81, 930, 934, 827, 916, 1271, 1272, - 904, 54, 307, 922, 791, 1390, 990, 947, 1365, 1010, - 1296, 949, 1154, 697, 1006, 393, 801, 697, 1271, 1272, - 51, 697, 343, 342, 345, 346, 347, 348, 967, 923, - 945, 344, 349, 1001, 1000, 655, 991, 992, 993, 994, - 954, 950, 953, 1207, 1391, 1013, 1516, 266, 81, 839, - 81, 54, 1002, 1003, 1004, 1512, 266, 266, 266, 266, - 266, 1298, 266, 266, 970, 1274, 266, 81, 1011, 1255, - 1181, 583, 821, 928, 931, 932, 933, 929, 938, 930, - 934, 795, 698, 1277, 1126, 266, 698, 266, 266, 1127, - 832, 1124, 266, 1276, 1123, 395, 1125, 1122, 1503, 997, - 998, 999, 395, 395, 395, 1496, 1007, 1008, 618, 617, + 323, 1522, 1512, 1317, 1476, 653, 327, 1114, 1206, 1375, + 1388, 966, 1132, 1421, 1257, 340, 1291, 939, 1258, 551, + 57, 962, 353, 1115, 1254, 1009, 652, 3, 975, 1270, + 1264, 995, 81, 965, 301, 1229, 266, 396, 1159, 266, + 877, 888, 799, 884, 329, 815, 1138, 1185, 1072, 1176, + 713, 979, 941, 854, 584, 926, 1005, 520, 385, 590, + 937, 354, 51, 712, 390, 596, 310, 266, 81, 325, + 989, 919, 266, 694, 266, 702, 605, 387, 300, 667, + 56, 1515, 382, 1499, 1510, 1028, 906, 668, 1486, 693, + 61, 540, 1507, 1318, 1498, 1246, 1485, 1347, 525, 1027, + 261, 257, 258, 259, 956, 314, 555, 276, 1286, 1287, + 957, 958, 292, 51, 562, 1285, 63, 64, 65, 66, + 67, 306, 253, 1147, 298, 251, 1146, 255, 1032, 1148, + 573, 714, 286, 715, 574, 571, 572, 1026, 1450, 618, + 617, 627, 628, 620, 621, 622, 623, 624, 625, 626, + 619, 578, 297, 629, 1167, 988, 1230, 293, 294, 295, + 296, 1208, 365, 299, 371, 372, 369, 370, 368, 367, + 366, 1378, 557, 996, 559, 1338, 1336, 1395, 373, 374, + 291, 788, 393, 269, 566, 567, 576, 1023, 1020, 1021, + 272, 1019, 787, 1210, 1232, 785, 1509, 1506, 280, 275, + 1469, 980, 1477, 1205, 920, 556, 558, 541, 1530, 1430, + 577, 527, 1422, 255, 1209, 1211, 982, 982, 792, 260, + 254, 789, 887, 1030, 1033, 1424, 776, 786, 1234, 1202, + 1238, 278, 1233, 1526, 1231, 1204, 1280, 285, 1279, 1236, + 1278, 252, 982, 523, 530, 268, 1160, 521, 1235, 1133, + 1135, 256, 266, 1193, 641, 642, 1040, 266, 316, 1039, + 1025, 1237, 1239, 266, 270, 1458, 1358, 1090, 1215, 266, + 1087, 963, 1143, 1100, 81, 1066, 81, 81, 826, 81, + 708, 81, 1024, 1191, 609, 547, 629, 81, 823, 604, + 554, 282, 273, 1423, 283, 284, 289, 952, 1303, 619, + 274, 277, 629, 271, 288, 287, 816, 1484, 1451, 996, + 820, 1431, 1429, 981, 981, 1467, 1439, 81, 553, 978, + 976, 1029, 977, 907, 1268, 592, 1134, 521, 974, 980, + 70, 537, 1203, 593, 1201, 561, 1031, 561, 561, 981, + 561, 1524, 561, 1085, 1525, 1084, 1523, 716, 561, 1304, + 1192, 543, 544, 545, 1248, 1197, 1194, 1187, 1195, 1190, + 519, 1186, 603, 602, 1188, 1189, 71, 778, 51, 639, + 641, 642, 1165, 641, 642, 907, 861, 1097, 1196, 604, + 266, 266, 266, 638, 1472, 599, 640, 1490, 817, 81, + 859, 860, 858, 1086, 534, 81, 535, 552, 594, 536, 627, 628, 620, 621, 622, 623, 624, 625, 626, 619, - 395, 1214, 629, 1051, 1046, 395, 395, 395, 1501, 395, - 395, 1061, 851, 1128, 852, 932, 933, 395, 395, 1060, - 855, 597, 1054, 311, 312, 1172, 721, 887, 597, 889, - 891, 585, 550, 1164, 598, 1055, 1473, 561, 1056, 561, - 1351, 598, 1472, 586, 595, 1405, 835, 1162, 1156, 1386, - 1016, 794, 936, 856, 308, 309, 561, 607, 1059, 302, - 395, 1444, 1068, 1443, 303, 58, 1058, 1393, 1139, 575, - 1518, 1517, 1518, 1091, 266, 266, 266, 266, 266, 1088, - 814, 600, 1454, 1379, 822, 60, 266, 62, 55, 266, - 1, 1510, 1319, 266, 1387, 1022, 1474, 266, 1420, 1290, - 973, 883, 643, 644, 645, 646, 647, 648, 649, 650, - 1096, 1067, 964, 69, 518, 68, 81, 909, 697, 697, - 697, 697, 697, 1116, 1465, 1152, 972, 971, 1428, 1377, - 1111, 1140, 984, 697, 913, 914, 1166, 1129, 987, 1118, - 1119, 697, 1121, 1117, 1137, 1297, 1120, 1163, 1470, 892, - 729, 967, 727, 1141, 1144, 1142, 1161, 728, 726, 731, - 395, 730, 725, 279, 81, 81, 388, 1171, 935, 1173, - 1174, 1175, 717, 395, 1157, 1158, 1012, 601, 72, 1112, - 1113, 1200, 1199, 698, 698, 698, 698, 698, 1018, 819, - 569, 570, 281, 637, 81, 1057, 1145, 394, 938, 1262, - 1136, 1178, 828, 589, 1442, 1392, 698, 1095, 266, 664, - 905, 328, 843, 341, 338, 339, 834, 81, 1198, 1108, - 611, 326, 318, 696, 689, 927, 925, 924, 395, 383, - 395, 1273, 1269, 1169, 1170, 695, 1217, 1213, 1346, 1449, - 838, 26, 59, 313, 1184, 19, 18, 395, 17, 20, - 16, 1222, 1220, 15, 14, 538, 30, 1075, 1221, 21, - 13, 1076, 12, 81, 81, 1247, 1256, 1241, 11, 1081, - 1082, 1083, 1259, 1228, 561, 10, 1089, 395, 1240, 1092, - 1093, 9, 8, 7, 6, 1099, 1251, 81, 852, 1101, - 5, 4, 1104, 1105, 1106, 1107, 1054, 304, 23, 2, - 1275, 0, 81, 561, 81, 81, 0, 0, 0, 1282, - 1116, 0, 0, 1289, 1131, 1281, 0, 0, 0, 0, - 1266, 0, 0, 1261, 0, 0, 0, 0, 0, 1288, - 0, 1293, 266, 0, 1294, 1295, 0, 967, 0, 967, - 0, 1301, 1302, 0, 0, 0, 1284, 0, 0, 0, - 266, 0, 0, 0, 0, 0, 81, 0, 0, 81, - 81, 81, 266, 0, 0, 0, 81, 0, 0, 266, - 1260, 588, 51, 909, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 853, 0, 0, 862, 863, 864, 865, - 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, - 876, 1220, 1325, 1327, 0, 0, 0, 264, 0, 1334, - 290, 1312, 0, 697, 1331, 1332, 395, 1333, 0, 0, - 1335, 0, 1337, 1326, 1313, 0, 1315, 0, 0, 0, - 1352, 0, 0, 0, 0, 317, 0, 1361, 386, 81, - 1362, 912, 0, 264, 1350, 264, 0, 81, 1152, 0, - 1371, 0, 0, 560, 1226, 1227, 0, 0, 0, 0, - 0, 0, 81, 0, 1182, 395, 0, 0, 1116, 81, - 0, 0, 0, 0, 967, 0, 1374, 0, 698, 0, - 0, 0, 618, 617, 627, 628, 620, 621, 622, 623, - 624, 625, 626, 619, 395, 0, 629, 0, 0, 0, - 0, 0, 0, 0, 1389, 0, 1345, 0, 81, 81, - 0, 81, 0, 0, 0, 1259, 81, 395, 81, 81, - 81, 266, 1406, 1414, 81, 1415, 1417, 1418, 0, 1413, - 0, 0, 0, 0, 0, 0, 0, 1419, 1367, 1368, - 1369, 81, 266, 1425, 0, 1432, 0, 1380, 1440, 1382, - 0, 395, 1433, 0, 1434, 0, 0, 0, 0, 0, - 909, 0, 0, 1263, 1265, 0, 0, 1408, 0, 1259, - 0, 561, 1455, 0, 1394, 0, 0, 0, 81, 0, - 0, 1463, 0, 1462, 0, 0, 0, 1265, 0, 81, - 81, 0, 0, 0, 0, 0, 0, 1343, 1328, 1478, - 316, 1477, 395, 1481, 395, 1292, 1330, 0, 81, 0, - 0, 1486, 0, 1260, 0, 0, 1409, 1339, 1340, 266, - 1456, 0, 0, 264, 1389, 967, 0, 81, 264, 0, - 0, 1069, 1070, 1071, 264, 1495, 0, 1354, 1355, 1356, - 264, 1359, 0, 0, 0, 0, 1437, 0, 1500, 1502, - 81, 0, 0, 1349, 0, 1116, 1316, 1504, 1370, 1321, - 1322, 1323, 0, 1515, 0, 0, 395, 1260, 0, 51, - 1526, 699, 618, 617, 627, 628, 620, 621, 622, 623, - 624, 625, 626, 619, 893, 894, 629, 0, 899, 902, - 903, 618, 617, 627, 628, 620, 621, 622, 623, 624, - 625, 626, 619, 0, 0, 629, 0, 263, 0, 0, - 0, 0, 0, 915, 0, 917, 918, 0, 909, 0, - 0, 1342, 0, 1401, 0, 0, 0, 0, 0, 563, - 564, 0, 565, 0, 568, 0, 0, 0, 384, 395, - 579, 0, 0, 522, 1416, 524, 0, 1376, 0, 0, - 0, 264, 264, 264, 0, 0, 0, 0, 0, 0, - 0, 0, 395, 0, 0, 0, 0, 0, 1513, 395, - 0, 0, 0, 1445, 1446, 1447, 1448, 0, 0, 0, - 1452, 1453, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1459, 0, 1460, 1461, 618, 617, 627, 628, - 620, 621, 622, 623, 624, 625, 626, 619, 1410, 1411, - 629, 1412, 0, 0, 0, 0, 1376, 0, 1376, 1376, - 1376, 0, 0, 0, 1292, 0, 1482, 0, 0, 0, - 0, 0, 0, 1487, 0, 0, 0, 0, 0, 0, - 0, 1376, 0, 0, 0, 1224, 1225, 0, 0, 0, - 0, 1492, 0, 0, 0, 0, 0, 0, 0, 1242, - 1243, 0, 1244, 1245, 0, 0, 1062, 0, 0, 0, - 0, 0, 0, 0, 1252, 1253, 0, 0, 1469, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, - 395, 264, 0, 0, 0, 0, 1527, 1528, 0, 0, - 0, 264, 264, 587, 591, 909, 0, 0, 1488, 264, - 0, 0, 264, 1077, 1078, 264, 0, 0, 0, 800, - 610, 0, 0, 531, 0, 0, 0, 1494, 539, 264, - 0, 0, 1094, 0, 546, 0, 1299, 0, 0, 0, - 548, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1376, 0, 0, 0, 0, 654, 0, 0, 0, 0, - 0, 0, 0, 0, 665, 0, 0, 0, 0, 0, - 0, 0, 264, 0, 0, 0, 0, 0, 775, 0, - 0, 800, 0, 0, 0, 782, 783, 784, 0, 0, - 0, 0, 0, 0, 0, 1329, 0, 0, 0, 0, - 0, 0, 0, 802, 0, 0, 1341, 0, 806, 807, - 808, 0, 810, 811, 0, 0, 0, 0, 0, 0, - 812, 813, 0, 317, 0, 0, 0, 0, 317, 317, - 0, 0, 317, 317, 317, 0, 0, 0, 910, 617, + 580, 581, 629, 1531, 651, 1384, 655, 656, 657, 658, + 659, 660, 661, 662, 663, 697, 666, 669, 669, 669, + 675, 669, 669, 675, 669, 683, 684, 685, 686, 687, + 688, 692, 698, 1383, 603, 602, 670, 672, 674, 676, + 678, 680, 681, 1532, 671, 673, 1180, 677, 679, 706, + 682, 604, 603, 602, 701, 1179, 710, 617, 627, 628, + 620, 621, 622, 623, 624, 625, 626, 619, 352, 604, + 629, 618, 617, 627, 628, 620, 621, 622, 623, 624, + 625, 626, 619, 1168, 583, 629, 622, 623, 624, 625, + 626, 619, 526, 393, 629, 1063, 1064, 1065, 602, 825, + 79, 620, 621, 622, 623, 624, 625, 626, 619, 54, + 266, 629, 829, 830, 604, 81, 250, 1492, 1468, 857, + 266, 266, 81, 81, 81, 1073, 603, 602, 266, 985, + 878, 266, 879, 1250, 266, 986, 395, 824, 266, 1402, + 81, 1465, 1149, 604, 1150, 81, 81, 81, 266, 81, + 81, 587, 591, 1381, 603, 602, 1177, 81, 81, 1049, + 803, 603, 602, 804, 22, 1427, 1508, 1436, 610, 528, + 529, 604, 1494, 583, 1435, 801, 561, 1320, 604, 1427, + 1480, 379, 380, 561, 561, 561, 81, 844, 846, 847, + 1160, 266, 1155, 845, 1427, 583, 1300, 81, 1427, 1459, + 983, 561, 880, 654, 1427, 1426, 561, 561, 561, 798, + 561, 561, 665, 793, 1373, 1372, 320, 831, 561, 561, + 797, 911, 1360, 583, 305, 855, 343, 342, 345, 346, + 347, 348, 1357, 583, 1267, 344, 349, 852, 1310, 1309, + 890, 81, 850, 1306, 1307, 1306, 1305, 856, 1079, 583, + 1353, 833, 923, 583, 890, 583, 24, 897, 900, 779, + 777, 774, 549, 908, 723, 722, 704, 24, 542, 533, + 532, 1438, 892, 848, 81, 81, 928, 931, 932, 933, + 929, 266, 930, 934, 583, 1407, 1271, 1272, 923, 266, + 266, 1109, 51, 266, 266, 704, 1110, 266, 266, 266, + 81, 881, 882, 1308, 1139, 54, 54, 655, 1139, 705, + 58, 707, 1255, 81, 851, 1267, 54, 922, 1218, 904, + 916, 618, 617, 627, 628, 620, 621, 622, 623, 624, + 625, 626, 619, 697, 1151, 629, 801, 697, 705, 24, + 703, 697, 395, 923, 395, 395, 955, 395, 923, 395, + 938, 1079, 1267, 947, 698, 395, 950, 949, 698, 997, + 998, 999, 945, 1079, 953, 954, 1103, 266, 81, 1102, + 81, 970, 946, 1079, 703, 703, 266, 266, 266, 266, + 266, 709, 266, 266, 827, 607, 266, 81, 54, 1011, + 791, 1500, 1390, 991, 992, 993, 994, 1350, 307, 990, + 1365, 1010, 805, 1344, 1296, 266, 1154, 266, 266, 1002, + 1003, 1004, 266, 1006, 393, 1271, 1272, 1007, 1008, 1001, + 1000, 1207, 1391, 1013, 818, 1046, 1517, 967, 1513, 561, + 1298, 561, 1274, 1255, 1181, 618, 617, 627, 628, 620, + 621, 622, 623, 624, 625, 626, 619, 54, 561, 629, + 821, 841, 842, 795, 852, 1126, 1124, 395, 839, 1054, + 1127, 1125, 1128, 718, 932, 933, 1277, 1276, 1123, 893, + 894, 855, 1122, 899, 902, 903, 1056, 1055, 618, 617, 627, 628, 620, 621, 622, 623, 624, 625, 626, 619, - 0, 691, 629, 700, 0, 0, 0, 317, 317, 317, - 317, 0, 264, 0, 0, 0, 0, 0, 0, 0, - 264, 943, 0, 0, 264, 264, 1223, 0, 264, 951, - 800, 618, 617, 627, 628, 620, 621, 622, 623, 624, - 625, 626, 619, 0, 0, 629, 618, 617, 627, 628, - 620, 621, 622, 623, 624, 625, 626, 619, 1074, 0, - 629, 0, 0, 1396, 1397, 1398, 1399, 1400, 0, 0, - 0, 1403, 1404, 0, 0, 0, 0, 0, 618, 617, + 311, 312, 629, 856, 1504, 1497, 1214, 1051, 915, 1502, + 917, 918, 1068, 1067, 654, 1061, 1060, 895, 896, 1172, + 597, 597, 721, 585, 266, 266, 266, 266, 266, 1116, + 1474, 550, 582, 598, 598, 586, 266, 595, 1164, 266, + 1473, 851, 1405, 266, 1162, 1156, 1111, 266, 928, 931, + 932, 933, 929, 1351, 930, 934, 1386, 643, 644, 645, + 646, 647, 648, 649, 650, 892, 81, 1016, 697, 697, + 697, 697, 697, 794, 936, 961, 1059, 1152, 308, 309, + 302, 1112, 1113, 697, 1058, 698, 698, 698, 698, 698, + 1129, 697, 1096, 1444, 1141, 1137, 1142, 303, 1118, 1119, + 938, 1121, 1136, 395, 58, 1443, 1144, 1140, 698, 1393, + 395, 395, 395, 1117, 81, 81, 1120, 1171, 1161, 1173, + 1174, 1175, 1139, 1169, 1170, 575, 1519, 1518, 395, 1157, + 1158, 1091, 1088, 395, 395, 395, 814, 395, 395, 600, + 1519, 1455, 1379, 822, 81, 395, 395, 60, 62, 55, + 1, 1178, 1511, 1319, 1349, 1387, 1022, 1475, 266, 1420, + 1290, 1062, 973, 964, 69, 518, 68, 81, 1198, 1466, + 972, 971, 1428, 1377, 835, 984, 561, 1166, 987, 1297, + 967, 1163, 1471, 1052, 1053, 607, 591, 729, 395, 727, + 728, 1213, 618, 617, 627, 628, 620, 621, 622, 623, + 624, 625, 626, 619, 726, 561, 629, 731, 1077, 1078, + 730, 1221, 725, 81, 81, 1247, 1116, 1256, 1222, 279, + 388, 1228, 1241, 935, 717, 1012, 1240, 1094, 601, 883, + 72, 1184, 1200, 1259, 1199, 1018, 819, 81, 852, 1261, + 569, 570, 281, 1054, 637, 909, 1057, 1145, 394, 1080, + 1275, 1262, 81, 828, 81, 81, 589, 1442, 1392, 1282, + 1095, 1266, 913, 914, 664, 1289, 1098, 1281, 905, 328, + 843, 341, 1260, 338, 51, 339, 834, 1108, 611, 326, + 318, 1220, 266, 696, 1293, 1288, 689, 1284, 395, 927, + 925, 1301, 1302, 924, 383, 1273, 1294, 1295, 1269, 695, + 266, 395, 1217, 1346, 699, 1449, 81, 838, 26, 81, + 81, 81, 266, 59, 313, 1251, 81, 19, 18, 266, + 17, 20, 16, 15, 14, 832, 560, 538, 30, 21, + 13, 12, 11, 1325, 10, 9, 8, 7, 853, 6, + 263, 862, 863, 864, 865, 866, 867, 868, 869, 870, + 871, 872, 873, 874, 875, 876, 395, 5, 395, 4, + 1334, 304, 23, 697, 1326, 2, 967, 0, 967, 0, + 0, 384, 0, 0, 1116, 395, 522, 0, 524, 1327, + 698, 1352, 0, 0, 889, 891, 0, 1361, 0, 81, + 0, 1362, 0, 0, 0, 0, 912, 81, 1312, 0, + 1152, 1331, 1332, 0, 1333, 395, 0, 1335, 1345, 1337, + 0, 1313, 81, 1315, 0, 0, 0, 1371, 0, 81, + 0, 0, 0, 1223, 0, 0, 0, 0, 0, 0, + 1220, 0, 0, 0, 0, 0, 0, 1380, 0, 1382, + 1367, 1368, 1369, 618, 617, 627, 628, 620, 621, 622, + 623, 624, 625, 626, 619, 0, 1249, 629, 81, 81, + 0, 81, 0, 1374, 1394, 0, 81, 0, 81, 81, + 81, 266, 1406, 561, 81, 1414, 1259, 1415, 1417, 1418, + 0, 1401, 0, 1408, 0, 0, 0, 0, 1419, 0, + 1425, 81, 266, 0, 0, 1432, 1413, 0, 1283, 0, + 1440, 909, 1433, 967, 1434, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1260, 0, 0, 1409, 0, + 0, 0, 1456, 0, 0, 0, 0, 0, 81, 0, + 1259, 0, 1463, 1389, 1464, 0, 1457, 0, 0, 81, + 81, 0, 0, 0, 395, 0, 0, 0, 1437, 1478, + 1482, 1479, 0, 0, 0, 0, 531, 0, 0, 81, + 0, 539, 1116, 1487, 0, 0, 0, 546, 0, 1260, + 266, 51, 0, 548, 0, 0, 0, 0, 81, 0, + 0, 1343, 0, 0, 0, 0, 1069, 1070, 1071, 1496, + 0, 0, 1182, 395, 746, 0, 0, 0, 0, 1501, + 1503, 81, 563, 564, 0, 565, 0, 568, 0, 1348, + 1505, 0, 1075, 579, 1516, 0, 1076, 0, 1342, 654, + 0, 1527, 395, 0, 1081, 1082, 1083, 1363, 0, 0, + 1364, 1089, 0, 1366, 1092, 1093, 0, 0, 0, 0, + 1099, 0, 0, 0, 1101, 395, 588, 1104, 1105, 1106, + 1107, 0, 0, 1389, 967, 0, 618, 617, 627, 628, + 620, 621, 622, 623, 624, 625, 626, 619, 0, 1131, + 629, 1514, 0, 734, 0, 0, 0, 0, 0, 395, + 0, 0, 264, 0, 691, 290, 700, 0, 909, 0, + 0, 1263, 1265, 618, 617, 627, 628, 620, 621, 622, + 623, 624, 625, 626, 619, 0, 0, 629, 0, 0, + 317, 747, 0, 386, 0, 1265, 0, 0, 264, 0, + 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 395, 0, 395, 1292, 760, 763, 764, 765, 766, 767, + 768, 0, 769, 770, 771, 772, 773, 748, 749, 750, + 751, 732, 733, 761, 0, 735, 0, 736, 737, 738, + 739, 740, 741, 742, 743, 744, 745, 752, 753, 754, + 755, 756, 757, 758, 759, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1316, 0, 0, 1321, 1322, 1323, + 0, 0, 0, 0, 395, 0, 0, 0, 0, 0, + 1224, 1225, 1481, 654, 0, 0, 0, 0, 0, 1226, + 1227, 0, 0, 0, 1242, 1243, 0, 1244, 1245, 0, + 0, 0, 0, 0, 724, 762, 0, 0, 0, 1252, + 1253, 0, 0, 0, 780, 781, 0, 0, 0, 0, + 0, 1341, 790, 0, 0, 384, 909, 0, 796, 0, + 0, 775, 0, 0, 0, 0, 0, 0, 782, 783, + 784, 0, 809, 0, 0, 0, 0, 395, 0, 0, + 0, 0, 0, 0, 0, 1376, 802, 0, 0, 0, + 0, 806, 807, 808, 0, 810, 811, 0, 0, 0, + 395, 1299, 0, 812, 813, 0, 0, 395, 264, 0, + 0, 0, 0, 264, 0, 840, 0, 0, 0, 264, + 0, 0, 0, 0, 0, 264, 618, 617, 627, 628, + 620, 621, 622, 623, 624, 625, 626, 619, 0, 0, + 629, 0, 0, 0, 0, 0, 1410, 1411, 0, 1412, + 0, 0, 0, 0, 1376, 0, 1376, 1376, 1376, 0, + 1329, 0, 1292, 1328, 1074, 0, 0, 0, 0, 0, + 0, 1330, 0, 0, 0, 0, 0, 0, 0, 1376, + 0, 0, 1339, 1340, 618, 617, 627, 628, 620, 621, + 622, 623, 624, 625, 626, 619, 0, 0, 629, 0, + 0, 0, 1354, 1355, 1356, 921, 1359, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1470, 0, 948, 0, + 0, 0, 0, 1370, 0, 0, 0, 395, 395, 0, + 0, 0, 0, 0, 0, 0, 264, 264, 264, 0, + 0, 0, 0, 0, 909, 0, 0, 1489, 618, 617, 627, 628, 620, 621, 622, 623, 624, 625, 626, 619, - 0, 0, 629, 0, 0, 0, 0, 0, 264, 0, - 0, 0, 0, 0, 805, 0, 0, 264, 264, 264, - 264, 264, 0, 264, 264, 0, 0, 264, 0, 0, - 0, 0, 0, 0, 0, 0, 818, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 264, 0, 1047, 1048, - 0, 724, 0, 264, 0, 0, 0, 0, 800, 0, - 0, 780, 781, 841, 842, 0, 0, 0, 0, 790, - 317, 0, 384, 0, 0, 796, 618, 617, 627, 628, - 620, 621, 622, 623, 624, 625, 626, 619, 0, 809, - 629, 1015, 0, 1017, 0, 0, 0, 0, 0, 0, - 746, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1044, 0, 0, 0, 0, 0, 654, 317, 317, 895, - 896, 627, 628, 620, 621, 622, 623, 624, 625, 626, - 619, 0, 840, 629, 0, 0, 317, 0, 0, 0, - 0, 0, 0, 0, 0, 1519, 0, 0, 0, 0, - 0, 0, 0, 0, 910, 264, 264, 264, 264, 264, - 0, 0, 0, 0, 0, 0, 0, 1130, 0, 0, - 264, 0, 0, 613, 943, 616, 0, 961, 264, 734, - 0, 630, 631, 632, 633, 634, 635, 636, 0, 614, - 615, 612, 618, 617, 627, 628, 620, 621, 622, 623, - 624, 625, 626, 619, 0, 0, 629, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 747, 0, 0, - 0, 0, 921, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 948, 0, 0, 0, 0, - 760, 763, 764, 765, 766, 767, 768, 0, 769, 770, - 771, 772, 773, 748, 749, 750, 751, 732, 733, 761, - 0, 735, 0, 736, 737, 738, 739, 740, 741, 742, - 743, 744, 745, 752, 753, 754, 755, 756, 757, 758, - 759, 0, 0, 0, 0, 1052, 1053, 0, 591, 264, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 317, - 0, 0, 0, 0, 0, 0, 0, 0, 1014, 0, - 0, 317, 0, 0, 0, 0, 0, 1034, 1035, 1036, - 1037, 1038, 0, 1041, 1042, 0, 0, 1043, 1183, 0, - 0, 762, 800, 0, 0, 24, 25, 52, 27, 28, - 0, 910, 0, 0, 0, 0, 1045, 0, 0, 0, - 0, 1080, 0, 1050, 43, 0, 0, 1212, 0, 29, - 48, 49, 0, 0, 0, 0, 0, 0, 1098, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 38, 0, 0, 0, 54, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 264, 0, 31, 32, 34, 33, 36, - 264, 50, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 629, 0, 0, 0, 1495, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1396, 1397, + 1398, 1399, 1400, 0, 0, 0, 1403, 1404, 0, 1376, + 0, 1014, 0, 0, 0, 0, 0, 0, 0, 0, + 1034, 1035, 1036, 1037, 1038, 0, 1041, 1042, 0, 1416, + 1043, 0, 0, 0, 1015, 0, 1017, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1045, + 0, 0, 0, 1044, 0, 0, 1050, 0, 1445, 1446, + 1447, 1448, 0, 1452, 0, 1453, 1454, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1460, 0, 1461, + 1462, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 264, 264, 0, 0, + 0, 1483, 0, 0, 264, 0, 0, 264, 0, 1488, + 264, 0, 0, 0, 800, 0, 0, 0, 0, 0, + 613, 0, 616, 0, 264, 0, 0, 1493, 630, 631, + 632, 633, 634, 635, 636, 0, 614, 615, 612, 618, + 617, 627, 628, 620, 621, 622, 623, 624, 625, 626, + 619, 0, 0, 629, 0, 0, 0, 0, 0, 0, + 0, 1520, 0, 0, 0, 0, 0, 264, 0, 0, + 0, 0, 1528, 1529, 0, 0, 800, 0, 0, 0, + 0, 0, 0, 0, 0, 24, 25, 52, 27, 28, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 43, 0, 0, 0, 0, 29, + 48, 49, 0, 0, 0, 0, 0, 0, 317, 0, + 0, 0, 0, 317, 317, 0, 0, 317, 317, 317, + 38, 0, 0, 910, 54, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 317, 317, 317, 317, 0, 264, 0, 0, + 0, 1183, 0, 0, 0, 264, 943, 0, 0, 264, + 264, 0, 0, 264, 951, 800, 0, 0, 0, 0, + 0, 0, 1216, 0, 0, 0, 0, 0, 0, 0, + 1212, 0, 0, 0, 0, 31, 32, 34, 33, 36, + 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 44, 45, 0, 0, 46, 47, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 39, 40, 0, 41, 42, 910, + 0, 0, 0, 264, 39, 40, 0, 41, 42, 0, + 0, 0, 264, 264, 264, 264, 264, 0, 264, 264, + 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 264, 0, 1047, 1048, 0, 0, 0, 264, 0, + 0, 0, 0, 800, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 317, 1311, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1314, 0, 0, 0, 0, 53, + 0, 0, 0, 0, 0, 0, 1324, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 317, 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 910, + 264, 264, 264, 264, 264, 0, 0, 0, 0, 0, + 0, 0, 1130, 0, 0, 264, 0, 0, 0, 943, + 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1283, 0, 943, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 264, 0, 1385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1441, 0, 0, 0, + 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 800, 0, 0, 0, 0, 0, 0, 0, 0, 910, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1348, 0, 1311, 0, 0, 0, 0, 0, 0, - 0, 654, 0, 0, 0, 0, 0, 0, 0, 1363, - 0, 1314, 1364, 0, 0, 1366, 0, 0, 0, 0, - 0, 0, 0, 1324, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1491, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, + 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 910, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1480, 654, 0, 0, 505, 493, - 0, 450, 508, 423, 440, 516, 441, 444, 481, 408, - 463, 165, 438, 1441, 427, 403, 434, 404, 425, 452, - 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, - 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, - 449, 482, 413, 471, 509, 439, 479, 510, 0, 0, - 0, 80, 0, 968, 969, 0, 0, 0, 0, 0, - 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, - 406, 409, 515, 500, 431, 432, 1153, 0, 0, 0, - 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, - 1490, 0, 0, 0, 0, 429, 0, 470, 0, 0, - 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, - 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, - 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, - 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, - 99, 421, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, - 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, - 501, 502, 468, 82, 91, 138, 246, 186, 116, 235, - 401, 414, 109, 424, 0, 0, 437, 442, 443, 455, - 457, 458, 459, 460, 467, 474, 475, 477, 483, 484, - 485, 486, 491, 498, 517, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 505, 493, 0, 450, 508, 423, 440, 516, 441, - 444, 481, 408, 463, 165, 438, 0, 427, 403, 434, - 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, - 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, - 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, - 510, 0, 0, 0, 80, 0, 968, 969, 0, 0, - 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, - 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, - 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, - 0, 0, 0, 0, 0, 0, 0, 0, 429, 0, - 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, - 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, - 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, - 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, - 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, - 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, - 186, 116, 235, 401, 414, 109, 424, 0, 0, 437, - 442, 443, 455, 457, 458, 459, 460, 467, 474, 475, - 477, 483, 484, 485, 486, 491, 498, 517, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 505, 493, 0, 450, 508, 423, - 440, 516, 441, 444, 481, 408, 463, 165, 438, 0, - 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, - 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, - 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, - 509, 439, 479, 510, 54, 0, 0, 80, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, - 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, - 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, - 462, 487, 447, 0, 0, 0, 0, 0, 0, 0, - 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, - 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, - 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, - 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, - 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, - 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, - 91, 138, 246, 186, 116, 235, 401, 414, 109, 424, - 0, 0, 437, 442, 443, 455, 457, 458, 459, 460, - 467, 474, 475, 477, 483, 484, 485, 486, 491, 498, - 517, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 505, 493, 0, - 450, 508, 423, 440, 516, 441, 444, 481, 408, 463, - 165, 438, 0, 427, 403, 434, 404, 425, 452, 111, - 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, - 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, - 482, 413, 471, 509, 439, 479, 510, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, - 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, - 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, - 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, - 0, 0, 1219, 0, 429, 0, 470, 0, 0, 0, - 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, - 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, - 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, - 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, - 421, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, - 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, - 502, 468, 82, 91, 138, 246, 186, 116, 235, 401, - 414, 109, 424, 0, 0, 437, 442, 443, 455, 457, - 458, 459, 460, 467, 474, 475, 477, 483, 484, 485, - 486, 491, 498, 517, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 505, 493, 0, 450, 508, 423, 440, 516, 441, 444, - 481, 408, 463, 165, 438, 0, 427, 403, 434, 404, - 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, - 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, - 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, - 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, - 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, - 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, - 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, - 0, 0, 0, 0, 0, 952, 0, 429, 0, 470, - 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, - 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, - 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, - 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, - 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, - 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, - 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, - 116, 235, 401, 414, 109, 424, 0, 0, 437, 442, - 443, 455, 457, 458, 459, 460, 467, 474, 475, 477, - 483, 484, 485, 486, 491, 498, 517, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 505, 493, 0, 450, 508, 423, 440, - 516, 441, 444, 481, 408, 463, 165, 438, 0, 427, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 943, 0, 0, + 0, 0, 0, 505, 493, 0, 450, 508, 423, 440, + 516, 441, 444, 481, 408, 463, 165, 438, 264, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, - 439, 479, 510, 0, 0, 0, 322, 0, 0, 0, + 439, 479, 510, 0, 0, 0, 80, 0, 968, 969, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, - 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, - 487, 447, 0, 0, 0, 0, 0, 0, 849, 0, + 432, 1153, 910, 0, 0, 0, 0, 0, 453, 462, + 487, 447, 0, 0, 0, 0, 264, 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, @@ -1376,7 +1207,7 @@ var yyAct = [...]int{ 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, 0, 0, 80, - 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, + 0, 968, 969, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, @@ -1409,8 +1240,8 @@ var yyAct = [...]int{ 408, 463, 165, 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, - 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, - 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, + 490, 449, 482, 413, 471, 509, 439, 479, 510, 54, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, @@ -1448,7 +1279,7 @@ var yyAct = [...]int{ 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, - 447, 0, 0, 0, 0, 0, 0, 0, 0, 429, + 447, 0, 0, 0, 0, 0, 0, 1219, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, @@ -1457,11 +1288,11 @@ var yyAct = [...]int{ 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 398, 236, 158, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 399, 397, 127, 209, 134, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, @@ -1483,7 +1314,7 @@ var yyAct = [...]int{ 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, 0, - 0, 0, 429, 0, 470, 0, 0, 0, 410, 407, + 952, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, @@ -1513,24 +1344,24 @@ var yyAct = [...]int{ 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, 0, 0, - 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, - 0, 0, 0, 0, 0, 429, 0, 470, 0, 0, + 0, 0, 0, 849, 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 711, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 398, 236, 158, 220, 228, 152, 145, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 399, 397, 127, 209, 134, 141, 188, 247, 171, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, 116, 235, @@ -1558,13 +1389,13 @@ var yyAct = [...]int{ 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 389, 102, 203, 88, 227, 214, 151, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 399, 397, 392, 391, 134, 141, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, @@ -1576,1012 +1407,1184 @@ var yyAct = [...]int{ 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 885, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 886, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 315, 0, 0, 0, - 377, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 378, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 959, - 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 960, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, - 377, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 378, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 583, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, - 377, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 378, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 315, 0, 0, 0, - 377, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 378, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 901, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 315, 0, 0, 0, - 377, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 378, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 898, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 315, 0, 0, 0, - 377, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 378, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 24, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 165, 0, 0, 0, - 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, - 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, 322, 343, 342, 345, - 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, - 0, 0, 0, 319, 336, 0, 363, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 333, 334, 0, 0, - 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, - 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, - 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, - 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 324, 0, 0, 0, 111, 0, 321, 0, 0, - 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, 322, 343, 342, 345, - 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, - 0, 0, 0, 319, 336, 0, 363, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 333, 334, 0, 0, - 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, - 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, - 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, - 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, 322, 343, 342, 345, - 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, - 0, 0, 0, 0, 336, 0, 363, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 333, 334, 0, 0, - 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, - 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 1520, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, - 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, - 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 583, 322, 343, 342, 345, - 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, - 0, 0, 0, 0, 336, 0, 363, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 333, 334, 0, 0, - 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, - 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, - 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, - 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 364, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 355, 356, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, 322, 343, 342, 345, - 346, 347, 348, 0, 0, 101, 344, 349, 350, 351, - 0, 0, 0, 0, 336, 0, 363, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 333, 334, 0, 0, - 0, 0, 377, 0, 335, 0, 0, 330, 331, 332, - 337, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 375, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 365, - 376, 371, 372, 369, 370, 368, 367, 366, 378, 357, - 358, 359, 360, 362, 0, 373, 374, 361, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 618, 617, 627, 628, 620, 621, 622, 623, - 624, 625, 626, 619, 0, 0, 629, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 606, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 80, 0, 608, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 603, 602, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 604, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 223, 232, 239, 242, 505, 493, 0, 450, 508, 423, + 440, 516, 441, 444, 481, 408, 463, 165, 438, 0, + 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, + 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, + 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, + 509, 439, 479, 510, 0, 0, 0, 322, 0, 0, + 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, + 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, + 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, + 462, 487, 447, 0, 0, 0, 0, 0, 0, 0, + 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, + 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, + 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, + 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, + 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, + 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, + 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, + 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, + 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, + 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, + 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, + 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, + 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, + 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, + 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, + 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, + 91, 138, 246, 186, 116, 235, 401, 414, 109, 424, + 0, 0, 437, 442, 443, 455, 457, 458, 459, 460, + 467, 474, 475, 477, 483, 484, 485, 486, 491, 498, + 517, 84, 85, 92, 98, 104, 108, 112, 115, 120, + 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, + 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, + 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, + 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, + 213, 216, 222, 223, 232, 239, 242, 505, 493, 0, + 450, 508, 423, 440, 516, 441, 444, 481, 408, 463, + 165, 438, 0, 427, 403, 434, 404, 425, 452, 111, + 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, + 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, + 482, 413, 471, 509, 439, 479, 510, 0, 0, 0, + 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, + 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, + 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, + 0, 0, 0, 0, 429, 0, 470, 0, 0, 0, + 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, + 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, + 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, + 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, + 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, + 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, + 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, + 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, + 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, + 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, + 421, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, + 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, + 502, 468, 82, 91, 138, 246, 186, 116, 235, 401, + 414, 109, 424, 0, 0, 437, 442, 443, 455, 457, + 458, 459, 460, 467, 474, 475, 477, 483, 484, 485, + 486, 491, 498, 517, 84, 85, 92, 98, 104, 108, + 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, + 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, + 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, + 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, + 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, + 505, 493, 0, 450, 508, 423, 440, 516, 441, 444, + 481, 408, 463, 165, 438, 0, 427, 403, 434, 404, + 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, + 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, + 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, + 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, + 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, + 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, + 0, 0, 0, 0, 0, 0, 0, 429, 0, 470, + 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, + 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, + 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, + 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, + 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, + 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, + 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, + 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, + 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, + 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, + 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, + 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, + 116, 235, 401, 414, 109, 424, 0, 0, 437, 442, + 443, 455, 457, 458, 459, 460, 467, 474, 475, 477, + 483, 484, 485, 486, 491, 498, 517, 84, 85, 92, + 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, + 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, + 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, + 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, + 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, + 232, 239, 242, 505, 493, 0, 450, 508, 423, 440, + 516, 441, 444, 481, 408, 463, 165, 438, 0, 427, + 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, + 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, + 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, + 439, 479, 510, 0, 0, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, + 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, + 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, + 487, 447, 0, 0, 0, 0, 0, 0, 0, 0, + 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, + 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, + 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, + 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 711, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 164, 224, 225, 106, 248, 94, 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 399, 397, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, + 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, + 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, + 138, 246, 186, 116, 235, 401, 414, 109, 424, 0, + 0, 437, 442, 443, 455, 457, 458, 459, 460, 467, + 474, 475, 477, 483, 484, 485, 486, 491, 498, 517, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, + 216, 222, 223, 232, 239, 242, 505, 493, 0, 450, + 508, 423, 440, 516, 441, 444, 481, 408, 463, 165, + 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, + 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, + 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, + 413, 471, 509, 439, 479, 510, 0, 0, 0, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, + 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, + 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, + 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, + 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, + 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, + 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, + 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, + 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 389, 102, + 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, + 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, + 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, + 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, + 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 399, + 397, 392, 391, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, + 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, + 468, 82, 91, 138, 246, 186, 116, 235, 401, 414, + 109, 424, 0, 0, 437, 442, 443, 455, 457, 458, + 459, 460, 467, 474, 475, 477, 483, 484, 485, 486, + 491, 498, 517, 84, 85, 92, 98, 104, 108, 112, + 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, + 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, + 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, + 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, + 0, 0, 885, 0, 324, 0, 0, 0, 111, 0, + 321, 0, 0, 0, 137, 886, 364, 139, 0, 0, + 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, + 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, + 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, + 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, + 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, + 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, + 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, + 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, + 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, + 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, + 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, + 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, + 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, + 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, + 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, + 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, + 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, + 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, + 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, + 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, + 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, + 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, + 0, 0, 0, 0, 959, 0, 54, 0, 0, 322, + 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, + 349, 350, 351, 960, 0, 0, 319, 336, 0, 363, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, + 334, 0, 0, 0, 0, 377, 0, 335, 0, 0, + 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, + 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, + 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, + 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, + 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, + 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, + 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, + 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, + 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, + 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, + 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, + 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, + 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, + 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, + 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, + 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, + 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, + 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 583, 322, + 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, + 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, + 334, 0, 0, 0, 0, 377, 0, 335, 0, 0, + 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, + 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, + 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, + 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, + 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, + 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, + 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, + 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, + 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, + 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, + 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, + 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, + 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, + 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, + 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, + 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, + 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, + 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, + 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, + 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, + 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, + 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, + 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, + 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, + 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, + 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, + 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, + 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, + 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, + 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, + 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, + 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, + 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, + 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, + 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, + 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, + 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, + 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, + 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, + 343, 901, 345, 346, 347, 348, 0, 0, 101, 344, + 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, + 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, + 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, + 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, + 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, + 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, + 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, + 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, + 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, + 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, + 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, + 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, + 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 76, 77, 0, 73, 0, 0, 0, 78, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, + 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, + 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, + 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, + 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, + 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, + 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, + 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, + 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, + 343, 898, 345, 346, 347, 348, 0, 0, 101, 344, + 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, + 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, + 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, + 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, + 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, + 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, + 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, + 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, + 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, + 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, + 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, + 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, + 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, + 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, + 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, + 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, + 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, + 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 165, 0, 0, 0, 0, 324, 0, 0, 0, + 111, 0, 321, 0, 0, 0, 137, 0, 364, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, + 101, 344, 349, 350, 351, 0, 0, 0, 319, 336, + 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, + 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, + 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, + 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, - 942, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 0, 324, 0, 0, 0, + 111, 0, 321, 0, 0, 0, 137, 0, 364, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, + 101, 344, 349, 350, 351, 0, 0, 0, 319, 336, + 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, + 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, + 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, + 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 265, 0, 944, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 137, 0, 364, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, + 101, 344, 349, 350, 351, 0, 0, 0, 0, 336, + 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, + 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 1521, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, + 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, + 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 137, 0, 364, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 583, 322, 343, 342, 345, 346, 347, 348, 0, 0, + 101, 344, 349, 350, 351, 0, 0, 0, 0, 336, + 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, + 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, + 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, + 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 137, 0, 364, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, + 101, 344, 349, 350, 351, 0, 0, 0, 0, 336, + 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, + 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, + 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, + 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 618, 617, 627, + 628, 620, 621, 622, 623, 624, 625, 626, 619, 0, + 0, 629, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, + 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 606, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 608, 0, 0, 0, 0, 0, 0, + 101, 0, 0, 0, 0, 0, 603, 602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 24, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 54, 0, 0, 80, 0, - 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 604, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, - 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 0, 0, 0, 0, 0, 119, 76, 77, 0, 73, + 0, 0, 0, 78, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 0, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 165, 0, 0, 0, 942, 0, 0, 0, 0, + 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, + 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 265, 0, 944, 0, 0, 0, 0, 0, 0, + 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 942, 0, 0, 0, 0, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, + 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, + 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, + 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, + 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, + 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, + 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, + 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, + 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, + 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, + 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, + 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 265, 0, 944, 0, 0, 0, 0, 0, 0, 101, + 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, + 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, + 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, + 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, + 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, + 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, + 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, + 242, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 165, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, + 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 940, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, + 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, + 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, + 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, + 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, + 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, + 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, + 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, + 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 91, 138, 246, 186, + 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 836, 0, 0, 837, 0, 0, 101, + 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, + 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, + 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, + 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, + 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, + 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, + 232, 239, 242, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 265, 0, 0, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 0, 720, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 942, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 80, 0, 719, 0, 0, 0, 0, 0, 0, 101, + 0, 0, 0, 0, 0, 265, 0, 944, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 940, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 583, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 0, 0, 0, 0, 0, 80, 0, 0, 836, 0, + 0, 837, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 720, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, - 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 0, 0, 0, 0, 0, 80, 0, 719, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 583, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 265, 0, 944, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 265, 0, 0, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 80, 0, 608, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 265, 0, 944, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 165, 0, 0, 0, 0, 0, 0, 0, 690, 111, - 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, - 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 80, 0, 608, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 119, 0, 0, 0, 267, 0, - 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, - 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 0, 0, 212, 234, 249, 99, - 0, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 82, 91, 138, 246, 186, 116, 235, 0, - 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 381, 0, 0, 0, 0, 0, 0, 165, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 0, 690, 111, 0, 0, 0, 0, 0, + 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, + 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 0, 0, 267, 0, 0, 0, 0, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, - 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 0, 0, 212, 234, 249, 99, 0, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, + 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, + 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, + 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, + 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, + 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, + 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, + 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, + 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, + 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 165, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, + 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, + 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, + 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, + 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, + 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, + 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, + 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, + 222, 223, 232, 239, 242, 381, 0, 0, 0, 0, + 0, 0, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 262, 0, 267, 0, 0, 0, 0, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, - 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 0, 0, 212, 234, 249, 99, 0, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 165, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, + 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 262, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 0, 0, 267, 0, 0, 0, 0, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, - 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 0, 0, 212, 234, 249, 99, 0, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 165, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 0, 0, 267, 0, 0, 0, 0, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, - 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 0, 0, 212, 234, 249, 99, 0, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 165, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 322, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 0, 0, 267, 0, 0, 0, 0, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, - 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 0, 0, 212, 234, 249, 99, 0, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, + 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, + 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, } var yyPact = [...]int{ - 2319, -1000, -268, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 2159, -1000, -268, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 900, 930, -1000, -1000, -1000, -1000, -1000, -1000, - 337, 11298, -24, 123, 9, 15619, 121, 125, 16279, -1000, - 15, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -89, -113, - -1000, 676, -1000, -1000, -1000, -1000, -1000, 892, 898, 726, - 884, 832, -1000, 7986, 84, 84, 15289, 6666, -1000, -1000, - 248, 16279, 115, 16279, -153, 79, 79, 79, -1000, -1000, + -1000, -1000, 989, 1042, -1000, -1000, -1000, -1000, -1000, -1000, + 275, 11323, -1, 127, -23, 15644, 121, 74, 16304, -1000, + 13, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -67, -95, + -1000, 743, -1000, -1000, -1000, -1000, -1000, 963, 981, 802, + 958, 859, -1000, 8011, 85, 85, 15314, 6691, -1000, -1000, + 269, 16304, 118, 16304, -152, 82, 82, 82, -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, @@ -2599,21 +2602,21 @@ 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, - 120, 16279, 575, 565, 217, -1000, 16279, 74, 564, 74, - 74, 74, 16279, -1000, 162, -1000, -1000, -1000, 16279, 558, - 852, 296, 47, 3579, -1000, 3579, 3579, -1000, 3579, 23, - 3579, -45, 907, 24, -47, -1000, 3579, -1000, -1000, -1000, + 120, 16304, 622, 621, 278, -1000, 16304, 78, 620, 78, + 78, 78, 16304, -1000, 172, -1000, -1000, -1000, 16304, 614, + 901, 306, 48, 3604, -1000, 3604, 3604, -1000, 3604, 23, + 3604, -89, 1013, 24, -9, -1000, 3604, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 580, 862, 9318, 9318, 900, -1000, 676, -1000, -1000, -1000, - 857, -1000, -1000, 328, 920, -1000, 10968, 160, -1000, 9318, - 2088, 633, -1000, -1000, 633, -1000, -1000, 145, -1000, -1000, - 10308, 10308, 10308, 10308, 10308, 10308, 10308, 10308, -1000, -1000, + 437, 904, 9343, 9343, 989, -1000, 743, -1000, -1000, -1000, + 900, -1000, -1000, 319, 1028, -1000, 10993, 171, -1000, 9343, + 2025, 661, -1000, -1000, 661, -1000, -1000, 140, -1000, -1000, + 10333, 10333, 10333, 10333, 10333, 10333, 10333, 10333, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 633, -1000, 8988, 633, 633, 633, 633, 633, - 633, 633, 633, 9318, 633, 633, 633, 633, 633, 633, - 633, 633, 633, 633, 633, 633, 633, 633, 633, 14952, - 13962, 16279, 625, 602, -1000, -1000, 159, 659, 6323, -79, - -1000, -1000, -1000, 257, 13302, -1000, -1000, -1000, 846, -1000, + -1000, -1000, 661, -1000, 9013, 661, 661, 661, 661, 661, + 661, 661, 661, 9343, 661, 661, 661, 661, 661, 661, + 661, 661, 661, 661, 661, 661, 661, 661, 661, 14977, + 13987, 16304, 694, 665, -1000, -1000, 167, 735, 6348, -101, + -1000, -1000, -1000, 264, 13327, -1000, -1000, -1000, 892, -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, @@ -2625,132 +2628,132 @@ 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, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 563, 16279, - -1000, 2060, -1000, 557, 3579, 109, 556, 297, 544, 16279, - 16279, 3579, 3579, 3579, 35, 69, 63, 16279, 678, 92, - 16279, 878, 758, 16279, 530, 528, -1000, 5980, -1000, 3579, - 296, -1000, 476, 9318, 3579, 3579, 3579, 16279, 3579, 3579, - -1000, -1000, -1000, -1000, -1000, -1000, 3579, 3579, -1000, 919, - 283, -1000, -1000, -1000, -1000, 9318, 206, -1000, 749, -1000, - -1000, -1000, -1000, -1000, -1000, 925, 202, 510, 158, 670, - -1000, 346, 892, 580, 832, 12972, 735, -1000, -1000, -1000, - 16279, -1000, 9318, 9318, 494, -1000, 14622, -1000, -1000, 4608, - 211, 10308, 389, 286, 10308, 10308, 10308, 10308, 10308, 10308, - 10308, 10308, 10308, 10308, 10308, 10308, 10308, 10308, 10308, 375, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 524, -1000, - 676, 693, 693, 170, 170, 170, 170, 170, 170, 170, - 10638, 6996, 580, 555, 230, 8988, 7986, 7986, 9318, 9318, - 8646, 8316, 7986, 850, 274, 230, 16609, -1000, -1000, 9978, - -1000, -1000, -1000, -1000, -1000, 580, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 15949, 15949, 7986, 7986, 7986, 7986, 49, - 16279, -1000, 703, 760, -1000, -1000, -1000, 880, 12312, 12642, - 49, 616, 13962, 16279, -1000, -1000, 13962, 16279, 4265, 5637, - 659, -79, 634, -1000, -132, -108, 7326, 140, -1000, -1000, - -1000, -1000, 3236, 188, 597, 290, -63, -1000, -1000, -1000, - 681, -1000, 681, 681, 681, 681, -34, -34, -34, -34, - -1000, -1000, -1000, -1000, -1000, 709, 708, -1000, 681, 681, - 681, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 689, - 689, 689, 684, 684, 721, -1000, 16279, 3579, 877, 3579, - -1000, 71, -1000, -1000, -1000, 16279, 16279, 16279, 16279, 16279, - 135, 16279, 16279, 649, -1000, 16279, 3579, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 230, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 16279, 296, 16279, 16279, 230, -1000, - 475, 16279, -1000, 814, 9318, 9318, 5294, 9318, -1000, -1000, - -1000, 862, -1000, 850, 897, -1000, 834, 826, 7986, -1000, - -1000, 211, 270, -1000, -1000, 371, -1000, -1000, -1000, -1000, - 155, 633, -1000, 1972, -1000, -1000, -1000, -1000, 389, 10308, - 10308, 10308, 397, 1972, 1884, 2015, 1794, 170, 309, 309, - 185, 185, 185, 185, 185, 527, 527, -1000, -1000, -1000, - 580, -1000, -1000, -1000, 580, 7986, 7986, 646, -1000, -1000, - 9318, -1000, 580, 549, 549, 349, 353, 247, 918, 549, - 243, 912, 549, 549, 7986, 285, -1000, 9318, 580, -1000, - 154, -1000, 744, 642, 636, 549, 580, 549, 549, 632, - 633, -1000, 16609, 13962, 13962, 13962, 13962, 13962, -1000, 784, - 781, -1000, 778, 771, 820, 16279, -1000, 552, 12312, 171, - 633, -1000, 14292, -1000, -1000, 906, 13962, 640, -1000, 640, - -1000, 153, -1000, -1000, 634, -79, -126, -1000, -1000, -1000, - -1000, 230, -1000, 464, 630, 2893, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 687, 522, -1000, 870, 205, 204, 520, - 869, -1000, -1000, -1000, 854, -1000, 315, -85, -1000, -1000, - 396, -34, -34, -1000, -1000, 140, 845, 140, 140, 140, - 463, 463, -1000, -1000, -1000, -1000, 376, -1000, -1000, -1000, - 369, -1000, 747, 15949, 3579, -1000, -1000, -1000, -1000, 268, - 268, 209, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 48, 719, -1000, -1000, -1000, -1000, -2, - 32, 87, -1000, 3579, -1000, 283, -1000, -1000, -1000, -1000, - -1000, 811, 230, 230, 152, -1000, -1000, 16279, -1000, -1000, - -1000, -1000, 650, -1000, -1000, -1000, 3922, 7986, -1000, 397, - 1972, 1852, -1000, 10308, 10308, -1000, -1000, 549, 549, 7986, - 230, -1000, -1000, -1000, 28, 375, 28, 10308, 10308, -1000, - 10308, 10308, -1000, -165, 645, 259, -1000, 9318, 212, -1000, - 5294, -1000, 10308, 10308, -1000, -1000, -1000, -1000, 746, 16609, - 633, -1000, 11970, 15949, 641, -1000, 233, 760, 695, 742, - 675, -1000, -1000, -1000, -1000, 780, -1000, 770, -1000, -1000, - -1000, -1000, -1000, 114, 113, 111, 15949, -1000, 900, 9318, - 640, -1000, -1000, 184, -1000, -1000, -136, -114, -1000, -1000, - -1000, 3236, -1000, 3236, 15949, 66, -1000, 520, 520, -1000, - -1000, -1000, 685, 738, 10308, -1000, -1000, -1000, 591, 140, - 140, -1000, 222, -1000, -1000, -1000, 542, -1000, 538, 627, - 536, 16279, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16279, - -1000, -1000, -1000, -1000, -1000, 15949, -171, 517, 15949, 15949, - 15949, 16279, -1000, 296, -1000, 4951, -1000, 906, 13962, -1000, - -1000, 580, -1000, 10308, 1972, 1972, -1000, -1000, -1000, 580, - 681, 681, -1000, 681, 684, -1000, 681, 6, 681, -5, - 580, 580, 1837, 1552, 1428, 413, 633, -160, -1000, 230, - 9318, -1000, 1447, 1238, -1000, 863, 593, 618, -1000, -1000, - 7656, 580, 534, 151, 516, -1000, 900, 16609, 9318, -1000, - -1000, 9318, 683, -1000, 9318, -1000, -1000, -1000, 633, 633, - 633, 516, 892, 230, -1000, -1000, -1000, -1000, 2893, -1000, - 514, -1000, 681, -1000, -1000, -1000, 15949, -76, 924, 1972, - -1000, -1000, -1000, -1000, -1000, -34, 459, -34, 368, -1000, - 357, 3579, -1000, -1000, -1000, -1000, 873, -1000, 4951, -1000, - -1000, 680, 720, -1000, -1000, -1000, 904, 622, -1000, 1972, - -1000, -1000, 101, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 10308, 10308, 10308, 10308, 10308, 580, 446, 230, 10308, - 10308, 867, -1000, 633, -1000, -1000, 638, 15949, 15949, -1000, - 15949, 892, -1000, 230, 230, 15949, 230, 13632, 15949, 15949, - 11628, -1000, 163, 15949, -1000, 505, -1000, 193, -1000, -81, - 140, -1000, 140, 583, 582, -1000, 633, 619, -1000, 231, - 15949, 16279, 899, 895, -1000, -1000, 744, 744, 744, 744, - 46, -1000, -1000, 744, 744, 923, -1000, 633, -1000, 676, - 142, -1000, -1000, -1000, 497, 495, -1000, 495, 495, 171, - 163, -1000, 491, 228, 401, -1000, 58, 15949, 324, 864, - -1000, 858, -1000, -1000, -1000, -1000, -1000, 45, 4951, 3236, - 490, -1000, -1000, 9318, 9318, -1000, -1000, -1000, -1000, 580, - 44, -177, -1000, -1000, 16609, 618, 580, 15949, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 345, -1000, -1000, 16279, -1000, - -1000, 400, -1000, -1000, 486, -1000, 15949, -1000, -1000, 719, - 230, 607, -1000, 795, -169, -183, 594, -1000, -1000, -1000, - 639, -1000, -1000, 45, 823, -171, -1000, 788, -1000, 15949, - -1000, 41, -1000, -173, 483, 39, -181, 732, 633, -184, - 723, -1000, 911, 9648, -1000, -1000, 913, 169, 169, 744, - 580, -1000, -1000, -1000, 70, 460, -1000, -1000, -1000, -1000, - -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 618, 16304, + -1000, 1464, -1000, 613, 3604, 100, 612, 292, 611, 16304, + 16304, 3604, 3604, 3604, 36, 68, 57, 16304, 744, 91, + 16304, 950, 810, 16304, 572, 561, -1000, 6005, -1000, 3604, + 306, -1000, 513, 9343, 3604, 3604, 3604, 16304, 3604, 3604, + -1000, -1000, -1000, -1000, -1000, -1000, 3604, 3604, -1000, 1025, + 295, -1000, -1000, -1000, -1000, 9343, 219, -1000, 807, -1000, + -1000, -1000, -1000, -1000, -1000, 1034, 195, 491, 165, 738, + -1000, 498, 963, 437, 859, 12997, 824, -1000, -1000, -1000, + 16304, -1000, 9343, 9343, 528, -1000, 14647, -1000, -1000, 4633, + 199, 10333, 464, 299, 10333, 10333, 10333, 10333, 10333, 10333, + 10333, 10333, 10333, 10333, 10333, 10333, 10333, 10333, 10333, 482, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 554, -1000, + 743, 577, 577, 178, 178, 178, 178, 178, 178, 178, + 10663, 7021, 437, 608, 389, 9013, 8011, 8011, 9343, 9343, + 8671, 8341, 8011, 899, 244, 389, 16634, -1000, -1000, 10003, + -1000, -1000, -1000, -1000, -1000, 437, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 15974, 15974, 8011, 8011, 8011, 8011, 51, + 16304, -1000, 697, 905, -1000, -1000, -1000, 952, 12337, 12667, + 51, 728, 13987, 16304, -1000, -1000, 13987, 16304, 4290, 5662, + 735, -101, 700, -1000, -129, -125, 7351, 163, -1000, -1000, + -1000, -1000, 3261, 189, 553, 470, -60, -1000, -1000, -1000, + 754, -1000, 754, 754, 754, 754, -17, -17, -17, -17, + -1000, -1000, -1000, -1000, -1000, 775, 774, -1000, 754, 754, + 754, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 768, + 768, 768, 756, 756, 779, -1000, 16304, 3604, 944, 3604, + -1000, 70, -1000, -1000, -1000, 16304, 16304, 16304, 16304, 16304, + 138, 16304, 16304, 729, -1000, 16304, 3604, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 389, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 16304, 306, 16304, 16304, 389, -1000, + 509, 16304, -1000, 868, 9343, 9343, 5319, 9343, -1000, -1000, + -1000, 904, -1000, 899, 965, -1000, 881, 880, 8011, -1000, + -1000, 199, 434, -1000, -1000, 436, -1000, -1000, -1000, -1000, + 162, 661, -1000, 1844, -1000, -1000, -1000, -1000, 464, 10333, + 10333, 10333, 387, 1844, 1780, 304, 372, 178, 396, 396, + 194, 194, 194, 194, 194, 413, 413, -1000, -1000, -1000, + 437, -1000, -1000, -1000, 437, 8011, 8011, 727, -1000, -1000, + 9343, -1000, 437, 602, 602, 289, 371, 259, 1021, 602, + 256, 1020, 602, 602, 8011, 296, -1000, 9343, 437, -1000, + 160, -1000, 637, 723, 720, 602, 437, 602, 602, 671, + 661, -1000, 16634, 13987, 13987, 13987, 13987, 13987, -1000, 839, + 835, -1000, 823, 822, 829, 16304, -1000, 606, 12337, 198, + 661, -1000, 14317, -1000, -1000, 1010, 13987, 702, -1000, 702, + -1000, 159, -1000, -1000, 700, -101, -111, -1000, -1000, -1000, + -1000, 389, -1000, 494, 688, 2918, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 761, 544, -1000, 917, 214, 188, 542, + 916, -1000, -1000, -1000, 909, -1000, 303, -62, -1000, -1000, + 432, -17, -17, -1000, -1000, 163, 889, 163, 163, 163, + 506, 506, -1000, -1000, -1000, -1000, 404, -1000, -1000, -1000, + 395, -1000, 791, 15974, 3604, -1000, -1000, -1000, -1000, 225, + 225, 207, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 50, 777, -1000, -1000, -1000, -1000, 2, + 34, 88, -1000, 3604, -1000, 295, -1000, -1000, -1000, -1000, + -1000, 866, 389, 389, 155, -1000, -1000, 16304, -1000, -1000, + -1000, -1000, 717, -1000, -1000, -1000, 3947, 8011, -1000, 387, + 1844, 1249, -1000, 10333, 10333, -1000, -1000, 602, 602, 8011, + 389, -1000, -1000, -1000, 47, 482, 47, 10333, 10333, -1000, + 10333, 10333, -1000, -165, 705, 272, -1000, 9343, 463, -1000, + 5319, -1000, 10333, 10333, -1000, -1000, -1000, -1000, 790, 16634, + 661, -1000, 11995, 15974, 706, -1000, 241, 905, 772, 789, + 643, -1000, -1000, -1000, -1000, 834, -1000, 833, -1000, -1000, + -1000, -1000, -1000, 115, 113, 111, 15974, -1000, 989, 9343, + 702, -1000, -1000, 197, -1000, -1000, -119, -130, -1000, -1000, + -1000, 3261, -1000, 3261, 15974, 61, -1000, 542, 542, -1000, + -1000, -1000, 759, 787, 10333, -1000, -1000, -1000, 549, 163, + 163, -1000, 240, -1000, -1000, -1000, 599, -1000, 597, 657, + 592, 16304, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16304, + -1000, -1000, -1000, -1000, -1000, 15974, -170, 529, 15974, 15974, + 15974, 16304, -1000, 306, -1000, 4976, -1000, 1010, 13987, -1000, + -1000, 437, -1000, 10333, 1844, 1844, -1000, -1000, -1000, 437, + 754, 754, -1000, 754, 756, -1000, 754, 1, 754, 0, + 437, 437, 1722, 1499, 1462, 794, 661, -159, -1000, 389, + 9343, -1000, 998, 751, -1000, 926, 669, 604, -1000, -1000, + 7681, 437, 586, 153, 576, -1000, 989, 16634, 9343, -1000, + -1000, 9343, 755, -1000, 9343, -1000, -1000, -1000, 661, 661, + 661, 576, 963, 389, -1000, -1000, -1000, -1000, 2918, -1000, + 568, -1000, 754, -1000, -1000, -1000, 15974, -41, 1033, 1844, + -1000, -1000, -1000, -1000, -1000, -17, 503, -17, 382, -1000, + 354, 3604, -1000, -1000, -1000, -1000, 930, -1000, 4976, -1000, + -1000, 747, 778, -1000, -1000, -1000, 996, 642, -1000, 1844, + -1000, -1000, 119, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 10333, 10333, 10333, 10333, 10333, 963, 489, 389, 10333, + 10333, 914, -1000, 661, -1000, -1000, 660, 15974, 15974, -1000, + 15974, 963, -1000, 389, 389, 15974, 389, 13657, 15974, 15974, + 11653, -1000, 158, 15974, -1000, 558, -1000, 181, -1000, -79, + 163, -1000, 163, 527, 520, -1000, 661, 625, -1000, 233, + 15974, 16304, 991, 977, -1000, -1000, 637, 637, 637, 637, + 45, 437, -1000, 637, 637, 1032, -1000, 661, -1000, 743, + 152, -1000, -1000, -1000, 552, 548, -1000, 548, 548, 198, + 158, -1000, 493, 232, 468, -1000, 58, 15974, 317, 912, + -1000, 902, -1000, -1000, -1000, -1000, -1000, 49, 4976, 3261, + 533, -1000, -1000, 9343, 9343, -1000, -1000, -1000, -1000, 437, + 46, -176, -1000, -1000, -1000, 16634, 604, 437, 15974, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 326, -1000, -1000, 16304, + -1000, -1000, 467, -1000, -1000, 526, -1000, 15974, -1000, -1000, + 777, 389, 594, -1000, 865, -168, -182, 588, -1000, -1000, + -1000, 746, -1000, -1000, 49, 874, -170, -1000, 864, -1000, + 15974, -1000, 42, -1000, -171, 519, 40, -180, 785, 661, + -184, 783, -1000, 1017, 9673, -1000, -1000, 1031, 203, 203, + 637, 437, -1000, -1000, -1000, 71, 384, -1000, -1000, -1000, + -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 1149, 60, 537, 1148, 1147, 1141, 1140, 1134, 1133, - 1132, 1131, 1125, 1118, 1112, 1110, 1109, 1106, 1105, 1104, - 1103, 1100, 1099, 1098, 1096, 1095, 89, 1093, 1092, 1091, - 66, 1090, 76, 1089, 1088, 31, 877, 40, 43, 1450, - 1086, 34, 69, 64, 1085, 29, 1082, 1081, 77, 1079, - 1077, 52, 1076, 1075, 1521, 1074, 63, 1073, 13, 55, - 1072, 1071, 1070, 1069, 71, 621, 1066, 1065, 15, 1064, - 1063, 85, 1062, 53, 8, 14, 11, 18, 1061, 44, - 50, 1060, 54, 1059, 1057, 1055, 1054, 20, 1053, 57, - 1052, 17, 56, 1049, 7, 59, 28, 23, 6, 84, - 58, 1047, 22, 62, 48, 1046, 1045, 438, 1043, 1042, - 41, 1041, 1040, 38, 1039, 88, 526, 1038, 1032, 1031, - 1028, 37, 0, 300, 35, 73, 1027, 1026, 1022, 1221, - 45, 51, 16, 1018, 75, 1303, 42, 1016, 1013, 30, - 1012, 1011, 1009, 1008, 1007, 1002, 1000, 33, 998, 997, - 995, 91, 21, 988, 986, 65, 24, 982, 979, 978, - 49, 70, 977, 976, 46, 26, 974, 965, 964, 963, - 962, 27, 9, 950, 19, 949, 10, 948, 25, 946, - 4, 945, 12, 944, 3, 942, 5, 47, 1, 941, - 2, 940, 938, 109, 226, 79, 937, 78, + 0, 1265, 26, 574, 1262, 1261, 1259, 1257, 1239, 1237, + 1236, 1235, 1234, 1232, 1231, 1230, 1229, 1228, 1227, 1224, + 1223, 1222, 1221, 1220, 1218, 1217, 90, 1214, 1213, 1208, + 65, 1207, 66, 1205, 1203, 48, 222, 43, 41, 258, + 1202, 60, 89, 73, 1199, 29, 1198, 1195, 82, 1194, + 1193, 55, 1190, 1189, 1204, 1186, 58, 1183, 12, 46, + 1180, 1179, 1178, 1177, 69, 626, 1176, 1175, 15, 1173, + 1171, 87, 1170, 53, 5, 14, 22, 18, 1169, 44, + 6, 1168, 86, 1164, 1160, 1158, 1157, 20, 1156, 59, + 1153, 34, 54, 1151, 9, 71, 30, 24, 7, 77, + 63, 1148, 23, 64, 50, 1147, 1146, 526, 1144, 1142, + 45, 1141, 1140, 19, 1136, 91, 502, 1135, 1134, 1132, + 1130, 37, 0, 478, 114, 76, 1128, 1125, 1124, 1546, + 42, 52, 17, 1123, 112, 1226, 40, 1120, 1119, 35, + 1112, 1110, 1107, 1104, 1090, 1089, 1087, 70, 1082, 1081, + 1079, 31, 21, 1078, 1077, 56, 25, 1075, 1073, 1072, + 49, 57, 1071, 1070, 51, 38, 1069, 1066, 1065, 1064, + 1063, 33, 11, 1062, 16, 1060, 13, 1059, 28, 1057, + 4, 1056, 10, 1055, 3, 1053, 8, 47, 1, 1052, + 2, 1050, 1049, 61, 631, 75, 1048, 79, } var yyR1 = [...]int{ @@ -2901,7 +2904,7 @@ var yyR2 = [...]int{ 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 4, 5, 5, 6, 4, 4, 6, 6, 6, 8, 8, 8, 8, 9, - 7, 5, 4, 2, 2, 2, 2, 2, 2, 2, + 8, 5, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 0, 2, 3, 4, 4, 4, 4, 4, 4, 0, 3, 4, 7, 3, 1, 1, 2, 3, 3, 1, 2, @@ -3090,20 +3093,20 @@ var yyChk = [...]int{ -194, -91, 57, 56, -147, -94, -123, -158, 212, 9, -151, 60, -151, 61, 61, -135, 26, -183, -182, -124, 55, 54, -85, 13, -151, 58, -65, -65, -65, -65, - -65, -194, 60, -65, -65, 28, -77, 35, -2, -193, + -65, -91, 60, -65, -65, 28, -77, 35, -2, -193, -123, -123, -123, -91, -94, -94, -194, -94, -94, -132, -177, -176, 54, 135, 67, -174, 57, 56, -159, 131, 28, 130, -68, -152, -152, 57, 57, -193, 56, 83, -94, -54, -86, 14, 16, -194, -194, -194, -194, -33, - 93, 263, -194, -194, 9, -75, -2, 113, 57, -194, - -194, -194, -58, -176, 58, -166, 83, 60, 142, -123, - -148, 67, 28, 28, -179, -180, 153, -182, -172, 57, - -39, -74, -194, 261, 50, 264, -98, -194, -123, 61, - -54, 60, -194, 56, -123, -186, 40, 262, 265, 55, - -180, 35, -184, 40, -94, 155, 263, 57, 156, 264, - -189, -190, 53, -193, 265, -190, 53, 10, 9, -65, - 152, -188, 143, 138, 141, 30, -188, -194, -194, 137, - 29, 69, + 93, 263, -194, -194, -194, 9, -75, -2, 113, 57, + -194, -194, -194, -58, -176, 58, -166, 83, 60, 142, + -123, -148, 67, 28, 28, -179, -180, 153, -182, -172, + 57, -39, -74, -194, 261, 50, 264, -98, -194, -123, + 61, -54, 60, -194, 56, -123, -186, 40, 262, 265, + 55, -180, 35, -184, 40, -94, 155, 263, 57, 156, + 264, -189, -190, 53, -193, 265, -190, 53, 10, 9, + -65, 152, -188, 143, 138, 141, 30, -188, -194, -194, + 137, 29, 69, } var yyDef = [...]int{ @@ -3241,26 +3244,26 @@ var yyDef = [...]int{ 0, 939, 216, 217, 218, 219, 0, 224, 0, 73, 74, 0, 0, 229, 251, 277, 561, 348, 479, 421, 482, 525, 159, 529, 530, 532, 534, 535, 537, 484, - 483, 0, 0, 0, 0, 0, 0, 0, 544, 0, + 483, 0, 0, 0, 0, 0, 573, 0, 544, 0, 0, 0, 34, 0, 595, -2, 0, 0, 0, 49, 0, 573, 599, 600, 367, 0, 372, 0, 0, 0, 375, 38, 173, 0, 192, 0, 359, 165, 158, 0, 162, 138, 162, 0, 0, 67, 0, 76, 77, 0, 0, 0, 563, 0, 526, 527, 0, 0, 0, 0, - 518, 490, 541, 0, 0, 0, 593, 0, -2, 0, + 518, 0, 541, 0, 0, 0, 593, 0, -2, 0, 588, 587, 362, 37, 0, 0, 397, 0, 0, 395, 172, 174, 0, 179, 0, 191, 0, 0, 170, 0, 167, 169, 156, 127, 128, 142, 145, 0, 0, 0, 0, 231, 28, 0, 0, 485, 487, 486, 488, 0, - 0, 0, 507, 508, 0, 583, 29, 0, 368, 396, - 398, 399, 358, 175, 176, 0, 180, 178, 0, 360, - 88, 0, 166, 168, 0, 245, 0, 78, 79, 72, - 564, 562, 489, 0, 0, 0, 591, -2, 589, 177, - 0, 171, 244, 0, 0, 75, 519, 0, 522, 0, - 246, 0, 228, 520, 0, 0, 0, 195, 0, 0, - 196, 197, 0, 0, 521, 198, 0, 0, 0, 0, - 0, 199, 201, 202, 0, 0, 200, 247, 248, 203, - 204, 205, + 0, 0, 490, 507, 508, 0, 583, 29, 0, 368, + 396, 398, 399, 358, 175, 176, 0, 180, 178, 0, + 360, 88, 0, 166, 168, 0, 245, 0, 78, 79, + 72, 564, 562, 489, 0, 0, 0, 591, -2, 589, + 177, 0, 171, 244, 0, 0, 75, 519, 0, 522, + 0, 246, 0, 228, 520, 0, 0, 0, 195, 0, + 0, 196, 197, 0, 0, 521, 198, 0, 0, 0, + 0, 0, 199, 201, 202, 0, 0, 200, 247, 248, + 203, 204, 205, } var yyTok1 = [...]int{ @@ -6489,10 +6492,10 @@ yydefault: yyVAL.expr = &MatchExpr{Columns: yyDollar[3].selectExprs, Expr: yyDollar[7].expr, Option: yyDollar[8].str} } case 490: - yyDollar = yyS[yypt-7 : yypt+1] + yyDollar = yyS[yypt-8 : yypt+1] //line sql.y:2551 { - yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str} + yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str, Limit: yyDollar[7].limit} } case 491: yyDollar = yyS[yypt-5 : yypt+1] diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 4e99f610d76..85a31e4818e 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -2547,9 +2547,9 @@ function_call_keyword: { $$ = &MatchExpr{Columns: $3, Expr: $7, Option: $8} } -| GROUP_CONCAT openb distinct_opt select_expression_list order_by_opt separator_opt closeb +| GROUP_CONCAT openb distinct_opt select_expression_list order_by_opt separator_opt limit_opt closeb { - $$ = &GroupConcatExpr{Distinct: $3, Exprs: $4, OrderBy: $5, Separator: $6} + $$ = &GroupConcatExpr{Distinct: $3, Exprs: $4, OrderBy: $5, Separator: $6, Limit: $7} } | CASE expression_opt when_expression_list else_expression_opt END { From 7f1dae86ea91aada6d4e28b39698affac64a374b Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 25 Feb 2020 16:47:37 +0100 Subject: [PATCH 184/825] Make it possible to update all tests in one pass Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/plan_test.go | 111 +++++++------- .../planbuilder/testdata/aggr_cases.txt | 39 ++--- .../planbuilder/testdata/filter_cases.txt | 113 ++++++++++---- .../planbuilder/testdata/from_cases.txt | 43 ++++-- .../testdata/memory_sort_cases.txt | 2 - .../testdata/postprocess_cases.txt | 144 ++++++++++-------- .../planbuilder/testdata/select_cases.txt | 33 ++-- .../planbuilder/testdata/symtab_cases.txt | 2 +- .../testdata/vindex_func_cases.txt | 16 +- 9 files changed, 319 insertions(+), 184 deletions(-) diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 885499d87a2..4e5c4760477 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -18,14 +18,17 @@ package planbuilder import ( "bufio" - "bytes" "encoding/json" "fmt" "io" + "io/ioutil" "os" "strings" "testing" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/sqlparser" @@ -140,29 +143,30 @@ func init() { func TestPlan(t *testing.T) { vschema := loadSchema(t, "schema_test.json") - + testOutputTempDir, err := ioutil.TempDir("", "plan_test") + require.NoError(t, err) // You will notice that some tests expect user.Id instead of user.id. // This is because we now pre-create vindex columns in the symbol // table, which come from vschema. In the test vschema, // the column is named as Id. This is to make sure that // column names are case-preserved, but treated as // case-insensitive even if they come from the vschema. - testFile(t, "aggr_cases.txt", vschema) - testFile(t, "dml_cases.txt", vschema) - testFile(t, "from_cases.txt", vschema) - testFile(t, "filter_cases.txt", vschema) - testFile(t, "postprocess_cases.txt", vschema) - testFile(t, "select_cases.txt", vschema) - testFile(t, "symtab_cases.txt", vschema) - testFile(t, "unsupported_cases.txt", vschema) - testFile(t, "vindex_func_cases.txt", vschema) - testFile(t, "wireup_cases.txt", vschema) - testFile(t, "memory_sort_cases.txt", vschema) + testFile(t, "aggr_cases.txt", testOutputTempDir, vschema) + testFile(t, "dml_cases.txt", testOutputTempDir, vschema) + testFile(t, "from_cases.txt", testOutputTempDir, vschema) + testFile(t, "filter_cases.txt", testOutputTempDir, vschema) + testFile(t, "postprocess_cases.txt", testOutputTempDir, vschema) + testFile(t, "select_cases.txt", testOutputTempDir, vschema) + testFile(t, "symtab_cases.txt", testOutputTempDir, vschema) + testFile(t, "unsupported_cases.txt", testOutputTempDir, vschema) + testFile(t, "vindex_func_cases.txt", testOutputTempDir, vschema) + testFile(t, "wireup_cases.txt", testOutputTempDir, vschema) + testFile(t, "memory_sort_cases.txt", testOutputTempDir, vschema) } func TestOne(t *testing.T) { vschema := loadSchema(t, "schema_test.json") - testFile(t, "onecase.txt", vschema) + testFile(t, "onecase.txt", "", vschema) } func loadSchema(t *testing.T, filename string) *vindexes.VSchema { @@ -225,35 +229,48 @@ type testPlan struct { Instructions engine.Primitive `json:",omitempty"` } -func testFile(t *testing.T, filename string, vschema *vindexes.VSchema) { - for tcase := range iterateExecFile(filename) { - t.Run(tcase.comments, func(t *testing.T) { - plan, err := Build(tcase.input, &vschemaWrapper{ - v: vschema, - }) - var out string - if err != nil { - out = err.Error() - } else { - bout, _ := json.Marshal(testPlan{ - Original: plan.Original, - Instructions: plan.Instructions, +func testFile(t *testing.T, filename, tempDir string, vschema *vindexes.VSchema) { + t.Run(filename, func(t *testing.T) { + expected := &strings.Builder{} + fail := false + for tcase := range iterateExecFile(filename) { + t.Run(tcase.comments, func(t *testing.T) { + plan, err := Build(tcase.input, &vschemaWrapper{ + v: vschema, }) - out = string(bout) - } - if out != tcase.output { - t.Errorf("File: %s, Line:%v\n got:\n%s, \nwant:\n%s", filename, tcase.lineno, out, tcase.output) - // Uncomment these lines to re-generate input files + + out := getPlanOrErrorOutput(err, plan) + + if out != tcase.output { + fail = true + t.Errorf("File: %s, Line: %v\n %s", filename, tcase.lineno, cmp.Diff(out, tcase.output)) + } + if err != nil { - out = fmt.Sprintf("\"%s\"", out) - } else { - bout, _ := json.MarshalIndent(plan, "", " ") - out = string(bout) + out = `"` + out + `"` } - fmt.Printf("%s\"%s\"\n%s\n\n", tcase.comments, tcase.input, out) - } - }) + + expected.WriteString(fmt.Sprintf("%s\"%s\"\n%s\n\n", tcase.comments, tcase.input, out)) + + }) + } + if fail && tempDir != "" { + gotFile := fmt.Sprintf("%s/%s", tempDir, filename) + ioutil.WriteFile(gotFile, []byte(strings.TrimSpace(expected.String())+"\n"), 0644) + fmt.Println(fmt.Sprintf("Errors found in plantests. If the output is correct, run `cp %s/* testdata/` to update test expectations", tempDir)) + } + }) +} + +func getPlanOrErrorOutput(err error, plan *engine.Plan) string { + if err != nil { + return err.Error() } + bout, _ := json.MarshalIndent(testPlan{ + Original: plan.Original, + Instructions: plan.Instructions, + }, "", " ") + return string(bout) } type testCase struct { @@ -281,8 +298,7 @@ func iterateExecFile(name string) (testCaseIterator chan testCase) { binput, err := r.ReadBytes('\n') if err != nil { if err != io.EOF { - fmt.Printf("Line: %d\n", lineno) - panic(fmt.Errorf("error reading file %s: %s", name, err.Error())) + panic(fmt.Errorf("error reading file %s: line %d: %s", name, lineno, err.Error())) } break } @@ -297,8 +313,7 @@ func iterateExecFile(name string) (testCaseIterator chan testCase) { } err = json.Unmarshal(binput, &input) if err != nil { - fmt.Printf("Line: %d, input: %s\n", lineno, binput) - panic(err) + panic(fmt.Sprintf("Line: %d, input: %s, error: %v\n", lineno, binput, err)) } input = strings.Trim(input, "\"") var output []byte @@ -306,19 +321,11 @@ func iterateExecFile(name string) (testCaseIterator chan testCase) { l, err := r.ReadBytes('\n') lineno++ if err != nil { - fmt.Printf("Line: %d\n", lineno) - panic(fmt.Errorf("error reading file %s: %s", name, err.Error())) + panic(fmt.Sprintf("error reading file %s line# %d: %s", name, lineno, err.Error())) } output = append(output, l...) if l[0] == '}' { output = output[:len(output)-1] - b := bytes.NewBuffer(make([]byte, 0, 64)) - err := json.Compact(b, output) - if err == nil { - output = b.Bytes() - } else { - panic("Invalid JSON " + string(output) + err.Error()) - } break } if l[0] == '"' { diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt index 05a8e4254e9..2ca7229980e 100644 --- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt @@ -1,5 +1,5 @@ # Test cases in this file follow the code in ordered_aggregate.go. - +# # Aggregate on unsharded "select count(*), col from unsharded" { @@ -29,7 +29,9 @@ "Query": "select count(*), col from user where id = 1", "FieldQuery": "select count(*), col from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -532,7 +534,9 @@ "Query": "select id, count(*) as c from user group by id having id = 1 and c = 10", "FieldQuery": "select id, count(*) as c from user where 1 != 1 group by id", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -1037,7 +1041,6 @@ } } - # scatter aggregate group by invalid column number "select col from user group by 2" "column number out of range: 2" @@ -1341,19 +1344,21 @@ # Group by with collate operator "select user.col1 as a from user where user.id = 5 group by a collate utf8_general_ci" { - "Original":"select user.col1 as a from user where user.id = 5 group by a collate utf8_general_ci", - "Instructions":{ - "Opcode":"SelectEqualUnique", - "Keyspace":{ - "Name":"user", - "Sharded":true - }, - "Query":"select user.col1 as a from user where user.id = 5 group by a collate utf8_general_ci", - "FieldQuery":"select user.col1 as a from user where 1 != 1 group by a collate utf8_general_ci", - "Vindex":"user_index", - "Values":[5], - "Table": "user" - } + "Original": "select user.col1 as a from user where user.id = 5 group by a collate utf8_general_ci", + "Instructions": { + "Opcode": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user.col1 as a from user where user.id = 5 group by a collate utf8_general_ci", + "FieldQuery": "select user.col1 as a from user where 1 != 1 group by a collate utf8_general_ci", + "Vindex": "user_index", + "Values": [ + 5 + ], + "Table": "user" + } } # routing rules for aggregates where sharded and unsharded match. Unsharded always wins. diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index 53fae69ba05..6e059a0a7ac 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -27,7 +27,9 @@ "Query": "select id from user where user.id = 5", "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -61,7 +63,9 @@ "Query": "select id from music where id = 5 and user_id = 4", "FieldQuery": "select id from music where 1 != 1", "Vindex": "user_index", - "Values": [4], + "Values": [ + 4 + ], "Table": "music" } } @@ -79,7 +83,9 @@ "Query": "select id from user where costly = 'aa' and name = 'bb'", "FieldQuery": "select id from user where 1 != 1", "Vindex": "name_user_map", - "Values": ["bb"], + "Values": [ + "bb" + ], "Table": "user" } } @@ -98,7 +104,10 @@ "FieldQuery": "select id from user where 1 != 1", "Vindex": "name_user_map", "Values": [ - ["aa", "bb"] + [ + "aa", + "bb" + ] ], "Table": "user" } @@ -133,7 +142,9 @@ "Query": "select id from user where name = :a", "FieldQuery": "select id from user where 1 != 1", "Vindex": "name_user_map", - "Values": [":a"], + "Values": [ + ":a" + ], "Table": "user" } } @@ -151,7 +162,9 @@ "Query": "select id from user where name = 18446744073709551615", "FieldQuery": "select id from user where 1 != 1", "Vindex": "name_user_map", - "Values": [18446744073709551615], + "Values": [ + 18446744073709551615 + ], "Table": "user" } } @@ -169,7 +182,9 @@ "Query": "select id from user where name in ::__vals", "FieldQuery": "select id from user where 1 != 1", "Vindex": "name_user_map", - "Values": ["::list"], + "Values": [ + "::list" + ], "Table": "user" } } @@ -187,7 +202,9 @@ "Query": "select user_extra.id from user join user_extra on user.id = user_extra.user_id where user.id = 5", "FieldQuery": "select user_extra.id from user join user_extra on user.id = user_extra.user_id where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -205,7 +222,9 @@ "Query": "select user_extra.id from user join user_extra on user.id = user_extra.user_id where user_extra.user_id = 5", "FieldQuery": "select user_extra.id from user join user_extra on user.id = user_extra.user_id where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -223,7 +242,9 @@ "Query": "select user_extra.id from user left join user_extra on user.id = user_extra.user_id where user.id = 5", "FieldQuery": "select user_extra.id from user left join user_extra on user.id = user_extra.user_id where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -263,7 +284,9 @@ "Query": "select user.col from user where user.id = 5", "FieldQuery": "select user.col from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" }, "Right": { @@ -300,7 +323,9 @@ "Query": "select user.col from user where user.id = 5", "FieldQuery": "select user.col from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" }, "Right": { @@ -312,7 +337,9 @@ "Query": "select user_extra.id from user_extra where user_extra.col = :user_col and user_extra.user_id = 5", "FieldQuery": "select user_extra.id from user_extra where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user_extra" }, "Cols": [ @@ -349,7 +376,9 @@ "Query": "select user_extra.id from user_extra where user_extra.col = :user_col and user_extra.user_id = :user_col", "FieldQuery": "select user_extra.id from user_extra where 1 != 1", "Vindex": "user_index", - "Values": [":user_col"], + "Values": [ + ":user_col" + ], "Table": "user_extra" }, "Cols": [ @@ -410,7 +439,10 @@ "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", "Values": [ - [1, 2] + [ + 1, + 2 + ] ], "Table": "user" } @@ -430,7 +462,10 @@ "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", "Values": [ - [1, 2] + [ + 1, + 2 + ] ], "Table": "user" } @@ -449,7 +484,9 @@ "Query": "select (id or col) as val from user where user.col = 5 and user.id in (1, 2) and user.name = 'aa'", "FieldQuery": "select (id or col) as val from user where 1 != 1", "Vindex": "name_user_map", - "Values": ["aa"], + "Values": [ + "aa" + ], "Table": "user" } } @@ -467,7 +504,9 @@ "Query": "select id from user where user.col = false and user.id in (1, 2) and user.name = 'aa'", "FieldQuery": "select id from user where 1 != 1", "Vindex": "name_user_map", - "Values": ["aa"], + "Values": [ + "aa" + ], "Table": "user" } } @@ -485,7 +524,9 @@ "Query": "select id from user where user.col = 5 and user.id in (1, 2) and user.name = 'aa' and user.id = 1", "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -503,7 +544,9 @@ "Query": "select id from user where user.id = 1 and user.name = 'aa' and user.id in (1, 2) and user.col = 5", "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -640,7 +683,10 @@ "FieldQuery": "select u.m from user as u where 1 != 1", "Vindex": "user_index", "Values": [ - [":user_extra_col", 1] + [ + ":user_extra_col", + 1 + ] ], "Table": "user" }, @@ -678,7 +724,9 @@ "Query": "select u.m from user as u where u.id = 5 and u.id in (select m2 from user where user.id = 5)", "FieldQuery": "select u.m from user as u where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" }, "Cols": [ @@ -713,7 +761,10 @@ "FieldQuery": "select u.m from user as u where 1 != 1", "Vindex": "user_index", "Values": [ - [":user_extra_col", 1] + [ + ":user_extra_col", + 1 + ] ], "Table": "user" }, @@ -755,7 +806,9 @@ "Query": "select id from user where id = 5 and user.col in (select user_extra.col from user_extra where user_extra.user_id = 5)", "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -773,7 +826,9 @@ "Query": "select id from user where id = 'aa' and user.col in (select user_extra.col from user_extra where user_extra.user_id = 'aa')", "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", - "Values": ["aa"], + "Values": [ + "aa" + ], "Table": "user" } } @@ -791,7 +846,9 @@ "Query": "select id from user where id = :a and user.col in (select user_extra.col from user_extra where user_extra.user_id = :a)", "FieldQuery": "select id from user where 1 != 1", "Vindex": "user_index", - "Values": [":a"], + "Values": [ + ":a" + ], "Table": "user" } } @@ -1063,7 +1120,9 @@ "Query": "select user_extra.Id from user join user_extra on user.iD = user_extra.User_Id where user.Id = 5", "FieldQuery": "select user_extra.Id from user join user_extra on user.iD = user_extra.User_Id where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.txt b/go/vt/vtgate/planbuilder/testdata/from_cases.txt index d8b6ed9ee6e..928ff6d0c22 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.txt @@ -715,7 +715,9 @@ "Query": "select user.col from user join user_extra on user.id = 5 and user.id = user_extra.user_id", "FieldQuery": "select user.col from user join user_extra on user.id = 5 and user.id = user_extra.user_id where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -770,7 +772,9 @@ "Query": "select user.col from user where user.id = 5", "FieldQuery": "select user.col from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" }, "Right": { @@ -804,7 +808,9 @@ "Query": "select user.col from user where user.id = 5", "FieldQuery": "select user.col from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" }, "Right": { @@ -883,7 +889,9 @@ "Query": "select user.col from user where user.name = :user_extra_user_id", "FieldQuery": "select user.col from user where 1 != 1", "Vindex": "name_user_map", - "Values": [":user_extra_user_id"], + "Values": [ + ":user_extra_user_id" + ], "Table": "user" }, "Cols": [ @@ -1004,7 +1012,9 @@ "Query": "select id from (select id, col from user where id = 5) as t", "FieldQuery": "select id from (select id, col from user where 1 != 1) as t where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -1022,7 +1032,9 @@ "Query": "select t.id from (select id from user where id = 5) as t join user_extra on t.id = user_extra.user_id", "FieldQuery": "select t.id from (select id from user where 1 != 1) as t join user_extra on t.id = user_extra.user_id where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -1040,7 +1052,9 @@ "Query": "select t.id from (select user.id from user where user.id = 5) as t join user_extra on t.id = user_extra.user_id", "FieldQuery": "select t.id from (select user.id from user where 1 != 1) as t join user_extra on t.id = user_extra.user_id where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -1080,7 +1094,9 @@ "Query": "select t.id from (select id from user where id = 5) as t", "FieldQuery": "select t.id from (select id from user where 1 != 1) as t where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" }, "Right": { @@ -1115,7 +1131,9 @@ "Query": "select id from (select id, col from user as route1 where id = 5) as t", "FieldQuery": "select id from (select id, col from user as route1 where 1 != 1) as t where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -1173,7 +1191,9 @@ "Query": "select u.col, e.col from (select col from user where id = 5) as u join (select col from user_extra where user_id = 5) as e", "FieldQuery": "select u.col, e.col from (select col from user where 1 != 1) as u join (select col from user_extra where 1 != 1) as e where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -1370,7 +1390,6 @@ } } - # Join with cross-shard subquery on rhs "select t.col1 from unsharded_a ua join (select user.id, user.col1 from user join user_extra) as t" { @@ -1747,7 +1766,7 @@ # subquery with join primitive (FROM) "select id, t.id from (select user.id from user join user_extra) as t" -{ +{ "Original": "select id, t.id from (select user.id from user join user_extra) as t", "Instructions": { "Cols": [ diff --git a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt index cdc6a6909b3..8947e10666d 100644 --- a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt @@ -1,5 +1,4 @@ # Test cases in this file follow the code in memory_sort.go. - # scatter aggregate order by references ungrouped column "select a, b, count(*) from user group by a order by b" { @@ -533,7 +532,6 @@ "Query": "select un.col2 from unsharded as un", "FieldQuery": "select un.col2 from unsharded as un where 1 != 1", "Table": "unsharded" - }, "Right": { "Opcode": "SelectScatter", diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt index 3332bde4b7c..508e9ebee77 100644 --- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt @@ -133,7 +133,9 @@ "Query": "select col from user where id = 5 order by aa asc", "FieldQuery": "select col from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -151,7 +153,9 @@ "Query": "select col from user where id = 1 order by 1 asc", "FieldQuery": "select col from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -406,7 +410,9 @@ "Query": "select music.col3 from music where music.id = :user_id order by null", "FieldQuery": "select music.col3 from music where 1 != 1", "Vindex": "music_user_map", - "Values": [":user_id"], + "Values": [ + ":user_id" + ], "Table": "music" }, "Cols": [ @@ -646,7 +652,9 @@ "Query": "select * from user where id = 5 order by col asc", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -664,7 +672,9 @@ "Query": "select user.* from user where id = 5 order by user.col asc", "FieldQuery": "select user.* from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -682,7 +692,9 @@ "Query": "select * from user where id = 5 order by user.col asc", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -752,7 +764,9 @@ "Query": "select * from user where id = 5 order by user.col collate utf8_general_ci asc", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -760,73 +774,81 @@ #Order by with math functions "select * from user where id = 5 order by -col1" { - "Original":"select * from user where id = 5 order by -col1", - "Instructions":{ - "Opcode":"SelectEqualUnique", - "Keyspace":{ - "Name":"user", - "Sharded":true - }, - "Query":"select * from user where id = 5 order by -col1 asc", - "FieldQuery":"select * from user where 1 != 1", - "Vindex":"user_index", - "Values":[5], - "Table": "user" - } + "Original": "select * from user where id = 5 order by -col1", + "Instructions": { + "Opcode": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select * from user where id = 5 order by -col1 asc", + "FieldQuery": "select * from user where 1 != 1", + "Vindex": "user_index", + "Values": [ + 5 + ], + "Table": "user" + } } #Order by with string operations "select * from user where id = 5 order by concat(col,col1) collate utf8_general_ci desc" { - "Original":"select * from user where id = 5 order by concat(col,col1) collate utf8_general_ci desc", - "Instructions":{ - "Opcode":"SelectEqualUnique", - "Keyspace":{ - "Name":"user", - "Sharded":true - }, - "Query":"select * from user where id = 5 order by concat(col, col1) collate utf8_general_ci desc", - "FieldQuery":"select * from user where 1 != 1", - "Vindex":"user_index", - "Values":[5], - "Table": "user" - } + "Original": "select * from user where id = 5 order by concat(col,col1) collate utf8_general_ci desc", + "Instructions": { + "Opcode": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select * from user where id = 5 order by concat(col, col1) collate utf8_general_ci desc", + "FieldQuery": "select * from user where 1 != 1", + "Vindex": "user_index", + "Values": [ + 5 + ], + "Table": "user" + } } #Order by with math operations "select * from user where id = 5 order by id+col collate utf8_general_ci desc" { - "Original":"select * from user where id = 5 order by id+col collate utf8_general_ci desc", - "Instructions":{ - "Opcode":"SelectEqualUnique", - "Keyspace":{ - "Name":"user", - "Sharded":true - }, - "Query":"select * from user where id = 5 order by id + col collate utf8_general_ci desc", - "FieldQuery":"select * from user where 1 != 1", - "Vindex":"user_index", - "Values":[5], - "Table": "user" - } + "Original": "select * from user where id = 5 order by id+col collate utf8_general_ci desc", + "Instructions": { + "Opcode": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select * from user where id = 5 order by id + col collate utf8_general_ci desc", + "FieldQuery": "select * from user where 1 != 1", + "Vindex": "user_index", + "Values": [ + 5 + ], + "Table": "user" + } } #Order by subquery column "select * from user u join (select user_id from user_extra where user_id = 5) eu on u.id = eu.user_id where u.id = 5 order by eu.user_id" { - "Original":"select * from user u join (select user_id from user_extra where user_id = 5) eu on u.id = eu.user_id where u.id = 5 order by eu.user_id", - "Instructions":{ - "Opcode":"SelectEqualUnique", - "Keyspace":{ - "Name":"user", - "Sharded":true - }, - "Query":"select * from user as u join (select user_id from user_extra where user_id = 5) as eu on u.id = eu.user_id where u.id = 5 order by eu.user_id asc", - "FieldQuery":"select * from user as u join (select user_id from user_extra where 1 != 1) as eu on u.id = eu.user_id where 1 != 1", - "Vindex":"user_index", - "Values":[5], - "Table": "user" - } + "Original": "select * from user u join (select user_id from user_extra where user_id = 5) eu on u.id = eu.user_id where u.id = 5 order by eu.user_id", + "Instructions": { + "Opcode": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select * from user as u join (select user_id from user_extra where user_id = 5) as eu on u.id = eu.user_id where u.id = 5 order by eu.user_id asc", + "FieldQuery": "select * from user as u join (select user_id from user_extra where 1 != 1) as eu on u.id = eu.user_id where 1 != 1", + "Vindex": "user_index", + "Values": [ + 5 + ], + "Table": "user" + } } # routing rules: order by test pushed to all options. table 1 is chosen. @@ -882,7 +904,9 @@ "Query": "select col1 from user where id = 1 limit 1", "FieldQuery": "select col1 from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt index 4d9f7891f82..c655e2f6d0e 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt @@ -443,7 +443,6 @@ } } - # select from dual on sharded keyspace "select @@session.auto_increment_increment from user.dual" { @@ -785,7 +784,9 @@ "Query": "select * from user where name = 'abc' and (id = 4) limit 5", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [4], + "Values": [ + 4 + ], "Table": "user" } } @@ -803,7 +804,9 @@ "Query": "select * from user where (id = 4) and (name = 'abc') limit 5", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [4], + "Values": [ + 4 + ], "Table": "user" } } @@ -841,7 +844,9 @@ "Query": "select user0_.col as col0_ from user as user0_ where id = 1 order by user0_.col desc limit 2", "FieldQuery": "select user0_.col as col0_ from user as user0_ where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -859,7 +864,9 @@ "Query": "select user0_.col as col0_ from user as user0_ where id = 1 order by col0_ desc limit 3", "FieldQuery": "select user0_.col as col0_ from user as user0_ where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -877,7 +884,9 @@ "Query": "select * from user where (id = 1) and name = true limit 5", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -895,7 +904,9 @@ "Query": "select * from user where (id = 1) and name limit 5", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "user" } } @@ -913,7 +924,9 @@ "Query": "select * from user where (id = 5) and name = true limit 5", "FieldQuery": "select * from user where 1 != 1", "Vindex": "user_index", - "Values": [5], + "Values": [ + 5 + ], "Table": "user" } } @@ -1055,7 +1068,9 @@ "Query": "select * from music where user_id = 1 union select * from user where id = 1", "FieldQuery": "select * from music where 1 != 1 union select * from user where 1 != 1", "Vindex": "user_index", - "Values": [1], + "Values": [ + 1 + ], "Table": "music" } } diff --git a/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt b/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt index e9d4d3a78b4..e094a6054ab 100644 --- a/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/symtab_cases.txt @@ -1,5 +1,5 @@ # Tests in this file are for testing symtab functionality - +# # Column names need not be qualified if they are predefined in vschema and unambiguous. "select predef2, predef3 from user join unsharded on predef2 = predef3" { diff --git a/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt b/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt index 5f91fd70352..6b3b182d2b7 100644 --- a/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/vindex_func_cases.txt @@ -88,7 +88,11 @@ "type": 10262 } ], - "Cols": [0, 1, 0], + "Cols": [ + 0, + 1, + 0 + ], "Vindex": "user_index", "Value": ":id" } @@ -128,7 +132,6 @@ } } - # You can even join with a vindexFunc primitive "select user_index.keyspace_id, unsharded.id from user_index join unsharded where user_index.id = :id" { @@ -143,7 +146,9 @@ "type": 10262 } ], - "Cols": [1], + "Cols": [ + 1 + ], "Vindex": "user_index", "Value": ":id" }, @@ -157,7 +162,10 @@ "FieldQuery": "select unsharded.id from unsharded where 1 != 1", "Table": "unsharded" }, - "Cols": [-1, 1] + "Cols": [ + -1, + 1 + ] } } From 5148a3848b6b474fb66333c1feb3a240401cf7ed Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Fri, 28 Feb 2020 19:14:38 +0000 Subject: [PATCH 185/825] Remove kubernetes template from example Signed-off-by: Yuvraj --- test/cluster/aws_environment.py | 30 - test/cluster/backup_test.py | 173 ------ test/cluster/base_cluster_test.py | 90 --- test/cluster/base_environment.py | 549 ------------------ test/cluster/drain_test.py | 177 ------ test/cluster/k8s_environment.py | 274 --------- test/cluster/local_environment.py | 27 - test/cluster/reparent_test.py | 300 ---------- test/cluster/sandbox/__init__.py | 0 test/cluster/sandbox/create_schema.py | 52 -- test/cluster/sandbox/example_sandbox.yaml | 46 -- test/cluster/sandbox/fix_served_types.py | 129 ---- test/cluster/sandbox/fix_served_types_test.py | 117 ---- test/cluster/sandbox/gke.py | 88 --- test/cluster/sandbox/initial_reparent.py | 105 ---- test/cluster/sandbox/kubernetes_components.py | 149 ----- test/cluster/sandbox/naming/README.md | 2 - test/cluster/sandbox/naming/adjectives.txt | 167 ------ test/cluster/sandbox/naming/animals.txt | 206 ------- test/cluster/sandbox/sandbox.py | 173 ------ test/cluster/sandbox/sandbox_utils.py | 54 -- test/cluster/sandbox/sandlet.py | 163 ------ test/cluster/sandbox/sandlet_test.py | 87 --- test/cluster/sandbox/subprocess_component.py | 60 -- .../sandbox/vitess_kubernetes_sandbox.py | 284 --------- test/cluster/sandbox/vtctl_sandbox.py | 88 --- test/cluster/sandbox/wait_for_mysql.py | 88 --- test/cluster/vtctl_helper.py | 106 ---- 28 files changed, 3784 deletions(-) delete mode 100644 test/cluster/aws_environment.py delete mode 100755 test/cluster/backup_test.py delete mode 100644 test/cluster/base_cluster_test.py delete mode 100644 test/cluster/base_environment.py delete mode 100755 test/cluster/drain_test.py delete mode 100644 test/cluster/k8s_environment.py delete mode 100644 test/cluster/local_environment.py delete mode 100755 test/cluster/reparent_test.py delete mode 100644 test/cluster/sandbox/__init__.py delete mode 100755 test/cluster/sandbox/create_schema.py delete mode 100644 test/cluster/sandbox/example_sandbox.yaml delete mode 100755 test/cluster/sandbox/fix_served_types.py delete mode 100644 test/cluster/sandbox/fix_served_types_test.py delete mode 100755 test/cluster/sandbox/gke.py delete mode 100755 test/cluster/sandbox/initial_reparent.py delete mode 100755 test/cluster/sandbox/kubernetes_components.py delete mode 100644 test/cluster/sandbox/naming/README.md delete mode 100644 test/cluster/sandbox/naming/adjectives.txt delete mode 100644 test/cluster/sandbox/naming/animals.txt delete mode 100755 test/cluster/sandbox/sandbox.py delete mode 100644 test/cluster/sandbox/sandbox_utils.py delete mode 100644 test/cluster/sandbox/sandlet.py delete mode 100644 test/cluster/sandbox/sandlet_test.py delete mode 100644 test/cluster/sandbox/subprocess_component.py delete mode 100755 test/cluster/sandbox/vitess_kubernetes_sandbox.py delete mode 100755 test/cluster/sandbox/vtctl_sandbox.py delete mode 100755 test/cluster/sandbox/wait_for_mysql.py delete mode 100644 test/cluster/vtctl_helper.py diff --git a/test/cluster/aws_environment.py b/test/cluster/aws_environment.py deleted file mode 100644 index 069b678a0d9..00000000000 --- a/test/cluster/aws_environment.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""AWS environment.""" - -import base_environment - - -class AwsEnvironment(base_environment.BaseEnvironment): - """Environment for AWS clusters. CURRENTLY UNSUPPORTED.""" - - def __init__(self): - super(AwsEnvironment, self).__init__() - - def use_named(self, instance_name): - pass - - def create(self, **kwargs): - pass diff --git a/test/cluster/backup_test.py b/test/cluster/backup_test.py deleted file mode 100755 index 3bfa01b32ff..00000000000 --- a/test/cluster/backup_test.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests backup/restore when a tablet is restarted.""" - -import json -import os -import random -import time - -import logging - -from vtdb import keyrange -from vttest import sharding_utils -import base_cluster_test - - -def tearDownModule(): - pass - - -class BackupTest(base_cluster_test.BaseClusterTest): - - @classmethod - def setUpClass(cls): - super(BackupTest, cls).setUpClass() - - # number of backup iterations - cls.num_backups = int(cls.test_params.get('num_backups', '1')) - - # number of insert statements - cls.num_inserts = int(cls.test_params.get('num_inserts', '100')) - - def setUp(self): - """Updates schema, adding a table and populating it with data.""" - super(BackupTest, self).setUp() - os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' - os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION'] = '2' - self.table_name = 'vt_insert_test' - - self.env.delete_table(self.table_name) - self.env.create_table(self.table_name, validate_deadline_s=120) - - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - for shard_name in sharding_utils.get_shard_names(num_shards): - logging.info('Inserting %d rows into %s', - self.num_inserts, self.table_name) - kr = keyrange.KeyRange('') if num_shards == 1 else keyrange.KeyRange( - shard_name) - - master_tablet = self.env.get_current_master_name(keyspace, shard_name) - master_cell = self.env.get_tablet_cell(master_tablet) - conn = self.env.get_vtgate_conn(master_cell) - cursor = conn.cursor(tablet_type='master', keyspace=keyspace, - keyranges=[kr], writable=True) - for i in xrange(self.num_inserts): - cursor.begin() - cursor.execute( - 'insert into %s (msg, keyspace_id) values (:msg, :keyspace_id)' % - self.table_name, {'msg': 'test %d' % i, 'keyspace_id': 0}) - cursor.commit() - cursor.close() - logging.info('Data insertion complete') - - def tearDown(self): - self.env.delete_table(self.table_name) - super(BackupTest, self).tearDown() - - def perform_backup(self, tablets): - """Backup specific tablets. - - Args: - tablets: List of tablet names to be backed up - """ - for tablet in tablets: - self.env.backup(tablet) - - def perform_restore(self, tablets, num_shards): - """Restore tablets by restarting the alloc. - - Args: - tablets: List of tablet names to be restored - num_shards: Number of shards for the specific keyspace (int) - """ - # First call restart on all tablets being restored - for tablet in tablets: - self.env.restart_mysql_task(tablet, 'mysql', is_alloc=True) - - # Wait for the tablets to be unhealthy - start_time = time.time() - unhealthy_tablets = [] - while time.time() - start_time < 120: - if len(unhealthy_tablets) == len(tablets): - break - for tablet_name in tablets: - if tablet_name in unhealthy_tablets: - continue - logging.info('Waiting for tablet %s to be unhealthy', tablet_name) - if not self.env.is_tablet_healthy(tablet_name): - unhealthy_tablets.append(tablet_name) - logging.info('Tablet %s is now unhealthy', tablet_name) - time.sleep(1) - else: - logging.info('Timed out, some tablets did not become unhealthy: %s', - ' '.join([x for x in tablets if x not in unhealthy_tablets])) - - # Wait for the tablet to be healthy according to vttablet health - start_time = time.time() - healthy_tablets = [] - while time.time() - start_time < 600: - if len(healthy_tablets) == len(tablets): - break - for tablet_name in tablets: - if tablet_name in healthy_tablets: - continue - logging.info('Waiting for tablet %s to be healthy', tablet_name) - if self.env.is_tablet_healthy(tablet_name): - healthy_tablets.append(tablet_name) - logging.info('Tablet %s is now healthy', tablet_name) - time.sleep(1) - else: - logging.info('Timed out, some tablets are still unhealthy: %s', - ' '.join([x for x in tablets if x not in healthy_tablets])) - - for tablet_name in tablets: - logging.info('Waiting for tablet %s to enter serving state', tablet_name) - self.env.poll_for_varz( - tablet_name, ['TabletStateName'], timeout=300.0, - condition_fn=lambda v: v['TabletStateName'] == 'SERVING', - condition_msg='TabletStateName == Serving') - logging.info('Done') - count = json.loads(self.env.vtctl_helper.execute_vtctl_command( - ['ExecuteFetchAsDba', '-json', tablet_name, - 'select * from %s' % self.table_name]))['rows_affected'] - logging.info('Select count: %d', count) - self.assertEquals(count, self.num_inserts) - - def test_backup(self): - logging.info('Performing %s backup cycles', self.num_backups) - for attempt in xrange(self.num_backups): - logging.info('Backup iteration %d of %d', attempt + 1, self.num_backups) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - backup_tablets = [] - for shard in xrange(num_shards): - # Pick a random replica tablet in each shard - tablets = self.env.get_tablet_types_for_shard( - keyspace, sharding_utils.get_shard_name(shard, num_shards)) - available_tablets = [x for x in tablets if x[1] == 'replica'] - self.assertTrue( - len(available_tablets), 'No available tablets found to backup!') - tablet_to_backup_name = random.choice(available_tablets)[0] - backup_tablets.append(tablet_to_backup_name) - - self.perform_backup(backup_tablets) - self.perform_restore(backup_tablets, num_shards) - - -if __name__ == '__main__': - base_cluster_test.main() - diff --git a/test/cluster/base_cluster_test.py b/test/cluster/base_cluster_test.py deleted file mode 100644 index cb8cd5a4e63..00000000000 --- a/test/cluster/base_cluster_test.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Base class for all cluster tests.""" - -import logging -import optparse -import unittest -import sys - -_options = None - - -class BaseClusterTest(unittest.TestCase): - - @classmethod - def setUpClass(cls): - environment = None - if _options.environment_type == 'k8s': - import k8s_environment # pylint: disable=g-import-not-at-top - environment = k8s_environment.K8sEnvironment - if _options.environment_type == 'local': - import local_environment # pylint: disable=g-import-not-at-top - environment = local_environment.LocalEnvironment - elif _options.environment_type == 'aws': - import aws_environment # pylint: disable=g-import-not-at-top - environment = aws_environment.AwsEnvironment - - if not environment: - logging.fatal('No environment type selected') - - cls.created_environment = False - if _options.name: - cls.env = environment() - cls.env.use_named(_options.name) - elif _options.environment_params: - cls.env = environment() - environment_params = dict((k, v.replace(':', ',')) for k, v in ( - param.split('=') for param in _options.environment_params.split(','))) - cls.env.create(**environment_params) - cls.created_environment = True - else: - cls.create_default_local_environment() - if not cls.env: - logging.fatal('No test environment exists to test against!') - if _options.test_params: - cls.test_params = dict((k, v.replace(':', ',')) for k, v in ( - param.split('=') for param in _options.test_params.split(','))) - else: - cls.test_params = {} - - @classmethod - def tearDownClass(cls): - if cls.created_environment: - logging.info('Tearing down environment') - cls.env.destroy() - - @classmethod - def create_default_local_environment(cls): - pass - - -def main(): - import utils # pylint: disable=g-import-not-at-top - parser = optparse.OptionParser(usage='usage: %prog [options] [test_names]') - parser.add_option('-e', '--environment_type', help='Environment type', - default=None) - parser.add_option('-n', '--name', help='Environment name', default=None) - parser.add_option('-p', '--environment_params', - help='Environment parameters if creating an environment ' - 'for the test', default=None) - parser.add_option('-t', '--test_params', help='Test parameters', - default=None) - utils.add_options(parser) - global _options - _options, _ = parser.parse_args() - del sys.argv[1:] - - utils.main() diff --git a/test/cluster/base_environment.py b/test/cluster/base_environment.py deleted file mode 100644 index 436353c4989..00000000000 --- a/test/cluster/base_environment.py +++ /dev/null @@ -1,549 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Base environment for full cluster tests. - -Contains functions that all environments should implement along with functions -common to all environments. -""" - -# pylint: disable=unused-argument - -import json -import random -from vttest import sharding_utils - - -class VitessEnvironmentError(Exception): - pass - - -class BaseEnvironment(object): - """Base Environment.""" - - def __init__(self): - self.vtctl_helper = None - - def create(self, **kwargs): - """Create the environment. - - Args: - **kwargs: kwargs parameterizing the environment. - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'create unsupported in this environment') - - def use_named(self, instance_name): - """Populate this instance based on a pre-existing environment. - - Args: - instance_name: Name of the existing environment instance (string). - """ - self.master_capable_tablets = {} - for keyspace, num_shards in zip(self.keyspaces, self.num_shards): - self.master_capable_tablets[keyspace] = {} - for shard_name in sharding_utils.get_shard_names(num_shards): - raw_shard_tablets = self.vtctl_helper.execute_vtctl_command( - ['ListShardTablets', '%s/%s' % (keyspace, shard_name)]) - split_shard_tablets = [ - t.split(' ') for t in raw_shard_tablets.split('\n') if t] - self.master_capable_tablets[keyspace][shard_name] = [ - t[0] for t in split_shard_tablets - if (self.get_tablet_cell(t[0]) in self.primary_cells - and (t[3] == 'master' or t[3] == 'replica'))] - - def destroy(self): - """Teardown the environment. - - Raises: - VitessEnvironmentError: Raised if unsupported - """ - raise VitessEnvironmentError( - 'destroy unsupported in this environment') - - def create_table(self, table_name, schema=None, validate_deadline_s=60): - schema = schema or ( - 'create table %s (id bigint auto_increment, msg varchar(64), ' - 'keyspace_id bigint(20) unsigned NOT NULL, primary key (id)) ' - 'Engine=InnoDB' % table_name) - for keyspace in self.keyspaces: - self.vtctl_helper.execute_vtctl_command( - ['ApplySchema', '-sql', schema, keyspace]) - - def delete_table(self, table_name): - for keyspace in self.keyspaces: - self.vtctl_helper.execute_vtctl_command( - ['ApplySchema', '-sql', 'drop table if exists %s' % table_name, - keyspace]) - - def get_vtgate_conn(self, cell): - """Gets a connection to a vtgate in a particular cell. - - Args: - cell: cell to obtain a vtgate connection from (string). - - Returns: - A vtgate connection. - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'get_vtgate_conn unsupported in this environment') - - def restart_mysql_task(self, tablet_name, task_name, is_alloc=False): - """Restart a job within the mysql alloc or the whole alloc itself. - - Args: - tablet_name: tablet associated with the mysql instance (string). - task_name: Name of specific task (droid, vttablet, mysql, etc.). - is_alloc: True to restart entire alloc. - - Returns: - return restart return val. - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'restart_mysql_task unsupported in this environment') - - def restart_vtgate(self, cell=None, task_num=None): - """Restarts a vtgate task. - - If cell and task_num are unspecified, restarts a random task in a random - cell. - - Args: - cell: cell containing the vtgate task to restart (string). - task_num: which vtgate task to restart (int). - - Returns: - return val for restart. - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'restart_vtgate unsupported in this environment') - - def wait_for_good_failover_status( - self, keyspace, shard_name, failover_completion_timeout_s=60): - """Wait until failover status shows complete. - - Repeatedly queries the master tablet for failover status until it is 'OFF'. - Most of the time the failover status check will immediately pass. When a - failover is in progress, it tends to take a good 5 to 10 attempts before - status is 'OFF'. - - Args: - keyspace: Name of the keyspace to reparent (string). - shard_name: name of the shard to verify (e.g. '-80') (string). - failover_completion_timeout_s: Failover completion timeout (int). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'wait_for_good_failover_status unsupported in this environment') - - def wait_for_healthy_tablets(self, deadline_s=300): - """Wait until all tablets report healthy status. - - Args: - deadline_s: Deadline timeout (seconds) (int). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'wait_for_healthy_tablets unsupported in this environment') - - def is_tablet_healthy(self, tablet_name): - vttablet_stream_health = json.loads(self.vtctl_helper.execute_vtctl_command( - ['VtTabletStreamHealth', tablet_name])) - return 'health_error' not in vttablet_stream_health['realtime_stats'] - - def get_next_master(self, keyspace, shard_name, cross_cell=False): - """Determine what instance to select as the next master. - - If the next master is cross-cell, rotate the master cell and use instance 0 - as the master. Otherwise, rotate the instance number. - - Args: - keyspace: the name of the keyspace to reparent (string). - shard_name: name of the shard to reparent (string). - cross_cell: Whether the desired reparent is to another cell (bool). - - Returns: - Tuple of cell, task num, tablet uid (string, int, string). - """ - num_tasks = self.keyspace_alias_to_num_instances_dict[keyspace]['replica'] - current_master = self.get_current_master_name(keyspace, shard_name) - current_master_cell = self.get_tablet_cell(current_master) - next_master_cell = current_master_cell - next_master_task = 0 - if cross_cell: - next_master_cell = self.primary_cells[( - self.primary_cells.index(current_master_cell) + 1) % len( - self.primary_cells)] - else: - next_master_task = ( - (self.get_tablet_task_number(current_master) + 1) % num_tasks) - tablets_in_cell = [tablet for tablet in - self.master_capable_tablets[keyspace][shard_name] - if self.get_tablet_cell(tablet) == next_master_cell] - return (next_master_cell, next_master_task, - tablets_in_cell[next_master_task]) - - def get_tablet_task_number(self, tablet_name): - """Gets a tablet's 0 based task number. - - Args: - tablet_name: Name of the tablet (string). - - Returns: - 0 based task number (int). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'get_tablet_task_number unsupported in this environment') - - def external_reparent(self, keyspace, shard_name, new_master_name): - """Perform a reparent through external means (Orchestrator, etc.). - - Args: - keyspace: name of the keyspace to reparent (string). - shard_name: shard name (string). - new_master_name: tablet name of the tablet to become master (string). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'external_reparent unsupported in this environment') - - def internal_reparent_available(self): - """Checks if the environment can do a vtctl reparent.""" - return 'PlannedReparentShard' in ( - self.vtctl_helper.execute_vtctl_command(['help'])) - - def automatic_reparent_available(self): - """Checks if the environment can automatically reparent.""" - return False - - def explicit_external_reparent_available(self): - """Checks if the environment can explicitly reparent via external tools.""" - return False - - def internal_reparent(self, keyspace, shard_name, new_master_name, - emergency=False): - """Performs an internal reparent through vtctl. - - Args: - keyspace: name of the keyspace to reparent (string). - shard_name: string representation of the shard to reparent (e.g. '-80'). - new_master_name: Name of the new master tablet (string). - emergency: True to perform an emergency reparent (bool). - """ - reparent_type = ( - 'EmergencyReparentShard' if emergency else 'PlannedReparentShard') - self.vtctl_helper.execute_vtctl_command( - [reparent_type, '%s/%s' % (keyspace, shard_name), new_master_name]) - self.vtctl_helper.execute_vtctl_command(['RebuildKeyspaceGraph', keyspace]) - - def get_current_master_cell(self, keyspace): - """Obtains current master cell. - - This gets the master cell for the first shard in the keyspace, and assumes - that all shards share the same master. - - Args: - keyspace: name of the keyspace to get the master cell for (string). - - Returns: - master cell name (string). - """ - num_shards = self.num_shards[self.keyspaces.index(keyspace)] - first_shard_name = sharding_utils.get_shard_name(0, num_shards) - first_shard_master_tablet = ( - self.get_current_master_name(keyspace, first_shard_name)) - return self.get_tablet_cell(first_shard_master_tablet) - - def get_current_master_name(self, keyspace, shard_name): - """Obtains current master's tablet name (cell-uid). - - Args: - keyspace: name of the keyspace to get information on the master. - shard_name: string representation of the shard in question (e.g. '-80'). - - Returns: - master tablet name (cell-uid) (string). - """ - shard_info = json.loads(self.vtctl_helper.execute_vtctl_command( - ['GetShard', '{0}/{1}'.format(keyspace, shard_name)])) - master_alias = shard_info['master_alias'] - return '%s-%s' % (master_alias['cell'], master_alias['uid']) - - def get_random_tablet(self, keyspace=None, shard_name=None, cell=None, - tablet_type=None, task_number=None): - """Get a random tablet name. - - Args: - keyspace: name of the keyspace to get information on the master. - shard_name: shard to select tablet from (None for random) (string). - cell: cell to select tablet from (None for random) (string). - tablet_type: type of tablet to select (None for random) (string). - task_number: a specific task number (None for random) (int). - - Returns: - random tablet name (cell-uid) (string). - """ - keyspace = keyspace or random.choice(self.keyspaces) - shard_name = shard_name or ( - sharding_utils.get_shard_name( - random.randint(0, self.shards[self.keyspaces.index(keyspace)]))) - cell = cell or random.choice(self.cells) - tablets = [t.split(' ') for t in self.vtctl_helper.execute_vtctl_command( - ['ListShardTablets', '%s/%s' % (keyspace, shard_name)]).split('\n')] - cell_tablets = [t for t in tablets if self.get_tablet_cell(t[0]) == cell] - if task_number: - return cell_tablets[task_number][0] - if tablet_type: - return random.choice([t[0] for t in cell_tablets if t[3] == tablet_type]) - return random.choice(cell_tablets)[0] - - def get_tablet_cell(self, tablet_name): - """Get the cell of a tablet. - - Args: - tablet_name: Name of the tablet, including cell prefix (string). - - Returns: - Tablet's cell (string). - """ - return tablet_name.split('-')[0] - - def get_tablet_uid(self, tablet_name): - """Get the uid of a tablet. - - Args: - tablet_name: Name of the tablet, including cell prefix (string). - - Returns: - Tablet's uid (int). - """ - return int(tablet_name.split('-')[-1]) - - def get_tablet_keyspace(self, tablet_name): - """Get the keyspace of a tablet. - - Args: - tablet_name: Name of the tablet, including cell prefix (string). - - Returns: - Tablet's keyspace (string). - """ - return json.loads(self.vtctl_helper.execute_vtctl_command( - ['GetTablet', tablet_name]))['keyspace'] - - def get_tablet_shard(self, tablet_name): - """Get the shard of a tablet. - - Args: - tablet_name: Name of the tablet, including cell prefix (string). - - Returns: - Tablet's shard (string). - """ - return json.loads(self.vtctl_helper.execute_vtctl_command( - ['GetTablet', tablet_name]))['shard'] - - def get_tablet_type(self, tablet_name): - """Get the current type of the tablet as reported via vtctl. - - Args: - tablet_name: Name of the tablet, including cell prefix (string). - - Returns: - Current tablet type (e.g. spare, replica, rdonly) (string). - """ - return json.loads(self.vtctl_helper.execute_vtctl_command( - ['GetTablet', tablet_name]))['type'] - - def get_tablet_ip_port(self, tablet_name): - """Get the ip and port of the tablet as reported via vtctl. - - Args: - tablet_name: Name of the tablet, including cell prefix (string). - - Returns: - ip:port (string). - """ - tablet_info = json.loads(self.vtctl_helper.execute_vtctl_command( - ['GetTablet', tablet_name])) - host = tablet_info['hostname'] - if ':' in host: - # If host is an IPv6 address we need to put it into square brackets to - # form a correct "host:port" value. - host = '[%s]' % host - return '%s:%s' % (host, tablet_info['port_map']['vt']) - - def get_tablet_types_for_shard(self, keyspace, shard_name): - """Get the types for all tablets in a shard. - - Args: - keyspace: Name of keyspace to get tablet information on (string). - shard_name: single shard to obtain tablet types from (string). - - Returns: - List of pairs of tablet's name and type. - """ - tablet_info = [] - raw_tablets = self.vtctl_helper.execute_vtctl_command( - ['ListShardTablets', '{0}/{1}'.format(keyspace, shard_name)]) - raw_tablets = filter(None, raw_tablets.split('\n')) - for tablet in raw_tablets: - tablet_words = tablet.split() - tablet_name = tablet_words[0] - tablet_type = tablet_words[3] - tablet_info.append((tablet_name, tablet_type)) - return tablet_info - - def get_all_tablet_types(self, keyspace, num_shards): - """Get the types for all tablets in a keyspace. - - Args: - keyspace: Name of keyspace to get tablet information on (string). - num_shards: number of shards in the keyspace (int). - - Returns: - List of pairs of tablet's name and type. - """ - tablet_info = [] - for shard_name in sharding_utils.get_shard_names(num_shards): - tablet_info += self.get_tablet_types_for_shard(keyspace, shard_name) - return tablet_info - - def backup(self, tablet_name): - """Wait until all tablets report healthy status. - - Args: - tablet_name: Name of tablet to backup (string). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'backup unsupported in this environment') - - def drain_tablet(self, tablet_name, duration_s=600): - """Add a drain from a tablet. - - Args: - tablet_name: vttablet to drain (string). - duration_s: how long to have the drain exist for, in seconds (int). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'drain_tablet unsupported in this environment') - - def is_tablet_drained(self, tablet_name): - """Checks whether a tablet is drained. - - Args: - tablet_name: vttablet to drain (string). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'is_tablet_drained unsupported in this environment') - - def undrain_tablet(self, tablet_name): - """Remove a drain from a tablet. - - Args: - tablet_name: vttablet name to undrain (string). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'undrain_tablet unsupported in this environment') - - def is_tablet_undrained(self, tablet_name): - """Checks whether a tablet is undrained. - - Args: - tablet_name: vttablet to undrain (string). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'is_tablet_undrained unsupported in this environment') - - def poll_for_varz(self, tablet_name, varz, timeout=60.0, - condition_fn=None, converter=str, condition_msg=None): - """Polls for varz to exist, or match specific conditions, within a timeout. - - Args: - tablet_name: the name of the process that we're trying to poll vars from. - varz: name of the vars to fetch from varz. - timeout: number of seconds that we should attempt to poll for. - condition_fn: a function that takes the var as input, and returns a truthy - value if it matches the success conditions. - converter: function to convert varz value. - condition_msg: string describing the conditions that we're polling for, - used for error messaging. - - Returns: - dict of requested varz. - - Raises: - VitessEnvironmentError: Raised if unsupported or if the varz conditions - aren't met within the given timeout. - """ - raise VitessEnvironmentError( - 'poll_for_varz unsupported in this environment') - - def truncate_usertable(self, keyspace, shard, table=None): - tablename = table or self.tablename - master_tablet = self.get_current_master_name(keyspace, shard) - self.vtctl_helper.execute_vtctl_command( - ['ExecuteFetchAsDba', master_tablet, 'truncate %s' % tablename]) - - def get_tablet_query_total_count(self, tablet_name): - """Gets the total query count of a specified tablet. - - Args: - tablet_name: Name of the tablet to get query count from (string). - - Returns: - Query total count (int). - - Raises: - VitessEnvironmentError: Raised if unsupported. - """ - raise VitessEnvironmentError( - 'get_tablet_query_total_count unsupported in this environment') - diff --git a/test/cluster/drain_test.py b/test/cluster/drain_test.py deleted file mode 100755 index ae573d5c90f..00000000000 --- a/test/cluster/drain_test.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests that the topology updates when performing drains.""" - -import json -import os -import random -import time - -import logging -import base_cluster_test -from vtdb import keyrange -from vttest import sharding_utils - - -def tearDownModule(): - pass - - -class DrainTest(base_cluster_test.BaseClusterTest): - - @classmethod - def setUpClass(cls): - super(DrainTest, cls).setUpClass() - - # number of drain iterations - cls.num_drains = int(cls.test_params.get('num_drains', '10')) - - # seconds to wait for adding/removing a drain to take effect - cls.drain_timeout_threshold = ( - int(cls.test_params.get('drain_timeout_threshold', '90'))) - - cls.num_inserts = int(cls.test_params.get('num_inserts', 100)) - - @classmethod - def tearDownClass(cls): - for keyspace, num_shards in zip(cls.env.keyspaces, cls.env.num_shards): - for shard_name in sharding_utils.get_shard_names(num_shards): - drained_tablets = [x[0] for x in cls.env.get_tablet_types_for_shard( - keyspace, shard_name) if x[1] == 'drained'] - for tablet in drained_tablets: - logging.info('Undraining tablet %s', tablet) - cls.env.undrain_tablet(tablet) - - def insert_rows(self, keyspace, num_rows, starting_index=0): - logging.info('Inserting %d rows into %s', num_rows, self.table_name) - master_cell = self.env.get_current_master_cell(keyspace) - conn = self.env.get_vtgate_conn(master_cell) - cursor = conn.cursor(tablet_type='master', keyspace=keyspace, - keyranges=[keyrange.KeyRange('')], writable=True) - - for i in xrange(starting_index, starting_index + num_rows): - cursor.begin() - cursor.execute( - 'insert into %s (msg, keyspace_id) values (:msg, :keyspace_id)' % - self.table_name, {'msg': 'test %d' % i, 'keyspace_id': 0}) - cursor.commit() - cursor.close() - logging.info('Data insertion complete') - - def read_rows(self, keyspace, num_reads, num_rows, cell, - tablet_type='replica'): - logging.info('Reading %d rows from %s', num_reads, self.table_name) - - conn = self.env.get_vtgate_conn(cell) - cursor = conn.cursor( - tablet_type=tablet_type, keyspace=keyspace, - keyranges=[keyrange.KeyRange('')]) - - for i in [random.randint(0, num_rows - 1) for _ in xrange(num_reads)]: - cursor.execute( - 'select * from %s where msg = "test %d"' % (self.table_name, i), {}) - cursor.close() - logging.info('Data reads complete') - - def get_row_count(self, tablet_name): - fetch = self.env.vtctl_helper.execute_vtctl_command( - ['ExecuteFetchAsDba', '-json', tablet_name, - 'select count(*) from %s' % self.table_name]) - return json.loads(fetch)['rows'][0][0] - - def get_tablet_to_drain(self, keyspace, num_shards): - # obtain information on all tablets in a keyspace - keyspace_tablets = self.env.get_all_tablet_types(keyspace, num_shards) - - # filter only the tablets whose type we care about. - available_tablets = [x for x in keyspace_tablets if x[1] == 'replica'] - self.assertNotEqual(len(available_tablets), 0, - 'No available tablets found to drain!') - - # choose a tablet randomly and get its name - tablet_to_drain_info = random.choice(available_tablets) - return str(tablet_to_drain_info[0]) - - def wait_for_drained_tablet(self, tablet_to_drain): - # Check tablet type until the tablet becomes drained - drain_added_time = time.time() - while time.time() - drain_added_time < self.drain_timeout_threshold: - if self.env.is_tablet_drained(tablet_to_drain): - logging.info('Tablet %s is now drained (took %f seconds)', - tablet_to_drain, time.time() - drain_added_time) - break - time.sleep(1) - else: - logging.info('Timed out waiting for tablet %s to go drained!', - tablet_to_drain) - - def wait_for_undrained_tablet(self, tablet_to_drain): - # Check tablet type until the tablet is no longer drained - drain_removed_time = time.time() - while time.time() - drain_removed_time < self.drain_timeout_threshold: - if self.env.is_tablet_undrained(tablet_to_drain): - logging.info('Tablet %s is now undrained (took %f seconds)', - tablet_to_drain, time.time() - drain_removed_time) - break - time.sleep(1) - else: - logging.info('Timed out waiting for tablet %s to be undrained!', - tablet_to_drain) - - def setUp(self): - super(DrainTest, self).setUp() - os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' - os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION'] = '2' - self.table_name = 'vt_insert_test' - - self.env.delete_table(self.table_name) - self.env.create_table(self.table_name) - - def test_drain(self): - """Drain tablets and verify topology is updated. - - Each iteration drains a random tablet in each keyspace. Once it is verified - that the tablet is unhealthy, the drain is removed and it is verified that - the tablet returns to a healthy state. - """ - logging.info('Performing %s drain cycles', self.num_drains) - for attempt in xrange(self.num_drains): - logging.info('Drain iteration %d of %d', attempt + 1, self.num_drains) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - tablet_to_drain = self.get_tablet_to_drain(keyspace, num_shards) - num_rows0 = self.get_row_count(tablet_to_drain) - self.env.drain_tablet(tablet_to_drain) - self.wait_for_drained_tablet(tablet_to_drain) - self.insert_rows(keyspace, self.num_inserts, - starting_index=self.num_inserts * attempt) - num_rows1 = self.get_row_count(tablet_to_drain) - self.assertEquals(num_rows0, num_rows1) - query_count0 = self.env.get_tablet_query_total_count(tablet_to_drain) - self.read_rows( - keyspace, self.num_inserts, self.num_inserts * (attempt + 1), - self.env.get_tablet_cell(tablet_to_drain)) - self.env.undrain_tablet(tablet_to_drain) - self.wait_for_undrained_tablet(tablet_to_drain) - query_count1 = self.env.get_tablet_query_total_count(tablet_to_drain) - logging.info('%s total query count before/after reads: %d/%d', - tablet_to_drain, query_count0, query_count1) - self.assertEquals(query_count0, query_count1) - - -if __name__ == '__main__': - base_cluster_test.main() - diff --git a/test/cluster/k8s_environment.py b/test/cluster/k8s_environment.py deleted file mode 100644 index d0026fb70b5..00000000000 --- a/test/cluster/k8s_environment.py +++ /dev/null @@ -1,274 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Kubernetes environment.""" - -import getpass -import json -import logging -import os -import subprocess -import time - -from sandbox import kubernetes_components -from vtproto import topodata_pb2 -from vtdb import vtgate_client -import base_environment -import protocols_flavor -import vtctl_helper - - -class K8sEnvironment(base_environment.BaseEnvironment): - """Environment for kubernetes clusters on Google Compute Engine.""" - - def __init__(self): - super(K8sEnvironment, self).__init__() - - def use_named(self, instance_name): - # Check to make sure kubectl exists - try: - subprocess.check_output(['kubectl']) - except OSError: - raise base_environment.VitessEnvironmentError( - 'kubectl not found, please install by visiting kubernetes.io or ' - 'running gcloud components update kubectl if using compute engine.') - - vtctld_ip = kubernetes_components.get_forwarded_ip( - 'vtctld', instance_name) - self.vtctl_addr = '%s:15999' % vtctld_ip - - self.vtctl_helper = vtctl_helper.VtctlHelper('grpc', self.vtctl_addr) - self.cluster_name = instance_name - - keyspaces = self.vtctl_helper.execute_vtctl_command(['GetKeyspaces']) - self.mobs = filter(None, keyspaces.split('\n')) - self.keyspaces = self.mobs - - if not self.keyspaces: - raise base_environment.VitessEnvironmentError( - 'Invalid environment, no keyspaces found') - - self.num_shards = [] - self.shards = [] - - for keyspace in self.keyspaces: - shards = json.loads(self.vtctl_helper.execute_vtctl_command( - ['FindAllShardsInKeyspace', keyspace])) - self.shards.append(shards) - self.num_shards.append(len(shards)) - - # This assumes that all keyspaces use the same set of cells - self.cells = json.loads(self.vtctl_helper.execute_vtctl_command( - ['GetShard', '%s/%s' % (self.keyspaces[0], self.shards[0].keys()[0])] - ))['cells'] - - self.primary_cells = self.cells - self.replica_instances = [] - self.rdonly_instances = [] - - # This assumes that all cells are equivalent for k8s environments. - all_tablets_in_a_cell = self.vtctl_helper.execute_vtctl_command( - ['ListAllTablets', self.cells[0]]) - all_tablets_in_a_cell = [x.split(' ') for x in - filter(None, all_tablets_in_a_cell.split('\n'))] - - for index, keyspace in enumerate(self.keyspaces): - keyspace_tablets_in_cell = [ - tablet for tablet in all_tablets_in_a_cell if tablet[1] == keyspace] - replica_tablets_in_cell = [ - tablet for tablet in keyspace_tablets_in_cell - if tablet[3] == 'master' or tablet[3] == 'replica'] - replica_instances = len(replica_tablets_in_cell) / self.num_shards[index] - self.replica_instances.append(replica_instances) - self.rdonly_instances.append( - (len(keyspace_tablets_in_cell) / self.num_shards[index]) - - replica_instances) - - # Converts keyspace name and alias to number of instances - self.keyspace_alias_to_num_instances_dict = {} - for index, keyspace in enumerate(self.keyspaces): - self.keyspace_alias_to_num_instances_dict[keyspace] = { - 'replica': int(self.replica_instances[index]), - 'rdonly': int(self.rdonly_instances[index]) - } - - self.vtgate_addrs = {} - for cell in self.cells: - vtgate_ip = kubernetes_components.get_forwarded_ip( - 'vtgate-%s' % cell, instance_name) - self.vtgate_addrs[cell] = '%s:15991' % vtgate_ip - super(K8sEnvironment, self).use_named(instance_name) - - def create(self, **kwargs): - self.create_gke_cluster = ( - kwargs.get('create_gke_cluster', 'false').lower() != 'false') - if self.create_gke_cluster and 'GKE_NUM_NODES' not in kwargs: - raise base_environment.VitessEnvironmentError( - 'Must specify GKE_NUM_NODES') - if 'GKE_CLUSTER_NAME' not in kwargs: - kwargs['GKE_CLUSTER_NAME'] = getpass.getuser() - if 'VITESS_NAME' not in kwargs: - kwargs['VITESS_NAME'] = getpass.getuser() - kwargs['TEST_MODE'] = '1' - self.script_dir = os.path.join(os.environ['VTROOT'], 'examples/kubernetes') - try: - subprocess.check_output(['gcloud', 'config', 'list']) - except OSError: - raise base_environment.VitessEnvironmentError( - 'gcloud not found, please install by visiting cloud.google.com') - if 'project' in kwargs: - logging.info('Setting project to %s', kwargs['project']) - subprocess.check_output( - ['gcloud', 'config', 'set', 'project', kwargs['project']]) - project_name_json = json.loads(subprocess.check_output( - ['gcloud', 'config', 'list', 'project', '--format', 'json'])) - project_name = project_name_json['core']['project'] - logging.info('Current project name: %s', project_name) - for k, v in kwargs.iteritems(): - os.environ[k] = v - if self.create_gke_cluster: - cluster_up_txt = subprocess.check_output( - [os.path.join(self.script_dir, 'cluster-up.sh')], - cwd=self.script_dir, stderr=subprocess.STDOUT) - logging.info(cluster_up_txt) - vitess_up_output = subprocess.check_output( - [os.path.join(self.script_dir, 'vitess-up.sh')], - cwd=self.script_dir, stderr=subprocess.STDOUT) - logging.info(vitess_up_output) - self.use_named(kwargs['VITESS_NAME']) - - def destroy(self): - vitess_down_output = subprocess.check_output( - [os.path.join(self.script_dir, 'vitess-down.sh')], - cwd=self.script_dir, stderr=subprocess.STDOUT) - logging.info(vitess_down_output) - if self.create_gke_cluster: - cluster_down_output = subprocess.check_output( - [os.path.join(self.script_dir, 'cluster-down.sh')], - cwd=self.script_dir, stderr=subprocess.STDOUT) - logging.info(cluster_down_output) - - def get_vtgate_conn(self, cell): - return vtgate_client.connect( - protocols_flavor.protocols_flavor().vtgate_python_protocol(), - self.vtgate_addrs[cell], 60) - - def restart_mysql_task(self, tablet_name, task_name, is_alloc=False): - # Delete the whole pod, which deletes mysql + vttablet tasks. - os.system('kubectl delete pod %s --namespace=%s' % ( - self.get_tablet_pod_name(tablet_name), self.cluster_name)) - return 0 - - def wait_for_good_failover_status( - self, keyspace, shard_name, failover_completion_timeout_s=60): - return 0 - - def poll_for_varz(self, tablet_name, varz, timeout=60.0, - condition_fn=None, converter=str, condition_msg=None): - """Polls for varz to exist, or match specific conditions, within a timeout. - - Args: - tablet_name: the name of the process that we're trying to poll vars from. - varz: name of the vars to fetch from varz - timeout: number of seconds that we should attempt to poll for. - condition_fn: a function that takes the var as input, and returns a truthy - value if it matches the success conditions. - converter: function to convert varz value - condition_msg: string describing the conditions that we're polling for, - used for error messaging. - - Raises: - VitessEnvironmentError: Raised if the varz conditions aren't met within - the given timeout. - - Returns: - dict of requested varz. - """ - start_time = time.time() - while True: - if (time.time() - start_time) >= timeout: - timeout_error_msg = 'Timed out polling for varz.' - if condition_fn and condition_msg: - timeout_error_msg += ' Condition "%s" not met.' % condition_msg - raise base_environment.VitessEnvironmentError(timeout_error_msg) - hostname = self.get_tablet_ip_port(tablet_name) - host_varz = subprocess.check_output([ - 'kubectl', 'exec', '-ti', self.get_tablet_pod_name(tablet_name), - '--namespace=%s' % self.cluster_name, - 'curl', '%s/debug/vars' % hostname]) - if not host_varz: - continue - host_varz = json.loads(host_varz) - if condition_fn is None or condition_fn(host_varz): - return host_varz - - def wait_for_healthy_tablets(self): - return 0 - - def get_tablet_pod_name(self, tablet_name): - tablet_info = json.loads(self.vtctl_helper.execute_vtctl_command( - ['GetTablet', tablet_name])) - # Hostname is .vttablet - return tablet_info['hostname'].split('.')[0] - - def get_tablet_task_number(self, tablet_name): - # Tablet pod name under StatefulSet is - # "----" - # Example: test1-foo-0-replica-0. - return int(self.get_tablet_pod_name(tablet_name).split('-')[-1]) - - def automatic_reparent_available(self): - """Checks if the environment can automatically reparent.""" - p1 = subprocess.Popen( - ['kubectl', 'get', 'pods', '--namespace=%s' % self.cluster_name], - stdout=subprocess.PIPE) - p2 = subprocess.Popen( - ['grep', 'orchestrator'], stdin=p1.stdout, stdout=subprocess.PIPE) - output = p2.communicate()[0] - return bool(output) - - def internal_reparent(self, keyspace, shard_name, new_master_uid, - emergency=False): - reparent_command = ( - 'EmergencyReparentShard' if emergency else 'PlannedReparentShard') - self.vtctl_helper.execute_vtctl_command( - [reparent_command, '-keyspace_shard', '%s/%s' % (keyspace, shard_name), - '-new_master', new_master_uid]) - self.vtctl_helper.execute_vtctl_command(['RebuildKeyspaceGraph', keyspace]) - return 0, 'No output' - - def backup(self, tablet_name): - logging.info('Backing up tablet %s', tablet_name) - self.vtctl_helper.execute_vtctl_command(['Backup', tablet_name]) - - def drain_tablet(self, tablet_name, duration_s=600): - self.vtctl_helper.execute_vtctl_command(['StopSlave', tablet_name]) - self.vtctl_helper.execute_vtctl_command( - ['ChangeSlaveType', tablet_name, 'drained']) - - def is_tablet_drained(self, tablet_name): - return self.get_tablet_type(tablet_name) == topodata_pb2.DRAINED - - def undrain_tablet(self, tablet_name): - self.vtctl_helper.execute_vtctl_command( - ['ChangeSlaveType', tablet_name, 'replica']) - self.vtctl_helper.execute_vtctl_command(['StartSlave', tablet_name]) - - def is_tablet_undrained(self, tablet_name): - return not self.is_tablet_drained(tablet_name) - - def get_tablet_query_total_count(self, tablet_name): - return self.poll_for_varz( - tablet_name, ['Queries'])['Queries']['TotalCount'] - diff --git a/test/cluster/local_environment.py b/test/cluster/local_environment.py deleted file mode 100644 index d868a2d7502..00000000000 --- a/test/cluster/local_environment.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""A local test environment.""" - -import base_environment - - -class LocalEnvironment(base_environment.BaseEnvironment): - """Environment for locally run instances, CURRENTLY UNSUPPORTED.""" - - def __init__(self): - super(LocalEnvironment, self).__init__() - - def create(self, **kwargs): - pass diff --git a/test/cluster/reparent_test.py b/test/cluster/reparent_test.py deleted file mode 100755 index 756cdea388c..00000000000 --- a/test/cluster/reparent_test.py +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests reparenting is picked up in topology.""" - -import json -import threading -import time - -import numpy - -import logging - -import base_cluster_test -import vtctl_helper - -from vtproto import topodata_pb2 -from vttest import sharding_utils - - -def tearDownModule(): - pass - - -class ReparentTest(base_cluster_test.BaseClusterTest): - - @classmethod - def setUpClass(cls): - super(ReparentTest, cls).setUpClass() - - # number of reparent iterations - cls.num_reparents = int(cls.test_params.get('num_reparents', '1')) - - # max allowable median master downtime in seconds - cls.master_downtime_threshold = int(cls.test_params.get( - 'master_downtime_threshold', '20')) - - # seconds to wait for reparent to result in a new master - cls.reparent_timeout_threshold = int(cls.test_params.get( - 'reparent_timeout_threshold', '60')) - - for keyspace, num_shards in zip(cls.env.keyspaces, cls.env.num_shards): - for shard in xrange(num_shards): - shard_name = sharding_utils.get_shard_name(shard, num_shards) - backup_tablet_uid = cls.env.get_random_tablet( - keyspace, shard_name, tablet_type='replica') - logging.info('Taking a backup on tablet %s for %s/%s', - backup_tablet_uid, keyspace, shard_name) - cls.env.vtctl_helper.execute_vtctl_command( - ['Backup', backup_tablet_uid]) - - @classmethod - def tearDownClass(cls): - logging.info('Tearing down ReparentTest, verifying tablets are healthy.') - cls.env.wait_for_healthy_tablets() - super(ReparentTest, cls).tearDownClass() - - @classmethod - def create_default_local_environment(cls): - pass - - def explicit_reparent(self, keyspace, num_shards, external=False, - cross_cell=False): - """Performs an explicit reparent. - - This function will explicitly select a new master and verify that the - topology is updated. - - Args: - keyspace: Name of the keyspace to reparent (string) - num_shards: Total number of shards (int) - external: Whether the reparent should be external or through vtctl (bool) - cross_cell: Whether to reparent to a different cell (bool) - - Returns: - How long we waited for the reparent. - The time begins just before calling an explicit reparent. - This is a list of floats, one for each shard. - For cross-cell reparents, it returns []. - """ - next_masters = [] - durations = [] - - for shard in xrange(num_shards): - shard_name = sharding_utils.get_shard_name(shard, num_shards) - original_master = self.env.get_current_master_name(keyspace, shard_name) - - next_master = self.env.get_next_master(keyspace, shard_name, cross_cell) - next_masters.append(next_master) - - self.env.wait_for_good_failover_status(keyspace, shard_name) - - # Call Reparent in a separate thread. - def reparent_shard(shard_name, original_master, next_master): - logging.info('Reparenting %s/%s from %s to %s', keyspace, shard_name, - original_master, next_master[2]) - reparent_fn = self.env.external_reparent if external else ( - self.env.internal_reparent) - return_code, return_output = reparent_fn( - keyspace, shard_name, next_master[2]) - logging.info('Reparent returned %d for %s/%s: %s', - return_code, keyspace, shard_name, return_output) - - thread = threading.Thread(target=reparent_shard, - args=[shard_name, original_master, next_master]) - start_time = time.time() - thread.start() - - # Wait for the reparent. - while time.time() - start_time < self.reparent_timeout_threshold: - try: - tablet_health = json.loads( - self.env.vtctl_helper.execute_vtctl_command( - ['VtTabletStreamHealth', next_master[2]])) - if tablet_health['target']['tablet_type'] == topodata_pb2.MASTER: - duration = time.time() - start_time - durations.append(duration) - logging.info('Reparent took %f seconds', duration) - break - except (IndexError, KeyError, vtctl_helper.VtctlClientError) as e: - logging.info( - 'While waiting for reparent, got the following error: %s', e) - else: - self.fail('Timed out waiting for reparent on %s/%s' % ( - keyspace, shard_name)) - - thread.join() - - return durations - - def implicit_reparent( - self, keyspace, shard, num_shards, perform_emergency_reparent=False): - """Performs an implicit reparent. - - This function will restart the current master task and verify that a new - task was selected to be the master. - - Args: - keyspace: Name of the keyspace to reparent (string) - shard: Numeric ID of the shard to reparent (zero based int) - num_shards: Total number of shards (int) - perform_emergency_reparent: Do an emergency reparent as well (bool) - """ - - shard_name = sharding_utils.get_shard_name(shard, num_shards) - - original_master_name = ( - self.env.get_current_master_name(keyspace, shard_name)) - - logging.info('Restarting %s/%s, current master: %s', - keyspace, shard_name, original_master_name) - ret_val = self.env.restart_mysql_task(original_master_name, 'mysql', True) - - self.assertEquals(ret_val, 0, - msg='restart failed (returned %d)' % ret_val) - - if perform_emergency_reparent: - next_master = self.env.get_next_master(keyspace, shard_name)[2] - logging.info('Emergency reparenting %s/%s to %s', keyspace, shard_name, - next_master) - self.env.internal_reparent( - keyspace, shard_name, next_master, emergency=True) - - start_time = time.time() - while time.time() - start_time < self.reparent_timeout_threshold: - new_master_name = self.env.get_current_master_name(keyspace, shard_name) - if new_master_name != original_master_name: - break - time.sleep(1) - self.assertNotEquals( - new_master_name, original_master_name, - msg='Expected master tablet to change, but it remained as %s' % ( - new_master_name)) - logging.info('restart on %s/%s resulted in new master: %s', - keyspace, shard_name, new_master_name) - - def test_implicit_reparent(self): - logging.info('Performing %s implicit reparents', self.num_reparents) - if not self.env.automatic_reparent_available(): - logging.info('Automatic reparents are unavailable, skipping test!') - return - for attempt in xrange(1, self.num_reparents + 1): - logging.info('Implicit reparent iteration number %d of %d', attempt, - self.num_reparents) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - for shard in xrange(num_shards): - self.implicit_reparent(keyspace, shard, num_shards) - self.env.wait_for_healthy_tablets() - - def _test_explicit_emergency_reparent(self): - # This test is currently disabled until the emergency reparent can be - # fleshed out better. If a master tablet is killed and there is no - # tool performing automatic reparents (like Orchestrator), then there may be - # a race condition between restarting the tablet (in which it would resume - # being the master), and the EmergencyReparentShard call. This can sometimes - # result in two tablets being master. - logging.info('Performing %s explicit emergency reparents', - self.num_reparents) - if self.env.automatic_reparent_available(): - logging.info('Automatic reparents are available, skipping test!') - return - if not self.env.internal_reparent_available(): - logging.info('Internal reparent unavailable, skipping test!') - return - for attempt in xrange(1, self.num_reparents + 1): - logging.info('Explicit emergency reparent iteration number %d of %d', - attempt, self.num_reparents) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - for shard in xrange(num_shards): - self.implicit_reparent(keyspace, shard, num_shards, True) - self.env.wait_for_healthy_tablets() - - def test_explicit_reparent(self): - logging.info('Performing %s explicit reparents', self.num_reparents) - if not self.env.internal_reparent_available(): - logging.info('Internal reparent unavailable, skipping test!') - return - durations = [] - for attempt in xrange(1, self.num_reparents + 1): - logging.info('Explicit reparent iteration number %d of %d', attempt, - self.num_reparents) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - durations.extend(self.explicit_reparent(keyspace, num_shards)) - - durations = numpy.array(durations) - median_duration = numpy.median(durations) - logging.info('%d total reparents, median duration %f seconds', - len(durations), median_duration) - self.assertLessEqual(median_duration, self.master_downtime_threshold, - 'master downtime too high (performance regression)') - - def test_explicit_external_reparent(self): - logging.info('Performing %s explicit external reparents', - self.num_reparents) - if not self.env.explicit_external_reparent_available(): - logging.info('Explicit external reparent unavailable, skipping test!') - return - durations = [] - for attempt in xrange(1, self.num_reparents + 1): - logging.info('Explicit external reparent iteration number %d of %d', - attempt, self.num_reparents) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - durations.extend( - self.explicit_reparent(keyspace, num_shards, external=True)) - - durations = numpy.array(durations) - median_duration = numpy.median(durations) - logging.info('%d total reparents, median duration %f seconds', - len(durations), median_duration) - self.assertLessEqual(median_duration, self.master_downtime_threshold, - 'master downtime too high (performance regression)') - - def test_explicit_reparent_cross_cell(self): - if len(self.env.cells) < 2: - logging.info('Not enough cells to test cross_cell reparents!') - return - if not self.env.internal_reparent_available(): - logging.info('Internal reparent unavailable, skipping test!') - return - logging.info('Performing %s cross-cell explicit reparents', - self.num_reparents) - for attempt in xrange(1, self.num_reparents + 1): - logging.info('Cross-cell explicit reparent iteration number %d of %d', - attempt, self.num_reparents) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - self.explicit_reparent(keyspace, num_shards, cross_cell=True) - - def test_explicit_external_reparent_cross_cell(self): - logging.info('Performing %s cross-cell explicit external reparents', - self.num_reparents) - if len(self.env.cells) < 2: - logging.info('Not enough cells to test cross_cell reparents!') - return - if not self.env.explicit_external_reparent_available(): - logging.info('Explicit external reparent unavailable, skipping test!') - return - for attempt in xrange(1, self.num_reparents + 1): - logging.info('Cross-cell explicit external reparent iteration number %d ' - 'of %d', attempt, self.num_reparents) - for keyspace, num_shards in zip(self.env.keyspaces, self.env.num_shards): - self.explicit_reparent( - keyspace, num_shards, external=True, cross_cell=True) - - -if __name__ == '__main__': - base_cluster_test.main() - diff --git a/test/cluster/sandbox/__init__.py b/test/cluster/sandbox/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/cluster/sandbox/create_schema.py b/test/cluster/sandbox/create_schema.py deleted file mode 100755 index d60839bd013..00000000000 --- a/test/cluster/sandbox/create_schema.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Create schema based on an input sql file.""" - -import logging -import optparse -import os -import vtctl_sandbox - - -def main(): - parser = optparse.OptionParser(usage='usage: %prog [options] [test_names]') - parser.add_option( - '-n', '--namespace', help='Kubernetes namespace', default='vitess') - parser.add_option( - '-k', '--keyspace', help='Keyspace name', default='test_keyspace') - parser.add_option( - '-d', '--drop_table', help='An optional table name to drop') - parser.add_option( - '-s', '--sql_file', help='File containing sql schema', - default=os.path.join( - os.environ['VTROOT'], 'examples/kubernetes/create_test_table.sql')) - logging.getLogger().setLevel(logging.INFO) - - options, _ = parser.parse_args() - with open(options.sql_file, 'r') as sql_file: - sql = sql_file.read() - if options.drop_table: - vtctl_sandbox.execute_vtctl_command( - ['ApplySchema', '-sql', 'drop table if exists %s' % options.drop_table, - options.keyspace], namespace=options.namespace) - vtctl_sandbox.execute_vtctl_command( - ['ApplySchema', '-sql', sql, options.keyspace], - namespace=options.namespace) - - -if __name__ == '__main__': - main() diff --git a/test/cluster/sandbox/example_sandbox.yaml b/test/cluster/sandbox/example_sandbox.yaml deleted file mode 100644 index 9eb607dfc4e..00000000000 --- a/test/cluster/sandbox/example_sandbox.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# All sandbox configs start with sandbox. -sandbox: - # sandbox_name is a special keyword replaced with a passed in command line - # parameter or by a randomly generated name. - name: {{sandbox_name}} - # cluster_type is a special keyword replaced with a passed in command line - # parameter designating what cluster environment to use, such as gke. - # The chosen cluster_type must be listed in the clusters entry. - cluster_type: {{cluster_type}} - # clusters is a list of supported cluster types, along with the parameters - # need in order to start the cluster. This typically will involve machine - # types and counts. - clusters: - # Currently only gke is supported - - type: gke - # cluster_name is a special keyword, like sandbox_name, replaced via - # command line or randomly generated name. - name: {{cluster_name}} - node_count: 5 - machine_type: n1-standard-4 - # Application is application-specific, used by the derived Sandbox class. - application: - vtgate_count: 1 - enable_orchestrator: True - enable_guestbook: True - etcd_count: 3 - cells: - - test1 - - test2 - keyspaces: - - name: {{sandbox_name}} - shard_count: 1 - replica_count: 3 - rdonly_count: 0 - vtctld_image: vitess/root - vttablet_image: vitess/root - vtgate_image: vitess/root - vttablet_ram: 1Gi - vttablet_cpu: 500m - mysql_ram: 1Gi - mysql_cpu: 500m - vtgate_ram: 512Mi - vtgate_cpu: 500m - backup_flags: - backup_storage_implementation: gcs - gcs_backup_storage_bucket: 'my-builtin-backup' diff --git a/test/cluster/sandbox/fix_served_types.py b/test/cluster/sandbox/fix_served_types.py deleted file mode 100755 index 10c40bf117a..00000000000 --- a/test/cluster/sandbox/fix_served_types.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Fixes served types for each keyspace. - -This is only useful in the scenario where a set of overlapping shards is -created. This will happen in resharding tests. Since helm brings up all -vttablets at the same time, there is a race condition that determines which -shards are considered serving. In a resharding test, the unsharded set -should be serving, and the sharded set should not be. - -This script iterates through all keyspaces, and ensures that for any set of -shards within the same keyspace, the set with the fewest shards will be set to -serving. -""" - -import json -import logging -import optparse -import sys -from vtproto import topodata_pb2 -from vttest import sharding_utils -import vtctl_sandbox - -TABLET_TYPES = [ - (topodata_pb2.REPLICA, 'replica'), - (topodata_pb2.RDONLY, 'rdonly'), - (topodata_pb2.MASTER, 'master'), -] - - -def get_vtctl_commands(keyspace, shards): - """Gets a list of vtctl SetShardServedTypes commands. - - Args: - keyspace: (string) Keyspace name to obtain commands for. - shards: Dict from shard name to shard info object from - FindAllShardsInKeyspace vtctl command. - - Returns: - List of vtctl commands. - """ - lowest_sharding_count = min( - sharding_utils.get_shard_index(x)[1] for x in shards.keys()) - if lowest_sharding_count == len(shards): - # Skip keyspaces with non-overlapping shards. - return [] - - logging.info('Keyspace %s has overlapping shards', keyspace) - vtctl_commands = [] - for shard_name, shard_info in shards.iteritems(): - served_tablet_types = [ - x['tablet_type'] for x in shard_info.get('served_types', [])] - _, num_shards = sharding_utils.get_shard_index(shard_name) - is_lowest_sharding = num_shards == lowest_sharding_count - - for tablet_type, tablet_type_name in TABLET_TYPES: - # Setting served types when a shard is already serving is allowable, - # but only remove served types from the sharded set if they are serving. - if not (is_lowest_sharding or tablet_type in served_tablet_types): - continue - - vtctl_command = [ - 'SetShardServedTypes', '%s/%s' % (keyspace, shard_name), - tablet_type_name] - if not is_lowest_sharding: - vtctl_command.insert(1, '--remove') - - vtctl_commands.append(vtctl_command) - - return vtctl_commands - - -def fix_served_types(keyspaces, namespace): - """Ensures the smallest set of non-overlapping shards is serving. - - Args: - keyspaces: [string], list of keyspaces to fix. - namespace: string, Kubernetes namespace for vtctld calls. - - Returns: - 0 for success, 1 for failure - """ - for keyspace in keyspaces: - output, success = vtctl_sandbox.execute_vtctl_command( - ['FindAllShardsInKeyspace', keyspace], namespace=namespace) - - if not success: - logging.error('Failed to execute FindAllShardsInKeyspace: %s', output) - return 1 - - for cmd in get_vtctl_commands(keyspace, json.loads(output)): - logging.info('Executing %s', ' '.join(cmd)) - vtctl_sandbox.execute_vtctl_command(cmd, namespace=namespace) - - logging.info('Rebuilding keyspace %s', keyspace) - vtctl_sandbox.execute_vtctl_command( - ['RebuildKeyspaceGraph', keyspace], namespace=namespace) - return 0 - - -def main(): - parser = optparse.OptionParser(usage='usage: %prog [options] [test_names]') - parser.add_option('-n', '--namespace', help='Kubernetes namespace', - default='vitess') - parser.add_option('-k', '--keyspaces', help='Comma delimited keyspaces', - default='test_keyspace') - logging.getLogger().setLevel(logging.INFO) - - options, _ = parser.parse_args() - sys.exit( - fix_served_types(set(options.keyspaces.split(',')), options.namespace)) - - -if __name__ == '__main__': - main() diff --git a/test/cluster/sandbox/fix_served_types_test.py b/test/cluster/sandbox/fix_served_types_test.py deleted file mode 100644 index ed9c39c1929..00000000000 --- a/test/cluster/sandbox/fix_served_types_test.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests for fix_served_types.""" - -import collections -import unittest -from vtproto import topodata_pb2 -import fix_served_types - -_ALL_TYPES = dict( - served_types=[ - dict(tablet_type=topodata_pb2.REPLICA), - dict(tablet_type=topodata_pb2.RDONLY), - dict(tablet_type=topodata_pb2.MASTER), - ]) - -_NO_MASTER = dict( - served_types=[ - dict(tablet_type=topodata_pb2.REPLICA), - dict(tablet_type=topodata_pb2.RDONLY), - ]) - -_JUST_MASTER = dict( - served_types=[ - dict(tablet_type=topodata_pb2.MASTER), - ]) - - -class FixServedTypesTest(unittest.TestCase): - - def test_partial_serving_overlap_shard_1(self): - """Source shard and destination shard 1 are serving all types.""" - shards = collections.OrderedDict( - [('-80', _ALL_TYPES), ('80-', {}), ('0', _ALL_TYPES)]) - expected_result = [ - ['SetShardServedTypes', '--remove', 'foo/-80', 'replica'], - ['SetShardServedTypes', '--remove', 'foo/-80', 'rdonly'], - ['SetShardServedTypes', '--remove', 'foo/-80', 'master'], - ['SetShardServedTypes', 'foo/0', 'replica'], - ['SetShardServedTypes', 'foo/0', 'rdonly'], - ['SetShardServedTypes', 'foo/0', 'master']] - self.assertEquals( - fix_served_types.get_vtctl_commands('foo', shards), expected_result) - - def test_destination_shards_serving(self): - """Destination shards are serving all types.""" - shards = collections.OrderedDict( - [('-80', _ALL_TYPES), ('80-', _ALL_TYPES), ('0', {})]) - expected_result = [ - ['SetShardServedTypes', '--remove', 'foo/-80', 'replica'], - ['SetShardServedTypes', '--remove', 'foo/-80', 'rdonly'], - ['SetShardServedTypes', '--remove', 'foo/-80', 'master'], - ['SetShardServedTypes', '--remove', 'foo/80-', 'replica'], - ['SetShardServedTypes', '--remove', 'foo/80-', 'rdonly'], - ['SetShardServedTypes', '--remove', 'foo/80-', 'master'], - ['SetShardServedTypes', 'foo/0', 'replica'], - ['SetShardServedTypes', 'foo/0', 'rdonly'], - ['SetShardServedTypes', 'foo/0', 'master']] - self.assertEquals( - fix_served_types.get_vtctl_commands('foo', shards), expected_result) - - def test_source_shards_serving(self): - """Nominal case where the source shard is serving all types.""" - shards = collections.OrderedDict( - [('-80', {}), ('80-', {}), ('0', _ALL_TYPES)]) - expected_result = [ - ['SetShardServedTypes', 'foo/0', 'replica'], - ['SetShardServedTypes', 'foo/0', 'rdonly'], - ['SetShardServedTypes', 'foo/0', 'master']] - self.assertEquals( - fix_served_types.get_vtctl_commands('foo', shards), expected_result) - - def test_partial_serving_overlap_shard_2(self): - """Source shard and destination shard 2 are serving all types.""" - shards = collections.OrderedDict( - [('-80', {}), ('80-', _ALL_TYPES), ('0', _ALL_TYPES)]) - expected_result = [ - ['SetShardServedTypes', '--remove', 'foo/80-', 'replica'], - ['SetShardServedTypes', '--remove', 'foo/80-', 'rdonly'], - ['SetShardServedTypes', '--remove', 'foo/80-', 'master'], - ['SetShardServedTypes', 'foo/0', 'replica'], - ['SetShardServedTypes', 'foo/0', 'rdonly'], - ['SetShardServedTypes', 'foo/0', 'master']] - self.assertEquals( - fix_served_types.get_vtctl_commands('foo', shards), expected_result) - - def test_mixed_serving_types(self): - """Source shard serves some types, destination shards serve other types.""" - shards = collections.OrderedDict( - [('-80', _NO_MASTER), ('80-', _NO_MASTER), ('0', _JUST_MASTER)]) - expected_result = [ - ['SetShardServedTypes', '--remove', 'foo/-80', 'replica'], - ['SetShardServedTypes', '--remove', 'foo/-80', 'rdonly'], - ['SetShardServedTypes', '--remove', 'foo/80-', 'replica'], - ['SetShardServedTypes', '--remove', 'foo/80-', 'rdonly'], - ['SetShardServedTypes', 'foo/0', 'replica'], - ['SetShardServedTypes', 'foo/0', 'rdonly'], - ['SetShardServedTypes', 'foo/0', 'master']] - self.assertEquals( - fix_served_types.get_vtctl_commands('foo', shards), expected_result) - - -if __name__ == '__main__': - unittest.main() - diff --git a/test/cluster/sandbox/gke.py b/test/cluster/sandbox/gke.py deleted file mode 100755 index e9bcf4c16b3..00000000000 --- a/test/cluster/sandbox/gke.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Google Compute Engine sandbox components.""" - -import logging -import os -import subprocess - -import sandbox -import sandlet - - -class Cluster(object): - """Google Container Engine Cluster.""" - - _DEFAULT_ZONE = 'us-central1-b' - _DEFAULT_MACHINE_TYPE = 'n1-standard-4' - _DEFAULT_NODE_COUNT = 5 - - def __init__(self, params): - self.params = params - - def start(self): - """Start the GKE cluster.""" - zone = self.params.get('gke_zone', self._DEFAULT_ZONE) - machine_type = self.params.get('machine_type', self._DEFAULT_MACHINE_TYPE) - node_count = str(self.params.get('node_count', self._DEFAULT_NODE_COUNT)) - subprocess.call(['gcloud', 'config', 'set', 'compute/zone', zone]) - cluster_create_args = [ - 'gcloud', 'container', 'clusters', 'create', self.params['name'], - '--machine-type', machine_type, '--num-nodes', node_count, '--scopes', - 'storage-rw'] - if 'cluster_version' in self.params: - cluster_create_args += [ - '--cluster-version=%s' % self.params['cluster_version']] - try: - subprocess.check_call(cluster_create_args) - except subprocess.CalledProcessError as e: - raise sandbox.SandboxError('Failed to create GKE cluster: %s', e.output) - - def stop(self): - zone = self.params.get('gke_zone', self._DEFAULT_ZONE) - subprocess.call(['gcloud', 'container', 'clusters', 'delete', - self.params['name'], '-z', zone, '-q']) - - -class Port(sandlet.SandletComponent): - """Used for forwarding ports in Google Container Engine.""" - - def __init__(self, name, port): - self.name = name - self.port = port - super(Port, self).__init__(name, None) - - def start(self): - # Check for existence first. - with open(os.devnull, 'w') as dn: - # Suppress output for the existence check to prevent unnecessary output. - firewall_rules = subprocess.check_output( - ['gcloud', 'compute', 'firewall-rules', 'list', self.name], - stderr=dn) - if self.name in firewall_rules: - logging.info('Firewall rule %s already exists, skipping creation.', - self.name) - return - subprocess.call(['gcloud', 'compute', 'firewall-rules', 'create', - self.name, '--allow', 'tcp:%s' % str(self.port)]) - - def stop(self): - try: - subprocess.check_call( - ['gcloud', 'compute', 'firewall-rules', 'delete', self.name, '-q']) - except subprocess.CalledProcessError: - logging.warn('Failed to delete firewall rule %s.', self.name) diff --git a/test/cluster/sandbox/initial_reparent.py b/test/cluster/sandbox/initial_reparent.py deleted file mode 100755 index 8ed555ff742..00000000000 --- a/test/cluster/sandbox/initial_reparent.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Performs the first reparent on every shard of a keyspace.""" - -import json -import logging -import optparse -import sys -import time -from vtproto import topodata_pb2 -from vttest import sharding_utils -import vtctl_sandbox - - -def is_master(tablet, namespace): - tablet_info = ( - vtctl_sandbox.execute_vtctl_command( - ['GetTablet', tablet], namespace=namespace)) - if json.loads(tablet_info[0])['type'] == topodata_pb2.MASTER: - return True - - -def initial_reparent(keyspace, master_cell, num_shards, namespace, timeout_s): - """Performs the first reparent.""" - successfully_reparented = [] - master_tablets = {} - start_time = time.time() - logging.info('Finding tablets to reparent to.') - while len(master_tablets) < num_shards: - if time.time() - start_time > timeout_s: - logging.error('Timed out waiting to find a replica tablet') - return 1 - - for shard_name in sharding_utils.get_shard_names(num_shards): - if shard_name in master_tablets: - continue - tablets = vtctl_sandbox.execute_vtctl_command( - ['ListShardTablets', '%s/%s' % (keyspace, shard_name)], - namespace=namespace)[0].split('\n') - tablets = [x.split(' ') for x in tablets if x] - potential_masters = [ - x[0] for x in tablets if x[3] == 'replica' - and x[0].split('-')[0] == master_cell] - if potential_masters: - master_tablets[shard_name] = potential_masters[0] - logging.info( - '%s selected for shard %s', potential_masters[0], shard_name) - - while time.time() - start_time < timeout_s: - for shard_name in sharding_utils.get_shard_names(num_shards): - master_tablet_id = master_tablets[shard_name] - if is_master(master_tablet_id, namespace): - logging.info('Tablet %s is the master of %s/%s.', - master_tablet_id, keyspace, shard_name) - successfully_reparented.append(shard_name) - if shard_name in successfully_reparented: - continue - logging.info('Setting tablet %s as master for %s/%s.', - master_tablet_id, keyspace, shard_name) - vtctl_sandbox.execute_vtctl_command( - ['InitShardMaster', '-force', '%s/%s' % (keyspace, shard_name), - master_tablet_id], namespace=namespace, timeout_s=5) - if len(successfully_reparented) == num_shards: - logging.info('Done with initial reparent.') - return 0 - - logging.error('Timed out waiting for initial reparent.') - return 1 - - -def main(): - parser = optparse.OptionParser(usage='usage: %prog [options] [test_names]') - parser.add_option('-n', '--namespace', help='Kubernetes namespace', - default='vitess') - parser.add_option('-k', '--keyspace', help='Keyspace name', - default='test_keyspace') - parser.add_option('-m', '--master_cell', help='Master cell') - parser.add_option('-s', '--shard_count', help='Number of shards', default=2, - type=int) - parser.add_option('-t', '--timeout', help='Reparent timeout (s)', default=300, - type=int) - logging.getLogger().setLevel(logging.INFO) - - options, _ = parser.parse_args() - sys.exit(initial_reparent(options.keyspace, options.master_cell, - options.shard_count, options.namespace, - options.timeout)) - - -if __name__ == '__main__': - main() diff --git a/test/cluster/sandbox/kubernetes_components.py b/test/cluster/sandbox/kubernetes_components.py deleted file mode 100755 index 94d3ac5e947..00000000000 --- a/test/cluster/sandbox/kubernetes_components.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Kubernetes sandbox components.""" - -import json -import logging -import os -import re -import subprocess -import tempfile -import time - -import sandbox -import sandlet - - -def set_gke_cluster_context(gke_cluster_name): - logging.info('Changing cluster to %s.', gke_cluster_name) - clusters = subprocess.check_output( - ['kubectl', 'config', 'get-clusters']).split('\n') - cluster = [c for c in clusters if c.endswith('_%s' % gke_cluster_name)] - if not cluster: - raise sandbox.SandboxError( - 'Cannot change GKE cluster context, cluster %s not found', - gke_cluster_name) - with open(os.devnull, 'w') as devnull: - subprocess.call(['kubectl', 'config', 'use-context', cluster[0]], - stdout=devnull) - - -class HelmComponent(sandlet.SandletComponent): - """A helm resource.""" - - def __init__(self, name, sandbox_name, helm_config): - super(HelmComponent, self).__init__(name, sandbox_name) - self.helm_config = helm_config - try: - subprocess.check_output(['helm'], stderr=subprocess.STDOUT) - except OSError: - raise sandbox.SandboxError( - 'Could not find helm binary. Please visit ' - 'https://github.com/kubernetes/helm to download helm.') - - def start(self): - logging.info('Initializing helm.') - try: - subprocess.check_output(['helm', 'init'], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - raise sandbox.SandboxError('Failed to initialize helm: %s', e.output) - - # helm init on a fresh cluster takes a while to be ready. - # Wait until 'helm list' returns cleanly. - with open(os.devnull, 'w') as devnull: - start_time = time.time() - while time.time() - start_time < 120: - try: - subprocess.check_call(['helm', 'list'], stdout=devnull, - stderr=devnull) - logging.info('Helm is ready.') - break - except subprocess.CalledProcessError: - time.sleep(5) - else: - raise sandbox.SandboxError( - 'Timed out waiting for helm to become ready.') - - logging.info('Installing helm.') - try: - subprocess.check_output( - ['helm', 'install', os.path.join(os.environ['VTROOT'], 'helm/vitess'), - '-n', self.sandbox_name, '--namespace', self.sandbox_name, - '--replace', '--values', self.helm_config], - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - raise sandbox.SandboxError('Failed to install helm: %s' % e.output) - logging.info('Finished installing helm.') - - def stop(self): - subprocess.call(['helm', 'delete', self.sandbox_name, '--purge']) - - def is_up(self): - return True - - def is_down(self): - return not bool(subprocess.check_output( - ['kubectl', 'get', 'pods', '--namespace', self.sandbox_name])) - - -class KubernetesResource(sandlet.SandletComponent): - """A Kubernetes resource (pod, replicationcontroller, etc.).""" - - def __init__(self, name, sandbox_name, template_file, **template_params): - super(KubernetesResource, self).__init__(name, sandbox_name) - self.template_file = template_file - self.template_params = template_params - - def start(self): - super(KubernetesResource, self).start() - with open(self.template_file, 'r') as template_file: - template = template_file.read() - for name, value in self.template_params.items(): - template = re.sub('{{%s}}' % name, str(value), template) - with tempfile.NamedTemporaryFile() as f: - f.write(template) - f.flush() - os.system('kubectl create --namespace %s -f %s' % ( - self.sandbox_name, f.name)) - - def stop(self): - with open(self.template_file, 'r') as template_file: - template = template_file.read() - for name, value in self.template_params.items(): - template = re.sub('{{%s}}' % name, str(value), template) - with tempfile.NamedTemporaryFile() as f: - f.write(template) - f.flush() - os.system('kubectl delete --namespace %s -f %s' % ( - self.sandbox_name, f.name)) - - super(KubernetesResource, self).stop() - - -def get_forwarded_ip(service, namespace='default', max_wait_s=60): - """Returns an external IP address exposed by a service.""" - start_time = time.time() - while time.time() - start_time < max_wait_s: - try: - service_info = json.loads(subprocess.check_output( - ['kubectl', 'get', 'service', service, '--namespace=%s' % namespace, - '-o', 'json'])) - return service_info['status']['loadBalancer']['ingress'][0]['ip'] - except (KeyError, subprocess.CalledProcessError): - time.sleep(1) - return '' - diff --git a/test/cluster/sandbox/naming/README.md b/test/cluster/sandbox/naming/README.md deleted file mode 100644 index e39e30bd37b..00000000000 --- a/test/cluster/sandbox/naming/README.md +++ /dev/null @@ -1,2 +0,0 @@ -These files contain word lists to generate randomly named sandboxes if no name -has been provided. diff --git a/test/cluster/sandbox/naming/adjectives.txt b/test/cluster/sandbox/naming/adjectives.txt deleted file mode 100644 index 861931ba3da..00000000000 --- a/test/cluster/sandbox/naming/adjectives.txt +++ /dev/null @@ -1,167 +0,0 @@ -adorable -adventurous -aggressive -alert -attractive -average -beautiful -blue-eyed -bloody -blushing -bright -clean -clear -cloudy -colorful -crowded -cute -dark -drab -distinct -dull -elegant -excited -fancy -filthy -glamorous -gleaming -gorgeous -graceful -grotesque -handsome -homely -light -long -magnificent -misty -motionless -muddy -old-fashioned -plain -poised -precious -quaint -shiny -smoggy -sparkling -spotless -stormy -strange -ugly -ugliest -unsightly -unusual -wide-eyed -alive -annoying -bad -better -beautiful -brainy -breakable -busy -careful -cautious -clever -clumsy -concerned -crazy -curious -dead -different -difficult -doubtful -easy -expensive -famous -fragile -frail -gifted -helpful -helpless -horrible -important -impossible -inexpensive -innocent -inquisitive -modern -mushy -odd -open -outstanding -poor -powerful -prickly -puzzled -real -rich -shy -sleepy -stupid -super -talented -tame -tender -tough -uninterested -vast -wandering -wild -wrong -agreeable -amused -brave -calm -charming -cheerful -comfortable -cooperative -courageous -delightful -determined -eager -elated -enchanting -encouraging -energetic -enthusiastic -excited -exuberant -fair -faithful -fantastic -fine -friendly -funny -gentle -glorious -good -happy -healthy -helpful -hilarious -jolly -joyous -kind -lively -lovely -lucky -nice -obedient -perfect -pleasant -proud -relieved -silly -smiling -splendid -successful -thankful -thoughtful -victorious -vivacious -witty -wonderful -zealous -zany diff --git a/test/cluster/sandbox/naming/animals.txt b/test/cluster/sandbox/naming/animals.txt deleted file mode 100644 index b230a082c79..00000000000 --- a/test/cluster/sandbox/naming/animals.txt +++ /dev/null @@ -1,206 +0,0 @@ -aardvark -albatross -alligator -alpaca -ant -anteater -antelope -ape -armadillo -herd -baboon -badger -barracuda -bat -bear -beaver -bee -bison -boar -galago -butterfly -camel -caribou -cat -caterpillar -cattle -chamois -cheetah -chicken -chimpanzee -chinchilla -chough -clam -cobra -cockroach -cod -cormorant -coyote -crab -herd -crocodile -crow -curlew -deer -dinosaur -dog -dolphin -donkey -dotterel -dove -dragonfly -duck -dugong -dunlin -eagle -echidna -eel -elephant -elk -emu -falcon -ferret -finch -fish -flamingo -fly -fox -frog -gaur -gazelle -gerbil -giraffe -gnat -goat -goose -goldfish -gorilla -goshawk -grasshopper -grouse -guanaco -poultry -herd -gull -hamster -hare -hawk -hedgehog -heron -herring -hippopotamus -hornet -horse -human -hummingbird -hyena -jackal -jaguar -jay -jellyfish -kangaroo -koala -kouprey -kudu -lapwing -lark -lemur -leopard -lion -llama -lobster -locust -loris -louse -lyrebird -magpie -mallard -manatee -marten -meerkat -mink -monkey -moose -mouse -mosquito -mule -narwhal -newt -nightingale -octopus -okapi -opossum -oryx -ostrich -otter -owl -ox -oyster -parrot -partridge -peafowl -pelican -penguin -pheasant -pig -pigeon -pony -porcupine -porpoise -quail -quelea -rabbit -raccoon -rat -raven -herd -reindeer -rhinoceros -ruff -salamander -salmon -sandpiper -sardine -scorpion -herd -seahorse -shark -sheep -shrew -shrimp -skunk -snail -snake -spider -squid -squirrel -starling -stingray -stinkbug -stork -swallow -swan -tapir -tarsier -termite -tiger -toad -trout -poultry -turtle -vulture -wallaby -walrus -wasp -carabeef -weasel -whale -wolf -wolverine -wombat -woodcock -woodpecker -worm -wren -yak -zebra diff --git a/test/cluster/sandbox/sandbox.py b/test/cluster/sandbox/sandbox.py deleted file mode 100755 index 366a0eb5726..00000000000 --- a/test/cluster/sandbox/sandbox.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""A cluster sandbox encompassing an application along with a cloud environment. - -Sandboxes consist of sandlets, which are abstractions used to divide an -application into individual pieces. Sandlets can be dependent on other sandlets. -In addition, they can be started or stopped individually from the command line. - -Users must create a new python class inheriting from this Sandbox class. This -combined with the yaml configuration defines the sandbox. -""" - -import argparse -import logging -import os -import re -import yaml - -import gke -import sandbox_utils -import sandlet - - -def parse_config( - config, sandbox_name=None, cluster_name=None, cluster_type='gke'): - random_name = sandbox_utils.generate_random_name() - config = re.sub('{{sandbox_name}}', sandbox_name or random_name, config) - config = re.sub('{{cluster_name}}', cluster_name or random_name, config) - config = re.sub('{{cluster_type}}', cluster_type, config) - return config - - -class SandboxError(Exception): - pass - - -class Sandbox(object): - """Sandbox class providing basic functionality. - - Derive this class in order to specify sandlets and dependencies. - """ - - _cluster_envs = { - 'gke': gke, - } - - def __init__(self, sandbox_options): - self.sandbox_options = sandbox_options - self.name = sandbox_options.get('name') - self.cluster_type = sandbox_options.get('cluster_type') - if self.cluster_type not in self._cluster_envs.keys(): - raise SandboxError('Invalid cluster type %s.' % self.cluster_type) - cluster_config = [c for c in sandbox_options.get('clusters') - if c['type'] == self.cluster_type] - if not cluster_config: - raise SandboxError( - 'Cluster config %s not listed in sandbox config.' % cluster_config) - self.cluster_config = cluster_config[0] - self.cluster_env = self._cluster_envs[self.cluster_type] - self.cluster = self.cluster_env.Cluster(self.cluster_config) - self.sandlets = sandlet.ComponentGroup() - - def set_log_dir(self, log_dir_in=None): - if log_dir_in: - self.log_dir = log_dir_in - else: - self.log_dir = '/tmp/sandbox_logs' - if not os.path.exists(self.log_dir): - os.makedirs(self.log_dir) - - def start(self, sandlets=None): - self.start_cluster() - self.start_sandlets(sandlets) - - def stop(self, sandlets=None): - self.stop_sandlets(sandlets) - self.stop_cluster() - - def start_cluster(self): - if not self.cluster_type: - raise SandboxError('Cannot start cluster, no cluster_type defined.') - self.cluster.start() - - def stop_cluster(self): - if not self.cluster: - raise SandboxError('Cannot stop cluster, no cluster_type defined.') - self.cluster.stop() - - def start_sandlets(self, sandlets=None): - self.sandlets.execute(sandlet.StartAction, sandlets) - - def stop_sandlets(self, sandlets=None): - self.sandlets.execute(sandlet.StopAction, sandlets) - - def print_banner(self): - pass - - def generate_from_config(self): - raise NotImplementedError('Generate From Config file not implemented!') - - -def sandbox_main(sandbox_cls): - """Main. - - Call this function from the main in the derived sandbox class. - - Args: - sandbox_cls: A derived sandbox class. This will be instantiated in this - main function. - """ - logging.getLogger().setLevel(logging.INFO) - parser = argparse.ArgumentParser(description='Create a sandbox') - parser.add_argument( - '-e', '--environment', help='Cluster environment', default='gke') - parser.add_argument('-c', '--config_file', help='Sandbox config file') - parser.add_argument('-n', '--sandbox_name', help='Sandbox name') - parser.add_argument('-k', '--cluster_name', help='Cluster name') - parser.add_argument( - '-l', '--log_dir', help='Directory for logs', default=None) - parser.add_argument('-s', '--sandlets', help='Sandlets') - available_actions = ['Start', 'StartCluster', 'StartApp', 'StopApp', 'Stop', - 'StopCluster', 'PrintSandlets', 'PrintBanner'] - parser.add_argument('-a', '--action', choices=available_actions) - sandbox_args = parser.parse_args() - sandlets = [] - if sandbox_args.sandlets: - sandlets = sandbox_args.sandlets.split(',') - - with open(sandbox_args.config_file, 'r') as yaml_file: - yaml_config = yaml_file.read() - sandbox_config = yaml.load( - parse_config( - yaml_config, sandbox_args.sandbox_name, sandbox_args.cluster_name, - sandbox_args.environment))['sandbox'] - sandbox = sandbox_cls(sandbox_config) - sandbox.set_log_dir(sandbox_args.log_dir) - sandbox.generate_from_config() - - if sandbox_args.action == 'Start': - sandbox.start(sandlets) - sandbox.print_banner() - elif sandbox_args.action == 'StartCluster': - sandbox.start_cluster() - elif sandbox_args.action == 'StopCluster': - sandbox.stop_cluster() - elif sandbox_args.action == 'StartApp': - sandbox.start_sandlets(sandlets) - sandbox.print_banner() - elif sandbox_args.action == 'StopApp': - sandbox.stop_sandlets(sandlets) - elif sandbox_args.action == 'Stop': - sandbox.stop(sandlets) - elif sandbox_args.action == 'PrintSandlets': - logging.info('Sandlets: %s.', ', '.join(s.name for s in sandbox.sandlets)) - elif sandbox_args.action == 'PrintBanner': - sandbox.print_banner() - else: - logging.info('No available action selected. Choices are: %s.', - ', '.join(available_actions)) diff --git a/test/cluster/sandbox/sandbox_utils.py b/test/cluster/sandbox/sandbox_utils.py deleted file mode 100644 index 02f39f1fb52..00000000000 --- a/test/cluster/sandbox/sandbox_utils.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Sandbox util functions.""" - -import datetime -import os -import random - - -def create_log_file(log_dir, filename): - """Create a log file. - - This function creates a timestamped log file, and updates a non-timestamped - symlink in the log directory. - - Example: For a log called init.INFO, this function will create a log file - called init.INFO.20170101-120000.100000 and update a symlink - init.INFO to point to it. - - Args: - log_dir: string, Base path for logs. - filename: string, The base name of the log file. - - Returns: - The opened file handle. - """ - timestamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S.%f') - symlink_name = os.path.join(log_dir, filename) - timestamped_name = '%s.%s' % (symlink_name, timestamp) - if os.path.islink(symlink_name): - os.remove(symlink_name) - os.symlink(timestamped_name, symlink_name) - return open(timestamped_name, 'w') - - -def generate_random_name(): - with open('naming/adjectives.txt', 'r') as f: - adjectives = [l.strip() for l in f if l.strip()] - with open('naming/animals.txt', 'r') as f: - animals = [l.strip() for l in f if l.strip()] - return '%s%s' % (random.choice(adjectives), random.choice(animals)) - diff --git a/test/cluster/sandbox/sandlet.py b/test/cluster/sandbox/sandlet.py deleted file mode 100644 index 107a090ec20..00000000000 --- a/test/cluster/sandbox/sandlet.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Top level component of a sandbox. - -A sandlet is an abstraction to be used by applications to organize a sandbox -into discrete groupings. A user can start an entire sandbox, which will start -all sandlets, or selectively choose which ones to start. - -Sandlets are made up of components, which are the actual subprocesses or jobs -to run. -""" - -import logging -import time - - -class DependencyError(Exception): - """Raised when the configuration has an incorrect set of dependencies.""" - - -class BaseAction(object): - - @classmethod - def able_to_act(cls, component, components, remaining): - pass - - @classmethod - def do_action(cls, component): - pass - - @classmethod - def get_unfinished(cls, component, remaining): - pass - - -class StartAction(BaseAction): - """Starts components.""" - - @classmethod - def able_to_act(cls, component, components, remaining): - # A component can start if none of its dependencies are remaining - return not set(component.dependencies).intersection(remaining) - - @classmethod - def do_action(cls, component): - component.start() - - @classmethod - def get_unfinished(cls, available): - return [x.name for x in available if not x.is_up()] - - -class StopAction(BaseAction): - """Stops components.""" - - @classmethod - def able_to_act(cls, component, components, remaining): - # A component can stop if there are no remaining components have it as a - # dependency - return not [a.name for a in components - if component.name in a.dependencies and a.name in remaining] - - @classmethod - def do_action(cls, component): - component.stop() - - @classmethod - def get_unfinished(cls, available): - return [x.name for x in available if not x.is_down()] - - -class ComponentGroup(object): - """A grouping of components with dependencies that can be executed.""" - - def __init__(self): - self.components = [] - - def add_component(self, component): - self.components.append(component) - - def execute(self, action, subcomponents=None): - remaining = subcomponents or [x.name for x in self.components] - while remaining: - available = [x for x in self.components if x.name in remaining - and action.able_to_act(x, self.components, remaining)] - if not available: - # This is a cycle, we have remaining tasks but none can run - raise DependencyError( - 'Cycle detected: remaining components: %s.' % remaining) - for component in available: - action.do_action(component) - remaining.remove(component.name) - while True: - unfinished_components = action.get_unfinished(available) - if not unfinished_components: - break - logging.info( - 'Waiting to be finished: %s.', ', '.join(unfinished_components)) - time.sleep(10) - - -class Sandlet(object): - """Top-level component of a sandbox. - - Sandlets should be defined in a way to split applications in a logical way. - """ - - def __init__(self, name): - self.name = name - self.dependencies = [] - self.components = ComponentGroup() - - def start(self): - logging.info('Starting sandlet %s.', self.name) - self.components.execute(StartAction) - - def stop(self): - logging.info('Stopping sandlet %s.', self.name) - self.components.execute(StopAction) - - def is_up(self): - """Whether the component has finished being started.""" - return True - - def is_down(self): - """Whether the component has finished being stopped.""" - return True - - -class SandletComponent(object): - """Entity of a sandlet that encapsulates a process or job.""" - - def __init__(self, name, sandbox_name): - self.name = name - self.dependencies = [] - self.sandbox_name = sandbox_name - - def start(self): - logging.info('Starting component %s.', self.name) - - def stop(self): - logging.info('Stopping component %s.', self.name) - - def is_up(self): - """Whether the component has finished being started.""" - return True - - def is_down(self): - """Whether the component has finished being stopped.""" - return True - diff --git a/test/cluster/sandbox/sandlet_test.py b/test/cluster/sandbox/sandlet_test.py deleted file mode 100644 index f8cf2d8ec87..00000000000 --- a/test/cluster/sandbox/sandlet_test.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Sandlet tests.""" - -import StringIO -import sys -import unittest - -import sandlet - - -class TestComponent(object): - - def __init__(self, name, dependencies): - self.name = name - self.dependencies = dependencies - - def start(self): - print 'Start %s' % self.name - - def stop(self): - print 'Stop %s' % self.name - - def is_up(self): - return True - - def is_down(self): - return True - - -class SandletTest(unittest.TestCase): - - def _test_dependency_graph( - self, components, expected_output, action, subcomponents=None): - saved_stdout = sys.stdout - group = sandlet.ComponentGroup() - for c in components: - group.add_component(c) - try: - out = StringIO.StringIO() - sys.stdout = out - group.execute(action, subcomponents) - output = out.getvalue().strip() - self.assertEquals(output, expected_output) - finally: - sys.stdout = saved_stdout - - def test_dependency_graph(self): - a = TestComponent('a', ['b']) - b = TestComponent('b', ['c']) - c = TestComponent('c', []) - self._test_dependency_graph( - [a, b, c], 'Start c\nStart b\nStart a', sandlet.StartAction) - self._test_dependency_graph( - [a, b, c], 'Start a\nStart c', sandlet.StartAction, - subcomponents=['a', 'c']) - self._test_dependency_graph( - [a, b, c], 'Start c\nStart b', sandlet.StartAction, - subcomponents=['b', 'c']) - self._test_dependency_graph( - [a, b, c], 'Stop a\nStop b\nStop c', sandlet.StopAction) - self._test_dependency_graph( - [a, b, c], 'Stop a\nStop c', sandlet.StopAction, - subcomponents=['a', 'c']) - - def test_cyclical_dependency_graph(self): - a = TestComponent('a', ['b']) - b = TestComponent('b', ['c']) - c = TestComponent('c', ['a']) - with self.assertRaises(sandlet.DependencyError): - self._test_dependency_graph([a, b, c], '', sandlet.StartAction) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/cluster/sandbox/subprocess_component.py b/test/cluster/sandbox/subprocess_component.py deleted file mode 100644 index 744ce4b203c..00000000000 --- a/test/cluster/sandbox/subprocess_component.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Subprocess sandlet component.""" - -import logging -import subprocess - -import sandbox -import sandbox_utils -import sandlet - - -class Subprocess(sandlet.SandletComponent): - """A sandlet component for running scripts.""" - - def __init__(self, name, sandbox_name, script, log_dir, **script_kwargs): - super(Subprocess, self).__init__(name, sandbox_name) - self.script = script - self.script_kwargs = script_kwargs - self.log_dir = log_dir - - def start(self): - super(Subprocess, self).start() - try: - script_args = [] - for k, v in self.script_kwargs.items(): - script_args += ['--%s' % k, str(v)] - logging.info('Executing subprocess script %s.', self.script) - infofile = sandbox_utils.create_log_file( - self.log_dir, '%s.INFO' % self.name) - errorfile = sandbox_utils.create_log_file( - self.log_dir, '%s.ERROR' % self.name) - subprocess.check_call( - ['./%s' % self.script] + script_args, stdout=infofile, - stderr=errorfile) - logging.info('Done.') - except subprocess.CalledProcessError as error: - raise sandbox.SandboxError( - 'Subprocess %s returned errorcode %d, find log at %s.' % ( - self.script, error.returncode, errorfile.name)) - finally: - if infofile: - infofile.close() - if errorfile: - errorfile.close() - - def stop(self): - pass diff --git a/test/cluster/sandbox/vitess_kubernetes_sandbox.py b/test/cluster/sandbox/vitess_kubernetes_sandbox.py deleted file mode 100755 index 016a8d99916..00000000000 --- a/test/cluster/sandbox/vitess_kubernetes_sandbox.py +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""A Vitess sandbox with Kubernetes.""" - -import collections -import copy -import kubernetes_components -import logging -import os -import tempfile -import yaml -from vttest import sharding_utils - -import sandbox -import sandlet -import subprocess_component - - -class VitessKubernetesSandbox(sandbox.Sandbox): - """Sandbox implementation for Vitess.""" - - # Constants used for generating tablet UIDs. Each constant represents the - # increment value for each property. - cell_epsilon = 100000000 - keyspace_epsilon = 1000000 - shard_epsilon = 100 - - def __init__(self, sandbox_options): - super(VitessKubernetesSandbox, self).__init__(sandbox_options) - - def generate_guestbook_sandlet(self): - """Creates a sandlet encompassing the guestbook app built on Vitess.""" - guestbook_sandlet = sandlet.Sandlet('guestbook') - guestbook_sandlet.dependencies = ['helm'] - template_dir = os.path.join(os.environ['VTROOT'], 'examples/kubernetes') - guestbook_sandlet.components.add_component( - self.cluster_env.Port('%s-guestbook' % self.name, 80)) - for keyspace in self.app_options.keyspaces: - create_schema_subprocess = subprocess_component.Subprocess( - 'create_schema_%s' % keyspace['name'], self.name, 'create_schema.py', - self.log_dir, namespace=self.name, keyspace=keyspace['name'], - drop_table='messages', sql_file=os.path.join( - os.environ['VTROOT'], 'examples/kubernetes/create_test_table.sql')) - guestbook_sandlet.components.add_component(create_schema_subprocess) - guestbook_sandlet.components.add_component( - kubernetes_components.KubernetesResource( - 'guestbook-service', self.name, - os.path.join(template_dir, 'guestbook-service.yaml'))) - guestbook_sandlet.components.add_component( - kubernetes_components.KubernetesResource( - 'guestbook-controller', self.name, - os.path.join(template_dir, 'guestbook-controller-template.yaml'), - port=8080, cell=self.app_options.cells[0], vtgate_port=15991, - keyspace=self.app_options.keyspaces[0]['name'])) - self.sandlets.add_component(guestbook_sandlet) - - def _generate_helm_keyspaces(self): - """Create helm keyspace configurations. - - These configurations include entries for keyspaces, shards, and tablets. - Note that the logic for calculating UIDs will go away once Kubernetes 1.5 - is widely available. Then tablets will use replication controllers with - StatefulSet, and UIDs will be calculated automatically. - - Returns: - Configuration for keyspaces in the form of a list of dicts. - """ - starting_cell_index = 0 - if len(self.app_options.cells) > 1: - starting_cell_index = self.cell_epsilon - keyspaces = [] - for ks_index, ks in enumerate(self.app_options.keyspaces): - keyspace = dict(name=ks['name'], shards=[]) - keyspaces.append(keyspace) - - for shard_index, shard_name in enumerate( - sharding_utils.get_shard_names(ks['shard_count'])): - shard = dict( - name=shard_name, - tablets=[dict( - type='replica', - vttablet=dict( - replicas=ks['replica_count'], - ), - )], - ) - uid_base = ( - (100 + shard_index * self.shard_epsilon) + starting_cell_index + ( - ks_index * self.keyspace_epsilon)) - shard['tablets'][0]['uidBase'] = uid_base - if ks['rdonly_count']: - shard['tablets'].append(dict( - type='rdonly', - uidBase=uid_base + ks['replica_count'], - vttablet=dict( - replicas=ks['rdonly_count'], - ))) - keyspace['shards'].append(shard) - return keyspaces - - def _generate_helm_values_config(self): - """Generate the values yaml config used for helm. - - Returns: - Filename of the generated helm config. - """ - - # First override general configurations, such as which docker image to use - # and resource usage. Set vtctld/vtgate services as LoadBalancers to allow - # external access more easily. - yaml_values = dict( - vtctld=dict( - serviceType='LoadBalancer', # Allows port forwarding. - image=self.app_options.vtctld_image, - extraFlags={'enable_queries': True}, - ), - vttablet=dict( - image=self.app_options.vttablet_image, - resources=dict( - limits=dict( - memory=self.app_options.vttablet_ram, - cpu=self.app_options.vttablet_cpu, - ), - ), - mysqlResources=dict( - limits=dict( - memory=self.app_options.mysql_ram, - cpu=self.app_options.mysql_cpu, - ), - ), - controllerType='StatefulSet', - ), - vtgate=dict( - serviceType='LoadBalancer', # Allows port forwarding. - image=self.app_options.vtgate_image, - resources=dict( - limits=dict( - memory=self.app_options.vtgate_ram, - cpu=self.app_options.vtgate_cpu, - ), - ), - ), - backupFlags=self.app_options.backup_flags, - topology=dict( - cells=[dict( - name='global', - etcd=dict( - replicas=self.app_options.etcd_count, - ), - )], - ), - ) - # Add an orchestrator entry if enabled - if self.app_options.enable_orchestrator: - yaml_values['topology']['cells'][0]['orchestrator'] = dict( - replicas=1, - ) - - keyspaces = self._generate_helm_keyspaces() - - # For the sandbox, each keyspace will exist in all defined cells. - # This means in the config, the keyspace entry is duplicated in every - # cell entry. - for index, cell in enumerate(self.app_options.cells): - cell_dict = dict( - name=cell, - etcd=dict(replicas=self.app_options.etcd_count), - vtgate=dict(replicas=self.app_options.vtgate_count), - keyspaces=copy.deepcopy(keyspaces), - ) - # Each tablet's UID must be unique, so increment the uidBase for tablets - # by the cell epsilon value to ensure uniqueness. Also convert the UID to - # a string, or else the parser will attempt to parse UID as a float, which - # causes issues when UID's are large. This logic will go away once - # StatefulSet is available. - for keyspace in cell_dict['keyspaces']: - for shard in keyspace['shards']: - for tablets in shard['tablets']: - tablets['uidBase'] = str( - tablets['uidBase'] + index * self.cell_epsilon) - yaml_values['topology']['cells'].append(cell_dict) - - if index == 0: - yaml_values['topology']['cells'][-1]['vtctld'] = dict(replicas=1) - - # Create the file and return the filename - with tempfile.NamedTemporaryFile(delete=False) as f: - f.write(yaml.dump(yaml_values, default_flow_style=False)) - yaml_filename = f.name - logging.info('Helm config generated at %s', yaml_filename) - return yaml_filename - - def generate_helm_sandlet(self): - """Creates a helm sandlet. - - This sandlet generates a dynamic values yaml file to be used with the Vitess - helm chart in order to encompass most of the Vitess stack. - """ - helm_sandlet = sandlet.Sandlet('helm') - helm_sandlet.components.add_component(kubernetes_components.HelmComponent( - 'helm', self.name, self._generate_helm_values_config())) - - # Add a subprocess task to wait for all mysql instances to be healthy. - tablet_count = 0 - for keyspace in self.app_options.keyspaces: - tablet_count += (keyspace['shard_count'] * len(self.app_options.cells) * ( - keyspace['replica_count'] + keyspace['rdonly_count'])) - wait_for_mysql_subprocess = subprocess_component.Subprocess( - 'wait_for_mysql', self.name, 'wait_for_mysql.py', - self.log_dir, namespace=self.name, - cells=','.join(self.app_options.cells), - tablet_count=tablet_count) - wait_for_mysql_subprocess.dependencies = ['helm'] - helm_sandlet.components.add_component(wait_for_mysql_subprocess) - - # Add a subprocess task to ensure serving types are correct. This is useful - # for resharding sandboxes where keyspaces have overlapping sets of shards. - fix_served_types_subprocess = subprocess_component.Subprocess( - 'fix_served_types', self.name, 'fix_served_types.py', self.log_dir, - namespace=self.name, - keyspaces=','.join(ks['name'] for ks in self.app_options.keyspaces)) - fix_served_types_subprocess.dependencies = ['wait_for_mysql'] - helm_sandlet.components.add_component(fix_served_types_subprocess) - - # Add a subprocess task for each keyspace to perform the initial reparent. - for keyspace in self.app_options.keyspaces: - name = keyspace['name'] - shard_count = keyspace['shard_count'] - initial_reparent_subprocess = subprocess_component.Subprocess( - 'initial_reparent_%s_%d' % (name, shard_count), self.name, - 'initial_reparent.py', self.log_dir, namespace=self.name, - keyspace=name, shard_count=shard_count, - master_cell=self.app_options.cells[0]) - initial_reparent_subprocess.dependencies = [ - wait_for_mysql_subprocess.name, - fix_served_types_subprocess.name, - ] - helm_sandlet.components.add_component(initial_reparent_subprocess) - self.sandlets.add_component(helm_sandlet) - - def generate_from_config(self): - """Creates a Vitess sandbox.""" - self.app_options = collections.namedtuple( - 'Struct', self.sandbox_options['application'].keys())( - *self.sandbox_options['application'].values()) - - self.generate_helm_sandlet() - if self.app_options.enable_guestbook: - self.generate_guestbook_sandlet() - - def print_banner(self): - logging.info('Fetching forwarded ports.') - banner = '\nVitess Sandbox Info:\n' - vtctld_ip = kubernetes_components.get_forwarded_ip( - 'vtctld', self.name) - banner += ' vtctld: http://%s:15000\n' % vtctld_ip - for cell in self.app_options.cells: - vtgate_ip = kubernetes_components.get_forwarded_ip( - 'vtgate-%s' % cell, self.name) - banner += ' vtgate-%s: http://%s:15001\n' % (cell, vtgate_ip) - if self.app_options.enable_guestbook: - guestbook_ip = kubernetes_components.get_forwarded_ip( - 'guestbook', self.name) - banner += ' guestbook: http://%s:80\n' % guestbook_ip - banner += ' logs dir: %s\n' % self.log_dir - logging.info(banner) - - -if __name__ == '__main__': - sandbox.sandbox_main(VitessKubernetesSandbox) diff --git a/test/cluster/sandbox/vtctl_sandbox.py b/test/cluster/sandbox/vtctl_sandbox.py deleted file mode 100755 index 3e495b414ab..00000000000 --- a/test/cluster/sandbox/vtctl_sandbox.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Wrapper around vtctl execute_vtctl_command for sandboxes. - -Note: This also provides a backup option of using kvtctl.sh, a kubernetes script -used to temporarily forward a port if vtctld has no forwarded port. - -TODO(thompsonja): This is heavily tied to the kubernetes and will need to be -updated if other systems are used. -""" - -import json -import os -import subprocess -import threading -import time - - -class Command(object): - - def __init__(self, cmd): - self.cmd = cmd - self.process = None - self.stdout = None - self.stderr = None - - def run(self, timeout_s): - """Runs the vtctl command.""" - def target(): - self.process = subprocess.Popen(self.cmd, stdout=subprocess.PIPE) - self.stdout, self.stderr = self.process.communicate() - - thread = threading.Thread(target=target) - thread.start() - - thread.join(timeout_s) - if thread.is_alive(): - self.process.terminate() - thread.join() - return self.process.returncode - - -def execute_vtctl_command(vtctl_args, namespace='default', timeout_s=180): - """Executes a vtctl command with some retry logic.""" - vtctl_cmd_args = [] - vtctld_info = json.loads(subprocess.check_output( - ['kubectl', 'get', 'service', 'vtctld', '--namespace=%s' % namespace, - '-o', 'json'])) - try: - # Check to see if the vtctld service has a forwarded port. - ip = vtctld_info['status']['loadBalancer']['ingress'][0]['ip'] - vtctl_cmd_args = ['vtctlclient', '-server', '%s:15999' % ip] + vtctl_args - except (KeyError, IndexError): - pass - if not vtctl_cmd_args: - # Default to trying to use kvtctl.sh if a forwarded port cannot be found. - os.environ['VITESS_NAME'] = namespace - vtctl_cmd_args = ( - [os.path.join(os.environ['VTROOT'], 'examples/kubernetes/kvtctl.sh')] - + vtctl_args) - - start_time = time.time() - while time.time() - start_time < timeout_s: - cmd = Command(vtctl_cmd_args) - retcode = cmd.run(10) - if cmd.stdout.startswith('Starting port forwarding'): - # Ignore this extra output line if using kvtctl.sh - cmd.stdout = cmd.stdout[cmd.stdout.find('\n')+1:] - if retcode: - last_error = 'Failed w/ errorcode %d, stdout %s, stderr %s' % ( - cmd.process.returncode, cmd.stdout, cmd.stderr) - else: - return cmd.stdout, True - - return ('Last error running %s: %s' % (' '.join(vtctl_cmd_args), last_error), - False) diff --git a/test/cluster/sandbox/wait_for_mysql.py b/test/cluster/sandbox/wait_for_mysql.py deleted file mode 100755 index ed880afa36a..00000000000 --- a/test/cluster/sandbox/wait_for_mysql.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Waits for mysql to be in a good state.""" - -import logging -import optparse -import re -import sys -import time -import vtctl_sandbox - - -def get_all_tablets(cells, namespace): - """Returns a list of all tablet names.""" - tablets = [] - cells = cells.split(',') - for cell in cells: - cell_tablets = vtctl_sandbox.execute_vtctl_command( - ['ListAllTablets', cell], namespace=namespace)[0].split('\n') - for t in cell_tablets: - tablets.append(t.split(' ')[0]) - r = re.compile('.*-.*') - tablets = filter(r.match, tablets) - logging.info('Tablets: %s.', ', '.join(tablets)) - return tablets - - -def main(): - parser = optparse.OptionParser(usage='usage: %prog [options] [test_names]') - parser.add_option('-n', '--namespace', help='Kubernetes namespace', - default='vitess') - parser.add_option('-c', '--cells', help='Comma separated list of cells') - parser.add_option('-t', '--tablet_count', - help='Total number of expected tablets', type=int) - parser.add_option('-w', '--wait', help='Max wait time (s)', type=int, - default=300) - logging.getLogger().setLevel(logging.INFO) - - options, _ = parser.parse_args() - - logging.info('Waiting for mysql to become healthy.') - - start_time = time.time() - good_tablets = [] - tablets = [] - - # Do this in a loop as the output of ListAllTablets may not be parseable - # until all tablets have been started. - while (time.time() - start_time < options.wait and - len(tablets) < options.tablet_count): - tablets = get_all_tablets(options.cells, options.namespace) - logging.info('Expecting %d tablets, found %d tablets', - options.tablet_count, len(tablets)) - - start_time = time.time() - while time.time() - start_time < options.wait: - for tablet in [t for t in tablets if t not in good_tablets]: - _, success = vtctl_sandbox.execute_vtctl_command( - ['ExecuteFetchAsDba', tablet, 'show databases'], - namespace=options.namespace, timeout_s=1) - if success: - good_tablets.append(tablet) - logging.info('%d of %d tablets healthy.', len(good_tablets), len(tablets)) - if len(good_tablets) == len(tablets): - logging.info('All tablets healthy in %f seconds.', - time.time() - start_time) - break - else: - logging.warn('Timed out waiting for tablets to be ready.') - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/test/cluster/vtctl_helper.py b/test/cluster/vtctl_helper.py deleted file mode 100644 index 4f72ee771cd..00000000000 --- a/test/cluster/vtctl_helper.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Helper module for running vtctl commands. - -This module allows for retry logic to ensure that vtctl commands are properly -executed. This should help reduce flakiness in the sandbox. -""" - -import logging -import subprocess -import time - -from vtctl import vtctl_client - - -class VtctlClientError(Exception): - pass - - -class VtctlHelper(object): - """Various functions for running vtctl commands.""" - - def __init__(self, protocol, vtctl_addr): - self.protocol = protocol - self.client = None - self.vtctl_addr = vtctl_addr - if vtctl_addr and protocol != 'grpc': - self.client = vtctl_client.connect(protocol, vtctl_addr, 30) - - def execute_vtctl_command(self, args, action_timeout=60.0, expect_fail=False, - max_wait_s=180.0): - """Executes a vtctl command on a running vtctl job. - - This function attempts to execute on any running vtctl job, returning - immediately when a call to execute_vtctl_command completes successfully. - - Args: - args: args to pass to vtctl_client's execute_vtctl_command function - action_timeout: total timeout for the action (float, in seconds) - expect_fail: whether or not the vtctl command should fail (bool) - max_wait_s: maximum amount of time to wait for success (float, in seconds) - - Returns: - Result of executing vtctl command - - Raises: - VtctlClientError: Could not successfully call execute_vtctl_command - """ - start_time = time.time() - while time.time() - start_time < max_wait_s: - try: - if self.protocol == 'grpc': - results = subprocess.check_output( - ['vtctlclient', '-vtctl_client_protocol', self.protocol, - '-server', self.vtctl_addr] + args, stderr=subprocess.STDOUT) - else: - results = vtctl_client.execute_vtctl_command( - self.client, args, action_timeout=action_timeout) - return results - except Exception as e: - if expect_fail: - logging.info('Expected vtctl error, got: %s', e.message or e.output) - raise VtctlClientError('Caught an expected vtctl error') - logging.info('Vtctl error (vtctl %s): %s', - ' '.join(args), e.message or e.output) - time.sleep(5) - raise VtctlClientError('Timed out on vtctl_client execute_vtctl_command') - - def execute_vtctl_command_until_success( - self, args, max_wait_s=180.0, retry_wait_s=5.0): - """Executes a vtctl command on a running vtctl job. - - This function attempts to execute on any running vtctl job, returning - immediately when a call to execute_vtctl_command returns nothing. Do not - use this if you expect execute_vtctl_client to return data. - - Args: - args: args to pass to vtctl_client's execute_vtctl_command function - max_wait_s: maximum amount of time to wait for success (float, in seconds) - retry_wait_s: time between vtctl calls to wait (float, in seconds) - - Raises: - VtctlClientError: execute_vtctl_command never returned empty data - """ - start_time = time.time() - while time.time() - start_time < max_wait_s: - try: - if not self.execute_vtctl_command(args): - return - except VtctlClientError: - pass - time.sleep(retry_wait_s) - raise VtctlClientError( - 'Timed out on vtctl_client execute_vtctl_command_until_success') From 1eb28aff247f09913408853c18ba024c84fbcdfe Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Fri, 28 Feb 2020 19:45:04 +0000 Subject: [PATCH 186/825] Documentation removed Signed-off-by: Yuvraj --- .gitignore | 3 --- Makefile | 3 --- docker/README.md | 8 -------- 3 files changed, 14 deletions(-) diff --git a/.gitignore b/.gitignore index a6b4a89918e..57701a9c309 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,6 @@ tags # C build dirs **/build -# site-local example files -/examples/kubernetes/config.sh - # generated protobuf files /go/vt/.proto.tmp diff --git a/Makefile b/Makefile index d8b9f9a4c50..c1315dffc99 100644 --- a/Makefile +++ b/Makefile @@ -284,9 +284,6 @@ docker_lite_alpine: chmod -R o=g * docker build -f docker/lite/Dockerfile.alpine -t vitess/lite:alpine . -docker_guestbook: - cd examples/kubernetes/guestbook && ./build.sh - # This rule loads the working copy of the code into a bootstrap image, # and then runs the tests inside Docker. # Example: $ make docker_test flavor=mariadb diff --git a/docker/README.md b/docker/README.md index 8627204bf49..0741e7a2f1a 100644 --- a/docker/README.md +++ b/docker/README.md @@ -40,11 +40,3 @@ All these Vitess images include a specific MySQL/MariaDB version ("flavor"). * On Docker Hub we publish only images with MySQL 5.7 to minimize maintenance overhead and avoid confusion. If you are looking for a stable version of Vitess, use the **lite** image with a fixed version. If you are looking for the latest Vitess code in binary form, use the "latest" tag of the **base** image. - -### Kubernetes Tutorial Dependencies - -| Image | How (When) Updated | Description | -| --- | --- | --- | -| **guestbook** | manual (updated with every Vitess release) | Vitess adaption of the Kubernetes guestbook example. Used to showcase sharding in Vitess. Dockerfile is located in [`examples/kubernetes/guestbook/`](https://github.com/vitessio/vitess/tree/master/examples/kubernetes/guestbook). | -| **orchestrator** | manual | Binaries for [Orchestrator](https://github.com/github/orchestrator). It can be used with Vitess for automatic failovers. Currently not part of the Kubernetes Tutorial and only used in tests. | - From 4bf60108765ea7bbe9a5dfae4759252091e593e6 Mon Sep 17 00:00:00 2001 From: Andy Bursavich Date: Fri, 28 Feb 2020 15:53:00 -0800 Subject: [PATCH 187/825] mysql: remove allocation of unused bytes Signed-off-by: Andy Bursavich --- go/mysql/server.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/go/mysql/server.go b/go/mysql/server.go index 0ee6dfb2132..cb6b4dbe3b0 100644 --- a/go/mysql/server.go +++ b/go/mysql/server.go @@ -380,9 +380,8 @@ func (l *Listener) handle(conn net.Conn, connectionID uint32, acceptTime time.Ti if err != nil { return } - //lint:ignore SA4006 This line is required because the binary protocol requires padding with 0 - data := make([]byte, 21) - data = append(salt, byte(0x00)) + // The binary protocol requires padding with 0 + data := append(salt, byte(0x00)) if err := c.writeAuthSwitchRequest(MysqlNativePassword, data); err != nil { log.Errorf("Error writing auth switch packet for %s: %v", c, err) return From 30febaa4ab4d27cab0e3e1f8b6a73a29e089b754 Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Sat, 29 Feb 2020 07:27:22 +0000 Subject: [PATCH 188/825] Update document for docker build Signed-off-by: Yuvraj --- doc/DockerBuild.md | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/doc/DockerBuild.md b/doc/DockerBuild.md index 986b8e672c6..afb96430655 100644 --- a/doc/DockerBuild.md +++ b/doc/DockerBuild.md @@ -1,4 +1,4 @@ -By default, the [Kubernetes configs](https://github.com/vitessio/vitess/tree/master/examples/kubernetes) +By default, the [Helm Charts](https://github.com/vitessio/vitess/tree/master/helm) point to the `vitess/lite` image on [Docker Hub](https://hub.docker.com/u/vitess/). We created the `lite` image as a stripped down version of our main image `base` such that Kubernetes pods can start faster. @@ -81,19 +81,5 @@ Then you can run our build script for the `lite` image which extracts the Vitess **Note:** If you chose a non-default flavor above, then change `vitess/lite` in the above command to `vitess/lite:`. -1. Change the Kubernetes configs to point to your personal repository: - ```sh - vitess/examples/kubernetes$ sed -i -e 's,image: vitess/lite,image: yourname/vitess:latest,' *.yaml - ``` - - Adding the `:latest` label at the end of the image name tells Kubernetes - to check for a newer image every time a pod is launched. - When you push a new version of your image, any new pods will use it - automatically without you having to clear the Kubernetes image cache. - - Once you've stabilized your image, you'll probably want to replace `:latest` - with a specific label that you change each time you make a new build, - so you can control when pods update. - -1. Launch [Vitess on Kubernetes]({% link getting-started/index.md %}) as usual. +1. Launch [Vitess on Kubernetes](https://vitess.io/docs/get-started/index.html) as usual. From 4065615b723553d90a91e1ef1ae7da40e4f405d2 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 2 Jan 2020 17:12:52 -0800 Subject: [PATCH 189/825] vrepl: document vstreamer/planbuilder.go Signed-off-by: Sugu Sougoumarane --- .../vreplication/table_plan_builder.go | 2 +- .../tabletserver/vstreamer/planbuilder.go | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go index 808d4d87437..9cf14cee39c 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go +++ b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go @@ -126,7 +126,7 @@ func buildReplicatorPlan(filter *binlogdatapb.Filter, tableKeys map[string][]str return plan, nil } -// MatchTable is similar to tableMatches defined in vstreamer. +// MatchTable is similar to tableMatches and buildPlan defined in vstreamer/planbuilder.go. func MatchTable(tableName string, filter *binlogdatapb.Filter) (*binlogdatapb.Rule, error) { for _, rule := range filter.Rules { switch { diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index 84a8c79851b..c084327c877 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -35,11 +35,15 @@ import ( // Plan represents the plan for a table. type Plan struct { - Table *Table + Table *Table + // ColExprs is the list of column expressions to be sent + // in the stream. ColExprs []ColExpr // Vindex, VindexColumns and KeyRange, if set, will be used // to filter the row. + // VindexColumns contains the column numbers of the table, + // and not the column numbers of the stream to be sent. Vindex vindexes.Vindex VindexColumns []int KeyRange *topodatapb.KeyRange @@ -52,9 +56,15 @@ type ColExpr struct { // Vindex and VindexColumns, if set, will be used to generate // a keyspace_id. If so, ColNum is ignored. + // VindexColumns contains the column numbers of the table, + // and not the column numbers of the stream to be sent. Vindex vindexes.Vindex VindexColumns []int + // Alias is usually the column name, but it can be changed + // if the select expression aliases with an "AS" expression. + // Also, "keyspace_id()" will be aliased as "keyspace_id". + // This Alias is sent as field info for the returned stream. Alias sqlparser.ColIdent Type querypb.Type } @@ -166,7 +176,7 @@ func mustSendDDL(query mysql.Query, dbname string, filter *binlogdatapb.Filter) return true } -// tableMatches is similar to the one defined in vreplication. +// tableMatches is similar to buildPlan below and MatchTable in vreplication/table_plan_builder.go. func tableMatches(table sqlparser.TableName, dbname string, filter *binlogdatapb.Filter) bool { if !table.Qualifier.IsEmpty() && table.Qualifier.String() != dbname { return false @@ -177,7 +187,7 @@ func tableMatches(table sqlparser.TableName, dbname string, filter *binlogdatapb expr := strings.Trim(rule.Match, "/") result, err := regexp.MatchString(expr, table.Name.String()) if err != nil { - continue + return false } if !result { continue @@ -210,6 +220,8 @@ func buildPlan(ti *Table, vschema *localVSchema, filter *binlogdatapb.Filter) (* return nil, nil } +// buildREPlan handles cases where Match has a regular expression. +// If so, the Filter can be an empty string or a keyrange, like "-80". func buildREPlan(ti *Table, vschema *localVSchema, filter string) (*Plan, error) { plan := &Plan{ Table: ti, @@ -248,6 +260,8 @@ func buildREPlan(ti *Table, vschema *localVSchema, filter string) (*Plan, error) return plan, nil } +// BuildTablePlan handles cases where a specific table name is specified. +// The filter must be a select statement. func buildTablePlan(ti *Table, vschema *localVSchema, query string) (*Plan, error) { sel, fromTable, err := analyzeSelect(query) if err != nil { @@ -376,6 +390,9 @@ func (plan *Plan) analyzeExpr(vschema *localVSchema, selExpr sqlparser.SelectExp } } +// analyzeInKeyRange allows the following constructs: "in_keyrange('-80')", +// "in_keyrange(col, 'hash', '-80')", "in_keyrange(col, 'local_vindex', '-80')", or +// "in_keyrange(col, 'ks.external_vindex', '-80')". func (plan *Plan) analyzeInKeyRange(vschema *localVSchema, exprs sqlparser.SelectExprs) error { var colnames []sqlparser.ColIdent var krExpr sqlparser.SelectExpr @@ -452,6 +469,8 @@ func selString(expr sqlparser.SelectExpr) (string, error) { return string(val.Val), nil } +// buildVindexColumns builds the list of column numbers of the table +// that will be the input to the vindex function. func buildVindexColumns(ti *Table, colnames []sqlparser.ColIdent) ([]int, error) { vindexColumns := make([]int, 0, len(colnames)) for _, colname := range colnames { From d95ad8ae43a219eb8de91f1f1d2ef586468a29c3 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 2 Jan 2020 23:26:48 -0800 Subject: [PATCH 190/825] vrepl: improve row and result streamers The new implementation starts a transaction after locking the tables instead of issuing the read query. This guarantees that we'll release the table locks immediately. Otherwise, there's no guarantee about when the first results will be returned. Also, the logic to do the snapshotting has been moved into a reusable connection wrapper, which is a more elegant approach. Signed-off-by: Sugu Sougoumarane --- .../tabletserver/vstreamer/resultstreamer.go | 52 ++------ .../tabletserver/vstreamer/rowstreamer.go | 72 ++++------- .../tabletserver/vstreamer/snapshot_conn.go | 115 ++++++++++++++++++ .../vstreamer/snapshot_conn_test.go | 60 +++++++++ 4 files changed, 208 insertions(+), 91 deletions(-) create mode 100644 go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go create mode 100644 go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go diff --git a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go index e79b1076434..44892ee821b 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go @@ -22,12 +22,14 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" - "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" "vitess.io/vitess/go/vt/sqlparser" ) +// resultStreamer streams the results of the requested query +// along with the GTID of the snapshot. This is used by vdiff +// to synchronize the target to that GTID before comparing +// the results. type resultStreamer struct { ctx context.Context cancel func() @@ -60,12 +62,12 @@ func (rs *resultStreamer) Stream() error { } rs.tableName = fromTable - conn, err := rs.mysqlConnect() + conn, err := snapshotConnect(rs.ctx, rs.cp) if err != nil { return err } defer conn.Close() - gtid, err := rs.startStreaming(conn) + gtid, err := conn.streamWithSnapshot(rs.ctx, rs.tableName.String(), rs.query) if err != nil { return err } @@ -112,7 +114,7 @@ func (rs *resultStreamer) Stream() error { } // empty the rows so we start over, but we keep the // same capacity - response.Rows = response.Rows[:0] + response.Rows = nil byteCount = 0 } } @@ -126,43 +128,3 @@ func (rs *resultStreamer) Stream() error { return nil } - -func (rs *resultStreamer) startStreaming(conn *mysql.Conn) (string, error) { - lockConn, err := rs.mysqlConnect() - if err != nil { - return "", err - } - // To be safe, always unlock tables, even if lock tables might fail. - defer func() { - _, err := lockConn.ExecuteFetch("unlock tables", 0, false) - if err != nil { - log.Warning("Unlock tables failed: %v", err) - } else { - log.Infof("Tables unlocked", rs.tableName) - } - lockConn.Close() - }() - - log.Infof("Locking table %s for copying", rs.tableName) - if _, err := lockConn.ExecuteFetch(fmt.Sprintf("lock tables %s read", sqlparser.String(rs.tableName)), 0, false); err != nil { - return "", err - } - pos, err := lockConn.MasterPosition() - if err != nil { - return "", err - } - - if err := conn.ExecuteStreamFetch(rs.query); err != nil { - return "", err - } - - return mysql.EncodePosition(pos), nil -} - -func (rs *resultStreamer) mysqlConnect() (*mysql.Conn, error) { - cp, err := dbconfigs.WithCredentials(rs.cp) - if err != nil { - return nil, err - } - return mysql.Connect(rs.ctx, cp) -} diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go index 71254a81689..b1ed30469b0 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go @@ -22,7 +22,6 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -43,6 +42,14 @@ func NewRowStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine return newRowStreamer(ctx, cp, se, query, lastpk, &localVSchema{vschema: &vindexes.VSchema{}}, send) } +// rowStreamer is used for copying the existing rows of a table +// before vreplication begins streaming binlogs. The rowStreamer +// responds to a request with the GTID position as of which it +// streams the rows of a table. This allows vreplication to synchronize +// its events as of the returned GTID before adding the new rows. +// For every set of rows sent, the last pk value is also sent. +// This allows for the streaming to be resumed based on the last +// pk value processed. type rowStreamer struct { ctx context.Context cancel func() @@ -88,7 +95,7 @@ func (rs *rowStreamer) Stream() error { return err } - conn, err := rs.mysqlConnect() + conn, err := snapshotConnect(rs.ctx, rs.cp) if err != nil { return err } @@ -114,6 +121,10 @@ func (rs *rowStreamer) buildPlan() error { Name: st.Name.String(), Columns: st.Columns, } + // The plan we build is identical to the one for vstreamer. + // This is because the row format of a read is identical + // to the row format of a binlog event. So, the same + // filtering will work. rs.plan, err = buildTablePlan(ti, rs.vschema, rs.query) if err != nil { return err @@ -147,6 +158,7 @@ func buildPKColumns(st *schema.Table) ([]int, error) { func (rs *rowStreamer) buildSelect() (string, error) { buf := sqlparser.NewTrackedBuffer(nil) + // We could have used select *, but being explicit is more predictable. buf.Myprintf("select ") prefix := "" for _, col := range rs.plan.Table.Columns { @@ -160,6 +172,11 @@ func (rs *rowStreamer) buildSelect() (string, error) { } buf.WriteString(" where ") prefix := "" + // This loop handles the case for composite pks. For example, + // if lastpk was (1,2), the where clause would be: + // (col1 = 1 and col2 > 2) or (col1 > 1). + // A tuple inequality like (col1,col2) > (1,2) ends up + // being a full table scan for mysql. for lastcol := len(rs.pkColumns) - 1; lastcol >= 0; lastcol-- { buf.Myprintf("%s(", prefix) prefix = " or " @@ -182,8 +199,9 @@ func (rs *rowStreamer) buildSelect() (string, error) { return buf.String(), nil } -func (rs *rowStreamer) streamQuery(conn *mysql.Conn, send func(*binlogdatapb.VStreamRowsResponse) error) error { - gtid, err := rs.startStreaming(conn) +func (rs *rowStreamer) streamQuery(conn *snapshotConn, send func(*binlogdatapb.VStreamRowsResponse) error) error { + log.Infof("Streaming query: %v\n", rs.sendQuery) + gtid, err := conn.streamWithSnapshot(rs.ctx, rs.plan.Table.Name, rs.sendQuery) if err != nil { return err } @@ -227,9 +245,12 @@ func (rs *rowStreamer) streamQuery(conn *mysql.Conn, send func(*binlogdatapb.VSt if row == nil { break } + // Compute lastpk here, because we'll neeed it + // at the end after the loop exits. for i, pk := range rs.pkColumns { lastpk[i] = row[pk] } + // Reuse the vstreamer's filter. ok, filtered, err := rs.plan.filter(row) if err != nil { return err @@ -249,7 +270,7 @@ func (rs *rowStreamer) streamQuery(conn *mysql.Conn, send func(*binlogdatapb.VSt } // empty the rows so we start over, but we keep the // same capacity - response.Rows = response.Rows[:0] + response.Rows = nil byteCount = 0 } } @@ -264,44 +285,3 @@ func (rs *rowStreamer) streamQuery(conn *mysql.Conn, send func(*binlogdatapb.VSt return nil } - -func (rs *rowStreamer) startStreaming(conn *mysql.Conn) (string, error) { - lockConn, err := rs.mysqlConnect() - if err != nil { - return "", err - } - // To be safe, always unlock tables, even if lock tables might fail. - defer func() { - _, err := lockConn.ExecuteFetch("unlock tables", 0, false) - if err != nil { - log.Warning("Unlock tables failed: %v", err) - } else { - log.Infof("Tables unlocked", rs.plan.Table.Name) - } - lockConn.Close() - }() - - log.Infof("Locking table %s for copying", rs.plan.Table.Name) - if _, err := lockConn.ExecuteFetch(fmt.Sprintf("lock tables %s read", sqlparser.String(sqlparser.NewTableIdent(rs.plan.Table.Name))), 0, false); err != nil { - return "", err - } - pos, err := lockConn.MasterPosition() - if err != nil { - return "", err - } - - log.Infof("Streaming query: %v\n", rs.sendQuery) - if err := conn.ExecuteStreamFetch(rs.sendQuery); err != nil { - return "", err - } - - return mysql.EncodePosition(pos), nil -} - -func (rs *rowStreamer) mysqlConnect() (*mysql.Conn, error) { - cp, err := dbconfigs.WithCredentials(rs.cp) - if err != nil { - return nil, err - } - return mysql.Connect(rs.ctx, cp) -} diff --git a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go new file mode 100644 index 00000000000..cf3954f00a2 --- /dev/null +++ b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go @@ -0,0 +1,115 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vstreamer + +import ( + "context" + "fmt" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/sqlparser" +) + +// snapshotConn is wrapper on mysql.Conn capable of +// reading a table along with a gtid snapshot. +type snapshotConn struct { + *mysql.Conn + cp *mysql.ConnParams +} + +func snapshotConnect(ctx context.Context, cp *mysql.ConnParams) (*snapshotConn, error) { + mconn, err := mysqlConnect(ctx, cp) + if err != nil { + return nil, err + } + return &snapshotConn{ + Conn: mconn, + cp: cp, + }, nil +} + +// startSnapshot starts a streaming query with a snapshot view of the specified table. +// It returns the gtid of the time when the snapshot was taken. +func (conn *snapshotConn) streamWithSnapshot(ctx context.Context, table, query string) (gtid string, err error) { + gtid, err = conn.startSnapshot(ctx, table) + if err != nil { + return "", err + } + if err := conn.ExecuteStreamFetch(query); err != nil { + return "", err + } + return gtid, nil +} + +// snapshot performs the snapshotting. +func (conn *snapshotConn) startSnapshot(ctx context.Context, table string) (gtid string, err error) { + lockConn, err := mysqlConnect(ctx, conn.cp) + if err != nil { + return "", err + } + // To be safe, always unlock tables, even if lock tables might fail. + defer func() { + _, err := lockConn.ExecuteFetch("unlock tables", 0, false) + if err != nil { + log.Warning("Unlock tables failed: %v", err) + } else { + log.Infof("Tables unlocked: %v", table) + } + lockConn.Close() + }() + + tableIdent := sqlparser.String(sqlparser.NewTableIdent(table)) + + log.Infof("Locking table %s for copying", table) + if _, err := lockConn.ExecuteFetch(fmt.Sprintf("lock tables %s read", tableIdent), 1, false); err != nil { + return "", err + } + mpos, err := lockConn.MasterPosition() + if err != nil { + return "", err + } + + // Starting a transaction now will allow us to start the read later, + // which will happen after we release the lock on the table. + if _, err := conn.ExecuteFetch("set transaction isolation level repeatable read", 1, false); err != nil { + return "", err + } + if _, err := conn.ExecuteFetch("start transaction read only", 1, false); err != nil { + return "", err + } + // For the transaction to really start, we have to perform a read. + if _, err := conn.ExecuteFetch(fmt.Sprintf("select 1 from %s limit 1", tableIdent), 1, false); err != nil { + return "", err + } + return mysql.EncodePosition(mpos), nil +} + +// Close rollsback any open transactions and closes the connection. +func (conn *snapshotConn) Close() { + _, _ = conn.ExecuteFetch("rollback", 1, false) + conn.Conn.Close() +} + +func mysqlConnect(ctx context.Context, cp *mysql.ConnParams) (*mysql.Conn, error) { + cp, err := dbconfigs.WithCredentials(cp) + if err != nil { + return nil, err + } + return mysql.Connect(ctx, cp) +} diff --git a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go new file mode 100644 index 00000000000..8824687b161 --- /dev/null +++ b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vstreamer + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" +) + +func TestStartSnapshot(t *testing.T) { + if testing.Short() { + t.Skip() + } + + execStatements(t, []string{ + "create table t1(id int, val varbinary(128), primary key(id))", + "insert into t1 values (1, 'aaa')", + }) + defer execStatements(t, []string{ + "drop table t1", + }) + + ctx := context.Background() + conn, err := snapshotConnect(ctx, engine.cp) + require.NoError(t, err) + defer conn.Close() + + conn.startSnapshot(ctx, "t1") + + // This second row should not be in the result. + execStatement(t, "insert into t1 values(2, 'bbb')") + + wantqr := &sqltypes.Result{ + Rows: [][]sqltypes.Value{ + {sqltypes.NewInt32(1), sqltypes.NewVarBinary("aaa")}, + }, + RowsAffected: 1, + } + qr, err := conn.ExecuteFetch("select * from t1", 10, false) + require.NoError(t, err) + assert.Equal(t, wantqr, qr) +} From 2724ba1e6f444cb798c8652c7fe6595c510acb86 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Thu, 2 Jan 2020 11:38:12 +0100 Subject: [PATCH 191/825] Added /debug/vrlog endpoint. Initial commit, untested Signed-off-by: Rohit Nayak --- .../tabletmanager/vreplication/vplayer.go | 13 +- .../tabletmanager/vreplication/vrlog.go | 126 ++++++++++++++++++ .../tabletmanager/vreplication/vrlog_test.go | 49 +++++++ 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 go/vt/vttablet/tabletmanager/vreplication/vrlog.go create mode 100644 go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 464a4fe46cb..9196d30b845 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -192,7 +192,10 @@ func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.Row } for _, change := range rowEvent.RowChanges { _, err := tplan.applyChange(change, func(sql string) (*sqltypes.Result, error) { - return vp.vr.dbClient.ExecuteWithRetry(ctx, sql) + stats := NewVrLogStats(ctx, "RowChange") + result, err := vp.vr.dbClient.ExecuteWithRetry(ctx, sql) + stats.Record(sql) + return result, err }) if err != nil { return err @@ -307,6 +310,7 @@ func hasAnotherCommit(items [][]*binlogdatapb.VEvent, i, j int) bool { } func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, mustSave bool) error { + stats := NewVrLogStats(ctx, event.String()) switch event.Type { case binlogdatapb.VEventType_GTID: pos, err := mysql.DecodePosition(event.Gtid) @@ -352,6 +356,8 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m return err } vp.tablePlans[event.FieldEvent.TableName] = tplan + stats.Record(fmt.Sprintf("%v", event.FieldEvent)) + case binlogdatapb.VEventType_INSERT, binlogdatapb.VEventType_DELETE, binlogdatapb.VEventType_UPDATE, binlogdatapb.VEventType_REPLACE: // This is a player using stament based replication if err := vp.vr.dbClient.Begin(); err != nil { @@ -361,6 +367,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if err := vp.applyStmtEvent(ctx, event); err != nil { return err } + stats.Record(fmt.Sprintf(event.Dml)) case binlogdatapb.VEventType_ROW: // This player is configured for row based replication if err := vp.vr.dbClient.Begin(); err != nil { @@ -369,6 +376,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if err := vp.applyRowEvent(ctx, event.RowEvent); err != nil { return err } + stats.Record(fmt.Sprintf("%v", event.RowEvent)) case binlogdatapb.VEventType_OTHER: if vp.vr.dbClient.InTransaction { // Unreachable @@ -417,6 +425,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, event.Ddl); err != nil { return err } + stats.Record(fmt.Sprintf("%v", event.Ddl)) posReached, err := vp.updatePos(event.Timestamp) if err != nil { return err @@ -428,6 +437,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, event.Ddl); err != nil { log.Infof("Ignoring error: %v for DDL: %s", err, event.Ddl) } + stats.Record(fmt.Sprintf("%v", event.Ddl)) posReached, err := vp.updatePos(event.Timestamp) if err != nil { return err @@ -481,6 +491,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m } return io.EOF } + stats.Record(fmt.Sprintf("%v", event.Journal)) return io.EOF case binlogdatapb.VEventType_HEARTBEAT: // No-op: heartbeat timings are calculated in outer loop. diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go new file mode 100644 index 00000000000..6993230c8c2 --- /dev/null +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go @@ -0,0 +1,126 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vreplication + +import ( + "html/template" + "net/http" + "strconv" + "time" + + "golang.org/x/net/context" + "vitess.io/vitess/go/streamlog" + "vitess.io/vitess/go/vt/log" +) + +var ( + vrLogStatsLogger = streamlog.New("VReplication", 50) + vrLogStatsTemplate = template.Must(template.New("vrlog").Parse(` + {{.Type}}\t + {{.Detail}}\t + {{.Time}}\t + {{.DurationNs}}\n + `)) +) + +type VrLogStats struct { + Ctx context.Context + Type string + Detail string + Time time.Time + DurationNs int64 +} + +func (stats *VrLogStats) Send() { + vrLogStatsLogger.Send(stats) +} + +func (stats *VrLogStats) Record(detail string) { + if stats.Ctx == nil || stats.Time.IsZero() { + log.Error("VrLogStats not initialized") + } + stats.Detail = detail + stats.DurationNs = time.Since(stats.Time).Nanoseconds() + stats.Send() +} + +func NewVrLogStats(ctx context.Context, eventType string) *VrLogStats { + return &VrLogStats{Ctx: ctx, Type: eventType, Time: time.Now()} +} + +func init() { + http.HandleFunc("/debug/vrlog", func(w http.ResponseWriter, r *http.Request) { + ch := vrLogStatsLogger.Subscribe("vrlogstats") + defer vrLogStatsLogger.Unsubscribe(ch) + vrlogStatsHandler(ch, w, r) + }) +} + +func vrlogStatsHandler(ch chan interface{}, w http.ResponseWriter, r *http.Request) { + timeout, limit := parseTimeoutLimitParams(r) + + tmr := time.NewTimer(timeout) + defer tmr.Stop() + for i := 0; i < limit; i++ { + select { + case out := <-ch: + select { + case <-tmr.C: + return + default: + } + stats, ok := out.(*VrLogStats) + if !ok { + log.Error("Log received is not of type VrLogStats") + continue + } + if err := vrLogStatsTemplate.Execute(w, stats); err != nil { + log.Errorf("vrlog: couldn't execute template: %v", err) + } + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + case <-tmr.C: + return + } + } +} + +func parseTimeoutLimitParams(req *http.Request) (time.Duration, int) { + timeout := 1000 + limit := 300 + if ts, ok := req.URL.Query()["timeout"]; ok { + if t, err := strconv.Atoi(ts[0]); err == nil { + timeout = adjustValue(t, 0, 60) + } + } + if l, ok := req.URL.Query()["limit"]; ok { + if lim, err := strconv.Atoi(l[0]); err == nil { + limit = adjustValue(lim, 1, 200000) + } + } + return time.Duration(timeout) * time.Second, limit +} + +func adjustValue(val int, lower int, upper int) int { + if val < lower { + return lower + } else if val > upper { + return upper + } + return val +} diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go new file mode 100644 index 00000000000..479c8c46f51 --- /dev/null +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go @@ -0,0 +1,49 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vreplication + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" +) + +func TestVrLog(t *testing.T) { + r, _ := http.NewRequest("GET", "/debug/vrlog?timeout=10&limit=1", nil) + w := httptest.NewRecorder() + + ch := vrLogStatsLogger.Subscribe("vrlog") + defer vrLogStatsLogger.Unsubscribe(ch) + go func() { + vrlogStatsHandler(ch, w, r) + }() + stats := VrLogStats{Ctx: context.Background()} + msg := "test msg" + stats.Detail = msg + stats.Send() + time.Sleep(1 * time.Millisecond) + s := w.Body.String() + + if !strings.Contains(s, msg) { //we use Contains since the printed log is in html and also prepends current time + t.Fatalf(fmt.Sprintf("want %s, got %s", msg, s)) + } + +} From 8383470c150aff3b7499aa5dc63d4c7ad334770d Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 1 Mar 2020 18:25:28 -0800 Subject: [PATCH 192/825] mysql: flush buffer on inactivity The mysql server uses buffered I/O. This works fine if the results are streamed all at once. But if the streaming is intermitent, like in the case of messages, the client may not get timely responses. This new change flushes any unsent data if no other data is sent within mysql_server_flush_delay (100ms). Signed-off-by: Sugu Sougoumarane --- go.mod | 1 + go/mysql/conn.go | 271 ++++++++++++++++++++++++---------------- go/mysql/conn_test.go | 2 +- go/mysql/query.go | 2 +- go/mysql/server.go | 4 +- go/mysql/server_test.go | 64 ++++++++++ 6 files changed, 235 insertions(+), 109 deletions(-) diff --git a/go.mod b/go.mod index 3094e484674..c1252dca071 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect github.com/golangci/golangci-lint v1.21.0 // indirect github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect + github.com/google/go-cmp v0.3.0 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf // indirect github.com/gorilla/websocket v1.4.0 github.com/gostaticanalysis/analysisutil v0.0.3 // indirect diff --git a/go/mysql/conn.go b/go/mysql/conn.go index 9908c7e3557..8cb4d04041f 100644 --- a/go/mysql/conn.go +++ b/go/mysql/conn.go @@ -21,6 +21,7 @@ import ( "crypto/tls" "crypto/x509" "errors" + "flag" "fmt" "io" "net" @@ -38,6 +39,8 @@ import ( "vitess.io/vitess/go/vt/vterrors" ) +var mysqlServerFlushDelay = flag.Duration("mysql_server_flush_delay", 100*time.Millisecond, "Delay after which buffered response will flushed to client.") + const ( // connBufferSize is how much we buffer for reading and // writing. It is also how much we allocate for ephemeral buffers. @@ -139,9 +142,13 @@ type Conn struct { ClientData interface{} // Packet encoding variables. + sequence uint8 bufferedReader *bufio.Reader + + // Buffered writing has a timer which flushes on inactivity. + bufMu sync.Mutex bufferedWriter *bufio.Writer - sequence uint8 + flushTimer *time.Timer // fields contains the fields definitions for an on-going // streaming query. It is set by ExecuteStreamFetch, and @@ -212,15 +219,20 @@ func newServerConn(conn net.Conn, listener *Listener) *Conn { } // startWriterBuffering starts using buffered writes. This should -// be terminated by a call to flush. +// be terminated by a call to endWriteBuffering. func (c *Conn) startWriterBuffering() { + c.bufMu.Lock() + defer c.bufMu.Unlock() + c.bufferedWriter = writersPool.Get().(*bufio.Writer) c.bufferedWriter.Reset(c.conn) } -// flush flushes the written data to the socket. -// This must be called to terminate startBuffering. -func (c *Conn) flush() error { +// endWriterBuffering must be called to terminate startWriteBuffering. +func (c *Conn) endWriterBuffering() error { + c.bufMu.Lock() + defer c.bufMu.Unlock() + if c.bufferedWriter == nil { return nil } @@ -231,16 +243,48 @@ func (c *Conn) flush() error { c.bufferedWriter = nil }() + c.stopFlushTimer() return c.bufferedWriter.Flush() } // getWriter returns the current writer. It may be either -// the original connection or a wrapper. -func (c *Conn) getWriter() io.Writer { +// the original connection or a wrapper. The returned unget +// function must be invoked after the writing is finished. +// In buffered mode, the unget starts a timer to flush any +// buffered data. +func (c *Conn) getWriter() (w io.Writer, unget func()) { + c.bufMu.Lock() if c.bufferedWriter != nil { - return c.bufferedWriter + return c.bufferedWriter, func() { + c.startFlushTimer() + c.bufMu.Unlock() + } + } + c.bufMu.Unlock() + return c.conn, func() {} +} + +// startFlushTimer must be called while holding lock on bufMu. +func (c *Conn) startFlushTimer() { + c.stopFlushTimer() + c.flushTimer = time.AfterFunc(*mysqlServerFlushDelay, func() { + c.bufMu.Lock() + defer c.bufMu.Unlock() + + if c.bufferedWriter == nil { + return + } + c.stopFlushTimer() + c.bufferedWriter.Flush() + }) +} + +// stopFlushTimer must be called while holding lock on bufMu. +func (c *Conn) stopFlushTimer() { + if c.flushTimer != nil { + c.flushTimer.Stop() + c.flushTimer = nil } - return c.conn } // getReader returns reader for connection. It can be *bufio.Reader or net.Conn @@ -474,7 +518,8 @@ func (c *Conn) writePacket(data []byte) error { index := 0 length := len(data) - w := c.getWriter() + w, unget := c.getWriter() + defer unget() for { // Packet length is capped to MaxPacketSize. @@ -740,43 +785,47 @@ func (c *Conn) handleNextCommand(handler Handler) error { return err } case ComQuery: - // flush is called at the end of this block. - // We cannot encapsulate it with a defer inside a func because - // we have to return from this func if it fails. - c.startWriterBuffering() - - queryStart := time.Now() - query := c.parseComQuery(data) - c.recycleReadPacket() + err := func() error { + c.startWriterBuffering() + defer func() { + if err := c.endWriterBuffering(); err != nil { + log.Errorf("conn %v: flush() failed: %v", c.ID(), err) + } + }() - var queries []string - if c.Capabilities&CapabilityClientMultiStatements != 0 { - queries, err = sqlparser.SplitStatementToPieces(query) - if err != nil { - log.Errorf("Conn %v: Error splitting query: %v", c, err) - if werr := c.writeErrorPacketFromError(err); werr != nil { - // If we can't even write the error, we're done. - log.Errorf("Conn %v: Error writing query error: %v", c, werr) - return werr + queryStart := time.Now() + query := c.parseComQuery(data) + c.recycleReadPacket() + + var queries []string + if c.Capabilities&CapabilityClientMultiStatements != 0 { + queries, err = sqlparser.SplitStatementToPieces(query) + if err != nil { + log.Errorf("Conn %v: Error splitting query: %v", c, err) + if werr := c.writeErrorPacketFromError(err); werr != nil { + // If we can't even write the error, we're done. + log.Errorf("Conn %v: Error writing query error: %v", c, werr) + return werr + } } + } else { + queries = []string{query} } - } else { - queries = []string{query} - } - for index, sql := range queries { - more := false - if index != len(queries)-1 { - more = true - } - if err := c.execQuery(sql, handler, more); err != nil { - return err + for index, sql := range queries { + more := false + if index != len(queries)-1 { + more = true + } + if err := c.execQuery(sql, handler, more); err != nil { + return err + } } - } - timings.Record(queryTimingKey, queryStart) + timings.Record(queryTimingKey, queryStart) - if err := c.flush(); err != nil { - log.Errorf("Conn %v: Flush() failed: %v", c.ID(), err) + return nil + }() + if err != nil { return err } @@ -896,84 +945,96 @@ func (c *Conn) handleNextCommand(handler Handler) error { } case ComStmtExecute: - queryStart := time.Now() - stmtID, _, err := c.parseComStmtExecute(c.PrepareData, data) - c.recycleReadPacket() - - if stmtID != uint32(0) { + err := func() error { + c.startWriterBuffering() defer func() { - // Allocate a new bindvar map every time since VTGate.Execute() mutates it. - prepare := c.PrepareData[stmtID] - prepare.BindVars = make(map[string]*querypb.BindVariable, prepare.ParamsCount) + if err := c.endWriterBuffering(); err != nil { + log.Errorf("conn %v: flush() failed: %v", c.ID(), err) + } }() - } - - if err != nil { - if werr := c.writeErrorPacketFromError(err); werr != nil { - // If we can't even write the error, we're done. - log.Error("Error writing query error to client %v: %v", c.ConnectionID, werr) - return werr + queryStart := time.Now() + stmtID, _, err := c.parseComStmtExecute(c.PrepareData, data) + c.recycleReadPacket() + + if stmtID != uint32(0) { + defer func() { + // Allocate a new bindvar map every time since VTGate.Execute() mutates it. + prepare := c.PrepareData[stmtID] + prepare.BindVars = make(map[string]*querypb.BindVariable, prepare.ParamsCount) + }() } - return nil - } - fieldSent := false - // sendFinished is set if the response should just be an OK packet. - sendFinished := false - prepare := c.PrepareData[stmtID] - err = handler.ComStmtExecute(c, prepare, func(qr *sqltypes.Result) error { - if sendFinished { - // Failsafe: Unreachable if server is well-behaved. - return io.EOF + if err != nil { + if werr := c.writeErrorPacketFromError(err); werr != nil { + // If we can't even write the error, we're done. + log.Error("Error writing query error to client %v: %v", c.ConnectionID, werr) + return werr + } + return nil } - if !fieldSent { - fieldSent = true - - if len(qr.Fields) == 0 { - sendFinished = true - // We should not send any more packets after this. - return c.writeOKPacket(qr.RowsAffected, qr.InsertID, c.StatusFlags, 0) + fieldSent := false + // sendFinished is set if the response should just be an OK packet. + sendFinished := false + prepare := c.PrepareData[stmtID] + err = handler.ComStmtExecute(c, prepare, func(qr *sqltypes.Result) error { + if sendFinished { + // Failsafe: Unreachable if server is well-behaved. + return io.EOF } - if err := c.writeFields(qr); err != nil { - return err + + if !fieldSent { + fieldSent = true + + if len(qr.Fields) == 0 { + sendFinished = true + // We should not send any more packets after this. + return c.writeOKPacket(qr.RowsAffected, qr.InsertID, c.StatusFlags, 0) + } + if err := c.writeFields(qr); err != nil { + return err + } } - } - return c.writeBinaryRows(qr) - }) + return c.writeBinaryRows(qr) + }) - // If no field was sent, we expect an error. - if !fieldSent { - // This is just a failsafe. Should never happen. - if err == nil || err == io.EOF { - err = NewSQLErrorFromError(errors.New("unexpected: query ended without no results and no error")) - } - if werr := c.writeErrorPacketFromError(err); werr != nil { - // If we can't even write the error, we're done. - log.Errorf("Error writing query error to %s: %v", c, werr) - return werr - } - } else { - if err != nil { - // We can't send an error in the middle of a stream. - // All we can do is abort the send, which will cause a 2013. - log.Errorf("Error in the middle of a stream to %s: %v", c, err) - return err - } - - // Send the end packet only sendFinished is false (results were streamed). - // In this case the affectedRows and lastInsertID are always 0 since it - // was a read operation. - if !sendFinished { - if err := c.writeEndResult(false, 0, 0, handler.WarningCount(c)); err != nil { - log.Errorf("Error writing result to %s: %v", c, err) + // If no field was sent, we expect an error. + if !fieldSent { + // This is just a failsafe. Should never happen. + if err == nil || err == io.EOF { + err = NewSQLErrorFromError(errors.New("unexpected: query ended without no results and no error")) + } + if werr := c.writeErrorPacketFromError(err); werr != nil { + // If we can't even write the error, we're done. + log.Errorf("Error writing query error to %s: %v", c, werr) + return werr + } + } else { + if err != nil { + // We can't send an error in the middle of a stream. + // All we can do is abort the send, which will cause a 2013. + log.Errorf("Error in the middle of a stream to %s: %v", c, err) return err } + + // Send the end packet only sendFinished is false (results were streamed). + // In this case the affectedRows and lastInsertID are always 0 since it + // was a read operation. + if !sendFinished { + if err := c.writeEndResult(false, 0, 0, handler.WarningCount(c)); err != nil { + log.Errorf("Error writing result to %s: %v", c, err) + return err + } + } } - } - timings.Record(queryTimingKey, queryStart) + timings.Record(queryTimingKey, queryStart) + return nil + }() + if err != nil { + return err + } case ComStmtSendLongData: stmtID, paramID, chunkData, ok := c.parseComStmtSendLongData(data) c.recycleReadPacket() diff --git a/go/mysql/conn_test.go b/go/mysql/conn_test.go index 810ffea7a5f..405b23c40d5 100644 --- a/go/mysql/conn_test.go +++ b/go/mysql/conn_test.go @@ -89,7 +89,7 @@ func useWriteEphemeralPacketBuffered(t *testing.T, cConn *Conn, data []byte) { } }() cConn.startWriterBuffering() - defer cConn.flush() + defer cConn.endWriterBuffering() buf := cConn.startEphemeralPacket(len(data)) copy(buf, data) diff --git a/go/mysql/query.go b/go/mysql/query.go index f6435bf4b64..89dfb5c8c98 100644 --- a/go/mysql/query.go +++ b/go/mysql/query.go @@ -1063,7 +1063,7 @@ func (c *Conn) writePrepare(fld []*querypb.Field, prepare *PrepareData) error { } } - return c.flush() + return nil } func (c *Conn) writeBinaryRow(fields []*querypb.Field, row []sqltypes.Value) error { diff --git a/go/mysql/server.go b/go/mysql/server.go index 0ee6dfb2132..e5ce5b3e6f1 100644 --- a/go/mysql/server.go +++ b/go/mysql/server.go @@ -277,9 +277,9 @@ func (l *Listener) handle(conn net.Conn, connectionID uint32, acceptTime time.Ti if x := recover(); x != nil { log.Errorf("mysql_server caught panic:\n%v\n%s", x, tb.Stack(4)) } - // We call flush here in case there's a premature return after + // We call endWriterBuffering here in case there's a premature return after // startWriterBuffering is called - c.flush() + c.endWriterBuffering() conn.Close() }() diff --git a/go/mysql/server_test.go b/go/mysql/server_test.go index 0f6be720591..1c09b07cc61 100644 --- a/go/mysql/server_test.go +++ b/go/mysql/server_test.go @@ -28,6 +28,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" @@ -186,6 +188,19 @@ func (th *testHandler) ComQuery(c *Conn, query string, callback func(*sqltypes.R }, }, }) + case "50ms delay": + callback(&sqltypes.Result{ + Fields: []*querypb.Field{{ + Name: "result", + Type: querypb.Type_VARCHAR, + }}, + }) + time.Sleep(50 * time.Millisecond) + callback(&sqltypes.Result{ + Rows: [][]sqltypes.Value{{ + sqltypes.MakeTrusted(querypb.Type_VARCHAR, []byte("delayed")), + }}, + }) default: if strings.HasPrefix(query, benchmarkQueryPrefix) { callback(&sqltypes.Result{ @@ -1368,3 +1383,52 @@ func TestParseConnAttrs(t *testing.T) { } } } + +func TestServerFlush(t *testing.T) { + defer func(saved time.Duration) { *mysqlServerFlushDelay = saved }(*mysqlServerFlushDelay) + *mysqlServerFlushDelay = 10 * time.Millisecond + + th := &testHandler{} + + l, err := NewListener("tcp", ":0", &AuthServerNone{}, th, 0, 0, false) + require.NoError(t, err) + defer l.Close() + go l.Accept() + + host, port := getHostPort(t, l.Addr()) + params := &ConnParams{ + Host: host, + Port: port, + } + + c, err := Connect(context.Background(), params) + require.NoError(t, err) + defer c.Close() + + start := time.Now() + err = c.ExecuteStreamFetch("50ms delay") + require.NoError(t, err) + + flds, err := c.Fields() + require.NoError(t, err) + if duration, want := time.Since(start), 20*time.Millisecond; duration < *mysqlServerFlushDelay || duration > want { + t.Errorf("duration: %v, want between %v and %v", duration, *mysqlServerFlushDelay, want) + } + want1 := []*querypb.Field{{ + Name: "result", + Type: querypb.Type_VARCHAR, + }} + assert.Equal(t, want1, flds) + + row, err := c.FetchNext() + require.NoError(t, err) + if duration, want := time.Since(start), 50*time.Millisecond; duration < want { + t.Errorf("duration: %v, want > %v", duration, want) + } + want2 := []sqltypes.Value{sqltypes.MakeTrusted(querypb.Type_VARCHAR, []byte("delayed"))} + assert.Equal(t, want2, row) + + row, err = c.FetchNext() + require.NoError(t, err) + assert.Nil(t, row) +} From dd1b5698c718acb7b7f9f6f2dec47f75c0100130 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 1 Mar 2020 21:50:48 -0800 Subject: [PATCH 193/825] stats: address review comments Signed-off-by: Sugu Sougoumarane --- go/stats/counters_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/go/stats/counters_test.go b/go/stats/counters_test.go index 2fd9bc0fea1..03c3b244345 100644 --- a/go/stats/counters_test.go +++ b/go/stats/counters_test.go @@ -264,7 +264,10 @@ func TestCountersCombineDimension(t *testing.T) { c3 := NewCountersWithSingleLabel("counter_combine_dim3", "help", "a") assert.Equal(t, `{"all": 0}`, c3.String()) + // Anything under "a" and "c" should get reported under a consolidated "all" value + // instead of the specific supplied values. c4 := NewCountersWithMultiLabels("counter_combine_dim4", "help", []string{"a", "b", "c"}) c4.Add([]string{"c1", "c2", "c3"}, 1) - assert.Equal(t, `{"all.c2.all": 1}`, c4.String()) + c4.Add([]string{"c4", "c2", "c5"}, 1) + assert.Equal(t, `{"all.c2.all": 2}`, c4.String()) } From 6bde9469d47adf2a79da0b3e1603c3e77f34a81f Mon Sep 17 00:00:00 2001 From: Kazushi Kitaya Date: Mon, 2 Mar 2020 16:47:31 +0900 Subject: [PATCH 194/825] fix flakiness of TestMessageManagerSend Signed-off-by: Kazushi Kitaya --- .../tabletserver/messager/message_manager_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index d895d7d719d..a1842a6fd09 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -219,6 +219,16 @@ func TestMessageManagerSend(t *testing.T) { t.Errorf("Postpone: %s, want %v", got, want) } + // Wait while the receiver is marked as busy. + for { + mm.mu.Lock() + busy := mm.receivers[0].busy + mm.mu.Unlock() + if !busy { + break + } + } + // Verify item has been removed from cache. // Need to obtain lock to prevent data race. mm.cache.mu.Lock() From 16bf5030281596e9d97e5cae3626ee6b22a111c0 Mon Sep 17 00:00:00 2001 From: prince Date: Mon, 2 Mar 2020 23:00:12 +0530 Subject: [PATCH 195/825] graceful shutdown using ctr+c (#5855) * created teardown handler on interupt signal. also added panic handler in each testcase to handle the unwanted panics. Signed-off-by: pradip parmar * handled nil cancelFun issue. Signed-off-by: pradip parmar * panic handler to each testcase. Signed-off-by: pradip parmar * panic handler at starting of each main test. Signed-off-by: pradip parmar * teardown fixes when process exits because of panic. Signed-off-by: pradip parmar * comment for mutex in cluster. Signed-off-by: pradip parmar --- .../transform/backup_transform_utils.go | 3 ++ .../backup/vtbackup/backup_only_test.go | 2 + go/test/endtoend/backup/vtbackup/main_test.go | 1 + .../backup/vtctlbackup/backup_utils.go | 2 + go/test/endtoend/binlog/binlog_test.go | 4 ++ go/test/endtoend/cellalias/cell_alias_test.go | 2 + go/test/endtoend/cluster/cluster_process.go | 38 ++++++++++++++++++- go/test/endtoend/cluster/cluster_util.go | 9 +++++ .../endtoend/clustertest/add_keyspace_test.go | 1 + go/test/endtoend/clustertest/etcd_test.go | 3 ++ go/test/endtoend/clustertest/main_test.go | 1 + go/test/endtoend/clustertest/vtcltd_test.go | 2 + go/test/endtoend/clustertest/vtgate_test.go | 2 + go/test/endtoend/clustertest/vttablet_test.go | 3 ++ .../encrypted_replication_test.go | 1 + .../encrypted_transport_test.go | 1 + go/test/endtoend/keyspace/keyspace_test.go | 11 +++++- go/test/endtoend/messaging/main_test.go | 1 + go/test/endtoend/messaging/messaging_test.go | 3 ++ go/test/endtoend/mysqlctl/mysqlctl_test.go | 3 ++ go/test/endtoend/mysqlctld/mysqlctld_test.go | 3 ++ go/test/endtoend/mysqlserver/main_test.go | 1 + .../endtoend/mysqlserver/mysql_server_test.go | 8 ++++ go/test/endtoend/preparestmt/main_test.go | 2 +- .../endtoend/preparestmt/stmt_methods_test.go | 7 +++- .../shardedrecovery/sharded_recovery_test.go | 2 + .../recovery/unshardedrecovery/recovery.go | 2 + go/test/endtoend/reparent/main_test.go | 1 + .../reparent/reparent_range_based_test.go | 1 + go/test/endtoend/reparent/reparent_test.go | 11 ++++++ .../endtoend/sharded/sharded_keyspace_test.go | 4 +- .../bytes/initial_sharding_bytes_test.go | 2 + .../multi/initial_sharding_multi_test.go | 2 + .../sharding/initialsharding/sharding_util.go | 3 +- .../v3/initial_sharding_test.go | 2 + .../mergesharding/mergesharding_base.go | 1 + .../sharding/resharding/resharding_base.go | 1 + .../verticalsplit/vertical_split_test.go | 3 +- .../endtoend/tabletmanager/commands_test.go | 5 +++ .../tabletmanager/custom_rule_topo_test.go | 1 + .../dbnameoverride/tablet_master_test.go | 2 + .../tabletmanager/lock_unlock_test.go | 4 ++ go/test/endtoend/tabletmanager/main_test.go | 1 + .../master/tablet_master_test.go | 3 ++ go/test/endtoend/tabletmanager/qps_test.go | 2 + .../tabletmanager/tablet_health_test.go | 5 +++ .../tablet_security_policy_test.go | 3 ++ go/test/endtoend/tabletmanager/tablet_test.go | 1 + .../vtctldweb/vtctld_web_main_test.go | 2 + go/test/endtoend/vtctldweb/vtctld_web_test.go | 7 ++++ go/test/endtoend/vtgate/aggr_test.go | 2 + go/test/endtoend/vtgate/buffer/buffer_test.go | 1 + go/test/endtoend/vtgate/lookup_test.go | 4 ++ go/test/endtoend/vtgate/main_test.go | 1 + go/test/endtoend/vtgate/schema/schema_test.go | 4 +- go/test/endtoend/vtgate/sequence/seq_test.go | 2 + .../rollback/txn_rollback_shutdown_test.go | 2 + .../vtgate/transaction/trxn_mode_test.go | 2 + .../endtoend/vtgate/vschema/vschema_test.go | 2 + go/test/endtoend/worker/worker_test.go | 3 ++ 60 files changed, 200 insertions(+), 8 deletions(-) diff --git a/go/test/endtoend/backup/transform/backup_transform_utils.go b/go/test/endtoend/backup/transform/backup_transform_utils.go index f1cd99e0f09..90d6f8827bc 100644 --- a/go/test/endtoend/backup/transform/backup_transform_utils.go +++ b/go/test/endtoend/backup/transform/backup_transform_utils.go @@ -60,6 +60,7 @@ var ( ) func TestMainSetup(m *testing.M, useMysqlctld bool) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode, err := func() (int, error) { @@ -183,6 +184,7 @@ var vtInsertTest = `create table vt_insert_test ( func TestBackupTransformImpl(t *testing.T) { // insert data in master, validate same in slave + defer cluster.PanicHandler(t) verifyInitialReplication(t) // restart the replica with transform hook parameter @@ -268,6 +270,7 @@ func TestBackupTransformImpl(t *testing.T) { // backup_storage_hook, which should fail. func TestBackupTransformErrorImpl(t *testing.T) { // restart the replica with transform hook parameter + defer cluster.PanicHandler(t) err := replica1.VttabletProcess.TearDown() require.Nil(t, err) diff --git a/go/test/endtoend/backup/vtbackup/backup_only_test.go b/go/test/endtoend/backup/vtbackup/backup_only_test.go index ee5239ac42e..1ac7a93a212 100644 --- a/go/test/endtoend/backup/vtbackup/backup_only_test.go +++ b/go/test/endtoend/backup/vtbackup/backup_only_test.go @@ -53,6 +53,7 @@ func TestTabletInitialBackup(t *testing.T) { // - Take a Second Backup // - Bring up a second replica, and restore from the second backup // - list the backups, remove them + defer cluster.PanicHandler(t) vtBackup(t, true) verifyBackupCount(t, shardKsName, 1) @@ -82,6 +83,7 @@ func TestTabletBackupOnly(t *testing.T) { // - Take a Second Backup // - Bring up a second replica, and restore from the second backup // - list the backups, remove them + defer cluster.PanicHandler(t) // Reset the tablet object values in order on init tablet in the next step. master.VttabletProcess.ServingStatus = "NOT_SERVING" diff --git a/go/test/endtoend/backup/vtbackup/main_test.go b/go/test/endtoend/backup/vtbackup/main_test.go index 42078af68ed..b7412abe27f 100644 --- a/go/test/endtoend/backup/vtbackup/main_test.go +++ b/go/test/endtoend/backup/vtbackup/main_test.go @@ -55,6 +55,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode, err := func() (int, error) { diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index b5503f9d1f7..aeb1d53365a 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -239,6 +239,8 @@ func TestBackup(t *testing.T, setupType int, streamMode string, stripes int) { }, // } + defer cluster.PanicHandler(t) + // setup cluster for the testing code, err := LaunchCluster(setupType, streamMode, stripes) require.Nilf(t, err, "setup failed with status code %d", code) diff --git a/go/test/endtoend/binlog/binlog_test.go b/go/test/endtoend/binlog/binlog_test.go index 0ed46feb0ee..5247b5c9633 100644 --- a/go/test/endtoend/binlog/binlog_test.go +++ b/go/test/endtoend/binlog/binlog_test.go @@ -96,6 +96,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -259,6 +260,7 @@ func TestMain(m *testing.M) { // pretend it's latin1. If the binlog player doesn't also pretend it's // latin1, it will be inserted as utf8, which will change its value. func TestCharset(t *testing.T) { + defer cluster.PanicHandler(t) position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) _, err := queryTablet(t, *srcMaster, fmt.Sprintf(insertSql, tableName, 1, "Šṛ́rỏé"), "latin1") @@ -272,6 +274,7 @@ func TestCharset(t *testing.T) { // Enable binlog_checksum, which will also force a log rotation that should // cause binlog streamer to notice the new checksum setting. func TestChecksumEnabled(t *testing.T) { + defer cluster.PanicHandler(t) position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) _, err := queryTablet(t, *destReplica, "SET @@global.binlog_checksum=1", "") require.Nil(t, err) @@ -290,6 +293,7 @@ func TestChecksumEnabled(t *testing.T) { // Disable binlog_checksum to make sure we can also talk to a server without // checksums enabled, in case they are enabled by default func TestChecksumDisabled(t *testing.T) { + defer cluster.PanicHandler(t) position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) _, err := queryTablet(t, *destReplica, "SET @@global.binlog_checksum=0", "") diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index a64df9e74df..b0ed2d9f358 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -93,6 +93,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -236,6 +237,7 @@ func TestMain(m *testing.M) { } func TestAlias(t *testing.T) { + defer cluster.PanicHandler(t) insertInitialValues(t) err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) require.Nil(t, err) diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index 131f6192ad3..ac6c113272d 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -17,14 +17,18 @@ limitations under the License. package cluster import ( + "context" "flag" "fmt" "io/ioutil" "math/rand" "os" "os/exec" + "os/signal" "path" "strconv" + "sync" + "syscall" "time" "vitess.io/vitess/go/vt/log" @@ -79,6 +83,13 @@ type LocalProcessCluster struct { VtctldExtraArgs []string EnableSemiSync bool + + // mutex added to handle the parallel teardowns + mx *sync.Mutex + teardownCompleted bool + + context.Context + context.CancelFunc } // Vttablet stores the properties needed to start a vttablet process @@ -137,6 +148,20 @@ func (shard *Shard) Replica() *Vttablet { return nil } +// CtrlCHandler handles the teardown for the ctrl-c. +func (cluster *LocalProcessCluster) CtrlCHandler() { + cluster.Context, cluster.CancelFunc = context.WithCancel(context.Background()) + + c := make(chan os.Signal, 2) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + select { + case <-c: + cluster.Teardown() + os.Exit(0) + case <-cluster.Done(): + } +} + // StartTopo starts topology server func (cluster *LocalProcessCluster) StartTopo() (err error) { if cluster.Cell == "" { @@ -401,7 +426,8 @@ func (cluster *LocalProcessCluster) GetVtgateInstance() *VtgateProcess { // NewCluster instantiates a new cluster func NewCluster(cell string, hostname string) *LocalProcessCluster { - cluster := &LocalProcessCluster{Cell: cell, Hostname: hostname} + cluster := &LocalProcessCluster{Cell: cell, Hostname: hostname, mx: new(sync.Mutex)} + go cluster.CtrlCHandler() cluster.OriginalVTDATAROOT = os.Getenv("VTDATAROOT") cluster.CurrentVTDATAROOT = path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("vtroot_%d", cluster.GetAndReservePort())) _ = createDirectory(cluster.CurrentVTDATAROOT, 0700) @@ -455,6 +481,15 @@ func (cluster *LocalProcessCluster) WaitForTabletsToHealthyInVtgate() (err error // Teardown brings down the cluster by invoking teardown for individual processes func (cluster *LocalProcessCluster) Teardown() { + PanicHandler(nil) + cluster.mx.Lock() + defer cluster.mx.Unlock() + if cluster.teardownCompleted { + return + } + if cluster.CancelFunc != nil { + cluster.CancelFunc() + } if err := cluster.VtgateProcess.TearDown(); err != nil { log.Errorf("Error in vtgate teardown - %s", err.Error()) } @@ -497,6 +532,7 @@ func (cluster *LocalProcessCluster) Teardown() { log.Errorf("Error in topo server teardown - %s", err.Error()) } + cluster.teardownCompleted = true } // StartVtworker starts a vtworker diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index df6f9ce9fa5..bce1cf73810 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -87,6 +87,15 @@ func VerifyRowsInTablet(t *testing.T, vttablet *Vttablet, ksName string, expecte assert.Fail(t, "expected rows not found.") } +// PanicHandler handles the panic in the testcase. +func PanicHandler(t *testing.T) { + err := recover() + if t == nil { + return + } + require.Nilf(t, err, "panic occured in testcase %v", t.Name()) +} + // VerifyLocalMetadata Verify Local Metadata of a tablet func VerifyLocalMetadata(t *testing.T, tablet *Vttablet, ksName string, shardName string, cell string) { qr, err := tablet.VttabletProcess.QueryTablet("select * from _vt.local_metadata", ksName, false) diff --git a/go/test/endtoend/clustertest/add_keyspace_test.go b/go/test/endtoend/clustertest/add_keyspace_test.go index aface0b1467..b16b5893941 100644 --- a/go/test/endtoend/clustertest/add_keyspace_test.go +++ b/go/test/endtoend/clustertest/add_keyspace_test.go @@ -57,6 +57,7 @@ primary key (id) ) func TestAddKeyspace(t *testing.T) { + defer cluster.PanicHandler(t) if err := clusterInstance.StartKeyspace(*testKeyspace, []string{"-80", "80-"}, 1, true); err != nil { println(err.Error()) t.Fatal(err) diff --git a/go/test/endtoend/clustertest/etcd_test.go b/go/test/endtoend/clustertest/etcd_test.go index cb0138b0d5e..1f5e548696f 100644 --- a/go/test/endtoend/clustertest/etcd_test.go +++ b/go/test/endtoend/clustertest/etcd_test.go @@ -19,9 +19,12 @@ package clustertest import ( "fmt" "testing" + + "vitess.io/vitess/go/test/endtoend/cluster" ) func TestEtcdServer(t *testing.T) { + defer cluster.PanicHandler(t) etcdURL := fmt.Sprintf("http://%s:%d/v2/keys", clusterInstance.Hostname, clusterInstance.TopoPort) testURL(t, etcdURL, "generic etcd url") testURL(t, etcdURL+"/vitess/global", "vitess global key") diff --git a/go/test/endtoend/clustertest/main_test.go b/go/test/endtoend/clustertest/main_test.go index 11c48433ac3..025ee77800f 100644 --- a/go/test/endtoend/clustertest/main_test.go +++ b/go/test/endtoend/clustertest/main_test.go @@ -60,6 +60,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/clustertest/vtcltd_test.go b/go/test/endtoend/clustertest/vtcltd_test.go index 3b35c383d6e..4b195de82d0 100644 --- a/go/test/endtoend/clustertest/vtcltd_test.go +++ b/go/test/endtoend/clustertest/vtcltd_test.go @@ -29,6 +29,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/endtoend/cluster" ) var ( @@ -41,6 +42,7 @@ var ( ) func TestVtctldProcess(t *testing.T) { + defer cluster.PanicHandler(t) url := fmt.Sprintf("http://%s:%d/api/keyspaces/", clusterInstance.Hostname, clusterInstance.VtctldHTTPPort) testURL(t, url, "keyspace url") diff --git a/go/test/endtoend/clustertest/vtgate_test.go b/go/test/endtoend/clustertest/vtgate_test.go index f83c4b0e468..a688125ead4 100644 --- a/go/test/endtoend/clustertest/vtgate_test.go +++ b/go/test/endtoend/clustertest/vtgate_test.go @@ -31,9 +31,11 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" ) func TestVtgateProcess(t *testing.T) { + defer cluster.PanicHandler(t) verifyVtgateVariables(t, clusterInstance.VtgateProcess.VerifyURL) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) diff --git a/go/test/endtoend/clustertest/vttablet_test.go b/go/test/endtoend/clustertest/vttablet_test.go index 30e7beeca15..f886951f101 100644 --- a/go/test/endtoend/clustertest/vttablet_test.go +++ b/go/test/endtoend/clustertest/vttablet_test.go @@ -23,9 +23,12 @@ import ( "io/ioutil" "net/http" "testing" + + "vitess.io/vitess/go/test/endtoend/cluster" ) func TestVttabletProcess(t *testing.T) { + defer cluster.PanicHandler(t) firstTabletPort := clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].HTTPPort testURL(t, fmt.Sprintf("http://localhost:%d/debug/vars/", firstTabletPort), "tablet debug var url") resp, _ := http.Get(fmt.Sprintf("http://localhost:%d/debug/vars", firstTabletPort)) diff --git a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go index 0f67a08ef69..5affd253a3c 100644 --- a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go +++ b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go @@ -40,6 +40,7 @@ var ( // This test makes sure that we can use SSL replication with Vitess func TestSecure(t *testing.T) { + defer cluster.PanicHandler(t) testReplicationBase(t, true) testReplicationBase(t, false) } diff --git a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go index d1ca3336cb6..3bca70c58eb 100644 --- a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go +++ b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go @@ -98,6 +98,7 @@ var ( ) func TestSecureTransport(t *testing.T) { + defer cluster.PanicHandler(t) flag.Parse() // initialize cluster diff --git a/go/test/endtoend/keyspace/keyspace_test.go b/go/test/endtoend/keyspace/keyspace_test.go index 413bb0e87f1..0bcff99b6cb 100644 --- a/go/test/endtoend/keyspace/keyspace_test.go +++ b/go/test/endtoend/keyspace/keyspace_test.go @@ -79,10 +79,11 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { - clusterForKSTest = &cluster.LocalProcessCluster{Cell: cell, Hostname: hostname} + clusterForKSTest = cluster.NewCluster(cell, hostname) defer clusterForKSTest.Teardown() // Start topo server @@ -137,6 +138,7 @@ func TestMain(m *testing.M) { } func TestGetSrvKeyspaceNames(t *testing.T) { + defer cluster.PanicHandler(t) output, err := clusterForKSTest.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvKeyspaceNames", cell) require.Nil(t, err) assert.Contains(t, strings.Split(output, "\n"), keyspaceUnshardedName) @@ -144,6 +146,7 @@ func TestGetSrvKeyspaceNames(t *testing.T) { } func TestGetSrvKeyspacePartitions(t *testing.T) { + defer cluster.PanicHandler(t) shardedSrvKeyspace := getSrvKeyspace(t, cell, keyspaceShardedName) otherShardRefFound := false for _, partition := range shardedSrvKeyspace.Partitions { @@ -172,6 +175,7 @@ func TestGetSrvKeyspacePartitions(t *testing.T) { } func TestShardNames(t *testing.T) { + defer cluster.PanicHandler(t) output, err := clusterForKSTest.VtctlclientProcess.ExecuteCommandWithOutput("GetSrvKeyspace", cell, keyspaceShardedName) require.Nil(t, err) var srvKeyspace topodata.SrvKeyspace @@ -181,6 +185,7 @@ func TestShardNames(t *testing.T) { } func TestGetKeyspace(t *testing.T) { + defer cluster.PanicHandler(t) output, err := clusterForKSTest.VtctlclientProcess.ExecuteCommandWithOutput("GetKeyspace", keyspaceUnshardedName) require.Nil(t, err) @@ -194,6 +199,7 @@ func TestGetKeyspace(t *testing.T) { } func TestDeleteKeyspace(t *testing.T) { + defer cluster.PanicHandler(t) _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("CreateKeyspace", "test_delete_keyspace") _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("CreateShard", "test_delete_keyspace/0") _ = clusterForKSTest.VtctlclientProcess.ExecuteCommand("InitTablet", "-keyspace=test_delete_keyspace", "-shard=0", "zone1-0000000100", "master") @@ -311,6 +317,7 @@ func RemoveKeyspaceCell(t *testing.T) { } func TestShardCountForAllKeyspaces(t *testing.T) { + defer cluster.PanicHandler(t) testShardCountForKeyspace(t, keyspaceUnshardedName, 1) testShardCountForKeyspace(t, keyspaceShardedName, 2) } @@ -327,6 +334,7 @@ func testShardCountForKeyspace(t *testing.T, keyspace string, count int) { } func TestShardNameForAllKeyspaces(t *testing.T) { + defer cluster.PanicHandler(t) testShardNameForKeyspace(t, keyspaceUnshardedName, []string{"test_ks_unsharded"}) testShardNameForKeyspace(t, keyspaceShardedName, []string{"-80", "80-"}) } @@ -345,6 +353,7 @@ func testShardNameForKeyspace(t *testing.T, keyspace string, shardNames []string } func TestKeyspaceToShardName(t *testing.T) { + defer cluster.PanicHandler(t) var id []byte srvKeyspace := getSrvKeyspace(t, cell, keyspaceShardedName) diff --git a/go/test/endtoend/messaging/main_test.go b/go/test/endtoend/messaging/main_test.go index f9ae7adfd5f..b6660fe4f7a 100644 --- a/go/test/endtoend/messaging/main_test.go +++ b/go/test/endtoend/messaging/main_test.go @@ -90,6 +90,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { diff --git a/go/test/endtoend/messaging/messaging_test.go b/go/test/endtoend/messaging/messaging_test.go index e8cc5973910..5281490c1d8 100644 --- a/go/test/endtoend/messaging/messaging_test.go +++ b/go/test/endtoend/messaging/messaging_test.go @@ -47,6 +47,7 @@ func TestUnsharded(t *testing.T) { // TestRepareting checks the client connection count after reparenting. func TestRepareting(t *testing.T) { + defer cluster.PanicHandler(t) name := "sharded_message" ctx := context.Background() @@ -105,6 +106,7 @@ func TestRepareting(t *testing.T) { // TestConnection validate the connection count and message streaming. func TestConnection(t *testing.T) { + defer cluster.PanicHandler(t) name := "sharded_message" @@ -159,6 +161,7 @@ func TestConnection(t *testing.T) { } func testMessaging(t *testing.T, name, ks string) { + defer cluster.PanicHandler(t) ctx := context.Background() stream, err := VtgateGrpcConn(ctx, clusterInstance) require.Nil(t, err) diff --git a/go/test/endtoend/mysqlctl/mysqlctl_test.go b/go/test/endtoend/mysqlctl/mysqlctl_test.go index 08ef2011bae..986e4ef1e24 100644 --- a/go/test/endtoend/mysqlctl/mysqlctl_test.go +++ b/go/test/endtoend/mysqlctl/mysqlctl_test.go @@ -40,6 +40,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -140,6 +141,7 @@ func initCluster(shardNames []string, totalTabletsRequired int) { } func TestRestart(t *testing.T) { + defer cluster.PanicHandler(t) err := masterTablet.MysqlctlProcess.Stop() require.Nil(t, err) masterTablet.MysqlctlProcess.CleanupFiles(masterTablet.TabletUID) @@ -148,6 +150,7 @@ func TestRestart(t *testing.T) { } func TestAutoDetect(t *testing.T) { + defer cluster.PanicHandler(t) // Start up tablets with an empty MYSQL_FLAVOR, which means auto-detect sqlFlavor := os.Getenv("MYSQL_FLAVOR") diff --git a/go/test/endtoend/mysqlctld/mysqlctld_test.go b/go/test/endtoend/mysqlctld/mysqlctld_test.go index 989d20c6e15..3967190c9ca 100644 --- a/go/test/endtoend/mysqlctld/mysqlctld_test.go +++ b/go/test/endtoend/mysqlctld/mysqlctld_test.go @@ -39,6 +39,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -136,6 +137,7 @@ func initCluster(shardNames []string, totalTabletsRequired int) error { } func TestRestart(t *testing.T) { + defer cluster.PanicHandler(t) err := masterTablet.MysqlctldProcess.Stop() require.Nil(t, err) masterTablet.MysqlctldProcess.CleanupFiles(masterTablet.TabletUID) @@ -144,6 +146,7 @@ func TestRestart(t *testing.T) { } func TestAutoDetect(t *testing.T) { + defer cluster.PanicHandler(t) // Start up tablets with an empty MYSQL_FLAVOR, which means auto-detect sqlFlavor := os.Getenv("MYSQL_FLAVOR") diff --git a/go/test/endtoend/mysqlserver/main_test.go b/go/test/endtoend/mysqlserver/main_test.go index 61929101ebc..e26f1962630 100644 --- a/go/test/endtoend/mysqlserver/main_test.go +++ b/go/test/endtoend/mysqlserver/main_test.go @@ -44,6 +44,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() // setting grpc max size diff --git a/go/test/endtoend/mysqlserver/mysql_server_test.go b/go/test/endtoend/mysqlserver/mysql_server_test.go index 1800ede0ec0..656e0b509f5 100644 --- a/go/test/endtoend/mysqlserver/mysql_server_test.go +++ b/go/test/endtoend/mysqlserver/mysql_server_test.go @@ -29,6 +29,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" "database/sql" @@ -37,6 +38,7 @@ import ( // TestMultiStmt checks that multiStatements=True and multiStatements=False work properly. func TestMultiStatement(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() // connect database with multiStatements=True @@ -64,6 +66,7 @@ func TestMultiStatement(t *testing.T) { // TestLargeComment add large comment in insert stmt and validate the insert process. func TestLargeComment(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -82,6 +85,7 @@ func TestLargeComment(t *testing.T) { // TestInsertLargerThenGrpcLimit insert blob larger then grpc limit and verify the error. func TestInsertLargerThenGrpcLimit(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() @@ -101,6 +105,7 @@ func TestInsertLargerThenGrpcLimit(t *testing.T) { // TestTimeout executes sleep(5) with query_timeout of 1 second, and verifies the error. func TestTimeout(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -116,6 +121,7 @@ func TestTimeout(t *testing.T) { // TestInvalidField tries to fetch invalid column and verifies the error. func TestInvalidField(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -131,6 +137,7 @@ func TestInvalidField(t *testing.T) { // TestWarnings validates the behaviour of SHOW WARNINGS. func TestWarnings(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) @@ -173,6 +180,7 @@ func TestWarnings(t *testing.T) { // TestSelectWithUnauthorizedUser verifies that an unauthorized user // is not able to read from the table. func TestSelectWithUnauthorizedUser(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() tmpVtParam := vtParams diff --git a/go/test/endtoend/preparestmt/main_test.go b/go/test/endtoend/preparestmt/main_test.go index 6ad68606930..082871658a9 100644 --- a/go/test/endtoend/preparestmt/main_test.go +++ b/go/test/endtoend/preparestmt/main_test.go @@ -138,11 +138,11 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { clusterInstance = cluster.NewCluster(cell, hostname) - defer clusterInstance.Teardown() // Start topo server diff --git a/go/test/endtoend/preparestmt/stmt_methods_test.go b/go/test/endtoend/preparestmt/stmt_methods_test.go index 05e24994cdd..20c9c97dba7 100644 --- a/go/test/endtoend/preparestmt/stmt_methods_test.go +++ b/go/test/endtoend/preparestmt/stmt_methods_test.go @@ -25,10 +25,12 @@ import ( "github.com/icrowley/fake" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/test/endtoend/cluster" ) // TestSelect simple select the data without any condition. func TestSelect(t *testing.T) { + defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() selectWhere(t, dbo, "") @@ -37,7 +39,7 @@ func TestSelect(t *testing.T) { // TestInsertUpdateDelete validates all insert, update and // delete method on prepared statements. func TestInsertUpdateDelete(t *testing.T) { - + defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() // prepare insert statement @@ -85,6 +87,7 @@ func TestInsertUpdateDelete(t *testing.T) { // testcount validates inserted rows count with expected count. func testcount(t *testing.T, dbo *sql.DB, except int) { + defer cluster.PanicHandler(t) r, err := dbo.Query("SELECT count(1) FROM " + tableName) require.Nil(t, err) @@ -98,6 +101,7 @@ func testcount(t *testing.T, dbo *sql.DB, except int) { // TestAutoIncColumns test insertion of row without passing // the value of auto increment columns (here it is id). func TestAutoIncColumns(t *testing.T) { + defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() // insert a row without id @@ -163,6 +167,7 @@ func reconnectAndTest(t *testing.T) { // TestWrongTableName query database using invalid // tablename and validate error. func TestWrongTableName(t *testing.T) { + defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() execWithError(t, dbo, []uint16{1105}, "select * from teseting_table;") diff --git a/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go b/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go index 80f8e27e1a7..9d9fc00756c 100644 --- a/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go +++ b/go/test/endtoend/recovery/shardedrecovery/sharded_recovery_test.go @@ -104,6 +104,7 @@ var ( // - check that vtgate queries work correctly func TestUnShardedRecoveryAfterSharding(t *testing.T) { + defer cluster.PanicHandler(t) _, err := initializeCluster(t) defer localCluster.Teardown() require.Nil(t, err) @@ -265,6 +266,7 @@ func TestUnShardedRecoveryAfterSharding(t *testing.T) { func TestShardedRecovery(t *testing.T) { + defer cluster.PanicHandler(t) _, err := initializeCluster(t) defer localCluster.Teardown() require.Nil(t, err) diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery.go b/go/test/endtoend/recovery/unshardedrecovery/recovery.go index e17dd76f82a..567c24e4fec 100644 --- a/go/test/endtoend/recovery/unshardedrecovery/recovery.go +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery.go @@ -75,6 +75,7 @@ var ( // TestMainImpl creates cluster for unsharded recovery testing. func TestMainImpl(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode, err := func() (int, error) { @@ -190,6 +191,7 @@ func TestMainImpl(m *testing.M) { // - check that new tablet has data created after backup1 but not data created after backup2 // - check that vtgate queries work correctly func TestRecoveryImpl(t *testing.T) { + defer cluster.PanicHandler(t) defer tabletsTeardown() verifyInitialReplication(t) diff --git a/go/test/endtoend/reparent/main_test.go b/go/test/endtoend/reparent/main_test.go index 37b2963c836..4e519b03511 100644 --- a/go/test/endtoend/reparent/main_test.go +++ b/go/test/endtoend/reparent/main_test.go @@ -65,6 +65,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/reparent/reparent_range_based_test.go b/go/test/endtoend/reparent/reparent_range_based_test.go index 2bca21bf5cf..4e7dfafbe75 100644 --- a/go/test/endtoend/reparent/reparent_range_based_test.go +++ b/go/test/endtoend/reparent/reparent_range_based_test.go @@ -28,6 +28,7 @@ import ( ) func TestReparentGracefulRangeBased(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() for _, tablet := range []cluster.Vttablet{*masterTablet, *replicaTablet} { diff --git a/go/test/endtoend/reparent/reparent_test.go b/go/test/endtoend/reparent/reparent_test.go index 1ad191f2301..2a5f8bacaf4 100644 --- a/go/test/endtoend/reparent/reparent_test.go +++ b/go/test/endtoend/reparent/reparent_test.go @@ -36,6 +36,7 @@ import ( ) func TestMasterToSpareStateChangeImpossible(t *testing.T) { + defer cluster.PanicHandler(t) args := []string{"InitTablet", "-hostname", hostname, "-port", fmt.Sprintf("%d", tablet62344.HTTPPort), "-allow_update", "-parent", @@ -65,6 +66,7 @@ func TestMasterToSpareStateChangeImpossible(t *testing.T) { } func TestReparentDownMaster(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { @@ -154,6 +156,7 @@ func TestReparentDownMaster(t *testing.T) { func TestReparentCrossCell(t *testing.T) { + defer cluster.PanicHandler(t) for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { // create database err := tablet.VttabletProcess.CreateDB(keyspaceName) @@ -207,6 +210,7 @@ func TestReparentGracefulRecovery(t *testing.T) { } func reparentGraceful(t *testing.T, confusedMaster bool) { + defer cluster.PanicHandler(t) ctx := context.Background() for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { @@ -301,6 +305,7 @@ func reparentGraceful(t *testing.T, confusedMaster bool) { } func TestReparentSlaveOffline(t *testing.T) { + defer cluster.PanicHandler(t) for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { // create database @@ -348,6 +353,7 @@ func TestReparentSlaveOffline(t *testing.T) { } func TestReparentAvoid(t *testing.T) { + defer cluster.PanicHandler(t) // Remove tablet41983 from topology as that tablet is not required for this test err := clusterInstance.VtctlclientProcess.ExecuteCommand("DeleteTablet", tablet41983.Alias) require.Nil(t, err) @@ -427,6 +433,7 @@ func TestReparentFromOutside(t *testing.T) { } func TestReparentFromOutsideWithNoMaster(t *testing.T) { + defer cluster.PanicHandler(t) reparentFromOutside(t, true) // We will have to restart mysql to avoid hanging/locks due to external Reparent @@ -448,6 +455,7 @@ func reparentFromOutside(t *testing.T, downMaster bool) { //- one replica will be busted and dead in the water and we'll call TabletExternallyReparented. //Args: //downMaster: kills the old master first + defer cluster.PanicHandler(t) ctx := context.Background() @@ -540,6 +548,7 @@ func reparentFromOutside(t *testing.T, downMaster bool) { } func TestReparentWithDownSlave(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { @@ -611,6 +620,7 @@ func TestReparentWithDownSlave(t *testing.T) { } func TestChangeTypeSemiSync(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() // Create new names for tablets, so this test is less confusing. @@ -699,6 +709,7 @@ func TestChangeTypeSemiSync(t *testing.T) { } func TestReparentDoesntHangIfMasterFails(t *testing.T) { + defer cluster.PanicHandler(t) for _, tablet := range []cluster.Vttablet{*tablet62344, *tablet62044, *tablet41983, *tablet31981} { // Create Database err := tablet.VttabletProcess.CreateDB(keyspaceName) diff --git a/go/test/endtoend/sharded/sharded_keyspace_test.go b/go/test/endtoend/sharded/sharded_keyspace_test.go index ef7bc979787..7b89dc2bf47 100644 --- a/go/test/endtoend/sharded/sharded_keyspace_test.go +++ b/go/test/endtoend/sharded/sharded_keyspace_test.go @@ -73,10 +73,11 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { - clusterInstance = &cluster.LocalProcessCluster{Cell: cell, Hostname: hostname} + clusterInstance = cluster.NewCluster(cell, hostname) defer clusterInstance.Teardown() // Start topo server @@ -101,6 +102,7 @@ func TestMain(m *testing.M) { } func TestShardedKeyspace(t *testing.T) { + defer cluster.PanicHandler(t) shard1 := clusterInstance.Keyspaces[0].Shards[0] shard2 := clusterInstance.Keyspaces[0].Shards[1] diff --git a/go/test/endtoend/sharding/initialsharding/bytes/initial_sharding_bytes_test.go b/go/test/endtoend/sharding/initialsharding/bytes/initial_sharding_bytes_test.go index 6202fb3f2f8..72794e88810 100644 --- a/go/test/endtoend/sharding/initialsharding/bytes/initial_sharding_bytes_test.go +++ b/go/test/endtoend/sharding/initialsharding/bytes/initial_sharding_bytes_test.go @@ -22,11 +22,13 @@ package bytes import ( "testing" + "vitess.io/vitess/go/test/endtoend/cluster" sharding "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" querypb "vitess.io/vitess/go/vt/proto/query" ) func TestInitialShardingBytes(t *testing.T) { + defer cluster.PanicHandler(t) code, err := sharding.ClusterWrapper(false) if err != nil { t.Errorf("setup failed with status code %d", code) diff --git a/go/test/endtoend/sharding/initialsharding/multi/initial_sharding_multi_test.go b/go/test/endtoend/sharding/initialsharding/multi/initial_sharding_multi_test.go index ccc5052fc5c..cab8b3fa36d 100644 --- a/go/test/endtoend/sharding/initialsharding/multi/initial_sharding_multi_test.go +++ b/go/test/endtoend/sharding/initialsharding/multi/initial_sharding_multi_test.go @@ -39,11 +39,13 @@ package multi import ( "testing" + "vitess.io/vitess/go/test/endtoend/cluster" sharding "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" querypb "vitess.io/vitess/go/vt/proto/query" ) func TestInitialShardingMulti(t *testing.T) { + defer cluster.PanicHandler(t) code, err := sharding.ClusterWrapper(true) if err != nil { t.Errorf("setup failed with status code %d", code) diff --git a/go/test/endtoend/sharding/initialsharding/sharding_util.go b/go/test/endtoend/sharding/initialsharding/sharding_util.go index 666e69585d6..542c700a8e1 100644 --- a/go/test/endtoend/sharding/initialsharding/sharding_util.go +++ b/go/test/endtoend/sharding/initialsharding/sharding_util.go @@ -94,7 +94,7 @@ var ( // ClusterWrapper common wrapper code for cluster func ClusterWrapper(isMulti bool) (int, error) { ClusterInstance = nil - ClusterInstance = &cluster.LocalProcessCluster{Cell: cell, Hostname: hostname} + ClusterInstance = cluster.NewCluster(cell, hostname) // Start topo server if err := ClusterInstance.StartTopo(); err != nil { @@ -223,6 +223,7 @@ func AssignMysqlPortFromKs1ToKs2() { // TestInitialSharding - main test which accepts different params for various test func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType querypb.Type, isMulti bool, isExternal bool) { + defer cluster.PanicHandler(t) if isExternal { commonTabletArg = append(commonTabletArg, "-db_host", "127.0.0.1") commonTabletArg = append(commonTabletArg, "-disable_active_reparents") diff --git a/go/test/endtoend/sharding/initialsharding/v3/initial_sharding_test.go b/go/test/endtoend/sharding/initialsharding/v3/initial_sharding_test.go index d971c874907..95e5e5c8975 100644 --- a/go/test/endtoend/sharding/initialsharding/v3/initial_sharding_test.go +++ b/go/test/endtoend/sharding/initialsharding/v3/initial_sharding_test.go @@ -31,11 +31,13 @@ package v3 import ( "testing" + "vitess.io/vitess/go/test/endtoend/cluster" sharding "vitess.io/vitess/go/test/endtoend/sharding/initialsharding" querypb "vitess.io/vitess/go/vt/proto/query" ) func TestInitialSharding(t *testing.T) { + defer cluster.PanicHandler(t) code, err := sharding.ClusterWrapper(false) if err != nil { t.Errorf("setup failed with status code %d", code) diff --git a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go index 15aa9fb14bf..d196123e39e 100644 --- a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go +++ b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go @@ -103,6 +103,7 @@ var ( // Note this test is just testing the full workflow, not corner cases or error // cases. These are mostly done by the other resharding tests. func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { + defer cluster.PanicHandler(t) clusterInstance = cluster.NewCluster(cell, hostname) defer clusterInstance.Teardown() diff --git a/go/test/endtoend/sharding/resharding/resharding_base.go b/go/test/endtoend/sharding/resharding/resharding_base.go index b58eda4bfc3..eebf38888a8 100644 --- a/go/test/endtoend/sharding/resharding/resharding_base.go +++ b/go/test/endtoend/sharding/resharding/resharding_base.go @@ -183,6 +183,7 @@ var ( // TestResharding - main test with accepts different params for various test func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { + defer cluster.PanicHandler(t) clusterInstance = cluster.NewCluster(cell1, hostname) defer clusterInstance.Teardown() diff --git a/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go b/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go index 43fb10a0ea9..594df68c590 100644 --- a/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go +++ b/go/test/endtoend/sharding/verticalsplit/vertical_split_test.go @@ -71,6 +71,7 @@ var ( ) func TestVerticalSplit(t *testing.T) { + defer cluster.PanicHandler(t) flag.Parse() code, err := initializeCluster() if err != nil { @@ -641,7 +642,7 @@ func checkSrvKeyspaceServedFrom(t *testing.T, cell string, ksname string, expect func initializeCluster() (int, error) { var mysqlProcesses []*exec.Cmd - clusterInstance = &cluster.LocalProcessCluster{Cell: cellj, Hostname: hostname} + clusterInstance = cluster.NewCluster(cellj, hostname) // Start topo server if err := clusterInstance.StartTopo(); err != nil { diff --git a/go/test/endtoend/tabletmanager/commands_test.go b/go/test/endtoend/tabletmanager/commands_test.go index 43c3342cca8..f8111896c1a 100644 --- a/go/test/endtoend/tabletmanager/commands_test.go +++ b/go/test/endtoend/tabletmanager/commands_test.go @@ -28,10 +28,12 @@ import ( "github.com/stretchr/testify/assert" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" ) // TabletCommands tests the basic tablet commands func TestTabletCommands(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) @@ -144,6 +146,7 @@ func assertExecuteFetch(t *testing.T, qr string) { // ActionAndTimeout test func TestActionAndTimeout(t *testing.T) { + defer cluster.PanicHandler(t) err := clusterInstance.VtctlclientProcess.ExecuteCommand("Sleep", masterTablet.Alias, "5s") require.Nil(t, err) time.Sleep(1 * time.Second) @@ -155,6 +158,7 @@ func TestActionAndTimeout(t *testing.T) { func TestHook(t *testing.T) { // test a regular program works + defer cluster.PanicHandler(t) runHookAndAssert(t, []string{ "ExecuteHook", masterTablet.Alias, "test.sh", "--flag1", "--param1=hello"}, "0", false, "") @@ -200,6 +204,7 @@ func runHookAndAssert(t *testing.T, params []string, expectedStatus string, expe func TestShardReplicationFix(t *testing.T) { // make sure the replica is in the replication graph, 2 nodes: 1 master, 1 replica + defer cluster.PanicHandler(t) result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetShardReplication", cell, keyspaceShard) require.Nil(t, err, "error should be Nil") assertNodeCount(t, result, int(3)) diff --git a/go/test/endtoend/tabletmanager/custom_rule_topo_test.go b/go/test/endtoend/tabletmanager/custom_rule_topo_test.go index 51a66fea62a..31a697249d0 100644 --- a/go/test/endtoend/tabletmanager/custom_rule_topo_test.go +++ b/go/test/endtoend/tabletmanager/custom_rule_topo_test.go @@ -31,6 +31,7 @@ import ( func TestTopoCustomRule(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) require.Nil(t, err) diff --git a/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go index 23a5f5edddd..88fcbc76743 100644 --- a/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go +++ b/go/test/endtoend/tabletmanager/dbnameoverride/tablet_master_test.go @@ -65,6 +65,7 @@ var ( const dbName = "myDbName" func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -107,6 +108,7 @@ func TestMain(m *testing.M) { } func TestDbNameOverride(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) diff --git a/go/test/endtoend/tabletmanager/lock_unlock_test.go b/go/test/endtoend/tabletmanager/lock_unlock_test.go index 9f6f5dbfb3e..ac413dc6f9c 100644 --- a/go/test/endtoend/tabletmanager/lock_unlock_test.go +++ b/go/test/endtoend/tabletmanager/lock_unlock_test.go @@ -27,10 +27,12 @@ import ( "github.com/stretchr/testify/assert" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" ) // TestLockAndUnlock tests the lock ability by locking a replica and asserting it does not see changes func TestLockAndUnlock(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) @@ -71,6 +73,7 @@ func TestLockAndUnlock(t *testing.T) { // TestStartSlaveUntilAfter tests by writing three rows, noting the gtid after each, and then replaying them one by one func TestStartSlaveUntilAfter(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) @@ -124,6 +127,7 @@ func TestStartSlaveUntilAfter(t *testing.T) { // TestLockAndTimeout tests that the lock times out and updates can be seen after timeout func TestLockAndTimeout(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) diff --git a/go/test/endtoend/tabletmanager/main_test.go b/go/test/endtoend/tabletmanager/main_test.go index e312182bb02..4cf93e30085 100644 --- a/go/test/endtoend/tabletmanager/main_test.go +++ b/go/test/endtoend/tabletmanager/main_test.go @@ -79,6 +79,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/tabletmanager/master/tablet_master_test.go b/go/test/endtoend/tabletmanager/master/tablet_master_test.go index 7727daccb54..d92c941172c 100644 --- a/go/test/endtoend/tabletmanager/master/tablet_master_test.go +++ b/go/test/endtoend/tabletmanager/master/tablet_master_test.go @@ -71,6 +71,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -119,6 +120,7 @@ func TestMain(m *testing.M) { } func TestRepeatedInitShardMaster(t *testing.T) { + defer cluster.PanicHandler(t) // Test that using InitShardMaster can go back and forth between 2 hosts. // Make replica tablet as master @@ -157,6 +159,7 @@ func TestRepeatedInitShardMaster(t *testing.T) { } func TestMasterRestartSetsTERTimestamp(t *testing.T) { + defer cluster.PanicHandler(t) // Test that TER timestamp is set when we restart the MASTER vttablet. // TER = TabletExternallyReparented. // See StreamHealthResponse.tablet_externally_reparented_timestamp for details. diff --git a/go/test/endtoend/tabletmanager/qps_test.go b/go/test/endtoend/tabletmanager/qps_test.go index b55b3bb227e..41f218f79ed 100644 --- a/go/test/endtoend/tabletmanager/qps_test.go +++ b/go/test/endtoend/tabletmanager/qps_test.go @@ -25,10 +25,12 @@ import ( "github.com/stretchr/testify/assert" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" querypb "vitess.io/vitess/go/vt/proto/query" ) func TestQPS(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ diff --git a/go/test/endtoend/tabletmanager/tablet_health_test.go b/go/test/endtoend/tabletmanager/tablet_health_test.go index e3ae0b27a55..79fa182acd5 100644 --- a/go/test/endtoend/tabletmanager/tablet_health_test.go +++ b/go/test/endtoend/tabletmanager/tablet_health_test.go @@ -37,6 +37,7 @@ import ( // TabletReshuffle test if a vttablet can be pointed at an existing mysql func TestTabletReshuffle(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() masterConn, err := mysql.Connect(ctx, &masterTabletParams) @@ -94,6 +95,7 @@ func TestTabletReshuffle(t *testing.T) { func TestHealthCheck(t *testing.T) { // Add one replica that starts not initialized // (for the replica, we let vttablet do the InitTablet) + defer cluster.PanicHandler(t) ctx := context.Background() rTablet := clusterInstance.GetVttabletInstance("replica", 0, "") @@ -207,6 +209,7 @@ func TestHealthCheckDrainedStateDoesNotShutdownQueryService(t *testing.T) { // - the query service won't be shutdown //Wait if tablet is not in service state + defer cluster.PanicHandler(t) err := rdonlyTablet.VttabletProcess.WaitForTabletType("SERVING") require.Nil(t, err) @@ -253,6 +256,7 @@ func TestIgnoreHealthError(t *testing.T) { // We will then ignore this error and verify if the status report the tablet as Healthy. // Create a new shard + defer cluster.PanicHandler(t) newShard := &cluster.Shard{ Name: "1", } @@ -320,6 +324,7 @@ func TestNoMysqlHealthCheck(t *testing.T) { // This test starts a vttablet with no mysql port, while mysql is down. // It makes sure vttablet will start properly and be unhealthy. // Then we start mysql, and make sure vttablet becomes healthy. + defer cluster.PanicHandler(t) ctx := context.Background() rTablet := clusterInstance.GetVttabletInstance("replica", 0, "") diff --git a/go/test/endtoend/tabletmanager/tablet_security_policy_test.go b/go/test/endtoend/tabletmanager/tablet_security_policy_test.go index 00b8df3b229..9bf78382040 100644 --- a/go/test/endtoend/tabletmanager/tablet_security_policy_test.go +++ b/go/test/endtoend/tabletmanager/tablet_security_policy_test.go @@ -28,6 +28,7 @@ import ( ) func TestFallbackSecurityPolicy(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() mTablet := clusterInstance.GetVttabletInstance("replica", 0, "") @@ -86,6 +87,7 @@ func assertAllowedURLTest(t *testing.T, url string) { } func TestDenyAllSecurityPolicy(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() mTablet := clusterInstance.GetVttabletInstance("replica", 0, "") @@ -121,6 +123,7 @@ func TestDenyAllSecurityPolicy(t *testing.T) { } func TestReadOnlySecurityPolicy(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() mTablet := clusterInstance.GetVttabletInstance("replica", 0, "") diff --git a/go/test/endtoend/tabletmanager/tablet_test.go b/go/test/endtoend/tabletmanager/tablet_test.go index 708cb5d711b..50ab6a75730 100644 --- a/go/test/endtoend/tabletmanager/tablet_test.go +++ b/go/test/endtoend/tabletmanager/tablet_test.go @@ -28,6 +28,7 @@ import ( // TestLocalMetadata tests the contents of local_metadata table after vttablet startup func TestLocalMetadata(t *testing.T) { + defer cluster.PanicHandler(t) // by default tablets are started with -restore_from_backup // so metadata should exist cluster.VerifyLocalMetadata(t, &replicaTablet, keyspaceName, shardName, cell) diff --git a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go index 755730087be..4e70e315824 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_main_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_main_test.go @@ -31,6 +31,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tebeka/selenium" "github.com/tebeka/selenium/chrome" + "vitess.io/vitess/go/test/endtoend/cluster" vttestpb "vitess.io/vitess/go/vt/proto/vttest" "vitess.io/vitess/go/vt/vttest" ) @@ -52,6 +53,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { diff --git a/go/test/endtoend/vtctldweb/vtctld_web_test.go b/go/test/endtoend/vtctldweb/vtctld_web_test.go index 70762344be8..29f8f7229ff 100644 --- a/go/test/endtoend/vtctldweb/vtctld_web_test.go +++ b/go/test/endtoend/vtctldweb/vtctld_web_test.go @@ -23,10 +23,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tebeka/selenium" + "vitess.io/vitess/go/test/endtoend/cluster" ) // TestRealtimeStats checks the status by changing dropdown values. func TestRealtimeStats(t *testing.T) { + defer cluster.PanicHandler(t) err := wd.Get(vtctldAddr + "/app2") require.Nil(t, err) @@ -63,6 +65,7 @@ func TestRealtimeStats(t *testing.T) { // TestShardView validates tablet type and uids. func TestShardView(t *testing.T) { + defer cluster.PanicHandler(t) navigateToShardView(t) tabletTypes, tabletUIDs := getShardTablets(t) @@ -73,6 +76,7 @@ func TestShardView(t *testing.T) { // TestKsView validates the shard names for keyspace. func TestKsView(t *testing.T) { + defer cluster.PanicHandler(t) navigateToKeyspaceView(t) shards := getKeyspaceShard(t) assert.ElementsMatch(t, []string{"-80", "80-"}, shards) @@ -80,6 +84,7 @@ func TestKsView(t *testing.T) { // TestCreateKs validates the keyspace creation using ui. func TestCreateKs(t *testing.T) { + defer cluster.PanicHandler(t) navigateToDashBoard(t) dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") @@ -174,6 +179,7 @@ func TestCreateKs(t *testing.T) { // TestDashboard validate the keyspaces and shard in dashboard. func TestDashboard(t *testing.T) { + defer cluster.PanicHandler(t) navigateToDashBoard(t) ksNames := getDashboardKeyspaces(t) assert.ElementsMatch(t, []string{"test_keyspace", "test_keyspace2"}, ksNames) @@ -183,6 +189,7 @@ func TestDashboard(t *testing.T) { // TestDashboardValidate validates the validate command from the ui. func TestDashboardValidate(t *testing.T) { + defer cluster.PanicHandler(t) navigateToDashBoard(t) dashboardContent, err := wd.FindElement(selenium.ByTagName, "vt-dashboard") require.Nil(t, err) diff --git a/go/test/endtoend/vtgate/aggr_test.go b/go/test/endtoend/vtgate/aggr_test.go index e661b30813a..f7035f7da09 100644 --- a/go/test/endtoend/vtgate/aggr_test.go +++ b/go/test/endtoend/vtgate/aggr_test.go @@ -24,9 +24,11 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" ) func TestAggregateTypes(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) diff --git a/go/test/endtoend/vtgate/buffer/buffer_test.go b/go/test/endtoend/vtgate/buffer/buffer_test.go index 5f2455af6a5..532822fe47d 100644 --- a/go/test/endtoend/vtgate/buffer/buffer_test.go +++ b/go/test/endtoend/vtgate/buffer/buffer_test.go @@ -230,6 +230,7 @@ func TestBufferExternalReparenting(t *testing.T) { } func testBufferBase(t *testing.T, isExternalParent bool) { + defer cluster.PanicHandler(t) clusterInstance, exitCode := createCluster() if exitCode != 0 { os.Exit(exitCode) diff --git a/go/test/endtoend/vtgate/lookup_test.go b/go/test/endtoend/vtgate/lookup_test.go index cfcd186d3cc..639ef71fef9 100644 --- a/go/test/endtoend/vtgate/lookup_test.go +++ b/go/test/endtoend/vtgate/lookup_test.go @@ -26,9 +26,11 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" ) func TestConsistentLookup(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) @@ -252,6 +254,7 @@ func TestDMLScatter(t *testing.T) { } func TestConsistentLookupMultiInsert(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) @@ -305,6 +308,7 @@ func TestConsistentLookupMultiInsert(t *testing.T) { } func TestHashLookupMultiInsertIgnore(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) require.Nil(t, err) diff --git a/go/test/endtoend/vtgate/main_test.go b/go/test/endtoend/vtgate/main_test.go index 550e0618cac..1903fdfcb62 100644 --- a/go/test/endtoend/vtgate/main_test.go +++ b/go/test/endtoend/vtgate/main_test.go @@ -207,6 +207,7 @@ create table t3_id7_idx( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { diff --git a/go/test/endtoend/vtgate/schema/schema_test.go b/go/test/endtoend/vtgate/schema/schema_test.go index 65d9239149a..0363bdcf8af 100644 --- a/go/test/endtoend/vtgate/schema/schema_test.go +++ b/go/test/endtoend/vtgate/schema/schema_test.go @@ -55,10 +55,11 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { - clusterInstance = &cluster.LocalProcessCluster{Cell: cell, Hostname: hostname} + clusterInstance = cluster.NewCluster(cell, hostname) schemaChangeDirectory = path.Join("/tmp", fmt.Sprintf("schema_change_dir_%d", clusterInstance.GetAndReserveTabletUID())) defer os.RemoveAll(schemaChangeDirectory) defer clusterInstance.Teardown() @@ -99,6 +100,7 @@ func TestMain(m *testing.M) { } func TestSchemaChange(t *testing.T) { + defer cluster.PanicHandler(t) testWithInitialSchema(t) testWithAlterSchema(t) testWithAlterDatabase(t) diff --git a/go/test/endtoend/vtgate/sequence/seq_test.go b/go/test/endtoend/vtgate/sequence/seq_test.go index 6f9ba004f66..2dab1944fed 100644 --- a/go/test/endtoend/vtgate/sequence/seq_test.go +++ b/go/test/endtoend/vtgate/sequence/seq_test.go @@ -81,6 +81,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitCode := func() int { @@ -120,6 +121,7 @@ func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { } func TestSeq(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() vtParams := mysql.ConnParams{ Host: "localhost", diff --git a/go/test/endtoend/vtgate/transaction/rollback/txn_rollback_shutdown_test.go b/go/test/endtoend/vtgate/transaction/rollback/txn_rollback_shutdown_test.go index 1345eb52092..7785ef6e4cc 100644 --- a/go/test/endtoend/vtgate/transaction/rollback/txn_rollback_shutdown_test.go +++ b/go/test/endtoend/vtgate/transaction/rollback/txn_rollback_shutdown_test.go @@ -44,6 +44,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -97,6 +98,7 @@ func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { } func TestTransactionRollBackWhenShutDown(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) if err != nil { diff --git a/go/test/endtoend/vtgate/transaction/trxn_mode_test.go b/go/test/endtoend/vtgate/transaction/trxn_mode_test.go index 106cb81010c..5ab5e247ec7 100644 --- a/go/test/endtoend/vtgate/transaction/trxn_mode_test.go +++ b/go/test/endtoend/vtgate/transaction/trxn_mode_test.go @@ -95,6 +95,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -156,6 +157,7 @@ func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { // TestTransactionModes tests trasactions using twopc mode func TestTransactionModes(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) if err != nil { diff --git a/go/test/endtoend/vtgate/vschema/vschema_test.go b/go/test/endtoend/vtgate/vschema/vschema_test.go index 1938d183e0e..18f833c7aff 100644 --- a/go/test/endtoend/vtgate/vschema/vschema_test.go +++ b/go/test/endtoend/vtgate/vschema/vschema_test.go @@ -51,6 +51,7 @@ var ( ) func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) flag.Parse() exitcode, err := func() (int, error) { @@ -94,6 +95,7 @@ func TestMain(m *testing.M) { } func TestVSchema(t *testing.T) { + defer cluster.PanicHandler(t) ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) if err != nil { diff --git a/go/test/endtoend/worker/worker_test.go b/go/test/endtoend/worker/worker_test.go index 066bfc42a31..48b8f2a7ef3 100644 --- a/go/test/endtoend/worker/worker_test.go +++ b/go/test/endtoend/worker/worker_test.go @@ -94,6 +94,7 @@ var ( ) func TestReparentDuringWorkerCopy(t *testing.T) { + defer cluster.PanicHandler(t) _, err := initializeCluster(t, false) defer localCluster.Teardown() require.Nil(t, err) @@ -102,6 +103,7 @@ func TestReparentDuringWorkerCopy(t *testing.T) { } func TestReparentDuringWorkerCopyMysqlDown(t *testing.T) { + defer cluster.PanicHandler(t) _, err := initializeCluster(t, false) defer localCluster.Teardown() require.Nil(t, err) @@ -110,6 +112,7 @@ func TestReparentDuringWorkerCopyMysqlDown(t *testing.T) { } func TestWebInterface(t *testing.T) { + defer cluster.PanicHandler(t) _, err := initializeCluster(t, true) require.Nil(t, err) defer localCluster.Teardown() From cc7ea16a1de792967489a7242d749012d4d6b425 Mon Sep 17 00:00:00 2001 From: Shaun Verch Date: Mon, 2 Mar 2020 14:47:23 -0500 Subject: [PATCH 196/825] Update examples/are-you-alive/README.md Co-Authored-By: Deepthi Sigireddi Signed-off-by: Shaun Verch --- examples/are-you-alive/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/are-you-alive/README.md b/examples/are-you-alive/README.md index e02e366ace9..39f1500b718 100644 --- a/examples/are-you-alive/README.md +++ b/examples/are-you-alive/README.md @@ -29,8 +29,7 @@ This client application: - Data loss (by writing predictable data and testing for that) 1. Reports all these metrics to Prometheus. -That's it! Keep it as simple and generic as possible, and someday our customers -can use this to test their clusters too! +That's it! ## Usage From 86ceabe245f3b865da38810f7311d79fb6f05645 Mon Sep 17 00:00:00 2001 From: Shaun Verch Date: Mon, 2 Mar 2020 14:50:07 -0500 Subject: [PATCH 197/825] Address review comments Signed-off-by: Shaun Verch --- examples/are-you-alive/README.md | 8 ++++++-- examples/are-you-alive/deploy/README.md | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) delete mode 100644 examples/are-you-alive/deploy/README.md diff --git a/examples/are-you-alive/README.md b/examples/are-you-alive/README.md index 39f1500b718..65bff90cad6 100644 --- a/examples/are-you-alive/README.md +++ b/examples/are-you-alive/README.md @@ -2,8 +2,8 @@ What does it mean to be alive? -Well we don't know what it means for you, but we know what it means for our -Cloud Database! +Well we don't know what it means for you, but we know what it means for a Vitess +Cluster! This project contains a simulated client application that can be used to measure the health of a Vitess cluster over time. @@ -75,6 +75,10 @@ exported. ## Push to Registry +If you have push access to the [planetscale public +registry](https://us.gcr.io/planetscale-vitess), you can use the following +commands to build and push the image: + ``` make build make push diff --git a/examples/are-you-alive/deploy/README.md b/examples/are-you-alive/deploy/README.md deleted file mode 100644 index 57592ab6665..00000000000 --- a/examples/are-you-alive/deploy/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Kubernetes Deployment - -Configuration for deploying `are-you-alive` on Kubernetes. - -Also deploys Prometheus and Alertmanager. From de0e7c473381b6442cff5bc2472dcd7d3b1bfcd3 Mon Sep 17 00:00:00 2001 From: Serry Park Date: Mon, 2 Mar 2020 13:30:49 -0800 Subject: [PATCH 198/825] Log when we fail to accept a conn Signed-off-by: Serry Park --- go/mysql/server.go | 2 ++ go/mysql/server_test.go | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/go/mysql/server.go b/go/mysql/server.go index 0ee6dfb2132..a15951c0669 100644 --- a/go/mysql/server.go +++ b/go/mysql/server.go @@ -55,6 +55,7 @@ var ( timings = stats.NewTimings("MysqlServerTimings", "MySQL server timings", "operation") connCount = stats.NewGauge("MysqlServerConnCount", "Active MySQL server connections") connAccept = stats.NewCounter("MysqlServerConnAccepted", "Connections accepted by MySQL server") + connRefuse = stats.NewCounter("MysqlServerConnRefused", "Connections refused by MySQL server") connSlow = stats.NewCounter("MysqlServerConnSlow", "Connections that took more than the configured mysql_slow_connect_warn_threshold to establish") connCountByTLSVer = stats.NewGaugesWithSingleLabel("MysqlServerConnCountByTLSVer", "Active MySQL server connections by TLS version", "tls") @@ -248,6 +249,7 @@ func (l *Listener) Accept() { conn, err := l.listener.Accept() if err != nil { // Close() was probably called. + connRefuse.Add(1) return } diff --git a/go/mysql/server_test.go b/go/mysql/server_test.go index 0f6be720591..1d1487c7780 100644 --- a/go/mysql/server_test.go +++ b/go/mysql/server_test.go @@ -587,6 +587,7 @@ func TestServer(t *testing.T) { initialTimingCounts := timings.Counts() initialConnAccept := connAccept.Get() initialConnSlow := connSlow.Get() + initialconnRefuse := connRefuse.Get() // Run an 'error' command. th.SetErr(NewSQLError(ERUnknownComError, SSUnknownComError, "forced query error")) @@ -607,6 +608,9 @@ func TestServer(t *testing.T) { if connSlow.Get()-initialConnSlow != 1 { t.Errorf("Expected ConnSlow delta=1, got %d", connSlow.Get()-initialConnSlow) } + if connRefuse.Get()-initialconnRefuse != 0 { + t.Errorf("Expected connRefuse delta=0, got %d", connRefuse.Get()-initialconnRefuse) + } expectedTimingDeltas := map[string]int64{ "All": 2, @@ -644,6 +648,9 @@ func TestServer(t *testing.T) { if connSlow.Get()-initialConnSlow != 1 { t.Errorf("Expected ConnSlow delta=1, got %d", connSlow.Get()-initialConnSlow) } + if connRefuse.Get()-initialconnRefuse != 0 { + t.Errorf("Expected connRefuse delta=0, got %d", connRefuse.Get()-initialconnRefuse) + } // Run a 'select rows' command with results. output, ok = runMysql(t, params, "select rows") @@ -1299,6 +1306,7 @@ func TestListenerShutdown(t *testing.T) { Uname: "user1", Pass: "password1", } + initialconnRefuse := connRefuse.Get() ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1314,6 +1322,10 @@ func TestListenerShutdown(t *testing.T) { l.Shutdown() + if connRefuse.Get()-initialconnRefuse != 1 { + t.Errorf("Expected connRefuse delta=1, got %d", connRefuse.Get()-initialconnRefuse) + } + if err := conn.Ping(); err != nil { sqlErr, ok := err.(*SQLError) if !ok { From 1e0fabcc91c1dd5b1bbe0f9f4e368342847a4b1d Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 3 Mar 2020 10:37:59 +0100 Subject: [PATCH 199/825] Instead of failing when trying to skip updates, fallback on slower mode When updating consistent lookup vindexes, we see if anything really did change, before updating the vindex. If nothing changes, we can skip updating the vindex. Sometimes, we encounter errors when checking if we can shortcut the updates. Instead of passing on there errors, we'll now fall back on the slower mode and execute the update. Signed-off-by: Andres Taylor --- go/vt/vtgate/vindexes/consistent_lookup.go | 6 ++-- .../vtgate/vindexes/consistent_lookup_test.go | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/go/vt/vtgate/vindexes/consistent_lookup.go b/go/vt/vtgate/vindexes/consistent_lookup.go index 0d62dca4b7a..1c64d218cc7 100644 --- a/go/vt/vtgate/vindexes/consistent_lookup.go +++ b/go/vt/vtgate/vindexes/consistent_lookup.go @@ -307,10 +307,8 @@ func (lu *clCommon) Update(vcursor VCursor, oldValues []sqltypes.Value, ksid []b equal := true for i := range oldValues { result, err := sqltypes.NullsafeCompare(oldValues[i], newValues[i]) - if err != nil { - return err - } - if result != 0 { + // errors from NullsafeCompare can be ignored. if they are real problems, we'll see them in the Create/Update + if err != nil || result != 0 { equal = false break } diff --git a/go/vt/vtgate/vindexes/consistent_lookup_test.go b/go/vt/vtgate/vindexes/consistent_lookup_test.go index 54a41c005e7..dbb95dbc4d6 100644 --- a/go/vt/vtgate/vindexes/consistent_lookup_test.go +++ b/go/vt/vtgate/vindexes/consistent_lookup_test.go @@ -445,6 +445,41 @@ func TestConsistentLookupNoUpdate(t *testing.T) { vc.verifyLog(t, []string{}) } +func TestConsistentLookupUpdateBecauseUncomparableTypes(t *testing.T) { + lookup := createConsistentLookup(t, "consistent_lookup", false) + vc := &loggingVCursor{} + + type test struct { + typ querypb.Type + val string + } + + tests := []test{ + {querypb.Type_TEXT, "some string"}, + {querypb.Type_VARCHAR, "some string"}, + {querypb.Type_CHAR, "some string"}, + {querypb.Type_BIT, "some string"}, + {querypb.Type_ENUM, "some string"}, + {querypb.Type_SET, "some string"}, + {querypb.Type_GEOMETRY, "some string"}, + {querypb.Type_JSON, "some string"}, + } + + for _, val := range tests { + t.Run(val.typ.String(), func(t *testing.T) { + vc.AddResult(&sqltypes.Result{}, nil) + vc.AddResult(&sqltypes.Result{}, nil) + sqlVal, err := sqltypes.NewValue(val.typ, []byte(val.val)) + require.NoError(t, err) + + err = lookup.(Lookup).Update(vc, []sqltypes.Value{sqlVal, sqlVal}, []byte("test"), []sqltypes.Value{sqlVal, sqlVal}) + require.NoError(t, err) + require.NotEmpty(t, vc.log) + vc.log = nil + }) + } +} + func createConsistentLookup(t *testing.T, name string, writeOnly bool) SingleColumn { t.Helper() write := "false" From e5d896a984584a5ef13b5f78f4f922ae67ed9b5d Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Sat, 29 Feb 2020 19:52:08 +0530 Subject: [PATCH 200/825] Support owned vindex update if partial column present in set and other present in where clause Signed-off-by: Harshit Gangal --- go/test/endtoend/vtgate/lookup_test.go | 69 +++++++++++++ go/test/endtoend/vtgate/main_test.go | 45 +++++++++ go/vt/vtgate/engine/update.go | 59 +++++++----- go/vt/vtgate/engine/update_test.go | 34 ++++--- .../vtgate/planbuilder/testdata/dml_cases.txt | 96 ++++++++++++------- .../testdata/unsupported_cases.txt | 4 - go/vt/vtgate/planbuilder/update.go | 17 ++-- 7 files changed, 232 insertions(+), 92 deletions(-) diff --git a/go/test/endtoend/vtgate/lookup_test.go b/go/test/endtoend/vtgate/lookup_test.go index 639ef71fef9..65c174bf8f1 100644 --- a/go/test/endtoend/vtgate/lookup_test.go +++ b/go/test/endtoend/vtgate/lookup_test.go @@ -344,6 +344,75 @@ func TestHashLookupMultiInsertIgnore(t *testing.T) { } } +func TestConsistentLookupUpdate(t *testing.T) { + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + /* Simple insert. after this dml, the tables will contain the following: + t4 (id1, id2): + 1 2 + 2 2 + 3 3 + 4 3 + + t4_id2_idx (id2, id1, keyspace_id:id1): + 2 1 1 + 2 2 2 + 3 3 3 + 3 4 4 + */ + exec(t, conn, "insert into t4(id1, id2) values(1, '2'), (2, '2'), (3, '3'), (4, '3')") + qr := exec(t, conn, "select id1, id2 from t4 order by id1") + if got, want := fmt.Sprintf("%v", qr.Rows), `[[INT64(1) VARCHAR("2")] [INT64(2) VARCHAR("2")] [INT64(3) VARCHAR("3")] [INT64(4) VARCHAR("3")]]`; got != want { + t.Errorf("select:\n%v want\n%v", got, want) + } + + /* Updating a lookup column. after this dml, the tables will contain the following: + t4 (id1, id2): + 1 42 + 2 2 + 3 3 + 4 3 + + t4_id2_idx (id2, id1, keyspace_id:id1): + 42 1 1 + 2 2 2 + 3 3 3 + 3 4 4 + */ + exec(t, conn, "update t4 set id2 = '42' where id1 = 1") + qr = exec(t, conn, "select id1, id2 from t4 order by id1") + if got, want := fmt.Sprintf("%v", qr.Rows), `[[INT64(1) VARCHAR("42")] [INT64(2) VARCHAR("2")] [INT64(3) VARCHAR("3")] [INT64(4) VARCHAR("3")]]`; got != want { + t.Errorf("select:\n%v want\n%v", got, want) + } + + /* delete one specific keyspace id. after this dml, the tables will contain the following: + t4 (id1, id2): + 2 2 + 3 3 + 4 3 + + t4_id2_idx (id2, id1, keyspace_id:id1): + 2 2 2 + 3 3 3 + 3 4 4 + */ + exec(t, conn, "delete from t4 where id2 = '42'") + qr = exec(t, conn, "select * from t4 where id2 = '42'") + require.Empty(t, qr.Rows) + qr = exec(t, conn, "select * from t4_id2_idx where id2 = '42'") + require.Empty(t, qr.Rows) + + // delete all the rows. + exec(t, conn, "delete from t4") + qr = exec(t, conn, "select * from t4") + require.Empty(t, qr.Rows) + qr = exec(t, conn, "select * from t4_id2_idx") + require.Empty(t, qr.Rows) +} + func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { t.Helper() qr, err := conn.ExecuteFetch(query, 1000, true) diff --git a/go/test/endtoend/vtgate/main_test.go b/go/test/endtoend/vtgate/main_test.go index 1903fdfcb62..5ca001fb8a8 100644 --- a/go/test/endtoend/vtgate/main_test.go +++ b/go/test/endtoend/vtgate/main_test.go @@ -81,12 +81,28 @@ create table t3_id7_idx( id7 bigint, id6 bigint, primary key(id) +) Engine=InnoDB; + +create table t4( + id1 bigint, + id2 varchar(10), + primary key(id1) +) Engine=InnoDB; + +create table t4_id2_idx( + id2 varchar(10), + id1 bigint, + keyspace_id varbinary(50), + primary key(id2, id1) ) Engine=InnoDB;` VSchema = ` { "sharded": true, "vindexes": { + "unicode_loose_md5" : { + "type": "unicode_loose_md5" + }, "hash": { "type": "hash" }, @@ -117,6 +133,15 @@ create table t3_id7_idx( "to": "id6" }, "owner": "t3" + }, + "t4_id2_vdx": { + "type": "consistent_lookup", + "params": { + "table": "t4_id2_idx", + "from": "id2,id1", + "to": "keyspace_id" + }, + "owner": "t4" } }, "tables": { @@ -179,6 +204,26 @@ create table t3_id7_idx( "name": "hash" } ] + }, + "t4": { + "column_vindexes": [ + { + "column": "id1", + "name": "hash" + }, + { + "columns": ["id2", "id1"], + "name": "t4_id2_vdx" + } + ] + }, + "t4_id2_idx": { + "column_vindexes": [ + { + "column": "id2", + "name": "unicode_loose_md5" + } + ] }, "vstream_test": { "column_vindexes": [ diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index 4fb54b0d841..c5c1c63f48b 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -34,12 +34,15 @@ import ( var _ Primitive = (*Update)(nil) +// VindexValues contains changed values for a vindex. +type VindexValues map[string]sqltypes.PlanValue + // Update represents the instructions to perform an update. type Update struct { DML // ChangedVindexValues contains values for updated Vindexes during an update statement. - ChangedVindexValues map[string][]sqltypes.PlanValue + ChangedVindexValues map[string]VindexValues // Update does not take inputs noInputs @@ -60,16 +63,16 @@ func (upd *Update) MarshalJSON() ([]byte, error) { } marshalUpdate := struct { Opcode string - Keyspace *vindexes.Keyspace `json:",omitempty"` - Query string `json:",omitempty"` - Vindex string `json:",omitempty"` - Values []sqltypes.PlanValue `json:",omitempty"` - ChangedVindexValues map[string][]sqltypes.PlanValue `json:",omitempty"` - Table string `json:",omitempty"` - OwnedVindexQuery string `json:",omitempty"` - KsidVindex string `json:",omitempty"` - MultiShardAutocommit bool `json:",omitempty"` - QueryTimeout int `json:",omitempty"` + Keyspace *vindexes.Keyspace `json:",omitempty"` + Query string `json:",omitempty"` + Vindex string `json:",omitempty"` + Values []sqltypes.PlanValue `json:",omitempty"` + ChangedVindexValues map[string]VindexValues `json:",omitempty"` + Table string `json:",omitempty"` + OwnedVindexQuery string `json:",omitempty"` + KsidVindex string `json:",omitempty"` + MultiShardAutocommit bool `json:",omitempty"` + QueryTimeout int `json:",omitempty"` }{ Opcode: upd.RouteType(), Keyspace: upd.Keyspace, @@ -197,29 +200,35 @@ func (upd *Update) updateVindexEntries(vcursor VCursor, bindVars map[string]*que return nil } + fieldColNumMap := make(map[string]int) + for colNum, field := range subQueryResult.Fields { + fieldColNumMap[field.Name] = colNum + } + for _, row := range subQueryResult.Rows { - colnum := 1 // we start from the first non-vindex col ksid, err := resolveKeyspaceID(vcursor, upd.KsidVindex, row[0]) if err != nil { return err } for _, colVindex := range upd.Table.Owned { - // Fetch the column values. colnum must keep incrementing. - fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) - for range colVindex.Columns { - fromIds = append(fromIds, row[colnum]) - colnum++ - } - // Update columns only if they're being changed. - if colValues, ok := upd.ChangedVindexValues[colVindex.Name]; ok { + if updColValues, ok := upd.ChangedVindexValues[colVindex.Name]; ok { + fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) var vindexColumnKeys []sqltypes.Value - for _, colValue := range colValues { - resolvedVal, err := colValue.ResolveValue(bindVars) - if err != nil { - return err + for _, vCol := range colVindex.Columns { + // Fetch the column values. + origColValue := row[fieldColNumMap[vCol.String()]] + fromIds = append(fromIds, origColValue) + if colValue, exists := updColValues[vCol.String()]; exists { + resolvedVal, err := colValue.ResolveValue(bindVars) + if err != nil { + return err + } + vindexColumnKeys = append(vindexColumnKeys, resolvedVal) + } else { + // Set the column value to original as this column in vindex is not updated. + vindexColumnKeys = append(vindexColumnKeys, origColValue) } - vindexColumnKeys = append(vindexColumnKeys, resolvedVal) } if err := colVindex.Vindex.(vindexes.Lookup).Update(vcursor, fromIds, ksid, vindexColumnKeys); err != nil { diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index b73590a7ca3..356c9c10745 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -203,15 +203,14 @@ func TestUpdateEqualChangedVindex(t *testing.T) { OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["hash"].(vindexes.SingleColumn), }, - ChangedVindexValues: map[string][]sqltypes.PlanValue{ - "twocol": {{ - Value: sqltypes.NewInt64(1), - }, { - Value: sqltypes.NewInt64(2), - }}, - "onecol": {{ - Value: sqltypes.NewInt64(3), - }}, + ChangedVindexValues: map[string]VindexValues{ + "twocol": { + "c1": {Value: sqltypes.NewInt64(1)}, + "c2": {Value: sqltypes.NewInt64(2)}, + }, + "onecol": { + "c3": {Value: sqltypes.NewInt64(3)}, + }, }, } @@ -309,15 +308,14 @@ func TestUpdateScatterChangedVindex(t *testing.T) { OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["hash"].(vindexes.SingleColumn), }, - ChangedVindexValues: map[string][]sqltypes.PlanValue{ - "twocol": {{ - Value: sqltypes.NewInt64(1), - }, { - Value: sqltypes.NewInt64(2), - }}, - "onecol": {{ - Value: sqltypes.NewInt64(3), - }}, + ChangedVindexValues: map[string]VindexValues{ + "twocol": { + "c1": {Value: sqltypes.NewInt64(1)}, + "c2": {Value: sqltypes.NewInt64(2)}, + }, + "onecol": { + "c3": {Value: sqltypes.NewInt64(3)}, + }, }, } diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt index 1de0c561b2d..4c84422be4f 100644 --- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt @@ -229,9 +229,9 @@ 1 ], "ChangedVindexValues": { - "email_user_map": [ - "juan@vitess.io" - ] + "email_user_map": { + "email": "juan@vitess.io" + } }, "Table": "user_metadata", "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update", @@ -259,12 +259,12 @@ 1 ], "ChangedVindexValues": { - "address_user_map": [ - "155 5th street" - ], - "email_user_map": [ - "juan@vitess.io" - ] + "address_user_map": { + "address": "155 5th street" + }, + "email_user_map": { + "email": "juan@vitess.io" + } }, "Table": "user_metadata", "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 for update", @@ -288,9 +288,9 @@ 1 ], "ChangedVindexValues": { - "email_user_map": [ - "juan@vitess.io" - ] + "email_user_map": { + "email": "juan@vitess.io" + } }, "Table": "user_metadata", "OwnedVindexQuery": "select user_id, email, address from user_metadata where user_id = 1 order by user_id asc limit 10 for update", @@ -1777,10 +1777,10 @@ 1 ], "ChangedVindexValues": { - "colb_colc_map": [ - 1, - 2 - ] + "colb_colc_map": { + "column_b": 1, + "column_c": 2 + } }, "Table": "multicolvin", "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update", @@ -1804,13 +1804,13 @@ 1 ], "ChangedVindexValues": { - "cola_map": [ - 0 - ], - "colb_colc_map": [ - 1, - 2 - ] + "cola_map": { + "column_a": 0 + }, + "colb_colc_map": { + "column_b": 1, + "column_c": 2 + } }, "Table": "multicolvin", "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update", @@ -2106,9 +2106,9 @@ 1 ], "ChangedVindexValues": { - "name_user_map": [ - null - ] + "name_user_map": { + "Name": null + } }, "Table": "user", "OwnedVindexQuery": "select Id, Name, Costly from user where id = 1 for update", @@ -2143,9 +2143,9 @@ }, "Query": "update user set name = null where id in (1, 2, 3)", "ChangedVindexValues": { - "name_user_map": [ - null - ] + "name_user_map": { + "Name": null + } }, "Table": "user", "OwnedVindexQuery": "select Id, Name, Costly from user where id in (1, 2, 3) for update", @@ -2165,9 +2165,9 @@ }, "Query": "update user set name = null", "ChangedVindexValues": { - "name_user_map": [ - null - ] + "name_user_map": { + "Name": null + } }, "Table": "user", "OwnedVindexQuery": "select Id, Name, Costly from user for update", @@ -2187,9 +2187,9 @@ }, "Query": "update user set name = null where id + 1 = 2", "ChangedVindexValues": { - "name_user_map": [ - null - ] + "name_user_map": { + "Name": null + } }, "Table": "user", "OwnedVindexQuery": "select Id, Name, Costly from user where id + 1 = 2 for update", @@ -2300,3 +2300,29 @@ "KsidVindex": "user_index" } } + +# update multi column vindex, without values for all the vindex columns +"update multicolvin set column_c = 2 where kid = 1" +{ + "Original": "update multicolvin set column_c = 2 where kid = 1", + "Instructions": { + "Opcode": "UpdateEqual", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "update multicolvin set column_c = 2 where kid = 1", + "Vindex": "kid_index", + "Values": [ + 1 + ], + "ChangedVindexValues": { + "colb_colc_map": { + "column_c": 2 + } + }, + "Table": "multicolvin", + "OwnedVindexQuery": "select kid, column_a, column_b, column_c from multicolvin where kid = 1 for update", + "KsidVindex": "kid_index" + } +} diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt index 0bf10af7c87..7bccf1373f2 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt @@ -259,10 +259,6 @@ "update user_metadata set email = 'juan@vitess.io' where user_id = 1 limit 10" "unsupported: Need to provide order by clause when using limit. Invalid update on vindex: email_user_map" -# update multi column vindex, without values for all the vindex columns -"update multicolvin set column_c = 2 where kid = 1" -"unsupported: update does not have values for all the columns in vindex (colb_colc_map)" - # cross-shard update tables "update (select id from user) as u set id = 4" "unsupported: subqueries in sharded DML" diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index fc343014d71..d8c84f40723 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -34,7 +34,7 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Upd } eupd := &engine.Update{ DML: *dml, - ChangedVindexValues: make(map[string][]sqltypes.PlanValue), + ChangedVindexValues: make(map[string]engine.VindexValues), } if dml.Opcode == engine.Unsharded { @@ -54,10 +54,10 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema ContextVSchema) (*engine.Upd // buildChangedVindexesValues adds to the plan all the lookup vindexes that are changing. // Updates can only be performed to secondary lookup vindexes with no complex expressions // in the set clause. -func buildChangedVindexesValues(update *sqlparser.Update, colVindexes []*vindexes.ColumnVindex) (map[string][]sqltypes.PlanValue, error) { - changedVindexes := make(map[string][]sqltypes.PlanValue) +func buildChangedVindexesValues(update *sqlparser.Update, colVindexes []*vindexes.ColumnVindex) (map[string]engine.VindexValues, error) { + changedVindexes := make(map[string]engine.VindexValues) for i, vindex := range colVindexes { - var vindexValues []sqltypes.PlanValue + vindexValueMap := make(engine.VindexValues) for _, vcol := range vindex.Columns { // Searching in order of columns in colvindex. found := false @@ -73,16 +73,13 @@ func buildChangedVindexesValues(update *sqlparser.Update, colVindexes []*vindexe if err != nil { return nil, err } - vindexValues = append(vindexValues, pv) + vindexValueMap[vcol.String()] = pv } } - if len(vindexValues) == 0 { + if len(vindexValueMap) == 0 { // Vindex not changing, continue continue } - if len(vindexValues) != len(vindex.Columns) { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: update does not have values for all the columns in vindex (%s)", vindex.Name) - } if update.Limit != nil && len(update.OrderBy) == 0 { return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: Need to provide order by clause when using limit. Invalid update on vindex: %v", vindex.Name) @@ -96,7 +93,7 @@ func buildChangedVindexesValues(update *sqlparser.Update, colVindexes []*vindexe if !vindex.Owned { return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: You can only update owned vindexes. Invalid update on vindex: %v", vindex.Name) } - changedVindexes[vindex.Name] = vindexValues + changedVindexes[vindex.Name] = vindexValueMap } return changedVindexes, nil From cb72dd971226031dc44a3cece379be2af168494a Mon Sep 17 00:00:00 2001 From: saurabh Date: Tue, 3 Mar 2020 18:47:28 +0530 Subject: [PATCH 201/825] reverting unit test case docker scripts config Signed-off-by: saurabh --- test/config.json | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/config.json b/test/config.json index aa5db0dc9b0..3e6278d224b 100644 --- a/test/config.json +++ b/test/config.json @@ -67,6 +67,41 @@ "RetryMax": 0, "Tags": [] }, + "e2e_race": { + "File": "", + "Args": [], + "Command": [ + "make", + "e2e_test_race" + ], + "Manual": false, + "Shard": 5, + "RetryMax": 0, + "Tags": [] + }, + "unit": { + "File": "", + "Args": [], + "Command": [ + "tools/unit_test_runner.sh" + ], + "Manual": false, + "Shard": 5, + "RetryMax": 0, + "Tags": [] + }, + "unit_race": { + "File": "", + "Args": [], + "Command": [ + "make", + "unit_test_race" + ], + "Manual": false, + "Shard": 5, + "RetryMax": 0, + "Tags": [] + }, "python_client": { "File": "python_client_test.py", "Args": [], From d4d2cf1b7148895c8ce9a2d6ff35bf67f29b1391 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Tue, 3 Mar 2020 08:28:43 -0700 Subject: [PATCH 202/825] Add initial security policy Signed-off-by: Morgan Tocker --- README.md | 6 ++++ SECURITY.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 SECURITY.md diff --git a/README.md b/README.md index c541c544693..af1c4b89a1a 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,12 @@ for low-frequency updates like new features and releases. ## Security +### Reporting Security Vulnerabilities + +To report a security vulnerability, please email [vitess-maintainers](mailto:cncf-vitess-maintainers@lists.cncf.io). + +See [Security](SECURITY.md) for a full outline of the security process. + ### Security Audit A third party security audit was performed by Cure53. You can see the full report [here](doc/VIT-01-report.pdf). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..601608c6d80 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,93 @@ +# Security Release Process + +Vitess is a large growing community of volunteers, users, and vendors. The Vitess community has +adopted this security disclosure and response policy to ensure we responsibly handle critical +issues. + +## Maintainers Team + +Security vulnerabilities should be handled quickly and sometimes privately. The primary goal of this +process is to reduce the total time users are vulnerable to publicly known exploits. + +The Vitess [maintainers](MAINTAINERS.md) team is responsible for the entire response including internal communication and external disclosure. In the future, we may delagate responsibility to a sub-team as other projects have elected to do so. + +## Disclosures + +### Private Disclosure Processes + +The Vitess community asks that all suspected vulnerabilities be privately and responsibly disclosed via the [reporting policy](README.md#reporting-security-vulnerabilities). + +### Public Disclosure Processes + +If you know of a publicly disclosed security vulnerability please IMMEDIATELY email +[vitess-maintainers](mailto:cncf-vitess-maintainers@lists.cncf.io) to inform about the vulnerability so that we may start the patch, release, and communication process. + +## Patch, Release, and Public Communication + +For each vulnerability a member of the maintainers team will volunteer to lead coordination of the fix (Fix Lead), and ensure that it is backported to each supported branch. They will then coordinate with the remainder of the maintainers team to coordinate new releases and ensure a communication plan is in place for vulnerability disclosure. + +All of the timelines below are suggestions and assume a private disclosure. The Fix Lead drives the +schedule using their best judgment based on severity and development time. If the Fix Lead is +dealing with a public disclosure all timelines become ASAP (assuming the vulnerability has a CVSS +score >= 4; see below). If the fix relies on another upstream project's disclosure timeline, that +will adjust the process as well. We will work with the upstream project to fit their timeline and +best protect our users. + +#### Policy for master-only vulnerabilities + +If a security vulnerability only affects master, but not a stable release (i.e. Vitess 5) then the following process will apply: + +* The fix will land in master. +* A courtesy email will be sent to [vitess@googlegroups.com](https://groups.google.com/forum/#!forum/vitess) along with a posted notice in #developers on Slack. + +#### Policy for unsupported releases + +If a security vulnerability affects only a stable release which is no longer under active support, then the following process will apply: + +* A fix **will not** be issued (exceptions may be made for extreme circumstances) +* An email will be sent to [vitess-announce@googlegroups.com](https://groups.google.com/forum/#!forum/vitess-announce) identifying the threat, and encouraging users to upgrade. + +#### Policy for supported releases + +If a security vulnerability affects supported branches (i.e. Vitess 5), then a Fix Lead will be appointed and the full security process as defined below will apply. + +### Fix Development Process + +These steps should be completed within the 1-7 days of Disclosure. + +- The Fix Lead will create a + [CVSS](https://www.first.org/cvss/specification-document) using the [CVSS + Calculator](https://www.first.org/cvss/calculator/3.0). The Fix Lead makes the final call on the + calculated CVSS; it is better to move quickly than making the CVSS perfect. +- The Fix Lead will notify the maintainers that work on the fix branch is complete. Maintainers will review the fix branch in a private repo and provide require LGTMs. + +If the CVSS score is under 4.0 ([a low severity +score](https://www.first.org/cvss/specification-document#i5)) the maintainers can decide to slow the +release process down in the face of holidays, developer bandwidth, etc. + +### Fix Disclosure Process + +With the fix development underway, the Fix Lead needs to come up with an overall communication plan +for the wider community. This Disclosure process should begin after the Fix Lead has developed a Fix +or mitigation so that a realistic timeline can be communicated to users. + +**Disclosure of Forthcoming Fix to Users** (Completed within 1-7 days of Disclosure) + +- The Fix Lead will email [vitess-announce@googlegroups.com](https://groups.google.com/forum/#!forum/vitess-announce) + informing users that a security vulnerability has been disclosed and that a fix will be made + available at YYYY-MM-DD HH:MM UTC in the future via this list. This time is the Release Date. +- The Fix Lead will include any mitigating steps users can take until a fix is available. + +The communication to users should be actionable. They should know when to block time to apply +patches, understand exact mitigation steps, etc. + +**Disclosure of Fixed Vulnerability** + +- The Fix Lead will email [vitess-announce@googlegroups.com](https://groups.google.com/forum/#!forum/vitess-announce) + informing users that there are new releases available to address an identified vulnerability. +- As much as possible this email should be actionable and include links to CVEs, and how to apply + the fix to user's environments; this can include links to external distributor documentation. + +### Embargo Policy + +The Vitess team currently does not provide advance notice of undisclosed vulnerabilities to any third parties. We are open to feedback on what such a policy can or should look like. For the interim, the best way to receive advanced notice of undisclosed vulnerabilities is to apply to join the maintainers team. From 52b698d6d0dd3d8230d067745bf65d1c0c4e5a86 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 3 Mar 2020 17:54:53 +0100 Subject: [PATCH 203/825] Add more test around last_insert_id() and rollback Signed-off-by: Andres Taylor --- go/vt/vtgate/endtoend/last_insert_id_test.go | 51 ++++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/go/vt/vtgate/endtoend/last_insert_id_test.go b/go/vt/vtgate/endtoend/last_insert_id_test.go index 4933ad4cd10..81b90d4fe76 100644 --- a/go/vt/vtgate/endtoend/last_insert_id_test.go +++ b/go/vt/vtgate/endtoend/last_insert_id_test.go @@ -21,21 +21,62 @@ import ( "fmt" "testing" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/mysql" ) func TestLastInsertId(t *testing.T) { ctx := context.Background() conn, err := mysql.Connect(ctx, &vtParams) - if err != nil { - t.Fatal(err) + require.NoError(t, err) + defer conn.Close() + + // figure out the last inserted id before we run change anything + qr := exec(t, conn, "select max(id) from t1_last_insert_id") + oldLastID, err := sqltypes.ToUint64(qr.Rows[0][0]) + require.NoError(t, err) + + exec(t, conn, "insert into t1_last_insert_id(id1) values(42)") + + // even without a transaction, we should get the last inserted id back + qr = exec(t, conn, "select last_insert_id()") + got := fmt.Sprintf("%v", qr.Rows) + want := fmt.Sprintf("[[INT64(%d)]]", oldLastID+1) + + if diff := cmp.Diff(want, got); diff != "" { + t.Error(diff) } +} + +func TestLastInsertIdWithRollback(t *testing.T) { + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) defer conn.Close() + // figure out the last inserted id before we run our tests + qr := exec(t, conn, "select max(id) from t1_last_insert_id") + oldLastID, err := sqltypes.ToUint64(qr.Rows[0][0]) + require.NoError(t, err) + + // add row inside explicit transaction + exec(t, conn, "begin") exec(t, conn, "insert into t1_last_insert_id(id1) values(42)") + qr = exec(t, conn, "select last_insert_id()") + got := fmt.Sprintf("%v", qr.Rows) + want := fmt.Sprintf("[[INT64(%d)]]", oldLastID+1) - qr := exec(t, conn, "select last_insert_id()") - if got, want := fmt.Sprintf("%v", qr.Rows), `[[INT64(1)]]`; got != want { - t.Errorf("select:\n%v want\n%v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Error(diff) + } + // even if we do a rollback, we should still get the same last_insert_id + exec(t, conn, "rollback") + qr = exec(t, conn, "select last_insert_id()") + got = fmt.Sprintf("%v", qr.Rows) + if diff := cmp.Diff(want, got); diff != "" { + t.Error(diff) } } From 118a3b4760abd9449e496164c33a351fababcea9 Mon Sep 17 00:00:00 2001 From: Adam Saponara Date: Tue, 3 Mar 2020 16:11:07 -0500 Subject: [PATCH 204/825] Add `-mysql_default_workload` for setting default workload. This saves us a roundtrip when setting up OLAP clients. Signed-off-by: Adam Saponara --- go/vt/vtgate/plugin_mysql_server.go | 11 +++++++++++ go/vt/vtgate/plugin_mysql_server_test.go | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/go/vt/vtgate/plugin_mysql_server.go b/go/vt/vtgate/plugin_mysql_server.go index 3ebb406e4df..b83330c34f6 100644 --- a/go/vt/vtgate/plugin_mysql_server.go +++ b/go/vt/vtgate/plugin_mysql_server.go @@ -22,6 +22,7 @@ import ( "net" "os" "regexp" + "strings" "sync" "sync/atomic" "syscall" @@ -67,6 +68,9 @@ var ( mysqlConnWriteTimeout = flag.Duration("mysql_server_write_timeout", 0, "connection write timeout") mysqlQueryTimeout = flag.Duration("mysql_server_query_timeout", 0, "mysql query timeout") + mysqlDefaultWorkloadName = flag.String("mysql_default_workload", "UNSPECIFIED", "Default session workload (OLTP, OLAP, DBA)") + mysqlDefaultWorkload int32 + busyConnections int32 ) @@ -316,6 +320,7 @@ func (vh *vtgateHandler) session(c *mysql.Conn) *vtgatepb.Session { session = &vtgatepb.Session{ Options: &querypb.ExecuteOptions{ IncludedFields: querypb.ExecuteOptions_ALL, + Workload: querypb.ExecuteOptions_Workload(mysqlDefaultWorkload), }, Autocommit: true, } @@ -351,6 +356,12 @@ func initMySQLProtocol() { } authServer := mysql.GetAuthServer(*mysqlAuthServerImpl) + // Check mysql_default_workload + var ok bool + if mysqlDefaultWorkload, ok = querypb.ExecuteOptions_Workload_value[strings.ToUpper(*mysqlDefaultWorkloadName)]; !ok { + log.Exitf("-mysql_default_workload must be one of [OLTP, OLAP, DBA, UNSPECIFIED]") + } + switch *mysqlTCPVersion { case "tcp", "tcp4", "tcp6": // Valid flag value. diff --git a/go/vt/vtgate/plugin_mysql_server_test.go b/go/vt/vtgate/plugin_mysql_server_test.go index dd6b125e86f..e056dda24cd 100644 --- a/go/vt/vtgate/plugin_mysql_server_test.go +++ b/go/vt/vtgate/plugin_mysql_server_test.go @@ -209,3 +209,20 @@ func newTestAuthServerStatic() *mysql.AuthServerStatic { jsonConfig := "{\"user1\":{\"Password\":\"password1\", \"UserData\":\"userData1\", \"SourceHost\":\"localhost\"}}" return mysql.NewAuthServerStatic("", jsonConfig, 0) } + +func TestDefaultWorkloadEmpty(t *testing.T) { + vh := &vtgateHandler{} + sess := vh.session(&mysql.Conn{}) + if sess.Options.Workload != querypb.ExecuteOptions_UNSPECIFIED { + t.Fatalf("Expected default workload UNSPECIFIED") + } +} + +func TestDefaultWorkloadOLAP(t *testing.T) { + vh := &vtgateHandler{} + mysqlDefaultWorkload = int32(querypb.ExecuteOptions_OLAP) + sess := vh.session(&mysql.Conn{}) + if sess.Options.Workload != querypb.ExecuteOptions_OLAP { + t.Fatalf("Expected default workload OLAP") + } +} From 10fe0498e2f200057b77d7ffe21fcc65b94612c9 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Wed, 4 Mar 2020 06:04:28 +0100 Subject: [PATCH 205/825] Better test setup Signed-off-by: Andres Taylor --- go/vt/vtgate/endtoend/main_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/go/vt/vtgate/endtoend/main_test.go b/go/vt/vtgate/endtoend/main_test.go index 00ee273fb77..e6fe2fa8b92 100644 --- a/go/vt/vtgate/endtoend/main_test.go +++ b/go/vt/vtgate/endtoend/main_test.go @@ -17,6 +17,7 @@ limitations under the License. package endtoend import ( + "context" "flag" "fmt" "os" @@ -212,7 +213,24 @@ func TestMain(m *testing.M) { mysqlParams = cluster.MySQLConnParams() grpcAddress = fmt.Sprintf("localhost:%d", cluster.Env.PortForProtocol("vtcombo", "grpc")) + insertStartValue() + return m.Run() }() os.Exit(exitCode) } + +func insertStartValue() { + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + if err != nil { + panic(err) + } + defer conn.Close() + + // lets insert a single starting value for tests + _, err = conn.ExecuteFetch("insert into t1_last_insert_id(id1) values(42)", 1000, true) + if err != nil { + panic(err) + } +} From cab5d7722688bb1cdf2816eb6b4580ca908bce47 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Fri, 3 Jan 2020 10:54:23 +0100 Subject: [PATCH 206/825] Improved test case. Modified template Signed-off-by: Rohit Nayak --- .../tabletmanager/vreplication/vplayer.go | 4 +- .../tabletmanager/vreplication/vrlog.go | 24 ++++++------ .../tabletmanager/vreplication/vrlog_test.go | 37 +++++++++++++++---- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 9196d30b845..1c89bc93e75 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -192,7 +192,7 @@ func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.Row } for _, change := range rowEvent.RowChanges { _, err := tplan.applyChange(change, func(sql string) (*sqltypes.Result, error) { - stats := NewVrLogStats(ctx, "RowChange") + stats := Fr(ctx, "ROWCHANGE") result, err := vp.vr.dbClient.ExecuteWithRetry(ctx, sql) stats.Record(sql) return result, err @@ -310,7 +310,7 @@ func hasAnotherCommit(items [][]*binlogdatapb.VEvent, i, j int) bool { } func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, mustSave bool) error { - stats := NewVrLogStats(ctx, event.String()) + stats := NewVrLogStats(ctx, event.Type.String()) switch event.Type { case binlogdatapb.VEventType_GTID: pos, err := mysql.DecodePosition(event.Gtid) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go index 6993230c8c2..f754ffdc801 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go @@ -17,9 +17,9 @@ limitations under the License. package vreplication import ( - "html/template" "net/http" "strconv" + "text/template" "time" "golang.org/x/net/context" @@ -29,12 +29,8 @@ import ( var ( vrLogStatsLogger = streamlog.New("VReplication", 50) - vrLogStatsTemplate = template.Must(template.New("vrlog").Parse(` - {{.Type}}\t - {{.Detail}}\t - {{.Time}}\t - {{.DurationNs}}\n - `)) + vrLogStatsTemplate = template.Must(template.New("vrlog"). + Parse("{{.Type}} Event {{.Detail}} {{.Time}} {{.DurationNs}}\n")) ) type VrLogStats struct { @@ -45,21 +41,23 @@ type VrLogStats struct { DurationNs int64 } +func NewVrLogStats(ctx context.Context, eventType string) *VrLogStats { + return &VrLogStats{Ctx: ctx, Type: eventType, Time: time.Now()} +} + func (stats *VrLogStats) Send() { vrLogStatsLogger.Send(stats) } -func (stats *VrLogStats) Record(detail string) { +func (stats *VrLogStats) Record(detail string) bool { if stats.Ctx == nil || stats.Time.IsZero() { - log.Error("VrLogStats not initialized") + log.Error("VrLogStats not initialized for %s", detail) + return false } stats.Detail = detail stats.DurationNs = time.Since(stats.Time).Nanoseconds() stats.Send() -} - -func NewVrLogStats(ctx context.Context, eventType string) *VrLogStats { - return &VrLogStats{Ctx: ctx, Type: eventType, Time: time.Now()} + return true } func init() { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go index 479c8c46f51..0c23b089f2a 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strconv" "strings" "testing" "time" @@ -35,15 +36,35 @@ func TestVrLog(t *testing.T) { go func() { vrlogStatsHandler(ch, w, r) }() - stats := VrLogStats{Ctx: context.Background()} - msg := "test msg" - stats.Detail = msg - stats.Send() + ctx := context.Background() + eventType, detail := "Test", "detail 1" + stats := NewVrLogStats(ctx, eventType) + time.Sleep(1 * time.Millisecond) + stats.Record(detail) time.Sleep(1 * time.Millisecond) s := w.Body.String() - - if !strings.Contains(s, msg) { //we use Contains since the printed log is in html and also prepends current time - t.Fatalf(fmt.Sprintf("want %s, got %s", msg, s)) + want := fmt.Sprintf("%s Event %s", eventType, detail) + if !strings.Contains(s, want) { + t.Fatalf(fmt.Sprintf("want %s, got %s", want, s)) + } + if strings.HasSuffix(s, "\\n") { + t.Fatalf("does not end in a newline: %s", s) + } + s = strings.TrimSuffix(s, "\n") + ss := strings.Split(s, " ") + numCols := 4 + if ss == nil || len(ss) != numCols { + t.Fatalf("log line should have %d columns, not %d, : %s", numCols, len(ss), strings.Join(ss, "|")) + } + lastColValue, err := strconv.Atoi(ss[len(ss)-1]) + if err != nil { + t.Fatalf("Duration is not an integer: %s", err) + } + if lastColValue < 1<<9 { + t.Fatalf("Waited 1 Millisecond, so duration should be greater than that: %d, %s", lastColValue, ss[len(ss)-1]) + } + stats = &VrLogStats{} + if stats.Record("should error out since stats is not initalized") != false { + t.Fatalf("Uninitialized stats should not log") } - } From 9c6bb47b5d308326a3ac8fec6df3c937b0d42a32 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sat, 4 Jan 2020 15:46:07 +0100 Subject: [PATCH 207/825] Test cases for debug log. Log stream template changed Signed-off-by: Rohit Nayak --- go.sum | 1 + .../vreplication/framework_test.go | 49 +++++++++++++++++++ .../tabletmanager/vreplication/vplayer.go | 2 +- .../vreplication/vplayer_test.go | 25 ++++++++++ .../tabletmanager/vreplication/vrlog.go | 13 +++-- 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/go.sum b/go.sum index d5373e2df2e..4b7e97903c3 100644 --- a/go.sum +++ b/go.sum @@ -456,6 +456,7 @@ github.com/mitchellh/copystructure v0.0.0-20160804032330-cdac8253d00f/go.mod h1: github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b h1:9+ke9YJ9KGWw5ANXK6ozjoK47uI3uNbXv4YVINBnGm8= github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 08c2b40465e..6f23c36dd31 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -53,8 +53,14 @@ var ( globalFBC = &fakeBinlogClient{} vrepldb = "vrepl" globalDBQueries = make(chan string, 1000) + globalLogs = make(chan interface{}, 1000) ) +type LogExpectation struct { + Type string + Detail string +} + func init() { tabletconn.RegisterDialer("test", func(tablet *topodatapb.Tablet, failFast grpcclient.FailFast) (queryservice.QueryService, error) { return &fakeTabletConn{ @@ -391,6 +397,49 @@ func expectDeleteQueries(t *testing.T) { }) } +func listenToLogs() <-chan interface{} { + globalLogs = vrLogStatsLogger.Subscribe("vrlogstats") + return globalLogs +} + +func expectLogs(t *testing.T, logs []LogExpectation) { + t.Helper() + failed := false + for i, log := range logs { + if failed { + t.Errorf("no logs received") + continue + } + select { + case data := <-globalLogs: + got, ok := data.(*VrLogStats) + if !ok { + t.Errorf("got not ok casting to VrLogStats: %v", data) + } + var match bool + match = (log.Type == got.Type) + if match { + if log.Detail[0] == '/' { + result, err := regexp.MatchString(log.Detail[1:], got.Detail) + if err != nil { + panic(err) + } + match = result + } else { + match = (got.Detail == log.Detail) + } + } + + if !match { + t.Errorf("log:\n%q, does not match log %d:\n%q", got, i, log) + } + case <-time.After(5 * time.Second): + t.Errorf("no logs received, expecting %s", log) + failed = true + } + } +} + func expectDBClientQueries(t *testing.T, queries []string) { t.Helper() failed := false diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 1c89bc93e75..d9f5d342d9e 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -192,7 +192,7 @@ func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.Row } for _, change := range rowEvent.RowChanges { _, err := tplan.applyChange(change, func(sql string) (*sqltypes.Result, error) { - stats := Fr(ctx, "ROWCHANGE") + stats := NewVrLogStats(ctx, "ROWCHANGE") result, err := vp.vr.dbClient.ExecuteWithRetry(ctx, sql) stats.Record(sql) return result, err diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go index ab876697fa0..18aaa934aa8 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go @@ -173,6 +173,7 @@ func TestPlayerFilters(t *testing.T) { Filter: filter, OnDdl: binlogdatapb.OnDDLAction_IGNORE, } + cancel, _ := startVReplication(t, bls, "") defer cancel() @@ -181,6 +182,7 @@ func TestPlayerFilters(t *testing.T) { output []string table string data [][]string + logs []LogExpectation }{{ // insert with insertNormal input: "insert into src1 values(1, 'aaa')", @@ -194,6 +196,11 @@ func TestPlayerFilters(t *testing.T) { data: [][]string{ {"1", "aaa"}, }, + logs: []LogExpectation{ + {"FIELD", "/src1.*id.*INT32.*val.*VARBINARY.*"}, + {"ROWCHANGE", "insert into dst1(id,val) values (1,'aaa')"}, + {"ROW", "/src1.*3.*1aaa.*"}, + }, }, { // update with insertNormal input: "update src1 set val='bbb'", @@ -207,6 +214,10 @@ func TestPlayerFilters(t *testing.T) { data: [][]string{ {"1", "bbb"}, }, + logs: []LogExpectation{ + {"ROWCHANGE", "update dst1 set val='bbb' where id=1"}, + {"ROW", "/src1.*3.*1aaa.*"}, + }, }, { // delete with insertNormal input: "delete from src1 where id=1", @@ -218,6 +229,10 @@ func TestPlayerFilters(t *testing.T) { }, table: "dst1", data: [][]string{}, + logs: []LogExpectation{ + {"ROWCHANGE", "delete from dst1 where id=1"}, + {"ROW", "/src1.*3.*1bbb.*"}, + }, }, { // insert with insertOnDup input: "insert into src2 values(1, 2, 3)", @@ -231,6 +246,10 @@ func TestPlayerFilters(t *testing.T) { data: [][]string{ {"1", "2", "3", "1"}, }, + logs: []LogExpectation{ + {"FIELD", "/src2.*id.*val1.*val2.*"}, + {"ROWCHANGE", "insert into dst2(id,val1,sval2,rcount) values (1,2,ifnull(3, 0),1) on duplicate key update val1=values(val1), sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1"}, + }, }, { // update with insertOnDup input: "update src2 set val1=5, val2=1 where id=1", @@ -244,6 +263,10 @@ func TestPlayerFilters(t *testing.T) { data: [][]string{ {"1", "5", "1", "1"}, }, + logs: []LogExpectation{ + {"ROW", "/src2.*123.*"}, + {"ROWCHANGE", "update dst2 set val1=5, sval2=sval2-ifnull(3, 0)+ifnull(1, 0), rcount=rcount where id=1"}, + }, }, { // delete with insertOnDup input: "delete from src2 where id=1", @@ -365,9 +388,11 @@ func TestPlayerFilters(t *testing.T) { data: [][]string{}, }} + listenToLogs() for _, tcases := range testcases { execStatements(t, []string{tcases.input}) expectDBClientQueries(t, tcases.output) + expectLogs(t, tcases.logs) if tcases.table != "" { expectData(t, tcases.table, tcases.data) } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go index f754ffdc801..cda0a1d850d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go @@ -30,19 +30,22 @@ import ( var ( vrLogStatsLogger = streamlog.New("VReplication", 50) vrLogStatsTemplate = template.Must(template.New("vrlog"). - Parse("{{.Type}} Event {{.Detail}} {{.Time}} {{.DurationNs}}\n")) + Parse("{{.Type}} Event {{.Detail}} {{.LogTime}} {{.DurationNs}}\n")) ) type VrLogStats struct { Ctx context.Context Type string Detail string - Time time.Time + StartTime time.Time + LogTime string DurationNs int64 } func NewVrLogStats(ctx context.Context, eventType string) *VrLogStats { - return &VrLogStats{Ctx: ctx, Type: eventType, Time: time.Now()} + stats := &VrLogStats{Ctx: ctx, Type: eventType, StartTime: time.Now()} + stats.LogTime = stats.StartTime.Format("2006-01-02T15:04:05") + return stats } func (stats *VrLogStats) Send() { @@ -50,12 +53,12 @@ func (stats *VrLogStats) Send() { } func (stats *VrLogStats) Record(detail string) bool { - if stats.Ctx == nil || stats.Time.IsZero() { + if stats.Ctx == nil || stats.StartTime.IsZero() { log.Error("VrLogStats not initialized for %s", detail) return false } stats.Detail = detail - stats.DurationNs = time.Since(stats.Time).Nanoseconds() + stats.DurationNs = time.Since(stats.StartTime).Nanoseconds() stats.Send() return true } From bdae6b26c66eea60ca7adef53ffda9665b0ce89d Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sat, 4 Jan 2020 18:34:23 +0100 Subject: [PATCH 208/825] Code cleanup Signed-off-by: Rohit Nayak --- .../vreplication/framework_test.go | 3 +-- .../tabletmanager/vreplication/vplayer.go | 14 +++++++------- .../tabletmanager/vreplication/vrlog.go | 19 ++++++++++--------- .../tabletmanager/vreplication/vrlog_test.go | 11 +++++++---- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 6f23c36dd31..5c86ef3cf6c 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -397,9 +397,8 @@ func expectDeleteQueries(t *testing.T) { }) } -func listenToLogs() <-chan interface{} { +func listenToLogs() { globalLogs = vrLogStatsLogger.Subscribe("vrlogstats") - return globalLogs } func expectLogs(t *testing.T, logs []LogExpectation) { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index d9f5d342d9e..93dc3fd4fae 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -194,7 +194,7 @@ func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.Row _, err := tplan.applyChange(change, func(sql string) (*sqltypes.Result, error) { stats := NewVrLogStats(ctx, "ROWCHANGE") result, err := vp.vr.dbClient.ExecuteWithRetry(ctx, sql) - stats.Record(sql) + stats.Send(sql) return result, err }) if err != nil { @@ -356,7 +356,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m return err } vp.tablePlans[event.FieldEvent.TableName] = tplan - stats.Record(fmt.Sprintf("%v", event.FieldEvent)) + stats.Send(fmt.Sprintf("%v", event.FieldEvent)) case binlogdatapb.VEventType_INSERT, binlogdatapb.VEventType_DELETE, binlogdatapb.VEventType_UPDATE, binlogdatapb.VEventType_REPLACE: // This is a player using stament based replication @@ -367,7 +367,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if err := vp.applyStmtEvent(ctx, event); err != nil { return err } - stats.Record(fmt.Sprintf(event.Dml)) + stats.Send(fmt.Sprintf(event.Dml)) case binlogdatapb.VEventType_ROW: // This player is configured for row based replication if err := vp.vr.dbClient.Begin(); err != nil { @@ -376,7 +376,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if err := vp.applyRowEvent(ctx, event.RowEvent); err != nil { return err } - stats.Record(fmt.Sprintf("%v", event.RowEvent)) + stats.Send(fmt.Sprintf("%v", event.RowEvent)) case binlogdatapb.VEventType_OTHER: if vp.vr.dbClient.InTransaction { // Unreachable @@ -425,7 +425,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, event.Ddl); err != nil { return err } - stats.Record(fmt.Sprintf("%v", event.Ddl)) + stats.Send(fmt.Sprintf("%v", event.Ddl)) posReached, err := vp.updatePos(event.Timestamp) if err != nil { return err @@ -437,7 +437,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, event.Ddl); err != nil { log.Infof("Ignoring error: %v for DDL: %s", err, event.Ddl) } - stats.Record(fmt.Sprintf("%v", event.Ddl)) + stats.Send(fmt.Sprintf("%v", event.Ddl)) posReached, err := vp.updatePos(event.Timestamp) if err != nil { return err @@ -491,7 +491,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m } return io.EOF } - stats.Record(fmt.Sprintf("%v", event.Journal)) + stats.Send(fmt.Sprintf("%v", event.Journal)) return io.EOF case binlogdatapb.VEventType_HEARTBEAT: // No-op: heartbeat timings are calculated in outer loop. diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go index cda0a1d850d..9c38e4bd878 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go @@ -14,6 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +/* + * A human readable streaming log of vreplication events for vttablets available at /debug/vrlog + */ + package vreplication import ( @@ -33,6 +37,7 @@ var ( Parse("{{.Type}} Event {{.Detail}} {{.LogTime}} {{.DurationNs}}\n")) ) +//VrLogStats collects attributes of a vreplication event for logging type VrLogStats struct { Ctx context.Context Type string @@ -42,25 +47,21 @@ type VrLogStats struct { DurationNs int64 } +//NewVrLogStats should be called at the start of the event to be logged func NewVrLogStats(ctx context.Context, eventType string) *VrLogStats { stats := &VrLogStats{Ctx: ctx, Type: eventType, StartTime: time.Now()} stats.LogTime = stats.StartTime.Format("2006-01-02T15:04:05") return stats } -func (stats *VrLogStats) Send() { - vrLogStatsLogger.Send(stats) -} - -func (stats *VrLogStats) Record(detail string) bool { +//Send records the log event, should be called on a stats object constructed by NewVrLogStats() +func (stats *VrLogStats) Send(detail string) { if stats.Ctx == nil || stats.StartTime.IsZero() { - log.Error("VrLogStats not initialized for %s", detail) - return false + panic("VrLogStats not initialized " + detail) } stats.Detail = detail stats.DurationNs = time.Since(stats.StartTime).Nanoseconds() - stats.Send() - return true + vrLogStatsLogger.Send(stats) } func init() { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go index 0c23b089f2a..70bdf5563cf 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go @@ -40,7 +40,7 @@ func TestVrLog(t *testing.T) { eventType, detail := "Test", "detail 1" stats := NewVrLogStats(ctx, eventType) time.Sleep(1 * time.Millisecond) - stats.Record(detail) + stats.Send(detail) time.Sleep(1 * time.Millisecond) s := w.Body.String() want := fmt.Sprintf("%s Event %s", eventType, detail) @@ -63,8 +63,11 @@ func TestVrLog(t *testing.T) { if lastColValue < 1<<9 { t.Fatalf("Waited 1 Millisecond, so duration should be greater than that: %d, %s", lastColValue, ss[len(ss)-1]) } + defer func() { + if err := recover(); err == nil { + t.Fatalf("Uninitialized stats should not log") + } + }() stats = &VrLogStats{} - if stats.Record("should error out since stats is not initalized") != false { - t.Fatalf("Uninitialized stats should not log") - } + stats.Send("should error out since stats is not initalized") } From 7117000342de0fdf58b6c2199952fa1b459b7381 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Mon, 6 Jan 2020 16:39:09 +0100 Subject: [PATCH 209/825] Addressed review comments. Added http test class to handle races Signed-off-by: Rohit Nayak --- .../vreplication/framework_test.go | 10 +--- .../vreplication/http_stream_mock.go | 59 +++++++++++++++++++ .../tabletmanager/vreplication/vplayer.go | 5 +- .../vreplication/vplayer_test.go | 20 ++++--- .../tabletmanager/vreplication/vrlog.go | 15 ++--- .../tabletmanager/vreplication/vrlog_test.go | 53 +++++++++++------ 6 files changed, 117 insertions(+), 45 deletions(-) create mode 100644 go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 5c86ef3cf6c..c51a5881545 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -53,7 +53,6 @@ var ( globalFBC = &fakeBinlogClient{} vrepldb = "vrepl" globalDBQueries = make(chan string, 1000) - globalLogs = make(chan interface{}, 1000) ) type LogExpectation struct { @@ -397,11 +396,8 @@ func expectDeleteQueries(t *testing.T) { }) } -func listenToLogs() { - globalLogs = vrLogStatsLogger.Subscribe("vrlogstats") -} - -func expectLogs(t *testing.T, logs []LogExpectation) { +func expectLogsAndUnsubscribe(t *testing.T, logs []LogExpectation, logCh chan interface{}) { + defer vrLogStatsLogger.Unsubscribe(logCh) t.Helper() failed := false for i, log := range logs { @@ -410,7 +406,7 @@ func expectLogs(t *testing.T, logs []LogExpectation) { continue } select { - case data := <-globalLogs: + case data := <-logCh: got, ok := data.(*VrLogStats) if !ok { t.Errorf("got not ok casting to VrLogStats: %v", data) diff --git a/go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go b/go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go new file mode 100644 index 00000000000..8b640969e5d --- /dev/null +++ b/go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go @@ -0,0 +1,59 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + * To be used instead of HttpTest in cases where a test needs to do synchronized sequential reads are required from the + * http stream. Note that this handles only one write at a time: the test should read the written data before the next + * write. Data is sent to the channel only on a Flush. + */ + +package vreplication + +import ( + "net/http" +) + +//HTTPStreamWriterMock implements http.ResponseWriter and adds a channel to sync writes and reads +type HTTPStreamWriterMock struct { + ch chan interface{} + data []byte +} + +//NewHTTPStreamWriterMock returns a new HTTPStreamWriterMock +func NewHTTPStreamWriterMock() *HTTPStreamWriterMock { + return &HTTPStreamWriterMock{ch: make(chan interface{}, 1), data: make([]byte, 0)} +} + +//Header is a stub +func (w *HTTPStreamWriterMock) Header() http.Header { + return nil +} + +//WriteHeader is a stub +func (w *HTTPStreamWriterMock) WriteHeader(statuscode int) { +} + +//Write buffers sent data +func (w *HTTPStreamWriterMock) Write(data []byte) (int, error) { + w.data = append(w.data, data...) + return 0, nil +} + +//Flush sends buffered data to the channel +func (w *HTTPStreamWriterMock) Flush() { + w.ch <- w.data + w.data = w.data[:0] +} diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 93dc3fd4fae..85159a8bfb8 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -192,7 +192,7 @@ func (vp *vplayer) applyRowEvent(ctx context.Context, rowEvent *binlogdatapb.Row } for _, change := range rowEvent.RowChanges { _, err := tplan.applyChange(change, func(sql string) (*sqltypes.Result, error) { - stats := NewVrLogStats(ctx, "ROWCHANGE") + stats := NewVrLogStats("ROWCHANGE") result, err := vp.vr.dbClient.ExecuteWithRetry(ctx, sql) stats.Send(sql) return result, err @@ -310,7 +310,7 @@ func hasAnotherCommit(items [][]*binlogdatapb.VEvent, i, j int) bool { } func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, mustSave bool) error { - stats := NewVrLogStats(ctx, event.Type.String()) + stats := NewVrLogStats(event.Type.String()) switch event.Type { case binlogdatapb.VEventType_GTID: pos, err := mysql.DecodePosition(event.Gtid) @@ -376,6 +376,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m if err := vp.applyRowEvent(ctx, event.RowEvent); err != nil { return err } + //Row event is logged AFTER RowChanges are applied so as to calculate the total elapsed time for the Row event stats.Send(fmt.Sprintf("%v", event.RowEvent)) case binlogdatapb.VEventType_OTHER: if vp.vr.dbClient.InTransaction { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go index 18aaa934aa8..6bb77ecfa1c 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go @@ -182,7 +182,7 @@ func TestPlayerFilters(t *testing.T) { output []string table string data [][]string - logs []LogExpectation + logs []LogExpectation //logs defined only for a few testcases since they are enough to test all log events }{{ // insert with insertNormal input: "insert into src1 values(1, 'aaa')", @@ -264,8 +264,8 @@ func TestPlayerFilters(t *testing.T) { {"1", "5", "1", "1"}, }, logs: []LogExpectation{ - {"ROW", "/src2.*123.*"}, {"ROWCHANGE", "update dst2 set val1=5, sval2=sval2-ifnull(3, 0)+ifnull(1, 0), rcount=rcount where id=1"}, + {"ROW", "/src2.*123.*"}, }, }, { // delete with insertOnDup @@ -388,13 +388,15 @@ func TestPlayerFilters(t *testing.T) { data: [][]string{}, }} - listenToLogs() - for _, tcases := range testcases { - execStatements(t, []string{tcases.input}) - expectDBClientQueries(t, tcases.output) - expectLogs(t, tcases.logs) - if tcases.table != "" { - expectData(t, tcases.table, tcases.data) + for _, tcase := range testcases { + if tcase.logs != nil { + logch := vrLogStatsLogger.Subscribe("vrlogstats") + defer expectLogsAndUnsubscribe(t, tcase.logs, logch) + } + execStatements(t, []string{tcase.input}) + expectDBClientQueries(t, tcase.output) + if tcase.table != "" { + expectData(t, tcase.table, tcase.data) } } } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go index 9c38e4bd878..5fb75d6f7d2 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go @@ -26,7 +26,6 @@ import ( "text/template" "time" - "golang.org/x/net/context" "vitess.io/vitess/go/streamlog" "vitess.io/vitess/go/vt/log" ) @@ -39,7 +38,6 @@ var ( //VrLogStats collects attributes of a vreplication event for logging type VrLogStats struct { - Ctx context.Context Type string Detail string StartTime time.Time @@ -48,17 +46,17 @@ type VrLogStats struct { } //NewVrLogStats should be called at the start of the event to be logged -func NewVrLogStats(ctx context.Context, eventType string) *VrLogStats { - stats := &VrLogStats{Ctx: ctx, Type: eventType, StartTime: time.Now()} - stats.LogTime = stats.StartTime.Format("2006-01-02T15:04:05") - return stats +func NewVrLogStats(eventType string) *VrLogStats { + return &VrLogStats{Type: eventType, StartTime: time.Now()} } //Send records the log event, should be called on a stats object constructed by NewVrLogStats() func (stats *VrLogStats) Send(detail string) { - if stats.Ctx == nil || stats.StartTime.IsZero() { - panic("VrLogStats not initialized " + detail) + if stats.StartTime.IsZero() { + stats.Type = "Error: Type not specified" + stats.StartTime = time.Now() } + stats.LogTime = stats.StartTime.Format("2006-01-02T15:04:05") stats.Detail = detail stats.DurationNs = time.Since(stats.StartTime).Nanoseconds() vrLogStatsLogger.Send(stats) @@ -74,7 +72,6 @@ func init() { func vrlogStatsHandler(ch chan interface{}, w http.ResponseWriter, r *http.Request) { timeout, limit := parseTimeoutLimitParams(r) - tmr := time.NewTimer(timeout) defer tmr.Stop() for i := 0; i < limit; i++ { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go index 70bdf5563cf..76785ee72d7 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go @@ -17,10 +17,8 @@ limitations under the License. package vreplication import ( - "context" "fmt" "net/http" - "net/http/httptest" "strconv" "strings" "testing" @@ -28,21 +26,30 @@ import ( ) func TestVrLog(t *testing.T) { - r, _ := http.NewRequest("GET", "/debug/vrlog?timeout=10&limit=1", nil) - w := httptest.NewRecorder() + r, _ := http.NewRequest("GET", "/debug/vrlog?timeout=100&limit=10", nil) + //w := httptest.NewRecorder() - ch := vrLogStatsLogger.Subscribe("vrlog") + w := NewHTTPStreamWriterMock() + + ch := vrLogStatsLogger.Subscribe("vrlogstats") defer vrLogStatsLogger.Unsubscribe(ch) go func() { vrlogStatsHandler(ch, w, r) }() - ctx := context.Background() eventType, detail := "Test", "detail 1" - stats := NewVrLogStats(ctx, eventType) - time.Sleep(1 * time.Millisecond) + stats := NewVrLogStats(eventType) stats.Send(detail) - time.Sleep(1 * time.Millisecond) - s := w.Body.String() + var s string + select { + case ret := <-w.ch: + b, ok := ret.([]byte) + if ok { + s = string(b) + } + case <-time.After(1 * time.Second): + s = "Timed out" + } + want := fmt.Sprintf("%s Event %s", eventType, detail) if !strings.Contains(s, want) { t.Fatalf(fmt.Sprintf("want %s, got %s", want, s)) @@ -60,14 +67,24 @@ func TestVrLog(t *testing.T) { if err != nil { t.Fatalf("Duration is not an integer: %s", err) } - if lastColValue < 1<<9 { - t.Fatalf("Waited 1 Millisecond, so duration should be greater than that: %d, %s", lastColValue, ss[len(ss)-1]) + if lastColValue == 0 { + t.Fatalf("Duration should not be zero") } - defer func() { - if err := recover(); err == nil { - t.Fatalf("Uninitialized stats should not log") - } - }() + stats = &VrLogStats{} - stats.Send("should error out since stats is not initalized") + stats.Send("detail123") + + select { + case ret := <-w.ch: + b, ok := ret.([]byte) + if ok { + s = string(b) + } + case <-time.After(1 * time.Second): + s = "Timed out" + } + prefix := "Error: Type not specified" + if !strings.HasPrefix(s, prefix) { + t.Fatalf("Incorrect Type for uninitialized stat, got %v", s) + } } From f174a555bd7979a89fb507779c536855eee509ef Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Mon, 6 Jan 2020 21:13:36 +0100 Subject: [PATCH 210/825] Addressed review comments Signed-off-by: Rohit Nayak --- .../tabletmanager/vreplication/framework_test.go | 2 +- .../tabletmanager/vreplication/http_stream_mock.go | 12 ++++++------ .../tabletmanager/vreplication/vplayer_test.go | 2 +- go/vt/vttablet/tabletmanager/vreplication/vrlog.go | 6 +++--- .../tabletmanager/vreplication/vrlog_test.go | 2 -- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index c51a5881545..4cbcc35ea5d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -397,8 +397,8 @@ func expectDeleteQueries(t *testing.T) { } func expectLogsAndUnsubscribe(t *testing.T, logs []LogExpectation, logCh chan interface{}) { - defer vrLogStatsLogger.Unsubscribe(logCh) t.Helper() + defer vrLogStatsLogger.Unsubscribe(logCh) failed := false for i, log := range logs { if failed { diff --git a/go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go b/go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go index 8b640969e5d..ab9d9f3e220 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go +++ b/go/vt/vttablet/tabletmanager/vreplication/http_stream_mock.go @@ -26,33 +26,33 @@ import ( "net/http" ) -//HTTPStreamWriterMock implements http.ResponseWriter and adds a channel to sync writes and reads +// HTTPStreamWriterMock implements http.ResponseWriter and adds a channel to sync writes and reads type HTTPStreamWriterMock struct { ch chan interface{} data []byte } -//NewHTTPStreamWriterMock returns a new HTTPStreamWriterMock +// NewHTTPStreamWriterMock returns a new HTTPStreamWriterMock func NewHTTPStreamWriterMock() *HTTPStreamWriterMock { return &HTTPStreamWriterMock{ch: make(chan interface{}, 1), data: make([]byte, 0)} } -//Header is a stub +// Header is a stub func (w *HTTPStreamWriterMock) Header() http.Header { return nil } -//WriteHeader is a stub +// WriteHeader is a stub func (w *HTTPStreamWriterMock) WriteHeader(statuscode int) { } -//Write buffers sent data +// Write buffers sent data func (w *HTTPStreamWriterMock) Write(data []byte) (int, error) { w.data = append(w.data, data...) return 0, nil } -//Flush sends buffered data to the channel +// Flush sends buffered data to the channel func (w *HTTPStreamWriterMock) Flush() { w.ch <- w.data w.data = w.data[:0] diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go index 6bb77ecfa1c..dd95e70912a 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_test.go @@ -182,7 +182,7 @@ func TestPlayerFilters(t *testing.T) { output []string table string data [][]string - logs []LogExpectation //logs defined only for a few testcases since they are enough to test all log events + logs []LogExpectation // logs are defined for a few testcases since they are enough to test all log events }{{ // insert with insertNormal input: "insert into src1 values(1, 'aaa')", diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go index 5fb75d6f7d2..1f1bcac9599 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog.go @@ -36,7 +36,7 @@ var ( Parse("{{.Type}} Event {{.Detail}} {{.LogTime}} {{.DurationNs}}\n")) ) -//VrLogStats collects attributes of a vreplication event for logging +// VrLogStats collects attributes of a vreplication event for logging type VrLogStats struct { Type string Detail string @@ -45,12 +45,12 @@ type VrLogStats struct { DurationNs int64 } -//NewVrLogStats should be called at the start of the event to be logged +// NewVrLogStats should be called at the start of the event to be logged func NewVrLogStats(eventType string) *VrLogStats { return &VrLogStats{Type: eventType, StartTime: time.Now()} } -//Send records the log event, should be called on a stats object constructed by NewVrLogStats() +// Send records the log event, should be called on a stats object constructed by NewVrLogStats() func (stats *VrLogStats) Send(detail string) { if stats.StartTime.IsZero() { stats.Type = "Error: Type not specified" diff --git a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go index 76785ee72d7..3bd0dcf3217 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vrlog_test.go @@ -27,8 +27,6 @@ import ( func TestVrLog(t *testing.T) { r, _ := http.NewRequest("GET", "/debug/vrlog?timeout=100&limit=10", nil) - //w := httptest.NewRecorder() - w := NewHTTPStreamWriterMock() ch := vrLogStatsLogger.Subscribe("vrlogstats") From d9d0ad6c94a9484fa9cd1428435fc7d48db9bfe0 Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Wed, 8 Jan 2020 20:55:48 -0800 Subject: [PATCH 211/825] Adding Azure Blob backup support This adds initial support for Azure blob service as a backup target Signed-off-by: Dan Kozlowski --- go.mod | 4 + go.sum | 6 + go/cmd/vtbackup/plugin_azblobbackupstorage.go | 21 ++ go/cmd/vtctl/plugin_azblobbackupstorage.go | 21 ++ go/cmd/vtctld/plugin_azblobbackupstorage.go | 21 ++ go/cmd/vttablet/plugin_azblobbackupstorage.go | 21 ++ go/vt/mysqlctl/azblobbackupstorage/azblob.go | 297 ++++++++++++++++++ 7 files changed, 391 insertions(+) create mode 100644 go/cmd/vtbackup/plugin_azblobbackupstorage.go create mode 100644 go/cmd/vtctl/plugin_azblobbackupstorage.go create mode 100644 go/cmd/vtctld/plugin_azblobbackupstorage.go create mode 100644 go/cmd/vttablet/plugin_azblobbackupstorage.go create mode 100644 go/vt/mysqlctl/azblobbackupstorage/azblob.go diff --git a/go.mod b/go.mod index c1252dca071..9286d41cfeb 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( cloud.google.com/go v0.45.1 + github.com/Azure/azure-storage-blob-go v0.8.0 github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 // indirect github.com/GeertJohan/go.rice v1.0.0 github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect @@ -47,6 +48,9 @@ require ( github.com/mattn/go-isatty v0.0.11 // indirect github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/go.sum b/go.sum index d5373e2df2e..5986a3f3719 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,10 @@ github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFEY95rZLK0Tj6o= +github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM= github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= @@ -437,6 +441,8 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149 h1:HfxbT6/JcvIljmERptWhwa8XzP7H3T+Z2N26gTsaDaA= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= diff --git a/go/cmd/vtbackup/plugin_azblobbackupstorage.go b/go/cmd/vtbackup/plugin_azblobbackupstorage.go new file mode 100644 index 00000000000..a4ca64096a9 --- /dev/null +++ b/go/cmd/vtbackup/plugin_azblobbackupstorage.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + _ "vitess.io/vitess/go/vt/mysqlctl/azblobbackupstorage" +) diff --git a/go/cmd/vtctl/plugin_azblobbackupstorage.go b/go/cmd/vtctl/plugin_azblobbackupstorage.go new file mode 100644 index 00000000000..a4ca64096a9 --- /dev/null +++ b/go/cmd/vtctl/plugin_azblobbackupstorage.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + _ "vitess.io/vitess/go/vt/mysqlctl/azblobbackupstorage" +) diff --git a/go/cmd/vtctld/plugin_azblobbackupstorage.go b/go/cmd/vtctld/plugin_azblobbackupstorage.go new file mode 100644 index 00000000000..a4ca64096a9 --- /dev/null +++ b/go/cmd/vtctld/plugin_azblobbackupstorage.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + _ "vitess.io/vitess/go/vt/mysqlctl/azblobbackupstorage" +) diff --git a/go/cmd/vttablet/plugin_azblobbackupstorage.go b/go/cmd/vttablet/plugin_azblobbackupstorage.go new file mode 100644 index 00000000000..a4ca64096a9 --- /dev/null +++ b/go/cmd/vttablet/plugin_azblobbackupstorage.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + _ "vitess.io/vitess/go/vt/mysqlctl/azblobbackupstorage" +) diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go new file mode 100644 index 00000000000..b829b8605f5 --- /dev/null +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -0,0 +1,297 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package azblobbackupstorage implements the BackupStorage interface +// for Azure Blob Storage +package azblobbackupstorage + +import ( + "context" + "flag" + "fmt" + "io" + "net/url" + "strings" + "sync" + "time" + + "github.com/Azure/azure-storage-blob-go/azblob" + "vitess.io/vitess/go/vt/concurrency" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/mysqlctl/backupstorage" +) + +var ( + // This is the account name + accountName = flag.String("azblob_backup_account_name", "", "Azure Account Name for Backups") + + // This is the private access key + accountKey = flag.String("azblob_backup_account_key", "", "Azure Account Key") + + // This is the name of the container that will store the backups + containerName = flag.String("azblob_backup_contain_name", "", "Azure Blob Contain Name") + + // This is an optional previx to prepend to all files + prefix = flag.String("azblob_backup_prefix", "", "Azure Blob prefix") + + azBlobParallelism = flag.Int("azblob_parallelism", 1, "Azure blob operation parallelism (requires extra memory when increased)") +) + +const ( + defaultRetryCount = 5 + delimiter = "/" +) + +func azCredentials() (*azblob.SharedKeyCredential, error) { + return azblob.NewSharedKeyCredential(*accountName, *accountKey) +} + +func azServiceURL(credentials azblob.Credential) azblob.ServiceURL { + pipeline := azblob.NewPipeline(credentials, azblob.PipelineOptions{ + Retry: azblob.RetryOptions{ + Policy: azblob.RetryPolicyFixed, + MaxTries: defaultRetryCount, + // Per https://godoc.org/github.com/Azure/azure-storage-blob-go/azblob#RetryOptions + // This shuld be set to a very nigh number ( they claim 60s per MB ). That could end up being days so we are limiting this to four hours + TryTimeout: 4 * time.Hour, + }, + }) + u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/", *accountName)) + return azblob.NewServiceURL(*u, pipeline) +} + +// AZBlobBackupHandle implements BackupHandle for Azure Blob service. +type AZBlobBackupHandle struct { + bs *AZBlobBackupStorage + dir string + name string + readOnly bool + waitGroup sync.WaitGroup + errors concurrency.AllErrorRecorder +} + +// Directory implements BackupHandle. +func (bh *AZBlobBackupHandle) Directory() string { + return bh.dir +} + +// Name implements BackupHandle. +func (bh *AZBlobBackupHandle) Name() string { + return bh.name +} + +// AddFile implements BackupHandle. +func (bh *AZBlobBackupHandle) AddFile(ctx context.Context, filename string, filesize int64) (io.WriteCloser, error) { + if bh.readOnly { + return nil, fmt.Errorf("AddFile cannot be called on read-only backup") + } + // Error out if the file size it too large ( ~4.75 TB) + if filesize > azblob.BlockBlobMaxStageBlockBytes*azblob.BlockBlobMaxBlocks { + return nil, fmt.Errorf("filesize is too large to upload to az blob (max size %v)", azblob.BlockBlobMaxStageBlockBytes*azblob.BlockBlobMaxBlocks) + } + + obj := objName(bh.dir, bh.name, filename) + containerURL, err := bh.bs.containerURL() + if err != nil { + return nil, err + } + + blockBlobURL := containerURL.NewBlockBlobURL(obj) + + reader, writer := io.Pipe() + bh.waitGroup.Add(1) + + go func() { + defer bh.waitGroup.Done() + _, err := azblob.UploadStreamToBlockBlob(ctx, reader, blockBlobURL, azblob.UploadStreamToBlockBlobOptions{ + BufferSize: azblob.BlockBlobMaxStageBlockBytes, + MaxBuffers: *azBlobParallelism, + }) + if err != nil { + reader.CloseWithError(err) + bh.errors.RecordError(err) + } + }() + + return writer, nil +} + +// EndBackup implements BackupHandle. +func (bh *AZBlobBackupHandle) EndBackup(ctx context.Context) error { + if bh.readOnly { + return fmt.Errorf("EndBackup cannot be called on read-only backup") + } + bh.waitGroup.Wait() + return bh.errors.Error() +} + +// AbortBackup implements BackupHandle. +func (bh *AZBlobBackupHandle) AbortBackup(ctx context.Context) error { + if bh.readOnly { + return fmt.Errorf("AbortBackup cannot be called on read-only backup") + } + return bh.bs.RemoveBackup(ctx, bh.dir, bh.name) +} + +// ReadFile implements BackupHandle. +func (bh *AZBlobBackupHandle) ReadFile(ctx context.Context, filename string) (io.ReadCloser, error) { + if !bh.readOnly { + return nil, fmt.Errorf("ReadFile cannot be called on read-write backup") + } + + obj := objName(bh.dir, bh.name, filename) + containerURL, err := bh.bs.containerURL() + if err != nil { + return nil, err + } + blobURL := containerURL.NewBlobURL(obj) + + resp, err := blobURL.Download(ctx, 0, azblob.CountToEnd, azblob.BlobAccessConditions{}, false) + if err != nil { + return nil, err + } + return resp.Body(azblob.RetryReaderOptions{ + MaxRetryRequests: defaultRetryCount, + NotifyFailedRead: func(failureCount int, lastError error, offset int64, count int64, willRetry bool) { + bh.errors.RecordError(lastError) + }, + TreatEarlyCloseAsError: true, + }), nil +} + +// AZBlobBackupStorage structs implements the BackupStorage interface for AZBlob +type AZBlobBackupStorage struct { +} + +func (bs *AZBlobBackupStorage) containerURL() (*azblob.ContainerURL, error) { + credentials, err := azCredentials() + if err != nil { + return nil, err + } + u := azServiceURL(credentials).NewContainerURL(*containerName) + return &u, nil +} + +// ListBackups implements BackupStorage. +func (bs *AZBlobBackupStorage) ListBackups(ctx context.Context, dir string) ([]backupstorage.BackupHandle, error) { + log.Infof("ListBackups: [azblob] container: %v, prefix: %v", *containerName, dir) + + containerURL, err := bs.containerURL() + if err != nil { + return nil, err + } + + searchPrefix := objName(dir, "") + + result := make([]backupstorage.BackupHandle, 0) + var subdirs []string + + for marker := (azblob.Marker{}); marker.NotDone(); { + resp, err := containerURL.ListBlobsHierarchySegment(ctx, marker, delimiter, azblob.ListBlobsSegmentOptions{ + Prefix: searchPrefix, + MaxResults: 0, + }) + + if err != nil { + return nil, err + } + + for _, item := range resp.Segment.BlobPrefixes { + subdir := strings.TrimPrefix(item.Name, searchPrefix) + subdir = strings.TrimSuffix(subdir, delimiter) + subdirs = append(subdirs, subdir) + } + + marker = resp.NextMarker + } + + for _, subdir := range subdirs { + result = append(result, &AZBlobBackupHandle{ + bs: bs, + dir: dir, + name: subdir, + readOnly: true, + }) + } + + return result, nil +} + +// StartBackup implements BackupStorage. +func (bs *AZBlobBackupStorage) StartBackup(ctx context.Context, dir, name string) (backupstorage.BackupHandle, error) { + return &AZBlobBackupHandle{ + bs: bs, + dir: dir, + name: name, + readOnly: false, + }, nil +} + +// RemoveBackup implements BackupStorage. +func (bs *AZBlobBackupStorage) RemoveBackup(ctx context.Context, dir, name string) error { + log.Infof("ListBackups: [azblob] container: %v, prefix: %v", *containerName, dir) + + containerURL, err := bs.containerURL() + if err != nil { + return err + } + + searchPrefix := objName(dir, name, "") + + for marker := (azblob.Marker{}); marker.NotDone(); { + resp, err := containerURL.ListBlobsHierarchySegment(ctx, marker, delimiter, azblob.ListBlobsSegmentOptions{ + Prefix: searchPrefix, + MaxResults: 0, + }) + + if err != nil { + return err + } + + // Right now there is no batch delete so we must iterate over all the blobs to delete them one by one + // One day we will be able to use this https://docs.microsoft.com/en-us/rest/api/storageservices/blob-batch + // but currently it is listed as a preview and its not in the go API + for _, item := range resp.Segment.BlobItems { + _, err = containerURL.NewBlobURL(item.Name).Delete(ctx, azblob.DeleteSnapshotsOptionNone, azblob.BlobAccessConditions{}) + if err != nil { + return err + } + } + marker = resp.NextMarker + } + + return nil +} + +// Close implements BackupStorage. +func (bs *AZBlobBackupStorage) Close() error { + // This function is a No-op + return nil +} + +// objName joins path parts into an object name. +// Unlike path.Join, it doesn't collapse ".." or strip trailing slashes. +// It also adds the value of the -gcs_backup_storage_root flag if set. +func objName(parts ...string) string { + if *prefix != "" { + return *prefix + "/" + strings.Join(parts, "/") + } + return strings.Join(parts, "/") +} + +func init() { + backupstorage.BackupStorageMap["azblob"] = &AZBlobBackupStorage{} +} From 7c8481895561312edfd3610ad77c0ee8c854700e Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Fri, 10 Jan 2020 11:32:12 -0800 Subject: [PATCH 212/825] Incorporating feedback from pull request. Giving Env paramaters for secrets, not just CLI. Rename prefix to be storage_root i.a.w what we do for s3 and gcs Signed-off-by: Dan Kozlowski --- go.mod | 3 +- go.sum | 18 +++++++- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 47 +++++++++++++++----- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 9286d41cfeb..fa25d9ffc0a 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.12 require ( cloud.google.com/go v0.45.1 github.com/Azure/azure-storage-blob-go v0.8.0 + github.com/Azure/go-autorest/autorest/adal v0.8.1 // indirect github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 // indirect github.com/GeertJohan/go.rice v1.0.0 github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect @@ -79,7 +80,7 @@ require ( github.com/ugorji/go v1.1.7 // indirect github.com/uudashr/gocognit v1.0.1 // indirect github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b - golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd + golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 golang.org/x/lint v0.0.0-20190409202823-959b441ac422 golang.org/x/net v0.0.0-20190926025831-c00fd9afed17 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 diff --git a/go.sum b/go.sum index 5986a3f3719..483eb2a9eee 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,20 @@ github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZ github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFEY95rZLK0Tj6o= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= +github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.1 h1:pZdL8o72rK+avFWl+p9nE8RWi1JInZrWJYlnpfXJwHk= +github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 h1:7tNlRGC3pUEPKS3DwgX5L0s+cBloaq/JBoi9ceN1MCM= github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= @@ -697,8 +711,8 @@ golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= -golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index b829b8605f5..4567fe823c1 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "net/url" + "os" "strings" "sync" "time" @@ -36,18 +37,18 @@ import ( var ( // This is the account name - accountName = flag.String("azblob_backup_account_name", "", "Azure Account Name for Backups") + accountName = flag.String("azblob_backup_account_name", "", "Azure Account Name for Backups, alternative environment paramater is VITESS_AZBLOB_ACCOUNT_NAME") // This is the private access key - accountKey = flag.String("azblob_backup_account_key", "", "Azure Account Key") + accountKey = flag.String("azblob_backup_account_key", "", "Azure Account Key, alternative environment paramater is VITESS_AZBLOB_ACCOUNT_KEY") // This is the name of the container that will store the backups - containerName = flag.String("azblob_backup_contain_name", "", "Azure Blob Contain Name") + containerName = flag.String("azblob_backup_container_name", "", "Azure Blob Container Name") // This is an optional previx to prepend to all files - prefix = flag.String("azblob_backup_prefix", "", "Azure Blob prefix") + storageRoot = flag.String("azblob_backup_storage_root", "", "Azure Blob storage root") - azBlobParallelism = flag.Int("azblob_parallelism", 1, "Azure blob operation parallelism (requires extra memory when increased)") + azBlobParallelism = flag.Int("azblob_backup_parallelism", 1, "Azure blob operation parallelism (requires extra memory when increased)") ) const ( @@ -55,7 +56,25 @@ const ( delimiter = "/" ) +// Return a Shared credential from the available credential sources. +// We will use credentials in the following order +// 1. Direct Command Line Flag (azblob_backup_account_name, azblob_backup_account_key) +// 2. Environment Paramaters func azCredentials() (*azblob.SharedKeyCredential, error) { + actName := *accountName + if len(actName) == 0 { + // Check the Environmental Value + actName = os.Getenv("VITESS_AZBLOB_ACCOUNT_NAME") + } + + actKey := *accountKey + if len(actKey) == 0 { + actKey = os.Getenv("VITESS_AZBLOB_ACCOUNT_NAME") + } + + if len(actName) == 0 || len(actKey) == 0 { + return nil, fmt.Errorf("can not get Account Credentials from CLI or Environment paramaters") + } return azblob.NewSharedKeyCredential(*accountName, *accountKey) } @@ -152,7 +171,7 @@ func (bh *AZBlobBackupHandle) ReadFile(ctx context.Context, filename string) (io return nil, fmt.Errorf("ReadFile cannot be called on read-write backup") } - obj := objName(bh.dir, bh.name, filename) + obj := objName(bh.dir, filename) containerURL, err := bh.bs.containerURL() if err != nil { return nil, err @@ -166,7 +185,10 @@ func (bh *AZBlobBackupHandle) ReadFile(ctx context.Context, filename string) (io return resp.Body(azblob.RetryReaderOptions{ MaxRetryRequests: defaultRetryCount, NotifyFailedRead: func(failureCount int, lastError error, offset int64, count int64, willRetry bool) { - bh.errors.RecordError(lastError) + log.Infof("ReadFile: [azblob] container: %s, directory: %s, filename: %s, error: %v", *containerName, objName(bh.dir, ""), filename, lastError) + if !willRetry { + bh.errors.RecordError(lastError) + } }, TreatEarlyCloseAsError: true, }), nil @@ -187,7 +209,7 @@ func (bs *AZBlobBackupStorage) containerURL() (*azblob.ContainerURL, error) { // ListBackups implements BackupStorage. func (bs *AZBlobBackupStorage) ListBackups(ctx context.Context, dir string) ([]backupstorage.BackupHandle, error) { - log.Infof("ListBackups: [azblob] container: %v, prefix: %v", *containerName, dir) + log.Infof("ListBackups: [azblob] container: %s, directory: %v", *containerName, objName(dir, "")) containerURL, err := bs.containerURL() if err != nil { @@ -200,6 +222,7 @@ func (bs *AZBlobBackupStorage) ListBackups(ctx context.Context, dir string) ([]b var subdirs []string for marker := (azblob.Marker{}); marker.NotDone(); { + // This returns Blobs in sorted order so we don't need to sort them a second time resp, err := containerURL.ListBlobsHierarchySegment(ctx, marker, delimiter, azblob.ListBlobsSegmentOptions{ Prefix: searchPrefix, MaxResults: 0, @@ -242,7 +265,7 @@ func (bs *AZBlobBackupStorage) StartBackup(ctx context.Context, dir, name string // RemoveBackup implements BackupStorage. func (bs *AZBlobBackupStorage) RemoveBackup(ctx context.Context, dir, name string) error { - log.Infof("ListBackups: [azblob] container: %v, prefix: %v", *containerName, dir) + log.Infof("ListBackups: [azblob] container: %s, directory: %s", *containerName, objName(dir, "")) containerURL, err := bs.containerURL() if err != nil { @@ -284,10 +307,10 @@ func (bs *AZBlobBackupStorage) Close() error { // objName joins path parts into an object name. // Unlike path.Join, it doesn't collapse ".." or strip trailing slashes. -// It also adds the value of the -gcs_backup_storage_root flag if set. +// It also adds the value of the -azblob_backup_storage_root flag if set. func objName(parts ...string) string { - if *prefix != "" { - return *prefix + "/" + strings.Join(parts, "/") + if *storageRoot != "" { + return *storageRoot + "/" + strings.Join(parts, "/") } return strings.Join(parts, "/") } From aae83c4f26942f8c21e42e822e5af79250ba2c53 Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Wed, 4 Mar 2020 14:54:30 -0800 Subject: [PATCH 213/825] Fixing directory paths for azure blobs Signed-off-by: Dan Kozlowski --- go.mod | 3 --- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/go.mod b/go.mod index fa25d9ffc0a..fd56022ed1b 100644 --- a/go.mod +++ b/go.mod @@ -49,9 +49,6 @@ require ( github.com/mattn/go-isatty v0.0.11 // indirect github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 - github.com/mitchellh/go-testing-interface v1.0.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index 4567fe823c1..fd6a7fe856b 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -244,7 +244,7 @@ func (bs *AZBlobBackupStorage) ListBackups(ctx context.Context, dir string) ([]b for _, subdir := range subdirs { result = append(result, &AZBlobBackupHandle{ bs: bs, - dir: dir, + dir: strings.Join([]string{dir, subdir}, "/"), name: subdir, readOnly: true, }) From 1eda630000d56e9ddc25b9f3bbee8085f354648b Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Wed, 4 Mar 2020 15:03:45 -0800 Subject: [PATCH 214/825] Text changes and fixes from PR Signed-off-by: Dan Kozlowski --- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index fd6a7fe856b..460f18bd601 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -37,10 +37,10 @@ import ( var ( // This is the account name - accountName = flag.String("azblob_backup_account_name", "", "Azure Account Name for Backups, alternative environment paramater is VITESS_AZBLOB_ACCOUNT_NAME") + accountName = flag.String("azblob_backup_account_name", "", "Azure Storage account name for backups, if this flag is unset the environment paramater VITESS_AZBLOB_ACCOUNT_NAME will be used") // This is the private access key - accountKey = flag.String("azblob_backup_account_key", "", "Azure Account Key, alternative environment paramater is VITESS_AZBLOB_ACCOUNT_KEY") + accountKey = flag.String("azblob_backup_account_key", "", "Azure Storage account key,if this flag is unset the environment paramater VITESS_AZBLOB_ACCOUNT_KEY will be used") // This is the name of the container that will store the backups containerName = flag.String("azblob_backup_container_name", "", "Azure Blob Container Name") @@ -59,7 +59,7 @@ const ( // Return a Shared credential from the available credential sources. // We will use credentials in the following order // 1. Direct Command Line Flag (azblob_backup_account_name, azblob_backup_account_key) -// 2. Environment Paramaters +// 2. Environment variables func azCredentials() (*azblob.SharedKeyCredential, error) { actName := *accountName if len(actName) == 0 { @@ -69,11 +69,11 @@ func azCredentials() (*azblob.SharedKeyCredential, error) { actKey := *accountKey if len(actKey) == 0 { - actKey = os.Getenv("VITESS_AZBLOB_ACCOUNT_NAME") + actKey = os.Getenv("VITESS_AZBLOB_ACCOUNT_KEY") } if len(actName) == 0 || len(actKey) == 0 { - return nil, fmt.Errorf("can not get Account Credentials from CLI or Environment paramaters") + return nil, fmt.Errorf("can not get Azure Storage account credentials from CLI or Environment variables") } return azblob.NewSharedKeyCredential(*accountName, *accountKey) } @@ -185,10 +185,7 @@ func (bh *AZBlobBackupHandle) ReadFile(ctx context.Context, filename string) (io return resp.Body(azblob.RetryReaderOptions{ MaxRetryRequests: defaultRetryCount, NotifyFailedRead: func(failureCount int, lastError error, offset int64, count int64, willRetry bool) { - log.Infof("ReadFile: [azblob] container: %s, directory: %s, filename: %s, error: %v", *containerName, objName(bh.dir, ""), filename, lastError) - if !willRetry { - bh.errors.RecordError(lastError) - } + log.Warningf("ReadFile: [azblob] container: %s, directory: %s, filename: %s, error: %v", *containerName, objName(bh.dir, ""), filename, lastError) }, TreatEarlyCloseAsError: true, }), nil From 56e018e66cb6e6b0513378043d4cd918e06e575d Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Wed, 4 Mar 2020 18:53:24 -0800 Subject: [PATCH 215/825] Moving accuont key to a file Signed-off-by: Dan Kozlowski --- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index 460f18bd601..71365906b31 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -23,6 +23,7 @@ import ( "flag" "fmt" "io" + "io/ioutil" "net/url" "os" "strings" @@ -40,7 +41,7 @@ var ( accountName = flag.String("azblob_backup_account_name", "", "Azure Storage account name for backups, if this flag is unset the environment paramater VITESS_AZBLOB_ACCOUNT_NAME will be used") // This is the private access key - accountKey = flag.String("azblob_backup_account_key", "", "Azure Storage account key,if this flag is unset the environment paramater VITESS_AZBLOB_ACCOUNT_KEY will be used") + accountKeyFile = flag.String("azblob_backup_account_key_file", "", "A file containing the Azure Storage account key,if this flag is unset the environment paramater VITESS_AZBLOB_ACCOUNT_KEY will be used") // This is the name of the container that will store the backups containerName = flag.String("azblob_backup_container_name", "", "Azure Blob Container Name") @@ -67,15 +68,19 @@ func azCredentials() (*azblob.SharedKeyCredential, error) { actName = os.Getenv("VITESS_AZBLOB_ACCOUNT_NAME") } - actKey := *accountKey - if len(actKey) == 0 { + var actKey string + if len(*accountKeyFile) > 0 { + if dat, err := ioutil.ReadFile(*accountKeyFile); err != nil { + actKey = string(dat) + } + } else { actKey = os.Getenv("VITESS_AZBLOB_ACCOUNT_KEY") } if len(actName) == 0 || len(actKey) == 0 { return nil, fmt.Errorf("can not get Azure Storage account credentials from CLI or Environment variables") } - return azblob.NewSharedKeyCredential(*accountName, *accountKey) + return azblob.NewSharedKeyCredential(actName, actKey) } func azServiceURL(credentials azblob.Credential) azblob.ServiceURL { From a0ce126a2dd114e62273c4ea8de245bfa7bdf8bd Mon Sep 17 00:00:00 2001 From: Ajeet jain Date: Thu, 27 Feb 2020 11:55:47 +0530 Subject: [PATCH 216/825] config.json cleanup Signed-off-by: Ajeet jain Signed-off-by: Arindam Nayak --- .github/workflows/check_make_visitor.yml | 35 ++++++++++ .github/workflows/java_test.yml | 35 ++++++++++ go/test/endtoend/README.md | 28 +++----- test/config.json | 83 ------------------------ 4 files changed, 80 insertions(+), 101 deletions(-) create mode 100644 .github/workflows/check_make_visitor.yml create mode 100644 .github/workflows/java_test.yml diff --git a/.github/workflows/check_make_visitor.yml b/.github/workflows/check_make_visitor.yml new file mode 100644 index 00000000000..5bc60788c9b --- /dev/null +++ b/.github/workflows/check_make_visitor.yml @@ -0,0 +1,35 @@ +name: check_make_visitor +on: [push, pull_request] +jobs: + + build: + name: Check Make Visitor + runs-on: ubuntu-latest + steps: + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.13 + + - name: Check out code + uses: actions/checkout@v2 + + - name: Get dependencies + run: | + sudo apt-get update + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget + sudo service mysql stop + sudo service etcd stop + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld + go mod download + + - name: Run make minimaltools + run: | + make minimaltools + + - name: check_make_parser + run: | + misc/git/hooks/visitorgen + diff --git a/.github/workflows/java_test.yml b/.github/workflows/java_test.yml new file mode 100644 index 00000000000..3de66662c4b --- /dev/null +++ b/.github/workflows/java_test.yml @@ -0,0 +1,35 @@ +name: java_test +on: [push, pull_request] +jobs: + + build: + name: Java Test + runs-on: ubuntu-latest + steps: + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.13 + + - name: Check out code + uses: actions/checkout@v2 + + - name: Get dependencies + run: | + sudo apt-get update + sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget + sudo service mysql stop + sudo service etcd stop + sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ + sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld + go mod download + + - name: Run make minimaltools + run: | + make minimaltools + + - name: check_make_parser + run: | + make java_test + diff --git a/go/test/endtoend/README.md b/go/test/endtoend/README.md index 9502e567264..7bd71f9bfef 100644 --- a/go/test/endtoend/README.md +++ b/go/test/endtoend/README.md @@ -15,6 +15,7 @@ These tests almost always launch a topology service, a few mysqld instances, a f All the end to end test are placed under path go/test/endtoend. The main purpose of grouping them together is to make sure we have single place for reference and to combine similar test to run them in the same cluster and save test running time. + ### Setup All the tests should be launching a real cluster just like the production setup and execute the tests on that setup followed by a teardown of all the services. @@ -31,22 +32,13 @@ In general the cluster is build in following order A good example to refer will be go/test/endtoend/clustertest -## Progress -So far we have converted the following Python end to end test cases -- Keyspace tests -- mysqlctl tests -- sharded tests -- tabletmanager tests -- vtgate v3 tests -- Inital sharding -- resharding -- vsplit -- reparent - -### In-progress -- Backup -- Encryption - -After a Python test is migrated in Go it will be removed from end to end ci test run by updating the shard value to 5 in `test/config.json` +### Pre-Requisite +Make sure you have vitess binary available in bin folder. If not, then you can run `./bootstrap.sh` follow by `make build` & `source build.env`. - +To make it easier to re-run test please add following to you bash profile. + +``` +export VTROOT=//vitess +export VTDATAROOT=${VTROOT}/vtdataroot +export PATH=${VTROOT}/bin:${PATH} +``` diff --git a/test/config.json b/test/config.json index 3e6278d224b..59286c32d27 100644 --- a/test/config.json +++ b/test/config.json @@ -1,61 +1,5 @@ { "Tests": { - "check_make_parser": { - "File": "", - "Args": [], - "Command": [ - "tools/check_make_parser.sh" - ], - "Manual": false, - "Shard": 5, - "RetryMax": 1, - "Tags": [] - }, - "check_make_visitor": { - "File": "", - "Args": [], - "Command": [ - "misc/git/hooks/visitorgen" - ], - "Manual": false, - "Shard": 5, - "RetryMax": 1, - "Tags": [] - }, - "java": { - "File": "", - "Args": [], - "Command": [ - "make", - "java_test" - ], - "Manual": false, - "Shard": 0, - "RetryMax": 0, - "Tags": [] - }, - "legacy_resharding": { - "File": "legacy_resharding.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 1, - "RetryMax": 0, - "Tags": [ - "worker_test" - ] - }, - "local_example": { - "File": "", - "Args": [], - "Command": [ - "test/local_example.sh" - ], - "Manual": false, - "Shard": 5, - "RetryMax": 0, - "Tags": [] - }, "client_test": { "File": "", "Args": [], @@ -102,33 +46,6 @@ "RetryMax": 0, "Tags": [] }, - "python_client": { - "File": "python_client_test.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 1, - "RetryMax": 0, - "Tags": [] - }, - "update_stream": { - "File": "update_stream.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 2, - "RetryMax": 0, - "Tags": [] - }, - "update_stream_rbr": { - "File": "update_stream_rbr.py", - "Args": [], - "Command": [], - "Manual": false, - "Shard": 2, - "RetryMax": 0, - "Tags": [] - }, "backup": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/backup/vtctlbackup"], From 9cfc70c0cecf1a6867b9277cd42cc125ffac8454 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Wed, 4 Mar 2020 13:38:12 +0530 Subject: [PATCH 217/825] removed python deps from bootstrap Signed-off-by: Arindam Nayak --- .github/workflows/java_test.yml | 19 +------ Makefile | 4 +- bootstrap.sh | 98 --------------------------------- test/config.json | 14 ++++- 4 files changed, 17 insertions(+), 118 deletions(-) diff --git a/.github/workflows/java_test.yml b/.github/workflows/java_test.yml index 3de66662c4b..4137a146452 100644 --- a/.github/workflows/java_test.yml +++ b/.github/workflows/java_test.yml @@ -15,21 +15,6 @@ jobs: - name: Check out code uses: actions/checkout@v2 - - name: Get dependencies + - name: Run java test run: | - sudo apt-get update - sudo apt-get install -y mysql-server mysql-client make unzip g++ etcd curl git wget - sudo service mysql stop - sudo service etcd stop - sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/ - sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld - go mod download - - - name: Run make minimaltools - run: | - make minimaltools - - - name: check_make_parser - run: | - make java_test - + go run test.go -docker=true java \ No newline at end of file diff --git a/Makefile b/Makefile index c1315dffc99..6c8618fbed5 100644 --- a/Makefile +++ b/Makefile @@ -324,11 +324,11 @@ packages: docker_base tools: echo $$(date): Installing dependencies - BUILD_PYTHON=0 ./bootstrap.sh + ./bootstrap.sh minimaltools: echo $$(date): Installing minimal dependencies - BUILD_PYTHON=0 BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh + BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh dependency_check: ./tools/dependency_check.sh diff --git a/bootstrap.sh b/bootstrap.sh index feeb04e3fb2..bc9f9170357 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -23,7 +23,6 @@ source ./dev.env # 0. Initialization and helper methods. # 1. Installation of dependencies. -BUILD_PYTHON=${BUILD_PYTHON:-1} BUILD_JAVA=${BUILD_JAVA:-1} BUILD_CONSUL=${BUILD_CONSUL:-1} @@ -89,34 +88,6 @@ function get_arch() { uname -m } - -# Install the gRPC Python library (grpcio) and the protobuf gRPC Python plugin (grpcio-tools) from PyPI. -# Dependencies like the Python protobuf package will be installed automatically. -function install_grpc() { - local version="$1" - local dist="$2" - - # Python requires a very recent version of virtualenv. - # We also require a recent version of pip, as we use it to - # upgrade the other tools. - # For instance, setuptools doesn't work with pip 6.0: - # https://github.com/pypa/setuptools/issues/945 - # (and setuptools is used by grpc install). - grpc_virtualenv="$dist/usr/local" - $VIRTUALENV -v "$grpc_virtualenv" - PIP=$grpc_virtualenv/bin/pip - $PIP install --upgrade pip - $PIP install --upgrade --ignore-installed virtualenv - $PIP install mysql-connector-python - - grpcio_ver=$version - $PIP install --upgrade grpcio=="$grpcio_ver" grpcio-tools=="$grpcio_ver" -} - -if [ "$BUILD_PYTHON" == 1 ] ; then - install_dep "gRPC" "1.16.0" "$VTROOT/dist/grpc" install_grpc -fi - # Install protoc. function install_protoc() { local version="$1" @@ -220,74 +191,5 @@ if [ "$BUILD_CONSUL" == 1 ] ; then install_dep "Consul" "1.4.0" "$VTROOT/dist/consul" install_consul fi -# Install py-mock. -function install_pymock() { - local version="$1" - local dist="$2" - - # For some reason, it seems like setuptools won't create directories even with the --prefix argument - mkdir -p lib/python2.7/site-packages - PYTHONPATH=$(prepend_path "$PYTHONPATH" "$dist/lib/python2.7/site-packages") - export PYTHONPATH - - pushd "$VTROOT/third_party/py" >/dev/null - tar -xzf "mock-$version.tar.gz" - cd "mock-$version" - $PYTHON ./setup.py install --prefix="$dist" - cd .. - rm -r "mock-$version" - popd >/dev/null -} -pymock_version=1.0.1 -if [ "$BUILD_PYTHON" == 1 ] ; then - install_dep "py-mock" "$pymock_version" "$VTROOT/dist/py-mock-$pymock_version" install_pymock -fi - -# Download Selenium (necessary to run test/vtctld_web_test.py). -function install_selenium() { - local version="$1" - local dist="$2" - - PYTHONPATH='' $VIRTUALENV "$dist" - PIP="$dist/bin/pip" - # PYTHONPATH is removed for `pip install` because otherwise it can pick up go/dist/grpc/usr/local/lib/python2.7/site-packages - # instead of go/dist/selenium/lib/python3.5/site-packages and then can't find module 'pip._vendor.requests' - PYTHONPATH='' $PIP install selenium -} -if [ "$BUILD_PYTHON" == 1 ] ; then - install_dep "Selenium" "latest" "$VTROOT/dist/selenium" install_selenium -fi - -# Download chromedriver (necessary to run test/vtctld_web_test.py). -function install_chromedriver() { - local version="$1" - local dist="$2" - - if [ "$(arch)" == "aarch64" ] ; then - os=$(cat /etc/*release | grep "^ID=" | cut -d '=' -f 2) - case $os in - ubuntu|debian) - sudo apt-get update -y && sudo apt install -y --no-install-recommends unzip libglib2.0-0 libnss3 libx11-6 - ;; - centos|fedora) - sudo yum update -y && yum install -y libX11 unzip wget - ;; - esac - echo "For Arm64, using prebuilt binary from electron (https://github.com/electron/electron/) of version 76.0.3809.126" - wget https://github.com/electron/electron/releases/download/v6.0.3/chromedriver-v6.0.3-linux-arm64.zip - unzip -o -q chromedriver-v6.0.3-linux-arm64.zip -d "$dist" - rm chromedriver-v6.0.3-linux-arm64.zip - else - curl -sL "https://chromedriver.storage.googleapis.com/$version/chromedriver_linux64.zip" > chromedriver_linux64.zip - unzip -o -q chromedriver_linux64.zip -d "$dist" - rm chromedriver_linux64.zip - fi -} -install_dep "chromedriver" "73.0.3683.20" "$VTROOT/dist/chromedriver" install_chromedriver - -if [ "$BUILD_PYTHON" == 1 ] ; then - PYTHONPATH='' $PIP install mysql-connector-python -fi - echo echo "bootstrap finished - run 'make build' to compile" diff --git a/test/config.json b/test/config.json index 59286c32d27..14fe6213f0d 100644 --- a/test/config.json +++ b/test/config.json @@ -1,5 +1,17 @@ { "Tests": { + "java": { + "File": "", + "Args": [], + "Command": [ + "make", + "java_test" + ], + "Manual": false, + "Shard": 5, + "RetryMax": 0, + "Tags": [] + }, "client_test": { "File": "", "Args": [], @@ -7,7 +19,7 @@ "test/client_test.sh" ], "Manual": false, - "Shard": 2, + "Shard": 5, "RetryMax": 0, "Tags": [] }, From 6275d1d34c89af42d72c7a590f425ae56bd5ea26 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Thu, 5 Mar 2020 12:05:10 +0530 Subject: [PATCH 218/825] updated config Signed-off-by: Arindam Nayak --- .github/workflows/check_make_visitor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_make_visitor.yml b/.github/workflows/check_make_visitor.yml index 5bc60788c9b..68d8660ba84 100644 --- a/.github/workflows/check_make_visitor.yml +++ b/.github/workflows/check_make_visitor.yml @@ -29,7 +29,7 @@ jobs: run: | make minimaltools - - name: check_make_parser + - name: check_make_visitor run: | misc/git/hooks/visitorgen From 19d07685af251f54444a4c7a038d7ac64d654154 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Thu, 5 Mar 2020 12:16:11 +0530 Subject: [PATCH 219/825] moved client test to shard 25 Signed-off-by: Arindam Nayak --- test/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/config.json b/test/config.json index 14fe6213f0d..2a43c1a7e86 100644 --- a/test/config.json +++ b/test/config.json @@ -19,7 +19,7 @@ "test/client_test.sh" ], "Manual": false, - "Shard": 5, + "Shard": 25, "RetryMax": 0, "Tags": [] }, From b7872e60c8c3907342148dc77bdec75154a5ebd7 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Thu, 5 Mar 2020 12:37:42 +0530 Subject: [PATCH 220/825] Added python grpc dep back as we need it for client_test Signed-off-by: Arindam Nayak --- Makefile | 4 ++-- bootstrap.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6c8618fbed5..c1315dffc99 100644 --- a/Makefile +++ b/Makefile @@ -324,11 +324,11 @@ packages: docker_base tools: echo $$(date): Installing dependencies - ./bootstrap.sh + BUILD_PYTHON=0 ./bootstrap.sh minimaltools: echo $$(date): Installing minimal dependencies - BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh + BUILD_PYTHON=0 BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh dependency_check: ./tools/dependency_check.sh diff --git a/bootstrap.sh b/bootstrap.sh index bc9f9170357..5c33aedc1f7 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -23,6 +23,7 @@ source ./dev.env # 0. Initialization and helper methods. # 1. Installation of dependencies. +BUILD_PYTHON=${BUILD_PYTHON:-1} BUILD_JAVA=${BUILD_JAVA:-1} BUILD_CONSUL=${BUILD_CONSUL:-1} @@ -88,6 +89,34 @@ function get_arch() { uname -m } +# This is only needed to execute client_test testcase. +# Install the gRPC Python library (grpcio) and the protobuf gRPC Python plugin (grpcio-tools) from PyPI. +# Dependencies like the Python protobuf package will be installed automatically. +function install_grpc() { + local version="$1" + local dist="$2" + + # Python requires a very recent version of virtualenv. + # We also require a recent version of pip, as we use it to + # upgrade the other tools. + # For instance, setuptools doesn't work with pip 6.0: + # https://github.com/pypa/setuptools/issues/945 + # (and setuptools is used by grpc install). + grpc_virtualenv="$dist/usr/local" + $VIRTUALENV -v "$grpc_virtualenv" + PIP=$grpc_virtualenv/bin/pip + $PIP install --upgrade pip + $PIP install --upgrade --ignore-installed virtualenv + $PIP install mysql-connector-python + + grpcio_ver=$version + $PIP install --upgrade grpcio=="$grpcio_ver" grpcio-tools=="$grpcio_ver" +} + +if [ "$BUILD_PYTHON" == 1 ] ; then + install_dep "gRPC" "1.16.0" "$VTROOT/dist/grpc" install_grpc +fi + # Install protoc. function install_protoc() { local version="$1" From 4b0adfbd64edecedaa366d66286c6aa1aec88d3a Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 5 Mar 2020 06:49:19 +0100 Subject: [PATCH 221/825] Make sure re-creating the visitor works as expected Signed-off-by: Andres Taylor --- go/vt/sqlparser/rewriter.go | 7 ++++++- go/vt/sqlparser/visitorgen/main/main.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/go/vt/sqlparser/rewriter.go b/go/vt/sqlparser/rewriter.go index 2f6c22879fe..3b912a79bb8 100644 --- a/go/vt/sqlparser/rewriter.go +++ b/go/vt/sqlparser/rewriter.go @@ -2,7 +2,7 @@ package sqlparser -//go:generate go run visitorgen/main -input=ast.go -output=rewriter.go +//go:generate go run ./visitorgen/main -input=ast.go -output=rewriter.go import ( "reflect" @@ -318,6 +318,10 @@ func replaceGroupConcatExprExprs(newNode, parent SQLNode) { parent.(*GroupConcatExpr).Exprs = newNode.(SelectExprs) } +func replaceGroupConcatExprLimit(newNode, parent SQLNode) { + parent.(*GroupConcatExpr).Limit = newNode.(*Limit) +} + func replaceGroupConcatExprOrderBy(newNode, parent SQLNode) { parent.(*GroupConcatExpr).OrderBy = newNode.(OrderBy) } @@ -998,6 +1002,7 @@ func (a *application) apply(parent, node SQLNode, replacer replacerFunc) { case *GroupConcatExpr: a.apply(node, n.Exprs, replaceGroupConcatExprExprs) + a.apply(node, n.Limit, replaceGroupConcatExprLimit) a.apply(node, n.OrderBy, replaceGroupConcatExprOrderBy) case *IndexDefinition: diff --git a/go/vt/sqlparser/visitorgen/main/main.go b/go/vt/sqlparser/visitorgen/main/main.go index 5b53b3d268f..0d940ea060f 100644 --- a/go/vt/sqlparser/visitorgen/main/main.go +++ b/go/vt/sqlparser/visitorgen/main/main.go @@ -104,7 +104,7 @@ const fileHeader = `// Code generated by visitorgen/main/main.go. DO NOT EDIT. package sqlparser -//go:generate go run visitorgen/main -input=ast.go -output=rewriter.go +//go:generate go run ./visitorgen/main -input=ast.go -output=rewriter.go import ( "reflect" From 291540c2fde54c60453b60a911007c0b9f3a6c4c Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Thu, 5 Mar 2020 13:15:41 +0530 Subject: [PATCH 222/825] grouped docker dependent test in one shard Signed-off-by: Arindam Nayak --- .github/workflows/cluster_endtoend.yml | 2 +- .github/workflows/{java_test.yml => misc_test_docker.yml} | 8 ++++---- test/config.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename .github/workflows/{java_test.yml => misc_test_docker.yml} (65%) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 48d2dca64b1..1761630018b 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] + name: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] steps: - name: Set up Go diff --git a/.github/workflows/java_test.yml b/.github/workflows/misc_test_docker.yml similarity index 65% rename from .github/workflows/java_test.yml rename to .github/workflows/misc_test_docker.yml index 4137a146452..fd617e46b34 100644 --- a/.github/workflows/java_test.yml +++ b/.github/workflows/misc_test_docker.yml @@ -1,9 +1,9 @@ -name: java_test +name: misc test on: [push, pull_request] jobs: build: - name: Java Test + name: Misc Test runs-on: ubuntu-latest steps: @@ -15,6 +15,6 @@ jobs: - name: Check out code uses: actions/checkout@v2 - - name: Run java test + - name: Run Misc test which requires docker run: | - go run test.go -docker=true java \ No newline at end of file + go run test.go -docker=true -shard 25 \ No newline at end of file diff --git a/test/config.json b/test/config.json index 2a43c1a7e86..5e1b11bec2a 100644 --- a/test/config.json +++ b/test/config.json @@ -8,7 +8,7 @@ "java_test" ], "Manual": false, - "Shard": 5, + "Shard": 25, "RetryMax": 0, "Tags": [] }, From ddbabb18ebf05d521dc66b62743af6fde2df7212 Mon Sep 17 00:00:00 2001 From: Ryan Leonard Date: Thu, 5 Mar 2020 11:29:20 -0500 Subject: [PATCH 223/825] Updated RegionJson description with comments from @sougou Signed-off-by: Ryan Leonard --- go/vt/vtgate/vindexes/region_json.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/go/vt/vtgate/vindexes/region_json.go b/go/vt/vtgate/vindexes/region_json.go index 0a3e3e8854a..3fa3086566c 100644 --- a/go/vt/vtgate/vindexes/region_json.go +++ b/go/vt/vtgate/vindexes/region_json.go @@ -38,9 +38,11 @@ func init() { // RegionMap is used to store mapping of country to region type RegionMap map[string]uint64 -// RegionJson defines a vindex that uses a lookup table. -// The table is expected to define the id column as unique. It's -// Unique and a Lookup. +// RegionJson is a multi-column unique vindex +// The first column is used to lookup the prefix part of the keyspace id, the second column is hashed, +// and the two values are combined to produce the keyspace id. +// RegionJson can be used for geo-partitioning because the first column can denote a region, +// and it will dictate the shard range for that region. type RegionJson struct { name string regionMap RegionMap From 124cc4399b94b1c32f7100ef5857e04fd37552f4 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 5 Mar 2020 08:31:25 -0800 Subject: [PATCH 224/825] DBConfigs Refactor (#5833) * Added wrapper struct, modified usage Signed-off-by: Saif Alharthi * Add go.mod Signed-off-by: Saif Alharthi * Refactor dbconfigs and its calls Signed-off-by: Saif Alharthi * Fix failing vstreamer test Signed-off-by: Saif Alharthi * Fix copy and paste mistakes Signed-off-by: Saif Alharthi * Made withCredentials a private function and refactored the calls Signed-off-by: Saif Alharthi * Fix Tests Signed-off-by: Saif Alharthi * Clean up code Signed-off-by: Saif Alharthi * Extend DbConfigs and change method name Signed-off-by: Saif Alharthi * Add Connect Method Signed-off-by: Saif Alharthi * Pass Context as parameter Signed-off-by: Saif Alharthi * Modify definition of DBConfigs Signed-off-by: Saif Alharthi * Make Connect function part of the Connector definition Signed-off-by: Saif Alharthi * Remove obselete functions Signed-off-by: Saif Alharthi --- go.mod | 10 +- go.sum | 30 ++- go/mysql/fakesqldb/server.go | 14 +- go/vt/binlog/binlog_streamer.go | 9 +- go/vt/binlog/binlog_streamer_rbr_test.go | 17 +- go/vt/binlog/binlog_streamer_test.go | 179 ++++++++++++++++-- go/vt/binlog/binlogplayer/dbclient.go | 19 +- go/vt/binlog/event_streamer.go | 3 +- go/vt/binlog/slave_connection.go | 14 +- go/vt/binlog/updatestreamctl.go | 7 +- go/vt/dbconfigs/credentials.go | 2 +- go/vt/dbconfigs/dbconfigs.go | 83 ++++++-- go/vt/dbconfigs/dbconfigs_test.go | 14 +- go/vt/dbconnpool/connection.go | 16 +- go/vt/dbconnpool/connection_pool.go | 10 +- go/vt/mysqlctl/mysqld.go | 8 +- go/vt/mysqlctl/replication.go | 3 +- go/vt/mysqlctl/schema.go | 3 +- go/vt/vtexplain/vtexplain_vttablet.go | 4 +- go/vt/vttablet/heartbeat/reader_test.go | 4 +- go/vt/vttablet/heartbeat/writer.go | 4 +- go/vt/vttablet/heartbeat/writer_test.go | 4 +- go/vt/vttablet/tabletmanager/action_agent.go | 5 +- .../vreplication/framework_test.go | 5 +- .../vreplication/vstreamer_client.go | 3 +- .../vreplication/vstreamer_client_test.go | 24 +-- .../vttablet/tabletserver/connpool/dbconn.go | 7 +- go/vt/vttablet/tabletserver/connpool/pool.go | 14 +- .../vttablet/tabletserver/messager/engine.go | 1 + .../tabletserver/messager/engine_test.go | 4 +- .../messager/message_manager_test.go | 5 +- go/vt/vttablet/tabletserver/query_engine.go | 2 +- .../tabletserver/replication_watcher.go | 1 + go/vt/vttablet/tabletserver/schema/engine.go | 5 +- .../tabletserver/schema/engine_test.go | 4 +- go/vt/vttablet/tabletserver/testutils_test.go | 4 +- go/vt/vttablet/tabletserver/twopc.go | 3 +- go/vt/vttablet/tabletserver/tx_pool.go | 8 +- .../vttablet/tabletserver/vstreamer/engine.go | 6 +- .../tabletserver/vstreamer/resultstreamer.go | 6 +- .../tabletserver/vstreamer/rowstreamer.go | 8 +- .../vstreamer/rowstreamer_test.go | 10 +- .../tabletserver/vstreamer/snapshot_conn.go | 12 +- .../tabletserver/vstreamer/vstreamer.go | 37 ++-- .../tabletserver/vstreamer/vstreamer_test.go | 23 ++- 45 files changed, 459 insertions(+), 195 deletions(-) diff --git a/go.mod b/go.mod index c1252dca071..ed437eac573 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect github.com/golangci/golangci-lint v1.21.0 // indirect github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect - github.com/google/go-cmp v0.3.0 + github.com/google/go-cmp v0.4.0 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf // indirect github.com/gorilla/websocket v1.4.0 github.com/gostaticanalysis/analysisutil v0.0.3 // indirect @@ -55,10 +55,8 @@ require ( github.com/philhofer/fwd v1.0.0 // indirect github.com/pires/go-proxyproto v0.0.0-20191211124218-517ecdf5bb2b github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v1.1.0 - github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect - github.com/prometheus/common v0.7.0 - github.com/prometheus/procfs v0.0.5 // indirect + github.com/prometheus/client_golang v1.4.1 + github.com/prometheus/common v0.9.1 github.com/satori/go.uuid v0.0.0-20160713180306-0aa62d5ddceb // indirect github.com/securego/gosec v0.0.0-20191217083152-cb4f343eaff1 // indirect github.com/spf13/afero v1.2.2 // indirect @@ -79,7 +77,6 @@ require ( golang.org/x/lint v0.0.0-20190409202823-959b441ac422 golang.org/x/net v0.0.0-20190926025831-c00fd9afed17 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 - golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 // indirect golang.org/x/text v0.3.2 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 golang.org/x/tools v0.0.0-20191219041853-979b82bfef62 @@ -91,4 +88,5 @@ require ( honnef.co/go/tools v0.0.1-2019.2.3 mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2 // indirect sourcegraph.com/sqs/pbtypes v1.0.0 // indirect + vitess.io/vitess/examples/are-you-alive v0.0.0-20200302220708-6b7695375ce9 // indirect ) diff --git a/go.sum b/go.sum index 4b7e97903c3..280450cc4b0 100644 --- a/go.sum +++ b/go.sum @@ -53,9 +53,9 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a h1:Ed33uJE74ksDaYfdY72gK7Cg//o2FgsqlqUfBW079T8= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= @@ -67,10 +67,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= @@ -254,10 +254,13 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= @@ -387,6 +390,7 @@ github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWx github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -530,12 +534,16 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8= +github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180326160409-38c53a9f4bfc/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= @@ -545,6 +553,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20180408092902-8b1c2da0d56d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= @@ -554,6 +564,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= @@ -741,6 +753,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -767,6 +780,8 @@ golang.org/x/sys v0.0.0-20190926180325-855e68c8590b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 h1:Y8q0zsdcgAd+JU8VUA8p8Qv2YhuY9zevDG2ORt5qBUI= golang.org/x/sys v0.0.0-20191218084908-4a24b4065292/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -814,6 +829,8 @@ golang.org/x/tools v0.0.0-20191219041853-979b82bfef62/go.mod h1:TB2adYChydJhpapK golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= @@ -880,6 +897,7 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= @@ -910,3 +928,5 @@ sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXA sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= sourcegraph.com/sqs/pbtypes v1.0.0 h1:f7lAwqviDEGvON4kRv0o5V7FT/IQK+tbkF664XMbP3o= sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4= +vitess.io/vitess/examples/are-you-alive v0.0.0-20200302220708-6b7695375ce9 h1:tB4oHYzfEkqCFNUO7yUFWg9xcYw9ULT5U9/tBVbjaD0= +vitess.io/vitess/examples/are-you-alive v0.0.0-20200302220708-6b7695375ce9/go.mod h1:3Lnc7tMP/PtC7X4Yxu2tcqYbXAX4aGLDKEDsaQwPDp4= diff --git a/go/mysql/fakesqldb/server.go b/go/mysql/fakesqldb/server.go index 58d393ae250..dd29a495a0e 100644 --- a/go/mysql/fakesqldb/server.go +++ b/go/mysql/fakesqldb/server.go @@ -32,6 +32,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -257,23 +258,24 @@ func (db *DB) WaitForClose(timeout time.Duration) error { } // ConnParams returns the ConnParams to connect to the DB. -func (db *DB) ConnParams() *mysql.ConnParams { - return &mysql.ConnParams{ +func (db *DB) ConnParams() dbconfigs.Connector { + return dbconfigs.New(&mysql.ConnParams{ UnixSocket: db.socketFile, Uname: "user1", Pass: "password1", Charset: "utf8", - } + }) + } // ConnParamsWithUname returns ConnParams to connect to the DB with the Uname set to the provided value. -func (db *DB) ConnParamsWithUname(uname string) *mysql.ConnParams { - return &mysql.ConnParams{ +func (db *DB) ConnParamsWithUname(uname string) dbconfigs.Connector { + return dbconfigs.New(&mysql.ConnParams{ UnixSocket: db.socketFile, Uname: uname, Pass: "password1", Charset: "utf8", - } + }) } // diff --git a/go/vt/binlog/binlog_streamer.go b/go/vt/binlog/binlog_streamer.go index 9a2dec4c866..24206437e3d 100644 --- a/go/vt/binlog/binlog_streamer.go +++ b/go/vt/binlog/binlog_streamer.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -133,7 +134,7 @@ type tableCacheEntry struct { // NewStreamer() again. type Streamer struct { // The following fields at set at creation and immutable. - cp *mysql.ConnParams + cp dbconfigs.Connector se *schema.Engine resolverFactory keyspaceIDResolverFactory extractPK bool @@ -155,7 +156,7 @@ type Streamer struct { // startPos is the position to start streaming at. Incompatible with timestamp. // timestamp is the timestamp to start streaming at. Incompatible with startPos. // sendTransaction is called each time a transaction is committed or rolled back. -func NewStreamer(cp *mysql.ConnParams, se *schema.Engine, clientCharset *binlogdatapb.Charset, startPos mysql.Position, timestamp int64, sendTransaction sendTransactionFunc) *Streamer { +func NewStreamer(cp dbconfigs.Connector, se *schema.Engine, clientCharset *binlogdatapb.Charset, startPos mysql.Position, timestamp int64, sendTransaction sendTransactionFunc) *Streamer { return &Streamer{ cp: cp, se: se, @@ -407,7 +408,7 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog return pos, err } default: // BL_DDL, BL_SET, BL_INSERT, BL_UPDATE, BL_DELETE, BL_UNRECOGNIZED - if q.Database != "" && q.Database != bls.cp.DbName { + if q.Database != "" && q.Database != bls.cp.DBName() { // Skip cross-db statements. continue } @@ -472,7 +473,7 @@ func (bls *Streamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog // Check we're in the right database, and if so, fill // in more data. - if tm.Database != "" && tm.Database != bls.cp.DbName { + if tm.Database != "" && tm.Database != bls.cp.DBName() { continue } diff --git a/go/vt/binlog/binlog_streamer_rbr_test.go b/go/vt/binlog/binlog_streamer_rbr_test.go index b235373a59f..58f0850cc55 100644 --- a/go/vt/binlog/binlog_streamer_rbr_test.go +++ b/go/vt/binlog/binlog_streamer_rbr_test.go @@ -23,6 +23,7 @@ import ( "golang.org/x/net/context" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -256,7 +257,13 @@ func TestStreamerParseRBREvents(t *testing.T) { }) return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, se, nil, mysql.Position{}, 0, sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, se, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -498,7 +505,13 @@ func TestStreamerParseRBRNameEscapes(t *testing.T) { }) return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, se, nil, mysql.Position{}, 0, sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, se, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) diff --git a/go/vt/binlog/binlog_streamer_test.go b/go/vt/binlog/binlog_streamer_test.go index d8edb2b7eb3..5126f330465 100644 --- a/go/vt/binlog/binlog_streamer_test.go +++ b/go/vt/binlog/binlog_streamer_test.go @@ -28,6 +28,7 @@ import ( "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/vt/dbconfigs" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -115,7 +116,14 @@ func TestStreamerParseEventsXID(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -170,8 +178,14 @@ func TestStreamerParseEventsCommit(t *testing.T) { }, }, } + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -190,7 +204,14 @@ func TestStreamerStop(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) // Start parseEvents(), but don't send it anything, so it just waits. ctx, cancel := context.WithCancel(context.Background()) @@ -235,7 +256,14 @@ func TestStreamerParseEventsClientEOF(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return io.EOF } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -253,8 +281,13 @@ func TestStreamerParseEventsServerEOF(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) _, err := bls.parseEvents(context.Background(), events) if err != want { t.Errorf("wrong error, got %#v, want %#v", err, want) @@ -283,7 +316,14 @@ func TestStreamerParseEventsSendErrorXID(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return fmt.Errorf("foobar") } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) @@ -321,7 +361,14 @@ func TestStreamerParseEventsSendErrorCommit(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return fmt.Errorf("foobar") } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -354,7 +401,14 @@ func TestStreamerParseEventsInvalid(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -389,7 +443,14 @@ func TestStreamerParseEventsInvalidFormat(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -424,7 +485,14 @@ func TestStreamerParseEventsNoFormat(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -457,7 +525,14 @@ func TestStreamerParseEventsInvalidQuery(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -538,7 +613,13 @@ func TestStreamerParseEventsRollback(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { @@ -603,7 +684,14 @@ func TestStreamerParseEventsDMLWithoutBegin(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { @@ -671,7 +759,14 @@ func TestStreamerParseEventsBeginWithoutCommit(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { @@ -726,7 +821,13 @@ func TestStreamerParseEventsSetInsertID(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { @@ -761,7 +862,13 @@ func TestStreamerParseEventsInvalidIntVar(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) go sendTestEvents(events, input) _, err := bls.parseEvents(context.Background(), events) @@ -818,7 +925,13 @@ func TestStreamerParseEventsOtherDB(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { @@ -874,7 +987,13 @@ func TestStreamerParseEventsOtherDBBegin(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { @@ -909,7 +1028,13 @@ func TestStreamerParseEventsBeginAgain(t *testing.T) { sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error { return nil } - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, sendTransaction) before := binlogStreamerErrors.Counts()["ParseEvents"] go sendTestEvents(events, input) @@ -972,7 +1097,13 @@ func TestStreamerParseEventsMariadbBeginGTID(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { @@ -1025,7 +1156,13 @@ func TestStreamerParseEventsMariadbStandaloneGTID(t *testing.T) { }, } var got binlogStatements - bls := NewStreamer(&mysql.ConnParams{DbName: "vt_test_keyspace"}, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) + // Set mock mysql.ConnParams and dbconfig + mcp := &mysql.ConnParams{ + DbName: "vt_test_keyspace", + } + dbcfgs := dbconfigs.New(mcp) + + bls := NewStreamer(dbcfgs, nil, nil, mysql.Position{}, 0, (&got).sendTransaction) go sendTestEvents(events, input) if _, err := bls.parseEvents(context.Background(), events); err != ErrServerEOF { diff --git a/go/vt/binlog/binlogplayer/dbclient.go b/go/vt/binlog/binlogplayer/dbclient.go index abaf3506c93..7dfeb36f2a4 100644 --- a/go/vt/binlog/binlogplayer/dbclient.go +++ b/go/vt/binlog/binlogplayer/dbclient.go @@ -17,10 +17,9 @@ limitations under the License. package binlogplayer import ( + "context" "fmt" - "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" @@ -40,12 +39,12 @@ type DBClient interface { // dbClientImpl is a real DBClient backed by a mysql connection. type dbClientImpl struct { - dbConfig *mysql.ConnParams + dbConfig dbconfigs.Connector dbConn *mysql.Conn } // NewDBClient creates a DBClient instance -func NewDBClient(params *mysql.ConnParams) DBClient { +func NewDBClient(params dbconfigs.Connector) DBClient { return &dbClientImpl{ dbConfig: params, } @@ -58,18 +57,16 @@ func (dc *dbClientImpl) handleError(err error) { } func (dc *dbClientImpl) DBName() string { - return dc.dbConfig.DbName + params, _ := dc.dbConfig.MysqlParams() + return params.DbName } func (dc *dbClientImpl) Connect() error { - params, err := dbconfigs.WithCredentials(dc.dbConfig) - if err != nil { - return err - } + var err error ctx := context.Background() - dc.dbConn, err = mysql.Connect(ctx, params) + dc.dbConn, err = dc.dbConfig.Connect(ctx) if err != nil { - return fmt.Errorf("error in connecting to mysql db, err %v", err) + return fmt.Errorf("error in connecting to mysql db with connection %v, err %v", dc.dbConn, err) } return nil } diff --git a/go/vt/binlog/event_streamer.go b/go/vt/binlog/event_streamer.go index 02bbb647cc7..4defc3fed83 100644 --- a/go/vt/binlog/event_streamer.go +++ b/go/vt/binlog/event_streamer.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -51,7 +52,7 @@ type EventStreamer struct { } // NewEventStreamer returns a new EventStreamer on top of a Streamer -func NewEventStreamer(cp *mysql.ConnParams, se *schema.Engine, startPos mysql.Position, timestamp int64, sendEvent sendEventFunc) *EventStreamer { +func NewEventStreamer(cp dbconfigs.Connector, se *schema.Engine, startPos mysql.Position, timestamp int64, sendEvent sendEventFunc) *EventStreamer { evs := &EventStreamer{ sendEvent: sendEvent, } diff --git a/go/vt/binlog/slave_connection.go b/go/vt/binlog/slave_connection.go index c38e67af859..64367757b7f 100644 --- a/go/vt/binlog/slave_connection.go +++ b/go/vt/binlog/slave_connection.go @@ -40,7 +40,7 @@ var ( // among actual slaves in the topology. type SlaveConnection struct { *mysql.Conn - cp *mysql.ConnParams + cp dbconfigs.Connector slaveID uint32 cancel context.CancelFunc wg sync.WaitGroup @@ -53,7 +53,7 @@ type SlaveConnection struct { // 1) No other processes are making fake slave connections to our mysqld. // 2) No real slave servers will have IDs in the range 1-N where N is the peak // number of concurrent fake slave connections we will ever make. -func NewSlaveConnection(cp *mysql.ConnParams) (*SlaveConnection, error) { +func NewSlaveConnection(cp dbconfigs.Connector) (*SlaveConnection, error) { conn, err := connectForReplication(cp) if err != nil { return nil, err @@ -69,18 +69,12 @@ func NewSlaveConnection(cp *mysql.ConnParams) (*SlaveConnection, error) { } // connectForReplication create a MySQL connection ready to use for replication. -func connectForReplication(cp *mysql.ConnParams) (*mysql.Conn, error) { - params, err := dbconfigs.WithCredentials(cp) - if err != nil { - return nil, err - } - +func connectForReplication(cp dbconfigs.Connector) (*mysql.Conn, error) { ctx := context.Background() - conn, err := mysql.Connect(ctx, params) + conn, err := cp.Connect(ctx) if err != nil { return nil, err } - // Tell the server that we understand the format of events // that will be used if binlog_checksum is enabled on the server. if _, err := conn.ExecuteFetch("SET @master_binlog_checksum=@@global.binlog_checksum", 0, false); err != nil { diff --git a/go/vt/binlog/updatestreamctl.go b/go/vt/binlog/updatestreamctl.go index 142324fd298..ff331d89a33 100644 --- a/go/vt/binlog/updatestreamctl.go +++ b/go/vt/binlog/updatestreamctl.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/stats" "vitess.io/vitess/go/sync2" "vitess.io/vitess/go/tb" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -108,7 +109,7 @@ type UpdateStreamImpl struct { ts *topo.Server keyspace string cell string - cp *mysql.ConnParams + cp dbconfigs.Connector se *schema.Engine // actionLock protects the following variables @@ -169,7 +170,7 @@ type RegisterUpdateStreamServiceFunc func(UpdateStream) var RegisterUpdateStreamServices []RegisterUpdateStreamServiceFunc // NewUpdateStream returns a new UpdateStreamImpl object -func NewUpdateStream(ts *topo.Server, keyspace string, cell string, cp *mysql.ConnParams, se *schema.Engine) *UpdateStreamImpl { +func NewUpdateStream(ts *topo.Server, keyspace string, cell string, cp dbconfigs.Connector, se *schema.Engine) *UpdateStreamImpl { return &UpdateStreamImpl{ ts: ts, keyspace: keyspace, @@ -210,7 +211,7 @@ func (updateStream *UpdateStreamImpl) Enable() { updateStream.state.Set(usEnabled) updateStream.streams.Init() - log.Infof("Enabling update stream, dbname: %s", updateStream.cp.DbName) + log.Infof("Enabling update stream, dbname: %s", updateStream.cp.DBName()) } // Disable will disallow any connection to the service diff --git a/go/vt/dbconfigs/credentials.go b/go/vt/dbconfigs/credentials.go index f437879e4b9..026c9bafd6b 100644 --- a/go/vt/dbconfigs/credentials.go +++ b/go/vt/dbconfigs/credentials.go @@ -113,7 +113,7 @@ func (fcs *FileCredentialsServer) GetUserAndPassword(user string) (string, strin // WithCredentials returns a copy of the provided ConnParams that we can use // to connect, after going through the CredentialsServer. -func WithCredentials(cp *mysql.ConnParams) (*mysql.ConnParams, error) { +func withCredentials(cp *mysql.ConnParams) (*mysql.ConnParams, error) { result := *cp user, passwd, err := GetCredentialsServer().GetUserAndPassword(cp.Uname) switch err { diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index 1c0ee4173b3..31ccc90aa7d 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -21,6 +21,7 @@ limitations under the License. package dbconfigs import ( + "context" "encoding/json" "flag" "fmt" @@ -135,69 +136,121 @@ func registerPerUserFlags(dbc *userConfig, userKey string) { } +// Connector contains Connection Parameters for mysql connection +type Connector struct { + connParams *mysql.ConnParams + dbName string + host string +} + +// New initializes a ConnParams from mysql connection parameters +func New(mcp *mysql.ConnParams) Connector { + return Connector{ + connParams: mcp, + } +} + +// Connect will invoke the mysql.connect method and return a connection +func (c Connector) Connect(ctx context.Context) (*mysql.Conn, error) { + params, err := c.MysqlParams() + if err != nil { + return nil, err + } + conn, err := mysql.Connect(ctx, params) + if err != nil { + return nil, err + } + return conn, nil +} + +// MysqlParams returns the connections params +func (c Connector) MysqlParams() (*mysql.ConnParams, error) { + params, err := withCredentials(c.connParams) + if err != nil { + return nil, err + } + return params, nil +} + +// DBName gets the dbname from mysql.ConnParams +func (c Connector) DBName() string { + params, _ := c.MysqlParams() + return params.DbName +} + +// Host gets the host from mysql.ConnParams +func (c Connector) Host() string { + params, _ := c.MysqlParams() + return params.Host +} + // AppWithDB returns connection parameters for app with dbname set. -func (dbcfgs *DBConfigs) AppWithDB() *mysql.ConnParams { +func (dbcfgs *DBConfigs) AppWithDB() Connector { return dbcfgs.makeParams(App, true) } // AppDebugWithDB returns connection parameters for appdebug with dbname set. -func (dbcfgs *DBConfigs) AppDebugWithDB() *mysql.ConnParams { +func (dbcfgs *DBConfigs) AppDebugWithDB() Connector { return dbcfgs.makeParams(AppDebug, true) } // AllPrivsWithDB returns connection parameters for appdebug with dbname set. -func (dbcfgs *DBConfigs) AllPrivsWithDB() *mysql.ConnParams { +func (dbcfgs *DBConfigs) AllPrivsWithDB() Connector { return dbcfgs.makeParams(AllPrivs, true) } // Dba returns connection parameters for dba with no dbname set. -func (dbcfgs *DBConfigs) Dba() *mysql.ConnParams { +func (dbcfgs *DBConfigs) Dba() Connector { return dbcfgs.makeParams(Dba, false) } // DbaWithDB returns connection parameters for appdebug with dbname set. -func (dbcfgs *DBConfigs) DbaWithDB() *mysql.ConnParams { +func (dbcfgs *DBConfigs) DbaWithDB() Connector { return dbcfgs.makeParams(Dba, true) } // FilteredWithDB returns connection parameters for filtered with dbname set. -func (dbcfgs *DBConfigs) FilteredWithDB() *mysql.ConnParams { +func (dbcfgs *DBConfigs) FilteredWithDB() Connector { return dbcfgs.makeParams(Filtered, true) } // Repl returns connection parameters for repl with no dbname set. -func (dbcfgs *DBConfigs) Repl() *mysql.ConnParams { +func (dbcfgs *DBConfigs) Repl() Connector { return dbcfgs.makeParams(Repl, false) } // ExternalRepl returns connection parameters for repl with no dbname set. -func (dbcfgs *DBConfigs) ExternalRepl() *mysql.ConnParams { +func (dbcfgs *DBConfigs) ExternalRepl() Connector { return dbcfgs.makeParams(ExternalRepl, true) } // ExternalReplWithDB returns connection parameters for repl with dbname set. -func (dbcfgs *DBConfigs) ExternalReplWithDB() *mysql.ConnParams { +func (dbcfgs *DBConfigs) ExternalReplWithDB() Connector { params := dbcfgs.makeParams(ExternalRepl, true) // TODO @rafael: This is a hack to allows to configure external databases by providing // db-config-erepl-dbname. - if params.DeprecatedDBName != "" { - params.DbName = params.DeprecatedDBName + if params.connParams.DeprecatedDBName != "" { + params.connParams.DbName = params.connParams.DeprecatedDBName return params } return params } // AppWithDB returns connection parameters for app with dbname set. -func (dbcfgs *DBConfigs) makeParams(userKey string, withDB bool) *mysql.ConnParams { +func (dbcfgs *DBConfigs) makeParams(userKey string, withDB bool) Connector { orig := dbcfgs.userConfigs[userKey] if orig == nil { - return &mysql.ConnParams{} + return Connector{ + connParams: &mysql.ConnParams{}, + } } result := orig.param if withDB { result.DbName = dbcfgs.DBName.Get() } - return &result + return Connector{ + connParams: &result, + } } // IsZero returns true if DBConfigs was uninitialized. @@ -293,7 +346,7 @@ func Init(defaultSocketFile string) (*DBConfigs, error) { // See if the CredentialsServer is working. We do not use the // result for anything, this is just a check. for _, uc := range dbConfigs.userConfigs { - if _, err := WithCredentials(&uc.param); err != nil { + if _, err := withCredentials(&uc.param); err != nil { return nil, fmt.Errorf("dbconfig cannot be initialized: %v", err) } // Check for only one. diff --git a/go/vt/dbconfigs/dbconfigs_test.go b/go/vt/dbconfigs/dbconfigs_test.go index 1917b1b3758..323da7eceee 100644 --- a/go/vt/dbconfigs/dbconfigs_test.go +++ b/go/vt/dbconfigs/dbconfigs_test.go @@ -327,25 +327,25 @@ func TestAccessors(t *testing.T) { }, } dbc.DBName.Set("db") - if got, want := dbc.AppWithDB().DbName, "db"; got != want { + if got, want := dbc.AppWithDB().connParams.DbName, "db"; got != want { t.Errorf("dbc.AppWithDB().DbName: %v, want %v", got, want) } - if got, want := dbc.AllPrivsWithDB().DbName, "db"; got != want { + if got, want := dbc.AllPrivsWithDB().connParams.DbName, "db"; got != want { t.Errorf("dbc.AllPrivsWithDB().DbName: %v, want %v", got, want) } - if got, want := dbc.AppDebugWithDB().DbName, "db"; got != want { + if got, want := dbc.AppDebugWithDB().connParams.DbName, "db"; got != want { t.Errorf("dbc.AppDebugWithDB().DbName: %v, want %v", got, want) } - if got, want := dbc.Dba().DbName, ""; got != want { + if got, want := dbc.Dba().connParams.DbName, ""; got != want { t.Errorf("dbc.Dba().DbName: %v, want %v", got, want) } - if got, want := dbc.DbaWithDB().DbName, "db"; got != want { + if got, want := dbc.DbaWithDB().connParams.DbName, "db"; got != want { t.Errorf("dbc.DbaWithDB().DbName: %v, want %v", got, want) } - if got, want := dbc.FilteredWithDB().DbName, "db"; got != want { + if got, want := dbc.FilteredWithDB().connParams.DbName, "db"; got != want { t.Errorf("dbc.FilteredWithDB().DbName: %v, want %v", got, want) } - if got, want := dbc.Repl().DbName, ""; got != want { + if got, want := dbc.Repl().connParams.DbName, ""; got != want { t.Errorf("dbc.Repl().DbName: %v, want %v", got, want) } } diff --git a/go/vt/dbconnpool/connection.go b/go/vt/dbconnpool/connection.go index e1a2890a41a..bf4de535c10 100644 --- a/go/vt/dbconnpool/connection.go +++ b/go/vt/dbconnpool/connection.go @@ -17,11 +17,10 @@ limitations under the License. package dbconnpool import ( + "context" "fmt" "time" - "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" @@ -116,20 +115,21 @@ func (dbc *DBConnection) ExecuteStreamFetch(query string, callback func(*sqltype // NewDBConnection returns a new DBConnection based on the ConnParams // and will use the provided stats to collect timing. -func NewDBConnection(info *mysql.ConnParams, mysqlStats *stats.Timings) (*DBConnection, error) { +func NewDBConnection(info dbconfigs.Connector, mysqlStats *stats.Timings) (*DBConnection, error) { start := time.Now() defer mysqlStats.Record("Connect", start) - params, err := dbconfigs.WithCredentials(info) + + ctx := context.Background() + params, err := info.MysqlParams() if err != nil { return nil, err } - ctx := context.Background() - if info.ConnectTimeoutMs != 0 { + if params.ConnectTimeoutMs != 0 { var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Duration(info.ConnectTimeoutMs)*time.Millisecond) + ctx, cancel = context.WithTimeout(ctx, time.Duration(params.ConnectTimeoutMs)*time.Millisecond) defer cancel() } - c, err := mysql.Connect(ctx, params) + c, err := info.Connect(ctx) if err != nil { mysqlStats.Record("ConnectError", start) } diff --git a/go/vt/dbconnpool/connection_pool.go b/go/vt/dbconnpool/connection_pool.go index 472f4942cde..76520467665 100644 --- a/go/vt/dbconnpool/connection_pool.go +++ b/go/vt/dbconnpool/connection_pool.go @@ -29,9 +29,9 @@ import ( "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/pools" "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" ) @@ -56,7 +56,7 @@ type ConnectionPool struct { resolutionFrequency time.Duration // info and mysqlStats are set at Open() time - info *mysql.ConnParams + info dbconfigs.Connector addresses []net.IP ticker *time.Ticker @@ -97,7 +97,7 @@ func (cp *ConnectionPool) pool() (p *pools.ResourcePool) { func (cp *ConnectionPool) refreshdns() { cp.mu.Lock() - host := cp.info.Host + host := cp.info.Host() cp.mu.Unlock() addrs, err := net.LookupHost(host) @@ -141,14 +141,14 @@ func (cp *ConnectionPool) validAddress(addr net.IP) bool { // ... // conn, err := pool.Get() // ... -func (cp *ConnectionPool) Open(info *mysql.ConnParams, mysqlStats *stats.Timings) { +func (cp *ConnectionPool) Open(info dbconfigs.Connector, mysqlStats *stats.Timings) { cp.mu.Lock() defer cp.mu.Unlock() cp.info = info cp.mysqlStats = mysqlStats cp.connections = pools.NewResourcePool(cp.connect, cp.capacity, cp.capacity, cp.idleTimeout, 0) // Check if we need to resolve a hostname (The Host is not just an IP address). - if cp.resolutionFrequency > 0 && net.ParseIP(info.Host) == nil { + if cp.resolutionFrequency > 0 && net.ParseIP(info.Host()) == nil { cp.hostIsNotIP = true cp.ticker = time.NewTicker(cp.resolutionFrequency) cp.stop = make(chan struct{}) diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index daead198112..0fe96edd0f7 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -111,7 +111,7 @@ func NewMysqld(dbcfgs *dbconfigs.DBConfigs) *Mysqld { // Create and open the connection pool for dba access. result.dbaPool = dbconnpool.NewConnectionPool("DbaConnPool", *dbaPoolSize, *dbaIdleTimeout, *poolDynamicHostnameResolution) - result.dbaPool.Open(dbcfgs.Dba(), dbaMysqlStats) + result.dbaPool.Open(dbcfgs.DbaWithDB(), dbaMysqlStats) // Create and open the connection pool for app access. result.appPool = dbconnpool.NewConnectionPool("AppConnPool", *appPoolSize, *appIdleTimeout, *poolDynamicHostnameResolution) @@ -274,7 +274,7 @@ func (mysqld *Mysqld) RunMysqlUpgrade() error { // privileges' right in the middle, and then subsequent // commands fail if we don't use valid credentials. So let's // use dba credentials. - params, err := dbconfigs.WithCredentials(mysqld.dbcfgs.Dba()) + params, err := mysqld.dbcfgs.Dba().MysqlParams() if err != nil { return err } @@ -441,7 +441,7 @@ func (mysqld *Mysqld) startNoWait(ctx context.Context, cnf *Mycnf, mysqldArgs .. // will use the dba credentials to try to connect. Use wait() with // different credentials if needed. func (mysqld *Mysqld) Wait(ctx context.Context, cnf *Mycnf) error { - params, err := dbconfigs.WithCredentials(mysqld.dbcfgs.Dba()) + params, err := mysqld.dbcfgs.Dba().MysqlParams() if err != nil { return err } @@ -531,7 +531,7 @@ func (mysqld *Mysqld) Shutdown(ctx context.Context, cnf *Mycnf, waitForMysqld bo if err != nil { return err } - params, err := dbconfigs.WithCredentials(mysqld.dbcfgs.Dba()) + params, err := mysqld.dbcfgs.Dba().MysqlParams() if err != nil { return err } diff --git a/go/vt/mysqlctl/replication.go b/go/vt/mysqlctl/replication.go index efdd4a859f8..4bcec1d8408 100644 --- a/go/vt/mysqlctl/replication.go +++ b/go/vt/mysqlctl/replication.go @@ -32,7 +32,6 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/hook" "vitess.io/vitess/go/vt/log" ) @@ -276,7 +275,7 @@ func (mysqld *Mysqld) SetSlavePosition(ctx context.Context, pos mysql.Position) // SetMaster makes the provided host / port the master. It optionally // stops replication before, and starts it after. func (mysqld *Mysqld) SetMaster(ctx context.Context, masterHost string, masterPort int, slaveStopBefore bool, slaveStartAfter bool) error { - params, err := dbconfigs.WithCredentials(mysqld.dbcfgs.Repl()) + params, err := mysqld.dbcfgs.Repl().MysqlParams() if err != nil { return err } diff --git a/go/vt/mysqlctl/schema.go b/go/vt/mysqlctl/schema.go index dd2a91e15d9..18706cb36ed 100644 --- a/go/vt/mysqlctl/schema.go +++ b/go/vt/mysqlctl/schema.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl/tmutils" @@ -38,7 +37,7 @@ var autoIncr = regexp.MustCompile(` AUTO_INCREMENT=\d+`) // executeSchemaCommands executes some SQL commands, using the mysql // command line tool. It uses the dba connection parameters, with credentials. func (mysqld *Mysqld) executeSchemaCommands(sql string) error { - params, err := dbconfigs.WithCredentials(mysqld.dbcfgs.Dba()) + params, err := mysqld.dbcfgs.Dba().MysqlParams() if err != nil { return err } diff --git a/go/vt/vtexplain/vtexplain_vttablet.go b/go/vt/vtexplain/vtexplain_vttablet.go index f66603b230a..a51841dae31 100644 --- a/go/vt/vtexplain/vtexplain_vttablet.go +++ b/go/vt/vtexplain/vtexplain_vttablet.go @@ -94,7 +94,9 @@ func newTablet(opts *Options, t *topodatapb.Tablet) *explainTablet { }, ) - dbcfgs := dbconfigs.NewTestDBConfigs(*db.ConnParams(), *db.ConnParams(), "") + params, _ := db.ConnParams().MysqlParams() + cp := *params + dbcfgs := dbconfigs.NewTestDBConfigs(cp, cp, "") cnf := mysqlctl.NewMycnf(22222, 6802) cnf.ServerID = 33333 diff --git a/go/vt/vttablet/heartbeat/reader_test.go b/go/vt/vttablet/heartbeat/reader_test.go index 3e9a97e1f4a..152a28a9b7c 100644 --- a/go/vt/vttablet/heartbeat/reader_test.go +++ b/go/vt/vttablet/heartbeat/reader_test.go @@ -106,7 +106,9 @@ func newReader(db *fakesqldb.DB, nowFunc func() time.Time) *Reader { config := tabletenv.DefaultQsConfig config.HeartbeatEnable = true config.PoolNamePrefix = fmt.Sprintf("Pool-%d-", randID) - dbc := dbconfigs.NewTestDBConfigs(*db.ConnParams(), *db.ConnParams(), "") + params, _ := db.ConnParams().MysqlParams() + cp := *params + dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") tr := NewReader(&fakeMysqlChecker{}, config) tr.dbName = sqlescape.EscapeID(dbc.SidecarDBName.Get()) diff --git a/go/vt/vttablet/heartbeat/writer.go b/go/vt/vttablet/heartbeat/writer.go index 9bc75128e61..a9dbae08799 100644 --- a/go/vt/vttablet/heartbeat/writer.go +++ b/go/vt/vttablet/heartbeat/writer.go @@ -25,7 +25,6 @@ import ( "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" @@ -105,6 +104,7 @@ func (w *Writer) Init(target querypb.Target) error { log.Info("Initializing heartbeat table.") w.dbName = sqlescape.EscapeID(w.dbconfigs.SidecarDBName.Get()) w.keyspaceShard = fmt.Sprintf("%s:%s", target.Keyspace, target.Shard) + err := w.initializeTables(w.dbconfigs.DbaWithDB()) if err != nil { w.recordError(err) @@ -155,7 +155,7 @@ func (w *Writer) Close() { // or master. For that reason, we use values that are common between them, such as keyspace:shard, // and we also execute them with an isolated connection that turns off the binlog and // is closed at the end. -func (w *Writer) initializeTables(cp *mysql.ConnParams) error { +func (w *Writer) initializeTables(cp dbconfigs.Connector) error { conn, err := dbconnpool.NewDBConnection(cp, stats.NewTimings("", "", "")) if err != nil { return vterrors.Wrap(err, "Failed to create connection for heartbeat") diff --git a/go/vt/vttablet/heartbeat/writer_test.go b/go/vt/vttablet/heartbeat/writer_test.go index df85ef83a2c..0b98e0ef2ed 100644 --- a/go/vt/vttablet/heartbeat/writer_test.go +++ b/go/vt/vttablet/heartbeat/writer_test.go @@ -111,7 +111,9 @@ func newTestWriter(db *fakesqldb.DB, nowFunc func() time.Time) *Writer { config.HeartbeatEnable = true config.PoolNamePrefix = fmt.Sprintf("Pool-%d-", randID) - dbc := dbconfigs.NewTestDBConfigs(*db.ConnParams(), *db.ConnParams(), "") + params, _ := db.ConnParams().MysqlParams() + cp := *params + dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") tw := NewWriter(&fakeMysqlChecker{}, topodatapb.TabletAlias{Cell: "test", Uid: 1111}, diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index 8ccf55c79bb..349e0d336c5 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -298,7 +298,7 @@ func NewActionAgent( var mysqlHost string var mysqlPort int32 - if appConfig := dbcfgs.AppWithDB(); appConfig.Host != "" { + if appConfig, _ := dbcfgs.AppWithDB().MysqlParams(); appConfig.Host != "" { mysqlHost = appConfig.Host mysqlPort = int32(appConfig.Port) @@ -324,10 +324,11 @@ func NewActionAgent( vreplication.InitVStreamerClient(agent.DBConfigs) // The db name is set by the Start function called above + filteredWithDBParams, _ := agent.DBConfigs.FilteredWithDB().MysqlParams() agent.VREngine = vreplication.NewEngine(ts, tabletAlias.Cell, mysqld, func() binlogplayer.DBClient { return binlogplayer.NewDBClient(agent.DBConfigs.FilteredWithDB()) }, - agent.DBConfigs.FilteredWithDB().DbName, + filteredWithDBParams.DbName, ) servenv.OnTerm(agent.VREngine.Close) diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 4cbcc35ea5d..9d5efb00d0a 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -346,7 +346,10 @@ func (dbc *realDBClient) DBName() string { } func (dbc *realDBClient) Connect() error { - app := env.Dbcfgs.AppWithDB() + app, err := env.Dbcfgs.AppWithDB().MysqlParams() + if err != nil { + return err + } app.DbName = vrepldb conn, err := mysql.Connect(context.Background(), app) if err != nil { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go index cf5f604166f..783f043b94a 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go @@ -23,7 +23,6 @@ import ( "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/grpcclient" @@ -78,7 +77,7 @@ type MySQLVStreamerClient struct { isOpen bool - sourceConnParams *mysql.ConnParams + sourceConnParams dbconfigs.Connector sourceSe *schema.Engine } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go index 1a50bfe18dc..d52703a6e8f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go @@ -27,6 +27,7 @@ import ( "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/vt/dbconfigs" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" @@ -272,7 +273,7 @@ func TestNewMySQLVStreamerClient(t *testing.T) { { name: "sets conn params for MySQLVStreamerClient ", want: &MySQLVStreamerClient{ - sourceConnParams: env.Dbcfgs.ExternalReplWithDB(), + sourceConnParams: dbcfgs.ExternalReplWithDB(), }, }, } @@ -286,8 +287,12 @@ func TestNewMySQLVStreamerClient(t *testing.T) { } func TestMySQLVStreamerClientOpen(t *testing.T) { + dbc := dbconfigs.New(&mysql.ConnParams{ + Host: "invalidhost", + Port: 3306, + }) type fields struct { - sourceConnParams *mysql.ConnParams + sourceConnParams dbconfigs.Connector } type args struct { ctx context.Context @@ -301,7 +306,7 @@ func TestMySQLVStreamerClientOpen(t *testing.T) { { name: "initializes streamer correctly", fields: fields{ - sourceConnParams: env.Dbcfgs.ExternalReplWithDB(), + sourceConnParams: dbcfgs.ExternalReplWithDB(), }, args: args{ ctx: context.Background(), @@ -310,10 +315,7 @@ func TestMySQLVStreamerClientOpen(t *testing.T) { { name: "returns error when invalid conn params are provided", fields: fields{ - sourceConnParams: &mysql.ConnParams{ - Host: "invalidhost", - Port: 3306, - }, + sourceConnParams: dbc, }, args: args{ ctx: context.Background(), @@ -354,7 +356,7 @@ func TestMySQLVStreamerClientOpen(t *testing.T) { func TestMySQLVStreamerClientClose(t *testing.T) { type fields struct { isOpen bool - sourceConnParams *mysql.ConnParams + sourceConnParams dbconfigs.Connector } type args struct { ctx context.Context @@ -369,7 +371,7 @@ func TestMySQLVStreamerClientClose(t *testing.T) { { name: "closes engine correctly", fields: fields{ - sourceConnParams: env.Dbcfgs.ExternalReplWithDB(), + sourceConnParams: dbcfgs.ExternalReplWithDB(), }, args: args{ ctx: context.Background(), @@ -409,7 +411,7 @@ func TestMySQLVStreamerClientClose(t *testing.T) { func TestMySQLVStreamerClientVStream(t *testing.T) { vsClient := &MySQLVStreamerClient{ - sourceConnParams: env.Dbcfgs.ExternalReplWithDB(), + sourceConnParams: dbcfgs.ExternalReplWithDB(), } filter := &binlogdatapb.Filter{ @@ -469,7 +471,7 @@ func TestMySQLVStreamerClientVStream(t *testing.T) { func TestMySQLVStreamerClientVStreamRows(t *testing.T) { vsClient := &MySQLVStreamerClient{ - sourceConnParams: env.Dbcfgs.ExternalReplWithDB(), + sourceConnParams: dbcfgs.ExternalReplWithDB(), } eventsChan := make(chan *querypb.Row, 1000) diff --git a/go/vt/vttablet/tabletserver/connpool/dbconn.go b/go/vt/vttablet/tabletserver/connpool/dbconn.go index 668dda6c6c2..d5f9a6c20d3 100644 --- a/go/vt/vttablet/tabletserver/connpool/dbconn.go +++ b/go/vt/vttablet/tabletserver/connpool/dbconn.go @@ -22,6 +22,7 @@ import ( "sync" "time" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/vterrors" "golang.org/x/net/context" @@ -54,7 +55,7 @@ const ( // It will also trigger a CheckMySQL whenever applicable. type DBConn struct { conn *dbconnpool.DBConnection - info *mysql.ConnParams + info dbconfigs.Connector dbaPool *dbconnpool.ConnectionPool pool *Pool current sync2.AtomicString @@ -63,7 +64,7 @@ type DBConn struct { // NewDBConn creates a new DBConn. It triggers a CheckMySQL if creation fails. func NewDBConn( cp *Pool, - appParams *mysql.ConnParams) (*DBConn, error) { + appParams dbconfigs.Connector) (*DBConn, error) { c, err := dbconnpool.NewDBConnection(appParams, tabletenv.MySQLStats) if err != nil { cp.checker.CheckMySQL() @@ -78,7 +79,7 @@ func NewDBConn( } // NewDBConnNoPool creates a new DBConn without a pool. -func NewDBConnNoPool(params *mysql.ConnParams, dbaPool *dbconnpool.ConnectionPool) (*DBConn, error) { +func NewDBConnNoPool(params dbconfigs.Connector, dbaPool *dbconnpool.ConnectionPool) (*DBConn, error) { c, err := dbconnpool.NewDBConnection(params, tabletenv.MySQLStats) if err != nil { return nil, err diff --git a/go/vt/vttablet/tabletserver/connpool/pool.go b/go/vt/vttablet/tabletserver/connpool/pool.go index 1c32929e91d..f9fc025150d 100644 --- a/go/vt/vttablet/tabletserver/connpool/pool.go +++ b/go/vt/vttablet/tabletserver/connpool/pool.go @@ -22,11 +22,11 @@ import ( "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/pools" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/trace" "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/dbconnpool" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/vterrors" @@ -66,7 +66,7 @@ type Pool struct { idleTimeout time.Duration dbaPool *dbconnpool.ConnectionPool checker MySQLChecker - appDebugParams *mysql.ConnParams + appDebugParams dbconfigs.Connector } // New creates a new Pool. The name is used @@ -110,7 +110,7 @@ func (cp *Pool) pool() (p *pools.ResourcePool) { } // Open must be called before starting to use the pool. -func (cp *Pool) Open(appParams, dbaParams, appDebugParams *mysql.ConnParams) { +func (cp *Pool) Open(appParams, dbaParams, appDebugParams dbconfigs.Connector) { cp.mu.Lock() defer cp.mu.Unlock() @@ -307,9 +307,13 @@ func (cp *Pool) Exhausted() int64 { } func (cp *Pool) isCallerIDAppDebug(ctx context.Context) bool { - if cp.appDebugParams == nil || cp.appDebugParams.Uname == "" { + params, err := cp.appDebugParams.MysqlParams() + if err != nil { + return false + } + if params == nil || params.Uname == "" { return false } callerID := callerid.ImmediateCallerIDFromContext(ctx) - return callerID != nil && callerID.Username == cp.appDebugParams.Uname + return callerID != nil && callerID.Username == params.Uname } diff --git a/go/vt/vttablet/tabletserver/messager/engine.go b/go/vt/vttablet/tabletserver/messager/engine.go index 58c048835f3..3be00a2a2db 100644 --- a/go/vt/vttablet/tabletserver/messager/engine.go +++ b/go/vt/vttablet/tabletserver/messager/engine.go @@ -86,6 +86,7 @@ func (me *Engine) Open() error { if me.isOpen { return nil } + me.conns.Open(me.dbconfigs.AppWithDB(), me.dbconfigs.DbaWithDB(), me.dbconfigs.AppDebugWithDB()) me.se.RegisterNotifier("messages", me.schemaChanged) me.isOpen = true diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index e345fd0807c..6bacdd82c1e 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -260,7 +260,9 @@ func newTestEngine(db *fakesqldb.DB) *Engine { tsv := newFakeTabletServer() se := schema.NewEngine(tsv, config) te := NewEngine(tsv, se, config) - te.InitDBConfig(dbconfigs.NewTestDBConfigs(*db.ConnParams(), *db.ConnParams(), "")) + params, _ := db.ConnParams().MysqlParams() + cp := *params + te.InitDBConfig(dbconfigs.NewTestDBConfigs(cp, cp, "")) te.Open() return te } diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index a1842a6fd09..7c88c81e9cf 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -795,7 +795,10 @@ func (fts *fakeTabletServer) PurgeMessages(ctx context.Context, target *querypb. func newMMConnPool(db *fakesqldb.DB) *connpool.Pool { pool := connpool.New("", 20, 0, time.Duration(10*time.Minute), newFakeTabletServer()) - dbconfigs := dbconfigs.NewTestDBConfigs(*db.ConnParams(), *db.ConnParams(), "") + params, _ := db.ConnParams().MysqlParams() + cp := *params + dbconfigs := dbconfigs.NewTestDBConfigs(cp, cp, "") + pool.Open(dbconfigs.AppWithDB(), dbconfigs.DbaWithDB(), dbconfigs.AppDebugWithDB()) return pool } diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index b77e49edab2..16d82967940 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -436,7 +436,7 @@ func (qe *QueryEngine) ClearQueryPlanCache() { // IsMySQLReachable returns true if we can connect to MySQL. func (qe *QueryEngine) IsMySQLReachable() bool { - conn, err := dbconnpool.NewDBConnection(qe.dbconfigs.AppWithDB(), tabletenv.MySQLStats) + conn, err := dbconnpool.NewDBConnection(qe.dbconfigs.DbaWithDB(), tabletenv.MySQLStats) if err != nil { if mysql.IsConnErr(err) { return false diff --git a/go/vt/vttablet/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go index e97e37c7d43..3a65508615a 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher.go +++ b/go/vt/vttablet/tabletserver/replication_watcher.go @@ -114,6 +114,7 @@ func (rpw *ReplicationWatcher) Process(ctx context.Context, dbconfigs *dbconfigs tabletenv.LogError() rpw.wg.Done() }() + for { log.Infof("Starting a binlog Streamer from current replication position to monitor binlogs") streamer := binlog.NewStreamer(dbconfigs.DbaWithDB(), rpw.se, nil /*clientCharset*/, mysql.Position{}, 0 /*timestamp*/, func(eventToken *querypb.EventToken, statements []binlog.FullBinlogStatement) error { diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index a1c26efc27e..53d6aee2f83 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -32,6 +32,7 @@ import ( "vitess.io/vitess/go/stats" "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/concurrency" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" @@ -48,7 +49,7 @@ type notifier func(full map[string]*Table, created, altered, dropped []string) // Engine stores the schema info and performs operations that // keep itself up-to-date. type Engine struct { - cp *mysql.ConnParams + cp dbconfigs.Connector // mu protects the following fields. mu sync.Mutex @@ -100,7 +101,7 @@ func NewEngine(checker connpool.MySQLChecker, config tabletenv.TabletConfig) *En } // InitDBConfig must be called before Open. -func (se *Engine) InitDBConfig(cp *mysql.ConnParams) { +func (se *Engine) InitDBConfig(cp dbconfigs.Connector) { se.cp = cp } diff --git a/go/vt/vttablet/tabletserver/schema/engine_test.go b/go/vt/vttablet/tabletserver/schema/engine_test.go index a25a4dac0a7..ef45c3fb144 100644 --- a/go/vt/vttablet/tabletserver/schema/engine_test.go +++ b/go/vt/vttablet/tabletserver/schema/engine_test.go @@ -417,5 +417,7 @@ func newEngine(queryPlanCacheSize int, reloadTime time.Duration, idleTimeout tim } func newDBConfigs(db *fakesqldb.DB) *dbconfigs.DBConfigs { - return dbconfigs.NewTestDBConfigs(*db.ConnParams(), *db.ConnParams(), "") + params, _ := db.ConnParams().MysqlParams() + cp := *params + return dbconfigs.NewTestDBConfigs(cp, cp, "") } diff --git a/go/vt/vttablet/tabletserver/testutils_test.go b/go/vt/vttablet/tabletserver/testutils_test.go index ef3afb94438..58980c9f2b9 100644 --- a/go/vt/vttablet/tabletserver/testutils_test.go +++ b/go/vt/vttablet/tabletserver/testutils_test.go @@ -50,7 +50,9 @@ func (util *testUtils) checkEqual(t *testing.T, expected interface{}, result int } func (util *testUtils) newDBConfigs(db *fakesqldb.DB) *dbconfigs.DBConfigs { - return dbconfigs.NewTestDBConfigs(*db.ConnParams(), *db.ConnParams(), "") + params, _ := db.ConnParams().MysqlParams() + cp := *params + return dbconfigs.NewTestDBConfigs(cp, cp, "") } func (util *testUtils) newQueryServiceConfig() tabletenv.TabletConfig { diff --git a/go/vt/vttablet/tabletserver/twopc.go b/go/vt/vttablet/tabletserver/twopc.go index 68bf9949e56..2be55ee5687 100644 --- a/go/vt/vttablet/tabletserver/twopc.go +++ b/go/vt/vttablet/tabletserver/twopc.go @@ -22,7 +22,6 @@ import ( "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" @@ -127,7 +126,7 @@ func NewTwoPC(readPool *connpool.Pool) *TwoPC { // Init initializes TwoPC. If the metadata database or tables // are not present, they are created. -func (tpc *TwoPC) Init(sidecarDBName string, dbaparams *mysql.ConnParams) error { +func (tpc *TwoPC) Init(sidecarDBName string, dbaparams dbconfigs.Connector) error { dbname := sqlescape.EscapeID(sidecarDBName) conn, err := dbconnpool.NewDBConnection(dbaparams, stats.NewTimings("", "", "")) if err != nil { diff --git a/go/vt/vttablet/tabletserver/tx_pool.go b/go/vt/vttablet/tabletserver/tx_pool.go index 42de72c3f0d..6c050a5a002 100644 --- a/go/vt/vttablet/tabletserver/tx_pool.go +++ b/go/vt/vttablet/tabletserver/tx_pool.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/timer" "vitess.io/vitess/go/trace" "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" @@ -140,12 +141,13 @@ func NewTxPool( // Open makes the TxPool operational. This also starts the transaction killer // that will kill long-running transactions. -func (axp *TxPool) Open(appParams, dbaParams, appDebugParams *mysql.ConnParams) { +func (axp *TxPool) Open(appParams, dbaParams, appDebugParams dbconfigs.Connector) { log.Infof("Starting transaction id: %d", axp.lastID) axp.conns.Open(appParams, dbaParams, appDebugParams) - foundRowsParam := *appParams + foundRowsParam, _ := appParams.MysqlParams() foundRowsParam.EnableClientFoundRows() - axp.foundRowsPool.Open(&foundRowsParam, dbaParams, appDebugParams) + appParams = dbconfigs.New(foundRowsParam) + axp.foundRowsPool.Open(appParams, dbaParams, appDebugParams) axp.ticks.Start(func() { axp.transactionKiller() }) } diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index 6871172c102..5afe7558cc7 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -25,9 +25,9 @@ import ( "sync" "vitess.io/vitess/go/acl" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/topo" @@ -47,7 +47,7 @@ var ( // Engine is the engine for handling vreplication streaming requests. type Engine struct { // cp is initialized by InitDBConfig - cp *mysql.ConnParams + cp dbconfigs.Connector // mu protects isOpen, streamers, streamIdx and vschema. mu sync.Mutex @@ -96,7 +96,7 @@ func NewEngine(ts srvtopo.Server, se *schema.Engine) *Engine { } // InitDBConfig performs saves the required info from dbconfigs for future use. -func (vse *Engine) InitDBConfig(cp *mysql.ConnParams) { +func (vse *Engine) InitDBConfig(cp dbconfigs.Connector) { vse.cp = cp } diff --git a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go index 44892ee821b..141b26f3fa4 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer.go @@ -20,8 +20,8 @@ import ( "context" "fmt" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" "vitess.io/vitess/go/vt/sqlparser" ) @@ -34,13 +34,13 @@ type resultStreamer struct { ctx context.Context cancel func() - cp *mysql.ConnParams + cp dbconfigs.Connector query string tableName sqlparser.TableIdent send func(*binlogdatapb.VStreamResultsResponse) error } -func newResultStreamer(ctx context.Context, cp *mysql.ConnParams, query string, send func(*binlogdatapb.VStreamResultsResponse) error) *resultStreamer { +func newResultStreamer(ctx context.Context, cp dbconfigs.Connector, query string, send func(*binlogdatapb.VStreamResultsResponse) error) *resultStreamer { ctx, cancel := context.WithCancel(ctx) return &resultStreamer{ ctx: ctx, diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go index b1ed30469b0..8982ce4591d 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go @@ -20,8 +20,8 @@ import ( "context" "fmt" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -38,7 +38,7 @@ type RowStreamer interface { } // NewRowStreamer returns a RowStreamer -func NewRowStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, query string, lastpk []sqltypes.Value, send func(*binlogdatapb.VStreamRowsResponse) error) RowStreamer { +func NewRowStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, query string, lastpk []sqltypes.Value, send func(*binlogdatapb.VStreamRowsResponse) error) RowStreamer { return newRowStreamer(ctx, cp, se, query, lastpk, &localVSchema{vschema: &vindexes.VSchema{}}, send) } @@ -54,7 +54,7 @@ type rowStreamer struct { ctx context.Context cancel func() - cp *mysql.ConnParams + cp dbconfigs.Connector se *schema.Engine query string lastpk []sqltypes.Value @@ -66,7 +66,7 @@ type rowStreamer struct { sendQuery string } -func newRowStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, query string, lastpk []sqltypes.Value, vschema *localVSchema, send func(*binlogdatapb.VStreamRowsResponse) error) *rowStreamer { +func newRowStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, query string, lastpk []sqltypes.Value, vschema *localVSchema, send func(*binlogdatapb.VStreamRowsResponse) error) *rowStreamer { ctx, cancel := context.WithCancel(ctx) return &rowStreamer{ ctx: ctx, diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index 9942108a961..ab294562dde 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -156,10 +156,14 @@ func TestStreamRowsUnicode(t *testing.T) { t.Fatal(err) } - savecp := *engine.cp + savecp := engine.cp // Rowstreamer must override this to "binary" - engine.cp.Charset = "latin1" - defer func() { engine.cp = &savecp }() + params, err := engine.cp.MysqlParams() + if err != nil { + t.Fatal(err) + } + params.Charset = "latin1" + defer func() { engine.cp = savecp }() err = engine.StreamRows(context.Background(), "select * from t1", nil, func(rows *binlogdatapb.VStreamRowsResponse) error { // Skip fields. if len(rows.Rows) == 0 { diff --git a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go index cf3954f00a2..7dfcdfbe68b 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go +++ b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn.go @@ -30,10 +30,10 @@ import ( // reading a table along with a gtid snapshot. type snapshotConn struct { *mysql.Conn - cp *mysql.ConnParams + cp dbconfigs.Connector } -func snapshotConnect(ctx context.Context, cp *mysql.ConnParams) (*snapshotConn, error) { +func snapshotConnect(ctx context.Context, cp dbconfigs.Connector) (*snapshotConn, error) { mconn, err := mysqlConnect(ctx, cp) if err != nil { return nil, err @@ -106,10 +106,6 @@ func (conn *snapshotConn) Close() { conn.Conn.Close() } -func mysqlConnect(ctx context.Context, cp *mysql.ConnParams) (*mysql.Conn, error) { - cp, err := dbconfigs.WithCredentials(cp) - if err != nil { - return nil, err - } - return mysql.Connect(ctx, cp) +func mysqlConnect(ctx context.Context, cp dbconfigs.Connector) (*mysql.Conn, error) { + return cp.Connect(ctx) } diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 438d7f3983e..467b57e0953 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -53,7 +53,7 @@ type VStreamer interface { } // NewVStreamer returns a VStreamer. -func NewVStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) VStreamer { +func NewVStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) VStreamer { return newVStreamer(ctx, cp, se, startPos, filter, &localVSchema{vschema: &vindexes.VSchema{}}, send) } @@ -70,7 +70,7 @@ type vstreamer struct { ctx context.Context cancel func() - cp *mysql.ConnParams + cp dbconfigs.Connector se *schema.Engine startPos string filter *binlogdatapb.Filter @@ -112,7 +112,7 @@ type streamerPlan struct { // Other constructs like joins, group by, etc. are not supported. // vschema: the current vschema. This value can later be changed through the SetVSchema method. // send: callback function to send events. -func newVStreamer(ctx context.Context, cp *mysql.ConnParams, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { +func newVStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { ctx, cancel := context.WithCancel(ctx) return &vstreamer{ ctx: ctx, @@ -186,11 +186,7 @@ func (vs *vstreamer) Stream() error { } func (vs *vstreamer) currentPosition() (mysql.Position, error) { - cp, err := dbconfigs.WithCredentials(vs.cp) - if err != nil { - return mysql.Position{}, err - } - conn, err := mysql.Connect(vs.ctx, cp) + conn, err := vs.cp.Connect(vs.ctx) if err != nil { return mysql.Position{}, err } @@ -363,6 +359,12 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e if err != nil { return nil, fmt.Errorf("can't strip checksum from binlog event: %v, event data: %#v", err, ev) } + + // Get the DbName for vstreamer + params, err := vs.cp.MysqlParams() + if err != nil { + return nil, err + } var vevents []*binlogdatapb.VEvent switch { case ev.IsGTID(): @@ -392,7 +394,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e // could be using SBR. Vitess itself will never run into cases where it needs to consume non rbr statements. switch cat := sqlparser.Preview(q.SQL); cat { case sqlparser.StmtInsert: - mustSend := mustSendStmt(q, vs.cp.DbName) + mustSend := mustSendStmt(q, params.DbName) if mustSend { vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_INSERT, @@ -400,7 +402,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e }) } case sqlparser.StmtUpdate: - mustSend := mustSendStmt(q, vs.cp.DbName) + mustSend := mustSendStmt(q, params.DbName) if mustSend { vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_UPDATE, @@ -408,7 +410,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e }) } case sqlparser.StmtDelete: - mustSend := mustSendStmt(q, vs.cp.DbName) + mustSend := mustSendStmt(q, params.DbName) if mustSend { vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_DELETE, @@ -416,7 +418,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e }) } case sqlparser.StmtReplace: - mustSend := mustSendStmt(q, vs.cp.DbName) + mustSend := mustSendStmt(q, params.DbName) if mustSend { vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_REPLACE, @@ -432,7 +434,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e Type: binlogdatapb.VEventType_COMMIT, }) case sqlparser.StmtDDL: - if mustSendDDL(q, vs.cp.DbName, vs.filter) { + if mustSendDDL(q, params.DbName, vs.filter) { vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_GTID, Gtid: mysql.EncodePosition(vs.pos), @@ -486,7 +488,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e // A journal is a special case that generates a JOURNAL event. return nil, vs.buildJournalPlan(id, tm) } - if tm.Database != "" && tm.Database != vs.cp.DbName { + if tm.Database != "" && tm.Database != params.DbName { vs.plans[id] = nil return nil, nil } @@ -627,6 +629,11 @@ func (vs *vstreamer) buildTableColumns(id uint64, tm *mysql.TableMap) ([]schema. } func (vs *vstreamer) processJounalEvent(vevents []*binlogdatapb.VEvent, plan *streamerPlan, rows mysql.Rows) ([]*binlogdatapb.VEvent, error) { + // Get DbName + params, err := vs.cp.MysqlParams() + if err != nil { + return nil, err + } nextrow: for _, row := range rows.Rows { afterOK, afterValues, err := vs.extractRowAndFilter(plan, row.Data, rows.DataColumns, row.NullColumns) @@ -639,7 +646,7 @@ nextrow: } // Exclude events that don't match the db_name. for i, fld := range plan.fields() { - if fld.Name == "db_name" && afterValues[i].ToString() != vs.cp.DbName { + if fld.Name == "db_name" && afterValues[i].ToString() != params.DbName { continue nextrow } } diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index abcab7cf476..154ca416a42 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -117,8 +117,12 @@ func TestStatements(t *testing.T) { runCases(t, nil, testcases, "current") // Test FilePos flavor - engine.cp.Flavor = "FilePos" - defer func() { engine.cp.Flavor = "" }() + params, err := engine.cp.MysqlParams() + if err != nil { + t.Fatal(err) + } + params.Flavor = "FilePos" + defer func() { params.Flavor = "" }() runCases(t, nil, testcases, "current") } @@ -183,8 +187,13 @@ func TestOther(t *testing.T) { customRun("gtid") // Test FilePos flavor - engine.cp.Flavor = "FilePos" - defer func() { engine.cp.Flavor = "" }() + params, err := engine.cp.MysqlParams() + if err != nil { + t.Fatal(err) + } + params.Flavor = "FilePos" + + defer func() { params.Flavor = "" }() customRun("filePos") } @@ -1266,7 +1275,11 @@ func masterPosition(t *testing.T) string { // We use the engine's cp because there is one test that overrides // the flavor to FilePos. If so, we have to obtain the position // in that flavor format. - conn, err := mysql.Connect(context.Background(), engine.cp) + connParam, err := engine.cp.MysqlParams() + if err != nil { + t.Fatal(err) + } + conn, err := mysql.Connect(context.Background(), connParam) if err != nil { t.Fatal(err) } From eae25db5388f08ebfdf2b215b18a792026ba22b8 Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Thu, 5 Mar 2020 10:41:28 -0800 Subject: [PATCH 225/825] Pass back error when failed to read key file Signed-off-by: Dan Kozlowski --- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index 71365906b31..83001836287 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -70,8 +70,11 @@ func azCredentials() (*azblob.SharedKeyCredential, error) { var actKey string if len(*accountKeyFile) > 0 { - if dat, err := ioutil.ReadFile(*accountKeyFile); err != nil { + log.Infof("Getting account crednetials from file: %s", *accountKeyFile) + if dat, err := ioutil.ReadFile(*accountKeyFile); err == nil { actKey = string(dat) + } else { + return nil, err } } else { actKey = os.Getenv("VITESS_AZBLOB_ACCOUNT_KEY") From 399a75afa13d1d9c27bbeba0171b9db1fc045947 Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Thu, 5 Mar 2020 11:07:41 -0800 Subject: [PATCH 226/825] Chaging credentials type to use actual methods not global variables Signed-off-by: Dan Kozlowski --- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index 83001836287..b2e335a9c2c 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -86,7 +86,7 @@ func azCredentials() (*azblob.SharedKeyCredential, error) { return azblob.NewSharedKeyCredential(actName, actKey) } -func azServiceURL(credentials azblob.Credential) azblob.ServiceURL { +func azServiceURL(credentials *azblob.SharedKeyCredential) azblob.ServiceURL { pipeline := azblob.NewPipeline(credentials, azblob.PipelineOptions{ Retry: azblob.RetryOptions{ Policy: azblob.RetryPolicyFixed, @@ -96,7 +96,7 @@ func azServiceURL(credentials azblob.Credential) azblob.ServiceURL { TryTimeout: 4 * time.Hour, }, }) - u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/", *accountName)) + u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/", credentials.AccountName())) return azblob.NewServiceURL(*u, pipeline) } From 5dfc757f1172efab2b79bfd2edccdc73ff42541f Mon Sep 17 00:00:00 2001 From: Mingjian Liu Date: Thu, 5 Mar 2020 14:56:59 -0800 Subject: [PATCH 227/825] Add more log when Insertion failed on Map Signed-off-by: Mingjian Liu --- go/vt/vtgate/engine/insert.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/go/vt/vtgate/engine/insert.go b/go/vt/vtgate/engine/insert.go index ec2a54f106e..a15c2c0e668 100644 --- a/go/vt/vtgate/engine/insert.go +++ b/go/vt/vtgate/engine/insert.go @@ -588,16 +588,22 @@ func (ins *Insert) processUnowned(vcursor VCursor, vindexColumnsKeys [][]sqltype if err != nil { return err } + + var mismatchVindexKeys [][]sqltypes.Value for i, v := range verified { + rowNum := verifyIndexes[i] if !v { if ins.Opcode != InsertShardedIgnore { - return fmt.Errorf("values %v for column %v does not map to keyspace ids", vindexColumnsKeys, colVindex.Columns) + mismatchVindexKeys = append(mismatchVindexKeys, vindexColumnsKeys[rowNum]) + continue } // InsertShardedIgnore: skip the row. ksids[verifyIndexes[i]] = nil - continue } } + if len(mismatchVindexKeys) > 0 { + return fmt.Errorf("values %v for column %v does not map to keyspace ids", mismatchVindexKeys, colVindex.Columns) + } } return nil } From 9597f6ad76de953115becf3c1804d1d8e4e28988 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Thu, 5 Mar 2020 16:11:11 -0700 Subject: [PATCH 228/825] PR Feedback Signed-off-by: Morgan Tocker --- SECURITY.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 601608c6d80..8720f50653a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -9,7 +9,7 @@ issues. Security vulnerabilities should be handled quickly and sometimes privately. The primary goal of this process is to reduce the total time users are vulnerable to publicly known exploits. -The Vitess [maintainers](MAINTAINERS.md) team is responsible for the entire response including internal communication and external disclosure. In the future, we may delagate responsibility to a sub-team as other projects have elected to do so. +The Vitess [maintainers](MAINTAINERS.md) team is responsible for the entire response including internal communication and external disclosure. In the future, we may delegate responsibility to a sub-team as other projects have elected to do so. ## Disclosures @@ -20,11 +20,11 @@ The Vitess community asks that all suspected vulnerabilities be privately and re ### Public Disclosure Processes If you know of a publicly disclosed security vulnerability please IMMEDIATELY email -[vitess-maintainers](mailto:cncf-vitess-maintainers@lists.cncf.io) to inform about the vulnerability so that we may start the patch, release, and communication process. +[vitess-maintainers](mailto:cncf-vitess-maintainers@lists.cncf.io) so that we may start the patch, release, and communication process. ## Patch, Release, and Public Communication -For each vulnerability a member of the maintainers team will volunteer to lead coordination of the fix (Fix Lead), and ensure that it is backported to each supported branch. They will then coordinate with the remainder of the maintainers team to coordinate new releases and ensure a communication plan is in place for vulnerability disclosure. +For each reported vulnerability, a member of the maintainers team will volunteer to lead coordination of the fix (Fix Lead), and ensure that it is backported to each supported branch. They will then coordinate with the remainder of the maintainers team to coordinate new releases and ensure a communication plan is in place for vulnerability disclosure. All of the timelines below are suggestions and assume a private disclosure. The Fix Lead drives the schedule using their best judgment based on severity and development time. If the Fix Lead is From 3c84f08415bbfe6ef59dcf9bdf30c5f41966e4c2 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Thu, 5 Mar 2020 17:42:40 -0700 Subject: [PATCH 229/825] Add other related circumstances Signed-off-by: Morgan Tocker --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 8720f50653a..d51899fb5f2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -63,7 +63,7 @@ These steps should be completed within the 1-7 days of Disclosure. If the CVSS score is under 4.0 ([a low severity score](https://www.first.org/cvss/specification-document#i5)) the maintainers can decide to slow the -release process down in the face of holidays, developer bandwidth, etc. +release process down in the face of holidays, developer bandwidth and other related circumstances. ### Fix Disclosure Process From e29d8f1da20c3e26ff008e36e0a304f6e4a1f2af Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Thu, 5 Mar 2020 17:51:45 -0700 Subject: [PATCH 230/825] Address feedback on stable versioning Signed-off-by: Morgan Tocker --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index d51899fb5f2..c524bdc11de 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -35,7 +35,7 @@ best protect our users. #### Policy for master-only vulnerabilities -If a security vulnerability only affects master, but not a stable release (i.e. Vitess 5) then the following process will apply: +If a security vulnerability affects master, but not a currently supported branch, then the following process will apply: * The fix will land in master. * A courtesy email will be sent to [vitess@googlegroups.com](https://groups.google.com/forum/#!forum/vitess) along with a posted notice in #developers on Slack. @@ -49,7 +49,7 @@ If a security vulnerability affects only a stable release which is no longer und #### Policy for supported releases -If a security vulnerability affects supported branches (i.e. Vitess 5), then a Fix Lead will be appointed and the full security process as defined below will apply. +If a security vulnerability affects supported branches, then a Fix Lead will be appointed and the full security process as defined below will apply. ### Fix Development Process From 5b07a289085eac70bea061c4959edddd347f398f Mon Sep 17 00:00:00 2001 From: pradip parmar Date: Thu, 5 Mar 2020 13:12:21 +0530 Subject: [PATCH 231/825] testcase docker run fixed. failing because topo was not able to create err file. Signed-off-by: pradip parmar --- go/test/endtoend/cluster/topo_process.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/go/test/endtoend/cluster/topo_process.go b/go/test/endtoend/cluster/topo_process.go index 2691497e42f..7e2a063b1c4 100644 --- a/go/test/endtoend/cluster/topo_process.go +++ b/go/test/endtoend/cluster/topo_process.go @@ -75,7 +75,15 @@ func (topo *TopoProcess) SetupEtcd() (err error) { "--initial-cluster", fmt.Sprintf("%s=%s", topo.Name, topo.PeerURL), ) - errFile, _ := os.Create(path.Join(topo.DataDirectory, "topo-stderr.txt")) + err = createDirectory(topo.DataDirectory, 0700) + if err != nil && !os.IsExist(err) { + return err + } + errFile, err := os.Create(path.Join(topo.DataDirectory, "topo-stderr.txt")) + if err != nil { + return err + } + topo.proc.Stderr = errFile topo.proc.Env = append(topo.proc.Env, os.Environ()...) From 3f5196be8f481b59ba72aa9cfb034c10a7b94522 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 6 Mar 2020 07:47:09 +0100 Subject: [PATCH 232/825] Don't lower case keywords Signed-off-by: Andres Taylor --- go/vt/sqlparser/parse_test.go | 6 ++++++ go/vt/sqlparser/token.go | 2 +- go/vt/vtexplain/vtexplain_flaky_test.go | 7 ++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 073c93dcd7a..9501bb51815 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1773,9 +1773,15 @@ func TestKeywords(t *testing.T) { }, { input: "select status from t", output: "select `status` from t", + }, { + input: "select Status from t", + output: "select `Status` from t", }, { input: "select variables from t", output: "select `variables` from t", + }, { + input: "select Variables from t", + output: "select `Variables` from t", }} for _, tcase := range validSQL { diff --git a/go/vt/sqlparser/token.go b/go/vt/sqlparser/token.go index d8fd871a997..e6d59753920 100644 --- a/go/vt/sqlparser/token.go +++ b/go/vt/sqlparser/token.go @@ -661,7 +661,7 @@ func (tkn *Tokenizer) scanIdentifier(firstByte byte, isDbSystemVariable bool) (i lowered := bytes.ToLower(buffer.Bytes()) loweredStr := string(lowered) if keywordID, found := keywords[loweredStr]; found { - return keywordID, lowered + return keywordID, buffer.Bytes() } // dual must always be case-insensitive if loweredStr == "dual" { diff --git a/go/vt/vtexplain/vtexplain_flaky_test.go b/go/vt/vtexplain/vtexplain_flaky_test.go index 56b26aea57d..ef35da06890 100644 --- a/go/vt/vtexplain/vtexplain_flaky_test.go +++ b/go/vt/vtexplain/vtexplain_flaky_test.go @@ -19,12 +19,13 @@ package vtexplain import ( "encoding/json" "fmt" - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/require" "io/ioutil" "path" "strings" "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" ) var testOutputTempDir string @@ -142,7 +143,7 @@ func TestErrors(t *testing.T) { { SQL: "SELECT * FROM THIS IS NOT SQL", - Err: "vtexplain execute error in 'SELECT * FROM THIS IS NOT SQL': syntax error at position 22 near 'is'", + Err: "vtexplain execute error in 'SELECT * FROM THIS IS NOT SQL': syntax error at position 22 near 'IS'", }, { From 2bff32beee2e55c95cd4fb4e8fe032ec7e99b74c Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Fri, 6 Mar 2020 13:02:54 +0530 Subject: [PATCH 233/825] Removed python grpc client test Signed-off-by: Arindam Nayak --- Makefile | 4 +-- bootstrap.sh | 29 ---------------- test/client.py | 85 --------------------------------------------- test/client.sh | 33 ------------------ test/client_test.sh | 3 -- test/config.json | 4 +-- 6 files changed, 4 insertions(+), 154 deletions(-) delete mode 100644 test/client.py delete mode 100755 test/client.sh diff --git a/Makefile b/Makefile index c1315dffc99..6c8618fbed5 100644 --- a/Makefile +++ b/Makefile @@ -324,11 +324,11 @@ packages: docker_base tools: echo $$(date): Installing dependencies - BUILD_PYTHON=0 ./bootstrap.sh + ./bootstrap.sh minimaltools: echo $$(date): Installing minimal dependencies - BUILD_PYTHON=0 BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh + BUILD_JAVA=0 BUILD_CONSUL=0 ./bootstrap.sh dependency_check: ./tools/dependency_check.sh diff --git a/bootstrap.sh b/bootstrap.sh index 5c33aedc1f7..bc9f9170357 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -23,7 +23,6 @@ source ./dev.env # 0. Initialization and helper methods. # 1. Installation of dependencies. -BUILD_PYTHON=${BUILD_PYTHON:-1} BUILD_JAVA=${BUILD_JAVA:-1} BUILD_CONSUL=${BUILD_CONSUL:-1} @@ -89,34 +88,6 @@ function get_arch() { uname -m } -# This is only needed to execute client_test testcase. -# Install the gRPC Python library (grpcio) and the protobuf gRPC Python plugin (grpcio-tools) from PyPI. -# Dependencies like the Python protobuf package will be installed automatically. -function install_grpc() { - local version="$1" - local dist="$2" - - # Python requires a very recent version of virtualenv. - # We also require a recent version of pip, as we use it to - # upgrade the other tools. - # For instance, setuptools doesn't work with pip 6.0: - # https://github.com/pypa/setuptools/issues/945 - # (and setuptools is used by grpc install). - grpc_virtualenv="$dist/usr/local" - $VIRTUALENV -v "$grpc_virtualenv" - PIP=$grpc_virtualenv/bin/pip - $PIP install --upgrade pip - $PIP install --upgrade --ignore-installed virtualenv - $PIP install mysql-connector-python - - grpcio_ver=$version - $PIP install --upgrade grpcio=="$grpcio_ver" grpcio-tools=="$grpcio_ver" -} - -if [ "$BUILD_PYTHON" == 1 ] ; then - install_dep "gRPC" "1.16.0" "$VTROOT/dist/grpc" install_grpc -fi - # Install protoc. function install_protoc() { local version="$1" diff --git a/test/client.py b/test/client.py deleted file mode 100644 index 1204ce309de..00000000000 --- a/test/client.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Sample Vitess client in Python. - -This is a sample for using the Python Vitess client. -It's a script that inserts some random messages on random pages of the -guestbook sample app. - -Before running this, start up a local example cluster. - -Then run client.sh, which sets up PYTHONPATH before running client.py: -vitess/examples/local$ ./client.sh -""" - -import argparse -import random -import time - -from vtdb import vtgate_client - -# register the python gRPC client upon import -from vtdb import grpc_vtgate_client # pylint: disable=unused-import - -# Parse args -parser = argparse.ArgumentParser() -parser.add_argument('--server', dest='server', default='localhost:15991') -parser.add_argument('--timeout', dest='timeout', type=float, default='10.0') -args = parser.parse_args() - -# Connect -conn = vtgate_client.connect('grpc', args.server, args.timeout) - -try: - # Insert some messages on random pages. - print 'Inserting into master...' - cursor = conn.cursor(tablet_type='master', writable=True) - for i in range(3): - page = random.randint(1, 100) - - cursor.begin() - cursor.execute( - 'INSERT INTO messages (page, time_created_ns, message)' - ' VALUES (:page, :time_created_ns, :message)', - { - 'page': page, - 'time_created_ns': int(time.time() * 1e9), - 'message': 'V is for speed', - }) - cursor.commit() - - # Read it back from the master. - print 'Reading from master...' - cursor.execute('SELECT page, time_created_ns, message FROM messages', {}) - for row in cursor.fetchall(): - print row - - cursor.close() - - # Read from a replica. - # Note that this may be behind master due to replication lag. - print 'Reading from replica...' - cursor = conn.cursor(tablet_type='replica') - cursor.execute('SELECT page, time_created_ns, message FROM messages', {}) - for row in cursor.fetchall(): - print row - cursor.close() - -finally: - # Clean up - conn.close() diff --git a/test/client.sh b/test/client.sh deleted file mode 100755 index 8e1064bb770..00000000000 --- a/test/client.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is a wrapper script that sets up the environment for client.py. - -set -e - -hostname=`hostname -f` - -# We expect to find zk-client-conf.json in the same folder as this script. -script_root=`dirname "${BASH_SOURCE}"` - -# Set up environment. -for pkg in `find $VTROOT/dist -name site-packages`; do - export PYTHONPATH=$pkg:$PYTHONPATH -done - -export PYTHONPATH=$VTROOT/py-vtdb:$PYTHONPATH - -exec env python $script_root/client.py $* diff --git a/test/client_test.sh b/test/client_test.sh index aa738efbb04..2e2ee517893 100755 --- a/test/client_test.sh +++ b/test/client_test.sh @@ -37,9 +37,6 @@ vtctlclient -server localhost:15999 RebuildVSchemaGraph CELL=test ./scripts/vtgate-up.sh -echo "Run Python client script.." -$VTROOT/test/client.sh - echo "Run Go client script..." go run $VTROOT/test/client.go -server=localhost:15991 diff --git a/test/config.json b/test/config.json index 5e1b11bec2a..91cb7999e57 100644 --- a/test/config.json +++ b/test/config.json @@ -31,7 +31,7 @@ "e2e_test_race" ], "Manual": false, - "Shard": 5, + "Shard": -1, "RetryMax": 0, "Tags": [] }, @@ -42,7 +42,7 @@ "tools/unit_test_runner.sh" ], "Manual": false, - "Shard": 5, + "Shard": -1, "RetryMax": 0, "Tags": [] }, From 10e5cf4219c745c4a1ad6aa27f4f29e04f4a11b5 Mon Sep 17 00:00:00 2001 From: Anthony Yeh Date: Fri, 6 Mar 2020 12:27:37 -0800 Subject: [PATCH 234/825] azblob: UX fixes Signed-off-by: Anthony Yeh --- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 45 +++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index b2e335a9c2c..870605a9d2d 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -38,18 +38,18 @@ import ( var ( // This is the account name - accountName = flag.String("azblob_backup_account_name", "", "Azure Storage account name for backups, if this flag is unset the environment paramater VITESS_AZBLOB_ACCOUNT_NAME will be used") + accountName = flag.String("azblob_backup_account_name", "", "Azure Storage Account name for backups; if this flag is unset, the environment variable VT_AZBLOB_ACCOUNT_NAME will be used") // This is the private access key - accountKeyFile = flag.String("azblob_backup_account_key_file", "", "A file containing the Azure Storage account key,if this flag is unset the environment paramater VITESS_AZBLOB_ACCOUNT_KEY will be used") + accountKeyFile = flag.String("azblob_backup_account_key_file", "", "Path to a file containing the Azure Storage account key; if this flag is unset, the environment variable VT_AZBLOB_ACCOUNT_KEY will be used as the key itself (NOT a file path)") // This is the name of the container that will store the backups containerName = flag.String("azblob_backup_container_name", "", "Azure Blob Container Name") - // This is an optional previx to prepend to all files - storageRoot = flag.String("azblob_backup_storage_root", "", "Azure Blob storage root") + // This is an optional prefix to prepend to all files + storageRoot = flag.String("azblob_backup_storage_root", "", "Root prefix for all backup-related Azure Blobs; this should exclude both initial and trailing '/' (e.g. just 'a/b' not '/a/b/')") - azBlobParallelism = flag.Int("azblob_backup_parallelism", 1, "Azure blob operation parallelism (requires extra memory when increased)") + azBlobParallelism = flag.Int("azblob_backup_parallelism", 1, "Azure Blob operation parallelism (requires extra memory when increased)") ) const ( @@ -63,25 +63,25 @@ const ( // 2. Environment variables func azCredentials() (*azblob.SharedKeyCredential, error) { actName := *accountName - if len(actName) == 0 { + if actName == "" { // Check the Environmental Value - actName = os.Getenv("VITESS_AZBLOB_ACCOUNT_NAME") + actName = os.Getenv("VT_AZBLOB_ACCOUNT_NAME") } var actKey string - if len(*accountKeyFile) > 0 { - log.Infof("Getting account crednetials from file: %s", *accountKeyFile) - if dat, err := ioutil.ReadFile(*accountKeyFile); err == nil { - actKey = string(dat) - } else { + if *accountKeyFile != "" { + log.Infof("Getting Azure Storage Account key from file: %s", *accountKeyFile) + dat, err := ioutil.ReadFile(*accountKeyFile) + if err != nil { return nil, err } + actKey = string(dat) } else { - actKey = os.Getenv("VITESS_AZBLOB_ACCOUNT_KEY") + actKey = os.Getenv("VT_AZBLOB_ACCOUNT_KEY") } - if len(actName) == 0 || len(actKey) == 0 { - return nil, fmt.Errorf("can not get Azure Storage account credentials from CLI or Environment variables") + if actName == "" || actKey == "" { + return nil, fmt.Errorf("Azure Storage Account credentials not found in command-line flags or environment variables") } return azblob.NewSharedKeyCredential(actName, actKey) } @@ -92,12 +92,17 @@ func azServiceURL(credentials *azblob.SharedKeyCredential) azblob.ServiceURL { Policy: azblob.RetryPolicyFixed, MaxTries: defaultRetryCount, // Per https://godoc.org/github.com/Azure/azure-storage-blob-go/azblob#RetryOptions - // This shuld be set to a very nigh number ( they claim 60s per MB ). That could end up being days so we are limiting this to four hours + // this should be set to a very nigh number (they claim 60s per MB). + // That could end up being days so we are limiting this to four hours. TryTimeout: 4 * time.Hour, }, }) - u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/", credentials.AccountName())) - return azblob.NewServiceURL(*u, pipeline) + u := url.URL{ + Scheme: "https", + Host: credentials.AccountName() + ".blob.core.windows.net", + Path: "/", + } + return azblob.NewServiceURL(u, pipeline) } // AZBlobBackupHandle implements BackupHandle for Azure Blob service. @@ -127,7 +132,7 @@ func (bh *AZBlobBackupHandle) AddFile(ctx context.Context, filename string, file } // Error out if the file size it too large ( ~4.75 TB) if filesize > azblob.BlockBlobMaxStageBlockBytes*azblob.BlockBlobMaxBlocks { - return nil, fmt.Errorf("filesize is too large to upload to az blob (max size %v)", azblob.BlockBlobMaxStageBlockBytes*azblob.BlockBlobMaxBlocks) + return nil, fmt.Errorf("filesize (%v) is too large to upload to az blob (max size %v)", filesize, azblob.BlockBlobMaxStageBlockBytes*azblob.BlockBlobMaxBlocks) } obj := objName(bh.dir, bh.name, filename) @@ -227,7 +232,7 @@ func (bs *AZBlobBackupStorage) ListBackups(ctx context.Context, dir string) ([]b var subdirs []string for marker := (azblob.Marker{}); marker.NotDone(); { - // This returns Blobs in sorted order so we don't need to sort them a second time + // This returns Blobs in sorted order so we don't need to sort them a second time. resp, err := containerURL.ListBlobsHierarchySegment(ctx, marker, delimiter, azblob.ListBlobsSegmentOptions{ Prefix: searchPrefix, MaxResults: 0, From a9bed0934090503fb59dd20850eed69ba10ae105 Mon Sep 17 00:00:00 2001 From: Jacques Grove Date: Fri, 6 Mar 2020 16:38:56 -0800 Subject: [PATCH 235/825] Do not drop leading zeroes in microsecond timestamps for prepared statements. Fixes #5900 Signed-off-by: Jacques Grove --- go/mysql/query.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/mysql/query.go b/go/mysql/query.go index 89dfb5c8c98..8d340a1e6cd 100644 --- a/go/mysql/query.go +++ b/go/mysql/query.go @@ -680,7 +680,7 @@ func (c *Conn) parseStmtArgs(data []byte, typ querypb.Type, pos int) (sqltypes.V strconv.Itoa(int(hour)) + ":" + strconv.Itoa(int(minute)) + ":" + strconv.Itoa(int(second)) + "." + - strconv.Itoa(int(microSecond)) + fmt.Sprintf("%06d", microSecond) return sqltypes.NewVarChar(val), pos, ok case 0x07: @@ -781,7 +781,7 @@ func (c *Conn) parseStmtArgs(data []byte, typ querypb.Type, pos int) (sqltypes.V val += strconv.Itoa(int(hours)) + ":" + strconv.Itoa(int(minute)) + ":" + strconv.Itoa(int(second)) + "." + - strconv.Itoa(int(microSecond)) + fmt.Sprintf("%06d", microSecond) return sqltypes.NewVarChar(val), pos, ok case 0x08: From 7ddd41a2dd0d820b09377489bc39afeaa67d1434 Mon Sep 17 00:00:00 2001 From: Jacques Grove Date: Fri, 6 Mar 2020 18:10:07 -0800 Subject: [PATCH 236/825] Add tests. Signed-off-by: Jacques Grove --- go/test/endtoend/preparestmt/main_test.go | 14 ++++++---- .../endtoend/preparestmt/stmt_methods_test.go | 26 +++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/go/test/endtoend/preparestmt/main_test.go b/go/test/endtoend/preparestmt/main_test.go index 082871658a9..1fa24bf62a3 100644 --- a/go/test/endtoend/preparestmt/main_test.go +++ b/go/test/endtoend/preparestmt/main_test.go @@ -23,6 +23,7 @@ import ( "os" "strings" "testing" + "time" "vitess.io/vitess/go/test/endtoend/cluster" @@ -32,9 +33,11 @@ import ( // tableData is a temporary structure to hold selected data. type tableData struct { - Msg string - Data string - TextCol string + Msg string + Data string + TextCol string + DateTime time.Time + DateTimeMicros time.Time } // DBInfo information about the database. @@ -119,6 +122,7 @@ var ( decimal_unsigned DECIMAL, t_date DATE, t_datetime DATETIME, + t_datetime_micros DATETIME(6), t_time TIME, t_timestamp TIMESTAMP, c8 bit(8) DEFAULT NULL, @@ -255,7 +259,7 @@ func execErr(dbo *sql.DB, stmt string, params ...interface{}) *mysql.MySQLError func selectWhere(t *testing.T, dbo *sql.DB, where string, params ...interface{}) []tableData { var out []tableData // prepare query - qry := "SELECT msg, data, text_col FROM " + tableName + qry := "SELECT msg, data, text_col, t_datetime, t_datetime_micros FROM " + tableName if where != "" { qry += " WHERE (" + where + ")" } @@ -267,7 +271,7 @@ func selectWhere(t *testing.T, dbo *sql.DB, where string, params ...interface{}) // prepare result for r.Next() { var t tableData - r.Scan(&t.Msg, &t.Data, &t.TextCol) + r.Scan(&t.Msg, &t.Data, &t.TextCol, &t.DateTime, &t.DateTimeMicros) out = append(out, t) } return out diff --git a/go/test/endtoend/preparestmt/stmt_methods_test.go b/go/test/endtoend/preparestmt/stmt_methods_test.go index 20c9c97dba7..d631b904068 100644 --- a/go/test/endtoend/preparestmt/stmt_methods_test.go +++ b/go/test/endtoend/preparestmt/stmt_methods_test.go @@ -43,22 +43,24 @@ func TestInsertUpdateDelete(t *testing.T) { dbo := Connect(t) defer dbo.Close() // prepare insert statement - insertStmt := `insert into ` + tableName + ` values( ?, ?, ?, ?, ?, ?, ?, + insertStmt := `insert into ` + tableName + ` values( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);` textValue := fake.FullName() largeComment := fake.Paragraph() + location, _ := time.LoadLocation("Local") // inserting multiple rows into test table for i := 1; i <= 100; i++ { // preparing value for the insert testing insertValue := []interface{}{ i, fmt.Sprint(i) + "21", i * 100, 127, 1, 32767, 8388607, 2147483647, 2.55, 64.9, 55.5, - time.Date(2009, 5, 5, 0, 0, 0, 0, time.UTC), - time.Date(2009, 5, 5, 0, 0, 0, 0, time.UTC), + time.Date(2009, 5, 5, 0, 0, 0, 50000, time.UTC), + time.Date(2009, 5, 5, 0, 0, 0, 50000, location), + time.Date(2009, 5, 5, 0, 0, 0, 50000, location), time.Now(), - time.Date(2009, 5, 5, 0, 0, 0, 0, time.UTC), + time.Date(2009, 5, 5, 0, 0, 0, 50000, time.UTC), 1, 1, 1, 1, 1, 1, 1, 1, 1, jsonExample, textValue, largeComment, } exec(t, dbo, insertStmt, insertValue...) @@ -75,6 +77,13 @@ func TestInsertUpdateDelete(t *testing.T) { // validate value of msg column in data assert.Equal(t, fmt.Sprintf("%d21", testingID), data[0].Msg) + // Validate a datetime field (without micros) + // The 50 microsecs we inserted should have been truncated + assert.Equal(t, time.Date(2009, 5, 5, 0, 0, 0, 0, location), data[0].DateTime) + + // Validate a datetime field (with micros) + assert.Equal(t, time.Date(2009, 5, 5, 0, 0, 0, 50000, location), data[0].DateTimeMicros) + // testing record update updateRecord(t, dbo) @@ -108,16 +117,17 @@ func TestAutoIncColumns(t *testing.T) { insertStmt := "INSERT INTO " + tableName + ` ( msg,keyspace_id,tinyint_unsigned,bool_signed,smallint_unsigned, mediumint_unsigned,int_unsigned,float_unsigned,double_unsigned, - decimal_unsigned,t_date,t_datetime,t_time,t_timestamp,c8,c16,c24, + decimal_unsigned,t_date,t_datetime,t_datetime_micros,t_time,t_timestamp,c8,c16,c24, c32,c40,c48,c56,c63,c64,json_col,text_col,data) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);` insertValue := []interface{}{ "21", 0, 127, 1, 32767, 8388607, 2147483647, 2.55, 64.9, 55.5, - time.Date(2009, 5, 5, 0, 0, 0, 0, time.UTC), - time.Date(2009, 5, 5, 0, 0, 0, 0, time.UTC), + time.Date(2009, 5, 5, 0, 0, 0, 50000, time.UTC), + time.Date(2009, 5, 5, 0, 0, 0, 50000, time.UTC), + time.Date(2009, 5, 5, 0, 0, 0, 50000, time.UTC), time.Now(), - time.Date(2009, 5, 5, 0, 0, 0, 0, time.UTC), + time.Date(2009, 5, 5, 0, 0, 0, 50000, time.UTC), 1, 1, 1, 1, 1, 1, 1, 1, 1, jsonExample, fake.DomainName(), fake.Paragraph(), } From 064c7da6571f901f171a3a458da3c207b461676f Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 4 Mar 2020 21:36:37 -0800 Subject: [PATCH 237/825] vstreamer: send immediate GTID on "current" If the current position is requested as start for vstream, it will be good to send the actual starting GTID as a separate event before streaming the rest. This is particularly useful for messager because it needs to know its current position from the beginning in order to determine which events to skip w.r.t to poller position. Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/vstreamer/vstreamer.go | 9 +++++++++ go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go | 5 +---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 467b57e0953..7f99791be36 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -154,6 +154,15 @@ func (vs *vstreamer) Stream() error { } if vs.startPos == "current" { vs.pos = curPos + vevents := []*binlogdatapb.VEvent{{ + Type: binlogdatapb.VEventType_GTID, + Gtid: mysql.EncodePosition(vs.pos), + }, { + Type: binlogdatapb.VEventType_OTHER, + }} + if err := vs.send(vevents); err != nil { + return wrapError(err, vs.pos) + } } else { pos, err := mysql.DecodePosition(vs.startPos) if err != nil { diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 154ca416a42..5b9edf50860 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -1144,7 +1144,7 @@ func runCases(t *testing.T, filter *binlogdatapb.Filter, testcases []testcase, p // If position is 'current', we wait for a heartbeat to be // sure the vstreamer has started. if position == "current" { - <-ch + expectLog(ctx, t, "current pos", ch, [][]string{{`gtid`, `type:OTHER `}}) } for _, tcase := range testcases { @@ -1212,9 +1212,6 @@ func expectLog(ctx context.Context, t *testing.T, input interface{}, ch <-chan [ t.Fatalf("%v (%d): event: %v, want commit", input, i, evs[i]) } default: - if evs[i].Timestamp == 0 { - t.Fatalf("evs[%d].Timestamp: 0, want non-zero", i) - } evs[i].Timestamp = 0 if got := fmt.Sprintf("%v", evs[i]); got != want { t.Fatalf("%v (%d): event:\n%q, want\n%q", input, i, got, want) From ea3ddf75a23d5b1c43ec4f29bb5bebeaf63242a0 Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Sun, 8 Mar 2020 16:08:55 +0530 Subject: [PATCH 238/825] replace repo with entire image name Signed-off-by: Yuvraj --- helm/vitess/templates/_cron-jobs.tpl | 7 ++++-- helm/vitess/templates/_jobs.tpl | 10 +++++---- helm/vitess/templates/_keyspace.tpl | 6 +++--- helm/vitess/templates/_shard.tpl | 8 ++++--- helm/vitess/templates/_vtctld.tpl | 4 ++-- helm/vitess/templates/_vtgate.tpl | 12 +++++------ helm/vitess/templates/_vttablet.tpl | 32 +++++++++++++++++----------- helm/vitess/templates/vitess.yaml | 16 +++++++------- helm/vitess/values.yaml | 20 +++++++++++------ 9 files changed, 69 insertions(+), 46 deletions(-) diff --git a/helm/vitess/templates/_cron-jobs.tpl b/helm/vitess/templates/_cron-jobs.tpl index c36a68a3b4a..41a5986b7c8 100644 --- a/helm/vitess/templates/_cron-jobs.tpl +++ b/helm/vitess/templates/_cron-jobs.tpl @@ -13,7 +13,10 @@ {{- $backup := index . 7 -}} {{- $namespace := index . 8 -}} {{- $defaultVtctlclient := index . 9 }} -{{- $repo := index . 10 }} + + +{{- $vitessTag := $defaultVtctlclient.vitessTag -}} +{{- $vtctlclientImage := $defaultVtctlclient.vtctlclientImage -}} {{ if $backup.enabled }} # create cron job for current shard @@ -56,7 +59,7 @@ spec: containers: - name: backup - image: "{{$repo}}/vtctlclient:{{$vitessTag}}" + image: "{{$vtctlclientImage}}:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 14 }} diff --git a/helm/vitess/templates/_jobs.tpl b/helm/vitess/templates/_jobs.tpl index c1c5c91f83a..c27fe5105d6 100644 --- a/helm/vitess/templates/_jobs.tpl +++ b/helm/vitess/templates/_jobs.tpl @@ -6,9 +6,9 @@ {{- $job := index . 0 -}} {{- $defaultVtctlclient := index . 1 -}} {{- $namespace := index . 2 -}} -{{- $repo := index . 3 -}} {{- $vitessTag := $job.vitessTag | default $defaultVtctlclient.vitessTag -}} +{{- $vtctlclientImage := $defaultVtctlclient.vtctlclientImage -}} {{- $secrets := $job.secrets | default $defaultVtctlclient.secrets }} --- ################################### @@ -31,7 +31,7 @@ spec: restartPolicy: OnFailure containers: - name: vtjob - image: "{{$repo}}/vtctlclient:{{$vitessTag}}" + image: "{{$vtctlclientImage}}:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} resources: @@ -55,9 +55,11 @@ spec: {{- $job := index . 0 -}} {{- $defaultVtworker := index . 1 -}} {{- $namespace := index . 2 -}} -{{- $repo := index . 3 -}} {{- $vitessTag := $job.vitessTag | default $defaultVtworker.vitessTag -}} +{{- $vtworkerImage := $job.vtworkerImage | default $defaultVtworker.vtworkerImage -}} + + {{- $secrets := $job.secrets | default $defaultVtworker.secrets }} --- ################################### @@ -81,7 +83,7 @@ spec: restartPolicy: OnFailure containers: - name: vtjob - image: "{{$repo}}/vtworker:{{$vitessTag}}" + image: "{{$vtworkerImage}}:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtworker.secrets | indent 10 }} resources: diff --git a/helm/vitess/templates/_keyspace.tpl b/helm/vitess/templates/_keyspace.tpl index 0061e15c83c..8fc74efd9ae 100644 --- a/helm/vitess/templates/_keyspace.tpl +++ b/helm/vitess/templates/_keyspace.tpl @@ -7,7 +7,6 @@ {{- $keyspace := index . 1 -}} {{- $defaultVtctlclient := index . 2 -}} {{- $namespace := index . 3 -}} -{{- $repo := index . 4 -}} # sanitize inputs for labels {{- $keyspaceClean := include "clean-label" $keyspace.name -}} @@ -16,6 +15,7 @@ # define image to use {{- $vitessTag := .vitessTag | default $defaultVtctlclient.vitessTag -}} +{{- $vtctlclientImage := .vtctlclientImage | default $defaultVtctlclient.vtctlclientImage -}} {{- $secrets := .secrets | default $defaultVtctlclient.secrets -}} {{- range $name, $schema := $keyspace.schema }} @@ -34,7 +34,7 @@ spec: restartPolicy: OnFailure containers: - name: apply-schema - image: "{{$repo}}/vtctlclient:{{$vitessTag}}" + image: "{{$vtctlclientImage}}:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} @@ -101,7 +101,7 @@ spec: restartPolicy: OnFailure containers: - name: apply-vschema - image: "{{$repo}}/vtctlclient:{{$vitessTag}}" + image: "{{$vtctlclientImage}}:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} diff --git a/helm/vitess/templates/_shard.tpl b/helm/vitess/templates/_shard.tpl index d58cfadb53f..6f9d330306d 100644 --- a/helm/vitess/templates/_shard.tpl +++ b/helm/vitess/templates/_shard.tpl @@ -9,7 +9,6 @@ {{- $defaultVtctlclient := index . 3 -}} {{- $namespace := index . 4 -}} {{- $totalTabletCount := index . 5 -}} -{{- $repo := index . 6 -}} {{- $cellClean := include "clean-label" $cell.name -}} {{- $keyspaceClean := include "clean-label" $keyspace.name -}} @@ -19,6 +18,9 @@ {{- with $cell.vtctld }} # define image to use {{- $vitessTag := .vitessTag | default $defaultVtctlclient.vitessTag }} +{{- $vtctlclientImage := .vtctlclientImage | default $defaultVtctlclient.vtctlclientImage }} + + --- ################################### # InitShardMaster Job @@ -43,7 +45,7 @@ spec: restartPolicy: OnFailure containers: - name: init-shard-master - image: "{{$repo}}/vtctlclient:{{$vitessTag}}" + image: "{{$vtctlclientImage}}:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} @@ -146,7 +148,7 @@ spec: restartPolicy: OnFailure containers: - name: copy-schema - image: "{{$repo}}/vtctlclient:{{$vitessTag}}" + image: "{{$vtctlclientImage}}/:{{$vitessTag}}" volumeMounts: {{ include "user-secret-volumeMounts" $defaultVtctlclient.secrets | indent 10 }} diff --git a/helm/vitess/templates/_vtctld.tpl b/helm/vitess/templates/_vtctld.tpl index 911c3c230c9..98d8ba4291b 100644 --- a/helm/vitess/templates/_vtctld.tpl +++ b/helm/vitess/templates/_vtctld.tpl @@ -8,12 +8,12 @@ {{- $defaultVtctld := index . 2 -}} {{- $namespace := index . 3 -}} {{- $config := index . 4 -}} -{{- $repo := index . 5 -}} {{- with $cell.vtctld -}} # define image to use {{- $vitessTag := .vitessTag | default $defaultVtctld.vitessTag -}} +{{- $vtcldImage := .vtcldImage | default $defaultVtctld.vtcldImage -}} {{- $cellClean := include "clean-label" $cell.name }} ################################### @@ -60,7 +60,7 @@ spec: {{ include "vtctld-affinity" (tuple $cellClean $cell.region) | indent 6 }} containers: - name: vtctld - image: {{$repo}}/vtctld:{{$vitessTag}} + image: {{$vtcldImage}}:{{$vitessTag}} imagePullPolicy: IfNotPresent readinessProbe: httpGet: diff --git a/helm/vitess/templates/_vtgate.tpl b/helm/vitess/templates/_vtgate.tpl index 580ab753cd1..62ec33ecd27 100644 --- a/helm/vitess/templates/_vtgate.tpl +++ b/helm/vitess/templates/_vtgate.tpl @@ -7,12 +7,12 @@ {{- $cell := index . 1 -}} {{- $defaultVtgate := index . 2 -}} {{- $namespace := index . 3 -}} -{{- $repo := index . 4 -}} {{- with $cell.vtgate -}} # define image to use {{- $vitessTag := .vitessTag | default $defaultVtgate.vitessTag -}} +{{- $vtgateImage := .vtgateImage | default $defaultVtgate.vtgateImage -}} {{- $cellClean := include "clean-label" $cell.name }} ################################### @@ -69,13 +69,13 @@ spec: {{ if $cell.mysqlProtocol.enabled }} {{ if eq $cell.mysqlProtocol.authType "secret" }} initContainers: -{{ include "init-mysql-creds" (tuple $vitessTag $cell $repo) | indent 8 }} +{{ include "init-mysql-creds" (tuple $vitessTag $vtgateImage $cell) | indent 8 }} {{ end }} {{ end }} containers: - name: vtgate - image: {{$repo}}/vtgate:{{$vitessTag}} + image: {{$vtgateImage}}:{{$vitessTag}} imagePullPolicy: IfNotPresent readinessProbe: httpGet: @@ -219,13 +219,13 @@ affinity: ################################### {{ define "init-mysql-creds" -}} {{- $vitessTag := index . 0 -}} -{{- $cell := index . 1 -}} -{{- $repo := index . 3 -}} +{{- $vtgateImage := index . 1 -}} +{{- $cell := index . 2 -}} {{- with $cell.mysqlProtocol }} - name: init-mysql-creds - image: "{{$repo}}/vtgate:{{$vitessTag}}" + image: "{{$vtgateImage}}:{{$vitessTag}}" imagePullPolicy: IfNotPresent volumeMounts: - name: creds diff --git a/helm/vitess/templates/_vttablet.tpl b/helm/vitess/templates/_vttablet.tpl index b342eca2e84..1c8fe02249e 100644 --- a/helm/vitess/templates/_vttablet.tpl +++ b/helm/vitess/templates/_vttablet.tpl @@ -48,9 +48,10 @@ spec: {{- $config := index . 8 -}} {{- $pmm := index . 9 -}} {{- $orc := index . 10 -}} -{{- $repo := index . 11 -}} +{{- $mysqlctld := index . 11 -}} {{- $logrotate := index . 12 -}} {{- $logtail := index . 13 -}} +{{- $vtctl := index . 14 -}} # sanitize inputs for labels {{- $cellClean := include "clean-label" $cell.name -}} @@ -64,6 +65,7 @@ spec: # define images to use {{- $vitessTag := .vitessTag | default $defaultVttablet.vitessTag -}} +{{- $vtctlclientImage := .vtctlclientImage | default $defaultVttablet.vtctlclientImage -}} {{- $image := .image | default $defaultVttablet.image -}} {{- $mysqlImage := .mysqlImage | default $defaultVttablet.mysqlImage -}} {{- $mysqlImage := .mysqlImage | default $defaultVttablet.mysqlImage }} @@ -104,17 +106,17 @@ spec: {{ include "vttablet-affinity" (tuple $cellClean $keyspaceClean $shardClean $cell.region) | indent 6 }} initContainers: -{{ include "init-mysql" (tuple $vitessTag $cellClean $repo) | indent 8 }} -{{ include "init-vttablet" (tuple $vitessTag $cell $cellClean $namespace $repo) | indent 8 }} +{{ include "init-mysql" (tuple $vitessTag $cellClean $mysqlctld) | indent 8 }} +{{ include "init-vttablet" (tuple $vitessTag $cell $cellClean $namespace $mysqlctld $vtctl) | indent 8 }} containers: {{ include "cont-mysql" (tuple $topology $cell $keyspace $shard $tablet $defaultVttablet $uid) | indent 8 }} -{{ include "cont-vttablet" (tuple $topology $cell $keyspace $shard $tablet $defaultVttablet $defaultVtctlclient $vitessTag $uid $namespace $config $orc $repo) | indent 8 }} +{{ include "cont-vttablet" (tuple $topology $cell $keyspace $shard $tablet $defaultVttablet $defaultVtctlclient $vitessTag $uid $namespace $config $orc) | indent 8 }} {{ include "cont-logrotate" (tuple $logrotate) | indent 8 }} {{ include "cont-mysql-generallog" (tuple $logrotate) | indent 8 }} {{ include "cont-mysql-errorlog" (tuple $logrotate) | indent 8 }} {{ include "cont-mysql-slowlog" (tuple $logrotate) | indent 8 }} -{{ if $pmm.enabled }}{{ include "cont-pmm-client" (tuple $pmm $namespace $keyspace $repo $logtail) | indent 8 }}{{ end }} +{{ if $pmm.enabled }}{{ include "cont-pmm-client" (tuple $pmm $namespace $keyspace $logtail) | indent 8 }}{{ end }} volumes: - name: vt @@ -156,7 +158,7 @@ spec: type: {{ $tablet.type | quote }} # conditionally add cron job -{{ include "vttablet-backup-cron" (tuple $cellClean $keyspaceClean $shardClean $shardName $keyspace $shard $vitessTag $config.backup $namespace $defaultVtctlclient $repo) }} +{{ include "vttablet-backup-cron" (tuple $cellClean $keyspaceClean $shardClean $shardName $keyspace $shard $vitessTag $config.backup $namespace $defaultVtctlclient) }} {{- end -}} {{- end -}} @@ -167,10 +169,13 @@ spec: {{ define "init-mysql" -}} {{- $vitessTag := index . 0 -}} {{- $cellClean := index . 1 }} -{{- $repo := index . 2 }} +{{- $mysqlctld := index . 2 }} + +{{- $vitessTag := .vitessTag | default $mysqlctld.vitessTag -}} +{{- $mysqlctldImage := .vtgateImage | default $mysqlctld.mysqlctldImage -}} - name: "init-mysql" - image: "{{$repo}}/mysqlctld:{{$vitessTag}}" + image: "{{$mysqlctldImage}}:{{$vitessTag}}" imagePullPolicy: IfNotPresent volumeMounts: - name: vtdataroot @@ -213,10 +218,11 @@ spec: {{- $cell := index . 1 -}} {{- $cellClean := index . 2 -}} {{- $namespace := index . 3 }} -{{- $repo := index . 4 }} +{{- $vtctl := index . 4 }} + - name: init-vttablet - image: "{{$repo}}/vtctl:{{$vitessTag}}" + image: "{{$vtctl.vtctlImage}}:{{$vitessTag}}" imagePullPolicy: IfNotPresent volumeMounts: - name: vtdataroot @@ -276,13 +282,15 @@ spec: {{- $namespace := index . 9 -}} {{- $config := index . 10 -}} {{- $orc := index . 11 -}} -{{- $repo := index . 12 -}} {{- $cellClean := include "clean-label" $cell.name -}} {{- with $tablet.vttablet }} +{{- $vitessTag := $defaultVttablet.vitessTag -}} +{{- $vttabletImage := $defaultVttablet.vttabletImage -}} + - name: vttablet - image: "{{$repo}}/vttablet:{{$vitessTag}}" + image: "{{$vttabletImage}}:{{$vitessTag}}" imagePullPolicy: IfNotPresent readinessProbe: httpGet: diff --git a/helm/vitess/templates/vitess.yaml b/helm/vitess/templates/vitess.yaml index 1ae18cf2c80..34984690be4 100644 --- a/helm/vitess/templates/vitess.yaml +++ b/helm/vitess/templates/vitess.yaml @@ -5,7 +5,7 @@ --- {{ if $.Values.pmm.enabled }} # create the pmm service and stateful set -{{ include "pmm" (tuple $.Values.pmm $.Release.Namespace $.Values.repo) }} +{{ include "pmm" (tuple $.Values.pmm $.Release.Namespace) }} --- {{ end }} @@ -47,26 +47,26 @@ {{ include "etcd" (tuple $cellClean $replicas $version $resources $clusterWide) }} --- # create one controller per cell -{{ include "vtctld" (tuple $.Values.topology $cell $.Values.vtctld $.Release.Namespace $.Values.config $.Values.repo) }} +{{ include "vtctld" (tuple $.Values.topology $cell $.Values.vtctld $.Release.Namespace $.Values.config) }} --- # create a pool of vtgates per cell -{{ include "vtgate" (tuple $.Values.topology $cell $.Values.vtgate $.Release.Namespace $.Values.repo) }} +{{ include "vtgate" (tuple $.Values.topology $cell $.Values.vtgate $.Release.Namespace) }} # Tablets for keyspaces {{ range $keyspace := $cell.keyspaces }} # Keyspace initializations - {{ include "keyspace" (tuple $cell $keyspace $.Values.vtctlclient $.Release.Namespace $.Values.repo) }} + {{ include "keyspace" (tuple $cell $keyspace $.Values.vtctlclient $.Release.Namespace) }} {{ range $shard := $keyspace.shards }} {{ $totalTabletCount := len (include "tablet-count" $shard.tablets) }} # Shard initializations - {{ include "shard" (tuple $cell $keyspace $shard $.Values.vtctlclient $.Release.Namespace $totalTabletCount $.Values.repo) }} + {{ include "shard" (tuple $cell $keyspace $shard $.Values.vtctlclient $.Release.Namespace $totalTabletCount) }} # Tablet initializations {{ range $tablet := $shard.tablets }} - {{ include "vttablet" (tuple $.Values.topology $cell $keyspace $shard $tablet $.Values.vttablet $.Values.vtctlclient $.Release.Namespace $.Values.config $.Values.pmm $.Values.orchestrator $.Values.repo $.Values.logrotate $.Values.logtail) }} + {{ include "vttablet" (tuple $.Values.topology $cell $keyspace $shard $tablet $.Values.vttablet $.Values.vtctlclient $.Release.Namespace $.Values.config $.Values.pmm $.Values.orchestrator $.Values.mysqlctld $.Values.logrotate $.Values.logtail $.Value.vtctl) }} {{ end }} # range $tablet {{ end }} # range $shard {{ end }} # range $keyspace @@ -75,9 +75,9 @@ {{ range $job := $.Values.jobs }} {{ if eq $job.kind "vtctlclient" }} - {{ include "vtctlclient-job" (tuple $job $.Values.vtctlclient $.Release.Namespace $.Values.repo) }} + {{ include "vtctlclient-job" (tuple $job $.Values.vtctlclient $.Release.Namespace) }} {{ else }} - {{ include "vtworker-job" (tuple $job $.Values.vtworker $.Release.Namespace $.Values.repo) }} + {{ include "vtworker-job" (tuple $job $.Values.vtworker $.Release.Namespace) }} {{ end }} {{ end }} --- diff --git a/helm/vitess/values.yaml b/helm/vitess/values.yaml index 2cf97c23edd..08ddbd7a42c 100644 --- a/helm/vitess/values.yaml +++ b/helm/vitess/values.yaml @@ -14,10 +14,6 @@ # will be taken from defaults defined below. # topology: -# Repository for vitess images. Defaults to vitess repo on dockerhub. -# Override by specifying the full path of alternate repository, e.g. -repo : vitess - # config will be stored as a ConfigMap and mounted where appropriate config: # Backup flags will be applied to components that need them. @@ -185,6 +181,7 @@ etcd: vtctld: serviceType: ClusterIP vitessTag: helm-1.0.7-5 + vtcldImage: vitess/vtctld resources: # requests: # cpu: 100m @@ -196,6 +193,7 @@ vtctld: vtgate: serviceType: ClusterIP vitessTag: helm-1.0.7-5 + vtgateImage: vitess/vtgate resources: # requests: # cpu: 500m @@ -215,12 +213,14 @@ vtgate: # Default values for vtctlclient resources defined in 'topology' vtctlclient: vitessTag: helm-1.0.7-5 + vtctlclientImage: vitess/vtctlclient extraFlags: {} secrets: [] # secrets are mounted under /vt/usersecrets/{secretname} # Default values for vtworker resources defined in 'jobs' vtworker: vitessTag: helm-1.0.7-5 + vtworkerImage: vitess/vtworker extraFlags: {} resources: # requests: @@ -232,7 +232,7 @@ vtworker: # Default values for vttablet resources defined in 'topology' vttablet: vitessTag: helm-1.0.7-5 - + vttabletImage: vitess/vttablet # valid values are # - mysql56 (for MySQL 8.0) # - mysql56 (for MySQL/Percona 5.6 or 5.7) @@ -325,6 +325,15 @@ vttablet: requests: storage: 10Gi +# Default values for mysqlctld resources defined in 'topology' +mysqlctld: + vitessTag: helm-1.0.7-5 + mysqlctldImage: vitess/mysqlctld + +vtctl: + vitessTag: helm-1.0.7-5 + vtctlImage: vitess/vtctl + # Default values for pmm pmm: enabled: false @@ -390,4 +399,3 @@ orchestrator: requests: cpu: 50m memory: 350Mi - From cf2cbb6bd19614c23a6d0029d0a4185c62319bd4 Mon Sep 17 00:00:00 2001 From: Yuvraj Date: Sun, 8 Mar 2020 16:50:04 +0530 Subject: [PATCH 239/825] Added logrotate and vtcl in values Signed-off-by: Yuvraj --- helm/vitess/templates/_vttablet.tpl | 4 ++-- helm/vitess/templates/vitess.yaml | 2 +- helm/vitess/values.yaml | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/helm/vitess/templates/_vttablet.tpl b/helm/vitess/templates/_vttablet.tpl index 1c8fe02249e..c27774ae936 100644 --- a/helm/vitess/templates/_vttablet.tpl +++ b/helm/vitess/templates/_vttablet.tpl @@ -171,8 +171,8 @@ spec: {{- $cellClean := index . 1 }} {{- $mysqlctld := index . 2 }} -{{- $vitessTag := .vitessTag | default $mysqlctld.vitessTag -}} -{{- $mysqlctldImage := .vtgateImage | default $mysqlctld.mysqlctldImage -}} +{{- $vitessTag := $mysqlctld.vitessTag -}} +{{- $mysqlctldImage := $mysqlctld.mysqlctldImage -}} - name: "init-mysql" image: "{{$mysqlctldImage}}:{{$vitessTag}}" diff --git a/helm/vitess/templates/vitess.yaml b/helm/vitess/templates/vitess.yaml index 34984690be4..62dc8d50f42 100644 --- a/helm/vitess/templates/vitess.yaml +++ b/helm/vitess/templates/vitess.yaml @@ -66,7 +66,7 @@ # Tablet initializations {{ range $tablet := $shard.tablets }} - {{ include "vttablet" (tuple $.Values.topology $cell $keyspace $shard $tablet $.Values.vttablet $.Values.vtctlclient $.Release.Namespace $.Values.config $.Values.pmm $.Values.orchestrator $.Values.mysqlctld $.Values.logrotate $.Values.logtail $.Value.vtctl) }} + {{ include "vttablet" (tuple $.Values.topology $cell $keyspace $shard $tablet $.Values.vttablet $.Values.vtctlclient $.Release.Namespace $.Values.config $.Values.pmm $.Values.orchestrator $.Values.mysqlctld $.Values.logrotate $.Values.logtail $.Values.vtctl) }} {{ end }} # range $tablet {{ end }} # range $shard {{ end }} # range $keyspace diff --git a/helm/vitess/values.yaml b/helm/vitess/values.yaml index 08ddbd7a42c..59af5639aee 100644 --- a/helm/vitess/values.yaml +++ b/helm/vitess/values.yaml @@ -399,3 +399,7 @@ orchestrator: requests: cpu: 50m memory: 350Mi + +logrotate: + vitessTag: 3.1.1 + image: vitess/orchestrator From 57c2f2994df64f150980ab12ec128263723eed42 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 3 Jan 2020 17:23:33 -0800 Subject: [PATCH 240/825] vrepl: document engine, vreplicator, copier Also fix an incorrect test. Signed-off-by: Sugu Sougoumarane --- .../tabletmanager/vreplication/engine.go | 19 ++++++++ .../vreplication/framework_test.go | 6 +++ .../tabletmanager/vreplication/vcopier.go | 27 ++++++++++++ .../vreplication/vcopier_flaky_test.go | 8 ++-- .../tabletmanager/vreplication/vreplicator.go | 43 ++++++++++++++++++- 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index 00ac39d04aa..0cf1bdd84c6 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -405,6 +405,17 @@ func (vre *Engine) fetchIDs(dbClient binlogplayer.DBClient, selector string) (id return ids, bv, nil } +// registerJounral is invoked if any of the vreplication streams encounters a journal event. +// Multiple registerJournal functions collaborate to converge on the final action. +// The first invocation creates an entry in vre.journaler. The entry is initialized +// with the list of participants that also need to converge. +// The middle invocation happens on the first and subsequent calls: the current participant +// marks itself as having joined the wait. +// The final invocation happens for the last participant that joins. Having confirmed +// that all the participants have joined, transitionJournal is invoked, which deletes +// all current participant streams and creates new ones to replace them. +// A unified journal event is identified by the workflow name and journal id. +// Multiple independent journal events can go through this cycle concurrently. func (vre *Engine) registerJournal(journal *binlogdatapb.Journal, id int) error { vre.mu.Lock() defer vre.mu.Unlock() @@ -417,6 +428,7 @@ func (vre *Engine) registerJournal(journal *binlogdatapb.Journal, id int) error key := fmt.Sprintf("%s:%d", workflow, journal.Id) je, ok := vre.journaler[key] if !ok { + // First invocation. Create the entry. log.Infof("Journal encountered: %v", journal) controllerSources := make(map[string]bool) for _, ct := range vre.controllers { @@ -441,19 +453,25 @@ func (vre *Engine) registerJournal(journal *binlogdatapb.Journal, id int) error vre.journaler[key] = je } + // Middle invocation. Register yourself ks := fmt.Sprintf("%s:%s", vre.controllers[id].source.Keyspace, vre.controllers[id].source.Shard) log.Infof("Registering id %v against %v", id, ks) je.participants[ks] = id + // Check if all participants have joined. for _, pid := range je.participants { if pid == 0 { // Still need to wait. return nil } } + + // Final invocation. Perform the transition. go vre.transitionJournal(key) return nil } +// transitionJournal stops all existing participants, deletes their vreplication +// entries, and creates new ones as instructed by the journal metadata. func (vre *Engine) transitionJournal(key string) { vre.mu.Lock() defer vre.mu.Unlock() @@ -484,6 +502,7 @@ func (vre *Engine) transitionJournal(key string) { return } + // Use the reference row to copy other fields like cell, tablet_types, etc. params, err := readRow(dbClient, refid) if err != nil { log.Errorf("transitionJournal: %v", err) diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 9d5efb00d0a..72b9e2c3b82 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -216,12 +216,18 @@ func (ftc *fakeTabletConn) StreamHealth(ctx context.Context, callback func(*quer }) } +// vstreamHook allows you to do work just before calling VStream. +var vstreamHook func(ctx context.Context) + // VStream directly calls into the pre-initialized engine. func (ftc *fakeTabletConn) VStream(ctx context.Context, target *querypb.Target, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { if target.Keyspace != "vttest" { <-ctx.Done() return io.EOF } + if vstreamHook != nil { + vstreamHook(ctx) + } return streamerEngine.Stream(ctx, startPos, filter, send) } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go index 7e3b0fe4fc9..c0f9cc693b6 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go @@ -48,6 +48,10 @@ func newVCopier(vr *vreplicator) *vcopier { } } +// initTablesForCopy (phase 1) identifies the list of tables to be copied and inserts +// them into copy_state. If there are no tables to copy, it explicitly stops +// the stream. Otherwise, the copy phase (phase 2) may think that all tables are copied. +// This will cause us to go into the replication phase (phase 3) without a starting position. func (vc *vcopier) initTablesForCopy(ctx context.Context) error { defer vc.vr.dbClient.Rollback() @@ -81,6 +85,23 @@ func (vc *vcopier) initTablesForCopy(ctx context.Context) error { return vc.vr.dbClient.Commit() } +// copyNext performs a multi-step process on each iteration. +// Step 1: catchup: During this step, it replicates from the source from the last position. +// This is a partial replication: events are applied only to tables or subsets of tables +// that have already been copied. This goes on until replication catches up. +// Step 2: Start streaming. This returns the initial field info along with the GTID +// as of which the snapshot is being streamed. +// Step 3: fastForward: The target is fast-forwarded to the GTID obtained. This should +// be quick because we were mostly caught up as of step 1. This ensures that the +// snapshot of the rows are consistent with the position where the target stopped. +// Step 4: copy rows: Copy the next set of rows from the stream that was started in Step 2. +// This goes on until all rows are copied, or a timeout. In both cases, copyNext +// returns, and the replicator decides whether to invoke copyNext again, or to +// go to the next phase if all the copying is done. +// Steps 2, 3 and 4 are performed by copyTable. +// copyNext also builds the copyState metadata that contains the tables and their last +// primary key that was copied. A nil Result means that nothing has been copied. +// A table that was fully copied is removed from copyState. func (vc *vcopier) copyNext(ctx context.Context, settings binlogplayer.VRSettings) error { qr, err := vc.vr.dbClient.Execute(fmt.Sprintf("select table_name, lastpk from _vt.copy_state where vrepl_id=%d", vc.vr.id)) if err != nil { @@ -112,6 +133,9 @@ func (vc *vcopier) copyNext(ctx context.Context, settings binlogplayer.VRSetting return vc.copyTable(ctx, tableToCopy, copyState) } +// catchup replays events to the subset of the tables that have been copied +// until replication is caught up. In order to stop, the seconds behind master has +// to fall below replicationLagTolerance. func (vc *vcopier) catchup(ctx context.Context, copyState map[string]*sqltypes.Result) error { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -159,6 +183,9 @@ func (vc *vcopier) catchup(ctx context.Context, copyState map[string]*sqltypes.R } } +// copyTable performs the synchronized copy of the next set of rows from +// the current table being copied. Each packet received is transactionally +// committed with the lastpk. This allows for consistent resumability. func (vc *vcopier) copyTable(ctx context.Context, tableName string, copyState map[string]*sqltypes.Result) error { defer vc.vr.dbClient.Rollback() diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go index 837054ed1d4..9ee4648750b 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier_flaky_test.go @@ -156,12 +156,12 @@ func TestPlayerCopyBigTable(t *testing.T) { vstreamRowsSendHook = nil } - vstreamRowsHook = func(context.Context) { + vstreamHook = func(context.Context) { // Sleeping 50ms guarantees that the catchup wait loop executes multiple times. // This is because waitRetryTime is set to 10ms. time.Sleep(50 * time.Millisecond) // Do this no more than once. - vstreamRowsHook = nil + vstreamHook = nil } filter := &binlogdatapb.Filter{ @@ -283,12 +283,12 @@ func TestPlayerCopyWildcardRule(t *testing.T) { vstreamRowsSendHook = nil } - vstreamRowsHook = func(context.Context) { + vstreamHook = func(context.Context) { // Sleeping 50ms guarantees that the catchup wait loop executes multiple times. // This is because waitRetryTime is set to 10ms. time.Sleep(50 * time.Millisecond) // Do this no more than once. - vstreamRowsHook = nil + vstreamHook = nil } filter := &binlogdatapb.Filter{ diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index 0e16bccdcce..15f1c311d62 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -59,7 +59,26 @@ type vreplicator struct { tableKeys map[string][]string } -// newVReplicator creates a new vreplicator +// newVReplicator creates a new vreplicator. The valid fields from the source are: +// Keyspce, Shard, Filter, OnDdl, ExternalMySql and StopAfterCopy. +// The Filter consists of Rules. Each Rule has a Match and an (inner) Filter field. +// The Match can be a table name or, if it begins with a "/", a wildcard. +// The Filter can be empty: get all rows and columns. +// The Filter can be a keyrange, like "-80": get all rows that are within the keyrange. +// The Filter can be a select expression. Examples. +// "select * from t", same as an empty Filter, +// "select * from t where in_keyrange('-80')", same as "-80", +// "select * from t where in_keyrange(col1, 'hash', '-80')", +// "select col1, col2 from t where...", +// "select col1, keyspace_id() as ksid from t where...", +// "select id, count(*), sum(price) from t group by id". +// Only "in_keyrange" expressions are supported in the where clause. +// The select expressions can be any valid non-aggregate expressions, +// or count(*), or sum(col). +// If the target column name does not match the source expression, an +// alias like "a+b as targetcol" must be used. +// More advanced constructs can be used. Please see the table plan builder +// documentation for more info. func newVReplicator(id uint32, source *binlogdatapb.BinlogSource, sourceVStreamer VStreamerClient, stats *binlogplayer.Stats, dbClient binlogplayer.DBClient, mysqld mysqlctl.MysqlDaemon, vre *Engine) *vreplicator { return &vreplicator{ vre: vre, @@ -72,7 +91,27 @@ func newVReplicator(id uint32, source *binlogdatapb.BinlogSource, sourceVStreame } } -// Replicate starts a vreplication stream. +// Replicate starts a vreplication stream. It can be in one of three phases: +// 1. Init: If a request is issued with no starting position, we assume that the +// contents of the tables must be copied first. During this phase, the list of +// tables to be copied is inserted into the copy_state table. A successful insert +// gets us out of this phase. +// 2. Copy: If the copy_state table has rows, then we are in this phase. During this +// phase, we repeatedly invoke copyNext until all the tables are copied. After each +// table is successfully copied, it's removed from the copy_state table. We exit this +// phase when there are no rows left in copy_state. +// 3. Replicate: In this phase, we replicate binlog events indefinitely, unless +// a stop position was requested. This phase differs from the Init phase because +// there is a replication position. +// If a request had a starting position, then we go directly into phase 3. +// During these phases, the state of vreplication is reported as 'Init', 'Copying', +// or 'Running'. They all mean the same thing. The difference in the phases depends +// on the criteria defined above. The different states reported are mainly +// informational. The 'Stopped' state is, however, honored. +// All phases share the same plan building framework. We leverage the fact the +// row representation of a read (during copy) and a binlog event are identical. +// However, there are some subtle differences, explained in the plan builder +// code. func (vr *vreplicator) Replicate(ctx context.Context) error { tableKeys, err := vr.buildTableKeys() if err != nil { From a96411a6070e732ecd989ecad15f0238ea4aec3b Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 4 Jan 2020 19:57:10 -0800 Subject: [PATCH 241/825] vrepl: document vplayer.go Signed-off-by: Sugu Sougoumarane --- .../vreplication/table_plan_builder.go | 6 +- .../tabletmanager/vreplication/vplayer.go | 107 +++++++++++++++--- 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go index 9cf14cee39c..a5b7c1291b8 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go +++ b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go @@ -28,6 +28,10 @@ import ( "vitess.io/vitess/go/vt/sqlparser" ) +// This file contains just the builders for ReplicatorPlan and TablePlan. +// ReplicatorPlan and TablePlan are in replicator_plan.go. +// TODO(sougou): reorganize this in a better fashion. + // ExcludeStr is the filter value for excluding tables that match a rule. // TODO(sougou): support this on vstreamer side also. const ExcludeStr = "exclude" @@ -43,7 +47,7 @@ type tablePlanBuilder struct { } // colExpr describes the processing to be performed to -// compute the value of the target table column. +// compute the value of one column of the target table. type colExpr struct { colName sqlparser.ColIdent // operation==opExpr: full expression is set diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 85159a8bfb8..464cc0d79c5 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -33,6 +33,7 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) +// vplayer replays binlog events by pulling them from a vstreamer. type vplayer struct { vr *vreplicator startPos mysql.Position @@ -44,8 +45,10 @@ type vplayer struct { tablePlans map[string]*TablePlan pos mysql.Position - // unsavedEvent is saved any time we skip an event without - // saving: This can be an empty commit or a skipped DDL. + // unsavedEvent is set any time we skip an event without + // saving, which is on an empty commit. + // If nothing else happens for idleTimeout since timeLastSaved, + // the position of the unsavedEvent gets saved. unsavedEvent *binlogdatapb.VEvent // timeLastSaved is set every time a GTID is saved. timeLastSaved time.Time @@ -53,10 +56,19 @@ type vplayer struct { lastTimestampNs int64 // timeOffsetNs keeps track of the clock difference with respect to source tablet. timeOffsetNs int64 - // canAcceptStmtEvents set to true if the current player can accept events in statement mode. Only true for filters that are match all. + // canAcceptStmtEvents is set to true if the current player can accept events in statement mode. Only true for filters that are match all. canAcceptStmtEvents bool } +// newVPlayer creates a new vplayer. Parameters: +// vreplicator: the outer replicator. It's used for common functions like setState. +// Also used to access the engine for registering journal events. +// settings: current settings read from _vt.vreplication. +// copyState: if set, contains the list of tables yet to be copied, or in the process +// of being copied. If copyState is non-nil, the plans generated make sure that +// replication is only applied to parts that have been copied so far. +// pausePos: if set, replication will stop at that position without updating the state to "Stopped". +// This is used by the fastForward function during copying. func newVPlayer(vr *vreplicator, settings binlogplayer.VRSettings, copyState map[string]*sqltypes.Result, pausePos mysql.Position) *vplayer { saveStop := true if !pausePos.IsZero() { @@ -75,7 +87,7 @@ func newVPlayer(vr *vreplicator, settings binlogplayer.VRSettings, copyState map } } -// play is not resumable. If pausePos is set, play returns without updating the vreplication state. +// play is the entry point for playing binlogs. func (vp *vplayer) play(ctx context.Context) error { if !vp.stopPos.IsZero() && vp.startPos.AtLeast(vp.stopPos) { log.Infof("Stop position %v already reached: %v", vp.startPos, vp.stopPos) @@ -114,6 +126,16 @@ func (vp *vplayer) play(ctx context.Context) error { return nil } +// fetchAndApply performs the fetching and application of the binlogs. +// This is done by two different threads. The fetcher thread pulls +// events from the vstreamer and adds them to the relayLog. +// The applyEvents thread pulls accumulated events from the relayLog +// to apply them to mysql. The reason for this separation is because +// commits are slow during apply. So, more events can accumulate in +// the relay log during a commit. In such situations, the next iteration +// of apply combines all the transactions in the relay log into a single +// one. This allows for the apply thread to catch up more quickly if +// a backlog builds up. func (vp *vplayer) fetchAndApply(ctx context.Context) (err error) { log.Infof("Starting VReplication player id: %v, startPos: %v, stop: %v, filter: %v", vp.vr.id, vp.startPos, vp.stopPos, vp.vr.source) @@ -224,6 +246,54 @@ func (vp *vplayer) updatePos(ts int64) (posReached bool, err error) { return posReached, nil } +// applyEvents is the main thread that applies the events. It has the following use +// cases to take into account: +// * Normal transaction that has row mutations. In this case, the transaction +// is committed along with an update of the position. +// * DDL event: the action depends on the OnDDL setting. +// * OTHER event: the current position of the event is saved. +// * JOURNAL event: if the event is relevant to the current stream, invoke registerJournal +// of the engine, and terminate. +// * HEARTBEAT: update SecondsBehindMaster. +// * Empty transaction: The event is remembered as an unsavedEvent. If no commits +// happen for idleTimeout since timeLastSaved, the current position of the unsavedEvent +// is committed (updatePos). +// * An empty transaction: Empty transactions are necessary because the current +// position of that transaction may be the stop position. If so, we have to record it. +// If not significant, we should avoid saving these empty transactions individually +// because they can cause unnecessary churn and binlog bloat. We should +// also not go for too long without saving because we should not fall way behind +// on the current replication position. Additionally, WaitForPos or other external +// agents could be waiting on that specific position by watching the vreplication +// record. +// * A group of transactions: Combine them into a single transaction. +// * Partial transaction: Replay the events received so far and refetch from relay log +// for more. +// * A combination of any of the above: The trickier case is the one where a group +// of transactions come in, with the last one being partial. In this case, all transactions +// up to the last one have to be committed, and the final one must be partially applied. +// +// Of the above events, the saveable ones are COMMIT, DDL, and OTHER. Eventhough +// A GTID comes as a separate event, it's not saveable until a subsequent saveable +// event occurs. VStreamer currently sequences the GTID to be sent just before +// a saveable event, but we do not rely on this. To handle this, we only remember +// the position when a GTID is encountered. The next saveable event causes the +// current position to be saved. +// +// In order to handle the above use cases, we use an implicit transaction scheme: +// A BEGIN does not really start a transaction. Ony a ROW event does. With this +// approach, no transaction gets started if an empty one arrives. If a we receive +// a commit, and a we are not in a transaction, we infer that the transaction was +// empty, and remember it as an unsaved event. The next GTID event will reset the +// unsaved event. If the next commit is also an empty transaction, then the latest +// one gets remembered as unsaved. A non-empty transaction, a must-save event, +// or a timeout will eventually cause the next save. +// The timeout (1s) plays another significant role: If the source and target shards of +// the replication are the same, then a commit of an unsaved event will generate +// another empty event. This is an infinite loop, and the timeout prevents +// this from becoming a tight loop. +// TODO(sougou): we can look at recognizing self-generated events and find a better +// way to handle them. func (vp *vplayer) applyEvents(ctx context.Context, relay *relayLog) error { defer vp.vr.dbClient.Rollback() @@ -242,19 +312,12 @@ func (vp *vplayer) applyEvents(ctx context.Context, relay *relayLog) error { behind := time.Now().UnixNano() - vp.lastTimestampNs - vp.timeOffsetNs vp.vr.stats.SecondsBehindMaster.Set(behind / 1e9) } - // Filtered replication often ends up receiving a large number of empty transactions. - // This is required because the player needs to know the latest position of the source. - // This allows it to stop at that position if requested. - // This position also needs to be saved, which will allow an external request - // to check if a required position has been reached. - // However, this leads to a large number of empty commits which not only slow - // down the replay, but also generate binlog bloat on the target. - // In order to mitigate this problem, empty transactions are saved at most - // once every idleTimeout. + // Empty transactions are saved at most once every idleTimeout. // This covers two situations: // 1. Fetch was idle for idleTimeout. // 2. We've been receiving empty events for longer than idleTimeout. - // In both cases, now > timeLastSaved. If so, any unsaved GTID should be saved. + // In both cases, now > timeLastSaved. If so, the GTID of the last unsavedEvent + // must be saved. if time.Since(vp.timeLastSaved) >= idleTimeout && vp.unsavedEvent != nil { posReached, err := vp.updatePos(vp.unsavedEvent.Timestamp) if err != nil { @@ -275,10 +338,18 @@ func (vp *vplayer) applyEvents(ctx context.Context, relay *relayLog) error { mustSave := false switch event.Type { case binlogdatapb.VEventType_COMMIT: + // If we've reached the stop position, we must save the current commit + // even if it's empty. So, the next applyEvent is invoked with the + // mustSave flag. if !vp.stopPos.IsZero() && vp.pos.AtLeast(vp.stopPos) { mustSave = true break } + // In order to group multiple commits into a single one, we look ahead for + // the next commit. If there is one, we skip the current commit, which ends up + // applying the next set of events as part of the current transaction. This approach + // also handles the case where the last transaction is partial. In that case, + // we only group the transactions with commits we've seen so far. if hasAnotherCommit(items, i, j+1) { continue } @@ -294,7 +365,7 @@ func (vp *vplayer) applyEvents(ctx context.Context, relay *relayLog) error { func hasAnotherCommit(items [][]*binlogdatapb.VEvent, i, j int) bool { for i < len(items) { for j < len(items[i]) { - // We ignore GTID, BEGIN, FIELD and ROW. + // We skip GTID, BEGIN, FIELD, ROW and DMLs. switch items[i][j].Type { case binlogdatapb.VEventType_COMMIT: return true @@ -318,7 +389,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m return err } vp.pos = pos - // A new position should not be saved until a commit or DDL. + // A new position should not be saved until a saveable event occurs. vp.unsavedEvent = nil if vp.stopPos.IsZero() { return nil @@ -423,6 +494,10 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m } return io.EOF case binlogdatapb.OnDDLAction_EXEC: + // It's impossible to save the position transactionally with the satement. + // So, we apply the DDL first, and then save the position. + // Manual intervention may be needed if there is a partial + // failure here. if _, err := vp.vr.dbClient.ExecuteWithRetry(ctx, event.Ddl); err != nil { return err } From e800254baef361139c6d7acba0624544a1388330 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 11 Jan 2020 17:38:38 -0800 Subject: [PATCH 242/825] vrepl: refactor and fix stats reporting Not all code paths were updating the stats. This was causing the stats reported in /debug/status to be unreliable. This refactor keeps the stats and reporting in sync by performing the updates at the lower level functions that also update the vreplication table. Signed-off-by: Sugu Sougoumarane --- go/vt/binlog/binlogplayer/binlog_player.go | 41 ++++++++++--------- .../tabletmanager/vreplication/controller.go | 1 + .../tabletmanager/vreplication/stats.go | 8 +--- .../tabletmanager/vreplication/vplayer.go | 13 +----- .../tabletmanager/vreplication/vreplicator.go | 23 ++++++++++- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/go/vt/binlog/binlogplayer/binlog_player.go b/go/vt/binlog/binlogplayer/binlog_player.go index d7614759a2c..d662129cd17 100644 --- a/go/vt/binlog/binlogplayer/binlog_player.go +++ b/go/vt/binlog/binlogplayer/binlog_player.go @@ -79,6 +79,8 @@ type Stats struct { SecondsBehindMaster sync2.AtomicInt64 History *history.History + + State sync2.AtomicString } // SetLastPosition sets the last replication position. @@ -176,17 +178,12 @@ func NewBinlogPlayerTables(dbClient DBClient, tablet *topodatapb.Tablet, tables // If an error is encountered, it updates the vreplication state to "Error". // If a stop position was specified, and reached, the state is updated to "Stopped". func (blp *BinlogPlayer) ApplyBinlogEvents(ctx context.Context) error { - if err := SetVReplicationState(blp.dbClient, blp.uid, BlpRunning, ""); err != nil { + if err := blp.setVReplicationState(BlpRunning, ""); err != nil { log.Errorf("Error writing Running state: %v", err) } if err := blp.applyEvents(ctx); err != nil { - msg := err.Error() - blp.blplStats.History.Add(&StatsHistoryRecord{ - Time: time.Now(), - Message: msg, - }) - if err := SetVReplicationState(blp.dbClient, blp.uid, BlpError, msg); err != nil { + if err := blp.setVReplicationState(BlpError, err.Error()); err != nil { log.Errorf("Error writing stop state: %v", err) } return err @@ -241,14 +238,14 @@ func (blp *BinlogPlayer) applyEvents(ctx context.Context) error { case blp.position.Equal(blp.stopPosition): msg := fmt.Sprintf("not starting BinlogPlayer, we're already at the desired position %v", blp.stopPosition) log.Info(msg) - if err := SetVReplicationState(blp.dbClient, blp.uid, BlpStopped, msg); err != nil { + if err := blp.setVReplicationState(BlpStopped, msg); err != nil { log.Errorf("Error writing stop state: %v", err) } return nil case blp.position.AtLeast(blp.stopPosition): msg := fmt.Sprintf("starting point %v greater than stopping point %v", blp.position, blp.stopPosition) log.Error(msg) - if err := SetVReplicationState(blp.dbClient, blp.uid, BlpStopped, msg); err != nil { + if err := blp.setVReplicationState(BlpStopped, msg); err != nil { log.Errorf("Error writing stop state: %v", err) } // Don't return an error. Otherwise, it will keep retrying. @@ -348,7 +345,7 @@ func (blp *BinlogPlayer) applyEvents(ctx context.Context) error { if blp.position.AtLeast(blp.stopPosition) { msg := "Reached stopping position, done playing logs" log.Info(msg) - if err := SetVReplicationState(blp.dbClient, blp.uid, BlpStopped, msg); err != nil { + if err := blp.setVReplicationState(BlpStopped, msg); err != nil { log.Errorf("Error writing stop state: %v", err) } return nil @@ -463,6 +460,21 @@ func (blp *BinlogPlayer) writeRecoveryPosition(tx *binlogdatapb.BinlogTransactio return nil } +func (blp *BinlogPlayer) setVReplicationState(state, message string) error { + if message != "" { + blp.blplStats.History.Add(&StatsHistoryRecord{ + Time: time.Now(), + Message: message, + }) + } + blp.blplStats.State.Set(state) + query := fmt.Sprintf("update _vt.vreplication set state='%v', message=%v where id=%v", state, encodeString(MessageTruncate(message)), blp.uid) + if _, err := blp.dbClient.ExecuteFetch(query, 1); err != nil { + return fmt.Errorf("could not set state: %v: %v", query, err) + } + return nil +} + // CreateVReplicationTable returns the statements required to create // the _vt.vreplication table. // id: is an auto-increment column that identifies the stream. @@ -507,15 +519,6 @@ func AlterVReplicationTable() []string { return []string{"ALTER TABLE _vt.vreplication ADD COLUMN db_name VARBINARY(255) NOT NULL"} } -// SetVReplicationState updates the state in the _vt.vreplication table. -func SetVReplicationState(dbClient DBClient, uid uint32, state, message string) error { - query := fmt.Sprintf("update _vt.vreplication set state='%v', message=%v where id=%v", state, encodeString(MessageTruncate(message)), uid) - if _, err := dbClient.ExecuteFetch(query, 1); err != nil { - return fmt.Errorf("could not set state: %v: %v", query, err) - } - return nil -} - // VRSettings contains the settings of a vreplication table. type VRSettings struct { StartPos mysql.Position diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller.go b/go/vt/vttablet/tabletmanager/vreplication/controller.go index 5e9a9b75eae..b215edd2771 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller.go @@ -90,6 +90,7 @@ func newController(ctx context.Context, params map[string]string, dbClientFactor ct.id = uint32(id) ct.workflow = params["workflow"] + blpStats.State.Set(params["state"]) // Nothing to do if replication is stopped. if params["state"] == binlogplayer.BlpStopped { ct.cancel = func() {} diff --git a/go/vt/vttablet/tabletmanager/vreplication/stats.go b/go/vt/vttablet/tabletmanager/vreplication/stats.go index ea688fa4774..a13b1e2e549 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/stats.go +++ b/go/vt/vttablet/tabletmanager/vreplication/stats.go @@ -148,12 +148,6 @@ func (st *vrStats) status() *EngineStatus { status.Controllers = make([]*ControllerStatus, len(st.controllers)) i := 0 for _, ct := range st.controllers { - state := "Running" - select { - case <-ct.done: - state = "Stopped" - default: - } status.Controllers[i] = &ControllerStatus{ Index: ct.id, Source: ct.source.String(), @@ -162,7 +156,7 @@ func (st *vrStats) status() *EngineStatus { SecondsBehindMaster: ct.blpStats.SecondsBehindMaster.Get(), Counts: ct.blpStats.Timings.Counts(), Rates: ct.blpStats.Rates.Get(), - State: state, + State: ct.blpStats.State.Get(), SourceTablet: ct.sourceTablet.Get(), Messages: ct.blpStats.MessageHistory(), } diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 464cc0d79c5..05d1ec288dc 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -112,18 +112,7 @@ func (vp *vplayer) play(ctx context.Context) error { } } - if err := vp.fetchAndApply(ctx); err != nil { - msg := err.Error() - vp.vr.stats.History.Add(&binlogplayer.StatsHistoryRecord{ - Time: time.Now(), - Message: msg, - }) - if err := vp.vr.setMessage(msg); err != nil { - log.Errorf("Failed to set error state: %v", err) - } - return err - } - return nil + return vp.fetchAndApply(ctx) } // fetchAndApply performs the fetching and application of the binlogs. diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index 15f1c311d62..e22dc9638a1 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -113,6 +113,16 @@ func newVReplicator(id uint32, source *binlogdatapb.BinlogSource, sourceVStreame // However, there are some subtle differences, explained in the plan builder // code. func (vr *vreplicator) Replicate(ctx context.Context) error { + err := vr.replicate(ctx) + if err != nil { + if err := vr.setMessage(err.Error()); err != nil { + log.Errorf("Failed to set error state: %v", err) + } + } + return err +} + +func (vr *vreplicator) replicate(ctx context.Context) error { tableKeys, err := vr.buildTableKeys() if err != nil { return err @@ -218,7 +228,18 @@ func (vr *vreplicator) setMessage(message string) error { } func (vr *vreplicator) setState(state, message string) error { - return binlogplayer.SetVReplicationState(vr.dbClient, vr.id, state, message) + if message != "" { + vr.stats.History.Add(&binlogplayer.StatsHistoryRecord{ + Time: time.Now(), + Message: message, + }) + } + vr.stats.State.Set(state) + query := fmt.Sprintf("update _vt.vreplication set state='%v', message=%v where id=%v", state, encodeString(binlogplayer.MessageTruncate(message)), vr.id) + if _, err := vr.dbClient.ExecuteFetch(query, 1); err != nil { + return fmt.Errorf("could not set state: %v: %v", query, err) + } + return nil } func encodeString(in string) string { From f3d09eac8d383d2525490e34e1396f19eafda131 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 12 Jan 2020 17:24:31 -0800 Subject: [PATCH 243/825] vrepl: document plan builders Signed-off-by: Sugu Sougoumarane --- .../vreplication/replicator_plan.go | 61 ++++++++++++++----- .../vreplication/replicator_plan_test.go | 2 +- .../vreplication/table_plan_builder.go | 53 +++++++++++++++- 3 files changed, 97 insertions(+), 19 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go index a89f887f92b..903810e27e3 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go @@ -29,12 +29,19 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" ) -// ReplicatorPlan is the execution plan for the replicator. -// The constructor for this is buildReplicatorPlan in table_plan_builder.go -// The initial build identifies the tables that need to be replicated, -// and builds partial TablePlan objects for them. The partial plan is used -// to send streaming requests. As the responses return field info, this -// information is used to build the final execution plan (buildExecutionPlan). +// ReplicatorPlan is the execution plan for the replicator. It contains +// plans for all the tables it's replicating. Every phase of vreplication +// builds its own instance of the ReplicatorPlan. This is because the plan +// depends on copyState, which changes on every iteration. +// The table plans within ReplicatorPlan will not be fully populated because +// all the information is not available initially. +// For simplicity, the ReplicatorPlan is immutable. +// Once we get the field info for a table from the stream response, +// we'll have all the necessary info to build the final plan. +// At that time, buildExecutionPlan is invoked, which will make a copy +// of the TablePlan from ReplicatorPlan, and fill the rest +// of the members, leaving the original plan unchanged. +// The constructor is buildReplicatorPlan in table_plan_builder.go type ReplicatorPlan struct { VStreamFilter *binlogdatapb.Filter TargetTables map[string]*TablePlan @@ -50,6 +57,8 @@ func (rp *ReplicatorPlan) buildExecutionPlan(fieldEvent *binlogdatapb.FieldEvent // Unreachable code. return nil, fmt.Errorf("plan not found for %s", fieldEvent.TableName) } + // If Insert is initialized, then it means that we knew the column + // names and have already built most of the plan. if prelim.Insert != nil { tplanv := *prelim // We know that we sent only column names, but they may be backticked. @@ -93,6 +102,7 @@ func (rp *ReplicatorPlan) buildFromFields(tableName string, lastpk *sqltypes.Res } tpb.colExprs = append(tpb.colExprs, cexpr) } + // The following actions are a subset of buildTablePlan. if err := tpb.analyzePK(rp.tableKeys); err != nil { return nil, err } @@ -118,19 +128,35 @@ func (rp *ReplicatorPlan) MarshalJSON() ([]byte, error) { return json.Marshal(&v) } -// TablePlan is the execution plan for a table within a player stream. +// TablePlan is the execution plan for a table within a replicator. +// If the column names are not known at the time of plan building (like +// select *), then only TargetName, SendRule and Lastpk are initialized. +// When the stream returns the field info, those are used as column +// names to build the final plan. +// Lastpk comes from copyState. If it's set, then the generated plans +// are significantly different because any events that fall beyond +// Lastpk must be excluded. +// If column names were known upfront, then all fields of TablePlan +// are built except for Fields. This member is populated only after +// the field info is received from the stream. // The ParsedQuery objects assume that a map of before and after values // will be built based on the streaming rows. Before image values will // be prefixed with a "b_", and after image values will be prefixed -// with a "a_". +// with a "a_". The TablePlan structure is used during all the phases +// of vreplication: catchup, copy, fastforward, or regular replication. type TablePlan struct { - TargetName string - SendRule *binlogdatapb.Rule - PKReferences []string - // Lastpk is used for delayed generation of replication queries. - Lastpk *sqltypes.Result + // TargetName, SendRule will always be initialized. + // Lastpk will also be initialized if it was specified, and + // will be used for building the final plan after field info + // is received. + TargetName string + SendRule *binlogdatapb.Rule + Lastpk *sqltypes.Result // BulkInsertFront, BulkInsertValues and BulkInsertOnDup are used - // by vcopier. + // by vcopier. These three parts are combined to build bulk insert + // statements. This is functionally equivalent to generating + // multiple statements using the "Insert" construct, but much more + // efficient for the copy phase. BulkInsertFront *sqlparser.ParsedQuery BulkInsertValues *sqlparser.ParsedQuery BulkInsertOnDup *sqlparser.ParsedQuery @@ -142,6 +168,9 @@ type TablePlan struct { Update *sqlparser.ParsedQuery Delete *sqlparser.ParsedQuery Fields []*querypb.Field + // PKReferences is used to check if an event changed + // a primary key column (row move). + PKReferences []string } // MarshalJSON performs a custom JSON Marshalling. @@ -149,23 +178,23 @@ func (tp *TablePlan) MarshalJSON() ([]byte, error) { v := struct { TargetName string SendRule string - PKReferences []string `json:",omitempty"` InsertFront *sqlparser.ParsedQuery `json:",omitempty"` InsertValues *sqlparser.ParsedQuery `json:",omitempty"` InsertOnDup *sqlparser.ParsedQuery `json:",omitempty"` Insert *sqlparser.ParsedQuery `json:",omitempty"` Update *sqlparser.ParsedQuery `json:",omitempty"` Delete *sqlparser.ParsedQuery `json:",omitempty"` + PKReferences []string `json:",omitempty"` }{ TargetName: tp.TargetName, SendRule: tp.SendRule.Match, - PKReferences: tp.PKReferences, InsertFront: tp.BulkInsertFront, InsertValues: tp.BulkInsertValues, InsertOnDup: tp.BulkInsertOnDup, Insert: tp.Insert, Update: tp.Update, Delete: tp.Delete, + PKReferences: tp.PKReferences, } return json.Marshal(&v) } diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go index 488f325ef0f..a7aec43d436 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan_test.go @@ -35,13 +35,13 @@ type TestReplicatorPlan struct { type TestTablePlan struct { TargetName string SendRule string - PKReferences []string `json:",omitempty"` InsertFront string `json:",omitempty"` InsertValues string `json:",omitempty"` InsertOnDup string `json:",omitempty"` Insert string `json:",omitempty"` Update string `json:",omitempty"` Delete string `json:",omitempty"` + PKReferences []string `json:",omitempty"` } func TestBuildPlayerPlan(t *testing.T) { diff --git a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go index a5b7c1291b8..42e1f31e3bc 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go +++ b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go @@ -36,9 +36,13 @@ import ( // TODO(sougou): support this on vstreamer side also. const ExcludeStr = "exclude" +// tablePlanBuilder contains the metadata needed for building a TablePlan. type tablePlanBuilder struct { name sqlparser.TableIdent sendSelect *sqlparser.Select + // selColumns keeps track of the columns we want to pull from source. + // If Lastpk is set, we compare this list against the table's pk and + // add missing references. selColumns map[string]bool colExprs []*colExpr onInsert insertType @@ -75,12 +79,22 @@ const ( ) // insertType describes the type of insert statement to generate. +// Please refer to TestBuildPlayerPlan for examples. type insertType int // The following values are the various insert types. const ( + // insertNormal is for normal selects without a group b, like + // "select a+b as c from t". insertNormal = insertType(iota) + // insertOnDup is for the more traditional grouped expressions, like + // "select a, b, count(*) as c from t group by a". For statements + // like these, "insert.. on duplicate key" statements will be generated + // causing "b" to be updated to the latest value (last value wins). insertOnDup + // insertIgnore is for special grouped expressions where all columns are + // in the group by, like "select a, b, c from to group by a, b, c". + // This generates "insert ignore" statements (first value wins). insertIgnore ) @@ -89,6 +103,14 @@ const ( // a table-specific rule is built to be sent to the source. We don't send the // original rule to the source because it may not match the same tables as the // target. +// tableKeys specifies the list of primary key columns for each table. +// copyState is a map of tables that have not been fully copied yet. +// If a table is not present in copyState, then it has been fully copied. If so, +// all replication events are applied. The table still has to match a Filter.Rule. +// If it has a non-nil entry, then the value is the last primary key (lastpk) +// that was copied. If so, only replication events < lastpk are applied. +// If the entry is nil, then copying of the table has not started yet. If so, +// no events are applied. // The TablePlan built is a partial plan. The full plan for a table is built // when we receive field information from events or rows sent by the source. // buildExecutionPlan is the function that builds the full plan. @@ -153,6 +175,7 @@ func MatchTable(tableName string, filter *binlogdatapb.Filter) (*binlogdatapb.Ru func buildTablePlan(tableName, filter string, tableKeys map[string][]string, lastpk *sqltypes.Result) (*TablePlan, error) { query := filter + // generate equivalent select statement if filter is empty or a keyrange. switch { case filter == "": buf := sqlparser.NewTrackedBuffer(nil) @@ -174,6 +197,8 @@ func buildTablePlan(tableName, filter string, tableKeys map[string][]string, las } if expr, ok := sel.SelectExprs[0].(*sqlparser.StarExpr); ok { + // If it's a "select *", we return a partial plan, and complete + // it when we get back field info from the stream. if len(sel.SelectExprs) != 1 { return nil, fmt.Errorf("unexpected: %v", sqlparser.String(sel)) } @@ -202,6 +227,13 @@ func buildTablePlan(tableName, filter string, tableKeys map[string][]string, las if err := tpb.analyzeExprs(sel.SelectExprs); err != nil { return nil, err } + // It's possible that the target table does not materialize all + // the primary keys of the source table. In such situations, + // we still have to be able to validate the incoming event + // against the current lastpk. For this, we have to request + // the missing columns so we can compare against those values. + // If there is no lastpk to validate against, then we don't + // care. if tpb.lastpk != nil { for _, f := range tpb.lastpk.Fields { tpb.addCol(sqlparser.NewColIdent(f.Name)) @@ -243,13 +275,13 @@ func (tpb *tablePlanBuilder) generate(tableKeys map[string][]string) *TablePlan return &TablePlan{ TargetName: tpb.name.String(), Lastpk: tpb.lastpk, - PKReferences: pkrefs, BulkInsertFront: tpb.generateInsertPart(sqlparser.NewTrackedBuffer(bvf.formatter)), BulkInsertValues: tpb.generateValuesPart(sqlparser.NewTrackedBuffer(bvf.formatter), bvf), BulkInsertOnDup: tpb.generateOnDupPart(sqlparser.NewTrackedBuffer(bvf.formatter)), Insert: tpb.generateInsertStatement(), Update: tpb.generateUpdateStatement(), Delete: tpb.generateDeleteStatement(), + PKReferences: pkrefs, } } @@ -360,6 +392,7 @@ func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr case *sqlparser.Subquery: return false, fmt.Errorf("unsupported subquery: %v", sqlparser.String(node)) case *sqlparser.FuncExpr: + // Other aggregates are not supported. if node.IsAggregate() { return false, fmt.Errorf("unexpected: %v", sqlparser.String(node)) } @@ -373,6 +406,8 @@ func (tpb *tablePlanBuilder) analyzeExpr(selExpr sqlparser.SelectExpr) (*colExpr return cexpr, nil } +// addCol adds the specified column to the send query +// if it's not already present. func (tpb *tablePlanBuilder) addCol(ident sqlparser.ColIdent) { if tpb.selColumns[ident.Lowered()] { return @@ -385,6 +420,7 @@ func (tpb *tablePlanBuilder) addCol(ident sqlparser.ColIdent) { func (tpb *tablePlanBuilder) analyzeGroupBy(groupBy sqlparser.GroupBy) error { if groupBy == nil { + // If there's no grouping, the it's an insertNormal. return nil } for _, expr := range groupBy { @@ -401,9 +437,11 @@ func (tpb *tablePlanBuilder) analyzeGroupBy(groupBy sqlparser.GroupBy) error { } cexpr.isGrouped = true } + // If all colExprs are grouped, then it's an insertIgnore. tpb.onInsert = insertIgnore for _, cExpr := range tpb.colExprs { if !cExpr.isGrouped { + // If some colExprs are not grouped, then it's an insertOnDup. tpb.onInsert = insertOnDup break } @@ -411,6 +449,7 @@ func (tpb *tablePlanBuilder) analyzeGroupBy(groupBy sqlparser.GroupBy) error { return nil } +// analyzePK builds tpb.pkCols. func (tpb *tablePlanBuilder) analyzePK(tableKeys map[string][]string) error { pkcols, ok := tableKeys[tpb.name.String()] if !ok { @@ -445,9 +484,12 @@ func (tpb *tablePlanBuilder) generateInsertStatement() *sqlparser.ParsedQuery { tpb.generateInsertPart(buf) if tpb.lastpk == nil { + // If there's no lastpk, generate straight values. buf.Myprintf(" values ", tpb.name) tpb.generateValuesPart(buf, bvf) } else { + // If there is a lastpk, generate values as a select from dual + // where the pks < lastpk tpb.generateSelectPart(buf, bvf) } tpb.generateOnDupPart(buf) @@ -482,6 +524,7 @@ func (tpb *tablePlanBuilder) generateValuesPart(buf *sqlparser.TrackedBuffer, bv case opCount: buf.WriteString("1") case opSum: + // NULL values must be treated as 0 for SUM. buf.Myprintf("ifnull(%v, 0)", cexpr.expr) } } @@ -517,12 +560,18 @@ func (tpb *tablePlanBuilder) generateOnDupPart(buf *sqlparser.TrackedBuffer) *sq buf.Myprintf(" on duplicate key update ") separator := "" for _, cexpr := range tpb.colExprs { + // We don't know of a use case where the group by columns + // don't match the pk of a table. But we'll allow this, + // and won't update the pk column with the new value if + // this does happen. This can be revisited if there's + // a legitimate use case in the future that demands + // a different behavior. This rule is applied uniformly + // for updates and deletes also. if cexpr.isGrouped || cexpr.isPK { continue } buf.Myprintf("%s%v=", separator, cexpr.colName) separator = ", " - // TODO: What to do here? switch cexpr.operation { case opExpr: buf.Myprintf("values(%v)", cexpr.colName) From 7801336724eb5e5d25bbbf4bdc913c2054b84559 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Mon, 9 Mar 2020 09:41:31 -0600 Subject: [PATCH 244/825] Remove Travis (no tests running) Signed-off-by: Morgan Tocker --- .travis.yml | 59 ------------------------------------ travis/check_make_proto.sh | 61 -------------------------------------- travis/log_gomaxprocs.go | 28 ----------------- 3 files changed, 148 deletions(-) delete mode 100644 .travis.yml delete mode 100755 travis/check_make_proto.sh delete mode 100644 travis/log_gomaxprocs.go diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c23b98c5341..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Travis CI configuration for Vitess. -# -# Note that we have our own test runner written in Go (test.go) which downloads -# our bootstrap Docker image and runs all tests within Docker. -# This solution is slower than running the tests natively, but has the -# advantage that we do not have to install (and cache) any dependencies within -# Travis itself. -# -# For the record, we expect the following overhead per Travis build: -# - 20 seconds Travis starting up the VM. -# - Up to 2 minutes to pull the Docker image. -# - More than a minute to run "make build" and cache the result as temporary -# Docker image. -# -# In total, it will always take up to 4 minutes until the environment is -# bootstrapped and the first test can be run. -# -# Open TODOs: -# - Re-add travis/check_make_proto.sh, ideally as part of test/config.json. -# - Add a presubmit which checks that if bootstrap has changed, and docker image is out of date. - -# sudo is required because we run Docker in our builds. -# See: https://docs.travis-ci.com/user/docker/ -sudo: required - -services: - - docker - -language: go -go: - - 1.12.x -go_import_path: vitess.io/vitess -env: - global: - # Run go build and test with -p 4 (i.e. up to 4 packages are compiled/tested in parallel). - # As of 07/2015 this value works best in a Travis CI container. - # TODO(mberlin): This will probably not be passed through to Docker. Verify and fix this. - - VT_GO_PARALLEL_VALUE=4 - # Note: The per test timeout must always be < 10 minutes because test.go - # does not produce any log output while running and Travis kills a - # build after 10 minutes without log output. - # See: https://docs.travis-ci.com/user/customizing-the-build#Build-Timeouts - # To diagnose stuck tests, add "-follow" to TEST_FLAGS below. Then test.go - # will print the test's output. - - TEST_FLAGS="-docker -use_docker_cache -timeout=8m -print-log" - matrix: - # NOTE: Travis CI schedules up to 5 tests simultaneously. - # All our tests should be spread out as evenly as possible across these 5 slots. - # We should always utilize all 5 slots because the cost of the setup is high (up to four minutes). - - TEST_MATRIX="-shard 0" - - TEST_MATRIX="-shard 1" - - TEST_MATRIX="-shard 2" -script: - - go run test.go $TEST_FLAGS $TEST_MATRIX - # Uncomment the next line to verify the GOMAXPROCS value (should be 2 as of 09/2017). - # - ./docker/test/run.sh mysql57 'go run travis/log_gomaxprocs.go' -notifications: - slack: - secure: S9n4rVWuEvSaF9RZUIx3Nkc2ycpM254zmalyMMbT5EmV1Xz6Zww2FL39RR5d57zsZ2M8GVW5n9uB8Bx57mr+L/wClEltzknYr7MA2/yYNMo5iK83tdQtNNw5U+dZG9/Plhlm4n883lcw9aZOyotNcLg2zBsd48Y74olk4NdmSfo= diff --git a/travis/check_make_proto.sh b/travis/check_make_proto.sh deleted file mode 100755 index 30af1bdcf83..00000000000 --- a/travis/check_make_proto.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Verifies that the generated protobuf and gRPC files under version control -# are up to date. - -# It does so by running "make proto" and verifying that any files tracked by -# Git do not change. If that's not the case, "make proto" must be run and all -# changed files must be included in the pull request. - - -# We do error checking manually. -set +e - -function error() { - script_name=`basename "${BASH_SOURCE[0]}"` - echo - echo -e "ERROR: $script_name: $1" - exit 1 -} - -# Undo changes to vendor.json. govendor sometimes changes it, which is not necessary. -git checkout vendor/vendor.json - -git diff --exit-code -if [ $? -ne 0 ]; then - error "We cannot check if 'make proto' is up to date because some files have already changed. Please see the diff above and fix any local modifications happening prior to this step." -fi - -make proto -if [ $? -ne 0 ]; then - error "Failed to run 'make proto'. Please see the error above and fix it. This should not happen." -fi - -# Check if the file only changed due to a different Go version. -# Go 1.7 has changes to the compression library which results in different -# output and therefore in different generated protobuf code. -git diff -U0 --no-prefix | grep -vE "(bytes of a gzipped FileDescriptorProto|[-\+]{3} .*\.go$|diff --git .*\.go$|^index |^@@ .+ @@ var fileDescriptor0 = \[\]byte\{$|^[-\+] (0x[0-9a-f]{2}, )*0x[0-9a-f]{2},$)" -if [ $? -eq 1 ]; then - # The protobuf files changed only due to a different Go version. - # Revert the changes and continue with the actual check. - git checkout . -fi - -git diff --exit-code -if [ $? -ne 0 ]; then - error "Generated protobuf and gRPC files are not up to date. See diff above. You have to generate them locally and include them in your pull request.\n\nTherefore run a) ./bootstrap.sh b) make proto and c) commit the changed files." -fi diff --git a/travis/log_gomaxprocs.go b/travis/log_gomaxprocs.go deleted file mode 100644 index d6c18b9ce15..00000000000 --- a/travis/log_gomaxprocs.go +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// The sole purpose of this file is to print the configured value of GOMAXPROCS. -// This way we can verify that the Travis CI worker is configured correctly by default. -package main - -import ( - "fmt" - "runtime" -) - -func main() { - fmt.Println("GOMAXPROCS is:", runtime.GOMAXPROCS(0)) -} From 0067c7e7906c7b63d607e6d941dc920bd5e15a8e Mon Sep 17 00:00:00 2001 From: Rafael Chacon Date: Mon, 9 Mar 2020 16:26:45 -0700 Subject: [PATCH 245/825] Adds support for autocommit in execute method Signed-off-by: Rafael Chacon --- go/vt/vtgate/executor.go | 2 +- go/vt/vtgate/resolver.go | 5 ++++- go/vt/vtgate/resolver_test.go | 8 +++++-- go/vt/vtgate/scatter_conn.go | 25 +++++++++++---------- go/vt/vtgate/scatter_conn_test.go | 32 +++++++++++++-------------- go/vt/vtgate/tx_conn_test.go | 36 +++++++++++++++---------------- go/vt/vtgate/vtgate.go | 5 +++-- 7 files changed, 62 insertions(+), 51 deletions(-) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index c3aff0561df..67be68b21bf 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -346,7 +346,7 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql } func (e *Executor) destinationExec(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { - return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession.Session, false /* notInTransaction */, safeSession.Options, logStats) + return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession.Session, false /* notInTransaction */, safeSession.Options, logStats, false /* autocommit */) } func (e *Executor) handleDDL(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go index f7bae5e9a7d..db20485c0ed 100644 --- a/go/vt/vtgate/resolver.go +++ b/go/vt/vtgate/resolver.go @@ -87,6 +87,7 @@ func (res *Resolver) Execute( notInTransaction bool, options *querypb.ExecuteOptions, logStats *LogStats, + autocommit bool, ) (*sqltypes.Result, error) { rss, err := res.resolver.ResolveDestination(ctx, keyspace, tabletType, destination) if err != nil { @@ -104,7 +105,9 @@ func (res *Resolver) Execute( tabletType, NewSafeSession(session), notInTransaction, - options) + options, + autocommit, + ) if isRetryableError(err) { newRss, err := res.resolver.ResolveDestination(ctx, keyspace, tabletType, destination) if err != nil { diff --git a/go/vt/vtgate/resolver_test.go b/go/vt/vtgate/resolver_test.go index 5a8c473db18..2c123b255fa 100644 --- a/go/vt/vtgate/resolver_test.go +++ b/go/vt/vtgate/resolver_test.go @@ -53,7 +53,9 @@ func TestResolverExecuteKeyspaceIds(t *testing.T) { nil, false, nil, - nil) + nil, + false, + ) }) } @@ -68,7 +70,9 @@ func TestResolverExecuteKeyRanges(t *testing.T) { nil, false, nil, - nil) + nil, + false, + ) }) } diff --git a/go/vt/vtgate/scatter_conn.go b/go/vt/vtgate/scatter_conn.go index b64402c7beb..919af86be2f 100644 --- a/go/vt/vtgate/scatter_conn.go +++ b/go/vt/vtgate/scatter_conn.go @@ -126,6 +126,7 @@ func (stc *ScatterConn) Execute( session *SafeSession, notInTransaction bool, options *querypb.ExecuteOptions, + autocommit bool, ) (*sqltypes.Result, error) { // mu protects qr @@ -140,19 +141,21 @@ func (stc *ScatterConn) Execute( session, notInTransaction, func(rs *srvtopo.ResolvedShard, i int, shouldBegin bool, transactionID int64) (int64, error) { - var innerqr *sqltypes.Result - if shouldBegin { - var err error + var ( + innerqr *sqltypes.Result + err error + opts *querypb.ExecuteOptions + ) + switch { + case autocommit: + innerqr, err = stc.executeAutocommit(ctx, rs, query, bindVars, opts) + case shouldBegin: innerqr, transactionID, err = rs.QueryService.BeginExecute(ctx, rs.Target, query, bindVars, options) - if err != nil { - return transactionID, err - } - } else { - var err error + default: innerqr, err = rs.QueryService.Execute(ctx, rs.Target, query, bindVars, transactionID, options) - if err != nil { - return transactionID, err - } + } + if err != nil { + return transactionID, err } mu.Lock() diff --git a/go/vt/vtgate/scatter_conn_test.go b/go/vt/vtgate/scatter_conn_test.go index 9eafc852113..79e21b96b0a 100644 --- a/go/vt/vtgate/scatter_conn_test.go +++ b/go/vt/vtgate/scatter_conn_test.go @@ -48,7 +48,7 @@ func TestScatterConnExecute(t *testing.T) { return nil, err } - return sc.Execute(context.Background(), "query", nil, rss, topodatapb.TabletType_REPLICA, NewSafeSession(nil), false, nil) + return sc.Execute(context.Background(), "query", nil, rss, topodatapb.TabletType_REPLICA, NewSafeSession(nil), false, nil, false) }) } @@ -279,7 +279,7 @@ func TestMaxMemoryRows(t *testing.T) { } session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - _, err = sc.Execute(context.Background(), "query1", nil, rss, topodatapb.TabletType_REPLICA, session, true, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss, topodatapb.TabletType_REPLICA, session, true, nil, false) want := "in-memory row count exceeded allowed limit of 3" if err == nil || err.Error() != want { t.Errorf("Execute(): %v, want %v", err, want) @@ -432,8 +432,8 @@ func TestScatterConnQueryNotInTransaction(t *testing.T) { } session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_REPLICA, session, true, nil) - sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_REPLICA, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_REPLICA, session, true, nil, false) + sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_REPLICA, session, false, nil, false) wantSession := vtgatepb.Session{ InTransaction: true, @@ -482,8 +482,8 @@ func TestScatterConnQueryNotInTransaction(t *testing.T) { t.Fatalf("ResolveDestination(1) failed: %v", err) } - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_REPLICA, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_REPLICA, session, true, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_REPLICA, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_REPLICA, session, true, nil, false) wantSession = vtgatepb.Session{ InTransaction: true, @@ -532,8 +532,8 @@ func TestScatterConnQueryNotInTransaction(t *testing.T) { t.Fatalf("ResolveDestination(1) failed: %v", err) } - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_REPLICA, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_REPLICA, session, true, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_REPLICA, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_REPLICA, session, true, nil, false) wantSession = vtgatepb.Session{ InTransaction: true, @@ -588,22 +588,22 @@ func TestScatterConnSingleDB(t *testing.T) { // SingleDb (legacy) session := NewSafeSession(&vtgatepb.Session{InTransaction: true, SingleDb: true}) - _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) if err != nil { t.Error(err) } - _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil, false) if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("Multi DB exec: %v, must contain %s", err, want) } // TransactionMode_SINGLE in session session = NewSafeSession(&vtgatepb.Session{InTransaction: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}) - _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) if err != nil { t.Error(err) } - _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil, false) if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("Multi DB exec: %v, must contain %s", err, want) } @@ -611,11 +611,11 @@ func TestScatterConnSingleDB(t *testing.T) { // TransactionMode_SINGLE in txconn sc.txConn.mode = vtgatepb.TransactionMode_SINGLE session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) - _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) if err != nil { t.Error(err) } - _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil, false) if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("Multi DB exec: %v, must contain %s", err, want) } @@ -623,11 +623,11 @@ func TestScatterConnSingleDB(t *testing.T) { // TransactionMode_MULTI in txconn. Should not fail. sc.txConn.mode = vtgatepb.TransactionMode_MULTI session = NewSafeSession(&vtgatepb.Session{InTransaction: true}) - _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) if err != nil { t.Error(err) } - _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) + _, err = sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil, false) if err != nil { t.Error(err) } diff --git a/go/vt/vtgate/tx_conn_test.go b/go/vt/vtgate/tx_conn_test.go index d05e98abd12..e570247e9db 100644 --- a/go/vt/vtgate/tx_conn_test.go +++ b/go/vt/vtgate/tx_conn_test.go @@ -47,7 +47,7 @@ func TestTxConnBegin(t *testing.T) { if !proto.Equal(session, wantSession) { t.Errorf("begin: %v, want %v", session, wantSession) } - if _, err := sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, NewSafeSession(session), false, nil); err != nil { + if _, err := sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, NewSafeSession(session), false, nil, false); err != nil { t.Error(err) } @@ -89,7 +89,7 @@ func TestTxConnCommitSuccess(t *testing.T) { // Sequence the executes to ensure commit order session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) wantSession := vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{{ @@ -104,7 +104,7 @@ func TestTxConnCommitSuccess(t *testing.T) { if !proto.Equal(session.Session, &wantSession) { t.Errorf("Session:\n%+v, want\n%+v", *session.Session, wantSession) } - sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil, false) wantSession = vtgatepb.Session{ InTransaction: true, ShardSessions: []*vtgatepb.Session_ShardSession{{ @@ -371,8 +371,8 @@ func TestTxConnCommit2PC(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PC") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil, false) session.TransactionMode = vtgatepb.TransactionMode_TWOPC if err := sc.txConn.Commit(context.Background(), session); err != nil { t.Error(err) @@ -397,7 +397,7 @@ func TestTxConnCommit2PC(t *testing.T) { func TestTxConnCommit2PCOneParticipant(t *testing.T) { sc, sbc0, _, rss0, _, _ := newTestTxConnEnv(t, "TestTxConnCommit2PCOneParticipant") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) session.TransactionMode = vtgatepb.TransactionMode_TWOPC if err := sc.txConn.Commit(context.Background(), session); err != nil { t.Error(err) @@ -411,8 +411,8 @@ func TestTxConnCommit2PCCreateTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, rss1, _ := newTestTxConnEnv(t, "TestTxConnCommit2PCCreateTransactionFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss1, topodatapb.TabletType_MASTER, session, false, nil, false) sbc0.MustFailCreateTransaction = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -448,8 +448,8 @@ func TestTxConnCommit2PCPrepareFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCPrepareFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil, false) sbc1.MustFailPrepare = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -479,8 +479,8 @@ func TestTxConnCommit2PCStartCommitFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCStartCommitFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil, false) sbc0.MustFailStartCommit = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -510,8 +510,8 @@ func TestTxConnCommit2PCCommitPreparedFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCCommitPreparedFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil, false) sbc1.MustFailCommitPrepared = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -541,8 +541,8 @@ func TestTxConnCommit2PCConcludeTransactionFail(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TestTxConnCommit2PCConcludeTransactionFail") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil, false) sbc0.MustFailConcludeTransaction = 1 session.TransactionMode = vtgatepb.TransactionMode_TWOPC @@ -572,8 +572,8 @@ func TestTxConnRollback(t *testing.T) { sc, sbc0, sbc1, rss0, _, rss01 := newTestTxConnEnv(t, "TxConnRollback") session := NewSafeSession(&vtgatepb.Session{InTransaction: true}) - sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil) - sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil) + sc.Execute(context.Background(), "query1", nil, rss0, topodatapb.TabletType_MASTER, session, false, nil, false) + sc.Execute(context.Background(), "query1", nil, rss01, topodatapb.TabletType_MASTER, session, false, nil, false) if err := sc.txConn.Rollback(context.Background(), session); err != nil { t.Error(err) } diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index a196881a9ab..1ed0b2dba79 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -409,6 +409,7 @@ func (vtg *VTGate) ExecuteShards(ctx context.Context, sql string, bindVariables notInTransaction, options, nil, + false, /* autocommit */ ) if err == nil { vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) @@ -451,7 +452,7 @@ func (vtg *VTGate) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVaria goto handleError } - qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyspaceIDs(keyspaceIds), session, notInTransaction, options, nil /* LogStats */) + qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyspaceIDs(keyspaceIds), session, notInTransaction, options, nil /* LogStats */, false /* autocommit */) if err == nil { vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) return qr, nil @@ -489,7 +490,7 @@ func (vtg *VTGate) ExecuteKeyRanges(ctx context.Context, sql string, bindVariabl sql = sqlannotation.AnnotateIfDML(sql, nil) - qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyRanges(keyRanges), session, notInTransaction, options, nil /* LogStats */) + qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyRanges(keyRanges), session, notInTransaction, options, nil /* LogStats */, false /* autocommit */) if err == nil { vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) return qr, nil From 6016e650ba6f7736eb9352f3deb6870e134ff694 Mon Sep 17 00:00:00 2001 From: Rafael Chacon Date: Mon, 9 Mar 2020 17:32:52 -0700 Subject: [PATCH 246/825] Start using autocommit for V2 vtgate api Signed-off-by: Rafael Chacon --- go/vt/vtgate/executor.go | 6 +----- go/vt/vtgate/resolver.go | 8 ++++++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 7ef95cb4610..25e80380a53 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -257,10 +257,6 @@ func (e *Executor) execute(ctx context.Context, safeSession *SafeSession, sql st func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, destKeyspace string, destTabletType topodatapb.TabletType, dest key.Destination, logStats *LogStats, stmtType sqlparser.StatementType) (*sqltypes.Result, error) { if dest != nil { - // V1 mode or V3 mode with a forced shard or range target - // TODO(sougou): change this flow to go through V3 functions - // which will allow us to benefit from the autocommitable flag. - if destKeyspace == "" { return nil, errNoKeyspace } @@ -368,7 +364,7 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql } func (e *Executor) destinationExec(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { - return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession.Session, false /* notInTransaction */, safeSession.Options, logStats, false /* autocommit */) + return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession.Session, false /* notInTransaction */, safeSession.Options, logStats, true /* canAutocommit */) } func (e *Executor) handleDDL(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go index 64c0b6fa165..a875b9c33bc 100644 --- a/go/vt/vtgate/resolver.go +++ b/go/vt/vtgate/resolver.go @@ -82,7 +82,7 @@ func (res *Resolver) Execute( notInTransaction bool, options *querypb.ExecuteOptions, logStats *LogStats, - autocommit bool, + canAutocommit bool, ) (*sqltypes.Result, error) { rss, err := res.resolver.ResolveDestination(ctx, keyspace, tabletType, destination) if err != nil { @@ -91,6 +91,10 @@ func (res *Resolver) Execute( if logStats != nil { logStats.ShardQueries = uint32(len(rss)) } + + safeSession := NewSafeSession(session) + autocommit := len(rss) == 1 && safeSession.AutocommitApproval() + for { qr, err := res.scatterConn.Execute( ctx, @@ -98,7 +102,7 @@ func (res *Resolver) Execute( bindVars, rss, tabletType, - NewSafeSession(session), + safeSession, notInTransaction, options, autocommit, From aecbe16cdf253c7b188919f0367828efc7d62a1b Mon Sep 17 00:00:00 2001 From: deepthi Date: Mon, 9 Mar 2020 18:02:48 -0700 Subject: [PATCH 247/825] vtgate test for AddCellAlias without restart Signed-off-by: deepthi --- go/test/endtoend/cellalias/cell_alias_test.go | 75 ++++++++++++++++++- go/test/endtoend/sharding/base_sharding.go | 11 +-- .../sharding/initialsharding/sharding_util.go | 9 +-- .../mergesharding/mergesharding_base.go | 12 +-- .../sharding/resharding/resharding_base.go | 16 ++-- 5 files changed, 96 insertions(+), 27 deletions(-) diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index b0ed2d9f358..9fe8f8c38f5 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -288,6 +288,55 @@ func TestAlias(t *testing.T) { testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, true) testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) + + deleteInitialValues(t) + _ = vtgateInstance.TearDown() +} + +func TestAddAliasWhileVtgateUp(t *testing.T) { + defer cluster.PanicHandler(t) + insertInitialValues(t) + err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) + require.Nil(t, err) + shard1 := localCluster.Keyspaces[0].Shards[0] + shard2 := localCluster.Keyspaces[0].Shards[1] + allCells := fmt.Sprintf("%s,%s", cell1, cell2) + + expectedPartitions := map[topodata.TabletType][]string{} + expectedPartitions[topodata.TabletType_MASTER] = []string{shard1.Name, shard2.Name} + expectedPartitions[topodata.TabletType_REPLICA] = []string{shard1.Name, shard2.Name} + expectedPartitions[topodata.TabletType_RDONLY] = []string{shard1.Name, shard2.Name} + sharding.CheckSrvKeyspace(t, cell1, keyspaceName, "", 0, expectedPartitions, *localCluster) + sharding.CheckSrvKeyspace(t, cell2, keyspaceName, "", 0, expectedPartitions, *localCluster) + + vtgateInstance := localCluster.GetVtgateInstance() + vtgateInstance.CellsToWatch = allCells + vtgateInstance.TabletTypesToWait = "MASTER,REPLICA,RDONLY" + err = vtgateInstance.Setup() + require.Nil(t, err) + waitTillAllTabletsAreHealthyInVtgate(t, *vtgateInstance, shard1.Name, shard2.Name) + + // since replica and rdonly tablets of all shards in cell2, the last 2 assertion is expected to fail + testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) + testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, true) + testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) + + // Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias + err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias", + "-cells", allCells, + "region_east_coast") + require.Nil(t, err) + err = localCluster.VtctlclientProcess.ExecuteCommand("UpdateCellsAlias", + "-cells", allCells, + "region_east_coast") + require.Nil(t, err) + + testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) + testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, false) + testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, false) + + deleteInitialValues(t) + _ = vtgateInstance.TearDown() } func waitTillAllTabletsAreHealthyInVtgate(t *testing.T, vtgateInstance cluster.VtgateProcess, shards ...string) { @@ -319,21 +368,41 @@ func testQueriesInDifferentTabletType(t *testing.T, tabletType string, vtgateGrp } func insertInitialValues(t *testing.T) { - sharding.InsertToTablet(t, + sharding.ExecuteOnTablet(t, fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 1, "msg1", 1), *shard1Master, keyspaceName, false) - sharding.InsertToTablet(t, + sharding.ExecuteOnTablet(t, fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 2, "msg2", 2), *shard1Master, keyspaceName, false) - sharding.InsertToTablet(t, + sharding.ExecuteOnTablet(t, fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, 4, "msg4", 4), *shard2Master, keyspaceName, false) } + +func deleteInitialValues(t *testing.T) { + sharding.ExecuteOnTablet(t, + fmt.Sprintf("delete from %s where id = %v", tableName, 1), + *shard1Master, + keyspaceName, + false) + + sharding.ExecuteOnTablet(t, + fmt.Sprintf("delete from %s where id = %v", tableName, 2), + *shard1Master, + keyspaceName, + false) + + sharding.ExecuteOnTablet(t, + fmt.Sprintf("delete from %s where id = %v", tableName, 4), + *shard2Master, + keyspaceName, + false) +} diff --git a/go/test/endtoend/sharding/base_sharding.go b/go/test/endtoend/sharding/base_sharding.go index d3d01a6c30b..b355532c6d6 100644 --- a/go/test/endtoend/sharding/base_sharding.go +++ b/go/test/endtoend/sharding/base_sharding.go @@ -271,13 +271,14 @@ func InsertLots(t *testing.T, count uint64, vttablet cluster.Vttablet, table str query1 = fmt.Sprintf(InsertTabletTemplateKsID, table, lotRange1+i, fmt.Sprintf("msg-range1-%d", 10000+i), lotRange1+i) query2 = fmt.Sprintf(InsertTabletTemplateKsID, table, lotRange2+i, fmt.Sprintf("msg-range2-%d", 20000+i), lotRange2+i) - InsertToTablet(t, query1, vttablet, ks, false) - InsertToTablet(t, query2, vttablet, ks, false) + ExecuteOnTablet(t, query1, vttablet, ks, false) + ExecuteOnTablet(t, query2, vttablet, ks, false) } } -// InsertToTablet inserts a single row to vttablet -func InsertToTablet(t *testing.T, query string, vttablet cluster.Vttablet, ks string, expectFail bool) { +// ExecuteOnTablet executes a write query on specified vttablet +// It should always be called with a master tablet for the keyspace/shard +func ExecuteOnTablet(t *testing.T, query string, vttablet cluster.Vttablet, ks string, expectFail bool) { _, _ = vttablet.VttabletProcess.QueryTablet("begin", ks, true) _, err := vttablet.VttabletProcess.QueryTablet(query, ks, true) if expectFail { @@ -309,7 +310,7 @@ func InsertMultiValues(t *testing.T, tablet cluster.Vttablet, keyspaceName strin queryStr += valueSQL queryStr += fmt.Sprintf(" /* vtgate:: keyspace_id:%s */", keyspaceIds) queryStr += fmt.Sprintf(" /* id:%s */", valueIds) - InsertToTablet(t, queryStr, tablet, keyspaceName, false) + ExecuteOnTablet(t, queryStr, tablet, keyspaceName, false) } // CheckLotsTimeout waits till all values are inserted diff --git a/go/test/endtoend/sharding/initialsharding/sharding_util.go b/go/test/endtoend/sharding/initialsharding/sharding_util.go index 542c700a8e1..d9b6f47d19f 100644 --- a/go/test/endtoend/sharding/initialsharding/sharding_util.go +++ b/go/test/endtoend/sharding/initialsharding/sharding_util.go @@ -109,15 +109,14 @@ func ClusterWrapper(isMulti bool) (int, error) { if err := ClusterInstance.VtctlProcess.CreateKeyspace(keyspaceName1); err != nil { return 1, err - } else { - ClusterInstance.Keyspaces = append(ClusterInstance.Keyspaces, cluster.Keyspace{Name: keyspaceName1}) } + ClusterInstance.Keyspaces = append(ClusterInstance.Keyspaces, cluster.Keyspace{Name: keyspaceName1}) + if isMulti { if err := ClusterInstance.VtctlProcess.CreateKeyspace(keyspaceName2); err != nil { return 1, err - } else { - ClusterInstance.Keyspaces = append(ClusterInstance.Keyspaces, cluster.Keyspace{Name: keyspaceName2}) } + ClusterInstance.Keyspaces = append(ClusterInstance.Keyspaces, cluster.Keyspace{Name: keyspaceName2}) } initClusterForInitialSharding(keyspaceName1, []string{"0"}, 3, true, isMulti) @@ -439,7 +438,7 @@ func TestInitialSharding(t *testing.T, keyspace *cluster.Keyspace, keyType query // Insert row 4 (provokes a delete). var ksid uint64 = 0xD000000000000000 insertSQL := fmt.Sprintf(sharding.InsertTabletTemplateKsID, tableName, ksid, "msg4", ksid) - sharding.InsertToTablet(t, insertSQL, *shard22.MasterTablet(), keyspaceName, true) + sharding.ExecuteOnTablet(t, insertSQL, *shard22.MasterTablet(), keyspaceName, true) _ = ClusterInstance.VtworkerProcess.ExecuteCommand("SplitClone", "--exclude_tables", "unrelated", diff --git a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go index d196123e39e..9b1850d28ba 100644 --- a/go/test/endtoend/sharding/mergesharding/mergesharding_base.go +++ b/go/test/endtoend/sharding/mergesharding/mergesharding_base.go @@ -515,18 +515,18 @@ func TestMergesharding(t *testing.T, useVarbinaryShardingKeyType bool) { func insertStartupValues(t *testing.T) { insertSQL := fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 1, "msg1", key1, key1, 1) - sharding.InsertToTablet(t, insertSQL, *shard0.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard0.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 2, "msg2", key2, key2, 2) - sharding.InsertToTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 3, "msg3", key3, key3, 3) - sharding.InsertToTablet(t, insertSQL, *shard2.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard2.MasterTablet(), keyspaceName, false) } func insertValue(t *testing.T, tablet *cluster.Vttablet, keyspaceName string, tableName string, id int, msg string, ksID uint64) { insertSQL := fmt.Sprintf(insertTabletTemplateKsID, tableName, fixedParentID, id, msg, ksID, ksID, id) - sharding.InsertToTablet(t, insertSQL, *tablet, keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *tablet, keyspaceName, false) } func checkStartupValues(t *testing.T, shardingKeyType querypb.Type) { @@ -627,8 +627,8 @@ func insertLots(t *testing.T, count uint64, base uint64, table string, parentID query2 = fmt.Sprintf(insertTabletTemplateKsID, table, parentID, 20000+base+i, fmt.Sprintf("msg-range1-%d", 20000+base+i), key2, key2, 20000+base+i) - sharding.InsertToTablet(t, query1, *shard0.MasterTablet(), ks, false) - sharding.InsertToTablet(t, query2, *shard1.MasterTablet(), ks, false) + sharding.ExecuteOnTablet(t, query1, *shard0.MasterTablet(), ks, false) + sharding.ExecuteOnTablet(t, query2, *shard1.MasterTablet(), ks, false) } } diff --git a/go/test/endtoend/sharding/resharding/resharding_base.go b/go/test/endtoend/sharding/resharding/resharding_base.go index eebf38888a8..b5223ed46bd 100644 --- a/go/test/endtoend/sharding/resharding/resharding_base.go +++ b/go/test/endtoend/sharding/resharding/resharding_base.go @@ -974,30 +974,30 @@ func TestResharding(t *testing.T, useVarbinaryShardingKeyType bool) { func insertStartupValues(t *testing.T) { insertSQL := fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 1, "msg1", key1, key1, 1) - sharding.InsertToTablet(t, insertSQL, *shard0.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard0.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 2, "msg2", key2, key2, 2) - sharding.InsertToTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding1", fixedParentID, 3, "msg3", key3, key3, 3) - sharding.InsertToTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding3", fixedParentID, 1, "a", key1, key1, 1) - sharding.InsertToTablet(t, insertSQL, *shard0.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard0.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding3", fixedParentID, 2, "b", key2, key2, 2) - sharding.InsertToTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "resharding3", fixedParentID, 3, "c", key3, key3, 3) - sharding.InsertToTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) insertSQL = fmt.Sprintf(insertTabletTemplateKsID, "no_pk", fixedParentID, 1, "msg1", key5, key5, 1) - sharding.InsertToTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *shard1.MasterTablet(), keyspaceName, false) } func insertValue(t *testing.T, tablet *cluster.Vttablet, keyspaceName string, tableName string, id int, msg string, ksID uint64) { insertSQL := fmt.Sprintf(insertTabletTemplateKsID, tableName, fixedParentID, id, msg, ksID, ksID, id) - sharding.InsertToTablet(t, insertSQL, *tablet, keyspaceName, false) + sharding.ExecuteOnTablet(t, insertSQL, *tablet, keyspaceName, false) } func execMultiShardDmls(t *testing.T, keyspaceName string) { From 96e84b56ebb39c65e3e8a89fb81392aa2086d5c4 Mon Sep 17 00:00:00 2001 From: Rafael Chacon Date: Mon, 9 Mar 2020 18:58:31 -0700 Subject: [PATCH 248/825] Use proper session to propagate autocommit information Signed-off-by: Rafael Chacon --- go/vt/vtgate/executor.go | 2 +- go/vt/vtgate/resolver.go | 7 +++---- go/vt/vtgate/vtgate.go | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 25e80380a53..a1d3f701a8e 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -364,7 +364,7 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql } func (e *Executor) destinationExec(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { - return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession.Session, false /* notInTransaction */, safeSession.Options, logStats, true /* canAutocommit */) + return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession, false /* notInTransaction */, safeSession.Options, logStats, true /* canAutocommit */) } func (e *Executor) handleDDL(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go index a875b9c33bc..ed0116f0e85 100644 --- a/go/vt/vtgate/resolver.go +++ b/go/vt/vtgate/resolver.go @@ -78,7 +78,7 @@ func (res *Resolver) Execute( keyspace string, tabletType topodatapb.TabletType, destination key.Destination, - session *vtgatepb.Session, + session *SafeSession, notInTransaction bool, options *querypb.ExecuteOptions, logStats *LogStats, @@ -92,8 +92,7 @@ func (res *Resolver) Execute( logStats.ShardQueries = uint32(len(rss)) } - safeSession := NewSafeSession(session) - autocommit := len(rss) == 1 && safeSession.AutocommitApproval() + autocommit := len(rss) == 1 && session.AutocommitApproval() for { qr, err := res.scatterConn.Execute( @@ -102,7 +101,7 @@ func (res *Resolver) Execute( bindVars, rss, tabletType, - safeSession, + session, notInTransaction, options, autocommit, diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index e55d2654488..e41781eddfe 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -400,7 +400,7 @@ func (vtg *VTGate) ExecuteShards(ctx context.Context, sql string, bindVariables keyspace, tabletType, key.DestinationShards(shards), - session, + NewSafeSession(session), notInTransaction, options, nil, @@ -447,7 +447,7 @@ func (vtg *VTGate) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVaria goto handleError } - qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyspaceIDs(keyspaceIds), session, notInTransaction, options, nil /* LogStats */, false /* autocommit */) + qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyspaceIDs(keyspaceIds), NewSafeSession(session), notInTransaction, options, nil /* LogStats */, false /* autocommit */) if err == nil { vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) return qr, nil @@ -485,7 +485,7 @@ func (vtg *VTGate) ExecuteKeyRanges(ctx context.Context, sql string, bindVariabl sql = sqlannotation.AnnotateIfDML(sql, nil) - qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyRanges(keyRanges), session, notInTransaction, options, nil /* LogStats */, false /* autocommit */) + qr, err = vtg.resolver.Execute(ctx, sql, bindVariables, keyspace, tabletType, key.DestinationKeyRanges(keyRanges), NewSafeSession(session), notInTransaction, options, nil /* LogStats */, false /* autocommit */) if err == nil { vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) return qr, nil From 79197f7624e2d95f304381bf343389acff85280f Mon Sep 17 00:00:00 2001 From: Mingjian Liu Date: Sat, 7 Mar 2020 21:06:47 -0800 Subject: [PATCH 249/825] Check zero for avgTimePerQuery Signed-off-by: Mingjian Liu --- go/vt/vtgate/executor_scatter_stats.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/go/vt/vtgate/executor_scatter_stats.go b/go/vt/vtgate/executor_scatter_stats.go index 24858564a54..9ba7ae3ea3a 100644 --- a/go/vt/vtgate/executor_scatter_stats.go +++ b/go/vt/vtgate/executor_scatter_stats.go @@ -89,13 +89,17 @@ func (e *Executor) gatherScatterStats() (statsResults, error) { resultItems := make([]*statsResultItem, len(plans)) for i, plan := range plans { route := routes[i] + var avgTimePerQuery int64 + if plan.ExecCount != 0 { + avgTimePerQuery = plan.ExecTime.Nanoseconds() / int64(plan.ExecCount) + } resultItems[i] = &statsResultItem{ Query: plan.Original, - AvgTimePerQuery: time.Duration(plan.ExecTime.Nanoseconds() / int64(plan.ExecCount)), - PercentTimeOfReads: float64(100 * plan.ExecTime / readOnlyTime), - PercentTimeOfScatters: float64(100 * plan.ExecTime / scatterExecTime), - PercentCountOfReads: float64(100 * plan.ExecCount / readOnlyCount), - PercentCountOfScatters: float64(100 * plan.ExecCount / scatterCount), + AvgTimePerQuery: time.Duration(avgTimePerQuery), + PercentTimeOfReads: 100 * float64(plan.ExecTime) / float64(readOnlyTime), + PercentTimeOfScatters: 100 * float64(plan.ExecTime) / float64(scatterExecTime), + PercentCountOfReads: 100 * float64(plan.ExecCount) / float64(readOnlyCount), + PercentCountOfScatters: 100 * float64(plan.ExecCount) / float64(scatterCount), From: route.Keyspace.Name + "." + route.TableName, Count: plan.ExecCount, } From 840258a2ea651498008527d02cd0466cfca87ff5 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 10 Mar 2020 13:18:06 -0700 Subject: [PATCH 250/825] vstream: address review comments Signed-off-by: Sugu Sougoumarane --- go/vt/proto/binlogdata/binlogdata.pb.go | 2 +- proto/binlogdata.proto | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index 583f9d2fcb1..564619addf2 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -1256,7 +1256,7 @@ func (m *Journal) GetSourceWorkflows() []string { type VEvent struct { Type VEventType `protobuf:"varint,1,opt,name=type,proto3,enum=binlogdata.VEventType" json:"type,omitempty"` // Timestamp is the binlog timestamp in seconds. - // It's set for all events. + // The value should be ignored if 0. Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Gtid is set if the event type is GTID. Gtid string `protobuf:"bytes,3,opt,name=gtid,proto3" json:"gtid,omitempty"` diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 22dbaec214a..7268f3342d2 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -320,7 +320,7 @@ message Journal { message VEvent { VEventType type = 1; // Timestamp is the binlog timestamp in seconds. - // It's set for all events. + // The value should be ignored if 0. int64 timestamp = 2; // Gtid is set if the event type is GTID. string gtid = 3; From 3b8c518de246d2d728aa18c20208967fc4cd0ebb Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 10 Mar 2020 13:51:27 -0700 Subject: [PATCH 251/825] vrepl: address review comments Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletmanager/vreplication/engine.go | 2 +- .../tabletmanager/vreplication/replicator_plan.go | 8 ++++---- .../tabletmanager/vreplication/table_plan_builder.go | 4 ++-- go/vt/vttablet/tabletmanager/vreplication/vplayer.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index 0cf1bdd84c6..2470d0a5a70 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -405,7 +405,7 @@ func (vre *Engine) fetchIDs(dbClient binlogplayer.DBClient, selector string) (id return ids, bv, nil } -// registerJounral is invoked if any of the vreplication streams encounters a journal event. +// registerJournal is invoked if any of the vreplication streams encounters a journal event. // Multiple registerJournal functions collaborate to converge on the final action. // The first invocation creates an entry in vre.journaler. The entry is initialized // with the list of participants that also need to converge. diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go index 903810e27e3..3726b36b3eb 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go +++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go @@ -146,12 +146,12 @@ func (rp *ReplicatorPlan) MarshalJSON() ([]byte, error) { // of vreplication: catchup, copy, fastforward, or regular replication. type TablePlan struct { // TargetName, SendRule will always be initialized. - // Lastpk will also be initialized if it was specified, and - // will be used for building the final plan after field info - // is received. TargetName string SendRule *binlogdatapb.Rule - Lastpk *sqltypes.Result + // Lastpk will be initialized if it was specified, and + // will be used for building the final plan after field info + // is received. + Lastpk *sqltypes.Result // BulkInsertFront, BulkInsertValues and BulkInsertOnDup are used // by vcopier. These three parts are combined to build bulk insert // statements. This is functionally equivalent to generating diff --git a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go index 42e1f31e3bc..904833b7348 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go +++ b/go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go @@ -84,7 +84,7 @@ type insertType int // The following values are the various insert types. const ( - // insertNormal is for normal selects without a group b, like + // insertNormal is for normal selects without a group by, like // "select a+b as c from t". insertNormal = insertType(iota) // insertOnDup is for the more traditional grouped expressions, like @@ -93,7 +93,7 @@ const ( // causing "b" to be updated to the latest value (last value wins). insertOnDup // insertIgnore is for special grouped expressions where all columns are - // in the group by, like "select a, b, c from to group by a, b, c". + // in the group by, like "select a, b, c from t group by a, b, c". // This generates "insert ignore" statements (first value wins). insertIgnore ) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 05d1ec288dc..a6b85772662 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -483,7 +483,7 @@ func (vp *vplayer) applyEvent(ctx context.Context, event *binlogdatapb.VEvent, m } return io.EOF case binlogdatapb.OnDDLAction_EXEC: - // It's impossible to save the position transactionally with the satement. + // It's impossible to save the position transactionally with the statement. // So, we apply the DDL first, and then save the position. // Manual intervention may be needed if there is a partial // failure here. From 2c36fb19842b9806fe43e9c1f47506eeba3e3fc7 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 23 Feb 2020 06:50:16 -0800 Subject: [PATCH 252/825] messager: refactor for readability The condvar for the messager had ambiguous meaning, which makes it hard to reason about it, or safely change the code. There is now a specific condition assigned to condvar which can be validated at all parts of the code. Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/messager/cache.go | 6 ++ .../tabletserver/messager/message_manager.go | 73 +++++++++---------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/cache.go b/go/vt/vttablet/tabletserver/messager/cache.go index 7137e6bcb78..113d0e62110 100644 --- a/go/vt/vttablet/tabletserver/messager/cache.go +++ b/go/vt/vttablet/tabletserver/messager/cache.go @@ -102,6 +102,12 @@ func newCache(size int) *cache { return mc } +func (mc *cache) IsEmpty() bool { + mc.mu.Lock() + defer mc.mu.Unlock() + return len(mc.sendQueue) == 0 +} + // Clear clears the cache. func (mc *cache) Clear() { mc.mu.Lock() diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 8585eaf0537..bee118839bd 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -130,7 +130,7 @@ type receiverWithStatus struct { // // messagesPending mode // This mode is a variation of the steady state mode. This mode is -// entered when there are outstanding items on disk that need to be sent +// entered when there are outstanding items in the database that need to be sent // but are not present in the cache. This state can be entered in one // of three ways: // 1. The poller read returns as many rows as the cache size @@ -178,9 +178,10 @@ type messageManager struct { mu sync.Mutex isOpen bool - // cond gets triggered if a receiver becomes available (curReceiver != -1), - // an item gets added to the cache, or if the manager is closed. - // The trigger wakes up the runSend thread. + // cond waits on curReceiver == -1 || cache.IsEmpty(): + // No current receivers available or cache is empty. + // Also, messagesPending && cache.IsEmpty() should trigger + // the poller to fetch more messages from the database. cond sync.Cond cache *cache receivers []*receiverWithStatus @@ -289,6 +290,7 @@ func (mm *messageManager) Close() { mm.receivers = nil MessageStats.Set([]string{mm.name.String(), "ClientCount"}, 0) mm.cache.Clear() + // This broadcast will cause runSend to exit. mm.cond.Broadcast() mm.mu.Unlock() @@ -375,16 +377,16 @@ func (mm *messageManager) Add(mr *MessageRow) bool { if len(mm.receivers) == 0 { return false } + // If cache is empty, we have to broadcast that we're not empty + // any more. + if mm.cache.IsEmpty() { + mm.cond.Broadcast() + } if !mm.cache.Add(mr) { - // Cache is full. Enter "messagesPending" mode to let the poller - // fill the cache with messages from disk as soon as a cache - // slot becomes available. - // We also skip notifying the send routine via mm.cond.Broadcast() - // because a full cache means that it's already active. + // Cache is full. Enter "messagesPending" mode. mm.messagesPending = true return false } - mm.cond.Broadcast() return true } @@ -402,8 +404,8 @@ func (mm *messageManager) runSend() { return } - // If there are no receivers, we wait. - if mm.curReceiver == -1 { + // If there are no receivers or cache is empty, we wait. + if mm.curReceiver == -1 || mm.cache.IsEmpty() { mm.cond.Wait() continue } @@ -412,25 +414,21 @@ func (mm *messageManager) runSend() { lateCount := int64(0) timingsKey := []string{mm.name.String()} for i := 0; i < mm.batchSize; i++ { - if mr := mm.cache.Pop(); mr != nil { - if mr.Epoch >= 1 { - lateCount++ - } - MessageDelayTimings.Record(timingsKey, time.Unix(0, mr.TimeCreated)) - rows = append(rows, mr.Row) - continue + mr := mm.cache.Pop() + if mr == nil { + break } - break + if mr.Epoch >= 1 { + lateCount++ + } + MessageDelayTimings.Record(timingsKey, time.Unix(0, mr.TimeCreated)) + rows = append(rows, mr.Row) } MessageStats.Add([]string{mm.name.String(), "Delayed"}, lateCount) - // We have rows to send, break out of this loop. - if rows != nil { - break - } - - if mm.messagesPending { - // If messages are pending, trigger the poller to fetch more. + // If cache became empty and there are messages pending, we have + // to trigger the poller to fetch more. + if mm.cache.IsEmpty() && mm.messagesPending { // Do this as a separate goroutine. Otherwise, this could cause // the following deadlock: // 1. runSend obtains a lock @@ -440,8 +438,10 @@ func (mm *messageManager) runSend() { go mm.pollerTicks.Trigger() } - // There are no rows in the cache. We wait. - mm.cond.Wait() + // We have rows to send, break out of this loop. + if rows != nil { + break + } } MessageStats.Add([]string{mm.name.String(), "Sent"}, int64(len(rows))) // If we're here, there is a current receiver, and messages @@ -488,18 +488,15 @@ func (mm *messageManager) send(receiver *receiverWithStatus, qr *sqltypes.Result if err := receiver.receiver.Send(qr); err != nil { if err == io.EOF { // If the receiver ended the stream, we do not postpone the message. - // Instead, we mark messagesPending, which will proactively trigger - // the poller when cache goes empty, and the message will be immediately - // resent through another receiver. + // Instead, we mark messagesPending. If the cache is already empty + // then we have to trigger the poller. Otherwise, it will get + // trigerred by runSend when it goes empty. mm.mu.Lock() mm.messagesPending = true - // If this was the last message from the cache, the send loop - // could have gone idle. If so, wake it up. - mm.cond.Broadcast() + if mm.cache.IsEmpty() { + go mm.pollerTicks.Trigger() + } mm.mu.Unlock() - // No need to call cancel. messageReceiver already - // does that before returning this error. - mm.unsubscribe(receiver.receiver) return } From 2e68bfd69e2b2fac15b146beeb648e85aa3f8ae1 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 23 Feb 2020 08:46:55 -0800 Subject: [PATCH 253/825] messager: simplify Subscribe Make Subscribe send the field info immediately. This allows the send function to only worry about sending rows. Signed-off-by: Sugu Sougoumarane --- .../tabletserver/messager/engine_test.go | 4 +- .../tabletserver/messager/message_manager.go | 33 +++++++------ .../messager/message_manager_test.go | 48 ++++++++++--------- .../tabletserver/query_executor_test.go | 2 +- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index 6bacdd82c1e..0b6e5eeeea3 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -167,7 +167,7 @@ func TestLockDB(t *testing.T) { default: } - ch2 := make(chan *sqltypes.Result) + ch2 := make(chan *sqltypes.Result, 1) var count sync2.AtomicInt64 engine.Subscribe(context.Background(), "t2", func(qr *sqltypes.Result) error { count.Add(1) @@ -268,7 +268,7 @@ func newTestEngine(db *fakesqldb.DB) *Engine { } func newEngineReceiver() (f func(qr *sqltypes.Result) error, ch chan *sqltypes.Result) { - ch = make(chan *sqltypes.Result) + ch = make(chan *sqltypes.Result, 1) return func(qr *sqltypes.Result) error { ch <- qr return nil diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index bee118839bd..4297e8277a8 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -303,18 +303,28 @@ func (mm *messageManager) Close() { // cancel or timeout, or tabletserver shutdown, etc. func (mm *messageManager) Subscribe(ctx context.Context, send func(*sqltypes.Result) error) <-chan struct{} { receiver, done := newMessageReceiver(ctx, send) + mm.mu.Lock() defer mm.mu.Unlock() + if !mm.isOpen { + receiver.cancel() + return done + } + + if err := receiver.Send(mm.fieldResult); err != nil { + log.Errorf("Terminating connection due to error sending field info: %v", err) + receiver.cancel() + return done + } + withStatus := &receiverWithStatus{ receiver: receiver, - busy: true, } mm.receivers = append(mm.receivers, withStatus) MessageStats.Set([]string{mm.name.String(), "ClientCount"}, int64(len(mm.receivers))) - - // Send the message asynchronously. - mm.wg.Add(1) - go mm.send(withStatus, mm.fieldResult) + if mm.curReceiver == -1 { + mm.rescanReceivers(-1) + } // Track the context and unsubscribe if it gets cancelled. go func() { @@ -438,7 +448,7 @@ func (mm *messageManager) runSend() { go mm.pollerTicks.Trigger() } - // We have rows to send, break out of this loop. + // If we have rows to send, break out of this loop. if rows != nil { break } @@ -500,17 +510,6 @@ func (mm *messageManager) send(receiver *receiverWithStatus, qr *sqltypes.Result return } - // A rare corner case: - // If we fail to send the field info, then we should not send - // rows on this connection anymore. We should instead terminate - // the connection. - if len(ids) == 0 { - receiver.receiver.cancel() - mm.unsubscribe(receiver.receiver) - log.Errorf("Terminating connection due to error sending field info: %v", err) - return - } - // Log the error, but we still want to postpone the message. // Otherwise, if this is a chronic failure like "message too // big", we'll end up spamming non-stop. diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index 7c88c81e9cf..c941d3a3f9c 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -105,10 +105,12 @@ func TestReceiverCancel(t *testing.T) { mm := newMessageManager(newFakeTabletServer(), mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() + r1 := newTestReceiver(0) ctx, cancel := context.WithCancel(context.Background()) + go cancel() _ = mm.Subscribe(ctx, r1.rcv) - cancel() + // r1 should eventually be unsubscribed. for i := 0; i < 10; i++ { runtime.Gosched() @@ -140,15 +142,6 @@ func TestMessageManagerState(t *testing.T) { // Idempotence. mm.Close() } - - for i := 0; i < 2; i++ { - mm.Open() - r1 := newTestReceiver(1) - mm.Subscribe(context.Background(), r1.rcv) - // This time the wait is in a different code path. - runtime.Gosched() - mm.Close() - } } func TestMessageManagerAdd(t *testing.T) { @@ -168,8 +161,9 @@ func TestMessageManagerAdd(t *testing.T) { } r1 := newTestReceiver(0) + go func() { <-r1.ch }() mm.Subscribe(context.Background(), r1.rcv) - <-r1.ch + if !mm.Add(row1) { t.Error("Add(1 receiver): false, want true") } @@ -191,8 +185,10 @@ func TestMessageManagerSend(t *testing.T) { mm := newMessageManager(tsv, mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() + r1 := newTestReceiver(1) mm.Subscribe(context.Background(), r1.rcv) + want := &sqltypes.Result{ Fields: testFields, } @@ -245,6 +241,7 @@ func TestMessageManagerSend(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) mm.Subscribe(ctx, r2.rcv) <-r2.ch + mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("2")}}) mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("3")}}) // Send should be round-robin. @@ -281,6 +278,7 @@ func TestMessageManagerPostponeThrottle(t *testing.T) { mm := newMessageManager(tsv, mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() + r1 := newTestReceiver(1) mm.Subscribe(context.Background(), r1.rcv) <-r1.ch @@ -352,16 +350,16 @@ func TestMessageManagerSendEOF(t *testing.T) { mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() - r1 := newTestReceiver(0) + ctx, cancel := context.WithCancel(context.Background()) + + r1 := newTestReceiver(0) + go func() { <-r1.ch }() mm.Subscribe(ctx, r1.rcv) - // Pull field info. - <-r1.ch r2 := newTestReceiver(0) + go func() { <-r2.ch }() mm.Subscribe(context.Background(), r2.rcv) - // Pull field info. - <-r2.ch mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("1"), sqltypes.NULL}}) // Wait for send to enqueue. @@ -391,6 +389,7 @@ func TestMessageManagerSendError(t *testing.T) { ctx := context.Background() ch := make(chan *sqltypes.Result) + go func() { <-ch }() fieldSent := false mm.Subscribe(ctx, func(qr *sqltypes.Result) error { ch <- qr @@ -400,8 +399,6 @@ func TestMessageManagerSendError(t *testing.T) { } return errors.New("non-eof") }) - // Pull field info. - <-ch postponech := make(chan string, 20) tsv.SetChannel(postponech) @@ -424,12 +421,11 @@ func TestMessageManagerFieldSendError(t *testing.T) { ctx := context.Background() ch := make(chan *sqltypes.Result) + go func() { <-ch }() done := mm.Subscribe(ctx, func(qr *sqltypes.Result) error { ch <- qr return errors.New("non-eof") }) - // Pull field info. - <-ch // This should not hang because a field send error must terminate // subscription. @@ -444,9 +440,11 @@ func TestMessageManagerBatchSend(t *testing.T) { mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() + r1 := newTestReceiver(1) mm.Subscribe(context.Background(), r1.rcv) <-r1.ch + row1 := &MessageRow{ Row: []sqltypes.Value{sqltypes.NewVarBinary("1"), sqltypes.NULL}, } @@ -523,10 +521,12 @@ func TestMessageManagerPoller(t *testing.T) { mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() - r1 := newTestReceiver(1) + ctx, cancel := context.WithCancel(context.Background()) + r1 := newTestReceiver(1) mm.Subscribe(ctx, r1.rcv) <-r1.ch + mm.pollerTicks.Trigger() want := [][]sqltypes.Value{{ sqltypes.NewInt64(2), @@ -593,9 +593,10 @@ func TestMessagesPending1(t *testing.T) { mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() + r1 := newTestReceiver(0) + go func() { <-r1.ch }() mm.Subscribe(context.Background(), r1.rcv) - <-r1.ch mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("1")}}) // Make sure the first message is enqueued. @@ -660,9 +661,10 @@ func TestMessagesPending2(t *testing.T) { mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() + r1 := newTestReceiver(0) + go func() { <-r1.ch }() mm.Subscribe(context.Background(), r1.rcv) - <-r1.ch // Trigger the poller. mm.pollerTicks.Trigger() diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index c6404f5fefb..10c4df3a889 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -323,6 +323,7 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { defer tsv.StopService() checkPlanID(t, planbuilder.PlanInsertMessage, qre.plan.PlanID) ch1 := make(chan *sqltypes.Result) + go func() { <-ch1 }() count := 0 tsv.messager.Subscribe(context.Background(), "msg", func(qr *sqltypes.Result) error { if count > 1 { @@ -332,7 +333,6 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { ch1 <- qr return nil }) - <-ch1 got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) From b6748ef1882f8854b7850f859eac13deffad3e22 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 23 Feb 2020 10:40:06 -0800 Subject: [PATCH 254/825] messager: trigger the poller more accurately The poller was not getting proactively trigerred under all circumstances. We now set messagesPending to true at the start to make sure that the poller starts off by fetching outstanding work. Also, the condition is checked every time we break out of the runSend wait. Also deleted a flaky test that wasn't testing much. Signed-off-by: Sugu Sougoumarane --- .../tabletserver/messager/message_manager.go | 48 ++++++++++--------- .../messager/message_manager_test.go | 8 ---- .../tabletserver/tabletserver_flaky_test.go | 20 -------- 3 files changed, 25 insertions(+), 51 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 4297e8277a8..d981691f085 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -211,14 +211,15 @@ func newMessageManager(tsv TabletService, table *schema.Table, conns *connpool.P fieldResult: &sqltypes.Result{ Fields: table.MessageInfo.Fields, }, - ackWaitTime: table.MessageInfo.AckWaitDuration, - purgeAfter: table.MessageInfo.PurgeAfterDuration, - batchSize: table.MessageInfo.BatchSize, - cache: newCache(table.MessageInfo.CacheSize), - pollerTicks: timer.NewTimer(table.MessageInfo.PollInterval), - purgeTicks: timer.NewTimer(table.MessageInfo.PollInterval), - conns: conns, - postponeSema: postponeSema, + ackWaitTime: table.MessageInfo.AckWaitDuration, + purgeAfter: table.MessageInfo.PurgeAfterDuration, + batchSize: table.MessageInfo.BatchSize, + cache: newCache(table.MessageInfo.CacheSize), + pollerTicks: timer.NewTimer(table.MessageInfo.PollInterval), + purgeTicks: timer.NewTimer(table.MessageInfo.PollInterval), + conns: conns, + postponeSema: postponeSema, + messagesPending: true, } mm.cond.L = &mm.mu @@ -414,6 +415,18 @@ func (mm *messageManager) runSend() { return } + // If cache became empty and there are messages pending, we have + // to trigger the poller to fetch more. + if mm.cache.IsEmpty() && mm.messagesPending { + // Do this as a separate goroutine. Otherwise, this could cause + // the following deadlock: + // 1. runSend obtains a lock + // 2. Poller gets trigerred, and waits for lock. + // 3. runSend calls this function, but the trigger will hang because + // this function cannot return until poller returns. + go mm.pollerTicks.Trigger() + } + // If there are no receivers or cache is empty, we wait. if mm.curReceiver == -1 || mm.cache.IsEmpty() { mm.cond.Wait() @@ -436,18 +449,6 @@ func (mm *messageManager) runSend() { } MessageStats.Add([]string{mm.name.String(), "Delayed"}, lateCount) - // If cache became empty and there are messages pending, we have - // to trigger the poller to fetch more. - if mm.cache.IsEmpty() && mm.messagesPending { - // Do this as a separate goroutine. Otherwise, this could cause - // the following deadlock: - // 1. runSend obtains a lock - // 2. Poller gets trigerred, and waits for lock. - // 3. runSend calls this function, but the trigger will hang because - // this function cannot return until poller returns. - go mm.pollerTicks.Trigger() - } - // If we have rows to send, break out of this loop. if rows != nil { break @@ -503,9 +504,10 @@ func (mm *messageManager) send(receiver *receiverWithStatus, qr *sqltypes.Result // trigerred by runSend when it goes empty. mm.mu.Lock() mm.messagesPending = true - if mm.cache.IsEmpty() { - go mm.pollerTicks.Trigger() - } + // Although messagesPending is not part of the condition, + // we broadcast to make it break out and trigger the poller + // to load more messages, if necessary. + mm.cond.Broadcast() mm.mu.Unlock() return } diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index c941d3a3f9c..cdc438cc5d5 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -527,7 +527,6 @@ func TestMessageManagerPoller(t *testing.T) { mm.Subscribe(ctx, r1.rcv) <-r1.ch - mm.pollerTicks.Trigger() want := [][]sqltypes.Value{{ sqltypes.NewInt64(2), sqltypes.NewInt64(20), @@ -553,7 +552,6 @@ func TestMessageManagerPoller(t *testing.T) { // If there are no receivers, nothing should fire. cancel() - mm.pollerTicks.Trigger() runtime.Gosched() select { case row := <-r1.ch: @@ -605,9 +603,6 @@ func TestMessagesPending1(t *testing.T) { mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("2")}}) mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("3")}}) - // Trigger the poller. It should do nothing. - mm.pollerTicks.Trigger() - // Wait for pending flag to be turned on. for { runtime.Gosched() @@ -666,9 +661,6 @@ func TestMessagesPending2(t *testing.T) { go func() { <-r1.ch }() mm.Subscribe(context.Background(), r1.rcv) - // Trigger the poller. - mm.pollerTicks.Trigger() - // Now, let's pull more than 1 item. It should // trigger the poller every time cache gets empty. start := time.Now() diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index e985f42c4d9..b3932c52cda 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -378,26 +378,6 @@ func TestTabletServerCheckMysql(t *testing.T) { checkTabletServerState(t, tsv, StateNotServing) } -func TestTabletServerCheckMysqlFailInvalidConn(t *testing.T) { - db := setUpTabletServerTest(t) - defer db.Close() - testUtils := newTestUtils() - config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) - dbcfgs := testUtils.newDBConfigs(db) - target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} - err := tsv.StartService(target, dbcfgs) - defer tsv.StopService() - if err != nil { - t.Fatalf("TabletServer.StartService should succeed, but got error: %v", err) - } - // make mysql conn fail - db.Close() - if tsv.isMySQLReachable() { - t.Fatalf("isMySQLReachable should return false") - } -} - func TestTabletServerReconnect(t *testing.T) { db := setUpTabletServerTest(t) defer db.Close() From be9fc97f05077264162366e2d5d98f3d8ef134e5 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 29 Feb 2020 19:53:20 -0800 Subject: [PATCH 255/825] messager: rbr mode: initial cut Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/endtoend/message_test.go | 40 +-- .../vttablet/tabletserver/messager/engine.go | 91 +----- .../tabletserver/messager/message_manager.go | 295 ++++++++++++++---- go/vt/vttablet/tabletserver/tabletserver.go | 7 +- 4 files changed, 251 insertions(+), 182 deletions(-) diff --git a/go/vt/vttablet/endtoend/message_test.go b/go/vt/vttablet/endtoend/message_test.go index 3dc4b0540ab..e5cee922198 100644 --- a/go/vt/vttablet/endtoend/message_test.go +++ b/go/vt/vttablet/endtoend/message_test.go @@ -55,9 +55,6 @@ func TestMessage(t *testing.T) { if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Acked"), 0; got != want { t.Errorf("Messages/vitess_message.Acked: %d, want %d", got, want) } - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Queued"), 0; got != want { - t.Errorf("Messages/vitess_message.Queued: %d, want %d", got, want) - } // Start goroutine to consume message stream. go waitForMessage(t, client, "vitess_message", ch, done) @@ -96,9 +93,6 @@ func TestMessage(t *testing.T) { t.Error(err) return } - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Queued"), 1; got != want { - t.Errorf("Messages/vitess_message.Queued: %d, want %d", got, want) - } // Consume first message. start := time.Now().UnixNano() @@ -198,9 +192,6 @@ func TestMessage(t *testing.T) { } // Verify final counts. - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Queued"), 1; got != want { - t.Errorf("Messages/vitess_message.Queued: %d, want %d", got, want) - } if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Acked"), 1; got != want { t.Errorf("Messages/vitess_message.Acked: %d, want %d", got, want) } @@ -351,11 +342,6 @@ func TestMessageAuto(t *testing.T) { return } - // Only three messages should be queued. - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message_auto.Queued"), 3; got != want { - t.Errorf("Messages/vitess_message_auto.Queued: %d, want %d", got, want) - } - wantResults := []*sqltypes.Result{{ Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(1), @@ -374,6 +360,12 @@ func TestMessageAuto(t *testing.T) { sqltypes.NULL, sqltypes.NewVarChar("msg5"), }}, + }, { + Rows: [][]sqltypes.Value{{ + sqltypes.NewInt64(6), + sqltypes.NULL, + sqltypes.NewVarChar("msg6"), + }}, }} // Consume first three messages @@ -488,16 +480,6 @@ func TestMessageTopic(t *testing.T) { return } - // Only three messages should be queued on the first subscription table - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_topic_subscriber_1.Queued"), 3; got != want { - t.Errorf("Messages/vitess_topic_subscriber_1.Queued: %d, want %d", got, want) - } - - // Only three messages should be queued on the second subscription table - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_topic_subscriber_2.Queued"), 3; got != want { - t.Errorf("Messages/vitess_topic_subscriber_2.Queued: %d, want %d", got, want) - } - wantResults := []*sqltypes.Result{{ Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(1), @@ -592,16 +574,6 @@ func TestMessageTopic(t *testing.T) { return } - // no messages should be queued on the first subscription table - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_topic_subscriber_1.Queued"), 3; got != want { - t.Errorf("Messages/vitess_topic_subscriber_1.Queued: %d, want %d", got, want) - } - - // Only three messages should be queued on the second subscription table - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_topic_subscriber_2.Queued"), 6; got != want { - t.Errorf("Messages/vitess_topic_subscriber_2.Queued: %d, want %d", got, want) - } - wantResults = []*sqltypes.Result{{ Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(4), diff --git a/go/vt/vttablet/tabletserver/messager/engine.go b/go/vt/vttablet/tabletserver/messager/engine.go index 3be00a2a2db..7c39aa47f2f 100644 --- a/go/vt/vttablet/tabletserver/messager/engine.go +++ b/go/vt/vttablet/tabletserver/messager/engine.go @@ -17,7 +17,6 @@ limitations under the License. package messager import ( - "sort" "sync" "time" @@ -33,6 +32,7 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) @@ -45,6 +45,13 @@ type TabletService interface { PurgeMessages(ctx context.Context, target *querypb.Target, name string, timeCutoff int64) (count int64, err error) } +// VStreamer defines the functions of VStreamer +// that the messager needs. +type VStreamer interface { + Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error + StreamResults(ctx context.Context, query string, send func(*binlogdatapb.VStreamResultsResponse) error) error +} + // Engine is the engine for handling messages. type Engine struct { dbconfigs *dbconfigs.DBConfigs @@ -55,15 +62,17 @@ type Engine struct { tsv TabletService se *schema.Engine + vs VStreamer conns *connpool.Pool postponeSema *sync2.Semaphore } // NewEngine creates a new Engine. -func NewEngine(tsv TabletService, se *schema.Engine, config tabletenv.TabletConfig) *Engine { +func NewEngine(tsv TabletService, se *schema.Engine, vs VStreamer, config tabletenv.TabletConfig) *Engine { return &Engine{ tsv: tsv, se: se, + vs: vs, conns: connpool.New( config.PoolNamePrefix+"MessagerPool", config.MessagePoolSize, @@ -132,84 +141,12 @@ func (me *Engine) Subscribe(ctx context.Context, name string, send func(*sqltype // LockDB obtains db locks for all messages that need to // be updated and returns the counterpart unlock function. func (me *Engine) LockDB(newMessages map[string][]*MessageRow, changedMessages map[string][]string) func() { - // Short-circuit to avoid taking any locks if there's nothing to do. - if len(newMessages) == 0 && len(changedMessages) == 0 { - return func() {} - } - - // Build the set of affected messages tables. - combined := make(map[string]struct{}) - for name := range newMessages { - combined[name] = struct{}{} - } - for name := range changedMessages { - combined[name] = struct{}{} - } - - // Build the list of manager objects (one per table). - var mms []*messageManager - // Don't do DBLock while holding lock on mu. - // It causes deadlocks. - func() { - me.mu.Lock() - defer me.mu.Unlock() - for name := range combined { - if mm := me.managers[name]; mm != nil { - mms = append(mms, mm) - } - } - }() - if len(mms) > 1 { - // Always use the same order in which manager objects are locked to avoid deadlocks. - // The previous order in "mms" is not guaranteed for multiple reasons: - // - We use a Go map above which does not guarantee an iteration order. - // - Transactions may not always use the same order when writing to multiple - // messages tables. - sort.Slice(mms, func(i, j int) bool { return mms[i].name.String() < mms[j].name.String() }) - } - - // Lock each manager/messages table. - for _, mm := range mms { - mm.DBLock.Lock() - } - return func() { - for _, mm := range mms { - mm.DBLock.Unlock() - } - } + return func() {} } // UpdateCaches updates the caches for the committed changes. func (me *Engine) UpdateCaches(newMessages map[string][]*MessageRow, changedMessages map[string][]string) { - // Short-circuit to avoid taking any locks if there's nothing to do. - if len(newMessages) == 0 && len(changedMessages) == 0 { - return - } - - me.mu.Lock() - defer me.mu.Unlock() - now := time.Now().UnixNano() - for name, mrs := range newMessages { - mm := me.managers[name] - if mm == nil { - continue - } - MessageStats.Add([]string{name, "Queued"}, int64(len(mrs))) - for _, mr := range mrs { - if mr.TimeNext > now { - // We don't handle future messages yet. - continue - } - mm.Add(mr) - } - } - for name, ids := range changedMessages { - mm := me.managers[name] - if mm == nil { - continue - } - mm.cache.Discard(ids) - } + return } // GenerateLoadMessagesQuery returns the ParsedQuery for loading messages by pk. @@ -273,7 +210,7 @@ func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altere log.Errorf("Newly created table already exists in messages: %s", name) continue } - mm := newMessageManager(me.tsv, t, me.conns, me.postponeSema) + mm := newMessageManager(me.tsv, me.vs, t, me.conns, me.postponeSema) me.managers[name] = mm mm.Open() } diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index d981691f085..d98a00315bd 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -17,12 +17,14 @@ limitations under the License. package messager import ( + "fmt" "io" "sync" "time" "golang.org/x/net/context" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/sync2" @@ -33,6 +35,7 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -165,6 +168,7 @@ type receiverWithStatus struct { type messageManager struct { DBLock sync.Mutex tsv TabletService + vs VStreamer name sqlparser.TableIdent fieldResult *sqltypes.Result @@ -188,12 +192,17 @@ type messageManager struct { curReceiver int messagesPending bool + streamMu sync.Mutex + streamCancel func() + lastPollPosition *mysql.Position + // wg is for ensuring all running goroutines have returned // before we can close the manager. You need to Add before // launching any gorooutine while holding a lock on mu. // The goroutine must in turn defer on Done. wg sync.WaitGroup + vsFilter *binlogdatapb.Filter readByTimeNext *sqlparser.ParsedQuery loadMessagesQuery *sqlparser.ParsedQuery ackQuery *sqlparser.ParsedQuery @@ -204,9 +213,10 @@ type messageManager struct { // newMessageManager creates a new message manager. // Calls into tsv have to be made asynchronously. Otherwise, // it can lead to deadlocks. -func newMessageManager(tsv TabletService, table *schema.Table, conns *connpool.Pool, postponeSema *sync2.Semaphore) *messageManager { +func newMessageManager(tsv TabletService, vs VStreamer, table *schema.Table, conns *connpool.Pool, postponeSema *sync2.Semaphore) *messageManager { mm := &messageManager{ tsv: tsv, + vs: vs, name: table.Name, fieldResult: &sqltypes.Result{ Fields: table.MessageInfo.Fields, @@ -224,6 +234,13 @@ func newMessageManager(tsv TabletService, table *schema.Table, conns *connpool.P mm.cond.L = &mm.mu columnList := buildSelectColumnList(table) + vsQuery := fmt.Sprintf("select time_next, epoch, time_created, %s from %v", columnList, mm.name) + mm.vsFilter = &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: table.Name.String(), + Filter: vsQuery, + }}, + } mm.readByTimeNext = sqlparser.BuildParsedQuery( "select time_next, epoch, time_created, %s from %v where time_next < %a order by time_next desc limit %a", columnList, mm.name, ":time_next", ":max") @@ -321,6 +338,9 @@ func (mm *messageManager) Subscribe(ctx context.Context, send func(*sqltypes.Res withStatus := &receiverWithStatus{ receiver: receiver, } + if len(mm.receivers) == 0 { + mm.startVStreamer(ctx) + } mm.receivers = append(mm.receivers, withStatus) MessageStats.Set([]string{mm.name.String(), "ClientCount"}, int64(len(mm.receivers))) if mm.curReceiver == -1 { @@ -353,6 +373,7 @@ func (mm *messageManager) unsubscribe(receiver *messageReceiver) { mm.rescanReceivers(-1) // If there are no receivers. Shut down the cache. if len(mm.receivers) == 0 { + mm.stopVStreamer() mm.cache.Clear() } } @@ -415,9 +436,9 @@ func (mm *messageManager) runSend() { return } - // If cache became empty and there are messages pending, we have - // to trigger the poller to fetch more. - if mm.cache.IsEmpty() && mm.messagesPending { + // If cache became empty, there are messages pending, and there are subscribed + // receivers, we have to trigger the poller to fetch more. + if mm.cache.IsEmpty() && mm.messagesPending && len(mm.receivers) != 0 { // Do this as a separate goroutine. Otherwise, this could cause // the following deadlock: // 1. runSend obtains a lock @@ -481,19 +502,25 @@ func (mm *messageManager) send(receiver *receiverWithStatus, qr *sqltypes.Result // This is the cleanup. defer func() { - // Discard messages from cache only at the end. This will - // prevent them from being requeued while they're being postponed. - mm.cache.Discard(ids) - - mm.mu.Lock() - defer mm.mu.Unlock() + func() { + mm.mu.Lock() + defer mm.mu.Unlock() - receiver.busy = false - // Rescan if there were no previously available receivers - // because the current receiver became non-busy. - if mm.curReceiver == -1 { - mm.rescanReceivers(-1) - } + receiver.busy = false + // Rescan if there were no previously available receivers + // because the current receiver became non-busy. + if mm.curReceiver == -1 { + mm.rescanReceivers(-1) + } + }() + + func() { + mm.streamMu.Lock() + defer mm.streamMu.Unlock() + // Discard messages from cache only at the end. This will + // prevent them from being requeued while they're being postponed. + mm.cache.Discard(ids) + }() }() if err := receiver.receiver.Send(qr); err != nil { @@ -521,10 +548,6 @@ func (mm *messageManager) send(receiver *receiverWithStatus, qr *sqltypes.Result } func (mm *messageManager) postpone(tsv TabletService, name string, ackWaitTime time.Duration, ids []string) { - // ids can be empty if it's the field info being sent. - if len(ids) == 0 { - return - } // Use the semaphore to limit parallelism. if !mm.postponeSema.Acquire() { // Unreachable. @@ -539,7 +562,128 @@ func (mm *messageManager) postpone(tsv TabletService, name string, ackWaitTime t } } +func (mm *messageManager) startVStreamer(ctx context.Context) { + mm.streamMu.Lock() + defer mm.streamMu.Unlock() + if mm.streamCancel != nil { + return + } + ctx, mm.streamCancel = context.WithCancel(ctx) + go mm.runVStreamer(ctx) +} + +func (mm *messageManager) stopVStreamer() { + mm.streamMu.Lock() + defer mm.streamMu.Unlock() + if mm.streamCancel != nil { + mm.streamCancel() + mm.streamCancel = nil + } +} + +func (mm *messageManager) runVStreamer(ctx context.Context) { + for { + var curPos string + var fields []*querypb.Field + err := mm.vs.Stream(ctx, "current", mm.vsFilter, func(events []*binlogdatapb.VEvent) error { + mm.streamMu.Lock() + defer mm.streamMu.Unlock() + + select { + case <-ctx.Done(): + return io.EOF + default: + } + + mustSkip := func() (bool, error) { + if mm.lastPollPosition == nil { + return false, nil + } + if curPos == "" { + return true, nil + } + cur, err := mysql.DecodePosition(curPos) + if err != nil { + return false, err + } + if cur.AtLeast(*mm.lastPollPosition) { + mm.lastPollPosition = nil + return false, nil + } + return true, nil + } + skipEvents, err := mustSkip() + if err != nil { + return err + } + var newPos string + for _, ev := range events { + switch ev.Type { + case binlogdatapb.VEventType_FIELD: + fields = ev.FieldEvent.Fields + case binlogdatapb.VEventType_ROW: + if skipEvents { + continue + } + if err := mm.processRowEvent(fields, ev.RowEvent); err != nil { + return err + } + case binlogdatapb.VEventType_GTID: + newPos = ev.Gtid + case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER: + curPos = newPos + skipEvents, err = mustSkip() + if err != nil { + return err + } + } + } + return nil + }) + select { + case <-ctx.Done(): + log.Info("Context canceled, exiting vstream") + return + default: + } + log.Infof("VStream ended: %v, retrying in 5 seconds", err) + time.Sleep(5 * time.Second) + } +} + +func (mm *messageManager) processRowEvent(fields []*querypb.Field, rowEvent *binlogdatapb.RowEvent) error { + if fields == nil { + // Unreachable. + return fmt.Errorf("internal error: unexpected rows without fields") + } + + now := time.Now().UnixNano() + for _, rc := range rowEvent.RowChanges { + if rc.After == nil { + continue + } + row := sqltypes.MakeRowTrusted(fields, rc.After) + mr, err := BuildMessageRow(row) + if err != nil { + return err + } + if mr.TimeNext == 0 || mr.TimeNext > now { + continue + } + mm.Add(mr) + } + return nil +} + func (mm *messageManager) runPoller() { + // Fast-path. Skip all the work. + if mm.receiverCount() == 0 { + return + } + + mm.streamMu.Lock() + defer mm.streamMu.Unlock() + ctx, cancel := context.WithTimeout(tabletenv.LocalContext(), mm.pollerTicks.Interval()) defer func() { tabletenv.LogError() @@ -553,54 +697,45 @@ func (mm *messageManager) runPoller() { } defer conn.Recycle() - func() { - // Fast-path. Skip all the work. - if mm.receiverCount() == 0 { - return - } - mm.DBLock.Lock() - defer mm.DBLock.Unlock() + size := mm.cache.Size() + bindVars := map[string]*querypb.BindVariable{ + "time_next": sqltypes.Int64BindVariable(time.Now().UnixNano()), + "max": sqltypes.Int64BindVariable(int64(size)), + } + qr, err := mm.readPending(ctx, conn, bindVars) + if err != nil { + return + } - size := mm.cache.Size() - bindVars := map[string]*querypb.BindVariable{ - "time_next": sqltypes.Int64BindVariable(time.Now().UnixNano()), - "max": sqltypes.Int64BindVariable(int64(size)), - } - qr, err := mm.read(ctx, conn, mm.readByTimeNext, bindVars) + // Obtain mu lock to verify and preserve that len(receivers) != 0. + mm.mu.Lock() + defer mm.mu.Unlock() + mm.messagesPending = false + if len(qr.Rows) >= size { + // There are probably more messages to be sent. + mm.messagesPending = true + } + if len(mm.receivers) == 0 { + // Almost never reachable because we just checked this. + return + } + if len(qr.Rows) != 0 { + // We've most likely added items. + // Wake up the sender. + defer mm.cond.Broadcast() + } + for _, row := range qr.Rows { + mr, err := BuildMessageRow(row) if err != nil { - return + tabletenv.InternalErrors.Add("Messages", 1) + log.Errorf("Error reading message row: %v", err) + continue } - - // Obtain mu lock to verify and preserve that len(receivers) != 0. - mm.mu.Lock() - defer mm.mu.Unlock() - mm.messagesPending = false - if len(qr.Rows) >= size { - // There are probably more messages to be sent. + if !mm.cache.Add(mr) { mm.messagesPending = true - } - if len(mm.receivers) == 0 { - // Almost never reachable because we just checked this. return } - if len(qr.Rows) != 0 { - // We've most likely added items. - // Wake up the sender. - defer mm.cond.Broadcast() - } - for _, row := range qr.Rows { - mr, err := BuildMessageRow(row) - if err != nil { - tabletenv.InternalErrors.Add("Messages", 1) - log.Errorf("Error reading message row: %v", err) - continue - } - if !mm.cache.Add(mr) { - mm.messagesPending = true - return - } - } - }() + } } func (mm *messageManager) runPurge() { @@ -676,9 +811,13 @@ func (mm *messageManager) GeneratePurgeQuery(timeCutoff int64) (string, map[stri // BuildMessageRow builds a MessageRow for a db row. func BuildMessageRow(row []sqltypes.Value) (*MessageRow, error) { - timeNext, err := sqltypes.ToInt64(row[0]) - if err != nil { - return nil, err + var timeNext int64 + if !row[0].IsNull() { + tn, err := sqltypes.ToInt64(row[0]) + if err != nil { + return nil, err + } + timeNext = tn } epoch, err := sqltypes.ToInt64(row[1]) if err != nil { @@ -702,12 +841,32 @@ func (mm *messageManager) receiverCount() int { return len(mm.receivers) } -func (mm *messageManager) read(ctx context.Context, conn *connpool.DBConn, pq *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - b, err := pq.GenerateQuery(bindVars, nil) +func (mm *messageManager) readPending(ctx context.Context, conn *connpool.DBConn, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + query, err := mm.readByTimeNext.GenerateQuery(bindVars, nil) if err != nil { tabletenv.InternalErrors.Add("Messages", 1) log.Errorf("Error reading rows from message table: %v", err) return nil, err } - return conn.Exec(ctx, string(b), mm.cache.Size()+1, false) + qr := &sqltypes.Result{} + err = mm.vs.StreamResults(ctx, query, func(response *binlogdatapb.VStreamResultsResponse) error { + if response.Fields != nil { + qr.Fields = response.Fields + } + if response.Gtid != "" { + pos, err := mysql.DecodePosition(response.Gtid) + if err != nil { + return err + } + mm.lastPollPosition = &pos + } + for _, row := range response.Rows { + qr.Rows = append(qr.Rows, sqltypes.MakeRowTrusted(qr.Fields, row)) + } + return nil + }) + if err != nil { + return nil, err + } + return qr, err } diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 3444a0ffc50..8eb0a47adc8 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -169,9 +169,9 @@ type TabletServer struct { teCtrl TxPoolController hw *heartbeat.Writer hr *heartbeat.Reader - messager *messager.Engine watcher *ReplicationWatcher vstreamer *vstreamer.Engine + messager *messager.Engine updateStreamList *binlog.StreamList // checkMySQLThrottler is used to throttle the number of @@ -280,7 +280,6 @@ func NewTabletServer(config tabletenv.TabletConfig, topoServer *topo.Server, ali tsv.hw = heartbeat.NewWriter(tsv, alias, config) tsv.hr = heartbeat.NewReader(tsv, config) tsv.txThrottler = txthrottler.CreateTxThrottlerFromTabletConfig(topoServer) - tsv.messager = messager.NewEngine(tsv, tsv.se, config) tsv.watcher = NewReplicationWatcher(tsv.se, config) tsv.updateStreamList = &binlog.StreamList{} // FIXME(alainjobart) could we move this to the Register method below? @@ -306,6 +305,7 @@ func NewTabletServer(config tabletenv.TabletConfig, topoServer *topo.Server, ali }) // TODO(sougou): move this up once the stats naming problem is fixed. tsv.vstreamer = vstreamer.NewEngine(srvTopoServer, tsv.se) + tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer, config) return tsv } @@ -392,9 +392,9 @@ func (tsv *TabletServer) InitDBConfig(target querypb.Target, dbcfgs *dbconfigs.D tsv.teCtrl.InitDBConfig(tsv.dbconfigs) tsv.hw.InitDBConfig(tsv.dbconfigs) tsv.hr.InitDBConfig(tsv.dbconfigs) - tsv.messager.InitDBConfig(tsv.dbconfigs) tsv.watcher.InitDBConfig(tsv.dbconfigs) tsv.vstreamer.InitDBConfig(tsv.dbconfigs.DbaWithDB()) + tsv.messager.InitDBConfig(tsv.dbconfigs) return nil } @@ -645,6 +645,7 @@ func (tsv *TabletServer) waitForShutdown() { // It forcibly shuts down everything. func (tsv *TabletServer) closeAll() { tsv.messager.Close() + tsv.vstreamer.Close() tsv.hr.Close() tsv.hw.Close() tsv.teCtrl.StopGently() From dac5515fffd6457d57b28696a708ca54f9e5f199 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 5 Mar 2020 08:52:17 -0800 Subject: [PATCH 256/825] messager: new end2end tests Signed-off-by: Sugu Sougoumarane --- .../endtoend/vtgate/tabletserver/main_test.go | 73 ++++ .../vtgate/tabletserver/message_test.go | 401 ++++++++++++++++++ 2 files changed, 474 insertions(+) create mode 100644 go/test/endtoend/vtgate/tabletserver/main_test.go create mode 100644 go/test/endtoend/vtgate/tabletserver/message_test.go diff --git a/go/test/endtoend/vtgate/tabletserver/main_test.go b/go/test/endtoend/vtgate/tabletserver/main_test.go new file mode 100644 index 00000000000..63b5c783738 --- /dev/null +++ b/go/test/endtoend/vtgate/tabletserver/main_test.go @@ -0,0 +1,73 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tabletserver + +import ( + "flag" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + keyspaceName = "ks" + cell = "zone1" + hostname = "localhost" +) + +func TestMain(m *testing.M) { + flag.Parse() + + exitCode := func() int { + clusterInstance = cluster.NewCluster(cell, hostname) + //defer clusterInstance.Teardown() + + // Start topo server + if err := clusterInstance.StartTopo(); err != nil { + return 1 + } + + // Start keyspace + keyspace := cluster.Keyspace{ + Name: keyspaceName, + } + if err := clusterInstance.StartUnshardedKeyspace(keyspace, 0, false); err != nil { + return 1 + } + + // Start vtgate + if err := clusterInstance.StartVtgate(); err != nil { + return 1 + } + + return m.Run() + }() + os.Exit(exitCode) +} + +func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { + t.Helper() + qr, err := conn.ExecuteFetch(query, 1000, true) + require.NoError(t, err) + return qr +} diff --git a/go/test/endtoend/vtgate/tabletserver/message_test.go b/go/test/endtoend/vtgate/tabletserver/message_test.go new file mode 100644 index 00000000000..04d23f97dc4 --- /dev/null +++ b/go/test/endtoend/vtgate/tabletserver/message_test.go @@ -0,0 +1,401 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tabletserver + +import ( + "context" + "reflect" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + querypb "vitess.io/vitess/go/vt/proto/query" +) + +var createMessage = `create table vitess_message( + time_scheduled bigint, + id bigint, + time_next bigint, + epoch bigint, + time_created bigint, + time_acked bigint, + message varchar(128), + primary key(time_scheduled, id), + unique index id_idx(id), + index next_idx(time_next, epoch)) +comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` + +func TestMessage(t *testing.T) { + ctx := context.Background() + + vtParams := mysql.ConnParams{ + Host: "localhost", + Port: clusterInstance.VtgateMySQLPort, + } + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + streamConn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer streamConn.Close() + + exec(t, conn, createMessage) + defer exec(t, conn, "drop table vitess_message") + + exec(t, streamConn, "set workload = 'olap'") + err = streamConn.ExecuteStreamFetch("stream * from vitess_message") + require.NoError(t, err) + + wantFields := []*querypb.Field{{ + Name: "id", + Type: sqltypes.Int64, + }, { + Name: "time_scheduled", + Type: sqltypes.Int64, + }, { + Name: "message", + Type: sqltypes.VarChar, + }} + gotFields, err := streamConn.Fields() + require.NoError(t, err) + assert.Equal(t, wantFields, gotFields) + + exec(t, conn, "insert into vitess_message(id, message) values(1, 'hello world')") + + // Consume first message. + start := time.Now().UnixNano() + got, err := streamConn.FetchNext() + require.NoError(t, err) + + // Check time_scheduled separately. + scheduled, err := sqltypes.ToInt64(got[1]) + require.NoError(t, err) + if now := time.Now().UnixNano(); now-scheduled >= int64(10*time.Second) { + t.Errorf("scheduled: %v, must be close to %v", scheduled, now) + } + want := []sqltypes.Value{ + sqltypes.NewInt64(1), + got[1], + sqltypes.NewVarChar("hello world"), + } + assert.Equal(t, want, got) + + qr := exec(t, conn, "select time_next, epoch from vitess_message where id = 1") + next, epoch := getTimeEpoch(qr) + // epoch could be 0 or 1, depending on how fast the row is updated + switch epoch { + case 0: + if !(start-1e9 < next && next < start) { + t.Errorf("next: %d. must be within 1s of start: %d", next/1e9, start/1e9) + } + case 1: + if !(start < next && next < start+3e9) { + t.Errorf("next: %d. must be about 1s after start: %d", next/1e9, start/1e9) + } + default: + t.Errorf("epoch: %d, must be 0 or 1", epoch) + } + + // Consume the resend. + _, err = streamConn.FetchNext() + require.NoError(t, err) + qr = exec(t, conn, "select time_next, epoch from vitess_message where id = 1") + next, epoch = getTimeEpoch(qr) + // epoch could be 1 or 2, depending on how fast the row is updated + switch epoch { + case 1: + if !(start < next && next < start+3e9) { + t.Errorf("next: %d. must be about 1s after start: %d", next/1e9, start/1e9) + } + case 2: + if !(start+2e9 < next && next < start+6e9) { + t.Errorf("next: %d. must be about 3s after start: %d", next/1e9, start/1e9) + } + default: + t.Errorf("epoch: %d, must be 1 or 2", epoch) + } + + // Ack the message. + qr = exec(t, conn, "update vitess_message set time_acked = 123, time_next = null where id = 1 and time_acked is null") + assert.Equal(t, uint64(1), qr.RowsAffected) + + // Within 3+1 seconds, the row should be deleted. + time.Sleep(4 * time.Second) + qr = exec(t, conn, "select time_acked, epoch from vitess_message where id = 1") + assert.Equal(t, 0, len(qr.Rows)) +} + +var createThreeColMessage = `create table vitess_message3( + time_scheduled bigint, + id bigint, + time_next bigint, + epoch bigint, + time_created bigint, + time_acked bigint, + msg1 varchar(128), + msg2 bigint, + primary key(time_scheduled, id), + unique index id_idx(id), + index next_idx(time_next, epoch)) +comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` + +func TestThreeColMessage(t *testing.T) { + ctx := context.Background() + + vtParams := mysql.ConnParams{ + Host: "localhost", + Port: clusterInstance.VtgateMySQLPort, + } + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + streamConn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer streamConn.Close() + + exec(t, conn, createThreeColMessage) + defer exec(t, conn, "drop table vitess_message3") + + exec(t, streamConn, "set workload = 'olap'") + err = streamConn.ExecuteStreamFetch("stream * from vitess_message3") + require.NoError(t, err) + + wantFields := []*querypb.Field{{ + Name: "id", + Type: sqltypes.Int64, + }, { + Name: "time_scheduled", + Type: sqltypes.Int64, + }, { + Name: "msg1", + Type: sqltypes.VarChar, + }, { + Name: "msg2", + Type: sqltypes.Int64, + }} + gotFields, err := streamConn.Fields() + require.NoError(t, err) + assert.Equal(t, wantFields, gotFields) + + exec(t, conn, "insert into vitess_message3(id, msg1, msg2) values(1, 'hello world', 3)") + + got, err := streamConn.FetchNext() + require.NoError(t, err) + want := []sqltypes.Value{ + sqltypes.NewInt64(1), + got[1], + sqltypes.NewVarChar("hello world"), + sqltypes.NewInt64(3), + } + assert.Equal(t, want, got) + + // Verify Ack. + qr := exec(t, conn, "update vitess_message3 set time_acked = 123, time_next = null where id = 1 and time_acked is null") + assert.Equal(t, uint64(1), qr.RowsAffected) +} + +var createMessageTopic1 = `create table vitess_topic_subscriber_1( + time_scheduled bigint, + id bigint, + time_next bigint, + epoch bigint, + time_created bigint, + time_acked bigint, + message varchar(128), + primary key(time_scheduled, id), + unique index id_idx(id), + index next_idx(time_next, epoch)) +comment 'vitess_message,vt_topic=test_topic,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=1'` + +var createMessageTopic2 = `create table vitess_topic_subscriber_2( + time_scheduled bigint, + id bigint, + time_next bigint, + epoch bigint, + time_created bigint, + time_acked bigint, + message varchar(128), + primary key(time_scheduled, id), + unique index id_idx(id), + index next_idx(time_next, epoch)) +comment 'vitess_message,vt_topic=test_topic,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=1'` + +// TestMessageTopic tests for the case where id is an auto-inc column. +func TestMessageTopic(t *testing.T) { + ctx := context.Background() + + vtParams := mysql.ConnParams{ + Host: "localhost", + Port: clusterInstance.VtgateMySQLPort, + } + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + streamConn1, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer streamConn1.Close() + streamConn2, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer streamConn2.Close() + + exec(t, conn, createMessageTopic1) + exec(t, conn, createMessageTopic2) + // These are failsafe drops. The test actually drops these tables during the flow. + defer conn.ExecuteFetch("drop table vitess_topic_subscriber_1", 1, false) + defer conn.ExecuteFetch("drop table vitess_topic_subscriber_2", 1, false) + + exec(t, streamConn1, "set workload = 'olap'") + err = streamConn1.ExecuteStreamFetch("stream * from vitess_topic_subscriber_1") + require.NoError(t, err) + exec(t, streamConn2, "set workload = 'olap'") + err = streamConn2.ExecuteStreamFetch("stream * from vitess_topic_subscriber_2") + require.NoError(t, err) + + exec(t, conn, "insert into test_topic(id, message) values(1, 'msg1'), (2, 'msg2'), (3, 'msg3')") + + wantRows := [][]sqltypes.Value{{ + sqltypes.NewInt64(1), + sqltypes.NULL, + sqltypes.NewVarChar("msg1"), + }, { + sqltypes.NewInt64(2), + sqltypes.NULL, + sqltypes.NewVarChar("msg2"), + }, { + sqltypes.NewInt64(3), + sqltypes.NULL, + sqltypes.NewVarChar("msg3"), + }} + + // Consume first three messages + // and ensure they were received promptly. + start := time.Now() + for i := 0; i < 3; i++ { + // make sure the first message table received all three messages + got1, err := streamConn1.FetchNext() + require.NoError(t, err) + got1[1] = sqltypes.NULL + + // Results can come in any order. + found := false + for _, want := range wantRows { + if reflect.DeepEqual(got1, want) { + found = true + } + } + assert.True(t, found) + + // make sure the second message table received all three messages + got2, err := streamConn2.FetchNext() + require.NoError(t, err) + got2[1] = sqltypes.NULL + + // Results can come in any order. + found = false + for _, want := range wantRows { + if reflect.DeepEqual(got2, want) { + found = true + } + } + assert.True(t, found) + } + if d := time.Since(start); d > 1*time.Second { + t.Errorf("messages were delayed: %v", d) + } + + // ack the first subscribers + _ = exec(t, conn, "update vitess_topic_subscriber_1 set time_acked = 123, time_next = null where id in (1, 2, 3) and time_acked is null") + _ = exec(t, conn, "update vitess_topic_subscriber_2 set time_acked = 123, time_next = null where id in (1, 2, 3) and time_acked is null") + + // + // phase 2 tests deleting one of the subscribers and making sure + // that inserts into a topic go to one subscribed message table + // + + exec(t, conn, "drop table vitess_topic_subscriber_1") + exec(t, conn, "insert into test_topic(id, message) values(4, 'msg4'), (5, 'msg5'), (6, 'msg6')") + + wantRows = [][]sqltypes.Value{{ + sqltypes.NewInt64(4), + sqltypes.NULL, + sqltypes.NewVarChar("msg4"), + }, { + sqltypes.NewInt64(5), + sqltypes.NULL, + sqltypes.NewVarChar("msg5"), + }, { + sqltypes.NewInt64(6), + sqltypes.NULL, + sqltypes.NewVarChar("msg6"), + }} + + // Consume first three messages + // and ensure they were received promptly. + start = time.Now() + for i := 0; i < 3; i++ { + // make sure the second message table received all three messages + got2, err := streamConn2.FetchNext() + require.NoError(t, err) + got2[1] = sqltypes.NULL + + // Results can come in any order. + found := false + for _, want := range wantRows { + if reflect.DeepEqual(got2, want) { + found = true + } + } + assert.True(t, found) + } + if d := time.Since(start); d > 1*time.Second { + t.Errorf("messages were delayed: %v", d) + } + + // ack the second subscriber + _ = exec(t, conn, "update vitess_topic_subscriber_2 set time_acked = 123, time_next = null where id in (4, 5, 6) and time_acked is null") + + // + // phase 3 tests deleting the last subscriber and making sure + // that inserts into a topic error out with table not found + // + + // remove the second subscriber which should remove the topic + exec(t, conn, "drop table vitess_topic_subscriber_2") + + // this should fail because the topic doesn't exist. Any other outcome fails the test + _, err = conn.ExecuteFetch("insert into test_topic(id, message) values(4, 'msg4'), (5, 'msg5'), (6, 'msg6')", 1, false) + want := "table test_topic not found in schema" + if err == nil || !strings.Contains(err.Error(), want) { + t.Errorf("non-topic insert err: %v, must contain %v", err, want) + } +} + +func getTimeEpoch(qr *sqltypes.Result) (int64, int64) { + if len(qr.Rows) != 1 { + return 0, 0 + } + t, _ := sqltypes.ToInt64(qr.Rows[0][0]) + e, _ := sqltypes.ToInt64(qr.Rows[0][1]) + return t, e +} From 14eb3fa075b8c8f07c8360b26a23eaef1186c4a0 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 5 Mar 2020 08:55:06 -0800 Subject: [PATCH 257/825] messages: delete old end2end tests Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/endtoend/main_test.go | 21 - go/vt/vttablet/endtoend/message_test.go | 660 ------------------------ 2 files changed, 681 deletions(-) delete mode 100644 go/vt/vttablet/endtoend/message_test.go diff --git a/go/vt/vttablet/endtoend/main_test.go b/go/vt/vttablet/endtoend/main_test.go index 1dda49412ba..b0a7bc892ec 100644 --- a/go/vt/vttablet/endtoend/main_test.go +++ b/go/vt/vttablet/endtoend/main_test.go @@ -229,27 +229,6 @@ var tableACLConfig = `{ "writers": ["dev"], "admins": ["dev"] }, - { - "name": "vitess_message", - "table_names_or_prefixes": ["vitess_message"], - "readers": ["dev"], - "writers": ["dev"], - "admins": ["dev"] - }, - { - "name": "vitess_message3", - "table_names_or_prefixes": ["vitess_message3"], - "readers": ["dev"], - "writers": ["dev"], - "admins": ["dev"] - }, - { - "name": "vitess_message_auto", - "table_names_or_prefixes": ["vitess_message_auto"], - "readers": ["dev"], - "writers": ["dev"], - "admins": ["dev"] - }, { "name": "test_topic", "table_names_or_prefixes": ["test_topic"], diff --git a/go/vt/vttablet/endtoend/message_test.go b/go/vt/vttablet/endtoend/message_test.go deleted file mode 100644 index e5cee922198..00000000000 --- a/go/vt/vttablet/endtoend/message_test.go +++ /dev/null @@ -1,660 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package endtoend - -import ( - "io" - "reflect" - "runtime" - "testing" - "time" - - "github.com/stretchr/testify/require" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/vttablet/endtoend/framework" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -var createMessage = `create table vitess_message( - time_scheduled bigint, - id bigint, - time_next bigint, - epoch bigint, - time_created bigint, - time_acked bigint, - message varchar(128), - primary key(time_scheduled, id), - unique index id_idx(id), - index next_idx(time_next, epoch)) -comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` - -func TestMessage(t *testing.T) { - ch := make(chan *sqltypes.Result) - done := make(chan struct{}) - client := framework.NewClient() - if _, err := client.Execute(createMessage, nil); err != nil { - t.Fatal(err) - } - defer client.Execute("drop table vitess_message", nil) - - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Acked"), 0; got != want { - t.Errorf("Messages/vitess_message.Acked: %d, want %d", got, want) - } - - // Start goroutine to consume message stream. - go waitForMessage(t, client, "vitess_message", ch, done) - got := <-ch - want := &sqltypes.Result{ - Fields: []*querypb.Field{{ - Name: "id", - Type: sqltypes.Int64, - }, { - Name: "time_scheduled", - Type: sqltypes.Int64, - }, { - Name: "message", - Type: sqltypes.VarChar, - }}, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("message(field) received:\n%v, want\n%v", got, want) - } - runtime.Gosched() - defer func() { close(done) }() - - // Create message. - err := client.Begin(false) - if err != nil { - t.Error(err) - return - } - _, err = client.Execute("insert into vitess_message(id, message) values(1, 'hello world')", nil) - if err != nil { - t.Error(err) - return - } - err = client.Commit() - if err != nil { - t.Error(err) - return - } - - // Consume first message. - start := time.Now().UnixNano() - got = <-ch - // Check time_scheduled separately. - scheduled, err := sqltypes.ToInt64(got.Rows[0][1]) - require.NoError(t, err) - if now := time.Now().UnixNano(); now-scheduled >= int64(10*time.Second) { - t.Errorf("scheduled: %v, must be close to %v", scheduled, now) - } - want = &sqltypes.Result{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - got.Rows[0][1], - sqltypes.NewVarChar("hello world"), - }}, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("message received:\n%v, want\n%v", got, want) - } - qr, err := client.Execute("select time_next, epoch from vitess_message where id = 1", nil) - if err != nil { - t.Fatal(err) - } - next, epoch := getTimeEpoch(qr) - // epoch could be 0 or 1, depending on how fast the row is updated - switch epoch { - case 0: - if !(start-1e9 < next && next < start) { - t.Errorf("next: %d. must be within 1s of start: %d", next/1e9, start/1e9) - } - case 1: - if !(start < next && next < start+3e9) { - t.Errorf("next: %d. must be about 1s after start: %d", next/1e9, start/1e9) - } - default: - t.Errorf("epoch: %d, must be 0 or 1", epoch) - } - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Delayed"), 0; got != want { - t.Errorf("Messages/vitess_message.Delayed: %d, want %d", got, want) - } - - // Consume the resend. - <-ch - qr, err = client.Execute("select time_next, epoch from vitess_message where id = 1", nil) - if err != nil { - t.Fatal(err) - } - next, epoch = getTimeEpoch(qr) - // epoch could be 1 or 2, depending on how fast the row is updated - switch epoch { - case 1: - if !(start < next && next < start+3e9) { - t.Errorf("next: %d. must be about 1s after start: %d", next/1e9, start/1e9) - } - case 2: - if !(start+2e9 < next && next < start+6e9) { - t.Errorf("next: %d. must be about 3s after start: %d", next/1e9, start/1e9) - } - default: - t.Errorf("epoch: %d, must be 1 or 2", epoch) - } - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Delayed"), 1; got != want { - t.Errorf("Messages/vitess_message.Delayed: %d, want %d", got, want) - } - - // Ack the message. - count, err := client.MessageAck("vitess_message", []string{"1"}) - require.NoError(t, err) - if count != 1 { - t.Errorf("count: %d, want 1", count) - } - qr, err = client.Execute("select time_acked, epoch from vitess_message where id = 1", nil) - if err != nil { - t.Fatal(err) - } - end := time.Now().UnixNano() - ack, _ := getTimeEpoch(qr) - if !(end-1e9 < ack && ack < end) { - t.Errorf("ack: %d. must be within 1s of end: %d", ack/1e9, end/1e9) - } - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Acked"), 1; got != want { - t.Errorf("Messages/vitess_message.Acked: %d, want %d", got, want) - } - - // Within 3+1 seconds, the row should be deleted. - time.Sleep(4 * time.Second) - qr, err = client.Execute("select time_acked, epoch from vitess_message where id = 1", nil) - if err != nil { - t.Fatal(err) - } - if qr.RowsAffected != 0 { - t.Error("The row has not been purged yet") - } - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Purged"), 1; got != want { - t.Errorf("Messages/vitess_message.Purged: %d, want %d", got, want) - } - - // Verify final counts. - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Acked"), 1; got != want { - t.Errorf("Messages/vitess_message.Acked: %d, want %d", got, want) - } - if got, want := framework.FetchInt(framework.DebugVars(), "Messages/vitess_message.Delayed"), 1; got != want { - t.Errorf("Messages/vitess_message.Delayed: %d, want %d", got, want) - } -} - -var createThreeColMessage = `create table vitess_message3( - time_scheduled bigint, - id bigint, - time_next bigint, - epoch bigint, - time_created bigint, - time_acked bigint, - msg1 varchar(128), - msg2 bigint, - primary key(time_scheduled, id), - unique index id_idx(id), - index next_idx(time_next, epoch)) -comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` - -func TestThreeColMessage(t *testing.T) { - ch := make(chan *sqltypes.Result) - done := make(chan struct{}) - client := framework.NewClient() - if _, err := client.Execute(createThreeColMessage, nil); err != nil { - t.Fatal(err) - } - defer client.Execute("drop table vitess_message3", nil) - - go waitForMessage(t, client, "vitess_message3", ch, done) - - // Verify fields. - got := <-ch - want := &sqltypes.Result{ - Fields: []*querypb.Field{{ - Name: "id", - Type: sqltypes.Int64, - }, { - Name: "time_scheduled", - Type: sqltypes.Int64, - }, { - Name: "msg1", - Type: sqltypes.VarChar, - }, { - Name: "msg2", - Type: sqltypes.Int64, - }}, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("message(field) received:\n%v, want\n%v", got, want) - } - runtime.Gosched() - defer func() { close(done) }() - err := client.Begin(false) - if err != nil { - t.Error(err) - return - } - _, err = client.Execute("insert into vitess_message3(id, msg1, msg2) values(1, 'hello world', 3)", nil) - if err != nil { - t.Error(err) - return - } - err = client.Commit() - if err != nil { - t.Error(err) - return - } - - // Verify row. - got = <-ch - want = &sqltypes.Result{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - got.Rows[0][1], - sqltypes.NewVarChar("hello world"), - sqltypes.NewInt64(3), - }}, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("message received:\n%v, want\n%v", got, want) - } - - // Verify Ack. - count, err := client.MessageAck("vitess_message3", []string{"1"}) - require.NoError(t, err) - if count != 1 { - t.Errorf("count: %d, want 1", count) - } -} - -func getTimeEpoch(qr *sqltypes.Result) (int64, int64) { - if len(qr.Rows) != 1 { - return 0, 0 - } - t, _ := sqltypes.ToInt64(qr.Rows[0][0]) - e, _ := sqltypes.ToInt64(qr.Rows[0][1]) - return t, e -} - -var createMessageAuto = `create table vitess_message_auto( - time_scheduled bigint, - id bigint auto_increment, - time_next bigint, - epoch bigint, - time_created bigint, - time_acked bigint, - message varchar(128), - primary key(time_scheduled, id), - unique index id_idx(id), - index next_idx(time_next, epoch)) -comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=1'` - -// TestMessageAuto tests for the case where id is an auto-inc column. -func TestMessageAuto(t *testing.T) { - ch := make(chan *sqltypes.Result) - done := make(chan struct{}) - client := framework.NewClient() - if _, err := client.Execute(createMessageAuto, nil); err != nil { - t.Fatal(err) - } - defer client.Execute("drop table vitess_message_auto", nil) - - // Start goroutine to consume message stream. - go waitForMessage(t, client, "vitess_message_auto", ch, done) - <-ch - defer func() { close(done) }() - - // Create message. - err := client.Begin(false) - if err != nil { - t.Error(err) - return - } - // This insert should cause the engine to make a best-effort guess at generated ids. - // It will expedite the first two rows with null values, and the third row, and will - // give up on the last row, which should eventually be picked up by the poller. - _, err = client.Execute("insert into vitess_message_auto(id, message) values(null, 'msg1'), (null, 'msg2'), (5, 'msg5'), (null, 'msg6')", nil) - if err != nil { - t.Error(err) - return - } - err = client.Commit() - if err != nil { - t.Error(err) - return - } - - wantResults := []*sqltypes.Result{{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NULL, - sqltypes.NewVarChar("msg1"), - }}, - }, { - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(2), - sqltypes.NULL, - sqltypes.NewVarChar("msg2"), - }}, - }, { - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(5), - sqltypes.NULL, - sqltypes.NewVarChar("msg5"), - }}, - }, { - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(6), - sqltypes.NULL, - sqltypes.NewVarChar("msg6"), - }}, - }} - - // Consume first three messages - // and ensure they were received promptly. - start := time.Now() - for i := 0; i < 3; i++ { - got := <-ch - got.Rows[0][1] = sqltypes.NULL - - // Results can come in any order. - found := false - for _, want := range wantResults { - if reflect.DeepEqual(got, want) { - found = true - } - } - if !found { - t.Errorf("message fetch: %v not found in expected list: %v", got, wantResults) - } - } - if d := time.Since(start); d > 1*time.Second { - t.Errorf("First three messages were delayed: %v", d) - } - - _, err = client.MessageAck("vitess_message_auto", []string{"1, 2, 5"}) - require.NoError(t, err) - - // Ensure msg6 is eventually received. - got := <-ch - got.Rows[0][1] = sqltypes.NULL - want := &sqltypes.Result{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(6), - sqltypes.NULL, - sqltypes.NewVarChar("msg6"), - }}, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("message received:\n%v, want\n%v", got, want) - } -} - -var createMessageTopic1 = `create table vitess_topic_subscriber_1( - time_scheduled bigint, - id bigint, - time_next bigint, - epoch bigint, - time_created bigint, - time_acked bigint, - message varchar(128), - primary key(time_scheduled, id), - unique index id_idx(id), - index next_idx(time_next, epoch)) -comment 'vitess_message,vt_topic=test_topic,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=1'` - -var createMessageTopic2 = `create table vitess_topic_subscriber_2( - time_scheduled bigint, - id bigint, - time_next bigint, - epoch bigint, - time_created bigint, - time_acked bigint, - message varchar(128), - primary key(time_scheduled, id), - unique index id_idx(id), - index next_idx(time_next, epoch)) -comment 'vitess_message,vt_topic=test_topic,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=1'` - -// TestMessageTopic tests for the case where id is an auto-inc column. -func TestMessageTopic(t *testing.T) { - ch1 := make(chan *sqltypes.Result) - ch2 := make(chan *sqltypes.Result) - done := make(chan struct{}) - client := framework.NewClient() - - // - // phase 1 tests inserts into a topic going to two subscribed message tables - // - if _, err := client.Execute(createMessageTopic1, nil); err != nil { - t.Fatal(err) - } - - if _, err := client.Execute(createMessageTopic2, nil); err != nil { - t.Fatal(err) - } - - // Start goroutines to consume message stream. - go waitForMessage(t, client, "vitess_topic_subscriber_1", ch1, done) - <-ch1 - go waitForMessage(t, client, "vitess_topic_subscriber_2", ch2, done) - <-ch2 - defer func() { close(done) }() - - // Create message. - err := client.Begin(false) - if err != nil { - t.Error(err) - return - } - // This insert should cause the engine to make a best-effort guess at generated ids. - // It will expedite the first two rows with null values, and the third row, and will - // give up on the last row, which should eventually be picked up by the poller. - _, err = client.Execute("insert into test_topic(id, message) values(1, 'msg1'), (2, 'msg2'), (3, 'msg3')", nil) - if err != nil { - t.Error(err) - return - } - - err = client.Commit() - if err != nil { - t.Error(err) - return - } - - wantResults := []*sqltypes.Result{{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NULL, - sqltypes.NewVarChar("msg1"), - }}, - }, { - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(2), - sqltypes.NULL, - sqltypes.NewVarChar("msg2"), - }}, - }, { - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(3), - sqltypes.NULL, - sqltypes.NewVarChar("msg3"), - }}, - }} - - // Consume first three messages - // and ensure they were received promptly. - start := time.Now() - for i := 0; i < 3; i++ { - // make sure the first message table received all three messages - got1 := <-ch1 - got1.Rows[0][1] = sqltypes.NULL - - // Results can come in any order. - found := false - for _, want := range wantResults { - if reflect.DeepEqual(got1, want) { - found = true - } - } - if !found { - t.Errorf("message fetch 1: %v not found in expected list: %v", got1, wantResults) - } - - // make sure the second message table received all three messages - got2 := <-ch2 - got2.Rows[0][1] = sqltypes.NULL - - // Results can come in any order. - found = false - for _, want := range wantResults { - if reflect.DeepEqual(got2, want) { - found = true - } - } - if !found { - t.Errorf("message fetch 2: %v not found in expected list: %v", got2, wantResults) - } - } - if d := time.Since(start); d > 1*time.Second { - t.Errorf("messages were delayed: %v", d) - } - - // ack the first subscriber - _, err = client.MessageAck("vitess_topic_subscriber_1", []string{"1, 2, 3"}) - require.NoError(t, err) - - // ack the second subscriber - _, err = client.MessageAck("vitess_topic_subscriber_2", []string{"1, 2, 3"}) - require.NoError(t, err) - - // - // phase 2 tests deleting one of the subscribers and making sure - // that inserts into a topic go to one subscribed message table - // - - client.Execute("drop table vitess_topic_subscriber_1", nil) - - // Create message. - err = client.Begin(false) - if err != nil { - t.Error(err) - return - } - // This insert should cause the engine to make a best-effort guess at generated ids. - // It will expedite the first two rows with null values, and the third row, and will - // give up on the last row, which should eventually be picked up by the poller. - _, err = client.Execute("insert into test_topic(id, message) values(4, 'msg4'), (5, 'msg5'), (6, 'msg6')", nil) - if err != nil { - t.Error(err) - return - } - - err = client.Commit() - if err != nil { - t.Error(err) - return - } - - wantResults = []*sqltypes.Result{{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(4), - sqltypes.NULL, - sqltypes.NewVarChar("msg4"), - }}, - }, { - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(5), - sqltypes.NULL, - sqltypes.NewVarChar("msg5"), - }}, - }, { - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(6), - sqltypes.NULL, - sqltypes.NewVarChar("msg6"), - }}, - }} - - // Consume first three messages - // and ensure they were received promptly. - start = time.Now() - for i := 0; i < 3; i++ { - // make sure the second message table received all three messages - got2 := <-ch2 - got2.Rows[0][1] = sqltypes.NULL - - // Results can come in any order. - found := false - for _, want := range wantResults { - if reflect.DeepEqual(got2, want) { - found = true - } - } - if !found { - t.Errorf("message fetch 2: %v not found in expected list: %v", got2, wantResults) - } - } - if d := time.Since(start); d > 1*time.Second { - t.Errorf("messages were delayed: %v", d) - } - - // ack the second subscriber - _, err = client.MessageAck("vitess_topic_subscriber_2", []string{"4, 5, 6"}) - require.NoError(t, err) - - // - // phase 3 tests deleting the last subscriber and making sure - // that inserts into a topic error out with table not found - // - - // remove the second subscriber which should remove the topic - if _, err := client.Execute("drop table vitess_topic_subscriber_2", nil); err != nil { - t.Fatal(err) - } - - // this should fail because the topic doesn't exist. Any other outcome fails the test - _, err = client.Execute("insert into test_topic(id, message) values(4, 'msg4'), (5, 'msg5'), (6, 'msg6')", nil) - switch { - case err == nil: - t.Error("test_topic shouldn't have existed for inserts to succeed") - - case err.Error() == "table test_topic not found in schema (CallerID: dev)": - - default: - t.Error(err) - } -} - -func waitForMessage(t *testing.T, client *framework.QueryClient, tableName string, ch chan *sqltypes.Result, done chan struct{}) { - if err := client.MessageStream(tableName, func(qr *sqltypes.Result) error { - select { - case <-done: - return io.EOF - default: - } - ch <- qr - return nil - }); err != nil { - t.Error(err) - } - close(ch) -} From 636dcae9011427a5e8d2288107e8b6fc7a7ca3ee Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 5 Mar 2020 13:50:23 -0800 Subject: [PATCH 258/825] messager: delete older code Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/messager/engine.go | 53 +------ .../tabletserver/messager/engine_test.go | 94 ----------- .../tabletserver/messager/message_manager.go | 45 +++--- .../messager/message_manager_test.go | 2 - .../vttablet/tabletserver/planbuilder/dml.go | 12 +- go/vt/vttablet/tabletserver/query_executor.go | 66 +------- .../vttablet/tabletserver/tabletenv/config.go | 148 +++++++++--------- go/vt/vttablet/tabletserver/tabletserver.go | 14 +- go/vt/vttablet/tabletserver/tx_engine.go | 4 +- go/vt/vttablet/tabletserver/tx_executor.go | 18 +-- go/vt/vttablet/tabletserver/tx_pool.go | 19 +-- 11 files changed, 117 insertions(+), 358 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/engine.go b/go/vt/vttablet/tabletserver/messager/engine.go index 7c39aa47f2f..c06b622104f 100644 --- a/go/vt/vttablet/tabletserver/messager/engine.go +++ b/go/vt/vttablet/tabletserver/messager/engine.go @@ -18,17 +18,13 @@ package messager import ( "sync" - "time" "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/sync2" - "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -40,7 +36,6 @@ import ( // TabletService defines the functions of TabletServer // that the messager needs for callback. type TabletService interface { - CheckMySQL() PostponeMessages(ctx context.Context, target *querypb.Target, name string, ids []string) (count int64, err error) PurgeMessages(ctx context.Context, target *querypb.Target, name string, timeCutoff int64) (count int64, err error) } @@ -54,8 +49,6 @@ type VStreamer interface { // Engine is the engine for handling messages. type Engine struct { - dbconfigs *dbconfigs.DBConfigs - mu sync.Mutex isOpen bool managers map[string]*messageManager @@ -63,40 +56,26 @@ type Engine struct { tsv TabletService se *schema.Engine vs VStreamer - conns *connpool.Pool postponeSema *sync2.Semaphore } // NewEngine creates a new Engine. func NewEngine(tsv TabletService, se *schema.Engine, vs VStreamer, config tabletenv.TabletConfig) *Engine { return &Engine{ - tsv: tsv, - se: se, - vs: vs, - conns: connpool.New( - config.PoolNamePrefix+"MessagerPool", - config.MessagePoolSize, - config.MessagePoolPrefillParallelism, - time.Duration(config.IdleTimeout*1e9), - tsv, - ), + tsv: tsv, + se: se, + vs: vs, postponeSema: sync2.NewSemaphore(config.MessagePostponeCap, 0), managers: make(map[string]*messageManager), } } -// InitDBConfig must be called before Open. -func (me *Engine) InitDBConfig(dbcfgs *dbconfigs.DBConfigs) { - me.dbconfigs = dbcfgs -} - // Open starts the Engine service. func (me *Engine) Open() error { if me.isOpen { return nil } - me.conns.Open(me.dbconfigs.AppWithDB(), me.dbconfigs.DbaWithDB(), me.dbconfigs.AppDebugWithDB()) me.se.RegisterNotifier("messages", me.schemaChanged) me.isOpen = true return nil @@ -115,7 +94,6 @@ func (me *Engine) Close() { mm.Close() } me.managers = make(map[string]*messageManager) - me.conns.Close() } // Subscribe subscribes to messages from the requested table. @@ -138,29 +116,6 @@ func (me *Engine) Subscribe(ctx context.Context, name string, send func(*sqltype return mm.Subscribe(ctx, send), nil } -// LockDB obtains db locks for all messages that need to -// be updated and returns the counterpart unlock function. -func (me *Engine) LockDB(newMessages map[string][]*MessageRow, changedMessages map[string][]string) func() { - return func() {} -} - -// UpdateCaches updates the caches for the committed changes. -func (me *Engine) UpdateCaches(newMessages map[string][]*MessageRow, changedMessages map[string][]string) { - return -} - -// GenerateLoadMessagesQuery returns the ParsedQuery for loading messages by pk. -// The results of the query can be used in a BuildMessageRow call. -func (me *Engine) GenerateLoadMessagesQuery(name string) (*sqlparser.ParsedQuery, error) { - me.mu.Lock() - defer me.mu.Unlock() - mm := me.managers[name] - if mm == nil { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "message table %s not found in schema", name) - } - return mm.loadMessagesQuery, nil -} - // GenerateAckQuery returns the query and bind vars for acking a message. func (me *Engine) GenerateAckQuery(name string, ids []string) (string, map[string]*querypb.BindVariable, error) { me.mu.Lock() @@ -210,7 +165,7 @@ func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altere log.Errorf("Newly created table already exists in messages: %s", name) continue } - mm := newMessageManager(me.tsv, me.vs, t, me.conns, me.postponeSema) + mm := newMessageManager(me.tsv, me.vs, t, me.postponeSema) me.managers[name] = mm mm.Open() } diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index 0b6e5eeeea3..945eec89efb 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -20,19 +20,14 @@ import ( "fmt" "math/rand" "reflect" - "runtime" "testing" - "time" - "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/sync2" "vitess.io/vitess/go/vt/dbconfigs" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -133,95 +128,6 @@ func TestSubscribe(t *testing.T) { } } -func TestLockDB(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - engine := newTestEngine(db) - defer engine.Close() - tables := map[string]*schema.Table{ - "t1": meTable, - "t2": meTable, - } - engine.schemaChanged(tables, []string{"t1", "t2"}, nil, nil) - f1, ch1 := newEngineReceiver() - engine.Subscribe(context.Background(), "t1", f1) - <-ch1 - - row1 := &MessageRow{ - Row: []sqltypes.Value{sqltypes.NewVarBinary("1")}, - } - row2 := &MessageRow{ - TimeNext: time.Now().UnixNano() + int64(10*time.Minute), - Row: []sqltypes.Value{sqltypes.NewVarBinary("2")}, - } - newMessages := map[string][]*MessageRow{"t1": {row1, row2}, "t3": {row1}} - unlock := engine.LockDB(newMessages, nil) - engine.UpdateCaches(newMessages, nil) - unlock() - <-ch1 - runtime.Gosched() - // row2 should not be sent. - select { - case mr := <-ch1: - t.Errorf("Unexpected message: %v", mr) - default: - } - - ch2 := make(chan *sqltypes.Result, 1) - var count sync2.AtomicInt64 - engine.Subscribe(context.Background(), "t2", func(qr *sqltypes.Result) error { - count.Add(1) - ch2 <- qr - return nil - }) - <-ch2 - mm := engine.managers["t2"] - mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("1")}}) - // Make sure the message is enqueued. - for { - runtime.Gosched() - time.Sleep(10 * time.Millisecond) - if count.Get() == int64(2) { - break - } - } - // "2" will be in the cache. - mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("2")}}) - changedMessages := map[string][]string{"t2": {"2"}, "t3": {"2"}} - unlock = engine.LockDB(nil, changedMessages) - // This should delete "2". - engine.UpdateCaches(nil, changedMessages) - unlock() - <-ch2 - runtime.Gosched() - // There should be no more messages. - select { - case mr := <-ch2: - t.Errorf("Unexpected message: %v", mr) - default: - } -} - -func TestGenerateLoadMessagesQuery(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - engine := newTestEngine(db) - defer engine.Close() - - table := *meTable - table.Name = sqlparser.NewTableIdent("t1") - engine.schemaChanged(map[string]*schema.Table{ - "t1": &table, - }, []string{"t1"}, nil, nil) - - q, err := engine.GenerateLoadMessagesQuery("t1") - require.NoError(t, err) - want := "select time_next, epoch, time_created, id, time_scheduled, message from t1 where :#pk" - if q.Query != want { - t.Errorf("GenerateLoadMessagesQuery: %s, want %s", q.Query, want) - } -} - func TestEngineGenerate(t *testing.T) { db := fakesqldb.New(t) defer db.Close() diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index d98a00315bd..e80545124f1 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -31,7 +31,6 @@ import ( "vitess.io/vitess/go/timer" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -166,9 +165,8 @@ type receiverWithStatus struct { // This thread is mostly independent. It wakes up periodically // to delete old rows that were successfully acked. type messageManager struct { - DBLock sync.Mutex - tsv TabletService - vs VStreamer + tsv TabletService + vs VStreamer name sqlparser.TableIdent fieldResult *sqltypes.Result @@ -177,7 +175,6 @@ type messageManager struct { batchSize int pollerTicks *timer.Timer purgeTicks *timer.Timer - conns *connpool.Pool postponeSema *sync2.Semaphore mu sync.Mutex @@ -192,6 +189,16 @@ type messageManager struct { curReceiver int messagesPending bool + // streamMu keeps the cache and database consistent with each other. + // Specifically: + // It prevents items from being removed from cache while the poller + // reads from the db and adds items to it. Otherwise, the poller + // might add an older snapshot of a row that was just postponed. + // It blocks vstreamer from receiving messages while the poller + // reads a snapshot and updates lastPollPosition. Any events older than + // lastPollPosition must be ignored by the vstreamer. It consequently + // also blocks vstreamer from updating the cache while the poller is + // active. streamMu sync.Mutex streamCancel func() lastPollPosition *mysql.Position @@ -202,18 +209,17 @@ type messageManager struct { // The goroutine must in turn defer on Done. wg sync.WaitGroup - vsFilter *binlogdatapb.Filter - readByTimeNext *sqlparser.ParsedQuery - loadMessagesQuery *sqlparser.ParsedQuery - ackQuery *sqlparser.ParsedQuery - postponeQuery *sqlparser.ParsedQuery - purgeQuery *sqlparser.ParsedQuery + vsFilter *binlogdatapb.Filter + readByTimeNext *sqlparser.ParsedQuery + ackQuery *sqlparser.ParsedQuery + postponeQuery *sqlparser.ParsedQuery + purgeQuery *sqlparser.ParsedQuery } // newMessageManager creates a new message manager. // Calls into tsv have to be made asynchronously. Otherwise, // it can lead to deadlocks. -func newMessageManager(tsv TabletService, vs VStreamer, table *schema.Table, conns *connpool.Pool, postponeSema *sync2.Semaphore) *messageManager { +func newMessageManager(tsv TabletService, vs VStreamer, table *schema.Table, postponeSema *sync2.Semaphore) *messageManager { mm := &messageManager{ tsv: tsv, vs: vs, @@ -227,7 +233,6 @@ func newMessageManager(tsv TabletService, vs VStreamer, table *schema.Table, con cache: newCache(table.MessageInfo.CacheSize), pollerTicks: timer.NewTimer(table.MessageInfo.PollInterval), purgeTicks: timer.NewTimer(table.MessageInfo.PollInterval), - conns: conns, postponeSema: postponeSema, messagesPending: true, } @@ -244,9 +249,6 @@ func newMessageManager(tsv TabletService, vs VStreamer, table *schema.Table, con mm.readByTimeNext = sqlparser.BuildParsedQuery( "select time_next, epoch, time_created, %s from %v where time_next < %a order by time_next desc limit %a", columnList, mm.name, ":time_next", ":max") - mm.loadMessagesQuery = sqlparser.BuildParsedQuery( - "select time_next, epoch, time_created, %s from %v where %a", - columnList, mm.name, ":#pk") mm.ackQuery = sqlparser.BuildParsedQuery( "update %v set time_acked = %a, time_next = null where id in %a and time_acked is null", mm.name, ":time_acked", "::ids") @@ -689,20 +691,13 @@ func (mm *messageManager) runPoller() { tabletenv.LogError() cancel() }() - conn, err := mm.conns.Get(ctx) - if err != nil { - tabletenv.InternalErrors.Add("Messages", 1) - log.Errorf("Error getting connection: %v", err) - return - } - defer conn.Recycle() size := mm.cache.Size() bindVars := map[string]*querypb.BindVariable{ "time_next": sqltypes.Int64BindVariable(time.Now().UnixNano()), "max": sqltypes.Int64BindVariable(int64(size)), } - qr, err := mm.readPending(ctx, conn, bindVars) + qr, err := mm.readPending(ctx, bindVars) if err != nil { return } @@ -841,7 +836,7 @@ func (mm *messageManager) receiverCount() int { return len(mm.receivers) } -func (mm *messageManager) readPending(ctx context.Context, conn *connpool.DBConn, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { +func (mm *messageManager) readPending(ctx context.Context, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { query, err := mm.readByTimeNext.GenerateQuery(bindVars, nil) if err != nil { tabletenv.InternalErrors.Add("Messages", 1) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index cdc438cc5d5..bd017b595b2 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -757,8 +757,6 @@ type fakeTabletServer struct { func newFakeTabletServer() *fakeTabletServer { return &fakeTabletServer{} } -func (fts *fakeTabletServer) CheckMySQL() {} - func (fts *fakeTabletServer) SetChannel(ch chan string) { fts.mu.Lock() fts.ch = ch diff --git a/go/vt/vttablet/tabletserver/planbuilder/dml.go b/go/vt/vttablet/tabletserver/planbuilder/dml.go index 6b1aba64e14..54be33c5486 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/dml.go +++ b/go/vt/vttablet/tabletserver/planbuilder/dml.go @@ -497,16 +497,6 @@ func analyzeInsertMessage(ins *sqlparser.Insert, plan *Plan, table *schema.Table if _, ok := ins.Rows.(sqlparser.SelectStatement); ok { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "subquery not allowed for message table: %s", table.Name.String()) } - if ins.OnDup != nil { - // only allow 'on duplicate key' where time_scheduled and id are not referenced - ts := sqlparser.NewColIdent("time_scheduled") - id := sqlparser.NewColIdent("id") - for _, updateExpr := range ins.OnDup { - if updateExpr.Name.Name.Equal(ts) || updateExpr.Name.Name.Equal(id) { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "'on duplicate key' cannot reference time_scheduled or id for message table: %s", table.Name.String()) - } - } - } if len(ins.Columns) == 0 { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "column list must be specified for message table insert: %s", table.Name.String()) } @@ -551,7 +541,7 @@ func analyzeInsertMessage(ins *sqlparser.Insert, plan *Plan, table *schema.Table } _ = addVal(ins, col, sqlparser.NewIntVal([]byte("0"))) - // time_acked should must not be specified. + // time_acked should not be specified. col = sqlparser.NewColIdent("time_acked") if num := ins.Columns.FindColumn(col); num >= 0 { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s must not be specified for message insert", col.String()) diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 02bc2752e67..ed6e613dd91 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -34,10 +34,8 @@ import ( "vitess.io/vitess/go/vt/tableacl" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" - "vitess.io/vitess/go/vt/vttablet/tabletserver/messager" "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" "vitess.io/vitess/go/vt/vttablet/tabletserver/rules" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" querypb "vitess.io/vitess/go/vt/proto/query" @@ -315,7 +313,7 @@ func (qre *QueryExecutor) execAsTransaction(f func(conn *TxConnection) (*sqltype qre.logStats.AddRewrittenSQL("rollback", start) return nil, err } - commitSQL, err := qre.tsv.te.txPool.LocalCommit(qre.ctx, conn, qre.tsv.messager) + commitSQL, err := qre.tsv.te.txPool.LocalCommit(qre.ctx, conn) // As above LocalCommit is a no-op for autocommmit so don't log anything. if commitSQL != "" { @@ -576,60 +574,7 @@ func (qre *QueryExecutor) execInsertMessage(conn *TxConnection) (*sqltypes.Resul if err != nil { return nil, err } - qr, err := qre.execInsertPKRows(conn, nil, pkRows) - if err != nil { - return nil, err - } - - // If an auto-inc value was generated, it could be the id column. - // If so, we have to populate it. - if qr.InsertID != 0 { - id := int64(qr.InsertID) - idPKIndex := qre.plan.Table.MessageInfo.IDPKIndex - for _, row := range pkRows { - if !row[idPKIndex].IsNull() { - // If a value was supplied, either it was not the auto-inc column - // or values were partially supplied for an auto-inc column. - // If it's the former, there is nothing more to do. - // If it's the latter, we cannot predict the values for subsequent - // rows. So, we still break out of this loop, and will rely on - // the poller to eventually pick up the rows from the database. - break - } - row[idPKIndex] = sqltypes.NewInt64(id) - id++ - } - } - - // Re-read the inserted rows to prime the cache. - extras := map[string]sqlparser.Encodable{ - "#pk": &sqlparser.TupleEqualityList{ - Columns: qre.plan.Table.Indexes[0].Columns, - Rows: pkRows, - }, - } - tableName := qre.plan.Table.Name.String() - loadMessages, err := qre.tsv.messager.GenerateLoadMessagesQuery(tableName) - if err != nil { - return nil, err - } - readback, err := qre.txFetch(conn, loadMessages, qre.bindVars, extras, "", true, false) - if err != nil { - return nil, err - } - - // Append to the list of pending rows to be sent - // to the cache on successful commit. - mrs := conn.NewMessages[tableName] - for _, row := range readback.Rows { - mr, err := messager.BuildMessageRow(row) - if err != nil { - return nil, err - } - mrs = append(mrs, mr) - } - conn.NewMessages[tableName] = mrs - return qr, nil + return qre.execInsertPKRows(conn, nil, pkRows) } func (qre *QueryExecutor) execInsertSubquery(conn *TxConnection) (*sqltypes.Result, error) { @@ -775,13 +720,6 @@ func (qre *QueryExecutor) execDMLPKRows(conn *TxConnection, query *sqlparser.Par // DMLs should all return RowsAffected. result.RowsAffected += r.RowsAffected } - if qre.plan.Table.Type == schema.Message { - ids := conn.ChangedMessages[qre.plan.Table.Name.String()] - for _, pkrow := range pkRows { - ids = append(ids, pkrow[qre.plan.Table.MessageInfo.IDPKIndex].ToString()) - } - conn.ChangedMessages[qre.plan.Table.Name.String()] = ids - } return result, nil } diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index 0d9a35a9f54..454ae05c29f 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -41,6 +41,10 @@ var ( // StatsLogger is the main stream logger object StatsLogger = streamlog.New("TabletServer", 50) + + // Placeholder for deprecated variable. + // TODO(sougou): deprecate the flag after release 7.0. + deprecatedMessagePoolPrefillParallelism int ) func init() { @@ -49,7 +53,7 @@ func init() { flag.IntVar(&Config.StreamPoolSize, "queryserver-config-stream-pool-size", DefaultQsConfig.StreamPoolSize, "query server stream connection pool size, stream pool is used by stream queries: queries that return results to client in a streaming fashion") flag.IntVar(&Config.StreamPoolPrefillParallelism, "queryserver-config-stream-pool-prefill-parallelism", DefaultQsConfig.StreamPoolPrefillParallelism, "query server stream pool prefill parallelism, a non-zero value will prefill the pool using the specified parallelism") flag.IntVar(&Config.MessagePoolSize, "queryserver-config-message-conn-pool-size", DefaultQsConfig.MessagePoolSize, "query server message connection pool size, message pool is used by message managers: recommended value is one per message table") - flag.IntVar(&Config.MessagePoolPrefillParallelism, "queryserver-config-message-conn-pool-prefill-parallelism", DefaultQsConfig.MessagePoolPrefillParallelism, "query server message pool prefill parallelism, a non-zero value will prefill the pool using the specified parallelism") + flag.IntVar(&deprecatedMessagePoolPrefillParallelism, "queryserver-config-message-conn-pool-prefill-parallelism", 0, "DEPRECATED: Unused.") flag.IntVar(&Config.TransactionCap, "queryserver-config-transaction-cap", DefaultQsConfig.TransactionCap, "query server transaction cap is the maximum number of transactions allowed to happen at any given point of a time for a single vttablet. E.g. by setting transaction cap to 100, there are at most 100 transactions will be processed by a vttablet and the 101th transaction will be blocked (and fail if it cannot get connection within specified timeout)") flag.IntVar(&Config.TxPoolPrefillParallelism, "queryserver-config-transaction-prefill-parallelism", DefaultQsConfig.TxPoolPrefillParallelism, "query server transaction prefill parallelism, a non-zero value will prefill the pool using the specified parallism.") flag.IntVar(&Config.MessagePostponeCap, "queryserver-config-message-postpone-cap", DefaultQsConfig.MessagePostponeCap, "query server message postpone cap is the maximum number of messages that can be postponed at any given time. Set this number to substantially lower than transaction cap, so that the transaction pool isn't exhausted by the message subsystem.") @@ -129,42 +133,41 @@ func Init() { // TabletConfig contains all the configuration for query service type TabletConfig struct { - PoolSize int - PoolPrefillParallelism int - StreamPoolSize int - StreamPoolPrefillParallelism int - MessagePoolSize int - MessagePoolPrefillParallelism int - TransactionCap int - MessagePostponeCap int - FoundRowsPoolSize int - TxPoolPrefillParallelism int - TransactionTimeout float64 - TxShutDownGracePeriod float64 - MaxResultSize int - WarnResultSize int - MaxDMLRows int - PassthroughDMLs bool - AllowUnsafeDMLs bool - StreamBufferSize int - QueryPlanCacheSize int - SchemaReloadTime float64 - QueryTimeout float64 - QueryPoolTimeout float64 - TxPoolTimeout float64 - IdleTimeout float64 - QueryPoolWaiterCap int - TxPoolWaiterCap int - StrictTableACL bool - TerseErrors bool - EnableAutoCommit bool - EnableTableACLDryRun bool - PoolNamePrefix string - TableACLExemptACL string - WatchReplication bool - TwoPCEnable bool - TwoPCCoordinatorAddress string - TwoPCAbandonAge float64 + PoolSize int + PoolPrefillParallelism int + StreamPoolSize int + StreamPoolPrefillParallelism int + MessagePoolSize int + TransactionCap int + MessagePostponeCap int + FoundRowsPoolSize int + TxPoolPrefillParallelism int + TransactionTimeout float64 + TxShutDownGracePeriod float64 + MaxResultSize int + WarnResultSize int + MaxDMLRows int + PassthroughDMLs bool + AllowUnsafeDMLs bool + StreamBufferSize int + QueryPlanCacheSize int + SchemaReloadTime float64 + QueryTimeout float64 + QueryPoolTimeout float64 + TxPoolTimeout float64 + IdleTimeout float64 + QueryPoolWaiterCap int + TxPoolWaiterCap int + StrictTableACL bool + TerseErrors bool + EnableAutoCommit bool + EnableTableACLDryRun bool + PoolNamePrefix string + TableACLExemptACL string + WatchReplication bool + TwoPCEnable bool + TwoPCCoordinatorAddress string + TwoPCAbandonAge float64 EnableTxThrottler bool TxThrottlerConfig string @@ -207,42 +210,41 @@ type TransactionLimitConfig struct { // great (the overhead makes the final packets on the wire about twice // bigger than this). var DefaultQsConfig = TabletConfig{ - PoolSize: 16, - PoolPrefillParallelism: 0, - StreamPoolSize: 200, - StreamPoolPrefillParallelism: 0, - MessagePoolSize: 5, - MessagePoolPrefillParallelism: 0, - TransactionCap: 20, - MessagePostponeCap: 4, - FoundRowsPoolSize: 20, - TxPoolPrefillParallelism: 0, - TransactionTimeout: 30, - TxShutDownGracePeriod: 0, - MaxResultSize: 10000, - WarnResultSize: 0, - MaxDMLRows: 500, - PassthroughDMLs: false, - AllowUnsafeDMLs: false, - QueryPlanCacheSize: 5000, - SchemaReloadTime: 30 * 60, - QueryTimeout: 30, - QueryPoolTimeout: 0, - TxPoolTimeout: 1, - IdleTimeout: 30 * 60, - QueryPoolWaiterCap: 50000, - TxPoolWaiterCap: 50000, - StreamBufferSize: 32 * 1024, - StrictTableACL: false, - TerseErrors: false, - EnableAutoCommit: false, - EnableTableACLDryRun: false, - PoolNamePrefix: "", - TableACLExemptACL: "", - WatchReplication: false, - TwoPCEnable: false, - TwoPCCoordinatorAddress: "", - TwoPCAbandonAge: 0, + PoolSize: 16, + PoolPrefillParallelism: 0, + StreamPoolSize: 200, + StreamPoolPrefillParallelism: 0, + MessagePoolSize: 5, + TransactionCap: 20, + MessagePostponeCap: 4, + FoundRowsPoolSize: 20, + TxPoolPrefillParallelism: 0, + TransactionTimeout: 30, + TxShutDownGracePeriod: 0, + MaxResultSize: 10000, + WarnResultSize: 0, + MaxDMLRows: 500, + PassthroughDMLs: false, + AllowUnsafeDMLs: false, + QueryPlanCacheSize: 5000, + SchemaReloadTime: 30 * 60, + QueryTimeout: 30, + QueryPoolTimeout: 0, + TxPoolTimeout: 1, + IdleTimeout: 30 * 60, + QueryPoolWaiterCap: 50000, + TxPoolWaiterCap: 50000, + StreamBufferSize: 32 * 1024, + StrictTableACL: false, + TerseErrors: false, + EnableAutoCommit: false, + EnableTableACLDryRun: false, + PoolNamePrefix: "", + TableACLExemptACL: "", + WatchReplication: false, + TwoPCEnable: false, + TwoPCCoordinatorAddress: "", + TwoPCAbandonAge: 0, EnableTxThrottler: false, TxThrottlerConfig: defaultTxThrottlerConfig(), diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 8eb0a47adc8..48ba8345369 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -245,7 +245,7 @@ type TxPoolController interface { // Commit commits the specified transaction, returning the statement used to execute // the commit or "" in autocommit settings. - Commit(ctx context.Context, transactionID int64, mc messageCommitter) (string, error) + Commit(ctx context.Context, transactionID int64) (string, error) // Rollback rolls back the specified transaction. Rollback(ctx context.Context, transactionID int64) error @@ -394,7 +394,6 @@ func (tsv *TabletServer) InitDBConfig(target querypb.Target, dbcfgs *dbconfigs.D tsv.hr.InitDBConfig(tsv.dbconfigs) tsv.watcher.InitDBConfig(tsv.dbconfigs) tsv.vstreamer.InitDBConfig(tsv.dbconfigs.DbaWithDB()) - tsv.messager.InitDBConfig(tsv.dbconfigs) return nil } @@ -811,7 +810,7 @@ func (tsv *TabletServer) Commit(ctx context.Context, target *querypb.Target, tra logStats.TransactionID = transactionID var commitSQL string - commitSQL, err = tsv.teCtrl.Commit(ctx, transactionID, tsv.messager) + commitSQL, err = tsv.teCtrl.Commit(ctx, transactionID) // If nothing was actually executed, don't count the operation in // the tablet metrics, and clear out the logStats Method so that @@ -851,7 +850,6 @@ func (tsv *TabletServer) Prepare(ctx context.Context, target *querypb.Target, tr ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } return txe.Prepare(transactionID, dtid) }, @@ -869,7 +867,6 @@ func (tsv *TabletServer) CommitPrepared(ctx context.Context, target *querypb.Tar ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } return txe.CommitPrepared(dtid) }, @@ -887,7 +884,6 @@ func (tsv *TabletServer) RollbackPrepared(ctx context.Context, target *querypb.T ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } return txe.RollbackPrepared(dtid, originalID) }, @@ -905,7 +901,6 @@ func (tsv *TabletServer) CreateTransaction(ctx context.Context, target *querypb. ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } return txe.CreateTransaction(dtid, participants) }, @@ -924,7 +919,6 @@ func (tsv *TabletServer) StartCommit(ctx context.Context, target *querypb.Target ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } return txe.StartCommit(transactionID, dtid) }, @@ -943,7 +937,6 @@ func (tsv *TabletServer) SetRollback(ctx context.Context, target *querypb.Target ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } return txe.SetRollback(dtid, transactionID) }, @@ -962,7 +955,6 @@ func (tsv *TabletServer) ConcludeTransaction(ctx context.Context, target *queryp ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } return txe.ConcludeTransaction(dtid) }, @@ -980,7 +972,6 @@ func (tsv *TabletServer) ReadTransaction(ctx context.Context, target *querypb.Ta ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, } metadata, err = txe.ReadTransaction(dtid) return err @@ -2138,7 +2129,6 @@ func (tsv *TabletServer) registerTwopczHandler() { ctx: ctx, logStats: tabletenv.NewLogStats(ctx, "twopcz"), te: tsv.te, - messager: tsv.messager, } twopczHandler(txe, w, r) }) diff --git a/go/vt/vttablet/tabletserver/tx_engine.go b/go/vt/vttablet/tabletserver/tx_engine.go index 7b663df5899..e467b6b18fa 100644 --- a/go/vt/vttablet/tabletserver/tx_engine.go +++ b/go/vt/vttablet/tabletserver/tx_engine.go @@ -299,10 +299,10 @@ func (te *TxEngine) Begin(ctx context.Context, options *querypb.ExecuteOptions) } // Commit commits the specified transaction. -func (te *TxEngine) Commit(ctx context.Context, transactionID int64, mc messageCommitter) (string, error) { +func (te *TxEngine) Commit(ctx context.Context, transactionID int64) (string, error) { span, ctx := trace.NewSpan(ctx, "TxEngine.Commit") defer span.Finish() - return te.txPool.Commit(ctx, transactionID, mc) + return te.txPool.Commit(ctx, transactionID) } // Rollback rolls back the specified transaction. diff --git a/go/vt/vttablet/tabletserver/tx_executor.go b/go/vt/vttablet/tabletserver/tx_executor.go index 69d3018c97f..31b9f283203 100644 --- a/go/vt/vttablet/tabletserver/tx_executor.go +++ b/go/vt/vttablet/tabletserver/tx_executor.go @@ -27,7 +27,6 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/messager" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) @@ -37,7 +36,6 @@ type TxExecutor struct { ctx context.Context logStats *tabletenv.LogStats te *TxEngine - messager *messager.Engine } // Prepare performs a prepare on a connection including the redo log work. @@ -79,7 +77,7 @@ func (txe *TxExecutor) Prepare(transactionID int64, dtid string) error { return err } - _, err = txe.te.txPool.LocalCommit(txe.ctx, localConn, txe.messager) + _, err = txe.te.txPool.LocalCommit(txe.ctx, localConn) if err != nil { return err } @@ -111,7 +109,7 @@ func (txe *TxExecutor) CommitPrepared(dtid string) error { txe.markFailed(ctx, dtid) return err } - _, err = txe.te.txPool.LocalCommit(ctx, conn, txe.messager) + _, err = txe.te.txPool.LocalCommit(ctx, conn) if err != nil { txe.markFailed(ctx, dtid) return err @@ -142,7 +140,7 @@ func (txe *TxExecutor) markFailed(ctx context.Context, dtid string) { return } - if _, err = txe.te.txPool.LocalCommit(ctx, conn, txe.messager); err != nil { + if _, err = txe.te.txPool.LocalCommit(ctx, conn); err != nil { log.Errorf("markFailed: Commit failed for dtid %s: %v", dtid, err) } } @@ -181,7 +179,7 @@ func (txe *TxExecutor) RollbackPrepared(dtid string, originalID int64) error { goto returnConn } - _, err = txe.te.txPool.LocalCommit(txe.ctx, conn, txe.messager) + _, err = txe.te.txPool.LocalCommit(txe.ctx, conn) returnConn: if preparedConn := txe.te.preparedPool.FetchForRollback(dtid); preparedConn != nil { @@ -210,7 +208,7 @@ func (txe *TxExecutor) CreateTransaction(dtid string, participants []*querypb.Ta if err != nil { return err } - _, err = txe.te.txPool.LocalCommit(txe.ctx, conn, txe.messager) + _, err = txe.te.txPool.LocalCommit(txe.ctx, conn) return err } @@ -233,7 +231,7 @@ func (txe *TxExecutor) StartCommit(transactionID int64, dtid string) error { if err != nil { return err } - _, err = txe.te.txPool.LocalCommit(txe.ctx, conn, txe.messager) + _, err = txe.te.txPool.LocalCommit(txe.ctx, conn) return err } @@ -261,7 +259,7 @@ func (txe *TxExecutor) SetRollback(dtid string, transactionID int64) error { return err } - _, err = txe.te.txPool.LocalCommit(txe.ctx, conn, txe.messager) + _, err = txe.te.txPool.LocalCommit(txe.ctx, conn) if err != nil { return err } @@ -287,7 +285,7 @@ func (txe *TxExecutor) ConcludeTransaction(dtid string) error { if err != nil { return err } - _, err = txe.te.txPool.LocalCommit(txe.ctx, conn, txe.messager) + _, err = txe.te.txPool.LocalCommit(txe.ctx, conn) return err } diff --git a/go/vt/vttablet/tabletserver/tx_pool.go b/go/vt/vttablet/tabletserver/tx_pool.go index 6c050a5a002..1bb5723bc9d 100644 --- a/go/vt/vttablet/tabletserver/tx_pool.go +++ b/go/vt/vttablet/tabletserver/tx_pool.go @@ -37,7 +37,6 @@ import ( "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" - "vitess.io/vitess/go/vt/vttablet/tabletserver/messager" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/txlimiter" @@ -75,11 +74,6 @@ var ( } ) -type messageCommitter interface { - UpdateCaches(newMessages map[string][]*messager.MessageRow, changedMessages map[string][]string) - LockDB(newMessages map[string][]*messager.MessageRow, changedMessages map[string][]string) func() -} - // TxPool is the transaction pool for the query service. type TxPool struct { // conns is the 'regular' pool. By default, connections @@ -296,14 +290,14 @@ func (axp *TxPool) Begin(ctx context.Context, options *querypb.ExecuteOptions) ( } // Commit commits the specified transaction. -func (axp *TxPool) Commit(ctx context.Context, transactionID int64, mc messageCommitter) (string, error) { +func (axp *TxPool) Commit(ctx context.Context, transactionID int64) (string, error) { span, ctx := trace.NewSpan(ctx, "TxPool.Commit") defer span.Finish() conn, err := axp.Get(transactionID, "for commit") if err != nil { return "", err } - return axp.LocalCommit(ctx, conn, mc) + return axp.LocalCommit(ctx, conn) } // Rollback rolls back the specified transaction. @@ -344,14 +338,12 @@ func (axp *TxPool) LocalBegin(ctx context.Context, options *querypb.ExecuteOptio } // LocalCommit is the commit function for LocalBegin. -func (axp *TxPool) LocalCommit(ctx context.Context, conn *TxConnection, mc messageCommitter) (string, error) { +func (axp *TxPool) LocalCommit(ctx context.Context, conn *TxConnection) (string, error) { span, ctx := trace.NewSpan(ctx, "TxPool.LocalCommit") defer span.Finish() defer conn.conclude(TxCommit, "transaction committed") - defer mc.LockDB(conn.NewMessages, conn.ChangedMessages)() if conn.Autocommit { - mc.UpdateCaches(conn.NewMessages, conn.ChangedMessages) return "", nil } @@ -359,7 +351,6 @@ func (axp *TxPool) LocalCommit(ctx context.Context, conn *TxConnection, mc messa conn.Close() return "", err } - mc.UpdateCaches(conn.NewMessages, conn.ChangedMessages) return "commit", nil } @@ -428,8 +419,6 @@ type TxConnection struct { StartTime time.Time EndTime time.Time Queries []string - NewMessages map[string][]*messager.MessageRow - ChangedMessages map[string][]string Conclusion string LogToFile sync2.AtomicInt32 ImmediateCallerID *querypb.VTGateCallerID @@ -443,8 +432,6 @@ func newTxConnection(conn *connpool.DBConn, transactionID int64, pool *TxPool, i TransactionID: transactionID, pool: pool, StartTime: time.Now(), - NewMessages: make(map[string][]*messager.MessageRow), - ChangedMessages: make(map[string][]string), ImmediateCallerID: immediate, EffectiveCallerID: effective, Autocommit: autocommit, From d1fd2b0a228796a90c8311f07ae371a49e2598a1 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 5 Mar 2020 22:26:43 -0800 Subject: [PATCH 259/825] messager: make tests pass Signed-off-by: Sugu Sougoumarane --- .../tabletserver/messager/engine_test.go | 8 +- .../tabletserver/messager/message_manager.go | 26 +- .../messager/message_manager_test.go | 313 ++++++++---------- 3 files changed, 138 insertions(+), 209 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index 945eec89efb..caa954507d2 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -26,7 +26,6 @@ import ( "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -35,7 +34,7 @@ import ( var meTable = &schema.Table{ Type: schema.Message, - MessageInfo: mmTable.MessageInfo, + MessageInfo: newMMTable().MessageInfo, } func TestEngineSchemaChanged(t *testing.T) { @@ -165,10 +164,7 @@ func newTestEngine(db *fakesqldb.DB) *Engine { config.PoolNamePrefix = fmt.Sprintf("Pool-%d-", randID) tsv := newFakeTabletServer() se := schema.NewEngine(tsv, config) - te := NewEngine(tsv, se, config) - params, _ := db.ConnParams().MysqlParams() - cp := *params - te.InitDBConfig(dbconfigs.NewTestDBConfigs(cp, cp, "")) + te := NewEngine(tsv, se, newFakeVStreamer(), config) te.Open() return te } diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index e80545124f1..2a2614dc743 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -123,9 +123,7 @@ type receiverWithStatus struct { // Each addition makes sure the send loop is awakened if it's waiting. // The send loop continuously pulls items out of the cache and sends them. // After every successful send, the messages are postponed, and are -// also removed from the cache. If a send failed, then the messages are -// just removed from the cache. This triggers the messagesPending state explained -// below. +// also removed from the cache. // The poller wakes up periodically, loads messages that are due and adds them // to the cache. Most of these items are likely to be those that did not // receive a timely ack. @@ -134,10 +132,9 @@ type receiverWithStatus struct { // This mode is a variation of the steady state mode. This mode is // entered when there are outstanding items in the database that need to be sent // but are not present in the cache. This state can be entered in one -// of three ways: +// of two ways: // 1. The poller read returns as many rows as the cache size -// 2. The Add of a message fails (cache full). -// 3. A send failed that caused a message to be discarded without postponement. +// 2. The Add of a message fails (cache full). This is invoked from the vstreamer. // In any of the above cases, the messagesPending flag gets turned on. // In this phase, the send loop proactively wakes up the poller every time // it clears the cache. @@ -181,8 +178,6 @@ type messageManager struct { isOpen bool // cond waits on curReceiver == -1 || cache.IsEmpty(): // No current receivers available or cache is empty. - // Also, messagesPending && cache.IsEmpty() should trigger - // the poller to fetch more messages from the database. cond sync.Cond cache *cache receivers []*receiverWithStatus @@ -526,21 +521,6 @@ func (mm *messageManager) send(receiver *receiverWithStatus, qr *sqltypes.Result }() if err := receiver.receiver.Send(qr); err != nil { - if err == io.EOF { - // If the receiver ended the stream, we do not postpone the message. - // Instead, we mark messagesPending. If the cache is already empty - // then we have to trigger the poller. Otherwise, it will get - // trigerred by runSend when it goes empty. - mm.mu.Lock() - mm.messagesPending = true - // Although messagesPending is not part of the condition, - // we broadcast to make it break out and trigger the poller - // to load more messages, if necessary. - mm.cond.Broadcast() - mm.mu.Unlock() - return - } - // Log the error, but we still want to postpone the message. // Otherwise, if this is a chronic failure like "message too // big", we'll end up spamming non-stop. diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index bd017b595b2..16e2ece9e3e 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -24,16 +24,16 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "golang.org/x/net/context" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/sync2" - "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" ) @@ -48,8 +48,11 @@ var ( Name: "message", Type: sqltypes.VarBinary, }} +) - mmTable = &schema.Table{ +// newMMTable is not a true copy, but enough to massage what we need. +func newMMTable() *schema.Table { + return &schema.Table{ Name: sqlparser.NewTableIdent("foo"), Type: schema.Message, MessageInfo: &schema.MessageInfo{ @@ -61,14 +64,6 @@ var ( PollInterval: 1 * time.Second, }, } -) - -// newMMTable is not a true copy, but enough to massage what we need. -func newMMTable() *schema.Table { - ti := *mmTable - msg := *ti.MessageInfo - ti.MessageInfo = &msg - return &ti } type testReceiver struct { @@ -102,7 +97,7 @@ func (tr *testReceiver) WaitForCount(n int) { func TestReceiverCancel(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - mm := newMessageManager(newFakeTabletServer(), mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -115,12 +110,9 @@ func TestReceiverCancel(t *testing.T) { for i := 0; i < 10; i++ { runtime.Gosched() time.Sleep(10 * time.Millisecond) - mm.mu.Lock() - if len(mm.receivers) != 0 { - mm.mu.Unlock() + if mm.receiverCount() != 0 { continue } - mm.mu.Unlock() return } t.Errorf("receivers were not cleared: %d", len(mm.receivers)) @@ -129,7 +121,7 @@ func TestReceiverCancel(t *testing.T) { func TestMessageManagerState(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - mm := newMessageManager(newFakeTabletServer(), mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) // Do it twice for i := 0; i < 2; i++ { mm.Open() @@ -149,7 +141,7 @@ func TestMessageManagerAdd(t *testing.T) { defer db.Close() ti := newMMTable() ti.MessageInfo.CacheSize = 1 - mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), ti, sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -182,7 +174,7 @@ func TestMessageManagerSend(t *testing.T) { db := fakesqldb.New(t) defer db.Close() tsv := newFakeTabletServer() - mm := newMessageManager(tsv, mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -275,7 +267,7 @@ func TestMessageManagerPostponeThrottle(t *testing.T) { db := fakesqldb.New(t) defer db.Close() tsv := newFakeTabletServer() - mm := newMessageManager(tsv, mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -321,69 +313,11 @@ func TestMessageManagerPostponeThrottle(t *testing.T) { <-ch } -func TestMessageManagerSendEOF(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - db.AddQueryPattern( - "select time_next, epoch, time_created, id, time_scheduled, message from foo.*", - &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.VarBinary}, - }, - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(2), - sqltypes.NewInt64(10), - sqltypes.NewVarBinary("a"), - }}, - }, - ) - // Set a large polling interval. - ti := newMMTable() - ti.MessageInfo.CacheSize = 2 - ti.MessageInfo.PollInterval = 30 * time.Second - mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) - mm.Open() - defer mm.Close() - - ctx, cancel := context.WithCancel(context.Background()) - - r1 := newTestReceiver(0) - go func() { <-r1.ch }() - mm.Subscribe(ctx, r1.rcv) - - r2 := newTestReceiver(0) - go func() { <-r2.ch }() - mm.Subscribe(context.Background(), r2.rcv) - - mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("1"), sqltypes.NULL}}) - // Wait for send to enqueue. - // After this is enquened, runSend will go into a wait state because - // there's nothing more to send. - r1.WaitForCount(2) - - // Now cancel, which will send an EOF to the sender. - cancel() - - // The EOF should immediately trigger the poller, which will result - // in a message being sent on r2. Verify that this happened in a timely fashion. - start := time.Now() - <-r2.ch - if d := time.Since(start); d > 15*time.Second { - t.Errorf("pending work trigger did not happen. Duration: %v", d) - } -} - func TestMessageManagerSendError(t *testing.T) { db := fakesqldb.New(t) defer db.Close() tsv := newFakeTabletServer() - mm := newMessageManager(tsv, mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() ctx := context.Background() @@ -397,7 +331,7 @@ func TestMessageManagerSendError(t *testing.T) { fieldSent = true return nil } - return errors.New("non-eof") + return errors.New("intentional error") }) postponech := make(chan string, 20) @@ -415,7 +349,7 @@ func TestMessageManagerFieldSendError(t *testing.T) { db := fakesqldb.New(t) defer db.Close() tsv := newFakeTabletServer() - mm := newMessageManager(tsv, mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() ctx := context.Background() @@ -437,7 +371,7 @@ func TestMessageManagerBatchSend(t *testing.T) { defer db.Close() ti := newMMTable() ti.MessageInfo.BatchSize = 2 - mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), ti, sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -480,45 +414,46 @@ func TestMessageManagerBatchSend(t *testing.T) { func TestMessageManagerPoller(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - db.AddQueryPattern( - "select time_next, epoch, time_created, id, time_scheduled, message from foo.*", - &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.VarBinary}, - }, - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(0), - sqltypes.NewInt64(1), - sqltypes.NewInt64(10), - sqltypes.NewVarBinary("01"), - }, { - sqltypes.NewInt64(2), - sqltypes.NewInt64(0), - sqltypes.NewInt64(1), - sqltypes.NewInt64(2), - sqltypes.NewInt64(20), - sqltypes.NewVarBinary("02"), - }, { - sqltypes.NewInt64(1), - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(3), - sqltypes.NewInt64(30), - sqltypes.NewVarBinary("11"), - }}, - }, - ) ti := newMMTable() ti.MessageInfo.BatchSize = 2 ti.MessageInfo.PollInterval = 20 * time.Second - mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + fvs := newFakeVStreamer() + fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ + Fields: []*querypb.Field{ + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.VarBinary}, + }, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + }, { + Rows: sqltypes.RowsToProto3([][]sqltypes.Value{{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(0), + sqltypes.NewInt64(0), + sqltypes.NewInt64(1), + sqltypes.NewInt64(10), + sqltypes.NewVarBinary("01"), + }, { + sqltypes.NewInt64(2), + sqltypes.NewInt64(0), + sqltypes.NewInt64(1), + sqltypes.NewInt64(2), + sqltypes.NewInt64(20), + sqltypes.NewVarBinary("02"), + }, { + sqltypes.NewInt64(1), + sqltypes.NewInt64(1), + sqltypes.NewInt64(0), + sqltypes.NewInt64(3), + sqltypes.NewInt64(30), + sqltypes.NewVarBinary("11"), + }}, + ), + }}) + mm := newMessageManager(newFakeTabletServer(), fvs, ti, sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -565,30 +500,12 @@ func TestMessageManagerPoller(t *testing.T) { func TestMessagesPending1(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - db.AddQueryPattern( - "select time_next, epoch, time_created, id, time_scheduled, message from foo.*", - &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.VarBinary}, - }, - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(2), - sqltypes.NewInt64(10), - sqltypes.NewVarBinary("a"), - }}, - }, - ) // Set a large polling interval. ti := newMMTable() ti.MessageInfo.CacheSize = 2 ti.MessageInfo.PollInterval = 30 * time.Second - mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + fvs := newFakeVStreamer() + mm := newMessageManager(newFakeTabletServer(), fvs, ti, sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -600,19 +517,32 @@ func TestMessagesPending1(t *testing.T) { // Make sure the first message is enqueued. r1.WaitForCount(2) // This will fill up the cache. - mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("2")}}) - mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("3")}}) - - // Wait for pending flag to be turned on. - for { - runtime.Gosched() - mm.mu.Lock() - if mm.messagesPending { - mm.mu.Unlock() - break - } - mm.mu.Unlock() - } + assert.True(t, mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("2")}})) + assert.True(t, mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("3")}})) + // This will fail and messagesPending will be set to true. + assert.False(t, mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("4")}})) + + fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ + Fields: []*querypb.Field{ + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.VarBinary}, + }, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + }, { + Rows: sqltypes.RowsToProto3([][]sqltypes.Value{{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(0), + sqltypes.NewInt64(0), + sqltypes.NewInt64(5), + sqltypes.NewInt64(10), + sqltypes.NewVarBinary("01"), + }}, + ), + }}) // Now, let's pull more than 3 items. It should // trigger the poller, and there should be no wait. @@ -630,30 +560,33 @@ func TestMessagesPending1(t *testing.T) { func TestMessagesPending2(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - db.AddQueryPattern( - "select time_next, epoch, time_created, id, time_scheduled, message from foo.*", - &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.VarBinary}, - }, - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(2), - sqltypes.NewInt64(10), - sqltypes.NewVarBinary("a"), - }}, - }, - ) // Set a large polling interval. ti := newMMTable() ti.MessageInfo.CacheSize = 1 ti.MessageInfo.PollInterval = 30 * time.Second - mm := newMessageManager(newFakeTabletServer(), ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + fvs := newFakeVStreamer() + fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ + Fields: []*querypb.Field{ + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.VarBinary}, + }, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + }, { + Rows: sqltypes.RowsToProto3([][]sqltypes.Value{{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(0), + sqltypes.NewInt64(0), + sqltypes.NewInt64(5), + sqltypes.NewInt64(10), + sqltypes.NewVarBinary("01"), + }}, + ), + }}) + mm := newMessageManager(newFakeTabletServer(), fvs, ti, sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -683,7 +616,7 @@ func TestMessageManagerPurge(t *testing.T) { ti := newMMTable() ti.MessageInfo.PollInterval = 1 * time.Millisecond - mm := newMessageManager(tsv, ti, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(tsv, newFakeVStreamer(), ti, sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() // Ensure Purge got called. @@ -695,7 +628,7 @@ func TestMessageManagerPurge(t *testing.T) { func TestMMGenerate(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - mm := newMessageManager(newFakeTabletServer(), mmTable, newMMConnPool(db), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() query, bv := mm.GenerateAckQuery([]string{"1", "2"}) @@ -757,6 +690,8 @@ type fakeTabletServer struct { func newFakeTabletServer() *fakeTabletServer { return &fakeTabletServer{} } +func (fts *fakeTabletServer) CheckMySQL() {} + func (fts *fakeTabletServer) SetChannel(ch chan string) { fts.mu.Lock() fts.ch = ch @@ -785,12 +720,30 @@ func (fts *fakeTabletServer) PurgeMessages(ctx context.Context, target *querypb. return 0, nil } -func newMMConnPool(db *fakesqldb.DB) *connpool.Pool { - pool := connpool.New("", 20, 0, time.Duration(10*time.Minute), newFakeTabletServer()) - params, _ := db.ConnParams().MysqlParams() - cp := *params - dbconfigs := dbconfigs.NewTestDBConfigs(cp, cp, "") +type fakeVStreamer struct { + mu sync.Mutex + pollerResponse []*binlogdatapb.VStreamResultsResponse +} + +func newFakeVStreamer() *fakeVStreamer { return &fakeVStreamer{} } + +func (fv *fakeVStreamer) setPollerResponse(pr []*binlogdatapb.VStreamResultsResponse) { + fv.mu.Lock() + defer fv.mu.Unlock() + fv.pollerResponse = pr +} - pool.Open(dbconfigs.AppWithDB(), dbconfigs.DbaWithDB(), dbconfigs.AppDebugWithDB()) - return pool +func (fv *fakeVStreamer) Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { + return nil +} + +func (fv *fakeVStreamer) StreamResults(ctx context.Context, query string, send func(*binlogdatapb.VStreamResultsResponse) error) error { + fv.mu.Lock() + defer fv.mu.Unlock() + for _, r := range fv.pollerResponse { + if err := send(r); err != nil { + return err + } + } + return nil } From a7409284f901f1332fcd8d67aa990f7648cf5882 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 6 Mar 2020 20:07:57 -0800 Subject: [PATCH 260/825] messager: full unit tests Signed-off-by: Sugu Sougoumarane --- .../messager/message_manager_test.go | 363 ++++++++++++------ 1 file changed, 238 insertions(+), 125 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index 16e2ece9e3e..ca62c67cbc8 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -18,6 +18,8 @@ package messager import ( "errors" + "fmt" + "io" "reflect" "runtime" "sync" @@ -27,7 +29,6 @@ import ( "github.com/stretchr/testify/assert" "golang.org/x/net/context" - "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/sync2" "vitess.io/vitess/go/vt/sqlparser" @@ -48,9 +49,17 @@ var ( Name: "message", Type: sqltypes.VarBinary, }} + + testDBFields = []*querypb.Field{ + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.Int64}, + {Type: sqltypes.VarBinary}, + } ) -// newMMTable is not a true copy, but enough to massage what we need. func newMMTable() *schema.Table { return &schema.Table{ Name: sqlparser.NewTableIdent("foo"), @@ -66,6 +75,17 @@ func newMMTable() *schema.Table { } } +func newMMRow(id int64) *querypb.Row { + return sqltypes.RowToProto3([]sqltypes.Value{ + sqltypes.NewInt64(1), + sqltypes.NewInt64(0), + sqltypes.NewInt64(0), + sqltypes.NewInt64(id), + sqltypes.NewInt64(id * 10), + sqltypes.NewVarBinary(fmt.Sprintf("%v", id)), + }) +} + type testReceiver struct { rcv func(*sqltypes.Result) error count sync2.AtomicInt64 @@ -95,8 +115,6 @@ func (tr *testReceiver) WaitForCount(n int) { } func TestReceiverCancel(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -119,8 +137,6 @@ func TestReceiverCancel(t *testing.T) { } func TestMessageManagerState(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) // Do it twice for i := 0; i < 2; i++ { @@ -137,8 +153,6 @@ func TestMessageManagerState(t *testing.T) { } func TestMessageManagerAdd(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() ti := newMMTable() ti.MessageInfo.CacheSize = 1 mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), ti, sync2.NewSemaphore(1, 0)) @@ -171,8 +185,6 @@ func TestMessageManagerAdd(t *testing.T) { } func TestMessageManagerSend(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() tsv := newFakeTabletServer() mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() @@ -207,26 +219,29 @@ func TestMessageManagerSend(t *testing.T) { t.Errorf("Postpone: %s, want %v", got, want) } - // Wait while the receiver is marked as busy. - for { - mm.mu.Lock() - busy := mm.receivers[0].busy - mm.mu.Unlock() - if !busy { - break - } - } - // Verify item has been removed from cache. // Need to obtain lock to prevent data race. - mm.cache.mu.Lock() - if _, ok := mm.cache.inQueue["1"]; ok { - t.Error("Message 1 is still present in inQueue cache") - } - if _, ok := mm.cache.inFlight["1"]; ok { - t.Error("Message 1 is still present in inFlight cache") + // It may take some time for this to happen. + inQueue := true + inFlight := true + for i := 0; i < 10; i++ { + mm.cache.mu.Lock() + if _, ok := mm.cache.inQueue["1"]; !ok { + inQueue = false + } + if _, ok := mm.cache.inFlight["1"]; !ok { + inFlight = false + } + mm.cache.mu.Unlock() + if inQueue || inFlight { + runtime.Gosched() + time.Sleep(10 * time.Millisecond) + continue + } + break } - mm.cache.mu.Unlock() + assert.False(t, inQueue) + assert.False(t, inFlight) // Test that mm stops sending to a canceled receiver. r2 := newTestReceiver(1) @@ -264,8 +279,6 @@ func TestMessageManagerSend(t *testing.T) { } func TestMessageManagerPostponeThrottle(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() tsv := newFakeTabletServer() mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() @@ -314,8 +327,6 @@ func TestMessageManagerPostponeThrottle(t *testing.T) { } func TestMessageManagerSendError(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() tsv := newFakeTabletServer() mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() @@ -346,10 +357,7 @@ func TestMessageManagerSendError(t *testing.T) { } func TestMessageManagerFieldSendError(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - tsv := newFakeTabletServer() - mm := newMessageManager(tsv, newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) + mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() ctx := context.Background() @@ -367,8 +375,6 @@ func TestMessageManagerFieldSendError(t *testing.T) { } func TestMessageManagerBatchSend(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() ti := newMMTable() ti.MessageInfo.BatchSize = 2 mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), ti, sync2.NewSemaphore(1, 0)) @@ -411,47 +417,159 @@ func TestMessageManagerBatchSend(t *testing.T) { } } -func TestMessageManagerPoller(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - ti := newMMTable() - ti.MessageInfo.BatchSize = 2 - ti.MessageInfo.PollInterval = 20 * time.Second +func TestMessageManagerStreamerSimple(t *testing.T) { fvs := newFakeVStreamer() - fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.VarBinary}, - }, + fvs.setStreamerResponse([][]*binlogdatapb.VEvent{{{ + // Event set 1. + Type: binlogdatapb.VEventType_GTID, Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", }, { - Rows: sqltypes.RowsToProto3([][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(0), + Type: binlogdatapb.VEventType_OTHER, + }}, {{ + // Event set 2. + Type: binlogdatapb.VEventType_FIELD, + FieldEvent: &binlogdatapb.FieldEvent{ + TableName: "foo", + Fields: testDBFields, + }, + }}, {{ + // Event set 3. + Type: binlogdatapb.VEventType_ROW, + RowEvent: &binlogdatapb.RowEvent{ + TableName: "foo", + RowChanges: []*binlogdatapb.RowChange{{ + After: newMMRow(1), + }}, + }, + }, { + Type: binlogdatapb.VEventType_GTID, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-101", + }, { + Type: binlogdatapb.VEventType_COMMIT, + }}}) + mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) + mm.Open() + + r1 := newTestReceiver(1) + mm.Subscribe(context.Background(), r1.rcv) + <-r1.ch + + want := &sqltypes.Result{ + Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(1), sqltypes.NewInt64(10), - sqltypes.NewVarBinary("01"), - }, { - sqltypes.NewInt64(2), - sqltypes.NewInt64(0), - sqltypes.NewInt64(1), - sqltypes.NewInt64(2), - sqltypes.NewInt64(20), - sqltypes.NewVarBinary("02"), - }, { - sqltypes.NewInt64(1), - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), + sqltypes.NewVarBinary("1"), + }}, + } + if got := <-r1.ch; !reflect.DeepEqual(got, want) { + t.Errorf("Received: %v, want %v", got, want) + } +} + +func TestMessageManagerStreamerAndPoller(t *testing.T) { + fvs := newFakeVStreamer() + fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ + Fields: testDBFields, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + }}) + mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) + mm.Open() + + r1 := newTestReceiver(1) + mm.Subscribe(context.Background(), r1.rcv) + <-r1.ch + + for { + runtime.Gosched() + time.Sleep(10 * time.Millisecond) + mm.streamMu.Lock() + pos := mm.lastPollPosition + mm.streamMu.Unlock() + if pos != nil { + break + } + } + + fvs.setStreamerResponse([][]*binlogdatapb.VEvent{{{ + // Event set 1: field info. + Type: binlogdatapb.VEventType_FIELD, + FieldEvent: &binlogdatapb.FieldEvent{ + TableName: "foo", + Fields: testDBFields, + }, + }}, {{ + // Event set 2: GTID won't be known till the first GTID event. + // Row will not be added. + Type: binlogdatapb.VEventType_ROW, + RowEvent: &binlogdatapb.RowEvent{ + TableName: "foo", + RowChanges: []*binlogdatapb.RowChange{{ + After: newMMRow(1), + }}, + }, + }, { + Type: binlogdatapb.VEventType_GTID, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-99", + }, { + Type: binlogdatapb.VEventType_COMMIT, + }}, {{ + // Event set 3: GTID will be known, but <= last poll. + // Row will not be added. + Type: binlogdatapb.VEventType_ROW, + RowEvent: &binlogdatapb.RowEvent{ + TableName: "foo", + RowChanges: []*binlogdatapb.RowChange{{ + After: newMMRow(2), + }}, + }, + }, { + Type: binlogdatapb.VEventType_GTID, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + }, { + Type: binlogdatapb.VEventType_COMMIT, + }}, {{ + // Event set 3: GTID will be > last poll. + // Row will be added. + Type: binlogdatapb.VEventType_ROW, + RowEvent: &binlogdatapb.RowEvent{ + TableName: "foo", + RowChanges: []*binlogdatapb.RowChange{{ + After: newMMRow(3), + }}, + }, + }, { + Type: binlogdatapb.VEventType_GTID, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-101", + }, { + Type: binlogdatapb.VEventType_COMMIT, + }}}) + + want := &sqltypes.Result{ + Rows: [][]sqltypes.Value{{ sqltypes.NewInt64(3), sqltypes.NewInt64(30), - sqltypes.NewVarBinary("11"), + sqltypes.NewVarBinary("3"), }}, - ), + } + if got := <-r1.ch; !reflect.DeepEqual(got, want) { + t.Errorf("Received: %v, want %v", got, want) + } +} + +func TestMessageManagerPoller(t *testing.T) { + ti := newMMTable() + ti.MessageInfo.BatchSize = 2 + ti.MessageInfo.PollInterval = 20 * time.Second + fvs := newFakeVStreamer() + fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ + Fields: testDBFields, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + }, { + Rows: []*querypb.Row{ + newMMRow(1), + newMMRow(2), + newMMRow(3), + }, }}) mm := newMessageManager(newFakeTabletServer(), fvs, ti, sync2.NewSemaphore(1, 0)) mm.Open() @@ -463,17 +581,17 @@ func TestMessageManagerPoller(t *testing.T) { <-r1.ch want := [][]sqltypes.Value{{ - sqltypes.NewInt64(2), - sqltypes.NewInt64(20), - sqltypes.NewVarBinary("02"), - }, { sqltypes.NewInt64(1), sqltypes.NewInt64(10), - sqltypes.NewVarBinary("01"), + sqltypes.NewVarBinary("1"), + }, { + sqltypes.NewInt64(2), + sqltypes.NewInt64(20), + sqltypes.NewVarBinary("2"), }, { sqltypes.NewInt64(3), sqltypes.NewInt64(30), - sqltypes.NewVarBinary("11"), + sqltypes.NewVarBinary("3"), }} var got [][]sqltypes.Value // We should get it in 2 iterations. @@ -481,8 +599,17 @@ func TestMessageManagerPoller(t *testing.T) { qr := <-r1.ch got = append(got, qr.Rows...) } - if !reflect.DeepEqual(got, want) { - t.Errorf("rows:\n%+v, want\n%+v", got, want) + for _, gotrow := range got { + found := false + for _, wantrow := range want { + if reflect.DeepEqual(gotrow, wantrow) { + found = true + break + } + } + if !found { + t.Errorf("row: %v not found in %v", gotrow, want) + } } // If there are no receivers, nothing should fire. @@ -498,8 +625,6 @@ func TestMessageManagerPoller(t *testing.T) { // TestMessagesPending1 tests for the case where you can't // add items because the cache is full. func TestMessagesPending1(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() // Set a large polling interval. ti := newMMTable() ti.MessageInfo.CacheSize = 2 @@ -523,25 +648,10 @@ func TestMessagesPending1(t *testing.T) { assert.False(t, mm.Add(&MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("4")}})) fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.VarBinary}, - }, - Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + Fields: testDBFields, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", }, { - Rows: sqltypes.RowsToProto3([][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(0), - sqltypes.NewInt64(5), - sqltypes.NewInt64(10), - sqltypes.NewVarBinary("01"), - }}, - ), + Rows: []*querypb.Row{newMMRow(1)}, }}) // Now, let's pull more than 3 items. It should @@ -558,33 +668,16 @@ func TestMessagesPending1(t *testing.T) { // TestMessagesPending2 tests for the case where // there are more pending items than the cache size. func TestMessagesPending2(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() // Set a large polling interval. ti := newMMTable() ti.MessageInfo.CacheSize = 1 ti.MessageInfo.PollInterval = 30 * time.Second fvs := newFakeVStreamer() fvs.setPollerResponse([]*binlogdatapb.VStreamResultsResponse{{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.VarBinary}, - }, - Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", + Fields: testDBFields, + Gtid: "MySQL56/33333333-3333-3333-3333-333333333333:1-100", }, { - Rows: sqltypes.RowsToProto3([][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(0), - sqltypes.NewInt64(0), - sqltypes.NewInt64(5), - sqltypes.NewInt64(10), - sqltypes.NewVarBinary("01"), - }}, - ), + Rows: []*querypb.Row{newMMRow(1)}, }}) mm := newMessageManager(newFakeTabletServer(), fvs, ti, sync2.NewSemaphore(1, 0)) mm.Open() @@ -606,8 +699,6 @@ func TestMessagesPending2(t *testing.T) { } func TestMessageManagerPurge(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() tsv := newFakeTabletServer() // Make a buffered channel so the thread doesn't block on repeated calls. @@ -626,8 +717,6 @@ func TestMessageManagerPurge(t *testing.T) { } func TestMMGenerate(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() mm := newMessageManager(newFakeTabletServer(), newFakeVStreamer(), newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() defer mm.Close() @@ -721,12 +810,19 @@ func (fts *fakeTabletServer) PurgeMessages(ctx context.Context, target *querypb. } type fakeVStreamer struct { - mu sync.Mutex - pollerResponse []*binlogdatapb.VStreamResultsResponse + mu sync.Mutex + streamerResponse [][]*binlogdatapb.VEvent + pollerResponse []*binlogdatapb.VStreamResultsResponse } func newFakeVStreamer() *fakeVStreamer { return &fakeVStreamer{} } +func (fv *fakeVStreamer) setStreamerResponse(sr [][]*binlogdatapb.VEvent) { + fv.mu.Lock() + defer fv.mu.Unlock() + fv.streamerResponse = sr +} + func (fv *fakeVStreamer) setPollerResponse(pr []*binlogdatapb.VStreamResultsResponse) { fv.mu.Lock() defer fv.mu.Unlock() @@ -734,7 +830,24 @@ func (fv *fakeVStreamer) setPollerResponse(pr []*binlogdatapb.VStreamResultsResp } func (fv *fakeVStreamer) Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { - return nil + for { + fv.mu.Lock() + sr := fv.streamerResponse + fv.streamerResponse = nil + fv.mu.Unlock() + for _, r := range sr { + if err := send(r); err != nil { + return err + } + } + select { + case <-ctx.Done(): + return io.EOF + default: + } + runtime.Gosched() + time.Sleep(10 * time.Millisecond) + } } func (fv *fakeVStreamer) StreamResults(ctx context.Context, query string, send func(*binlogdatapb.VStreamResultsResponse) error) error { From 338a37e6f84a54cd566909b458b217e54304ec16 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 10:31:49 -0800 Subject: [PATCH 261/825] messager: fix rest of the tests Signed-off-by: Sugu Sougoumarane --- go/vt/vtexplain/vtexplain_vttablet.go | 9 +- go/vt/vttablet/endtoend/framework/server.go | 3 +- go/vt/vttablet/tabletserver/bench_test.go | 5 +- .../planbuilder/testdata/exec_cases.txt | 8 - go/vt/vttablet/tabletserver/query_executor.go | 6 +- .../tabletserver/query_executor_test.go | 252 +----------------- go/vt/vttablet/tabletserver/tabletserver.go | 6 - .../tabletserver/tabletserver_flaky_test.go | 93 +++---- .../vttablet/tabletserver/tx_executor_test.go | 3 - go/vt/vttablet/tabletserver/tx_pool_test.go | 17 +- 10 files changed, 63 insertions(+), 339 deletions(-) diff --git a/go/vt/vtexplain/vtexplain_vttablet.go b/go/vt/vtexplain/vtexplain_vttablet.go index a51841dae31..f4f56f4adc4 100644 --- a/go/vt/vtexplain/vtexplain_vttablet.go +++ b/go/vt/vtexplain/vtexplain_vttablet.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/tabletserver" @@ -82,7 +83,7 @@ func newTablet(opts *Options, t *topodatapb.Tablet) *explainTablet { } // XXX much of this is cloned from the tabletserver tests - tsv := tabletserver.NewTabletServerWithNilTopoServer(config) + tsv := tabletserver.NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) tablet := explainTablet{db: db, tsv: tsv} db.Handler = &tablet @@ -505,11 +506,11 @@ func (t *explainTablet) HandleQuery(c *mysql.Conn, query string, callback func(* } var selStmt *sqlparser.Select - switch stmt.(type) { + switch stmt := stmt.(type) { case *sqlparser.Select: - selStmt = stmt.(*sqlparser.Select) + selStmt = stmt case *sqlparser.Union: - selStmt = stmt.(*sqlparser.Union).Right.(*sqlparser.Select) + selStmt = stmt.Right.(*sqlparser.Select) default: return fmt.Errorf("vtexplain: unsupported statement type +%v", reflect.TypeOf(stmt)) } diff --git a/go/vt/vttablet/endtoend/framework/server.go b/go/vt/vttablet/endtoend/framework/server.go index 707a2f9db67..9d235ea946b 100644 --- a/go/vt/vttablet/endtoend/framework/server.go +++ b/go/vt/vttablet/endtoend/framework/server.go @@ -22,6 +22,7 @@ import ( "net/http" "time" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vterrors" "golang.org/x/net/context" @@ -77,7 +78,7 @@ func StartServer(connParams, connAppDebugParams mysql.ConnParams, dbName string) TabletType: topodatapb.TabletType_MASTER, } - Server = tabletserver.NewTabletServerWithNilTopoServer(config) + Server = tabletserver.NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) Server.Register() err := Server.StartService(Target, dbcfgs) if err != nil { diff --git a/go/vt/vttablet/tabletserver/bench_test.go b/go/vt/vttablet/tabletserver/bench_test.go index 328b685038a..546c23881c8 100644 --- a/go/vt/vttablet/tabletserver/bench_test.go +++ b/go/vt/vttablet/tabletserver/bench_test.go @@ -27,6 +27,7 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/topo/memorytopo" ) // Benchmark run on 6/27/17, with optimized byte-level operations @@ -67,7 +68,7 @@ func BenchmarkExecuteVarBinary(b *testing.B) { } config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbconfigs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbconfigs); err != nil { @@ -99,7 +100,7 @@ func BenchmarkExecuteExpression(b *testing.B) { } config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbconfigs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbconfigs); err != nil { diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index 2e7f09d1db0..d26044b6c73 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -1101,14 +1101,6 @@ options:PassthroughDMLs "insert into msg(time_scheduled, id, message) select * from a" "subquery not allowed for message table: msg" -# message insert upsert time_scheduled -"insert into msg(time_scheduled, id, message) Values(1, 2, 'aa') on duplicate key update time_scheduled=123" -"'on duplicate key' cannot reference time_scheduled or id for message table: msg" - -# message insert upsert id -"insert into msg(time_scheduled, id, message) Values(1, 2, 'aa') on duplicate key update id=123" -"'on duplicate key' cannot reference time_scheduled or id for message table: msg" - # message insert without column list "insert into msg values(1)" "column list must be specified for message table insert: msg" diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index ed6e613dd91..e15c428708b 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -570,11 +570,7 @@ func (qre *QueryExecutor) execInsertPK(conn *TxConnection) (*sqltypes.Result, er func (qre *QueryExecutor) execInsertMessage(conn *TxConnection) (*sqltypes.Result, error) { qre.bindVars["#time_now"] = sqltypes.Int64BindVariable(time.Now().UnixNano()) - pkRows, err := buildValueList(qre.plan.Table, qre.plan.PKValues, qre.bindVars) - if err != nil { - return nil, err - } - return qre.execInsertPKRows(conn, nil, pkRows) + return qre.execInsertPK(conn) } func (qre *QueryExecutor) execInsertSubquery(conn *TxConnection) (*sqltypes.Result, error) { diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 10c4df3a889..027544ecad8 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -21,10 +21,8 @@ import ( "io" "math/rand" "reflect" - "runtime" "strings" "testing" - "time" "golang.org/x/net/context" @@ -36,9 +34,9 @@ import ( "vitess.io/vitess/go/vt/callinfo/fakecallinfo" "vitess.io/vitess/go/vt/tableacl" "vitess.io/vitess/go/vt/tableacl/simpleacl" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" - "vitess.io/vitess/go/vt/vttablet/tabletserver/messager" "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" "vitess.io/vitess/go/vt/vttablet/tabletserver/rules" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -293,28 +291,6 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() db.AddQueryPattern("insert into msg\\(time_scheduled, id, message, time_next, time_created, epoch\\) values \\(1, 2, 3, 1,.*", &sqltypes.Result{}) - db.AddQuery( - "select time_next, epoch, time_created, id, time_scheduled, message from msg where (time_scheduled = 1 and id = 2)", - &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - }, - RowsAffected: 1, - Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("1"), - sqltypes.NewVarBinary("0"), - sqltypes.NewVarBinary("10"), - sqltypes.NewVarBinary("1"), - sqltypes.NewVarBinary("10"), - sqltypes.NewVarBinary("2"), - }}, - }, - ) want := &sqltypes.Result{} query := "insert into msg(time_scheduled, id, message) values(1, 2, 3)" ctx := context.Background() @@ -322,17 +298,6 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() checkPlanID(t, planbuilder.PlanInsertMessage, qre.plan.PlanID) - ch1 := make(chan *sqltypes.Result) - go func() { <-ch1 }() - count := 0 - tsv.messager.Subscribe(context.Background(), "msg", func(qr *sqltypes.Result) error { - if count > 1 { - return io.EOF - } - count++ - ch1 <- qr - return nil - }) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -340,104 +305,6 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { if !reflect.DeepEqual(got, want) { t.Fatalf("got: %v, want: %v", got, want) } - mr := <-ch1 - wantqr := &sqltypes.Result{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewInt64(1), - sqltypes.NewInt64(10), - sqltypes.NewInt64(2), - }}, - } - if !reflect.DeepEqual(mr, wantqr) { - t.Errorf("rows:\n%+v, want\n%+v", mr, wantqr) - } - - txid := newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, query, txid) - defer testCommitHelper(t, tsv, qre) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -// TestQueryExecutorPlanInsertMessageAutoInc tests that the query that reads -// back rows correctly handles auto-inc values. -func TestQueryExecutorPlanInsertMessageAutoInc(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - db.AddQueryPattern("insert into msg\\(time_scheduled, id, message, time_next, time_created, epoch\\) values \\(1, .*", &sqltypes.Result{InsertID: 2}) - query := "insert into msg(time_scheduled, id, message) values(1, null, 3), (1, 3, 3), (1, null, 3)" - // First auto-inc value should be used. - // But subsequent ones will not be used because value was supplied - // for the second row. - db.AddQuery( - "select time_next, epoch, time_created, id, time_scheduled, message from msg where (time_scheduled = 1 and id = 2) or (time_scheduled = 1 and id = 3) or (time_scheduled = 1 and id = null)", - &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - }, - RowsAffected: 0, - Rows: [][]sqltypes.Value{}, - }, - ) - want := &sqltypes.Result{InsertID: 2} - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanInsertMessage, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -func TestQueryExecutorInsertMessageACL(t *testing.T) { - aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) - tableacl.Register(aclName, &simpleacl.Factory{}) - tableacl.SetDefaultACL(aclName) - config := &tableaclpb.Config{ - TableGroups: []*tableaclpb.TableGroupSpec{{ - Name: "group02", - TableNamesOrPrefixes: []string{"msg"}, - Readers: []string{"u2"}, - }}, - } - if err := tableacl.InitFromProto(config); err != nil { - t.Fatalf("unable to load tableacl config, error: %v", err) - } - - db := setUpQueryExecutorTest(t) - defer db.Close() - - query := "insert into msg(time_scheduled, id, message) values(1, 2, 3)" - - callerID := &querypb.VTGateCallerID{ - Username: "u2", - } - ctx := callerid.NewContext(context.Background(), nil, callerID) - - tsv := newTestTabletServer(ctx, enableStrictTableACL, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - - _, err := qre.Execute() - want := `table acl error: "u2" [] cannot run INSERT_MESSAGE on table "msg"` - if err == nil || err.Error() != want { - t.Errorf("qre.Execute(insert into msg) error: %v, want %s", err, want) - } } func TestQueryExecutorPlanInsertSubQueryAutoCommmit(t *testing.T) { @@ -945,45 +812,6 @@ func TestQueryExecutorPlanDmlPkRBR(t *testing.T) { } } -func TestQueryExecutorPlanDmlMessage(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update msg set time_acked = 2, time_next = null where id in (1)" - want := &sqltypes.Result{} - db.AddQuery("select time_scheduled, id from msg where id in (1) limit 10001 for update", &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int64}, - {Type: sqltypes.Int64}, - }, - RowsAffected: 1, - Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("12"), - sqltypes.NewVarBinary("1"), - }}, - }) - db.AddQuery("update msg set time_acked = 2, time_next = null where (time_scheduled = 12 and id = 1) /* _stream msg (time_scheduled id ) (12 1 ); */", want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanDMLSubquery, qre.plan.PlanID) - _, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - conn, err := qre.tsv.te.txPool.Get(txid, "for test") - if err != nil { - t.Fatal(err) - } - wantChanged := map[string][]string{"msg": {"1"}} - if !reflect.DeepEqual(conn.ChangedMessages, wantChanged) { - t.Errorf("conn.ChangedMessages: %+v, want: %+v", conn.ChangedMessages, wantChanged) - } - conn.Recycle() -} - func TestQueryExecutorPlanDmlAutoCommit(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() @@ -1490,82 +1318,6 @@ func TestQueryExecutorPlanNextval(t *testing.T) { } } -func TestQueryExecutorMessageStream(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - defer tsv.StopService() - logStats := tabletenv.NewLogStats(ctx, "TestQueryExecutor") - - plan, err := tsv.qe.GetMessageStreamPlan("msg") - if err != nil { - t.Fatal(err) - } - - // We can't call newTestQueryExecutor because there's no - // SQL syntax for message streaming yet. - qre := &QueryExecutor{ - ctx: ctx, - query: "stream from msg", - plan: plan, - logStats: logStats, - tsv: tsv, - } - checkPlanID(t, planbuilder.PlanMessageStream, qre.plan.PlanID) - - ch := make(chan *sqltypes.Result, 1) - done := make(chan struct{}) - skippedField := false - go func() { - if err := qre.MessageStream(func(qr *sqltypes.Result) error { - // Skip first result (field info). - if !skippedField { - skippedField = true - return nil - } - ch <- qr - return io.EOF - }); err != nil { - t.Error(err) - } - close(done) - }() - - newMessages := map[string][]*messager.MessageRow{ - "msg": { - &messager.MessageRow{Row: []sqltypes.Value{sqltypes.NewVarBinary("1"), sqltypes.NULL}}, - }, - } - // We hack-push a new message into the cache, which will cause - // the message manager to send it. - // We may have to iterate a few times before the stream kicks in. - for { - runtime.Gosched() - time.Sleep(10 * time.Millisecond) - unlock := tsv.messager.LockDB(newMessages, nil) - tsv.messager.UpdateCaches(newMessages, nil) - unlock() - want := &sqltypes.Result{ - Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("1"), - sqltypes.NULL, - }}, - } - select { - case got := <-ch: - if !want.Equal(got) { - t.Errorf("Stream:\n%v, want\n%v", got, want) - } - default: - continue - } - break - } - // This should not hang. - <-done -} - func TestQueryExecutorMessageStreamACL(t *testing.T) { aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63()) tableacl.Register(aclName, &simpleacl.Factory{}) @@ -2053,7 +1805,7 @@ func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb } else { config.TwoPCAbandonAge = 10 } - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) testUtils := newTestUtils() dbconfigs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 48ba8345369..a7b62f19879 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -254,12 +254,6 @@ type TxPoolController interface { var tsOnce sync.Once var srvTopoServer srvtopo.Server -// NewTabletServerWithNilTopoServer is typically used in tests that -// don't need a topoServer member. -func NewTabletServerWithNilTopoServer(config tabletenv.TabletConfig) *TabletServer { - return NewTabletServer(config, nil, topodatapb.TabletAlias{}) -} - // NewTabletServer creates an instance of TabletServer. Only the first // instance of TabletServer will expose its state variables. func NewTabletServer(config tabletenv.TabletConfig, topoServer *topo.Server, alias topodatapb.TabletAlias) *TabletServer { diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index b3932c52cda..767c284c2d3 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -40,6 +40,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/tableacl" "vitess.io/vitess/go/vt/tableacl/simpleacl" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -66,7 +67,7 @@ func TestTabletServerGetState(t *testing.T) { } testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) for i, state := range states { tsv.setState(state) if stateName := tsv.GetState(); stateName != names[i] { @@ -85,7 +86,7 @@ func TestTabletServerAllowQueriesFailBadConn(t *testing.T) { db.EnableConnFail() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) checkTabletServerState(t, tsv, StateNotConnected) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} @@ -101,7 +102,7 @@ func TestTabletServerAllowQueries(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) checkTabletServerState(t, tsv, StateNotConnected) dbcfgs := testUtils.newDBConfigs(db) tsv.setState(StateServing) @@ -125,7 +126,7 @@ func TestTabletServerInitDBConfig(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) tsv.setState(StateServing) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} dbcfgs := testUtils.newDBConfigs(db) @@ -144,7 +145,7 @@ func TestDecideAction(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} dbcfgs := testUtils.newDBConfigs(db) err := tsv.InitDBConfig(target, dbcfgs) @@ -235,7 +236,7 @@ func TestSetServingType(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.InitDBConfig(target, dbcfgs) @@ -305,7 +306,7 @@ func TestTabletServerSingleSchemaFailure(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} originalSchemaErrorCount := tabletenv.InternalErrors.Counts()["Schema"] @@ -337,7 +338,7 @@ func TestTabletServerAllSchemaFailure(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -354,7 +355,7 @@ func TestTabletServerCheckMysql(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -387,7 +388,7 @@ func TestTabletServerReconnect(t *testing.T) { db.AddQuery("select addr from test_table where 1 != 1", &sqltypes.Result{}) testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -429,7 +430,7 @@ func TestTabletServerTarget(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target1 := querypb.Target{ Keyspace: "test_keyspace", @@ -511,7 +512,7 @@ func TestBeginOnReplica(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target1 := querypb.Target{ Keyspace: "test_keyspace", @@ -873,7 +874,7 @@ func TestTabletServerBeginFail(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() config.TransactionCap = 1 - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -908,7 +909,7 @@ func TestTabletServerCommitTransaction(t *testing.T) { } db.AddQuery(executeSQL, executeSQLResult) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -934,7 +935,7 @@ func TestTabletServerCommiRollbacktFail(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -971,7 +972,7 @@ func TestTabletServerRollback(t *testing.T) { } db.AddQuery(executeSQL, executeSQLResult) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1075,7 +1076,7 @@ func TestTabletServerStreamExecute(t *testing.T) { db.AddQuery(executeSQL, executeSQLResult) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1102,7 +1103,7 @@ func TestTabletServerExecuteBatch(t *testing.T) { db.AddQuery(sql, sqlResult) db.AddQuery(expanedSQL, sqlResult) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1126,7 +1127,7 @@ func TestTabletServerExecuteBatchFailEmptyQueryList(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1147,7 +1148,7 @@ func TestTabletServerExecuteBatchFailAsTransaction(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1175,7 +1176,7 @@ func TestTabletServerExecuteBatchBeginFail(t *testing.T) { // make "begin" query fail db.AddRejectedQuery("begin", errRejected) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1201,7 +1202,7 @@ func TestTabletServerExecuteBatchCommitFail(t *testing.T) { // make "commit" query fail db.AddRejectedQuery("commit", errRejected) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1240,7 +1241,7 @@ func TestTabletServerExecuteBatchSqlExecFailInTransaction(t *testing.T) { db.AddRejectedQuery(expanedSQL, errRejected) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1283,7 +1284,7 @@ func TestTabletServerExecuteBatchSqlSucceedInTransaction(t *testing.T) { config := testUtils.newQueryServiceConfig() config.EnableAutoCommit = true - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1307,7 +1308,7 @@ func TestTabletServerExecuteBatchCallCommitWithoutABegin(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1337,7 +1338,7 @@ func TestExecuteBatchNestedTransaction(t *testing.T) { db.AddQuery(sql, sqlResult) db.AddQuery(expanedSQL, sqlResult) config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) @@ -1389,7 +1390,7 @@ func TestSerializeTransactionsSameRow(t *testing.T) { config.HotRowProtectionConcurrentTransactions = 1 // Reduce the txpool to 2 because we should never consume more than two slots. config.TransactionCap = 2 - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbcfgs); err != nil { @@ -1516,7 +1517,7 @@ func TestSerializeTransactionsSameRow_ExecuteBatchAsTransaction(t *testing.T) { config.HotRowProtectionConcurrentTransactions = 1 // Reduce the txpool to 2 because we should never consume more than two slots. config.TransactionCap = 2 - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbcfgs); err != nil { @@ -1634,7 +1635,7 @@ func TestSerializeTransactionsSameRow_ConcurrentTransactions(t *testing.T) { config.HotRowProtectionConcurrentTransactions = 2 // Reduce the txpool to 2 because we should never consume more than two slots. config.TransactionCap = 2 - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbcfgs); err != nil { @@ -1775,7 +1776,7 @@ func TestSerializeTransactionsSameRow_TooManyPendingRequests(t *testing.T) { config.EnableHotRowProtection = true config.HotRowProtectionMaxQueueSize = 1 config.HotRowProtectionConcurrentTransactions = 1 - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbcfgs); err != nil { @@ -1864,7 +1865,7 @@ func TestSerializeTransactionsSameRow_TooManyPendingRequests_ExecuteBatchAsTrans config.EnableHotRowProtection = true config.HotRowProtectionMaxQueueSize = 1 config.HotRowProtectionConcurrentTransactions = 1 - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbcfgs); err != nil { @@ -1956,7 +1957,7 @@ func TestSerializeTransactionsSameRow_RequestCanceled(t *testing.T) { config := testUtils.newQueryServiceConfig() config.EnableHotRowProtection = true config.HotRowProtectionConcurrentTransactions = 1 - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} if err := tsv.StartService(target, dbcfgs); err != nil { @@ -2244,7 +2245,7 @@ func TestTabletServerSplitQuery(t *testing.T) { }) testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} err := tsv.StartService(target, dbcfgs) @@ -2288,7 +2289,7 @@ func TestTabletServerSplitQueryKeywords(t *testing.T) { }) testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} err := tsv.StartService(target, dbcfgs) @@ -2319,7 +2320,7 @@ func TestTabletServerSplitQueryInvalidQuery(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} err := tsv.StartService(target, dbcfgs) @@ -2349,7 +2350,7 @@ func TestTabletServerSplitQueryInvalidParams(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} err := tsv.StartService(target, dbcfgs) @@ -2378,7 +2379,7 @@ func TestTabletServerSplitQueryEqualSplitsOnStringColumn(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} err := tsv.StartService(target, dbcfgs) @@ -2411,7 +2412,7 @@ func TestHandleExecUnknownError(t *testing.T) { logStats := tabletenv.NewLogStats(ctx, "TestHandleExecError") testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) defer tsv.handlePanicAndSendLogStats("select * from test_table", nil, logStats) panic("unknown exec error") } @@ -2452,7 +2453,7 @@ func TestHandleExecTabletError(t *testing.T) { ctx := context.Background() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) setupTestLogger() defer clearTestLogger() err := tsv.convertAndLogError( @@ -2478,7 +2479,7 @@ func TestTerseErrorsNonSQLError(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() config.TerseErrors = true - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) setupTestLogger() defer clearTestLogger() err := tsv.convertAndLogError( @@ -2503,7 +2504,7 @@ func TestTerseErrorsBindVars(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() config.TerseErrors = true - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) setupTestLogger() defer clearTestLogger() @@ -2533,7 +2534,7 @@ func TestTerseErrorsNoBindVars(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() config.TerseErrors = true - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) setupTestLogger() defer clearTestLogger() err := tsv.convertAndLogError(ctx, "", nil, vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "sensitive message"), nil) @@ -2552,7 +2553,7 @@ func TestTruncateErrors(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() config.TerseErrors = true - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) setupTestLogger() defer clearTestLogger() @@ -2603,7 +2604,7 @@ func TestTerseErrorsIgnoreFailoverInProgress(t *testing.T) { testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() config.TerseErrors = true - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) setupTestLogger() defer clearTestLogger() err := tsv.convertAndLogError(ctx, "select * from test_table where id = :a", @@ -2646,7 +2647,7 @@ func TestACLHUP(t *testing.T) { tableacl.Register("simpleacl", &simpleacl.Factory{}) testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) f, err := ioutil.TempFile("", "tableacl") if err != nil { @@ -2699,7 +2700,7 @@ func TestConfigChanges(t *testing.T) { defer db.Close() testUtils := newTestUtils() config := testUtils.newQueryServiceConfig() - tsv := NewTabletServerWithNilTopoServer(config) + tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbcfgs) diff --git a/go/vt/vttablet/tabletserver/tx_executor_test.go b/go/vt/vttablet/tabletserver/tx_executor_test.go index c7085bbf78a..d4c3fef16b3 100644 --- a/go/vt/vttablet/tabletserver/tx_executor_test.go +++ b/go/vt/vttablet/tabletserver/tx_executor_test.go @@ -534,7 +534,6 @@ func newTestTxExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db *fa ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, }, tsv, db } @@ -553,7 +552,6 @@ func newShortAgeExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db * ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, }, tsv, db } @@ -567,7 +565,6 @@ func newNoTwopcExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db *f ctx: ctx, logStats: logStats, te: tsv.te, - messager: tsv.messager, }, tsv, db } diff --git a/go/vt/vttablet/tabletserver/tx_pool_test.go b/go/vt/vttablet/tabletserver/tx_pool_test.go index eccb7030064..9912896cd7d 100644 --- a/go/vt/vttablet/tabletserver/tx_pool_test.go +++ b/go/vt/vttablet/tabletserver/tx_pool_test.go @@ -37,7 +37,6 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vttablet/tabletserver/messager" ) func TestTxPoolExecuteCommit(t *testing.T) { @@ -67,7 +66,7 @@ func TestTxPoolExecuteCommit(t *testing.T) { _, _ = txConn.Exec(ctx, sql, 1, true) txConn.Recycle() - commitSQL, err := txPool.Commit(ctx, transactionID, &fakeMessageCommitter{}) + commitSQL, err := txPool.Commit(ctx, transactionID) if err != nil { t.Fatal(err) } @@ -335,7 +334,7 @@ func TestTxPoolAutocommit(t *testing.T) { if beginSQL != "" { t.Errorf("beginSQL got %q want ''", beginSQL) } - commitSQL, err := txPool.Commit(ctx, txid, &fakeMessageCommitter{}) + commitSQL, err := txPool.Commit(ctx, txid) if err != nil { t.Fatal(err) } @@ -564,7 +563,7 @@ func TestTxPoolGetConnRecentlyRemovedTransaction(t *testing.T) { txPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) id, _, err = txPool.Begin(ctx, &querypb.ExecuteOptions{}) - if _, err := txPool.Commit(ctx, id, &fakeMessageCommitter{}); err != nil { + if _, err := txPool.Commit(ctx, id); err != nil { t.Fatalf("got error: %v", err) } @@ -601,16 +600,6 @@ func TestTxPoolGetConnRecentlyRemovedTransaction(t *testing.T) { assertErrorMatch(id, "closed") } -type fakeMessageCommitter struct { -} - -func (f *fakeMessageCommitter) LockDB(newMessages map[string][]*messager.MessageRow, changedMessages map[string][]string) func() { - return func() {} -} - -func (f *fakeMessageCommitter) UpdateCaches(newMessages map[string][]*messager.MessageRow, changedMessages map[string][]string) { -} - func TestTxPoolExecFailDueToConnFail_Errno2006(t *testing.T) { db := fakesqldb.New(t) defer db.Close() From eff235eb5124c15bd76438405a30fcdc5608c196 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 11:37:27 -0800 Subject: [PATCH 262/825] messager: schema change handle altered tables Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/messager/engine.go | 22 +++++++++---------- .../tabletserver/messager/engine_test.go | 15 ++++++++++++- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/engine.go b/go/vt/vttablet/tabletserver/messager/engine.go index c06b622104f..42aa93db53f 100644 --- a/go/vt/vttablet/tabletserver/messager/engine.go +++ b/go/vt/vttablet/tabletserver/messager/engine.go @@ -155,7 +155,16 @@ func (me *Engine) GeneratePurgeQuery(name string, timeCutoff int64) (string, map func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altered, dropped []string) { me.mu.Lock() defer me.mu.Unlock() - for _, name := range created { + for _, name := range append(dropped, altered...) { + mm := me.managers[name] + if mm == nil { + continue + } + mm.Close() + delete(me.managers, name) + } + + for _, name := range append(created, altered...) { t := tables[name] if t.Type != schema.Message { continue @@ -169,15 +178,4 @@ func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altere me.managers[name] = mm mm.Open() } - - // TODO(sougou): Update altered tables. - - for _, name := range dropped { - mm := me.managers[name] - if mm == nil { - continue - } - mm.Close() - delete(me.managers, name) - } } diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index caa954507d2..953d405f38f 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -76,11 +76,24 @@ func TestEngineSchemaChanged(t *testing.T) { } engine.schemaChanged(tables, []string{"t4"}, nil, []string{"t3", "t5"}) got = extractManagerNames(engine.managers) - // schemaChanged is only additive. want = map[string]bool{"t1": true, "t4": true} if !reflect.DeepEqual(got, want) { t.Errorf("got: %+v, want %+v", got, want) } + // Test update + tables = map[string]*schema.Table{ + "t1": meTable, + "t2": meTable, + "t4": { + Type: schema.NoType, + }, + } + engine.schemaChanged(tables, nil, []string{"t2", "t4"}, nil) + got = extractManagerNames(engine.managers) + want = map[string]bool{"t1": true, "t2": true} + if !reflect.DeepEqual(got, want) { + t.Errorf("got: %+v, want %+v", got, want) + } } func extractManagerNames(in map[string]*messageManager) map[string]bool { From fb5e4d45abe2b371806d4834bf924e770d1574d6 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 14:28:49 -0800 Subject: [PATCH 263/825] messages: robustize vstream Signed-off-by: Sugu Sougoumarane --- .../tabletserver/messager/message_manager.go | 197 +++++++++++------- .../messager/message_manager_test.go | 36 +++- 2 files changed, 154 insertions(+), 79 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 2a2614dc743..9eeaa3909a9 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -38,17 +38,23 @@ import ( querypb "vitess.io/vitess/go/vt/proto/query" ) -// MessageStats tracks stats for messages. -var MessageStats = stats.NewGaugesWithMultiLabels( - "Messages", - "Stats for messages", - []string{"TableName", "Metric"}) - -// MessageDelayTimings records total latency from queueing to sent to clients. -var MessageDelayTimings = stats.NewMultiTimings( - "MessageDelay", - "MessageDelayTimings records total latency from queueing to client sends", - []string{"TableName"}) +var ( + // MessageStats tracks stats for messages. + MessageStats = stats.NewGaugesWithMultiLabels( + "Messages", + "Stats for messages", + []string{"TableName", "Metric"}) + + // MessageDelayTimings records total latency from queueing to sent to clients. + MessageDelayTimings = stats.NewMultiTimings( + "MessageDelay", + "MessageDelayTimings records total latency from queueing to client sends", + []string{"TableName"}) + + // The following variables are changed for testing only. + streamEventGracePeriod = 10 * time.Second + vstreamRetryWait = 5 * time.Second +) type messageReceiver struct { ctx context.Context @@ -134,7 +140,7 @@ type receiverWithStatus struct { // but are not present in the cache. This state can be entered in one // of two ways: // 1. The poller read returns as many rows as the cache size -// 2. The Add of a message fails (cache full). This is invoked from the vstreamer. +// 2. The Add of a message fails (cache full). This is invoked from the vstream. // In any of the above cases, the messagesPending flag gets turned on. // In this phase, the send loop proactively wakes up the poller every time // it clears the cache. @@ -189,10 +195,10 @@ type messageManager struct { // It prevents items from being removed from cache while the poller // reads from the db and adds items to it. Otherwise, the poller // might add an older snapshot of a row that was just postponed. - // It blocks vstreamer from receiving messages while the poller + // It blocks vstream from receiving messages while the poller // reads a snapshot and updates lastPollPosition. Any events older than - // lastPollPosition must be ignored by the vstreamer. It consequently - // also blocks vstreamer from updating the cache while the poller is + // lastPollPosition must be ignored by the vstream. It consequently + // also blocks vstream from updating the cache while the poller is // active. streamMu sync.Mutex streamCancel func() @@ -309,6 +315,8 @@ func (mm *messageManager) Close() { mm.cond.Broadcast() mm.mu.Unlock() + mm.stopVStream() + mm.wg.Wait() } @@ -336,7 +344,7 @@ func (mm *messageManager) Subscribe(ctx context.Context, send func(*sqltypes.Res receiver: receiver, } if len(mm.receivers) == 0 { - mm.startVStreamer(ctx) + mm.startVStream() } mm.receivers = append(mm.receivers, withStatus) MessageStats.Set([]string{mm.name.String(), "ClientCount"}, int64(len(mm.receivers))) @@ -370,7 +378,7 @@ func (mm *messageManager) unsubscribe(receiver *messageReceiver) { mm.rescanReceivers(-1) // If there are no receivers. Shut down the cache. if len(mm.receivers) == 0 { - mm.stopVStreamer() + mm.stopVStream() mm.cache.Clear() } } @@ -544,17 +552,19 @@ func (mm *messageManager) postpone(tsv TabletService, name string, ackWaitTime t } } -func (mm *messageManager) startVStreamer(ctx context.Context) { +func (mm *messageManager) startVStream() { mm.streamMu.Lock() defer mm.streamMu.Unlock() if mm.streamCancel != nil { return } - ctx, mm.streamCancel = context.WithCancel(ctx) - go mm.runVStreamer(ctx) + var ctx context.Context + ctx, mm.streamCancel = context.WithCancel(tabletenv.LocalContext()) + mm.wg.Add(1) + go mm.runVStream(ctx) } -func (mm *messageManager) stopVStreamer() { +func (mm *messageManager) stopVStream() { mm.streamMu.Lock() defer mm.streamMu.Unlock() if mm.streamCancel != nil { @@ -563,74 +573,109 @@ func (mm *messageManager) stopVStreamer() { } } -func (mm *messageManager) runVStreamer(ctx context.Context) { +func (mm *messageManager) runVStream(ctx context.Context) { + defer mm.wg.Done() + for { - var curPos string - var fields []*querypb.Field - err := mm.vs.Stream(ctx, "current", mm.vsFilter, func(events []*binlogdatapb.VEvent) error { - mm.streamMu.Lock() - defer mm.streamMu.Unlock() + err := mm.runOneVStream(ctx) + select { + case <-ctx.Done(): + log.Info("Context canceled, exiting vstream") + return + default: + } + log.Infof("VStream ended: %v, retrying in 5 seconds", err) + time.Sleep(vstreamRetryWait) + } +} +func (mm *messageManager) runOneVStream(ctx context.Context) error { + var curPos string + var fields []*querypb.Field + + // The watchdog goroutine polls lastEventTime. + // If it exceeds streamEventGracePeriod, it cancels the vstream + // and exits. + lastEventTime := time.Now() + ctx, cancel := context.WithCancel(ctx) + go func() { + ticker := time.NewTicker(streamEventGracePeriod) + defer ticker.Stop() + + for { select { case <-ctx.Done(): - return io.EOF - default: + return + case <-ticker.C: + } + mm.streamMu.Lock() + idleTime := time.Since(lastEventTime) + mm.streamMu.Unlock() + if idleTime > streamEventGracePeriod { + log.Infof("VStream received no events for %v, restarting", idleTime) + cancel() + return } + } + }() - mustSkip := func() (bool, error) { - if mm.lastPollPosition == nil { - return false, nil - } - if curPos == "" { - return true, nil - } - cur, err := mysql.DecodePosition(curPos) - if err != nil { - return false, err - } - if cur.AtLeast(*mm.lastPollPosition) { - mm.lastPollPosition = nil - return false, nil - } + err := mm.vs.Stream(ctx, "current", mm.vsFilter, func(events []*binlogdatapb.VEvent) error { + mm.streamMu.Lock() + defer mm.streamMu.Unlock() + lastEventTime = time.Now() + + select { + case <-ctx.Done(): + return io.EOF + default: + } + + mustSkip := func() (bool, error) { + if mm.lastPollPosition == nil { + return false, nil + } + if curPos == "" { return true, nil } - skipEvents, err := mustSkip() + cur, err := mysql.DecodePosition(curPos) if err != nil { - return err + return false, err + } + if cur.AtLeast(*mm.lastPollPosition) { + mm.lastPollPosition = nil + return false, nil } - var newPos string - for _, ev := range events { - switch ev.Type { - case binlogdatapb.VEventType_FIELD: - fields = ev.FieldEvent.Fields - case binlogdatapb.VEventType_ROW: - if skipEvents { - continue - } - if err := mm.processRowEvent(fields, ev.RowEvent); err != nil { - return err - } - case binlogdatapb.VEventType_GTID: - newPos = ev.Gtid - case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER: - curPos = newPos - skipEvents, err = mustSkip() - if err != nil { - return err - } + return true, nil + } + skipEvents, err := mustSkip() + if err != nil { + return err + } + var newPos string + for _, ev := range events { + switch ev.Type { + case binlogdatapb.VEventType_FIELD: + fields = ev.FieldEvent.Fields + case binlogdatapb.VEventType_ROW: + if skipEvents { + continue + } + if err := mm.processRowEvent(fields, ev.RowEvent); err != nil { + return err + } + case binlogdatapb.VEventType_GTID: + newPos = ev.Gtid + case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER: + curPos = newPos + skipEvents, err = mustSkip() + if err != nil { + return err } } - return nil - }) - select { - case <-ctx.Done(): - log.Info("Context canceled, exiting vstream") - return - default: } - log.Infof("VStream ended: %v, retrying in 5 seconds", err) - time.Sleep(5 * time.Second) - } + return nil + }) + return err } func (mm *messageManager) processRowEvent(fields []*querypb.Field, rowEvent *binlogdatapb.RowEvent) error { diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index ca62c67cbc8..6896b5f1bbc 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -556,6 +556,34 @@ func TestMessageManagerStreamerAndPoller(t *testing.T) { } } +func TestMessageManagerStreamerIdleKill(t *testing.T) { + defer func(d time.Duration) { streamEventGracePeriod = d }(streamEventGracePeriod) + streamEventGracePeriod = 4 * time.Millisecond + + defer func(d time.Duration) { vstreamRetryWait = d }(vstreamRetryWait) + vstreamRetryWait = 1 * time.Microsecond + + fvs := newFakeVStreamer() + mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) + mm.Open() + + r1 := newTestReceiver(1) + mm.Subscribe(context.Background(), r1.rcv) + <-r1.ch + + for { + if fvs.streamInvocations.Get() >= 1 { + break + } + runtime.Gosched() + time.Sleep(1 * time.Millisecond) + } + time.Sleep(10 * time.Millisecond) + if got := fvs.streamInvocations.Get(); got < 2 { + t.Errorf("invocations: %d, want > %d", got, 2) + } +} + func TestMessageManagerPoller(t *testing.T) { ti := newMMTable() ti.MessageInfo.BatchSize = 2 @@ -810,9 +838,10 @@ func (fts *fakeTabletServer) PurgeMessages(ctx context.Context, target *querypb. } type fakeVStreamer struct { - mu sync.Mutex - streamerResponse [][]*binlogdatapb.VEvent - pollerResponse []*binlogdatapb.VStreamResultsResponse + streamInvocations sync2.AtomicInt64 + mu sync.Mutex + streamerResponse [][]*binlogdatapb.VEvent + pollerResponse []*binlogdatapb.VStreamResultsResponse } func newFakeVStreamer() *fakeVStreamer { return &fakeVStreamer{} } @@ -830,6 +859,7 @@ func (fv *fakeVStreamer) setPollerResponse(pr []*binlogdatapb.VStreamResultsResp } func (fv *fakeVStreamer) Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { + fv.streamInvocations.Add(1) for { fv.mu.Lock() sr := fv.streamerResponse From f5208ed9bfa43b4ab0781301c028291ab06160ca Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 14:48:05 -0800 Subject: [PATCH 264/825] messager: fix data race Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/messager/message_manager.go | 3 +++ go/vt/vttablet/tabletserver/messager/message_manager_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 9eeaa3909a9..3bd66fe0102 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -598,7 +598,10 @@ func (mm *messageManager) runOneVStream(ctx context.Context) error { // and exits. lastEventTime := time.Now() ctx, cancel := context.WithCancel(ctx) + mm.wg.Add(1) go func() { + defer mm.wg.Done() + ticker := time.NewTicker(streamEventGracePeriod) defer ticker.Stop() diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index 6896b5f1bbc..247c3ec204c 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -449,6 +449,7 @@ func TestMessageManagerStreamerSimple(t *testing.T) { }}}) mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() + defer mm.Close() r1 := newTestReceiver(1) mm.Subscribe(context.Background(), r1.rcv) @@ -474,6 +475,7 @@ func TestMessageManagerStreamerAndPoller(t *testing.T) { }}) mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() + defer mm.Close() r1 := newTestReceiver(1) mm.Subscribe(context.Background(), r1.rcv) @@ -566,6 +568,7 @@ func TestMessageManagerStreamerIdleKill(t *testing.T) { fvs := newFakeVStreamer() mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) mm.Open() + defer mm.Close() r1 := newTestReceiver(1) mm.Subscribe(context.Background(), r1.rcv) From d82400fa440de06175cc6f1396444a21216ddb55 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 17:40:59 -0800 Subject: [PATCH 265/825] messager: add tabletserver to test matrix Signed-off-by: Sugu Sougoumarane --- test/config.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/config.json b/test/config.json index 91cb7999e57..433ae3f29e1 100644 --- a/test/config.json +++ b/test/config.json @@ -435,6 +435,15 @@ "RetryMax": 0, "Tags": [] }, + "vtgate_vschema": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/tabletserver"], + "Command": [], + "Manual": false, + "Shard": 17, + "RetryMax": 0, + "Tags": [] + }, "web_test": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtctldweb"], @@ -474,4 +483,4 @@ ] } } -} \ No newline at end of file +} From 98567757c9128fa54b34e1bd1e69a762c9ef7962 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 18:26:12 -0800 Subject: [PATCH 266/825] messager: fix deadlock with schema engine Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/schema/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index 53d6aee2f83..057167e18a9 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -309,7 +309,7 @@ func (se *Engine) Reload(ctx context.Context) error { // must run after se.tables is set se.registerTopics() - se.broadcast(created, altered, dropped) + go se.broadcast(created, altered, dropped) return rec.Error() } From 738b7c55cd4dfbce71d3205cfa06b8e28fc7b878 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 20:04:30 -0800 Subject: [PATCH 267/825] messager: consolidate end2end tests into messaging Signed-off-by: Sugu Sougoumarane --- ...{messaging_test.go => cluster_ops_test.go} | 0 go/test/endtoend/messaging/main_test.go | 80 +++++++++++-------- .../message_test.go | 14 ++-- .../endtoend/vtgate/tabletserver/main_test.go | 73 ----------------- .../vttablet/tabletserver/messager/engine.go | 2 + test/config.json | 9 --- 6 files changed, 56 insertions(+), 122 deletions(-) rename go/test/endtoend/messaging/{messaging_test.go => cluster_ops_test.go} (100%) rename go/test/endtoend/{vtgate/tabletserver => messaging}/message_test.go (97%) delete mode 100644 go/test/endtoend/vtgate/tabletserver/main_test.go diff --git a/go/test/endtoend/messaging/messaging_test.go b/go/test/endtoend/messaging/cluster_ops_test.go similarity index 100% rename from go/test/endtoend/messaging/messaging_test.go rename to go/test/endtoend/messaging/cluster_ops_test.go diff --git a/go/test/endtoend/messaging/main_test.go b/go/test/endtoend/messaging/main_test.go index b6660fe4f7a..41da7389b34 100644 --- a/go/test/endtoend/messaging/main_test.go +++ b/go/test/endtoend/messaging/main_test.go @@ -22,6 +22,9 @@ import ( "os" "testing" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" _ "vitess.io/vitess/go/vt/vtgate/grpcvtgateconn" "vitess.io/vitess/go/test/endtoend/cluster" @@ -50,43 +53,45 @@ var ( index next_idx(time_next, epoch) ) comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` createUnshardedMessage = `create table unsharded_message( - time_scheduled bigint, - id bigint, - time_next bigint, - epoch bigint, - time_created bigint, - time_acked bigint, - message varchar(128), - primary key(time_scheduled, id), - unique index id_idx(id), - index next_idx(time_next, epoch) - ) comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` + time_scheduled bigint, + id bigint, + time_next bigint, + epoch bigint, + time_created bigint, + time_acked bigint, + message varchar(128), + primary key(time_scheduled, id), + unique index id_idx(id), + index next_idx(time_next, epoch) + ) comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1'` userVschema = `{ - "sharded": true, - "vindexes": { - "hash_index": { - "type": "hash" - } - }, - "tables": { - "sharded_message": { - "column_vindexes": [ - { - "column": "id", - "name": "hash_index" - } - ] + "sharded": true, + "vindexes": { + "hash_index": { + "type": "hash" + } + }, + "tables": { + "sharded_message": { + "column_vindexes": [ + { + "column": "id", + "name": "hash_index" } - } - }` + ] + } + } + }` lookupVschema = `{ - "sharded": false, - "tables": { - "unsharded_message": { - "type": "sequence" - } - } - }` + "sharded": false, + "tables": { + "unsharded_message": {}, + "vitess_message": {}, + "vitess_message3": {}, + "vitess_topic_subscriber_1": {}, + "vitess_topic_subscriber_2": {} + } + }` ) func TestMain(m *testing.M) { @@ -142,3 +147,10 @@ func TestMain(m *testing.M) { } } + +func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { + t.Helper() + qr, err := conn.ExecuteFetch(query, 1000, true) + require.NoError(t, err) + return qr +} diff --git a/go/test/endtoend/vtgate/tabletserver/message_test.go b/go/test/endtoend/messaging/message_test.go similarity index 97% rename from go/test/endtoend/vtgate/tabletserver/message_test.go rename to go/test/endtoend/messaging/message_test.go index 04d23f97dc4..7bcdf056a19 100644 --- a/go/test/endtoend/vtgate/tabletserver/message_test.go +++ b/go/test/endtoend/messaging/message_test.go @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package tabletserver +package messaging import ( "context" + "fmt" "reflect" "strings" "testing" @@ -58,6 +59,7 @@ func TestMessage(t *testing.T) { require.NoError(t, err) defer streamConn.Close() + exec(t, conn, fmt.Sprintf("use %s", lookupKeyspace)) exec(t, conn, createMessage) defer exec(t, conn, "drop table vitess_message") @@ -173,6 +175,7 @@ func TestThreeColMessage(t *testing.T) { require.NoError(t, err) defer streamConn.Close() + exec(t, conn, fmt.Sprintf("use %s", lookupKeyspace)) exec(t, conn, createThreeColMessage) defer exec(t, conn, "drop table vitess_message3") @@ -259,6 +262,7 @@ func TestMessageTopic(t *testing.T) { require.NoError(t, err) defer streamConn2.Close() + exec(t, conn, fmt.Sprintf("use %s", lookupKeyspace)) exec(t, conn, createMessageTopic1) exec(t, conn, createMessageTopic2) // These are failsafe drops. The test actually drops these tables during the flow. @@ -330,7 +334,9 @@ func TestMessageTopic(t *testing.T) { // // phase 2 tests deleting one of the subscribers and making sure - // that inserts into a topic go to one subscribed message table + // that inserts into a topic go to one subscribed message table. + // This test takes longer because vttablet will drop and recreate + // vitess_topic_subscriber_2, which will make vtgate wait 5s and retry. // exec(t, conn, "drop table vitess_topic_subscriber_1") @@ -352,7 +358,6 @@ func TestMessageTopic(t *testing.T) { // Consume first three messages // and ensure they were received promptly. - start = time.Now() for i := 0; i < 3; i++ { // make sure the second message table received all three messages got2, err := streamConn2.FetchNext() @@ -368,9 +373,6 @@ func TestMessageTopic(t *testing.T) { } assert.True(t, found) } - if d := time.Since(start); d > 1*time.Second { - t.Errorf("messages were delayed: %v", d) - } // ack the second subscriber _ = exec(t, conn, "update vitess_topic_subscriber_2 set time_acked = 123, time_next = null where id in (4, 5, 6) and time_acked is null") diff --git a/go/test/endtoend/vtgate/tabletserver/main_test.go b/go/test/endtoend/vtgate/tabletserver/main_test.go deleted file mode 100644 index 63b5c783738..00000000000 --- a/go/test/endtoend/vtgate/tabletserver/main_test.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package tabletserver - -import ( - "flag" - "os" - "testing" - - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" -) - -var ( - clusterInstance *cluster.LocalProcessCluster - keyspaceName = "ks" - cell = "zone1" - hostname = "localhost" -) - -func TestMain(m *testing.M) { - flag.Parse() - - exitCode := func() int { - clusterInstance = cluster.NewCluster(cell, hostname) - //defer clusterInstance.Teardown() - - // Start topo server - if err := clusterInstance.StartTopo(); err != nil { - return 1 - } - - // Start keyspace - keyspace := cluster.Keyspace{ - Name: keyspaceName, - } - if err := clusterInstance.StartUnshardedKeyspace(keyspace, 0, false); err != nil { - return 1 - } - - // Start vtgate - if err := clusterInstance.StartVtgate(); err != nil { - return 1 - } - - return m.Run() - }() - os.Exit(exitCode) -} - -func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { - t.Helper() - qr, err := conn.ExecuteFetch(query, 1000, true) - require.NoError(t, err) - return qr -} diff --git a/go/vt/vttablet/tabletserver/messager/engine.go b/go/vt/vttablet/tabletserver/messager/engine.go index 42aa93db53f..b603e75dcc0 100644 --- a/go/vt/vttablet/tabletserver/messager/engine.go +++ b/go/vt/vttablet/tabletserver/messager/engine.go @@ -160,6 +160,7 @@ func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altere if mm == nil { continue } + log.Infof("Stopping messager for dropped/updated table: %v", name) mm.Close() delete(me.managers, name) } @@ -176,6 +177,7 @@ func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altere } mm := newMessageManager(me.tsv, me.vs, t, me.postponeSema) me.managers[name] = mm + log.Infof("Starting messager for table: %v", name) mm.Open() } } diff --git a/test/config.json b/test/config.json index 433ae3f29e1..6f9ec4b7a81 100644 --- a/test/config.json +++ b/test/config.json @@ -435,15 +435,6 @@ "RetryMax": 0, "Tags": [] }, - "vtgate_vschema": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/vtgate/tabletserver"], - "Command": [], - "Manual": false, - "Shard": 17, - "RetryMax": 0, - "Tags": [] - }, "web_test": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vtctldweb"], From 29bd0e219cc51cdcc86e1b43caa3c8cd6e3706a7 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 7 Mar 2020 23:10:00 -0800 Subject: [PATCH 268/825] messager: removed waitgroup on vstream Adding a waitgroup for vstream caused all kinds of deadlocks because vstreamer calls se.Reload, which calls back into engine on schemaChanged. stopVStream is already safe: it will eventually cancel the VStreamer. Any call back after stop will be a no-op. So, there's not much value in waiting for that goroutine to return before declaring Close as done. So, not waiting on vstream to exit before closing makes things more stable. Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/messager/message_manager.go | 3 --- go/vt/vttablet/tabletserver/schema/engine.go | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 3bd66fe0102..1b552ba4646 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -560,7 +560,6 @@ func (mm *messageManager) startVStream() { } var ctx context.Context ctx, mm.streamCancel = context.WithCancel(tabletenv.LocalContext()) - mm.wg.Add(1) go mm.runVStream(ctx) } @@ -574,8 +573,6 @@ func (mm *messageManager) stopVStream() { } func (mm *messageManager) runVStream(ctx context.Context) { - defer mm.wg.Done() - for { err := mm.runOneVStream(ctx) select { diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index 057167e18a9..53d6aee2f83 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -309,7 +309,7 @@ func (se *Engine) Reload(ctx context.Context) error { // must run after se.tables is set se.registerTopics() - go se.broadcast(created, altered, dropped) + se.broadcast(created, altered, dropped) return rec.Error() } From 78032e1004df3332e26dbf56e1a2796ffafa62d8 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 8 Mar 2020 14:59:06 -0700 Subject: [PATCH 269/825] messager: a few more tweaks for stability Signed-off-by: Sugu Sougoumarane --- .../tabletserver/messager/message_manager.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 1b552ba4646..306bff730fa 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -432,12 +432,18 @@ func (mm *messageManager) runSend() { tabletenv.LogError() mm.wg.Done() }() + + mm.mu.Lock() + defer mm.mu.Unlock() + for { - var rows [][]sqltypes.Value + // Release and acquire the lock to avoid starving other contenders. + mm.mu.Unlock() mm.mu.Lock() + + var rows [][]sqltypes.Value for { if !mm.isOpen { - mm.mu.Unlock() return } @@ -490,7 +496,6 @@ func (mm *messageManager) runSend() { // Send the message asynchronously. mm.wg.Add(1) go mm.send(receiver, &sqltypes.Result{Rows: rows}) - mm.mu.Unlock() } } @@ -595,10 +600,9 @@ func (mm *messageManager) runOneVStream(ctx context.Context) error { // and exits. lastEventTime := time.Now() ctx, cancel := context.WithCancel(ctx) - mm.wg.Add(1) - go func() { - defer mm.wg.Done() + defer cancel() + go func() { ticker := time.NewTicker(streamEventGracePeriod) defer ticker.Stop() From 6bb3f036e6d4914708e5cae9982d8c0edc595716 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 8 Mar 2020 21:05:29 -0700 Subject: [PATCH 270/825] messager: fix flaky data race Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/messager/message_manager.go | 10 +++++----- .../tabletserver/messager/message_manager_test.go | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 306bff730fa..7b84a9e8cf6 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -52,8 +52,8 @@ var ( []string{"TableName"}) // The following variables are changed for testing only. - streamEventGracePeriod = 10 * time.Second - vstreamRetryWait = 5 * time.Second + streamEventGracePeriod = sync2.NewAtomicDuration(10 * time.Second) + vstreamRetryWait = sync2.NewAtomicDuration(5 * time.Second) ) type messageReceiver struct { @@ -587,7 +587,7 @@ func (mm *messageManager) runVStream(ctx context.Context) { default: } log.Infof("VStream ended: %v, retrying in 5 seconds", err) - time.Sleep(vstreamRetryWait) + time.Sleep(vstreamRetryWait.Get()) } } @@ -603,7 +603,7 @@ func (mm *messageManager) runOneVStream(ctx context.Context) error { defer cancel() go func() { - ticker := time.NewTicker(streamEventGracePeriod) + ticker := time.NewTicker(streamEventGracePeriod.Get()) defer ticker.Stop() for { @@ -615,7 +615,7 @@ func (mm *messageManager) runOneVStream(ctx context.Context) error { mm.streamMu.Lock() idleTime := time.Since(lastEventTime) mm.streamMu.Unlock() - if idleTime > streamEventGracePeriod { + if idleTime > streamEventGracePeriod.Get() { log.Infof("VStream received no events for %v, restarting", idleTime) cancel() return diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index 247c3ec204c..f3077dc7e8f 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -559,11 +559,11 @@ func TestMessageManagerStreamerAndPoller(t *testing.T) { } func TestMessageManagerStreamerIdleKill(t *testing.T) { - defer func(d time.Duration) { streamEventGracePeriod = d }(streamEventGracePeriod) - streamEventGracePeriod = 4 * time.Millisecond + defer func(d time.Duration) { streamEventGracePeriod.Set(d) }(streamEventGracePeriod.Get()) + streamEventGracePeriod.Set(4 * time.Millisecond) - defer func(d time.Duration) { vstreamRetryWait = d }(vstreamRetryWait) - vstreamRetryWait = 1 * time.Microsecond + defer func(d time.Duration) { vstreamRetryWait.Set(d) }(vstreamRetryWait.Get()) + vstreamRetryWait.Set(1 * time.Microsecond) fvs := newFakeVStreamer() mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) @@ -581,7 +581,7 @@ func TestMessageManagerStreamerIdleKill(t *testing.T) { runtime.Gosched() time.Sleep(1 * time.Millisecond) } - time.Sleep(10 * time.Millisecond) + time.Sleep(20 * time.Millisecond) if got := fvs.streamInvocations.Get(); got < 2 { t.Errorf("invocations: %d, want > %d", got, 2) } From 851a00f14d4233d58951e0cf4398a3cf122402ec Mon Sep 17 00:00:00 2001 From: deepthi Date: Tue, 10 Mar 2020 15:55:10 -0700 Subject: [PATCH 271/825] fix vtgate teardown Signed-off-by: deepthi --- go/test/endtoend/cellalias/cell_alias_test.go | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index 9fe8f8c38f5..71da8014709 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -238,7 +238,10 @@ func TestMain(m *testing.M) { func TestAlias(t *testing.T) { defer cluster.PanicHandler(t) + insertInitialValues(t) + defer deleteInitialValues(t) + err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) require.Nil(t, err) shard1 := localCluster.Keyspaces[0].Shards[0] @@ -267,6 +270,11 @@ func TestAlias(t *testing.T) { vtgateInstance.TabletTypesToWait = "MASTER,REPLICA" err = vtgateInstance.Setup() require.Nil(t, err) + + // Cluster teardown will not teardown vtgate because we are not + // actually setting this on localCluster.VtgateInstance + defer vtgateInstance.TearDown() + waitTillAllTabletsAreHealthyInVtgate(t, *vtgateInstance, shard1.Name, shard2.Name) testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) @@ -279,8 +287,9 @@ func TestAlias(t *testing.T) { require.Nil(t, err) // restarts the vtgate process - _ = vtgateInstance.TearDown() vtgateInstance.TabletTypesToWait = "MASTER" + err = vtgateInstance.TearDown() + require.Nil(t, err) err = vtgateInstance.Setup() require.Nil(t, err) @@ -289,13 +298,14 @@ func TestAlias(t *testing.T) { testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, true) testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) - deleteInitialValues(t) - _ = vtgateInstance.TearDown() } func TestAddAliasWhileVtgateUp(t *testing.T) { defer cluster.PanicHandler(t) + insertInitialValues(t) + defer deleteInitialValues(t) + err := localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) require.Nil(t, err) shard1 := localCluster.Keyspaces[0].Shards[0] @@ -314,6 +324,8 @@ func TestAddAliasWhileVtgateUp(t *testing.T) { vtgateInstance.TabletTypesToWait = "MASTER,REPLICA,RDONLY" err = vtgateInstance.Setup() require.Nil(t, err) + defer vtgateInstance.TearDown() + waitTillAllTabletsAreHealthyInVtgate(t, *vtgateInstance, shard1.Name, shard2.Name) // since replica and rdonly tablets of all shards in cell2, the last 2 assertion is expected to fail @@ -335,8 +347,6 @@ func TestAddAliasWhileVtgateUp(t *testing.T) { testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, false) testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, false) - deleteInitialValues(t) - _ = vtgateInstance.TearDown() } func waitTillAllTabletsAreHealthyInVtgate(t *testing.T, vtgateInstance cluster.VtgateProcess, shards ...string) { From b45b2488fc8de23a07790edb34e2a867d12be3a8 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 10 Mar 2020 16:29:23 -0700 Subject: [PATCH 272/825] messager: remove unnecessary watchdog The watchdog for vstreamer is unnecessary because an in-process call to vstreamer is guaranteed to generate a heartbeat on inactivity. Signed-off-by: Sugu Sougoumarane --- .../tabletserver/messager/message_manager.go | 36 ++----------------- .../messager/message_manager_test.go | 29 --------------- 2 files changed, 2 insertions(+), 63 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 7b84a9e8cf6..97a9d39cc02 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -50,10 +50,6 @@ var ( "MessageDelay", "MessageDelayTimings records total latency from queueing to client sends", []string{"TableName"}) - - // The following variables are changed for testing only. - streamEventGracePeriod = sync2.NewAtomicDuration(10 * time.Second) - vstreamRetryWait = sync2.NewAtomicDuration(5 * time.Second) ) type messageReceiver struct { @@ -586,8 +582,9 @@ func (mm *messageManager) runVStream(ctx context.Context) { return default: } + MessageStats.Add([]string{mm.name.String(), "VStreamFailed"}, 1) log.Infof("VStream ended: %v, retrying in 5 seconds", err) - time.Sleep(vstreamRetryWait.Get()) + time.Sleep(5 * time.Second) } } @@ -595,38 +592,9 @@ func (mm *messageManager) runOneVStream(ctx context.Context) error { var curPos string var fields []*querypb.Field - // The watchdog goroutine polls lastEventTime. - // If it exceeds streamEventGracePeriod, it cancels the vstream - // and exits. - lastEventTime := time.Now() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - go func() { - ticker := time.NewTicker(streamEventGracePeriod.Get()) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - } - mm.streamMu.Lock() - idleTime := time.Since(lastEventTime) - mm.streamMu.Unlock() - if idleTime > streamEventGracePeriod.Get() { - log.Infof("VStream received no events for %v, restarting", idleTime) - cancel() - return - } - } - }() - err := mm.vs.Stream(ctx, "current", mm.vsFilter, func(events []*binlogdatapb.VEvent) error { mm.streamMu.Lock() defer mm.streamMu.Unlock() - lastEventTime = time.Now() select { case <-ctx.Done(): diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index f3077dc7e8f..a1071ffec6a 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -558,35 +558,6 @@ func TestMessageManagerStreamerAndPoller(t *testing.T) { } } -func TestMessageManagerStreamerIdleKill(t *testing.T) { - defer func(d time.Duration) { streamEventGracePeriod.Set(d) }(streamEventGracePeriod.Get()) - streamEventGracePeriod.Set(4 * time.Millisecond) - - defer func(d time.Duration) { vstreamRetryWait.Set(d) }(vstreamRetryWait.Get()) - vstreamRetryWait.Set(1 * time.Microsecond) - - fvs := newFakeVStreamer() - mm := newMessageManager(newFakeTabletServer(), fvs, newMMTable(), sync2.NewSemaphore(1, 0)) - mm.Open() - defer mm.Close() - - r1 := newTestReceiver(1) - mm.Subscribe(context.Background(), r1.rcv) - <-r1.ch - - for { - if fvs.streamInvocations.Get() >= 1 { - break - } - runtime.Gosched() - time.Sleep(1 * time.Millisecond) - } - time.Sleep(20 * time.Millisecond) - if got := fvs.streamInvocations.Get(); got < 2 { - t.Errorf("invocations: %d, want > %d", got, 2) - } -} - func TestMessageManagerPoller(t *testing.T) { ti := newMMTable() ti.MessageInfo.BatchSize = 2 From bdbcd383caa3b56bff5b390fa71e4f338403a678 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 16 Jan 2020 12:29:00 -0800 Subject: [PATCH 273/825] Allow SCHEMA() as a synonym for DATABASE() Signed-off-by: Andres Taylor --- go/vt/sqlparser/expression_rewriting.go | 6 +- go/vt/sqlparser/expression_rewriting_test.go | 5 + go/vt/sqlparser/parse_test.go | 46 +- go/vt/sqlparser/sql.go | 4089 +++++++++--------- go/vt/sqlparser/sql.y | 4 + 5 files changed, 2088 insertions(+), 2062 deletions(-) diff --git a/go/vt/sqlparser/expression_rewriting.go b/go/vt/sqlparser/expression_rewriting.go index fb934f266cd..5d52312b337 100644 --- a/go/vt/sqlparser/expression_rewriting.go +++ b/go/vt/sqlparser/expression_rewriting.go @@ -113,9 +113,11 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { cursor.Replace(bindVarExpression(LastInsertIDName)) er.lastInsertID = true } - case node.Name.EqualString("database") && er.shouldRewriteDatabaseFunc: + case er.shouldRewriteDatabaseFunc && + (node.Name.EqualString("database") || + node.Name.EqualString("schema")): if len(node.Exprs) > 0 { - er.err = vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Syntax error. DATABASE() takes no arguments") + er.err = vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "Syntax error. %s() takes no arguments", node.Name.String()) } else { cursor.Replace(bindVarExpression(DBVarName)) er.database = true diff --git a/go/vt/sqlparser/expression_rewriting_test.go b/go/vt/sqlparser/expression_rewriting_test.go index cf9eef04cef..2951db2dc5c 100644 --- a/go/vt/sqlparser/expression_rewriting_test.go +++ b/go/vt/sqlparser/expression_rewriting_test.go @@ -84,6 +84,11 @@ func TestRewrites(in *testing.T) { expected: "select table_name from information_schema.tables where table_schema = database()", db: false, liid: false, }, + { + in: "select schema()", + expected: "select :__vtdbname as 'schema()'", + db: true, liid: false, + }, } for _, tc := range tests { diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 9501bb51815..c986277f3d2 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1451,6 +1451,12 @@ var ( input: "select match(a) against ('foo') from t", }, { input: "select match(a1, a2) against ('foo' in natural language mode with query expansion) from t", + }, { + input: "select database()", + output: "select database() from dual", + }, { + input: "select schema()", + output: "select schema() from dual", }, { input: "select title from video as v where match(v.title, v.tag) against ('DEMO' in boolean mode)", }, { @@ -1522,25 +1528,27 @@ var ( func TestValid(t *testing.T) { for _, tcase := range validSQL { - if tcase.output == "" { - tcase.output = tcase.input - } - tree, err := Parse(tcase.input) - if err != nil { - t.Errorf("Parse(%q) err: %v, want nil", tcase.input, err) - continue - } - out := String(tree) - if out != tcase.output { - t.Errorf("Parse(%q) = %q, want: %q", tcase.input, out, tcase.output) - } - // This test just exercises the tree walking functionality. - // There's no way automated way to verify that a node calls - // all its children. But we can examine code coverage and - // ensure that all walkSubtree functions were called. - Walk(func(node SQLNode) (bool, error) { - return true, nil - }, tree) + t.Run(tcase.input, func(t *testing.T) { + if tcase.output == "" { + tcase.output = tcase.input + } + tree, err := Parse(tcase.input) + if err != nil { + t.Errorf("Parse(%q) err: %v, want nil", tcase.input, err) + return + } + out := String(tree) + if out != tcase.output { + t.Errorf("Parse(%q) = %q, want: %q", tcase.input, out, tcase.output) + } + // This test just exercises the tree walking functionality. + // There's no way automated way to verify that a node calls + // all its children. But we can examine code coverage and + // ensure that all walkSubtree functions were called. + Walk(func(node SQLNode) (bool, error) { + return true, nil + }, tree) + }) } } diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index a7d1ea2a5db..3415bbfbbbe 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -804,24 +804,24 @@ var yyExca = [...]int{ 162, 302, -2, 290, -1, 322, - 113, 645, - -2, 641, - -1, 323, 113, 646, -2, 642, - -1, 391, - 83, 894, - -2, 63, + -1, 323, + 113, 647, + -2, 643, -1, 392, - 83, 812, + 83, 895, + -2, 63, + -1, 393, + 83, 813, -2, 64, - -1, 397, - 83, 781, - -2, 607, - -1, 399, - 83, 842, - -2, 609, - -1, 697, + -1, 398, + 83, 782, + -2, 608, + -1, 400, + 83, 843, + -2, 610, + -1, 699, 1, 355, 5, 355, 12, 355, @@ -845,318 +845,320 @@ var yyExca = [...]int{ 57, 355, 348, 355, -2, 373, - -1, 700, + -1, 702, 54, 44, 56, 44, -2, 48, - -1, 852, - 113, 648, - -2, 644, - -1, 1081, + -1, 854, + 113, 649, + -2, 645, + -1, 1084, 5, 30, -2, 441, - -1, 1111, + -1, 1115, 5, 29, - -2, 581, - -1, 1355, - 5, 30, -2, 582, - -1, 1408, - 5, 29, - -2, 584, - -1, 1488, + -1, 1359, 5, 30, + -2, 583, + -1, 1412, + 5, 29, -2, 585, + -1, 1492, + 5, 30, + -2, 586, } const yyPrivate = 57344 -const yyLast = 16982 +const yyLast = 16995 var yyAct = [...]int{ - 323, 1522, 1512, 1317, 1476, 653, 327, 1114, 1206, 1375, - 1388, 966, 1132, 1421, 1257, 340, 1291, 939, 1258, 551, - 57, 962, 353, 1115, 1254, 1009, 652, 3, 975, 1270, - 1264, 995, 81, 965, 301, 1229, 266, 396, 1159, 266, - 877, 888, 799, 884, 329, 815, 1138, 1185, 1072, 1176, - 713, 979, 941, 854, 584, 926, 1005, 520, 385, 590, - 937, 354, 51, 712, 390, 596, 310, 266, 81, 325, - 989, 919, 266, 694, 266, 702, 605, 387, 300, 667, - 56, 1515, 382, 1499, 1510, 1028, 906, 668, 1486, 693, - 61, 540, 1507, 1318, 1498, 1246, 1485, 1347, 525, 1027, - 261, 257, 258, 259, 956, 314, 555, 276, 1286, 1287, - 957, 958, 292, 51, 562, 1285, 63, 64, 65, 66, - 67, 306, 253, 1147, 298, 251, 1146, 255, 1032, 1148, - 573, 714, 286, 715, 574, 571, 572, 1026, 1450, 618, - 617, 627, 628, 620, 621, 622, 623, 624, 625, 626, - 619, 578, 297, 629, 1167, 988, 1230, 293, 294, 295, - 296, 1208, 365, 299, 371, 372, 369, 370, 368, 367, - 366, 1378, 557, 996, 559, 1338, 1336, 1395, 373, 374, - 291, 788, 393, 269, 566, 567, 576, 1023, 1020, 1021, - 272, 1019, 787, 1210, 1232, 785, 1509, 1506, 280, 275, - 1469, 980, 1477, 1205, 920, 556, 558, 541, 1530, 1430, - 577, 527, 1422, 255, 1209, 1211, 982, 982, 792, 260, - 254, 789, 887, 1030, 1033, 1424, 776, 786, 1234, 1202, - 1238, 278, 1233, 1526, 1231, 1204, 1280, 285, 1279, 1236, - 1278, 252, 982, 523, 530, 268, 1160, 521, 1235, 1133, - 1135, 256, 266, 1193, 641, 642, 1040, 266, 316, 1039, - 1025, 1237, 1239, 266, 270, 1458, 1358, 1090, 1215, 266, - 1087, 963, 1143, 1100, 81, 1066, 81, 81, 826, 81, - 708, 81, 1024, 1191, 609, 547, 629, 81, 823, 604, - 554, 282, 273, 1423, 283, 284, 289, 952, 1303, 619, - 274, 277, 629, 271, 288, 287, 816, 1484, 1451, 996, - 820, 1431, 1429, 981, 981, 1467, 1439, 81, 553, 978, - 976, 1029, 977, 907, 1268, 592, 1134, 521, 974, 980, - 70, 537, 1203, 593, 1201, 561, 1031, 561, 561, 981, - 561, 1524, 561, 1085, 1525, 1084, 1523, 716, 561, 1304, - 1192, 543, 544, 545, 1248, 1197, 1194, 1187, 1195, 1190, - 519, 1186, 603, 602, 1188, 1189, 71, 778, 51, 639, - 641, 642, 1165, 641, 642, 907, 861, 1097, 1196, 604, - 266, 266, 266, 638, 1472, 599, 640, 1490, 817, 81, - 859, 860, 858, 1086, 534, 81, 535, 552, 594, 536, - 627, 628, 620, 621, 622, 623, 624, 625, 626, 619, - 580, 581, 629, 1531, 651, 1384, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 697, 666, 669, 669, 669, - 675, 669, 669, 675, 669, 683, 684, 685, 686, 687, - 688, 692, 698, 1383, 603, 602, 670, 672, 674, 676, - 678, 680, 681, 1532, 671, 673, 1180, 677, 679, 706, - 682, 604, 603, 602, 701, 1179, 710, 617, 627, 628, - 620, 621, 622, 623, 624, 625, 626, 619, 352, 604, - 629, 618, 617, 627, 628, 620, 621, 622, 623, 624, - 625, 626, 619, 1168, 583, 629, 622, 623, 624, 625, - 626, 619, 526, 393, 629, 1063, 1064, 1065, 602, 825, - 79, 620, 621, 622, 623, 624, 625, 626, 619, 54, - 266, 629, 829, 830, 604, 81, 250, 1492, 1468, 857, - 266, 266, 81, 81, 81, 1073, 603, 602, 266, 985, - 878, 266, 879, 1250, 266, 986, 395, 824, 266, 1402, - 81, 1465, 1149, 604, 1150, 81, 81, 81, 266, 81, - 81, 587, 591, 1381, 603, 602, 1177, 81, 81, 1049, - 803, 603, 602, 804, 22, 1427, 1508, 1436, 610, 528, - 529, 604, 1494, 583, 1435, 801, 561, 1320, 604, 1427, - 1480, 379, 380, 561, 561, 561, 81, 844, 846, 847, - 1160, 266, 1155, 845, 1427, 583, 1300, 81, 1427, 1459, - 983, 561, 880, 654, 1427, 1426, 561, 561, 561, 798, - 561, 561, 665, 793, 1373, 1372, 320, 831, 561, 561, - 797, 911, 1360, 583, 305, 855, 343, 342, 345, 346, - 347, 348, 1357, 583, 1267, 344, 349, 852, 1310, 1309, - 890, 81, 850, 1306, 1307, 1306, 1305, 856, 1079, 583, - 1353, 833, 923, 583, 890, 583, 24, 897, 900, 779, - 777, 774, 549, 908, 723, 722, 704, 24, 542, 533, - 532, 1438, 892, 848, 81, 81, 928, 931, 932, 933, - 929, 266, 930, 934, 583, 1407, 1271, 1272, 923, 266, - 266, 1109, 51, 266, 266, 704, 1110, 266, 266, 266, - 81, 881, 882, 1308, 1139, 54, 54, 655, 1139, 705, - 58, 707, 1255, 81, 851, 1267, 54, 922, 1218, 904, - 916, 618, 617, 627, 628, 620, 621, 622, 623, 624, - 625, 626, 619, 697, 1151, 629, 801, 697, 705, 24, - 703, 697, 395, 923, 395, 395, 955, 395, 923, 395, - 938, 1079, 1267, 947, 698, 395, 950, 949, 698, 997, - 998, 999, 945, 1079, 953, 954, 1103, 266, 81, 1102, - 81, 970, 946, 1079, 703, 703, 266, 266, 266, 266, - 266, 709, 266, 266, 827, 607, 266, 81, 54, 1011, - 791, 1500, 1390, 991, 992, 993, 994, 1350, 307, 990, - 1365, 1010, 805, 1344, 1296, 266, 1154, 266, 266, 1002, - 1003, 1004, 266, 1006, 393, 1271, 1272, 1007, 1008, 1001, - 1000, 1207, 1391, 1013, 818, 1046, 1517, 967, 1513, 561, - 1298, 561, 1274, 1255, 1181, 618, 617, 627, 628, 620, - 621, 622, 623, 624, 625, 626, 619, 54, 561, 629, - 821, 841, 842, 795, 852, 1126, 1124, 395, 839, 1054, - 1127, 1125, 1128, 718, 932, 933, 1277, 1276, 1123, 893, - 894, 855, 1122, 899, 902, 903, 1056, 1055, 618, 617, - 627, 628, 620, 621, 622, 623, 624, 625, 626, 619, - 311, 312, 629, 856, 1504, 1497, 1214, 1051, 915, 1502, - 917, 918, 1068, 1067, 654, 1061, 1060, 895, 896, 1172, - 597, 597, 721, 585, 266, 266, 266, 266, 266, 1116, - 1474, 550, 582, 598, 598, 586, 266, 595, 1164, 266, - 1473, 851, 1405, 266, 1162, 1156, 1111, 266, 928, 931, - 932, 933, 929, 1351, 930, 934, 1386, 643, 644, 645, - 646, 647, 648, 649, 650, 892, 81, 1016, 697, 697, - 697, 697, 697, 794, 936, 961, 1059, 1152, 308, 309, - 302, 1112, 1113, 697, 1058, 698, 698, 698, 698, 698, - 1129, 697, 1096, 1444, 1141, 1137, 1142, 303, 1118, 1119, - 938, 1121, 1136, 395, 58, 1443, 1144, 1140, 698, 1393, - 395, 395, 395, 1117, 81, 81, 1120, 1171, 1161, 1173, - 1174, 1175, 1139, 1169, 1170, 575, 1519, 1518, 395, 1157, - 1158, 1091, 1088, 395, 395, 395, 814, 395, 395, 600, - 1519, 1455, 1379, 822, 81, 395, 395, 60, 62, 55, - 1, 1178, 1511, 1319, 1349, 1387, 1022, 1475, 266, 1420, - 1290, 1062, 973, 964, 69, 518, 68, 81, 1198, 1466, - 972, 971, 1428, 1377, 835, 984, 561, 1166, 987, 1297, - 967, 1163, 1471, 1052, 1053, 607, 591, 729, 395, 727, - 728, 1213, 618, 617, 627, 628, 620, 621, 622, 623, - 624, 625, 626, 619, 726, 561, 629, 731, 1077, 1078, - 730, 1221, 725, 81, 81, 1247, 1116, 1256, 1222, 279, - 388, 1228, 1241, 935, 717, 1012, 1240, 1094, 601, 883, - 72, 1184, 1200, 1259, 1199, 1018, 819, 81, 852, 1261, - 569, 570, 281, 1054, 637, 909, 1057, 1145, 394, 1080, - 1275, 1262, 81, 828, 81, 81, 589, 1442, 1392, 1282, - 1095, 1266, 913, 914, 664, 1289, 1098, 1281, 905, 328, - 843, 341, 1260, 338, 51, 339, 834, 1108, 611, 326, - 318, 1220, 266, 696, 1293, 1288, 689, 1284, 395, 927, - 925, 1301, 1302, 924, 383, 1273, 1294, 1295, 1269, 695, - 266, 395, 1217, 1346, 699, 1449, 81, 838, 26, 81, - 81, 81, 266, 59, 313, 1251, 81, 19, 18, 266, - 17, 20, 16, 15, 14, 832, 560, 538, 30, 21, - 13, 12, 11, 1325, 10, 9, 8, 7, 853, 6, - 263, 862, 863, 864, 865, 866, 867, 868, 869, 870, - 871, 872, 873, 874, 875, 876, 395, 5, 395, 4, - 1334, 304, 23, 697, 1326, 2, 967, 0, 967, 0, - 0, 384, 0, 0, 1116, 395, 522, 0, 524, 1327, - 698, 1352, 0, 0, 889, 891, 0, 1361, 0, 81, - 0, 1362, 0, 0, 0, 0, 912, 81, 1312, 0, - 1152, 1331, 1332, 0, 1333, 395, 0, 1335, 1345, 1337, - 0, 1313, 81, 1315, 0, 0, 0, 1371, 0, 81, - 0, 0, 0, 1223, 0, 0, 0, 0, 0, 0, - 1220, 0, 0, 0, 0, 0, 0, 1380, 0, 1382, - 1367, 1368, 1369, 618, 617, 627, 628, 620, 621, 622, - 623, 624, 625, 626, 619, 0, 1249, 629, 81, 81, - 0, 81, 0, 1374, 1394, 0, 81, 0, 81, 81, - 81, 266, 1406, 561, 81, 1414, 1259, 1415, 1417, 1418, - 0, 1401, 0, 1408, 0, 0, 0, 0, 1419, 0, - 1425, 81, 266, 0, 0, 1432, 1413, 0, 1283, 0, - 1440, 909, 1433, 967, 1434, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1260, 0, 0, 1409, 0, - 0, 0, 1456, 0, 0, 0, 0, 0, 81, 0, - 1259, 0, 1463, 1389, 1464, 0, 1457, 0, 0, 81, - 81, 0, 0, 0, 395, 0, 0, 0, 1437, 1478, - 1482, 1479, 0, 0, 0, 0, 531, 0, 0, 81, - 0, 539, 1116, 1487, 0, 0, 0, 546, 0, 1260, - 266, 51, 0, 548, 0, 0, 0, 0, 81, 0, - 0, 1343, 0, 0, 0, 0, 1069, 1070, 1071, 1496, - 0, 0, 1182, 395, 746, 0, 0, 0, 0, 1501, - 1503, 81, 563, 564, 0, 565, 0, 568, 0, 1348, - 1505, 0, 1075, 579, 1516, 0, 1076, 0, 1342, 654, - 0, 1527, 395, 0, 1081, 1082, 1083, 1363, 0, 0, - 1364, 1089, 0, 1366, 1092, 1093, 0, 0, 0, 0, - 1099, 0, 0, 0, 1101, 395, 588, 1104, 1105, 1106, - 1107, 0, 0, 1389, 967, 0, 618, 617, 627, 628, - 620, 621, 622, 623, 624, 625, 626, 619, 0, 1131, - 629, 1514, 0, 734, 0, 0, 0, 0, 0, 395, - 0, 0, 264, 0, 691, 290, 700, 0, 909, 0, - 0, 1263, 1265, 618, 617, 627, 628, 620, 621, 622, - 623, 624, 625, 626, 619, 0, 0, 629, 0, 0, - 317, 747, 0, 386, 0, 1265, 0, 0, 264, 0, - 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 395, 0, 395, 1292, 760, 763, 764, 765, 766, 767, - 768, 0, 769, 770, 771, 772, 773, 748, 749, 750, - 751, 732, 733, 761, 0, 735, 0, 736, 737, 738, - 739, 740, 741, 742, 743, 744, 745, 752, 753, 754, - 755, 756, 757, 758, 759, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1316, 0, 0, 1321, 1322, 1323, - 0, 0, 0, 0, 395, 0, 0, 0, 0, 0, - 1224, 1225, 1481, 654, 0, 0, 0, 0, 0, 1226, - 1227, 0, 0, 0, 1242, 1243, 0, 1244, 1245, 0, - 0, 0, 0, 0, 724, 762, 0, 0, 0, 1252, - 1253, 0, 0, 0, 780, 781, 0, 0, 0, 0, - 0, 1341, 790, 0, 0, 384, 909, 0, 796, 0, - 0, 775, 0, 0, 0, 0, 0, 0, 782, 783, - 784, 0, 809, 0, 0, 0, 0, 395, 0, 0, - 0, 0, 0, 0, 0, 1376, 802, 0, 0, 0, - 0, 806, 807, 808, 0, 810, 811, 0, 0, 0, - 395, 1299, 0, 812, 813, 0, 0, 395, 264, 0, - 0, 0, 0, 264, 0, 840, 0, 0, 0, 264, - 0, 0, 0, 0, 0, 264, 618, 617, 627, 628, - 620, 621, 622, 623, 624, 625, 626, 619, 0, 0, - 629, 0, 0, 0, 0, 0, 1410, 1411, 0, 1412, - 0, 0, 0, 0, 1376, 0, 1376, 1376, 1376, 0, - 1329, 0, 1292, 1328, 1074, 0, 0, 0, 0, 0, - 0, 1330, 0, 0, 0, 0, 0, 0, 0, 1376, - 0, 0, 1339, 1340, 618, 617, 627, 628, 620, 621, - 622, 623, 624, 625, 626, 619, 0, 0, 629, 0, - 0, 0, 1354, 1355, 1356, 921, 1359, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1470, 0, 948, 0, - 0, 0, 0, 1370, 0, 0, 0, 395, 395, 0, - 0, 0, 0, 0, 0, 0, 264, 264, 264, 0, - 0, 0, 0, 0, 909, 0, 0, 1489, 618, 617, - 627, 628, 620, 621, 622, 623, 624, 625, 626, 619, - 0, 0, 629, 0, 0, 0, 1495, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1396, 1397, - 1398, 1399, 1400, 0, 0, 0, 1403, 1404, 0, 1376, - 0, 1014, 0, 0, 0, 0, 0, 0, 0, 0, - 1034, 1035, 1036, 1037, 1038, 0, 1041, 1042, 0, 1416, - 1043, 0, 0, 0, 1015, 0, 1017, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1045, - 0, 0, 0, 1044, 0, 0, 1050, 0, 1445, 1446, - 1447, 1448, 0, 1452, 0, 1453, 1454, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1460, 0, 1461, - 1462, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, + 323, 1526, 1516, 1321, 1480, 654, 1392, 1379, 1210, 1118, + 327, 353, 1261, 340, 969, 1295, 942, 1425, 1262, 998, + 965, 1136, 1119, 940, 552, 1258, 968, 1012, 1163, 978, + 1268, 57, 81, 397, 301, 1274, 266, 1233, 801, 266, + 817, 879, 890, 1189, 1180, 886, 1075, 982, 715, 992, + 908, 292, 856, 585, 944, 591, 714, 521, 386, 701, + 391, 922, 1008, 310, 929, 597, 606, 266, 81, 383, + 325, 388, 266, 1142, 266, 696, 668, 704, 56, 1519, + 1503, 61, 695, 669, 1514, 653, 3, 1031, 1490, 1511, + 1322, 541, 1502, 563, 1489, 263, 293, 294, 295, 296, + 1250, 1030, 299, 329, 1351, 526, 314, 63, 64, 65, + 66, 67, 1454, 619, 618, 628, 629, 621, 622, 623, + 624, 625, 626, 627, 620, 1289, 385, 630, 556, 1151, + 1035, 523, 1150, 525, 716, 1152, 717, 300, 365, 1029, + 371, 372, 369, 370, 368, 367, 366, 1290, 1291, 960, + 961, 959, 579, 298, 373, 374, 261, 257, 258, 259, + 574, 394, 297, 1171, 575, 572, 573, 1234, 991, 1212, + 1382, 1399, 253, 999, 1342, 251, 1340, 255, 291, 790, + 577, 567, 568, 789, 1214, 787, 1513, 1510, 1481, 1026, + 1023, 1024, 1209, 1022, 558, 923, 560, 983, 1473, 1534, + 542, 1215, 528, 255, 1426, 1236, 794, 1530, 778, 1137, + 1139, 578, 1284, 985, 1283, 1282, 524, 1428, 788, 791, + 531, 268, 1213, 1434, 1206, 1033, 1036, 557, 559, 538, + 1208, 256, 1043, 642, 643, 1042, 1093, 1462, 1090, 1238, + 1362, 1242, 1219, 1237, 1147, 1235, 1103, 1069, 985, 828, + 1240, 710, 266, 610, 548, 1307, 966, 266, 620, 1239, + 630, 630, 1028, 266, 24, 25, 52, 27, 28, 266, + 254, 955, 1241, 1243, 81, 260, 81, 81, 1164, 81, + 825, 81, 1455, 43, 1027, 1427, 1138, 81, 29, 48, + 49, 252, 535, 605, 536, 822, 1471, 537, 522, 1443, + 554, 1272, 718, 999, 818, 1488, 1308, 70, 276, 38, + 984, 532, 555, 54, 1252, 1528, 540, 81, 1529, 780, + 1527, 909, 547, 1032, 1476, 1435, 1433, 1207, 549, 1205, + 1169, 520, 863, 286, 54, 1088, 593, 1087, 1034, 642, + 643, 642, 643, 71, 859, 984, 861, 862, 860, 581, + 582, 544, 545, 546, 604, 603, 619, 618, 628, 629, + 621, 622, 623, 624, 625, 626, 627, 620, 1089, 603, + 630, 605, 600, 1494, 31, 32, 34, 33, 36, 553, + 50, 266, 266, 266, 269, 605, 819, 1388, 988, 595, + 81, 272, 594, 909, 989, 1100, 81, 1535, 1387, 280, + 275, 527, 37, 44, 45, 694, 1184, 46, 47, 35, + 1076, 621, 622, 623, 624, 625, 626, 627, 620, 604, + 603, 630, 1183, 39, 40, 985, 41, 42, 640, 584, + 1172, 880, 278, 881, 1348, 1496, 605, 1536, 285, 1153, + 693, 1154, 702, 671, 673, 675, 677, 679, 681, 682, + 672, 674, 703, 678, 680, 522, 683, 846, 848, 849, + 1472, 712, 708, 847, 1406, 270, 619, 618, 628, 629, + 621, 622, 623, 624, 625, 626, 627, 620, 529, 530, + 630, 1385, 1181, 394, 1052, 699, 623, 624, 625, 626, + 627, 620, 282, 273, 630, 283, 284, 289, 831, 832, + 806, 274, 277, 22, 271, 288, 287, 1469, 53, 619, + 618, 628, 629, 621, 622, 623, 624, 625, 626, 627, + 620, 266, 984, 630, 604, 603, 81, 981, 979, 1324, + 980, 266, 266, 81, 81, 81, 977, 983, 1164, 266, + 827, 605, 266, 1431, 1512, 266, 1159, 604, 603, 266, + 250, 81, 882, 352, 1498, 584, 81, 81, 81, 266, + 81, 81, 800, 305, 605, 799, 604, 603, 81, 81, + 913, 1431, 1484, 1254, 1431, 584, 805, 781, 826, 779, + 726, 776, 803, 605, 550, 79, 1066, 1067, 1068, 543, + 782, 783, 534, 1354, 533, 604, 603, 81, 792, 1431, + 1463, 385, 266, 584, 798, 1431, 1430, 1440, 81, 889, + 1439, 795, 605, 1377, 1376, 380, 381, 1304, 811, 354, + 51, 396, 1364, 584, 986, 857, 1361, 584, 833, 1314, + 1313, 619, 618, 628, 629, 621, 622, 623, 624, 625, + 626, 627, 620, 1271, 854, 630, 1310, 1311, 1259, 852, + 1211, 1271, 81, 343, 342, 345, 346, 347, 348, 835, + 892, 842, 344, 349, 1310, 1309, 1082, 584, 926, 584, + 1222, 51, 899, 902, 850, 892, 584, 706, 910, 306, + 725, 724, 58, 1357, 706, 81, 81, 24, 931, 934, + 935, 936, 932, 266, 933, 937, 1143, 24, 1275, 1276, + 24, 266, 266, 1442, 853, 266, 266, 1143, 926, 266, + 266, 266, 81, 883, 884, 1082, 1411, 858, 1312, 925, + 707, 1113, 709, 1082, 1155, 81, 1114, 707, 950, 705, + 906, 958, 952, 918, 919, 1106, 54, 949, 1105, 705, + 926, 1082, 894, 705, 803, 926, 54, 307, 711, 54, + 1395, 1271, 924, 829, 793, 54, 1504, 1394, 993, 1000, + 1001, 1002, 1369, 1013, 948, 951, 1300, 1158, 953, 1009, + 957, 1004, 956, 1275, 1276, 1521, 1003, 1016, 1517, 266, + 81, 1302, 81, 973, 994, 995, 996, 997, 266, 266, + 266, 266, 266, 1278, 266, 266, 54, 1259, 266, 81, + 1005, 1006, 1007, 1014, 699, 394, 1185, 823, 699, 797, + 1130, 1128, 699, 1197, 841, 1131, 1129, 266, 970, 266, + 266, 1281, 1280, 1132, 266, 935, 936, 396, 1054, 396, + 396, 1127, 396, 1126, 396, 1010, 1011, 1508, 1017, 1501, + 396, 1218, 1049, 1195, 311, 312, 1506, 1037, 1038, 1039, + 1040, 1041, 598, 1044, 1045, 1064, 1063, 1046, 1176, 723, + 598, 1168, 854, 586, 551, 599, 1478, 1057, 596, 1477, + 608, 583, 857, 599, 1409, 587, 1048, 1166, 1160, 1355, + 1390, 1448, 1019, 1053, 796, 1058, 939, 1059, 308, 309, + 302, 1062, 303, 562, 58, 562, 562, 1447, 562, 1061, + 562, 931, 934, 935, 936, 932, 562, 933, 937, 1397, + 1196, 1143, 576, 1071, 1094, 1201, 1198, 1191, 1199, 1194, + 1091, 1190, 853, 816, 1192, 1193, 51, 266, 266, 266, + 266, 266, 1523, 1522, 1523, 601, 1120, 1459, 1200, 266, + 1383, 639, 266, 396, 641, 824, 266, 60, 62, 720, + 266, 55, 1, 1515, 1323, 1391, 1025, 1479, 1099, 1424, + 1294, 976, 967, 69, 858, 519, 68, 1470, 975, 81, + 974, 1432, 652, 1144, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 1156, 667, 670, 670, 670, 676, 670, + 670, 676, 670, 684, 685, 686, 687, 688, 689, 690, + 1141, 700, 1133, 1122, 1123, 1381, 1125, 1148, 1115, 1121, + 987, 1165, 1124, 1170, 1173, 1174, 1347, 81, 81, 1175, + 990, 1177, 1178, 1179, 1145, 1301, 1146, 894, 1161, 1162, + 699, 699, 699, 699, 699, 1167, 1475, 731, 729, 730, + 728, 733, 732, 727, 279, 699, 389, 81, 938, 1182, + 719, 1015, 602, 699, 72, 1204, 1203, 1021, 821, 570, + 571, 266, 970, 281, 638, 1060, 1149, 1202, 395, 1266, + 81, 830, 590, 1188, 1446, 1396, 1098, 665, 907, 396, + 328, 845, 341, 338, 339, 836, 396, 396, 396, 1217, + 1112, 619, 618, 628, 629, 621, 622, 623, 624, 625, + 626, 627, 620, 320, 396, 630, 612, 326, 318, 396, + 396, 396, 698, 396, 396, 691, 1225, 81, 81, 1226, + 1220, 396, 396, 1260, 1120, 1232, 1263, 1245, 930, 1251, + 1244, 928, 927, 384, 1277, 1273, 697, 854, 1221, 1350, + 1453, 81, 1057, 840, 26, 562, 59, 313, 19, 18, + 837, 1346, 562, 562, 562, 17, 81, 20, 81, 81, + 1279, 608, 16, 1224, 396, 834, 15, 14, 539, 30, + 562, 1285, 1293, 21, 1286, 562, 562, 562, 13, 562, + 562, 12, 1292, 11, 10, 9, 266, 562, 562, 1297, + 1298, 1299, 1270, 8, 1305, 1306, 7, 1255, 6, 5, + 4, 304, 1265, 23, 266, 885, 2, 0, 0, 0, + 81, 0, 0, 81, 81, 81, 266, 0, 1288, 0, + 81, 911, 0, 266, 891, 893, 619, 618, 628, 629, + 621, 622, 623, 624, 625, 626, 627, 620, 915, 916, + 630, 1316, 1329, 0, 0, 1315, 1331, 0, 0, 970, + 0, 970, 0, 0, 1317, 0, 1319, 0, 0, 0, + 0, 51, 0, 1318, 0, 396, 1338, 895, 896, 0, + 0, 901, 904, 905, 0, 1328, 656, 0, 396, 0, + 0, 0, 1120, 0, 1335, 1336, 1356, 1337, 0, 316, + 1339, 0, 1341, 81, 1366, 1330, 917, 0, 0, 920, + 921, 81, 1365, 0, 0, 0, 0, 1156, 0, 0, + 0, 0, 0, 1224, 0, 0, 81, 0, 0, 0, + 941, 1375, 0, 81, 700, 0, 699, 0, 700, 1384, + 0, 1386, 0, 396, 0, 396, 628, 629, 621, 622, + 623, 624, 625, 626, 627, 620, 1378, 0, 630, 0, + 0, 0, 396, 0, 0, 0, 1398, 0, 0, 0, + 0, 0, 81, 81, 0, 81, 0, 0, 0, 1263, + 81, 0, 81, 81, 81, 266, 1410, 1418, 81, 1419, + 1421, 1422, 396, 0, 0, 1405, 970, 0, 0, 0, + 0, 1423, 0, 1429, 0, 81, 266, 1436, 0, 562, + 1417, 562, 1444, 0, 0, 1437, 0, 1438, 0, 0, + 0, 0, 0, 0, 0, 0, 1393, 0, 562, 0, + 0, 0, 0, 1263, 1460, 0, 0, 0, 0, 0, + 0, 0, 81, 0, 644, 645, 646, 647, 648, 649, + 650, 651, 1468, 81, 81, 1467, 1412, 0, 0, 1482, + 1065, 0, 0, 1078, 1486, 1445, 0, 1079, 1483, 0, + 0, 0, 0, 81, 0, 1084, 1085, 1086, 0, 1491, + 1120, 0, 1092, 1070, 266, 1095, 1096, 0, 0, 911, + 0, 1102, 81, 0, 0, 1104, 0, 0, 1107, 1108, + 1109, 1110, 1111, 1500, 0, 0, 0, 1080, 1081, 1461, + 0, 0, 0, 1505, 1507, 81, 0, 0, 0, 0, + 561, 1135, 1509, 0, 0, 0, 1097, 0, 1520, 0, + 0, 0, 396, 0, 0, 1531, 0, 0, 0, 0, + 0, 0, 0, 1495, 1353, 0, 1393, 970, 0, 0, + 0, 0, 1116, 1117, 0, 0, 700, 700, 700, 700, + 700, 0, 0, 0, 0, 589, 0, 0, 0, 0, + 0, 941, 0, 1140, 0, 0, 0, 0, 0, 700, + 1186, 396, 619, 618, 628, 629, 621, 622, 623, 624, + 625, 626, 627, 620, 0, 0, 630, 0, 0, 0, + 0, 264, 588, 592, 290, 0, 0, 1345, 0, 0, + 396, 0, 0, 0, 0, 0, 0, 0, 0, 611, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 317, + 0, 0, 387, 396, 0, 0, 0, 264, 0, 264, + 0, 0, 0, 0, 0, 0, 0, 562, 0, 0, + 0, 0, 0, 0, 655, 0, 0, 0, 0, 0, + 0, 1230, 1231, 666, 0, 0, 0, 396, 0, 0, + 0, 0, 0, 0, 0, 0, 562, 911, 0, 0, + 1267, 1269, 619, 618, 628, 629, 621, 622, 623, 624, + 625, 626, 627, 620, 0, 0, 630, 0, 0, 0, + 0, 0, 0, 0, 1269, 0, 619, 618, 628, 629, + 621, 622, 623, 624, 625, 626, 627, 620, 0, 396, + 630, 396, 1296, 0, 0, 0, 855, 1227, 0, 864, + 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, + 875, 876, 877, 878, 1264, 0, 51, 619, 618, 628, + 629, 621, 622, 623, 624, 625, 626, 627, 620, 0, + 0, 630, 618, 628, 629, 621, 622, 623, 624, 625, + 626, 627, 620, 1320, 0, 630, 1325, 1326, 1327, 0, + 0, 0, 0, 396, 914, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 564, 565, 0, 566, + 0, 569, 0, 0, 0, 0, 1332, 580, 0, 0, + 0, 0, 0, 0, 1334, 0, 0, 264, 0, 0, + 0, 0, 264, 0, 0, 1343, 1344, 0, 264, 0, + 0, 0, 0, 0, 264, 911, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1358, 1359, 1360, 0, 1363, + 0, 0, 700, 0, 807, 0, 396, 0, 0, 0, + 0, 0, 0, 0, 1380, 0, 1374, 1077, 0, 0, + 0, 0, 0, 0, 0, 0, 820, 0, 0, 396, + 1349, 0, 0, 0, 0, 0, 396, 619, 618, 628, + 629, 621, 622, 623, 624, 625, 626, 627, 620, 0, + 0, 630, 0, 843, 844, 0, 0, 0, 0, 0, + 0, 0, 1371, 1372, 1373, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1414, 1415, 0, 1416, 0, + 0, 0, 0, 1380, 0, 1380, 1380, 1380, 0, 0, + 0, 1296, 0, 0, 0, 562, 264, 264, 264, 0, + 0, 0, 1420, 0, 0, 0, 655, 0, 1380, 897, + 898, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1072, 1073, 1074, 0, 0, + 0, 1449, 1450, 1451, 1452, 0, 1456, 1264, 1457, 1458, + 1413, 0, 0, 0, 0, 1474, 0, 0, 0, 0, + 1464, 0, 1465, 1466, 0, 0, 396, 396, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 964, 0, + 1441, 0, 0, 911, 0, 0, 1493, 0, 0, 0, + 0, 0, 0, 0, 1487, 0, 0, 0, 0, 0, + 0, 1264, 1492, 51, 0, 1499, 777, 0, 0, 0, + 0, 0, 0, 784, 785, 786, 0, 0, 0, 0, + 1497, 0, 0, 0, 0, 0, 0, 0, 1380, 0, + 0, 804, 0, 0, 0, 0, 808, 809, 810, 0, + 812, 813, 0, 0, 0, 0, 264, 0, 814, 815, 0, 0, 0, 0, 0, 0, 264, 264, 0, 0, - 0, 1483, 0, 0, 264, 0, 0, 264, 0, 1488, - 264, 0, 0, 0, 800, 0, 0, 0, 0, 0, - 613, 0, 616, 0, 264, 0, 0, 1493, 630, 631, - 632, 633, 634, 635, 636, 0, 614, 615, 612, 618, - 617, 627, 628, 620, 621, 622, 623, 624, 625, 626, - 619, 0, 0, 629, 0, 0, 0, 0, 0, 0, - 0, 1520, 0, 0, 0, 0, 0, 264, 0, 0, - 0, 0, 1528, 1529, 0, 0, 800, 0, 0, 0, - 0, 0, 0, 0, 0, 24, 25, 52, 27, 28, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 43, 0, 0, 0, 0, 29, - 48, 49, 0, 0, 0, 0, 0, 0, 317, 0, - 0, 0, 0, 317, 317, 0, 0, 317, 317, 317, - 38, 0, 0, 910, 54, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 317, 317, 317, 317, 0, 264, 0, 0, - 0, 1183, 0, 0, 0, 264, 943, 0, 0, 264, - 264, 0, 0, 264, 951, 800, 0, 0, 0, 0, - 0, 0, 1216, 0, 0, 0, 0, 0, 0, 0, - 1212, 0, 0, 0, 0, 31, 32, 34, 33, 36, - 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 37, 44, 45, 0, 0, 46, 47, - 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 264, 39, 40, 0, 41, 42, 0, - 0, 0, 264, 264, 264, 264, 264, 0, 264, 264, - 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 264, 0, 1047, 1048, 0, 0, 0, 264, 0, - 0, 0, 0, 800, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 317, 1311, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1314, 0, 0, 0, 0, 53, - 0, 0, 0, 0, 0, 0, 1324, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 317, 317, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 910, - 264, 264, 264, 264, 264, 0, 0, 0, 0, 0, - 0, 0, 1130, 0, 0, 264, 0, 0, 0, 943, - 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1385, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 264, 1532, 1533, 264, 0, 0, + 264, 0, 0, 0, 802, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 264, 0, 1055, 1056, 614, 592, + 617, 0, 0, 0, 0, 0, 631, 632, 633, 634, + 635, 636, 637, 1518, 615, 616, 613, 619, 618, 628, + 629, 621, 622, 623, 624, 625, 626, 627, 620, 0, + 0, 630, 0, 0, 0, 0, 0, 264, 0, 0, + 0, 0, 0, 0, 0, 0, 802, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1228, 1229, 1083, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1246, 1247, 0, 1248, 1249, 1101, + 0, 0, 0, 0, 0, 0, 0, 0, 317, 1256, + 1257, 0, 0, 317, 317, 0, 0, 317, 317, 317, + 0, 0, 0, 912, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 317, 317, 317, 317, 317, 0, 264, 0, + 0, 0, 0, 0, 0, 0, 264, 946, 0, 0, + 264, 264, 0, 0, 264, 954, 802, 0, 0, 0, + 0, 0, 1303, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1018, 0, 1020, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1047, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1333, 0, 0, 264, 0, 0, 0, 0, 0, + 0, 0, 0, 264, 264, 264, 264, 264, 0, 264, + 264, 0, 0, 264, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 264, 0, 1050, 1051, 0, 0, 0, 264, + 0, 0, 0, 0, 802, 0, 0, 0, 0, 0, + 1253, 0, 0, 0, 0, 0, 317, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 748, + 0, 0, 0, 1287, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 317, 317, 0, 0, 0, 0, 1400, + 1401, 1402, 1403, 1404, 0, 0, 0, 1407, 1408, 0, + 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 912, 264, 264, 264, 264, 264, 0, 0, 0, + 0, 0, 0, 0, 1134, 0, 0, 264, 0, 0, + 0, 946, 0, 0, 0, 264, 0, 0, 736, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1187, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1352, 0, 749, 0, 0, 0, + 0, 0, 0, 0, 655, 0, 0, 1216, 0, 0, + 0, 0, 1367, 0, 0, 1368, 0, 0, 1370, 762, + 765, 766, 767, 768, 769, 770, 0, 771, 772, 773, + 774, 775, 750, 751, 752, 753, 734, 735, 763, 0, + 737, 0, 738, 739, 740, 741, 742, 743, 744, 745, + 746, 747, 754, 755, 756, 757, 758, 759, 760, 761, + 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, + 0, 0, 1524, 0, 0, 0, 317, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 802, + 764, 0, 0, 0, 0, 0, 0, 0, 0, 912, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1441, 0, 0, 0, - 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 800, 0, 0, - 0, 0, 0, 0, 0, 0, 910, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1491, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1485, 655, 0, + 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, + 0, 264, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, - 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1389, 912, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 910, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1166,1087 +1168,1252 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 943, 0, 0, - 0, 0, 0, 505, 493, 0, 450, 508, 423, 440, - 516, 441, 444, 481, 408, 463, 165, 438, 264, 427, - 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, - 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, - 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, - 439, 479, 510, 0, 0, 0, 80, 0, 968, 969, - 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, - 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, - 432, 1153, 910, 0, 0, 0, 0, 0, 453, 462, - 487, 447, 0, 0, 0, 0, 264, 0, 0, 0, - 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, - 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, - 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, - 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, - 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, - 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, - 138, 246, 186, 116, 235, 401, 414, 109, 424, 0, - 0, 437, 442, 443, 455, 457, 458, 459, 460, 467, - 474, 475, 477, 483, 484, 485, 486, 491, 498, 517, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 505, 493, 0, 450, - 508, 423, 440, 516, 441, 444, 481, 408, 463, 165, - 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, - 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, - 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, - 413, 471, 509, 439, 479, 510, 0, 0, 0, 80, - 0, 968, 969, 0, 0, 0, 0, 0, 101, 0, - 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, - 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, - 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, - 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, - 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, - 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, - 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, - 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, + 946, 0, 0, 0, 0, 0, 506, 494, 0, 451, + 509, 424, 441, 517, 442, 445, 482, 409, 464, 165, + 439, 264, 428, 404, 435, 405, 426, 453, 111, 457, + 423, 496, 467, 508, 137, 429, 515, 139, 473, 0, + 211, 153, 0, 0, 455, 498, 462, 491, 450, 483, + 414, 472, 510, 440, 480, 511, 0, 0, 0, 80, + 0, 971, 972, 0, 0, 0, 0, 0, 101, 0, + 477, 505, 437, 479, 481, 403, 474, 0, 407, 410, + 516, 501, 432, 433, 1157, 912, 0, 0, 0, 0, + 0, 454, 463, 488, 448, 0, 0, 0, 0, 264, + 0, 0, 0, 430, 0, 471, 0, 0, 0, 411, + 408, 0, 0, 452, 0, 0, 0, 413, 0, 431, + 489, 0, 401, 119, 493, 500, 449, 267, 504, 447, + 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 497, 427, 436, 105, 434, + 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, + 154, 156, 0, 406, 0, 212, 234, 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, - 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, - 468, 82, 91, 138, 246, 186, 116, 235, 401, 414, - 109, 424, 0, 0, 437, 442, 443, 455, 457, 458, - 459, 460, 467, 474, 475, 477, 483, 484, 485, 486, - 491, 498, 517, 84, 85, 92, 98, 104, 108, 112, + 233, 210, 418, 421, 416, 417, 465, 466, 512, 513, + 514, 490, 412, 0, 419, 420, 0, 495, 502, 503, + 469, 82, 91, 138, 246, 186, 116, 235, 402, 415, + 109, 425, 0, 0, 438, 443, 444, 456, 458, 459, + 460, 461, 468, 475, 476, 478, 484, 485, 486, 487, + 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 505, - 493, 0, 450, 508, 423, 440, 516, 441, 444, 481, - 408, 463, 165, 438, 0, 427, 403, 434, 404, 425, - 452, 111, 456, 422, 495, 466, 507, 137, 428, 514, - 139, 472, 0, 211, 153, 0, 0, 454, 497, 461, - 490, 449, 482, 413, 471, 509, 439, 479, 510, 54, - 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 0, 476, 504, 436, 478, 480, 402, 473, - 0, 406, 409, 515, 500, 431, 432, 0, 0, 0, - 0, 0, 0, 0, 453, 462, 487, 447, 0, 0, - 0, 0, 0, 0, 0, 0, 429, 0, 470, 0, - 0, 0, 410, 407, 0, 0, 451, 0, 0, 0, - 412, 0, 430, 488, 0, 400, 119, 492, 499, 448, - 267, 503, 446, 445, 506, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 496, 426, - 435, 105, 433, 193, 172, 231, 469, 174, 192, 140, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 506, + 494, 0, 451, 509, 424, 441, 517, 442, 445, 482, + 409, 464, 165, 439, 0, 428, 404, 435, 405, 426, + 453, 111, 457, 423, 496, 467, 508, 137, 429, 515, + 139, 473, 0, 211, 153, 0, 0, 455, 498, 462, + 491, 450, 483, 414, 472, 510, 440, 480, 511, 0, + 0, 0, 80, 0, 971, 972, 0, 0, 0, 0, + 0, 101, 0, 477, 505, 437, 479, 481, 403, 474, + 0, 407, 410, 516, 501, 432, 433, 0, 0, 0, + 0, 0, 0, 0, 454, 463, 488, 448, 0, 0, + 0, 0, 0, 0, 0, 0, 430, 0, 471, 0, + 0, 0, 411, 408, 0, 0, 452, 0, 0, 0, + 413, 0, 431, 489, 0, 401, 119, 493, 500, 449, + 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 497, 427, + 436, 105, 434, 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 405, 0, 212, 234, - 249, 99, 421, 219, 243, 244, 0, 0, 100, 118, + 183, 125, 155, 154, 156, 0, 406, 0, 212, 234, + 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 417, 420, 415, 416, 464, - 465, 511, 512, 513, 489, 411, 0, 418, 419, 0, - 494, 501, 502, 468, 82, 91, 138, 246, 186, 116, - 235, 401, 414, 109, 424, 0, 0, 437, 442, 443, - 455, 457, 458, 459, 460, 467, 474, 475, 477, 483, - 484, 485, 486, 491, 498, 517, 84, 85, 92, 98, + 171, 194, 103, 233, 210, 418, 421, 416, 417, 465, + 466, 512, 513, 514, 490, 412, 0, 419, 420, 0, + 495, 502, 503, 469, 82, 91, 138, 246, 186, 116, + 235, 402, 415, 109, 425, 0, 0, 438, 443, 444, + 456, 458, 459, 460, 461, 468, 475, 476, 478, 484, + 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 505, 493, 0, 450, 508, 423, 440, 516, - 441, 444, 481, 408, 463, 165, 438, 0, 427, 403, - 434, 404, 425, 452, 111, 456, 422, 495, 466, 507, - 137, 428, 514, 139, 472, 0, 211, 153, 0, 0, - 454, 497, 461, 490, 449, 482, 413, 471, 509, 439, - 479, 510, 0, 0, 0, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 476, 504, 436, 478, - 480, 402, 473, 0, 406, 409, 515, 500, 431, 432, - 0, 0, 0, 0, 0, 0, 0, 453, 462, 487, - 447, 0, 0, 0, 0, 0, 0, 1219, 0, 429, - 0, 470, 0, 0, 0, 410, 407, 0, 0, 451, - 0, 0, 0, 412, 0, 430, 488, 0, 400, 119, - 492, 499, 448, 267, 503, 446, 445, 506, 184, 0, + 239, 242, 506, 494, 0, 451, 509, 424, 441, 517, + 442, 445, 482, 409, 464, 165, 439, 0, 428, 404, + 435, 405, 426, 453, 111, 457, 423, 496, 467, 508, + 137, 429, 515, 139, 473, 0, 211, 153, 0, 0, + 455, 498, 462, 491, 450, 483, 414, 472, 510, 440, + 480, 511, 54, 0, 0, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 101, 0, 477, 505, 437, 479, + 481, 403, 474, 0, 407, 410, 516, 501, 432, 433, + 0, 0, 0, 0, 0, 0, 0, 454, 463, 488, + 448, 0, 0, 0, 0, 0, 0, 0, 0, 430, + 0, 471, 0, 0, 0, 411, 408, 0, 0, 452, + 0, 0, 0, 413, 0, 431, 489, 0, 401, 119, + 493, 500, 449, 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 496, 426, 435, 105, 433, 193, 172, 231, 469, + 195, 497, 427, 436, 105, 434, 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 405, - 0, 212, 234, 249, 99, 421, 219, 243, 244, 0, + 124, 182, 142, 183, 125, 155, 154, 156, 0, 406, + 0, 212, 234, 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 417, 420, - 415, 416, 464, 465, 511, 512, 513, 489, 411, 0, - 418, 419, 0, 494, 501, 502, 468, 82, 91, 138, - 246, 186, 116, 235, 401, 414, 109, 424, 0, 0, - 437, 442, 443, 455, 457, 458, 459, 460, 467, 474, - 475, 477, 483, 484, 485, 486, 491, 498, 517, 84, + 141, 188, 247, 171, 194, 103, 233, 210, 418, 421, + 416, 417, 465, 466, 512, 513, 514, 490, 412, 0, + 419, 420, 0, 495, 502, 503, 469, 82, 91, 138, + 246, 186, 116, 235, 402, 415, 109, 425, 0, 0, + 438, 443, 444, 456, 458, 459, 460, 461, 468, 475, + 476, 478, 484, 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 505, 493, 0, 450, 508, - 423, 440, 516, 441, 444, 481, 408, 463, 165, 438, - 0, 427, 403, 434, 404, 425, 452, 111, 456, 422, - 495, 466, 507, 137, 428, 514, 139, 472, 0, 211, - 153, 0, 0, 454, 497, 461, 490, 449, 482, 413, - 471, 509, 439, 479, 510, 0, 0, 0, 265, 0, - 0, 0, 0, 0, 0, 0, 0, 101, 0, 476, - 504, 436, 478, 480, 402, 473, 0, 406, 409, 515, - 500, 431, 432, 0, 0, 0, 0, 0, 0, 0, - 453, 462, 487, 447, 0, 0, 0, 0, 0, 0, - 952, 0, 429, 0, 470, 0, 0, 0, 410, 407, - 0, 0, 451, 0, 0, 0, 412, 0, 430, 488, - 0, 400, 119, 492, 499, 448, 267, 503, 446, 445, - 506, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 496, 426, 435, 105, 433, 193, - 172, 231, 469, 174, 192, 140, 221, 185, 230, 240, + 222, 223, 232, 239, 242, 506, 494, 0, 451, 509, + 424, 441, 517, 442, 445, 482, 409, 464, 165, 439, + 0, 428, 404, 435, 405, 426, 453, 111, 457, 423, + 496, 467, 508, 137, 429, 515, 139, 473, 0, 211, + 153, 0, 0, 455, 498, 462, 491, 450, 483, 414, + 472, 510, 440, 480, 511, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 101, 0, 477, + 505, 437, 479, 481, 403, 474, 0, 407, 410, 516, + 501, 432, 433, 0, 0, 0, 0, 0, 0, 0, + 454, 463, 488, 448, 0, 0, 0, 0, 0, 0, + 1223, 0, 430, 0, 471, 0, 0, 0, 411, 408, + 0, 0, 452, 0, 0, 0, 413, 0, 431, 489, + 0, 401, 119, 493, 500, 449, 267, 504, 447, 446, + 507, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 497, 427, 436, 105, 434, 193, + 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 405, 0, 212, 234, 249, 99, 421, 219, + 156, 0, 406, 0, 212, 234, 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 417, 420, 415, 416, 464, 465, 511, 512, 513, - 489, 411, 0, 418, 419, 0, 494, 501, 502, 468, - 82, 91, 138, 246, 186, 116, 235, 401, 414, 109, - 424, 0, 0, 437, 442, 443, 455, 457, 458, 459, - 460, 467, 474, 475, 477, 483, 484, 485, 486, 491, - 498, 517, 84, 85, 92, 98, 104, 108, 112, 115, + 210, 418, 421, 416, 417, 465, 466, 512, 513, 514, + 490, 412, 0, 419, 420, 0, 495, 502, 503, 469, + 82, 91, 138, 246, 186, 116, 235, 402, 415, 109, + 425, 0, 0, 438, 443, 444, 456, 458, 459, 460, + 461, 468, 475, 476, 478, 484, 485, 486, 487, 492, + 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 505, 493, - 0, 450, 508, 423, 440, 516, 441, 444, 481, 408, - 463, 165, 438, 0, 427, 403, 434, 404, 425, 452, - 111, 456, 422, 495, 466, 507, 137, 428, 514, 139, - 472, 0, 211, 153, 0, 0, 454, 497, 461, 490, - 449, 482, 413, 471, 509, 439, 479, 510, 0, 0, - 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, - 101, 0, 476, 504, 436, 478, 480, 402, 473, 0, - 406, 409, 515, 500, 431, 432, 0, 0, 0, 0, - 0, 0, 0, 453, 462, 487, 447, 0, 0, 0, - 0, 0, 0, 849, 0, 429, 0, 470, 0, 0, - 0, 410, 407, 0, 0, 451, 0, 0, 0, 412, - 0, 430, 488, 0, 400, 119, 492, 499, 448, 267, - 503, 446, 445, 506, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 496, 426, 435, - 105, 433, 193, 172, 231, 469, 174, 192, 140, 221, + 207, 213, 216, 222, 223, 232, 239, 242, 506, 494, + 0, 451, 509, 424, 441, 517, 442, 445, 482, 409, + 464, 165, 439, 0, 428, 404, 435, 405, 426, 453, + 111, 457, 423, 496, 467, 508, 137, 429, 515, 139, + 473, 0, 211, 153, 0, 0, 455, 498, 462, 491, + 450, 483, 414, 472, 510, 440, 480, 511, 0, 0, + 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 0, 477, 505, 437, 479, 481, 403, 474, 0, + 407, 410, 516, 501, 432, 433, 0, 0, 0, 0, + 0, 0, 0, 454, 463, 488, 448, 0, 0, 0, + 0, 0, 0, 955, 0, 430, 0, 471, 0, 0, + 0, 411, 408, 0, 0, 452, 0, 0, 0, 413, + 0, 431, 489, 0, 401, 119, 493, 500, 449, 267, + 504, 447, 446, 507, 184, 0, 215, 122, 136, 97, + 83, 93, 0, 121, 162, 191, 195, 497, 427, 436, + 105, 434, 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 405, 0, 212, 234, 249, - 99, 421, 219, 243, 244, 0, 0, 100, 118, 113, + 125, 155, 154, 156, 0, 406, 0, 212, 234, 249, + 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 417, 420, 415, 416, 464, 465, - 511, 512, 513, 489, 411, 0, 418, 419, 0, 494, - 501, 502, 468, 82, 91, 138, 246, 186, 116, 235, - 401, 414, 109, 424, 0, 0, 437, 442, 443, 455, - 457, 458, 459, 460, 467, 474, 475, 477, 483, 484, - 485, 486, 491, 498, 517, 84, 85, 92, 98, 104, + 194, 103, 233, 210, 418, 421, 416, 417, 465, 466, + 512, 513, 514, 490, 412, 0, 419, 420, 0, 495, + 502, 503, 469, 82, 91, 138, 246, 186, 116, 235, + 402, 415, 109, 425, 0, 0, 438, 443, 444, 456, + 458, 459, 460, 461, 468, 475, 476, 478, 484, 485, + 486, 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 505, 493, 0, 450, 508, 423, 440, 516, 441, - 444, 481, 408, 463, 165, 438, 0, 427, 403, 434, - 404, 425, 452, 111, 456, 422, 495, 466, 507, 137, - 428, 514, 139, 472, 0, 211, 153, 0, 0, 454, - 497, 461, 490, 449, 482, 413, 471, 509, 439, 479, - 510, 0, 0, 0, 80, 0, 0, 0, 0, 0, - 0, 0, 0, 101, 0, 476, 504, 436, 478, 480, - 402, 473, 0, 406, 409, 515, 500, 431, 432, 0, - 0, 0, 0, 0, 0, 0, 453, 462, 487, 447, - 0, 0, 0, 0, 0, 0, 0, 0, 429, 0, - 470, 0, 0, 0, 410, 407, 0, 0, 451, 0, - 0, 0, 412, 0, 430, 488, 0, 400, 119, 492, - 499, 448, 267, 503, 446, 445, 506, 184, 0, 215, + 242, 506, 494, 0, 451, 509, 424, 441, 517, 442, + 445, 482, 409, 464, 165, 439, 0, 428, 404, 435, + 405, 426, 453, 111, 457, 423, 496, 467, 508, 137, + 429, 515, 139, 473, 0, 211, 153, 0, 0, 455, + 498, 462, 491, 450, 483, 414, 472, 510, 440, 480, + 511, 0, 0, 0, 322, 0, 0, 0, 0, 0, + 0, 0, 0, 101, 0, 477, 505, 437, 479, 481, + 403, 474, 0, 407, 410, 516, 501, 432, 433, 0, + 0, 0, 0, 0, 0, 0, 454, 463, 488, 448, + 0, 0, 0, 0, 0, 0, 851, 0, 430, 0, + 471, 0, 0, 0, 411, 408, 0, 0, 452, 0, + 0, 0, 413, 0, 431, 489, 0, 401, 119, 493, + 500, 449, 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 496, 426, 435, 105, 433, 193, 172, 231, 469, 174, + 497, 427, 436, 105, 434, 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 405, 0, - 212, 234, 249, 99, 421, 219, 243, 244, 0, 0, + 182, 142, 183, 125, 155, 154, 156, 0, 406, 0, + 212, 234, 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 417, 420, 415, - 416, 464, 465, 511, 512, 513, 489, 411, 0, 418, - 419, 0, 494, 501, 502, 468, 82, 91, 138, 246, - 186, 116, 235, 401, 414, 109, 424, 0, 0, 437, - 442, 443, 455, 457, 458, 459, 460, 467, 474, 475, - 477, 483, 484, 485, 486, 491, 498, 517, 84, 85, + 188, 247, 171, 194, 103, 233, 210, 418, 421, 416, + 417, 465, 466, 512, 513, 514, 490, 412, 0, 419, + 420, 0, 495, 502, 503, 469, 82, 91, 138, 246, + 186, 116, 235, 402, 415, 109, 425, 0, 0, 438, + 443, 444, 456, 458, 459, 460, 461, 468, 475, 476, + 478, 484, 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 505, 493, 0, 450, 508, 423, - 440, 516, 441, 444, 481, 408, 463, 165, 438, 0, - 427, 403, 434, 404, 425, 452, 111, 456, 422, 495, - 466, 507, 137, 428, 514, 139, 472, 0, 211, 153, - 0, 0, 454, 497, 461, 490, 449, 482, 413, 471, - 509, 439, 479, 510, 0, 0, 0, 322, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 476, 504, - 436, 478, 480, 402, 473, 0, 406, 409, 515, 500, - 431, 432, 0, 0, 0, 0, 0, 0, 0, 453, - 462, 487, 447, 0, 0, 0, 0, 0, 0, 0, - 0, 429, 0, 470, 0, 0, 0, 410, 407, 0, - 0, 451, 0, 0, 0, 412, 0, 430, 488, 0, - 400, 119, 492, 499, 448, 267, 503, 446, 445, 506, + 223, 232, 239, 242, 506, 494, 0, 451, 509, 424, + 441, 517, 442, 445, 482, 409, 464, 165, 439, 0, + 428, 404, 435, 405, 426, 453, 111, 457, 423, 496, + 467, 508, 137, 429, 515, 139, 473, 0, 211, 153, + 0, 0, 455, 498, 462, 491, 450, 483, 414, 472, + 510, 440, 480, 511, 0, 0, 0, 80, 0, 0, + 0, 0, 0, 0, 0, 0, 101, 0, 477, 505, + 437, 479, 481, 403, 474, 0, 407, 410, 516, 501, + 432, 433, 0, 0, 0, 0, 0, 0, 0, 454, + 463, 488, 448, 0, 0, 0, 0, 0, 0, 0, + 0, 430, 0, 471, 0, 0, 0, 411, 408, 0, + 0, 452, 0, 0, 0, 413, 0, 431, 489, 0, + 401, 119, 493, 500, 449, 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 496, 426, 435, 105, 433, 193, 172, - 231, 469, 174, 192, 140, 221, 185, 230, 240, 241, + 162, 191, 195, 497, 427, 436, 105, 434, 193, 172, + 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 405, 0, 212, 234, 249, 99, 421, 219, 243, + 0, 406, 0, 212, 234, 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, - 417, 420, 415, 416, 464, 465, 511, 512, 513, 489, - 411, 0, 418, 419, 0, 494, 501, 502, 468, 82, - 91, 138, 246, 186, 116, 235, 401, 414, 109, 424, - 0, 0, 437, 442, 443, 455, 457, 458, 459, 460, - 467, 474, 475, 477, 483, 484, 485, 486, 491, 498, - 517, 84, 85, 92, 98, 104, 108, 112, 115, 120, + 418, 421, 416, 417, 465, 466, 512, 513, 514, 490, + 412, 0, 419, 420, 0, 495, 502, 503, 469, 82, + 91, 138, 246, 186, 116, 235, 402, 415, 109, 425, + 0, 0, 438, 443, 444, 456, 458, 459, 460, 461, + 468, 475, 476, 478, 484, 485, 486, 487, 492, 499, + 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 505, 493, 0, - 450, 508, 423, 440, 516, 441, 444, 481, 408, 463, - 165, 438, 0, 427, 403, 434, 404, 425, 452, 111, - 456, 422, 495, 466, 507, 137, 428, 514, 139, 472, - 0, 211, 153, 0, 0, 454, 497, 461, 490, 449, - 482, 413, 471, 509, 439, 479, 510, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, - 0, 476, 504, 436, 478, 480, 402, 473, 0, 406, - 409, 515, 500, 431, 432, 0, 0, 0, 0, 0, - 0, 0, 453, 462, 487, 447, 0, 0, 0, 0, - 0, 0, 0, 0, 429, 0, 470, 0, 0, 0, - 410, 407, 0, 0, 451, 0, 0, 0, 412, 0, - 430, 488, 0, 400, 119, 492, 499, 448, 267, 503, - 446, 445, 506, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 496, 426, 435, 105, - 433, 193, 172, 231, 469, 174, 192, 140, 221, 185, + 213, 216, 222, 223, 232, 239, 242, 506, 494, 0, + 451, 509, 424, 441, 517, 442, 445, 482, 409, 464, + 165, 439, 0, 428, 404, 435, 405, 426, 453, 111, + 457, 423, 496, 467, 508, 137, 429, 515, 139, 473, + 0, 211, 153, 0, 0, 455, 498, 462, 491, 450, + 483, 414, 472, 510, 440, 480, 511, 0, 0, 0, + 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, + 0, 477, 505, 437, 479, 481, 403, 474, 0, 407, + 410, 516, 501, 432, 433, 0, 0, 0, 0, 0, + 0, 0, 454, 463, 488, 448, 0, 0, 0, 0, + 0, 0, 0, 0, 430, 0, 471, 0, 0, 0, + 411, 408, 0, 0, 452, 0, 0, 0, 413, 0, + 431, 489, 0, 401, 119, 493, 500, 449, 267, 504, + 447, 446, 507, 184, 0, 215, 122, 136, 97, 83, + 93, 0, 121, 162, 191, 195, 497, 427, 436, 105, + 434, 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 398, 236, 158, 220, 228, 152, 145, 89, + 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 405, 0, 212, 234, 249, 99, - 421, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 399, 397, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 417, 420, 415, 416, 464, 465, 511, - 512, 513, 489, 411, 0, 418, 419, 0, 494, 501, - 502, 468, 82, 91, 138, 246, 186, 116, 235, 401, - 414, 109, 424, 0, 0, 437, 442, 443, 455, 457, - 458, 459, 460, 467, 474, 475, 477, 483, 484, 485, - 486, 491, 498, 517, 84, 85, 92, 98, 104, 108, + 155, 154, 156, 0, 406, 0, 212, 234, 249, 99, + 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, + 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, + 103, 233, 210, 418, 421, 416, 417, 465, 466, 512, + 513, 514, 490, 412, 0, 419, 420, 0, 495, 502, + 503, 469, 82, 91, 138, 246, 186, 116, 235, 402, + 415, 109, 425, 0, 0, 438, 443, 444, 456, 458, + 459, 460, 461, 468, 475, 476, 478, 484, 485, 486, + 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 505, 493, 0, 450, 508, 423, 440, 516, 441, 444, - 481, 408, 463, 165, 438, 0, 427, 403, 434, 404, - 425, 452, 111, 456, 422, 495, 466, 507, 137, 428, - 514, 139, 472, 0, 211, 153, 0, 0, 454, 497, - 461, 490, 449, 482, 413, 471, 509, 439, 479, 510, - 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, - 0, 0, 101, 0, 476, 504, 436, 478, 480, 402, - 473, 0, 406, 409, 515, 500, 431, 432, 0, 0, - 0, 0, 0, 0, 0, 453, 462, 487, 447, 0, - 0, 0, 0, 0, 0, 0, 0, 429, 0, 470, - 0, 0, 0, 410, 407, 0, 0, 451, 0, 0, - 0, 412, 0, 430, 488, 0, 400, 119, 492, 499, - 448, 267, 503, 446, 445, 506, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 496, - 426, 435, 105, 433, 193, 172, 231, 469, 174, 192, + 506, 494, 0, 451, 509, 424, 441, 517, 442, 445, + 482, 409, 464, 165, 439, 0, 428, 404, 435, 405, + 426, 453, 111, 457, 423, 496, 467, 508, 137, 429, + 515, 139, 473, 0, 211, 153, 0, 0, 455, 498, + 462, 491, 450, 483, 414, 472, 510, 440, 480, 511, + 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 101, 0, 477, 505, 437, 479, 481, 403, + 474, 0, 407, 410, 516, 501, 432, 433, 0, 0, + 0, 0, 0, 0, 0, 454, 463, 488, 448, 0, + 0, 0, 0, 0, 0, 0, 0, 430, 0, 471, + 0, 0, 0, 411, 408, 0, 0, 452, 0, 0, + 0, 413, 0, 431, 489, 0, 401, 119, 493, 500, + 449, 267, 504, 447, 446, 507, 184, 0, 215, 122, + 136, 97, 83, 93, 0, 121, 162, 191, 195, 497, + 427, 436, 105, 434, 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, + 106, 248, 94, 237, 90, 399, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 405, 0, 212, - 234, 249, 99, 421, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 417, 420, 415, 416, - 464, 465, 511, 512, 513, 489, 411, 0, 418, 419, - 0, 494, 501, 502, 468, 82, 91, 138, 246, 186, - 116, 235, 401, 414, 109, 424, 0, 0, 437, 442, - 443, 455, 457, 458, 459, 460, 467, 474, 475, 477, - 483, 484, 485, 486, 491, 498, 517, 84, 85, 92, + 142, 183, 125, 155, 154, 156, 0, 406, 0, 212, + 234, 249, 99, 422, 219, 243, 244, 0, 0, 100, + 118, 113, 181, 400, 398, 127, 209, 134, 141, 188, + 247, 171, 194, 103, 233, 210, 418, 421, 416, 417, + 465, 466, 512, 513, 514, 490, 412, 0, 419, 420, + 0, 495, 502, 503, 469, 82, 91, 138, 246, 186, + 116, 235, 402, 415, 109, 425, 0, 0, 438, 443, + 444, 456, 458, 459, 460, 461, 468, 475, 476, 478, + 484, 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 505, 493, 0, 450, 508, 423, 440, - 516, 441, 444, 481, 408, 463, 165, 438, 0, 427, - 403, 434, 404, 425, 452, 111, 456, 422, 495, 466, - 507, 137, 428, 514, 139, 472, 0, 211, 153, 0, - 0, 454, 497, 461, 490, 449, 482, 413, 471, 509, - 439, 479, 510, 0, 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 476, 504, 436, - 478, 480, 402, 473, 0, 406, 409, 515, 500, 431, - 432, 0, 0, 0, 0, 0, 0, 0, 453, 462, - 487, 447, 0, 0, 0, 0, 0, 0, 0, 0, - 429, 0, 470, 0, 0, 0, 410, 407, 0, 0, - 451, 0, 0, 0, 412, 0, 430, 488, 0, 400, - 119, 492, 499, 448, 267, 503, 446, 445, 506, 184, + 232, 239, 242, 506, 494, 0, 451, 509, 424, 441, + 517, 442, 445, 482, 409, 464, 165, 439, 0, 428, + 404, 435, 405, 426, 453, 111, 457, 423, 496, 467, + 508, 137, 429, 515, 139, 473, 0, 211, 153, 0, + 0, 455, 498, 462, 491, 450, 483, 414, 472, 510, + 440, 480, 511, 0, 0, 0, 265, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 0, 477, 505, 437, + 479, 481, 403, 474, 0, 407, 410, 516, 501, 432, + 433, 0, 0, 0, 0, 0, 0, 0, 454, 463, + 488, 448, 0, 0, 0, 0, 0, 0, 0, 0, + 430, 0, 471, 0, 0, 0, 411, 408, 0, 0, + 452, 0, 0, 0, 413, 0, 431, 489, 0, 401, + 119, 493, 500, 449, 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 496, 426, 435, 105, 433, 193, 172, 231, - 469, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 711, 102, 203, 88, 227, + 191, 195, 497, 427, 436, 105, 434, 193, 172, 231, + 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 398, 236, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 405, 0, 212, 234, 249, 99, 421, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 399, 397, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 417, - 420, 415, 416, 464, 465, 511, 512, 513, 489, 411, - 0, 418, 419, 0, 494, 501, 502, 468, 82, 91, - 138, 246, 186, 116, 235, 401, 414, 109, 424, 0, - 0, 437, 442, 443, 455, 457, 458, 459, 460, 467, - 474, 475, 477, 483, 484, 485, 486, 491, 498, 517, + 406, 0, 212, 234, 249, 99, 422, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 418, + 421, 416, 417, 465, 466, 512, 513, 514, 490, 412, + 0, 419, 420, 0, 495, 502, 503, 469, 82, 91, + 138, 246, 186, 116, 235, 402, 415, 109, 425, 0, + 0, 438, 443, 444, 456, 458, 459, 460, 461, 468, + 475, 476, 478, 484, 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 505, 493, 0, 450, - 508, 423, 440, 516, 441, 444, 481, 408, 463, 165, - 438, 0, 427, 403, 434, 404, 425, 452, 111, 456, - 422, 495, 466, 507, 137, 428, 514, 139, 472, 0, - 211, 153, 0, 0, 454, 497, 461, 490, 449, 482, - 413, 471, 509, 439, 479, 510, 0, 0, 0, 80, + 216, 222, 223, 232, 239, 242, 506, 494, 0, 451, + 509, 424, 441, 517, 442, 445, 482, 409, 464, 165, + 439, 0, 428, 404, 435, 405, 426, 453, 111, 457, + 423, 496, 467, 508, 137, 429, 515, 139, 473, 0, + 211, 153, 0, 0, 455, 498, 462, 491, 450, 483, + 414, 472, 510, 440, 480, 511, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, - 476, 504, 436, 478, 480, 402, 473, 0, 406, 409, - 515, 500, 431, 432, 0, 0, 0, 0, 0, 0, - 0, 453, 462, 487, 447, 0, 0, 0, 0, 0, - 0, 0, 0, 429, 0, 470, 0, 0, 0, 410, - 407, 0, 0, 451, 0, 0, 0, 412, 0, 430, - 488, 0, 400, 119, 492, 499, 448, 267, 503, 446, - 445, 506, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 496, 426, 435, 105, 433, - 193, 172, 231, 469, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 389, 102, + 477, 505, 437, 479, 481, 403, 474, 0, 407, 410, + 516, 501, 432, 433, 0, 0, 0, 0, 0, 0, + 0, 454, 463, 488, 448, 0, 0, 0, 0, 0, + 0, 0, 0, 430, 0, 471, 0, 0, 0, 411, + 408, 0, 0, 452, 0, 0, 0, 413, 0, 431, + 489, 0, 401, 119, 493, 500, 449, 267, 504, 447, + 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, + 0, 121, 162, 191, 195, 497, 427, 436, 105, 434, + 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, + 240, 241, 218, 238, 245, 208, 86, 217, 713, 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 398, 236, 158, 220, 228, 152, 145, 89, 226, + 90, 399, 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 405, 0, 212, 234, 249, 99, 421, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 399, - 397, 392, 391, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 417, 420, 415, 416, 464, 465, 511, 512, - 513, 489, 411, 0, 418, 419, 0, 494, 501, 502, - 468, 82, 91, 138, 246, 186, 116, 235, 401, 414, - 109, 424, 0, 0, 437, 442, 443, 455, 457, 458, - 459, 460, 467, 474, 475, 477, 483, 484, 485, 486, - 491, 498, 517, 84, 85, 92, 98, 104, 108, 112, + 154, 156, 0, 406, 0, 212, 234, 249, 99, 422, + 219, 243, 244, 0, 0, 100, 118, 113, 181, 400, + 398, 127, 209, 134, 141, 188, 247, 171, 194, 103, + 233, 210, 418, 421, 416, 417, 465, 466, 512, 513, + 514, 490, 412, 0, 419, 420, 0, 495, 502, 503, + 469, 82, 91, 138, 246, 186, 116, 235, 402, 415, + 109, 425, 0, 0, 438, 443, 444, 456, 458, 459, + 460, 461, 468, 475, 476, 478, 484, 485, 486, 487, + 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 885, 0, 324, 0, 0, 0, 111, 0, - 321, 0, 0, 0, 137, 886, 364, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, - 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, - 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, - 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, - 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, - 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, - 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 206, 207, 213, 216, 222, 223, 232, 239, 242, 506, + 494, 0, 451, 509, 424, 441, 517, 442, 445, 482, + 409, 464, 165, 439, 0, 428, 404, 435, 405, 426, + 453, 111, 457, 423, 496, 467, 508, 137, 429, 515, + 139, 473, 0, 211, 153, 0, 0, 455, 498, 462, + 491, 450, 483, 414, 472, 510, 440, 480, 511, 0, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 0, 477, 505, 437, 479, 481, 403, 474, + 0, 407, 410, 516, 501, 432, 433, 0, 0, 0, + 0, 0, 0, 0, 454, 463, 488, 448, 0, 0, + 0, 0, 0, 0, 0, 0, 430, 0, 471, 0, + 0, 0, 411, 408, 0, 0, 452, 0, 0, 0, + 413, 0, 431, 489, 0, 401, 119, 493, 500, 449, + 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 497, 427, + 436, 105, 434, 193, 172, 231, 470, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 390, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 399, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 406, 0, 212, 234, + 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 400, 398, 393, 392, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 418, 421, 416, 417, 465, + 466, 512, 513, 514, 490, 412, 0, 419, 420, 0, + 495, 502, 503, 469, 82, 91, 138, 246, 186, 116, + 235, 402, 415, 109, 425, 0, 0, 438, 443, 444, + 456, 458, 459, 460, 461, 468, 475, 476, 478, 484, + 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 887, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 888, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, - 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, - 0, 0, 0, 0, 959, 0, 54, 0, 0, 322, - 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, - 349, 350, 351, 960, 0, 0, 319, 336, 0, 363, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, - 334, 0, 0, 0, 0, 377, 0, 335, 0, 0, - 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, - 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, - 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 962, 0, 54, + 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 963, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 0, 0, 0, 0, 378, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, - 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 583, 322, - 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, - 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, - 334, 0, 0, 0, 0, 377, 0, 335, 0, 0, - 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, - 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, - 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 584, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 0, 0, 0, 0, 378, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, - 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, - 343, 342, 345, 346, 347, 348, 0, 0, 101, 344, - 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, - 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, - 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, - 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, - 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, - 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, - 343, 901, 345, 346, 347, 348, 0, 0, 101, 344, - 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, - 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, - 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, - 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, - 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 903, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, - 0, 0, 0, 0, 324, 0, 0, 0, 111, 0, - 321, 0, 0, 0, 137, 0, 364, 139, 0, 0, - 211, 153, 0, 0, 0, 0, 355, 356, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 322, - 343, 898, 345, 346, 347, 348, 0, 0, 101, 344, - 349, 350, 351, 0, 0, 0, 319, 336, 0, 363, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 333, - 334, 315, 0, 0, 0, 377, 0, 335, 0, 0, - 330, 331, 332, 337, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 119, 0, 0, 0, 267, 0, 0, - 375, 0, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, - 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 0, 0, 212, 234, 249, 99, 0, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 365, 376, 371, 372, 369, 370, 368, 367, - 366, 378, 357, 358, 359, 360, 362, 0, 373, 374, - 361, 82, 91, 138, 246, 186, 116, 235, 0, 0, - 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, + 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, + 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, + 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 322, 343, 900, 345, 346, 347, 348, 0, + 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, + 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, + 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, + 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, + 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, + 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, + 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, + 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, + 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, + 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, + 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, + 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, + 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, + 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, + 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, + 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, + 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, + 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 24, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 165, 0, 0, 0, 0, 324, 0, 0, 0, - 111, 0, 321, 0, 0, 0, 137, 0, 364, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, - 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, - 101, 344, 349, 350, 351, 0, 0, 0, 319, 336, - 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, - 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, - 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, - 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, + 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, + 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, + 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, + 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, + 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, + 239, 242, 24, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 165, 0, 0, 0, 0, 324, + 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, + 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, + 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, + 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, + 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, + 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, + 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, + 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, + 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, + 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 0, 324, 0, 0, 0, - 111, 0, 321, 0, 0, 0, 137, 0, 364, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, - 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, - 101, 344, 349, 350, 351, 0, 0, 0, 319, 336, - 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, - 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, - 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, - 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 0, 324, + 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, + 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, + 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, + 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, + 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, + 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, + 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, + 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, + 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, + 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, - 111, 0, 0, 0, 0, 0, 137, 0, 364, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, - 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, - 101, 344, 349, 350, 351, 0, 0, 0, 0, 336, - 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, - 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 1521, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, - 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, - 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, + 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, + 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, + 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, + 0, 0, 336, 0, 363, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, + 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, + 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 1525, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, + 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, + 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, - 111, 0, 0, 0, 0, 0, 137, 0, 364, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, - 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 583, 322, 343, 342, 345, 346, 347, 348, 0, 0, - 101, 344, 349, 350, 351, 0, 0, 0, 0, 336, - 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, - 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, - 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, - 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, + 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, + 0, 54, 0, 584, 322, 343, 342, 345, 346, 347, + 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, + 0, 0, 336, 0, 363, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, + 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, + 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, + 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, + 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, - 111, 0, 0, 0, 0, 0, 137, 0, 364, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 355, 356, - 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 0, 322, 343, 342, 345, 346, 347, 348, 0, 0, - 101, 344, 349, 350, 351, 0, 0, 0, 0, 336, - 0, 363, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 333, 334, 0, 0, 0, 0, 377, 0, 335, - 0, 0, 330, 331, 332, 337, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 375, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 365, 376, 371, 372, 369, 370, - 368, 367, 366, 378, 357, 358, 359, 360, 362, 0, - 373, 374, 361, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, + 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, + 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, + 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, + 0, 0, 336, 0, 363, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, + 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, + 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, + 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, + 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, - 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, - 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 618, 617, 627, - 628, 620, 621, 622, 623, 624, 625, 626, 619, 0, - 0, 629, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, + 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 606, 0, 0, 0, 0, - 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 80, 0, 608, 0, 0, 0, 0, 0, 0, - 101, 0, 0, 0, 0, 0, 603, 602, 0, 0, + 619, 618, 628, 629, 621, 622, 623, 624, 625, 626, + 627, 620, 0, 0, 630, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, + 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 604, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 607, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 80, 0, 609, 0, 0, 0, + 0, 0, 0, 101, 0, 0, 0, 0, 0, 604, + 603, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 605, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 0, 0, 0, 0, 0, - 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, + 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, - 101, 0, 0, 0, 0, 0, 74, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, + 0, 0, 0, 101, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 76, 77, 0, 73, - 0, 0, 0, 78, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 0, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 165, 0, 0, 0, 942, 0, 0, 0, 0, - 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, - 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 76, + 77, 0, 73, 0, 0, 0, 78, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 0, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 265, 0, 944, 0, 0, 0, 0, 0, 0, - 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 165, 0, 0, 0, 945, 0, + 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, + 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 265, 0, 947, 0, 0, 0, + 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 119, 0, 0, 0, 267, - 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, - 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 0, 0, 212, 234, 249, - 99, 0, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 91, 138, 246, 186, 116, 235, - 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 24, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 165, 0, 0, 0, 0, 0, 0, - 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, - 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, + 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, + 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, + 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, + 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, + 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, + 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, + 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, + 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, + 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, + 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, + 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, + 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 80, 0, 0, 0, 0, 0, 0, - 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, + 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, + 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, + 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, + 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, + 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, + 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, + 223, 232, 239, 242, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 165, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, + 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 80, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, - 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, - 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 95, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 0, 0, 212, - 234, 249, 99, 0, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 157, 96, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 82, 91, 138, 246, 186, - 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 24, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 265, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, + 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, + 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, + 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, + 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, + 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, + 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, + 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, + 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, + 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, + 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, + 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, + 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, + 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, + 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, + 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, + 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, + 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, + 216, 222, 223, 232, 239, 242, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 265, 0, + 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 942, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 944, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 940, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 945, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, + 947, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 0, 836, 0, - 0, 837, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 943, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, + 0, 838, 0, 0, 839, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 720, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 719, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 722, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, + 721, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 584, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 265, 0, + 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, + 947, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, + 609, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, + 0, 0, 0, 0, 0, 0, 692, 111, 0, 0, + 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, + 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, + 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, + 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, + 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, + 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, + 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, + 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, + 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, + 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, + 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, + 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, + 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, + 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, + 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, + 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, + 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, + 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, + 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, + 207, 213, 216, 222, 223, 232, 239, 242, 382, 0, + 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 583, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2279,14 +2446,14 @@ var yyAct = [...]int{ 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 265, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, + 0, 262, 0, 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, @@ -2312,7 +2479,7 @@ var yyAct = [...]int{ 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 944, 0, 0, + 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2345,7 +2512,7 @@ var yyAct = [...]int{ 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 608, 0, 0, + 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2375,10 +2542,10 @@ var yyAct = [...]int{ 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 690, 111, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2407,184 +2574,19 @@ var yyAct = [...]int{ 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 381, 0, 0, 0, 0, - 0, 0, 165, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, - 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 0, 262, 0, - 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, - 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, - 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 0, 0, 0, - 0, 111, 0, 0, 0, 0, 0, 137, 0, 0, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, - 267, 0, 0, 0, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, + 222, 223, 232, 239, 242, } var yyPact = [...]int{ - 2159, -1000, -268, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 258, -1000, -270, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 879, 942, -1000, -1000, -1000, -1000, -1000, -1000, + 252, 11336, 49, 107, 33, 15657, 97, 275, 16317, -1000, + 11, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -57, -66, + -1000, 694, -1000, -1000, -1000, -1000, -1000, 873, 876, 741, + 868, 803, -1000, 8024, 75, 75, 15327, 6704, -1000, -1000, + 240, 16317, 91, 16317, -145, 73, 73, 73, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 989, 1042, -1000, -1000, -1000, -1000, -1000, -1000, - 275, 11323, -1, 127, -23, 15644, 121, 74, 16304, -1000, - 13, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -67, -95, - -1000, 743, -1000, -1000, -1000, -1000, -1000, 963, 981, 802, - 958, 859, -1000, 8011, 85, 85, 15314, 6691, -1000, -1000, - 269, 16304, 118, 16304, -152, 82, 82, 82, -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, @@ -2601,22 +2603,22 @@ 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, + 96, 16317, 536, 534, 176, -1000, 16317, 71, 531, 71, + 71, 71, 16317, -1000, 141, -1000, -1000, -1000, 16317, 526, + 834, 288, 70, 3617, -1000, 3617, 3617, -1000, 3617, 20, + 3617, -59, 900, 18, -8, -1000, 3617, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 120, 16304, 622, 621, 278, -1000, 16304, 78, 620, 78, - 78, 78, 16304, -1000, 172, -1000, -1000, -1000, 16304, 614, - 901, 306, 48, 3604, -1000, 3604, 3604, -1000, 3604, 23, - 3604, -89, 1013, 24, -9, -1000, 3604, -1000, -1000, -1000, + 546, 844, 9356, 9356, 879, -1000, 694, -1000, -1000, -1000, + 831, -1000, -1000, 306, 924, -1000, 11006, 140, -1000, 9356, + 2043, 700, -1000, -1000, 700, -1000, -1000, 119, -1000, -1000, + 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 437, 904, 9343, 9343, 989, -1000, 743, -1000, -1000, -1000, - 900, -1000, -1000, 319, 1028, -1000, 10993, 171, -1000, 9343, - 2025, 661, -1000, -1000, 661, -1000, -1000, 140, -1000, -1000, - 10333, 10333, 10333, 10333, 10333, 10333, 10333, 10333, -1000, -1000, + -1000, -1000, 700, -1000, 9026, 700, 700, 700, 700, 700, + 700, 700, 700, 9356, 700, 700, 700, 700, 700, 700, + 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, + 14990, 14000, 16317, 673, 666, -1000, -1000, 138, 692, 6361, + -98, -1000, -1000, -1000, 219, 13340, -1000, -1000, -1000, 829, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 661, -1000, 9013, 661, 661, 661, 661, 661, - 661, 661, 661, 9343, 661, 661, 661, 661, 661, 661, - 661, 661, 661, 661, 661, 661, 661, 661, 661, 14977, - 13987, 16304, 694, 665, -1000, -1000, 167, 735, 6348, -101, - -1000, -1000, -1000, 264, 13327, -1000, -1000, -1000, 892, -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, @@ -2627,133 +2629,132 @@ 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, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 624, + 16317, -1000, 2399, -1000, 523, 3617, 82, 521, 244, 519, + 16317, 16317, 3617, 3617, 3617, 26, 59, 55, 16317, 698, + 79, 16317, 861, 756, 16317, 507, 504, -1000, 6018, -1000, + 3617, 288, -1000, 440, 9356, 3617, 3617, 3617, 16317, 3617, + 3617, -1000, -1000, -1000, -1000, -1000, -1000, 3617, 3617, -1000, + 912, 293, -1000, -1000, -1000, -1000, 9356, 204, -1000, 754, + -1000, -1000, -1000, -1000, -1000, -1000, 936, 187, 522, 136, + 697, -1000, 474, 873, 546, 803, 13010, 770, -1000, -1000, + -1000, 16317, -1000, 9356, 9356, 388, -1000, 14660, -1000, -1000, + 4646, 203, 10346, 279, 255, 10346, 10346, 10346, 10346, 10346, + 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, + 373, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 494, + -1000, 694, 594, 594, 152, 152, 152, 152, 152, 152, + 152, 10676, 7034, 546, 619, 451, 9026, 8024, 8024, 9356, + 9356, 8684, 8354, 8024, 839, 242, 451, 16647, -1000, -1000, + 10016, -1000, -1000, -1000, -1000, -1000, 546, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 15987, 15987, 8024, 8024, 8024, 8024, + 8024, 42, 16317, -1000, 689, 858, -1000, -1000, -1000, 864, + 12350, 12680, 42, 683, 14000, 16317, -1000, -1000, 14000, 16317, + 4303, 5675, 692, -98, 675, -1000, -82, -86, 7364, 148, + -1000, -1000, -1000, -1000, 3274, 397, 567, 319, -47, -1000, + -1000, -1000, 703, -1000, 703, 703, 703, 703, -17, -17, + -17, -17, -1000, -1000, -1000, -1000, -1000, 721, 716, -1000, + 703, 703, 703, -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, 618, 16304, - -1000, 1464, -1000, 613, 3604, 100, 612, 292, 611, 16304, - 16304, 3604, 3604, 3604, 36, 68, 57, 16304, 744, 91, - 16304, 950, 810, 16304, 572, 561, -1000, 6005, -1000, 3604, - 306, -1000, 513, 9343, 3604, 3604, 3604, 16304, 3604, 3604, - -1000, -1000, -1000, -1000, -1000, -1000, 3604, 3604, -1000, 1025, - 295, -1000, -1000, -1000, -1000, 9343, 219, -1000, 807, -1000, - -1000, -1000, -1000, -1000, -1000, 1034, 195, 491, 165, 738, - -1000, 498, 963, 437, 859, 12997, 824, -1000, -1000, -1000, - 16304, -1000, 9343, 9343, 528, -1000, 14647, -1000, -1000, 4633, - 199, 10333, 464, 299, 10333, 10333, 10333, 10333, 10333, 10333, - 10333, 10333, 10333, 10333, 10333, 10333, 10333, 10333, 10333, 482, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 554, -1000, - 743, 577, 577, 178, 178, 178, 178, 178, 178, 178, - 10663, 7021, 437, 608, 389, 9013, 8011, 8011, 9343, 9343, - 8671, 8341, 8011, 899, 244, 389, 16634, -1000, -1000, 10003, - -1000, -1000, -1000, -1000, -1000, 437, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 15974, 15974, 8011, 8011, 8011, 8011, 51, - 16304, -1000, 697, 905, -1000, -1000, -1000, 952, 12337, 12667, - 51, 728, 13987, 16304, -1000, -1000, 13987, 16304, 4290, 5662, - 735, -101, 700, -1000, -129, -125, 7351, 163, -1000, -1000, - -1000, -1000, 3261, 189, 553, 470, -60, -1000, -1000, -1000, - 754, -1000, 754, 754, 754, 754, -17, -17, -17, -17, - -1000, -1000, -1000, -1000, -1000, 775, 774, -1000, 754, 754, - 754, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 768, - 768, 768, 756, 756, 779, -1000, 16304, 3604, 944, 3604, - -1000, 70, -1000, -1000, -1000, 16304, 16304, 16304, 16304, 16304, - 138, 16304, 16304, 729, -1000, 16304, 3604, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 389, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 16304, 306, 16304, 16304, 389, -1000, - 509, 16304, -1000, 868, 9343, 9343, 5319, 9343, -1000, -1000, - -1000, 904, -1000, 899, 965, -1000, 881, 880, 8011, -1000, - -1000, 199, 434, -1000, -1000, 436, -1000, -1000, -1000, -1000, - 162, 661, -1000, 1844, -1000, -1000, -1000, -1000, 464, 10333, - 10333, 10333, 387, 1844, 1780, 304, 372, 178, 396, 396, - 194, 194, 194, 194, 194, 413, 413, -1000, -1000, -1000, - 437, -1000, -1000, -1000, 437, 8011, 8011, 727, -1000, -1000, - 9343, -1000, 437, 602, 602, 289, 371, 259, 1021, 602, - 256, 1020, 602, 602, 8011, 296, -1000, 9343, 437, -1000, - 160, -1000, 637, 723, 720, 602, 437, 602, 602, 671, - 661, -1000, 16634, 13987, 13987, 13987, 13987, 13987, -1000, 839, - 835, -1000, 823, 822, 829, 16304, -1000, 606, 12337, 198, - 661, -1000, 14317, -1000, -1000, 1010, 13987, 702, -1000, 702, - -1000, 159, -1000, -1000, 700, -101, -111, -1000, -1000, -1000, - -1000, 389, -1000, 494, 688, 2918, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 761, 544, -1000, 917, 214, 188, 542, - 916, -1000, -1000, -1000, 909, -1000, 303, -62, -1000, -1000, - 432, -17, -17, -1000, -1000, 163, 889, 163, 163, 163, - 506, 506, -1000, -1000, -1000, -1000, 404, -1000, -1000, -1000, - 395, -1000, 791, 15974, 3604, -1000, -1000, -1000, -1000, 225, - 225, 207, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 50, 777, -1000, -1000, -1000, -1000, 2, - 34, 88, -1000, 3604, -1000, 295, -1000, -1000, -1000, -1000, - -1000, 866, 389, 389, 155, -1000, -1000, 16304, -1000, -1000, - -1000, -1000, 717, -1000, -1000, -1000, 3947, 8011, -1000, 387, - 1844, 1249, -1000, 10333, 10333, -1000, -1000, 602, 602, 8011, - 389, -1000, -1000, -1000, 47, 482, 47, 10333, 10333, -1000, - 10333, 10333, -1000, -165, 705, 272, -1000, 9343, 463, -1000, - 5319, -1000, 10333, 10333, -1000, -1000, -1000, -1000, 790, 16634, - 661, -1000, 11995, 15974, 706, -1000, 241, 905, 772, 789, - 643, -1000, -1000, -1000, -1000, 834, -1000, 833, -1000, -1000, - -1000, -1000, -1000, 115, 113, 111, 15974, -1000, 989, 9343, - 702, -1000, -1000, 197, -1000, -1000, -119, -130, -1000, -1000, - -1000, 3261, -1000, 3261, 15974, 61, -1000, 542, 542, -1000, - -1000, -1000, 759, 787, 10333, -1000, -1000, -1000, 549, 163, - 163, -1000, 240, -1000, -1000, -1000, 599, -1000, 597, 657, - 592, 16304, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16304, - -1000, -1000, -1000, -1000, -1000, 15974, -170, 529, 15974, 15974, - 15974, 16304, -1000, 306, -1000, 4976, -1000, 1010, 13987, -1000, - -1000, 437, -1000, 10333, 1844, 1844, -1000, -1000, -1000, 437, - 754, 754, -1000, 754, 756, -1000, 754, 1, 754, 0, - 437, 437, 1722, 1499, 1462, 794, 661, -159, -1000, 389, - 9343, -1000, 998, 751, -1000, 926, 669, 604, -1000, -1000, - 7681, 437, 586, 153, 576, -1000, 989, 16634, 9343, -1000, - -1000, 9343, 755, -1000, 9343, -1000, -1000, -1000, 661, 661, - 661, 576, 963, 389, -1000, -1000, -1000, -1000, 2918, -1000, - 568, -1000, 754, -1000, -1000, -1000, 15974, -41, 1033, 1844, - -1000, -1000, -1000, -1000, -1000, -17, 503, -17, 382, -1000, - 354, 3604, -1000, -1000, -1000, -1000, 930, -1000, 4976, -1000, - -1000, 747, 778, -1000, -1000, -1000, 996, 642, -1000, 1844, - -1000, -1000, 119, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 10333, 10333, 10333, 10333, 10333, 963, 489, 389, 10333, - 10333, 914, -1000, 661, -1000, -1000, 660, 15974, 15974, -1000, - 15974, 963, -1000, 389, 389, 15974, 389, 13657, 15974, 15974, - 11653, -1000, 158, 15974, -1000, 558, -1000, 181, -1000, -79, - 163, -1000, 163, 527, 520, -1000, 661, 625, -1000, 233, - 15974, 16304, 991, 977, -1000, -1000, 637, 637, 637, 637, - 45, 437, -1000, 637, 637, 1032, -1000, 661, -1000, 743, - 152, -1000, -1000, -1000, 552, 548, -1000, 548, 548, 198, - 158, -1000, 493, 232, 468, -1000, 58, 15974, 317, 912, - -1000, 902, -1000, -1000, -1000, -1000, -1000, 49, 4976, 3261, - 533, -1000, -1000, 9343, 9343, -1000, -1000, -1000, -1000, 437, - 46, -176, -1000, -1000, -1000, 16634, 604, 437, 15974, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 326, -1000, -1000, 16304, - -1000, -1000, 467, -1000, -1000, 526, -1000, 15974, -1000, -1000, - 777, 389, 594, -1000, 865, -168, -182, 588, -1000, -1000, - -1000, 746, -1000, -1000, 49, 874, -170, -1000, 864, -1000, - 15974, -1000, 42, -1000, -171, 519, 40, -180, 785, 661, - -184, 783, -1000, 1017, 9673, -1000, -1000, 1031, 203, 203, - 637, 437, -1000, -1000, -1000, 71, 384, -1000, -1000, -1000, - -1000, -1000, -1000, + -1000, 714, 714, 714, 708, 708, 723, -1000, 16317, 3617, + 859, 3617, -1000, 72, -1000, -1000, -1000, 16317, 16317, 16317, + 16317, 16317, 114, 16317, 16317, 687, -1000, 16317, 3617, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 451, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 16317, 288, 16317, 16317, + 451, -1000, 424, 16317, -1000, 789, 9356, 9356, 5332, 9356, + -1000, -1000, -1000, 844, -1000, 839, 880, -1000, 821, 820, + 8024, -1000, -1000, 203, 295, -1000, -1000, 517, -1000, -1000, + -1000, -1000, 134, 700, -1000, 1602, -1000, -1000, -1000, -1000, + 279, 10346, 10346, 10346, 262, 1602, 1783, 1240, 1657, 152, + 386, 386, 153, 153, 153, 153, 153, 313, 313, -1000, + -1000, -1000, 546, -1000, -1000, -1000, 546, 8024, 8024, 685, + -1000, -1000, 9356, -1000, 546, 610, 610, 281, 346, 227, + 909, 610, 225, 903, 610, 610, 8024, 314, -1000, 9356, + 546, -1000, 133, -1000, 372, 682, 679, 610, 546, 546, + 610, 610, 691, 700, -1000, 16647, 14000, 14000, 14000, 14000, + 14000, -1000, 790, 788, -1000, 768, 767, 780, 16317, -1000, + 612, 12350, 158, 700, -1000, 14330, -1000, -1000, 899, 14000, + 684, -1000, 684, -1000, 131, -1000, -1000, 675, -98, -105, + -1000, -1000, -1000, -1000, 451, -1000, 381, 668, 2931, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 712, 488, -1000, 850, + 185, 220, 480, 849, -1000, -1000, -1000, 832, -1000, 261, + -53, -1000, -1000, 369, -17, -17, -1000, -1000, 148, 828, + 148, 148, 148, 422, 422, -1000, -1000, -1000, -1000, 361, + -1000, -1000, -1000, 345, -1000, 753, 15987, 3617, -1000, -1000, + -1000, -1000, 785, 785, 202, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 39, 596, -1000, -1000, + -1000, -1000, 10, 25, 74, -1000, 3617, -1000, 293, -1000, + -1000, -1000, -1000, -1000, 801, 451, 451, 129, -1000, -1000, + 16317, -1000, -1000, -1000, -1000, 659, -1000, -1000, -1000, 3960, + 8024, -1000, 262, 1602, 1643, -1000, 10346, 10346, -1000, -1000, + 610, 610, 8024, 451, -1000, -1000, -1000, 58, 373, 58, + 10346, 10346, -1000, 10346, 10346, -1000, -160, 667, 232, -1000, + 9356, 493, -1000, 5332, -1000, 10346, 10346, -1000, -1000, -1000, + -1000, -1000, 744, 16647, 700, -1000, 12008, 15987, 695, -1000, + 218, 858, 720, 740, 645, -1000, -1000, -1000, -1000, 779, + -1000, 778, -1000, -1000, -1000, -1000, -1000, 90, 89, 87, + 15987, -1000, 879, 9356, 684, -1000, -1000, 171, -1000, -1000, + -109, -91, -1000, -1000, -1000, 3274, -1000, 3274, 15987, 57, + -1000, 480, 480, -1000, -1000, -1000, 711, 728, 10346, -1000, + -1000, -1000, 560, 148, 148, -1000, 197, -1000, -1000, -1000, + 608, -1000, 590, 662, 573, 16317, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 16317, -1000, -1000, -1000, -1000, -1000, 15987, + -173, 471, 15987, 15987, 15987, 16317, -1000, 288, -1000, 4989, + -1000, 899, 14000, -1000, -1000, 546, -1000, 10346, 1602, 1602, + -1000, -1000, -1000, 546, 703, 703, -1000, 703, 708, -1000, + 703, 1, 703, -1, 546, 546, 1578, 1132, 997, 415, + 700, -152, -1000, 451, 9356, -1000, 1478, 537, -1000, 852, + 595, 627, -1000, -1000, 7694, 546, 570, 127, 566, -1000, + 879, 16647, 9356, -1000, -1000, 9356, 707, -1000, 9356, -1000, + -1000, -1000, 700, 700, 700, 566, 873, 451, -1000, -1000, + -1000, -1000, 2931, -1000, 557, -1000, 703, -1000, -1000, -1000, + 15987, -42, 931, 1602, -1000, -1000, -1000, -1000, -1000, -17, + 421, -17, 337, -1000, 326, 3617, -1000, -1000, -1000, -1000, + 854, -1000, 4989, -1000, -1000, 702, 696, -1000, -1000, -1000, + 896, 652, -1000, 1602, -1000, -1000, 113, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 10346, 10346, 10346, 10346, 10346, + 873, 404, 451, 10346, 10346, 846, -1000, 700, -1000, -1000, + 681, 15987, 15987, -1000, 15987, 873, -1000, 451, 451, 15987, + 451, 13670, 15987, 15987, 11666, -1000, 150, 15987, -1000, 549, + -1000, 195, -1000, -103, 148, -1000, 148, 553, 550, -1000, + 700, 647, -1000, 216, 15987, 16317, 883, 865, -1000, -1000, + 372, 372, 372, 372, 19, 546, -1000, 372, 372, 928, + -1000, 700, -1000, 694, 124, -1000, -1000, -1000, 543, 518, + -1000, 518, 518, 158, 150, -1000, 449, 213, 400, -1000, + 56, 15987, 257, 841, -1000, 838, -1000, -1000, -1000, -1000, + -1000, 35, 4989, 3274, 515, -1000, -1000, 9356, 9356, -1000, + -1000, -1000, -1000, 546, 44, -176, -1000, -1000, -1000, 16647, + 627, 546, 15987, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 312, -1000, -1000, 16317, -1000, -1000, 375, -1000, -1000, 498, + -1000, 15987, -1000, -1000, 596, 451, 604, -1000, 799, -170, + -185, 587, -1000, -1000, -1000, 701, -1000, -1000, 35, 811, + -173, -1000, 797, -1000, 15987, -1000, 32, -1000, -174, 487, + 30, -180, 725, 700, -186, 722, -1000, 923, 9686, -1000, + -1000, 925, 177, 177, 372, 546, -1000, -1000, -1000, 62, + 368, -1000, -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 1265, 26, 574, 1262, 1261, 1259, 1257, 1239, 1237, - 1236, 1235, 1234, 1232, 1231, 1230, 1229, 1228, 1227, 1224, - 1223, 1222, 1221, 1220, 1218, 1217, 90, 1214, 1213, 1208, - 65, 1207, 66, 1205, 1203, 48, 222, 43, 41, 258, - 1202, 60, 89, 73, 1199, 29, 1198, 1195, 82, 1194, - 1193, 55, 1190, 1189, 1204, 1186, 58, 1183, 12, 46, - 1180, 1179, 1178, 1177, 69, 626, 1176, 1175, 15, 1173, - 1171, 87, 1170, 53, 5, 14, 22, 18, 1169, 44, - 6, 1168, 86, 1164, 1160, 1158, 1157, 20, 1156, 59, - 1153, 34, 54, 1151, 9, 71, 30, 24, 7, 77, - 63, 1148, 23, 64, 50, 1147, 1146, 526, 1144, 1142, - 45, 1141, 1140, 19, 1136, 91, 502, 1135, 1134, 1132, - 1130, 37, 0, 478, 114, 76, 1128, 1125, 1124, 1546, - 42, 52, 17, 1123, 112, 1226, 40, 1120, 1119, 35, - 1112, 1110, 1107, 1104, 1090, 1089, 1087, 70, 1082, 1081, - 1079, 31, 21, 1078, 1077, 56, 25, 1075, 1073, 1072, - 49, 57, 1071, 1070, 51, 38, 1069, 1066, 1065, 1064, - 1063, 33, 11, 1062, 16, 1060, 13, 1059, 28, 1057, - 4, 1056, 10, 1055, 3, 1053, 8, 47, 1, 1052, - 2, 1050, 1049, 61, 631, 75, 1048, 79, + 0, 1206, 85, 503, 1203, 1201, 1200, 1199, 1198, 1196, + 1193, 1185, 1184, 1183, 1181, 1178, 1173, 1169, 1168, 1167, + 1166, 1162, 1157, 1155, 1149, 1148, 81, 1147, 1146, 1144, + 65, 1143, 63, 1140, 1139, 46, 609, 45, 42, 1289, + 1138, 23, 82, 75, 1136, 35, 1135, 1134, 69, 1133, + 1132, 64, 1131, 1128, 59, 1115, 58, 1112, 21, 73, + 1108, 1107, 1106, 1090, 70, 1103, 1085, 1084, 13, 1083, + 1082, 83, 1081, 52, 5, 12, 11, 18, 1080, 103, + 10, 1078, 50, 1077, 1076, 1075, 1074, 31, 1072, 55, + 1071, 34, 53, 1069, 7, 61, 30, 25, 9, 71, + 56, 1068, 22, 60, 48, 1066, 1065, 550, 1064, 1063, + 40, 1060, 1059, 24, 1058, 91, 401, 1057, 1056, 1055, + 1054, 33, 0, 553, 93, 66, 1052, 1051, 1050, 1555, + 38, 54, 16, 1048, 51, 1510, 41, 1046, 1044, 37, + 1043, 1042, 1041, 1040, 1039, 1038, 1037, 49, 1036, 1035, + 1025, 19, 20, 1020, 1013, 62, 27, 1010, 1005, 971, + 44, 57, 970, 968, 47, 28, 967, 966, 965, 963, + 962, 26, 14, 961, 15, 960, 17, 959, 29, 957, + 4, 956, 6, 955, 3, 954, 8, 43, 1, 953, + 2, 952, 951, 619, 570, 77, 948, 76, } var yyR1 = [...]int{ @@ -2808,20 +2809,20 @@ var yyR1 = [...]int{ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 197, - 197, 71, 70, 70, 70, 70, 70, 70, 33, 33, - 33, 33, 33, 136, 136, 139, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 83, 83, - 34, 34, 81, 81, 82, 84, 84, 80, 80, 80, - 64, 64, 64, 64, 64, 64, 64, 64, 66, 66, - 66, 85, 85, 86, 86, 87, 87, 88, 88, 89, - 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, - 63, 63, 63, 63, 63, 63, 93, 93, 93, 93, - 97, 97, 75, 75, 77, 77, 76, 78, 98, 98, - 102, 99, 99, 103, 103, 103, 103, 101, 101, 101, - 128, 128, 128, 106, 106, 115, 115, 116, 116, 107, - 107, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 118, 118, 118, 119, 119, 120, 120, 120, 127, - 127, 123, 123, 124, 124, 129, 129, 130, 130, 121, + 197, 71, 70, 70, 70, 70, 70, 70, 70, 33, + 33, 33, 33, 33, 136, 136, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 83, + 83, 34, 34, 81, 81, 82, 84, 84, 80, 80, + 80, 64, 64, 64, 64, 64, 64, 64, 64, 66, + 66, 66, 85, 85, 86, 86, 87, 87, 88, 88, + 89, 90, 90, 90, 91, 91, 91, 91, 92, 92, + 92, 63, 63, 63, 63, 63, 63, 93, 93, 93, + 93, 97, 97, 75, 75, 77, 77, 76, 78, 98, + 98, 102, 99, 99, 103, 103, 103, 103, 101, 101, + 101, 128, 128, 128, 106, 106, 115, 115, 116, 116, + 107, 107, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 118, 118, 118, 119, 119, 120, 120, 120, + 127, 127, 123, 123, 124, 124, 129, 129, 130, 130, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, @@ -2833,7 +2834,7 @@ var yyR1 = [...]int{ 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, - 121, 121, 121, 121, 121, 121, 121, 122, 122, 122, + 121, 121, 121, 121, 121, 121, 121, 121, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, @@ -2850,8 +2851,8 @@ var yyR1 = [...]int{ 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 193, 194, 134, 135, - 135, 135, + 122, 122, 122, 122, 122, 122, 122, 193, 194, 134, + 135, 135, 135, } var yyR2 = [...]int{ @@ -2906,20 +2907,20 @@ var yyR2 = [...]int{ 4, 4, 6, 6, 6, 8, 8, 8, 8, 9, 8, 5, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 0, - 2, 3, 4, 4, 4, 4, 4, 4, 0, 3, - 4, 7, 3, 1, 1, 2, 3, 3, 1, 2, - 2, 1, 2, 1, 2, 2, 1, 2, 0, 1, - 0, 2, 1, 2, 4, 0, 2, 1, 3, 5, - 1, 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, - 2, 1, 3, 5, 4, 6, 1, 3, 3, 5, - 0, 5, 1, 3, 1, 2, 3, 1, 1, 3, - 3, 1, 3, 3, 3, 3, 3, 1, 2, 1, - 1, 1, 1, 1, 1, 0, 2, 0, 3, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, - 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 3, 4, 4, 4, 4, 4, 4, 4, 0, + 3, 4, 7, 3, 1, 1, 2, 3, 3, 1, + 2, 2, 1, 2, 1, 2, 2, 1, 2, 0, + 1, 0, 2, 1, 2, 4, 0, 2, 1, 3, + 5, 1, 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, 2, 1, 3, 5, 4, 6, 1, 3, 3, + 5, 0, 5, 1, 3, 1, 2, 3, 1, 1, + 3, 3, 1, 3, 3, 3, 3, 3, 1, 2, + 1, 1, 1, 1, 1, 1, 0, 2, 0, 3, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 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, @@ -2948,8 +2949,8 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, - 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, } var yyChk = [...]int{ @@ -2990,167 +2991,167 @@ var yyChk = [...]int{ -68, -70, 60, 59, 68, 61, 62, 63, 64, 69, 70, 71, -123, -76, -193, 45, 46, 251, 252, 253, 254, 259, 255, 78, 35, 241, 249, 248, 247, 245, - 246, 243, 244, 257, 258, 129, 242, 104, 250, -107, - -107, 11, -48, -49, -54, -56, -129, -99, -137, 167, - -103, 231, 230, -124, -101, -123, -121, 229, 190, 228, - 121, 267, 74, 22, 24, 212, 77, 109, 16, 78, - 108, 251, 116, 49, 268, 243, 244, 241, 253, 254, - 242, 218, 29, 10, 270, 25, 145, 21, 34, 102, - 118, 81, 82, 148, 23, 146, 71, 273, 19, 52, - 11, 13, 274, 275, 14, 129, 128, 93, 125, 47, - 8, 112, 26, 90, 43, 276, 28, 277, 278, 279, - 280, 45, 91, 17, 245, 246, 31, 281, 259, 152, - 104, 50, 37, 75, 282, 283, 69, 284, 72, 53, - 73, 15, 48, 285, 286, 287, 288, 92, 119, 250, - 46, 289, 123, 6, 256, 30, 144, 44, 290, 124, - 80, 257, 258, 127, 70, 5, 130, 32, 9, 51, - 54, 247, 248, 249, 35, 79, 12, 291, -168, 91, - -161, 58, -54, 125, -54, 250, -116, 129, -116, -116, - 124, -54, 58, 58, 116, 118, 121, 53, -18, -54, - -115, 129, 58, -115, -115, -115, -54, 113, -54, 58, - 30, -113, 91, 12, 242, 58, 157, 124, 158, 126, - -135, -193, -124, -135, -135, -135, 161, 162, -135, -112, - -111, 224, 225, 219, 223, 12, 162, 219, 160, -135, - -134, -134, -194, 57, -92, 19, 31, -39, -129, -88, - -89, -39, -87, -2, -26, 37, -30, 21, 34, 66, - 11, -126, 74, 73, 90, -125, 22, -123, 60, 113, - -39, -62, 93, 75, 91, 92, 77, 95, 94, 105, - 98, 99, 100, 101, 102, 103, 104, 96, 97, 108, - 83, 84, 85, 86, 87, 88, 89, -108, -193, -79, - -193, 114, 115, -65, -65, -65, -65, -65, -65, -65, - -65, -193, -2, -74, -39, -193, -193, -193, -193, -193, - -193, -193, -193, -193, -83, -39, -193, -197, -71, -193, - -197, -71, -197, -71, -197, -193, -197, -71, -197, -71, - -197, -197, -71, -193, -193, -193, -193, -193, -193, -55, - 26, -54, -41, -42, -43, -44, -57, -79, -193, -54, - -54, -48, -195, 56, 11, 54, -195, 56, 113, 56, - -99, 167, -100, -104, 232, 234, 83, -128, -123, 60, - 29, 30, 57, 56, -54, -140, -143, -145, -144, -146, - -141, -142, 187, 188, 109, 191, 193, 194, 195, 196, - 197, 198, 199, 200, 201, 202, 30, 147, 183, 184, - 185, 186, 203, 204, 205, 206, 207, 208, 209, 210, - 170, 189, 261, 171, 172, 173, 174, 175, 176, 178, - 179, 180, 181, 182, 58, -135, 126, 58, 75, 58, - -54, -54, -135, -135, -135, 159, 159, 124, 124, 164, - -54, 56, 127, -48, 23, 53, -54, 58, 58, -130, - -129, -121, -135, -113, 60, -39, -135, -135, -135, -54, - -135, -135, -135, -135, 11, -110, 11, 93, -39, -114, - 91, 53, 9, 93, 56, 18, 113, 56, -90, 24, - 25, -91, -194, -32, -66, -123, 61, 64, -31, 44, - -54, -39, -39, -72, 69, 75, 70, 71, -125, 100, - -130, -124, -121, -65, -73, -76, -79, 65, 93, 91, - 92, 77, -65, -65, -65, -65, -65, -65, -65, -65, - -65, -65, -65, -65, -65, -65, -65, -136, 58, 60, - 58, -64, -64, -123, -37, 21, 34, -36, -38, -194, - 56, -194, -2, -36, -36, -39, -39, -80, 60, -36, - -80, 60, -36, -36, -30, -81, -82, 79, -80, -123, - -129, -194, -65, -123, -123, -36, -37, -36, -36, -95, - 153, -54, 30, 56, -50, -52, -51, -53, 43, 47, - 49, 44, 45, 46, 50, -133, 22, -41, -193, -132, - 153, -131, 22, -129, 60, -95, 54, -41, -54, -41, - -56, -129, 100, -103, -100, 56, 233, 235, 236, 53, - 72, -39, -152, 108, -170, -171, -172, -124, 60, 61, - -161, -162, -163, -173, 139, -178, 131, 133, 130, -164, - 140, 125, 28, 57, -157, 69, 75, -153, 215, -147, - 55, -147, -147, -147, -147, -151, 190, -151, -151, -151, - 55, 55, -147, -147, -147, -155, 55, -155, -155, -156, - 55, -156, -127, 54, -54, -135, 23, -135, -117, 121, - 118, 119, -181, 117, 212, 190, 67, 29, 15, 251, - 153, 266, 58, 154, -54, -54, -54, -54, -54, 121, - 118, -54, -54, -54, -135, -54, -113, -129, -129, 60, - -54, 39, -39, -39, -130, -89, -92, -106, 19, 11, - 35, 35, -36, 69, 70, 71, 113, -193, -73, -65, - -65, -65, -35, 148, 74, -194, -194, -36, -36, 56, - -39, -194, -194, -194, 56, 54, 22, 11, 11, -194, - 11, 11, -194, -194, -36, -84, -82, 81, -39, -194, - 113, -194, 56, 56, -194, -194, -194, -194, -63, 30, - 35, -2, -193, -193, -98, -102, -80, -42, -43, -43, - -42, -43, 43, 43, 43, 48, 43, 48, 43, -51, - -129, -194, -58, 51, 128, 52, -193, -131, -59, 12, - -41, -59, -59, 113, -104, -105, 237, 234, 240, 58, - 60, 56, -172, 83, 55, 58, 28, -164, -164, -165, - 58, -165, 28, -149, 29, 69, -154, 216, 61, -151, - -151, -152, 30, -152, -152, -152, -160, 60, -160, 61, - 61, 53, -123, -135, -134, -187, 136, 132, 139, 140, - 134, 58, 125, 28, 131, 133, 153, 130, -187, -118, - -119, 127, 22, 125, 28, 153, -186, 54, 159, 212, - 159, 127, -135, -110, 40, 113, -54, -40, 11, 100, - -124, -37, -35, 74, -65, -65, -194, -194, -38, -139, - 109, 187, 147, 185, 181, 201, 192, 214, 183, 215, - -136, -139, -65, -65, -65, -65, 260, -87, 82, -39, - 80, -124, -65, -65, -97, 53, -98, -75, -77, -76, - -193, -2, -93, -123, -96, -123, -59, 56, 83, -46, - -45, 53, 54, -47, 53, -45, 43, 43, 125, 125, - 125, -96, -87, -39, -59, 234, 238, 239, -171, -172, - -175, -174, -123, -178, -165, -165, 55, -150, 53, -65, - 57, -152, -152, 58, 109, 57, 56, 57, 56, 57, - 56, -54, -134, -134, -54, -134, -123, -184, 263, -185, - 58, -123, -123, -123, -54, -113, -59, -41, -194, -65, - -194, -147, -147, -147, -156, -147, 175, -147, 175, -194, - -194, 19, 19, 19, 19, -193, -34, 256, -39, 56, - 56, 27, -97, 56, -194, -194, -194, 56, 113, -194, - 56, -87, -102, -39, -39, 55, -39, -193, -193, -193, - -194, -91, 57, 56, -147, -94, -123, -158, 212, 9, - -151, 60, -151, 61, 61, -135, 26, -183, -182, -124, - 55, 54, -85, 13, -151, 58, -65, -65, -65, -65, - -65, -91, 60, -65, -65, 28, -77, 35, -2, -193, - -123, -123, -123, -91, -94, -94, -194, -94, -94, -132, - -177, -176, 54, 135, 67, -174, 57, 56, -159, 131, - 28, 130, -68, -152, -152, 57, 57, -193, 56, 83, - -94, -54, -86, 14, 16, -194, -194, -194, -194, -33, - 93, 263, -194, -194, -194, 9, -75, -2, 113, 57, - -194, -194, -194, -58, -176, 58, -166, 83, 60, 142, - -123, -148, 67, 28, 28, -179, -180, 153, -182, -172, - 57, -39, -74, -194, 261, 50, 264, -98, -194, -123, - 61, -54, 60, -194, 56, -123, -186, 40, 262, 265, - 55, -180, 35, -184, 40, -94, 155, 263, 57, 156, - 264, -189, -190, 53, -193, 265, -190, 53, 10, 9, - -65, 152, -188, 143, 138, 141, 30, -188, -194, -194, - 137, 29, 69, + 246, 243, 244, 257, 258, 129, 242, 123, 104, 250, + -107, -107, 11, -48, -49, -54, -56, -129, -99, -137, + 167, -103, 231, 230, -124, -101, -123, -121, 229, 190, + 228, 121, 267, 74, 22, 24, 212, 77, 109, 16, + 78, 108, 251, 116, 49, 268, 243, 244, 241, 253, + 254, 242, 218, 29, 10, 270, 25, 145, 21, 34, + 102, 118, 81, 82, 148, 23, 146, 71, 273, 19, + 52, 11, 13, 274, 275, 14, 129, 128, 93, 125, + 47, 8, 112, 26, 90, 43, 276, 28, 277, 278, + 279, 280, 45, 91, 17, 245, 246, 31, 281, 259, + 152, 104, 50, 37, 75, 282, 283, 69, 284, 72, + 53, 73, 15, 48, 285, 286, 287, 288, 92, 119, + 250, 46, 289, 123, 6, 256, 30, 144, 44, 290, + 124, 80, 257, 258, 127, 70, 5, 130, 32, 9, + 51, 54, 247, 248, 249, 35, 79, 12, 291, -168, + 91, -161, 58, -54, 125, -54, 250, -116, 129, -116, + -116, 124, -54, 58, 58, 116, 118, 121, 53, -18, + -54, -115, 129, 58, -115, -115, -115, -54, 113, -54, + 58, 30, -113, 91, 12, 242, 58, 157, 124, 158, + 126, -135, -193, -124, -135, -135, -135, 161, 162, -135, + -112, -111, 224, 225, 219, 223, 12, 162, 219, 160, + -135, -134, -134, -194, 57, -92, 19, 31, -39, -129, + -88, -89, -39, -87, -2, -26, 37, -30, 21, 34, + 66, 11, -126, 74, 73, 90, -125, 22, -123, 60, + 113, -39, -62, 93, 75, 91, 92, 77, 95, 94, + 105, 98, 99, 100, 101, 102, 103, 104, 96, 97, + 108, 83, 84, 85, 86, 87, 88, 89, -108, -193, + -79, -193, 114, 115, -65, -65, -65, -65, -65, -65, + -65, -65, -193, -2, -74, -39, -193, -193, -193, -193, + -193, -193, -193, -193, -193, -83, -39, -193, -197, -71, + -193, -197, -71, -197, -71, -197, -193, -197, -71, -197, + -71, -197, -197, -71, -193, -193, -193, -193, -193, -193, + -193, -55, 26, -54, -41, -42, -43, -44, -57, -79, + -193, -54, -54, -48, -195, 56, 11, 54, -195, 56, + 113, 56, -99, 167, -100, -104, 232, 234, 83, -128, + -123, 60, 29, 30, 57, 56, -54, -140, -143, -145, + -144, -146, -141, -142, 187, 188, 109, 191, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 30, 147, + 183, 184, 185, 186, 203, 204, 205, 206, 207, 208, + 209, 210, 170, 189, 261, 171, 172, 173, 174, 175, + 176, 178, 179, 180, 181, 182, 58, -135, 126, 58, + 75, 58, -54, -54, -135, -135, -135, 159, 159, 124, + 124, 164, -54, 56, 127, -48, 23, 53, -54, 58, + 58, -130, -129, -121, -135, -113, 60, -39, -135, -135, + -135, -54, -135, -135, -135, -135, 11, -110, 11, 93, + -39, -114, 91, 53, 9, 93, 56, 18, 113, 56, + -90, 24, 25, -91, -194, -32, -66, -123, 61, 64, + -31, 44, -54, -39, -39, -72, 69, 75, 70, 71, + -125, 100, -130, -124, -121, -65, -73, -76, -79, 65, + 93, 91, 92, 77, -65, -65, -65, -65, -65, -65, + -65, -65, -65, -65, -65, -65, -65, -65, -65, -136, + 58, 60, 58, -64, -64, -123, -37, 21, 34, -36, + -38, -194, 56, -194, -2, -36, -36, -39, -39, -80, + 60, -36, -80, 60, -36, -36, -30, -81, -82, 79, + -80, -123, -129, -194, -65, -123, -123, -36, -37, -37, + -36, -36, -95, 153, -54, 30, 56, -50, -52, -51, + -53, 43, 47, 49, 44, 45, 46, 50, -133, 22, + -41, -193, -132, 153, -131, 22, -129, 60, -95, 54, + -41, -54, -41, -56, -129, 100, -103, -100, 56, 233, + 235, 236, 53, 72, -39, -152, 108, -170, -171, -172, + -124, 60, 61, -161, -162, -163, -173, 139, -178, 131, + 133, 130, -164, 140, 125, 28, 57, -157, 69, 75, + -153, 215, -147, 55, -147, -147, -147, -147, -151, 190, + -151, -151, -151, 55, 55, -147, -147, -147, -155, 55, + -155, -155, -156, 55, -156, -127, 54, -54, -135, 23, + -135, -117, 121, 118, 119, -181, 117, 212, 190, 67, + 29, 15, 251, 153, 266, 58, 154, -54, -54, -54, + -54, -54, 121, 118, -54, -54, -54, -135, -54, -113, + -129, -129, 60, -54, 39, -39, -39, -130, -89, -92, + -106, 19, 11, 35, 35, -36, 69, 70, 71, 113, + -193, -73, -65, -65, -65, -35, 148, 74, -194, -194, + -36, -36, 56, -39, -194, -194, -194, 56, 54, 22, + 11, 11, -194, 11, 11, -194, -194, -36, -84, -82, + 81, -39, -194, 113, -194, 56, 56, -194, -194, -194, + -194, -194, -63, 30, 35, -2, -193, -193, -98, -102, + -80, -42, -43, -43, -42, -43, 43, 43, 43, 48, + 43, 48, 43, -51, -129, -194, -58, 51, 128, 52, + -193, -131, -59, 12, -41, -59, -59, 113, -104, -105, + 237, 234, 240, 58, 60, 56, -172, 83, 55, 58, + 28, -164, -164, -165, 58, -165, 28, -149, 29, 69, + -154, 216, 61, -151, -151, -152, 30, -152, -152, -152, + -160, 60, -160, 61, 61, 53, -123, -135, -134, -187, + 136, 132, 139, 140, 134, 58, 125, 28, 131, 133, + 153, 130, -187, -118, -119, 127, 22, 125, 28, 153, + -186, 54, 159, 212, 159, 127, -135, -110, 40, 113, + -54, -40, 11, 100, -124, -37, -35, 74, -65, -65, + -194, -194, -38, -139, 109, 187, 147, 185, 181, 201, + 192, 214, 183, 215, -136, -139, -65, -65, -65, -65, + 260, -87, 82, -39, 80, -124, -65, -65, -97, 53, + -98, -75, -77, -76, -193, -2, -93, -123, -96, -123, + -59, 56, 83, -46, -45, 53, 54, -47, 53, -45, + 43, 43, 125, 125, 125, -96, -87, -39, -59, 234, + 238, 239, -171, -172, -175, -174, -123, -178, -165, -165, + 55, -150, 53, -65, 57, -152, -152, 58, 109, 57, + 56, 57, 56, 57, 56, -54, -134, -134, -54, -134, + -123, -184, 263, -185, 58, -123, -123, -123, -54, -113, + -59, -41, -194, -65, -194, -147, -147, -147, -156, -147, + 175, -147, 175, -194, -194, 19, 19, 19, 19, -193, + -34, 256, -39, 56, 56, 27, -97, 56, -194, -194, + -194, 56, 113, -194, 56, -87, -102, -39, -39, 55, + -39, -193, -193, -193, -194, -91, 57, 56, -147, -94, + -123, -158, 212, 9, -151, 60, -151, 61, 61, -135, + 26, -183, -182, -124, 55, 54, -85, 13, -151, 58, + -65, -65, -65, -65, -65, -91, 60, -65, -65, 28, + -77, 35, -2, -193, -123, -123, -123, -91, -94, -94, + -194, -94, -94, -132, -177, -176, 54, 135, 67, -174, + 57, 56, -159, 131, 28, 130, -68, -152, -152, 57, + 57, -193, 56, 83, -94, -54, -86, 14, 16, -194, + -194, -194, -194, -33, 93, 263, -194, -194, -194, 9, + -75, -2, 113, 57, -194, -194, -194, -58, -176, 58, + -166, 83, 60, 142, -123, -148, 67, 28, 28, -179, + -180, 153, -182, -172, 57, -39, -74, -194, 261, 50, + 264, -98, -194, -123, 61, -54, 60, -194, 56, -123, + -186, 40, 262, 265, 55, -180, 35, -184, 40, -94, + 155, 263, 57, 156, 264, -189, -190, 53, -193, 265, + -190, 53, 10, 9, -65, 152, -188, 143, 138, 141, + 30, -188, -194, -194, 137, 29, 69, } var yyDef = [...]int{ 23, -2, 2, -2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 565, 0, 319, 319, 319, 319, 319, 319, - 0, 636, 619, 0, 0, 0, 0, -2, 306, 307, - 0, 309, 310, 938, 938, 938, 938, 938, 0, 0, - 938, 0, 35, 36, 936, 1, 3, 573, 0, 0, - 323, 326, 321, 0, 619, 619, 0, 0, 65, 66, - 0, 0, 0, 925, 0, 617, 617, 617, 637, 638, - 641, 642, 767, 768, 769, 770, 771, 772, 773, 774, - 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, - 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, - 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, - 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, - 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, - 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, - 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, - 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, - 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, - 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, - 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, - 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, - 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, - 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, - 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, - 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, - 0, 0, 0, 0, 0, 620, 0, 615, 0, 615, - 615, 615, 0, 258, 392, 645, 646, 925, 0, 0, - 0, 297, 0, 939, 270, 939, 939, 273, 939, 0, - 939, 0, 280, 0, 0, 286, 939, 303, 304, 291, - 305, 308, 311, 312, 313, 314, 315, 938, 938, 318, - 29, 577, 0, 0, 565, 31, 0, 319, 324, 325, + 21, 22, 566, 0, 319, 319, 319, 319, 319, 319, + 0, 637, 620, 0, 0, 0, 0, -2, 306, 307, + 0, 309, 310, 939, 939, 939, 939, 939, 0, 0, + 939, 0, 35, 36, 937, 1, 3, 574, 0, 0, + 323, 326, 321, 0, 620, 620, 0, 0, 65, 66, + 0, 0, 0, 926, 0, 618, 618, 618, 638, 639, + 642, 643, 768, 769, 770, 771, 772, 773, 774, 775, + 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, + 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, + 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, + 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, + 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, + 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, + 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, + 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, + 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, + 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, + 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, + 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, + 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, + 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, + 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, + 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, + 0, 0, 0, 0, 0, 621, 0, 616, 0, 616, + 616, 616, 0, 258, 392, 646, 647, 926, 0, 0, + 0, 297, 0, 940, 270, 940, 940, 273, 940, 0, + 940, 0, 280, 0, 0, 286, 940, 303, 304, 291, + 305, 308, 311, 312, 313, 314, 315, 939, 939, 318, + 29, 578, 0, 0, 566, 31, 0, 319, 324, 325, 329, 327, 328, 320, 0, 338, 342, 0, 402, 0, 407, 409, -2, -2, 0, 444, 445, 446, 447, 448, 0, 0, 0, 0, 0, 0, 0, 0, 472, 473, - 474, 475, 550, 551, 552, 553, 554, 555, 556, 557, - 411, 412, 547, 597, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 538, 0, 509, 509, 509, 509, 509, + 474, 475, 551, 552, 553, 554, 555, 556, 557, 558, + 411, 412, 548, 598, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 539, 0, 509, 509, 509, 509, 509, 509, 509, 509, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 44, 46, 392, 50, 0, 914, - 601, -2, -2, 0, 0, 643, 644, -2, 780, -2, - 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, + 0, 0, 0, 0, 0, 44, 46, 392, 50, 0, + 915, 602, -2, -2, 0, 0, 644, 645, -2, 781, + -2, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, @@ -3161,109 +3162,109 @@ var yyDef = [...]int{ 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, - 759, 760, 761, 762, 763, 764, 765, 766, 0, 0, - 84, 0, 82, 0, 939, 0, 0, 0, 0, 0, - 0, 939, 939, 939, 0, 0, 0, 0, 249, 0, - 0, 0, 0, 0, 0, 0, 257, 0, 259, 939, - 297, 262, 0, 0, 939, 939, 939, 0, 939, 939, - 269, 940, 941, 271, 272, 274, 939, 939, 276, 0, - 294, 292, 293, 288, 289, 0, 300, 283, 284, 287, - 316, 317, 30, 937, 24, 0, 0, 574, 0, 566, - 567, 570, 573, 29, 326, 0, 332, 330, 331, 322, - 0, 339, 0, 0, 0, 343, 0, 345, 346, 0, - 405, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 429, 430, 431, 432, 433, 434, 435, 408, 0, 422, - 0, 0, 0, 464, 465, 466, 467, 468, 469, 470, - 0, 334, 29, 0, 442, 0, 0, 0, 0, 0, - 0, 0, 0, 329, 0, 539, 0, 493, 501, 0, - 494, 502, 495, 503, 496, 0, 497, 504, 498, 505, - 499, 500, 506, 0, 0, 0, 334, 0, 0, 48, - 0, 391, 0, 349, 351, 352, 353, -2, 0, 375, - -2, 0, 0, 0, 42, 43, 0, 0, 0, 0, - 51, 914, 53, 54, 0, 0, 0, 162, 610, 611, - 612, 608, 206, 0, 0, 150, 146, 90, 91, 92, - 139, 94, 139, 139, 139, 139, 159, 159, 159, 159, - 122, 123, 124, 125, 126, 0, 0, 109, 139, 139, - 139, 113, 129, 130, 131, 132, 133, 134, 135, 136, - 95, 96, 97, 98, 99, 100, 101, 102, 103, 141, - 141, 141, 143, 143, 639, 68, 0, 939, 0, 939, - 80, 0, 220, 222, 223, 0, 0, 0, 0, 0, - 0, 0, 0, 252, 616, 0, 939, 255, 256, 393, - 647, 648, 260, 261, 298, 299, 263, 264, 265, 266, - 267, 268, 275, 279, 0, 297, 0, 0, 281, 282, - 0, 0, 578, 0, 0, 0, 0, 0, 569, 571, - 572, 577, 32, 329, 0, 558, 0, 0, 0, 333, - 27, 403, 404, 406, 423, 0, 425, 427, 344, 340, - 0, 548, -2, 413, 414, 438, 439, 440, 0, 0, - 0, 0, 436, 418, 0, 449, 450, 451, 452, 453, - 454, 455, 456, 457, 458, 459, 460, 463, 523, 524, - 0, 461, 462, 471, 0, 0, 0, 335, 336, 441, - 0, 596, 29, 0, 0, 0, 0, 446, 550, 0, - 446, 550, 0, 0, 0, 545, 542, 0, 0, 547, - 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 390, 0, 0, 0, 0, 0, 0, 380, 0, - 0, 383, 0, 0, 0, 0, 374, 0, 0, 395, - 859, 376, 0, 378, 379, 400, 0, 400, 45, 400, - 47, 0, 394, 602, 52, 0, 0, 57, 58, 603, - 604, 605, 606, 0, 81, 207, 209, 212, 213, 214, - 85, 86, 87, 0, 0, 194, 0, 0, 188, 188, - 0, 186, 187, 83, 153, 151, 0, 148, 147, 93, - 0, 159, 159, 116, 117, 162, 0, 162, 162, 162, - 0, 0, 110, 111, 112, 104, 0, 105, 106, 107, - 0, 108, 0, 0, 939, 70, 618, 71, 938, 0, - 0, 631, 221, 621, 622, 623, 624, 625, 626, 627, - 628, 629, 630, 0, 72, 225, 227, 226, 230, 0, - 0, 0, 250, 939, 254, 294, 278, 295, 296, 301, - 285, 0, 575, 576, 0, 568, 25, 0, 613, 614, - 559, 560, 347, 424, 426, 428, 0, 334, 415, 436, - 419, 0, 416, 0, 0, 410, 476, 0, 0, 0, - 443, -2, 480, 481, 0, 0, 0, 0, 0, 516, - 0, 0, 517, 0, 565, 0, 543, 0, 0, 492, - 0, 511, 0, 0, 512, 513, 514, 515, 590, 0, - 0, -2, 0, 0, 400, 598, 0, 350, 369, 371, - 0, 366, 381, 382, 384, 0, 386, 0, 388, 389, - 354, 356, 357, 0, 0, 0, 0, 377, 565, 0, - 400, 40, 41, 0, 55, 56, 0, 0, 62, 163, - 164, 0, 210, 0, 0, 0, 181, 188, 188, 184, - 189, 185, 0, 155, 0, 152, 89, 149, 0, 162, - 162, 118, 0, 119, 120, 121, 0, 137, 0, 0, - 0, 0, 640, 69, 215, 938, 232, 233, 234, 235, - 236, 237, 238, 239, 240, 241, 242, 243, 938, 0, - 938, 632, 633, 634, 635, 0, 75, 0, 0, 0, - 0, 0, 253, 297, 579, 0, 26, 400, 0, 341, - 549, 0, 417, 0, 437, 420, 477, 478, 337, 0, - 139, 139, 528, 139, 143, 531, 139, 533, 139, 536, - 0, 0, 0, 0, 0, 0, 0, 540, 491, 546, - 0, 548, 0, 0, 33, 0, 590, 580, 592, 594, - 0, 29, 0, 586, 0, 361, 565, 0, 0, 363, - 370, 0, 0, 364, 0, 365, 385, 387, 0, 0, - 0, 0, 573, 401, 39, 59, 60, 61, 208, 211, - 0, 190, 139, 193, 182, 183, 0, 157, 0, 154, - 140, 114, 115, 160, 161, 159, 0, 159, 0, 144, - 0, 939, 216, 217, 218, 219, 0, 224, 0, 73, - 74, 0, 0, 229, 251, 277, 561, 348, 479, 421, - 482, 525, 159, 529, 530, 532, 534, 535, 537, 484, - 483, 0, 0, 0, 0, 0, 573, 0, 544, 0, - 0, 0, 34, 0, 595, -2, 0, 0, 0, 49, - 0, 573, 599, 600, 367, 0, 372, 0, 0, 0, - 375, 38, 173, 0, 192, 0, 359, 165, 158, 0, - 162, 138, 162, 0, 0, 67, 0, 76, 77, 0, - 0, 0, 563, 0, 526, 527, 0, 0, 0, 0, - 518, 0, 541, 0, 0, 0, 593, 0, -2, 0, - 588, 587, 362, 37, 0, 0, 397, 0, 0, 395, - 172, 174, 0, 179, 0, 191, 0, 0, 170, 0, - 167, 169, 156, 127, 128, 142, 145, 0, 0, 0, - 0, 231, 28, 0, 0, 485, 487, 486, 488, 0, - 0, 0, 490, 507, 508, 0, 583, 29, 0, 368, - 396, 398, 399, 358, 175, 176, 0, 180, 178, 0, - 360, 88, 0, 166, 168, 0, 245, 0, 78, 79, - 72, 564, 562, 489, 0, 0, 0, 591, -2, 589, - 177, 0, 171, 244, 0, 0, 75, 519, 0, 522, - 0, 246, 0, 228, 520, 0, 0, 0, 195, 0, - 0, 196, 197, 0, 0, 521, 198, 0, 0, 0, - 0, 0, 199, 201, 202, 0, 0, 200, 247, 248, - 203, 204, 205, + 759, 760, 761, 762, 763, 764, 765, 766, 767, 0, + 0, 84, 0, 82, 0, 940, 0, 0, 0, 0, + 0, 0, 940, 940, 940, 0, 0, 0, 0, 249, + 0, 0, 0, 0, 0, 0, 0, 257, 0, 259, + 940, 297, 262, 0, 0, 940, 940, 940, 0, 940, + 940, 269, 941, 942, 271, 272, 274, 940, 940, 276, + 0, 294, 292, 293, 288, 289, 0, 300, 283, 284, + 287, 316, 317, 30, 938, 24, 0, 0, 575, 0, + 567, 568, 571, 574, 29, 326, 0, 332, 330, 331, + 322, 0, 339, 0, 0, 0, 343, 0, 345, 346, + 0, 405, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 429, 430, 431, 432, 433, 434, 435, 408, 0, + 422, 0, 0, 0, 464, 465, 466, 467, 468, 469, + 470, 0, 334, 29, 0, 442, 0, 0, 0, 0, + 0, 0, 0, 0, 329, 0, 540, 0, 493, 501, + 0, 494, 502, 495, 503, 496, 0, 497, 504, 498, + 505, 499, 500, 506, 0, 0, 0, 334, 334, 0, + 0, 48, 0, 391, 0, 349, 351, 352, 353, -2, + 0, 375, -2, 0, 0, 0, 42, 43, 0, 0, + 0, 0, 51, 915, 53, 54, 0, 0, 0, 162, + 611, 612, 613, 609, 206, 0, 0, 150, 146, 90, + 91, 92, 139, 94, 139, 139, 139, 139, 159, 159, + 159, 159, 122, 123, 124, 125, 126, 0, 0, 109, + 139, 139, 139, 113, 129, 130, 131, 132, 133, 134, + 135, 136, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 141, 141, 141, 143, 143, 640, 68, 0, 940, + 0, 940, 80, 0, 220, 222, 223, 0, 0, 0, + 0, 0, 0, 0, 0, 252, 617, 0, 940, 255, + 256, 393, 648, 649, 260, 261, 298, 299, 263, 264, + 265, 266, 267, 268, 275, 279, 0, 297, 0, 0, + 281, 282, 0, 0, 579, 0, 0, 0, 0, 0, + 570, 572, 573, 578, 32, 329, 0, 559, 0, 0, + 0, 333, 27, 403, 404, 406, 423, 0, 425, 427, + 344, 340, 0, 549, -2, 413, 414, 438, 439, 440, + 0, 0, 0, 0, 436, 418, 0, 449, 450, 451, + 452, 453, 454, 455, 456, 457, 458, 459, 460, 463, + 524, 525, 0, 461, 462, 471, 0, 0, 0, 335, + 336, 441, 0, 597, 29, 0, 0, 0, 0, 446, + 551, 0, 446, 551, 0, 0, 0, 546, 543, 0, + 0, 548, 0, 510, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 390, 0, 0, 0, 0, 0, + 0, 380, 0, 0, 383, 0, 0, 0, 0, 374, + 0, 0, 395, 860, 376, 0, 378, 379, 400, 0, + 400, 45, 400, 47, 0, 394, 603, 52, 0, 0, + 57, 58, 604, 605, 606, 607, 0, 81, 207, 209, + 212, 213, 214, 85, 86, 87, 0, 0, 194, 0, + 0, 188, 188, 0, 186, 187, 83, 153, 151, 0, + 148, 147, 93, 0, 159, 159, 116, 117, 162, 0, + 162, 162, 162, 0, 0, 110, 111, 112, 104, 0, + 105, 106, 107, 0, 108, 0, 0, 940, 70, 619, + 71, 939, 0, 0, 632, 221, 622, 623, 624, 625, + 626, 627, 628, 629, 630, 631, 0, 72, 225, 227, + 226, 230, 0, 0, 0, 250, 940, 254, 294, 278, + 295, 296, 301, 285, 0, 576, 577, 0, 569, 25, + 0, 614, 615, 560, 561, 347, 424, 426, 428, 0, + 334, 415, 436, 419, 0, 416, 0, 0, 410, 476, + 0, 0, 0, 443, -2, 480, 481, 0, 0, 0, + 0, 0, 517, 0, 0, 518, 0, 566, 0, 544, + 0, 0, 492, 0, 511, 0, 0, 512, 513, 514, + 515, 516, 591, 0, 0, -2, 0, 0, 400, 599, + 0, 350, 369, 371, 0, 366, 381, 382, 384, 0, + 386, 0, 388, 389, 354, 356, 357, 0, 0, 0, + 0, 377, 566, 0, 400, 40, 41, 0, 55, 56, + 0, 0, 62, 163, 164, 0, 210, 0, 0, 0, + 181, 188, 188, 184, 189, 185, 0, 155, 0, 152, + 89, 149, 0, 162, 162, 118, 0, 119, 120, 121, + 0, 137, 0, 0, 0, 0, 641, 69, 215, 939, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 939, 0, 939, 633, 634, 635, 636, 0, + 75, 0, 0, 0, 0, 0, 253, 297, 580, 0, + 26, 400, 0, 341, 550, 0, 417, 0, 437, 420, + 477, 478, 337, 0, 139, 139, 529, 139, 143, 532, + 139, 534, 139, 537, 0, 0, 0, 0, 0, 0, + 0, 541, 491, 547, 0, 549, 0, 0, 33, 0, + 591, 581, 593, 595, 0, 29, 0, 587, 0, 361, + 566, 0, 0, 363, 370, 0, 0, 364, 0, 365, + 385, 387, 0, 0, 0, 0, 574, 401, 39, 59, + 60, 61, 208, 211, 0, 190, 139, 193, 182, 183, + 0, 157, 0, 154, 140, 114, 115, 160, 161, 159, + 0, 159, 0, 144, 0, 940, 216, 217, 218, 219, + 0, 224, 0, 73, 74, 0, 0, 229, 251, 277, + 562, 348, 479, 421, 482, 526, 159, 530, 531, 533, + 535, 536, 538, 484, 483, 0, 0, 0, 0, 0, + 574, 0, 545, 0, 0, 0, 34, 0, 596, -2, + 0, 0, 0, 49, 0, 574, 600, 601, 367, 0, + 372, 0, 0, 0, 375, 38, 173, 0, 192, 0, + 359, 165, 158, 0, 162, 138, 162, 0, 0, 67, + 0, 76, 77, 0, 0, 0, 564, 0, 527, 528, + 0, 0, 0, 0, 519, 0, 542, 0, 0, 0, + 594, 0, -2, 0, 589, 588, 362, 37, 0, 0, + 397, 0, 0, 395, 172, 174, 0, 179, 0, 191, + 0, 0, 170, 0, 167, 169, 156, 127, 128, 142, + 145, 0, 0, 0, 0, 231, 28, 0, 0, 485, + 487, 486, 488, 0, 0, 0, 490, 507, 508, 0, + 584, 29, 0, 368, 396, 398, 399, 358, 175, 176, + 0, 180, 178, 0, 360, 88, 0, 166, 168, 0, + 245, 0, 78, 79, 72, 565, 563, 489, 0, 0, + 0, 592, -2, 590, 177, 0, 171, 244, 0, 0, + 75, 520, 0, 523, 0, 246, 0, 228, 521, 0, + 0, 0, 195, 0, 0, 196, 197, 0, 0, 522, + 198, 0, 0, 0, 0, 0, 199, 201, 202, 0, + 0, 200, 247, 248, 203, 204, 205, } var yyTok1 = [...]int{ @@ -6627,19 +6628,19 @@ yydefault: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2667 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("schema"), Exprs: yyDollar[3].selectExprs} } case 515: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2671 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } case 516: yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2675 { - yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} + yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } case 517: yyDollar = yyS[yypt-4 : yypt+1] @@ -6648,40 +6649,40 @@ yydefault: yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 518: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2685 + yyDollar = yyS[yypt-4 : yypt+1] +//line sql.y:2683 { - yyVAL.str = "" + yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 519: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:2689 { - yyVAL.str = BooleanModeStr + yyVAL.str = "" } case 520: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2693 { - yyVAL.str = NaturalLanguageModeStr + yyVAL.str = BooleanModeStr } case 521: - yyDollar = yyS[yypt-7 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] //line sql.y:2697 { - yyVAL.str = NaturalLanguageModeWithQueryExpansionStr + yyVAL.str = NaturalLanguageModeStr } case 522: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-7 : yypt+1] //line sql.y:2701 { - yyVAL.str = QueryExpansionStr + yyVAL.str = NaturalLanguageModeWithQueryExpansionStr } case 523: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2707 + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:2705 { - yyVAL.str = string(yyDollar[1].bytes) + yyVAL.str = QueryExpansionStr } case 524: yyDollar = yyS[yypt-1 : yypt+1] @@ -6690,63 +6691,63 @@ yydefault: yyVAL.str = string(yyDollar[1].bytes) } case 525: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2717 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2715 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.str = string(yyDollar[1].bytes) } case 526: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2721 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Operator: CharacterSetStr} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 527: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2725 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Operator: CharacterSetStr} } case 528: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2729 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].bytes)} } case 529: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2733 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 530: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2737 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} - yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 531: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2743 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2741 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale } case 532: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2747 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 533: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2751 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 534: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2755 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} @@ -6755,143 +6756,149 @@ yydefault: yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2759 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 536: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:2763 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 537: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2767 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 538: + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:2771 + { + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} + } + case 539: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2772 +//line sql.y:2776 { yyVAL.expr = nil } - case 539: + case 540: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2776 +//line sql.y:2780 { yyVAL.expr = yyDollar[1].expr } - case 540: + case 541: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2781 +//line sql.y:2785 { yyVAL.str = string("") } - case 541: + case 542: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2785 +//line sql.y:2789 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } - case 542: + case 543: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2791 +//line sql.y:2795 { yyVAL.whens = []*When{yyDollar[1].when} } - case 543: + case 544: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2795 +//line sql.y:2799 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } - case 544: + case 545: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2801 +//line sql.y:2805 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } - case 545: + case 546: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2806 +//line sql.y:2810 { yyVAL.expr = nil } - case 546: + case 547: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2810 +//line sql.y:2814 { yyVAL.expr = yyDollar[2].expr } - case 547: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2816 - { - yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} - } case 548: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2820 { - yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} + yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } case 549: - yyDollar = yyS[yypt-5 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:2824 { - yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} + yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } case 550: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2830 + yyDollar = yyS[yypt-5 : yypt+1] +//line sql.y:2828 { - yyVAL.expr = NewStrVal(yyDollar[1].bytes) + yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } case 551: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2834 { - yyVAL.expr = NewHexVal(yyDollar[1].bytes) + yyVAL.expr = NewStrVal(yyDollar[1].bytes) } case 552: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2838 { - yyVAL.expr = NewBitVal(yyDollar[1].bytes) + yyVAL.expr = NewHexVal(yyDollar[1].bytes) } case 553: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2842 { - yyVAL.expr = NewIntVal(yyDollar[1].bytes) + yyVAL.expr = NewBitVal(yyDollar[1].bytes) } case 554: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2846 { - yyVAL.expr = NewFloatVal(yyDollar[1].bytes) + yyVAL.expr = NewIntVal(yyDollar[1].bytes) } case 555: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2850 { - yyVAL.expr = NewHexNum(yyDollar[1].bytes) + yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } case 556: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2854 { - yyVAL.expr = NewValArg(yyDollar[1].bytes) + yyVAL.expr = NewHexNum(yyDollar[1].bytes) } case 557: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:2858 { - yyVAL.expr = &NullVal{} + yyVAL.expr = NewValArg(yyDollar[1].bytes) } case 558: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2864 +//line sql.y:2862 + { + yyVAL.expr = &NullVal{} + } + case 559: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:2868 { // TODO(sougou): Deprecate this construct. if yyDollar[1].colIdent.Lowered() != "value" { @@ -6900,239 +6907,239 @@ yydefault: } yyVAL.expr = NewIntVal([]byte("1")) } - case 559: + case 560: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2873 +//line sql.y:2877 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 560: + case 561: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2877 +//line sql.y:2881 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 561: + case 562: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2882 +//line sql.y:2886 { yyVAL.exprs = nil } - case 562: + case 563: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2886 +//line sql.y:2890 { yyVAL.exprs = yyDollar[3].exprs } - case 563: + case 564: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2891 +//line sql.y:2895 { yyVAL.expr = nil } - case 564: + case 565: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2895 +//line sql.y:2899 { yyVAL.expr = yyDollar[2].expr } - case 565: + case 566: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2900 +//line sql.y:2904 { yyVAL.orderBy = nil } - case 566: + case 567: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2904 +//line sql.y:2908 { yyVAL.orderBy = yyDollar[3].orderBy } - case 567: + case 568: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2910 +//line sql.y:2914 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } - case 568: + case 569: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2914 +//line sql.y:2918 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } - case 569: + case 570: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2920 +//line sql.y:2924 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } - case 570: + case 571: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2925 +//line sql.y:2929 { yyVAL.str = AscScr } - case 571: + case 572: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2929 +//line sql.y:2933 { yyVAL.str = AscScr } - case 572: + case 573: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2933 +//line sql.y:2937 { yyVAL.str = DescScr } - case 573: + case 574: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2938 +//line sql.y:2942 { yyVAL.limit = nil } - case 574: + case 575: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2942 +//line sql.y:2946 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } - case 575: + case 576: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2946 +//line sql.y:2950 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } - case 576: + case 577: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2950 +//line sql.y:2954 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } - case 577: + case 578: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2955 +//line sql.y:2959 { yyVAL.str = "" } - case 578: + case 579: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2959 +//line sql.y:2963 { yyVAL.str = ForUpdateStr } - case 579: + case 580: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2963 +//line sql.y:2967 { yyVAL.str = ShareModeStr } - case 580: + case 581: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2976 +//line sql.y:2980 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } - case 581: + case 582: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2980 +//line sql.y:2984 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } - case 582: + case 583: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2984 +//line sql.y:2988 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } - case 583: + case 584: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2989 +//line sql.y:2993 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } - case 584: + case 585: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2993 +//line sql.y:2997 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } - case 585: + case 586: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2997 +//line sql.y:3001 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } - case 586: + case 587: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3004 +//line sql.y:3008 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 587: + case 588: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3008 +//line sql.y:3012 { yyVAL.columns = Columns{yyDollar[3].colIdent} } - case 588: + case 589: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3012 +//line sql.y:3016 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 589: + case 590: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3016 +//line sql.y:3020 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } - case 590: + case 591: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3021 +//line sql.y:3025 { yyVAL.updateExprs = nil } - case 591: + case 592: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3025 +//line sql.y:3029 { yyVAL.updateExprs = yyDollar[5].updateExprs } - case 592: + case 593: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3031 +//line sql.y:3035 { yyVAL.values = Values{yyDollar[1].valTuple} } - case 593: + case 594: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3035 +//line sql.y:3039 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } - case 594: + case 595: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3041 +//line sql.y:3045 { yyVAL.valTuple = yyDollar[1].valTuple } - case 595: + case 596: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3045 +//line sql.y:3049 { yyVAL.valTuple = ValTuple{} } - case 596: + case 597: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3051 +//line sql.y:3055 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } - case 597: + case 598: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3057 +//line sql.y:3061 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -7140,195 +7147,189 @@ yydefault: yyVAL.expr = yyDollar[1].valTuple } } - case 598: + case 599: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3067 +//line sql.y:3071 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } - case 599: + case 600: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3071 +//line sql.y:3075 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } - case 600: + case 601: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3077 +//line sql.y:3081 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } - case 601: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3083 - { - yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} - } case 602: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:3087 { - yyVAL.setExprs = append(yyDollar[1].setExprs, yyDollar[3].setExpr) + yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} } case 603: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3093 +//line sql.y:3091 { - yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("on"))} + yyVAL.setExprs = append(yyDollar[1].setExprs, yyDollar[3].setExpr) } case 604: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:3097 { - yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("off"))} + yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("on"))} } case 605: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:3101 { - yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: yyDollar[3].expr} + yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("off"))} } case 606: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:3105 + { + yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: yyDollar[3].expr} + } + case 607: + yyDollar = yyS[yypt-3 : yypt+1] +//line sql.y:3109 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(string(yyDollar[1].bytes)), Expr: yyDollar[2].expr} } - case 608: + case 609: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3112 +//line sql.y:3116 { yyVAL.bytes = []byte("charset") } - case 610: + case 611: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3119 +//line sql.y:3123 { yyVAL.expr = NewStrVal([]byte(yyDollar[1].colIdent.String())) } - case 611: + case 612: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3123 +//line sql.y:3127 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } - case 612: + case 613: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3127 +//line sql.y:3131 { yyVAL.expr = &Default{} } - case 615: + case 616: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3136 +//line sql.y:3140 { yyVAL.byt = 0 } - case 616: + case 617: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3138 +//line sql.y:3142 { yyVAL.byt = 1 } - case 617: + case 618: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3141 +//line sql.y:3145 { yyVAL.empty = struct{}{} } - case 618: + case 619: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3143 +//line sql.y:3147 { yyVAL.empty = struct{}{} } - case 619: + case 620: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3146 +//line sql.y:3150 { yyVAL.str = "" } - case 620: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3148 - { - yyVAL.str = IgnoreStr - } case 621: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:3152 { - yyVAL.empty = struct{}{} + yyVAL.str = IgnoreStr } case 622: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3154 +//line sql.y:3156 { yyVAL.empty = struct{}{} } case 623: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3156 +//line sql.y:3158 { yyVAL.empty = struct{}{} } case 624: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3158 +//line sql.y:3160 { yyVAL.empty = struct{}{} } case 625: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3160 +//line sql.y:3162 { yyVAL.empty = struct{}{} } case 626: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3162 +//line sql.y:3164 { yyVAL.empty = struct{}{} } case 627: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3164 +//line sql.y:3166 { yyVAL.empty = struct{}{} } case 628: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3166 +//line sql.y:3168 { yyVAL.empty = struct{}{} } case 629: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3168 +//line sql.y:3170 { yyVAL.empty = struct{}{} } case 630: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3170 +//line sql.y:3172 { yyVAL.empty = struct{}{} } case 631: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3173 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3174 { yyVAL.empty = struct{}{} } case 632: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3175 + yyDollar = yyS[yypt-0 : yypt+1] +//line sql.y:3177 { yyVAL.empty = struct{}{} } case 633: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3177 +//line sql.y:3179 { yyVAL.empty = struct{}{} } @@ -7340,45 +7341,45 @@ yydefault: } case 635: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3183 +//line sql.y:3185 { yyVAL.empty = struct{}{} } case 636: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3186 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3187 { yyVAL.empty = struct{}{} } case 637: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3188 + yyDollar = yyS[yypt-0 : yypt+1] +//line sql.y:3190 { yyVAL.empty = struct{}{} } case 638: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3190 +//line sql.y:3192 { yyVAL.empty = struct{}{} } case 639: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3193 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3194 { - yyVAL.colIdent = ColIdent{} + yyVAL.empty = struct{}{} } case 640: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3195 + yyDollar = yyS[yypt-0 : yypt+1] +//line sql.y:3197 { - yyVAL.colIdent = yyDollar[2].colIdent + yyVAL.colIdent = ColIdent{} } case 641: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] //line sql.y:3199 { - yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) + yyVAL.colIdent = yyDollar[2].colIdent } case 642: yyDollar = yyS[yypt-1 : yypt+1] @@ -7386,17 +7387,17 @@ yydefault: { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 644: + case 643: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3210 +//line sql.y:3207 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 645: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3216 +//line sql.y:3214 { - yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) + yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 646: yyDollar = yyS[yypt-1 : yypt+1] @@ -7404,41 +7405,41 @@ yydefault: { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 648: + case 647: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3224 + { + yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) + } + case 649: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3227 +//line sql.y:3231 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 936: + case 937: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3540 +//line sql.y:3544 { if incNesting(yylex) { yylex.Error("max nesting level reached") return 1 } } - case 937: + case 938: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3549 +//line sql.y:3553 { decNesting(yylex) } - case 938: - yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3554 - { - skipToEnd(yylex) - } case 939: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3559 +//line sql.y:3558 { skipToEnd(yylex) } case 940: - yyDollar = yyS[yypt-1 : yypt+1] + yyDollar = yyS[yypt-0 : yypt+1] //line sql.y:3563 { skipToEnd(yylex) @@ -7449,6 +7450,12 @@ yydefault: { skipToEnd(yylex) } + case 942: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:3571 + { + skipToEnd(yylex) + } } goto yystack /* stack new state and value */ } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 85a31e4818e..f26b31d6cad 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -2663,6 +2663,10 @@ function_call_conflict: { $$ = &FuncExpr{Name: NewColIdent("database"), Exprs: $3} } +| SCHEMA openb select_expression_list_opt closeb + { + $$ = &FuncExpr{Name: NewColIdent("schema"), Exprs: $3} + } | MOD openb select_expression_list closeb { $$ = &FuncExpr{Name: NewColIdent("mod"), Exprs: $3} From cbef5f2d5547ca85c519ca8bdfac51d27a815ac8 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 17 Jan 2020 10:46:53 -0800 Subject: [PATCH 274/825] Refactored system function rewriting Signed-off-by: Andres Taylor --- go/vt/sqlparser/expression_rewriting.go | 58 +++++++++++++++++-------- go/vt/vtgate/engine/primitive.go | 8 ++-- go/vt/vtgate/executor.go | 9 ++-- go/vt/vtgate/planbuilder/builder.go | 11 +++-- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/go/vt/sqlparser/expression_rewriting.go b/go/vt/sqlparser/expression_rewriting.go index 5d52312b337..d33491422a5 100644 --- a/go/vt/sqlparser/expression_rewriting.go +++ b/go/vt/sqlparser/expression_rewriting.go @@ -29,17 +29,29 @@ func PrepareAST(in Statement, bindVars map[string]*querypb.BindVariable, prefix return RewriteAST(in) } +// BindVarNeeds represents the bind vars that need to be provided as the result of expression rewriting. +type BindVarNeeds struct { + NeedLastInsertID bool + NeedDatabase bool +} + // RewriteAST rewrites the whole AST, replacing function calls and adding column aliases to queries func RewriteAST(in Statement) (*RewriteASTResult, error) { - er := new(expressionRewriter) + er := newExpressionRewriter() er.shouldRewriteDatabaseFunc = shouldRewriteDatabaseFunc(in) Rewrite(in, er.goingDown, nil) - return &RewriteASTResult{ - AST: in, - NeedLastInsertID: er.lastInsertID, - NeedDatabase: er.database, - }, nil + r := &RewriteASTResult{ + AST: in, + } + if _, ok := er.bindVars[LastInsertIDName]; ok { + r.NeedLastInsertID = true + } + if _, ok := er.bindVars[DBVarName]; ok { + r.NeedDatabase = true + } + + return r, nil } func shouldRewriteDatabaseFunc(in Statement) bool { @@ -63,20 +75,24 @@ func shouldRewriteDatabaseFunc(in Statement) bool { // RewriteASTResult contains the rewritten ast and meta information about it type RewriteASTResult struct { - AST Statement - NeedLastInsertID bool - NeedDatabase bool + BindVarNeeds + AST Statement // The rewritten AST } type expressionRewriter struct { - lastInsertID, database bool + bindVars map[string]struct{} shouldRewriteDatabaseFunc bool err error } +func newExpressionRewriter() *expressionRewriter { + return &expressionRewriter{bindVars: make(map[string]struct{})} +} + const ( //LastInsertIDName is a reserved bind var name for last_insert_id() LastInsertIDName = "__lastInsertId" + //DBVarName is a reserved bind var name for database() DBVarName = "__vtdbname" ) @@ -87,7 +103,7 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { if node.As.IsEmpty() { buf := NewTrackedBuffer(nil) node.Expr.Format(buf) - inner := new(expressionRewriter) + inner := newExpressionRewriter() inner.shouldRewriteDatabaseFunc = er.shouldRewriteDatabaseFunc tmp := Rewrite(node.Expr, inner.goingDown, nil) newExpr, ok := tmp.(Expr) @@ -96,11 +112,12 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { return false } node.Expr = newExpr - er.database = er.database || inner.database - er.lastInsertID = er.lastInsertID || inner.lastInsertID if inner.didAnythingChange() { node.As = NewColIdent(buf.String()) } + for k := range inner.bindVars { + er.needBindVarFor(k) + } return false } @@ -111,24 +128,31 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { er.err = vterrors.New(vtrpc.Code_UNIMPLEMENTED, "Argument to LAST_INSERT_ID() not supported") } else { cursor.Replace(bindVarExpression(LastInsertIDName)) - er.lastInsertID = true + er.needBindVarFor(LastInsertIDName) } case er.shouldRewriteDatabaseFunc && (node.Name.EqualString("database") || - node.Name.EqualString("schema")): + node.Name.EqualString("schema")): if len(node.Exprs) > 0 { er.err = vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "Syntax error. %s() takes no arguments", node.Name.String()) } else { cursor.Replace(bindVarExpression(DBVarName)) - er.database = true + er.needBindVarFor(DBVarName) } } } return true } +// instead of creating new objects, we'll reuse this one +var token = struct{}{} + +func (er *expressionRewriter) needBindVarFor(name string) { + er.bindVars[name] = token +} + func (er *expressionRewriter) didAnythingChange() bool { - return er.database || er.lastInsertID + return len(er.bindVars) > 0 } func bindVarExpression(name string) *SQLVal { diff --git a/go/vt/vtgate/engine/primitive.go b/go/vt/vtgate/engine/primitive.go index a62bac5942f..282c8656892 100644 --- a/go/vt/vtgate/engine/primitive.go +++ b/go/vt/vtgate/engine/primitive.go @@ -20,6 +20,8 @@ import ( "sync" "time" + "vitess.io/vitess/go/vt/sqlparser" + "golang.org/x/net/context" "vitess.io/vitess/go/sqltypes" @@ -94,10 +96,8 @@ type Plan struct { Rows uint64 `json:",omitempty"` // Total number of errors Errors uint64 `json:",omitempty"` - // NeedsLastInsertID signals whether this plan will need to be provided with last_insert_id - NeedsLastInsertID bool `json:"-"` // don't include in the json representation - // NeedsDatabaseName signals whether this plan will need to be provided with the database name - NeedsDatabaseName bool `json:"-"` // don't include in the json representation + + sqlparser.BindVarNeeds } // AddStats updates the plan execution statistics diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index b28c5a909db..e68ed782f4b 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -328,10 +328,11 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql return nil, err } - if plan.NeedsLastInsertID { + bindVarNeeds := plan.BindVarNeeds + if bindVarNeeds.NeedLastInsertID { bindVars[sqlparser.LastInsertIDName] = sqltypes.Uint64BindVariable(safeSession.GetLastInsertId()) } - if plan.NeedsDatabaseName { + if bindVarNeeds.NeedDatabase { keyspace, _, _, _ := e.ParseDestinationTarget(safeSession.TargetString) if keyspace == "" { bindVars[sqlparser.DBVarName] = sqltypes.NullBindVariable @@ -1415,7 +1416,7 @@ func (e *Executor) getPlan(vcursor *vcursorImpl, sql string, comments sqlparser. return nil, err } if !e.normalize { - plan, err := planbuilder.BuildFromStmt(sql, stmt, vcursor, false, false) + plan, err := planbuilder.BuildFromStmt(sql, stmt, vcursor, sqlparser.BindVarNeeds{}) if err != nil { return nil, err } @@ -1442,7 +1443,7 @@ func (e *Executor) getPlan(vcursor *vcursorImpl, sql string, comments sqlparser. if result, ok := e.plans.Get(planKey); ok { return result.(*engine.Plan), nil } - plan, err := planbuilder.BuildFromStmt(normalized, rewrittenStatement, vcursor, result.NeedLastInsertID, result.NeedDatabase) + plan, err := planbuilder.BuildFromStmt(normalized, rewrittenStatement, vcursor, result.BindVarNeeds) if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/builder.go b/go/vt/vtgate/planbuilder/builder.go index dc912cd72e6..bd016333523 100644 --- a/go/vt/vtgate/planbuilder/builder.go +++ b/go/vt/vtgate/planbuilder/builder.go @@ -263,14 +263,14 @@ func Build(query string, vschema ContextVSchema) (*engine.Plan, error) { return nil, err } - return BuildFromStmt(query, result.AST, vschema, result.NeedLastInsertID, result.NeedDatabase) + return BuildFromStmt(query, result.AST, vschema, result.BindVarNeeds) } // BuildFromStmt builds a plan based on the AST provided. // TODO(sougou): The query input is trusted as the source // of the AST. Maybe this function just returns instructions // and engine.Plan can be built by the caller. -func BuildFromStmt(query string, stmt sqlparser.Statement, vschema ContextVSchema, needsLastInsertID, needsDBName bool) (*engine.Plan, error) { +func BuildFromStmt(query string, stmt sqlparser.Statement, vschema ContextVSchema, bindVarNeeds sqlparser.BindVarNeeds) (*engine.Plan, error) { var err error var instruction engine.Primitive switch stmt := stmt.(type) { @@ -309,10 +309,9 @@ func BuildFromStmt(query string, stmt sqlparser.Statement, vschema ContextVSchem return nil, err } plan := &engine.Plan{ - Original: query, - Instructions: instruction, - NeedsLastInsertID: needsLastInsertID, - NeedsDatabaseName: needsDBName, + Original: query, + Instructions: instruction, } + plan.BindVarNeeds = bindVarNeeds return plan, nil } From 1241ef68e7a5fbcd3918962f70f80f597c2ca0a8 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 17 Jan 2020 11:51:46 -0800 Subject: [PATCH 275/825] Add support for found_rows() Signed-off-by: Andres Taylor --- go/vt/proto/vtgate/vtgate.pb.go | 4 +- go/vt/sqlparser/expression_rewriting.go | 62 ++++++++++++++++++++ go/vt/sqlparser/expression_rewriting_test.go | 24 +++++--- go/vt/vtgate/executor.go | 6 ++ go/vt/vtgate/executor_select_test.go | 21 +++++++ go/vt/vtgate/executor_test.go | 6 +- go/vt/vtgate/vtgate_test.go | 1 + 7 files changed, 111 insertions(+), 13 deletions(-) diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index 74af5414ddd..61ba91e777b 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -137,7 +137,9 @@ type Session struct { // post_sessions contains sessions that have to be committed last. PostSessions []*Session_ShardSession `protobuf:"bytes,10,rep,name=post_sessions,json=postSessions,proto3" json:"post_sessions,omitempty"` // last_insert_id keeps track of the last seen insert_id for this session - LastInsertId uint64 `protobuf:"varint,11,opt,name=last_insert_id,json=lastInsertId,proto3" json:"last_insert_id,omitempty"` + LastInsertId uint64 `protobuf:"varint,11,opt,name=last_insert_id,json=lastInsertId,proto3" json:"last_insert_id,omitempty"` + // found_rows keeps track of how many rows the last query returned + FoundRows uint64 `protobuf:"varint,11,opt,name=found_rows,json=foundRows,proto3" json:"found_rows,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` diff --git a/go/vt/sqlparser/expression_rewriting.go b/go/vt/sqlparser/expression_rewriting.go index d33491422a5..d2936973433 100644 --- a/go/vt/sqlparser/expression_rewriting.go +++ b/go/vt/sqlparser/expression_rewriting.go @@ -33,6 +33,7 @@ func PrepareAST(in Statement, bindVars map[string]*querypb.BindVariable, prefix type BindVarNeeds struct { NeedLastInsertID bool NeedDatabase bool + NeedFoundRows bool } // RewriteAST rewrites the whole AST, replacing function calls and adding column aliases to queries @@ -50,6 +51,9 @@ func RewriteAST(in Statement) (*RewriteASTResult, error) { if _, ok := er.bindVars[DBVarName]; ok { r.NeedDatabase = true } + if _, ok := er.bindVars[FoundRowsName]; ok { + r.NeedFoundRows = true + } return r, nil } @@ -95,8 +99,58 @@ const ( //DBVarName is a reserved bind var name for database() DBVarName = "__vtdbname" + + //FoundRowsName is a reserved bind var name for found_rows() + FoundRowsName = "__vtfrows" ) +//<<<<<<< HEAD +//======= +//type funcRewrite struct { +// checkValid func(f *FuncExpr) error +// bindVarName string +//} +// +//var lastInsertID = funcRewrite{ +// checkValid: func(f *FuncExpr) error { +// if len(f.Exprs) > 0 { +// return vterrors.New(vtrpc.Code_UNIMPLEMENTED, "Argument to LAST_INSERT_ID() not supported") +// } +// return nil +// }, +// bindVarName: LastInsertIDName, +//} +// +//var dbName = funcRewrite{ +// checkValid: func(f *FuncExpr) error { +// if len(f.Exprs) > 0 { +// return vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Argument to DATABASE() not supported") +// } +// return nil +// }, +// bindVarName: DBVarName, +//} +//var foundRows = funcRewrite{ +// checkValid: func(f *FuncExpr) error { +// if len(f.Exprs) > 0 { +// return vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Argument to FOUND_ROWS() not supported") +// } +// return nil +// }, +// bindVarName: FoundRowsName, +//} +// +//var functions = map[string]*funcRewrite{ +// "last_insert_id": &lastInsertID, +// "database": &dbName, +// "schema": &dbName, +// "found_rows": &foundRows, +//} +// +//// instead of creating new objects, we'll reuse this one +//var token = struct{}{} +// +//>>>>>>> Add support for found_rows() func (er *expressionRewriter) goingDown(cursor *Cursor) bool { switch node := cursor.Node().(type) { case *AliasedExpr: @@ -139,7 +193,15 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { cursor.Replace(bindVarExpression(DBVarName)) er.needBindVarFor(DBVarName) } + case node.Name.EqualString("found_rows"): + if len(node.Exprs) > 0 { + er.err = vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Arguments to FOUND_ROWS() not supported") + } else { + cursor.Replace(bindVarExpression(FoundRowsName)) + er.needBindVarFor(FoundRowsName) + } } + } return true } diff --git a/go/vt/sqlparser/expression_rewriting_test.go b/go/vt/sqlparser/expression_rewriting_test.go index 2951db2dc5c..b7dd524cbbb 100644 --- a/go/vt/sqlparser/expression_rewriting_test.go +++ b/go/vt/sqlparser/expression_rewriting_test.go @@ -23,8 +23,8 @@ import ( ) type myTestCase struct { - in, expected string - liid, db bool + in, expected string + liid, db, foundRows bool } func TestRewrites(in *testing.T) { @@ -32,7 +32,7 @@ func TestRewrites(in *testing.T) { { in: "SELECT 42", expected: "SELECT 42", - db: false, liid: false, + db: false, liid: false, foundRows: false, }, { in: "SELECT last_insert_id()", @@ -42,7 +42,7 @@ func TestRewrites(in *testing.T) { { in: "SELECT database()", expected: "SELECT :__vtdbname as `database()`", - db: true, liid: false, + db: true, liid: false, foundRows: false, }, { in: "SELECT database() from test", @@ -52,12 +52,12 @@ func TestRewrites(in *testing.T) { { in: "SELECT last_insert_id() as test", expected: "SELECT :__lastInsertId as test", - db: false, liid: true, + db: false, liid: true, foundRows: false, }, { in: "SELECT last_insert_id() + database()", expected: "SELECT :__lastInsertId + :__vtdbname as `last_insert_id() + database()`", - db: true, liid: true, + db: true, liid: true, foundRows: false, }, { in: "select (select database()) from test", @@ -72,7 +72,7 @@ func TestRewrites(in *testing.T) { { in: "select (select database() from dual) from dual", expected: "select (select :__vtdbname as `database()` from dual) as `(select database() from dual)` from dual", - db: true, liid: false, + db: true, liid: false, foundRows: false, }, { in: "select id from user where database()", @@ -82,12 +82,17 @@ func TestRewrites(in *testing.T) { { in: "select table_name from information_schema.tables where table_schema = database()", expected: "select table_name from information_schema.tables where table_schema = database()", - db: false, liid: false, + db: false, liid: false, foundRows: false, }, { in: "select schema()", expected: "select :__vtdbname as 'schema()'", - db: true, liid: false, + db: true, liid: false, foundRows: false, + }, + { + in: "select found_rows()", + expected: "select :__vtfrows as 'found_rows()'", + db: false, liid: false, foundRows: true, }, } @@ -106,6 +111,7 @@ func TestRewrites(in *testing.T) { require.Equal(t, s, String(result.AST)) require.Equal(t, tc.liid, result.NeedLastInsertID, "should need last insert id") require.Equal(t, tc.db, result.NeedDatabase, "should need database name") + require.Equal(t, tc.foundRows, result.NeedFoundRows, "should need found rows") }) } } diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index e68ed782f4b..3084c036416 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -145,6 +145,9 @@ func (e *Executor) Execute(ctx context.Context, method string, safeSession *Safe logStats := NewLogStats(ctx, method, sql, bindVars) result, err = e.execute(ctx, safeSession, sql, bindVars, logStats) + if err == nil { + safeSession.FoundRows = result.RowsAffected + } logStats.Error = err if result != nil && len(result.Rows) > *warnMemoryRows { warnings.Add("ResultsExceeded", 1) @@ -340,6 +343,9 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql bindVars[sqlparser.DBVarName] = sqltypes.StringBindVariable(keyspace) } } + if bindVarNeeds.NeedFoundRows { + bindVars[sqlparser.FoundRowsName] = sqltypes.Uint64BindVariable(safeSession.FoundRows) + } qr, err := plan.Instructions.Execute(vcursor, bindVars, true) logStats.ExecuteTime = time.Since(execStart) diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index 14363bb6449..3cc40311a57 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -235,6 +235,27 @@ func TestSelectLastInsertId(t *testing.T) { assert.Equal(t, wantQueries, sbc1.Queries) } +func TestFoundRows(t *testing.T) { + executor, sbc1, _, _ := createExecutorEnv() + executor.normalize = true + logChan := QueryLogger.Subscribe("Test") + defer QueryLogger.Unsubscribe(logChan) + + // run this extra query so we can assert on the number of rows found + _, err := executorExec(executor, "select 42", map[string]*querypb.BindVariable{}) + require.NoError(t, err) + + sql := "select found_rows()" + _, err = executorExec(executor, sql, map[string]*querypb.BindVariable{}) + require.NoError(t, err) + expected := &querypb.BoundQuery{ + Sql: "select :__vtfrows as `found_rows()` from dual", + BindVariables: map[string]*querypb.BindVariable{"__vtfrows": sqltypes.Uint64BindVariable(1)}, + } + + assert.Equal(t, expected, sbc1.Queries[1]) +} + func TestSelectLastInsertIdInUnion(t *testing.T) { executor, sbc1, _, _ := createExecutorEnv() executor.normalize = true diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index ec148f45594..d23b00131b2 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -609,7 +609,7 @@ func TestExecutorAutocommit(t *testing.T) { if err != nil { t.Fatal(err) } - wantSession := &vtgatepb.Session{TargetString: "@master", InTransaction: true} + wantSession := &vtgatepb.Session{TargetString: "@master", InTransaction: true, FoundRows: 1} testSession := *session.Session testSession.ShardSessions = nil if !proto.Equal(&testSession, wantSession) { @@ -643,7 +643,7 @@ func TestExecutorAutocommit(t *testing.T) { if err != nil { t.Fatal(err) } - wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@master"} + wantSession = &vtgatepb.Session{Autocommit: true, TargetString: "@master", FoundRows: 1} if !proto.Equal(session.Session, wantSession) { t.Errorf("autocommit=1: %v, want %v", session.Session, wantSession) } @@ -672,7 +672,7 @@ func TestExecutorAutocommit(t *testing.T) { if err != nil { t.Fatal(err) } - wantSession = &vtgatepb.Session{InTransaction: true, Autocommit: true, TargetString: "@master"} + wantSession = &vtgatepb.Session{InTransaction: true, Autocommit: true, TargetString: "@master", FoundRows: 1} testSession = *session.Session testSession.ShardSessions = nil if !proto.Equal(&testSession, wantSession) { diff --git a/go/vt/vtgate/vtgate_test.go b/go/vt/vtgate/vtgate_test.go index 560aca613b0..2705dc42008 100644 --- a/go/vt/vtgate/vtgate_test.go +++ b/go/vt/vtgate/vtgate_test.go @@ -254,6 +254,7 @@ func TestVTGateExecute(t *testing.T) { }, TransactionId: 1, }}, + FoundRows: 1, } if !proto.Equal(wantSession, session) { t.Errorf("want \n%+v, got \n%+v", wantSession, session) From 12cab68030b29a6e7916a328c18527ed8288b48b Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Wed, 11 Mar 2020 19:00:02 +0530 Subject: [PATCH 276/825] addressed review comments Signed-off-by: Harshit Gangal --- go/vt/sqlparser/expression_rewriting.go | 47 ------------------------- go/vt/vtgate/engine/primitive.go | 4 +-- go/vt/vtgate/executor.go | 8 ++--- 3 files changed, 6 insertions(+), 53 deletions(-) diff --git a/go/vt/sqlparser/expression_rewriting.go b/go/vt/sqlparser/expression_rewriting.go index d2936973433..667c3eb6667 100644 --- a/go/vt/sqlparser/expression_rewriting.go +++ b/go/vt/sqlparser/expression_rewriting.go @@ -104,53 +104,6 @@ const ( FoundRowsName = "__vtfrows" ) -//<<<<<<< HEAD -//======= -//type funcRewrite struct { -// checkValid func(f *FuncExpr) error -// bindVarName string -//} -// -//var lastInsertID = funcRewrite{ -// checkValid: func(f *FuncExpr) error { -// if len(f.Exprs) > 0 { -// return vterrors.New(vtrpc.Code_UNIMPLEMENTED, "Argument to LAST_INSERT_ID() not supported") -// } -// return nil -// }, -// bindVarName: LastInsertIDName, -//} -// -//var dbName = funcRewrite{ -// checkValid: func(f *FuncExpr) error { -// if len(f.Exprs) > 0 { -// return vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Argument to DATABASE() not supported") -// } -// return nil -// }, -// bindVarName: DBVarName, -//} -//var foundRows = funcRewrite{ -// checkValid: func(f *FuncExpr) error { -// if len(f.Exprs) > 0 { -// return vterrors.New(vtrpc.Code_INVALID_ARGUMENT, "Argument to FOUND_ROWS() not supported") -// } -// return nil -// }, -// bindVarName: FoundRowsName, -//} -// -//var functions = map[string]*funcRewrite{ -// "last_insert_id": &lastInsertID, -// "database": &dbName, -// "schema": &dbName, -// "found_rows": &foundRows, -//} -// -//// instead of creating new objects, we'll reuse this one -//var token = struct{}{} -// -//>>>>>>> Add support for found_rows() func (er *expressionRewriter) goingDown(cursor *Cursor) bool { switch node := cursor.Node().(type) { case *AliasedExpr: diff --git a/go/vt/vtgate/engine/primitive.go b/go/vt/vtgate/engine/primitive.go index 282c8656892..34a8d96a226 100644 --- a/go/vt/vtgate/engine/primitive.go +++ b/go/vt/vtgate/engine/primitive.go @@ -96,8 +96,8 @@ type Plan struct { Rows uint64 `json:",omitempty"` // Total number of errors Errors uint64 `json:",omitempty"` - - sqlparser.BindVarNeeds + // Stores BindVars needed to be provided as part of expression rewriting + sqlparser.BindVarNeeds `json:"-"` } // AddStats updates the plan execution statistics diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 3084c036416..108482b643f 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -1414,8 +1414,8 @@ func (e *Executor) getPlan(vcursor *vcursorImpl, sql string, comments sqlparser. } keyspace := vcursor.keyspace planKey := keyspace + vindexes.TabletTypeSuffix[vcursor.tabletType] + ":" + sql - if result, ok := e.plans.Get(planKey); ok { - return result.(*engine.Plan), nil + if plan, ok := e.plans.Get(planKey); ok { + return plan.(*engine.Plan), nil } stmt, err := sqlparser.Parse(sql) if err != nil { @@ -1446,8 +1446,8 @@ func (e *Executor) getPlan(vcursor *vcursorImpl, sql string, comments sqlparser. } planKey = keyspace + vindexes.TabletTypeSuffix[vcursor.tabletType] + ":" + normalized - if result, ok := e.plans.Get(planKey); ok { - return result.(*engine.Plan), nil + if plan, ok := e.plans.Get(planKey); ok { + return plan.(*engine.Plan), nil } plan, err := planbuilder.BuildFromStmt(normalized, rewrittenStatement, vcursor, result.BindVarNeeds) if err != nil { From c9e81557b6877539cc7920b85abd7697f08cd93e Mon Sep 17 00:00:00 2001 From: deepthi Date: Wed, 11 Mar 2020 16:19:59 -0700 Subject: [PATCH 277/825] update go.mod Signed-off-by: deepthi --- examples/are-you-alive/go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/are-you-alive/go.mod b/examples/are-you-alive/go.mod index 5f3d04ddccd..9bb69e1307b 100644 --- a/examples/are-you-alive/go.mod +++ b/examples/are-you-alive/go.mod @@ -4,8 +4,6 @@ go 1.12 require ( github.com/go-sql-driver/mysql v1.5.0 - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/prometheus/client_golang v1.4.1 github.com/sirupsen/logrus v1.4.2 ) From 866e42aa3d4920ecbd3a8789756570e58fb0a49d Mon Sep 17 00:00:00 2001 From: deepthi Date: Wed, 11 Mar 2020 16:20:59 -0700 Subject: [PATCH 278/825] rename testQueries function, change test to expect failure until issue #5911 is fixed Signed-off-by: deepthi --- go/test/endtoend/cellalias/cell_alias_test.go | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go index 71da8014709..c87380d86b4 100644 --- a/go/test/endtoend/cellalias/cell_alias_test.go +++ b/go/test/endtoend/cellalias/cell_alias_test.go @@ -277,9 +277,9 @@ func TestAlias(t *testing.T) { waitTillAllTabletsAreHealthyInVtgate(t, *vtgateInstance, shard1.Name, shard2.Name) - testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) - testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, false) - testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, false) + testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false) + testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, false) + testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, false) // now, delete the alias, so that if we run above assertions again, it will fail for replica,rdonly target type err = localCluster.VtctlclientProcess.ExecuteCommand("DeleteCellsAlias", @@ -294,9 +294,9 @@ func TestAlias(t *testing.T) { require.Nil(t, err) // since replica and rdonly tablets of all shards in cell2, the last 2 assertion is expected to fail - testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) - testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, true) - testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) + testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false) + testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, true) + testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) } @@ -329,23 +329,20 @@ func TestAddAliasWhileVtgateUp(t *testing.T) { waitTillAllTabletsAreHealthyInVtgate(t, *vtgateInstance, shard1.Name, shard2.Name) // since replica and rdonly tablets of all shards in cell2, the last 2 assertion is expected to fail - testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) - testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, true) - testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) + testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false) + testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, true) + testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) // Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias err = localCluster.VtctlclientProcess.ExecuteCommand("AddCellsAlias", "-cells", allCells, "region_east_coast") require.Nil(t, err) - err = localCluster.VtctlclientProcess.ExecuteCommand("UpdateCellsAlias", - "-cells", allCells, - "region_east_coast") - require.Nil(t, err) - testQueriesInDifferentTabletType(t, "master", vtgateInstance.GrpcPort, false) - testQueriesInDifferentTabletType(t, "replica", vtgateInstance.GrpcPort, false) - testQueriesInDifferentTabletType(t, "rdonly", vtgateInstance.GrpcPort, false) + testQueriesOnTabletType(t, "master", vtgateInstance.GrpcPort, false) + // TODO(deepthi) change the following to shouldFail:false when fixing https://github.com/vitessio/vitess/issues/5911 + testQueriesOnTabletType(t, "replica", vtgateInstance.GrpcPort, true) + testQueriesOnTabletType(t, "rdonly", vtgateInstance.GrpcPort, true) } @@ -360,7 +357,7 @@ func waitTillAllTabletsAreHealthyInVtgate(t *testing.T, vtgateInstance cluster.V } } -func testQueriesInDifferentTabletType(t *testing.T, tabletType string, vtgateGrpcPort int, shouldFail bool) { +func testQueriesOnTabletType(t *testing.T, tabletType string, vtgateGrpcPort int, shouldFail bool) { output, err := localCluster.VtctlProcess.ExecuteCommandWithOutput("VtGateExecute", "-json", "-server", fmt.Sprintf("%s:%d", localCluster.Hostname, vtgateGrpcPort), "-target", "@"+tabletType, From 4e5ae1d79334c711823f7349a9f6e08785059bda Mon Sep 17 00:00:00 2001 From: deepthi Date: Wed, 11 Mar 2020 18:06:01 -0700 Subject: [PATCH 279/825] endtoend: log process when we fail to start mysql Signed-off-by: deepthi --- go/test/endtoend/cluster/cluster_process.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index ac6c113272d..bda184260f5 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -282,7 +282,7 @@ func (cluster *LocalProcessCluster) StartKeyspace(keyspace Keyspace, shardNames // wait till all mysqlctl is instantiated for _, proc := range mysqlctlProcessList { if err = proc.Wait(); err != nil { - log.Errorf("Unable to start mysql , error %v", err.Error()) + log.Errorf("Unable to start mysql process %v, error %v", proc, err) return err } } From 9c3dc87726a2f18624bdca182146a9b479c36d8e Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 12 Mar 2020 12:54:42 +0100 Subject: [PATCH 280/825] Format function names correctly (#5918) Signed-off-by: Andres Taylor --- go/vt/sqlparser/ast.go | 12 +++++++++--- go/vt/sqlparser/ast_funcs.go | 29 ++++++++++++++++++++--------- go/vt/sqlparser/parse_test.go | 6 ++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 0777d9c3a4f..f99edf0495d 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -1544,9 +1544,15 @@ func (node *FuncExpr) Format(buf *TrackedBuffer) { buf.Myprintf("%v.", node.Qualifier) } // Function names should not be back-quoted even - // if they match a reserved word. So, print the - // name as is. - buf.Myprintf("%s(%s%v)", node.Name.String(), distinct, node.Exprs) + // if they match a reserved word, only if they contain illegal characters + funcName := node.Name.String() + + if containEscapableChars(funcName) { + writeEscapedString(buf, funcName) + } else { + buf.WriteString(funcName) + } + buf.Myprintf("(%s%v)", distinct, node.Exprs) } // Format formats the node diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go index a746301fdce..e1b6c07bec0 100644 --- a/go/vt/sqlparser/ast_funcs.go +++ b/go/vt/sqlparser/ast_funcs.go @@ -631,26 +631,37 @@ func (node *TableIdent) UnmarshalJSON(b []byte) error { return nil } -func formatID(buf *TrackedBuffer, original, lowered string) { +func containEscapableChars(s string) bool { isDbSystemVariable := false - if len(original) > 1 && original[:2] == "@@" { + if len(s) > 1 && s[:2] == "@@" { isDbSystemVariable = true } - for i, c := range original { + for i, c := range s { if !isLetter(uint16(c)) && (!isDbSystemVariable || !isCarat(uint16(c))) { if i == 0 || !isDigit(uint16(c)) { - goto mustEscape + return true } } } - if _, ok := keywords[lowered]; ok { - goto mustEscape + + return false +} + +func isKeyword(s string) bool { + _, isKeyword := keywords[s] + return isKeyword +} + +func formatID(buf *TrackedBuffer, original, lowered string) { + if containEscapableChars(original) || isKeyword(lowered) { + writeEscapedString(buf, original) + } else { + buf.Myprintf("%s", original) } - buf.Myprintf("%s", original) - return +} -mustEscape: +func writeEscapedString(buf *TrackedBuffer, original string) { buf.WriteByte('`') for _, c := range original { buf.WriteRune(c) diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index c986277f3d2..2ba6a02cf05 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1523,6 +1523,12 @@ var ( }, { input: "select distinctrow a.* from (select (1) from dual union all select 1 from dual) a", output: "select distinct a.* from (select (1) from dual union all select 1 from dual) as a", + }, { + input: "select `weird function name`() from t", + }, { + input: "select status() from t", // should not escape function names that are keywords + }, { + input: "select * from `weird table name`", }} ) From e1676cb3ec3e05cabf2d9f3109352fc0574059f2 Mon Sep 17 00:00:00 2001 From: Rafael Chacon Date: Thu, 12 Mar 2020 11:47:12 -0700 Subject: [PATCH 281/825] Fixes bug caught by test, update test to reflect new behavior Signed-off-by: Rafael Chacon --- .../vtexplain/testdata/multi-output/target-output.txt | 2 +- go/vt/vtgate/autocommit_test.go | 10 +++++----- go/vt/vtgate/resolver.go | 2 +- go/vt/vtgate/resolver_test.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go/vt/vtexplain/testdata/multi-output/target-output.txt b/go/vt/vtexplain/testdata/multi-output/target-output.txt index e7ed228009e..10ce1dddf1e 100644 --- a/go/vt/vtexplain/testdata/multi-output/target-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/target-output.txt @@ -13,6 +13,6 @@ insert into user (id, name) values (2, 'bob') 1 ks_sharded/40-80: begin 1 ks_sharded/40-80: insert into user(id, name) values (2, 'bob')/* vtgate:: filtered_replication_unfriendly */ -2 ks_sharded/40-80: commit +1 ks_sharded/40-80: commit ---------------------------------------------------------------------- diff --git a/go/vt/vtgate/autocommit_test.go b/go/vt/vtgate/autocommit_test.go index ab066c432bb..1ca4750cfb9 100644 --- a/go/vt/vtgate/autocommit_test.go +++ b/go/vt/vtgate/autocommit_test.go @@ -393,7 +393,7 @@ func TestAutocommitTransactionStarted(t *testing.T) { testCommitCount(t, "sbc1", sbc1, 0) } -// TestAutocommitDirectTarget: no instant-commit. +// TestAutocommitDirectTarget: instant-commit. func TestAutocommitDirectTarget(t *testing.T) { executor, _, _, sbclookup := createExecutorEnv() @@ -407,12 +407,12 @@ func TestAutocommitDirectTarget(t *testing.T) { _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{}) require.NoError(t, err) - testQueries(t, "sbclookup", sbclookup, []*querypb.BoundQuery{{ + testBatchQuery(t, "sbclookup", sbclookup, &querypb.BoundQuery{ Sql: sql + "/* vtgate:: filtered_replication_unfriendly */", BindVariables: map[string]*querypb.BindVariable{}, - }}) - testAsTransactionCount(t, "sbclookup", sbclookup, 0) - testCommitCount(t, "sbclookup", sbclookup, 1) + }) + testAsTransactionCount(t, "sbclookup", sbclookup, 1) + testCommitCount(t, "sbclookup", sbclookup, 0) } // TestAutocommitDirectRangeTarget: no instant-commit. diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go index ed0116f0e85..c3c1cf42299 100644 --- a/go/vt/vtgate/resolver.go +++ b/go/vt/vtgate/resolver.go @@ -92,7 +92,7 @@ func (res *Resolver) Execute( logStats.ShardQueries = uint32(len(rss)) } - autocommit := len(rss) == 1 && session.AutocommitApproval() + autocommit := len(rss) == 1 && canAutocommit && session.AutocommitApproval() for { qr, err := res.scatterConn.Execute( diff --git a/go/vt/vtgate/resolver_test.go b/go/vt/vtgate/resolver_test.go index d6a460deed4..71a43cbbc0a 100644 --- a/go/vt/vtgate/resolver_test.go +++ b/go/vt/vtgate/resolver_test.go @@ -48,7 +48,7 @@ func TestResolverExecuteKeyspaceIds(t *testing.T) { "TestResolverExecuteKeyspaceIds", topodatapb.TabletType_MASTER, key.DestinationKeyspaceIDs([][]byte{{0x10}, {0x25}}), - nil, + NewSafeSession(&vtgatepb.Session{}), false, nil, nil, @@ -65,7 +65,7 @@ func TestResolverExecuteKeyRanges(t *testing.T) { "TestResolverExecuteKeyRanges", topodatapb.TabletType_MASTER, key.DestinationKeyRanges([]*topodatapb.KeyRange{{Start: []byte{0x10}, End: []byte{0x25}}}), - nil, + NewSafeSession(&vtgatepb.Session{}), false, nil, nil, From 56ee13b15947c2a7b46791dacead24133f782fe3 Mon Sep 17 00:00:00 2001 From: Deepthi Sigireddi Date: Fri, 13 Mar 2020 09:40:55 -0700 Subject: [PATCH 282/825] Log warning when vttablet StreamHealth RPC fails (#5917) Signed-off-by: deepthi --- go/vt/discovery/healthcheck.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/vt/discovery/healthcheck.go b/go/vt/discovery/healthcheck.go index e7abf1dbbde..b282b846d7b 100644 --- a/go/vt/discovery/healthcheck.go +++ b/go/vt/discovery/healthcheck.go @@ -621,6 +621,7 @@ func (hcc *healthCheckConn) stream(ctx context.Context, hc *HealthCheckImpl, cal } if err := hcc.conn.StreamHealth(ctx, callback); err != nil { + log.Warningf("tablet %v healthcheck stream error: %v", hcc.tabletStats.Tablet.Alias, err) hcc.setServingState(false, err.Error()) hcc.tabletStats.LastError = err // Send nil because we intend to close the connection. From 1feb97b35eba829c56c9cdbba54de721b36a2af9 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 13 Mar 2020 12:00:08 -0700 Subject: [PATCH 283/825] messager: address review comments Signed-off-by: Sugu Sougoumarane --- .../tabletserver/messager/message_manager.go | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager.go b/go/vt/vttablet/tabletserver/messager/message_manager.go index 97a9d39cc02..e3f6fe48972 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager.go @@ -196,7 +196,10 @@ type messageManager struct { // lastPollPosition must be ignored by the vstream. It consequently // also blocks vstream from updating the cache while the poller is // active. - streamMu sync.Mutex + streamMu sync.Mutex + // streamCancel is set when a vstream is running, and is reset + // to nil after a cancel. This allows for startVStream and stopVstream + // to be idempotent. streamCancel func() lastPollPosition *mysql.Position @@ -433,7 +436,10 @@ func (mm *messageManager) runSend() { defer mm.mu.Unlock() for { - // Release and acquire the lock to avoid starving other contenders. + // It's theoretically possible that this loop can keep going without + // a wait. If so, the lock will never be released for other functions + // like Close to take action. So, let's release and acquire the lock + // to avoid starving other contenders. mm.mu.Unlock() mm.mu.Lock() @@ -449,7 +455,7 @@ func (mm *messageManager) runSend() { // Do this as a separate goroutine. Otherwise, this could cause // the following deadlock: // 1. runSend obtains a lock - // 2. Poller gets trigerred, and waits for lock. + // 2. Poller gets triggered, and waits for lock. // 3. runSend calls this function, but the trigger will hang because // this function cannot return until poller returns. go mm.pollerTicks.Trigger() @@ -506,27 +512,26 @@ func (mm *messageManager) send(receiver *receiverWithStatus, qr *sqltypes.Result ids[i] = row[0].ToString() } - // This is the cleanup. defer func() { - func() { - mm.mu.Lock() - defer mm.mu.Unlock() + // Hold streamMu to prevent the ids from being discarded + // if poller is active. Otherwise, it could have read a + // snapshot of a row before the postponement and requeue + // the message. + mm.streamMu.Lock() + defer mm.streamMu.Unlock() + mm.cache.Discard(ids) + }() - receiver.busy = false - // Rescan if there were no previously available receivers - // because the current receiver became non-busy. - if mm.curReceiver == -1 { - mm.rescanReceivers(-1) - } - }() - - func() { - mm.streamMu.Lock() - defer mm.streamMu.Unlock() - // Discard messages from cache only at the end. This will - // prevent them from being requeued while they're being postponed. - mm.cache.Discard(ids) - }() + defer func() { + mm.mu.Lock() + defer mm.mu.Unlock() + + receiver.busy = false + // Rescan if there were no previously available receivers + // because the current receiver became non-busy. + if mm.curReceiver == -1 { + mm.rescanReceivers(-1) + } }() if err := receiver.receiver.Send(qr); err != nil { @@ -588,6 +593,13 @@ func (mm *messageManager) runVStream(ctx context.Context) { } } +// runOneVStream watches for any new rows or rows that have been modified. +// Whether it's an insert or an update, if the new value of the +// row indicates that the message is eligible to be sent, it's added to +// the cache. +// Deletes are ignored. +// If the poller updates lastPollPosition, then all GTIDs up to that +// point are deemed obsolete and are skipped. func (mm *messageManager) runOneVStream(ctx context.Context) error { var curPos string var fields []*querypb.Field @@ -638,6 +650,8 @@ func (mm *messageManager) runOneVStream(ctx context.Context) error { case binlogdatapb.VEventType_GTID: newPos = ev.Gtid case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER: + // Update curPos only when the GTID concludes, which is through one + // of the above events. curPos = newPos skipEvents, err = mustSkip() if err != nil { @@ -666,6 +680,8 @@ func (mm *messageManager) processRowEvent(fields []*querypb.Field, rowEvent *bin if err != nil { return err } + // timeNext will be zero for acked messages. + // So, they should not be sent. if mr.TimeNext == 0 || mr.TimeNext > now { continue } From 249846983f5e53c92b9a9264e1a7291c82ee5ee7 Mon Sep 17 00:00:00 2001 From: Rafael Chacon Date: Fri, 13 Mar 2020 13:44:54 -0700 Subject: [PATCH 284/825] Only use autocommit in the context of target shards. Signed-off-by: Rafael Chacon --- go/vt/vtgate/executor.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index a1d3f701a8e..6d137a38cbc 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -299,7 +299,7 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql logStats.PlanTime = execStart.Sub(logStats.StartTime) logStats.SQL = sql logStats.BindVariables = bindVars - result, err := e.destinationExec(ctx, safeSession, sql, bindVars, dest, destKeyspace, destTabletType, logStats) + result, err := e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession, false /* notInTransaction */, safeSession.Options, logStats, true /* canAutocommit */) logStats.ExecuteTime = time.Since(execStart) e.updateQueryCounts("ShardDirect", "", "", int64(logStats.ShardQueries)) return result, err @@ -364,7 +364,7 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql } func (e *Executor) destinationExec(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { - return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession, false /* notInTransaction */, safeSession.Options, logStats, true /* canAutocommit */) + return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession, false /* notInTransaction */, safeSession.Options, logStats, false /* canAutocommit */) } func (e *Executor) handleDDL(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { From fcf14732734ef5170fcb230cb1de2d375499dc4e Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Thu, 27 Dec 2018 21:24:53 -0700 Subject: [PATCH 285/825] Add Kubernetes topo implementation Signed-off-by: Carson Anderson --- Makefile | 22 ++ bootstrap.sh | 27 ++ examples/local/101_initial_cluster.sh | 4 +- examples/local/401_teardown.sh | 2 + examples/local/env.sh | 15 +- examples/local/scripts/k3s-down.sh | 30 +++ examples/local/scripts/k3s-up.sh | 49 ++++ examples/local/topo-k8s.sh | 21 ++ go.mod | 86 ++++++- go.sum | 211 +++++++++++++++- go/cmd/topo2topo/plugin_kubernetestopo.go | 23 ++ go/cmd/vtctld/plugin_kubernetestopo.go | 23 ++ go/cmd/vtgate/plugin_kubernetestopo.go | 23 ++ go/cmd/vttablet/plugin_kubernetestopo.go | 23 ++ go/cmd/vtworker/plugin_kubernetestopo.go | 23 ++ go/vt/topo/k8stopo/VitessTopoNodes-crd.yaml | 43 ++++ go/vt/topo/k8stopo/apis/topo/v1beta1/doc.go | 5 + .../k8stopo/apis/topo/v1beta1/register.go | 38 +++ go/vt/topo/k8stopo/apis/topo/v1beta1/types.go | 32 +++ .../topo/v1beta1/zz_generated.deepcopy.go | 101 ++++++++ go/vt/topo/k8stopo/boilerplate.go.txt | 16 ++ .../client/clientset/versioned/clientset.go | 97 ++++++++ .../k8stopo/client/clientset/versioned/doc.go | 20 ++ .../versioned/fake/clientset_generated.go | 82 ++++++ .../client/clientset/versioned/fake/doc.go | 20 ++ .../clientset/versioned/fake/register.go | 56 +++++ .../client/clientset/versioned/scheme/doc.go | 20 ++ .../clientset/versioned/scheme/register.go | 56 +++++ .../versioned/typed/topo/v1beta1/doc.go | 20 ++ .../versioned/typed/topo/v1beta1/fake/doc.go | 20 ++ .../topo/v1beta1/fake/fake_topo_client.go | 40 +++ .../topo/v1beta1/fake/fake_vitesstoponode.go | 128 ++++++++++ .../typed/topo/v1beta1/generated_expansion.go | 21 ++ .../typed/topo/v1beta1/topo_client.go | 89 +++++++ .../typed/topo/v1beta1/vitesstoponode.go | 174 +++++++++++++ .../informers/externalversions/factory.go | 180 ++++++++++++++ .../informers/externalversions/generic.go | 62 +++++ .../internalinterfaces/factory_interfaces.go | 40 +++ .../externalversions/topo/interface.go | 46 ++++ .../topo/v1beta1/interface.go | 45 ++++ .../topo/v1beta1/vitesstoponode.go | 89 +++++++ .../topo/v1beta1/expansion_generated.go | 27 ++ .../listers/topo/v1beta1/vitesstoponode.go | 94 +++++++ go/vt/topo/k8stopo/config.go | 17 ++ go/vt/topo/k8stopo/directory.go | 103 ++++++++ go/vt/topo/k8stopo/election.go | 123 +++++++++ go/vt/topo/k8stopo/error.go | 54 ++++ go/vt/topo/k8stopo/file.go | 223 +++++++++++++++++ go/vt/topo/k8stopo/lock.go | 112 +++++++++ go/vt/topo/k8stopo/server.go | 234 ++++++++++++++++++ go/vt/topo/k8stopo/server_test.go | 149 +++++++++++ go/vt/topo/k8stopo/version.go | 40 +++ go/vt/topo/k8stopo/watch.go | 121 +++++++++ go/vt/topo/server.go | 2 +- go/vt/vtctl/plugin_kubernetestopo.go | 23 ++ 55 files changed, 3426 insertions(+), 18 deletions(-) create mode 100755 examples/local/scripts/k3s-down.sh create mode 100755 examples/local/scripts/k3s-up.sh create mode 100644 examples/local/topo-k8s.sh create mode 100644 go/cmd/topo2topo/plugin_kubernetestopo.go create mode 100644 go/cmd/vtctld/plugin_kubernetestopo.go create mode 100644 go/cmd/vtgate/plugin_kubernetestopo.go create mode 100644 go/cmd/vttablet/plugin_kubernetestopo.go create mode 100644 go/cmd/vtworker/plugin_kubernetestopo.go create mode 100644 go/vt/topo/k8stopo/VitessTopoNodes-crd.yaml create mode 100644 go/vt/topo/k8stopo/apis/topo/v1beta1/doc.go create mode 100644 go/vt/topo/k8stopo/apis/topo/v1beta1/register.go create mode 100644 go/vt/topo/k8stopo/apis/topo/v1beta1/types.go create mode 100644 go/vt/topo/k8stopo/apis/topo/v1beta1/zz_generated.deepcopy.go create mode 100644 go/vt/topo/k8stopo/boilerplate.go.txt create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/clientset.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/doc.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/fake/clientset_generated.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/fake/doc.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/fake/register.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/scheme/doc.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/scheme/register.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/doc.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/doc.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_topo_client.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_vitesstoponode.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/generated_expansion.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/topo_client.go create mode 100644 go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/vitesstoponode.go create mode 100644 go/vt/topo/k8stopo/client/informers/externalversions/factory.go create mode 100644 go/vt/topo/k8stopo/client/informers/externalversions/generic.go create mode 100644 go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces/factory_interfaces.go create mode 100644 go/vt/topo/k8stopo/client/informers/externalversions/topo/interface.go create mode 100644 go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/interface.go create mode 100644 go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/vitesstoponode.go create mode 100644 go/vt/topo/k8stopo/client/listers/topo/v1beta1/expansion_generated.go create mode 100644 go/vt/topo/k8stopo/client/listers/topo/v1beta1/vitesstoponode.go create mode 100644 go/vt/topo/k8stopo/config.go create mode 100644 go/vt/topo/k8stopo/directory.go create mode 100644 go/vt/topo/k8stopo/election.go create mode 100644 go/vt/topo/k8stopo/error.go create mode 100644 go/vt/topo/k8stopo/file.go create mode 100644 go/vt/topo/k8stopo/lock.go create mode 100644 go/vt/topo/k8stopo/server.go create mode 100644 go/vt/topo/k8stopo/server_test.go create mode 100644 go/vt/topo/k8stopo/version.go create mode 100644 go/vt/topo/k8stopo/watch.go create mode 100644 go/vt/vtctl/plugin_kubernetestopo.go diff --git a/Makefile b/Makefile index 6c8618fbed5..959b5f2f83e 100644 --- a/Makefile +++ b/Makefile @@ -332,3 +332,25 @@ minimaltools: dependency_check: ./tools/dependency_check.sh + +GEN_BASE_DIR ?= ./go/vt/topo/k8stopo + +client_go_gen: + echo $$(date): Regenerating client-go code + # Delete and re-generate the deepcopy types + find $(GEN_BASE_DIR)/apis/topo/v1beta1 -type f -name 'zz_generated*' -exec rm '{}' \; + deepcopy-gen -i $(GEN_BASE_DIR)/apis/topo/v1beta1 -O zz_generated.deepcopy -o ./ --bounding-dirs $(GEN_BASE_DIR)/apis --go-header-file $(GEN_BASE_DIR)/boilerplate.go.txt + + # Delete, generate, and move the client libraries + rm -rf go/vt/topo/k8stopo/client + + # There is no way to get client-gen to automatically put files in the right place and still have the right import path so we generate and move them + + # Generate client, informers, and listers + client-gen -o ./ --input 'topo/v1beta1' --clientset-name versioned --input-base 'vitess.io/vitess/go/vt/topo/k8stopo/apis/' -i vitess.io/vitess --output-package vitess.io/vitess/go/vt/topo/k8stopo/client/clientset --go-header-file $(GEN_BASE_DIR)/boilerplate.go.txt + lister-gen -o ./ --input-dirs vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1 --output-package vitess.io/vitess/go/vt/topo/k8stopo/client/listers --go-header-file $(GEN_BASE_DIR)/boilerplate.go.txt + informer-gen -o ./ --input-dirs vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1 --versioned-clientset-package vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned --listers-package vitess.io/vitess/go/vt/topo/k8stopo/client/listers --output-package vitess.io/vitess/go/vt/topo/k8stopo/client/informers --go-header-file $(GEN_BASE_DIR)/boilerplate.go.txt + + # Move and cleanup + mv vitess.io/vitess/go/vt/topo/k8stopo/client go/vt/topo/k8stopo/ + rmdir -p vitess.io/vitess/go/vt/topo/k8stopo/ diff --git a/bootstrap.sh b/bootstrap.sh index bc9f9170357..40e26ad7ce0 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -165,6 +165,33 @@ function install_etcd() { command -v etcd && echo "etcd already installed" || install_dep "etcd" "v3.3.10" "$VTROOT/dist/etcd" install_etcd +# Download and install k3s, link k3s binary into our root +function install_k3s() { + local version="$1" + local dist="$2" + + case $(uname) in + Linux) local platform=linux;; + *) echo "ERROR: unsupported platform. K3s only supports running on Linux"; exit 1;; + esac + + case $(get_arch) in + aarch64) local target="-arm64";; + x86_64) local target="";; + *) echo "ERROR: unsupported architecture"; exit 1;; + esac + + download_url=https://github.com/rancher/k3s/releases/download + file="k3s${target}" + + local dest="$dist/k3s${target}-${version}-${platform}" + wget -O $dest "$download_url/$version/$file" + chmod +x $dest + ln -snf $dest "$VTROOT/bin/k3s" +} +command -v k3s || install_dep "k3s" "v1.0.0" "$VTROOT/dist/k3s" install_k3s + + # Download and install consul, link consul binary into our root. function install_consul() { local version="$1" diff --git a/examples/local/101_initial_cluster.sh b/examples/local/101_initial_cluster.sh index 62e4542cc09..4472412e944 100755 --- a/examples/local/101_initial_cluster.sh +++ b/examples/local/101_initial_cluster.sh @@ -21,7 +21,9 @@ source ./env.sh # start topo server if [ "${TOPO}" = "zk2" ]; then - CELL=zone1 ./scripts/zk-up.sh + CELL=zone1 ./scripts/zk-up.sh +elif [ "${TOPO}" = "k8s" ]; then + CELL=zone1 ./scripts/k3s-up.sh else CELL=zone1 ./scripts/etcd-up.sh fi diff --git a/examples/local/401_teardown.sh b/examples/local/401_teardown.sh index d044b568afd..0193f04ff9e 100755 --- a/examples/local/401_teardown.sh +++ b/examples/local/401_teardown.sh @@ -36,6 +36,8 @@ done if [ "${TOPO}" = "zk2" ]; then CELL=zone1 ./scripts/zk-down.sh +elif [ "${TOPO}" = "k8s" ]; then + CELL=zone1 ./scripts/k3s-down.sh else CELL=zone1 ./scripts/etcd-down.sh fi diff --git a/examples/local/env.sh b/examples/local/env.sh index 987dcc2f379..1d21fafc98f 100644 --- a/examples/local/env.sh +++ b/examples/local/env.sh @@ -31,7 +31,7 @@ fi # mysqld might be in /usr/sbin which will not be in the default PATH PATH="/usr/sbin:$PATH" -for binary in mysqld etcd etcdctl curl vtctlclient vttablet vtgate vtctld mysqlctl; do +for binary in mysqld etcd etcdctl curl vtctlclient vttablet vtgate vtctld vtctl mysqlctl; do command -v "$binary" > /dev/null || fail "${binary} is not installed in PATH. See https://vitess.io/docs/get-started/local/ for install instructions." done; @@ -54,12 +54,19 @@ if [ "${TOPO}" = "zk2" ]; then ZK_SERVER="localhost:21811,localhost:21812,localhost:21813" # shellcheck disable=SC2034 TOPOLOGY_FLAGS="-topo_implementation zk2 -topo_global_server_address ${ZK_SERVER} -topo_global_root /vitess/global" - - mkdir -p $VTDATAROOT/tmp +elif [ "${TOPO}" = "k8s" ]; then + # Set topology environment parameters. + K8S_ADDR="localhost" + K8S_PORT="8443" + K8S_KUBECONFIG=$VTDATAROOT/tmp/k8s.kubeconfig + # shellcheck disable=SC2034 + TOPOLOGY_FLAGS="-topo_implementation k8s -topo_k8s_kubeconfig ${K8S_KUBECONFIG} -topo_global_server_address ${K8S_ADDR}:${K8S_PORT} -topo_global_root /vitess/global" else ETCD_SERVER="localhost:2379" TOPOLOGY_FLAGS="-topo_implementation etcd2 -topo_global_server_address $ETCD_SERVER -topo_global_root /vitess/global" - mkdir -p "${VTDATAROOT}/tmp" mkdir -p "${VTDATAROOT}/etcd" fi + +# Create a tmp dir +mkdir -p "${VTDATAROOT}/tmp" diff --git a/examples/local/scripts/k3s-down.sh b/examples/local/scripts/k3s-down.sh new file mode 100755 index 00000000000..590dc604e3e --- /dev/null +++ b/examples/local/scripts/k3s-down.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is an example script that stops the k3s server started by k3s-up.sh. + +set -e + +# shellcheck source=./env.sh +# shellcheck disable=SC1091 +source ./env.sh + +# Stop K3s server. +echo "Stopping k3s server..." + +pid=`cat $VTDATAROOT/tmp/k3s.pid` +echo "Stopping k3s..." +kill -9 $pid diff --git a/examples/local/scripts/k3s-up.sh b/examples/local/scripts/k3s-up.sh new file mode 100755 index 00000000000..3b77600018e --- /dev/null +++ b/examples/local/scripts/k3s-up.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is an example script that creates a Kubernetes api for topo use by running k3s + +set -e +cell=${CELL:-'test'} + +script_root=$(dirname "${BASH_SOURCE[0]}") + +# shellcheck source=./env.sh +# shellcheck disable=SC1091 +source ./env.sh + +k3s server --disable-agent --data-dir "${VTDATAROOT}/k3s/" --https-listen-port "${K8S_PORT}" --write-kubeconfig "${K8S_KUBECONFIG}" > "${VTDATAROOT}"/tmp/k3s.out 2>&1 & +PID=$! +echo $PID > "${VTDATAROOT}/tmp/k3s.pid" +disown -a +echo "Waiting for k3s server to start" +sleep 15 + +# Use k3s built-in kubectl with custom config +KUBECTL="k3s kubectl --kubeconfig=${K8S_KUBECONFIG}" + +# Create the CRD for vitesstopologynodes +$KUBECTL create -f ../../go/vt/topo/k8stopo/VitessTopoNodes-crd.yaml + +# Add the CellInfo description for the cell +set +e +echo "add $cell CellInfo" +vtctl $TOPOLOGY_FLAGS AddCellInfo \ + -root /vitess/$cell \ + $cell +set -e + +echo "k3s start done..." diff --git a/examples/local/topo-k8s.sh b/examples/local/topo-k8s.sh new file mode 100644 index 00000000000..92e14ba4d69 --- /dev/null +++ b/examples/local/topo-k8s.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is an example script that creates a single shard vttablet deployment. + +export TOPO='k8s' + + diff --git a/go.mod b/go.mod index 9f2afc72d8e..4274af73af3 100644 --- a/go.mod +++ b/go.mod @@ -8,23 +8,49 @@ require ( github.com/Azure/go-autorest/autorest/adal v0.8.1 // indirect github.com/Bowery/prompt v0.0.0-20190419144237-972d0ceb96f5 // indirect github.com/GeertJohan/go.rice v1.0.0 + github.com/Jeffail/gabs v1.1.0 // indirect + github.com/Microsoft/go-winio v0.4.3 // indirect + github.com/NYTimes/gziphandler v1.0.1 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/SAP/go-hdb v0.12.0 // indirect + github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc // indirect + github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect github.com/aws/aws-sdk-go v1.28.8 + github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect + github.com/boltdb/bolt v1.3.1 // indirect github.com/bombsimon/wsl v1.2.8 // indirect + github.com/cenkalti/backoff v2.1.1+incompatible // indirect github.com/cespare/xxhash/v2 v2.1.1 github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 // indirect github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect + github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect + github.com/coredns/coredns v1.1.2 // indirect github.com/coreos/etcd v3.3.10+incompatible - github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/corpix/uarand v0.1.1 // indirect github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect + github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f // indirect + github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 // indirect + github.com/digitalocean/godo v1.10.0 // indirect + github.com/docker/go-connections v0.3.0 // indirect + github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 // indirect + github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 // indirect + github.com/envoyproxy/go-control-plane v0.0.0-20180919002855-2137d9196328 // indirect github.com/evanphx/json-patch v4.5.0+incompatible + github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7 // indirect github.com/go-critic/go-critic v0.4.0 // indirect + github.com/go-ini/ini v1.25.4 // indirect + github.com/go-ldap/ldap v3.0.2+incompatible // indirect github.com/go-sql-driver/mysql v1.5.0 + github.com/go-test/deep v1.0.1 // indirect + github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78 // indirect + github.com/gogo/googleapis v1.1.0 // indirect github.com/gogo/protobuf v1.3.1 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 // indirect github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.2 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db @@ -33,25 +59,60 @@ require ( github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect github.com/google/go-cmp v0.4.0 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf // indirect + github.com/googleapis/gnostic v0.2.0 // indirect github.com/gorilla/websocket v1.4.0 github.com/gostaticanalysis/analysisutil v0.0.3 // indirect + github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/hashicorp/consul v1.5.1 - github.com/hashicorp/consul/api v1.1.0 + github.com/hashicorp/consul v1.4.5 + github.com/hashicorp/go-bexpr v0.1.0 // indirect + github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de // indirect + github.com/hashicorp/go-hclog v0.0.0-20180402200405-69ff559dc25f // indirect + github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71 // indirect github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031 // indirect github.com/hashicorp/golang-lru v0.5.3 // indirect + github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5 // indirect + github.com/hashicorp/mdns v1.0.1 // indirect + github.com/hashicorp/memberlist v0.1.4 // indirect + github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69 // indirect + github.com/hashicorp/raft v1.0.1-0.20190409200437-d9fe23f7d472 // indirect + github.com/hashicorp/raft-boltdb v0.0.0-20150201200839-d1e82c1ec3f1 // indirect + github.com/hashicorp/serf v0.8.5 // indirect + github.com/hashicorp/vault v0.10.3 // indirect + github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1 // indirect + github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect + github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 + github.com/imdario/mergo v0.3.6 // indirect + github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da // indirect + github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee // indirect + github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 // indirect + github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b // indirect github.com/klauspost/crc32 v1.2.0 // indirect github.com/klauspost/pgzip v1.2.0 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/krishicks/yaml-patch v0.0.10 + github.com/lyft/protoc-gen-validate v0.0.0-20180911180927-64fcb82c878e // indirect github.com/mattn/go-isatty v0.0.11 // indirect - github.com/mattn/go-runewidth v0.0.1 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 - github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 + github.com/mitchellh/copystructure v0.0.0-20160804032330-cdac8253d00f // indirect + github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 // indirect + github.com/mitchellh/reflectwalk v1.0.1 // indirect + github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect + github.com/oklog/run v0.0.0-20180308005104-6934b124db28 // indirect + github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 + github.com/opencontainers/go-digest v1.0.0-rc1 // indirect + github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/runc v0.1.1 // indirect github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02 github.com/opentracing/opentracing-go v1.1.0 + github.com/ory/dockertest v3.3.4+incompatible // indirect + github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect + github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8 // indirect github.com/pborman/uuid v1.2.0 github.com/pelletier/go-toml v1.6.0 // indirect github.com/philhofer/fwd v1.0.0 // indirect @@ -61,23 +122,25 @@ require ( github.com/prometheus/common v0.9.1 github.com/satori/go.uuid v0.0.0-20160713180306-0aa62d5ddceb // indirect github.com/securego/gosec v0.0.0-20191217083152-cb4f343eaff1 // indirect - github.com/spf13/afero v1.2.2 // indirect + github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/spf13/cast v1.3.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.6.1 // indirect github.com/stretchr/testify v1.4.0 github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b github.com/tebeka/selenium v0.9.9 + github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9 // indirect github.com/tinylib/msgp v1.1.1 // indirect github.com/uber-go/atomic v1.4.0 // indirect github.com/uber/jaeger-client-go v2.16.0+incompatible github.com/uber/jaeger-lib v2.0.0+incompatible // indirect github.com/ugorji/go v1.1.7 // indirect github.com/uudashr/gocognit v1.0.1 // indirect + github.com/vmware/govmomi v0.18.0 // indirect github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 golang.org/x/lint v0.0.0-20190409202823-959b441ac422 - golang.org/x/net v0.0.0-20190926025831-c00fd9afed17 + golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/text v0.3.2 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 @@ -86,8 +149,17 @@ require ( google.golang.org/genproto v0.0.0-20190926190326-7ee9db18f195 // indirect google.golang.org/grpc v1.24.0 gopkg.in/DataDog/dd-trace-go.v1 v1.17.0 + gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect + gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect + gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect gopkg.in/ldap.v2 v2.5.0 + gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 // indirect + gopkg.in/ory-am/dockertest.v3 v3.3.4 // indirect + gopkg.in/square/go-jose.v2 v2.3.1 // indirect honnef.co/go/tools v0.0.1-2019.2.3 + k8s.io/apiextensions-apiserver v0.17.3 + k8s.io/apimachinery v0.17.3 + k8s.io/client-go v0.17.3 mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2 // indirect sourcegraph.com/sqs/pbtypes v1.0.0 // indirect vitess.io/vitess/examples/are-you-alive v0.0.0-20200302220708-6b7695375ce9 // indirect diff --git a/go.sum b/go.sum index 65126378038..75acda52c0f 100644 --- a/go.sum +++ b/go.sum @@ -8,14 +8,14 @@ cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/azure-pipeline-go v0.2.1 h1:OLBdZJ3yvOn2MezlWvbrBMTEUQC72zAftRZOMdj5HYo= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFEY95rZLK0Tj6o= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= @@ -49,15 +49,22 @@ github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2o github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0= github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -66,6 +73,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -75,6 +83,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a h1:Ed33uJE74ksDaYfdY72gK7Cg//o2FgsqlqUfBW079T8= github.com/aws/aws-sdk-go v0.0.0-20180223184012-ebef4262e06a/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k= github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= @@ -87,6 +97,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= @@ -108,6 +119,7 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 h1:dzj1/xcivGjNPwwifh/dWTczkwcuqsXXFHY1X/TZMtw= github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292/go.mod h1:qRiX68mZX1lGBkTWyp3CLcenw9I94W2dLeRvMzcn9N4= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= @@ -119,12 +131,16 @@ github.com/coreos/etcd v0.0.0-20170626015032-703663d1f6ed/go.mod h1:uF7uidLiAD3T github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U= @@ -133,6 +149,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -146,12 +163,21 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.0.0-20180919002855-2137d9196328/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= @@ -159,9 +185,12 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v0.0.0-20161207003320-04f313413ffd h1:U3yHrYB7NWH2o3UFzJ1J+TknZqM9QQtF8KVIE6Qzrfs= github.com/ghodss/yaml v0.0.0-20161207003320-04f313413ffd/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db h1:GYXWx7Vr3+zv833u+8IoXbNnQY0AdXsxAgI0kX7xcwA= github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= github.com/go-critic/go-critic v0.4.0 h1:sXD3pix0wDemuPuSlrXpJNNYXlUiKiysLrtPVQmxkzI= @@ -176,7 +205,51 @@ github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0 github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -211,11 +284,13 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -224,6 +299,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= @@ -279,7 +355,9 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -290,17 +368,23 @@ github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmI github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v0.0.0-20160912153041-2d1e4548da23 h1:NqeYYy/q+eU5bXzLrVTFSEMp5/VFwp3eJ6nkDsi7wos= github.com/gorilla/websocket v0.0.0-20160912153041-2d1e4548da23/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw= @@ -310,6 +394,7 @@ github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7 github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= @@ -317,9 +402,12 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v0.0.0-20161128002007-199c40a060d1 h1:HpW72214zD3cWQsV9DW4Sd9piuuw8rPmE4/TAjTgYIE= github.com/grpc-ecosystem/grpc-gateway v0.0.0-20161128002007-199c40a060d1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw= github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= +github.com/hashicorp/consul v1.4.5 h1:ubKneQZvooWl/UkkIdx/Rhr/YKOo4OYL3qDAAfkU1Mw= +github.com/hashicorp/consul v1.4.5/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= github.com/hashicorp/consul v1.5.1 h1:p7tRmQ4m3ZMYkGQkuyjLXKbdU1weeumgZFqZOvw7o4c= github.com/hashicorp/consul v1.5.1/go.mod h1:QsmgXh2YA9Njv6y3/FHXqHYhsMye++3oBoAZ6SR8R8I= github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= @@ -351,6 +439,8 @@ github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0 github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg= github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -380,6 +470,8 @@ github.com/hashicorp/serf v0.0.0-20161207011743-d3a67ab21bc8 h1:Vd9tjFEMH3X1AMV1 github.com/hashicorp/serf v0.0.0-20161207011743-d3a67ab21bc8/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.8.5 h1:ZynDUIQiA8usmRgPdGPHFdPnb1wgGI9tK3mO9hcAJjc= +github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= github.com/hashicorp/vault v0.10.3/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= @@ -387,6 +479,8 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKe github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -405,9 +499,12 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= @@ -439,6 +536,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -453,20 +551,29 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE= github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149 h1:HfxbT6/JcvIljmERptWhwa8XzP7H3T+Z2N26gTsaDaA= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-runewidth v0.0.1 h1:+EiaBVXhogb1Klb4tRJ7hYnuGK6PkKOZlK04D/GMOqk= github.com/mattn/go-runewidth v0.0.1/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -492,11 +599,17 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= @@ -507,9 +620,13 @@ github.com/oklog/run v0.0.0-20180308005104-6934b124db28/go.mod h1:dlhp/R75TPv97u github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984 h1:c9gVtoY8wPlhJIN2V2I1V+Fn9UcXM8mDG8IHv/1c3r8= github.com/olekukonko/tablewriter v0.0.0-20160115111002-cca8bbc07984/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 h1:58+kh9C6jJVXYjt8IE48G2eWl6BjwU5Gj0gqY84fy78= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -543,9 +660,11 @@ github.com/pires/go-proxyproto v0.0.0-20191211124218-517ecdf5bb2b/go.mod h1:Odh9 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180328130430-f504d69affe1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= @@ -588,6 +707,7 @@ github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLk github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -603,6 +723,7 @@ github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d h1:BzRvVq1EHuIjxpij github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do= github.com/securego/gosec v0.0.0-20191217083152-cb4f343eaff1 h1:p7IOnYri8VyitvXJfgXw7yt2G/teasqQHQ6f/u1RQvc= github.com/securego/gosec v0.0.0-20191217083152-cb4f343eaff1/go.mod h1:sM2KJ/O9PKom+0jAmXpblJ8PWrLbGAk6F2Lzwj4H6wg= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= @@ -633,12 +754,15 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -653,6 +777,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= @@ -664,10 +789,12 @@ github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b/go.mod h1:bmLyhP github.com/tebeka/selenium v0.9.9 h1:cNziB+etNgyH/7KlNI7RMC1ua5aH1+5wUlFQyzeMh+w= github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc= github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tinylib/msgp v1.1.1 h1:TnCZ3FIuKeaIy+F45+Cnp+caqdXGy4z74HvwXN+570Y= github.com/tinylib/msgp v1.1.1/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= @@ -686,6 +813,7 @@ github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbd github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg= github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517 h1:ChMKTho2hWKpks/nD/FL2KqM1wuVt62oJeiE8+eFpGs= github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs= @@ -697,6 +825,7 @@ github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8W github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -704,9 +833,15 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1: github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b h1:Itr7GbuXoM1PK/eCeNNia4Qd3ib9IgX9g9SpXgo8BwQ= github.com/z-division/go-zookeeper v0.0.0-20190128072838-6d7457066b9b/go.mod h1:JNALoWa+nCXR8SmgLluHcBNVJgyejzpKPZk9pX2yXXE= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -715,10 +850,15 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM= golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= @@ -727,6 +867,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -739,10 +881,12 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -751,6 +895,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -759,9 +904,13 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190926025831-c00fd9afed17 h1:qPnAdmjNA41t3QBTx2mFGf/SD1IoslhYu7AmdsVzCcs= golang.org/x/net v0.0.0-20190926025831-c00fd9afed17/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -774,6 +923,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -783,17 +933,21 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190926180325-855e68c8590b h1:/8GN4qrAmRZQXgjWZHj9z/UJI5vNqQhPtgcw02z2f+8= golang.org/x/sys v0.0.0-20190926180325-855e68c8590b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -802,6 +956,7 @@ golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 h1:Y8q0zsdcgAd+JU8VUA8p8Qv2Y golang.org/x/sys v0.0.0-20191218084908-4a24b4065292/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -814,10 +969,13 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -830,6 +988,8 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -838,6 +998,7 @@ golang.org/x/tools v0.0.0-20190830154057-c17b040389b9 h1:5/jaG/gKlo3xxvUn85ReNyT golang.org/x/tools v0.0.0-20190830154057-c17b040389b9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911151314-feee8acb394c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -851,6 +1012,9 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbO golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= @@ -881,6 +1045,7 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= gopkg.in/DataDog/dd-trace-go.v1 v1.17.0 h1:j9vAp9Re9bbtA/QFehkJpNba/6W2IbJtNuXZophCa54= @@ -896,9 +1061,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.41.0 h1:Ka3ViY6gNYSKiVy71zXBEqKplnV35ImDLVG+8uoIklE= gopkg.in/ini.v1 v1.41.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -907,8 +1074,10 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ldap.v2 v2.5.0 h1:1rO3ojzsHUk+gq4ZYhC4Pg+EzWaaKIV8+DJwExS5/QQ= gopkg.in/ldap.v2 v2.5.0/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk= gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -920,6 +1089,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -932,9 +1103,35 @@ honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXe honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.0.0-20180806132203-61b11ee65332/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= k8s.io/api v0.0.0-20190325185214-7544f9db76f6/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0= +k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= +k8s.io/apiextensions-apiserver v0.17.3 h1:WDZWkPcbgvchEdDd7ysL21GGPx3UKZQLDZXEkevT6n4= +k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY= k8s.io/apimachinery v0.0.0-20180821005732-488889b0007f/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg= +k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= +k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY= +k8s.io/client-go v0.17.3 h1:deUna1Ksx05XeESH6XGCyONNFfiQmDdqeqUvicvP6nU= +k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/code-generator v0.17.3/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ= +k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= +k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= @@ -944,6 +1141,10 @@ mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZI mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2 h1:K7wru2CfJGumS5hkiguQ0Rb9ebKM2Jo8s5d4Jm9lFaM= mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2/go.mod h1:rCqoQrfAmpTX/h2APczwM7UymU/uvaOluiVPIYCSY/k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= sourcegraph.com/sqs/pbtypes v1.0.0 h1:f7lAwqviDEGvON4kRv0o5V7FT/IQK+tbkF664XMbP3o= diff --git a/go/cmd/topo2topo/plugin_kubernetestopo.go b/go/cmd/topo2topo/plugin_kubernetestopo.go new file mode 100644 index 00000000000..671d0c8321f --- /dev/null +++ b/go/cmd/topo2topo/plugin_kubernetestopo.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +// This plugin imports k8stopo to register the kubernetes implementation of TopoServer. + +import ( + _ "vitess.io/vitess/go/vt/topo/k8stopo" +) diff --git a/go/cmd/vtctld/plugin_kubernetestopo.go b/go/cmd/vtctld/plugin_kubernetestopo.go new file mode 100644 index 00000000000..97612df6ed7 --- /dev/null +++ b/go/cmd/vtctld/plugin_kubernetestopo.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +// Imports and register the 'kubernetes' topo.Server. + +import ( + _ "vitess.io/vitess/go/vt/topo/k8stopo" +) diff --git a/go/cmd/vtgate/plugin_kubernetestopo.go b/go/cmd/vtgate/plugin_kubernetestopo.go new file mode 100644 index 00000000000..671d0c8321f --- /dev/null +++ b/go/cmd/vtgate/plugin_kubernetestopo.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +// This plugin imports k8stopo to register the kubernetes implementation of TopoServer. + +import ( + _ "vitess.io/vitess/go/vt/topo/k8stopo" +) diff --git a/go/cmd/vttablet/plugin_kubernetestopo.go b/go/cmd/vttablet/plugin_kubernetestopo.go new file mode 100644 index 00000000000..671d0c8321f --- /dev/null +++ b/go/cmd/vttablet/plugin_kubernetestopo.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +// This plugin imports k8stopo to register the kubernetes implementation of TopoServer. + +import ( + _ "vitess.io/vitess/go/vt/topo/k8stopo" +) diff --git a/go/cmd/vtworker/plugin_kubernetestopo.go b/go/cmd/vtworker/plugin_kubernetestopo.go new file mode 100644 index 00000000000..671d0c8321f --- /dev/null +++ b/go/cmd/vtworker/plugin_kubernetestopo.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +// This plugin imports k8stopo to register the kubernetes implementation of TopoServer. + +import ( + _ "vitess.io/vitess/go/vt/topo/k8stopo" +) diff --git a/go/vt/topo/k8stopo/VitessTopoNodes-crd.yaml b/go/vt/topo/k8stopo/VitessTopoNodes-crd.yaml new file mode 100644 index 00000000000..b80465451ed --- /dev/null +++ b/go/vt/topo/k8stopo/VitessTopoNodes-crd.yaml @@ -0,0 +1,43 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: vitesstoponodes.topo.vitess.io +spec: + group: topo.vitess.io + versions: + - name: v1beta1 + served: true + storage: true + additionalPrinterColumns: + - name: Key + type: string + description: The full key path + jsonPath: .data.key + schema: + openAPIV3Schema: + type: object + required: + - data + properties: + data: + type: object + required: + - key + - value + properties: + key: + description: A file-path like key. Must be an absolute path. Must not end with a /. + type: string + pattern: '^\/.+[^\/]$' + value: + description: A base64 encoded value. Must be a base64 encoded string or empty string. + type: string + pattern: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$" + ephemeral: + description: Whether or not the node is considered ephemeral. True for lock and election nodes. + type: boolean + scope: Namespaced + names: + plural: vitesstoponodes + singular: vitesstoponode + kind: VitessTopoNode diff --git a/go/vt/topo/k8stopo/apis/topo/v1beta1/doc.go b/go/vt/topo/k8stopo/apis/topo/v1beta1/doc.go new file mode 100644 index 00000000000..e2be94ae46e --- /dev/null +++ b/go/vt/topo/k8stopo/apis/topo/v1beta1/doc.go @@ -0,0 +1,5 @@ +// +k8s:deepcopy-gen=package +// +k8s:defaulter-gen=TypeMeta +// +groupName=topo.vitess.io + +package v1beta1 diff --git a/go/vt/topo/k8stopo/apis/topo/v1beta1/register.go b/go/vt/topo/k8stopo/apis/topo/v1beta1/register.go new file mode 100644 index 00000000000..49a9ee9a2a5 --- /dev/null +++ b/go/vt/topo/k8stopo/apis/topo/v1beta1/register.go @@ -0,0 +1,38 @@ +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var SchemeGroupVersion = schema.GroupVersion{Group: "topo.vitess.io", Version: "v1beta1"} + +var ( + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + localSchemeBuilder.Register(addKnownTypes) +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// Adds the list of known types to the given scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &VitessTopoNode{}, + &VitessTopoNodeList{}, + ) + + scheme.AddKnownTypes(SchemeGroupVersion, + &metav1.Status{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/go/vt/topo/k8stopo/apis/topo/v1beta1/types.go b/go/vt/topo/k8stopo/apis/topo/v1beta1/types.go new file mode 100644 index 00000000000..48d48001d64 --- /dev/null +++ b/go/vt/topo/k8stopo/apis/topo/v1beta1/types.go @@ -0,0 +1,32 @@ +package v1beta1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// VitessTopoNode is a container for Vitess topology data +type VitessTopoNode struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + Data VitessTopoNodeData `json:"data"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// VitessTopoNodeList is a top-level list type. The client methods for lists are automatically created. +type VitessTopoNodeList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + Items []VitessTopoNode `json:"items"` +} + +// VitessTopoNodeData contains the basic data for the node +type VitessTopoNodeData struct { + Key string `json:"key"` + Value string `json:"value"` + Ephemeral bool `json:"ephemeral"` +} diff --git a/go/vt/topo/k8stopo/apis/topo/v1beta1/zz_generated.deepcopy.go b/go/vt/topo/k8stopo/apis/topo/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..7464d0c2381 --- /dev/null +++ b/go/vt/topo/k8stopo/apis/topo/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,101 @@ +// +build !ignore_autogenerated + +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VitessTopoNode) DeepCopyInto(out *VitessTopoNode) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Data = in.Data + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VitessTopoNode. +func (in *VitessTopoNode) DeepCopy() *VitessTopoNode { + if in == nil { + return nil + } + out := new(VitessTopoNode) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VitessTopoNode) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VitessTopoNodeData) DeepCopyInto(out *VitessTopoNodeData) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VitessTopoNodeData. +func (in *VitessTopoNodeData) DeepCopy() *VitessTopoNodeData { + if in == nil { + return nil + } + out := new(VitessTopoNodeData) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VitessTopoNodeList) DeepCopyInto(out *VitessTopoNodeList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]VitessTopoNode, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VitessTopoNodeList. +func (in *VitessTopoNodeList) DeepCopy() *VitessTopoNodeList { + if in == nil { + return nil + } + out := new(VitessTopoNodeList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VitessTopoNodeList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/go/vt/topo/k8stopo/boilerplate.go.txt b/go/vt/topo/k8stopo/boilerplate.go.txt new file mode 100644 index 00000000000..3f6ccc17d97 --- /dev/null +++ b/go/vt/topo/k8stopo/boilerplate.go.txt @@ -0,0 +1,16 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/clientset.go b/go/vt/topo/k8stopo/client/clientset/versioned/clientset.go new file mode 100644 index 00000000000..e152c58f327 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/clientset.go @@ -0,0 +1,97 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" + topov1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + TopoV1beta1() topov1beta1.TopoV1beta1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + topoV1beta1 *topov1beta1.TopoV1beta1Client +} + +// TopoV1beta1 retrieves the TopoV1beta1Client +func (c *Clientset) TopoV1beta1() topov1beta1.TopoV1beta1Interface { + return c.topoV1beta1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.topoV1beta1, err = topov1beta1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.topoV1beta1 = topov1beta1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.topoV1beta1 = topov1beta1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/doc.go b/go/vt/topo/k8stopo/client/clientset/versioned/doc.go new file mode 100644 index 00000000000..499efc0b13e --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/fake/clientset_generated.go b/go/vt/topo/k8stopo/client/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 00000000000..71e764f4709 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,82 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" + clientset "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned" + topov1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1" + faketopov1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var _ clientset.Interface = &Clientset{} + +// TopoV1beta1 retrieves the TopoV1beta1Client +func (c *Clientset) TopoV1beta1() topov1beta1.TopoV1beta1Interface { + return &faketopov1beta1.FakeTopoV1beta1{Fake: &c.Fake} +} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/fake/doc.go b/go/vt/topo/k8stopo/client/clientset/versioned/fake/doc.go new file mode 100644 index 00000000000..97b3e5c56db --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/fake/register.go b/go/vt/topo/k8stopo/client/clientset/versioned/fake/register.go new file mode 100644 index 00000000000..55ae45f14c3 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/fake/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + topov1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + topov1beta1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/scheme/doc.go b/go/vt/topo/k8stopo/client/clientset/versioned/scheme/doc.go new file mode 100644 index 00000000000..35280ae27c6 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/scheme/register.go b/go/vt/topo/k8stopo/client/clientset/versioned/scheme/register.go new file mode 100644 index 00000000000..dbfa0f69fab --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/scheme/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + topov1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + topov1beta1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/doc.go b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/doc.go new file mode 100644 index 00000000000..4911ad316da --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1beta1 diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/doc.go b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/doc.go new file mode 100644 index 00000000000..e8f15d37732 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_topo_client.go b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_topo_client.go new file mode 100644 index 00000000000..a669d391f35 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_topo_client.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1" +) + +type FakeTopoV1beta1 struct { + *testing.Fake +} + +func (c *FakeTopoV1beta1) VitessTopoNodes(namespace string) v1beta1.VitessTopoNodeInterface { + return &FakeVitessTopoNodes{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeTopoV1beta1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_vitesstoponode.go b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_vitesstoponode.go new file mode 100644 index 00000000000..659d392ba45 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/fake/fake_vitesstoponode.go @@ -0,0 +1,128 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +// FakeVitessTopoNodes implements VitessTopoNodeInterface +type FakeVitessTopoNodes struct { + Fake *FakeTopoV1beta1 + ns string +} + +var vitesstoponodesResource = schema.GroupVersionResource{Group: "topo.vitess.io", Version: "v1beta1", Resource: "vitesstoponodes"} + +var vitesstoponodesKind = schema.GroupVersionKind{Group: "topo.vitess.io", Version: "v1beta1", Kind: "VitessTopoNode"} + +// Get takes name of the vitessTopoNode, and returns the corresponding vitessTopoNode object, and an error if there is any. +func (c *FakeVitessTopoNodes) Get(name string, options v1.GetOptions) (result *v1beta1.VitessTopoNode, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(vitesstoponodesResource, c.ns, name), &v1beta1.VitessTopoNode{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.VitessTopoNode), err +} + +// List takes label and field selectors, and returns the list of VitessTopoNodes that match those selectors. +func (c *FakeVitessTopoNodes) List(opts v1.ListOptions) (result *v1beta1.VitessTopoNodeList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(vitesstoponodesResource, vitesstoponodesKind, c.ns, opts), &v1beta1.VitessTopoNodeList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta1.VitessTopoNodeList{ListMeta: obj.(*v1beta1.VitessTopoNodeList).ListMeta} + for _, item := range obj.(*v1beta1.VitessTopoNodeList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested vitessTopoNodes. +func (c *FakeVitessTopoNodes) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(vitesstoponodesResource, c.ns, opts)) + +} + +// Create takes the representation of a vitessTopoNode and creates it. Returns the server's representation of the vitessTopoNode, and an error, if there is any. +func (c *FakeVitessTopoNodes) Create(vitessTopoNode *v1beta1.VitessTopoNode) (result *v1beta1.VitessTopoNode, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(vitesstoponodesResource, c.ns, vitessTopoNode), &v1beta1.VitessTopoNode{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.VitessTopoNode), err +} + +// Update takes the representation of a vitessTopoNode and updates it. Returns the server's representation of the vitessTopoNode, and an error, if there is any. +func (c *FakeVitessTopoNodes) Update(vitessTopoNode *v1beta1.VitessTopoNode) (result *v1beta1.VitessTopoNode, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(vitesstoponodesResource, c.ns, vitessTopoNode), &v1beta1.VitessTopoNode{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.VitessTopoNode), err +} + +// Delete takes name of the vitessTopoNode and deletes it. Returns an error if one occurs. +func (c *FakeVitessTopoNodes) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(vitesstoponodesResource, c.ns, name), &v1beta1.VitessTopoNode{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeVitessTopoNodes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(vitesstoponodesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1beta1.VitessTopoNodeList{}) + return err +} + +// Patch applies the patch and returns the patched vitessTopoNode. +func (c *FakeVitessTopoNodes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VitessTopoNode, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(vitesstoponodesResource, c.ns, name, pt, data, subresources...), &v1beta1.VitessTopoNode{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.VitessTopoNode), err +} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/generated_expansion.go b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/generated_expansion.go new file mode 100644 index 00000000000..fa0096543d2 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +type VitessTopoNodeExpansion interface{} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/topo_client.go b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/topo_client.go new file mode 100644 index 00000000000..4a94ffe7d61 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/topo_client.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + rest "k8s.io/client-go/rest" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" + "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned/scheme" +) + +type TopoV1beta1Interface interface { + RESTClient() rest.Interface + VitessTopoNodesGetter +} + +// TopoV1beta1Client is used to interact with features provided by the topo.vitess.io group. +type TopoV1beta1Client struct { + restClient rest.Interface +} + +func (c *TopoV1beta1Client) VitessTopoNodes(namespace string) VitessTopoNodeInterface { + return newVitessTopoNodes(c, namespace) +} + +// NewForConfig creates a new TopoV1beta1Client for the given config. +func NewForConfig(c *rest.Config) (*TopoV1beta1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &TopoV1beta1Client{client}, nil +} + +// NewForConfigOrDie creates a new TopoV1beta1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *TopoV1beta1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new TopoV1beta1Client for the given RESTClient. +func New(c rest.Interface) *TopoV1beta1Client { + return &TopoV1beta1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1beta1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *TopoV1beta1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/vitesstoponode.go b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/vitesstoponode.go new file mode 100644 index 00000000000..c3193d48202 --- /dev/null +++ b/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1/vitesstoponode.go @@ -0,0 +1,174 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" + scheme "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned/scheme" +) + +// VitessTopoNodesGetter has a method to return a VitessTopoNodeInterface. +// A group's client should implement this interface. +type VitessTopoNodesGetter interface { + VitessTopoNodes(namespace string) VitessTopoNodeInterface +} + +// VitessTopoNodeInterface has methods to work with VitessTopoNode resources. +type VitessTopoNodeInterface interface { + Create(*v1beta1.VitessTopoNode) (*v1beta1.VitessTopoNode, error) + Update(*v1beta1.VitessTopoNode) (*v1beta1.VitessTopoNode, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1beta1.VitessTopoNode, error) + List(opts v1.ListOptions) (*v1beta1.VitessTopoNodeList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VitessTopoNode, err error) + VitessTopoNodeExpansion +} + +// vitessTopoNodes implements VitessTopoNodeInterface +type vitessTopoNodes struct { + client rest.Interface + ns string +} + +// newVitessTopoNodes returns a VitessTopoNodes +func newVitessTopoNodes(c *TopoV1beta1Client, namespace string) *vitessTopoNodes { + return &vitessTopoNodes{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the vitessTopoNode, and returns the corresponding vitessTopoNode object, and an error if there is any. +func (c *vitessTopoNodes) Get(name string, options v1.GetOptions) (result *v1beta1.VitessTopoNode, err error) { + result = &v1beta1.VitessTopoNode{} + err = c.client.Get(). + Namespace(c.ns). + Resource("vitesstoponodes"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of VitessTopoNodes that match those selectors. +func (c *vitessTopoNodes) List(opts v1.ListOptions) (result *v1beta1.VitessTopoNodeList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta1.VitessTopoNodeList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("vitesstoponodes"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested vitessTopoNodes. +func (c *vitessTopoNodes) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("vitesstoponodes"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a vitessTopoNode and creates it. Returns the server's representation of the vitessTopoNode, and an error, if there is any. +func (c *vitessTopoNodes) Create(vitessTopoNode *v1beta1.VitessTopoNode) (result *v1beta1.VitessTopoNode, err error) { + result = &v1beta1.VitessTopoNode{} + err = c.client.Post(). + Namespace(c.ns). + Resource("vitesstoponodes"). + Body(vitessTopoNode). + Do(). + Into(result) + return +} + +// Update takes the representation of a vitessTopoNode and updates it. Returns the server's representation of the vitessTopoNode, and an error, if there is any. +func (c *vitessTopoNodes) Update(vitessTopoNode *v1beta1.VitessTopoNode) (result *v1beta1.VitessTopoNode, err error) { + result = &v1beta1.VitessTopoNode{} + err = c.client.Put(). + Namespace(c.ns). + Resource("vitesstoponodes"). + Name(vitessTopoNode.Name). + Body(vitessTopoNode). + Do(). + Into(result) + return +} + +// Delete takes name of the vitessTopoNode and deletes it. Returns an error if one occurs. +func (c *vitessTopoNodes) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("vitesstoponodes"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *vitessTopoNodes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("vitesstoponodes"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched vitessTopoNode. +func (c *vitessTopoNodes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.VitessTopoNode, err error) { + result = &v1beta1.VitessTopoNode{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("vitesstoponodes"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/go/vt/topo/k8stopo/client/informers/externalversions/factory.go b/go/vt/topo/k8stopo/client/informers/externalversions/factory.go new file mode 100644 index 00000000000..b2769718168 --- /dev/null +++ b/go/vt/topo/k8stopo/client/informers/externalversions/factory.go @@ -0,0 +1,180 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" + versioned "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned" + internalinterfaces "vitess.io/vitess/go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces" + topo "vitess.io/vitess/go/vt/topo/k8stopo/client/informers/externalversions/topo" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Topo() topo.Interface +} + +func (f *sharedInformerFactory) Topo() topo.Interface { + return topo.New(f, f.namespace, f.tweakListOptions) +} diff --git a/go/vt/topo/k8stopo/client/informers/externalversions/generic.go b/go/vt/topo/k8stopo/client/informers/externalversions/generic.go new file mode 100644 index 00000000000..005272fb669 --- /dev/null +++ b/go/vt/topo/k8stopo/client/informers/externalversions/generic.go @@ -0,0 +1,62 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=topo.vitess.io, Version=v1beta1 + case v1beta1.SchemeGroupVersion.WithResource("vitesstoponodes"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Topo().V1beta1().VitessTopoNodes().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 00000000000..51a70f91749 --- /dev/null +++ b/go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" + versioned "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/go/vt/topo/k8stopo/client/informers/externalversions/topo/interface.go b/go/vt/topo/k8stopo/client/informers/externalversions/topo/interface.go new file mode 100644 index 00000000000..655cf46d4da --- /dev/null +++ b/go/vt/topo/k8stopo/client/informers/externalversions/topo/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package topo + +import ( + internalinterfaces "vitess.io/vitess/go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1beta1 provides access to shared informers for resources in V1beta1. + V1beta1() v1beta1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1beta1 returns a new v1beta1.Interface. +func (g *group) V1beta1() v1beta1.Interface { + return v1beta1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/interface.go b/go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/interface.go new file mode 100644 index 00000000000..f1bc2dc3db6 --- /dev/null +++ b/go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/interface.go @@ -0,0 +1,45 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + internalinterfaces "vitess.io/vitess/go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // VitessTopoNodes returns a VitessTopoNodeInformer. + VitessTopoNodes() VitessTopoNodeInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// VitessTopoNodes returns a VitessTopoNodeInformer. +func (v *version) VitessTopoNodes() VitessTopoNodeInformer { + return &vitessTopoNodeInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/vitesstoponode.go b/go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/vitesstoponode.go new file mode 100644 index 00000000000..0c25232176f --- /dev/null +++ b/go/vt/topo/k8stopo/client/informers/externalversions/topo/v1beta1/vitesstoponode.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + topov1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" + versioned "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned" + internalinterfaces "vitess.io/vitess/go/vt/topo/k8stopo/client/informers/externalversions/internalinterfaces" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/client/listers/topo/v1beta1" +) + +// VitessTopoNodeInformer provides access to a shared informer and lister for +// VitessTopoNodes. +type VitessTopoNodeInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta1.VitessTopoNodeLister +} + +type vitessTopoNodeInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewVitessTopoNodeInformer constructs a new informer for VitessTopoNode type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewVitessTopoNodeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredVitessTopoNodeInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredVitessTopoNodeInformer constructs a new informer for VitessTopoNode type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredVitessTopoNodeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TopoV1beta1().VitessTopoNodes(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TopoV1beta1().VitessTopoNodes(namespace).Watch(options) + }, + }, + &topov1beta1.VitessTopoNode{}, + resyncPeriod, + indexers, + ) +} + +func (f *vitessTopoNodeInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredVitessTopoNodeInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *vitessTopoNodeInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&topov1beta1.VitessTopoNode{}, f.defaultInformer) +} + +func (f *vitessTopoNodeInformer) Lister() v1beta1.VitessTopoNodeLister { + return v1beta1.NewVitessTopoNodeLister(f.Informer().GetIndexer()) +} diff --git a/go/vt/topo/k8stopo/client/listers/topo/v1beta1/expansion_generated.go b/go/vt/topo/k8stopo/client/listers/topo/v1beta1/expansion_generated.go new file mode 100644 index 00000000000..148dd86628c --- /dev/null +++ b/go/vt/topo/k8stopo/client/listers/topo/v1beta1/expansion_generated.go @@ -0,0 +1,27 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +// VitessTopoNodeListerExpansion allows custom methods to be added to +// VitessTopoNodeLister. +type VitessTopoNodeListerExpansion interface{} + +// VitessTopoNodeNamespaceListerExpansion allows custom methods to be added to +// VitessTopoNodeNamespaceLister. +type VitessTopoNodeNamespaceListerExpansion interface{} diff --git a/go/vt/topo/k8stopo/client/listers/topo/v1beta1/vitesstoponode.go b/go/vt/topo/k8stopo/client/listers/topo/v1beta1/vitesstoponode.go new file mode 100644 index 00000000000..3859fe4b365 --- /dev/null +++ b/go/vt/topo/k8stopo/client/listers/topo/v1beta1/vitesstoponode.go @@ -0,0 +1,94 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +// VitessTopoNodeLister helps list VitessTopoNodes. +type VitessTopoNodeLister interface { + // List lists all VitessTopoNodes in the indexer. + List(selector labels.Selector) (ret []*v1beta1.VitessTopoNode, err error) + // VitessTopoNodes returns an object that can list and get VitessTopoNodes. + VitessTopoNodes(namespace string) VitessTopoNodeNamespaceLister + VitessTopoNodeListerExpansion +} + +// vitessTopoNodeLister implements the VitessTopoNodeLister interface. +type vitessTopoNodeLister struct { + indexer cache.Indexer +} + +// NewVitessTopoNodeLister returns a new VitessTopoNodeLister. +func NewVitessTopoNodeLister(indexer cache.Indexer) VitessTopoNodeLister { + return &vitessTopoNodeLister{indexer: indexer} +} + +// List lists all VitessTopoNodes in the indexer. +func (s *vitessTopoNodeLister) List(selector labels.Selector) (ret []*v1beta1.VitessTopoNode, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.VitessTopoNode)) + }) + return ret, err +} + +// VitessTopoNodes returns an object that can list and get VitessTopoNodes. +func (s *vitessTopoNodeLister) VitessTopoNodes(namespace string) VitessTopoNodeNamespaceLister { + return vitessTopoNodeNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// VitessTopoNodeNamespaceLister helps list and get VitessTopoNodes. +type VitessTopoNodeNamespaceLister interface { + // List lists all VitessTopoNodes in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1beta1.VitessTopoNode, err error) + // Get retrieves the VitessTopoNode from the indexer for a given namespace and name. + Get(name string) (*v1beta1.VitessTopoNode, error) + VitessTopoNodeNamespaceListerExpansion +} + +// vitessTopoNodeNamespaceLister implements the VitessTopoNodeNamespaceLister +// interface. +type vitessTopoNodeNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all VitessTopoNodes in the indexer for a given namespace. +func (s vitessTopoNodeNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.VitessTopoNode, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1beta1.VitessTopoNode)) + }) + return ret, err +} + +// Get retrieves the VitessTopoNode from the indexer for a given namespace and name. +func (s vitessTopoNodeNamespaceLister) Get(name string) (*v1beta1.VitessTopoNode, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta1.Resource("vitesstoponode"), name) + } + return obj.(*v1beta1.VitessTopoNode), nil +} diff --git a/go/vt/topo/k8stopo/config.go b/go/vt/topo/k8stopo/config.go new file mode 100644 index 00000000000..7db781d6497 --- /dev/null +++ b/go/vt/topo/k8stopo/config.go @@ -0,0 +1,17 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo diff --git a/go/vt/topo/k8stopo/directory.go b/go/vt/topo/k8stopo/directory.go new file mode 100644 index 00000000000..df32312a1b3 --- /dev/null +++ b/go/vt/topo/k8stopo/directory.go @@ -0,0 +1,103 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "path/filepath" + "sort" + "strings" + + "golang.org/x/net/context" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/topo" + vtv1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +// ListDir is part of the topo.Conn interface. +// It uses an internal cache to find all the objects matching a specific key and returns +// a slice of results sorted alphabetically to emulate the behavior of etcd, zk, consul, etc +func (s *Server) ListDir(ctx context.Context, dirPath string, full bool) ([]topo.DirEntry, error) { + dirPath = filepath.Join(s.root, dirPath) + + log.V(7).Infof("Listing dir at: '%s', full: %v", dirPath, full) + + dirMap := map[string]topo.DirEntry{} + + if children, err := s.memberIndexer.ByIndex("by_parent", dirPath); err == nil { + for _, obj := range children { + vtn := obj.(*vtv1beta1.VitessTopoNode) + + key := vtn.Data.Key + + // skip duplicates + if _, ok := dirMap[key]; ok { + continue + } + + // new empty entry + e := topo.DirEntry{ + Ephemeral: vtn.Data.Ephemeral, + } + + // Clean dirPath from key to get name + key = strings.TrimPrefix(key, dirPath+"/") + + // If the key represents a directory + if strings.Contains(key, "/") { + if full { + e.Type = topo.TypeDirectory + } + + // get first part of path as name + key = strings.Split(filepath.Dir(key), "/")[0] + } else if full { + e.Type = topo.TypeFile + } + + // set name + e.Name = key + + // add to results + dirMap[e.Name] = e + } + } else { + return nil, err + } + + // An empty map means not found + if len(dirMap) == 0 { + return nil, topo.NewError(topo.NoNode, dirPath) + } + + // Get slice of keys + var keys []string + for key := range dirMap { + keys = append(keys, key) + } + + // sort keys + sort.Strings(keys) + + // Get ordered result + var result []topo.DirEntry + for _, k := range keys { + result = append(result, dirMap[k]) + } + + return result, nil +} diff --git a/go/vt/topo/k8stopo/election.go b/go/vt/topo/k8stopo/election.go new file mode 100644 index 00000000000..e8c1d03285c --- /dev/null +++ b/go/vt/topo/k8stopo/election.go @@ -0,0 +1,123 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "path" + + "golang.org/x/net/context" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/topo" +) + +const electionsPath = "elections" + +// NewMasterParticipation is part of the topo.Server interface +func (s *Server) NewMasterParticipation(name, id string) (topo.MasterParticipation, error) { + return &kubernetesMasterParticipation{ + s: s, + name: name, + id: id, + stop: make(chan struct{}), + done: make(chan struct{}), + }, nil +} + +// kubernetesMasterParticipation implements topo.MasterParticipation. +// +// We use a directory (in global election path, with the name) with +// ephemeral files in it, that contains the id. The oldest revision +// wins the election. +type kubernetesMasterParticipation struct { + // s is our parent kubernetes topo Server + s *Server + + // name is the name of this MasterParticipation + name string + + // id is the process's current id. + id string + + // stop is a channel closed when Stop is called. + stop chan struct{} + + // done is a channel closed when we're done processing the Stop + done chan struct{} +} + +func (mp *kubernetesMasterParticipation) getElectionPath() string { + return path.Join(mp.s.root, electionsPath, mp.name) +} + +// WaitForMastership is part of the topo.MasterParticipation interface. +func (mp *kubernetesMasterParticipation) WaitForMastership() (context.Context, error) { + // If Stop was already called, mp.done is closed, so we are interrupted. + select { + case <-mp.done: + return nil, topo.NewError(topo.Interrupted, "mastership") + default: + } + + electionPath := mp.getElectionPath() + var ld topo.LockDescriptor + + // We use a cancelable context here. If stop is closed, + // we just cancel that context. + lockCtx, lockCancel := context.WithCancel(context.Background()) + go func() { + <-mp.stop + if ld != nil { + if err := ld.Unlock(context.Background()); err != nil { + log.Errorf("failed to unlock electionPath %v: %v", electionPath, err) + } + } + lockCancel() + close(mp.done) + }() + + // Try to get the mastership, by getting a lock. + var err error + ld, err = mp.s.lock(lockCtx, electionPath, mp.id, true) + if err != nil { + // It can be that we were interrupted. + return nil, err + } + + // We got the lock. Return the lockContext. If Stop() is called, + // it will cancel the lockCtx, and cancel the returned context. + return lockCtx, nil +} + +// Stop is part of the topo.MasterParticipation interface +func (mp *kubernetesMasterParticipation) Stop() { + close(mp.stop) + <-mp.done +} + +// GetCurrentMasterID is part of the topo.MasterParticipation interface +func (mp *kubernetesMasterParticipation) GetCurrentMasterID(ctx context.Context) (string, error) { + id, _, err := mp.s.Get(ctx, mp.getElectionPath()) + if err != nil { + // NoNode means nobody is the master + if topo.IsErrType(err, topo.NoNode) { + return "", nil + } + return "", err + } + return string(id), nil +} diff --git a/go/vt/topo/k8stopo/error.go b/go/vt/topo/k8stopo/error.go new file mode 100644 index 00000000000..32f44d0beef --- /dev/null +++ b/go/vt/topo/k8stopo/error.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/errors" + + "vitess.io/vitess/go/vt/topo" +) + +// convertError converts errors into a topo error. All errors +// are either application-level errors, or context errors. +func convertError(err error, nodePath string) error { + if err == nil { + return nil + } + + // Check for specific kubernetes errors + if errors.IsAlreadyExists(err) { + return topo.NewError(topo.NodeExists, nodePath) + } + if errors.IsNotFound(err) { + return topo.NewError(topo.NoNode, nodePath) + } + if errors.IsServerTimeout(err) { + return topo.NewError(topo.Timeout, nodePath) + } + + // Convert specific context sentinel values. + switch err { + case context.Canceled: + return topo.NewError(topo.Interrupted, nodePath) + case context.DeadlineExceeded: + return topo.NewError(topo.Timeout, nodePath) + } + + return err +} diff --git a/go/vt/topo/k8stopo/file.go b/go/vt/topo/k8stopo/file.go new file mode 100644 index 00000000000..d00fdad4524 --- /dev/null +++ b/go/vt/topo/k8stopo/file.go @@ -0,0 +1,223 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "encoding/base64" + "fmt" + "hash/fnv" + "path/filepath" + "strconv" + "time" + + "golang.org/x/net/context" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/util/retry" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/topo" + vtv1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +// NodeReference contains the data relating to a node +type NodeReference struct { + id string + key string + value string +} + +// ToData converts a nodeReference to the data type used in the VitessTopoNode +func (n *NodeReference) ToData() vtv1beta1.VitessTopoNodeData { + return vtv1beta1.VitessTopoNodeData{ + Key: n.key, + Value: base64.StdEncoding.EncodeToString([]byte(n.value)), + } +} + +func getHash(parent string) string { + hasher := fnv.New64a() + hasher.Write([]byte(parent)) + return strconv.FormatUint(hasher.Sum64(), 10) +} + +func (s *Server) newNodeReference(key string) *NodeReference { + key = filepath.Join(s.root, key) + + node := &NodeReference{ + id: fmt.Sprintf("vt-%s", getHash(key)), + key: key, + } + + return node +} + +func (s *Server) buildFileResource(filePath string, contents []byte) *vtv1beta1.VitessTopoNode { + node := s.newNodeReference(filePath) + + // create data + node.value = string(contents) + + // Create "file" object + return &vtv1beta1.VitessTopoNode{ + ObjectMeta: metav1.ObjectMeta{ + Name: node.id, + Namespace: s.namespace, + }, + Data: node.ToData(), + } +} + +// Create is part of the topo.Conn interface. +func (s *Server) Create(ctx context.Context, filePath string, contents []byte) (topo.Version, error) { + log.V(7).Infof("Create at '%s' Contents: '%s'", filePath, string(contents)) + + resource := s.buildFileResource(filePath, contents) + + final, err := s.resourceClient.Create(resource) + if err != nil { + return nil, convertError(err, filePath) + } + + // Update the internal cache + err = s.memberIndexer.Update(final) + if err != nil { + return nil, convertError(err, filePath) + } + + return KubernetesVersion(final.GetResourceVersion()), nil +} + +// Update is part of the topo.Conn interface. +func (s *Server) Update(ctx context.Context, filePath string, contents []byte, version topo.Version) (topo.Version, error) { + log.V(7).Infof("Update at '%s' Contents: '%s'", filePath, string(contents)) + + resource := s.buildFileResource(filePath, contents) + + var finalVersion KubernetesVersion + + err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + result, err := s.resourceClient.Get(resource.Name, metav1.GetOptions{}) + if err != nil && errors.IsNotFound(err) && version == nil { + // Update should create objects when the version is nil and the object is not found + createdVersion, err := s.Create(ctx, filePath, contents) + if err != nil { + return err + } + finalVersion = KubernetesVersion(createdVersion.String()) + return nil + } + + // If a non-nil version is given to update, fail on mismatched version + if version != nil && KubernetesVersion(result.GetResourceVersion()) != version { + return topo.NewError(topo.BadVersion, filePath) + } + + // set new contents + result.Data.Value = resource.Data.Value + + // get result or err + final, err := s.resourceClient.Update(result) + if err != nil { + return convertError(err, filePath) + } + + // Update the internal cache + err = s.memberIndexer.Update(final) + if err != nil { + return convertError(err, filePath) + } + + finalVersion = KubernetesVersion(final.GetResourceVersion()) + + return nil + }) + if err != nil { + return nil, err + } + + return finalVersion, nil +} + +// Get is part of the topo.Conn interface. +func (s *Server) Get(ctx context.Context, filePath string) ([]byte, topo.Version, error) { + log.V(7).Infof("Get at '%s'", filePath) + + node := s.newNodeReference(filePath) + + result, err := s.resourceClient.Get(node.id, metav1.GetOptions{}) + if err != nil { + return []byte{}, nil, convertError(err, filePath) + } + + out, err := base64.StdEncoding.DecodeString(result.Data.Value) + if err != nil { + return []byte{}, nil, convertError(fmt.Errorf("unable to decode object contents"), filePath) + } + + return out, KubernetesVersion(result.GetResourceVersion()), nil +} + +// Delete is part of the topo.Conn interface. +func (s *Server) Delete(ctx context.Context, filePath string, version topo.Version) error { + log.V(7).Infof("Delete at '%s'", filePath) + + node := s.newNodeReference(filePath) + + // Check version before delete + current, err := s.resourceClient.Get(node.id, metav1.GetOptions{}) + if err != nil { + return convertError(err, filePath) + } + if version != nil { + if KubernetesVersion(current.GetResourceVersion()) != version { + return topo.NewError(topo.BadVersion, filePath) + } + } + + err = s.resourceClient.Delete(node.id, &metav1.DeleteOptions{}) + if err != nil { + return convertError(err, filePath) + } + + // Wait for one of the following conditions + // 1. Context is cancelled + // 2. The object is no longer in the cache + // 3. The object in the cache has a new uid (was deleted but recreated since we last checked) + for { + select { + case <-ctx.Done(): + return convertError(ctx.Err(), filePath) + case <-time.After(50 * time.Millisecond): + } + + obj, ok, err := s.memberIndexer.Get(current) + if err != nil { // error getting from cache + return convertError(err, filePath) + } + if !ok { // deleted from cache + break + } + cached := obj.(*vtv1beta1.VitessTopoNode) + if cached.GetUID() != current.GetUID() { + break // deleted and recreated + } + } + + return nil +} diff --git a/go/vt/topo/k8stopo/lock.go b/go/vt/topo/k8stopo/lock.go new file mode 100644 index 00000000000..7b551fa6774 --- /dev/null +++ b/go/vt/topo/k8stopo/lock.go @@ -0,0 +1,112 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "time" + + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "vitess.io/vitess/go/vt/topo" + vtv1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +// kubernetesLockDescriptor implements topo.LockDescriptor. +type kubernetesLockDescriptor struct { + s *Server + leaseID string + leasePath string +} + +// Lock is part of the topo.Conn interface. +func (s *Server) Lock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) { + return s.lock(ctx, dirPath, contents, false) +} + +// lock is used by both Lock() and master election. +// it blocks until the lock is taken, interrupted, or times out +func (s *Server) lock(ctx context.Context, nodePath, contents string, createMissing bool) (topo.LockDescriptor, error) { + // Satisfy the topo.Conn interface + if !createMissing { + // Per the topo.Conn interface: + // "Returns ErrNoNode if the directory doesn't exist (meaning + // there is no existing file under that directory)." + if _, err := s.ListDir(ctx, nodePath, false); err != nil { + return nil, convertError(err, nodePath) + } + } + + resource := s.buildFileResource(nodePath, []byte(contents)) + + // mark locks as ephemeral + resource.Data.Ephemeral = true + + var final *vtv1beta1.VitessTopoNode + var err error + + for { + // Try and and create the resource. The kube api will handle the actual atomic lock creation + final, err = s.resourceClient.Create(resource) + if errors.IsAlreadyExists(err) { + select { + case <-time.After(10 * time.Millisecond): + continue // retry + case <-ctx.Done(): + return nil, convertError(ctx.Err(), nodePath) + } + } else if err != nil { + return nil, convertError(err, nodePath) + } + + break + } + + // Update the internal cache + err = s.memberIndexer.Update(final) + if err != nil { + return nil, convertError(err, nodePath) + } + + return &kubernetesLockDescriptor{ + s: s, + leaseID: resource.Name, + leasePath: resource.Data.Key, + }, nil +} + +// Check is part of the topo.LockDescriptor interface. +func (ld *kubernetesLockDescriptor) Check(ctx context.Context) error { + // Get the object and ensure the leaseid + _, err := ld.s.resourceClient.Get(ld.leaseID, metav1.GetOptions{}) // TODO namespacing + if err != nil { + return convertError(err, ld.leasePath) + + } + + return nil +} + +// Unlock is part of the topo.LockDescriptor interface. +func (ld *kubernetesLockDescriptor) Unlock(ctx context.Context) error { + err := ld.s.resourceClient.Delete(ld.leaseID, &metav1.DeleteOptions{}) // TODO namespacing + if err != nil { + return convertError(err, ld.leasePath) + } + return nil +} diff --git a/go/vt/topo/k8stopo/server.go b/go/vt/topo/k8stopo/server.go new file mode 100644 index 00000000000..50b81465c71 --- /dev/null +++ b/go/vt/topo/k8stopo/server.go @@ -0,0 +1,234 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +Package k8stopo implements topo.Server with the Kubernetes API as the backend. + +We expect the following behavior from the kubernetes client library: + + - TODO + +We follow these conventions within this package: + + - TODO +*/ +package k8stopo + +import ( + "flag" + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "k8s.io/apimachinery/pkg/fields" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/clientcmd" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/topo" + vtv1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" + vtkube "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned" + vttyped "vitess.io/vitess/go/vt/topo/k8stopo/client/clientset/versioned/typed/topo/v1beta1" +) + +var ( + // kubeconfigPath is a string that gives the location of a valid kubeconfig file + kubeconfigPath = flag.String("topo_k8s_kubeconfig", "", "Path to a valid kubeconfig file.") + + // configContext is a string that can be used to override the default context + configContext = flag.String("topo_k8s_context", "", "The kubeconfig context to use, overrides the 'current-context' from the config") + + // configNamespace is a string that can be used to override the default namespace for objects + configNamespace = flag.String("topo_k8s_namespace", "", "The kubernetes namespace to use for all objects. Default comes from the context or in-cluster config") +) + +// Factory is the Kubernetes topo.Factory implementation. +type Factory struct{} + +// HasGlobalReadOnlyCell is part of the topo.Factory interface. +func (f Factory) HasGlobalReadOnlyCell(serverAddr, root string) bool { + return false +} + +// Create is part of the topo.Factory interface. +func (f Factory) Create(cell, serverAddr, root string) (topo.Conn, error) { + return NewServer(serverAddr, root) +} + +// Server is the implementation of topo.Server for Kubernetes. +type Server struct { + // kubeClient is the entire kubernetes interface + kubeClient kubernetes.Interface + + // vtKubeClient is the client for vitess api types + vtKubeClient vtkube.Interface + + // resource is a scoped-down kubernetes.Interface used for convenience + resourceClient vttyped.VitessTopoNodeInterface + + // stopChan is used to tell the client-go informers to quit + stopChan chan struct{} + + // memberInformer is the controller that syncronized the cache of data + memberInformer cache.Controller + + // memberIndexer is the cache of tree data + memberIndexer cache.Indexer + + // namespace is the Kubernetes namespace to be used for all resources + namespace string + + // root is the root path for this client. + // used for resource prefixing + root string +} + +// Close implements topo.Server.Close. +func (s *Server) Close() { + close(s.stopChan) +} + +func getKeyParents(key string) []string { + parents := []string{""} + parent := []string{} + for _, segment := range strings.Split(filepath.Dir(key), "/") { + parent = append(parent, segment) + parents = append(parents, strings.Join(parent, "/")) + } + return parents +} + +func indexByParent(obj interface{}) ([]string, error) { + return getKeyParents(obj.(*vtv1beta1.VitessTopoNode).Data.Key), nil +} + +// syncTree starts and syncs the member objects that form the directory "tree" +func (s *Server) syncTree() error { + // Create the informer / indexer + restClient := s.vtKubeClient.TopoV1beta1().RESTClient() + listwatch := cache.NewListWatchFromClient(restClient, "vitesstoponodes", s.namespace, fields.Everything()) + + // set up index funcs + indexers := cache.Indexers{} + indexers["by_parent"] = indexByParent + + s.memberIndexer, s.memberInformer = cache.NewIndexerInformer(listwatch, &vtv1beta1.VitessTopoNode{}, 0, + cache.ResourceEventHandlerFuncs{}, indexers) + + // Start indexer + go s.memberInformer.Run(s.stopChan) + + // Wait for sync + log.Info("Waiting for Kubernetes topo cache sync") + if !cache.WaitForCacheSync(s.stopChan, s.memberInformer.HasSynced) { + return fmt.Errorf("timed out waiting for caches to sync") + } + log.Info("Kubernetes topo cache sync completed") + + return nil +} + +// NewServer returns a new k8stopo.Server. +func NewServer(_, root string) (*Server, error) { + log.Info("Creating new Kubernetes topo server with root: ", root) + + var config *rest.Config + var err error + namespace := "default" + + if *kubeconfigPath == "" { + log.Info("Creating new in-cluster Kubernetes config") + + config, err = rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("error getting Kubernetes in-cluster client config: %s", err) + } + + // When running in the cluster, use the namespace file to detect the current namespace + nsBytes, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") + if err != nil { + return nil, err + } + namespace = string(nsBytes) + } else { + log.Info("Creating new Kubernetes config from kubeconfig", *kubeconfigPath) + + configOverrides := &clientcmd.ConfigOverrides{} + + // respect the context flag + if *configContext != "" { + configOverrides.CurrentContext = *configContext + log.V(7).Info("Overriding Kubernetes config context with: ", configOverrides.CurrentContext) + } + + configLoader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: *kubeconfigPath}, + configOverrides, + ) + + config, err = configLoader.ClientConfig() + if err != nil { + return nil, fmt.Errorf("error getting Kubernetes client config: %s", err) + } + + // When given a kubeconfig file, use the namespace from the current context + namespace, _, err = configLoader.Namespace() + if err != nil { + return nil, fmt.Errorf("error getting namespace from Kubernetes client config: %s", err) + } + } + + // respect the namespace flag + if *configNamespace != "" { + namespace = *configNamespace + log.V(7).Info("Overriding Kubernetes config namespace with: ", namespace) + } + + // create the kubernetes client + kubeClientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("error creating official Kubernetes client: %s", err) + } + + vtKubeClientset, err := vtkube.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("error creating vitess Kubernetes client: %s", err) + } + + // Create the server + s := &Server{ + namespace: namespace, + kubeClient: kubeClientset, + vtKubeClient: vtKubeClientset, + resourceClient: vtKubeClientset.TopoV1beta1().VitessTopoNodes(namespace), + root: root, + stopChan: make(chan struct{}), + } + + // Sync cache + if err = s.syncTree(); err != nil { + return nil, err + } + + return s, nil +} + +func init() { + topo.RegisterFactory("k8s", Factory{}) +} diff --git a/go/vt/topo/k8stopo/server_test.go b/go/vt/topo/k8stopo/server_test.go new file mode 100644 index 00000000000..3127e1ce2c6 --- /dev/null +++ b/go/vt/topo/k8stopo/server_test.go @@ -0,0 +1,149 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "context" + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "runtime" + "time" + + "testing" + + extensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + kubeyaml "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/tools/clientcmd" + + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/test" +) + +func TestKubernetesTopo(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("k3s not supported on non-linux platforms. Skipping k8stopo integration tests") + } + + // Create a data dir for test data + testDataDir, err := ioutil.TempDir("", "vt-test-k3s") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(testDataDir) // clean up + + // Gen a temp file name for the config + testConfig, err := ioutil.TempFile("", "vt-test-k3s-config") + if err != nil { + t.Fatal(err) + } + testConfigPath := testConfig.Name() + defer os.Remove(testConfigPath) // clean up + + k3sArgs := []string{ + "server", "start", + "--write-kubeconfig=" + testConfigPath, + "--data-dir=" + testDataDir, + "--https-listen-port=6663", + "--disable-agent", "--flannel-backend=none", + "--disable-network-policy", + "--disable-cloud-controller", + "--disable-scheduler", + "--no-deploy=coredns,servicelb,traefik,local-storage,metrics-server", + "--kube-controller-manager-arg=port=10253", + + "--log=/tmp/k3svtlog", + } + + // Start a minimal k3s daemon, and close it after all tests are done. + ctx, killK3s := context.WithCancel(context.Background()) + c := exec.CommandContext(ctx, "k3s", k3sArgs...) + + // Start in the background and kill when tests end + t.Log("Starting k3s") + err = c.Start() + if err != nil { + t.Fatal("Unable to start k3s", err) + } + defer killK3s() + + // Wait for server to be ready + for { + t.Log("Waiting for server to be ready") + time.Sleep(time.Second) + config, err := clientcmd.BuildConfigFromFlags("", testConfigPath) + if err != nil { + continue + } + + // Create the vitesstoponode crd + apiextensionsClientSet, err := apiextensionsclient.NewForConfig(config) + if err != nil { + t.Fatal(err) + } + + crdFile, err := os.Open("./VitessTopoNodes-crd.yaml") + defer crdFile.Close() + if err != nil { + t.Fatal(err) + } + + crd := &extensionsv1.CustomResourceDefinition{} + + kubeyaml.NewYAMLOrJSONDecoder(crdFile, 2048).Decode(crd) + + _, err = apiextensionsClientSet.ApiextensionsV1().CustomResourceDefinitions().Create(crd) + if err != nil { + t.Fatal(err) + } + + break + } + + serverAddr := "default" + flag.Set("topo_k8s_kubeconfig", testConfigPath) + + // Run the test suite. + testIndex := 0 + test.TopoServerTestSuite(t, func() *topo.Server { + // Each test will use its own sub-directories. + // The directories will be created when used the first time. + testRoot := fmt.Sprintf("/test-%v", testIndex) + testIndex++ + + globalRoot := path.Join(testRoot, topo.GlobalCell) + cellRoot := path.Join(testRoot, test.LocalCellName) + + ts, err := topo.OpenServer("k8s", serverAddr, globalRoot) + if err != nil { + t.Fatalf("OpenServer() failed: %v", err) + } + if err := ts.CreateCellInfo(context.Background(), test.LocalCellName, &topodatapb.CellInfo{ + ServerAddress: serverAddr, + Root: cellRoot, + }); err != nil { + t.Fatalf("CreateCellInfo() failed: %v", err) + } + + return ts + }) +} diff --git a/go/vt/topo/k8stopo/version.go b/go/vt/topo/k8stopo/version.go new file mode 100644 index 00000000000..9f8eeb79915 --- /dev/null +++ b/go/vt/topo/k8stopo/version.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "vitess.io/vitess/go/vt/topo" +) + +// KubernetesVersion is Kubernetes's idea of a version. +// It implements topo.Version. +type KubernetesVersion string + +// String is part of the topo.Version interface. +func (v KubernetesVersion) String() string { + return string(v) +} + +// VersionFromInt is used by old-style functions to create a proper +// Version: if version is -1, returns nil. Otherwise returns the +// KubernetesVersion object. +func VersionFromInt(version int64) topo.Version { + if version == -1 { + return nil + } + return KubernetesVersion(version) +} diff --git a/go/vt/topo/k8stopo/watch.go b/go/vt/topo/k8stopo/watch.go new file mode 100644 index 00000000000..a4a21884ef8 --- /dev/null +++ b/go/vt/topo/k8stopo/watch.go @@ -0,0 +1,121 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package k8stopo + +import ( + "encoding/base64" + + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/client-go/tools/cache" + + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/topo" + vtv1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1" +) + +// Watch is part of the topo.Conn interface. +func (s *Server) Watch(ctx context.Context, filePath string) (*topo.WatchData, <-chan *topo.WatchData, topo.CancelFunc) { + log.Info("Starting Kubernetes topo Watch on ", filePath) + + current := &topo.WatchData{} + + // get current + contents, ver, err := s.Get(ctx, filePath) + if err != nil { + // Per the topo.Conn interface: + // "If the initial read fails, or the file doesn't + // exist, current.Err is set, and 'changes'/'cancel' are nil." + current.Err = err + return current, nil, nil + } + current.Contents = contents + current.Version = ver + + // Create a context, will be used to cancel the watch. + watchCtx, watchCancel := context.WithCancel(context.Background()) + + // Create the changes channel + changes := make(chan *topo.WatchData, 10) + + // Create a signal channel for non-interrupt shutdowns + gracefulShutdown := make(chan struct{}) + + // Create the informer / indexer to watch the single resource + restClient := s.vtKubeClient.TopoV1beta1().RESTClient() + listwatch := cache.NewListWatchFromClient(restClient, "vitesstoponodes", s.namespace, fields.OneTermEqualSelector("metadata.name", s.buildFileResource(filePath, []byte{}).Name)) + + // set up index funcs + indexers := cache.Indexers{} + indexers["by_parent"] = indexByParent + + _, memberInformer := cache.NewIndexerInformer(listwatch, &vtv1beta1.VitessTopoNode{}, 0, + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + vtn := obj.(*vtv1beta1.VitessTopoNode) + out, err := base64.StdEncoding.DecodeString(vtn.Data.Value) + if err != nil { + changes <- &topo.WatchData{Err: err} + close(gracefulShutdown) + } else { + changes <- &topo.WatchData{ + Contents: out, + Version: KubernetesVersion(vtn.GetResourceVersion()), + } + } + }, + UpdateFunc: func(oldObj, newObj interface{}) { + vtn := newObj.(*vtv1beta1.VitessTopoNode) + out, err := base64.StdEncoding.DecodeString(vtn.Data.Value) + if err != nil { + changes <- &topo.WatchData{Err: err} + close(gracefulShutdown) + } else { + changes <- &topo.WatchData{ + Contents: out, + Version: KubernetesVersion(vtn.GetResourceVersion()), + } + } + }, + DeleteFunc: func(obj interface{}) { + vtn := obj.(*vtv1beta1.VitessTopoNode) + changes <- &topo.WatchData{Err: topo.NewError(topo.NoNode, vtn.Name)} + close(gracefulShutdown) + }, + }, indexers) + + // create control chan for informer and start it + informerChan := make(chan struct{}) + go memberInformer.Run(informerChan) + + // Handle interrupts + go closeOnDone(watchCtx, filePath, informerChan, gracefulShutdown, changes) + + return current, changes, topo.CancelFunc(watchCancel) +} + +func closeOnDone(ctx context.Context, filePath string, informerChan chan struct{}, gracefulShutdown chan struct{}, changes chan *topo.WatchData) { + select { + case <-ctx.Done(): + if err := ctx.Err(); err != nil && err == context.Canceled { + changes <- &topo.WatchData{Err: topo.NewError(topo.Interrupted, filePath)} + } + case <-gracefulShutdown: + } + close(informerChan) + close(changes) +} diff --git a/go/vt/topo/server.go b/go/vt/topo/server.go index 4e913d431de..6da787dc251 100644 --- a/go/vt/topo/server.go +++ b/go/vt/topo/server.go @@ -215,7 +215,7 @@ func OpenServer(implementation, serverAddress, root string) (*Server, error) { // Open returns a Server using the command line parameter flags // for implementation, address and root. It log.Exits out if an error occurs. func Open() *Server { - if *topoGlobalServerAddress == "" { + if *topoGlobalServerAddress == "" && *topoImplementation != "k8s" { log.Exitf("topo_global_server_address must be configured") } ts, err := OpenServer(*topoImplementation, *topoGlobalServerAddress, *topoGlobalRoot) diff --git a/go/vt/vtctl/plugin_kubernetestopo.go b/go/vt/vtctl/plugin_kubernetestopo.go new file mode 100644 index 00000000000..271633fc2bc --- /dev/null +++ b/go/vt/vtctl/plugin_kubernetestopo.go @@ -0,0 +1,23 @@ +/* +Copyright 2020 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreedto in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtctl + +import ( + // Imports k8stopo to register the kubernetes implementation of + // TopoServer. + _ "vitess.io/vitess/go/vt/topo/k8stopo" +) From 72f14234066067683e8abc2123544063076bef70 Mon Sep 17 00:00:00 2001 From: Carson Anderson Date: Wed, 11 Mar 2020 16:04:27 -0600 Subject: [PATCH 286/825] Test the k8s and etcd topos in the local-example Signed-off-by: Carson Anderson --- .github/workflows/local_example.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/local_example.yml b/.github/workflows/local_example.yml index c702a9684aa..a1b2de262ca 100644 --- a/.github/workflows/local_example.yml +++ b/.github/workflows/local_example.yml @@ -3,11 +3,12 @@ on: [push, pull_request] jobs: build: - name: Local Example on ${{ matrix.os }} + name: Local example using ${{ matrix.topo }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] + topo: [etcd,k8s] steps: @@ -43,6 +44,7 @@ jobs: - name: local_example run: | + export TOPO=${{matrix.topo}} if [ ${{matrix.os}} = "macos-latest" ]; then export PATH="/usr/local/opt/mysql@5.7/bin:$PATH" fi From aaaea24732568949f9713b6b61fa3135571e24ce Mon Sep 17 00:00:00 2001 From: JohnnyThree Date: Mon, 16 Mar 2020 10:37:06 +0800 Subject: [PATCH 287/825] 1.set grpc_keepalive_time to 10 seconds 2.set grpc_keepalive_timeout to 10 seconds These settings will help to avoid the situation which query will hang there when pod or Bare metal is broken. Signed-off-by: JohnnyThree --- go/vt/grpcclient/client.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/go/vt/grpcclient/client.go b/go/vt/grpcclient/client.go index 2dae863a9a9..f2fd3e40ddf 100644 --- a/go/vt/grpcclient/client.go +++ b/go/vt/grpcclient/client.go @@ -20,6 +20,7 @@ package grpcclient import ( "flag" + "time" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" @@ -35,8 +36,8 @@ import ( ) var ( - keepaliveTime = flag.Duration("grpc_keepalive_time", 0, "After a duration of this time if the client doesn't see any activity it pings the server to see if the transport is still alive.") - keepaliveTimeout = flag.Duration("grpc_keepalive_timeout", 0, "After having pinged for keepalive check, the client waits for a duration of Timeout and if no activity is seen even after that the connection is closed.") + keepaliveTime = flag.Duration("grpc_keepalive_time", 10*time.Second, "After a duration of this time if the client doesn't see any activity it pings the server to see if the transport is still alive.") + keepaliveTimeout = flag.Duration("grpc_keepalive_timeout", 10*time.Second, "After having pinged for keepalive check, the client waits for a duration of Timeout and if no activity is seen even after that the connection is closed.") initialConnWindowSize = flag.Int("grpc_initial_conn_window_size", 0, "grpc initial connection window size") initialWindowSize = flag.Int("grpc_initial_window_size", 0, "grpc initial window size") ) From 6ed2db45e70102ffd4cd2d3c35a657d86154de4e Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 16 Mar 2020 05:50:04 +0100 Subject: [PATCH 288/825] Fixed proto defition It somehow got mangled when merging, and one line was lost. Signed-off-by: Andres Taylor --- proto/vtgate.proto | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proto/vtgate.proto b/proto/vtgate.proto index a8f36301dfb..636d0130c8b 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -106,8 +106,11 @@ message Session { // post_sessions contains sessions that have to be committed last. repeated ShardSession post_sessions = 10; - // last_insert_id keeps track of the last seen insert_id for this session + // last_insert_id keeps track of the last seen insert_id for this session uint64 last_insert_id = 11; + + // found_rows keeps track of how many rows the last query returned + uint64 rows_affected = 12; } // ExecuteRequest is the payload to Execute. From da21fe824c0f75948ed50476de535b575eec362b Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 16 Mar 2020 08:20:23 +0100 Subject: [PATCH 289/825] Really fix proto Signed-off-by: Andres Taylor --- go/vt/proto/vtgate/vtgate.pb.go | 270 ++++++++++++++++---------------- proto/vtgate.proto | 2 +- 2 files changed, 140 insertions(+), 132 deletions(-) diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index 61ba91e777b..c31bdbbb0d0 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -139,7 +139,7 @@ type Session struct { // last_insert_id keeps track of the last seen insert_id for this session LastInsertId uint64 `protobuf:"varint,11,opt,name=last_insert_id,json=lastInsertId,proto3" json:"last_insert_id,omitempty"` // found_rows keeps track of how many rows the last query returned - FoundRows uint64 `protobuf:"varint,11,opt,name=found_rows,json=foundRows,proto3" json:"found_rows,omitempty"` + FoundRows uint64 `protobuf:"varint,12,opt,name=found_rows,json=foundRows,proto3" json:"found_rows,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -247,6 +247,13 @@ func (m *Session) GetLastInsertId() uint64 { return 0 } +func (m *Session) GetFoundRows() uint64 { + if m != nil { + return m.FoundRows + } + return 0 +} + type Session_ShardSession struct { Target *query.Target `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` TransactionId int64 `protobuf:"varint,2,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` @@ -3697,135 +3704,136 @@ func init() { func init() { proto.RegisterFile("vtgate.proto", fileDescriptor_aab96496ceaf1ebb) } var fileDescriptor_aab96496ceaf1ebb = []byte{ - // 2067 bytes of a gzipped FileDescriptorProto + // 2086 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xcd, 0x8f, 0x23, 0x47, - 0x15, 0x4f, 0x77, 0xfb, 0xf3, 0xf9, 0x73, 0x6b, 0xbc, 0xbb, 0x8e, 0x33, 0xec, 0x38, 0x9d, 0x8c, - 0xd6, 0xd9, 0xac, 0x3c, 0xc4, 0x81, 0x80, 0x50, 0x50, 0x98, 0xf1, 0x4e, 0x56, 0x56, 0x76, 0x3e, - 0x28, 0x7b, 0x67, 0x01, 0x11, 0xb5, 0x7a, 0xec, 0xc2, 0xdb, 0x8c, 0xdd, 0xed, 0x74, 0x95, 0xbd, - 0x0c, 0x07, 0x94, 0x0b, 0xe7, 0x88, 0x03, 0x12, 0x8a, 0x90, 0x10, 0x12, 0x12, 0x27, 0xae, 0x48, - 0xc0, 0x85, 0x1b, 0x12, 0x17, 0xc4, 0x89, 0x3b, 0xff, 0x00, 0x12, 0x7f, 0x41, 0xd4, 0x55, 0xd5, - 0x1f, 0xf6, 0x7c, 0x79, 0x3c, 0x3b, 0x23, 0xef, 0xc5, 0xea, 0xaa, 0xf7, 0xea, 0xd5, 0xab, 0xdf, - 0xfb, 0xd5, 0xab, 0xd7, 0xe5, 0x86, 0xec, 0x84, 0xf5, 0x4d, 0x46, 0xea, 0x23, 0xd7, 0x61, 0x0e, - 0x4a, 0x88, 0x56, 0xa5, 0x78, 0x68, 0xd9, 0x03, 0xa7, 0xdf, 0x33, 0x99, 0x29, 0x24, 0x95, 0xcc, - 0x67, 0x63, 0xe2, 0x1e, 0xcb, 0x46, 0x9e, 0x39, 0x23, 0x27, 0x2a, 0x9c, 0x30, 0x77, 0xd4, 0x15, - 0x0d, 0xfd, 0x97, 0x71, 0x48, 0xb6, 0x09, 0xa5, 0x96, 0x63, 0xa3, 0x75, 0xc8, 0x5b, 0xb6, 0xc1, - 0x5c, 0xd3, 0xa6, 0x66, 0x97, 0x59, 0x8e, 0x5d, 0x56, 0xaa, 0x4a, 0x2d, 0x85, 0x73, 0x96, 0xdd, - 0x09, 0x3b, 0x51, 0x13, 0xf2, 0xf4, 0xb9, 0xe9, 0xf6, 0x0c, 0x2a, 0xc6, 0xd1, 0xb2, 0x5a, 0xd5, - 0x6a, 0x99, 0xc6, 0x6a, 0x5d, 0x7a, 0x27, 0xed, 0xd5, 0xdb, 0x9e, 0x96, 0x6c, 0xe0, 0x1c, 0x8d, - 0xb4, 0x28, 0x7a, 0x03, 0xd2, 0xd4, 0xb2, 0xfb, 0x03, 0x62, 0xf4, 0x0e, 0xcb, 0x1a, 0x9f, 0x26, - 0x25, 0x3a, 0x1e, 0x1d, 0xa2, 0x7b, 0x00, 0xe6, 0x98, 0x39, 0x5d, 0x67, 0x38, 0xb4, 0x58, 0x39, - 0xc6, 0xa5, 0x91, 0x1e, 0xf4, 0x16, 0xe4, 0x98, 0xe9, 0xf6, 0x09, 0x33, 0x28, 0x73, 0x2d, 0xbb, - 0x5f, 0x8e, 0x57, 0x95, 0x5a, 0x1a, 0x67, 0x45, 0x67, 0x9b, 0xf7, 0xa1, 0x0d, 0x48, 0x3a, 0x23, - 0xc6, 0xfd, 0x4b, 0x54, 0x95, 0x5a, 0xa6, 0x71, 0xbb, 0x2e, 0x50, 0xd9, 0xfe, 0x19, 0xe9, 0x8e, - 0x19, 0xd9, 0x13, 0x42, 0xec, 0x6b, 0xa1, 0x2d, 0x28, 0x46, 0xd6, 0x6e, 0x0c, 0x9d, 0x1e, 0x29, - 0x27, 0xab, 0x4a, 0x2d, 0xdf, 0xb8, 0xeb, 0xaf, 0x2c, 0x02, 0xc3, 0x8e, 0xd3, 0x23, 0xb8, 0xc0, - 0xa6, 0x3b, 0xd0, 0x06, 0xa4, 0x5e, 0x98, 0xae, 0x6d, 0xd9, 0x7d, 0x5a, 0x4e, 0x71, 0x54, 0x56, - 0xe4, 0xac, 0xdf, 0xf7, 0x7e, 0x9f, 0x09, 0x19, 0x0e, 0x94, 0xd0, 0x47, 0x90, 0x1d, 0xb9, 0x24, - 0x84, 0x32, 0x3d, 0x07, 0x94, 0x99, 0x91, 0x4b, 0x02, 0x20, 0x37, 0x21, 0x37, 0x72, 0x28, 0x0b, - 0x2d, 0xc0, 0x1c, 0x16, 0xb2, 0xde, 0x90, 0xc0, 0xc4, 0xdb, 0x90, 0x1f, 0x98, 0x94, 0x19, 0x96, - 0x4d, 0x89, 0xcb, 0x0c, 0xab, 0x57, 0xce, 0x54, 0x95, 0x5a, 0x0c, 0x67, 0xbd, 0xde, 0x16, 0xef, - 0x6c, 0xf5, 0x2a, 0x3f, 0x86, 0x6c, 0xd4, 0x06, 0x5a, 0x87, 0x84, 0xc0, 0x9b, 0xb3, 0x24, 0xd3, - 0xc8, 0xc9, 0x85, 0x76, 0x78, 0x27, 0x96, 0x42, 0x8f, 0x54, 0x51, 0x54, 0xad, 0x5e, 0x59, 0xad, - 0x2a, 0x35, 0x0d, 0xe7, 0x22, 0xbd, 0xad, 0x9e, 0xfe, 0x2f, 0x15, 0xf2, 0x32, 0x30, 0x98, 0x7c, - 0x36, 0x26, 0x94, 0xa1, 0x87, 0x90, 0xee, 0x9a, 0x83, 0x01, 0x71, 0xbd, 0x41, 0x62, 0x8e, 0x42, - 0x5d, 0x70, 0xb7, 0xc9, 0xfb, 0x5b, 0x8f, 0x70, 0x4a, 0x68, 0xb4, 0x7a, 0xe8, 0x1d, 0x48, 0x4a, - 0x08, 0xf8, 0x04, 0x42, 0x37, 0x8a, 0x00, 0xf6, 0xe5, 0xe8, 0x3e, 0xc4, 0xb9, 0xab, 0x9c, 0x77, - 0x99, 0xc6, 0x2d, 0xe9, 0xf8, 0x96, 0x33, 0xb6, 0x7b, 0x3c, 0x4c, 0x58, 0xc8, 0xd1, 0x37, 0x21, - 0xc3, 0xcc, 0xc3, 0x01, 0x61, 0x06, 0x3b, 0x1e, 0x11, 0x4e, 0xc4, 0x7c, 0xa3, 0x54, 0x0f, 0xf6, - 0x53, 0x87, 0x0b, 0x3b, 0xc7, 0x23, 0x82, 0x81, 0x05, 0xcf, 0xe8, 0x21, 0x20, 0xdb, 0xf1, 0xe0, - 0x9c, 0xda, 0x4b, 0x71, 0x4e, 0xe3, 0xa2, 0xed, 0xb0, 0xd6, 0xd4, 0x76, 0x5a, 0x87, 0xfc, 0x11, - 0x39, 0xa6, 0x23, 0xb3, 0x4b, 0x0c, 0xbe, 0x47, 0x38, 0x5d, 0xd3, 0x38, 0xe7, 0xf7, 0x72, 0xd4, - 0xa3, 0x74, 0x4e, 0xce, 0x43, 0x67, 0xfd, 0x0b, 0x05, 0x0a, 0x01, 0xa2, 0x74, 0xe4, 0xd8, 0x94, - 0xa0, 0x75, 0x88, 0x13, 0xd7, 0x75, 0xdc, 0x19, 0x38, 0xf1, 0x7e, 0x73, 0xdb, 0xeb, 0xc6, 0x42, - 0x7a, 0x19, 0x2c, 0x1f, 0x40, 0xc2, 0x25, 0x74, 0x3c, 0x60, 0x12, 0x4c, 0x14, 0xa5, 0x3b, 0xe6, - 0x12, 0x2c, 0x35, 0xf4, 0xff, 0xaa, 0x50, 0x92, 0x1e, 0xf1, 0x35, 0xd1, 0xe5, 0x89, 0x74, 0x05, - 0x52, 0x3e, 0xdc, 0x3c, 0xcc, 0x69, 0x1c, 0xb4, 0xd1, 0x1d, 0x48, 0xf0, 0xb8, 0xd0, 0x72, 0xbc, - 0xaa, 0xd5, 0xd2, 0x58, 0xb6, 0x66, 0xd9, 0x91, 0xb8, 0x12, 0x3b, 0x92, 0x67, 0xb0, 0x23, 0x12, - 0xf6, 0xd4, 0x5c, 0x61, 0xff, 0xb5, 0x02, 0xb7, 0x67, 0x40, 0x5e, 0x8a, 0xe0, 0xff, 0x5f, 0x85, - 0xd7, 0xa5, 0x5f, 0x9f, 0x48, 0x64, 0x5b, 0xaf, 0x0a, 0x03, 0xde, 0x84, 0x6c, 0xb0, 0x45, 0x2d, - 0xc9, 0x83, 0x2c, 0xce, 0x1c, 0x85, 0xeb, 0x58, 0x52, 0x32, 0x7c, 0xa9, 0x40, 0xe5, 0x34, 0xd0, - 0x97, 0x82, 0x11, 0x9f, 0x6b, 0x70, 0x37, 0x74, 0x0e, 0x9b, 0x76, 0x9f, 0xbc, 0x22, 0x7c, 0x78, - 0x0f, 0xe0, 0x88, 0x1c, 0x1b, 0x2e, 0x77, 0x99, 0xb3, 0xc1, 0x5b, 0x69, 0x10, 0x6b, 0x7f, 0x35, - 0x38, 0x7d, 0xe4, 0xaf, 0x6b, 0x49, 0xf9, 0xf1, 0x1b, 0x05, 0xca, 0x27, 0x43, 0xb0, 0x14, 0xec, - 0xf8, 0x4b, 0x2c, 0x60, 0xc7, 0xb6, 0xcd, 0x2c, 0x76, 0xfc, 0xca, 0x64, 0x8b, 0x87, 0x80, 0x08, - 0xf7, 0xd8, 0xe8, 0x3a, 0x83, 0xf1, 0xd0, 0x36, 0x6c, 0x73, 0x48, 0x64, 0x89, 0x5a, 0x14, 0x92, - 0x26, 0x17, 0xec, 0x9a, 0x43, 0x82, 0x7e, 0x00, 0x2b, 0x52, 0x7b, 0x2a, 0xc5, 0x24, 0x38, 0xa9, - 0x6a, 0xbe, 0xa7, 0x67, 0x20, 0x51, 0xf7, 0x3b, 0xf0, 0x2d, 0x61, 0xe4, 0x93, 0xb3, 0x53, 0x52, - 0xf2, 0x4a, 0x94, 0x4b, 0x5d, 0x4c, 0xb9, 0xf4, 0x3c, 0x94, 0xab, 0x1c, 0x42, 0xca, 0x77, 0x1a, - 0xad, 0x41, 0x8c, 0xbb, 0xa6, 0x70, 0xd7, 0x32, 0x7e, 0x01, 0xe9, 0x79, 0xc4, 0x05, 0xa8, 0x04, - 0xf1, 0x89, 0x39, 0x18, 0x13, 0x1e, 0xb8, 0x2c, 0x16, 0x0d, 0xb4, 0x06, 0x99, 0x08, 0x56, 0x3c, - 0x56, 0x59, 0x0c, 0x61, 0x36, 0x8e, 0xd2, 0x3a, 0x82, 0xd8, 0x52, 0xd0, 0xfa, 0xdf, 0x2a, 0xac, - 0x48, 0xd7, 0xb6, 0x4c, 0xd6, 0x7d, 0x7e, 0xed, 0x94, 0x7e, 0x17, 0x92, 0x9e, 0x37, 0x16, 0xa1, - 0x65, 0x8d, 0x73, 0xea, 0x14, 0x52, 0xfb, 0x1a, 0x8b, 0x16, 0xbc, 0xeb, 0x90, 0x37, 0xe9, 0x29, - 0xc5, 0x6e, 0xce, 0xa4, 0x37, 0x51, 0xe9, 0x7e, 0xa9, 0x04, 0x75, 0xa5, 0xc4, 0xf4, 0xda, 0x42, - 0xfd, 0x75, 0x48, 0x8a, 0x40, 0xfa, 0x68, 0xde, 0x91, 0xbe, 0x89, 0x30, 0x3f, 0xb3, 0xd8, 0x73, - 0x61, 0xda, 0x57, 0xd3, 0x6d, 0x28, 0x70, 0xa4, 0xf9, 0xda, 0x38, 0xdc, 0x61, 0x96, 0x51, 0x2e, - 0x91, 0x65, 0xd4, 0x33, 0xab, 0x52, 0x2d, 0x5a, 0x95, 0xea, 0x7f, 0x0e, 0xeb, 0x2c, 0x0e, 0xc6, - 0x0d, 0x55, 0xda, 0xef, 0xcd, 0xd2, 0x2c, 0x78, 0x67, 0x9e, 0x59, 0xfd, 0x4d, 0x91, 0xed, 0xb2, - 0xaf, 0xff, 0xfa, 0x6f, 0xc3, 0x5a, 0x69, 0x0a, 0xb8, 0x6b, 0xe3, 0xd2, 0xc3, 0x59, 0x2e, 0x9d, - 0x96, 0x37, 0x02, 0x1e, 0xfd, 0x02, 0x4a, 0x1c, 0xc9, 0x30, 0xc3, 0xbf, 0x44, 0x32, 0xcd, 0x16, - 0xb8, 0xda, 0x89, 0x02, 0x57, 0xff, 0xbb, 0x0a, 0xf7, 0xa2, 0xf0, 0xdc, 0x64, 0x11, 0xff, 0xc1, - 0x2c, 0xb9, 0x56, 0xa7, 0xc8, 0x35, 0x03, 0xc9, 0xd2, 0x32, 0xec, 0xf7, 0x0a, 0xac, 0x9d, 0x09, - 0xe1, 0x92, 0xd0, 0xec, 0x8f, 0x2a, 0x94, 0xda, 0xcc, 0x25, 0xe6, 0xf0, 0x4a, 0xb7, 0x31, 0x01, - 0x2b, 0xd5, 0xcb, 0x5d, 0xb1, 0x68, 0xf3, 0x87, 0x68, 0xe6, 0x28, 0x89, 0x5d, 0x70, 0x94, 0xc4, - 0xe7, 0xba, 0x03, 0x8c, 0xe0, 0x9a, 0x38, 0x1f, 0x57, 0xbd, 0x09, 0xb7, 0x67, 0x80, 0x92, 0x21, - 0x0c, 0xcb, 0x01, 0xe5, 0xc2, 0x72, 0xe0, 0x0b, 0x15, 0x2a, 0x53, 0x56, 0xae, 0x92, 0xae, 0xe7, - 0x06, 0x3d, 0x9a, 0x0a, 0xb4, 0x33, 0xcf, 0x95, 0xd8, 0x79, 0xb7, 0x1d, 0xf1, 0x39, 0x03, 0x75, - 0xe9, 0x4d, 0xd2, 0x82, 0x37, 0x4e, 0x05, 0x64, 0x01, 0x70, 0x7f, 0xa7, 0xc2, 0xda, 0x94, 0xad, - 0x2b, 0xe7, 0xac, 0x97, 0x82, 0xf0, 0x6c, 0xb2, 0x8d, 0x5d, 0x78, 0x9b, 0x70, 0x6d, 0x60, 0xef, - 0x42, 0xf5, 0x6c, 0x80, 0x16, 0x40, 0xfc, 0x4f, 0x2a, 0x7c, 0x6d, 0xd6, 0xe0, 0x55, 0x5e, 0xec, - 0x5f, 0x0a, 0xde, 0xd3, 0x6f, 0xeb, 0xb1, 0x05, 0xde, 0xd6, 0xaf, 0x0d, 0xff, 0x27, 0x70, 0xef, - 0x2c, 0xb8, 0x16, 0x40, 0xff, 0x87, 0x90, 0xdd, 0x22, 0x7d, 0xcb, 0x5e, 0x0c, 0xeb, 0xa9, 0x7f, - 0x64, 0xd4, 0xe9, 0x7f, 0x64, 0xf4, 0xef, 0x40, 0x4e, 0x9a, 0x96, 0x7e, 0x45, 0x12, 0xa5, 0x72, - 0x41, 0xa2, 0xfc, 0x5c, 0x81, 0x5c, 0x93, 0xff, 0x71, 0x73, 0xed, 0x85, 0xc2, 0x1d, 0x48, 0x98, - 0xcc, 0x19, 0x5a, 0x5d, 0xf9, 0x97, 0x92, 0x6c, 0xe9, 0x45, 0xc8, 0xfb, 0x1e, 0x08, 0xff, 0xf5, - 0x9f, 0x42, 0x01, 0x3b, 0x83, 0xc1, 0xa1, 0xd9, 0x3d, 0xba, 0x6e, 0xaf, 0x74, 0x04, 0xc5, 0x70, - 0x2e, 0x39, 0xff, 0xa7, 0xf0, 0x3a, 0x26, 0xd4, 0x19, 0x4c, 0x48, 0xa4, 0xa4, 0x58, 0xcc, 0x13, - 0x04, 0xb1, 0x1e, 0x93, 0xff, 0xab, 0xa4, 0x31, 0x7f, 0xd6, 0xff, 0xa6, 0x40, 0x69, 0x87, 0x50, - 0x6a, 0xf6, 0x89, 0x20, 0xd8, 0x62, 0xa6, 0xcf, 0xab, 0x19, 0x4b, 0x10, 0x17, 0x27, 0xaf, 0xd8, - 0x6f, 0xa2, 0x81, 0x36, 0x20, 0x1d, 0x6c, 0x36, 0x7e, 0x26, 0x9f, 0xbe, 0xd7, 0x52, 0xfe, 0x5e, - 0xf3, 0xbc, 0x8f, 0xdc, 0x8f, 0xf0, 0x67, 0xfd, 0x57, 0x0a, 0xdc, 0x92, 0xde, 0x6f, 0x2e, 0x1a, - 0x9f, 0xf3, 0x5c, 0xf7, 0xe7, 0xd4, 0xc2, 0x39, 0xd1, 0x3d, 0xd0, 0xfc, 0x64, 0x9c, 0x69, 0x64, - 0xe5, 0x2e, 0x3b, 0x30, 0x07, 0x63, 0x82, 0x3d, 0x81, 0xbe, 0x03, 0xd9, 0x56, 0xa4, 0xd2, 0x44, - 0xab, 0xa0, 0x06, 0x6e, 0x4c, 0xab, 0xab, 0x56, 0x6f, 0xf6, 0x8a, 0x42, 0x3d, 0x71, 0x45, 0xf1, - 0x57, 0x05, 0x56, 0xc3, 0x25, 0x5e, 0xf9, 0x60, 0xba, 0xec, 0x6a, 0x3f, 0x84, 0x82, 0xd5, 0x33, - 0x4e, 0x1c, 0x43, 0x99, 0x46, 0xc9, 0x67, 0x71, 0x74, 0xb1, 0x38, 0x67, 0x45, 0x5a, 0x54, 0x5f, - 0x85, 0xca, 0x69, 0xe4, 0x95, 0xd4, 0xfe, 0x9f, 0x0a, 0xb7, 0xda, 0xa3, 0x81, 0xc5, 0x64, 0x8e, - 0x7a, 0xd9, 0xeb, 0x99, 0xfb, 0x92, 0xee, 0x4d, 0xc8, 0x52, 0xcf, 0x0f, 0x79, 0x0f, 0x27, 0x0b, - 0x9a, 0x0c, 0xef, 0x13, 0x37, 0x70, 0x5e, 0x9c, 0x7c, 0x95, 0xb1, 0xcd, 0x38, 0x09, 0x35, 0x0c, - 0x52, 0x63, 0x6c, 0x33, 0xf4, 0x0d, 0xb8, 0x6b, 0x8f, 0x87, 0x86, 0xeb, 0xbc, 0xa0, 0xc6, 0x88, - 0xb8, 0x06, 0xb7, 0x6c, 0x8c, 0x4c, 0x97, 0xf1, 0x14, 0xaf, 0xe1, 0x15, 0x7b, 0x3c, 0xc4, 0xce, - 0x0b, 0xba, 0x4f, 0x5c, 0x3e, 0xf9, 0xbe, 0xe9, 0x32, 0xf4, 0x3d, 0x48, 0x9b, 0x83, 0xbe, 0xe3, - 0x5a, 0xec, 0xf9, 0x50, 0x5e, 0xbc, 0xe9, 0xd2, 0xcd, 0x13, 0xc8, 0xd4, 0x37, 0x7d, 0x4d, 0x1c, - 0x0e, 0x42, 0xef, 0x02, 0x1a, 0x53, 0x62, 0x08, 0xe7, 0xc4, 0xa4, 0x93, 0x86, 0xbc, 0x85, 0x2b, - 0x8c, 0x29, 0x09, 0xcd, 0x1c, 0x34, 0xf4, 0x7f, 0x68, 0x80, 0xa2, 0x76, 0x65, 0x8e, 0xfe, 0x16, - 0x24, 0xf8, 0x78, 0x5a, 0x56, 0x78, 0x6c, 0xd7, 0x82, 0x0c, 0x75, 0x42, 0xb7, 0xee, 0xb9, 0x8d, - 0xa5, 0x7a, 0xe5, 0x53, 0xc8, 0xfa, 0x3b, 0x95, 0x2f, 0x27, 0x1a, 0x0d, 0xe5, 0xdc, 0xd3, 0x55, - 0x9d, 0xe3, 0x74, 0xad, 0x7c, 0x04, 0x69, 0x5e, 0xd5, 0x5d, 0x68, 0x3b, 0xac, 0x45, 0xd5, 0x68, - 0x2d, 0x5a, 0xf9, 0x8f, 0x02, 0x31, 0x3e, 0x78, 0xee, 0x97, 0xdf, 0x1d, 0xfe, 0xbe, 0x20, 0xbc, - 0x14, 0xd1, 0x13, 0x49, 0xfb, 0xfe, 0x39, 0x90, 0x44, 0x21, 0xc0, 0xd9, 0xa3, 0x28, 0x20, 0x4d, - 0x00, 0xf1, 0x09, 0x04, 0x37, 0x25, 0x78, 0xf8, 0xf6, 0x39, 0xa6, 0x82, 0xe5, 0xe2, 0x34, 0x0d, - 0x56, 0x8e, 0x20, 0x46, 0xad, 0x9f, 0x8b, 0x2c, 0xa9, 0x61, 0xfe, 0xac, 0xbf, 0x0f, 0xb7, 0x1f, - 0x13, 0xd6, 0x76, 0x27, 0xfe, 0x76, 0xf3, 0xb7, 0xcf, 0x39, 0x30, 0xe9, 0x18, 0xee, 0xcc, 0x0e, - 0x92, 0x0c, 0xf8, 0x36, 0x64, 0xa9, 0x3b, 0x31, 0xa6, 0x46, 0x7a, 0x55, 0x49, 0x10, 0x9e, 0xe8, - 0xa0, 0x0c, 0x0d, 0x1b, 0xfa, 0x3f, 0x15, 0xc8, 0x1f, 0x5c, 0xe5, 0xe8, 0x98, 0x29, 0xa1, 0xd4, - 0x39, 0x4b, 0xa8, 0xfb, 0x10, 0x9f, 0xf4, 0x99, 0xbc, 0xd5, 0xf5, 0x22, 0x1a, 0xf9, 0xb6, 0xe5, - 0xe0, 0x31, 0xb3, 0x7a, 0x58, 0xc8, 0xbd, 0xc2, 0xe8, 0x27, 0xd6, 0x80, 0x11, 0x37, 0x38, 0x65, - 0x22, 0x9a, 0x1f, 0x73, 0x09, 0x96, 0x1a, 0xfa, 0x77, 0xa1, 0x10, 0xac, 0x25, 0xac, 0xab, 0xc8, - 0x84, 0xd8, 0xc1, 0xde, 0x98, 0x1a, 0x7e, 0xb0, 0xed, 0x89, 0xb0, 0xd4, 0xd0, 0xff, 0xa0, 0xc2, - 0xca, 0xd3, 0x51, 0xcf, 0x64, 0xcb, 0x7e, 0x96, 0x2e, 0x58, 0xb6, 0xae, 0x42, 0x9a, 0x59, 0x43, - 0x42, 0x99, 0x39, 0x1c, 0xc9, 0xac, 0x16, 0x76, 0x78, 0x11, 0xe1, 0x38, 0xc8, 0xcb, 0x58, 0x7f, - 0x8f, 0x71, 0x88, 0x3a, 0xce, 0x11, 0xb1, 0xb1, 0x90, 0xeb, 0x47, 0x50, 0x9a, 0x46, 0x49, 0x42, - 0x5d, 0xf3, 0x0d, 0x4c, 0x57, 0xb0, 0xb2, 0xf0, 0xe5, 0x48, 0x0b, 0x05, 0xf4, 0x0e, 0x14, 0xbd, - 0x52, 0x76, 0x48, 0x8c, 0xd0, 0x1f, 0xf1, 0xb5, 0x48, 0x41, 0xf4, 0x77, 0xfc, 0xee, 0x07, 0x8f, - 0xa0, 0x30, 0xf3, 0x31, 0x0e, 0x2a, 0x40, 0xe6, 0xe9, 0x6e, 0x7b, 0x7f, 0xbb, 0xd9, 0xfa, 0xb8, - 0xb5, 0xfd, 0xa8, 0xf8, 0x1a, 0x02, 0x48, 0xb4, 0x5b, 0xbb, 0x8f, 0x9f, 0x6c, 0x17, 0x15, 0x94, - 0x86, 0xf8, 0xce, 0xd3, 0x27, 0x9d, 0x56, 0x51, 0xf5, 0x1e, 0x3b, 0xcf, 0xf6, 0xf6, 0x9b, 0x45, - 0xed, 0xc1, 0x87, 0x90, 0x11, 0x75, 0xe1, 0x9e, 0xdb, 0x23, 0xae, 0x37, 0x60, 0x77, 0x0f, 0xef, - 0x6c, 0x3e, 0x29, 0xbe, 0x86, 0x92, 0xa0, 0xed, 0x63, 0x6f, 0x64, 0x0a, 0x62, 0xfb, 0x7b, 0xed, - 0x4e, 0x51, 0x45, 0x79, 0x80, 0xcd, 0xa7, 0x9d, 0xbd, 0xe6, 0xde, 0xce, 0x4e, 0xab, 0x53, 0xd4, - 0xb6, 0x3e, 0x80, 0x82, 0xe5, 0xd4, 0x27, 0x16, 0x23, 0x94, 0x8a, 0xcf, 0xa9, 0x7e, 0xf4, 0x96, - 0x6c, 0x59, 0xce, 0x86, 0x78, 0xda, 0xe8, 0x3b, 0x1b, 0x13, 0xb6, 0xc1, 0xa5, 0x1b, 0x22, 0x41, - 0x1c, 0x26, 0x78, 0xeb, 0xfd, 0xaf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x03, 0xdd, 0xba, 0xce, - 0x25, 0x00, 0x00, + 0x15, 0x4f, 0x77, 0xfb, 0xf3, 0xb9, 0xfd, 0xb1, 0x35, 0xb3, 0xbb, 0x8e, 0x33, 0xd9, 0x99, 0x74, + 0x32, 0x5a, 0x67, 0xb3, 0xf2, 0x10, 0x07, 0x02, 0x42, 0x41, 0x61, 0xc6, 0x3b, 0x59, 0x59, 0xd9, + 0xf9, 0xa0, 0xec, 0x9d, 0x05, 0x44, 0xd4, 0xea, 0xb1, 0x2b, 0xde, 0x66, 0xec, 0x6e, 0xa7, 0xab, + 0xec, 0x65, 0x38, 0xa0, 0xfc, 0x07, 0x11, 0x07, 0x24, 0x14, 0x21, 0x21, 0xa4, 0x95, 0x38, 0x71, + 0x45, 0x02, 0x2e, 0xdc, 0x90, 0xb8, 0x20, 0x4e, 0xdc, 0xf9, 0x07, 0x90, 0xf8, 0x0b, 0xa2, 0xae, + 0xaa, 0xfe, 0xb0, 0xe7, 0xcb, 0xe3, 0xd9, 0x59, 0x79, 0x2f, 0x56, 0xd7, 0x7b, 0xaf, 0xaa, 0x5e, + 0xfd, 0xde, 0xaf, 0x5e, 0xbd, 0xae, 0x36, 0xe8, 0x63, 0xd6, 0xb3, 0x18, 0xa9, 0x0d, 0x3d, 0x97, + 0xb9, 0x28, 0x25, 0x5a, 0x95, 0xd2, 0xa1, 0xed, 0xf4, 0xdd, 0x5e, 0xd7, 0x62, 0x96, 0xd0, 0x54, + 0x72, 0x5f, 0x8c, 0x88, 0x77, 0x2c, 0x1b, 0x05, 0xe6, 0x0e, 0xdd, 0xb8, 0x72, 0xcc, 0xbc, 0x61, + 0x47, 0x34, 0x8c, 0xe7, 0x49, 0x48, 0xb7, 0x08, 0xa5, 0xb6, 0xeb, 0xa0, 0x75, 0x28, 0xd8, 0x8e, + 0xc9, 0x3c, 0xcb, 0xa1, 0x56, 0x87, 0xd9, 0xae, 0x53, 0x56, 0xd6, 0x94, 0x6a, 0x06, 0xe7, 0x6d, + 0xa7, 0x1d, 0x09, 0x51, 0x03, 0x0a, 0xf4, 0xa9, 0xe5, 0x75, 0x4d, 0x2a, 0xfa, 0xd1, 0xb2, 0xba, + 0xa6, 0x55, 0x73, 0xf5, 0x95, 0x9a, 0xf4, 0x4e, 0x8e, 0x57, 0x6b, 0xf9, 0x56, 0xb2, 0x81, 0xf3, + 0x34, 0xd6, 0xa2, 0xe8, 0x0d, 0xc8, 0x52, 0xdb, 0xe9, 0xf5, 0x89, 0xd9, 0x3d, 0x2c, 0x6b, 0x7c, + 0x9a, 0x8c, 0x10, 0x3c, 0x38, 0x44, 0x77, 0x00, 0xac, 0x11, 0x73, 0x3b, 0xee, 0x60, 0x60, 0xb3, + 0x72, 0x82, 0x6b, 0x63, 0x12, 0xf4, 0x36, 0xe4, 0x99, 0xe5, 0xf5, 0x08, 0x33, 0x29, 0xf3, 0x6c, + 0xa7, 0x57, 0x4e, 0xae, 0x29, 0xd5, 0x2c, 0xd6, 0x85, 0xb0, 0xc5, 0x65, 0x68, 0x03, 0xd2, 0xee, + 0x90, 0x71, 0xff, 0x52, 0x6b, 0x4a, 0x35, 0x57, 0xbf, 0x59, 0x13, 0xa8, 0x6c, 0xff, 0x82, 0x74, + 0x46, 0x8c, 0xec, 0x09, 0x25, 0x0e, 0xac, 0xd0, 0x16, 0x94, 0x62, 0x6b, 0x37, 0x07, 0x6e, 0x97, + 0x94, 0xd3, 0x6b, 0x4a, 0xb5, 0x50, 0xbf, 0x1d, 0xac, 0x2c, 0x06, 0xc3, 0x8e, 0xdb, 0x25, 0xb8, + 0xc8, 0x26, 0x05, 0x68, 0x03, 0x32, 0xcf, 0x2c, 0xcf, 0xb1, 0x9d, 0x1e, 0x2d, 0x67, 0x38, 0x2a, + 0x4b, 0x72, 0xd6, 0x1f, 0xf9, 0xbf, 0x4f, 0x84, 0x0e, 0x87, 0x46, 0xe8, 0x63, 0xd0, 0x87, 0x1e, + 0x89, 0xa0, 0xcc, 0xce, 0x00, 0x65, 0x6e, 0xe8, 0x91, 0x10, 0xc8, 0x4d, 0xc8, 0x0f, 0x5d, 0xca, + 0xa2, 0x11, 0x60, 0x86, 0x11, 0x74, 0xbf, 0x4b, 0x38, 0xc4, 0x3b, 0x50, 0xe8, 0x5b, 0x94, 0x99, + 0xb6, 0x43, 0x89, 0xc7, 0x4c, 0xbb, 0x5b, 0xce, 0xad, 0x29, 0xd5, 0x04, 0xd6, 0x7d, 0x69, 0x93, + 0x0b, 0x9b, 0x5d, 0xf4, 0x26, 0xc0, 0xe7, 0xee, 0xc8, 0xe9, 0x9a, 0x9e, 0xfb, 0x8c, 0x96, 0x75, + 0x6e, 0x91, 0xe5, 0x12, 0xec, 0x3e, 0xa3, 0x95, 0x9f, 0x81, 0x1e, 0x9f, 0x02, 0xad, 0x43, 0x4a, + 0x84, 0x83, 0x93, 0x28, 0x57, 0xcf, 0x4b, 0x1c, 0xda, 0x5c, 0x88, 0xa5, 0xd2, 0xe7, 0x5c, 0x1c, + 0x74, 0xbb, 0x5b, 0x56, 0xd7, 0x94, 0xaa, 0x86, 0xf3, 0x31, 0x69, 0xb3, 0x6b, 0xfc, 0x4b, 0x85, + 0x82, 0x8c, 0x1b, 0x26, 0x5f, 0x8c, 0x08, 0x65, 0xe8, 0x3e, 0x64, 0x3b, 0x56, 0xbf, 0x4f, 0x3c, + 0xbf, 0x93, 0x98, 0xa3, 0x58, 0x13, 0xd4, 0x6e, 0x70, 0x79, 0xf3, 0x01, 0xce, 0x08, 0x8b, 0x66, + 0x17, 0xbd, 0x0b, 0x69, 0x89, 0x10, 0x9f, 0x40, 0xd8, 0xc6, 0x01, 0xc2, 0x81, 0x1e, 0xdd, 0x85, + 0x24, 0x77, 0x95, 0xd3, 0x32, 0x57, 0xbf, 0x21, 0x1d, 0xdf, 0xf2, 0x97, 0xca, 0xa3, 0x88, 0x85, + 0x1e, 0x7d, 0x07, 0x72, 0xcc, 0x3a, 0xec, 0x13, 0x66, 0xb2, 0xe3, 0x21, 0xe1, 0x3c, 0x2d, 0xd4, + 0x97, 0x6b, 0xe1, 0x76, 0x6b, 0x73, 0x65, 0xfb, 0x78, 0x48, 0x30, 0xb0, 0xf0, 0x19, 0xdd, 0x07, + 0xe4, 0xb8, 0x3e, 0xda, 0x13, 0x5b, 0x2d, 0xc9, 0x59, 0x5e, 0x72, 0x5c, 0xd6, 0x9c, 0xd8, 0x6d, + 0xeb, 0x50, 0x38, 0x22, 0xc7, 0x74, 0x68, 0x75, 0x88, 0xc9, 0xb7, 0x10, 0x67, 0x73, 0x16, 0xe7, + 0x03, 0x29, 0x47, 0x3d, 0xce, 0xf6, 0xf4, 0x2c, 0x6c, 0x37, 0xbe, 0x52, 0xa0, 0x18, 0x22, 0x4a, + 0x87, 0xae, 0x43, 0x09, 0x5a, 0x87, 0x24, 0xf1, 0x3c, 0xd7, 0x9b, 0x82, 0x13, 0xef, 0x37, 0xb6, + 0x7d, 0x31, 0x16, 0xda, 0xcb, 0x60, 0x79, 0x0f, 0x52, 0x1e, 0xa1, 0xa3, 0x3e, 0x93, 0x60, 0xa2, + 0xf8, 0x6e, 0xc0, 0x5c, 0x83, 0xa5, 0x85, 0xf1, 0x5f, 0x15, 0x96, 0xa5, 0x47, 0x7c, 0x4d, 0x74, + 0x71, 0x22, 0x5d, 0x81, 0x4c, 0x00, 0x37, 0x0f, 0x73, 0x16, 0x87, 0x6d, 0x74, 0x0b, 0x52, 0x3c, + 0x2e, 0xb4, 0x9c, 0x5c, 0xd3, 0xaa, 0x59, 0x2c, 0x5b, 0xd3, 0xec, 0x48, 0x5d, 0x89, 0x1d, 0xe9, + 0x33, 0xd8, 0x11, 0x0b, 0x7b, 0x66, 0xa6, 0xb0, 0xff, 0x46, 0x81, 0x9b, 0x53, 0x20, 0x2f, 0x44, + 0xf0, 0xff, 0xaf, 0xc2, 0xeb, 0xd2, 0xaf, 0x4f, 0x25, 0xb2, 0xcd, 0x57, 0x85, 0x01, 0x6f, 0x81, + 0x1e, 0x6e, 0x51, 0x5b, 0xf2, 0x40, 0xc7, 0xb9, 0xa3, 0x68, 0x1d, 0x0b, 0x4a, 0x86, 0xaf, 0x15, + 0xa8, 0x9c, 0x06, 0xfa, 0x42, 0x30, 0xe2, 0x4b, 0x0d, 0x6e, 0x47, 0xce, 0x61, 0xcb, 0xe9, 0x91, + 0x57, 0x84, 0x0f, 0xef, 0x03, 0x1c, 0x91, 0x63, 0xd3, 0xe3, 0x2e, 0x73, 0x36, 0xf8, 0x2b, 0x0d, + 0x63, 0x1d, 0xac, 0x06, 0x67, 0x8f, 0x82, 0x75, 0x2d, 0x28, 0x3f, 0x7e, 0xab, 0x40, 0xf9, 0x64, + 0x08, 0x16, 0x82, 0x1d, 0x7f, 0x49, 0x84, 0xec, 0xd8, 0x76, 0x98, 0xcd, 0x8e, 0x5f, 0x99, 0x6c, + 0x71, 0x1f, 0x10, 0xe1, 0x1e, 0x9b, 0x1d, 0xb7, 0x3f, 0x1a, 0x38, 0xa6, 0x63, 0x0d, 0x88, 0xac, + 0x60, 0x4b, 0x42, 0xd3, 0xe0, 0x8a, 0x5d, 0x6b, 0x40, 0xd0, 0x8f, 0x61, 0x49, 0x5a, 0x4f, 0xa4, + 0x98, 0x14, 0x27, 0x55, 0x35, 0xf0, 0xf4, 0x0c, 0x24, 0x6a, 0x81, 0x00, 0xdf, 0x10, 0x83, 0x7c, + 0x7a, 0x76, 0x4a, 0x4a, 0x5f, 0x89, 0x72, 0x99, 0x8b, 0x29, 0x97, 0x9d, 0x85, 0x72, 0x95, 0x43, + 0xc8, 0x04, 0x4e, 0xa3, 0x55, 0x48, 0x70, 0xd7, 0x14, 0xee, 0x5a, 0x2e, 0x28, 0x20, 0x7d, 0x8f, + 0xb8, 0x02, 0x2d, 0x43, 0x72, 0x6c, 0xf5, 0x47, 0x84, 0x07, 0x4e, 0xc7, 0xa2, 0x81, 0x56, 0x21, + 0x17, 0xc3, 0x8a, 0xc7, 0x4a, 0xc7, 0x10, 0x65, 0xe3, 0x38, 0xad, 0x63, 0x88, 0x2d, 0x04, 0xad, + 0xff, 0xad, 0xc2, 0x92, 0x74, 0x6d, 0xcb, 0x62, 0x9d, 0xa7, 0xd7, 0x4e, 0xe9, 0xf7, 0x20, 0xed, + 0x7b, 0x63, 0x13, 0x5a, 0xd6, 0x38, 0xa7, 0x4e, 0x21, 0x75, 0x60, 0x31, 0x6f, 0xc1, 0xbb, 0x0e, + 0x05, 0x8b, 0x9e, 0x52, 0xec, 0xe6, 0x2d, 0xfa, 0x32, 0x2a, 0xdd, 0xaf, 0x95, 0xb0, 0xae, 0x94, + 0x98, 0x5e, 0x5b, 0xa8, 0xbf, 0x05, 0x69, 0x11, 0xc8, 0x00, 0xcd, 0x5b, 0xd2, 0x37, 0x11, 0xe6, + 0x27, 0x36, 0x7b, 0x2a, 0x86, 0x0e, 0xcc, 0x0c, 0x07, 0x8a, 0x1c, 0x69, 0xbe, 0x36, 0x0e, 0x77, + 0x94, 0x65, 0x94, 0x4b, 0x64, 0x19, 0xf5, 0xcc, 0xaa, 0x54, 0x8b, 0x57, 0xa5, 0xc6, 0x9f, 0xa3, + 0x3a, 0x8b, 0x83, 0xf1, 0x92, 0x2a, 0xed, 0xf7, 0xa7, 0x69, 0x16, 0xbe, 0x52, 0x4f, 0xad, 0xfe, + 0x65, 0x91, 0xed, 0xb2, 0xb7, 0x03, 0xc6, 0xef, 0xa2, 0x5a, 0x69, 0x02, 0xb8, 0x6b, 0xe3, 0xd2, + 0xfd, 0x69, 0x2e, 0x9d, 0x96, 0x37, 0x42, 0x1e, 0xfd, 0x0a, 0x96, 0x39, 0x92, 0x51, 0x86, 0x7f, + 0x81, 0x64, 0x9a, 0x2e, 0x70, 0xb5, 0x13, 0x05, 0xae, 0xf1, 0x77, 0x15, 0xee, 0xc4, 0xe1, 0x79, + 0x99, 0x45, 0xfc, 0x87, 0xd3, 0xe4, 0x5a, 0x99, 0x20, 0xd7, 0x14, 0x24, 0x0b, 0xcb, 0xb0, 0x3f, + 0x28, 0xb0, 0x7a, 0x26, 0x84, 0x0b, 0x42, 0xb3, 0x3f, 0xaa, 0xb0, 0xdc, 0x62, 0x1e, 0xb1, 0x06, + 0x57, 0xba, 0x8d, 0x09, 0x59, 0xa9, 0x5e, 0xee, 0x8a, 0x45, 0x9b, 0x3d, 0x44, 0x53, 0x47, 0x49, + 0xe2, 0x82, 0xa3, 0x24, 0x39, 0xd3, 0x15, 0x61, 0x0c, 0xd7, 0xd4, 0xf9, 0xb8, 0x1a, 0x0d, 0xb8, + 0x39, 0x05, 0x94, 0x0c, 0x61, 0x54, 0x0e, 0x28, 0x17, 0x96, 0x03, 0x5f, 0xa9, 0x50, 0x99, 0x18, + 0xe5, 0x2a, 0xe9, 0x7a, 0x66, 0xd0, 0xe3, 0xa9, 0x40, 0x3b, 0xf3, 0x5c, 0x49, 0x9c, 0x77, 0xdb, + 0x91, 0x9c, 0x31, 0x50, 0x97, 0xde, 0x24, 0x4d, 0x78, 0xe3, 0x54, 0x40, 0xe6, 0x00, 0xf7, 0xf7, + 0x2a, 0xac, 0x4e, 0x8c, 0x75, 0xe5, 0x9c, 0xf5, 0x42, 0x10, 0x9e, 0x4e, 0xb6, 0x89, 0x0b, 0x6f, + 0x13, 0xae, 0x0d, 0xec, 0x5d, 0x58, 0x3b, 0x1b, 0xa0, 0x39, 0x10, 0xff, 0x93, 0x0a, 0x6f, 0x4e, + 0x0f, 0x78, 0x95, 0x17, 0xfb, 0x17, 0x82, 0xf7, 0xe4, 0xdb, 0x7a, 0x62, 0x8e, 0xb7, 0xf5, 0x6b, + 0xc3, 0xff, 0x11, 0xdc, 0x39, 0x0b, 0xae, 0x39, 0xd0, 0xff, 0x09, 0xe8, 0x5b, 0xa4, 0x67, 0x3b, + 0xf3, 0x61, 0x3d, 0xf1, 0xc1, 0x46, 0x9d, 0xfc, 0x60, 0x63, 0x7c, 0x1f, 0xf2, 0x72, 0x68, 0xe9, + 0x57, 0x2c, 0x51, 0x2a, 0x17, 0x24, 0xca, 0x2f, 0x15, 0xc8, 0x37, 0xf8, 0x77, 0x9d, 0x6b, 0x2f, + 0x14, 0x6e, 0x41, 0xca, 0x62, 0xee, 0xc0, 0xee, 0xc8, 0x2f, 0x4e, 0xb2, 0x65, 0x94, 0xa0, 0x10, + 0x78, 0x20, 0xfc, 0x37, 0x7e, 0x0e, 0x45, 0xec, 0xf6, 0xfb, 0x87, 0x56, 0xe7, 0xe8, 0xba, 0xbd, + 0x32, 0x10, 0x94, 0xa2, 0xb9, 0xe4, 0xfc, 0x9f, 0xc1, 0xeb, 0x98, 0x50, 0xb7, 0x3f, 0x26, 0xb1, + 0x92, 0x62, 0x3e, 0x4f, 0x10, 0x24, 0xba, 0x4c, 0x7e, 0x57, 0xc9, 0x62, 0xfe, 0x6c, 0xfc, 0x4d, + 0x81, 0xe5, 0x1d, 0x42, 0xa9, 0xd5, 0x23, 0x82, 0x60, 0xf3, 0x0d, 0x7d, 0x5e, 0xcd, 0xb8, 0x0c, + 0x49, 0x71, 0xf2, 0x8a, 0xfd, 0x26, 0x1a, 0x68, 0x03, 0xb2, 0xe1, 0x66, 0xe3, 0x67, 0xf2, 0xe9, + 0x7b, 0x2d, 0x13, 0xec, 0x35, 0xdf, 0xfb, 0xd8, 0xfd, 0x08, 0x7f, 0x36, 0x7e, 0xad, 0xc0, 0x0d, + 0xe9, 0xfd, 0xe6, 0xbc, 0xf1, 0x39, 0xcf, 0xf5, 0x60, 0x4e, 0x2d, 0x9a, 0x13, 0xdd, 0x01, 0x2d, + 0x48, 0xc6, 0xb9, 0xba, 0x2e, 0x77, 0xd9, 0x81, 0xd5, 0x1f, 0x11, 0xec, 0x2b, 0x8c, 0x1d, 0xd0, + 0x9b, 0xb1, 0x4a, 0x13, 0xad, 0x80, 0x1a, 0xba, 0x31, 0x69, 0xae, 0xda, 0xdd, 0xe9, 0x2b, 0x0a, + 0xf5, 0xc4, 0x15, 0xc5, 0x5f, 0x15, 0x58, 0x89, 0x96, 0x78, 0xe5, 0x83, 0xe9, 0xb2, 0xab, 0xfd, + 0x08, 0x8a, 0x76, 0xd7, 0x3c, 0x71, 0x0c, 0xe5, 0xea, 0xcb, 0x01, 0x8b, 0xe3, 0x8b, 0xc5, 0x79, + 0x3b, 0xd6, 0xa2, 0xc6, 0x0a, 0x54, 0x4e, 0x23, 0xaf, 0xa4, 0xf6, 0xff, 0x54, 0xb8, 0xd1, 0x1a, + 0xf6, 0x6d, 0x26, 0x73, 0xd4, 0x8b, 0x5e, 0xcf, 0xcc, 0x97, 0x74, 0x6f, 0x81, 0x4e, 0x7d, 0x3f, + 0xe4, 0x3d, 0x9c, 0x2c, 0x68, 0x72, 0x5c, 0x26, 0x6e, 0xe0, 0xfc, 0x38, 0x05, 0x26, 0x23, 0x87, + 0x71, 0x12, 0x6a, 0x18, 0xa4, 0xc5, 0xc8, 0x61, 0xe8, 0xdb, 0x70, 0xdb, 0x19, 0x0d, 0xf8, 0x27, + 0x51, 0x73, 0x48, 0x3c, 0x93, 0x8f, 0x6c, 0x0e, 0x2d, 0x8f, 0xf1, 0x14, 0xaf, 0xe1, 0x25, 0x67, + 0x34, 0xc0, 0xee, 0x33, 0xba, 0x4f, 0x3c, 0x3e, 0xf9, 0xbe, 0xe5, 0x31, 0xf4, 0x43, 0xc8, 0x5a, + 0xfd, 0x9e, 0xeb, 0xd9, 0xec, 0xe9, 0x40, 0x5e, 0xbc, 0x19, 0xd2, 0xcd, 0x13, 0xc8, 0xd4, 0x36, + 0x03, 0x4b, 0x1c, 0x75, 0x42, 0xef, 0x01, 0x1a, 0x51, 0x62, 0x0a, 0xe7, 0xc4, 0xa4, 0xe3, 0xba, + 0xbc, 0x85, 0x2b, 0x8e, 0x28, 0x89, 0x86, 0x39, 0xa8, 0x1b, 0xff, 0xd0, 0x00, 0xc5, 0xc7, 0x95, + 0x39, 0xfa, 0xbb, 0x90, 0xe2, 0xfd, 0x69, 0x59, 0xe1, 0xb1, 0x5d, 0x0d, 0x33, 0xd4, 0x09, 0xdb, + 0x9a, 0xef, 0x36, 0x96, 0xe6, 0x95, 0xcf, 0x40, 0x0f, 0x76, 0x2a, 0x5f, 0x4e, 0x3c, 0x1a, 0xca, + 0xb9, 0xa7, 0xab, 0x3a, 0xc3, 0xe9, 0x5a, 0xf9, 0x18, 0xb2, 0xbc, 0xaa, 0xbb, 0x70, 0xec, 0xa8, + 0x16, 0x55, 0xe3, 0xb5, 0x68, 0xe5, 0x3f, 0x0a, 0x24, 0x78, 0xe7, 0x99, 0x5f, 0x7e, 0x77, 0xf8, + 0xfb, 0x82, 0xf0, 0x52, 0x44, 0x4f, 0x24, 0xed, 0xbb, 0xe7, 0x40, 0x12, 0x87, 0x00, 0xeb, 0x47, + 0x71, 0x40, 0x1a, 0x00, 0xe2, 0x1f, 0x12, 0x7c, 0x28, 0xc1, 0xc3, 0x77, 0xce, 0x19, 0x2a, 0x5c, + 0x2e, 0xce, 0xd2, 0x70, 0xe5, 0x08, 0x12, 0xd4, 0xfe, 0xa5, 0xc8, 0x92, 0x1a, 0xe6, 0xcf, 0xc6, + 0x07, 0x70, 0xf3, 0x21, 0x61, 0x2d, 0x6f, 0x1c, 0x6c, 0xb7, 0x60, 0xfb, 0x9c, 0x03, 0x93, 0x81, + 0xe1, 0xd6, 0x74, 0x27, 0xc9, 0x80, 0xef, 0x81, 0x4e, 0xbd, 0xb1, 0x39, 0xd1, 0xd3, 0xaf, 0x4a, + 0xc2, 0xf0, 0xc4, 0x3b, 0xe5, 0x68, 0xd4, 0x30, 0xfe, 0xa9, 0x40, 0xe1, 0xe0, 0x2a, 0x47, 0xc7, + 0x54, 0x09, 0xa5, 0xce, 0x58, 0x42, 0xdd, 0x85, 0xe4, 0xb8, 0xc7, 0xe4, 0xad, 0xae, 0x1f, 0xd1, + 0xd8, 0x5f, 0x5f, 0x0e, 0x1e, 0x32, 0xbb, 0x8b, 0x85, 0xde, 0x2f, 0x8c, 0x3e, 0xb7, 0xfb, 0x8c, + 0x78, 0xe1, 0x29, 0x13, 0xb3, 0xfc, 0x84, 0x6b, 0xb0, 0xb4, 0x30, 0x7e, 0x00, 0xc5, 0x70, 0x2d, + 0x51, 0x5d, 0x45, 0xc6, 0xc4, 0x09, 0xf7, 0xc6, 0x44, 0xf7, 0x83, 0x6d, 0x5f, 0x85, 0xa5, 0x85, + 0xf1, 0x5c, 0x85, 0xa5, 0xc7, 0xc3, 0xae, 0xc5, 0x16, 0xfd, 0x2c, 0x9d, 0xb3, 0x6c, 0x5d, 0x81, + 0x2c, 0xb3, 0x07, 0x84, 0x32, 0x6b, 0x30, 0x94, 0x59, 0x2d, 0x12, 0xf8, 0x11, 0xe1, 0x38, 0xc8, + 0xcb, 0xd8, 0x60, 0x8f, 0x71, 0x88, 0xda, 0xee, 0x11, 0x71, 0xb0, 0xd0, 0x1b, 0x47, 0xb0, 0x3c, + 0x89, 0x92, 0x84, 0xba, 0x1a, 0x0c, 0x30, 0x59, 0xc1, 0xca, 0xc2, 0x97, 0x23, 0x2d, 0x0c, 0xd0, + 0xbb, 0x50, 0xf2, 0x4b, 0xd9, 0x01, 0x31, 0x23, 0x7f, 0xc4, 0xbf, 0x45, 0x8a, 0x42, 0xde, 0x0e, + 0xc4, 0xf7, 0x1e, 0x40, 0x71, 0xea, 0xbf, 0x3a, 0xa8, 0x08, 0xb9, 0xc7, 0xbb, 0xad, 0xfd, 0xed, + 0x46, 0xf3, 0x93, 0xe6, 0xf6, 0x83, 0xd2, 0x6b, 0x08, 0x20, 0xd5, 0x6a, 0xee, 0x3e, 0x7c, 0xb4, + 0x5d, 0x52, 0x50, 0x16, 0x92, 0x3b, 0x8f, 0x1f, 0xb5, 0x9b, 0x25, 0xd5, 0x7f, 0x6c, 0x3f, 0xd9, + 0xdb, 0x6f, 0x94, 0xb4, 0x7b, 0x1f, 0x41, 0x4e, 0xd4, 0x85, 0x7b, 0x5e, 0x97, 0x78, 0x7e, 0x87, + 0xdd, 0x3d, 0xbc, 0xb3, 0xf9, 0xa8, 0xf4, 0x1a, 0x4a, 0x83, 0xb6, 0x8f, 0xfd, 0x9e, 0x19, 0x48, + 0xec, 0xef, 0xb5, 0xda, 0x25, 0x15, 0x15, 0x00, 0x36, 0x1f, 0xb7, 0xf7, 0x1a, 0x7b, 0x3b, 0x3b, + 0xcd, 0x76, 0x49, 0xdb, 0xfa, 0x10, 0x8a, 0xb6, 0x5b, 0x1b, 0xdb, 0x8c, 0x50, 0x2a, 0xfe, 0x6d, + 0xf5, 0xd3, 0xb7, 0x65, 0xcb, 0x76, 0x37, 0xc4, 0xd3, 0x46, 0xcf, 0xdd, 0x18, 0xb3, 0x0d, 0xae, + 0xdd, 0x10, 0x09, 0xe2, 0x30, 0xc5, 0x5b, 0x1f, 0x7c, 0x13, 0x00, 0x00, 0xff, 0xff, 0x02, 0x83, + 0xbb, 0x0c, 0xed, 0x25, 0x00, 0x00, } diff --git a/proto/vtgate.proto b/proto/vtgate.proto index 636d0130c8b..480367104b0 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -110,7 +110,7 @@ message Session { uint64 last_insert_id = 11; // found_rows keeps track of how many rows the last query returned - uint64 rows_affected = 12; + uint64 found_rows = 12; } // ExecuteRequest is the payload to Execute. From 86d7892166fbb1ff392753bb9a774907fff21d4c Mon Sep 17 00:00:00 2001 From: Mike Cronce Date: Mon, 16 Mar 2020 13:59:07 -0400 Subject: [PATCH 290/825] vttablet: Added -table-acl-config-reload-interval to reload the file specified by -table-acl-config on a timer Signed-off-by: Mike Cronce --- go/cmd/vttablet/vttablet.go | 9 +++++---- go/vt/vttablet/tabletserver/tabletserver.go | 11 ++++++++++- .../vttablet/tabletserver/tabletserver_flaky_test.go | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go index 0906e7879c3..52615741cb5 100644 --- a/go/cmd/vttablet/vttablet.go +++ b/go/cmd/vttablet/vttablet.go @@ -35,9 +35,10 @@ import ( ) var ( - enforceTableACLConfig = flag.Bool("enforce-tableacl-config", false, "if this flag is true, vttablet will fail to start if a valid tableacl config does not exist") - tableACLConfig = flag.String("table-acl-config", "", "path to table access checker config file; send SIGHUP to reload this file") - tabletPath = flag.String("tablet-path", "", "tablet alias") + enforceTableACLConfig = flag.Bool("enforce-tableacl-config", false, "if this flag is true, vttablet will fail to start if a valid tableacl config does not exist") + tableACLConfig = flag.String("table-acl-config", "", "path to table access checker config file; send SIGHUP to reload this file") + tableACLConfigReloadInterval = flag.Duration("table-acl-config-reload-interval", 0, "Ticker to reload ACLs") + tabletPath = flag.String("tablet-path", "", "tablet alias") agent *tabletmanager.ActionAgent ) @@ -112,7 +113,7 @@ func main() { qsc.StopService() }) - qsc.InitACL(*tableACLConfig, *enforceTableACLConfig) + qsc.InitACL(*tableACLConfig, *enforceTableACLConfig, *tableACLConfigReloadInterval) // Create mysqld and register the health reporter (needs to be done // before initializing the agent, so the initial health check diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 3444a0ffc50..a21cfac057d 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -415,7 +415,7 @@ func (tsv *TabletServer) initACL(tableACLConfigFile string, enforceTableACLConfi } // InitACL loads the table ACL and sets up a SIGHUP handler for reloading it. -func (tsv *TabletServer) InitACL(tableACLConfigFile string, enforceTableACLConfig bool) { +func (tsv *TabletServer) InitACL(tableACLConfigFile string, enforceTableACLConfig bool, reloadACLConfigFileInterval time.Duration) { tsv.initACL(tableACLConfigFile, enforceTableACLConfig) sigChan := make(chan os.Signal, 1) @@ -425,6 +425,15 @@ func (tsv *TabletServer) InitACL(tableACLConfigFile string, enforceTableACLConfi tsv.initACL(tableACLConfigFile, enforceTableACLConfig) } }() + + if reloadACLConfigFileInterval != 0 { + ticker := time.NewTicker(reloadACLConfigFileInterval) + go func() { + for range ticker.C { + sigChan <- syscall.SIGHUP + } + }() + } } // StartService is a convenience function for InitDBConfig->SetServingType diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index e985f42c4d9..3b1b67194c9 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -2681,7 +2681,7 @@ func TestACLHUP(t *testing.T) { t.Fatal(err) } - tsv.InitACL(f.Name(), true) + tsv.InitACL(f.Name(), true, 0) groups1 := tableacl.GetCurrentConfig().TableGroups if name1 := groups1[0].GetName(); name1 != "group01" { From 21f83a8871ed5c4c2d5c77fe2e0558ae3ca8fa99 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 10 Mar 2020 21:58:19 +0100 Subject: [PATCH 291/825] Fix issue where stop_pos was being set incorrectly Signed-off-by: Rohit Nayak --- go/vt/wrangler/stream_migrater.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/go/vt/wrangler/stream_migrater.go b/go/vt/wrangler/stream_migrater.go index a0fce8a048b..77e9192254a 100644 --- a/go/vt/wrangler/stream_migrater.go +++ b/go/vt/wrangler/stream_migrater.go @@ -266,7 +266,7 @@ func (sm *streamMigrater) syncSourceStreams(ctx context.Context) (map[string]mys continue } wg.Add(1) - go func(vrs *vrStream, shard string) { + go func(vrs *vrStream, shard string, pos mysql.Position) { defer wg.Done() si, err := sm.mi.wr.ts.GetShard(ctx, sm.mi.sourceKeyspace, vrs.bls.Shard) if err != nil { @@ -283,13 +283,13 @@ func (sm *streamMigrater) syncSourceStreams(ctx context.Context) (map[string]mys allErrors.RecordError(err) return } - sm.mi.wr.Logger().Infof("waiting for keyspace:shard: %v:%v, position %v", sm.mi.sourceKeyspace, shard, pos) + sm.mi.wr.Logger().Infof("Waiting for keyspace:shard: %v:%v, position %v", sm.mi.sourceKeyspace, shard, pos) if err := sm.mi.wr.tmc.VReplicationWaitForPos(ctx, master.Tablet, int(vrs.id), mysql.EncodePosition(pos)); err != nil { allErrors.RecordError(err) return } - sm.mi.wr.Logger().Infof("position for keyspace:shard: %v:%v reached", sm.mi.sourceKeyspace, shard) - }(vrs, shard) + sm.mi.wr.Logger().Infof("Position for keyspace:shard: %v:%v reached", sm.mi.sourceKeyspace, shard) + }(vrs, shard, pos) } } wg.Wait() @@ -482,6 +482,7 @@ func (sm *streamMigrater) templatizeKeyRange(ctx context.Context, rule *binlogda } func (sm *streamMigrater) createTargetStreams(ctx context.Context, tmpl []*vrStream) error { + if len(tmpl) == 0 { return nil } From ffd3bdbc802d87fffe7429664ad5885ca5d75a5f Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Mon, 16 Mar 2020 21:51:31 +0100 Subject: [PATCH 292/825] Pick correct shard Signed-off-by: Rohit Nayak --- go/vt/wrangler/stream_migrater.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/go/vt/wrangler/stream_migrater.go b/go/vt/wrangler/stream_migrater.go index 77e9192254a..ad87f9d895b 100644 --- a/go/vt/wrangler/stream_migrater.go +++ b/go/vt/wrangler/stream_migrater.go @@ -268,7 +268,9 @@ func (sm *streamMigrater) syncSourceStreams(ctx context.Context) (map[string]mys wg.Add(1) go func(vrs *vrStream, shard string, pos mysql.Position) { defer wg.Done() - si, err := sm.mi.wr.ts.GetShard(ctx, sm.mi.sourceKeyspace, vrs.bls.Shard) + sm.mi.wr.Logger().Infof("syncSourceStreams beginning of go func %s %s %+v %d", shard, vrs.bls.Shard, pos, vrs.id) + + si, err := sm.mi.wr.ts.GetShard(ctx, sm.mi.sourceKeyspace, shard) if err != nil { allErrors.RecordError(err) return From 541bf419233a70285a54c894645e88606e4d848d Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Mon, 16 Mar 2020 15:43:54 -0700 Subject: [PATCH 293/825] vrepl: where clause filtering WIP Signed-off-by: Sugu Sougoumarane --- .../tabletserver/vstreamer/planbuilder.go | 165 +++++++++++++++--- 1 file changed, 140 insertions(+), 25 deletions(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index c084327c877..6441cc01c2b 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -36,10 +36,30 @@ import ( // Plan represents the plan for a table. type Plan struct { Table *Table + + // Filters is the list of filters to be applied to the columns + // of the table. + Filters []Filter + // ColExprs is the list of column expressions to be sent // in the stream. ColExprs []ColExpr +} + +type Opcode int + +const ( + Equal = Opcode(iota) + VindexMatch +) + +// Filter contains opcodes for filtering. +type Filter struct { + Opcode Opcode + ColNum int + Value sqltypes.Value + // Parameters for VindexMatch. // Vindex, VindexColumns and KeyRange, if set, will be used // to filter the row. // VindexColumns contains the column numbers of the table, @@ -90,13 +110,24 @@ func (plan *Plan) fields() []*querypb.Field { // filter filters the row against the plan. It returns false if the row did not match. // If the row matched, it returns the columns to be sent. func (plan *Plan) filter(values []sqltypes.Value) (bool, []sqltypes.Value, error) { - if plan.Vindex != nil { - ksid, err := getKeyspaceID(values, plan.Vindex, plan.VindexColumns) - if err != nil { - return false, nil, err - } - if !key.KeyRangeContains(plan.KeyRange, ksid) { - return false, nil, nil + for _, filter := range plan.Filters { + switch filter.Opcode { + case Equal: + result, err := sqltypes.NullsafeCompare(values[filter.ColNum], filter.Value) + if err != nil { + return false, nil, err + } + if result != 0 { + return false, nil, nil + } + case VindexMatch: + ksid, err := getKeyspaceID(values, filter.Vindex, filter.VindexColumns) + if err != nil { + return false, nil, err + } + if !key.KeyRangeContains(filter.KeyRange, ksid) { + return false, nil, nil + } } } @@ -242,8 +273,11 @@ func buildREPlan(ti *Table, vschema *localVSchema, filter string) (*Plan, error) if err != nil { return nil, err } - plan.Vindex = cv.Vindex - plan.VindexColumns, err = buildVindexColumns(plan.Table, cv.Columns) + whereFilter := Filter{ + Opcode: VindexMatch, + Vindex: cv.Vindex, + } + whereFilter.VindexColumns, err = buildVindexColumns(plan.Table, cv.Columns) if err != nil { return nil, err } @@ -256,7 +290,8 @@ func buildREPlan(ti *Table, vschema *localVSchema, filter string) (*Plan, error) if len(keyranges) != 1 { return nil, fmt.Errorf("error parsing keyrange: %v", filter) } - plan.KeyRange = keyranges[0] + whereFilter.KeyRange = keyranges[0] + plan.Filters = append(plan.Filters, whereFilter) return plan, nil } @@ -274,6 +309,9 @@ func buildTablePlan(ti *Table, vschema *localVSchema, query string) (*Plan, erro plan := &Plan{ Table: ti, } + if err := plan.analyzeWhere(vschema, sel.Where); err != nil { + return nil, err + } if err := plan.analyzeExprs(vschema, sel.SelectExprs); err != nil { return nil, err } @@ -282,16 +320,6 @@ func buildTablePlan(ti *Table, vschema *localVSchema, query string) (*Plan, erro return plan, nil } - funcExpr, ok := sel.Where.Expr.(*sqlparser.FuncExpr) - if !ok { - return nil, fmt.Errorf("unsupported where clause: %v", sqlparser.String(sel.Where)) - } - if !funcExpr.Name.EqualString("in_keyrange") { - return nil, fmt.Errorf("unsupported where clause: %v", sqlparser.String(sel.Where)) - } - if err := plan.analyzeInKeyRange(vschema, funcExpr.Exprs); err != nil { - return nil, err - } return plan, nil } @@ -318,6 +346,89 @@ func analyzeSelect(query string) (sel *sqlparser.Select, fromTable sqlparser.Tab return sel, fromTable, nil } +func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) error { + if where == nil { + return nil + } + exprs := splitAndExpression(nil, where.Expr) + for _, expr := range exprs { + switch expr := expr.(type) { + case *sqlparser.ComparisonExpr: + qualifiedName, ok := expr.Left.(*sqlparser.ColName) + if !ok { + return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + } + if !qualifiedName.Qualifier.IsEmpty() { + return fmt.Errorf("unsupported qualifier for column: %v", sqlparser.String(qualifiedName)) + } + colnum, err := findColumn(plan.Table, qualifiedName.Name) + if err != nil { + return err + } + val, ok := expr.Right.(*sqlparser.SQLVal) + if !ok { + return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + } + if val.Type != sqlparser.IntVal { + return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) + } + pv, err := sqlparser.NewPlanValue(val) + if err != nil { + return err + } + resolved, err := pv.ResolveValue(nil) + if err != nil { + return err + } + plan.Filters = append(plan.Filters, Filter{ + Opcode: Equal, + ColNum: colnum, + Value: resolved, + }) + case *sqlparser.FuncExpr: + if !expr.Name.EqualString("in_keyrange") { + return fmt.Errorf("unsupported constraint: %v", sqlparser.String(expr)) + } + if err := plan.analyzeInKeyRange(vschema, expr.Exprs); err != nil { + return err + } + default: + return fmt.Errorf("unsupported constraint: %v", sqlparser.String(expr)) + } + } + return nil +} + +// splitAndExpression breaks up the Expr into AND-separated conditions +// and appends them to filters, which can be shuffled and recombined +// as needed. +func splitAndExpression(filters []sqlparser.Expr, node sqlparser.Expr) []sqlparser.Expr { + if node == nil { + return filters + } + switch node := node.(type) { + case *sqlparser.AndExpr: + filters = splitAndExpression(filters, node.Left) + return splitAndExpression(filters, node.Right) + case *sqlparser.ParenExpr: + // If the inner expression is AndExpr, then we can remove + // the parenthesis because they are unnecessary. + if node, ok := node.Expr.(*sqlparser.AndExpr); ok { + return splitAndExpression(filters, node) + } + } + return append(filters, node) +} + +// skipParenthesis skips the parenthesis (if any) of an expression and +// returns the innermost unparenthesized expression. +func skipParenthesis(node sqlparser.Expr) sqlparser.Expr { + if node, ok := node.(*sqlparser.ParenExpr); ok { + return skipParenthesis(node.Expr) + } + return node +} + func (plan *Plan) analyzeExprs(vschema *localVSchema, selExprs sqlparser.SelectExprs) error { if _, ok := selExprs[0].(*sqlparser.StarExpr); !ok { for _, expr := range selExprs { @@ -396,6 +507,9 @@ func (plan *Plan) analyzeExpr(vschema *localVSchema, selExpr sqlparser.SelectExp func (plan *Plan) analyzeInKeyRange(vschema *localVSchema, exprs sqlparser.SelectExprs) error { var colnames []sqlparser.ColIdent var krExpr sqlparser.SelectExpr + whereFilter := Filter{ + Opcode: VindexMatch, + } switch { case len(exprs) == 1: cv, err := vschema.FindColVindex(plan.Table.Name) @@ -403,7 +517,7 @@ func (plan *Plan) analyzeInKeyRange(vschema *localVSchema, exprs sqlparser.Selec return err } colnames = cv.Columns - plan.Vindex = cv.Vindex + whereFilter.Vindex = cv.Vindex krExpr = exprs[0] case len(exprs) >= 3: for _, expr := range exprs[:len(exprs)-2] { @@ -425,11 +539,11 @@ func (plan *Plan) analyzeInKeyRange(vschema *localVSchema, exprs sqlparser.Selec if err != nil { return err } - plan.Vindex, err = vschema.FindOrCreateVindex(vtype) + whereFilter.Vindex, err = vschema.FindOrCreateVindex(vtype) if err != nil { return err } - if !plan.Vindex.IsUnique() { + if !whereFilter.Vindex.IsUnique() { return fmt.Errorf("vindex must be Unique to be used for VReplication: %s", vtype) } @@ -438,7 +552,7 @@ func (plan *Plan) analyzeInKeyRange(vschema *localVSchema, exprs sqlparser.Selec return fmt.Errorf("unexpected in_keyrange parameters: %v", sqlparser.String(exprs)) } var err error - plan.VindexColumns, err = buildVindexColumns(plan.Table, colnames) + whereFilter.VindexColumns, err = buildVindexColumns(plan.Table, colnames) if err != nil { return err } @@ -453,7 +567,8 @@ func (plan *Plan) analyzeInKeyRange(vschema *localVSchema, exprs sqlparser.Selec if len(keyranges) != 1 { return fmt.Errorf("unexpected in_keyrange parameter: %v", sqlparser.String(krExpr)) } - plan.KeyRange = keyranges[0] + whereFilter.KeyRange = keyranges[0] + plan.Filters = append(plan.Filters, whereFilter) return nil } From e9658261bbdda45a8c500214b4a447d3b027233b Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 8 Mar 2020 19:05:35 -0700 Subject: [PATCH 294/825] tests: simplify vtgate grpc test structure Signed-off-by: Sugu Sougoumarane --- go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go | 19 +++++++++---------- .../suite_test.go} | 17 ++++++++++------- 2 files changed, 19 insertions(+), 17 deletions(-) rename go/vt/vtgate/{vtgateconntest/client.go => grpcvtgateconn/suite_test.go} (99%) diff --git a/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go b/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go index 3a774554f5a..b9f0e3cb42b 100644 --- a/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go +++ b/go/vt/vtgate/grpcvtgateconn/conn_rpc_test.go @@ -30,13 +30,12 @@ import ( "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/vtgate/grpcvtgateservice" "vitess.io/vitess/go/vt/vtgate/vtgateconn" - "vitess.io/vitess/go/vt/vtgate/vtgateconntest" ) // TestGRPCVTGateConn makes sure the grpc service works func TestGRPCVTGateConn(t *testing.T) { // fake service - service := vtgateconntest.CreateFakeServer(t) + service := CreateFakeServer(t) // listen on a random port listener, err := net.Listen("tcp", ":0") @@ -55,11 +54,11 @@ func TestGRPCVTGateConn(t *testing.T) { if err != nil { t.Fatalf("dial failed: %v", err) } - vtgateconntest.RegisterTestDialProtocol(client) + RegisterTestDialProtocol(client) // run the test suite - vtgateconntest.TestSuite(t, client, service) - vtgateconntest.TestErrorSuite(t, service) + RunTests(t, client, service) + RunErrorTests(t, service) // and clean up client.Close() @@ -69,7 +68,7 @@ func TestGRPCVTGateConn(t *testing.T) { func TestGRPCVTGateConnAuth(t *testing.T) { var opts []grpc.ServerOption // fake service - service := vtgateconntest.CreateFakeServer(t) + service := CreateFakeServer(t) // listen on a random port listener, err := net.Listen("tcp", ":0") @@ -110,11 +109,11 @@ func TestGRPCVTGateConnAuth(t *testing.T) { if err != nil { t.Fatalf("dial failed: %v", err) } - vtgateconntest.RegisterTestDialProtocol(client) + RegisterTestDialProtocol(client) // run the test suite - vtgateconntest.TestSuite(t, client, service) - vtgateconntest.TestErrorSuite(t, service) + RunTests(t, client, service) + RunErrorTests(t, service) // and clean up client.Close() @@ -143,7 +142,7 @@ func TestGRPCVTGateConnAuth(t *testing.T) { if err != nil { t.Fatalf("dial failed: %v", err) } - vtgateconntest.RegisterTestDialProtocol(client) + RegisterTestDialProtocol(client) conn, _ := vtgateconn.DialProtocol(context.Background(), "test", "") // run the test suite _, err = conn.Begin(context.Background()) diff --git a/go/vt/vtgate/vtgateconntest/client.go b/go/vt/vtgate/grpcvtgateconn/suite_test.go similarity index 99% rename from go/vt/vtgate/vtgateconntest/client.go rename to go/vt/vtgate/grpcvtgateconn/suite_test.go index 9908dfd33cd..bf4fcbcc9a8 100644 --- a/go/vt/vtgate/vtgateconntest/client.go +++ b/go/vt/vtgate/grpcvtgateconn/suite_test.go @@ -14,9 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package vtgateconntest provides the test methods to make sure a -// vtgateconn/vtgateservice pair over RPC works correctly. -package vtgateconntest +package grpcvtgateconn + +// This is agnostic of grpc and was in a separate package 'vtgateconntest'. +// This has been moved here for better readability. If we introduce +// protocols other than grpc in the future, this will have to be +// moved back to its own package for reusability. import ( "errors" @@ -992,8 +995,8 @@ func (f *fakeVTGateService) HandlePanic(err *error) { } } -// TestSuite runs all the tests -func TestSuite(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGateService) { +// RunTests runs all the tests +func RunTests(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGateService) { vtgateconn.RegisterDialer("test", func(ctx context.Context, address string) (vtgateconn.Impl, error) { return impl, nil }) @@ -1055,8 +1058,8 @@ func TestSuite(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGa fs.panics = false } -// TestErrorSuite runs all the tests that expect errors -func TestErrorSuite(t *testing.T, fakeServer vtgateservice.VTGateService) { +// RunErrorTests runs all the tests that expect errors +func RunErrorTests(t *testing.T, fakeServer vtgateservice.VTGateService) { conn, err := vtgateconn.DialProtocol(context.Background(), "test", "") if err != nil { t.Fatalf("Got err: %v from vtgateconn.DialProtocol", err) From 3f7839ecac21b12ebdd8ba021a0469859d9ade88 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 8 Mar 2020 20:28:21 -0700 Subject: [PATCH 295/825] deprecation: delete SplitQuery code Signed-off-by: Sugu Sougoumarane --- go/sqltypes/proto3.go | 14 - go/vt/proto/query/query.pb.go | 631 ++++++----------- go/vt/proto/queryservice/queryservice.pb.go | 115 +--- go/vt/proto/vtgate/vtgate.pb.go | 647 ++++-------------- go/vt/proto/vtgateservice/vtgateservice.pb.go | 116 +--- go/vt/vitessdriver/fakeserver_test.go | 14 - go/vt/vtcombo/tablet_map.go | 24 - go/vt/vtctl/query.go | 76 -- go/vt/vtgate/fakerpcvtgateconn/conn.go | 97 +-- go/vt/vtgate/grpcvtgateconn/conn.go | 29 - go/vt/vtgate/grpcvtgateconn/suite_test.go | 136 ---- go/vt/vtgate/grpcvtgateservice/server.go | 22 - go/vt/vtgate/scatter_conn.go | 111 --- go/vt/vtgate/scatter_conn_test.go | 43 -- go/vt/vtgate/vtgate.go | 106 --- go/vt/vtgate/vtgate_test.go | 185 +---- go/vt/vtgate/vtgateconn/vtgateconn.go | 12 - go/vt/vtgate/vtgateservice/interface.go | 11 - go/vt/vttablet/grpcqueryservice/server.go | 21 - go/vt/vttablet/grpctabletconn/conn.go | 34 - go/vt/vttablet/queryservice/queryservice.go | 5 - go/vt/vttablet/queryservice/wrapped.go | 9 - go/vt/vttablet/sandboxconn/sandboxconn.go | 26 - .../tabletconntest/fakequeryservice.go | 76 -- .../vttablet/tabletconntest/tabletconntest.go | 61 -- .../tabletserver/query_executor_test.go | 1 - .../schema/schematest/schematest.go | 1 - go/vt/vttablet/tabletserver/splitquery/doc.go | 25 - .../splitquery/equal_splits_algorithm.go | 281 -------- .../splitquery/equal_splits_algorithm_test.go | 200 ------ .../tabletserver/splitquery/example_test.go | 83 --- .../splitquery/full_scan_algorithm.go | 260 ------- .../splitquery/full_scan_algorithm_test.go | 198 ------ .../splitquery/split_algorithm_interface.go | 41 -- .../tabletserver/splitquery/split_params.go | 284 -------- .../splitquery/split_params_test.go | 262 ------- .../splitquery_testing/mock_sqlexecuter.go | 49 -- .../tabletserver/splitquery/splitter.go | 259 ------- .../tabletserver/splitquery/splitter_test.go | 604 ---------------- .../splitquery/sql_executer_interface.go | 31 - .../tabletserver/splitquery/testutils_test.go | 97 --- .../vttablet/tabletserver/splitquery/utils.go | 81 --- go/vt/vttablet/tabletserver/tabletserver.go | 211 ------ .../tabletserver/tabletserver_flaky_test.go | 214 ------ proto/query.proto | 36 - proto/queryservice.proto | 4 - proto/vtgate.proto | 131 ---- proto/vtgateservice.proto | 4 - 48 files changed, 400 insertions(+), 5578 deletions(-) delete mode 100644 go/vt/vttablet/tabletserver/splitquery/doc.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm_test.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/example_test.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm_test.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/split_algorithm_interface.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/split_params.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/split_params_test.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/splitter.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/splitter_test.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/sql_executer_interface.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/testutils_test.go delete mode 100644 go/vt/vttablet/tabletserver/splitquery/utils.go diff --git a/go/sqltypes/proto3.go b/go/sqltypes/proto3.go index 88150d6559a..c42cc81c3ce 100644 --- a/go/sqltypes/proto3.go +++ b/go/sqltypes/proto3.go @@ -22,7 +22,6 @@ import ( "vitess.io/vitess/go/vt/vterrors" querypb "vitess.io/vitess/go/vt/proto/query" - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" ) // This file contains the proto3 conversion functions for the structures @@ -220,16 +219,3 @@ func Proto3ValuesEqual(v1, v2 []*querypb.Value) bool { } return true } - -// SplitQueryResponsePartsEqual compares two arrays of SplitQueryResponse_Part. -func SplitQueryResponsePartsEqual(s1, s2 []*vtgatepb.SplitQueryResponse_Part) bool { - if len(s1) != len(s2) { - return false - } - for i, s := range s1 { - if !proto.Equal(s, s2[i]) { - return false - } - } - return true -} diff --git a/go/vt/proto/query/query.pb.go b/go/vt/proto/query/query.pb.go index acbf4ea49da..1a6cd4af77a 100644 --- a/go/vt/proto/query/query.pb.go +++ b/go/vt/proto/query/query.pb.go @@ -491,31 +491,6 @@ func (StreamEvent_Statement_Category) EnumDescriptor() ([]byte, []int) { return fileDescriptor_5c6ac9b241082464, []int{12, 0, 0} } -type SplitQueryRequest_Algorithm int32 - -const ( - SplitQueryRequest_EQUAL_SPLITS SplitQueryRequest_Algorithm = 0 - SplitQueryRequest_FULL_SCAN SplitQueryRequest_Algorithm = 1 -) - -var SplitQueryRequest_Algorithm_name = map[int32]string{ - 0: "EQUAL_SPLITS", - 1: "FULL_SCAN", -} - -var SplitQueryRequest_Algorithm_value = map[string]int32{ - "EQUAL_SPLITS": 0, - "FULL_SCAN": 1, -} - -func (x SplitQueryRequest_Algorithm) String() string { - return proto.EnumName(SplitQueryRequest_Algorithm_name, int32(x)) -} - -func (SplitQueryRequest_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{50, 0} -} - // Target describes what the client expects the tablet is. // If the tablet does not match, an error is returned. type Target struct { @@ -3483,195 +3458,6 @@ func (m *MessageAckResponse) GetResult() *QueryResult { return nil } -// SplitQueryRequest is the payload for SplitQuery sent by VTGate to a VTTablet. -// See vtgate.SplitQueryRequest for more details. -type SplitQueryRequest struct { - EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId,proto3" json:"effective_caller_id,omitempty"` - ImmediateCallerId *VTGateCallerID `protobuf:"bytes,2,opt,name=immediate_caller_id,json=immediateCallerId,proto3" json:"immediate_caller_id,omitempty"` - Target *Target `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` - Query *BoundQuery `protobuf:"bytes,4,opt,name=query,proto3" json:"query,omitempty"` - SplitColumn []string `protobuf:"bytes,5,rep,name=split_column,json=splitColumn,proto3" json:"split_column,omitempty"` - // Exactly one of the following must be nonzero. - SplitCount int64 `protobuf:"varint,6,opt,name=split_count,json=splitCount,proto3" json:"split_count,omitempty"` - NumRowsPerQueryPart int64 `protobuf:"varint,8,opt,name=num_rows_per_query_part,json=numRowsPerQueryPart,proto3" json:"num_rows_per_query_part,omitempty"` - Algorithm SplitQueryRequest_Algorithm `protobuf:"varint,9,opt,name=algorithm,proto3,enum=query.SplitQueryRequest_Algorithm" json:"algorithm,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SplitQueryRequest) Reset() { *m = SplitQueryRequest{} } -func (m *SplitQueryRequest) String() string { return proto.CompactTextString(m) } -func (*SplitQueryRequest) ProtoMessage() {} -func (*SplitQueryRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{50} -} - -func (m *SplitQueryRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SplitQueryRequest.Unmarshal(m, b) -} -func (m *SplitQueryRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SplitQueryRequest.Marshal(b, m, deterministic) -} -func (m *SplitQueryRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_SplitQueryRequest.Merge(m, src) -} -func (m *SplitQueryRequest) XXX_Size() int { - return xxx_messageInfo_SplitQueryRequest.Size(m) -} -func (m *SplitQueryRequest) XXX_DiscardUnknown() { - xxx_messageInfo_SplitQueryRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_SplitQueryRequest proto.InternalMessageInfo - -func (m *SplitQueryRequest) GetEffectiveCallerId() *vtrpc.CallerID { - if m != nil { - return m.EffectiveCallerId - } - return nil -} - -func (m *SplitQueryRequest) GetImmediateCallerId() *VTGateCallerID { - if m != nil { - return m.ImmediateCallerId - } - return nil -} - -func (m *SplitQueryRequest) GetTarget() *Target { - if m != nil { - return m.Target - } - return nil -} - -func (m *SplitQueryRequest) GetQuery() *BoundQuery { - if m != nil { - return m.Query - } - return nil -} - -func (m *SplitQueryRequest) GetSplitColumn() []string { - if m != nil { - return m.SplitColumn - } - return nil -} - -func (m *SplitQueryRequest) GetSplitCount() int64 { - if m != nil { - return m.SplitCount - } - return 0 -} - -func (m *SplitQueryRequest) GetNumRowsPerQueryPart() int64 { - if m != nil { - return m.NumRowsPerQueryPart - } - return 0 -} - -func (m *SplitQueryRequest) GetAlgorithm() SplitQueryRequest_Algorithm { - if m != nil { - return m.Algorithm - } - return SplitQueryRequest_EQUAL_SPLITS -} - -// QuerySplit represents one query to execute on the tablet -type QuerySplit struct { - // query is the query to execute - Query *BoundQuery `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` - // row_count is the approximate row count the query will return - RowCount int64 `protobuf:"varint,2,opt,name=row_count,json=rowCount,proto3" json:"row_count,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *QuerySplit) Reset() { *m = QuerySplit{} } -func (m *QuerySplit) String() string { return proto.CompactTextString(m) } -func (*QuerySplit) ProtoMessage() {} -func (*QuerySplit) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{51} -} - -func (m *QuerySplit) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_QuerySplit.Unmarshal(m, b) -} -func (m *QuerySplit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_QuerySplit.Marshal(b, m, deterministic) -} -func (m *QuerySplit) XXX_Merge(src proto.Message) { - xxx_messageInfo_QuerySplit.Merge(m, src) -} -func (m *QuerySplit) XXX_Size() int { - return xxx_messageInfo_QuerySplit.Size(m) -} -func (m *QuerySplit) XXX_DiscardUnknown() { - xxx_messageInfo_QuerySplit.DiscardUnknown(m) -} - -var xxx_messageInfo_QuerySplit proto.InternalMessageInfo - -func (m *QuerySplit) GetQuery() *BoundQuery { - if m != nil { - return m.Query - } - return nil -} - -func (m *QuerySplit) GetRowCount() int64 { - if m != nil { - return m.RowCount - } - return 0 -} - -// SplitQueryResponse is returned by SplitQuery and represents all the queries -// to execute in order to get the entire data set. -type SplitQueryResponse struct { - Queries []*QuerySplit `protobuf:"bytes,1,rep,name=queries,proto3" json:"queries,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SplitQueryResponse) Reset() { *m = SplitQueryResponse{} } -func (m *SplitQueryResponse) String() string { return proto.CompactTextString(m) } -func (*SplitQueryResponse) ProtoMessage() {} -func (*SplitQueryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{52} -} - -func (m *SplitQueryResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SplitQueryResponse.Unmarshal(m, b) -} -func (m *SplitQueryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SplitQueryResponse.Marshal(b, m, deterministic) -} -func (m *SplitQueryResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_SplitQueryResponse.Merge(m, src) -} -func (m *SplitQueryResponse) XXX_Size() int { - return xxx_messageInfo_SplitQueryResponse.Size(m) -} -func (m *SplitQueryResponse) XXX_DiscardUnknown() { - xxx_messageInfo_SplitQueryResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_SplitQueryResponse proto.InternalMessageInfo - -func (m *SplitQueryResponse) GetQueries() []*QuerySplit { - if m != nil { - return m.Queries - } - return nil -} - // StreamHealthRequest is the payload for StreamHealth type StreamHealthRequest struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -3683,7 +3469,7 @@ func (m *StreamHealthRequest) Reset() { *m = StreamHealthRequest{} } func (m *StreamHealthRequest) String() string { return proto.CompactTextString(m) } func (*StreamHealthRequest) ProtoMessage() {} func (*StreamHealthRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{53} + return fileDescriptor_5c6ac9b241082464, []int{50} } func (m *StreamHealthRequest) XXX_Unmarshal(b []byte) error { @@ -3743,7 +3529,7 @@ func (m *RealtimeStats) Reset() { *m = RealtimeStats{} } func (m *RealtimeStats) String() string { return proto.CompactTextString(m) } func (*RealtimeStats) ProtoMessage() {} func (*RealtimeStats) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{54} + return fileDescriptor_5c6ac9b241082464, []int{51} } func (m *RealtimeStats) XXX_Unmarshal(b []byte) error { @@ -3832,7 +3618,7 @@ func (m *AggregateStats) Reset() { *m = AggregateStats{} } func (m *AggregateStats) String() string { return proto.CompactTextString(m) } func (*AggregateStats) ProtoMessage() {} func (*AggregateStats) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{55} + return fileDescriptor_5c6ac9b241082464, []int{52} } func (m *AggregateStats) XXX_Unmarshal(b []byte) error { @@ -3938,7 +3724,7 @@ func (m *StreamHealthResponse) Reset() { *m = StreamHealthResponse{} } func (m *StreamHealthResponse) String() string { return proto.CompactTextString(m) } func (*StreamHealthResponse) ProtoMessage() {} func (*StreamHealthResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{56} + return fileDescriptor_5c6ac9b241082464, []int{53} } func (m *StreamHealthResponse) XXX_Unmarshal(b []byte) error { @@ -4016,7 +3802,7 @@ func (m *UpdateStreamRequest) Reset() { *m = UpdateStreamRequest{} } func (m *UpdateStreamRequest) String() string { return proto.CompactTextString(m) } func (*UpdateStreamRequest) ProtoMessage() {} func (*UpdateStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{57} + return fileDescriptor_5c6ac9b241082464, []int{54} } func (m *UpdateStreamRequest) XXX_Unmarshal(b []byte) error { @@ -4084,7 +3870,7 @@ func (m *UpdateStreamResponse) Reset() { *m = UpdateStreamResponse{} } func (m *UpdateStreamResponse) String() string { return proto.CompactTextString(m) } func (*UpdateStreamResponse) ProtoMessage() {} func (*UpdateStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{58} + return fileDescriptor_5c6ac9b241082464, []int{55} } func (m *UpdateStreamResponse) XXX_Unmarshal(b []byte) error { @@ -4127,7 +3913,7 @@ func (m *TransactionMetadata) Reset() { *m = TransactionMetadata{} } func (m *TransactionMetadata) String() string { return proto.CompactTextString(m) } func (*TransactionMetadata) ProtoMessage() {} func (*TransactionMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{59} + return fileDescriptor_5c6ac9b241082464, []int{56} } func (m *TransactionMetadata) XXX_Unmarshal(b []byte) error { @@ -4185,7 +3971,6 @@ func init() { proto.RegisterEnum("query.ExecuteOptions_Workload", ExecuteOptions_Workload_name, ExecuteOptions_Workload_value) proto.RegisterEnum("query.ExecuteOptions_TransactionIsolation", ExecuteOptions_TransactionIsolation_name, ExecuteOptions_TransactionIsolation_value) proto.RegisterEnum("query.StreamEvent_Statement_Category", StreamEvent_Statement_Category_name, StreamEvent_Statement_Category_value) - proto.RegisterEnum("query.SplitQueryRequest_Algorithm", SplitQueryRequest_Algorithm_name, SplitQueryRequest_Algorithm_value) proto.RegisterType((*Target)(nil), "query.Target") proto.RegisterType((*VTGateCallerID)(nil), "query.VTGateCallerID") proto.RegisterType((*EventToken)(nil), "query.EventToken") @@ -4238,9 +4023,6 @@ func init() { proto.RegisterType((*MessageStreamResponse)(nil), "query.MessageStreamResponse") proto.RegisterType((*MessageAckRequest)(nil), "query.MessageAckRequest") proto.RegisterType((*MessageAckResponse)(nil), "query.MessageAckResponse") - proto.RegisterType((*SplitQueryRequest)(nil), "query.SplitQueryRequest") - proto.RegisterType((*QuerySplit)(nil), "query.QuerySplit") - proto.RegisterType((*SplitQueryResponse)(nil), "query.SplitQueryResponse") proto.RegisterType((*StreamHealthRequest)(nil), "query.StreamHealthRequest") proto.RegisterType((*RealtimeStats)(nil), "query.RealtimeStats") proto.RegisterType((*AggregateStats)(nil), "query.AggregateStats") @@ -4253,209 +4035,198 @@ func init() { func init() { proto.RegisterFile("query.proto", fileDescriptor_5c6ac9b241082464) } var fileDescriptor_5c6ac9b241082464 = []byte{ - // 3258 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcb, 0x73, 0xdb, 0x48, - 0x7a, 0x37, 0xf8, 0x12, 0xf9, 0x51, 0xa4, 0x5a, 0x4d, 0xc9, 0xe6, 0x68, 0x5e, 0x5a, 0xec, 0xce, - 0xae, 0xa2, 0xdd, 0xc8, 0x1e, 0x8d, 0xd7, 0x71, 0x66, 0x37, 0x89, 0x21, 0x0a, 0xf2, 0x70, 0x4c, - 0x82, 0x74, 0x13, 0xb4, 0xd7, 0x53, 0x5b, 0x85, 0x82, 0xc8, 0x36, 0x85, 0x12, 0x08, 0xd0, 0x00, - 0x28, 0x59, 0x37, 0x27, 0x9b, 0xcd, 0xfb, 0x31, 0x79, 0x4e, 0x36, 0xa9, 0x4c, 0x52, 0x95, 0x43, - 0x2a, 0x97, 0xfc, 0x0d, 0xa9, 0x1c, 0x72, 0xcc, 0x2d, 0x87, 0x24, 0x87, 0x9c, 0x52, 0xb9, 0x6d, - 0xe5, 0x94, 0x43, 0x0e, 0xa9, 0x54, 0x3f, 0x00, 0x82, 0x12, 0x67, 0xec, 0x75, 0x72, 0x91, 0x67, - 0x6e, 0xdd, 0xdf, 0xf7, 0xf5, 0xe3, 0xf7, 0xfb, 0x3e, 0x7c, 0xdd, 0xe8, 0x6e, 0x28, 0x3f, 0x99, - 0xd2, 0xe0, 0x6c, 0x67, 0x12, 0xf8, 0x91, 0x8f, 0xf3, 0xbc, 0xb2, 0x51, 0x8d, 0xfc, 0x89, 0x3f, - 0xb4, 0x23, 0x5b, 0x88, 0x37, 0xca, 0x27, 0x51, 0x30, 0x19, 0x88, 0x8a, 0xfa, 0x43, 0x05, 0x0a, - 0xa6, 0x1d, 0x8c, 0x68, 0x84, 0x37, 0xa0, 0x78, 0x4c, 0xcf, 0xc2, 0x89, 0x3d, 0xa0, 0x75, 0x65, - 0x53, 0xd9, 0x2a, 0x91, 0xa4, 0x8e, 0xd7, 0x20, 0x1f, 0x1e, 0xd9, 0xc1, 0xb0, 0x9e, 0xe1, 0x0a, - 0x51, 0xc1, 0xdf, 0x86, 0x72, 0x64, 0x1f, 0xba, 0x34, 0xb2, 0xa2, 0xb3, 0x09, 0xad, 0x67, 0x37, - 0x95, 0xad, 0xea, 0xee, 0xda, 0x4e, 0x32, 0x9e, 0xc9, 0x95, 0xe6, 0xd9, 0x84, 0x12, 0x88, 0x92, - 0x32, 0xc6, 0x90, 0x1b, 0x50, 0xd7, 0xad, 0xe7, 0x78, 0x5f, 0xbc, 0xac, 0xee, 0x43, 0xf5, 0x81, - 0x79, 0xd7, 0x8e, 0x68, 0xc3, 0x76, 0x5d, 0x1a, 0x34, 0xf7, 0xd9, 0x74, 0xa6, 0x21, 0x0d, 0x3c, - 0x7b, 0x9c, 0x4c, 0x27, 0xae, 0xe3, 0xab, 0x50, 0x18, 0x05, 0xfe, 0x74, 0x12, 0xd6, 0x33, 0x9b, - 0xd9, 0xad, 0x12, 0x91, 0x35, 0xf5, 0xfb, 0x00, 0xfa, 0x09, 0xf5, 0x22, 0xd3, 0x3f, 0xa6, 0x1e, - 0x7e, 0x03, 0x4a, 0x91, 0x33, 0xa6, 0x61, 0x64, 0x8f, 0x27, 0xbc, 0x8b, 0x2c, 0x99, 0x09, 0x3e, - 0x03, 0xd2, 0x06, 0x14, 0x27, 0x7e, 0xe8, 0x44, 0x8e, 0xef, 0x71, 0x3c, 0x25, 0x92, 0xd4, 0xd5, - 0x9f, 0x87, 0xfc, 0x03, 0xdb, 0x9d, 0x52, 0xfc, 0x36, 0xe4, 0x38, 0x60, 0x85, 0x03, 0x2e, 0xef, - 0x08, 0xd2, 0x39, 0x4e, 0xae, 0x60, 0x7d, 0x9f, 0x30, 0x4b, 0xde, 0xf7, 0x32, 0x11, 0x15, 0xf5, - 0x18, 0x96, 0xf7, 0x1c, 0x6f, 0xf8, 0xc0, 0x0e, 0x1c, 0x46, 0xc6, 0x4b, 0x76, 0x83, 0xbf, 0x06, - 0x05, 0x5e, 0x08, 0xeb, 0xd9, 0xcd, 0xec, 0x56, 0x79, 0x77, 0x59, 0x36, 0xe4, 0x73, 0x23, 0x52, - 0xa7, 0xfe, 0xbd, 0x02, 0xb0, 0xe7, 0x4f, 0xbd, 0xe1, 0x7d, 0xa6, 0xc4, 0x08, 0xb2, 0xe1, 0x13, - 0x57, 0x12, 0xc9, 0x8a, 0xf8, 0x1e, 0x54, 0x0f, 0x1d, 0x6f, 0x68, 0x9d, 0xc8, 0xe9, 0x08, 0x2e, - 0xcb, 0xbb, 0x5f, 0x93, 0xdd, 0xcd, 0x1a, 0xef, 0xa4, 0x67, 0x1d, 0xea, 0x5e, 0x14, 0x9c, 0x91, - 0xca, 0x61, 0x5a, 0xb6, 0xd1, 0x07, 0x7c, 0xd1, 0x88, 0x0d, 0x7a, 0x4c, 0xcf, 0xe2, 0x41, 0x8f, - 0xe9, 0x19, 0xfe, 0xa9, 0x34, 0xa2, 0xf2, 0x6e, 0x2d, 0x1e, 0x2b, 0xd5, 0x56, 0xc2, 0x7c, 0x3f, - 0x73, 0x5b, 0x51, 0xff, 0xa2, 0x00, 0x55, 0xfd, 0x29, 0x1d, 0x4c, 0x23, 0xda, 0x99, 0x30, 0x1f, - 0x84, 0x78, 0x07, 0x6a, 0x8e, 0x37, 0x70, 0xa7, 0x43, 0x6a, 0x51, 0xe6, 0x6a, 0x2b, 0x62, 0xbe, - 0xe6, 0xfd, 0x15, 0xc9, 0xaa, 0x54, 0xa5, 0x82, 0x40, 0x83, 0xda, 0xc0, 0x1f, 0x4f, 0xec, 0x60, - 0xde, 0x3e, 0xcb, 0xc7, 0x5f, 0x95, 0xe3, 0xcf, 0xec, 0xc9, 0xaa, 0xb4, 0x4e, 0x75, 0xd1, 0x86, - 0x15, 0xd9, 0xef, 0xd0, 0x7a, 0xec, 0x50, 0x77, 0x18, 0xf2, 0xd0, 0xad, 0x26, 0x54, 0xcd, 0x4f, - 0x71, 0xa7, 0x29, 0x8d, 0x0f, 0xb8, 0x2d, 0xa9, 0x3a, 0x73, 0x75, 0xbc, 0x0d, 0xab, 0x03, 0xd7, - 0x61, 0x53, 0x79, 0xcc, 0x28, 0xb6, 0x02, 0xff, 0x34, 0xac, 0xe7, 0xf9, 0xfc, 0x57, 0x84, 0xe2, - 0x80, 0xc9, 0x89, 0x7f, 0x1a, 0xe2, 0xf7, 0xa1, 0x78, 0xea, 0x07, 0xc7, 0xae, 0x6f, 0x0f, 0xeb, - 0x05, 0x3e, 0xe6, 0x5b, 0x8b, 0xc7, 0x7c, 0x28, 0xad, 0x48, 0x62, 0x8f, 0xb7, 0x00, 0x85, 0x4f, - 0x5c, 0x2b, 0xa4, 0x2e, 0x1d, 0x44, 0x96, 0xeb, 0x8c, 0x9d, 0xa8, 0x5e, 0xe4, 0x5f, 0x41, 0x35, - 0x7c, 0xe2, 0xf6, 0xb8, 0xb8, 0xc5, 0xa4, 0xd8, 0x82, 0xf5, 0x28, 0xb0, 0xbd, 0xd0, 0x1e, 0xb0, - 0xce, 0x2c, 0x27, 0xf4, 0x5d, 0x9b, 0x7f, 0x01, 0x25, 0x3e, 0xe4, 0xf6, 0xe2, 0x21, 0xcd, 0x59, - 0x93, 0x66, 0xdc, 0x82, 0xac, 0x45, 0x0b, 0xa4, 0xf8, 0x5d, 0x58, 0x0f, 0x8f, 0x9d, 0x89, 0xc5, - 0xfb, 0xb1, 0x26, 0xae, 0xed, 0x59, 0x03, 0x7b, 0x70, 0x44, 0xeb, 0xc0, 0x61, 0x63, 0xa6, 0xe4, - 0xa1, 0xd6, 0x75, 0x6d, 0xaf, 0xc1, 0x34, 0xea, 0x77, 0xa0, 0x3a, 0xcf, 0x23, 0x5e, 0x85, 0x8a, - 0xf9, 0xa8, 0xab, 0x5b, 0x9a, 0xb1, 0x6f, 0x19, 0x5a, 0x5b, 0x47, 0x57, 0x70, 0x05, 0x4a, 0x5c, - 0xd4, 0x31, 0x5a, 0x8f, 0x90, 0x82, 0x97, 0x20, 0xab, 0xb5, 0x5a, 0x28, 0xa3, 0xde, 0x86, 0x62, - 0x4c, 0x08, 0x5e, 0x81, 0x72, 0xdf, 0xe8, 0x75, 0xf5, 0x46, 0xf3, 0xa0, 0xa9, 0xef, 0xa3, 0x2b, - 0xb8, 0x08, 0xb9, 0x4e, 0xcb, 0xec, 0x22, 0x45, 0x94, 0xb4, 0x2e, 0xca, 0xb0, 0x96, 0xfb, 0x7b, - 0x1a, 0xca, 0xaa, 0x7f, 0xad, 0xc0, 0xda, 0x22, 0x60, 0xb8, 0x0c, 0x4b, 0xfb, 0xfa, 0x81, 0xd6, - 0x6f, 0x99, 0xe8, 0x0a, 0xae, 0xc1, 0x0a, 0xd1, 0xbb, 0xba, 0x66, 0x6a, 0x7b, 0x2d, 0xdd, 0x22, - 0xba, 0xb6, 0x8f, 0x14, 0x8c, 0xa1, 0xca, 0x4a, 0x56, 0xa3, 0xd3, 0x6e, 0x37, 0x4d, 0x53, 0xdf, - 0x47, 0x19, 0xbc, 0x06, 0x88, 0xcb, 0xfa, 0xc6, 0x4c, 0x9a, 0xc5, 0x08, 0x96, 0x7b, 0x3a, 0x69, - 0x6a, 0xad, 0xe6, 0x47, 0xac, 0x03, 0x94, 0xc3, 0x5f, 0x81, 0x37, 0x1b, 0x1d, 0xa3, 0xd7, 0xec, - 0x99, 0xba, 0x61, 0x5a, 0x3d, 0x43, 0xeb, 0xf6, 0x3e, 0xe8, 0x98, 0xbc, 0x67, 0x01, 0x2e, 0x8f, - 0xab, 0x00, 0x5a, 0xdf, 0xec, 0x88, 0x7e, 0x50, 0xe1, 0xc3, 0x5c, 0x51, 0x41, 0x19, 0xf5, 0x93, - 0x0c, 0xe4, 0x39, 0x3f, 0x2c, 0xab, 0xa6, 0x72, 0x25, 0x2f, 0x27, 0x19, 0x26, 0xf3, 0x39, 0x19, - 0x86, 0x27, 0x66, 0x99, 0xeb, 0x44, 0x05, 0xbf, 0x0e, 0x25, 0x3f, 0x18, 0x59, 0x42, 0x23, 0xb2, - 0x74, 0xd1, 0x0f, 0x46, 0x3c, 0x9d, 0xb3, 0x0c, 0xc9, 0x92, 0xfb, 0xa1, 0x1d, 0x52, 0x1e, 0xb5, - 0x25, 0x92, 0xd4, 0xf1, 0x6b, 0xc0, 0xec, 0x2c, 0x3e, 0x8f, 0x02, 0xd7, 0x2d, 0xf9, 0xc1, 0xc8, - 0x60, 0x53, 0xf9, 0x2a, 0x54, 0x06, 0xbe, 0x3b, 0x1d, 0x7b, 0x96, 0x4b, 0xbd, 0x51, 0x74, 0x54, - 0x5f, 0xda, 0x54, 0xb6, 0x2a, 0x64, 0x59, 0x08, 0x5b, 0x5c, 0x86, 0xeb, 0xb0, 0x34, 0x38, 0xb2, - 0x83, 0x90, 0x8a, 0x48, 0xad, 0x90, 0xb8, 0xca, 0x47, 0xa5, 0x03, 0x67, 0x6c, 0xbb, 0x21, 0x8f, - 0xca, 0x0a, 0x49, 0xea, 0x0c, 0xc4, 0x63, 0xd7, 0x1e, 0x85, 0x3c, 0x9a, 0x2a, 0x44, 0x54, 0xd4, - 0x9f, 0x81, 0x2c, 0xf1, 0x4f, 0x59, 0x97, 0x62, 0xc0, 0xb0, 0xae, 0x6c, 0x66, 0xb7, 0x30, 0x89, - 0xab, 0x6c, 0x11, 0x91, 0x79, 0x54, 0xa4, 0xd7, 0x38, 0x73, 0x7e, 0x1f, 0x96, 0x09, 0x0d, 0xa7, - 0x6e, 0xa4, 0x3f, 0x8d, 0x02, 0x3b, 0xc4, 0xbb, 0x50, 0x4e, 0x67, 0x0e, 0xe5, 0xb3, 0x32, 0x07, - 0xd0, 0x59, 0xca, 0xa8, 0xc3, 0xd2, 0xe3, 0x80, 0x86, 0x47, 0x34, 0x90, 0x99, 0x29, 0xae, 0xb2, - 0xbc, 0x5c, 0xe6, 0xa1, 0x2e, 0xc6, 0x60, 0xd9, 0x5c, 0xe6, 0x14, 0x65, 0x2e, 0x9b, 0x73, 0xa7, - 0x12, 0xa9, 0x63, 0xec, 0xb1, 0x34, 0x61, 0xd9, 0x8f, 0x1f, 0xd3, 0x41, 0x44, 0xc5, 0xa2, 0x95, - 0x23, 0xcb, 0x4c, 0xa8, 0x49, 0x19, 0x73, 0x9b, 0xe3, 0x85, 0x34, 0x88, 0x2c, 0x67, 0xc8, 0x1d, - 0x9a, 0x23, 0x45, 0x21, 0x68, 0x0e, 0xf1, 0x5b, 0x90, 0xe3, 0x89, 0x26, 0xc7, 0x47, 0x01, 0x39, - 0x0a, 0xf1, 0x4f, 0x09, 0x97, 0xe3, 0x6f, 0x42, 0x81, 0x72, 0xbc, 0xdc, 0xa9, 0xb3, 0xd4, 0x9c, - 0xa6, 0x82, 0x48, 0x13, 0xf5, 0xbb, 0xb0, 0xcc, 0x31, 0x3c, 0xb4, 0x03, 0xcf, 0xf1, 0x46, 0x7c, - 0x45, 0xf7, 0x87, 0x22, 0xf6, 0x2a, 0x84, 0x97, 0x19, 0x05, 0x63, 0x1a, 0x86, 0xf6, 0x88, 0xca, - 0x15, 0x36, 0xae, 0xaa, 0x7f, 0x99, 0x85, 0x72, 0x2f, 0x0a, 0xa8, 0x3d, 0xe6, 0xec, 0xe1, 0xef, - 0x02, 0x84, 0x91, 0x1d, 0xd1, 0x31, 0xf5, 0xa2, 0x98, 0x86, 0x37, 0xe4, 0xf0, 0x29, 0xbb, 0x9d, - 0x5e, 0x6c, 0x44, 0x52, 0xf6, 0xe7, 0xdd, 0x93, 0x79, 0x01, 0xf7, 0x6c, 0x7c, 0x9a, 0x81, 0x52, - 0xd2, 0x1b, 0xd6, 0xa0, 0x38, 0xb0, 0x23, 0x3a, 0xf2, 0x83, 0x33, 0xb9, 0x16, 0xbf, 0xf3, 0x79, - 0xa3, 0xef, 0x34, 0xa4, 0x31, 0x49, 0x9a, 0xe1, 0x37, 0x41, 0x6c, 0x70, 0x44, 0xe8, 0x0b, 0xbc, - 0x25, 0x2e, 0xe1, 0xc1, 0xff, 0x3e, 0xe0, 0x49, 0xe0, 0x8c, 0xed, 0xe0, 0xcc, 0x3a, 0xa6, 0x67, - 0xf1, 0x22, 0x92, 0x5d, 0xe0, 0x70, 0x24, 0xed, 0xee, 0xd1, 0x33, 0x99, 0xf6, 0x6e, 0xcf, 0xb7, - 0x95, 0x21, 0x7b, 0xd1, 0x8d, 0xa9, 0x96, 0x7c, 0x27, 0x10, 0xc6, 0x6b, 0x7e, 0x9e, 0x47, 0x37, - 0x2b, 0xaa, 0xdf, 0x80, 0x62, 0x3c, 0x79, 0x5c, 0x82, 0xbc, 0x1e, 0x04, 0x7e, 0x80, 0xae, 0xf0, - 0xec, 0xd7, 0x6e, 0x89, 0x04, 0xba, 0xbf, 0xcf, 0x12, 0xe8, 0xdf, 0x65, 0x92, 0x85, 0x97, 0xd0, - 0x27, 0x53, 0x1a, 0x46, 0xf8, 0x17, 0xa0, 0x46, 0x79, 0xa4, 0x39, 0x27, 0xd4, 0x1a, 0xf0, 0x5d, - 0x1a, 0x8b, 0x33, 0xf1, 0x39, 0xac, 0xec, 0x88, 0x4d, 0x65, 0xbc, 0x7b, 0x23, 0xab, 0x89, 0xad, - 0x14, 0x0d, 0xb1, 0x0e, 0x35, 0x67, 0x3c, 0xa6, 0x43, 0xc7, 0x8e, 0xd2, 0x1d, 0x08, 0x87, 0xad, - 0xc7, 0x9b, 0x98, 0xb9, 0x4d, 0x20, 0x59, 0x4d, 0x5a, 0x24, 0xdd, 0xbc, 0x03, 0x85, 0x88, 0x6f, - 0x58, 0xe5, 0x1a, 0x5e, 0x89, 0xb3, 0x1a, 0x17, 0x12, 0xa9, 0xc4, 0xdf, 0x00, 0xb1, 0xfd, 0xe5, - 0xf9, 0x6b, 0x16, 0x10, 0xb3, 0x5d, 0x0d, 0x11, 0x7a, 0xfc, 0x0e, 0x54, 0xe7, 0x16, 0xbf, 0x21, - 0x27, 0x2c, 0x4b, 0x2a, 0xe9, 0x95, 0x6c, 0x88, 0xaf, 0xc3, 0x92, 0x2f, 0x16, 0x3e, 0x9e, 0xd9, - 0x66, 0x33, 0x9e, 0x5f, 0x15, 0x49, 0x6c, 0xa5, 0xfe, 0x1c, 0xac, 0x24, 0x0c, 0x86, 0x13, 0xdf, - 0x0b, 0x29, 0xde, 0x86, 0x42, 0xc0, 0x3f, 0x27, 0xc9, 0x1a, 0x96, 0x5d, 0xa4, 0xf2, 0x01, 0x91, - 0x16, 0xea, 0x10, 0x56, 0x84, 0xe4, 0xa1, 0x13, 0x1d, 0x71, 0x47, 0xe1, 0x77, 0x20, 0x4f, 0x59, - 0xe1, 0x1c, 0xe7, 0xa4, 0xdb, 0xe0, 0x7a, 0x22, 0xb4, 0xa9, 0x51, 0x32, 0xcf, 0x1d, 0xe5, 0x3f, - 0x33, 0x50, 0x93, 0xb3, 0xdc, 0xb3, 0xa3, 0xc1, 0xd1, 0x25, 0x75, 0xf6, 0x37, 0x61, 0x89, 0xc9, - 0x9d, 0xe4, 0xc3, 0x58, 0xe0, 0xee, 0xd8, 0x82, 0x39, 0xdc, 0x0e, 0xad, 0x94, 0x77, 0xe5, 0xe6, - 0xab, 0x62, 0x87, 0xa9, 0x95, 0x7f, 0x41, 0x5c, 0x14, 0x9e, 0x13, 0x17, 0x4b, 0x2f, 0x14, 0x17, - 0xfb, 0xb0, 0x36, 0xcf, 0xb8, 0x0c, 0x8e, 0x6f, 0xc1, 0x92, 0x70, 0x4a, 0x9c, 0x02, 0x17, 0xf9, - 0x2d, 0x36, 0x51, 0xff, 0x21, 0x03, 0x6b, 0x32, 0x3b, 0x7d, 0x31, 0x3e, 0xd3, 0x14, 0xcf, 0xf9, - 0x17, 0xe1, 0xf9, 0x05, 0xfd, 0xa7, 0x36, 0x60, 0xfd, 0x1c, 0x8f, 0x2f, 0xf1, 0xb1, 0xfe, 0x58, - 0x81, 0xe5, 0x3d, 0x3a, 0x72, 0xbc, 0x4b, 0xea, 0x85, 0x14, 0xb9, 0xb9, 0x17, 0x0a, 0xe2, 0x5b, - 0x50, 0x91, 0x78, 0x25, 0x5b, 0x17, 0xd9, 0x56, 0x16, 0xb1, 0xfd, 0xef, 0x0a, 0x54, 0x1a, 0xfe, - 0x78, 0xec, 0x44, 0x97, 0x94, 0xa9, 0x8b, 0x38, 0x73, 0x8b, 0x70, 0x22, 0xa8, 0xc6, 0x30, 0x05, - 0x41, 0xea, 0x7f, 0x28, 0xb0, 0x42, 0x7c, 0xd7, 0x3d, 0xb4, 0x07, 0xc7, 0xaf, 0x36, 0x76, 0x0c, - 0x68, 0x06, 0x54, 0xa2, 0xff, 0x6f, 0x05, 0xaa, 0xdd, 0x80, 0xb2, 0x1f, 0xeb, 0x57, 0x1a, 0x3c, - 0xdb, 0x09, 0x0f, 0x23, 0xb9, 0x87, 0x28, 0x11, 0x5e, 0x56, 0x57, 0x61, 0x25, 0xc1, 0x2e, 0xf9, - 0xf8, 0x17, 0x05, 0xd6, 0x45, 0x80, 0x48, 0xcd, 0xf0, 0x92, 0xd2, 0x12, 0xe3, 0xcd, 0xa5, 0xf0, - 0xd6, 0xe1, 0xea, 0x79, 0x6c, 0x12, 0xf6, 0x0f, 0x32, 0x70, 0x2d, 0x8e, 0x8d, 0x4b, 0x0e, 0xfc, - 0xff, 0x10, 0x0f, 0x1b, 0x50, 0xbf, 0x48, 0x82, 0x64, 0xe8, 0xe3, 0x0c, 0xd4, 0x1b, 0x01, 0xb5, - 0x23, 0x9a, 0xda, 0x8b, 0xbc, 0x3a, 0xb1, 0x81, 0xdf, 0x85, 0xe5, 0x89, 0x1d, 0x44, 0xce, 0xc0, - 0x99, 0xd8, 0xec, 0x6f, 0x2f, 0xcf, 0xb7, 0x3a, 0xe7, 0x3a, 0x98, 0x33, 0x51, 0x5f, 0x87, 0xd7, - 0x16, 0x30, 0x22, 0xf9, 0xfa, 0x1f, 0x05, 0x70, 0x2f, 0xb2, 0x83, 0xe8, 0x0b, 0xb0, 0xaa, 0x2c, - 0x0c, 0xa6, 0x75, 0xa8, 0xcd, 0xe1, 0x4f, 0xf3, 0x42, 0xa3, 0x2f, 0xc4, 0x8a, 0xf3, 0x99, 0xbc, - 0xa4, 0xf1, 0x4b, 0x5e, 0xfe, 0x4d, 0x81, 0x8d, 0x86, 0x2f, 0x0e, 0x16, 0x5f, 0xc9, 0x2f, 0x4c, - 0x7d, 0x13, 0x5e, 0x5f, 0x08, 0x50, 0x12, 0xf0, 0xaf, 0x0a, 0x5c, 0x25, 0xd4, 0x1e, 0xbe, 0x9a, - 0xe0, 0xef, 0xc3, 0xb5, 0x0b, 0xe0, 0xe4, 0x0e, 0xf5, 0x16, 0x14, 0xc7, 0x34, 0xb2, 0x87, 0x76, - 0x64, 0x4b, 0x48, 0x1b, 0x71, 0xbf, 0x33, 0xeb, 0xb6, 0xb4, 0x20, 0x89, 0xad, 0xfa, 0x69, 0x06, - 0x6a, 0x7c, 0xaf, 0xfb, 0xe5, 0x8f, 0xd6, 0xe2, 0x7f, 0x81, 0x8f, 0x15, 0x58, 0x9b, 0x27, 0x28, - 0xf9, 0x27, 0xf8, 0xff, 0x3e, 0xaf, 0x58, 0x90, 0x10, 0xb2, 0x8b, 0xb6, 0xa0, 0xff, 0x98, 0x81, - 0x7a, 0x7a, 0x4a, 0x5f, 0x9e, 0x6d, 0xcc, 0x9f, 0x6d, 0xfc, 0xc4, 0x87, 0x59, 0x9f, 0x28, 0xf0, - 0xda, 0x02, 0x42, 0x7f, 0x32, 0x47, 0xa7, 0x4e, 0x38, 0x32, 0xcf, 0x3d, 0xe1, 0x78, 0x51, 0x57, - 0xff, 0xb3, 0x02, 0x6b, 0x6d, 0x71, 0xb0, 0x2c, 0xfe, 0xe3, 0x2f, 0x6f, 0x36, 0xe3, 0x67, 0xc7, - 0xb9, 0xd9, 0xf5, 0x8d, 0xda, 0x80, 0xf5, 0x73, 0xd0, 0x5e, 0xe2, 0x6c, 0xe2, 0xbf, 0x14, 0x58, - 0x95, 0xbd, 0x68, 0x97, 0x76, 0x23, 0xb0, 0x80, 0x1d, 0xfc, 0x16, 0x64, 0x9d, 0x61, 0xbc, 0x83, - 0x9c, 0xbf, 0x04, 0x67, 0x0a, 0xf5, 0x0e, 0xe0, 0x34, 0xee, 0x97, 0xa0, 0xee, 0x9f, 0xb2, 0xb0, - 0xda, 0x9b, 0xb8, 0x4e, 0x24, 0x95, 0xaf, 0x76, 0xe2, 0xff, 0x0a, 0x2c, 0x87, 0x0c, 0xac, 0x25, - 0xae, 0xe4, 0x38, 0xb1, 0x25, 0x52, 0xe6, 0xb2, 0x06, 0x17, 0xe1, 0xb7, 0xa1, 0x1c, 0x9b, 0x4c, - 0xbd, 0x48, 0x1e, 0xa8, 0x81, 0xb4, 0x98, 0x7a, 0x11, 0xbe, 0x09, 0xd7, 0xbc, 0xe9, 0x98, 0x5f, - 0x69, 0x5b, 0x13, 0x1a, 0xc4, 0x17, 0xbe, 0x76, 0x10, 0x5f, 0x3d, 0xd7, 0xbc, 0xe9, 0x98, 0xf8, - 0xa7, 0x61, 0x97, 0x06, 0xe2, 0xc2, 0xd7, 0x0e, 0x22, 0x7c, 0x07, 0x4a, 0xb6, 0x3b, 0xf2, 0x03, - 0x27, 0x3a, 0x1a, 0xcb, 0x3b, 0x67, 0x35, 0xbe, 0x81, 0x39, 0x4f, 0xff, 0x8e, 0x16, 0x5b, 0x92, - 0x59, 0x23, 0xf5, 0x5b, 0x50, 0x4a, 0xe4, 0x18, 0xc1, 0xb2, 0x7e, 0xbf, 0xaf, 0xb5, 0xac, 0x5e, - 0xb7, 0xd5, 0x34, 0x7b, 0xe2, 0x9e, 0xf8, 0xa0, 0xdf, 0x6a, 0x59, 0xbd, 0x86, 0x66, 0x20, 0x45, - 0x25, 0x00, 0xbc, 0x4b, 0xde, 0xf9, 0x8c, 0x20, 0xe5, 0x39, 0x04, 0xbd, 0x0e, 0xa5, 0xc0, 0x3f, - 0x95, 0xd8, 0x33, 0x1c, 0x4e, 0x31, 0xf0, 0x4f, 0x39, 0x72, 0x55, 0x03, 0x9c, 0x9e, 0xab, 0x8c, - 0xb6, 0x54, 0xf2, 0x56, 0xe6, 0x92, 0xf7, 0x6c, 0xfc, 0x24, 0x79, 0x8b, 0xad, 0x3c, 0xfb, 0xce, - 0x3f, 0xa0, 0xb6, 0x1b, 0xc5, 0xeb, 0x95, 0xfa, 0x57, 0x19, 0xa8, 0x10, 0x26, 0x71, 0xc6, 0xb4, - 0x17, 0xd9, 0x51, 0xc8, 0x3c, 0x75, 0xc4, 0x4d, 0xac, 0x59, 0xda, 0x2d, 0x91, 0xb2, 0x90, 0x89, - 0xbb, 0x82, 0x5d, 0x58, 0x0f, 0xe9, 0xc0, 0xf7, 0x86, 0xa1, 0x75, 0x48, 0x8f, 0x1c, 0x6f, 0x68, - 0x8d, 0xed, 0x30, 0x92, 0xd7, 0x91, 0x15, 0x52, 0x93, 0xca, 0x3d, 0xae, 0x6b, 0x73, 0x15, 0xbe, - 0x01, 0x6b, 0x87, 0x8e, 0xe7, 0xfa, 0x23, 0x6b, 0xe2, 0xda, 0x67, 0x34, 0x08, 0x25, 0x54, 0x16, - 0x5e, 0x79, 0x82, 0x85, 0xae, 0x2b, 0x54, 0xc2, 0xdd, 0x1f, 0xc1, 0xf6, 0xc2, 0x51, 0xac, 0xc7, - 0x8e, 0x1b, 0xd1, 0x80, 0x0e, 0xad, 0x80, 0x4e, 0x5c, 0x67, 0x20, 0x5e, 0x13, 0x88, 0xbd, 0xfb, - 0xd7, 0x17, 0x0c, 0x7d, 0x20, 0xcd, 0xc9, 0xcc, 0x9a, 0xb1, 0x3d, 0x98, 0x4c, 0xad, 0x29, 0xbf, - 0x41, 0x64, 0xab, 0x98, 0x42, 0x8a, 0x83, 0xc9, 0xb4, 0xcf, 0xea, 0x18, 0x41, 0xf6, 0xc9, 0x44, - 0x2c, 0x5e, 0x0a, 0x61, 0x45, 0xf5, 0xc7, 0x0a, 0x54, 0xb5, 0xd1, 0x28, 0xa0, 0x23, 0x3b, 0x92, - 0x34, 0xdd, 0x80, 0x35, 0x41, 0xc9, 0x99, 0x25, 0x9f, 0x29, 0x09, 0x3c, 0x8a, 0xc0, 0x23, 0x75, - 0xe2, 0x91, 0x52, 0x1c, 0xbe, 0x57, 0xa7, 0xde, 0xc2, 0x36, 0x19, 0xde, 0x66, 0x2d, 0xd1, 0xa6, - 0x5b, 0xfd, 0x2c, 0xbc, 0xb6, 0x98, 0x85, 0xb1, 0x23, 0x1e, 0x9a, 0x54, 0xc8, 0xd5, 0x05, 0xa0, - 0xdb, 0x8e, 0xf7, 0x39, 0x4d, 0xed, 0xa7, 0x9c, 0xaf, 0xcf, 0x68, 0x6a, 0x3f, 0x55, 0xff, 0x26, - 0xb9, 0x01, 0x88, 0xc3, 0x25, 0x59, 0x8d, 0xe3, 0xbc, 0xa0, 0x7c, 0x5e, 0x5e, 0xa8, 0xc3, 0x52, - 0x48, 0x83, 0x13, 0xc7, 0x1b, 0xc5, 0x57, 0xd4, 0xb2, 0x8a, 0x7b, 0xf0, 0x75, 0x89, 0x9d, 0x3e, - 0x8d, 0x68, 0xe0, 0xd9, 0xae, 0x7b, 0x66, 0x89, 0x83, 0x0a, 0x2f, 0xa2, 0x43, 0x6b, 0xf6, 0xa8, - 0x4a, 0xac, 0xc8, 0x5f, 0x15, 0xd6, 0x7a, 0x62, 0x4c, 0x12, 0x5b, 0x33, 0x79, 0x6e, 0xf5, 0x1d, - 0xa8, 0x06, 0x32, 0x88, 0xad, 0x90, 0xb9, 0x47, 0xe6, 0xa3, 0xb5, 0xe4, 0x9e, 0x39, 0x15, 0xe1, - 0xa4, 0x12, 0xcc, 0x05, 0xfc, 0x6d, 0x58, 0x96, 0x33, 0xb2, 0x5d, 0xc7, 0x9e, 0x6d, 0x4c, 0xcf, - 0xbd, 0x34, 0xd3, 0x98, 0x92, 0xc8, 0x37, 0x69, 0xbc, 0xf2, 0x61, 0xae, 0x58, 0x40, 0x4b, 0xec, - 0x6f, 0xb8, 0xd6, 0x9f, 0x0c, 0x79, 0x64, 0x5c, 0xe2, 0x3d, 0x42, 0xfa, 0x71, 0x5a, 0x6e, 0xfe, - 0x71, 0xda, 0xfc, 0x63, 0xb7, 0xfc, 0xb9, 0xc7, 0x6e, 0xea, 0x1d, 0x58, 0x9b, 0xc7, 0x2f, 0x63, - 0x65, 0x0b, 0xf2, 0xfc, 0x5a, 0xfc, 0xdc, 0x62, 0x98, 0xba, 0xf7, 0x26, 0xc2, 0x40, 0xfd, 0x5b, - 0x05, 0x6a, 0x0b, 0x7e, 0x94, 0x92, 0xbf, 0x30, 0x25, 0x75, 0xc8, 0xf3, 0xd3, 0x90, 0xe7, 0x17, - 0xf4, 0xf2, 0xdd, 0xc9, 0xb5, 0x8b, 0xff, 0x59, 0xfc, 0x32, 0x9d, 0x08, 0x2b, 0x96, 0xce, 0x78, - 0x58, 0x0c, 0xf8, 0x29, 0x4f, 0xbc, 0xcf, 0x2b, 0x33, 0x99, 0x38, 0xf8, 0xb9, 0x78, 0x6c, 0x94, - 0x7b, 0xee, 0xb1, 0xd1, 0xf6, 0xef, 0x67, 0xa1, 0xd4, 0x3e, 0xeb, 0x3d, 0x71, 0x0f, 0x5c, 0x7b, - 0xc4, 0x6f, 0xbb, 0xdb, 0x5d, 0xf3, 0x11, 0xba, 0x82, 0x57, 0xa1, 0x62, 0x74, 0x4c, 0xcb, 0x60, - 0x0b, 0xc2, 0x41, 0x4b, 0xbb, 0x8b, 0x14, 0xb6, 0x62, 0x74, 0x49, 0xd3, 0xba, 0xa7, 0x3f, 0x12, - 0x92, 0x0c, 0xae, 0xc1, 0x4a, 0xdf, 0x68, 0xde, 0xef, 0xeb, 0x33, 0x61, 0x0e, 0xaf, 0xc3, 0x6a, - 0xbb, 0xdf, 0x32, 0x9b, 0xdd, 0x56, 0x4a, 0x5c, 0x64, 0xab, 0xcb, 0x5e, 0xab, 0xb3, 0x27, 0xaa, - 0x88, 0xf5, 0xdf, 0x37, 0x7a, 0xcd, 0xbb, 0x86, 0xbe, 0x2f, 0x44, 0x9b, 0x4c, 0xf4, 0x91, 0x4e, - 0x3a, 0x07, 0xcd, 0x78, 0xc8, 0x3b, 0x18, 0x41, 0x79, 0xaf, 0x69, 0x68, 0x44, 0xf6, 0xf2, 0x4c, - 0xc1, 0x55, 0x28, 0xe9, 0x46, 0xbf, 0x2d, 0xeb, 0x19, 0x5c, 0x87, 0x9a, 0xd6, 0x37, 0x3b, 0x56, - 0xd3, 0x68, 0x10, 0xbd, 0xad, 0x1b, 0xa6, 0xd4, 0xe4, 0x70, 0x0d, 0xaa, 0x66, 0xb3, 0xad, 0xf7, - 0x4c, 0xad, 0xdd, 0x95, 0x42, 0x36, 0x8b, 0x62, 0x4f, 0x8f, 0x6d, 0x10, 0xde, 0x80, 0x75, 0xa3, - 0x63, 0xc9, 0x27, 0x4b, 0xd6, 0x03, 0xad, 0xd5, 0xd7, 0xa5, 0x6e, 0x13, 0x5f, 0x03, 0xdc, 0x31, - 0xac, 0x7e, 0x77, 0x5f, 0x33, 0x75, 0xcb, 0xe8, 0x3c, 0x94, 0x8a, 0x3b, 0xb8, 0x0a, 0xc5, 0xd9, - 0x0c, 0x9e, 0x31, 0x16, 0x2a, 0x5d, 0x8d, 0x98, 0x33, 0xb0, 0xcf, 0x9e, 0x31, 0xb2, 0xe0, 0x2e, - 0xe9, 0xf4, 0xbb, 0x33, 0xb3, 0x55, 0x28, 0x4b, 0xb2, 0xa4, 0x28, 0xc7, 0x44, 0x7b, 0x4d, 0xa3, - 0x91, 0xcc, 0xef, 0x59, 0x71, 0x23, 0x83, 0x94, 0xed, 0x63, 0xc8, 0x71, 0x77, 0x14, 0x21, 0x67, - 0x74, 0x0c, 0x1d, 0x5d, 0xc1, 0x2b, 0x00, 0xcd, 0x5e, 0xd3, 0x30, 0xf5, 0xbb, 0x44, 0x6b, 0x31, - 0xd8, 0x5c, 0x10, 0x13, 0xc8, 0xd0, 0x2e, 0xc3, 0x52, 0xb3, 0x77, 0xd0, 0xea, 0x68, 0xa6, 0x84, - 0xd9, 0xec, 0xdd, 0xef, 0x77, 0x4c, 0xa6, 0x44, 0xb8, 0x0c, 0x85, 0x66, 0xcf, 0xd4, 0xbf, 0x67, - 0x32, 0x5c, 0x5c, 0x27, 0x58, 0x45, 0xcf, 0xee, 0x6c, 0xff, 0x28, 0x0b, 0x39, 0xfe, 0xe0, 0xb4, - 0x02, 0x25, 0xee, 0x6d, 0xf3, 0x51, 0x97, 0x0d, 0x59, 0x82, 0x5c, 0xd3, 0x30, 0x6f, 0xa3, 0x5f, - 0xcc, 0x60, 0x80, 0x7c, 0x9f, 0x97, 0x7f, 0xa9, 0xc0, 0xca, 0x4d, 0xc3, 0x7c, 0xf7, 0x16, 0xfa, - 0x41, 0x86, 0x75, 0xdb, 0x17, 0x95, 0x5f, 0x8e, 0x15, 0xbb, 0x37, 0xd1, 0x0f, 0x13, 0xc5, 0xee, - 0x4d, 0xf4, 0x2b, 0xb1, 0xe2, 0xbd, 0x5d, 0xf4, 0xab, 0x89, 0xe2, 0xbd, 0x5d, 0xf4, 0x6b, 0xb1, - 0xe2, 0xd6, 0x4d, 0xf4, 0xeb, 0x89, 0xe2, 0xd6, 0x4d, 0xf4, 0x1b, 0x05, 0x86, 0x85, 0x23, 0x79, - 0x6f, 0x17, 0xfd, 0x66, 0x31, 0xa9, 0xdd, 0xba, 0x89, 0x7e, 0xab, 0xc8, 0xfc, 0x9f, 0x78, 0x15, - 0xfd, 0x36, 0x62, 0xd3, 0x64, 0x0e, 0x42, 0xbf, 0xc3, 0x8b, 0x4c, 0x85, 0x7e, 0x17, 0x31, 0x8c, - 0x4c, 0xca, 0xab, 0x1f, 0x73, 0xcd, 0x23, 0x5d, 0x23, 0xe8, 0xf7, 0x0a, 0xe2, 0x85, 0x5a, 0xa3, - 0xd9, 0xd6, 0x5a, 0x08, 0xf3, 0x16, 0x8c, 0x95, 0x3f, 0xb8, 0xc1, 0x8a, 0x2c, 0x3c, 0xd1, 0x1f, - 0x76, 0xd9, 0x80, 0x0f, 0x34, 0xd2, 0xf8, 0x40, 0x23, 0xe8, 0x8f, 0x6e, 0xb0, 0x01, 0x1f, 0x68, - 0x44, 0xf2, 0xf5, 0xc7, 0x5d, 0x66, 0xc8, 0x55, 0x9f, 0xdc, 0x60, 0x93, 0x96, 0xf2, 0x3f, 0xe9, - 0xe2, 0x22, 0x64, 0xf7, 0x9a, 0x26, 0xfa, 0x11, 0x1f, 0x8d, 0x85, 0x28, 0xfa, 0x53, 0xc4, 0x84, - 0x3d, 0xdd, 0x44, 0x7f, 0xc6, 0x84, 0x79, 0xb3, 0xdf, 0x6d, 0xe9, 0xe8, 0x0d, 0x36, 0xb9, 0xbb, - 0x7a, 0xa7, 0xad, 0x9b, 0xe4, 0x11, 0xfa, 0x73, 0x6e, 0xfe, 0x61, 0xaf, 0x63, 0xa0, 0x4f, 0x11, - 0xae, 0x02, 0xe8, 0xdf, 0xeb, 0x12, 0xbd, 0xd7, 0x6b, 0x76, 0x0c, 0xf4, 0xf6, 0xf6, 0x01, 0xa0, - 0xf3, 0xe9, 0x80, 0x01, 0xe8, 0x1b, 0xf7, 0x8c, 0xce, 0x43, 0x03, 0x5d, 0x61, 0x95, 0x2e, 0xd1, - 0xbb, 0x1a, 0xd1, 0x91, 0x82, 0x01, 0x0a, 0xf2, 0xdd, 0x5b, 0x06, 0x2f, 0x43, 0x91, 0x74, 0x5a, - 0xad, 0x3d, 0xad, 0x71, 0x0f, 0x65, 0xf7, 0xbe, 0x0d, 0x2b, 0x8e, 0xbf, 0x73, 0xe2, 0x44, 0x34, - 0x0c, 0xc5, 0x93, 0xe6, 0x8f, 0x54, 0x59, 0x73, 0xfc, 0xeb, 0xa2, 0x74, 0x7d, 0xe4, 0x5f, 0x3f, - 0x89, 0xae, 0x73, 0xed, 0x75, 0x9e, 0x31, 0x0e, 0x0b, 0xbc, 0xf2, 0xde, 0xff, 0x06, 0x00, 0x00, - 0xff, 0xff, 0x40, 0x99, 0x5d, 0x63, 0x30, 0x2d, 0x00, 0x00, + // 3087 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcb, 0x73, 0x1b, 0x47, + 0x7a, 0xd7, 0xe0, 0x45, 0xe0, 0x03, 0x01, 0x36, 0x9b, 0xa4, 0x04, 0x51, 0x7e, 0xd0, 0x63, 0xcb, + 0x66, 0xe8, 0x84, 0x92, 0x29, 0x59, 0x51, 0x6c, 0x27, 0xd1, 0x10, 0x1c, 0xca, 0xb0, 0x80, 0x01, + 0xd4, 0x18, 0x48, 0x96, 0xca, 0x55, 0x53, 0x43, 0xa0, 0x05, 0x4e, 0x71, 0x80, 0x81, 0x66, 0x86, + 0x94, 0x78, 0x53, 0xe2, 0x38, 0xef, 0x87, 0xf3, 0x74, 0x9c, 0x54, 0x9c, 0x54, 0xe5, 0x90, 0xca, + 0x25, 0x7f, 0x43, 0x2a, 0x87, 0x1c, 0xf7, 0xbe, 0xbb, 0x87, 0x3d, 0x6d, 0xed, 0xcd, 0xb5, 0xa7, + 0x3d, 0xec, 0x61, 0x6b, 0xab, 0x1f, 0x33, 0x18, 0x90, 0xd0, 0xc3, 0xda, 0xbd, 0x50, 0xf6, 0xad, + 0xbf, 0x47, 0xf7, 0xd7, 0xdf, 0xef, 0xfb, 0xe6, 0xeb, 0x6f, 0x66, 0x1a, 0x8a, 0xf7, 0xf7, 0xa9, + 0x7f, 0xb8, 0x3e, 0xf2, 0xbd, 0xd0, 0xc3, 0x59, 0x4e, 0x2c, 0x97, 0x43, 0x6f, 0xe4, 0xf5, 0xec, + 0xd0, 0x16, 0xec, 0xe5, 0xe2, 0x41, 0xe8, 0x8f, 0xba, 0x82, 0x50, 0x3f, 0x53, 0x20, 0x67, 0xda, + 0x7e, 0x9f, 0x86, 0x78, 0x19, 0xf2, 0x7b, 0xf4, 0x30, 0x18, 0xd9, 0x5d, 0x5a, 0x51, 0x56, 0x94, + 0xd5, 0x02, 0x89, 0x69, 0xbc, 0x08, 0xd9, 0x60, 0xd7, 0xf6, 0x7b, 0x95, 0x14, 0x17, 0x08, 0x02, + 0xbf, 0x0b, 0xc5, 0xd0, 0xde, 0x71, 0x69, 0x68, 0x85, 0x87, 0x23, 0x5a, 0x49, 0xaf, 0x28, 0xab, + 0xe5, 0x8d, 0xc5, 0xf5, 0xd8, 0x9e, 0xc9, 0x85, 0xe6, 0xe1, 0x88, 0x12, 0x08, 0xe3, 0x31, 0xc6, + 0x90, 0xe9, 0x52, 0xd7, 0xad, 0x64, 0xf8, 0x5a, 0x7c, 0xac, 0x6e, 0x41, 0xf9, 0x96, 0x79, 0xdd, + 0x0e, 0x69, 0xd5, 0x76, 0x5d, 0xea, 0xd7, 0xb6, 0xd8, 0x76, 0xf6, 0x03, 0xea, 0x0f, 0xed, 0x41, + 0xbc, 0x9d, 0x88, 0xc6, 0xa7, 0x21, 0xd7, 0xf7, 0xbd, 0xfd, 0x51, 0x50, 0x49, 0xad, 0xa4, 0x57, + 0x0b, 0x44, 0x52, 0xea, 0x27, 0x00, 0xfa, 0x01, 0x1d, 0x86, 0xa6, 0xb7, 0x47, 0x87, 0xf8, 0x25, + 0x28, 0x84, 0xce, 0x80, 0x06, 0xa1, 0x3d, 0x18, 0xf1, 0x25, 0xd2, 0x64, 0xcc, 0x78, 0x8c, 0x4b, + 0xcb, 0x90, 0x1f, 0x79, 0x81, 0x13, 0x3a, 0xde, 0x90, 0xfb, 0x53, 0x20, 0x31, 0xad, 0xfe, 0x1e, + 0x64, 0x6f, 0xd9, 0xee, 0x3e, 0xc5, 0xaf, 0x42, 0x86, 0x3b, 0xac, 0x70, 0x87, 0x8b, 0xeb, 0x02, + 0x74, 0xee, 0x27, 0x17, 0xb0, 0xb5, 0x0f, 0x98, 0x26, 0x5f, 0x7b, 0x96, 0x08, 0x42, 0xdd, 0x83, + 0xd9, 0x4d, 0x67, 0xd8, 0xbb, 0x65, 0xfb, 0x0e, 0x03, 0xe3, 0x39, 0x97, 0xc1, 0x6f, 0x40, 0x8e, + 0x0f, 0x82, 0x4a, 0x7a, 0x25, 0xbd, 0x5a, 0xdc, 0x98, 0x95, 0x13, 0xf9, 0xde, 0x88, 0x94, 0xa9, + 0xff, 0xa7, 0x00, 0x6c, 0x7a, 0xfb, 0xc3, 0xde, 0x4d, 0x26, 0xc4, 0x08, 0xd2, 0xc1, 0x7d, 0x57, + 0x02, 0xc9, 0x86, 0xf8, 0x06, 0x94, 0x77, 0x9c, 0x61, 0xcf, 0x3a, 0x90, 0xdb, 0x11, 0x58, 0x16, + 0x37, 0xde, 0x90, 0xcb, 0x8d, 0x27, 0xaf, 0x27, 0x77, 0x1d, 0xe8, 0xc3, 0xd0, 0x3f, 0x24, 0xa5, + 0x9d, 0x24, 0x6f, 0xb9, 0x03, 0xf8, 0xb8, 0x12, 0x33, 0xba, 0x47, 0x0f, 0x23, 0xa3, 0x7b, 0xf4, + 0x10, 0xff, 0x46, 0xd2, 0xa3, 0xe2, 0xc6, 0x42, 0x64, 0x2b, 0x31, 0x57, 0xba, 0xf9, 0x5e, 0xea, + 0xaa, 0xa2, 0xfe, 0x7b, 0x0e, 0xca, 0xfa, 0x43, 0xda, 0xdd, 0x0f, 0x69, 0x73, 0xc4, 0x62, 0x10, + 0xe0, 0x75, 0x58, 0x70, 0x86, 0x5d, 0x77, 0xbf, 0x47, 0x2d, 0xca, 0x42, 0x6d, 0x85, 0x2c, 0xd6, + 0x7c, 0xbd, 0x3c, 0x99, 0x97, 0xa2, 0x44, 0x12, 0x68, 0xb0, 0xd0, 0xf5, 0x06, 0x23, 0xdb, 0x9f, + 0xd4, 0x4f, 0x73, 0xfb, 0xf3, 0xd2, 0xfe, 0x58, 0x9f, 0xcc, 0x4b, 0xed, 0xc4, 0x12, 0x0d, 0x98, + 0x93, 0xeb, 0xf6, 0xac, 0x7b, 0x0e, 0x75, 0x7b, 0x01, 0x4f, 0xdd, 0x72, 0x0c, 0xd5, 0xe4, 0x16, + 0xd7, 0x6b, 0x52, 0x79, 0x9b, 0xeb, 0x92, 0xb2, 0x33, 0x41, 0xe3, 0x35, 0x98, 0xef, 0xba, 0x0e, + 0xdb, 0xca, 0x3d, 0x06, 0xb1, 0xe5, 0x7b, 0x0f, 0x82, 0x4a, 0x96, 0xef, 0x7f, 0x4e, 0x08, 0xb6, + 0x19, 0x9f, 0x78, 0x0f, 0x02, 0xfc, 0x1e, 0xe4, 0x1f, 0x78, 0xfe, 0x9e, 0xeb, 0xd9, 0xbd, 0x4a, + 0x8e, 0xdb, 0x7c, 0x65, 0xba, 0xcd, 0xdb, 0x52, 0x8b, 0xc4, 0xfa, 0x78, 0x15, 0x50, 0x70, 0xdf, + 0xb5, 0x02, 0xea, 0xd2, 0x6e, 0x68, 0xb9, 0xce, 0xc0, 0x09, 0x2b, 0x79, 0xfe, 0x14, 0x94, 0x83, + 0xfb, 0x6e, 0x9b, 0xb3, 0xeb, 0x8c, 0x8b, 0x2d, 0x58, 0x0a, 0x7d, 0x7b, 0x18, 0xd8, 0x5d, 0xb6, + 0x98, 0xe5, 0x04, 0x9e, 0x6b, 0xf3, 0x27, 0xa0, 0xc0, 0x4d, 0xae, 0x4d, 0x37, 0x69, 0x8e, 0xa7, + 0xd4, 0xa2, 0x19, 0x64, 0x31, 0x9c, 0xc2, 0xc5, 0xef, 0xc0, 0x52, 0xb0, 0xe7, 0x8c, 0x2c, 0xbe, + 0x8e, 0x35, 0x72, 0xed, 0xa1, 0xd5, 0xb5, 0xbb, 0xbb, 0xb4, 0x02, 0xdc, 0x6d, 0xcc, 0x84, 0x3c, + 0xd5, 0x5a, 0xae, 0x3d, 0xac, 0x32, 0x89, 0xfa, 0x3e, 0x94, 0x27, 0x71, 0xc4, 0xf3, 0x50, 0x32, + 0xef, 0xb4, 0x74, 0x4b, 0x33, 0xb6, 0x2c, 0x43, 0x6b, 0xe8, 0xe8, 0x14, 0x2e, 0x41, 0x81, 0xb3, + 0x9a, 0x46, 0xfd, 0x0e, 0x52, 0xf0, 0x0c, 0xa4, 0xb5, 0x7a, 0x1d, 0xa5, 0xd4, 0xab, 0x90, 0x8f, + 0x00, 0xc1, 0x73, 0x50, 0xec, 0x18, 0xed, 0x96, 0x5e, 0xad, 0x6d, 0xd7, 0xf4, 0x2d, 0x74, 0x0a, + 0xe7, 0x21, 0xd3, 0xac, 0x9b, 0x2d, 0xa4, 0x88, 0x91, 0xd6, 0x42, 0x29, 0x36, 0x73, 0x6b, 0x53, + 0x43, 0x69, 0xf5, 0xbf, 0x14, 0x58, 0x9c, 0xe6, 0x18, 0x2e, 0xc2, 0xcc, 0x96, 0xbe, 0xad, 0x75, + 0xea, 0x26, 0x3a, 0x85, 0x17, 0x60, 0x8e, 0xe8, 0x2d, 0x5d, 0x33, 0xb5, 0xcd, 0xba, 0x6e, 0x11, + 0x5d, 0xdb, 0x42, 0x0a, 0xc6, 0x50, 0x66, 0x23, 0xab, 0xda, 0x6c, 0x34, 0x6a, 0xa6, 0xa9, 0x6f, + 0xa1, 0x14, 0x5e, 0x04, 0xc4, 0x79, 0x1d, 0x63, 0xcc, 0x4d, 0x63, 0x04, 0xb3, 0x6d, 0x9d, 0xd4, + 0xb4, 0x7a, 0xed, 0x2e, 0x5b, 0x00, 0x65, 0xf0, 0x6b, 0xf0, 0x72, 0xb5, 0x69, 0xb4, 0x6b, 0x6d, + 0x53, 0x37, 0x4c, 0xab, 0x6d, 0x68, 0xad, 0xf6, 0x87, 0x4d, 0x93, 0xaf, 0x2c, 0x9c, 0xcb, 0xe2, + 0x32, 0x80, 0xd6, 0x31, 0x9b, 0x62, 0x1d, 0x94, 0xfb, 0x28, 0x93, 0x57, 0x50, 0x4a, 0xfd, 0x22, + 0x05, 0x59, 0x8e, 0x0f, 0xab, 0xaa, 0x89, 0x5a, 0xc9, 0xc7, 0x71, 0x85, 0x49, 0x3d, 0xa1, 0xc2, + 0xf0, 0xc2, 0x2c, 0x6b, 0x9d, 0x20, 0xf0, 0x39, 0x28, 0x78, 0x7e, 0xdf, 0x12, 0x12, 0x51, 0xa5, + 0xf3, 0x9e, 0xdf, 0xe7, 0xe5, 0x9c, 0x55, 0x48, 0x56, 0xdc, 0x77, 0xec, 0x80, 0xf2, 0xac, 0x2d, + 0x90, 0x98, 0xc6, 0x67, 0x81, 0xe9, 0x59, 0x7c, 0x1f, 0x39, 0x2e, 0x9b, 0xf1, 0xfc, 0xbe, 0xc1, + 0xb6, 0xf2, 0x3a, 0x94, 0xba, 0x9e, 0xbb, 0x3f, 0x18, 0x5a, 0x2e, 0x1d, 0xf6, 0xc3, 0xdd, 0xca, + 0xcc, 0x8a, 0xb2, 0x5a, 0x22, 0xb3, 0x82, 0x59, 0xe7, 0x3c, 0x5c, 0x81, 0x99, 0xee, 0xae, 0xed, + 0x07, 0x54, 0x64, 0x6a, 0x89, 0x44, 0x24, 0xb7, 0x4a, 0xbb, 0xce, 0xc0, 0x76, 0x03, 0x9e, 0x95, + 0x25, 0x12, 0xd3, 0xcc, 0x89, 0x7b, 0xae, 0xdd, 0x0f, 0x78, 0x36, 0x95, 0x88, 0x20, 0xd4, 0xdf, + 0x86, 0x34, 0xf1, 0x1e, 0xb0, 0x25, 0x85, 0xc1, 0xa0, 0xa2, 0xac, 0xa4, 0x57, 0x31, 0x89, 0x48, + 0x76, 0x88, 0xc8, 0x3a, 0x2a, 0xca, 0x6b, 0x54, 0x39, 0x3f, 0x81, 0x59, 0x42, 0x83, 0x7d, 0x37, + 0xd4, 0x1f, 0x86, 0xbe, 0x1d, 0xe0, 0x0d, 0x28, 0x26, 0x2b, 0x87, 0xf2, 0xb8, 0xca, 0x01, 0x74, + 0x5c, 0x32, 0x2a, 0x30, 0x73, 0xcf, 0xa7, 0xc1, 0x2e, 0xf5, 0x65, 0x65, 0x8a, 0x48, 0x56, 0x97, + 0x8b, 0x3c, 0xd5, 0x85, 0x0d, 0x56, 0xcd, 0x65, 0x4d, 0x51, 0x26, 0xaa, 0x39, 0x0f, 0x2a, 0x91, + 0x32, 0x86, 0x1e, 0x2b, 0x13, 0x96, 0x7d, 0xef, 0x1e, 0xed, 0x86, 0x54, 0x1c, 0x5a, 0x19, 0x32, + 0xcb, 0x98, 0x9a, 0xe4, 0xb1, 0xb0, 0x39, 0xc3, 0x80, 0xfa, 0xa1, 0xe5, 0xf4, 0x78, 0x40, 0x33, + 0x24, 0x2f, 0x18, 0xb5, 0x1e, 0x7e, 0x05, 0x32, 0xbc, 0xd0, 0x64, 0xb8, 0x15, 0x90, 0x56, 0x88, + 0xf7, 0x80, 0x70, 0x3e, 0x7e, 0x1b, 0x72, 0x94, 0xfb, 0xcb, 0x83, 0x3a, 0x2e, 0xcd, 0x49, 0x28, + 0x88, 0x54, 0x51, 0x3f, 0x80, 0x59, 0xee, 0xc3, 0x6d, 0xdb, 0x1f, 0x3a, 0xc3, 0x3e, 0x3f, 0xd1, + 0xbd, 0x9e, 0xc8, 0xbd, 0x12, 0xe1, 0x63, 0x06, 0xc1, 0x80, 0x06, 0x81, 0xdd, 0xa7, 0xf2, 0x84, + 0x8d, 0x48, 0xf5, 0x3f, 0xd2, 0x50, 0x6c, 0x87, 0x3e, 0xb5, 0x07, 0x1c, 0x3d, 0xfc, 0x01, 0x40, + 0x10, 0xda, 0x21, 0x1d, 0xd0, 0x61, 0x18, 0xc1, 0xf0, 0x92, 0x34, 0x9f, 0xd0, 0x5b, 0x6f, 0x47, + 0x4a, 0x24, 0xa1, 0x7f, 0x34, 0x3c, 0xa9, 0x67, 0x08, 0xcf, 0xf2, 0x57, 0x29, 0x28, 0xc4, 0xab, + 0x61, 0x0d, 0xf2, 0x5d, 0x3b, 0xa4, 0x7d, 0xcf, 0x3f, 0x94, 0x67, 0xf1, 0xf9, 0x27, 0x59, 0x5f, + 0xaf, 0x4a, 0x65, 0x12, 0x4f, 0xc3, 0x2f, 0x83, 0x68, 0x70, 0x44, 0xea, 0x0b, 0x7f, 0x0b, 0x9c, + 0xc3, 0x93, 0xff, 0x3d, 0xc0, 0x23, 0xdf, 0x19, 0xd8, 0xfe, 0xa1, 0xb5, 0x47, 0x0f, 0xa3, 0x43, + 0x24, 0x3d, 0x25, 0xe0, 0x48, 0xea, 0xdd, 0xa0, 0x87, 0xb2, 0xec, 0x5d, 0x9d, 0x9c, 0x2b, 0x53, + 0xf6, 0x78, 0x18, 0x13, 0x33, 0x79, 0x27, 0x10, 0x44, 0x67, 0x7e, 0x96, 0x67, 0x37, 0x1b, 0xaa, + 0x6f, 0x41, 0x3e, 0xda, 0x3c, 0x2e, 0x40, 0x56, 0xf7, 0x7d, 0xcf, 0x47, 0xa7, 0x78, 0xf5, 0x6b, + 0xd4, 0x45, 0x01, 0xdd, 0xda, 0x62, 0x05, 0xf4, 0x7f, 0x53, 0xf1, 0xc1, 0x4b, 0xe8, 0xfd, 0x7d, + 0x1a, 0x84, 0xf8, 0xf7, 0x61, 0x81, 0xf2, 0x4c, 0x73, 0x0e, 0xa8, 0xd5, 0xe5, 0x5d, 0x1a, 0xcb, + 0x33, 0xf1, 0x38, 0xcc, 0xad, 0x8b, 0xa6, 0x32, 0xea, 0xde, 0xc8, 0x7c, 0xac, 0x2b, 0x59, 0x3d, + 0xac, 0xc3, 0x82, 0x33, 0x18, 0xd0, 0x9e, 0x63, 0x87, 0xc9, 0x05, 0x44, 0xc0, 0x96, 0xa2, 0x26, + 0x66, 0xa2, 0x09, 0x24, 0xf3, 0xf1, 0x8c, 0x78, 0x99, 0xf3, 0x90, 0x0b, 0x79, 0xc3, 0x2a, 0xcf, + 0xf0, 0x52, 0x54, 0xd5, 0x38, 0x93, 0x48, 0x21, 0x7e, 0x0b, 0x44, 0xfb, 0xcb, 0xeb, 0xd7, 0x38, + 0x21, 0xc6, 0x5d, 0x0d, 0x11, 0x72, 0x7c, 0x1e, 0xca, 0x13, 0x87, 0x5f, 0x8f, 0x03, 0x96, 0x26, + 0xa5, 0xe4, 0x49, 0xd6, 0xc3, 0x17, 0x60, 0xc6, 0x13, 0x07, 0x1f, 0xaf, 0x6c, 0xe3, 0x1d, 0x4f, + 0x9e, 0x8a, 0x24, 0xd2, 0x52, 0x7f, 0x17, 0xe6, 0x62, 0x04, 0x83, 0x91, 0x37, 0x0c, 0x28, 0x5e, + 0x83, 0x9c, 0xcf, 0x1f, 0x27, 0x89, 0x1a, 0x96, 0x4b, 0x24, 0xea, 0x01, 0x91, 0x1a, 0x6a, 0x0f, + 0xe6, 0x04, 0xe7, 0xb6, 0x13, 0xee, 0xf2, 0x40, 0xe1, 0xf3, 0x90, 0xa5, 0x6c, 0x70, 0x04, 0x73, + 0xd2, 0xaa, 0x72, 0x39, 0x11, 0xd2, 0x84, 0x95, 0xd4, 0x53, 0xad, 0xfc, 0x34, 0x05, 0x0b, 0x72, + 0x97, 0x9b, 0x76, 0xd8, 0xdd, 0x3d, 0xa1, 0xc1, 0x7e, 0x1b, 0x66, 0x18, 0xdf, 0x89, 0x1f, 0x8c, + 0x29, 0xe1, 0x8e, 0x34, 0x58, 0xc0, 0xed, 0xc0, 0x4a, 0x44, 0x57, 0x36, 0x5f, 0x25, 0x3b, 0x48, + 0x9c, 0xfc, 0x53, 0xf2, 0x22, 0xf7, 0x94, 0xbc, 0x98, 0x79, 0xa6, 0xbc, 0xd8, 0x82, 0xc5, 0x49, + 0xc4, 0x65, 0x72, 0xfc, 0x26, 0xcc, 0x88, 0xa0, 0x44, 0x25, 0x70, 0x5a, 0xdc, 0x22, 0x15, 0xf5, + 0xff, 0x53, 0xb0, 0x28, 0xab, 0xd3, 0xb7, 0xe3, 0x31, 0x4d, 0xe0, 0x9c, 0x7d, 0x16, 0x9c, 0x9f, + 0x31, 0x7e, 0x6a, 0x15, 0x96, 0x8e, 0xe0, 0xf8, 0x1c, 0x0f, 0xeb, 0xd7, 0x0a, 0xcc, 0x6e, 0xd2, + 0xbe, 0x33, 0x3c, 0xa1, 0x51, 0x48, 0x80, 0x9b, 0x79, 0xa6, 0x24, 0xbe, 0x02, 0x25, 0xe9, 0xaf, + 0x44, 0xeb, 0x38, 0xda, 0xca, 0x34, 0xb4, 0x7f, 0xac, 0x40, 0xa9, 0xea, 0x0d, 0x06, 0x4e, 0x78, + 0x42, 0x91, 0x3a, 0xee, 0x67, 0x66, 0x9a, 0x9f, 0x08, 0xca, 0x91, 0x9b, 0x02, 0x20, 0xf5, 0x27, + 0x0a, 0xcc, 0x11, 0xcf, 0x75, 0x77, 0xec, 0xee, 0xde, 0x8b, 0xed, 0x3b, 0x06, 0x34, 0x76, 0x54, + 0x7a, 0xff, 0x73, 0x05, 0xca, 0x2d, 0x9f, 0xb2, 0x17, 0xeb, 0x17, 0xda, 0x79, 0xd6, 0x09, 0xf7, + 0x42, 0xd9, 0x43, 0x14, 0x08, 0x1f, 0xab, 0xf3, 0x30, 0x17, 0xfb, 0x2e, 0xf1, 0xf8, 0x81, 0x02, + 0x4b, 0x22, 0x41, 0xa4, 0xa4, 0x77, 0x42, 0x61, 0x89, 0xfc, 0xcd, 0x24, 0xfc, 0xad, 0xc0, 0xe9, + 0xa3, 0xbe, 0x49, 0xb7, 0x3f, 0x4d, 0xc1, 0x99, 0x28, 0x37, 0x4e, 0xb8, 0xe3, 0xbf, 0x42, 0x3e, + 0x2c, 0x43, 0xe5, 0x38, 0x08, 0x12, 0xa1, 0xcf, 0x53, 0x50, 0xa9, 0xfa, 0xd4, 0x0e, 0x69, 0xa2, + 0x17, 0x79, 0x71, 0x72, 0x03, 0xbf, 0x03, 0xb3, 0x23, 0xdb, 0x0f, 0x9d, 0xae, 0x33, 0xb2, 0xd9, + 0xdb, 0x5e, 0x96, 0xb7, 0x3a, 0x47, 0x16, 0x98, 0x50, 0x51, 0xcf, 0xc1, 0xd9, 0x29, 0x88, 0x48, + 0xbc, 0x7e, 0xa1, 0x00, 0x6e, 0x87, 0xb6, 0x1f, 0x7e, 0x0b, 0x4e, 0x95, 0xa9, 0xc9, 0xb4, 0x04, + 0x0b, 0x13, 0xfe, 0x27, 0x71, 0xa1, 0xe1, 0xb7, 0xe2, 0xc4, 0x79, 0x2c, 0x2e, 0x49, 0xff, 0x25, + 0x2e, 0x3f, 0x52, 0x60, 0xb9, 0xea, 0x89, 0x0f, 0x8b, 0x2f, 0xe4, 0x13, 0xa6, 0xbe, 0x0c, 0xe7, + 0xa6, 0x3a, 0x28, 0x01, 0xf8, 0xa1, 0x02, 0xa7, 0x09, 0xb5, 0x7b, 0x2f, 0xa6, 0xf3, 0x37, 0xe1, + 0xcc, 0x31, 0xe7, 0x64, 0x87, 0x7a, 0x05, 0xf2, 0x03, 0x1a, 0xda, 0x3d, 0x3b, 0xb4, 0xa5, 0x4b, + 0xcb, 0xd1, 0xba, 0x63, 0xed, 0x86, 0xd4, 0x20, 0xb1, 0xae, 0xfa, 0x55, 0x0a, 0x16, 0x78, 0xaf, + 0xfb, 0xdd, 0x8b, 0xd6, 0xf4, 0x77, 0x81, 0xcf, 0x15, 0x58, 0x9c, 0x04, 0x28, 0x7e, 0x27, 0xf8, + 0x75, 0x7f, 0xaf, 0x98, 0x52, 0x10, 0xd2, 0xd3, 0x5a, 0xd0, 0xef, 0xa5, 0xa0, 0x92, 0xdc, 0xd2, + 0x77, 0xdf, 0x36, 0x26, 0xbf, 0x6d, 0x7c, 0xe3, 0x8f, 0x59, 0x5f, 0x28, 0x70, 0x76, 0x0a, 0xa0, + 0xdf, 0x2c, 0xd0, 0x89, 0x2f, 0x1c, 0xa9, 0xa7, 0x7e, 0xe1, 0x78, 0xd6, 0x50, 0x7f, 0x5f, 0x81, + 0xc5, 0x86, 0xf8, 0xb0, 0x2c, 0xde, 0xe3, 0x4f, 0x6e, 0x35, 0xe3, 0xdf, 0x8e, 0x33, 0xe3, 0xdf, + 0x37, 0x6a, 0x15, 0x96, 0x8e, 0xb8, 0xf6, 0x1c, 0xdf, 0x26, 0x7e, 0xa6, 0xc0, 0xbc, 0x5c, 0x45, + 0x3b, 0xb1, 0x8d, 0xc0, 0x14, 0x74, 0xf0, 0x2b, 0x90, 0x76, 0x7a, 0x51, 0x07, 0x39, 0xf9, 0x13, + 0x9c, 0x09, 0xd4, 0x6b, 0x80, 0x93, 0x7e, 0x3f, 0x07, 0x74, 0xbc, 0xb7, 0x62, 0xc0, 0x7f, 0x48, + 0x6d, 0x37, 0x8c, 0x0a, 0x88, 0xfa, 0x9f, 0x29, 0x28, 0x11, 0xc6, 0x71, 0x06, 0xb4, 0x1d, 0xda, + 0x61, 0x80, 0x5f, 0x83, 0xd9, 0x5d, 0xae, 0x62, 0x8d, 0x9f, 0x83, 0x02, 0x29, 0x0a, 0x9e, 0xf8, + 0x78, 0xbb, 0x01, 0x4b, 0x01, 0xed, 0x7a, 0xc3, 0x5e, 0x60, 0xed, 0xd0, 0x5d, 0x67, 0xd8, 0xb3, + 0x06, 0x76, 0x10, 0xca, 0xff, 0x43, 0x25, 0xb2, 0x20, 0x85, 0x9b, 0x5c, 0xd6, 0xe0, 0x22, 0x7c, + 0x11, 0x16, 0x77, 0x9c, 0xa1, 0xeb, 0xf5, 0xad, 0x91, 0x6b, 0x1f, 0x52, 0x3f, 0xb0, 0xba, 0xde, + 0xfe, 0x50, 0x40, 0x95, 0x25, 0x58, 0xc8, 0x5a, 0x42, 0x54, 0x65, 0x12, 0x7c, 0x17, 0xd6, 0xa6, + 0x5a, 0xb1, 0xee, 0x39, 0x6e, 0x48, 0x7d, 0xda, 0xb3, 0x7c, 0x3a, 0x72, 0x9d, 0xae, 0xf8, 0xbd, + 0x2b, 0x9a, 0xa9, 0x37, 0xa7, 0x98, 0xde, 0x96, 0xea, 0x64, 0xac, 0x8d, 0xcf, 0x41, 0xa1, 0x3b, + 0xda, 0xb7, 0xf6, 0xf9, 0x2f, 0x1d, 0x56, 0x56, 0x14, 0x92, 0xef, 0x8e, 0xf6, 0x3b, 0x8c, 0xc6, + 0x08, 0xd2, 0xf7, 0x47, 0xa2, 0x9a, 0x28, 0x84, 0x0d, 0xd5, 0xaf, 0x15, 0x28, 0x6b, 0xfd, 0xbe, + 0x4f, 0xfb, 0x76, 0x28, 0x61, 0xba, 0x08, 0x8b, 0x02, 0x92, 0x43, 0x4b, 0xde, 0x1b, 0x11, 0xfe, + 0x28, 0xc2, 0x1f, 0x29, 0x13, 0xb7, 0x46, 0x84, 0x3f, 0x97, 0xe1, 0xf4, 0xfe, 0x70, 0xea, 0x9c, + 0x14, 0x9f, 0xb3, 0x18, 0x4b, 0x93, 0xb3, 0x7e, 0x07, 0xce, 0x4e, 0x47, 0x61, 0xe0, 0x88, 0x3f, + 0xff, 0x25, 0x72, 0x7a, 0x8a, 0xd3, 0x0d, 0x67, 0xf8, 0x84, 0xa9, 0xf6, 0x43, 0x8e, 0xd7, 0x63, + 0xa6, 0xda, 0x0f, 0xd5, 0xff, 0x8e, 0x3f, 0xc9, 0x46, 0xe9, 0x12, 0x97, 0xc7, 0x28, 0xc7, 0x95, + 0x27, 0xe5, 0x78, 0x05, 0x66, 0x02, 0xea, 0x1f, 0x38, 0xc3, 0x7e, 0xf4, 0xcf, 0x50, 0x92, 0xb8, + 0x0d, 0x6f, 0x4a, 0xdf, 0xe9, 0xc3, 0x90, 0xfa, 0x43, 0xdb, 0x75, 0x0f, 0x2d, 0xf1, 0xe6, 0x38, + 0x0c, 0x69, 0xcf, 0x1a, 0xdf, 0x72, 0x11, 0x25, 0xf2, 0x75, 0xa1, 0xad, 0xc7, 0xca, 0x24, 0xd6, + 0x35, 0xe3, 0xfb, 0x2f, 0xef, 0x43, 0xd9, 0x97, 0x49, 0x6c, 0x05, 0x2c, 0x3c, 0xb2, 0x33, 0x58, + 0x8c, 0x7f, 0xfc, 0x25, 0x32, 0x9c, 0x94, 0xfc, 0x89, 0x84, 0xbf, 0x0a, 0xb3, 0x72, 0x47, 0xb6, + 0xeb, 0xd8, 0xe3, 0x4e, 0xe1, 0xc8, 0xd5, 0x1f, 0x8d, 0x09, 0x89, 0xbc, 0x24, 0xc4, 0x89, 0x8f, + 0x32, 0xf9, 0x1c, 0x9a, 0x61, 0xaf, 0x27, 0x0b, 0x9d, 0x51, 0x8f, 0x67, 0xc6, 0x09, 0x2e, 0xda, + 0xc9, 0xdb, 0x42, 0x99, 0xc9, 0xdb, 0x42, 0x93, 0xb7, 0x8f, 0xb2, 0x47, 0x6e, 0x1f, 0xa9, 0xd7, + 0x60, 0x71, 0xd2, 0x7f, 0x99, 0x2b, 0xab, 0x90, 0xe5, 0xff, 0x29, 0x8f, 0x54, 0xa7, 0xc4, 0x8f, + 0x48, 0x22, 0x14, 0xd4, 0xff, 0x51, 0x60, 0x61, 0x4a, 0xe7, 0x1a, 0xb7, 0xc5, 0x4a, 0xe2, 0xad, + 0xfb, 0xb7, 0x20, 0xcb, 0xff, 0x98, 0xca, 0x8b, 0x00, 0x67, 0x8e, 0x37, 0xbe, 0xfc, 0xef, 0x26, + 0x11, 0x5a, 0xac, 0x9c, 0xf1, 0xb4, 0xe8, 0xf2, 0xd7, 0xee, 0xe8, 0xe0, 0x2d, 0x32, 0x9e, 0x78, + 0x13, 0x3f, 0xfe, 0x1e, 0x9f, 0x79, 0xea, 0x7b, 0xfc, 0xda, 0xdf, 0xa5, 0xa1, 0xd0, 0x38, 0x6c, + 0xdf, 0x77, 0xb7, 0x5d, 0xbb, 0xcf, 0x7f, 0x3f, 0x36, 0x5a, 0xe6, 0x1d, 0x74, 0x0a, 0xcf, 0x43, + 0xc9, 0x68, 0x9a, 0x96, 0xd1, 0xa9, 0xd7, 0xad, 0xed, 0xba, 0x76, 0x1d, 0x29, 0x18, 0xc1, 0x6c, + 0x8b, 0xd4, 0xac, 0x1b, 0xfa, 0x1d, 0xc1, 0x49, 0xe1, 0x05, 0x98, 0xeb, 0x18, 0xb5, 0x9b, 0x1d, + 0x7d, 0xcc, 0xcc, 0xe0, 0x25, 0x98, 0x6f, 0x74, 0xea, 0x66, 0xad, 0x55, 0x4f, 0xb0, 0xf3, 0xb8, + 0x04, 0x85, 0xcd, 0x7a, 0x73, 0x53, 0x90, 0x88, 0xad, 0xdf, 0x31, 0xda, 0xb5, 0xeb, 0x86, 0xbe, + 0x25, 0x58, 0x2b, 0x8c, 0x75, 0x57, 0x27, 0xcd, 0xed, 0x5a, 0x64, 0xf2, 0x1a, 0x46, 0x50, 0xdc, + 0xac, 0x19, 0x1a, 0x91, 0xab, 0x3c, 0x52, 0x70, 0x19, 0x0a, 0xba, 0xd1, 0x69, 0x48, 0x3a, 0x85, + 0x2b, 0xb0, 0xa0, 0x75, 0xcc, 0xa6, 0x55, 0x33, 0xaa, 0x44, 0x6f, 0xe8, 0x86, 0x29, 0x25, 0x19, + 0xbc, 0x00, 0x65, 0xb3, 0xd6, 0xd0, 0xdb, 0xa6, 0xd6, 0x68, 0x49, 0x26, 0xdb, 0x45, 0xbe, 0xad, + 0x47, 0x3a, 0x08, 0x2f, 0xc3, 0x92, 0xd1, 0xb4, 0xe4, 0x1d, 0x12, 0xeb, 0x96, 0x56, 0xef, 0xe8, + 0x52, 0xb6, 0x82, 0xcf, 0x00, 0x6e, 0x1a, 0x56, 0xa7, 0xb5, 0xa5, 0x99, 0xba, 0x65, 0x34, 0x6f, + 0x4b, 0xc1, 0x35, 0x5c, 0x86, 0xfc, 0x78, 0x07, 0x8f, 0x18, 0x0a, 0xa5, 0x96, 0x46, 0xcc, 0xb1, + 0xb3, 0x8f, 0x1e, 0x31, 0xb0, 0xe0, 0x3a, 0x69, 0x76, 0x5a, 0x63, 0xb5, 0x79, 0x28, 0x4a, 0xb0, + 0x24, 0x2b, 0xc3, 0x58, 0x9b, 0x35, 0xa3, 0x1a, 0xef, 0xef, 0x51, 0x7e, 0x39, 0x85, 0x94, 0xb5, + 0x3d, 0xc8, 0xf0, 0x70, 0xe4, 0x21, 0x63, 0x34, 0x0d, 0x1d, 0x9d, 0xc2, 0x73, 0x00, 0xb5, 0x76, + 0xcd, 0x30, 0xf5, 0xeb, 0x44, 0xab, 0x33, 0xb7, 0x39, 0x23, 0x02, 0x90, 0x79, 0x3b, 0x0b, 0x33, + 0xb5, 0xf6, 0x76, 0xbd, 0xa9, 0x99, 0xd2, 0xcd, 0x5a, 0xfb, 0x66, 0xa7, 0x69, 0x32, 0x21, 0xc2, + 0x45, 0xc8, 0xd5, 0xda, 0xa6, 0xfe, 0xb1, 0xc9, 0xfc, 0xe2, 0x32, 0x81, 0x2a, 0x7a, 0x74, 0x6d, + 0xed, 0xcb, 0x34, 0x64, 0xf8, 0x0d, 0xc0, 0x12, 0x14, 0x78, 0xb4, 0xcd, 0x3b, 0x2d, 0x66, 0xb2, + 0x00, 0x99, 0x9a, 0x61, 0x5e, 0x45, 0x7f, 0x90, 0xc2, 0x00, 0xd9, 0x0e, 0x1f, 0xff, 0x61, 0x8e, + 0x8d, 0x6b, 0x86, 0xf9, 0xce, 0x15, 0xf4, 0x69, 0x8a, 0x2d, 0xdb, 0x11, 0xc4, 0x1f, 0x45, 0x82, + 0x8d, 0xcb, 0xe8, 0xb3, 0x58, 0xb0, 0x71, 0x19, 0xfd, 0x71, 0x24, 0xb8, 0xb4, 0x81, 0xfe, 0x24, + 0x16, 0x5c, 0xda, 0x40, 0x7f, 0x1a, 0x09, 0xae, 0x5c, 0x46, 0x7f, 0x16, 0x0b, 0xae, 0x5c, 0x46, + 0x7f, 0x9e, 0x63, 0xbe, 0x70, 0x4f, 0x2e, 0x6d, 0xa0, 0xbf, 0xc8, 0xc7, 0xd4, 0x95, 0xcb, 0xe8, + 0x2f, 0xf3, 0x2c, 0xfe, 0x71, 0x54, 0xd1, 0x5f, 0x21, 0xb6, 0x4d, 0x16, 0x20, 0xf4, 0xd7, 0x7c, + 0xc8, 0x44, 0xe8, 0x6f, 0x10, 0xf3, 0x91, 0x71, 0x39, 0xf9, 0x39, 0x97, 0xdc, 0xd1, 0x35, 0x82, + 0xfe, 0x36, 0x27, 0xae, 0x0c, 0x55, 0x6b, 0x0d, 0xad, 0x8e, 0x30, 0x9f, 0xc1, 0x50, 0xf9, 0xfb, + 0x8b, 0x6c, 0xc8, 0xd2, 0x13, 0xfd, 0x43, 0x8b, 0x19, 0xbc, 0xa5, 0x91, 0xea, 0x87, 0x1a, 0x41, + 0xff, 0x78, 0x91, 0x19, 0xbc, 0xa5, 0x11, 0x89, 0xd7, 0x3f, 0xb5, 0x98, 0x22, 0x17, 0x7d, 0x71, + 0x91, 0x6d, 0x5a, 0xf2, 0xff, 0xb9, 0x85, 0xf3, 0x90, 0xde, 0xac, 0x99, 0xe8, 0x4b, 0x6e, 0x8d, + 0xa5, 0x28, 0xfa, 0x17, 0xc4, 0x98, 0x6d, 0xdd, 0x44, 0xff, 0xca, 0x98, 0x59, 0xb3, 0xd3, 0xaa, + 0xeb, 0xe8, 0x25, 0xb6, 0xb9, 0xeb, 0x7a, 0xb3, 0xa1, 0x9b, 0xe4, 0x0e, 0xfa, 0x37, 0xae, 0xfe, + 0x51, 0xbb, 0x69, 0xa0, 0xaf, 0x10, 0x2e, 0x03, 0xe8, 0x1f, 0xb7, 0x88, 0xde, 0x6e, 0xd7, 0x9a, + 0x06, 0x7a, 0x75, 0x6d, 0x1b, 0xd0, 0xd1, 0x72, 0xc0, 0x1c, 0xe8, 0x18, 0x37, 0x8c, 0xe6, 0x6d, + 0x03, 0x9d, 0x62, 0x44, 0x8b, 0xe8, 0x2d, 0x8d, 0xe8, 0x48, 0xc1, 0x00, 0x39, 0x79, 0x11, 0x29, + 0x85, 0x67, 0x21, 0x4f, 0x9a, 0xf5, 0xfa, 0xa6, 0x56, 0xbd, 0x81, 0xd2, 0x9b, 0xef, 0xc2, 0x9c, + 0xe3, 0xad, 0x1f, 0x38, 0x21, 0x0d, 0x02, 0x71, 0xc7, 0xf4, 0xae, 0x2a, 0x29, 0xc7, 0xbb, 0x20, + 0x46, 0x17, 0xfa, 0xde, 0x85, 0x83, 0xf0, 0x02, 0x97, 0x5e, 0xe0, 0x15, 0x63, 0x27, 0xc7, 0x89, + 0x4b, 0xbf, 0x0c, 0x00, 0x00, 0xff, 0xff, 0xa7, 0xa1, 0x8c, 0xaf, 0xc1, 0x2a, 0x00, 0x00, } diff --git a/go/vt/proto/queryservice/queryservice.pb.go b/go/vt/proto/queryservice/queryservice.pb.go index 74b816b4ede..051d9026b97 100644 --- a/go/vt/proto/queryservice/queryservice.pb.go +++ b/go/vt/proto/queryservice/queryservice.pb.go @@ -30,44 +30,43 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package func init() { proto.RegisterFile("queryservice.proto", fileDescriptor_4bd2dde8711f22e3) } var fileDescriptor_4bd2dde8711f22e3 = []byte{ - // 582 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x95, 0xdf, 0x6f, 0xd3, 0x30, - 0x10, 0xc7, 0xe1, 0x61, 0x2b, 0xba, 0x96, 0x32, 0x3c, 0x06, 0x2c, 0x1b, 0xdd, 0x8f, 0x37, 0x84, - 0xd4, 0x22, 0x40, 0x42, 0x9a, 0xc4, 0xc3, 0x5a, 0x31, 0x81, 0x26, 0x7e, 0xb5, 0x6c, 0x42, 0x20, - 0x21, 0xb9, 0xa9, 0x55, 0xa2, 0xa5, 0x71, 0x16, 0x3b, 0x1d, 0xfc, 0x37, 0xfc, 0xa9, 0xd3, 0xe2, - 0xdc, 0xc5, 0x76, 0x93, 0xbd, 0xcd, 0xdf, 0xef, 0xdd, 0x67, 0x17, 0x5f, 0xef, 0x0c, 0xec, 0x32, - 0x17, 0xd9, 0x3f, 0x25, 0xb2, 0x65, 0x14, 0x8a, 0x7e, 0x9a, 0x49, 0x2d, 0x59, 0xc7, 0xd6, 0x82, - 0x76, 0x71, 0x32, 0x56, 0xb0, 0x31, 0x8d, 0x92, 0x58, 0xce, 0x67, 0x5c, 0x73, 0xa3, 0xbc, 0xfa, - 0xdf, 0x85, 0xb5, 0x6f, 0x37, 0x11, 0xec, 0x08, 0x5a, 0xef, 0xff, 0x8a, 0x30, 0xd7, 0x82, 0x6d, - 0xf5, 0x4d, 0x52, 0x79, 0x1e, 0x8b, 0xcb, 0x5c, 0x28, 0x1d, 0x3c, 0xf6, 0x65, 0x95, 0xca, 0x44, - 0x89, 0xc3, 0x3b, 0xec, 0x23, 0x74, 0x4a, 0x71, 0xc8, 0x75, 0xf8, 0x87, 0x05, 0x6e, 0x64, 0x21, - 0x22, 0x65, 0xa7, 0xd6, 0x23, 0xd4, 0x67, 0xb8, 0x3f, 0xd1, 0x99, 0xe0, 0x0b, 0x2c, 0x06, 0xe3, - 0x1d, 0x15, 0x61, 0xbb, 0xf5, 0x26, 0xd2, 0x5e, 0xde, 0x65, 0x6f, 0x60, 0x6d, 0x28, 0xe6, 0x51, - 0xc2, 0x36, 0xcb, 0xd0, 0xe2, 0x84, 0xf9, 0x8f, 0x5c, 0x91, 0xaa, 0x78, 0x0b, 0xeb, 0x23, 0xb9, - 0x58, 0x44, 0x9a, 0x61, 0x84, 0x39, 0x62, 0xde, 0x96, 0xa7, 0x52, 0xe2, 0x3b, 0xb8, 0x37, 0x96, - 0x71, 0x3c, 0xe5, 0xe1, 0x05, 0xc3, 0xfb, 0x42, 0x01, 0x93, 0x9f, 0xac, 0xe8, 0x94, 0x7e, 0x04, - 0xad, 0xaf, 0x99, 0x48, 0x79, 0x56, 0x35, 0xa1, 0x3c, 0xfb, 0x4d, 0x20, 0x99, 0x72, 0xbf, 0x40, - 0xd7, 0x94, 0x53, 0x5a, 0x33, 0xb6, 0xeb, 0x54, 0x89, 0x32, 0x92, 0x9e, 0x35, 0xb8, 0x04, 0x3c, - 0x83, 0x0d, 0x2c, 0x91, 0x90, 0x3d, 0xaf, 0x76, 0x1f, 0xba, 0xd7, 0xe8, 0x13, 0xf6, 0x07, 0x3c, - 0x1c, 0x65, 0x82, 0x6b, 0xf1, 0x3d, 0xe3, 0x89, 0xe2, 0xa1, 0x8e, 0x64, 0xc2, 0x30, 0x6f, 0xc5, - 0x41, 0xf0, 0x7e, 0x73, 0x00, 0x91, 0x4f, 0xa0, 0x3d, 0xd1, 0x3c, 0xd3, 0x65, 0xeb, 0xb6, 0xe9, - 0xc7, 0x41, 0x1a, 0xd2, 0x82, 0x3a, 0xcb, 0xe1, 0x08, 0x4d, 0x7d, 0x24, 0x4e, 0xa5, 0xad, 0x70, - 0x6c, 0x8b, 0x38, 0xbf, 0x61, 0x73, 0x24, 0x93, 0x30, 0xce, 0x67, 0xce, 0xb7, 0x1e, 0xd0, 0xc5, - 0xaf, 0x78, 0xc8, 0x3d, 0xbc, 0x2d, 0x84, 0xf8, 0x63, 0x78, 0x30, 0x16, 0x7c, 0x66, 0xb3, 0xb1, - 0xa9, 0x9e, 0x8e, 0xdc, 0x5e, 0x93, 0x6d, 0x8f, 0x72, 0x31, 0x0c, 0x38, 0x7e, 0x81, 0x3d, 0x21, - 0xde, 0xf4, 0xed, 0xd4, 0x7a, 0x76, 0xa3, 0x6d, 0xc7, 0xac, 0x86, 0xbd, 0x9a, 0x1c, 0x67, 0x3f, - 0xec, 0x37, 0x07, 0xd8, 0x4b, 0xe2, 0x93, 0x50, 0x8a, 0xcf, 0x85, 0x19, 0x7c, 0x5a, 0x12, 0x8e, - 0xea, 0x2f, 0x09, 0xcf, 0xb4, 0x96, 0xc4, 0x08, 0xa0, 0x34, 0x8f, 0xc3, 0x0b, 0xf6, 0xd4, 0x8d, - 0x3f, 0xae, 0xda, 0xbd, 0x5d, 0xe3, 0x50, 0x51, 0x23, 0x80, 0x49, 0x1a, 0x47, 0xda, 0xac, 0x53, - 0x84, 0x54, 0x92, 0x0f, 0xb1, 0x1d, 0x82, 0x9c, 0x42, 0xc7, 0xd4, 0xf7, 0x41, 0xf0, 0x58, 0x57, - 0x9b, 0xd4, 0x16, 0xfd, 0xeb, 0x77, 0x3d, 0xeb, 0xb3, 0x4e, 0xa1, 0x73, 0x96, 0xce, 0xb8, 0xc6, - 0x5b, 0x42, 0x98, 0x2d, 0xfa, 0x30, 0xd7, 0xb3, 0x60, 0x27, 0xd0, 0x3a, 0x27, 0x8e, 0xf5, 0x8e, - 0x9c, 0xfb, 0x9c, 0x3a, 0xcf, 0xe2, 0x8c, 0xa1, 0x8d, 0xb2, 0xbc, 0x52, 0xac, 0x57, 0x17, 0x2f, - 0xaf, 0x54, 0xb5, 0x50, 0x9a, 0x7c, 0x8b, 0xf9, 0x0b, 0xba, 0xd5, 0xbf, 0xca, 0x63, 0xad, 0xd8, - 0x41, 0x7d, 0x19, 0x37, 0x5e, 0x35, 0x63, 0xb7, 0x84, 0x54, 0xf0, 0xe1, 0x8b, 0x9f, 0xcf, 0x97, - 0x91, 0x16, 0x4a, 0xf5, 0x23, 0x39, 0x30, 0x7f, 0x0d, 0xe6, 0x72, 0xb0, 0xd4, 0x83, 0xe2, 0x09, - 0x1d, 0xd8, 0xcf, 0xed, 0x74, 0xbd, 0xd0, 0x5e, 0x5f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xbe, 0xb4, - 0xc0, 0x34, 0x99, 0x07, 0x00, 0x00, + // 566 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x95, 0xdf, 0x4f, 0x13, 0x41, + 0x10, 0xc7, 0xf5, 0x01, 0x6a, 0xa6, 0x05, 0x71, 0x11, 0x95, 0x03, 0xcb, 0x8f, 0x37, 0x63, 0xd2, + 0x1a, 0x35, 0x31, 0x21, 0xf1, 0x81, 0x36, 0x12, 0x0d, 0xf1, 0x57, 0x11, 0x62, 0x34, 0x31, 0xd9, + 0x5e, 0x27, 0xf5, 0xc2, 0xf5, 0xb6, 0xdc, 0xee, 0x15, 0xfd, 0x5b, 0xfd, 0x67, 0x0c, 0x77, 0x37, + 0x73, 0xbb, 0xdb, 0x3b, 0xde, 0xd8, 0xef, 0x77, 0xe6, 0xc3, 0xdc, 0x4e, 0x67, 0x16, 0xc4, 0x55, + 0x86, 0xe9, 0x5f, 0x8d, 0xe9, 0x22, 0x0a, 0xb1, 0x37, 0x4f, 0x95, 0x51, 0xa2, 0x63, 0x6b, 0x41, + 0x3b, 0x3f, 0x15, 0x56, 0xb0, 0x31, 0x8e, 0x92, 0x58, 0x4d, 0x27, 0xd2, 0xc8, 0x42, 0x79, 0xf9, + 0x6f, 0x0d, 0x56, 0xbe, 0xde, 0x44, 0x88, 0x23, 0x68, 0xbd, 0xfb, 0x83, 0x61, 0x66, 0x50, 0x6c, + 0xf5, 0x8a, 0xa4, 0xf2, 0x3c, 0xc2, 0xab, 0x0c, 0xb5, 0x09, 0x1e, 0xf9, 0xb2, 0x9e, 0xab, 0x44, + 0xe3, 0xe1, 0x1d, 0xf1, 0x01, 0x3a, 0xa5, 0x38, 0x90, 0x26, 0xfc, 0x2d, 0x02, 0x37, 0x32, 0x17, + 0x89, 0xb2, 0x53, 0xeb, 0x31, 0xea, 0x13, 0xac, 0x9d, 0x99, 0x14, 0xe5, 0x8c, 0x8a, 0xa1, 0x78, + 0x47, 0x25, 0xd8, 0x6e, 0xbd, 0x49, 0xb4, 0x17, 0x77, 0xc5, 0x6b, 0x58, 0x19, 0xe0, 0x34, 0x4a, + 0xc4, 0x66, 0x19, 0x9a, 0x9f, 0x28, 0xff, 0xa1, 0x2b, 0x72, 0x15, 0x6f, 0x60, 0x75, 0xa8, 0x66, + 0xb3, 0xc8, 0x08, 0x8a, 0x28, 0x8e, 0x94, 0xb7, 0xe5, 0xa9, 0x9c, 0xf8, 0x16, 0xee, 0x8d, 0x54, + 0x1c, 0x8f, 0x65, 0x78, 0x29, 0xe8, 0xbe, 0x48, 0xa0, 0xe4, 0xc7, 0x4b, 0x3a, 0xa7, 0x1f, 0x41, + 0xeb, 0x4b, 0x8a, 0x73, 0x99, 0x56, 0x4d, 0x28, 0xcf, 0x7e, 0x13, 0x58, 0xe6, 0xdc, 0xcf, 0xb0, + 0x5e, 0x94, 0x53, 0x5a, 0x13, 0xb1, 0xeb, 0x54, 0x49, 0x32, 0x91, 0x9e, 0x36, 0xb8, 0x0c, 0x3c, + 0x87, 0x0d, 0x2a, 0x91, 0x91, 0x5d, 0xaf, 0x76, 0x1f, 0xba, 0xd7, 0xe8, 0x33, 0xf6, 0x3b, 0x3c, + 0x18, 0xa6, 0x28, 0x0d, 0x7e, 0x4b, 0x65, 0xa2, 0x65, 0x68, 0x22, 0x95, 0x08, 0xca, 0x5b, 0x72, + 0x08, 0xbc, 0xdf, 0x1c, 0xc0, 0xe4, 0x13, 0x68, 0x9f, 0x19, 0x99, 0x9a, 0xb2, 0x75, 0xdb, 0xfc, + 0xe3, 0x60, 0x8d, 0x68, 0x41, 0x9d, 0xe5, 0x70, 0xd0, 0x70, 0x1f, 0x99, 0x53, 0x69, 0x4b, 0x1c, + 0xdb, 0x62, 0xce, 0x2f, 0xd8, 0x1c, 0xaa, 0x24, 0x8c, 0xb3, 0x89, 0xf3, 0xad, 0x07, 0x7c, 0xf1, + 0x4b, 0x1e, 0x71, 0x0f, 0x6f, 0x0b, 0x61, 0xfe, 0x08, 0xee, 0x8f, 0x50, 0x4e, 0x6c, 0x36, 0x35, + 0xd5, 0xd3, 0x89, 0xdb, 0x6d, 0xb2, 0xed, 0x51, 0xce, 0x87, 0x81, 0xc6, 0x2f, 0xb0, 0x27, 0xc4, + 0x9b, 0xbe, 0x9d, 0x5a, 0xcf, 0x6e, 0xb4, 0xed, 0x14, 0xab, 0x61, 0xaf, 0x26, 0xc7, 0xd9, 0x0f, + 0xfb, 0xcd, 0x01, 0xf6, 0x92, 0xf8, 0x88, 0x5a, 0xcb, 0x29, 0x16, 0x83, 0xcf, 0x4b, 0xc2, 0x51, + 0xfd, 0x25, 0xe1, 0x99, 0xd6, 0x92, 0x18, 0x02, 0x94, 0xe6, 0x71, 0x78, 0x29, 0x9e, 0xb8, 0xf1, + 0xc7, 0x55, 0xbb, 0xb7, 0x6b, 0x1c, 0x2e, 0xea, 0x14, 0x3a, 0x05, 0xfa, 0x3d, 0xca, 0xd8, 0x54, + 0x4b, 0xd0, 0x16, 0xfd, 0x9b, 0x73, 0x3d, 0xab, 0xa2, 0x53, 0xe8, 0x9c, 0xcf, 0x27, 0xd2, 0xd0, + 0x07, 0x12, 0xcc, 0x16, 0x7d, 0x98, 0xeb, 0x59, 0xb0, 0x13, 0x68, 0x5d, 0x30, 0xc7, 0x7a, 0x02, + 0x2e, 0x7c, 0x4e, 0x9d, 0x67, 0x71, 0x46, 0xd0, 0x26, 0x59, 0x5d, 0x6b, 0xd1, 0xad, 0x8b, 0x57, + 0xd7, 0xba, 0xda, 0x05, 0x4d, 0xbe, 0xc5, 0xfc, 0x09, 0xeb, 0xd5, 0xbf, 0xca, 0x62, 0xa3, 0xc5, + 0x41, 0x7d, 0x19, 0x37, 0x5e, 0x35, 0x1e, 0xb7, 0x84, 0x54, 0xf0, 0xc1, 0xf3, 0x1f, 0xcf, 0x16, + 0x91, 0x41, 0xad, 0x7b, 0x91, 0xea, 0x17, 0x7f, 0xf5, 0xa7, 0xaa, 0xbf, 0x30, 0xfd, 0xfc, 0xf5, + 0xeb, 0xdb, 0x2f, 0xe5, 0x78, 0x35, 0xd7, 0x5e, 0xfd, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xf3, + 0x72, 0x91, 0x54, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -123,9 +122,6 @@ type QueryClient interface { MessageStream(ctx context.Context, in *query.MessageStreamRequest, opts ...grpc.CallOption) (Query_MessageStreamClient, error) // MessageAck acks messages for a table. MessageAck(ctx context.Context, in *query.MessageAckRequest, opts ...grpc.CallOption) (*query.MessageAckResponse, error) - // SplitQuery is the API to facilitate MapReduce-type iterations - // over large data sets (like full table dumps). - SplitQuery(ctx context.Context, in *query.SplitQueryRequest, opts ...grpc.CallOption) (*query.SplitQueryResponse, error) // StreamHealth runs a streaming RPC to the tablet, that returns the // current health of the tablet on a regular basis. StreamHealth(ctx context.Context, in *query.StreamHealthRequest, opts ...grpc.CallOption) (Query_StreamHealthClient, error) @@ -355,15 +351,6 @@ func (c *queryClient) MessageAck(ctx context.Context, in *query.MessageAckReques return out, nil } -func (c *queryClient) SplitQuery(ctx context.Context, in *query.SplitQueryRequest, opts ...grpc.CallOption) (*query.SplitQueryResponse, error) { - out := new(query.SplitQueryResponse) - err := c.cc.Invoke(ctx, "/queryservice.Query/SplitQuery", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *queryClient) StreamHealth(ctx context.Context, in *query.StreamHealthRequest, opts ...grpc.CallOption) (Query_StreamHealthClient, error) { stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[2], "/queryservice.Query/StreamHealth", opts...) if err != nil { @@ -567,9 +554,6 @@ type QueryServer interface { MessageStream(*query.MessageStreamRequest, Query_MessageStreamServer) error // MessageAck acks messages for a table. MessageAck(context.Context, *query.MessageAckRequest) (*query.MessageAckResponse, error) - // SplitQuery is the API to facilitate MapReduce-type iterations - // over large data sets (like full table dumps). - SplitQuery(context.Context, *query.SplitQueryRequest) (*query.SplitQueryResponse, error) // StreamHealth runs a streaming RPC to the tablet, that returns the // current health of the tablet on a regular basis. StreamHealth(*query.StreamHealthRequest, Query_StreamHealthServer) error @@ -641,9 +625,6 @@ func (*UnimplementedQueryServer) MessageStream(req *query.MessageStreamRequest, func (*UnimplementedQueryServer) MessageAck(ctx context.Context, req *query.MessageAckRequest) (*query.MessageAckResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method MessageAck not implemented") } -func (*UnimplementedQueryServer) SplitQuery(ctx context.Context, req *query.SplitQueryRequest) (*query.SplitQueryResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SplitQuery not implemented") -} func (*UnimplementedQueryServer) StreamHealth(req *query.StreamHealthRequest, srv Query_StreamHealthServer) error { return status.Errorf(codes.Unimplemented, "method StreamHealth not implemented") } @@ -994,24 +975,6 @@ func _Query_MessageAck_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } -func _Query_SplitQuery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(query.SplitQueryRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(QueryServer).SplitQuery(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/queryservice.Query/SplitQuery", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).SplitQuery(ctx, req.(*query.SplitQueryRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Query_StreamHealth_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(query.StreamHealthRequest) if err := stream.RecvMsg(m); err != nil { @@ -1185,10 +1148,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "MessageAck", Handler: _Query_MessageAck_Handler, }, - { - MethodName: "SplitQuery", - Handler: _Query_SplitQuery_Handler, - }, }, Streams: []grpc.StreamDesc{ { diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index c31bdbbb0d0..8da164ca2af 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -2918,388 +2918,6 @@ func (m *ResolveTransactionResponse) XXX_DiscardUnknown() { var xxx_messageInfo_ResolveTransactionResponse proto.InternalMessageInfo -// SplitQueryRequest is the payload to SplitQuery. -// -// SplitQuery takes a "SELECT" query and generates a list of queries called -// "query-parts". Each query-part consists of the original query with an -// added WHERE clause that restricts the query-part to operate only on -// rows whose values in the columns listed in the "split_column" field -// of the request (see below) are in a particular range. -// -// It is guaranteed that the set of rows obtained from -// executing each query-part on a database snapshot -// and merging (without deduping) the results is equal to the set of rows -// obtained from executing the original query on the same snapshot -// with the rows containing NULL values in any of the split_column's excluded. -// -// This is typically called by the MapReduce master when reading from Vitess. -// There it's desirable that the sets of rows returned by the query-parts -// have roughly the same size. -type SplitQueryRequest struct { - // caller_id identifies the caller. This is the effective caller ID, - // set by the application to further identify the caller. - CallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=caller_id,json=callerId,proto3" json:"caller_id,omitempty"` - // keyspace to target the query to. - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - // The query and bind variables to produce splits for. - // The given query must be a simple query of the form - // SELECT FROM WHERE . - // It must not contain subqueries nor any of the keywords - // JOIN, GROUP BY, ORDER BY, LIMIT, DISTINCT. - // Furthermore,
must be a single "concrete" table. - // It cannot be a view. - Query *query.BoundQuery `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"` - // Each generated query-part will be restricted to rows whose values - // in the columns listed in this field are in a particular range. - // The list of columns named here must be a prefix of the list of - // columns defining some index or primary key of the table - // referenced in 'query'. For many tables using the primary key columns - // (in order) is sufficient and this is the default if this field is omitted. - // See the comment on the 'algorithm' field for more restrictions and - // information. - SplitColumn []string `protobuf:"bytes,4,rep,name=split_column,json=splitColumn,proto3" json:"split_column,omitempty"` - // You can specify either an estimate of the number of query-parts to - // generate or an estimate of the number of rows each query-part should - // return. - // Thus, exactly one of split_count or num_rows_per_query_part - // should be nonzero. - // The non-given parameter is calculated from the given parameter - // using the formula: split_count * num_rows_per_query_pary = table_size, - // where table_size is an approximation of the number of rows in the - // table. - // Note that if "split_count" is given it is regarded as an estimate. - // The number of query-parts returned may differ slightly (in particular, - // if it's not a whole multiple of the number of vitess shards). - SplitCount int64 `protobuf:"varint,5,opt,name=split_count,json=splitCount,proto3" json:"split_count,omitempty"` - NumRowsPerQueryPart int64 `protobuf:"varint,6,opt,name=num_rows_per_query_part,json=numRowsPerQueryPart,proto3" json:"num_rows_per_query_part,omitempty"` - // The algorithm to use to split the query. The split algorithm is performed - // on each database shard in parallel. The lists of query-parts generated - // by the shards are merged and returned to the caller. - // Two algorithms are supported: - // EQUAL_SPLITS - // If this algorithm is selected then only the first 'split_column' given - // is used (or the first primary key column if the 'split_column' field is - // empty). In the rest of this algorithm's description, we refer to - // this column as "the split column". - // The split column must have numeric type (integral or floating point). - // The algorithm works by taking the interval [min, max], where min and - // max are the minimum and maximum values of the split column in - // the table-shard, respectively, and partitioning it into 'split_count' - // sub-intervals of equal size. The added WHERE clause of each query-part - // restricts that part to rows whose value in the split column belongs to - // a particular sub-interval. This is fast, but requires that the - // distribution of values of the split column be uniform in [min, max] - // for the number of rows returned by each query part to be roughly the - // same. - // FULL_SCAN - // If this algorithm is used then the split_column must be the primary key - // columns (in order). - // This algorithm performs a full-scan of the table-shard referenced - // in 'query' to get "boundary" rows that are num_rows_per_query_part - // apart when the table is ordered by the columns listed in - // 'split_column'. It then restricts each query-part to the rows - // located between two successive boundary rows. - // This algorithm supports multiple split_column's of any type, - // but is slower than EQUAL_SPLITS. - Algorithm query.SplitQueryRequest_Algorithm `protobuf:"varint,7,opt,name=algorithm,proto3,enum=query.SplitQueryRequest_Algorithm" json:"algorithm,omitempty"` - // TODO(erez): This field is no longer used by the server code. - // Remove this field after this new server code is released to prod. - // We must keep it for now, so that clients can still send it to the old - // server code currently in production. - UseSplitQueryV2 bool `protobuf:"varint,8,opt,name=use_split_query_v2,json=useSplitQueryV2,proto3" json:"use_split_query_v2,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SplitQueryRequest) Reset() { *m = SplitQueryRequest{} } -func (m *SplitQueryRequest) String() string { return proto.CompactTextString(m) } -func (*SplitQueryRequest) ProtoMessage() {} -func (*SplitQueryRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{39} -} - -func (m *SplitQueryRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SplitQueryRequest.Unmarshal(m, b) -} -func (m *SplitQueryRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SplitQueryRequest.Marshal(b, m, deterministic) -} -func (m *SplitQueryRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_SplitQueryRequest.Merge(m, src) -} -func (m *SplitQueryRequest) XXX_Size() int { - return xxx_messageInfo_SplitQueryRequest.Size(m) -} -func (m *SplitQueryRequest) XXX_DiscardUnknown() { - xxx_messageInfo_SplitQueryRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_SplitQueryRequest proto.InternalMessageInfo - -func (m *SplitQueryRequest) GetCallerId() *vtrpc.CallerID { - if m != nil { - return m.CallerId - } - return nil -} - -func (m *SplitQueryRequest) GetKeyspace() string { - if m != nil { - return m.Keyspace - } - return "" -} - -func (m *SplitQueryRequest) GetQuery() *query.BoundQuery { - if m != nil { - return m.Query - } - return nil -} - -func (m *SplitQueryRequest) GetSplitColumn() []string { - if m != nil { - return m.SplitColumn - } - return nil -} - -func (m *SplitQueryRequest) GetSplitCount() int64 { - if m != nil { - return m.SplitCount - } - return 0 -} - -func (m *SplitQueryRequest) GetNumRowsPerQueryPart() int64 { - if m != nil { - return m.NumRowsPerQueryPart - } - return 0 -} - -func (m *SplitQueryRequest) GetAlgorithm() query.SplitQueryRequest_Algorithm { - if m != nil { - return m.Algorithm - } - return query.SplitQueryRequest_EQUAL_SPLITS -} - -func (m *SplitQueryRequest) GetUseSplitQueryV2() bool { - if m != nil { - return m.UseSplitQueryV2 - } - return false -} - -// SplitQueryResponse is the returned value from SplitQuery. -type SplitQueryResponse struct { - // splits contains the queries to run to fetch the entire data set. - Splits []*SplitQueryResponse_Part `protobuf:"bytes,1,rep,name=splits,proto3" json:"splits,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SplitQueryResponse) Reset() { *m = SplitQueryResponse{} } -func (m *SplitQueryResponse) String() string { return proto.CompactTextString(m) } -func (*SplitQueryResponse) ProtoMessage() {} -func (*SplitQueryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{40} -} - -func (m *SplitQueryResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SplitQueryResponse.Unmarshal(m, b) -} -func (m *SplitQueryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SplitQueryResponse.Marshal(b, m, deterministic) -} -func (m *SplitQueryResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_SplitQueryResponse.Merge(m, src) -} -func (m *SplitQueryResponse) XXX_Size() int { - return xxx_messageInfo_SplitQueryResponse.Size(m) -} -func (m *SplitQueryResponse) XXX_DiscardUnknown() { - xxx_messageInfo_SplitQueryResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_SplitQueryResponse proto.InternalMessageInfo - -func (m *SplitQueryResponse) GetSplits() []*SplitQueryResponse_Part { - if m != nil { - return m.Splits - } - return nil -} - -type SplitQueryResponse_KeyRangePart struct { - // keyspace to target the query to. - Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - // key ranges to target the query to. - KeyRanges []*topodata.KeyRange `protobuf:"bytes,2,rep,name=key_ranges,json=keyRanges,proto3" json:"key_ranges,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SplitQueryResponse_KeyRangePart) Reset() { *m = SplitQueryResponse_KeyRangePart{} } -func (m *SplitQueryResponse_KeyRangePart) String() string { return proto.CompactTextString(m) } -func (*SplitQueryResponse_KeyRangePart) ProtoMessage() {} -func (*SplitQueryResponse_KeyRangePart) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{40, 0} -} - -func (m *SplitQueryResponse_KeyRangePart) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SplitQueryResponse_KeyRangePart.Unmarshal(m, b) -} -func (m *SplitQueryResponse_KeyRangePart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SplitQueryResponse_KeyRangePart.Marshal(b, m, deterministic) -} -func (m *SplitQueryResponse_KeyRangePart) XXX_Merge(src proto.Message) { - xxx_messageInfo_SplitQueryResponse_KeyRangePart.Merge(m, src) -} -func (m *SplitQueryResponse_KeyRangePart) XXX_Size() int { - return xxx_messageInfo_SplitQueryResponse_KeyRangePart.Size(m) -} -func (m *SplitQueryResponse_KeyRangePart) XXX_DiscardUnknown() { - xxx_messageInfo_SplitQueryResponse_KeyRangePart.DiscardUnknown(m) -} - -var xxx_messageInfo_SplitQueryResponse_KeyRangePart proto.InternalMessageInfo - -func (m *SplitQueryResponse_KeyRangePart) GetKeyspace() string { - if m != nil { - return m.Keyspace - } - return "" -} - -func (m *SplitQueryResponse_KeyRangePart) GetKeyRanges() []*topodata.KeyRange { - if m != nil { - return m.KeyRanges - } - return nil -} - -type SplitQueryResponse_ShardPart struct { - // keyspace to target the query to. - Keyspace string `protobuf:"bytes,1,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - // shards to target the query to. - Shards []string `protobuf:"bytes,2,rep,name=shards,proto3" json:"shards,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SplitQueryResponse_ShardPart) Reset() { *m = SplitQueryResponse_ShardPart{} } -func (m *SplitQueryResponse_ShardPart) String() string { return proto.CompactTextString(m) } -func (*SplitQueryResponse_ShardPart) ProtoMessage() {} -func (*SplitQueryResponse_ShardPart) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{40, 1} -} - -func (m *SplitQueryResponse_ShardPart) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SplitQueryResponse_ShardPart.Unmarshal(m, b) -} -func (m *SplitQueryResponse_ShardPart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SplitQueryResponse_ShardPart.Marshal(b, m, deterministic) -} -func (m *SplitQueryResponse_ShardPart) XXX_Merge(src proto.Message) { - xxx_messageInfo_SplitQueryResponse_ShardPart.Merge(m, src) -} -func (m *SplitQueryResponse_ShardPart) XXX_Size() int { - return xxx_messageInfo_SplitQueryResponse_ShardPart.Size(m) -} -func (m *SplitQueryResponse_ShardPart) XXX_DiscardUnknown() { - xxx_messageInfo_SplitQueryResponse_ShardPart.DiscardUnknown(m) -} - -var xxx_messageInfo_SplitQueryResponse_ShardPart proto.InternalMessageInfo - -func (m *SplitQueryResponse_ShardPart) GetKeyspace() string { - if m != nil { - return m.Keyspace - } - return "" -} - -func (m *SplitQueryResponse_ShardPart) GetShards() []string { - if m != nil { - return m.Shards - } - return nil -} - -type SplitQueryResponse_Part struct { - // query is the query and bind variables to execute. - Query *query.BoundQuery `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` - // key_range_part is set if the query should be executed by - // ExecuteKeyRanges. - KeyRangePart *SplitQueryResponse_KeyRangePart `protobuf:"bytes,2,opt,name=key_range_part,json=keyRangePart,proto3" json:"key_range_part,omitempty"` - // shard_part is set if the query should be executed by ExecuteShards. - ShardPart *SplitQueryResponse_ShardPart `protobuf:"bytes,3,opt,name=shard_part,json=shardPart,proto3" json:"shard_part,omitempty"` - // size is the approximate number of rows this query will return. - Size int64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SplitQueryResponse_Part) Reset() { *m = SplitQueryResponse_Part{} } -func (m *SplitQueryResponse_Part) String() string { return proto.CompactTextString(m) } -func (*SplitQueryResponse_Part) ProtoMessage() {} -func (*SplitQueryResponse_Part) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{40, 2} -} - -func (m *SplitQueryResponse_Part) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SplitQueryResponse_Part.Unmarshal(m, b) -} -func (m *SplitQueryResponse_Part) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SplitQueryResponse_Part.Marshal(b, m, deterministic) -} -func (m *SplitQueryResponse_Part) XXX_Merge(src proto.Message) { - xxx_messageInfo_SplitQueryResponse_Part.Merge(m, src) -} -func (m *SplitQueryResponse_Part) XXX_Size() int { - return xxx_messageInfo_SplitQueryResponse_Part.Size(m) -} -func (m *SplitQueryResponse_Part) XXX_DiscardUnknown() { - xxx_messageInfo_SplitQueryResponse_Part.DiscardUnknown(m) -} - -var xxx_messageInfo_SplitQueryResponse_Part proto.InternalMessageInfo - -func (m *SplitQueryResponse_Part) GetQuery() *query.BoundQuery { - if m != nil { - return m.Query - } - return nil -} - -func (m *SplitQueryResponse_Part) GetKeyRangePart() *SplitQueryResponse_KeyRangePart { - if m != nil { - return m.KeyRangePart - } - return nil -} - -func (m *SplitQueryResponse_Part) GetShardPart() *SplitQueryResponse_ShardPart { - if m != nil { - return m.ShardPart - } - return nil -} - -func (m *SplitQueryResponse_Part) GetSize() int64 { - if m != nil { - return m.Size - } - return 0 -} - // GetSrvKeyspaceRequest is the payload to GetSrvKeyspace. type GetSrvKeyspaceRequest struct { // keyspace name to fetch. @@ -3313,7 +2931,7 @@ func (m *GetSrvKeyspaceRequest) Reset() { *m = GetSrvKeyspaceRequest{} } func (m *GetSrvKeyspaceRequest) String() string { return proto.CompactTextString(m) } func (*GetSrvKeyspaceRequest) ProtoMessage() {} func (*GetSrvKeyspaceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{41} + return fileDescriptor_aab96496ceaf1ebb, []int{39} } func (m *GetSrvKeyspaceRequest) XXX_Unmarshal(b []byte) error { @@ -3354,7 +2972,7 @@ func (m *GetSrvKeyspaceResponse) Reset() { *m = GetSrvKeyspaceResponse{} func (m *GetSrvKeyspaceResponse) String() string { return proto.CompactTextString(m) } func (*GetSrvKeyspaceResponse) ProtoMessage() {} func (*GetSrvKeyspaceResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{42} + return fileDescriptor_aab96496ceaf1ebb, []int{40} } func (m *GetSrvKeyspaceResponse) XXX_Unmarshal(b []byte) error { @@ -3400,7 +3018,7 @@ func (m *VStreamRequest) Reset() { *m = VStreamRequest{} } func (m *VStreamRequest) String() string { return proto.CompactTextString(m) } func (*VStreamRequest) ProtoMessage() {} func (*VStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{43} + return fileDescriptor_aab96496ceaf1ebb, []int{41} } func (m *VStreamRequest) XXX_Unmarshal(b []byte) error { @@ -3461,7 +3079,7 @@ func (m *VStreamResponse) Reset() { *m = VStreamResponse{} } func (m *VStreamResponse) String() string { return proto.CompactTextString(m) } func (*VStreamResponse) ProtoMessage() {} func (*VStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{44} + return fileDescriptor_aab96496ceaf1ebb, []int{42} } func (m *VStreamResponse) XXX_Unmarshal(b []byte) error { @@ -3520,7 +3138,7 @@ func (m *UpdateStreamRequest) Reset() { *m = UpdateStreamRequest{} } func (m *UpdateStreamRequest) String() string { return proto.CompactTextString(m) } func (*UpdateStreamRequest) ProtoMessage() {} func (*UpdateStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{45} + return fileDescriptor_aab96496ceaf1ebb, []int{43} } func (m *UpdateStreamRequest) XXX_Unmarshal(b []byte) error { @@ -3609,7 +3227,7 @@ func (m *UpdateStreamResponse) Reset() { *m = UpdateStreamResponse{} } func (m *UpdateStreamResponse) String() string { return proto.CompactTextString(m) } func (*UpdateStreamResponse) ProtoMessage() {} func (*UpdateStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{46} + return fileDescriptor_aab96496ceaf1ebb, []int{44} } func (m *UpdateStreamResponse) XXX_Unmarshal(b []byte) error { @@ -3688,11 +3306,6 @@ func init() { proto.RegisterType((*IdKeyspaceId)(nil), "vtgate.IdKeyspaceId") proto.RegisterType((*MessageAckKeyspaceIdsRequest)(nil), "vtgate.MessageAckKeyspaceIdsRequest") proto.RegisterType((*ResolveTransactionResponse)(nil), "vtgate.ResolveTransactionResponse") - proto.RegisterType((*SplitQueryRequest)(nil), "vtgate.SplitQueryRequest") - proto.RegisterType((*SplitQueryResponse)(nil), "vtgate.SplitQueryResponse") - proto.RegisterType((*SplitQueryResponse_KeyRangePart)(nil), "vtgate.SplitQueryResponse.KeyRangePart") - proto.RegisterType((*SplitQueryResponse_ShardPart)(nil), "vtgate.SplitQueryResponse.ShardPart") - proto.RegisterType((*SplitQueryResponse_Part)(nil), "vtgate.SplitQueryResponse.Part") proto.RegisterType((*GetSrvKeyspaceRequest)(nil), "vtgate.GetSrvKeyspaceRequest") proto.RegisterType((*GetSrvKeyspaceResponse)(nil), "vtgate.GetSrvKeyspaceResponse") proto.RegisterType((*VStreamRequest)(nil), "vtgate.VStreamRequest") @@ -3704,136 +3317,120 @@ func init() { func init() { proto.RegisterFile("vtgate.proto", fileDescriptor_aab96496ceaf1ebb) } var fileDescriptor_aab96496ceaf1ebb = []byte{ - // 2086 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xcd, 0x8f, 0x23, 0x47, - 0x15, 0x4f, 0x77, 0xfb, 0xf3, 0xb9, 0xfd, 0xb1, 0x35, 0xb3, 0xbb, 0x8e, 0x33, 0xd9, 0x99, 0x74, - 0x32, 0x5a, 0x67, 0xb3, 0xf2, 0x10, 0x07, 0x02, 0x42, 0x41, 0x61, 0xc6, 0x3b, 0x59, 0x59, 0xd9, - 0xf9, 0xa0, 0xec, 0x9d, 0x05, 0x44, 0xd4, 0xea, 0xb1, 0x2b, 0xde, 0x66, 0xec, 0x6e, 0xa7, 0xab, - 0xec, 0x65, 0x38, 0xa0, 0xfc, 0x07, 0x11, 0x07, 0x24, 0x14, 0x21, 0x21, 0xa4, 0x95, 0x38, 0x71, - 0x45, 0x02, 0x2e, 0xdc, 0x90, 0xb8, 0x20, 0x4e, 0xdc, 0xf9, 0x07, 0x90, 0xf8, 0x0b, 0xa2, 0xae, - 0xaa, 0xfe, 0xb0, 0xe7, 0xcb, 0xe3, 0xd9, 0x59, 0x79, 0x2f, 0x56, 0xd7, 0x7b, 0xaf, 0xaa, 0x5e, - 0xfd, 0xde, 0xaf, 0x5e, 0xbd, 0xae, 0x36, 0xe8, 0x63, 0xd6, 0xb3, 0x18, 0xa9, 0x0d, 0x3d, 0x97, - 0xb9, 0x28, 0x25, 0x5a, 0x95, 0xd2, 0xa1, 0xed, 0xf4, 0xdd, 0x5e, 0xd7, 0x62, 0x96, 0xd0, 0x54, - 0x72, 0x5f, 0x8c, 0x88, 0x77, 0x2c, 0x1b, 0x05, 0xe6, 0x0e, 0xdd, 0xb8, 0x72, 0xcc, 0xbc, 0x61, - 0x47, 0x34, 0x8c, 0xe7, 0x49, 0x48, 0xb7, 0x08, 0xa5, 0xb6, 0xeb, 0xa0, 0x75, 0x28, 0xd8, 0x8e, - 0xc9, 0x3c, 0xcb, 0xa1, 0x56, 0x87, 0xd9, 0xae, 0x53, 0x56, 0xd6, 0x94, 0x6a, 0x06, 0xe7, 0x6d, - 0xa7, 0x1d, 0x09, 0x51, 0x03, 0x0a, 0xf4, 0xa9, 0xe5, 0x75, 0x4d, 0x2a, 0xfa, 0xd1, 0xb2, 0xba, - 0xa6, 0x55, 0x73, 0xf5, 0x95, 0x9a, 0xf4, 0x4e, 0x8e, 0x57, 0x6b, 0xf9, 0x56, 0xb2, 0x81, 0xf3, - 0x34, 0xd6, 0xa2, 0xe8, 0x0d, 0xc8, 0x52, 0xdb, 0xe9, 0xf5, 0x89, 0xd9, 0x3d, 0x2c, 0x6b, 0x7c, - 0x9a, 0x8c, 0x10, 0x3c, 0x38, 0x44, 0x77, 0x00, 0xac, 0x11, 0x73, 0x3b, 0xee, 0x60, 0x60, 0xb3, - 0x72, 0x82, 0x6b, 0x63, 0x12, 0xf4, 0x36, 0xe4, 0x99, 0xe5, 0xf5, 0x08, 0x33, 0x29, 0xf3, 0x6c, - 0xa7, 0x57, 0x4e, 0xae, 0x29, 0xd5, 0x2c, 0xd6, 0x85, 0xb0, 0xc5, 0x65, 0x68, 0x03, 0xd2, 0xee, - 0x90, 0x71, 0xff, 0x52, 0x6b, 0x4a, 0x35, 0x57, 0xbf, 0x59, 0x13, 0xa8, 0x6c, 0xff, 0x82, 0x74, - 0x46, 0x8c, 0xec, 0x09, 0x25, 0x0e, 0xac, 0xd0, 0x16, 0x94, 0x62, 0x6b, 0x37, 0x07, 0x6e, 0x97, - 0x94, 0xd3, 0x6b, 0x4a, 0xb5, 0x50, 0xbf, 0x1d, 0xac, 0x2c, 0x06, 0xc3, 0x8e, 0xdb, 0x25, 0xb8, - 0xc8, 0x26, 0x05, 0x68, 0x03, 0x32, 0xcf, 0x2c, 0xcf, 0xb1, 0x9d, 0x1e, 0x2d, 0x67, 0x38, 0x2a, - 0x4b, 0x72, 0xd6, 0x1f, 0xf9, 0xbf, 0x4f, 0x84, 0x0e, 0x87, 0x46, 0xe8, 0x63, 0xd0, 0x87, 0x1e, - 0x89, 0xa0, 0xcc, 0xce, 0x00, 0x65, 0x6e, 0xe8, 0x91, 0x10, 0xc8, 0x4d, 0xc8, 0x0f, 0x5d, 0xca, - 0xa2, 0x11, 0x60, 0x86, 0x11, 0x74, 0xbf, 0x4b, 0x38, 0xc4, 0x3b, 0x50, 0xe8, 0x5b, 0x94, 0x99, - 0xb6, 0x43, 0x89, 0xc7, 0x4c, 0xbb, 0x5b, 0xce, 0xad, 0x29, 0xd5, 0x04, 0xd6, 0x7d, 0x69, 0x93, - 0x0b, 0x9b, 0x5d, 0xf4, 0x26, 0xc0, 0xe7, 0xee, 0xc8, 0xe9, 0x9a, 0x9e, 0xfb, 0x8c, 0x96, 0x75, - 0x6e, 0x91, 0xe5, 0x12, 0xec, 0x3e, 0xa3, 0x95, 0x9f, 0x81, 0x1e, 0x9f, 0x02, 0xad, 0x43, 0x4a, - 0x84, 0x83, 0x93, 0x28, 0x57, 0xcf, 0x4b, 0x1c, 0xda, 0x5c, 0x88, 0xa5, 0xd2, 0xe7, 0x5c, 0x1c, - 0x74, 0xbb, 0x5b, 0x56, 0xd7, 0x94, 0xaa, 0x86, 0xf3, 0x31, 0x69, 0xb3, 0x6b, 0xfc, 0x4b, 0x85, - 0x82, 0x8c, 0x1b, 0x26, 0x5f, 0x8c, 0x08, 0x65, 0xe8, 0x3e, 0x64, 0x3b, 0x56, 0xbf, 0x4f, 0x3c, - 0xbf, 0x93, 0x98, 0xa3, 0x58, 0x13, 0xd4, 0x6e, 0x70, 0x79, 0xf3, 0x01, 0xce, 0x08, 0x8b, 0x66, - 0x17, 0xbd, 0x0b, 0x69, 0x89, 0x10, 0x9f, 0x40, 0xd8, 0xc6, 0x01, 0xc2, 0x81, 0x1e, 0xdd, 0x85, - 0x24, 0x77, 0x95, 0xd3, 0x32, 0x57, 0xbf, 0x21, 0x1d, 0xdf, 0xf2, 0x97, 0xca, 0xa3, 0x88, 0x85, - 0x1e, 0x7d, 0x07, 0x72, 0xcc, 0x3a, 0xec, 0x13, 0x66, 0xb2, 0xe3, 0x21, 0xe1, 0x3c, 0x2d, 0xd4, - 0x97, 0x6b, 0xe1, 0x76, 0x6b, 0x73, 0x65, 0xfb, 0x78, 0x48, 0x30, 0xb0, 0xf0, 0x19, 0xdd, 0x07, - 0xe4, 0xb8, 0x3e, 0xda, 0x13, 0x5b, 0x2d, 0xc9, 0x59, 0x5e, 0x72, 0x5c, 0xd6, 0x9c, 0xd8, 0x6d, - 0xeb, 0x50, 0x38, 0x22, 0xc7, 0x74, 0x68, 0x75, 0x88, 0xc9, 0xb7, 0x10, 0x67, 0x73, 0x16, 0xe7, - 0x03, 0x29, 0x47, 0x3d, 0xce, 0xf6, 0xf4, 0x2c, 0x6c, 0x37, 0xbe, 0x52, 0xa0, 0x18, 0x22, 0x4a, - 0x87, 0xae, 0x43, 0x09, 0x5a, 0x87, 0x24, 0xf1, 0x3c, 0xd7, 0x9b, 0x82, 0x13, 0xef, 0x37, 0xb6, - 0x7d, 0x31, 0x16, 0xda, 0xcb, 0x60, 0x79, 0x0f, 0x52, 0x1e, 0xa1, 0xa3, 0x3e, 0x93, 0x60, 0xa2, - 0xf8, 0x6e, 0xc0, 0x5c, 0x83, 0xa5, 0x85, 0xf1, 0x5f, 0x15, 0x96, 0xa5, 0x47, 0x7c, 0x4d, 0x74, - 0x71, 0x22, 0x5d, 0x81, 0x4c, 0x00, 0x37, 0x0f, 0x73, 0x16, 0x87, 0x6d, 0x74, 0x0b, 0x52, 0x3c, - 0x2e, 0xb4, 0x9c, 0x5c, 0xd3, 0xaa, 0x59, 0x2c, 0x5b, 0xd3, 0xec, 0x48, 0x5d, 0x89, 0x1d, 0xe9, - 0x33, 0xd8, 0x11, 0x0b, 0x7b, 0x66, 0xa6, 0xb0, 0xff, 0x46, 0x81, 0x9b, 0x53, 0x20, 0x2f, 0x44, - 0xf0, 0xff, 0xaf, 0xc2, 0xeb, 0xd2, 0xaf, 0x4f, 0x25, 0xb2, 0xcd, 0x57, 0x85, 0x01, 0x6f, 0x81, - 0x1e, 0x6e, 0x51, 0x5b, 0xf2, 0x40, 0xc7, 0xb9, 0xa3, 0x68, 0x1d, 0x0b, 0x4a, 0x86, 0xaf, 0x15, - 0xa8, 0x9c, 0x06, 0xfa, 0x42, 0x30, 0xe2, 0x4b, 0x0d, 0x6e, 0x47, 0xce, 0x61, 0xcb, 0xe9, 0x91, - 0x57, 0x84, 0x0f, 0xef, 0x03, 0x1c, 0x91, 0x63, 0xd3, 0xe3, 0x2e, 0x73, 0x36, 0xf8, 0x2b, 0x0d, - 0x63, 0x1d, 0xac, 0x06, 0x67, 0x8f, 0x82, 0x75, 0x2d, 0x28, 0x3f, 0x7e, 0xab, 0x40, 0xf9, 0x64, - 0x08, 0x16, 0x82, 0x1d, 0x7f, 0x49, 0x84, 0xec, 0xd8, 0x76, 0x98, 0xcd, 0x8e, 0x5f, 0x99, 0x6c, - 0x71, 0x1f, 0x10, 0xe1, 0x1e, 0x9b, 0x1d, 0xb7, 0x3f, 0x1a, 0x38, 0xa6, 0x63, 0x0d, 0x88, 0xac, - 0x60, 0x4b, 0x42, 0xd3, 0xe0, 0x8a, 0x5d, 0x6b, 0x40, 0xd0, 0x8f, 0x61, 0x49, 0x5a, 0x4f, 0xa4, - 0x98, 0x14, 0x27, 0x55, 0x35, 0xf0, 0xf4, 0x0c, 0x24, 0x6a, 0x81, 0x00, 0xdf, 0x10, 0x83, 0x7c, - 0x7a, 0x76, 0x4a, 0x4a, 0x5f, 0x89, 0x72, 0x99, 0x8b, 0x29, 0x97, 0x9d, 0x85, 0x72, 0x95, 0x43, - 0xc8, 0x04, 0x4e, 0xa3, 0x55, 0x48, 0x70, 0xd7, 0x14, 0xee, 0x5a, 0x2e, 0x28, 0x20, 0x7d, 0x8f, - 0xb8, 0x02, 0x2d, 0x43, 0x72, 0x6c, 0xf5, 0x47, 0x84, 0x07, 0x4e, 0xc7, 0xa2, 0x81, 0x56, 0x21, - 0x17, 0xc3, 0x8a, 0xc7, 0x4a, 0xc7, 0x10, 0x65, 0xe3, 0x38, 0xad, 0x63, 0x88, 0x2d, 0x04, 0xad, - 0xff, 0xad, 0xc2, 0x92, 0x74, 0x6d, 0xcb, 0x62, 0x9d, 0xa7, 0xd7, 0x4e, 0xe9, 0xf7, 0x20, 0xed, - 0x7b, 0x63, 0x13, 0x5a, 0xd6, 0x38, 0xa7, 0x4e, 0x21, 0x75, 0x60, 0x31, 0x6f, 0xc1, 0xbb, 0x0e, - 0x05, 0x8b, 0x9e, 0x52, 0xec, 0xe6, 0x2d, 0xfa, 0x32, 0x2a, 0xdd, 0xaf, 0x95, 0xb0, 0xae, 0x94, - 0x98, 0x5e, 0x5b, 0xa8, 0xbf, 0x05, 0x69, 0x11, 0xc8, 0x00, 0xcd, 0x5b, 0xd2, 0x37, 0x11, 0xe6, - 0x27, 0x36, 0x7b, 0x2a, 0x86, 0x0e, 0xcc, 0x0c, 0x07, 0x8a, 0x1c, 0x69, 0xbe, 0x36, 0x0e, 0x77, - 0x94, 0x65, 0x94, 0x4b, 0x64, 0x19, 0xf5, 0xcc, 0xaa, 0x54, 0x8b, 0x57, 0xa5, 0xc6, 0x9f, 0xa3, - 0x3a, 0x8b, 0x83, 0xf1, 0x92, 0x2a, 0xed, 0xf7, 0xa7, 0x69, 0x16, 0xbe, 0x52, 0x4f, 0xad, 0xfe, - 0x65, 0x91, 0xed, 0xb2, 0xb7, 0x03, 0xc6, 0xef, 0xa2, 0x5a, 0x69, 0x02, 0xb8, 0x6b, 0xe3, 0xd2, - 0xfd, 0x69, 0x2e, 0x9d, 0x96, 0x37, 0x42, 0x1e, 0xfd, 0x0a, 0x96, 0x39, 0x92, 0x51, 0x86, 0x7f, - 0x81, 0x64, 0x9a, 0x2e, 0x70, 0xb5, 0x13, 0x05, 0xae, 0xf1, 0x77, 0x15, 0xee, 0xc4, 0xe1, 0x79, - 0x99, 0x45, 0xfc, 0x87, 0xd3, 0xe4, 0x5a, 0x99, 0x20, 0xd7, 0x14, 0x24, 0x0b, 0xcb, 0xb0, 0x3f, - 0x28, 0xb0, 0x7a, 0x26, 0x84, 0x0b, 0x42, 0xb3, 0x3f, 0xaa, 0xb0, 0xdc, 0x62, 0x1e, 0xb1, 0x06, - 0x57, 0xba, 0x8d, 0x09, 0x59, 0xa9, 0x5e, 0xee, 0x8a, 0x45, 0x9b, 0x3d, 0x44, 0x53, 0x47, 0x49, - 0xe2, 0x82, 0xa3, 0x24, 0x39, 0xd3, 0x15, 0x61, 0x0c, 0xd7, 0xd4, 0xf9, 0xb8, 0x1a, 0x0d, 0xb8, - 0x39, 0x05, 0x94, 0x0c, 0x61, 0x54, 0x0e, 0x28, 0x17, 0x96, 0x03, 0x5f, 0xa9, 0x50, 0x99, 0x18, - 0xe5, 0x2a, 0xe9, 0x7a, 0x66, 0xd0, 0xe3, 0xa9, 0x40, 0x3b, 0xf3, 0x5c, 0x49, 0x9c, 0x77, 0xdb, - 0x91, 0x9c, 0x31, 0x50, 0x97, 0xde, 0x24, 0x4d, 0x78, 0xe3, 0x54, 0x40, 0xe6, 0x00, 0xf7, 0xf7, - 0x2a, 0xac, 0x4e, 0x8c, 0x75, 0xe5, 0x9c, 0xf5, 0x42, 0x10, 0x9e, 0x4e, 0xb6, 0x89, 0x0b, 0x6f, - 0x13, 0xae, 0x0d, 0xec, 0x5d, 0x58, 0x3b, 0x1b, 0xa0, 0x39, 0x10, 0xff, 0x93, 0x0a, 0x6f, 0x4e, - 0x0f, 0x78, 0x95, 0x17, 0xfb, 0x17, 0x82, 0xf7, 0xe4, 0xdb, 0x7a, 0x62, 0x8e, 0xb7, 0xf5, 0x6b, - 0xc3, 0xff, 0x11, 0xdc, 0x39, 0x0b, 0xae, 0x39, 0xd0, 0xff, 0x09, 0xe8, 0x5b, 0xa4, 0x67, 0x3b, - 0xf3, 0x61, 0x3d, 0xf1, 0xc1, 0x46, 0x9d, 0xfc, 0x60, 0x63, 0x7c, 0x1f, 0xf2, 0x72, 0x68, 0xe9, - 0x57, 0x2c, 0x51, 0x2a, 0x17, 0x24, 0xca, 0x2f, 0x15, 0xc8, 0x37, 0xf8, 0x77, 0x9d, 0x6b, 0x2f, - 0x14, 0x6e, 0x41, 0xca, 0x62, 0xee, 0xc0, 0xee, 0xc8, 0x2f, 0x4e, 0xb2, 0x65, 0x94, 0xa0, 0x10, - 0x78, 0x20, 0xfc, 0x37, 0x7e, 0x0e, 0x45, 0xec, 0xf6, 0xfb, 0x87, 0x56, 0xe7, 0xe8, 0xba, 0xbd, - 0x32, 0x10, 0x94, 0xa2, 0xb9, 0xe4, 0xfc, 0x9f, 0xc1, 0xeb, 0x98, 0x50, 0xb7, 0x3f, 0x26, 0xb1, - 0x92, 0x62, 0x3e, 0x4f, 0x10, 0x24, 0xba, 0x4c, 0x7e, 0x57, 0xc9, 0x62, 0xfe, 0x6c, 0xfc, 0x4d, - 0x81, 0xe5, 0x1d, 0x42, 0xa9, 0xd5, 0x23, 0x82, 0x60, 0xf3, 0x0d, 0x7d, 0x5e, 0xcd, 0xb8, 0x0c, - 0x49, 0x71, 0xf2, 0x8a, 0xfd, 0x26, 0x1a, 0x68, 0x03, 0xb2, 0xe1, 0x66, 0xe3, 0x67, 0xf2, 0xe9, - 0x7b, 0x2d, 0x13, 0xec, 0x35, 0xdf, 0xfb, 0xd8, 0xfd, 0x08, 0x7f, 0x36, 0x7e, 0xad, 0xc0, 0x0d, - 0xe9, 0xfd, 0xe6, 0xbc, 0xf1, 0x39, 0xcf, 0xf5, 0x60, 0x4e, 0x2d, 0x9a, 0x13, 0xdd, 0x01, 0x2d, - 0x48, 0xc6, 0xb9, 0xba, 0x2e, 0x77, 0xd9, 0x81, 0xd5, 0x1f, 0x11, 0xec, 0x2b, 0x8c, 0x1d, 0xd0, - 0x9b, 0xb1, 0x4a, 0x13, 0xad, 0x80, 0x1a, 0xba, 0x31, 0x69, 0xae, 0xda, 0xdd, 0xe9, 0x2b, 0x0a, - 0xf5, 0xc4, 0x15, 0xc5, 0x5f, 0x15, 0x58, 0x89, 0x96, 0x78, 0xe5, 0x83, 0xe9, 0xb2, 0xab, 0xfd, - 0x08, 0x8a, 0x76, 0xd7, 0x3c, 0x71, 0x0c, 0xe5, 0xea, 0xcb, 0x01, 0x8b, 0xe3, 0x8b, 0xc5, 0x79, - 0x3b, 0xd6, 0xa2, 0xc6, 0x0a, 0x54, 0x4e, 0x23, 0xaf, 0xa4, 0xf6, 0xff, 0x54, 0xb8, 0xd1, 0x1a, - 0xf6, 0x6d, 0x26, 0x73, 0xd4, 0x8b, 0x5e, 0xcf, 0xcc, 0x97, 0x74, 0x6f, 0x81, 0x4e, 0x7d, 0x3f, - 0xe4, 0x3d, 0x9c, 0x2c, 0x68, 0x72, 0x5c, 0x26, 0x6e, 0xe0, 0xfc, 0x38, 0x05, 0x26, 0x23, 0x87, - 0x71, 0x12, 0x6a, 0x18, 0xa4, 0xc5, 0xc8, 0x61, 0xe8, 0xdb, 0x70, 0xdb, 0x19, 0x0d, 0xf8, 0x27, - 0x51, 0x73, 0x48, 0x3c, 0x93, 0x8f, 0x6c, 0x0e, 0x2d, 0x8f, 0xf1, 0x14, 0xaf, 0xe1, 0x25, 0x67, - 0x34, 0xc0, 0xee, 0x33, 0xba, 0x4f, 0x3c, 0x3e, 0xf9, 0xbe, 0xe5, 0x31, 0xf4, 0x43, 0xc8, 0x5a, - 0xfd, 0x9e, 0xeb, 0xd9, 0xec, 0xe9, 0x40, 0x5e, 0xbc, 0x19, 0xd2, 0xcd, 0x13, 0xc8, 0xd4, 0x36, - 0x03, 0x4b, 0x1c, 0x75, 0x42, 0xef, 0x01, 0x1a, 0x51, 0x62, 0x0a, 0xe7, 0xc4, 0xa4, 0xe3, 0xba, - 0xbc, 0x85, 0x2b, 0x8e, 0x28, 0x89, 0x86, 0x39, 0xa8, 0x1b, 0xff, 0xd0, 0x00, 0xc5, 0xc7, 0x95, - 0x39, 0xfa, 0xbb, 0x90, 0xe2, 0xfd, 0x69, 0x59, 0xe1, 0xb1, 0x5d, 0x0d, 0x33, 0xd4, 0x09, 0xdb, - 0x9a, 0xef, 0x36, 0x96, 0xe6, 0x95, 0xcf, 0x40, 0x0f, 0x76, 0x2a, 0x5f, 0x4e, 0x3c, 0x1a, 0xca, - 0xb9, 0xa7, 0xab, 0x3a, 0xc3, 0xe9, 0x5a, 0xf9, 0x18, 0xb2, 0xbc, 0xaa, 0xbb, 0x70, 0xec, 0xa8, - 0x16, 0x55, 0xe3, 0xb5, 0x68, 0xe5, 0x3f, 0x0a, 0x24, 0x78, 0xe7, 0x99, 0x5f, 0x7e, 0x77, 0xf8, - 0xfb, 0x82, 0xf0, 0x52, 0x44, 0x4f, 0x24, 0xed, 0xbb, 0xe7, 0x40, 0x12, 0x87, 0x00, 0xeb, 0x47, - 0x71, 0x40, 0x1a, 0x00, 0xe2, 0x1f, 0x12, 0x7c, 0x28, 0xc1, 0xc3, 0x77, 0xce, 0x19, 0x2a, 0x5c, - 0x2e, 0xce, 0xd2, 0x70, 0xe5, 0x08, 0x12, 0xd4, 0xfe, 0xa5, 0xc8, 0x92, 0x1a, 0xe6, 0xcf, 0xc6, - 0x07, 0x70, 0xf3, 0x21, 0x61, 0x2d, 0x6f, 0x1c, 0x6c, 0xb7, 0x60, 0xfb, 0x9c, 0x03, 0x93, 0x81, - 0xe1, 0xd6, 0x74, 0x27, 0xc9, 0x80, 0xef, 0x81, 0x4e, 0xbd, 0xb1, 0x39, 0xd1, 0xd3, 0xaf, 0x4a, - 0xc2, 0xf0, 0xc4, 0x3b, 0xe5, 0x68, 0xd4, 0x30, 0xfe, 0xa9, 0x40, 0xe1, 0xe0, 0x2a, 0x47, 0xc7, - 0x54, 0x09, 0xa5, 0xce, 0x58, 0x42, 0xdd, 0x85, 0xe4, 0xb8, 0xc7, 0xe4, 0xad, 0xae, 0x1f, 0xd1, - 0xd8, 0x5f, 0x5f, 0x0e, 0x1e, 0x32, 0xbb, 0x8b, 0x85, 0xde, 0x2f, 0x8c, 0x3e, 0xb7, 0xfb, 0x8c, - 0x78, 0xe1, 0x29, 0x13, 0xb3, 0xfc, 0x84, 0x6b, 0xb0, 0xb4, 0x30, 0x7e, 0x00, 0xc5, 0x70, 0x2d, - 0x51, 0x5d, 0x45, 0xc6, 0xc4, 0x09, 0xf7, 0xc6, 0x44, 0xf7, 0x83, 0x6d, 0x5f, 0x85, 0xa5, 0x85, - 0xf1, 0x5c, 0x85, 0xa5, 0xc7, 0xc3, 0xae, 0xc5, 0x16, 0xfd, 0x2c, 0x9d, 0xb3, 0x6c, 0x5d, 0x81, - 0x2c, 0xb3, 0x07, 0x84, 0x32, 0x6b, 0x30, 0x94, 0x59, 0x2d, 0x12, 0xf8, 0x11, 0xe1, 0x38, 0xc8, - 0xcb, 0xd8, 0x60, 0x8f, 0x71, 0x88, 0xda, 0xee, 0x11, 0x71, 0xb0, 0xd0, 0x1b, 0x47, 0xb0, 0x3c, - 0x89, 0x92, 0x84, 0xba, 0x1a, 0x0c, 0x30, 0x59, 0xc1, 0xca, 0xc2, 0x97, 0x23, 0x2d, 0x0c, 0xd0, - 0xbb, 0x50, 0xf2, 0x4b, 0xd9, 0x01, 0x31, 0x23, 0x7f, 0xc4, 0xbf, 0x45, 0x8a, 0x42, 0xde, 0x0e, - 0xc4, 0xf7, 0x1e, 0x40, 0x71, 0xea, 0xbf, 0x3a, 0xa8, 0x08, 0xb9, 0xc7, 0xbb, 0xad, 0xfd, 0xed, - 0x46, 0xf3, 0x93, 0xe6, 0xf6, 0x83, 0xd2, 0x6b, 0x08, 0x20, 0xd5, 0x6a, 0xee, 0x3e, 0x7c, 0xb4, - 0x5d, 0x52, 0x50, 0x16, 0x92, 0x3b, 0x8f, 0x1f, 0xb5, 0x9b, 0x25, 0xd5, 0x7f, 0x6c, 0x3f, 0xd9, - 0xdb, 0x6f, 0x94, 0xb4, 0x7b, 0x1f, 0x41, 0x4e, 0xd4, 0x85, 0x7b, 0x5e, 0x97, 0x78, 0x7e, 0x87, - 0xdd, 0x3d, 0xbc, 0xb3, 0xf9, 0xa8, 0xf4, 0x1a, 0x4a, 0x83, 0xb6, 0x8f, 0xfd, 0x9e, 0x19, 0x48, - 0xec, 0xef, 0xb5, 0xda, 0x25, 0x15, 0x15, 0x00, 0x36, 0x1f, 0xb7, 0xf7, 0x1a, 0x7b, 0x3b, 0x3b, - 0xcd, 0x76, 0x49, 0xdb, 0xfa, 0x10, 0x8a, 0xb6, 0x5b, 0x1b, 0xdb, 0x8c, 0x50, 0x2a, 0xfe, 0x6d, - 0xf5, 0xd3, 0xb7, 0x65, 0xcb, 0x76, 0x37, 0xc4, 0xd3, 0x46, 0xcf, 0xdd, 0x18, 0xb3, 0x0d, 0xae, - 0xdd, 0x10, 0x09, 0xe2, 0x30, 0xc5, 0x5b, 0x1f, 0x7c, 0x13, 0x00, 0x00, 0xff, 0xff, 0x02, 0x83, - 0xbb, 0x0c, 0xed, 0x25, 0x00, 0x00, + // 1839 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x5b, 0x6f, 0x1b, 0xc7, + 0x15, 0xce, 0xee, 0xf2, 0x7a, 0x96, 0x37, 0x8f, 0x69, 0x87, 0x51, 0x14, 0x9b, 0xdd, 0x54, 0x08, + 0xe3, 0x1a, 0x52, 0xa3, 0xa0, 0x41, 0x51, 0xa4, 0x28, 0x2c, 0x5a, 0x31, 0x88, 0x58, 0x97, 0x8e, + 0x68, 0xbb, 0x2d, 0x5a, 0x2c, 0x56, 0xdc, 0x09, 0xbd, 0x15, 0xb9, 0xcb, 0xec, 0x0c, 0xe9, 0xea, + 0xa5, 0xc8, 0x3f, 0x08, 0xfa, 0x50, 0xa0, 0x08, 0x0a, 0x14, 0x05, 0x0c, 0xf4, 0xa9, 0xaf, 0x05, + 0xda, 0xbe, 0xf4, 0xb9, 0x2f, 0x45, 0xff, 0x42, 0x7f, 0x42, 0x7f, 0x41, 0xb0, 0x33, 0xb3, 0x57, + 0x4b, 0x16, 0x45, 0x5a, 0x06, 0xfd, 0x22, 0xec, 0x9c, 0x33, 0x97, 0x33, 0xdf, 0xf9, 0xe6, 0x9c, + 0xc3, 0x19, 0x41, 0x65, 0xc6, 0x86, 0x16, 0x23, 0x9b, 0x13, 0xdf, 0x63, 0x1e, 0x2a, 0x88, 0xd6, + 0x5a, 0xe3, 0xd8, 0x71, 0x47, 0xde, 0xd0, 0xb6, 0x98, 0x25, 0x34, 0x6b, 0xfa, 0x97, 0x53, 0xe2, + 0x9f, 0xca, 0x46, 0x8d, 0x79, 0x13, 0x2f, 0xa9, 0x9c, 0x31, 0x7f, 0x32, 0x10, 0x0d, 0xe3, 0x79, + 0x1e, 0x8a, 0x47, 0x84, 0x52, 0xc7, 0x73, 0xd1, 0x06, 0xd4, 0x1c, 0xd7, 0x64, 0xbe, 0xe5, 0x52, + 0x6b, 0xc0, 0x1c, 0xcf, 0x6d, 0x29, 0x6d, 0xa5, 0x53, 0xc2, 0x55, 0xc7, 0xed, 0xc7, 0x42, 0xd4, + 0x85, 0x1a, 0x7d, 0x6a, 0xf9, 0xb6, 0x49, 0xc5, 0x38, 0xda, 0x52, 0xdb, 0x5a, 0x47, 0xdf, 0x5e, + 0xdf, 0x94, 0xd6, 0xc9, 0xf9, 0x36, 0x8f, 0x82, 0x5e, 0xb2, 0x81, 0xab, 0x34, 0xd1, 0xa2, 0xe8, + 0x5d, 0x28, 0x53, 0xc7, 0x1d, 0x8e, 0x88, 0x69, 0x1f, 0xb7, 0x34, 0xbe, 0x4c, 0x49, 0x08, 0xee, + 0x1f, 0xa3, 0x5b, 0x00, 0xd6, 0x94, 0x79, 0x03, 0x6f, 0x3c, 0x76, 0x58, 0x2b, 0xc7, 0xb5, 0x09, + 0x09, 0x7a, 0x1f, 0xaa, 0xcc, 0xf2, 0x87, 0x84, 0x99, 0x94, 0xf9, 0x8e, 0x3b, 0x6c, 0xe5, 0xdb, + 0x4a, 0xa7, 0x8c, 0x2b, 0x42, 0x78, 0xc4, 0x65, 0x68, 0x0b, 0x8a, 0xde, 0x84, 0x71, 0xfb, 0x0a, + 0x6d, 0xa5, 0xa3, 0x6f, 0xdf, 0xd8, 0x14, 0xa8, 0xec, 0xfe, 0x86, 0x0c, 0xa6, 0x8c, 0x1c, 0x08, + 0x25, 0x0e, 0x7b, 0xa1, 0x1d, 0x68, 0x24, 0xf6, 0x6e, 0x8e, 0x3d, 0x9b, 0xb4, 0x8a, 0x6d, 0xa5, + 0x53, 0xdb, 0x7e, 0x3b, 0xdc, 0x59, 0x02, 0x86, 0x3d, 0xcf, 0x26, 0xb8, 0xce, 0xd2, 0x02, 0xb4, + 0x05, 0xa5, 0x67, 0x96, 0xef, 0x3a, 0xee, 0x90, 0xb6, 0x4a, 0x1c, 0x95, 0xeb, 0x72, 0xd5, 0x9f, + 0x06, 0x7f, 0x9f, 0x08, 0x1d, 0x8e, 0x3a, 0xa1, 0x9f, 0x40, 0x65, 0xe2, 0x93, 0x18, 0xca, 0xf2, + 0x1c, 0x50, 0xea, 0x13, 0x9f, 0x44, 0x40, 0xde, 0x83, 0xea, 0xc4, 0xa3, 0x2c, 0x9e, 0x01, 0xe6, + 0x98, 0xa1, 0x12, 0x0c, 0x89, 0xa6, 0xf8, 0x2e, 0xd4, 0x46, 0x16, 0x65, 0xa6, 0xe3, 0x52, 0xe2, + 0x33, 0xd3, 0xb1, 0x5b, 0x7a, 0x5b, 0xe9, 0xe4, 0x70, 0x25, 0x90, 0xf6, 0xb8, 0xb0, 0x67, 0xa3, + 0xf7, 0x00, 0xbe, 0xf0, 0xa6, 0xae, 0x6d, 0xfa, 0xde, 0x33, 0xda, 0xaa, 0xf0, 0x1e, 0x65, 0x2e, + 0xc1, 0xde, 0x33, 0xba, 0xf6, 0x4b, 0xa8, 0x24, 0x97, 0x40, 0x1b, 0x50, 0x10, 0xee, 0xe0, 0x24, + 0xd2, 0xb7, 0xab, 0x12, 0x87, 0x3e, 0x17, 0x62, 0xa9, 0x0c, 0x38, 0x97, 0x04, 0xdd, 0xb1, 0x5b, + 0x6a, 0x5b, 0xe9, 0x68, 0xb8, 0x9a, 0x90, 0xf6, 0x6c, 0xe3, 0x3f, 0x2a, 0xd4, 0xa4, 0xdf, 0x30, + 0xf9, 0x72, 0x4a, 0x28, 0x43, 0x77, 0xa1, 0x3c, 0xb0, 0x46, 0x23, 0xe2, 0x07, 0x83, 0xc4, 0x1a, + 0xf5, 0x4d, 0x41, 0xed, 0x2e, 0x97, 0xf7, 0xee, 0xe3, 0x92, 0xe8, 0xd1, 0xb3, 0xd1, 0x87, 0x50, + 0x94, 0x08, 0xf1, 0x05, 0x44, 0xdf, 0x24, 0x40, 0x38, 0xd4, 0xa3, 0x0f, 0x20, 0xcf, 0x4d, 0xe5, + 0xb4, 0xd4, 0xb7, 0xaf, 0x49, 0xc3, 0x77, 0x82, 0xad, 0x72, 0x2f, 0x62, 0xa1, 0x47, 0x3f, 0x00, + 0x9d, 0x59, 0xc7, 0x23, 0xc2, 0x4c, 0x76, 0x3a, 0x21, 0x9c, 0xa7, 0xb5, 0xed, 0xe6, 0x66, 0x74, + 0xdc, 0xfa, 0x5c, 0xd9, 0x3f, 0x9d, 0x10, 0x0c, 0x2c, 0xfa, 0x46, 0x77, 0x01, 0xb9, 0x5e, 0x80, + 0x76, 0xea, 0xa8, 0xe5, 0x39, 0xcb, 0x1b, 0xae, 0xc7, 0x7a, 0xa9, 0xd3, 0xb6, 0x01, 0xb5, 0x13, + 0x72, 0x4a, 0x27, 0xd6, 0x80, 0x98, 0xfc, 0x08, 0x71, 0x36, 0x97, 0x71, 0x35, 0x94, 0x72, 0xd4, + 0x93, 0x6c, 0x2f, 0xce, 0xc3, 0x76, 0xe3, 0x6b, 0x05, 0xea, 0x11, 0xa2, 0x74, 0xe2, 0xb9, 0x94, + 0xa0, 0x0d, 0xc8, 0x13, 0xdf, 0xf7, 0xfc, 0x0c, 0x9c, 0xf8, 0xb0, 0xbb, 0x1b, 0x88, 0xb1, 0xd0, + 0x5e, 0x06, 0xcb, 0x3b, 0x50, 0xf0, 0x09, 0x9d, 0x8e, 0x98, 0x04, 0x13, 0x25, 0x4f, 0x03, 0xe6, + 0x1a, 0x2c, 0x7b, 0x18, 0xff, 0x53, 0xa1, 0x29, 0x2d, 0xe2, 0x7b, 0xa2, 0xab, 0xe3, 0xe9, 0x35, + 0x28, 0x85, 0x70, 0x73, 0x37, 0x97, 0x71, 0xd4, 0x46, 0x37, 0xa1, 0xc0, 0xfd, 0x42, 0x5b, 0xf9, + 0xb6, 0xd6, 0x29, 0x63, 0xd9, 0xca, 0xb2, 0xa3, 0xb0, 0x14, 0x3b, 0x8a, 0xe7, 0xb0, 0x23, 0xe1, + 0xf6, 0xd2, 0x5c, 0x6e, 0xff, 0xbd, 0x02, 0x37, 0x32, 0x20, 0xaf, 0x84, 0xf3, 0xff, 0xaf, 0xc2, + 0x3b, 0xd2, 0xae, 0xcf, 0x25, 0xb2, 0xbd, 0x37, 0x85, 0x01, 0xdf, 0x81, 0x4a, 0x74, 0x44, 0x1d, + 0xc9, 0x83, 0x0a, 0xd6, 0x4f, 0xe2, 0x7d, 0xac, 0x28, 0x19, 0xbe, 0x51, 0x60, 0xed, 0x2c, 0xd0, + 0x57, 0x82, 0x11, 0x5f, 0x69, 0xf0, 0x76, 0x6c, 0x1c, 0xb6, 0xdc, 0x21, 0x79, 0x43, 0xf8, 0xf0, + 0x11, 0xc0, 0x09, 0x39, 0x35, 0x7d, 0x6e, 0x32, 0x67, 0x43, 0xb0, 0xd3, 0xc8, 0xd7, 0xe1, 0x6e, + 0x70, 0xf9, 0x24, 0xdc, 0xd7, 0x8a, 0xf2, 0xe3, 0x0f, 0x0a, 0xb4, 0x5e, 0x74, 0xc1, 0x4a, 0xb0, + 0xe3, 0xef, 0xb9, 0x88, 0x1d, 0xbb, 0x2e, 0x73, 0xd8, 0xe9, 0x1b, 0x13, 0x2d, 0xee, 0x02, 0x22, + 0xdc, 0x62, 0x73, 0xe0, 0x8d, 0xa6, 0x63, 0xd7, 0x74, 0xad, 0x31, 0x91, 0x15, 0x6c, 0x43, 0x68, + 0xba, 0x5c, 0xb1, 0x6f, 0x8d, 0x09, 0xfa, 0x19, 0x5c, 0x97, 0xbd, 0x53, 0x21, 0xa6, 0xc0, 0x49, + 0xd5, 0x09, 0x2d, 0x3d, 0x07, 0x89, 0xcd, 0x50, 0x80, 0xaf, 0x89, 0x49, 0x3e, 0x3f, 0x3f, 0x24, + 0x15, 0x97, 0xa2, 0x5c, 0xe9, 0x62, 0xca, 0x95, 0xe7, 0xa1, 0xdc, 0xda, 0x31, 0x94, 0x42, 0xa3, + 0xd1, 0x6d, 0xc8, 0x71, 0xd3, 0x14, 0x6e, 0x9a, 0x1e, 0x16, 0x90, 0x81, 0x45, 0x5c, 0x81, 0x9a, + 0x90, 0x9f, 0x59, 0xa3, 0x29, 0xe1, 0x8e, 0xab, 0x60, 0xd1, 0x40, 0xb7, 0x41, 0x4f, 0x60, 0xc5, + 0x7d, 0x55, 0xc1, 0x10, 0x47, 0xe3, 0x24, 0xad, 0x13, 0x88, 0xad, 0x04, 0xad, 0xff, 0xab, 0xc2, + 0x75, 0x69, 0xda, 0x8e, 0xc5, 0x06, 0x4f, 0xaf, 0x9c, 0xd2, 0xdf, 0x83, 0x62, 0x60, 0x8d, 0x43, + 0x68, 0x4b, 0xe3, 0x9c, 0x3a, 0x83, 0xd4, 0x61, 0x8f, 0x45, 0x0b, 0xde, 0x0d, 0xa8, 0x59, 0xf4, + 0x8c, 0x62, 0xb7, 0x6a, 0xd1, 0xd7, 0x51, 0xe9, 0x7e, 0xa3, 0x44, 0x75, 0xa5, 0xc4, 0xf4, 0xca, + 0x5c, 0xfd, 0x7d, 0x28, 0x0a, 0x47, 0x86, 0x68, 0xde, 0x94, 0xb6, 0x09, 0x37, 0x3f, 0x71, 0xd8, + 0x53, 0x31, 0x75, 0xd8, 0xcd, 0x70, 0xa1, 0xce, 0x91, 0xe6, 0x7b, 0xe3, 0x70, 0xc7, 0x51, 0x46, + 0xb9, 0x44, 0x94, 0x51, 0xcf, 0xad, 0x4a, 0xb5, 0x64, 0x55, 0x6a, 0xfc, 0x2d, 0xae, 0xb3, 0x38, + 0x18, 0xaf, 0xa9, 0xd2, 0xfe, 0x28, 0x4b, 0xb3, 0xe8, 0x27, 0x75, 0x66, 0xf7, 0xaf, 0x8b, 0x6c, + 0x97, 0xbd, 0x1d, 0x30, 0xfe, 0x18, 0xd7, 0x4a, 0x29, 0xe0, 0xae, 0x8c, 0x4b, 0x77, 0xb3, 0x5c, + 0x3a, 0x2b, 0x6e, 0x44, 0x3c, 0xfa, 0x2d, 0x34, 0x39, 0x92, 0x71, 0x84, 0x7f, 0x85, 0x64, 0xca, + 0x16, 0xb8, 0xda, 0x0b, 0x05, 0xae, 0xf1, 0x2f, 0x15, 0x6e, 0x25, 0xe1, 0x79, 0x9d, 0x45, 0xfc, + 0x27, 0x59, 0x72, 0xad, 0xa7, 0xc8, 0x95, 0x81, 0x64, 0x65, 0x19, 0xf6, 0x67, 0x05, 0x6e, 0x9f, + 0x0b, 0xe1, 0x8a, 0xd0, 0xec, 0x2f, 0x2a, 0x34, 0x8f, 0x98, 0x4f, 0xac, 0xf1, 0x52, 0xb7, 0x31, + 0x11, 0x2b, 0xd5, 0xcb, 0x5d, 0xb1, 0x68, 0xf3, 0xbb, 0x28, 0x93, 0x4a, 0x72, 0x17, 0xa4, 0x92, + 0xfc, 0x5c, 0x57, 0x84, 0x09, 0x5c, 0x0b, 0x2f, 0xc7, 0xd5, 0xe8, 0xc2, 0x8d, 0x0c, 0x50, 0xd2, + 0x85, 0x71, 0x39, 0xa0, 0x5c, 0x58, 0x0e, 0x7c, 0xad, 0xc2, 0x5a, 0x6a, 0x96, 0x65, 0xc2, 0xf5, + 0xdc, 0xa0, 0x27, 0x43, 0x81, 0x76, 0x6e, 0x5e, 0xc9, 0xbd, 0xec, 0xb6, 0x23, 0x3f, 0xa7, 0xa3, + 0x2e, 0x7d, 0x48, 0x7a, 0xf0, 0xee, 0x99, 0x80, 0x2c, 0x00, 0xee, 0x9f, 0x54, 0xb8, 0x9d, 0x9a, + 0x6b, 0xe9, 0x98, 0xf5, 0x4a, 0x10, 0xce, 0x06, 0xdb, 0xdc, 0x85, 0xb7, 0x09, 0x57, 0x06, 0xf6, + 0x3e, 0xb4, 0xcf, 0x07, 0x68, 0x01, 0xc4, 0xff, 0xaa, 0xc2, 0x7b, 0xd9, 0x09, 0x97, 0xf9, 0x61, + 0xff, 0x4a, 0xf0, 0x4e, 0xff, 0x5a, 0xcf, 0x2d, 0xf0, 0x6b, 0xfd, 0xca, 0xf0, 0x7f, 0x08, 0xb7, + 0xce, 0x83, 0x6b, 0x01, 0xf4, 0x7f, 0x0e, 0x95, 0x1d, 0x32, 0x74, 0xdc, 0xc5, 0xb0, 0x4e, 0x3d, + 0xd8, 0xa8, 0xe9, 0x07, 0x1b, 0xe3, 0x47, 0x50, 0x95, 0x53, 0x4b, 0xbb, 0x12, 0x81, 0x52, 0xb9, + 0x20, 0x50, 0x7e, 0xa5, 0x40, 0xb5, 0xcb, 0xdf, 0x75, 0xae, 0xbc, 0x50, 0xb8, 0x09, 0x05, 0x8b, + 0x79, 0x63, 0x67, 0x20, 0x5f, 0x9c, 0x64, 0xcb, 0x68, 0x40, 0x2d, 0xb4, 0x40, 0xd8, 0x6f, 0xfc, + 0x1a, 0xea, 0xd8, 0x1b, 0x8d, 0x8e, 0xad, 0xc1, 0xc9, 0x55, 0x5b, 0x65, 0x20, 0x68, 0xc4, 0x6b, + 0xc9, 0xf5, 0x7f, 0x05, 0xef, 0x60, 0x42, 0xbd, 0xd1, 0x8c, 0x24, 0x4a, 0x8a, 0xc5, 0x2c, 0x41, + 0x90, 0xb3, 0x99, 0x7c, 0x57, 0x29, 0x63, 0xfe, 0x6d, 0xfc, 0x53, 0x81, 0xe6, 0x1e, 0xa1, 0xd4, + 0x1a, 0x12, 0x41, 0xb0, 0xc5, 0xa6, 0x7e, 0x59, 0xcd, 0xd8, 0x84, 0xbc, 0xc8, 0xbc, 0xe2, 0xbc, + 0x89, 0x06, 0xda, 0x82, 0x72, 0x74, 0xd8, 0x78, 0x4e, 0x3e, 0xfb, 0xac, 0x95, 0xc2, 0xb3, 0x16, + 0x58, 0x9f, 0xb8, 0x1f, 0xe1, 0xdf, 0xc6, 0xef, 0x14, 0xb8, 0x26, 0xad, 0xbf, 0xb7, 0xa8, 0x7f, + 0x5e, 0x66, 0x7a, 0xb8, 0xa6, 0x16, 0xaf, 0x89, 0x6e, 0x81, 0x16, 0x06, 0x63, 0x7d, 0xbb, 0x22, + 0x4f, 0xd9, 0x63, 0x6b, 0x34, 0x25, 0x38, 0x50, 0x18, 0x7b, 0x50, 0xe9, 0x25, 0x2a, 0x4d, 0xb4, + 0x0e, 0x6a, 0x64, 0x46, 0xba, 0xbb, 0xea, 0xd8, 0xd9, 0x2b, 0x0a, 0xf5, 0x85, 0x2b, 0x8a, 0x7f, + 0x28, 0xb0, 0x1e, 0x6f, 0x71, 0xe9, 0xc4, 0x74, 0xd9, 0xdd, 0x7e, 0x0a, 0x75, 0xc7, 0x36, 0x5f, + 0x48, 0x43, 0xfa, 0x76, 0x33, 0x64, 0x71, 0x72, 0xb3, 0xb8, 0xea, 0x24, 0x5a, 0xd4, 0x58, 0x87, + 0xb5, 0xb3, 0xc8, 0x2b, 0xa9, 0xfd, 0x31, 0xdc, 0x78, 0x40, 0xd8, 0x91, 0x3f, 0x0b, 0x87, 0x84, + 0x5b, 0x4a, 0x1a, 0xa9, 0xa4, 0x8d, 0x34, 0x30, 0xdc, 0xcc, 0x0e, 0x92, 0x91, 0xe6, 0x87, 0x50, + 0xa1, 0xfe, 0xcc, 0x4c, 0x8d, 0x0c, 0x22, 0x6b, 0x44, 0xaa, 0xe4, 0x20, 0x9d, 0xc6, 0x0d, 0xe3, + 0xdf, 0x0a, 0xd4, 0x1e, 0x2f, 0x43, 0xff, 0x4c, 0x1a, 0x50, 0xe7, 0x4c, 0x03, 0x1f, 0x40, 0x7e, + 0x36, 0x64, 0xf2, 0x66, 0x2a, 0xc8, 0x5a, 0x89, 0xe7, 0xfb, 0xc7, 0x0f, 0x98, 0x63, 0x63, 0xa1, + 0x0f, 0x82, 0xfb, 0x17, 0xce, 0x88, 0x11, 0x3f, 0x3a, 0x29, 0x89, 0x9e, 0x9f, 0x71, 0x0d, 0x96, + 0x3d, 0x8c, 0x1f, 0x43, 0x3d, 0xda, 0x4b, 0x9c, 0x1b, 0xc8, 0x8c, 0xb8, 0x8c, 0xb6, 0x14, 0x99, + 0xd4, 0x92, 0x0b, 0xed, 0x06, 0x2a, 0x2c, 0x7b, 0x18, 0xcf, 0x55, 0xb8, 0xfe, 0x68, 0x62, 0x5b, + 0x6c, 0xd5, 0xe3, 0xc1, 0x82, 0xa9, 0x77, 0x1d, 0xca, 0xcc, 0x19, 0x13, 0xca, 0xac, 0xf1, 0x84, + 0x27, 0x5f, 0x0d, 0xc7, 0x82, 0xc0, 0x23, 0x1c, 0x07, 0x79, 0xa1, 0x14, 0xd6, 0x11, 0x1c, 0xa2, + 0xbe, 0x77, 0x42, 0x5c, 0x2c, 0xf4, 0xc6, 0x09, 0x34, 0xd3, 0x28, 0x49, 0xa8, 0x3b, 0xe1, 0x04, + 0xe9, 0x2c, 0x2c, 0x93, 0x37, 0x47, 0x5a, 0x74, 0x40, 0x1f, 0x42, 0x23, 0x48, 0xc7, 0x63, 0x62, + 0xc6, 0xf6, 0x88, 0x17, 0xef, 0xba, 0x90, 0xf7, 0x43, 0xf1, 0x9d, 0xfb, 0x50, 0xcf, 0xfc, 0xbf, + 0x01, 0xaa, 0x83, 0xfe, 0x68, 0xff, 0xe8, 0x70, 0xb7, 0xdb, 0xfb, 0xac, 0xb7, 0x7b, 0xbf, 0xf1, + 0x16, 0x02, 0x28, 0x1c, 0xf5, 0xf6, 0x1f, 0x3c, 0xdc, 0x6d, 0x28, 0xa8, 0x0c, 0xf9, 0xbd, 0x47, + 0x0f, 0xfb, 0xbd, 0x86, 0x1a, 0x7c, 0xf6, 0x9f, 0x1c, 0x1c, 0x76, 0x1b, 0xda, 0x9d, 0x4f, 0x41, + 0x17, 0xb9, 0xed, 0xc0, 0xb7, 0x89, 0x1f, 0x0c, 0xd8, 0x3f, 0xc0, 0x7b, 0xf7, 0x1e, 0x36, 0xde, + 0x42, 0x45, 0xd0, 0x0e, 0x71, 0x30, 0xb2, 0x04, 0xb9, 0xc3, 0x83, 0xa3, 0x7e, 0x43, 0x45, 0x35, + 0x80, 0x7b, 0x8f, 0xfa, 0x07, 0xdd, 0x83, 0xbd, 0xbd, 0x5e, 0xbf, 0xa1, 0xed, 0x7c, 0x02, 0x75, + 0xc7, 0xdb, 0x9c, 0x39, 0x8c, 0x50, 0x2a, 0xfe, 0x63, 0xe4, 0x17, 0xef, 0xcb, 0x96, 0xe3, 0x6d, + 0x89, 0xaf, 0xad, 0xa1, 0xb7, 0x35, 0x63, 0x5b, 0x5c, 0xbb, 0x25, 0xc2, 0xc3, 0x71, 0x81, 0xb7, + 0x3e, 0xfe, 0x36, 0x00, 0x00, 0xff, 0xff, 0x0c, 0xb7, 0x26, 0xa1, 0xb1, 0x22, 0x00, 0x00, } diff --git a/go/vt/proto/vtgateservice/vtgateservice.pb.go b/go/vt/proto/vtgateservice/vtgateservice.pb.go index 2f82480284e..0dd813a77a9 100644 --- a/go/vt/proto/vtgateservice/vtgateservice.pb.go +++ b/go/vt/proto/vtgateservice/vtgateservice.pb.go @@ -30,45 +30,43 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package func init() { proto.RegisterFile("vtgateservice.proto", fileDescriptor_601ae27c95081e0f) } var fileDescriptor_601ae27c95081e0f = []byte{ - // 594 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x95, 0x5f, 0x6f, 0xd3, 0x30, - 0x14, 0xc5, 0xe1, 0x81, 0x0e, 0x5d, 0x5a, 0x40, 0xde, 0xd6, 0x6e, 0xe5, 0xef, 0x0a, 0x6c, 0x88, - 0x87, 0x16, 0x81, 0x84, 0x84, 0x84, 0x40, 0x2d, 0x54, 0x08, 0x4d, 0x03, 0xd6, 0x42, 0x91, 0x26, - 0xf1, 0xe0, 0xa6, 0x57, 0x59, 0xd4, 0x36, 0x49, 0x63, 0x37, 0xa2, 0x5f, 0x80, 0xcf, 0x3d, 0x2d, - 0xb1, 0x1d, 0xdb, 0x71, 0xdb, 0xb7, 0xe4, 0x9c, 0xe3, 0x9f, 0xed, 0xeb, 0x2b, 0x1b, 0x76, 0x53, - 0xee, 0x53, 0x8e, 0x0c, 0x93, 0x34, 0xf0, 0xb0, 0x1d, 0x27, 0x11, 0x8f, 0x48, 0xcd, 0x10, 0x9b, - 0xd5, 0xfc, 0x37, 0x37, 0x9b, 0x77, 0x16, 0x4b, 0x4c, 0x56, 0xf9, 0xcf, 0x9b, 0xff, 0xf7, 0xa0, - 0x32, 0x0a, 0x38, 0x32, 0x46, 0x3e, 0xc0, 0x4e, 0xff, 0x1f, 0x7a, 0x4b, 0x8e, 0xa4, 0xde, 0x16, - 0x23, 0x84, 0x30, 0xc0, 0xc5, 0x12, 0x19, 0x6f, 0x36, 0x4a, 0x3a, 0x8b, 0xa3, 0x90, 0x61, 0xeb, - 0x06, 0x39, 0x85, 0xaa, 0x10, 0x7b, 0x94, 0x7b, 0x97, 0xe4, 0x81, 0x15, 0xcd, 0x54, 0xc9, 0x79, - 0xe8, 0x36, 0x15, 0xec, 0x27, 0xd4, 0x86, 0x3c, 0x41, 0x3a, 0x97, 0x0b, 0x52, 0x03, 0x0c, 0x59, - 0xe2, 0x1e, 0xad, 0x71, 0x25, 0xef, 0xf5, 0x4d, 0xf2, 0x1d, 0x6a, 0x42, 0x1e, 0x5e, 0xd2, 0x64, - 0xc2, 0x88, 0xbd, 0x84, 0x5c, 0x2e, 0x11, 0x2d, 0x57, 0xad, 0xf0, 0x2f, 0x10, 0x61, 0x9d, 0xe2, - 0x8a, 0xc5, 0xd4, 0xc3, 0x6f, 0x13, 0x46, 0x8e, 0xac, 0x61, 0x9a, 0x27, 0xc9, 0xad, 0x4d, 0x11, - 0x85, 0xff, 0x03, 0xf7, 0x0b, 0x7f, 0x40, 0x43, 0x1f, 0x19, 0x79, 0x52, 0x1e, 0x99, 0x3b, 0x12, - 0xfd, 0x74, 0x7d, 0xc0, 0x01, 0xee, 0x87, 0x3c, 0xe0, 0xab, 0xeb, 0x55, 0xdb, 0x60, 0xe5, 0xac, - 0x03, 0x6b, 0x01, 0x47, 0x41, 0xb2, 0xc3, 0x14, 0x55, 0x3e, 0x72, 0x1d, 0xb4, 0x59, 0xea, 0xd6, - 0xa6, 0x88, 0xc2, 0xcf, 0xa0, 0xa1, 0xfb, 0x7a, 0xd1, 0x8f, 0x5d, 0x00, 0x47, 0xe5, 0x4f, 0xb6, - 0xe6, 0xd4, 0x6c, 0x63, 0xd8, 0x35, 0x5a, 0x49, 0xec, 0xa6, 0xe5, 0xec, 0x33, 0x73, 0x3b, 0xcf, - 0x36, 0x66, 0xb4, 0x8e, 0x5c, 0xc0, 0x81, 0x11, 0xd1, 0xb7, 0x74, 0xe2, 0x84, 0x38, 0xf6, 0xf4, - 0x72, 0x7b, 0x50, 0x9b, 0x72, 0x0a, 0x75, 0x3b, 0x27, 0x7a, 0xeb, 0xc5, 0x3a, 0x8e, 0xd9, 0x61, - 0xc7, 0xdb, 0x62, 0xda, 0x64, 0xef, 0xe0, 0x56, 0x0f, 0xfd, 0x20, 0x24, 0x7b, 0x72, 0x50, 0xf6, - 0x2b, 0x51, 0xfb, 0x96, 0xaa, 0x6a, 0xff, 0x1e, 0x2a, 0x9f, 0xa3, 0xf9, 0x3c, 0xe0, 0x44, 0x45, - 0xf2, 0x7f, 0x39, 0xb2, 0x6e, 0xcb, 0x6a, 0xe8, 0x27, 0xb8, 0x3d, 0x88, 0x66, 0xb3, 0x31, 0xf5, - 0xa6, 0x44, 0x5d, 0x55, 0x52, 0x91, 0xc3, 0x0f, 0xca, 0x86, 0xde, 0xc4, 0x03, 0x64, 0xd1, 0x2c, - 0xc5, 0x5f, 0x09, 0x0d, 0x19, 0xf5, 0x78, 0x10, 0x85, 0x45, 0x13, 0x97, 0xbd, 0x52, 0x13, 0xbb, - 0x22, 0x0a, 0xff, 0x03, 0x6a, 0x67, 0xc8, 0x18, 0xf5, 0x31, 0xaf, 0x5f, 0x71, 0x09, 0x19, 0x72, - 0x71, 0x4b, 0xe6, 0x37, 0xb5, 0x65, 0x6a, 0x35, 0xfe, 0x02, 0x20, 0xcc, 0xae, 0x37, 0x25, 0x87, - 0x16, 0xad, 0x5b, 0x6c, 0xfa, 0xd0, 0x44, 0x75, 0x8d, 0x5d, 0x5f, 0xc0, 0x7e, 0xa1, 0xeb, 0x6d, - 0xf8, 0xbc, 0x0c, 0x74, 0xf4, 0xe0, 0x46, 0x76, 0x1f, 0x60, 0x18, 0xcf, 0x02, 0x7e, 0x7e, 0x1d, - 0x29, 0x56, 0x58, 0x68, 0x92, 0xd2, 0x74, 0x59, 0x0a, 0x73, 0x0e, 0x77, 0xbf, 0x22, 0x1f, 0x26, - 0xa9, 0x9c, 0x9f, 0xa8, 0x1b, 0xda, 0xd4, 0x25, 0xee, 0xf1, 0x3a, 0x5b, 0x21, 0x3f, 0xc2, 0xce, - 0x48, 0x1c, 0x83, 0xea, 0xa8, 0x91, 0x79, 0x00, 0x8d, 0x92, 0xae, 0xd5, 0xfe, 0x0c, 0xaa, 0xbf, - 0xe3, 0x09, 0xe5, 0xf2, 0x2c, 0xd5, 0x83, 0xa7, 0xab, 0xa5, 0x07, 0xcf, 0x34, 0x0b, 0x5c, 0xaf, - 0x07, 0x7b, 0x41, 0xd4, 0x4e, 0xb3, 0xa7, 0x38, 0x7f, 0x9b, 0xdb, 0x7e, 0x12, 0x7b, 0x17, 0xaf, - 0x84, 0x14, 0x44, 0x9d, 0xfc, 0xab, 0xe3, 0x47, 0x9d, 0x94, 0x77, 0xb2, 0x48, 0xc7, 0x78, 0xe7, - 0xc7, 0x95, 0x4c, 0x7c, 0x7b, 0x15, 0x00, 0x00, 0xff, 0xff, 0x98, 0xf7, 0xf0, 0x85, 0x14, 0x08, - 0x00, 0x00, + // 576 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x95, 0xdb, 0x6f, 0x12, 0x41, + 0x14, 0xc6, 0xf5, 0x41, 0x6a, 0x8e, 0xd0, 0x98, 0x69, 0x0b, 0x2d, 0x5e, 0x8b, 0xda, 0x1a, 0x1f, + 0xc0, 0x68, 0x62, 0x62, 0x62, 0x34, 0xa0, 0x8d, 0x31, 0x4d, 0xbd, 0x80, 0x62, 0xd2, 0xc4, 0x87, + 0x61, 0x39, 0xd9, 0x6e, 0x80, 0x5d, 0xd8, 0x19, 0x36, 0xf2, 0xd7, 0xf9, 0xaf, 0x99, 0xee, 0x5c, + 0x76, 0x66, 0x76, 0x80, 0x37, 0xe6, 0xfb, 0xbe, 0xf3, 0x63, 0xe6, 0xec, 0xc9, 0x0c, 0xec, 0x65, + 0x3c, 0xa4, 0x1c, 0x19, 0xa6, 0x59, 0x14, 0x60, 0x7b, 0x9e, 0x26, 0x3c, 0x21, 0x35, 0x4b, 0x6c, + 0x56, 0xc5, 0x52, 0x98, 0xcd, 0x3b, 0x8b, 0x25, 0xa6, 0x2b, 0xb1, 0x78, 0xf5, 0x6f, 0x17, 0x2a, + 0xc3, 0x88, 0x23, 0x63, 0xe4, 0x1d, 0xec, 0x9c, 0xfd, 0xc5, 0x60, 0xc9, 0x91, 0xd4, 0xdb, 0xb2, + 0x42, 0x0a, 0x7d, 0x5c, 0x2c, 0x91, 0xf1, 0x66, 0xa3, 0xa4, 0xb3, 0x79, 0x12, 0x33, 0x6c, 0xdd, + 0x20, 0xe7, 0x50, 0x95, 0x62, 0x8f, 0xf2, 0xe0, 0x8a, 0xdc, 0x73, 0xa2, 0xb9, 0xaa, 0x38, 0xf7, + 0xfd, 0xa6, 0x86, 0x7d, 0x87, 0xda, 0x80, 0xa7, 0x48, 0x67, 0x6a, 0x43, 0xba, 0xc0, 0x92, 0x15, + 0xee, 0xc1, 0x1a, 0x57, 0xf1, 0x5e, 0xde, 0x24, 0x5f, 0xa1, 0x26, 0xe5, 0xc1, 0x15, 0x4d, 0xc7, + 0x8c, 0xb8, 0x5b, 0x10, 0x72, 0x89, 0xe8, 0xb8, 0x7a, 0x87, 0x7f, 0x80, 0x48, 0xeb, 0x1c, 0x57, + 0x6c, 0x4e, 0x03, 0xfc, 0x32, 0x66, 0xe4, 0xd8, 0x29, 0x33, 0x3c, 0x45, 0x6e, 0x6d, 0x8a, 0x68, + 0xfc, 0x6f, 0xb8, 0x5b, 0xf8, 0x7d, 0x1a, 0x87, 0xc8, 0xc8, 0xa3, 0x72, 0xa5, 0x70, 0x14, 0xfa, + 0xf1, 0xfa, 0x80, 0x07, 0x7c, 0x16, 0xf3, 0x88, 0xaf, 0xae, 0x77, 0xed, 0x82, 0xb5, 0xb3, 0x0e, + 0x6c, 0x04, 0x3c, 0x0d, 0xc9, 0x3f, 0xa6, 0xec, 0xf2, 0xb1, 0xef, 0x43, 0xdb, 0xad, 0x6e, 0x6d, + 0x8a, 0x68, 0xfc, 0x14, 0x1a, 0xa6, 0x6f, 0x36, 0xfd, 0xc4, 0x07, 0xf0, 0x74, 0xfe, 0x74, 0x6b, + 0x4e, 0xff, 0xdb, 0x08, 0xf6, 0xac, 0x51, 0x92, 0xa7, 0x69, 0x79, 0xe7, 0xcc, 0x3e, 0xce, 0x93, + 0x8d, 0x19, 0x63, 0x22, 0x17, 0x70, 0x68, 0x45, 0xcc, 0x23, 0x9d, 0x7a, 0x21, 0x9e, 0x33, 0x3d, + 0xdf, 0x1e, 0x34, 0xfe, 0x72, 0x02, 0x75, 0x37, 0x27, 0x67, 0xeb, 0xd9, 0x3a, 0x8e, 0x3d, 0x61, + 0x27, 0xdb, 0x62, 0xc6, 0x9f, 0xbd, 0x81, 0x5b, 0x3d, 0x0c, 0xa3, 0x98, 0xec, 0xab, 0xa2, 0x7c, + 0xa9, 0x50, 0x07, 0x8e, 0xaa, 0x7b, 0xff, 0x16, 0x2a, 0x1f, 0x93, 0xd9, 0x2c, 0xe2, 0x44, 0x47, + 0xc4, 0x5a, 0x55, 0xd6, 0x5d, 0x59, 0x97, 0x7e, 0x80, 0xdb, 0xfd, 0x64, 0x3a, 0x1d, 0xd1, 0x60, + 0x42, 0xf4, 0x55, 0xa5, 0x14, 0x55, 0x7e, 0x58, 0x36, 0xcc, 0x21, 0xee, 0x23, 0x4b, 0xa6, 0x19, + 0xfe, 0x4c, 0x69, 0xcc, 0x68, 0xc0, 0xa3, 0x24, 0x2e, 0x86, 0xb8, 0xec, 0x95, 0x86, 0xd8, 0x17, + 0xd1, 0xf8, 0x6f, 0x50, 0xbb, 0x40, 0xc6, 0x68, 0x88, 0xa2, 0x7f, 0xc5, 0x25, 0x64, 0xc9, 0xc5, + 0x2d, 0x29, 0x6e, 0x6a, 0xc7, 0x34, 0x7a, 0xfc, 0x09, 0x40, 0x9a, 0xdd, 0x60, 0x42, 0x8e, 0x1c, + 0x5a, 0xb7, 0x38, 0xf4, 0x91, 0x8d, 0xea, 0x5a, 0xa7, 0xbe, 0x84, 0x83, 0x42, 0x37, 0xc7, 0xf0, + 0x69, 0x19, 0xe8, 0x99, 0xc1, 0x8d, 0xec, 0x1f, 0xb0, 0xfb, 0x19, 0xf9, 0x20, 0xcd, 0x54, 0x21, + 0xd1, 0x57, 0xab, 0xad, 0x2b, 0xda, 0xc3, 0x75, 0xb6, 0x46, 0xbe, 0x87, 0x9d, 0xa1, 0xec, 0x9f, + 0x1e, 0x85, 0xa1, 0xdd, 0xb9, 0x46, 0x49, 0x37, 0x9a, 0x76, 0x01, 0xd5, 0x5f, 0xf3, 0x31, 0xe5, + 0xea, 0x23, 0xe8, 0x97, 0xca, 0x54, 0x4b, 0x2f, 0x95, 0x6d, 0x16, 0xb8, 0x5e, 0x0f, 0xf6, 0xa3, + 0xa4, 0x9d, 0xe5, 0x6f, 0xa8, 0x78, 0x54, 0xdb, 0x61, 0x3a, 0x0f, 0x2e, 0x5f, 0x48, 0x29, 0x4a, + 0x3a, 0xe2, 0x57, 0x27, 0x4c, 0x3a, 0x19, 0xef, 0xe4, 0x91, 0x8e, 0xf5, 0x40, 0x8f, 0x2a, 0xb9, + 0xf8, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfb, 0xe3, 0xd1, 0x31, 0xcd, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -148,9 +146,6 @@ type VitessClient interface { // MessageAckKeyspaceIds routes Message Acks using the associated // keyspace ids. MessageAckKeyspaceIds(ctx context.Context, in *vtgate.MessageAckKeyspaceIdsRequest, opts ...grpc.CallOption) (*query.MessageAckResponse, error) - // Split a query into non-overlapping sub queries - // API group: Map Reduce - SplitQuery(ctx context.Context, in *vtgate.SplitQueryRequest, opts ...grpc.CallOption) (*vtgate.SplitQueryResponse, error) // GetSrvKeyspace returns a SrvKeyspace object (as seen by this vtgate). // This method is provided as a convenient way for clients to take a // look at the sharding configuration for a Keyspace. Looking at the @@ -461,15 +456,6 @@ func (c *vitessClient) MessageAckKeyspaceIds(ctx context.Context, in *vtgate.Mes return out, nil } -func (c *vitessClient) SplitQuery(ctx context.Context, in *vtgate.SplitQueryRequest, opts ...grpc.CallOption) (*vtgate.SplitQueryResponse, error) { - out := new(vtgate.SplitQueryResponse) - err := c.cc.Invoke(ctx, "/vtgateservice.Vitess/SplitQuery", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *vitessClient) GetSrvKeyspace(ctx context.Context, in *vtgate.GetSrvKeyspaceRequest, opts ...grpc.CallOption) (*vtgate.GetSrvKeyspaceResponse, error) { out := new(vtgate.GetSrvKeyspaceResponse) err := c.cc.Invoke(ctx, "/vtgateservice.Vitess/GetSrvKeyspace", in, out, opts...) @@ -610,9 +596,6 @@ type VitessServer interface { // MessageAckKeyspaceIds routes Message Acks using the associated // keyspace ids. MessageAckKeyspaceIds(context.Context, *vtgate.MessageAckKeyspaceIdsRequest) (*query.MessageAckResponse, error) - // Split a query into non-overlapping sub queries - // API group: Map Reduce - SplitQuery(context.Context, *vtgate.SplitQueryRequest) (*vtgate.SplitQueryResponse, error) // GetSrvKeyspace returns a SrvKeyspace object (as seen by this vtgate). // This method is provided as a convenient way for clients to take a // look at the sharding configuration for a Keyspace. Looking at the @@ -690,9 +673,6 @@ func (*UnimplementedVitessServer) MessageAck(ctx context.Context, req *vtgate.Me func (*UnimplementedVitessServer) MessageAckKeyspaceIds(ctx context.Context, req *vtgate.MessageAckKeyspaceIdsRequest) (*query.MessageAckResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method MessageAckKeyspaceIds not implemented") } -func (*UnimplementedVitessServer) SplitQuery(ctx context.Context, req *vtgate.SplitQueryRequest) (*vtgate.SplitQueryResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SplitQuery not implemented") -} func (*UnimplementedVitessServer) GetSrvKeyspace(ctx context.Context, req *vtgate.GetSrvKeyspaceRequest) (*vtgate.GetSrvKeyspaceResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSrvKeyspace not implemented") } @@ -1064,24 +1044,6 @@ func _Vitess_MessageAckKeyspaceIds_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } -func _Vitess_SplitQuery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(vtgate.SplitQueryRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(VitessServer).SplitQuery(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/vtgateservice.Vitess/SplitQuery", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(VitessServer).SplitQuery(ctx, req.(*vtgate.SplitQueryRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Vitess_GetSrvKeyspace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(vtgate.GetSrvKeyspaceRequest) if err := dec(in); err != nil { @@ -1202,10 +1164,6 @@ var _Vitess_serviceDesc = grpc.ServiceDesc{ MethodName: "MessageAckKeyspaceIds", Handler: _Vitess_MessageAckKeyspaceIds_Handler, }, - { - MethodName: "SplitQuery", - Handler: _Vitess_SplitQuery_Handler, - }, { MethodName: "GetSrvKeyspace", Handler: _Vitess_GetSrvKeyspace_Handler, diff --git a/go/vt/vitessdriver/fakeserver_test.go b/go/vt/vitessdriver/fakeserver_test.go index e47103b2696..6bd04da762d 100644 --- a/go/vt/vitessdriver/fakeserver_test.go +++ b/go/vt/vitessdriver/fakeserver_test.go @@ -217,20 +217,6 @@ func (f *fakeVTGateService) MessageAckKeyspaceIds(ctx context.Context, keyspace return 0, nil } -// SplitQuery is part of the VTGateService interface -func (f *fakeVTGateService) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, -) ([]*vtgatepb.SplitQueryResponse_Part, error) { - return nil, nil -} - // GetSrvKeyspace is part of the VTGateService interface func (f *fakeVTGateService) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { return &topodatapb.SrvKeyspace{}, nil diff --git a/go/vt/vtcombo/tablet_map.go b/go/vt/vtcombo/tablet_map.go index 49d0454270d..0b982608576 100644 --- a/go/vt/vtcombo/tablet_map.go +++ b/go/vt/vtcombo/tablet_map.go @@ -442,30 +442,6 @@ func (itc *internalTabletConn) Tablet() *topodatapb.Tablet { return itc.topoTablet } -// SplitQuery is part of queryservice.QueryService -func (itc *internalTabletConn) SplitQuery( - ctx context.Context, - target *querypb.Target, - query *querypb.BoundQuery, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*querypb.QuerySplit, error) { - - splits, err := itc.tablet.qsc.QueryService().SplitQuery( - ctx, - target, - query, - splitColumns, - splitCount, - numRowsPerQueryPart, - algorithm) - if err != nil { - return nil, tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err)) - } - return splits, nil -} - // StreamHealth is part of queryservice.QueryService func (itc *internalTabletConn) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error { err := itc.tablet.qsc.QueryService().StreamHealth(ctx, callback) diff --git a/go/vt/vtctl/query.go b/go/vt/vtctl/query.go index d9d72943b80..e8919efc6ed 100644 --- a/go/vt/vtctl/query.go +++ b/go/vt/vtctl/query.go @@ -69,11 +69,6 @@ func init() { commandVtGateExecuteKeyspaceIds, "-server -keyspace -keyspace_ids ,,... [-bind_variables ] [-tablet_type ] [-options ] [-json] ", "Executes the given SQL query with the provided bound variables against the vtgate server. It is routed to the shards that contain the provided keyspace ids."}) - addCommand(queriesGroupName, command{ - "VtGateSplitQuery", - commandVtGateSplitQuery, - "-server -keyspace [-split_column ] -split_count [-bind_variables ] ", - "Executes the SplitQuery computation for the given SQL query with the provided bound variables against the vtgate server (this is the base query for Map-Reduce workloads, and is provided here for debug / test purposes)."}) // VtTablet commands addCommand(queriesGroupName, command{ @@ -319,77 +314,6 @@ func commandVtGateExecuteKeyspaceIds(ctx context.Context, wr *wrangler.Wrangler, return nil } -func commandVtGateSplitQuery(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - if !*enableQueries { - return fmt.Errorf("query commands are disabled (set the -enable_queries flag to enable)") - } - - server := subFlags.String("server", "", "VtGate server to connect to") - bindVariables := newBindvars(subFlags) - splitColumnsStr := subFlags.String( - "split_columns", - "", - "A comma-separated list of the split columns to use to split the query."+ - " If this is empty the table's primary key columns will be used.") - splitCount := subFlags.Int64("split_count", 0, "number of splits to generate.") - numRowsPerQueryPart := subFlags.Int64( - "num_rows_per_query_part", 0, "The number of rows to return in each query part.") - algorithmStr := subFlags.String("algorithm", "EQUAL_SPLITS", "The algorithm to"+ - " use for splitting the query. Either 'FULL_SCAN' or 'EQUAL_SPLITS'") - keyspace := subFlags.String("keyspace", "", "keyspace to send query to") - - if err := subFlags.Parse(args); err != nil { - return err - } - if (*splitCount == 0 && *numRowsPerQueryPart == 0) || - (*splitCount != 0 && *numRowsPerQueryPart != 0) { - return fmt.Errorf("exactly one of split_count or num_rows_per_query_part"+ - "must be nonzero. Got: split_count:%v, num_rows_per_query_part:%v", - *splitCount, *numRowsPerQueryPart) - } - splitColumns := []string{} - if *splitColumnsStr != "" { - splitColumns = strings.Split(*splitColumnsStr, ",") - } - var algorithm querypb.SplitQueryRequest_Algorithm - switch *algorithmStr { - case "FULL_SCAN": - algorithm = querypb.SplitQueryRequest_FULL_SCAN - case "EQUAL_SPLITS": - algorithm = querypb.SplitQueryRequest_EQUAL_SPLITS - default: - return fmt.Errorf("unknown split-query algorithm: %v", algorithmStr) - } - if subFlags.NArg() != 1 { - return fmt.Errorf("the argument is required for the VtGateSplitQuery command") - } - vtgateConn, err := vtgateconn.Dial(ctx, *server) - if err != nil { - return fmt.Errorf("error connecting to vtgate '%v': %v", *server, err) - } - defer vtgateConn.Close() - - bindVars, err := sqltypes.BuildBindVariables(*bindVariables) - if err != nil { - return fmt.Errorf("BuildBindVariables failed: %v", err) - } - - r, err := vtgateConn.SplitQuery( - ctx, - *keyspace, - subFlags.Arg(0), - bindVars, - splitColumns, - int64(*splitCount), - int64(*numRowsPerQueryPart), - algorithm, - ) - if err != nil { - return fmt.Errorf("SplitQuery failed: %v", err) - } - return printJSON(wr.Logger(), r) -} - func commandVtTabletExecute(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { if !*enableQueries { return fmt.Errorf("query commands are disabled (set the -enable_queries flag to enable)") diff --git a/go/vt/vtgate/fakerpcvtgateconn/conn.go b/go/vt/vtgate/fakerpcvtgateconn/conn.go index 2edfebef6f1..e9f84249e87 100644 --- a/go/vt/vtgate/fakerpcvtgateconn/conn.go +++ b/go/vt/vtgate/fakerpcvtgateconn/conn.go @@ -63,27 +63,9 @@ type queryResponse struct { err error } -// querySplitQuery contains all the fields we use to test SplitQuery -type querySplitQuery struct { - Keyspace string - SQL string - BindVariables map[string]*querypb.BindVariable - SplitColumns []string - SplitCount int64 - NumRowsPerQueryPart int64 - Algorithm querypb.SplitQueryRequest_Algorithm -} - -type splitQueryResponse struct { - splitQuery *querySplitQuery - reply []*vtgatepb.SplitQueryResponse_Part - err error -} - // FakeVTGateConn provides a fake implementation of vtgateconn.Impl type FakeVTGateConn struct { - execMap map[string]*queryResponse - splitQueryMap map[string]*splitQueryResponse + execMap map[string]*queryResponse } // RegisterFakeVTGateConnDialer registers the proper dialer for this fake, @@ -92,8 +74,7 @@ type FakeVTGateConn struct { func RegisterFakeVTGateConnDialer() (*FakeVTGateConn, string) { protocol := "fake" impl := &FakeVTGateConn{ - execMap: make(map[string]*queryResponse), - splitQueryMap: make(map[string]*splitQueryResponse), + execMap: make(map[string]*queryResponse), } vtgateconn.RegisterDialer(protocol, func(ctx context.Context, address string) (vtgateconn.Impl, error) { return impl, nil @@ -141,35 +122,6 @@ func (conn *FakeVTGateConn) AddShardQuery( } } -// AddSplitQuery adds a split query and expected result. -func (conn *FakeVTGateConn) AddSplitQuery( - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, - expectedResult []*vtgatepb.SplitQueryResponse_Part) { - - reply := make([]*vtgatepb.SplitQueryResponse_Part, len(expectedResult)) - copy(reply, expectedResult) - key := getSplitQueryKey(keyspace, sql, splitColumns, splitCount, numRowsPerQueryPart, algorithm) - conn.splitQueryMap[key] = &splitQueryResponse{ - splitQuery: &querySplitQuery{ - Keyspace: keyspace, - SQL: sql, - BindVariables: bindVariables, - SplitColumns: splitColumns, - SplitCount: splitCount, - NumRowsPerQueryPart: numRowsPerQueryPart, - Algorithm: algorithm, - }, - reply: reply, - err: nil, - } -} - // Execute please see vtgateconn.Impl.Execute func (conn *FakeVTGateConn) Execute(ctx context.Context, session *vtgatepb.Session, sql string, bindVars map[string]*querypb.BindVariable) (*vtgatepb.Session, *sqltypes.Result, error) { response, ok := conn.execMap[sql] @@ -354,34 +306,6 @@ func (conn *FakeVTGateConn) MessageAckKeyspaceIds(ctx context.Context, keyspace panic("not implemented") } -// SplitQuery please see vtgateconn.Impl.SplitQuery -func (conn *FakeVTGateConn) SplitQuery( - ctx context.Context, - keyspace string, - query string, - bindVars map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - - response, ok := conn.splitQueryMap[getSplitQueryKey( - keyspace, query, splitColumns, splitCount, numRowsPerQueryPart, algorithm)] - if !ok { - return nil, fmt.Errorf( - "no match for keyspace: %s,"+ - " query: %v,"+ - " splitColumns: %v,"+ - " splitCount: %v"+ - " numRowsPerQueryPart: %v"+ - " algorithm: %v", - keyspace, query, splitColumns, splitCount, numRowsPerQueryPart, algorithm) - } - reply := make([]*vtgatepb.SplitQueryResponse_Part, len(response.reply)) - copy(reply, response.reply) - return reply, nil -} - // GetSrvKeyspace please see vtgateconn.Impl.GetSrvKeyspace func (conn *FakeVTGateConn) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { return nil, fmt.Errorf("NYI") @@ -406,23 +330,6 @@ func getShardQueryKey(sql string, shards []string) string { return fmt.Sprintf("%s-%s", sql, strings.Join(shards, ":")) } -func getSplitQueryKey( - keyspace string, - query string, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) string { - return fmt.Sprintf( - "%v:%v:%v:%v:%v:%v", - keyspace, - query, - splitColumns, - splitCount, - numRowsPerQueryPart, - algorithm) -} - func newSession( inTransaction bool, keyspace string, diff --git a/go/vt/vtgate/grpcvtgateconn/conn.go b/go/vt/vtgate/grpcvtgateconn/conn.go index 79fc977d683..63523f45aab 100644 --- a/go/vt/vtgate/grpcvtgateconn/conn.go +++ b/go/vt/vtgate/grpcvtgateconn/conn.go @@ -478,35 +478,6 @@ func (conn *vtgateConn) MessageAckKeyspaceIds(ctx context.Context, keyspace stri return int64(r.Result.RowsAffected), nil } -func (conn *vtgateConn) SplitQuery( - ctx context.Context, - keyspace string, - query string, - bindVars map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - - request := &vtgatepb.SplitQueryRequest{ - CallerId: callerid.EffectiveCallerIDFromContext(ctx), - Keyspace: keyspace, - Query: &querypb.BoundQuery{ - Sql: query, - BindVariables: bindVars, - }, - SplitColumn: splitColumns, - SplitCount: splitCount, - NumRowsPerQueryPart: numRowsPerQueryPart, - Algorithm: algorithm, - } - response, err := conn.c.SplitQuery(ctx, request) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return response.Splits, nil -} - func (conn *vtgateConn) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { request := &vtgatepb.GetSrvKeyspaceRequest{ Keyspace: keyspace, diff --git a/go/vt/vtgate/grpcvtgateconn/suite_test.go b/go/vt/vtgate/grpcvtgateconn/suite_test.go index bf4fcbcc9a8..4bbe6e7ffea 100644 --- a/go/vt/vtgate/grpcvtgateconn/suite_test.go +++ b/go/vt/vtgate/grpcvtgateconn/suite_test.go @@ -822,59 +822,6 @@ func (f *fakeVTGateService) MessageAckKeyspaceIds(ctx context.Context, keyspace return messageAckRowsAffected, nil } -// querySplitQuery contains all the fields we use to test SplitQuery -type querySplitQuery struct { - Keyspace string - SQL string - BindVariables map[string]*querypb.BindVariable - SplitColumns []string - SplitCount int64 - NumRowsPerQueryPart int64 - Algorithm querypb.SplitQueryRequest_Algorithm -} - -func (q *querySplitQuery) equal(q2 *querySplitQuery) bool { - return q.Keyspace == q2.Keyspace && - q.SQL == q2.SQL && - sqltypes.BindVariablesEqual(q.BindVariables, q2.BindVariables) && - reflect.DeepEqual(q.SplitColumns, q2.SplitColumns) && - q.SplitCount == q2.SplitCount && - q.NumRowsPerQueryPart == q2.NumRowsPerQueryPart && - q.Algorithm == q2.Algorithm -} - -// SplitQuery is part of the VTGateService interface -func (f *fakeVTGateService) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - if f.hasError { - return nil, errTestVtGateError - } - if f.panics { - panic(fmt.Errorf("test forced panic")) - } - f.checkCallerID(ctx, "SplitQuery") - query := &querySplitQuery{ - Keyspace: keyspace, - SQL: sql, - BindVariables: bindVariables, - SplitColumns: splitColumns, - SplitCount: splitCount, - NumRowsPerQueryPart: numRowsPerQueryPart, - Algorithm: algorithm, - } - if !query.equal(splitQueryRequest) { - f.t.Errorf("SplitQuery has wrong input: got %#v wanted %#v", query, splitQueryRequest) - } - return splitQueryResult, nil -} - // GetSrvKeyspace is part of the VTGateService interface func (f *fakeVTGateService) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { if f.hasError { @@ -1027,7 +974,6 @@ func RunTests(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGat testMessageStream(t, conn) testMessageAck(t, conn) testMessageAckKeyspaceIds(t, conn) - testSplitQuery(t, conn) testGetSrvKeyspace(t, conn) testUpdateStream(t, conn) @@ -1052,7 +998,6 @@ func RunTests(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGat testMessageStreamPanic(t, conn) testMessageAckPanic(t, conn) testMessageAckKeyspaceIdsPanic(t, conn) - testSplitQueryPanic(t, conn) testGetSrvKeyspacePanic(t, conn) testUpdateStreamPanic(t, conn) fs.panics = false @@ -1089,7 +1034,6 @@ func RunErrorTests(t *testing.T, fakeServer vtgateservice.VTGateService) { testMessageStreamError(t, conn) testMessageAckError(t, conn) testMessageAckKeyspaceIdsError(t, conn) - testSplitQueryError(t, conn) testGetSrvKeyspaceError(t, conn) testUpdateStreamError(t, conn, fs) fs.hasError = false @@ -1971,53 +1915,6 @@ func testMessageAckKeyspaceIdsPanic(t *testing.T, conn *vtgateconn.VTGateConn) { expectPanic(t, err) } -func testSplitQuery(t *testing.T, conn *vtgateconn.VTGateConn) { - ctx := newContext() - qsl, err := conn.SplitQuery(ctx, - splitQueryRequest.Keyspace, - splitQueryRequest.SQL, - splitQueryRequest.BindVariables, - splitQueryRequest.SplitColumns, - splitQueryRequest.SplitCount, - splitQueryRequest.NumRowsPerQueryPart, - splitQueryRequest.Algorithm, - ) - if err != nil { - t.Fatalf("SplitQuery failed: %v", err) - } - if !sqltypes.SplitQueryResponsePartsEqual(qsl, splitQueryResult) { - t.Errorf("SplitQuery returned wrong result: got %#v wanted %#v", qsl, splitQueryResult) - } -} - -func testSplitQueryError(t *testing.T, conn *vtgateconn.VTGateConn) { - ctx := newContext() - _, err := conn.SplitQuery(ctx, - splitQueryRequest.Keyspace, - splitQueryRequest.SQL, - splitQueryRequest.BindVariables, - splitQueryRequest.SplitColumns, - splitQueryRequest.SplitCount, - splitQueryRequest.NumRowsPerQueryPart, - splitQueryRequest.Algorithm, - ) - verifyError(t, err, "SplitQuery") -} - -func testSplitQueryPanic(t *testing.T, conn *vtgateconn.VTGateConn) { - ctx := newContext() - _, err := conn.SplitQuery(ctx, - splitQueryRequest.Keyspace, - splitQueryRequest.SQL, - splitQueryRequest.BindVariables, - splitQueryRequest.SplitColumns, - splitQueryRequest.SplitCount, - splitQueryRequest.NumRowsPerQueryPart, - splitQueryRequest.Algorithm, - ) - expectPanic(t, err) -} - func testGetSrvKeyspace(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := newContext() sk, err := conn.GetSrvKeyspace(ctx, getSrvKeyspaceKeyspace) @@ -2573,39 +2470,6 @@ var session2 = &vtgatepb.Session{ var dtid2 = "aa" -var splitQueryRequest = &querySplitQuery{ - Keyspace: "ks2", - SQL: "in for SplitQuery", - BindVariables: map[string]*querypb.BindVariable{ - "bind2": sqltypes.Int64BindVariable(43), - }, - SplitColumns: []string{"split_column1", "split_column2"}, - SplitCount: 145, - NumRowsPerQueryPart: 4000, - Algorithm: querypb.SplitQueryRequest_FULL_SCAN, -} - -var splitQueryResult = []*vtgatepb.SplitQueryResponse_Part{ - { - Query: &querypb.BoundQuery{ - Sql: "out for SplitQuery", - BindVariables: map[string]*querypb.BindVariable{ - "bind1": sqltypes.Int64BindVariable(1114444), - }, - }, - KeyRangePart: &vtgatepb.SplitQueryResponse_KeyRangePart{ - Keyspace: "ksout", - KeyRanges: []*topodatapb.KeyRange{ - { - Start: []byte{'s'}, - End: []byte{'e'}, - }, - }, - }, - Size: 12344, - }, -} - var getSrvKeyspaceKeyspace = "test_keyspace" var getSrvKeyspaceResult = &topodatapb.SrvKeyspace{ diff --git a/go/vt/vtgate/grpcvtgateservice/server.go b/go/vt/vtgate/grpcvtgateservice/server.go index aa568905d3d..5321c9325fc 100644 --- a/go/vt/vtgate/grpcvtgateservice/server.go +++ b/go/vt/vtgate/grpcvtgateservice/server.go @@ -452,28 +452,6 @@ func (vtg *VTGate) MessageAckKeyspaceIds(ctx context.Context, request *vtgatepb. }, nil } -// SplitQuery is the RPC version of vtgateservice.VTGateService method -func (vtg *VTGate) SplitQuery(ctx context.Context, request *vtgatepb.SplitQueryRequest) (response *vtgatepb.SplitQueryResponse, err error) { - - defer vtg.server.HandlePanic(&err) - ctx = withCallerIDContext(ctx, request.CallerId) - splits, vtgErr := vtg.server.SplitQuery( - ctx, - request.Keyspace, - request.Query.Sql, - request.Query.BindVariables, - request.SplitColumn, - request.SplitCount, - request.NumRowsPerQueryPart, - request.Algorithm) - if vtgErr != nil { - return nil, vterrors.ToGRPC(vtgErr) - } - return &vtgatepb.SplitQueryResponse{ - Splits: splits, - }, nil -} - // GetSrvKeyspace is the RPC version of vtgateservice.VTGateService method func (vtg *VTGate) GetSrvKeyspace(ctx context.Context, request *vtgatepb.GetSrvKeyspaceRequest) (response *vtgatepb.GetSrvKeyspaceResponse, err error) { defer vtg.server.HandlePanic(&err) diff --git a/go/vt/vtgate/scatter_conn.go b/go/vt/vtgate/scatter_conn.go index 919af86be2f..8e0ebff3342 100644 --- a/go/vt/vtgate/scatter_conn.go +++ b/go/vt/vtgate/scatter_conn.go @@ -19,7 +19,6 @@ package vtgate import ( "flag" "io" - "math/rand" "sync" "time" @@ -621,116 +620,6 @@ func (stc *ScatterConn) UpdateStream(ctx context.Context, rs *srvtopo.ResolvedSh return rs.QueryService.UpdateStream(ctx, rs.Target, position, timestamp, callback) } -// SplitQuery scatters a SplitQuery request to the shards whose names are given in 'shards'. -// For every set of *querypb.QuerySplit's received from a shard, it applies the given -// 'querySplitToPartFunc' function to convert each *querypb.QuerySplit into a -// 'SplitQueryResponse_Part' message. Finally, it aggregates the obtained -// SplitQueryResponse_Parts across all shards and returns the resulting slice. -func (stc *ScatterConn) SplitQuery( - ctx context.Context, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - perShardSplitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, - rss []*srvtopo.ResolvedShard, - querySplitToQueryPartFunc func( - querySplit *querypb.QuerySplit, rs *srvtopo.ResolvedShard) (*vtgatepb.SplitQueryResponse_Part, error)) ([]*vtgatepb.SplitQueryResponse_Part, error) { - - tabletType := topodatapb.TabletType_RDONLY - // allParts will collect the query-parts from all the shards. It's protected - // by allPartsMutex. - var allParts []*vtgatepb.SplitQueryResponse_Part - var allPartsMutex sync.Mutex - - allErrors := stc.multiGo( - ctx, - "SplitQuery", - rss, - tabletType, - func(rs *srvtopo.ResolvedShard, i int) error { - // Get all splits from this shard - query := &querypb.BoundQuery{ - Sql: sql, - BindVariables: bindVariables, - } - querySplits, err := rs.QueryService.SplitQuery( - ctx, - rs.Target, - query, - splitColumns, - perShardSplitCount, - numRowsPerQueryPart, - algorithm) - if err != nil { - return err - } - parts := make([]*vtgatepb.SplitQueryResponse_Part, len(querySplits)) - for i, querySplit := range querySplits { - parts[i], err = querySplitToQueryPartFunc(querySplit, rs) - if err != nil { - return err - } - } - // Aggregate the parts from this shard into allParts. - allPartsMutex.Lock() - defer allPartsMutex.Unlock() - allParts = append(allParts, parts...) - return nil - }, - ) - - if allErrors.HasErrors() { - err := allErrors.AggrError(vterrors.Aggregate) - return nil, err - } - // We shuffle the query-parts here. External frameworks like MapReduce may - // "deal" these jobs to workers in the order they are in the list. Without - // shuffling workers can be very unevenly distributed among - // the shards they query. E.g. all workers will first query the first shard, - // then most of them to the second shard, etc, which results with uneven - // load balancing among shards. - shuffleQueryParts(allParts) - return allParts, nil -} - -// randomGenerator is the randomGenerator used for the randomness -// of 'shuffleQueryParts'. It's initialized in 'init()' below. -type shuffleQueryPartsRandomGeneratorInterface interface { - Intn(n int) int -} - -var shuffleQueryPartsRandomGenerator shuffleQueryPartsRandomGeneratorInterface - -func init() { - shuffleQueryPartsRandomGenerator = - rand.New(rand.NewSource(time.Now().UnixNano())) -} - -// injectShuffleQueryParsRandomGenerator injects the given object -// as the random generator used by shuffleQueryParts. This function -// should only be used in tests and should not be called concurrently. -// It returns the previous shuffleQueryPartsRandomGenerator used. -// lint:ignore U1000 available for tests to use -func injectShuffleQueryPartsRandomGenerator( - randGen shuffleQueryPartsRandomGeneratorInterface) shuffleQueryPartsRandomGeneratorInterface { - oldRandGen := shuffleQueryPartsRandomGenerator - shuffleQueryPartsRandomGenerator = randGen - return oldRandGen -} - -// shuffleQueryParts performs an in-place shuffle of the given array. -// The result is a pseudo-random permutation of the array chosen uniformally -// from the space of all permutations. -func shuffleQueryParts(splits []*vtgatepb.SplitQueryResponse_Part) { - for i := len(splits) - 1; i >= 1; i-- { - randIndex := shuffleQueryPartsRandomGenerator.Intn(i + 1) - // swap splits[i], splits[randIndex] - splits[randIndex], splits[i] = splits[i], splits[randIndex] - } -} - // Close closes the underlying Gateway. func (stc *ScatterConn) Close() error { return stc.gateway.Close(context.Background()) diff --git a/go/vt/vtgate/scatter_conn_test.go b/go/vt/vtgate/scatter_conn_test.go index 434c7ddb97d..407b0969be6 100644 --- a/go/vt/vtgate/scatter_conn_test.go +++ b/go/vt/vtgate/scatter_conn_test.go @@ -673,49 +673,6 @@ func TestAppendResult(t *testing.T) { } } -// MockShuffleQueryPartsRandomGenerator implements the ShuffleQueryPartsRandomGeneratorInterface -// and returns a canned set of responses given in 'intNResults' for successive calls to its Intn() -// method. -type mockShuffleQueryPartsRandomGenerator struct { - intNResults []int -} - -func (mockRandGen *mockShuffleQueryPartsRandomGenerator) Intn(unused int) int { - if len(mockRandGen.intNResults) == 0 { - panic("MockShuffleQueryPartsRandomGenerator exhausted.") - } - result := mockRandGen.intNResults[0] - mockRandGen.intNResults = mockRandGen.intNResults[1:] - return result -} - -func TestShuffleQueryParts(t *testing.T) { - mockRandGen := &mockShuffleQueryPartsRandomGenerator{ - intNResults: []int{1, 0}, - } - oldGen := injectShuffleQueryPartsRandomGenerator(mockRandGen) - queryPart1 := vtgatepb.SplitQueryResponse_Part{ - Query: &querypb.BoundQuery{Sql: "part_1"}, - } - queryPart2 := vtgatepb.SplitQueryResponse_Part{ - Query: &querypb.BoundQuery{Sql: "part_2"}, - } - queryPart3 := vtgatepb.SplitQueryResponse_Part{ - Query: &querypb.BoundQuery{Sql: "part_3"}, - } - queryParts := []*vtgatepb.SplitQueryResponse_Part{&queryPart1, &queryPart2, &queryPart3} - queryPartsExpectedOutput := []*vtgatepb.SplitQueryResponse_Part{ - &queryPart3, &queryPart1, &queryPart2, - } - shuffleQueryParts(queryParts) - if !sqltypes.SplitQueryResponsePartsEqual(queryParts, queryPartsExpectedOutput) { - t.Errorf("want: %+v, got %+v", queryPartsExpectedOutput, queryParts) - } - - // Return the generator to what it was to avoid disrupting other tests. - injectShuffleQueryPartsRandomGenerator(oldGen) -} - func newTestScatterConn(hc discovery.HealthCheck, serv srvtopo.Server, cell string) *ScatterConn { // The topo.Server is used to start watching the cells described // in '-cells_to_watch' command line parameter, which is diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index e41781eddfe..b7a766ddfec 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -21,7 +21,6 @@ package vtgate import ( "flag" "fmt" - "math" "net/http" "time" @@ -39,7 +38,6 @@ import ( "vitess.io/vitess/go/vt/sqlannotation" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" - "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vterrors" @@ -848,110 +846,6 @@ handleError: return session, nil, err } -// isKeyspaceRangeBasedSharded returns true if a keyspace is sharded -// by range. This is true when there is a ShardingColumnType defined -// in the SrvKeyspace (that is using the range-based sharding with the -// client specifying the sharding key), or when the VSchema for the -// keyspace is Sharded. -func (vtg *VTGate) isKeyspaceRangeBasedSharded(keyspace string, srvKeyspace *topodatapb.SrvKeyspace) bool { - if srvKeyspace.ShardingColumnType != topodatapb.KeyspaceIdType_UNSET { - // We are using range based sharding with the application - // providing the sharding key value. - return true - } - if vtg.executor.IsKeyspaceRangeBasedSharded(keyspace) { - // We are using range based sharding with the VSchema - // poviding the routing information - return true - } - - // Not range based sharded, might be un-sharded or custom sharded. - return false -} - -// SplitQuery implements the SplitQuery RPC. This is the new version that -// supports multiple split-columns and multiple splitting algorithms. -// See the documentation of SplitQueryRequest in "proto/vtgate.proto" for more -// information. -func (vtg *VTGate) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - - if bvErr := sqltypes.ValidateBindVariables(bindVariables); bvErr != nil { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", bvErr) - } - - // TODO(erez): Add validation of SplitQuery parameters. - rss, srvKeyspace, err := vtg.resolver.resolver.GetAllShards(ctx, keyspace, topodatapb.TabletType_RDONLY) - if err != nil { - return nil, err - } - - // If the caller specified a splitCount (vs. specifying 'numRowsPerQueryPart') scale it by the - // number of shards (otherwise it stays 0). - perShardSplitCount := int64(math.Ceil(float64(splitCount) / float64(len(rss)))) - - // Determine whether to return SplitQueryResponse_KeyRangeParts or SplitQueryResponse_ShardParts. - // We return 'KeyRangeParts' for sharded keyspaces that are not custom sharded. If the - // keyspace is custom sharded or unsharded we return 'ShardParts'. - var querySplitToQueryPartFunc func( - querySplit *querypb.QuerySplit, rs *srvtopo.ResolvedShard) (*vtgatepb.SplitQueryResponse_Part, error) - if vtg.isKeyspaceRangeBasedSharded(keyspace, srvKeyspace) { - querySplitToQueryPartFunc = func(querySplit *querypb.QuerySplit, rs *srvtopo.ResolvedShard) (*vtgatepb.SplitQueryResponse_Part, error) { - // Use ValidateShardName to extract the keyrange. - _, kr, err := topo.ValidateShardName(rs.Target.Shard) - if err != nil { - return nil, fmt.Errorf("cannot extract keyrange from shard name %v: %v", rs.Target.Shard, err) - } - if kr == nil { - // Keyrange can be nil for the shard (e.g. for single-sharded keyspaces during resharding). - // In this case we append an empty keyrange that represents the entire keyspace. - kr = &topodatapb.KeyRange{ - Start: []byte{}, - End: []byte{}, - } - } - return &vtgatepb.SplitQueryResponse_Part{ - Query: querySplit.Query, - KeyRangePart: &vtgatepb.SplitQueryResponse_KeyRangePart{ - Keyspace: keyspace, - KeyRanges: []*topodatapb.KeyRange{kr}, - }, - Size: querySplit.RowCount, - }, nil - } - } else { - // Keyspace is either unsharded or custom-sharded. - querySplitToQueryPartFunc = func(querySplit *querypb.QuerySplit, rs *srvtopo.ResolvedShard) (*vtgatepb.SplitQueryResponse_Part, error) { - return &vtgatepb.SplitQueryResponse_Part{ - Query: querySplit.Query, - ShardPart: &vtgatepb.SplitQueryResponse_ShardPart{ - Keyspace: keyspace, - Shards: []string{rs.Target.Shard}, - }, - Size: querySplit.RowCount, - }, nil - } - } - - return vtg.resolver.scatterConn.SplitQuery( - ctx, - sql, - bindVariables, - splitColumns, - perShardSplitCount, - numRowsPerQueryPart, - algorithm, - rss, - querySplitToQueryPartFunc) -} - // GetSrvKeyspace is part of the vtgate service API. func (vtg *VTGate) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { return vtg.resolver.toposerv.GetSrvKeyspace(ctx, vtg.resolver.cell, keyspace) diff --git a/go/vt/vtgate/vtgate_test.go b/go/vt/vtgate/vtgate_test.go index 2705dc42008..40929264352 100644 --- a/go/vt/vtgate/vtgate_test.go +++ b/go/vt/vtgate/vtgate_test.go @@ -20,7 +20,6 @@ import ( "encoding/hex" "fmt" "io" - "math" "reflect" "strings" "testing" @@ -32,7 +31,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/discovery" - "vitess.io/vitess/go/vt/key" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/sandboxconn" @@ -1113,89 +1111,6 @@ func TestVTGateStreamExecuteShards(t *testing.T) { } } -func TestVTGateSplitQuerySharded(t *testing.T) { - keyspace := "TestVTGateSplitQuery" - keyranges, err := key.ParseShardingSpec(DefaultShardSpec) - if err != nil { - t.Fatalf("got: %v, want: nil", err) - } - createSandbox(keyspace) - hcVTGateTest.Reset() - port := int32(1001) - for _, kr := range keyranges { - hcVTGateTest.AddTestTablet("aa", "1.1.1.1", port, keyspace, key.KeyRangeString(kr), topodatapb.TabletType_RDONLY, true, 1, nil) - port++ - } - sql := "select col1, col2 from table" - bindVars := map[string]*querypb.BindVariable{} - splitColumns := []string{"sc1", "sc2"} - algorithm := querypb.SplitQueryRequest_FULL_SCAN - type testCaseType struct { - splitCount int64 - numRowsPerQueryPart int64 - } - testCases := []testCaseType{ - {splitCount: 100, numRowsPerQueryPart: 0}, - {splitCount: 0, numRowsPerQueryPart: 123}, - } - for _, testCase := range testCases { - splits, err := rpcVTGate.SplitQuery( - context.Background(), - keyspace, - sql, - bindVars, - splitColumns, - testCase.splitCount, - testCase.numRowsPerQueryPart, - algorithm) - if err != nil { - t.Errorf("got %v, want: nil. testCase: %+v", err, testCase) - } - // Total number of splits should be number of shards as our sandbox returns a single split - // for its fake implementation of SplitQuery. - if len(keyranges) != len(splits) { - t.Errorf("wrong number of splits, got %+v, want %+v. testCase:\n%+v", - len(splits), len(keyranges), testCase) - } - actualSqlsByKeyRange := map[string][]string{} - for _, split := range splits { - if split.KeyRangePart.Keyspace != keyspace { - t.Errorf("wrong keyspace, got \n%+v, want \n%+v. testCase:\n%+v", - keyspace, split.KeyRangePart.Keyspace, testCase) - } - if len(split.KeyRangePart.KeyRanges) != 1 { - t.Errorf("wrong number of keyranges, got \n%+v, want \n%+v. testCase:\n%+v", - 1, len(split.KeyRangePart.KeyRanges), testCase) - } - kr := key.KeyRangeString(split.KeyRangePart.KeyRanges[0]) - actualSqlsByKeyRange[kr] = append(actualSqlsByKeyRange[kr], split.Query.Sql) - } - expectedSqlsByKeyRange := map[string][]string{} - for _, kr := range keyranges { - perShardSplitCount := int64(math.Ceil(float64(testCase.splitCount) / float64(len(keyranges)))) - shard := key.KeyRangeString(kr) - expectedSqlsByKeyRange[shard] = []string{ - fmt.Sprintf( - "query:%v, splitColumns:%v, splitCount:%v,"+ - " numRowsPerQueryPart:%v, algorithm:%v, shard:%v", - &querypb.BoundQuery{Sql: sql, BindVariables: map[string]*querypb.BindVariable{}}, - splitColumns, - perShardSplitCount, - testCase.numRowsPerQueryPart, - algorithm, - shard, - ), - } - } - if !reflect.DeepEqual(actualSqlsByKeyRange, expectedSqlsByKeyRange) { - t.Errorf( - "splits contain the wrong sqls and/or keyranges, "+ - "got:\n%+v\n, want:\n%+v\n. testCase:\n%+v", - actualSqlsByKeyRange, expectedSqlsByKeyRange, testCase) - } - } -} - func TestVTGateMessageStreamSharded(t *testing.T) { ks := "TestVTGateMessageStreamSharded" createSandbox(ks) @@ -1463,74 +1378,6 @@ func TestVTGateMessageAckKeyspaceIds(t *testing.T) { } } -func TestVTGateSplitQueryUnsharded(t *testing.T) { - keyspace := KsTestUnsharded - createSandbox(keyspace) - hcVTGateTest.Reset() - hcVTGateTest.AddTestTablet("aa", "1.1.1.1", 1001, keyspace, "0", topodatapb.TabletType_RDONLY, true, 1, nil) - sql := "select col1, col2 from table" - bindVars := map[string]*querypb.BindVariable{} - splitColumns := []string{"sc1", "sc2"} - algorithm := querypb.SplitQueryRequest_FULL_SCAN - type testCaseType struct { - splitCount int64 - numRowsPerQueryPart int64 - } - testCases := []testCaseType{ - {splitCount: 100, numRowsPerQueryPart: 0}, - {splitCount: 0, numRowsPerQueryPart: 123}, - } - for _, testCase := range testCases { - splits, err := rpcVTGate.SplitQuery( - context.Background(), - keyspace, - sql, - bindVars, - splitColumns, - testCase.splitCount, - testCase.numRowsPerQueryPart, - algorithm) - if err != nil { - t.Errorf("got %v, want: nil. testCase: %+v", err, testCase) - } - // Total number of splits should be number of shards (1) as our sandbox returns a single split - // for its fake implementation of SplitQuery. - if len(splits) != 1 { - t.Errorf("wrong number of splits, got %+v, want %+v. testCase:\n%+v", - len(splits), 1, testCase) - continue - } - split := splits[0] - if split.KeyRangePart != nil { - t.Errorf("KeyRangePart should not be populated. Got:\n%+v\n, testCase:\n%+v\n", - keyspace, split.KeyRangePart) - } - if split.ShardPart.Keyspace != keyspace { - t.Errorf("wrong keyspace, got \n%+v, want \n%+v. testCase:\n%+v", - keyspace, split.ShardPart.Keyspace, testCase) - } - if len(split.ShardPart.Shards) != 1 { - t.Errorf("wrong number of shards, got \n%+v, want \n%+v. testCase:\n%+v", - 1, len(split.ShardPart.Shards), testCase) - } - expectedShard := "0" - expectedSQL := fmt.Sprintf( - "query:%v, splitColumns:%v, splitCount:%v,"+ - " numRowsPerQueryPart:%v, algorithm:%v, shard:%v", - &querypb.BoundQuery{Sql: sql, BindVariables: map[string]*querypb.BindVariable{}}, - splitColumns, - testCase.splitCount, - testCase.numRowsPerQueryPart, - algorithm, - expectedShard, - ) - if split.Query.Sql != expectedSQL { - t.Errorf("got:\n%v\n, want:\n%v\n, testCase:\n%+v", - split.Query.Sql, expectedSQL, testCase) - } - } -} - func TestVTGateBindVarError(t *testing.T) { ks := KsTestUnsharded createSandbox(ks) @@ -1628,12 +1475,6 @@ func TestVTGateBindVarError(t *testing.T) { f: func() error { return rpcVTGate.StreamExecuteShards(ctx, "", bindVars, "", []string{}, topodatapb.TabletType_MASTER, nil, func(_ *sqltypes.Result) error { return nil }) }, - }, { - name: "SplitQuery", - f: func() error { - _, err := rpcVTGate.SplitQuery(ctx, "", "", bindVars, []string{}, 0, 0, querypb.SplitQueryRequest_FULL_SCAN) - return err - }, }} for _, tcase := range tcases { if err := tcase.f(); err == nil || !strings.Contains(err.Error(), want) { @@ -2354,36 +2195,12 @@ func testErrorPropagation(t *testing.T, sbcs []*sandboxconn.SandboxConn, before } // Rollback is skipped, it doesn't forward errors. - - // SplitQuery - for _, sbc := range sbcs { - before(sbc) - } - _, err = rpcVTGate.SplitQuery(context.Background(), - KsTestUnsharded, - "select col1, col2 from table", - nil, - []string{"sc1", "sc2"}, - 100, - 0, - querypb.SplitQueryRequest_FULL_SCAN) - if err == nil { - t.Errorf("error %v not propagated for SplitQuery", expected) - } else { - ec := vterrors.Code(err) - if ec != expected { - t.Errorf("unexpected error, got %v want %v: %v", ec, expected, err) - } - } - for _, sbc := range sbcs { - after(sbc) - } } // TestErrorPropagation tests an error returned by sandboxconn is // properly propagated through vtgate layers. We need both a master // tablet and a rdonly tablet because we don't control the routing of -// Commit nor SplitQuery{,V2}. +// Commit. func TestErrorPropagation(t *testing.T) { createSandbox(KsTestUnsharded) hcVTGateTest.Reset() diff --git a/go/vt/vtgate/vtgateconn/vtgateconn.go b/go/vt/vtgate/vtgateconn/vtgateconn.go index 81c6cb74bf7..470a22824e1 100644 --- a/go/vt/vtgate/vtgateconn/vtgateconn.go +++ b/go/vt/vtgate/vtgateconn/vtgateconn.go @@ -160,13 +160,6 @@ func (conn *VTGateConn) Close() { conn.impl = nil } -// SplitQuery splits a query into smaller queries. It is mostly used by batch job frameworks -// such as MapReduce. See the documentation for the vtgate.SplitQueryRequest protocol buffer message -// in 'proto/vtgate.proto'. -func (conn *VTGateConn) SplitQuery(ctx context.Context, keyspace string, query string, bindVars map[string]*querypb.BindVariable, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - return conn.impl.SplitQuery(ctx, keyspace, query, bindVars, splitColumns, splitCount, numRowsPerQueryPart, algorithm) -} - // GetSrvKeyspace returns a topo.SrvKeyspace object. func (conn *VTGateConn) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { return conn.impl.GetSrvKeyspace(ctx, keyspace) @@ -380,11 +373,6 @@ type Impl interface { MessageAck(ctx context.Context, keyspace string, name string, ids []*querypb.Value) (int64, error) MessageAckKeyspaceIds(ctx context.Context, keyspace string, name string, idKeyspaceIDs []*vtgatepb.IdKeyspaceId) (int64, error) - // SplitQuery splits a query into smaller queries. It is mostly used by batch job frameworks - // such as MapReduce. See the documentation for the vtgate.SplitQueryRequest protocol buffer - // message in 'proto/vtgate.proto'. - SplitQuery(ctx context.Context, keyspace string, query string, bindVars map[string]*querypb.BindVariable, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) - // GetSrvKeyspace returns a topo.SrvKeyspace. GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) diff --git a/go/vt/vtgate/vtgateservice/interface.go b/go/vt/vtgate/vtgateservice/interface.go index 69f14553804..ae2acf182be 100644 --- a/go/vt/vtgate/vtgateservice/interface.go +++ b/go/vt/vtgate/vtgateservice/interface.go @@ -58,17 +58,6 @@ type VTGateService interface { MessageAck(ctx context.Context, keyspace string, name string, ids []*querypb.Value) (int64, error) MessageAckKeyspaceIds(ctx context.Context, keyspace string, name string, idKeyspaceIDs []*vtgatepb.IdKeyspaceId) (int64, error) - // Map Reduce support - SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) - // Topology support GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) diff --git a/go/vt/vttablet/grpcqueryservice/server.go b/go/vt/vttablet/grpcqueryservice/server.go index 7e56b983926..15f78172275 100644 --- a/go/vt/vttablet/grpcqueryservice/server.go +++ b/go/vt/vttablet/grpcqueryservice/server.go @@ -324,27 +324,6 @@ func (q *query) MessageAck(ctx context.Context, request *querypb.MessageAckReque }, nil } -// SplitQuery is part of the queryservice.QueryServer interface -func (q *query) SplitQuery(ctx context.Context, request *querypb.SplitQueryRequest) (response *querypb.SplitQueryResponse, err error) { - defer q.server.HandlePanic(&err) - ctx = callerid.NewContext(callinfo.GRPCCallInfo(ctx), - request.EffectiveCallerId, - request.ImmediateCallerId, - ) - splits, err := q.server.SplitQuery( - ctx, - request.Target, - request.Query, - request.SplitColumn, - request.SplitCount, - request.NumRowsPerQueryPart, - request.Algorithm) - if err != nil { - return nil, vterrors.ToGRPC(err) - } - return &querypb.SplitQueryResponse{Queries: splits}, nil -} - // StreamHealth is part of the queryservice.QueryServer interface func (q *query) StreamHealth(request *querypb.StreamHealthRequest, stream queryservicepb.Query_StreamHealthServer) (err error) { defer q.server.HandlePanic(&err) diff --git a/go/vt/vttablet/grpctabletconn/conn.go b/go/vt/vttablet/grpctabletconn/conn.go index a10ba36320c..baffd9b7c31 100644 --- a/go/vt/vttablet/grpctabletconn/conn.go +++ b/go/vt/vttablet/grpctabletconn/conn.go @@ -557,40 +557,6 @@ func (conn *gRPCQueryClient) MessageAck(ctx context.Context, target *querypb.Tar return int64(reply.Result.RowsAffected), nil } -// SplitQuery is the stub for TabletServer.SplitQuery RPC -func (conn *gRPCQueryClient) SplitQuery( - ctx context.Context, - target *querypb.Target, - query *querypb.BoundQuery, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) (queries []*querypb.QuerySplit, err error) { - - conn.mu.RLock() - defer conn.mu.RUnlock() - if conn.cc == nil { - err = tabletconn.ConnClosed - return - } - - req := &querypb.SplitQueryRequest{ - Target: target, - EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), - ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), - Query: query, - SplitColumn: splitColumns, - SplitCount: splitCount, - NumRowsPerQueryPart: numRowsPerQueryPart, - Algorithm: algorithm, - } - sqr, err := conn.c.SplitQuery(ctx, req) - if err != nil { - return nil, tabletconn.ErrorFromGRPC(err) - } - return sqr.Queries, nil -} - // StreamHealth starts a streaming RPC for VTTablet health status updates. func (conn *gRPCQueryClient) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error { // Please see comments in StreamExecute to see how this works. diff --git a/go/vt/vttablet/queryservice/queryservice.go b/go/vt/vttablet/queryservice/queryservice.go index 4182161a294..37366ca4167 100644 --- a/go/vt/vttablet/queryservice/queryservice.go +++ b/go/vt/vttablet/queryservice/queryservice.go @@ -92,11 +92,6 @@ type QueryService interface { MessageStream(ctx context.Context, target *querypb.Target, name string, callback func(*sqltypes.Result) error) error MessageAck(ctx context.Context, target *querypb.Target, name string, ids []*querypb.Value) (count int64, err error) - // SplitQuery is a MapReduce helper function - // This version of SplitQuery supports multiple algorithms and multiple split columns. - // See the documentation of SplitQueryRequest in 'proto/vtgate.proto' for more information. - SplitQuery(ctx context.Context, target *querypb.Target, query *querypb.BoundQuery, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm querypb.SplitQueryRequest_Algorithm) ([]*querypb.QuerySplit, error) - // UpdateStream streams updates from the provided position or timestamp. UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error diff --git a/go/vt/vttablet/queryservice/wrapped.go b/go/vt/vttablet/queryservice/wrapped.go index 202dd0a3213..27d47ffc49d 100644 --- a/go/vt/vttablet/queryservice/wrapped.go +++ b/go/vt/vttablet/queryservice/wrapped.go @@ -235,15 +235,6 @@ func (ws *wrappedService) MessageAck(ctx context.Context, target *querypb.Target return count, err } -func (ws *wrappedService) SplitQuery(ctx context.Context, target *querypb.Target, query *querypb.BoundQuery, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm querypb.SplitQueryRequest_Algorithm) (queries []*querypb.QuerySplit, err error) { - err = ws.wrapper(ctx, target, ws.impl, "SplitQuery", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (bool, error) { - var innerErr error - queries, innerErr = conn.SplitQuery(ctx, target, query, splitColumns, splitCount, numRowsPerQueryPart, algorithm) - return canRetry(ctx, innerErr), innerErr - }) - return queries, err -} - func (ws *wrappedService) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error { return ws.wrapper(ctx, target, ws.impl, "UpdateStream", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (bool, error) { innerErr := conn.UpdateStream(ctx, target, position, timestamp, callback) diff --git a/go/vt/vttablet/sandboxconn/sandboxconn.go b/go/vt/vttablet/sandboxconn/sandboxconn.go index bc47b69fae4..add0cb97baa 100644 --- a/go/vt/vttablet/sandboxconn/sandboxconn.go +++ b/go/vt/vttablet/sandboxconn/sandboxconn.go @@ -327,32 +327,6 @@ func (sbc *SandboxConn) MessageAck(ctx context.Context, target *querypb.Target, // SandboxSQRowCount is the default number of fake splits returned. var SandboxSQRowCount = int64(10) -// SplitQuery returns a single QuerySplit whose 'sql' field describes the received arguments. -func (sbc *SandboxConn) SplitQuery( - ctx context.Context, - target *querypb.Target, - query *querypb.BoundQuery, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*querypb.QuerySplit, error) { - err := sbc.getError() - if err != nil { - return nil, err - } - splits := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: fmt.Sprintf( - "query:%v, splitColumns:%v, splitCount:%v,"+ - " numRowsPerQueryPart:%v, algorithm:%v, shard:%v", - query, splitColumns, splitCount, numRowsPerQueryPart, algorithm, sbc.tablet.Shard), - }, - }, - } - return splits, nil -} - // StreamHealth is not implemented. func (sbc *SandboxConn) StreamHealth(ctx context.Context, callback func(*querypb.StreamHealthResponse) error) error { return fmt.Errorf("not implemented in test") diff --git a/go/vt/vttablet/tabletconntest/fakequeryservice.go b/go/vt/vttablet/tabletconntest/fakequeryservice.go index 32ea47b3c26..ba8a61dddc6 100644 --- a/go/vt/vttablet/tabletconntest/fakequeryservice.go +++ b/go/vt/vttablet/tabletconntest/fakequeryservice.go @@ -19,7 +19,6 @@ package tabletconntest import ( "errors" "fmt" - "reflect" "testing" "golang.org/x/net/context" @@ -587,40 +586,6 @@ func (f *FakeQueryService) ExecuteBatch(ctx context.Context, target *querypb.Tar return ExecuteBatchQueryResultList, nil } -// SplitQuerySplitColumns is a test list for column splits. -var SplitQuerySplitColumns = []string{"nice_column_to_split"} - -// SplitQuerySplitCount is a test split count. -const SplitQuerySplitCount = 372 - -// SplitQueryBoundQuery is a test query for splits. -var SplitQueryBoundQuery = &querypb.BoundQuery{ - Sql: "splitQuery", - BindVariables: map[string]*querypb.BindVariable{ - "bind1": sqltypes.Int64BindVariable(43), - }, -} - -// SplitQueryNumRowsPerQueryPart is a test num rows for split. -const SplitQueryNumRowsPerQueryPart = 123 - -// SplitQueryAlgorithm is a test algorithm for splits. -const SplitQueryAlgorithm = querypb.SplitQueryRequest_FULL_SCAN - -// SplitQueryQuerySplitList is a test result for splits. -var SplitQueryQuerySplitList = []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "splitQuery", - BindVariables: map[string]*querypb.BindVariable{ - "bind1": sqltypes.Int64BindVariable(43), - "keyspace_id": sqltypes.Int64BindVariable(3333), - }, - }, - RowCount: 4456, - }, -} - // BeginExecute combines Begin and Execute. func (f *FakeQueryService) BeginExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]*querypb.BindVariable, options *querypb.ExecuteOptions) (*sqltypes.Result, int64, error) { transactionID, err := f.Begin(ctx, target, options) @@ -704,47 +669,6 @@ func (f *FakeQueryService) MessageAck(ctx context.Context, target *querypb.Targe return 1, nil } -// SplitQuery is part of the queryservice.QueryService interface -func (f *FakeQueryService) SplitQuery( - ctx context.Context, - target *querypb.Target, - query *querypb.BoundQuery, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, -) ([]*querypb.QuerySplit, error) { - - if f.HasError { - return nil, f.TabletError - } - if f.Panics { - panic(fmt.Errorf("test-triggered panic")) - } - f.checkTargetCallerID(ctx, "SplitQuery", target) - if !proto.Equal(query, SplitQueryBoundQuery) { - f.t.Errorf("invalid SplitQuery.SplitQueryRequest.Query: got %v expected %v", - query, SplitQueryBoundQuery) - } - if !reflect.DeepEqual(splitColumns, SplitQuerySplitColumns) { - f.t.Errorf("invalid SplitQuery.SplitColumn: got %v expected %v", - splitColumns, SplitQuerySplitColumns) - } - if splitCount != SplitQuerySplitCount { - f.t.Errorf("invalid SplitQuery.SplitCount: got %v expected %v", - splitCount, SplitQuerySplitCount) - } - if numRowsPerQueryPart != SplitQueryNumRowsPerQueryPart { - f.t.Errorf("invalid SplitQuery.numRowsPerQueryPart: got %v expected %v", - numRowsPerQueryPart, SplitQueryNumRowsPerQueryPart) - } - if algorithm != SplitQueryAlgorithm { - f.t.Errorf("invalid SplitQuery.algorithm: got %v expected %v", - algorithm, SplitQueryAlgorithm) - } - return SplitQueryQuerySplitList, nil -} - // TestStreamHealthStreamHealthResponse is a test stream health response. var TestStreamHealthStreamHealthResponse = &querypb.StreamHealthResponse{ Target: &querypb.Target{ diff --git a/go/vt/vttablet/tabletconntest/tabletconntest.go b/go/vt/vttablet/tabletconntest/tabletconntest.go index c29cecca15f..2da1b5028ca 100644 --- a/go/vt/vttablet/tabletconntest/tabletconntest.go +++ b/go/vt/vttablet/tabletconntest/tabletconntest.go @@ -725,64 +725,6 @@ func testMessageAckPanics(t *testing.T, conn queryservice.QueryService, f *FakeQ }) } -func testSplitQuery(t *testing.T, conn queryservice.QueryService, f *FakeQueryService) { - t.Log("testSplitQuery") - ctx := context.Background() - ctx = callerid.NewContext(ctx, TestCallerID, TestVTGateCallerID) - qsl, err := conn.SplitQuery( - ctx, - TestTarget, - SplitQueryBoundQuery, - SplitQuerySplitColumns, - SplitQuerySplitCount, - SplitQueryNumRowsPerQueryPart, - SplitQueryAlgorithm, - ) - if err != nil { - t.Fatalf("SplitQuery failed: %v", err) - } - if !proto.Equal( - &querypb.SplitQueryResponse{Queries: qsl}, - &querypb.SplitQueryResponse{Queries: SplitQueryQuerySplitList}, - ) { - t.Errorf("Unexpected result from SplitQuery: got %v wanted %v", qsl, SplitQueryQuerySplitList) - } -} - -func testSplitQueryError(t *testing.T, conn queryservice.QueryService, f *FakeQueryService) { - t.Log("testSplitQueryError") - f.HasError = true - testErrorHelper(t, f, "SplitQuery", func(ctx context.Context) error { - _, err := conn.SplitQuery( - ctx, - TestTarget, - SplitQueryBoundQuery, - SplitQuerySplitColumns, - SplitQuerySplitCount, - SplitQueryNumRowsPerQueryPart, - SplitQueryAlgorithm, - ) - return err - }) - f.HasError = false -} - -func testSplitQueryPanics(t *testing.T, conn queryservice.QueryService, f *FakeQueryService) { - t.Log("testSplitQueryPanics") - testPanicHelper(t, f, "SplitQuery", func(ctx context.Context) error { - _, err := conn.SplitQuery( - ctx, - TestTarget, - SplitQueryBoundQuery, - SplitQuerySplitColumns, - SplitQuerySplitCount, - SplitQueryNumRowsPerQueryPart, - SplitQueryAlgorithm, - ) - return err - }) -} - // this test is a bit of a hack: we write something on the channel // upon registration, and we also return an error, so the streaming query // ends right there. Otherwise we have no real way to trigger a real @@ -943,7 +885,6 @@ func TestSuite(t *testing.T, protocol string, tablet *topodatapb.Tablet, fake *F testBeginExecuteBatch, testMessageStream, testMessageAck, - testSplitQuery, testUpdateStream, // error test cases @@ -967,7 +908,6 @@ func TestSuite(t *testing.T, protocol string, tablet *topodatapb.Tablet, fake *F testBeginExecuteBatchErrorInExecuteBatch, testMessageStreamError, testMessageAckError, - testSplitQueryError, testUpdateStreamError, // panic test cases @@ -989,7 +929,6 @@ func TestSuite(t *testing.T, protocol string, tablet *topodatapb.Tablet, fake *F testBeginExecuteBatchPanics, testMessageStreamPanics, testMessageAckPanics, - testSplitQueryPanics, testUpdateStreamPanics, } diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 027544ecad8..782cda8a8c6 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -1995,7 +1995,6 @@ func getQueryExecutorSupportedQueries(testTableHasMultipleUniqueKeys bool) map[s mysql.DescribeTableRow("addr", "int(11)", false, "", "0"), }, }, - // for SplitQuery because it needs a primary key column "show index from test_table": { Fields: mysql.ShowIndexFromTableFields, RowsAffected: 2, diff --git a/go/vt/vttablet/tabletserver/schema/schematest/schematest.go b/go/vt/vttablet/tabletserver/schema/schematest/schematest.go index 7ba71c07336..b31c429bd95 100644 --- a/go/vt/vttablet/tabletserver/schema/schematest/schematest.go +++ b/go/vt/vttablet/tabletserver/schema/schematest/schematest.go @@ -127,7 +127,6 @@ func Queries() map[string]*sqltypes.Result { mysql.DescribeTableRow("pk", "int(11)", false, "PRI", "0"), }, }, - // for SplitQuery because it needs a primary key column "show index from test_table_01": { Fields: mysql.ShowIndexFromTableFields, RowsAffected: 1, diff --git a/go/vt/vttablet/tabletserver/splitquery/doc.go b/go/vt/vttablet/tabletserver/splitquery/doc.go deleted file mode 100644 index 25a0ac2f1cf..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/doc.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package splitquery contains the logic needed for implementing the tabletserver's SplitQuery RPC. -// -// It defines the Splitter type that drives the query splitting procedure. It cooperates with the -// SplitParams type and splitAlgorithmInterface interface. See example_test.go for a usage example. -// -// General guidelines for contributing to this package: -// 1) Error messages should not contain the "splitquery:" prefix. It will be added by the calling -// code in 'tabletserver'. -package splitquery diff --git a/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm.go b/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm.go deleted file mode 100644 index 2e4e4fc22f1..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm.go +++ /dev/null @@ -1,281 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - "math/big" - "strconv" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" -) - -// EqualSplitsAlgorithm implements the SplitAlgorithmInterface and represents the equal-splits -// algorithm for generating the boundary tuples. If this algorithm is used then -// SplitParams.split_columns must contain only one split_column. Additionally, the split_column -// must have numeric type (integral or floating point). -// -// The algorithm works by issuing a query to the database to find the minimum and maximum -// elements of the split column in the table referenced by the given SQL query. Denote these -// by min and max, respectively. The algorithm then "splits" the interval [min, max] into -// SplitParams.split_count sub-intervals of equal length: -// [a_1, a_2], [a_2, a_3],..., [a_{split_count}, a_{split_count+1}], -// where min=a_1 < a_2 < a_3 < ... < a_split_count < a_{split_count+1}=max. -// The boundary points returned by this algorithm are then: a_2, a_3, ..., a_{split_count} -// (an empty list of boundary points is returned if split_count <= 1). If the type of the -// split column is integral, the boundary points are truncated to the integer part. -type EqualSplitsAlgorithm struct { - splitParams *SplitParams - sqlExecuter SQLExecuter - - minMaxQuery string -} - -// NewEqualSplitsAlgorithm constructs a new equal splits algorithm. -// It requires an SQLExecuter since it needs to execute a query to figure out the -// minimum and maximum elements in the table. -func NewEqualSplitsAlgorithm(splitParams *SplitParams, sqlExecuter SQLExecuter) ( - *EqualSplitsAlgorithm, error) { - - if len(splitParams.splitColumns) == 0 { - panic(fmt.Sprintf("len(splitParams.splitColumns) == 0." + - " SplitParams should have defaulted the split columns to the primary key columns.")) - } - // This algorithm only uses the first splitColumn. - // Note that we do not force the user to specify only one split column, since a common - // use-case is not to specify split columns at all, which will make them default to the table - // primary key columns, and there can be more than one primary key column for a table. - if !sqltypes.IsFloat(splitParams.splitColumns[0].Type) && - !sqltypes.IsIntegral(splitParams.splitColumns[0].Type) { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "using the EQUAL_SPLITS algorithm in SplitQuery requires having"+ - " a numeric (integral or float) split-column. Got type: %v", splitParams.splitColumns[0]) - } - if splitParams.splitCount <= 0 { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "using the EQUAL_SPLITS algorithm in SplitQuery requires a positive"+ - " splitParams.splitCount. Got: %v", splitParams.splitCount) - } - result := &EqualSplitsAlgorithm{ - splitParams: splitParams, - sqlExecuter: sqlExecuter, - - minMaxQuery: buildMinMaxQuery(splitParams), - } - return result, nil -} - -// getSplitColumns is part of the SplitAlgorithmInterface interface -func (a *EqualSplitsAlgorithm) getSplitColumns() []*schema.TableColumn { - return a.splitParams.splitColumns[0:1] -} - -func (a *EqualSplitsAlgorithm) generateBoundaries() ([]tuple, error) { - // generateBoundaries should work for a split_column whose type is integral - // (both signed and unsigned) as well as for floating point values. - // We perform the calculation of the boundaries using precise big.Rat arithmetic and only - // truncate the result in the end if necessary. - // We do this since using float64 arithmetic does not have enough precision: - // for example, if max=math.MaxUint64 and min=math.MaxUint64-1000 then float64(min)==float64(max). - // On the other hand, using integer arithmetic for the case where the split_column is integral - // (i.e., rounding (max-min)/split_count to an integer) may cause very dissimilar interval - // lengths or a large deviation between split_count and the number of query-parts actually - // returned (consider min=0, max=9.5*10^6, and split_count=10^6). - // Note(erez): We can probably get away with using big.Float with ~64 bits of precision which - // will likely be more efficient. However, we defer optimizing this code until we see if this - // is a bottle-neck. - minValue, maxValue, err := a.executeMinMaxQuery() - if err != nil { - return nil, err - } - // If the table is empty, minValue and maxValue will be NULL. - if (minValue.IsNull() && !maxValue.IsNull()) || - !minValue.IsNull() && maxValue.IsNull() { - panic(fmt.Sprintf("minValue and maxValue must both be NULL or both be non-NULL."+ - " minValue: %v, maxValue: %v, splitParams.sql: %v", - minValue, maxValue, a.splitParams.sql)) - } - if minValue.IsNull() { - log.Infof("Splitting an empty table. splitParams.sql: %v. Query will not be split.", - a.splitParams.sql) - return []tuple{}, nil - } - min, err := valueToBigRat(minValue, a.splitParams.splitColumns[0].Type) - if err != nil { - panic(fmt.Sprintf("Failed to convert min to a big.Rat: %v, min: %+v", err, min)) - } - max, err := valueToBigRat(maxValue, a.splitParams.splitColumns[0].Type) - if err != nil { - panic(fmt.Sprintf("Failed to convert max to a big.Rat: %v, max: %+v", err, max)) - } - minCmpMax := min.Cmp(max) - if minCmpMax > 0 { - panic(fmt.Sprintf("max(splitColumn) < min(splitColumn): max:%v, min:%v", max, min)) - } - if minCmpMax == 0 { - log.Infof("max(%v)=min(%v)=%v. splitParams.sql: %v. Query will not be split.", - a.splitParams.splitColumns[0].Name, - a.splitParams.splitColumns[0].Name, - min, - a.splitParams.sql) - return []tuple{}, nil - } - - // subIntervalSize = (max - min) / splitCount - maxMinDiff := new(big.Rat) - maxMinDiff.Sub(max, min) - subIntervalSize := new(big.Rat) - subIntervalSize.Quo(maxMinDiff, new(big.Rat).SetInt64(a.splitParams.splitCount)) - // If the split-column type is integral then it's wasteful to have a sub-intervale-size smaller - // than 1, as it'll result with some query-parts being trivially empty. We set the - // sub-interval size to 1 in this case. - one := new(big.Rat).SetInt64(1) - if sqltypes.IsIntegral(a.splitParams.splitColumns[0].Type) && - subIntervalSize.Cmp(one) < 0 { - subIntervalSize = one - } - boundary := new(big.Rat).Add(min, subIntervalSize) - result := []tuple{} - for ; boundary.Cmp(max) < 0; boundary.Add(boundary, subIntervalSize) { - boundaryValue := bigRatToValue(boundary, a.splitParams.splitColumns[0].Type) - result = append(result, tuple{boundaryValue}) - } - return result, nil -} - -func (a *EqualSplitsAlgorithm) executeMinMaxQuery() (minValue, maxValue sqltypes.Value, err error) { - sqlResults, err := a.sqlExecuter.SQLExecute(a.minMaxQuery, nil /* Bind Variables */) - if err != nil { - return sqltypes.Value{}, sqltypes.Value{}, err - } - if len(sqlResults.Rows) != 1 { - panic(fmt.Sprintf("MinMaxQuery should return exactly 1 row from query. MinMaxQuery: %v"+ - " Results: %v", a.minMaxQuery, sqlResults)) - } - if len(sqlResults.Rows[0]) != 2 { - panic(fmt.Sprintf("MinMaxQuery should return exactly 2 columns. MinMaxQuery: %v, Results:%v", - a.minMaxQuery, sqlResults)) - } - return sqlResults.Rows[0][0], sqlResults.Rows[0][1], nil -} - -// buildMinMaxQuery returns the query to execute to get the minimum and maximum of the splitColumn. -// The query returned is: -// SELECT MIN(), MAX() FROM
; -// where
is the table referenced in the original query (held in splitParams.sql). -func buildMinMaxQuery(splitParams *SplitParams) string { - // The SplitParams constructor should have already checked that the FROM clause of the query - // is a simple table expression, so this type-assertion should succeed. - tableName := sqlparser.GetTableName( - splitParams.selectAST.From[0].(*sqlparser.AliasedTableExpr).Expr) - if tableName.IsEmpty() { - panic(fmt.Sprintf("Can't get tableName from query %v", splitParams.sql)) - } - return fmt.Sprintf("select min(%v), max(%v) from %v", - sqlparser.String(splitParams.splitColumns[0].Name), - sqlparser.String(splitParams.splitColumns[0].Name), - sqlparser.String(tableName)) -} - -// bigRatToValue converts 'number' to an SQL value with SQL type: valueType. -// If valueType is integral it truncates 'number' to the integer part according to the -// semantics of the big.Rat.Int method. -func bigRatToValue(number *big.Rat, valueType querypb.Type) sqltypes.Value { - var numberAsBytes []byte - switch { - case sqltypes.IsIntegral(valueType): - // 'number.Num()' returns a reference to the numerator of 'number'. - // We copy it here to avoid changing 'number'. - truncatedNumber := new(big.Int).Set(number.Num()) - truncatedNumber.Quo(truncatedNumber, number.Denom()) - numberAsBytes = bigIntToSliceOfBytes(truncatedNumber) - case sqltypes.IsFloat(valueType): - // Truncate to the closest 'float'. - // There's not much we can do if there isn't an exact representation. - numberAsFloat64, _ := number.Float64() - numberAsBytes = strconv.AppendFloat([]byte{}, numberAsFloat64, 'f', -1, 64) - default: - panic(fmt.Sprintf("Unsupported type: %v", valueType)) - } - result, err := sqltypes.NewValue(valueType, numberAsBytes) - if err != nil { - panic(fmt.Sprintf("sqltypes.ValueFromBytes failed with: %v", err)) - } - return result -} - -// Converts a big.Int into a slice of bytes. -func bigIntToSliceOfBytes(bigInt *big.Int) []byte { - // Go1.6 introduced the method bigInt.Append() which makes this conversion - // a lot easier. - // TODO(erez): Use bigInt.Append() once we switch to GO-1.6. - result := strconv.AppendQuoteToASCII([]byte{}, bigInt.String()) - // AppendQuoteToASCII adds a double-quoted string. We need to remove them. - return result[1 : len(result)-1] -} - -// valueToBigRat converts a numeric 'value' regarded as having type 'valueType' into a -// big.Rat object. -// Note: -// We use an explicit valueType rather than depend on the type stored in 'value' to force -// the type of MAX(column) or MIN(column) to correspond to the type of 'column'. -// (We've had issues where the type of MAX(column) returned by Vitess was signed even if the -// type of column was unsigned). -func valueToBigRat(value sqltypes.Value, valueType querypb.Type) (*big.Rat, error) { - switch { - case sqltypes.IsUnsigned(valueType): - nativeValue, err := sqltypes.ToUint64(value) - if err != nil { - return nil, err - } - return uint64ToBigRat(nativeValue), nil - case sqltypes.IsSigned(valueType): - nativeValue, err := sqltypes.ToInt64(value) - if err != nil { - return nil, err - } - return int64ToBigRat(nativeValue), nil - case sqltypes.IsFloat(valueType): - nativeValue, err := sqltypes.ToFloat64(value) - if err != nil { - return nil, err - } - return float64ToBigRat(nativeValue), nil - default: - panic(fmt.Sprintf("got value with a non numeric type: %v", value)) - } -} - -func int64ToBigRat(value int64) *big.Rat { - return new(big.Rat).SetInt64(value) -} - -func uint64ToBigRat(value uint64) *big.Rat { - // big.Rat does not have a 'setUint64()' so we have to use an intermediate 'big.Int'. - return new(big.Rat).SetInt(big.NewInt(0).SetUint64(value)) -} - -func float64ToBigRat(value float64) *big.Rat { - return new(big.Rat).SetFloat64(value) -} diff --git a/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm_test.go b/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm_test.go deleted file mode 100644 index 94c6dca74f1..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/equal_splits_algorithm_test.go +++ /dev/null @@ -1,200 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - "reflect" - "testing" - - "github.com/golang/mock/gomock" - - "vitess.io/vitess/go/sqltypes" - querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/splitquery/splitquery_testing" -) - -// Table-driven test for equal-splits algorithm. -// Fields are exported so that "%v" would print them using their String() method. -type equalSplitsAlgorithmTestCaseType struct { - SplitColumn string - SplitCount int64 - MinValue sqltypes.Value - MaxValue sqltypes.Value - ExpectedBoundaries []tuple -} - -var equalSplitsAlgorithmTestCases = []equalSplitsAlgorithmTestCaseType{ - { // Split the interval [10, 60] into 5 parts. - SplitColumn: "int64_col", - SplitCount: 5, - MinValue: sqltypes.NewInt64(10), - MaxValue: sqltypes.NewInt64(60), - ExpectedBoundaries: []tuple{ - {sqltypes.NewInt64(20)}, - {sqltypes.NewInt64(30)}, - {sqltypes.NewInt64(40)}, - {sqltypes.NewInt64(50)}, - }, - }, - { // Split the interval [10, 60] into 4 parts. - SplitColumn: "int64_col", - SplitCount: 4, - MinValue: sqltypes.NewInt64(10), - MaxValue: sqltypes.NewInt64(60), - ExpectedBoundaries: []tuple{ - {sqltypes.NewInt64(22)}, - {sqltypes.NewInt64(35)}, - {sqltypes.NewInt64(47)}, - }, - }, - { // Split the interval [-30, 60] into 4 parts. - SplitColumn: "int64_col", - SplitCount: 4, - MinValue: sqltypes.NewInt64(-30), - MaxValue: sqltypes.NewInt64(60), - ExpectedBoundaries: []tuple{ - {sqltypes.NewInt64(-7)}, - {sqltypes.NewInt64(15)}, - {sqltypes.NewInt64(37)}, - }, - }, - { // Split the interval [18446744073709551610,18446744073709551615] into 4 parts. - SplitColumn: "uint64_col", - SplitCount: 4, - MinValue: sqltypes.NewUint64(18446744073709551610), - MaxValue: sqltypes.NewUint64(18446744073709551615), - ExpectedBoundaries: []tuple{ - {sqltypes.NewUint64(18446744073709551611)}, - {sqltypes.NewUint64(18446744073709551612)}, - {sqltypes.NewUint64(18446744073709551613)}, - }, - }, - { // Split the interval [0,95] into 10 parts. - SplitColumn: "uint64_col", - SplitCount: 10, - MinValue: sqltypes.NewUint64(0), - MaxValue: sqltypes.NewUint64(95), - ExpectedBoundaries: []tuple{ - {sqltypes.NewUint64(9)}, - {sqltypes.NewUint64(19)}, - {sqltypes.NewUint64(28)}, - {sqltypes.NewUint64(38)}, - {sqltypes.NewUint64(47)}, - {sqltypes.NewUint64(57)}, - {sqltypes.NewUint64(66)}, - {sqltypes.NewUint64(76)}, - {sqltypes.NewUint64(85)}, - }, - }, - { // Split the interval [-30.25, 60.25] into 4 parts. - SplitColumn: "float64_col", - SplitCount: 4, - MinValue: sqltypes.NewFloat64(-30.25), - MaxValue: sqltypes.NewFloat64(60.25), - ExpectedBoundaries: []tuple{ - {sqltypes.NewFloat64(-7.625)}, - {sqltypes.NewFloat64(15)}, - {sqltypes.NewFloat64(37.625)}, - }, - }, - { // Split the interval [-30, -30] into 4 parts. - // (should return an empty boundary list). - SplitColumn: "int64_col", - SplitCount: 4, - MinValue: sqltypes.NewInt64(-30), - MaxValue: sqltypes.NewInt64(-30), - ExpectedBoundaries: []tuple{}, - }, - { // Split the interval [-30, -29] into 4 parts. - SplitColumn: "int64_col", - SplitCount: 4, - MinValue: sqltypes.NewInt64(-30), - MaxValue: sqltypes.NewInt64(-29), - ExpectedBoundaries: []tuple{}, - }, - { // Split the interval [-30, -28] into 4 parts. - SplitColumn: "int64_col", - SplitCount: 4, - MinValue: sqltypes.NewInt64(-30), - MaxValue: sqltypes.NewInt64(-28), - ExpectedBoundaries: []tuple{{sqltypes.NewInt64(-29)}}, - }, - { // Split the interval [-30, -28] into 4 parts with a floating-point split-column. - SplitColumn: "float64_col", - SplitCount: 4, - MinValue: sqltypes.NewFloat64(-30), - MaxValue: sqltypes.NewFloat64(-28), - ExpectedBoundaries: []tuple{ - {sqltypes.NewFloat64(-29.5)}, - {sqltypes.NewFloat64(-29.0)}, - {sqltypes.NewFloat64(-28.5)}, - }, - }, -} - -func TestEqualSplitsAlgorithm(t *testing.T) { - // singleTest is a function that executes a single-test. - singleTest := func(testCase *equalSplitsAlgorithmTestCaseType) { - splitParams, err := NewSplitParamsGivenSplitCount( - &querypb.BoundQuery{Sql: "select * from test_table where int_col > 5"}, - []sqlparser.ColIdent{sqlparser.NewColIdent(testCase.SplitColumn)}, - testCase.SplitCount, - getTestSchema(), - ) - if err != nil { - t.Errorf("NewSplitParamsWithNumRowsPerQueryPart failed with: %v", err) - return - } - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - mockSQLExecuter := splitquery_testing.NewMockSQLExecuter(mockCtrl) - expectedCall1 := mockSQLExecuter.EXPECT().SQLExecute( - fmt.Sprintf( - "select min(%v), max(%v) from test_table", - testCase.SplitColumn, testCase.SplitColumn), - nil /* Bind Variables */) - expectedCall1.Return( - &sqltypes.Result{ - Rows: [][]sqltypes.Value{ - {testCase.MinValue, testCase.MaxValue}, - }, - }, - nil) - algorithm, err := NewEqualSplitsAlgorithm(splitParams, mockSQLExecuter) - if err != nil { - t.Errorf("NewEqualSplitsAlgorithm() failed with: %v", err) - return - } - boundaries, err := algorithm.generateBoundaries() - if err != nil { - t.Errorf("EqualSplitsAlgorithm.generateBoundaries() failed with: %v", err) - return - } - if !reflect.DeepEqual(boundaries, testCase.ExpectedBoundaries) { - t.Errorf("EqualSplitsAlgorith.generateBoundaries()=%+v, expected: %+v. testCase: %+v", - boundaries, testCase.ExpectedBoundaries, testCase) - } - } // singleTest() - for _, testCase := range equalSplitsAlgorithmTestCases { - singleTest(&testCase) - } -} - -//TODO(erez): Add test that checks we return an error if column is not numeric (and maybe also -// for other assumptions). diff --git a/go/vt/vttablet/tabletserver/splitquery/example_test.go b/go/vt/vttablet/tabletserver/splitquery/example_test.go deleted file mode 100644 index 64638d87cd2..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/example_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -func Example() { - // 1. Create a SplitParams object. - // There are two "constructors": NewSplitParamsGivenSplitCount and - // NewSplitParamsGivenNumRowsPerQueryPart. They each take several parameters including a "schema" - // object which should be a map[string]*schema.Table that maps a table name to its schema.Table - // object. It is used for error-checking the split columns and their types. We use an empty - // object for this toy example, but in real code this object must have correct entries. - // - // This schema can is typically derived from tabletserver.TabletServer.qe.se. - schema := map[string]*schema.Table{} - splitParams, err := NewSplitParamsGivenSplitCount( - &querypb.BoundQuery{ - Sql: "SELECT * FROM table WHERE id > :id", - BindVariables: map[string]*querypb.BindVariable{"id": sqltypes.Int64BindVariable(5)}, - }, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, // SplitColumns - 1000, // SplitCount - schema) - if err != nil { - panic(fmt.Sprintf("NewSplitParamsGivenSplitCount failed with: %v", err)) - } - - // 2. Create the SplitAlgorithmInterface object used for splitting. - // SplitQuery supports multiple algorithms for splitting the query. These are encapsulated as - // types implementing the SplitAlgorithmInterface. Currently two algorithms are supported - // represented by the FullScanAlgorithm and EqualSplitsAlgorithm types. See the documentation - // of these types for more details on each algorithm. - // To do the split we'll need to create an object of one of these types and pass it to the - // Splitter (see below). Here we use the FullScan algorithm. - // We also pass a type implementing the SQLExecuter interface that the algorithm will - // use to send statements to MySQL. - algorithm, err := NewFullScanAlgorithm(splitParams, getSQLExecuter()) - if err != nil { - panic(fmt.Sprintf("NewFullScanAlgorithm failed with: %v", err)) - } - - // 3. Create a splitter object. Always succeeds. - splitter := NewSplitter(splitParams, algorithm) - - // 4. Call splitter.Split() to Split the query. - // The result is a slice of &querypb.QuerySplit objects (and an error object). - queryParts, err := splitter.Split() - if err != nil { - panic(fmt.Sprintf("splitter.Split() failed with: %v", err)) - } - fmt.Println(queryParts) -} - -func getSQLExecuter() SQLExecuter { - // In real code, this should be an object implementing the SQLExecuter interface. - return nil -} diff --git a/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go deleted file mode 100644 index d81762d4a21..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm.go +++ /dev/null @@ -1,260 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" -) - -// FullScanAlgorithm implements the SplitAlgorithmInterface and represents the full-scan algorithm -// for generating the boundary tuples. The algorithm regards the table as ordered (ascendingly) by -// the split columns. It then returns boundary tuples from rows which are -// splitParams.numRowsPerQueryPart rows apart. More precisely, it works as follows: -// It iteratively executes the following query over the replica’s database (recall that MySQL -// performs tuple comparisons lexicographically): -// SELECT FROM
FORCE INDEX (PRIMARY) -// WHERE :prev_boundary <= () -// ORDER BY -// LIMIT , 1 -// where denotes the ordered list of split columns and
is the -// value of the FROM clause. -// The 'prev_boundary' bind variable holds a tuple consisting of split column values. -// It is updated after each iteration with the result of the query. In the query executed in the -// first iteration (the initial query) the term ':prev_boundary <= ()' is -// omitted. -// The algorithm stops when the query returns no results. The result of this algorithm is the list -// consisting of the result of each query in order. -// -// Actually, the code below differs slightly from the above description: the lexicographial tuple -// inequality in the query above is re-written to use only scalar comparisons since MySQL -// does not optimize queries involving tuple inequalities correctly. Instead of using a single -// tuple bind variable: 'prev_boundary', the code uses a list of scalar bind-variables--one for each -// element of the tuple. The bind variable storing the tuple element corresponding to a -// split-column named 'x' is called , where prevBindVariablePrefix is -// the string constant defined below. -type FullScanAlgorithm struct { - splitParams *SplitParams - sqlExecuter SQLExecuter - - prevBindVariableNames []string - initialQuery *querypb.BoundQuery - noninitialQuery *querypb.BoundQuery -} - -// NewFullScanAlgorithm constructs a new FullScanAlgorithm. -func NewFullScanAlgorithm( - splitParams *SplitParams, sqlExecuter SQLExecuter) (*FullScanAlgorithm, error) { - - if !splitParams.areSplitColumnsPrimaryKey() { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "Using the FULL_SCAN algorithm requires split columns to be"+ - " the primary key. Got: %+v", splitParams) - } - result := &FullScanAlgorithm{ - splitParams: splitParams, - sqlExecuter: sqlExecuter, - prevBindVariableNames: buildPrevBindVariableNames(splitParams.splitColumns), - } - result.initialQuery = buildInitialQuery(splitParams) - result.noninitialQuery = buildNoninitialQuery(splitParams, result.prevBindVariableNames) - return result, nil -} - -// getSplitColumns is part of the SplitAlgorithmInterface interface -func (a *FullScanAlgorithm) getSplitColumns() []*schema.TableColumn { - return a.splitParams.splitColumns -} - -func (a *FullScanAlgorithm) generateBoundaries() ([]tuple, error) { - prevTuple, err := a.executeQuery(a.initialQuery) - if err != nil { - return nil, err - } - result := make([]tuple, 0, a.splitParams.splitCount) - var iteration int64 - // 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++ { - result = append(result, prevTuple) - a.populatePrevTupleInBindVariables(prevTuple, a.noninitialQuery.BindVariables) - prevTuple, err = a.executeQuery(a.noninitialQuery) - if err != nil { - return nil, err - } - } - return result, nil -} - -func (a *FullScanAlgorithm) populatePrevTupleInBindVariables( - prevTuple tuple, bindVariables map[string]*querypb.BindVariable) { - if len(prevTuple) != len(a.prevBindVariableNames) { - panic(fmt.Sprintf("len(prevTuple) != len(a.prevBindVariableNames): %v != %v", - len(prevTuple), len(a.prevBindVariableNames))) - } - for i, tupleElement := range prevTuple { - bindVariables[a.prevBindVariableNames[i]] = sqltypes.ValueBindVariable(tupleElement) - } -} - -// buildInitialQuery returns the initial query to execute to get the -// initial boundary tuple. -// If the query to split (given in splitParams.sql) is -// "SELECT
WHERE ", -// the Sql field of the result will be: -// "SELECT sc_1,sc_2,...,sc_n FROM
-// FORCE INDEX (PRIMARY) -// ORDER BY -// LIMIT splitParams.numRowsPerQueryPart, 1", -// The BindVariables field of the result will contain a deep-copy of splitParams.BindVariables. -func buildInitialQuery(splitParams *SplitParams) *querypb.BoundQuery { - resultSelectAST := buildInitialQueryAST(splitParams) - return &querypb.BoundQuery{ - Sql: sqlparser.String(resultSelectAST), - BindVariables: cloneBindVariables(splitParams.bindVariables), - } -} - -func buildInitialQueryAST(splitParams *SplitParams) *sqlparser.Select { - return &sqlparser.Select{ - SelectExprs: convertColumnsToSelectExprs(splitParams.splitColumns), - // For the scanning here, we override any specified index hint to be the - // PRIMARY index. If we do not override, even if the user doesn't specify a hint, MySQL - // sometimes decides to use a different index than the primary key which results with a - // significant increase in running time. - // Note that we do not override the index for the actual query part since the list of - // columns selected there is different; so overriding it there may hurt performance. - From: buildFromClause(splitParams.GetSplitTableName()), - Limit: buildLimitClause(splitParams.numRowsPerQueryPart, 1), - OrderBy: buildOrderByClause(splitParams.splitColumns), - } -} - -// buildNonInitialQuery returns the non-initial query to execute to get the -// noninitial boundary tuples. -// If the query to split (given in splitParams.sql) is -// "SELECT
WHERE ", -// the Sql field of the result will be: -// "SELECT sc_1,sc_2,...,sc_n FROM
FORCE INDEX (PRIMARY) -// WHERE :prev_sc_1,...,:prev_sc_n) <= (sc_1,...,sc_n) -// ORDER BY -// LIMIT splitParams.numRowsPerQueryPart, 1", -// where sc_1,...,sc_n are the split columns, -// and :prev_sc_1,...,:_prev_sc_n are the bind variable names for the previous tuple. -// The BindVariables field of the result will contain a deep-copy of splitParams.BindVariables. -// The new "prev_" bind variables are not populated yet. -func buildNoninitialQuery( - splitParams *SplitParams, prevBindVariableNames []string) *querypb.BoundQuery { - resultSelectAST := buildInitialQueryAST(splitParams) - addAndTermToWhereClause( - resultSelectAST, - constructTupleInequality( - convertBindVariableNamesToExpr(prevBindVariableNames), - convertColumnsToExpr(splitParams.splitColumns), - false /* strict */)) - return &querypb.BoundQuery{ - Sql: sqlparser.String(resultSelectAST), - BindVariables: cloneBindVariables(splitParams.bindVariables), - } -} - -func convertColumnsToSelectExprs(columns []*schema.TableColumn) sqlparser.SelectExprs { - result := make([]sqlparser.SelectExpr, 0, len(columns)) - for _, column := range columns { - result = append(result, - &sqlparser.AliasedExpr{ - Expr: &sqlparser.ColName{ - Name: column.Name, - }, - }) - } - return result -} - -func buildFromClause(splitTableName sqlparser.TableIdent) sqlparser.TableExprs { - return sqlparser.TableExprs{ - &sqlparser.AliasedTableExpr{ - Expr: sqlparser.TableName{Name: splitTableName}, - Hints: &sqlparser.IndexHints{ - Type: sqlparser.ForceStr, - Indexes: []sqlparser.ColIdent{sqlparser.NewColIdent("PRIMARY")}, - }, - }, - } -} - -func buildLimitClause(offset, rowcount int64) *sqlparser.Limit { - return &sqlparser.Limit{ - Offset: sqlparser.NewIntVal([]byte(fmt.Sprintf("%d", offset))), - Rowcount: sqlparser.NewIntVal([]byte(fmt.Sprintf("%d", rowcount))), - } -} - -func buildOrderByClause(splitColumns []*schema.TableColumn) sqlparser.OrderBy { - result := make(sqlparser.OrderBy, 0, len(splitColumns)) - for _, splitColumn := range splitColumns { - result = append(result, - &sqlparser.Order{ - Expr: &sqlparser.ColName{Name: splitColumn.Name}, - Direction: sqlparser.AscScr, - }, - ) - } - return result -} - -const ( - prevBindVariablePrefix string = "_splitquery_prev_" -) - -func buildPrevBindVariableNames(splitColumns []*schema.TableColumn) []string { - result := make([]string, 0, len(splitColumns)) - for _, splitColumn := range splitColumns { - result = append(result, prevBindVariablePrefix+splitColumn.Name.CompliantName()) - } - return result -} - -func (a *FullScanAlgorithm) executeQuery(boundQuery *querypb.BoundQuery) (tuple, error) { - sqlResult, err := a.sqlExecuter.SQLExecute( - boundQuery.Sql, - boundQuery.BindVariables) - if err != nil { - return nil, err - } - if len(sqlResult.Rows) == 0 { - return nil, nil - } - if len(sqlResult.Rows) == 1 { - if len(sqlResult.Rows[0]) != len(a.splitParams.splitColumns) { - panic(fmt.Sprintf("splitquery.executeQuery: expected a tuple of length %v."+ - " Got tuple: %v", len(a.splitParams.splitColumns), sqlResult.Rows[0])) - } - return sqlResult.Rows[0], nil - } - panic(fmt.Sprintf("splitquery.executeQuery: got more than 1 row from query: %v. result: %v", - *boundQuery, sqlResult)) -} diff --git a/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm_test.go b/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm_test.go deleted file mode 100644 index f5a9393e941..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/full_scan_algorithm_test.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - "reflect" - "testing" - - "github.com/golang/mock/gomock" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/splitquery/splitquery_testing" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -func TestMultipleBoundaries(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{Sql: "select * from test_table where int_col > 5"}, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, /* splitColumns */ - 1000, - getTestSchema(), - ) - if err != nil { - t.Fatalf("NewSplitParamsGivenNumRowsPerQueryPart failed with: %v", err) - } - mockSQLExecuter := splitquery_testing.NewMockSQLExecuter(mockCtrl) - expectedCall1 := mockSQLExecuter.EXPECT().SQLExecute( - "select id, user_id from test_table force index (`PRIMARY`)"+ - " order by id asc, user_id asc"+ - " limit 1000, 1", - map[string]*querypb.BindVariable{}) - expectedCall1.Return( - &sqltypes.Result{ - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt64(1), sqltypes.NewInt64(1)}}, - }, - nil) - expectedCall2 := mockSQLExecuter.EXPECT().SQLExecute( - "select id, user_id from test_table force index (`PRIMARY`)"+ - " where"+ - " :_splitquery_prev_id < id or"+ - " (:_splitquery_prev_id = id and :_splitquery_prev_user_id <= user_id)"+ - " order by id asc, user_id asc"+ - " limit 1000, 1", - map[string]*querypb.BindVariable{ - "_splitquery_prev_id": sqltypes.Int64BindVariable(1), - "_splitquery_prev_user_id": sqltypes.Int64BindVariable(1), - }) - expectedCall2.Return( - &sqltypes.Result{ - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt64(2), sqltypes.NewInt64(10)}}, - }, - nil) - expectedCall2.After(expectedCall1) - expectedCall3 := mockSQLExecuter.EXPECT().SQLExecute( - "select id, user_id from test_table force index (`PRIMARY`)"+ - " where"+ - " :_splitquery_prev_id < id or"+ - " (:_splitquery_prev_id = id and :_splitquery_prev_user_id <= user_id)"+ - " order by id asc, user_id asc"+ - " limit 1000, 1", - map[string]*querypb.BindVariable{ - "_splitquery_prev_id": sqltypes.Int64BindVariable(2), - "_splitquery_prev_user_id": sqltypes.Int64BindVariable(10), - }) - expectedCall3.Return( - &sqltypes.Result{Rows: [][]sqltypes.Value{}}, nil) - expectedCall3.After(expectedCall2) - - algorithm, err := NewFullScanAlgorithm(splitParams, mockSQLExecuter) - if err != nil { - t.Fatalf("NewFullScanAlgorithm failed with: %v", err) - } - boundaries, err := algorithm.generateBoundaries() - if err != nil { - t.Fatalf("FullScanAlgorithm.generateBoundaries() failed with: %v", err) - } - expectedBoundaries := []tuple{ - {sqltypes.NewInt64(1), sqltypes.NewInt64(1)}, - {sqltypes.NewInt64(2), sqltypes.NewInt64(10)}, - } - if !reflect.DeepEqual(expectedBoundaries, boundaries) { - t.Fatalf("expected: %v, got: %v", expectedBoundaries, boundaries) - } -} - -func TestSmallNumberOfRows(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{Sql: "select * from test_table where int_col > 5"}, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, /* splitColumns */ - 1000, - getTestSchema(), - ) - if err != nil { - t.Fatalf("NewSplitParamsGivenNumRowsPerQueryPart failed with: %v", err) - } - mockSQLExecuter := splitquery_testing.NewMockSQLExecuter(mockCtrl) - expectedCall1 := mockSQLExecuter.EXPECT().SQLExecute( - "select id, user_id from test_table force index (`PRIMARY`)"+ - " order by id asc, user_id asc"+ - " limit 1000, 1", - map[string]*querypb.BindVariable{}) - expectedCall1.Return( - &sqltypes.Result{Rows: [][]sqltypes.Value{}}, nil) - - algorithm, err := NewFullScanAlgorithm(splitParams, mockSQLExecuter) - if err != nil { - t.Fatalf("NewFullScanAlgorithm failed with: %v", err) - } - boundaries, err := algorithm.generateBoundaries() - if err != nil { - t.Fatalf("FullScanAlgorithm.generateBoundaries() failed with: %v", err) - } - expectedBoundaries := []tuple{} - if !reflect.DeepEqual(expectedBoundaries, boundaries) { - t.Fatalf("expected: %v, got: %v", expectedBoundaries, boundaries) - } -} - -func TestSQLExecuterReturnsError(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{Sql: "select * from test_table where int_col > 5"}, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, /* splitColumns */ - 1000, - getTestSchema(), - ) - if err != nil { - t.Fatalf("NewSplitParamsGivenNumRowsPerQueryPart failed with: %v", err) - } - mockSQLExecuter := splitquery_testing.NewMockSQLExecuter(mockCtrl) - expectedCall1 := mockSQLExecuter.EXPECT().SQLExecute( - "select id, user_id from test_table force index (`PRIMARY`)"+ - " order by id asc, user_id asc"+ - " limit 1000, 1", - map[string]*querypb.BindVariable{}) - expectedCall1.Return( - &sqltypes.Result{ - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt64(1), sqltypes.NewInt64(1)}}, - }, - nil) - expectedCall2 := mockSQLExecuter.EXPECT().SQLExecute( - "select id, user_id from test_table force index (`PRIMARY`)"+ - " where"+ - " :_splitquery_prev_id < id or"+ - " (:_splitquery_prev_id = id and :_splitquery_prev_user_id <= user_id)"+ - " order by id asc, user_id asc"+ - " limit 1000, 1", - map[string]*querypb.BindVariable{ - "_splitquery_prev_id": sqltypes.Int64BindVariable(1), - "_splitquery_prev_user_id": sqltypes.Int64BindVariable(1), - }) - expectedErr := fmt.Errorf("Error accessing database!") - expectedCall2.Return(nil, expectedErr) - algorithm, err := NewFullScanAlgorithm(splitParams, mockSQLExecuter) - if err != nil { - t.Fatalf("NewFullScanAlgorithm failed with: %v", err) - } - boundaries, err := algorithm.generateBoundaries() - if err != expectedErr { - t.Fatalf("FullScanAlgorithm.generateBoundaries() did not fail as expected. err: %v", err) - } - if boundaries != nil { - t.Fatalf("boundaries: %v, expected: nil", boundaries) - } -} diff --git a/go/vt/vttablet/tabletserver/splitquery/split_algorithm_interface.go b/go/vt/vttablet/tabletserver/splitquery/split_algorithm_interface.go deleted file mode 100644 index b97cb2c5eb7..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/split_algorithm_interface.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" -) - -type tuple []sqltypes.Value - -// SplitAlgorithmInterface defines the interface for a splitting algorithm. -type SplitAlgorithmInterface interface { - // generateBoundaries() method should return a list of "boundary tuples". - // Each tuple is expected to contain values of the split columns, in the order these are returned - // in getSplitColumns(), at a specific "boundary point". The returned list should be - // ordered using ascending lexicographical order. - // If the resulting list of boundary tuples is: {t1, t2, ..., t_k}, the - // splitquery.Splitter.Split() method would generate k+1 query parts. - // For i=0,1,...,k, the ith query-part contains the rows whose tuple of split-column values 't' - // satisfies t_i < t <= t+1, where the comparison is performed lexicographically, and t_0 and - // t_k+1 are taken to be a "-infinity" tuple and a "+infinity" tuple, respectively. - generateBoundaries() ([]tuple, error) - - // getSplitColumns() should return the list of split-columns used by the algorithm. - getSplitColumns() []*schema.TableColumn -} diff --git a/go/vt/vttablet/tabletserver/splitquery/split_params.go b/go/vt/vttablet/tabletserver/splitquery/split_params.go deleted file mode 100644 index bcd062364c5..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/split_params.go +++ /dev/null @@ -1,284 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" -) - -// SplitParams stores the context for a splitquery computation. It is used by -// both the Splitter object and the SplitAlgorithm object and caches some data that is used by -// both. -type SplitParams struct { - // The following fields are directly given by the caller -- they have a corresponding - // parameter in each constructor. - sql string - bindVariables map[string]*querypb.BindVariable - - // Exactly one of splitCount, numRowsPerQueryPart will be given by the caller. - // See the two NewSplitParams... constructors below. The other field member - // will be computed using the equation: max(1, floor(numTableRows / x)), - // where numTableRows is the approximate number of rows in the table (referenced in 'sql') taken - // from the information schema of the database, and 'x' is the given field member. - splitCount int64 - numRowsPerQueryPart int64 - - // The fields below will be computed by the appropriate constructor. - - splitColumns []*schema.TableColumn - // The schema of the table referenced in the SELECT query given in 'sql'. - splitTableSchema *schema.Table - // The AST for the SELECT query given in 'sql'. - selectAST *sqlparser.Select -} - -// NewSplitParamsGivenNumRowsPerQueryPart returns a new SplitParams object to be used in -// a splitquery request in which the Vitess client specified a numRowsPerQueryPart parameter. -// See NewSplitParamsGivenSplitCount for the constructor to use if the Vitess client specified -// a splitCount parameter. -// -// Parameters: -// -// 'sql' is the SQL query to split. The query must satisfy the restrictions found in the -// documentation of the vtgate.SplitQueryRequest.query protocol buffer field. -// -// 'bindVariables' are the bind-variables for the sql query. -// -// 'splitColumnNames' should contain the names of split columns to use. These must adhere to the -// restrictions found in the documentation of the vtgate.SplitQueryRequest.split_column protocol -// buffer field. If splitColumnNames is empty, the split columns used are the primary key columns -// (in order). -// -// 'numRowsPerQueryPart' is the desired number of rows per query part returned. The actual number -// may be different depending on the split-algorithm used. -// -// 'schema' should map a table name to a schema.Table. It is used for looking up the split-column -// types and error checking. -func NewSplitParamsGivenNumRowsPerQueryPart( - query *querypb.BoundQuery, - splitColumnNames []sqlparser.ColIdent, - numRowsPerQueryPart int64, - schema map[string]*schema.Table, -) (*SplitParams, error) { - if numRowsPerQueryPart <= 0 { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "numRowsPerQueryPart must be positive. Got: %v", numRowsPerQueryPart) - } - result, err := newSplitParams(query, splitColumnNames, schema) - if err != nil { - return nil, err - } - result.numRowsPerQueryPart = numRowsPerQueryPart - result.splitCount = int64Max(1, result.splitTableSchema.TableRows.Get()/numRowsPerQueryPart) - return result, nil -} - -// NewSplitParamsGivenSplitCount returns a new SplitParams object to be used in -// a splitquery request in which the Vitess client specified a splitCount parameter. -// See NewSplitParamsGivenNumRowsPerQueryPart for the constructor to use if the Vitess client -// specified a numRowsPerQueryPart parameter. -// -// Parameters: -// -// 'sql' is the SQL query to split. The query must satisfy the restrictions found in the -// documentation of the vtgate.SplitQueryRequest.query protocol buffer field. -// -// 'bindVariables' are the bind-variables for the sql query. -// -// 'splitColumnNames' should contain the names of split columns to use. These must adhere to the -// restrictions found in the documentation of the vtgate.SplitQueryRequest.split_column protocol -// buffer field. If splitColumnNames is empty, the split columns used are the primary key columns -// (in order). -// -// 'splitCount' is the desired splitCount to use. The actual number may be different depending on -// the split-algorithm used. -// -// 'schema' should map a table name to a schema.Table. It is used for looking up the split-column -// types and error checking. -func NewSplitParamsGivenSplitCount( - query *querypb.BoundQuery, - splitColumnNames []sqlparser.ColIdent, - splitCount int64, - schema map[string]*schema.Table, -) (*SplitParams, error) { - if splitCount <= 0 { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "splitCount must be positive. Got: %v", splitCount) - } - result, err := newSplitParams(query, splitColumnNames, schema) - if err != nil { - return nil, err - } - result.splitCount = splitCount - result.numRowsPerQueryPart = int64Max(1, result.splitTableSchema.TableRows.Get()/splitCount) - return result, nil -} - -// GetSplitTableName returns the name of the table to split. -func (sp *SplitParams) GetSplitTableName() sqlparser.TableIdent { - return sp.splitTableSchema.Name -} - -// newSplitParams validates and initializes all the fields except splitCount and -// numRowsPerQueryPart. It contains the common code for the constructors above. -func newSplitParams( - query *querypb.BoundQuery, - splitColumnNames []sqlparser.ColIdent, - schemaMap map[string]*schema.Table, -) (*SplitParams, error) { - statement, err := sqlparser.Parse(query.Sql) - if err != nil { - return nil, vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, "failed parsing query: '%v', err: '%v'", query.Sql, err) - } - selectAST, ok := statement.(*sqlparser.Select) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "not a select statement") - } - if selectAST.Distinct != "" || selectAST.GroupBy != nil || - selectAST.Having != nil || len(selectAST.From) != 1 || - selectAST.OrderBy != nil || selectAST.Limit != nil || - selectAST.Lock != "" { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported query: %v", query.Sql) - } - var aliasedTableExpr *sqlparser.AliasedTableExpr - aliasedTableExpr, ok = selectAST.From[0].(*sqlparser.AliasedTableExpr) - if !ok { - return nil, vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, "unsupported FROM clause in query: %v", query.Sql) - } - tableName := sqlparser.GetTableName(aliasedTableExpr.Expr) - if tableName.IsEmpty() { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported FROM clause in query"+ - " (must be a simple table expression): %v", query.Sql) - } - tableSchema, ok := schemaMap[tableName.String()] - if !ok || tableSchema == nil { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "can't find table in schema") - } - - // Get the schema.TableColumn representation of each splitColumnName. - var splitColumns []*schema.TableColumn - if len(splitColumnNames) == 0 { - splitColumns = getPrimaryKeyColumns(tableSchema) - if len(splitColumns) == 0 { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, - "no split columns where given and the queried table has"+ - " no primary key columns (is the table a view? Running SplitQuery on a view"+ - " is not supported). query: %v", query.Sql) - } - } else { - splitColumns, err = findSplitColumnsInSchema(splitColumnNames, tableSchema) - if err != nil { - return nil, err - } - if !areColumnsAPrefixOfAnIndex(splitColumns, tableSchema) { - return nil, vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, - "split-columns must be a prefix of the columns composing"+ - " an index. Sql: %v, split-columns: %v", query.Sql, splitColumns) - } - } - - if len(splitColumns) == 0 { - panic(fmt.Sprintf( - "Empty set of split columns. splitColumns: %+v, tableSchema: %+v", - splitColumns, tableSchema)) - } - - return &SplitParams{ - sql: query.Sql, - bindVariables: query.BindVariables, - splitColumns: splitColumns, - selectAST: selectAST, - splitTableSchema: tableSchema, - }, nil -} - -func findSplitColumnsInSchema( - splitColumnNames []sqlparser.ColIdent, tableSchema *schema.Table, -) ([]*schema.TableColumn, error) { - result := make([]*schema.TableColumn, 0, len(splitColumnNames)) - for _, splitColumnName := range splitColumnNames { - i := tableSchema.FindColumn(splitColumnName) - if i == -1 { - return nil, vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, - "can't find split column: %v", splitColumnName) - } - result = append(result, &tableSchema.Columns[i]) - } - return result, nil -} - -// getPrimaryKeyColumns returns the list of primary-key column names, in order, for the -// given table. -func getPrimaryKeyColumns(table *schema.Table) []*schema.TableColumn { - result := make([]*schema.TableColumn, 0, len(table.PKColumns)) - for _, pkColIndex := range table.PKColumns { - result = append(result, &table.Columns[pkColIndex]) - } - return result -} - -// areColumnsAPrefixOfAnIndex returns true if 'columns' form a prefix of the columns that -// make up some index in 'table'. -func areColumnsAPrefixOfAnIndex(columns []*schema.TableColumn, table *schema.Table) bool { - for _, index := range table.Indexes { - if areColumnsAPrefixOfIndex(columns, index) { - return true - } - } - return false -} - -// areColumnsAPrefixOfIndex returns true if 'potentialPrefix' forms a prefix of the columns -// composing 'index'. -func areColumnsAPrefixOfIndex(potentialPrefix []*schema.TableColumn, index *schema.Index) bool { - if len(potentialPrefix) > len(index.Columns) { - return false - } - for i := range potentialPrefix { - if !potentialPrefix[i].Name.Equal(index.Columns[i]) { - return false - } - } - return true -} - -// areSplitColumnsPrimaryKey returns true if the splitColumns in 'splitParams' -// are the primary key columns in order. -func (sp *SplitParams) areSplitColumnsPrimaryKey() bool { - pkCols := getPrimaryKeyColumns(sp.splitTableSchema) - if len(sp.splitColumns) != len(pkCols) { - return false - } - // Compare the names of sp.splitColumns to the names of pkCols. - for i := 0; i < len(sp.splitColumns); i++ { - if !sp.splitColumns[i].Name.Equal(pkCols[i].Name) { - return false - } - } - return true -} diff --git a/go/vt/vttablet/tabletserver/splitquery/split_params_test.go b/go/vt/vttablet/tabletserver/splitquery/split_params_test.go deleted file mode 100644 index b5400128177..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/split_params_test.go +++ /dev/null @@ -1,262 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "reflect" - "regexp" - "testing" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -var splitParamsTestCases = []struct { - SQL string - BindVariables map[string]*querypb.BindVariable - SplitColumnNames []sqlparser.ColIdent - NumRowsPerQueryPart int64 - SplitCount int64 - Schema map[string]*schema.Table - - ExpectedErrorRegex *regexp.Regexp - ExpectedSplitParams SplitParams -}{ - { // Test NewSplitParamsGivenSplitCount; correct input. - SQL: "select id from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - SplitCount: 100, - Schema: getTestSchema(), - - ExpectedSplitParams: SplitParams{ - splitCount: 100, - numRowsPerQueryPart: 10, // TableRows of 'test_table' should be 1000 - splitColumns: []*schema.TableColumn{getTestSchemaColumn("test_table", "id")}, - splitTableSchema: testSchema["test_table"], - }, - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; correct input. - SQL: "select user_id from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedSplitParams: SplitParams{ - splitCount: 10, - numRowsPerQueryPart: 100, // TableRows of 'test_table' should be 1000 - splitColumns: []*schema.TableColumn{getTestSchemaColumn("test_table", "id")}, - splitTableSchema: testSchema["test_table"], - }, - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; correct input; default split columns - SQL: "select user_id from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedSplitParams: SplitParams{ - splitCount: 10, - numRowsPerQueryPart: 100, // TableRows of 'test_table' should be 1000 - splitColumns: []*schema.TableColumn{ - getTestSchemaColumn("test_table", "id"), - getTestSchemaColumn("test_table", "user_id"), - }, - splitTableSchema: testSchema["test_table"], - }, - }, - - { // Test NewSplitParamsGivenNumRowsPerQueryPart; invalid query. - SQL: "not a valid query", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("failed parsing query: 'not a valid query'"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; not a select statement. - SQL: "delete from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("not a select statement"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unsupported select statement. - SQL: "select t1.user_id from test_table as t1 join test_table as t2", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("unsupported FROM clause"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unsupported select statement. - SQL: "select distinct user_id from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("unsupported query"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unsupported select statement. - SQL: "select user_id from test_table group by id", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("unsupported query"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unsupported select statement. - SQL: "select user_id from test_table having user_id > 5", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("unsupported query"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unsupported select statement. - SQL: "select user_id from test_table order by id asc", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("unsupported query"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unsupported select statement. - SQL: "select user_id from test_table lock in share mode", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("unsupported query"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unsupported select statement. - SQL: "select user_id from (select * from test_table) as t1", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("unsupported FROM clause"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unknown table. - SQL: "select user_id from missing_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("can't find table in schema"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; unknown split column. - SQL: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("missing_column")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile("can't find split column"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; split columns not a prefix of an index. - SQL: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{sqlparser.NewColIdent("float32_col")}, - NumRowsPerQueryPart: 100, - Schema: getTestSchema(), - - ExpectedErrorRegex: regexp.MustCompile( - "split-columns must be a prefix of the columns composing an index"), - }, - { // Test NewSplitParamsGivenNumRowsPerQueryPart; no split columns and no primary keys. - SQL: "select id from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.StringBindVariable("123")}, - SplitColumnNames: []sqlparser.ColIdent{}, - NumRowsPerQueryPart: 100, - Schema: func() map[string]*schema.Table { - // Returns the testSchema without the primary key columns. - result := getTestSchema() - result["test_table"].PKColumns = []int{} - return result - }(), - - ExpectedErrorRegex: regexp.MustCompile( - "no split columns where given and the queried table has no primary key columns"), - }, -} - -func TestSplitParams(t *testing.T) { - for _, testCase := range splitParamsTestCases { - var splitParams *SplitParams - var err error - if testCase.NumRowsPerQueryPart != 0 { - splitParams, err = NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{ - Sql: testCase.SQL, - BindVariables: testCase.BindVariables, - }, - testCase.SplitColumnNames, - testCase.NumRowsPerQueryPart, - testCase.Schema) - } else { - splitParams, err = NewSplitParamsGivenSplitCount( - &querypb.BoundQuery{ - Sql: testCase.SQL, - BindVariables: testCase.BindVariables, - }, - testCase.SplitColumnNames, - testCase.SplitCount, - testCase.Schema) - } - if testCase.ExpectedErrorRegex != nil { - if !testCase.ExpectedErrorRegex.MatchString(err.Error()) { - t.Errorf("Testcase: %+v, want: %+v, got: %+v", testCase, testCase.ExpectedErrorRegex, err) - } - continue - } - // Here, we don't expect an error. - if err != nil { - t.Errorf("TestCase: %+v, want: %+v, got: %+v", testCase, nil, err) - continue - } - if splitParams == nil { - t.Errorf("TestCase: %+v, got nil splitParams", testCase) - continue - } - expectedSplitParams := testCase.ExpectedSplitParams - // We don't require testCaset.ExpectedSplitParams to specify common expected fields like 'sql', - // so we compute them here and store them in 'expectedSplitParams'. - expectedSplitParams.sql = testCase.SQL - expectedSplitParams.bindVariables = testCase.BindVariables - statement, _ := sqlparser.Parse(testCase.SQL) - expectedSplitParams.selectAST = statement.(*sqlparser.Select) - if !reflect.DeepEqual(&expectedSplitParams, splitParams) { - t.Errorf("TestCase: %+v, want: %+v, got: %+v", testCase, expectedSplitParams, *splitParams) - } - } -} diff --git a/go/vt/vttablet/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go b/go/vt/vttablet/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go deleted file mode 100644 index 95fb7e9513e..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/splitquery_testing/mock_sqlexecuter.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: sql_executer_interface.go - -// Package splitquery_testing is a generated GoMock package. -package splitquery_testing - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - sqltypes "vitess.io/vitess/go/sqltypes" - query "vitess.io/vitess/go/vt/proto/query" -) - -// MockSQLExecuter is a mock of SQLExecuter interface -type MockSQLExecuter struct { - ctrl *gomock.Controller - recorder *MockSQLExecuterMockRecorder -} - -// MockSQLExecuterMockRecorder is the mock recorder for MockSQLExecuter -type MockSQLExecuterMockRecorder struct { - mock *MockSQLExecuter -} - -// NewMockSQLExecuter creates a new mock instance -func NewMockSQLExecuter(ctrl *gomock.Controller) *MockSQLExecuter { - mock := &MockSQLExecuter{ctrl: ctrl} - mock.recorder = &MockSQLExecuterMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockSQLExecuter) EXPECT() *MockSQLExecuterMockRecorder { - return m.recorder -} - -// SQLExecute mocks base method -func (m *MockSQLExecuter) SQLExecute(sql string, bindVariables map[string]*query.BindVariable) (*sqltypes.Result, error) { - ret := m.ctrl.Call(m, "SQLExecute", sql, bindVariables) - ret0, _ := ret[0].(*sqltypes.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SQLExecute indicates an expected call of SQLExecute -func (mr *MockSQLExecuterMockRecorder) SQLExecute(sql, bindVariables interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SQLExecute", reflect.TypeOf((*MockSQLExecuter)(nil).SQLExecute), sql, bindVariables) -} diff --git a/go/vt/vttablet/tabletserver/splitquery/splitter.go b/go/vt/vttablet/tabletserver/splitquery/splitter.go deleted file mode 100644 index ee516197a22..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/splitter.go +++ /dev/null @@ -1,259 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -// Splitter is used to drive the splitting procedure. -type Splitter struct { - algorithm SplitAlgorithmInterface - splitParams *SplitParams - - startBindVariableNames []string - endBindVariableNames []string - firstQueryPartSQL string - middleQueryPartSQL string - lastQueryPartSQL string -} - -// NewSplitter creates a new Splitter object. -func NewSplitter(splitParams *SplitParams, algorithm SplitAlgorithmInterface) *Splitter { - var splitter Splitter - splitter.splitParams = splitParams - splitter.algorithm = algorithm - splitter.splitParams = splitParams - - splitColumns := algorithm.getSplitColumns() - splitter.startBindVariableNames = make([]string, 0, len(splitColumns)) - splitter.endBindVariableNames = make([]string, 0, len(splitColumns)) - for _, splitColumn := range splitColumns { - splitter.startBindVariableNames = append( - splitter.startBindVariableNames, startBindVariablePrefix+splitColumn.Name.CompliantName()) - splitter.endBindVariableNames = append( - splitter.endBindVariableNames, endBindVariablePrefix+splitColumn.Name.CompliantName()) - } - splitter.initQueryPartSQLs() - return &splitter -} - -// Split does the actual work of splitting the query. -// It returns a slice of *querypb.QuerySplit objects representing -// the query parts. -func (splitter *Splitter) Split() ([]*querypb.QuerySplit, error) { - var boundaries []tuple - var err error - boundaries, err = splitter.algorithm.generateBoundaries() - if err != nil { - return nil, err - } - boundaries = append(boundaries, nil) - splits := []*querypb.QuerySplit{} - var start tuple - for _, end := range boundaries { - splits = append(splits, splitter.constructQueryPart(start, end)) - start = end - } - return splits, nil -} - -// initQueryPartSQLs initializes the firstQueryPartSQL, middleQueryPartSQL and lastQueryPartSQL -// fields. -func (splitter *Splitter) initQueryPartSQLs() { - splitColumns := convertColumnsToExpr(splitter.algorithm.getSplitColumns()) - startBindVariables := convertBindVariableNamesToExpr(splitter.startBindVariableNames) - endBindVariables := convertBindVariableNamesToExpr(splitter.endBindVariableNames) - splitColsLessThanEnd := constructTupleInequality( - splitColumns, - endBindVariables, - true /* strict */) - splitColsGreaterThanOrEqualToStart := constructTupleInequality( - startBindVariables, - splitColumns, - false /* not strict */) - - splitter.firstQueryPartSQL = sqlparser.String( - queryWithAdditionalWhere(splitter.splitParams.selectAST, splitColsLessThanEnd)) - splitter.middleQueryPartSQL = sqlparser.String( - queryWithAdditionalWhere(splitter.splitParams.selectAST, - &sqlparser.AndExpr{ - Left: &sqlparser.ParenExpr{Expr: splitColsGreaterThanOrEqualToStart}, - Right: &sqlparser.ParenExpr{Expr: splitColsLessThanEnd}, - })) - splitter.lastQueryPartSQL = sqlparser.String( - queryWithAdditionalWhere(splitter.splitParams.selectAST, splitColsGreaterThanOrEqualToStart)) -} - -func (splitter *Splitter) constructQueryPart(start, end tuple) *querypb.QuerySplit { - result := &querypb.QuerySplit{} - result.Query = &querypb.BoundQuery{ - BindVariables: cloneBindVariables(splitter.splitParams.bindVariables), - } - // TODO(erez): Fill result.RowCount - if start != nil { - populateBoundaryBindVariables( - start, splitter.startBindVariableNames, result.Query.BindVariables) - } - if end != nil { - populateBoundaryBindVariables( - end, splitter.endBindVariableNames, result.Query.BindVariables) - } - switch { - case start == nil && end == nil: - // If there's no upper or lower bound then just use the original query as the query part. - // This can happen if the boundaries list is empty. - result.Query.Sql = splitter.splitParams.sql - case start == nil && end != nil: - result.Query.Sql = splitter.firstQueryPartSQL - case start != nil && end != nil: - result.Query.Sql = splitter.middleQueryPartSQL - case start != nil && end == nil: - result.Query.Sql = splitter.lastQueryPartSQL - } - return result -} - -// -// Below are utility functions called by the Splitter methods above. -// - -// populateBoundaryBindVariables populates 'resultBindVariables' with new bind variables. -// The ith bind-variable has name bindVariableNames[i] and value inputTuple[i]. -// The function panics if a bind variable name already exists in 'resultBindVariables'. -func populateBoundaryBindVariables( - inputTuple tuple, bindVariableNames []string, resultBindVariables map[string]*querypb.BindVariable) { - if len(inputTuple) != len(bindVariableNames) { - panic(fmt.Sprintf("len(inputTuple) != len(bindVariableNames): %v != %v", - len(inputTuple), len(bindVariableNames))) - } - for i := range inputTuple { - populateNewBindVariable(bindVariableNames[i], sqltypes.ValueBindVariable(inputTuple[i]), resultBindVariables) - } -} - -func convertColumnsToExpr(columns []*schema.TableColumn) []sqlparser.Expr { - valExprs := make([]sqlparser.Expr, 0, len(columns)) - for _, column := range columns { - valExprs = append(valExprs, &sqlparser.ColName{Name: column.Name}) - } - return valExprs -} - -func convertBindVariableNamesToExpr(bindVariableNames []string) []sqlparser.Expr { - valExprs := make([]sqlparser.Expr, 0, len(bindVariableNames)) - for _, bindVariableName := range bindVariableNames { - valExprs = append(valExprs, sqlparser.NewValArg([]byte([]byte(":"+bindVariableName)))) - } - return valExprs -} - -// constructTupleInequality constructs a boolean expression representing a tuple lexicographical -// comparison using only scalar comparisons. -// -// MySQL does support tuple-inequalities ((a,b) <= (c,d) and interpretes them using the -// lexicographical ordering of tuples. However, it does not optimize queries with such inequalities -// well. Specifically, it does not recognize that a query with such inequalities, that involve only -// the columns of an index, can be done as an index scan. Rather, it resorts to a full-table scan. -// Thus, we convert such tuple inequalties to an expression involving only scalar inequalties. -// -// The expression returned by this function represents the lexicographical comparisons: -// lhsTuple <= rhsTuple, if strict is false, or lhsTuple < rhsTuple, otherwise. -// For example: if lhsTuple = (l1, l2) and rhsTuple = (r1, r2) then the returned expression is -// (l1 < r1) or ((l1 = r1) and (l2 <= r2)) if strict is false, -// and -// (l1 < r1) or ((l1 = r1) and (l2 < r2)), otherwise. -func constructTupleInequality( - lhsTuple []sqlparser.Expr, rhsTuple []sqlparser.Expr, strict bool) sqlparser.Expr { - if len(lhsTuple) != len(rhsTuple) { - panic(fmt.Sprintf("len(lhsTuple)!=len(rhsTuple): %v!=%v", len(lhsTuple), len(rhsTuple))) - } - if len(lhsTuple) == 0 { - panic("len(lhsTuple)==0") - } - // The actual work of this function is done in a "nested" function - // 'constructTupleInequalityUnchecked' which does not further check the lengths. - // It's a recursive function and so we must define the 'constructTupleInequalityUnchecked' - // variable beforehand. - var constructTupleInequalityUnchecked func( - lhsTuple []sqlparser.Expr, rhsTuple []sqlparser.Expr, strict bool) sqlparser.Expr - constructTupleInequalityUnchecked = func( - lhsTuple []sqlparser.Expr, rhsTuple []sqlparser.Expr, strict bool) sqlparser.Expr { - if len(lhsTuple) == 1 { - op := sqlparser.LessEqualStr - if strict { - op = sqlparser.LessThanStr - } - return &sqlparser.ComparisonExpr{ - Operator: op, - Left: lhsTuple[0], - Right: rhsTuple[0], - } - } - restOfTupleInequality := constructTupleInequalityUnchecked(lhsTuple[1:], rhsTuple[1:], strict) - if len(lhsTuple[1:]) > 1 { - // A non-scalar inequality needs to be parenthesized since we combine it below with - // other expressions. - restOfTupleInequality = &sqlparser.ParenExpr{ - Expr: restOfTupleInequality, - } - } - // Return: - // lhsTuple[0] < rhsTuple[0] OR - // ( lhsTuple[0] = rhsTuple[0] AND restOfTupleInequality) - return &sqlparser.OrExpr{ - Left: &sqlparser.ComparisonExpr{ - Operator: sqlparser.LessThanStr, - Left: lhsTuple[0], - Right: rhsTuple[0], - }, - Right: &sqlparser.ParenExpr{ - Expr: &sqlparser.AndExpr{ - Left: &sqlparser.ComparisonExpr{ - Operator: sqlparser.EqualStr, - Left: lhsTuple[0], - Right: rhsTuple[0], - }, - Right: restOfTupleInequality, - }, - }, - } - } - - return constructTupleInequalityUnchecked(lhsTuple, rhsTuple, strict) -} - -// queryWithAdditionalWhere returns a copy of the given SELECT query with 'addedWhere' ANDed with -// the query's WHERE clause. If the query does not already have a WHERE clause, 'addedWhere' -// becomes the query's WHERE clause. -func queryWithAdditionalWhere( - selectAST *sqlparser.Select, addedWhere sqlparser.Expr) *sqlparser.Select { - result := *selectAST // Create a shallow-copy of 'selectAST' - addAndTermToWhereClause(&result, addedWhere) - return &result -} - -const ( - startBindVariablePrefix = "_splitquery_start_" - endBindVariablePrefix = "_splitquery_end_" -) diff --git a/go/vt/vttablet/tabletserver/splitquery/splitter_test.go b/go/vt/vttablet/tabletserver/splitquery/splitter_test.go deleted file mode 100644 index f0b91bb6e92..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/splitter_test.go +++ /dev/null @@ -1,604 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "fmt" - "reflect" - "testing" - - "github.com/golang/mock/gomock" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - "vitess.io/vitess/go/vt/vttablet/tabletserver/splitquery/splitquery_testing" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -type FakeSplitAlgorithm struct { - boundaries []tuple - splitColumns []*schema.TableColumn -} - -func (a *FakeSplitAlgorithm) generateBoundaries() ([]tuple, error) { - return a.boundaries, nil -} -func (a *FakeSplitAlgorithm) getSplitColumns() []*schema.TableColumn { - return a.splitColumns -} - -func verifyQueryPartsEqual(t *testing.T, expected, got []*querypb.QuerySplit) { - if reflect.DeepEqual(expected, got) { - return - } - message := fmt.Sprintf("\nexpected: %v\ngot: %v\n", expected, got) - if len(expected) != len(got) { - message += fmt.Sprintf("len is different: expected: %v vs got:%v\n", len(expected), len(got)) - return - } - for i := range expected { - if expected[i].Query.Sql != got[i].Query.Sql { - message += fmt.Sprintf("expected[%v].Sql:\n%v\n!=\ngot[%v].Sql:\n%v\n", - i, expected[i].Query.Sql, i, got[i].Query.Sql) - } - if expected[i].RowCount != got[i].RowCount { - message += fmt.Sprintf("expected[%v].RowCount: %v != got[%v].RowCount: %v\n", - i, expected[i].RowCount, i, got[i].RowCount) - } - if !reflect.DeepEqual(expected[i].Query.BindVariables, got[i].Query.BindVariables) { - message += fmt.Sprintf("expected[%v].BindVariables:\n%v\n!=\ngot[%v].BindVariables:\n%v\n", - i, expected[i].Query.BindVariables, i, got[i].Query.BindVariables) - } - } - t.Errorf("%s", message) -} - -func TestSplit1SplitColumn(t *testing.T) { - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{ - Sql: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{}, - }, - []sqlparser.ColIdent{sqlparser.NewColIdent("id")}, - 1000, // numRowsPerQueryPart - getTestSchema()) - if err != nil { - t.Fatalf("SplitParams.Initialize() failed with: %v", err) - } - splitter := NewSplitter(splitParams, - &FakeSplitAlgorithm{ - boundaries: []tuple{ - {sqltypes.NewInt64(1)}, - {sqltypes.NewInt64(10)}, - {sqltypes.NewInt64(50)}, - }, - splitColumns: splitParams.splitColumns, - }) - var queryParts []*querypb.QuerySplit - queryParts, err = splitter.Split() - if err != nil { - t.Errorf("Splitter.Split() failed with: %v", err) - } - expected := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where id < :_splitquery_end_id", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id <= id)" + - " and" + - " (id < :_splitquery_end_id)", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_id": sqltypes.Int64BindVariable(10), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id <= id)" + - " and" + - " (id < :_splitquery_end_id)", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(10), - "_splitquery_end_id": sqltypes.Int64BindVariable(50), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " :_splitquery_start_id <= id", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(50), - }, - }, - }, - } - verifyQueryPartsEqual(t, expected, queryParts) -} - -func TestSplit2SplitColumns(t *testing.T) { - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{ - Sql: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{}, - }, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, /* splitColumns */ - 1000, // numRowsPerQueryPart - getTestSchema()) - if err != nil { - t.Fatalf("SplitParams.Initialize() failed with: %v", err) - } - splitter := NewSplitter(splitParams, - &FakeSplitAlgorithm{ - boundaries: []tuple{ - {sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, - {sqltypes.NewInt64(1), sqltypes.NewInt64(3)}, - {sqltypes.NewInt64(5), sqltypes.NewInt64(1)}, - }, - splitColumns: splitParams.splitColumns, - }) - var queryParts []*querypb.QuerySplit - queryParts, err = splitter.Split() - if err != nil { - t.Errorf("Splitter.Split() failed with: %v", err) - } - expected := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id)", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(2), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id))" + - " and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(2), - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(3), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id))" + - " and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(3), - "_splitquery_end_id": sqltypes.Int64BindVariable(5), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(1), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " :_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id)", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_user_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_id": sqltypes.Int64BindVariable(5), - }, - }, - }, - } - verifyQueryPartsEqual(t, expected, queryParts) -} - -func TestSplit3SplitColumns(t *testing.T) { - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{ - Sql: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{}, - }, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - sqlparser.NewColIdent("user_id2"), - }, /* splitColumns */ - 1000, // numRowsPerQueryPart - getTestSchema()) - if err != nil { - t.Fatalf("SplitParams.Initialize() failed with: %v", err) - } - splitter := NewSplitter(splitParams, - &FakeSplitAlgorithm{ - boundaries: []tuple{ - { - sqltypes.NewInt64(1), - sqltypes.NewInt64(2), - sqltypes.NewInt64(2), - }, - { - sqltypes.NewInt64(2), - sqltypes.NewInt64(1), - sqltypes.NewInt64(1), - }, - }, - splitColumns: splitParams.splitColumns, - }) - var queryParts []*querypb.QuerySplit - queryParts, err = splitter.Split() - if err != nil { - t.Errorf("Splitter.Split() failed with: %v", err) - } - expected := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and" + - " (user_id < :_splitquery_end_user_id or" + - " (user_id = :_splitquery_end_user_id and user_id2 < :_splitquery_end_user_id2)))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(2), - "_splitquery_end_user_id2": sqltypes.Int64BindVariable(2), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and" + - " (:_splitquery_start_user_id < user_id or" + - " (:_splitquery_start_user_id = user_id and :_splitquery_start_user_id2 <= user_id2))))" + - " and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and" + - " (user_id < :_splitquery_end_user_id or" + - " (user_id = :_splitquery_end_user_id and user_id2 < :_splitquery_end_user_id2))))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(2), - "_splitquery_start_user_id2": sqltypes.Int64BindVariable(2), - "_splitquery_end_id": sqltypes.Int64BindVariable(2), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id2": sqltypes.Int64BindVariable(1), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " :_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and" + - " (:_splitquery_start_user_id < user_id or" + - " (:_splitquery_start_user_id = user_id and :_splitquery_start_user_id2 <= user_id2)))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(2), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id2": sqltypes.Int64BindVariable(1), - }, - }, - }, - } - verifyQueryPartsEqual(t, expected, queryParts) -} - -func TestSplitWithWhereClause(t *testing.T) { - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{ - Sql: "select * from test_table where name!='foo'", - BindVariables: map[string]*querypb.BindVariable{}, - }, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, /* splitColumns */ - 1000, // numRowsPerQueryPart - getTestSchema()) - if err != nil { - t.Fatalf("SplitParams.Initialize() failed with: %v", err) - } - splitter := NewSplitter(splitParams, - &FakeSplitAlgorithm{ - boundaries: []tuple{ - {sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, - {sqltypes.NewInt64(1), sqltypes.NewInt64(3)}, - {sqltypes.NewInt64(5), sqltypes.NewInt64(1)}, - }, - splitColumns: splitParams.splitColumns, - }) - var queryParts []*querypb.QuerySplit - queryParts, err = splitter.Split() - if err != nil { - t.Errorf("Splitter.Split() failed with: %v", err) - } - expected := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where (name != 'foo') and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(2), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where (name != 'foo') and" + - " ((:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id))" + - " and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id)))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(2), - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(3), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where (name != 'foo') and" + - " ((:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id))" + - " and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id)))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(3), - "_splitquery_end_id": sqltypes.Int64BindVariable(5), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(1), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where (name != 'foo') and" + - " (:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id))", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_user_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_id": sqltypes.Int64BindVariable(5), - }, - }, - }, - } - verifyQueryPartsEqual(t, expected, queryParts) -} - -func TestSplitWithExistingBindVariables(t *testing.T) { - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{ - Sql: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.Int64BindVariable(100)}, - }, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, /* splitColumns */ - 1000, // numRowsPerQueryPart - getTestSchema()) - if err != nil { - t.Fatalf("SplitParams.Initialize() failed with: %v", err) - } - splitter := NewSplitter(splitParams, - &FakeSplitAlgorithm{ - boundaries: []tuple{ - {sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, - {sqltypes.NewInt64(1), sqltypes.NewInt64(3)}, - {sqltypes.NewInt64(5), sqltypes.NewInt64(1)}, - }, - splitColumns: splitParams.splitColumns, - }) - var queryParts []*querypb.QuerySplit - queryParts, err = splitter.Split() - if err != nil { - t.Errorf("Splitter.Split() failed with: %v", err) - } - expected := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id)", - BindVariables: map[string]*querypb.BindVariable{ - "foo": sqltypes.Int64BindVariable(100), - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(2), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id))" + - " and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id))", - BindVariables: map[string]*querypb.BindVariable{ - "foo": sqltypes.Int64BindVariable(100), - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(2), - "_splitquery_end_id": sqltypes.Int64BindVariable(1), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(3), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id))" + - " and" + - " (id < :_splitquery_end_id or" + - " (id = :_splitquery_end_id and user_id < :_splitquery_end_user_id))", - BindVariables: map[string]*querypb.BindVariable{ - "foo": sqltypes.Int64BindVariable(100), - "_splitquery_start_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(3), - "_splitquery_end_id": sqltypes.Int64BindVariable(5), - "_splitquery_end_user_id": sqltypes.Int64BindVariable(1), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " :_splitquery_start_id < id or" + - " (:_splitquery_start_id = id and :_splitquery_start_user_id <= user_id)", - BindVariables: map[string]*querypb.BindVariable{ - "foo": sqltypes.Int64BindVariable(100), - "_splitquery_start_user_id": sqltypes.Int64BindVariable(1), - "_splitquery_start_id": sqltypes.Int64BindVariable(5), - }, - }, - }, - } - verifyQueryPartsEqual(t, expected, queryParts) -} - -func TestSplitWithEmptyBoundaryList(t *testing.T) { - splitParams, err := NewSplitParamsGivenNumRowsPerQueryPart( - &querypb.BoundQuery{ - Sql: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{"foo": sqltypes.Int64BindVariable(100)}, - }, - []sqlparser.ColIdent{ - sqlparser.NewColIdent("id"), - sqlparser.NewColIdent("user_id"), - }, /* splitColumns */ - 1000, - getTestSchema()) - if err != nil { - t.Fatalf("SplitParams.Initialize() failed with: %v", err) - } - splitter := NewSplitter(splitParams, - &FakeSplitAlgorithm{ - boundaries: []tuple{}, - splitColumns: splitParams.splitColumns, - }) - var queryParts []*querypb.QuerySplit - queryParts, err = splitter.Split() - if err != nil { - t.Errorf("Splitter.Split() failed with: %v", err) - } - expected := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{ - "foo": sqltypes.Int64BindVariable(100), - }, - }, - }, - } - verifyQueryPartsEqual(t, expected, queryParts) -} - -func TestWithRealEqualSplits(t *testing.T) { - splitParams, err := NewSplitParamsGivenSplitCount( - &querypb.BoundQuery{ - Sql: "select * from test_table", - BindVariables: map[string]*querypb.BindVariable{}, - }, - []sqlparser.ColIdent{sqlparser.NewColIdent("id"), sqlparser.NewColIdent("user_id")}, - 3, /* split_count */ - getTestSchema()) - if err != nil { - t.Fatalf("want: nil, got: %v", err) - } - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - mockSQLExecuter := splitquery_testing.NewMockSQLExecuter(mockCtrl) - expectedCall1 := mockSQLExecuter.EXPECT().SQLExecute( - "select min(id), max(id) from test_table", - nil /* Bind Variables */) - expectedCall1.Return( - &sqltypes.Result{ - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt64(10), sqltypes.NewInt64(3010)}, - }, - }, - nil) - equalSplits, _ := NewEqualSplitsAlgorithm(splitParams, mockSQLExecuter) - splitter := NewSplitter(splitParams, equalSplits) - queryParts, err := splitter.Split() - if err != nil { - t.Errorf("Splitter.Split() failed with: %v", err) - } - expected := []*querypb.QuerySplit{ - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where id < :_splitquery_end_id", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_end_id": sqltypes.Int64BindVariable(1010), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " (:_splitquery_start_id <= id)" + - " and" + - " (id < :_splitquery_end_id)", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(1010), - "_splitquery_end_id": sqltypes.Int64BindVariable(2010), - }, - }, - }, - { - Query: &querypb.BoundQuery{ - Sql: "select * from test_table where" + - " :_splitquery_start_id <= id", - BindVariables: map[string]*querypb.BindVariable{ - "_splitquery_start_id": sqltypes.Int64BindVariable(2010), - }, - }, - }, - } - verifyQueryPartsEqual(t, expected, queryParts) -} diff --git a/go/vt/vttablet/tabletserver/splitquery/sql_executer_interface.go b/go/vt/vttablet/tabletserver/splitquery/sql_executer_interface.go deleted file mode 100644 index cc393fccb0b..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/sql_executer_interface.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -import ( - "vitess.io/vitess/go/sqltypes" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -// SQLExecuter enacpsulates access to the MySQL database for the this package. -type SQLExecuter interface { - SQLExecute(sql string, bindVariables map[string]*querypb.BindVariable) (*sqltypes.Result, error) -} - -// Command to generate a mock for this interface with mockgen. -//go:generate mockgen -source $GOFILE -destination splitquery_testing/mock_sqlexecuter.go -package splitquery_testing diff --git a/go/vt/vttablet/tabletserver/splitquery/testutils_test.go b/go/vt/vttablet/tabletserver/splitquery/testutils_test.go deleted file mode 100644 index 746bc18aa43..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/testutils_test.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -// This file contains utility routines for used in splitquery tests. - -import ( - "fmt" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" -) - -// getSchema returns a fake schema object that can be given to SplitParams -func getTestSchema() map[string]*schema.Table { - table := schema.Table{ - Name: sqlparser.NewTableIdent("test_table"), - } - zero := sqltypes.NewInt64(0) - table.AddColumn("id", sqltypes.Int64, zero, "") - table.AddColumn("int32_col", sqltypes.Int32, zero, "") - table.AddColumn("uint32_col", sqltypes.Uint32, zero, "") - table.AddColumn("int64_col", sqltypes.Int64, zero, "") - table.AddColumn("uint64_col", sqltypes.Uint64, zero, "") - table.AddColumn("float32_col", sqltypes.Float32, zero, "") - table.AddColumn("float64_col", sqltypes.Float64, zero, "") - table.AddColumn("user_id", sqltypes.Int64, zero, "") - table.AddColumn("user_id2", sqltypes.Int64, zero, "") - table.AddColumn("id2", sqltypes.Int64, zero, "") - table.AddColumn("count", sqltypes.Int64, zero, "") - table.PKColumns = []int{0, 7} - addIndexToTable(&table, "PRIMARY", true, "id", "user_id") - addIndexToTable(&table, "idx_id2", false, "id2") - addIndexToTable(&table, "idx_int64_col", false, "int64_col") - addIndexToTable(&table, "idx_uint64_col", false, "uint64_col") - addIndexToTable(&table, "idx_float64_col", false, "float64_col") - addIndexToTable(&table, "idx_id_user_id", false, "id", "user_id") - addIndexToTable(&table, "idx_id_user_id_user_id_2", false, "id", "user_id", "user_id2") - - table.SetMysqlStats( - sqltypes.NewInt64(1000), /* TableRows */ - sqltypes.NewInt64(100), /* DataLength */ - sqltypes.NewInt64(123), /* IndexLength */ - sqltypes.NewInt64(456), /* DataFree */ - sqltypes.NewInt64(457), /* MaxDataLength */ - ) - - result := make(map[string]*schema.Table) - result["test_table"] = &table - - tableNoPK := schema.Table{ - Name: sqlparser.NewTableIdent("test_table_no_pk"), - } - tableNoPK.AddColumn("id", sqltypes.Int64, zero, "") - tableNoPK.PKColumns = []int{} - result["test_table_no_pk"] = &tableNoPK - - return result -} - -var testSchema = getTestSchema() - -func getTestSchemaColumn(tableName, columnName string) *schema.TableColumn { - tableSchema := testSchema[tableName] - columnIndex := tableSchema.FindColumn(sqlparser.NewColIdent(columnName)) - if columnIndex < 0 { - panic(fmt.Sprintf( - "Can't find columnName: %v (tableName: %v) in test schema.", columnName, tableName)) - } - return &tableSchema.Columns[columnIndex] -} - -// addIndexToTable adds an index named 'indexName' to 'table' with the given 'indexCols'. -// It uses 12345 as the cardinality. -// It returns the new index. -func addIndexToTable(table *schema.Table, indexName string, unique bool, indexCols ...string) *schema.Index { - index := table.AddIndex(indexName, unique) - for _, indexCol := range indexCols { - index.AddColumn(indexCol, 12345) - } - return index -} diff --git a/go/vt/vttablet/tabletserver/splitquery/utils.go b/go/vt/vttablet/tabletserver/splitquery/utils.go deleted file mode 100644 index a626fab59a9..00000000000 --- a/go/vt/vttablet/tabletserver/splitquery/utils.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package splitquery - -// utils.go contains general utility functions used in the splitquery package. - -import ( - "fmt" - - "vitess.io/vitess/go/vt/sqlparser" - - querypb "vitess.io/vitess/go/vt/proto/query" -) - -// populateNewBindVariable inserts 'bindVariableName' with 'bindVariableValue' to the -// 'resultBindVariables' map. Panics if 'bindVariableName' already exists in the map. -func populateNewBindVariable( - bindVariableName string, - bindVariableValue *querypb.BindVariable, - resultBindVariables map[string]*querypb.BindVariable) { - _, alreadyInMap := resultBindVariables[bindVariableName] - if alreadyInMap { - panic(fmt.Sprintf( - "bindVariable %v already exists in map: %v. bindVariableValue given: %v", - bindVariableName, - resultBindVariables, - bindVariableValue)) - } - resultBindVariables[bindVariableName] = bindVariableValue -} - -// cloneBindVariables returns a shallow-copy of the given bindVariables map. -func cloneBindVariables(bindVariables map[string]*querypb.BindVariable) map[string]*querypb.BindVariable { - result := make(map[string]*querypb.BindVariable) - for key, value := range bindVariables { - result[key] = value - } - return result -} - -// addAndTermToWhereClause replaces the WHERE clause of the query given in 'selectAST' with -// a new WHERE clause consisting of the boolean expression in the original 'WHERE' clause with -// the additional 'andTerm' ANDed to it. -// Note that it does not modify the original 'Where' clause of 'selectAST' (so it does not affect -// other objects holding a pointer to the original 'Where' clause). -// If 'selectAST' does not have a WHERE clause, this function will add a new WHERE clause -// consisting of 'andTerm' alone. -func addAndTermToWhereClause(selectAST *sqlparser.Select, andTerm sqlparser.Expr) { - if selectAST.Where == nil || selectAST.Where.Expr == nil { - selectAST.Where = sqlparser.NewWhere(sqlparser.WhereStr, andTerm) - } else { - selectAST.Where = sqlparser.NewWhere(sqlparser.WhereStr, - &sqlparser.AndExpr{ - Left: &sqlparser.ParenExpr{Expr: selectAST.Where.Expr}, - Right: &sqlparser.ParenExpr{Expr: andTerm}, - }, - ) - } -} - -// int64Max computes the max of two int64 values. -func int64Max(a, b int64) int64 { - if b > a { - return b - } - return a -} diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index a7b62f19879..d54f0b70acd 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -56,12 +56,10 @@ import ( "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/heartbeat" "vitess.io/vitess/go/vt/vttablet/queryservice" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/messager" "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" "vitess.io/vitess/go/vt/vttablet/tabletserver/rules" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - "vitess.io/vitess/go/vt/vttablet/tabletserver/splitquery" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/txserializer" "vitess.io/vitess/go/vt/vttablet/tabletserver/txthrottler" @@ -1402,69 +1400,6 @@ func (tsv *TabletServer) VStreamResults(ctx context.Context, target *querypb.Tar return tsv.vstreamer.StreamResults(ctx, query, send) } -// SplitQuery splits a query + bind variables into smaller queries that return a -// subset of rows from the original query. This is the new version that supports multiple -// split columns and multiple split algorithms. -// See the documentation of SplitQueryRequest in proto/vtgate.proto for more details. -func (tsv *TabletServer) SplitQuery( - ctx context.Context, - target *querypb.Target, - query *querypb.BoundQuery, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, -) (splits []*querypb.QuerySplit, err error) { - err = tsv.execRequest( - ctx, 0, - "SplitQuery", query.Sql, query.BindVariables, - target, nil, false /* isBegin */, false, /* allowOnShutdown */ - func(ctx context.Context, logStats *tabletenv.LogStats) error { - // SplitQuery using the Full Scan algorithm can take a while and - // we don't expect too many of these queries to run concurrently. - ciSplitColumns := make([]sqlparser.ColIdent, 0, len(splitColumns)) - for _, s := range splitColumns { - ciSplitColumns = append(ciSplitColumns, sqlparser.NewColIdent(s)) - } - - if err := validateSplitQueryParameters( - target, - query, - splitCount, - numRowsPerQueryPart, - algorithm, - ); err != nil { - return err - } - schema := tsv.se.GetSchema() - splitParams, err := createSplitParams( - query, ciSplitColumns, splitCount, numRowsPerQueryPart, schema) - if err != nil { - return err - } - defer func(start time.Time) { - splitTableName := splitParams.GetSplitTableName() - tabletenv.RecordUserQuery(ctx, splitTableName, "SplitQuery", int64(time.Since(start))) - }(time.Now()) - sqlExecuter, err := newSplitQuerySQLExecuter(ctx, logStats, tsv) - if err != nil { - return err - } - defer sqlExecuter.done() - algorithmObject, err := createSplitQueryAlgorithmObject(algorithm, splitParams, sqlExecuter) - if err != nil { - return err - } - splits, err = splitquery.NewSplitter(splitParams, algorithmObject).Split() - if err != nil { - return err - } - return nil - }, - ) - return splits, err -} - // execRequest performs verifications, sets up the necessary environments // and calls the supplied function for executing the request. func (tsv *TabletServer) execRequest( @@ -1739,152 +1674,6 @@ func convertErrorCode(err error) vtrpcpb.Code { return errCode } -// validateSplitQueryParameters perform some validations on the SplitQuery parameters -// returns an error that can be returned to the user if a validation fails. -func validateSplitQueryParameters( - target *querypb.Target, - query *querypb.BoundQuery, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, -) error { - // Check that the caller requested a RDONLY tablet. - // Since we're called by VTGate this should not normally be violated. - if target.TabletType != topodatapb.TabletType_RDONLY { - return vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, - "SplitQuery must be called with a RDONLY tablet. TableType passed is: %v", - target.TabletType) - } - if numRowsPerQueryPart < 0 { - return vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, - "splitQuery: numRowsPerQueryPart must be non-negative. Got: %v. SQL: %v", - numRowsPerQueryPart, - queryAsString(query.Sql, query.BindVariables)) - } - if splitCount < 0 { - return vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, - "splitQuery: splitCount must be non-negative. Got: %v. SQL: %v", - splitCount, - queryAsString(query.Sql, query.BindVariables)) - } - if (splitCount == 0 && numRowsPerQueryPart == 0) || - (splitCount != 0 && numRowsPerQueryPart != 0) { - return vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, - "splitQuery: exactly one of {numRowsPerQueryPart, splitCount} must be"+ - " non zero. Got: numRowsPerQueryPart=%v, splitCount=%v. SQL: %v", - numRowsPerQueryPart, - splitCount, - queryAsString(query.Sql, query.BindVariables)) - } - if algorithm != querypb.SplitQueryRequest_EQUAL_SPLITS && - algorithm != querypb.SplitQueryRequest_FULL_SCAN { - return vterrors.Errorf( - vtrpcpb.Code_INVALID_ARGUMENT, - "splitquery: unsupported algorithm: %v. SQL: %v", - algorithm, - queryAsString(query.Sql, query.BindVariables)) - } - return nil -} - -func createSplitParams( - query *querypb.BoundQuery, - splitColumns []sqlparser.ColIdent, - splitCount int64, - numRowsPerQueryPart int64, - schema map[string]*schema.Table, -) (*splitquery.SplitParams, error) { - switch { - case numRowsPerQueryPart != 0 && splitCount == 0: - return splitquery.NewSplitParamsGivenNumRowsPerQueryPart( - query, splitColumns, numRowsPerQueryPart, schema) - case numRowsPerQueryPart == 0 && splitCount != 0: - return splitquery.NewSplitParamsGivenSplitCount( - query, splitColumns, splitCount, schema) - default: - panic(fmt.Errorf("exactly one of {numRowsPerQueryPart, splitCount} must be"+ - " non zero. This should have already been caught by 'validateSplitQueryParameters' and "+ - " returned as an error. Got: numRowsPerQueryPart=%v, splitCount=%v. SQL: %v", - numRowsPerQueryPart, - splitCount, - queryAsString(query.Sql, query.BindVariables))) - } -} - -// splitQuerySQLExecuter implements splitquery.SQLExecuterInterface and allows the splitquery -// package to send SQL statements to MySQL -type splitQuerySQLExecuter struct { - queryExecutor *QueryExecutor - conn *connpool.DBConn -} - -// Constructs a new splitQuerySQLExecuter object. The 'done' method must be called on -// the object after it's no longer used, to recycle the database connection. -func newSplitQuerySQLExecuter( - ctx context.Context, logStats *tabletenv.LogStats, tsv *TabletServer, -) (*splitQuerySQLExecuter, error) { - queryExecutor := &QueryExecutor{ - ctx: ctx, - logStats: logStats, - tsv: tsv, - } - result := &splitQuerySQLExecuter{ - queryExecutor: queryExecutor, - } - var err error - result.conn, err = queryExecutor.getConn() - if err != nil { - return nil, err - } - return result, nil -} - -func (se *splitQuerySQLExecuter) done() { - se.conn.Recycle() -} - -// SQLExecute is part of the SQLExecuter interface. -func (se *splitQuerySQLExecuter) SQLExecute( - sql string, bindVariables map[string]*querypb.BindVariable, -) (*sqltypes.Result, error) { - // We need to parse the query since we're dealing with bind-vars. - // TODO(erez): Add an SQLExecute() to SQLExecuterInterface that gets a parsed query so that - // we don't have to parse the query again here. - ast, err := sqlparser.Parse(sql) - if err != nil { - return nil, vterrors.Wrap(err, "splitQuerySQLExecuter: parsing sql failed with") - } - parsedQuery := sqlparser.NewParsedQuery(ast) - - // We clone "bindVariables" since fullFetch() changes it. - return se.queryExecutor.dbConnFetch( - se.conn, - parsedQuery, - sqltypes.CopyBindVariables(bindVariables), - "", /* buildStreamComment */ - true, /* wantfields */ - ) -} - -func createSplitQueryAlgorithmObject( - algorithm querypb.SplitQueryRequest_Algorithm, - splitParams *splitquery.SplitParams, - sqlExecuter splitquery.SQLExecuter) (splitquery.SplitAlgorithmInterface, error) { - - switch algorithm { - case querypb.SplitQueryRequest_FULL_SCAN: - return splitquery.NewFullScanAlgorithm(splitParams, sqlExecuter) - case querypb.SplitQueryRequest_EQUAL_SPLITS: - return splitquery.NewEqualSplitsAlgorithm(splitParams, sqlExecuter) - default: - panic(fmt.Errorf("unknown algorithm enum: %+v", algorithm)) - } -} - // StreamHealth streams the health status to callback. // At the beginning, if TabletServer has a valid health // state, that response is immediately sent. diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index 767c284c2d3..90ed7bf7274 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -2227,186 +2227,6 @@ func TestPurgeMessages(t *testing.T) { } } -func TestTabletServerSplitQuery(t *testing.T) { - db := setUpTabletServerTest(t) - defer db.Close() - db.AddQuery("SELECT MIN(pk), MAX(pk) FROM test_table", &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int32}, - {Type: sqltypes.Int32}, - }, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - { - sqltypes.NewInt32(1), - sqltypes.NewInt32(100), - }, - }, - }) - testUtils := newTestUtils() - config := testUtils.newQueryServiceConfig() - tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) - dbcfgs := testUtils.newDBConfigs(db) - target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} - err := tsv.StartService(target, dbcfgs) - if err != nil { - t.Fatalf("StartService failed: %v", err) - } - defer tsv.StopService() - ctx := context.Background() - sql := "select * from test_table where count > :count" - splits, err := tsv.SplitQuery( - ctx, - &querypb.Target{TabletType: topodatapb.TabletType_RDONLY}, - &querypb.BoundQuery{Sql: sql}, - []string{}, /* splitColumns */ - 10, /* splitCount */ - 0, /* numRowsPerQueryPart */ - querypb.SplitQueryRequest_EQUAL_SPLITS) - if err != nil { - t.Fatalf("TabletServer.SplitQuery should succeed: %v, but get error: %v", sql, err) - } - if len(splits) != 10 { - t.Fatalf("got: %v, want: %v.\nsplits: %+v", len(splits), 10, splits) - } -} - -func TestTabletServerSplitQueryKeywords(t *testing.T) { - db := setUpTabletServerTest(t) - defer db.Close() - db.AddQuery("SELECT MIN(`by`), MAX(`by`) FROM `order`", &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int32}, - {Type: sqltypes.Int32}, - }, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - { - sqltypes.NewInt32(1), - sqltypes.NewInt32(100), - }, - }, - }) - testUtils := newTestUtils() - config := testUtils.newQueryServiceConfig() - tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) - dbcfgs := testUtils.newDBConfigs(db) - target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} - err := tsv.StartService(target, dbcfgs) - if err != nil { - t.Fatalf("StartService failed: %v", err) - } - defer tsv.StopService() - ctx := context.Background() - sql := "select * from `order` where `value` > :count" - splits, err := tsv.SplitQuery( - ctx, - &querypb.Target{TabletType: topodatapb.TabletType_RDONLY}, - &querypb.BoundQuery{Sql: sql}, - []string{}, /* splitColumns */ - 10, /* splitCount */ - 0, /* numRowsPerQueryPart */ - querypb.SplitQueryRequest_EQUAL_SPLITS) - if err != nil { - t.Fatalf("TabletServer.SplitQuery should succeed: %v, but get error: %v", sql, err) - } - if len(splits) != 10 { - t.Fatalf("got: %v, want: %v.\nsplits: %+v", len(splits), 10, splits) - } -} - -func TestTabletServerSplitQueryInvalidQuery(t *testing.T) { - db := setUpTabletServerTest(t) - defer db.Close() - testUtils := newTestUtils() - config := testUtils.newQueryServiceConfig() - tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) - dbcfgs := testUtils.newDBConfigs(db) - target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} - err := tsv.StartService(target, dbcfgs) - if err != nil { - t.Fatalf("StartService failed: %v", err) - } - defer tsv.StopService() - ctx := context.Background() - // SplitQuery should not support SQLs with a LIMIT clause: - sql := "select * from test_table where count > :count limit 10" - _, err = tsv.SplitQuery( - ctx, - &querypb.Target{TabletType: topodatapb.TabletType_RDONLY}, - &querypb.BoundQuery{Sql: sql}, - []string{}, /* splitColumns */ - 10, /* splitCount */ - 0, /* numRowsPerQueryPart */ - querypb.SplitQueryRequest_EQUAL_SPLITS) - if err == nil { - t.Fatalf("TabletServer.SplitQuery should fail") - } -} - -func TestTabletServerSplitQueryInvalidParams(t *testing.T) { - // Tests that SplitQuery returns an error when both numRowsPerQueryPart and splitCount are given. - db := setUpTabletServerTest(t) - defer db.Close() - testUtils := newTestUtils() - config := testUtils.newQueryServiceConfig() - tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) - dbcfgs := testUtils.newDBConfigs(db) - target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} - err := tsv.StartService(target, dbcfgs) - if err != nil { - t.Fatalf("StartService failed: %v", err) - } - defer tsv.StopService() - ctx := context.Background() - sql := "select * from test_table where count > :count" - _, err = tsv.SplitQuery( - ctx, - &querypb.Target{TabletType: topodatapb.TabletType_RDONLY}, - &querypb.BoundQuery{Sql: sql}, - []string{}, /* splitColumns */ - 10, /* splitCount */ - 11, /* numRowsPerQueryPart */ - querypb.SplitQueryRequest_EQUAL_SPLITS) - if err == nil { - t.Fatalf("TabletServer.SplitQuery should fail") - } -} - -// Tests that using Equal Splits on a string column returns an error. -func TestTabletServerSplitQueryEqualSplitsOnStringColumn(t *testing.T) { - db := setUpTabletServerTest(t) - defer db.Close() - testUtils := newTestUtils() - config := testUtils.newQueryServiceConfig() - tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) - dbcfgs := testUtils.newDBConfigs(db) - target := querypb.Target{TabletType: topodatapb.TabletType_RDONLY} - err := tsv.StartService(target, dbcfgs) - if err != nil { - t.Fatalf("StartService failed: %v", err) - } - defer tsv.StopService() - ctx := context.Background() - sql := "select * from test_table" - _, err = tsv.SplitQuery( - ctx, - &querypb.Target{TabletType: topodatapb.TabletType_RDONLY}, - &querypb.BoundQuery{Sql: sql}, - // EQUAL_SPLITS should not work on a string column. - []string{"name_string"}, /* splitColumns */ - 10, /* splitCount */ - 0, /* numRowsPerQueryPart */ - querypb.SplitQueryRequest_EQUAL_SPLITS) - want := - "using the EQUAL_SPLITS algorithm in SplitQuery" + - " requires having a numeric (integral or float) split-column." + - " Got type: {Name: 'name_string', Type: VARCHAR}" - if err.Error() != want { - t.Fatalf("got: %v, want: %v", err, want) - } -} - func TestHandleExecUnknownError(t *testing.T) { ctx := context.Background() logStats := tabletenv.NewLogStats(ctx, "TestHandleExecError") @@ -2900,7 +2720,6 @@ func getSupportedQueries() map[string]*sqltypes.Result { RowsAffected: 2, Rows: [][]sqltypes.Value{ mysql.BaseShowTablesRow("test_table", false, ""), - mysql.BaseShowTablesRow("order", false, ""), mysql.BaseShowTablesRow("msg", false, "vitess_message,vt_ack_wait=30,vt_purge_after=120,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=30"), }, }, @@ -2936,7 +2755,6 @@ func getSupportedQueries() map[string]*sqltypes.Result { mysql.BaseShowTablesRow("test_table", false, ""), }, }, - // for SplitQuery because it needs a primary key column "show index from test_table": { Fields: mysql.ShowIndexFromTableFields, RowsAffected: 3, @@ -2946,38 +2764,6 @@ func getSupportedQueries() map[string]*sqltypes.Result { mysql.ShowIndexFromTableRow("test_table", false, "name_string_INDEX", 1, "name_string", true), }, }, - // Define table that uses keywords to test SplitQuery escaping. - "select * from `order` where 1 != 1": { - Fields: []*querypb.Field{{ - Name: "by", - Type: sqltypes.Int32, - }, { - Name: "value", - Type: sqltypes.Int32, - }}, - }, - "describe `order`": { - Fields: mysql.DescribeTableFields, - RowsAffected: 4, - Rows: [][]sqltypes.Value{ - mysql.DescribeTableRow("by", "int(11)", false, "PRI", "0"), - mysql.DescribeTableRow("value", "int(11)", false, "", "0"), - }, - }, - mysql.BaseShowTablesForTable("order"): { - Fields: mysql.BaseShowTablesFields, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - mysql.BaseShowTablesRow("order", false, ""), - }, - }, - "show index from `order`": { - Fields: mysql.ShowIndexFromTableFields, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - mysql.ShowIndexFromTableRow("order", true, "PRIMARY", 1, "by", false), - }, - }, "select * from msg where 1 != 1": { Fields: []*querypb.Field{{ Name: "time_scheduled", diff --git a/proto/query.proto b/proto/query.proto index 4ea0a3136f2..3a5aaf9cf90 100644 --- a/proto/query.proto +++ b/proto/query.proto @@ -676,42 +676,6 @@ message MessageAckResponse { QueryResult result = 1; } -// SplitQueryRequest is the payload for SplitQuery sent by VTGate to a VTTablet. -// See vtgate.SplitQueryRequest for more details. -message SplitQueryRequest { - vtrpc.CallerID effective_caller_id = 1; - VTGateCallerID immediate_caller_id = 2; - Target target = 3; - - BoundQuery query = 4; - repeated string split_column = 5; - - // Exactly one of the following must be nonzero. - int64 split_count = 6; - int64 num_rows_per_query_part = 8; - - enum Algorithm { - EQUAL_SPLITS = 0; - FULL_SCAN = 1; - } - Algorithm algorithm = 9; -} - -// QuerySplit represents one query to execute on the tablet -message QuerySplit { - // query is the query to execute - BoundQuery query = 1; - - // row_count is the approximate row count the query will return - int64 row_count = 2; -} - -// SplitQueryResponse is returned by SplitQuery and represents all the queries -// to execute in order to get the entire data set. -message SplitQueryResponse { - repeated QuerySplit queries = 1; -} - // StreamHealthRequest is the payload for StreamHealth message StreamHealthRequest { } diff --git a/proto/queryservice.proto b/proto/queryservice.proto index fe2bd018150..9533a9dcd81 100644 --- a/proto/queryservice.proto +++ b/proto/queryservice.proto @@ -85,10 +85,6 @@ service Query { // MessageAck acks messages for a table. rpc MessageAck(query.MessageAckRequest) returns (query.MessageAckResponse) {}; - // SplitQuery is the API to facilitate MapReduce-type iterations - // over large data sets (like full table dumps). - rpc SplitQuery(query.SplitQueryRequest) returns (query.SplitQueryResponse) {}; - // StreamHealth runs a streaming RPC to the tablet, that returns the // current health of the tablet on a regular basis. rpc StreamHealth(query.StreamHealthRequest) returns (stream query.StreamHealthResponse) {}; diff --git a/proto/vtgate.proto b/proto/vtgate.proto index 480367104b0..eb2ecf67a20 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -724,137 +724,6 @@ message MessageAckKeyspaceIdsRequest { message ResolveTransactionResponse { } -// SplitQueryRequest is the payload to SplitQuery. -// -// SplitQuery takes a "SELECT" query and generates a list of queries called -// "query-parts". Each query-part consists of the original query with an -// added WHERE clause that restricts the query-part to operate only on -// rows whose values in the columns listed in the "split_column" field -// of the request (see below) are in a particular range. -// -// It is guaranteed that the set of rows obtained from -// executing each query-part on a database snapshot -// and merging (without deduping) the results is equal to the set of rows -// obtained from executing the original query on the same snapshot -// with the rows containing NULL values in any of the split_column's excluded. -// -// This is typically called by the MapReduce master when reading from Vitess. -// There it's desirable that the sets of rows returned by the query-parts -// have roughly the same size. -message SplitQueryRequest { - // caller_id identifies the caller. This is the effective caller ID, - // set by the application to further identify the caller. - vtrpc.CallerID caller_id = 1; - - // keyspace to target the query to. - string keyspace = 2; - - // The query and bind variables to produce splits for. - // The given query must be a simple query of the form - // SELECT FROM
WHERE . - // It must not contain subqueries nor any of the keywords - // JOIN, GROUP BY, ORDER BY, LIMIT, DISTINCT. - // Furthermore,
must be a single "concrete" table. - // It cannot be a view. - query.BoundQuery query = 3; - - // Each generated query-part will be restricted to rows whose values - // in the columns listed in this field are in a particular range. - // The list of columns named here must be a prefix of the list of - // columns defining some index or primary key of the table - // referenced in 'query'. For many tables using the primary key columns - // (in order) is sufficient and this is the default if this field is omitted. - // See the comment on the 'algorithm' field for more restrictions and - // information. - repeated string split_column = 4; - - // You can specify either an estimate of the number of query-parts to - // generate or an estimate of the number of rows each query-part should - // return. - // Thus, exactly one of split_count or num_rows_per_query_part - // should be nonzero. - // The non-given parameter is calculated from the given parameter - // using the formula: split_count * num_rows_per_query_pary = table_size, - // where table_size is an approximation of the number of rows in the - // table. - // Note that if "split_count" is given it is regarded as an estimate. - // The number of query-parts returned may differ slightly (in particular, - // if it's not a whole multiple of the number of vitess shards). - int64 split_count = 5; - int64 num_rows_per_query_part = 6; - - // The algorithm to use to split the query. The split algorithm is performed - // on each database shard in parallel. The lists of query-parts generated - // by the shards are merged and returned to the caller. - // Two algorithms are supported: - // EQUAL_SPLITS - // If this algorithm is selected then only the first 'split_column' given - // is used (or the first primary key column if the 'split_column' field is - // empty). In the rest of this algorithm's description, we refer to - // this column as "the split column". - // The split column must have numeric type (integral or floating point). - // The algorithm works by taking the interval [min, max], where min and - // max are the minimum and maximum values of the split column in - // the table-shard, respectively, and partitioning it into 'split_count' - // sub-intervals of equal size. The added WHERE clause of each query-part - // restricts that part to rows whose value in the split column belongs to - // a particular sub-interval. This is fast, but requires that the - // distribution of values of the split column be uniform in [min, max] - // for the number of rows returned by each query part to be roughly the - // same. - // FULL_SCAN - // If this algorithm is used then the split_column must be the primary key - // columns (in order). - // This algorithm performs a full-scan of the table-shard referenced - // in 'query' to get "boundary" rows that are num_rows_per_query_part - // apart when the table is ordered by the columns listed in - // 'split_column'. It then restricts each query-part to the rows - // located between two successive boundary rows. - // This algorithm supports multiple split_column's of any type, - // but is slower than EQUAL_SPLITS. - query.SplitQueryRequest.Algorithm algorithm = 7; - // TODO(erez): This field is no longer used by the server code. - // Remove this field after this new server code is released to prod. - // We must keep it for now, so that clients can still send it to the old - // server code currently in production. - bool use_split_query_v2 = 8; -} - -// SplitQueryResponse is the returned value from SplitQuery. -message SplitQueryResponse { - message KeyRangePart { - // keyspace to target the query to. - string keyspace = 1; - - // key ranges to target the query to. - repeated topodata.KeyRange key_ranges = 2; - } - message ShardPart { - // keyspace to target the query to. - string keyspace = 1; - - // shards to target the query to. - repeated string shards = 2; - } - message Part { - // query is the query and bind variables to execute. - query.BoundQuery query = 1; - - // key_range_part is set if the query should be executed by - // ExecuteKeyRanges. - KeyRangePart key_range_part = 2; - - // shard_part is set if the query should be executed by ExecuteShards. - ShardPart shard_part = 3; - - // size is the approximate number of rows this query will return. - int64 size = 4; - } - - // splits contains the queries to run to fetch the entire data set. - repeated Part splits = 1; -} - // GetSrvKeyspaceRequest is the payload to GetSrvKeyspace. message GetSrvKeyspaceRequest { // keyspace name to fetch. diff --git a/proto/vtgateservice.proto b/proto/vtgateservice.proto index 8a184990af3..121aaa4b36d 100644 --- a/proto/vtgateservice.proto +++ b/proto/vtgateservice.proto @@ -114,10 +114,6 @@ service Vitess { // keyspace ids. rpc MessageAckKeyspaceIds(vtgate.MessageAckKeyspaceIdsRequest) returns (query.MessageAckResponse) {}; - // Split a query into non-overlapping sub queries - // API group: Map Reduce - rpc SplitQuery(vtgate.SplitQueryRequest) returns (vtgate.SplitQueryResponse) {}; - // GetSrvKeyspace returns a SrvKeyspace object (as seen by this vtgate). // This method is provided as a convenient way for clients to take a // look at the sharding configuration for a Keyspace. Looking at the From 5323272e8739c1a4f9a994e3023f53767f092ce4 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 8 Mar 2020 21:40:36 -0700 Subject: [PATCH 296/825] deprecation: delete SplitQuery java Signed-off-by: Sugu Sougoumarane --- go/cmd/vtgateclienttest/goclienttest/echo.go | 21 -- go/cmd/vtgateclienttest/services/callerid.go | 23 -- go/cmd/vtgateclienttest/services/echo.go | 35 -- go/cmd/vtgateclienttest/services/errors.go | 24 -- go/cmd/vtgateclienttest/services/fallback.go | 14 - go/cmd/vtgateclienttest/services/terminal.go | 13 - .../mock_vtgateservice.go | 328 ------------------ .../main/java/io/vitess/client/RpcClient.java | 12 - .../io/vitess/client/VTGateBlockingConn.java | 16 - .../client/VTGateBlockingConnection.java | 30 -- .../java/io/vitess/client/VTGateConn.java | 31 -- .../io/vitess/client/VTGateConnection.java | 45 --- .../java/io/vitess/client/RpcClientTest.java | 24 -- .../io/vitess/client/grpc/GrpcClient.java | 9 - java/hadoop/pom.xml | 125 ------- .../src/main/java/io/vitess/hadoop/README.md | 96 ----- .../java/io/vitess/hadoop/RowWritable.java | 92 ----- .../main/java/io/vitess/hadoop/SplitQuery.png | Bin 33951 -> 0 bytes .../java/io/vitess/hadoop/VitessConf.java | 129 ------- .../io/vitess/hadoop/VitessInputFormat.java | 126 ------- .../io/vitess/hadoop/VitessInputSplit.java | 69 ---- .../io/vitess/hadoop/VitessRecordReader.java | 149 -------- .../java/io/vitess/hadoop/MapReduceIT.java | 250 ------------- java/pom.xml | 28 +- 24 files changed, 2 insertions(+), 1687 deletions(-) delete mode 100644 go/vt/vtgate/vtgateservice/vtgateservice_testing/mock_vtgateservice.go delete mode 100644 java/hadoop/pom.xml delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/README.md delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/SplitQuery.png delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessConf.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java delete mode 100644 java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java diff --git a/go/cmd/vtgateclienttest/goclienttest/echo.go b/go/cmd/vtgateclienttest/goclienttest/echo.go index ba1faef39f6..a3ba6e30cba 100644 --- a/go/cmd/vtgateclienttest/goclienttest/echo.go +++ b/go/cmd/vtgateclienttest/goclienttest/echo.go @@ -108,7 +108,6 @@ func testEcho(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgateconn.VTG testEchoExecute(t, conn, session) testEchoStreamExecute(t, conn, session) testEchoTransactionExecute(t, conn) - testEchoSplitQuery(t, conn) testEchoUpdateStream(t, conn) } @@ -402,26 +401,6 @@ func testEchoTransactionExecute(t *testing.T, conn *vtgateconn.VTGateConn) { }) } -func testEchoSplitQuery(t *testing.T, conn *vtgateconn.VTGateConn) { - want := &vtgatepb.SplitQueryResponse_Part{ - Query: &querypb.BoundQuery{ - Sql: echoPrefix + query + ":[split_column1,split_column2]:123:1000:FULL_SCAN", - BindVariables: bindVars, - }, - KeyRangePart: &vtgatepb.SplitQueryResponse_KeyRangePart{Keyspace: keyspace}, - } - got, err := conn.SplitQuery(context.Background(), keyspace, echoPrefix+query, bindVars, []string{"split_column1,split_column2"}, 123, 1000, querypb.SplitQueryRequest_FULL_SCAN) - if err != nil { - t.Fatalf("SplitQuery error: %v", err) - } - // For some reason, proto.Equal() is calling them unequal even though no diffs - // are found. - gotstr, wantstr := got[0].String(), want.String() - if gotstr != wantstr { - t.Errorf("SplitQuery() = %v, want %v", gotstr, wantstr) - } -} - func testEchoUpdateStream(t *testing.T, conn *vtgateconn.VTGateConn) { var stream vtgateconn.UpdateStreamReader var err error diff --git a/go/cmd/vtgateclienttest/services/callerid.go b/go/cmd/vtgateclienttest/services/callerid.go index 746c6c697b3..a13830cbaff 100644 --- a/go/cmd/vtgateclienttest/services/callerid.go +++ b/go/cmd/vtgateclienttest/services/callerid.go @@ -181,29 +181,6 @@ func (c *callerIDClient) MessageAck(ctx context.Context, keyspace string, name s return c.fallback.MessageAck(ctx, keyspace, name, ids) } -func (c *callerIDClient) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - if ok, err := c.checkCallerID(ctx, sql); ok { - return nil, err - } - return c.fallbackClient.SplitQuery( - ctx, - sql, - keyspace, - bindVariables, - splitColumns, - splitCount, - numRowsPerQueryPart, - algorithm) -} - func (c *callerIDClient) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { if ok, err := c.checkCallerID(ctx, shard); ok { return err diff --git a/go/cmd/vtgateclienttest/services/echo.go b/go/cmd/vtgateclienttest/services/echo.go index f0b175d3d7e..aeaa4b0c0df 100644 --- a/go/cmd/vtgateclienttest/services/echo.go +++ b/go/cmd/vtgateclienttest/services/echo.go @@ -340,41 +340,6 @@ func (c *echoClient) MessageAckKeyspaceIds(ctx context.Context, keyspace string, return c.fallback.MessageAckKeyspaceIds(ctx, keyspace, name, idKeyspaceIDs) } -func (c *echoClient) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - - if strings.HasPrefix(sql, EchoPrefix) { - return []*vtgatepb.SplitQueryResponse_Part{ - { - Query: &querypb.BoundQuery{ - Sql: fmt.Sprintf("%v:%v:%v:%v:%v", - sql, splitColumns, splitCount, numRowsPerQueryPart, algorithm), - BindVariables: bindVariables, - }, - KeyRangePart: &vtgatepb.SplitQueryResponse_KeyRangePart{ - Keyspace: keyspace, - }, - }, - }, nil - } - return c.fallback.SplitQuery( - ctx, - sql, - keyspace, - bindVariables, - splitColumns, - splitCount, - numRowsPerQueryPart, - algorithm) -} - func (c *echoClient) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { if strings.HasPrefix(shard, EchoPrefix) { m := map[string]interface{}{ diff --git a/go/cmd/vtgateclienttest/services/errors.go b/go/cmd/vtgateclienttest/services/errors.go index 4d82fbc83b7..53a2684c1e1 100644 --- a/go/cmd/vtgateclienttest/services/errors.go +++ b/go/cmd/vtgateclienttest/services/errors.go @@ -290,30 +290,6 @@ func (c *errorClient) MessageAckKeyspaceIds(ctx context.Context, keyspace string return c.fallback.MessageAckKeyspaceIds(ctx, keyspace, name, idKeyspaceIDs) } -func (c *errorClient) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) { - - if err := requestToError(sql); err != nil { - return nil, err - } - return c.fallbackClient.SplitQuery( - ctx, - sql, - keyspace, - bindVariables, - splitColumns, - splitCount, - numRowsPerQueryPart, - algorithm) -} - func (c *errorClient) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { if err := requestToError(keyspace); err != nil { return nil, err diff --git a/go/cmd/vtgateclienttest/services/fallback.go b/go/cmd/vtgateclienttest/services/fallback.go index ca895a0942f..5597078e645 100644 --- a/go/cmd/vtgateclienttest/services/fallback.go +++ b/go/cmd/vtgateclienttest/services/fallback.go @@ -116,20 +116,6 @@ func (c fallbackClient) MessageAckKeyspaceIds(ctx context.Context, keyspace stri return c.fallback.MessageAckKeyspaceIds(ctx, keyspace, name, idKeyspaceIDs) } -func (c fallbackClient) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, -) ([]*vtgatepb.SplitQueryResponse_Part, error) { - return c.fallback.SplitQuery( - ctx, sql, keyspace, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm) -} - func (c fallbackClient) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { return c.fallback.GetSrvKeyspace(ctx, keyspace) } diff --git a/go/cmd/vtgateclienttest/services/terminal.go b/go/cmd/vtgateclienttest/services/terminal.go index 7d828c79a38..1caf7123922 100644 --- a/go/cmd/vtgateclienttest/services/terminal.go +++ b/go/cmd/vtgateclienttest/services/terminal.go @@ -125,19 +125,6 @@ func (c *terminalClient) MessageAckKeyspaceIds(ctx context.Context, keyspace str return 0, errTerminal } -func (c *terminalClient) SplitQuery( - ctx context.Context, - keyspace string, - sql string, - bindVariables map[string]*querypb.BindVariable, - splitColumns []string, - splitCount int64, - numRowsPerQueryPart int64, - algorithm querypb.SplitQueryRequest_Algorithm, -) ([]*vtgatepb.SplitQueryResponse_Part, error) { - return nil, errTerminal -} - func (c *terminalClient) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) { return nil, errTerminal } diff --git a/go/vt/vtgate/vtgateservice/vtgateservice_testing/mock_vtgateservice.go b/go/vt/vtgate/vtgateservice/vtgateservice_testing/mock_vtgateservice.go deleted file mode 100644 index 2d1fdfaa189..00000000000 --- a/go/vt/vtgate/vtgateservice/vtgateservice_testing/mock_vtgateservice.go +++ /dev/null @@ -1,328 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: interface.go - -// Package vtgateservice_testing is a generated GoMock package. -package vtgateservice_testing - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - context "golang.org/x/net/context" - sqltypes "vitess.io/vitess/go/sqltypes" - query "vitess.io/vitess/go/vt/proto/query" - topodata "vitess.io/vitess/go/vt/proto/topodata" - vtgate "vitess.io/vitess/go/vt/proto/vtgate" -) - -// MockVTGateService is a mock of VTGateService interface -type MockVTGateService struct { - ctrl *gomock.Controller - recorder *MockVTGateServiceMockRecorder -} - -// MockVTGateServiceMockRecorder is the mock recorder for MockVTGateService -type MockVTGateServiceMockRecorder struct { - mock *MockVTGateService -} - -// NewMockVTGateService creates a new mock instance -func NewMockVTGateService(ctrl *gomock.Controller) *MockVTGateService { - mock := &MockVTGateService{ctrl: ctrl} - mock.recorder = &MockVTGateServiceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockVTGateService) EXPECT() *MockVTGateServiceMockRecorder { - return m.recorder -} - -// Execute mocks base method -func (m *MockVTGateService) Execute(ctx context.Context, session *vtgate.Session, sql string, bindVariables map[string]*query.BindVariable) (*vtgate.Session, *sqltypes.Result, error) { - ret := m.ctrl.Call(m, "Execute", ctx, session, sql, bindVariables) - ret0, _ := ret[0].(*vtgate.Session) - ret1, _ := ret[1].(*sqltypes.Result) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// Execute indicates an expected call of Execute -func (mr *MockVTGateServiceMockRecorder) Execute(ctx, session, sql, bindVariables interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockVTGateService)(nil).Execute), ctx, session, sql, bindVariables) -} - -// ExecuteBatch mocks base method -func (m *MockVTGateService) ExecuteBatch(ctx context.Context, session *vtgate.Session, sqlList []string, bindVariablesList []map[string]*query.BindVariable) (*vtgate.Session, []sqltypes.QueryResponse, error) { - ret := m.ctrl.Call(m, "ExecuteBatch", ctx, session, sqlList, bindVariablesList) - ret0, _ := ret[0].(*vtgate.Session) - ret1, _ := ret[1].([]sqltypes.QueryResponse) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ExecuteBatch indicates an expected call of ExecuteBatch -func (mr *MockVTGateServiceMockRecorder) ExecuteBatch(ctx, session, sqlList, bindVariablesList interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBatch", reflect.TypeOf((*MockVTGateService)(nil).ExecuteBatch), ctx, session, sqlList, bindVariablesList) -} - -// StreamExecute mocks base method -func (m *MockVTGateService) StreamExecute(ctx context.Context, session *vtgate.Session, sql string, bindVariables map[string]*query.BindVariable, callback func(*sqltypes.Result) error) error { - ret := m.ctrl.Call(m, "StreamExecute", ctx, session, sql, bindVariables, callback) - ret0, _ := ret[0].(error) - return ret0 -} - -// StreamExecute indicates an expected call of StreamExecute -func (mr *MockVTGateServiceMockRecorder) StreamExecute(ctx, session, sql, bindVariables, callback interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamExecute", reflect.TypeOf((*MockVTGateService)(nil).StreamExecute), ctx, session, sql, bindVariables, callback) -} - -// ExecuteShards mocks base method -func (m *MockVTGateService) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]*query.BindVariable, keyspace string, shards []string, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool, options *query.ExecuteOptions) (*sqltypes.Result, error) { - ret := m.ctrl.Call(m, "ExecuteShards", ctx, sql, bindVariables, keyspace, shards, tabletType, session, notInTransaction, options) - ret0, _ := ret[0].(*sqltypes.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExecuteShards indicates an expected call of ExecuteShards -func (mr *MockVTGateServiceMockRecorder) ExecuteShards(ctx, sql, bindVariables, keyspace, shards, tabletType, session, notInTransaction, options interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteShards", reflect.TypeOf((*MockVTGateService)(nil).ExecuteShards), ctx, sql, bindVariables, keyspace, shards, tabletType, session, notInTransaction, options) -} - -// ExecuteKeyspaceIds mocks base method -func (m *MockVTGateService) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]*query.BindVariable, keyspace string, keyspaceIds [][]byte, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool, options *query.ExecuteOptions) (*sqltypes.Result, error) { - ret := m.ctrl.Call(m, "ExecuteKeyspaceIds", ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, session, notInTransaction, options) - ret0, _ := ret[0].(*sqltypes.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExecuteKeyspaceIds indicates an expected call of ExecuteKeyspaceIds -func (mr *MockVTGateServiceMockRecorder) ExecuteKeyspaceIds(ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, session, notInTransaction, options interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteKeyspaceIds", reflect.TypeOf((*MockVTGateService)(nil).ExecuteKeyspaceIds), ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, session, notInTransaction, options) -} - -// ExecuteKeyRanges mocks base method -func (m *MockVTGateService) ExecuteKeyRanges(ctx context.Context, sql string, bindVariables map[string]*query.BindVariable, keyspace string, keyRanges []*topodata.KeyRange, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool, options *query.ExecuteOptions) (*sqltypes.Result, error) { - ret := m.ctrl.Call(m, "ExecuteKeyRanges", ctx, sql, bindVariables, keyspace, keyRanges, tabletType, session, notInTransaction, options) - ret0, _ := ret[0].(*sqltypes.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExecuteKeyRanges indicates an expected call of ExecuteKeyRanges -func (mr *MockVTGateServiceMockRecorder) ExecuteKeyRanges(ctx, sql, bindVariables, keyspace, keyRanges, tabletType, session, notInTransaction, options interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteKeyRanges", reflect.TypeOf((*MockVTGateService)(nil).ExecuteKeyRanges), ctx, sql, bindVariables, keyspace, keyRanges, tabletType, session, notInTransaction, options) -} - -// ExecuteEntityIds mocks base method -func (m *MockVTGateService) ExecuteEntityIds(ctx context.Context, sql string, bindVariables map[string]*query.BindVariable, keyspace, entityColumnName string, entityKeyspaceIDs []*vtgate.ExecuteEntityIdsRequest_EntityId, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool, options *query.ExecuteOptions) (*sqltypes.Result, error) { - ret := m.ctrl.Call(m, "ExecuteEntityIds", ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction, options) - ret0, _ := ret[0].(*sqltypes.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExecuteEntityIds indicates an expected call of ExecuteEntityIds -func (mr *MockVTGateServiceMockRecorder) ExecuteEntityIds(ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction, options interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteEntityIds", reflect.TypeOf((*MockVTGateService)(nil).ExecuteEntityIds), ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction, options) -} - -// ExecuteBatchShards mocks base method -func (m *MockVTGateService) ExecuteBatchShards(ctx context.Context, queries []*vtgate.BoundShardQuery, tabletType topodata.TabletType, asTransaction bool, session *vtgate.Session, options *query.ExecuteOptions) ([]sqltypes.Result, error) { - ret := m.ctrl.Call(m, "ExecuteBatchShards", ctx, queries, tabletType, asTransaction, session, options) - ret0, _ := ret[0].([]sqltypes.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExecuteBatchShards indicates an expected call of ExecuteBatchShards -func (mr *MockVTGateServiceMockRecorder) ExecuteBatchShards(ctx, queries, tabletType, asTransaction, session, options interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBatchShards", reflect.TypeOf((*MockVTGateService)(nil).ExecuteBatchShards), ctx, queries, tabletType, asTransaction, session, options) -} - -// ExecuteBatchKeyspaceIds mocks base method -func (m *MockVTGateService) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtgate.BoundKeyspaceIdQuery, tabletType topodata.TabletType, asTransaction bool, session *vtgate.Session, options *query.ExecuteOptions) ([]sqltypes.Result, error) { - ret := m.ctrl.Call(m, "ExecuteBatchKeyspaceIds", ctx, queries, tabletType, asTransaction, session, options) - ret0, _ := ret[0].([]sqltypes.Result) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExecuteBatchKeyspaceIds indicates an expected call of ExecuteBatchKeyspaceIds -func (mr *MockVTGateServiceMockRecorder) ExecuteBatchKeyspaceIds(ctx, queries, tabletType, asTransaction, session, options interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBatchKeyspaceIds", reflect.TypeOf((*MockVTGateService)(nil).ExecuteBatchKeyspaceIds), ctx, queries, tabletType, asTransaction, session, options) -} - -// StreamExecuteShards mocks base method -func (m *MockVTGateService) StreamExecuteShards(ctx context.Context, sql string, bindVariables map[string]*query.BindVariable, keyspace string, shards []string, tabletType topodata.TabletType, options *query.ExecuteOptions, callback func(*sqltypes.Result) error) error { - ret := m.ctrl.Call(m, "StreamExecuteShards", ctx, sql, bindVariables, keyspace, shards, tabletType, options, callback) - ret0, _ := ret[0].(error) - return ret0 -} - -// StreamExecuteShards indicates an expected call of StreamExecuteShards -func (mr *MockVTGateServiceMockRecorder) StreamExecuteShards(ctx, sql, bindVariables, keyspace, shards, tabletType, options, callback interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamExecuteShards", reflect.TypeOf((*MockVTGateService)(nil).StreamExecuteShards), ctx, sql, bindVariables, keyspace, shards, tabletType, options, callback) -} - -// StreamExecuteKeyspaceIds mocks base method -func (m *MockVTGateService) StreamExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]*query.BindVariable, keyspace string, keyspaceIds [][]byte, tabletType topodata.TabletType, options *query.ExecuteOptions, callback func(*sqltypes.Result) error) error { - ret := m.ctrl.Call(m, "StreamExecuteKeyspaceIds", ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, options, callback) - ret0, _ := ret[0].(error) - return ret0 -} - -// StreamExecuteKeyspaceIds indicates an expected call of StreamExecuteKeyspaceIds -func (mr *MockVTGateServiceMockRecorder) StreamExecuteKeyspaceIds(ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, options, callback interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamExecuteKeyspaceIds", reflect.TypeOf((*MockVTGateService)(nil).StreamExecuteKeyspaceIds), ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, options, callback) -} - -// StreamExecuteKeyRanges mocks base method -func (m *MockVTGateService) StreamExecuteKeyRanges(ctx context.Context, sql string, bindVariables map[string]*query.BindVariable, keyspace string, keyRanges []*topodata.KeyRange, tabletType topodata.TabletType, options *query.ExecuteOptions, callback func(*sqltypes.Result) error) error { - ret := m.ctrl.Call(m, "StreamExecuteKeyRanges", ctx, sql, bindVariables, keyspace, keyRanges, tabletType, options, callback) - ret0, _ := ret[0].(error) - return ret0 -} - -// StreamExecuteKeyRanges indicates an expected call of StreamExecuteKeyRanges -func (mr *MockVTGateServiceMockRecorder) StreamExecuteKeyRanges(ctx, sql, bindVariables, keyspace, keyRanges, tabletType, options, callback interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamExecuteKeyRanges", reflect.TypeOf((*MockVTGateService)(nil).StreamExecuteKeyRanges), ctx, sql, bindVariables, keyspace, keyRanges, tabletType, options, callback) -} - -// Begin mocks base method -func (m *MockVTGateService) Begin(ctx context.Context, singledb bool) (*vtgate.Session, error) { - ret := m.ctrl.Call(m, "Begin", ctx, singledb) - ret0, _ := ret[0].(*vtgate.Session) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Begin indicates an expected call of Begin -func (mr *MockVTGateServiceMockRecorder) Begin(ctx, singledb interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Begin", reflect.TypeOf((*MockVTGateService)(nil).Begin), ctx, singledb) -} - -// Commit mocks base method -func (m *MockVTGateService) Commit(ctx context.Context, twopc bool, session *vtgate.Session) error { - ret := m.ctrl.Call(m, "Commit", ctx, twopc, session) - ret0, _ := ret[0].(error) - return ret0 -} - -// Commit indicates an expected call of Commit -func (mr *MockVTGateServiceMockRecorder) Commit(ctx, twopc, session interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockVTGateService)(nil).Commit), ctx, twopc, session) -} - -// Rollback mocks base method -func (m *MockVTGateService) Rollback(ctx context.Context, session *vtgate.Session) error { - ret := m.ctrl.Call(m, "Rollback", ctx, session) - ret0, _ := ret[0].(error) - return ret0 -} - -// Rollback indicates an expected call of Rollback -func (mr *MockVTGateServiceMockRecorder) Rollback(ctx, session interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rollback", reflect.TypeOf((*MockVTGateService)(nil).Rollback), ctx, session) -} - -// ResolveTransaction mocks base method -func (m *MockVTGateService) ResolveTransaction(ctx context.Context, dtid string) error { - ret := m.ctrl.Call(m, "ResolveTransaction", ctx, dtid) - ret0, _ := ret[0].(error) - return ret0 -} - -// ResolveTransaction indicates an expected call of ResolveTransaction -func (mr *MockVTGateServiceMockRecorder) ResolveTransaction(ctx, dtid interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResolveTransaction", reflect.TypeOf((*MockVTGateService)(nil).ResolveTransaction), ctx, dtid) -} - -// MessageStream mocks base method -func (m *MockVTGateService) MessageStream(ctx context.Context, keyspace, shard string, keyRange *topodata.KeyRange, name string, callback func(*sqltypes.Result) error) error { - ret := m.ctrl.Call(m, "MessageStream", ctx, keyspace, shard, keyRange, name, callback) - ret0, _ := ret[0].(error) - return ret0 -} - -// MessageStream indicates an expected call of MessageStream -func (mr *MockVTGateServiceMockRecorder) MessageStream(ctx, keyspace, shard, keyRange, name, callback interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageStream", reflect.TypeOf((*MockVTGateService)(nil).MessageStream), ctx, keyspace, shard, keyRange, name, callback) -} - -// MessageAck mocks base method -func (m *MockVTGateService) MessageAck(ctx context.Context, keyspace, name string, ids []*query.Value) (int64, error) { - ret := m.ctrl.Call(m, "MessageAck", ctx, keyspace, name, ids) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// MessageAck indicates an expected call of MessageAck -func (mr *MockVTGateServiceMockRecorder) MessageAck(ctx, keyspace, name, ids interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageAck", reflect.TypeOf((*MockVTGateService)(nil).MessageAck), ctx, keyspace, name, ids) -} - -// MessageAckKeyspaceIds mocks base method -func (m *MockVTGateService) MessageAckKeyspaceIds(ctx context.Context, keyspace, name string, idKeyspaceIDs []*vtgate.IdKeyspaceId) (int64, error) { - ret := m.ctrl.Call(m, "MessageAckKeyspaceIds", ctx, keyspace, name, idKeyspaceIDs) - ret0, _ := ret[0].(int64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// MessageAckKeyspaceIds indicates an expected call of MessageAckKeyspaceIds -func (mr *MockVTGateServiceMockRecorder) MessageAckKeyspaceIds(ctx, keyspace, name, idKeyspaceIDs interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageAckKeyspaceIds", reflect.TypeOf((*MockVTGateService)(nil).MessageAckKeyspaceIds), ctx, keyspace, name, idKeyspaceIDs) -} - -// SplitQuery mocks base method -func (m *MockVTGateService) SplitQuery(ctx context.Context, keyspace, sql string, bindVariables map[string]*query.BindVariable, splitColumns []string, splitCount, numRowsPerQueryPart int64, algorithm query.SplitQueryRequest_Algorithm) ([]*vtgate.SplitQueryResponse_Part, error) { - ret := m.ctrl.Call(m, "SplitQuery", ctx, keyspace, sql, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm) - ret0, _ := ret[0].([]*vtgate.SplitQueryResponse_Part) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SplitQuery indicates an expected call of SplitQuery -func (mr *MockVTGateServiceMockRecorder) SplitQuery(ctx, keyspace, sql, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SplitQuery", reflect.TypeOf((*MockVTGateService)(nil).SplitQuery), ctx, keyspace, sql, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm) -} - -// GetSrvKeyspace mocks base method -func (m *MockVTGateService) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodata.SrvKeyspace, error) { - ret := m.ctrl.Call(m, "GetSrvKeyspace", ctx, keyspace) - ret0, _ := ret[0].(*topodata.SrvKeyspace) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSrvKeyspace indicates an expected call of GetSrvKeyspace -func (mr *MockVTGateServiceMockRecorder) GetSrvKeyspace(ctx, keyspace interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSrvKeyspace", reflect.TypeOf((*MockVTGateService)(nil).GetSrvKeyspace), ctx, keyspace) -} - -// UpdateStream mocks base method -func (m *MockVTGateService) UpdateStream(ctx context.Context, keyspace, shard string, keyRange *topodata.KeyRange, tabletType topodata.TabletType, timestamp int64, event *query.EventToken, callback func(*query.StreamEvent, int64) error) error { - ret := m.ctrl.Call(m, "UpdateStream", ctx, keyspace, shard, keyRange, tabletType, timestamp, event, callback) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateStream indicates an expected call of UpdateStream -func (mr *MockVTGateServiceMockRecorder) UpdateStream(ctx, keyspace, shard, keyRange, tabletType, timestamp, event, callback interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStream", reflect.TypeOf((*MockVTGateService)(nil).UpdateStream), ctx, keyspace, shard, keyRange, tabletType, timestamp, event, callback) -} - -// HandlePanic mocks base method -func (m *MockVTGateService) HandlePanic(err *error) { - m.ctrl.Call(m, "HandlePanic", err) -} - -// HandlePanic indicates an expected call of HandlePanic -func (mr *MockVTGateServiceMockRecorder) HandlePanic(err interface{}) *gomock.Call { - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandlePanic", reflect.TypeOf((*MockVTGateService)(nil).HandlePanic), err) -} diff --git a/java/client/src/main/java/io/vitess/client/RpcClient.java b/java/client/src/main/java/io/vitess/client/RpcClient.java index a08e22745d4..eb8da35a96c 100644 --- a/java/client/src/main/java/io/vitess/client/RpcClient.java +++ b/java/client/src/main/java/io/vitess/client/RpcClient.java @@ -42,8 +42,6 @@ import io.vitess.proto.Vtgate.GetSrvKeyspaceResponse; import io.vitess.proto.Vtgate.RollbackRequest; import io.vitess.proto.Vtgate.RollbackResponse; -import io.vitess.proto.Vtgate.SplitQueryRequest; -import io.vitess.proto.Vtgate.SplitQueryResponse; import io.vitess.proto.Vtgate.StreamExecuteKeyRangesRequest; import io.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsRequest; import io.vitess.proto.Vtgate.StreamExecuteRequest; @@ -228,16 +226,6 @@ StreamIterator streamExecuteKeyRanges( ListenableFuture rollback(Context ctx, RollbackRequest request) throws SQLException; - /** - * Splits a query into smaller queries. - * - *

See the - * proto - * definition for canonical documentation on this VTGate API. - */ - ListenableFuture splitQuery(Context ctx, SplitQueryRequest request) - throws SQLException; - /** * Returns a list of serving keyspaces. * diff --git a/java/client/src/main/java/io/vitess/client/VTGateBlockingConn.java b/java/client/src/main/java/io/vitess/client/VTGateBlockingConn.java index 36caf96960b..0473da5000a 100644 --- a/java/client/src/main/java/io/vitess/client/VTGateBlockingConn.java +++ b/java/client/src/main/java/io/vitess/client/VTGateBlockingConn.java @@ -19,13 +19,11 @@ import io.vitess.client.cursor.Cursor; import io.vitess.client.cursor.CursorWithError; import io.vitess.proto.Query; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; import io.vitess.proto.Topodata.KeyRange; import io.vitess.proto.Topodata.SrvKeyspace; import io.vitess.proto.Topodata.TabletType; import io.vitess.proto.Vtgate.BoundKeyspaceIdQuery; import io.vitess.proto.Vtgate.BoundShardQuery; -import io.vitess.proto.Vtgate.SplitQueryResponse; import java.io.Closeable; import java.io.IOException; @@ -191,20 +189,6 @@ public VTGateBlockingTx begin(Context ctx, boolean singleDB) throws SQLException return new VTGateBlockingTx(conn.begin(ctx, singleDB).checkedGet()); } - public List splitQuery( - Context ctx, - String keyspace, - String query, - Map bindVars, - Iterable splitColumns, - int splitCount, - int numRowsPerQueryPart, - Algorithm algorithm) throws SQLException { - return conn.splitQuery( - ctx, keyspace, query, bindVars, splitColumns, splitCount, numRowsPerQueryPart, algorithm) - .checkedGet(); - } - public SrvKeyspace getSrvKeyspace(Context ctx, String keyspace) throws SQLException { return conn.getSrvKeyspace(ctx, keyspace).checkedGet(); } diff --git a/java/client/src/main/java/io/vitess/client/VTGateBlockingConnection.java b/java/client/src/main/java/io/vitess/client/VTGateBlockingConnection.java index 22684c98179..826167d21c0 100644 --- a/java/client/src/main/java/io/vitess/client/VTGateBlockingConnection.java +++ b/java/client/src/main/java/io/vitess/client/VTGateBlockingConnection.java @@ -18,8 +18,6 @@ import io.vitess.client.cursor.Cursor; import io.vitess.client.cursor.CursorWithError; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; -import io.vitess.proto.Vtgate.SplitQueryResponse; import java.io.Closeable; import java.io.IOException; @@ -138,34 +136,6 @@ public Cursor streamExecute(Context ctx, return vtGateConnection.streamExecute(ctx, query, bindVars, vtSession); } - /** - * This method splits the query into small parts based on the splitColumn and Algorithm type - * provided. - * - * @param ctx Context on user and execution deadline if any. - * @param keyspace Keyspace to execute the query on. - * @param query Sql Query to be executed. - * @param bindVars Parameters to bind with sql. - * @param splitColumns Column to be used to split the data. - * @param splitCount Number of Partitions - * @param numRowsPerQueryPart Limit the number of records per query part. - * @param algorithm EQUAL_SPLITS or FULL_SCAN - * @return Query Parts - * @throws SQLException If anything fails on query execution. - */ - public List splitQuery(Context ctx, - String keyspace, - String query, - @Nullable Map bindVars, - Iterable splitColumns, - int splitCount, - int numRowsPerQueryPart, - Algorithm algorithm) throws SQLException { - return vtGateConnection - .splitQuery(ctx, keyspace, query, bindVars, splitColumns, splitCount, numRowsPerQueryPart, - algorithm).checkedGet(); - } - /** * @inheritDoc */ diff --git a/java/client/src/main/java/io/vitess/client/VTGateConn.java b/java/client/src/main/java/io/vitess/client/VTGateConn.java index 0431043a30b..bf1eb296823 100644 --- a/java/client/src/main/java/io/vitess/client/VTGateConn.java +++ b/java/client/src/main/java/io/vitess/client/VTGateConn.java @@ -30,7 +30,6 @@ import io.vitess.client.cursor.SimpleCursor; import io.vitess.client.cursor.StreamCursor; import io.vitess.proto.Query; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; import io.vitess.proto.Topodata.KeyRange; import io.vitess.proto.Topodata.SrvKeyspace; import io.vitess.proto.Topodata.TabletType; @@ -55,8 +54,6 @@ import io.vitess.proto.Vtgate.ExecuteShardsResponse; import io.vitess.proto.Vtgate.GetSrvKeyspaceRequest; import io.vitess.proto.Vtgate.GetSrvKeyspaceResponse; -import io.vitess.proto.Vtgate.SplitQueryRequest; -import io.vitess.proto.Vtgate.SplitQueryResponse; import io.vitess.proto.Vtgate.StreamExecuteKeyRangesRequest; import io.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsRequest; import io.vitess.proto.Vtgate.StreamExecuteRequest; @@ -492,34 +489,6 @@ public ListenableFuture apply(BeginResponse response) throws Exception directExecutor())); } - public SQLFuture> splitQuery(Context ctx, String keyspace, - String query, @Nullable Map bindVars, Iterable splitColumns, - int splitCount, int numRowsPerQueryPart, Algorithm algorithm) throws SQLException { - SplitQueryRequest.Builder requestBuilder = - SplitQueryRequest.newBuilder() - .setKeyspace(checkNotNull(keyspace)) - .setQuery(Proto.bindQuery(checkNotNull(query), bindVars)) - .addAllSplitColumn(splitColumns) - .setSplitCount(splitCount) - .setNumRowsPerQueryPart(numRowsPerQueryPart) - .setAlgorithm(algorithm); - if (ctx.getCallerId() != null) { - requestBuilder.setCallerId(ctx.getCallerId()); - } - return new SQLFuture>( - transformAsync( - client.splitQuery(ctx, requestBuilder.build()), - new AsyncFunction>() { - @Override - public ListenableFuture> apply( - SplitQueryResponse response) throws Exception { - return Futures.>immediateFuture( - response.getSplitsList()); - } - }, - directExecutor())); - } - public SQLFuture getSrvKeyspace(Context ctx, String keyspace) throws SQLException { GetSrvKeyspaceRequest.Builder requestBuilder = GetSrvKeyspaceRequest.newBuilder().setKeyspace(checkNotNull(keyspace)); diff --git a/java/client/src/main/java/io/vitess/client/VTGateConnection.java b/java/client/src/main/java/io/vitess/client/VTGateConnection.java index 70c977d6f85..75339d4f100 100644 --- a/java/client/src/main/java/io/vitess/client/VTGateConnection.java +++ b/java/client/src/main/java/io/vitess/client/VTGateConnection.java @@ -29,12 +29,9 @@ import io.vitess.client.cursor.SimpleCursor; import io.vitess.client.cursor.StreamCursor; import io.vitess.proto.Query; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; import io.vitess.proto.Vtgate; import io.vitess.proto.Vtgate.ExecuteRequest; import io.vitess.proto.Vtgate.ExecuteResponse; -import io.vitess.proto.Vtgate.SplitQueryRequest; -import io.vitess.proto.Vtgate.SplitQueryResponse; import io.vitess.proto.Vtgate.StreamExecuteRequest; import io.vitess.proto.Vtgate.VStreamRequest; import io.vitess.proto.Vtgate.VStreamResponse; @@ -212,48 +209,6 @@ public Cursor streamExecute(Context ctx, String query, @Nullable Map return new StreamCursor(client.streamExecute(ctx, requestBuilder.build())); } - /** - * This method splits the query into small parts based on the splitColumn and Algorithm type - * provided. - * - * @param ctx Context on user and execution deadline if any. - * @param keyspace Keyspace to execute the query on. - * @param query Sql Query to be executed. - * @param bindVars Parameters to bind with sql. - * @param splitColumns Column to be used to split the data. - * @param splitCount Number of Partitions - * @param numRowsPerQueryPart Limit the number of records per query part. - * @param algorithm EQUAL_SPLITS or FULL_SCAN - * @return SQL Future with Query Parts - * @throws SQLException If anything fails on query execution. - */ - public SQLFuture> splitQuery(Context ctx, String keyspace, - String query, @Nullable Map bindVars, Iterable splitColumns, - int splitCount, int numRowsPerQueryPart, Algorithm algorithm) throws SQLException { - SplitQueryRequest.Builder requestBuilder = - SplitQueryRequest.newBuilder() - .setKeyspace(checkNotNull(keyspace)) - .setQuery(Proto.bindQuery(checkNotNull(query), bindVars)) - .addAllSplitColumn(splitColumns) - .setSplitCount(splitCount) - .setNumRowsPerQueryPart(numRowsPerQueryPart) - .setAlgorithm(algorithm); - - if (ctx.getCallerId() != null) { - requestBuilder.setCallerId(ctx.getCallerId()); - } - - return new SQLFuture<>( - transformAsync(client.splitQuery(ctx, requestBuilder.build()), - new AsyncFunction>() { - @Override - public ListenableFuture> apply( - SplitQueryResponse response) throws Exception { - return Futures.immediateFuture(response.getSplitsList()); - } - }, directExecutor())); - } - /** * Starts streaming the vstream binlog events. * diff --git a/java/client/src/test/java/io/vitess/client/RpcClientTest.java b/java/client/src/test/java/io/vitess/client/RpcClientTest.java index 048fffb4569..4f59f3c177a 100644 --- a/java/client/src/test/java/io/vitess/client/RpcClientTest.java +++ b/java/client/src/test/java/io/vitess/client/RpcClientTest.java @@ -31,14 +31,12 @@ import io.vitess.client.cursor.Row; import io.vitess.proto.Query; import io.vitess.proto.Query.Field; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; import io.vitess.proto.Topodata.KeyRange; import io.vitess.proto.Topodata.KeyspaceIdType; import io.vitess.proto.Topodata.ShardReference; import io.vitess.proto.Topodata.SrvKeyspace; import io.vitess.proto.Topodata.SrvKeyspace.KeyspacePartition; import io.vitess.proto.Topodata.TabletType; -import io.vitess.proto.Vtgate.SplitQueryResponse; import io.vitess.proto.Vtgate.VStreamRequest; import io.vitess.proto.Vtgate.VStreamResponse; import io.vitess.proto.Vtrpc.CallerID; @@ -459,28 +457,6 @@ public void testEchoTransactionExecute() throws Exception { tx.commit(ctx); } - @Test - public void testEchoSplitQuery() throws Exception { - SplitQueryResponse.Part expected = SplitQueryResponse.Part.newBuilder() - .setQuery(Proto.bindQuery( - ECHO_PREFIX + QUERY + ":[split_column1 split_column2]:123:1000:FULL_SCAN", - BIND_VARS)) - .setKeyRangePart(SplitQueryResponse.KeyRangePart.newBuilder().setKeyspace(KEYSPACE).build()) - .build(); - SplitQueryResponse.Part actual = - conn.splitQuery( - ctx, - KEYSPACE, - ECHO_PREFIX + QUERY, - BIND_VARS, - ImmutableList.of("split_column1", "split_column2"), - 123, - 1000, - Algorithm.FULL_SCAN) - .get(0); - Assert.assertEquals(expected, actual); - } - @Test public void testGetSrvKeyspace() throws Exception { SrvKeyspace expected = SrvKeyspace.newBuilder() diff --git a/java/grpc-client/src/main/java/io/vitess/client/grpc/GrpcClient.java b/java/grpc-client/src/main/java/io/vitess/client/grpc/GrpcClient.java index a8285f26806..9396846880a 100644 --- a/java/grpc-client/src/main/java/io/vitess/client/grpc/GrpcClient.java +++ b/java/grpc-client/src/main/java/io/vitess/client/grpc/GrpcClient.java @@ -53,8 +53,6 @@ import io.vitess.proto.Vtgate.GetSrvKeyspaceResponse; import io.vitess.proto.Vtgate.RollbackRequest; import io.vitess.proto.Vtgate.RollbackResponse; -import io.vitess.proto.Vtgate.SplitQueryRequest; -import io.vitess.proto.Vtgate.SplitQueryResponse; import io.vitess.proto.Vtgate.StreamExecuteKeyRangesRequest; import io.vitess.proto.Vtgate.StreamExecuteKeyRangesResponse; import io.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsRequest; @@ -270,13 +268,6 @@ public ListenableFuture rollback(Context ctx, RollbackRequest new ExceptionConverter(), MoreExecutors.directExecutor()); } - @Override - public ListenableFuture splitQuery(Context ctx, SplitQueryRequest request) - throws SQLException { - return Futures.catchingAsync(getFutureStub(ctx).splitQuery(request), Exception.class, - new ExceptionConverter(), MoreExecutors.directExecutor()); - } - @Override public ListenableFuture getSrvKeyspace(Context ctx, GetSrvKeyspaceRequest request) throws SQLException { diff --git a/java/hadoop/pom.xml b/java/hadoop/pom.xml deleted file mode 100644 index 37e815dde24..00000000000 --- a/java/hadoop/pom.xml +++ /dev/null @@ -1,125 +0,0 @@ - - - 4.0.0 - - io.vitess - vitess-parent - 5.0-SNAPSHOT - - vitess-hadoop - - - - com.google.guava - guava - - - com.google.protobuf - protobuf-java - - - - io.vitess - vitess-client - - - io.vitess - vitess-client - test-jar - test - - - io.vitess - vitess-grpc-client - - - - joda-time - joda-time - - - - org.apache.hadoop - hadoop-common - - - org.apache.hadoop - hadoop-mapreduce-client-core - - - org.apache.hadoop - hadoop-mapreduce-client-jobclient - - test-jar - test - - - - com.google.code.gson - gson - - - - - junit - junit - test - - - - - org.apache.hadoop - hadoop-client - test - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 - - false - true - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.1 - - - - integration-test - verify - - - - - false - true - - io.vitess.client.TestEnv - grpc_port - io.vitess.client.grpc.GrpcClientFactory - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - io.vitess:vitess-grpc-client - - org.apache.hadoop:hadoop-client - - - - - - diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/README.md b/java/hadoop/src/main/java/io/vitess/hadoop/README.md deleted file mode 100644 index 9e4a083bd4c..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# Hadoop Integration - -This package contains the necessary implementations for providing Hadoop support on Vitess. This allows mapreducing over tables stored in Vitess from Hadoop. - -Let's look at an example. Consider a table with the following schema that is stored in Vitess across several shards. - -``` -create table sample_table ( -id bigint auto_increment, -name varchar(64), -keyspace_id bigint(20) unsigned NOT NULL, -primary key (id)) Engine=InnoDB; -``` - -Let's say we want to write a MapReduce job that imports this table from Vitess to HDFS where each row is turned into a CSV record in HDFS. - -We can use [VitessInputFormat](https://github.com/vitessio/vitess/blob/master/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java), an implementation of Hadoop's [InputFormat](https://hadoop.apache.org/docs/stable/api/org/apache/hadoop/mapred/InputFormat.html), for that. With VitessInputFormat, rows from the source table are streamed to the mapper task. Each input record has a [NullWritable](https://hadoop.apache.org/docs/current/api/org/apache/hadoop/io/NullWritable.html) key (no key, really), and [RowWritable](https://github.com/vitessio/vitess/blob/master/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java) as value, which is a writable implementation for the entire row's contents. - -Here is an example implementation of our mapper, which transforms each row into a CSV Text. - -```java -public class TableCsvMapper extends - Mapper { - @Override - public void map(NullWritable key, RowWritable value, Context context) - throws IOException, InterruptedException { - Row row = value.getRow(); - StringBuilder asCsv = new StringBuilder(); - asCsv.append(row.getInt("id")); - asCsv.append(","); - asCsv.append(row.getString("name")); - asCsv.append(","); - asCsv.append(row.getULong("keyspace_id")); - context.write(NullWritable.get(), new Text(asCsv.toString())); - } -} -``` - -The controller code for this MR job is shown below. Note that we are not specifying any sharding/replication related information here. VtGate figures out the right number of shards and replicas to fetch the rows from. The MR author only needs to worry about which rows to fetch (query), how to process them (mapper) and the extent of parallelism (splitCount). - -```java -public static void main(String[] args) { - Configuration conf = new Configuration(); - Job job = Job.getInstance(conf, "map vitess table"); - job.setJarByClass(VitessInputFormat.class); - job.setMapperClass(TableCsvMapper.class); - String vtgateAddresses = "localhost:15011,localhost:15012,localhost:15013"; - String keyspace = "SAMPLE_KEYSPACE"; - String query = "select id, name from sample_table"; - int splitCount = 100; - VitessInputFormat.setInput(job, vtgateAddresses, keyspace, query, splitCount); - job.setMapOutputKeyClass(NullWritable.class); - job.setMapOutputValueClass(Text.class); - ...// set reducer and outputpath and launch the job -} -``` - -Refer [this integration test](https://github.com/vitessio/vitess/blob/master/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java) for a working example a MapReduce job on Vitess. - -## How it Works - -VitessInputFormat relies on VtGate's [SplitQuery](https://github.com/vitessio/vitess/blob/21515f5c1a85c0054ddf7d2ff068702670ab93b5/proto/vtgateservice.proto#L98) RPC to obtain the input splits. This RPC method accepts a SplitQueryRequest which consists of an input query and the desired number of splits (splitCount). SplitQuery returns SplitQueryResult, which has a list of SplitQueryParts. SplitQueryPart consists of a KeyRangeQuery and a size estimate of how many rows this sub-query might return. SplitQueryParts return rows that are mutually exclusive and collectively exhaustive - all rows belonging to the original input query will be returned by one and exactly one SplitQueryPart. - -VitessInputFormat turns each SplitQueryPart into a mapper task. The number of splits generated may not be exactly equal to the desired split count specified in the input. Specifically, if the desired split count is not a multiple of the number of shards, then VtGate will round it up to the next bigger multiple of number of shards. - -In addition to splitting the query, the SplitQuery service also acts as a gatekeeper that rejects queries unsuitable for MapReduce. Queries with potentially expensive operations such as Joins, Group By, inner queries, Distinct, Order By, etc are not allowed. Specifically, only input queries of the following syntax are permitted. - -``` -select [list of columns] from table where [list of simple column filters]; -``` - -There are additional constraints on the table schema to ensure that the sub queries do not result in full table scans. The table must have a primary key (simple or composite) and the leading primary key must be of one of the following types. -``` -VT_TINY, VT_SHORT, VT_LONG, VT_LONGLONG, VT_INT24, VT_FLOAT, VT_DOUBLE -``` - -#### Split Generation - -Here's how SplitQuery works. VtGate forwards the input query to randomly chosen ‘rdonly’ vttablets in each shard with a split count, M = original split count / N, where N is the number of shards. Each vttablet parses the query and rejects it if it does not meet the constraints. If it is a valid query, the tablet fetches the min and max value for the leading primary key column from MySQL. Split the [min, max] range of into M intervals. Construct subqueries by appending where clauses corresponding to PK range intervals to the original query and return it to VtGate. VtGate aggregates the splits received from tablets and constructs KeyRangeQueries by appending KeyRange corresponding to that shard. The following diagram depicts this flow for a sample request of split size 6 on a cluster with two shards. - -![Image](SplitQuery.png) - -## Other Considerations - -1. Specifying splitCount - Each split is a streaming query executed by a single mapper task. splitCount determines the number of mapper tasks that will be created and thus the extent of parallelism. Having too few, but long-running, splits would limit the throughput of the MR job as a whole. Long-running splits also makes retries of individual tasks failures more expensive as compared to leaner splits. On the other side, having too many splits can lead to extra overhead in task setup and connection overhead with VtGate. So, identifying the ideal split count is a balance between the two. - -2. Joining multiple tables - Currently Vitess does not currently mapping over joined tables. However, this can be easily achieved by writing a multi-mapper MapReduce job and performing a reduce-side join in the MR job. - -3. Views - Database Views are not great for full-table scans. If you need to map over a View, consider mapping over the underlying tables instead. - - - - - - - diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java b/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java deleted file mode 100644 index ca91d512ed9..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import com.google.common.collect.Lists; -import com.google.common.io.BaseEncoding; -import com.google.gson.Gson; - -import io.vitess.client.cursor.Row; -import io.vitess.proto.Query; -import io.vitess.proto.Query.Field; - -import org.apache.hadoop.io.Writable; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class RowWritable implements Writable { - - private Row row; - - public RowWritable() { - } - - public RowWritable(Row row) { - this.row = row; - } - - public Row get() { - return row; - } - - @Override - public void write(DataOutput out) throws IOException { - out.writeInt(row.getFields().size()); - for (Field field : row.getFields()) { - out.writeUTF(BaseEncoding.base64().encode(field.toByteArray())); - } - out.writeUTF(BaseEncoding.base64().encode(row.getRowProto().toByteArray())); - } - - @Override - public void readFields(DataInput in) throws IOException { - int numFields = in.readInt(); - List fields = Lists.newArrayListWithCapacity(numFields); - for (int i = 0; i < numFields; i++) { - fields.add(Field.parseFrom(BaseEncoding.base64().decode(in.readUTF()))); - } - Query.Row rowProto = Query.Row.parseFrom(BaseEncoding.base64().decode(in.readUTF())); - row = new Row(fields, rowProto); - } - - @Override - public String toString() { - List fields = row.getFields(); - - Map map = new HashMap<>(); - for (int i = 0; i < fields.size(); i++) { - String key = fields.get(i).getName(); - // Prefer the first column if there are duplicate names. - // This matches JDBC ResultSet behavior. - if (!map.containsKey(key)) { - try { - map.put(key, row.getRawValue(i + 1).toStringUtf8()); - } catch (SQLException exc) { - map.put(key, exc.toString()); - } - } - } - - return new Gson().toJson(map); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/SplitQuery.png b/java/hadoop/src/main/java/io/vitess/hadoop/SplitQuery.png deleted file mode 100644 index 1040e0b2ae637a3e17c466733bd807423d4e11e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33951 zcmcG#cU)6l^Dk-xDI!e-sR9B5(xi9k9h4%7RFNjVhENql?;Qn#B2~Hw1QI|(uaP1_ zs-a6s=mA2xi9XMJ-gD0He(qoQpX_Aqz1Ga0nf0BrCNYooH7Lj!$u3>GM4_drYIy0= zW%Q*>SL8{E@gt`uIog*ly}qQSs$?86wb??c3)^^x1?KxWOvk9%3%K{h`dM*h8oSnf zGlw^8ki_ImMCTfau> zEwQAc^$s1aZUEq?<;=3*i0D9;kL7C9$_fw+bjS|7F>l~~-C^b3DncF`5`x)izL3^1 zk2sz-4w(NAxD|S&5tbSg6Z5;h3?n~p_}a2bk5;S}G_DCzJlDzRV?OJkqot)CdrRAy z1J@&o$iY8~zkc)-6%ly?A^qpdI|F(Qd6%7ie+EQFCW(JOBIn=he@%+Wfe&?ds-n0- z|6c!{^cccBqpPcX%U${YKiB_IipU|3`b+bFr2St^kh0%$=jP@nll*)A&lmi_e+Uu? z{~sK%q~}wX+>Np~}BlkBO<*JqtAwyE`{^+yIrUOSmhtnL;`bbhRf% z9As7>^%pyR`y=Q^*OM9RGdmQ8hi*?Snk@|FK`vBe7~wG-GQBbXCZ!t}cmq^?r?dDc z=sAW*7qat=pX@eX;&`1wyPenGvNel}we19jF`gW2gssy8@-N^eq4K-TMIa_{An)5m zD*nV;+vOUNUBPgMDkRN91@luvpHW~Y>>Q8ulwd+2wSA8-ig>Il$<*O1V8s!)sU^<9H`AyWwZ zH@1`3J~(~LZ#}i+x97nZQ;-!v2&QvZB)ae}*+fvt~=;RaZ@^~4c8ITiVYsfTTO6mKjUPd_hJh$U=li-9m$3_?VJ0= z6a}gavGEMzka{6)9IKv$vuw_q;qCse>7K?<@Sk`D$woj&!B@( z$)Tl9AR$U> zSJ^iM5>h9$0m`4)H=q4$!rCNZ%8%;jX+lbEkJD5k2|Ka6e8E)OTWmiR4EM*( z4XEHmJL7hq`BdT{fX;!J21RZh4O%$Gc&Eeh2Mzk(M{iJP3Y%?ynPe3rc4O*$TNvgX z=X4LWOrBVuAin?h6Vp{{GPiG#WM`LK?gV^76F;yDKYiU;F~=JJ72}q{IkPt1;8m}|NLnt1EH`0GKah%44+682iwk9>2dY&iVTGM-Y-aX^0)v5?j7kafx z48uXiKkUnJM^j&We!cwo#T~AhwUP#P3wpOV+b3ru+f&;i`+h2(JBrGSwh1J3?(wdX ztE)j5GBU9D@C4i*6ZK(8^nI(1aIyE_k(*ITBS{xjsEOJuwr|ILuQjpcK6OJ)Tn>KU z_FmXYn9rECp7j#mDwI)GO5xb@kY4{zPhM2F4L6Cn`p1n^7u}XoD*oc@N|VN4w|?=E zd`JT>NfFN@%dBMHKO>@=SJ7m{zTR7W|Fq&W`wZVGgx-Q4%*9A8bHYrDSb7r2Ocqk4!epsVO;1{d`fpTk%8A6$)vw^~Va^Fvf_Qgd1|@c?BAHSbq$0 z$^B*D{y}3ouI*A>pv@HhD-AW3qs=Q1gpa7ypr7f_qac$+3fnAUA8*4NR!5H~B;sA^ zpO8Mk)x|A8k$;nzx`DjrQFL$;ZS_to>($F|J%7L8IsEB6EXsH%pz@f$;kC~Al?POA z&`abXS=Y>b1-CJTZmL>0B>QPT1O&5xMp{gì+dM7 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%0DAlP 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 z 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 getSplitColumns() { - return conf.getStringCollection(SPLIT_COLUMNS); - } - - public void setSplitColumns(Collection splitColumns) { - conf.setStrings(SPLIT_COLUMNS, splitColumns.toArray(new String[0])); - } - - public int getNumRowsPerQueryPart() { - return conf.getInt(NUM_ROWS_PER_QUERY_PART, 100000); - } - - public void setNumRowsPerQueryPart(int numRowsPerQueryPart) { - conf.setInt(NUM_ROWS_PER_QUERY_PART, numRowsPerQueryPart); - } - - public SplitQueryRequest.Algorithm getAlgorithm() { - return conf.getEnum(ALGORITHM, SplitQueryRequest.Algorithm.EQUAL_SPLITS); - } - - public void setAlgorithm(SplitQueryRequest.Algorithm algorithm) { - conf.setEnum(ALGORITHM, algorithm); - } - - public String getRpcFactoryClass() { - return conf.get(RPC_FACTORY_CLASS); - } - - public void setRpcFactoryClass(Class clz) { - conf.set(RPC_FACTORY_CLASS, clz.getName()); - } - - public Query.ExecuteOptions.IncludedFields getIncludedFields() { - return conf.getEnum(INCLUDED_FIELDS, Query.ExecuteOptions.IncludedFields.TYPE_AND_NAME); - } - - public void setIncludedFields(Query.ExecuteOptions.IncludedFields includedFields) { - conf.setEnum(INCLUDED_FIELDS, includedFields); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java b/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java deleted file mode 100644 index 5a9326980d8..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.collect.Lists; - -import io.vitess.client.Context; -import io.vitess.client.RpcClient; -import io.vitess.client.RpcClientFactory; -import io.vitess.client.VTGateBlockingConn; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; -import io.vitess.proto.Vtgate.SplitQueryResponse; - -import org.apache.hadoop.io.NullWritable; -import org.apache.hadoop.mapreduce.InputFormat; -import org.apache.hadoop.mapreduce.InputSplit; -import org.apache.hadoop.mapreduce.Job; -import org.apache.hadoop.mapreduce.JobContext; -import org.apache.hadoop.mapreduce.RecordReader; -import org.apache.hadoop.mapreduce.TaskAttemptContext; - -import org.joda.time.Duration; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Random; - -/** - * {@link VitessInputFormat} is the {@link org.apache.hadoop.mapreduce.InputFormat} for tables in - * Vitess. Input splits ({@link VitessInputSplit}) are fetched from VtGate via an RPC. map() calls - * are supplied with a {@link RowWritable}. - */ -public class VitessInputFormat extends InputFormat { - - @Override - public List getSplits(JobContext context) { - VitessConf conf = new VitessConf(context.getConfiguration()); - List splitResult; - try { - @SuppressWarnings("unchecked") - Class rpcFactoryClass = - (Class) Class.forName(conf.getRpcFactoryClass()); - List addressList = Arrays.asList(conf.getHosts().split(",")); - int index = new Random().nextInt(addressList.size()); - - RpcClient rpcClient = rpcFactoryClass.newInstance().create( - Context.getDefault().withDeadlineAfter(Duration.millis(conf.getTimeoutMs())), - addressList.get(index)); - - try (VTGateBlockingConn vtgate = new VTGateBlockingConn(rpcClient)) { - splitResult = vtgate.splitQuery( - Context.getDefault(), - conf.getKeyspace(), - conf.getInputQuery(), - null /* bind vars */, - conf.getSplitColumns(), - conf.getSplitCount(), - conf.getNumRowsPerQueryPart(), - conf.getAlgorithm()); - } - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException - | IOException exc) { - throw new RuntimeException(exc); - } - - List splits = Lists.newArrayList(); - for (SplitQueryResponse.Part part : splitResult) { - splits.add(new VitessInputSplit(part)); - } - - for (InputSplit split : splits) { - ((VitessInputSplit) split).setLocations(conf.getHosts().split(VitessConf.HOSTS_DELIM)); - } - return splits; - } - - @Override - public RecordReader createRecordReader(InputSplit split, - TaskAttemptContext context) throws IOException, InterruptedException { - return new VitessRecordReader(); - } - - /** - * Sets the necessary configurations for Vitess table input source - */ - public static void setInput( - Job job, - String hosts, - String keyspace, - String query, - Collection splitColumns, - int splitCount, - int numRowsPerQueryPart, - Algorithm algorithm, - Class rpcFactoryClass) { - job.setInputFormatClass(VitessInputFormat.class); - VitessConf vtConf = new VitessConf(job.getConfiguration()); - vtConf.setHosts(checkNotNull(hosts)); - vtConf.setKeyspace(checkNotNull(keyspace)); - vtConf.setInputQuery(checkNotNull(query)); - vtConf.setSplitColumns(checkNotNull(splitColumns)); - vtConf.setSplitCount(splitCount); - vtConf.setNumRowsPerQueryPart(numRowsPerQueryPart); - vtConf.setAlgorithm(checkNotNull(algorithm)); - vtConf.setRpcFactoryClass(checkNotNull(rpcFactoryClass)); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java b/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java deleted file mode 100644 index 251bdf1f763..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import com.google.common.io.BaseEncoding; - -import io.vitess.proto.Vtgate.SplitQueryResponse; - -import org.apache.hadoop.io.Writable; -import org.apache.hadoop.mapreduce.InputSplit; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - -public class VitessInputSplit extends InputSplit implements Writable { - - private String[] locations; - private SplitQueryResponse.Part split; - - public VitessInputSplit(SplitQueryResponse.Part split) { - this.split = split; - } - - public VitessInputSplit() { - } - - public SplitQueryResponse.Part getSplit() { - return split; - } - - public void setLocations(String[] locations) { - this.locations = locations; - } - - @Override - public long getLength() throws IOException, InterruptedException { - return split.getSize(); - } - - @Override - public String[] getLocations() throws IOException, InterruptedException { - return locations; - } - - @Override - public void readFields(DataInput input) throws IOException { - split = SplitQueryResponse.Part.parseFrom(BaseEncoding.base64().decode(input.readUTF())); - } - - @Override - public void write(DataOutput output) throws IOException { - output.writeUTF(BaseEncoding.base64().encode(split.toByteArray())); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java b/java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java deleted file mode 100644 index 0f649f281e6..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import io.vitess.client.Context; -import io.vitess.client.RpcClient; -import io.vitess.client.RpcClientFactory; -import io.vitess.client.VTGateBlockingConn; -import io.vitess.client.cursor.Cursor; -import io.vitess.client.cursor.Row; -import io.vitess.proto.Query; -import io.vitess.proto.Query.BoundQuery; -import io.vitess.proto.Topodata.TabletType; -import io.vitess.proto.Vtgate.SplitQueryResponse; - -import org.apache.hadoop.io.NullWritable; -import org.apache.hadoop.mapreduce.InputSplit; -import org.apache.hadoop.mapreduce.RecordReader; -import org.apache.hadoop.mapreduce.TaskAttemptContext; - -import org.joda.time.Duration; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -public class VitessRecordReader extends RecordReader { - - private VitessInputSplit split; - private VTGateBlockingConn vtgate; - private VitessConf conf; - private long rowsProcessed = 0; - private Cursor cursor; - private RowWritable currentRow; - private Query.ExecuteOptions.IncludedFields includedFields; - - /** - * Fetch connection parameters from Configuration and open VtGate connection. - */ - @Override - public void initialize(InputSplit split, TaskAttemptContext context) - throws IOException, InterruptedException { - this.split = (VitessInputSplit) split; - conf = new VitessConf(context.getConfiguration()); - try { - @SuppressWarnings("unchecked") - Class rpcFactoryClass = - (Class) Class.forName(conf.getRpcFactoryClass()); - List addressList = Arrays.asList(conf.getHosts().split(",")); - int index = new Random().nextInt(addressList.size()); - - RpcClient rpcClient = rpcFactoryClass.newInstance().create( - Context.getDefault().withDeadlineAfter(Duration.millis(conf.getTimeoutMs())), - addressList.get(index)); - vtgate = new VTGateBlockingConn(rpcClient); - includedFields = conf.getIncludedFields(); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException exc) { - throw new RuntimeException(exc); - } - } - - @Override - public void close() throws IOException { - if (vtgate != null) { - try { - vtgate.close(); - vtgate = null; - } catch (IOException exc) { - throw new RuntimeException(exc); - } - } - } - - @Override - public NullWritable getCurrentKey() throws IOException, InterruptedException { - return NullWritable.get(); - } - - @Override - public RowWritable getCurrentValue() throws IOException, InterruptedException { - return currentRow; - } - - @Override - public float getProgress() throws IOException, InterruptedException { - if (rowsProcessed > split.getLength()) { - return 0.9f; - } - return rowsProcessed / split.getLength(); - } - - /** - * Fetches the next row. If this is the first invocation for the split, execute the streaming - * query. Subsequent calls just advance the iterator. - */ - @Override - public boolean nextKeyValue() throws IOException, InterruptedException { - try { - if (cursor == null) { - SplitQueryResponse.Part splitInfo = split.getSplit(); - if (splitInfo.hasKeyRangePart()) { - BoundQuery query = splitInfo.getQuery(); - SplitQueryResponse.KeyRangePart keyRangePart = splitInfo.getKeyRangePart(); - cursor = vtgate.streamExecuteKeyRanges(Context.getDefault(), query.getSql(), - keyRangePart.getKeyspace(), keyRangePart.getKeyRangesList(), query.getBindVariables(), - TabletType.RDONLY, includedFields); - } else if (splitInfo.hasShardPart()) { - BoundQuery query = splitInfo.getQuery(); - SplitQueryResponse.ShardPart shardPart = splitInfo.getShardPart(); - cursor = vtgate.streamExecuteShards(Context.getDefault(), query.getSql(), - shardPart.getKeyspace(), shardPart.getShardsList(), query.getBindVariables(), - TabletType.RDONLY, includedFields); - } else { - throw new IllegalArgumentException("unknown split info: " + splitInfo); - } - } - Row row = cursor.next(); - if (row == null) { - currentRow = null; - } else { - currentRow = new RowWritable(row); - } - } catch (SQLException exc) { - throw new RuntimeException(exc); - } - - if (currentRow == null) { - return false; - } - rowsProcessed++; - return true; - } -} diff --git a/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java b/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java deleted file mode 100644 index 01e20b6eaf4..00000000000 --- a/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import com.google.common.collect.ImmutableList; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import io.vitess.client.TestEnv; -import io.vitess.client.TestUtil; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; - -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; -import org.apache.hadoop.io.IntWritable; -import org.apache.hadoop.io.NullWritable; -import org.apache.hadoop.mapred.HadoopTestCase; -import org.apache.hadoop.mapreduce.Job; -import org.apache.hadoop.mapreduce.MapReduceTestUtil; -import org.apache.hadoop.mapreduce.Mapper; -import org.apache.hadoop.mapreduce.Reducer; -import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; -import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.sql.SQLException; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import junit.extensions.TestSetup; -import junit.framework.TestSuite; -import vttest.Vttest.Keyspace; -import vttest.Vttest.Shard; -import vttest.Vttest.VTTestTopology; - - -/** - * Integration tests for MapReductions in Vitess. These tests use an in-process Hadoop cluster via - * {@link HadoopTestCase}. These tests are JUnit3 style because of this dependency. Vitess setup for - * these tests require at least one rdonly instance per shard. - */ -public class MapReduceIT extends HadoopTestCase { - - private static final int NUM_ROWS = 40; - - public static TestEnv testEnv = getTestEnv(); - - public MapReduceIT() throws IOException { - super(HadoopTestCase.LOCAL_MR, HadoopTestCase.LOCAL_FS, 1, 1); - } - - /** - * Run a mapper only MR job and verify all the rows in the source table were outputted into HDFS. - */ - public void testDumpTableToHDFS() throws Exception { - // Configurations for the job, output from mapper as Text - Configuration conf = createJobConf(); - Job job = Job.getInstance(conf); - job.setJobName("table"); - job.setJarByClass(VitessInputFormat.class); - job.setMapperClass(TableMapper.class); - VitessInputFormat.setInput( - job, - "localhost:" + testEnv.getPort(), - testEnv.getKeyspace(), - "select id, name, age from vtgate_test", - ImmutableList.of(), - 4 /* splitCount */, - 0 /* numRowsPerQueryPart */, - Algorithm.EQUAL_SPLITS, - TestUtil.getRpcClientFactory().getClass()); - job.setOutputKeyClass(NullWritable.class); - job.setOutputValueClass(RowWritable.class); - job.setOutputFormatClass(TextOutputFormat.class); - job.setNumReduceTasks(0); - - Path outDir = new Path(testEnv.getTestOutputPath(), "mrvitess/output"); - FileSystem fs = FileSystem.get(conf); - if (fs.exists(outDir)) { - fs.delete(outDir, true); - } - FileOutputFormat.setOutputPath(job, outDir); - - job.waitForCompletion(true); - assertTrue(job.isSuccessful()); - - String[] outputLines = MapReduceTestUtil.readOutput(outDir, conf).split("\n"); - // there should be one line per row in the source table - assertEquals(NUM_ROWS, outputLines.length); - Set actualAges = new HashSet<>(); - Set actualNames = new HashSet<>(); - - // Parse and verify we've gotten all the ages and rows. - Gson gson = new Gson(); - for (String line : outputLines) { - String[] parts = line.split("\t"); - actualAges.add(Long.valueOf(parts[0])); - - // Rows are written as JSON since this is TextOutputFormat. - String rowJson = parts[1]; - Type mapType = new TypeToken>() { - }.getType(); - @SuppressWarnings("unchecked") - Map map = (Map) gson.fromJson(rowJson, mapType); - actualNames.add(map.get("name")); - } - - Set expectedAges = new HashSet<>(); - Set expectedNames = new HashSet<>(); - for (long i = 1; i <= NUM_ROWS; i++) { - // Generate values that match TestUtil.insertRows(). - expectedAges.add(i % 10); - expectedNames.add("name_" + i); - } - assertEquals(expectedAges.size(), actualAges.size()); - assertTrue(actualAges.containsAll(expectedAges)); - assertEquals(NUM_ROWS, actualNames.size()); - assertTrue(actualNames.containsAll(expectedNames)); - } - - /** - * Map all rows and aggregate by age at the reducer. - */ - public void testReducerAggregateRows() throws Exception { - Configuration conf = createJobConf(); - - Job job = Job.getInstance(conf); - job.setJobName("table"); - job.setJarByClass(VitessInputFormat.class); - job.setMapperClass(TableMapper.class); - VitessInputFormat.setInput( - job, - "localhost:" + testEnv.getPort(), - testEnv.getKeyspace(), - "select id, name, age from vtgate_test", - ImmutableList.of(), - 1 /* splitCount */, - 0 /* numRowsPerQueryPart */, - Algorithm.EQUAL_SPLITS, - TestUtil.getRpcClientFactory().getClass()); - - job.setMapOutputKeyClass(IntWritable.class); - job.setMapOutputValueClass(RowWritable.class); - - job.setReducerClass(CountReducer.class); - job.setOutputKeyClass(NullWritable.class); - job.setOutputValueClass(IntWritable.class); - job.setOutputFormatClass(TextOutputFormat.class); - - Path outDir = new Path(testEnv.getTestOutputPath(), "mrvitess/output"); - FileSystem fs = FileSystem.get(conf); - if (fs.exists(outDir)) { - fs.delete(outDir, true); - } - FileOutputFormat.setOutputPath(job, outDir); - - job.waitForCompletion(true); - assertTrue(job.isSuccessful()); - - String[] outputLines = MapReduceTestUtil.readOutput(outDir, conf).split("\n"); - // There should be 10 different ages, because age = i % 10. - assertEquals(10, outputLines.length); - // All rows should be accounted for. - int totalRowsReduced = 0; - for (String line : outputLines) { - totalRowsReduced += Integer.parseInt(line); - } - assertEquals(NUM_ROWS, totalRowsReduced); - } - - public static class TableMapper - extends Mapper { - - @Override - public void map(NullWritable key, RowWritable value, Context context) - throws IOException, InterruptedException { - // Tag each record with its age. - try { - context.write(new IntWritable(value.get().getInt("age")), value); - } catch (SQLException e) { - throw new IOException(e); - } - } - } - - public static class CountReducer - extends Reducer { - - @Override - public void reduce(IntWritable key, Iterable values, Context context) - throws IOException, InterruptedException { - // Count how many records there are for each age. - int count = 0; - Iterator itr = values.iterator(); - while (itr.hasNext()) { - count++; - itr.next(); - } - context.write(NullWritable.get(), new IntWritable(count)); - } - } - - /** - * Create env with two shards each having a master, replica, and rdonly. - */ - static TestEnv getTestEnv() { - Keyspace keyspace = Keyspace.newBuilder().setName("test_keyspace") - .addShards(Shard.newBuilder().setName("-80").build()) - .addShards(Shard.newBuilder().setName("80-").build()).build(); - VTTestTopology topology = VTTestTopology.newBuilder().addKeyspaces(keyspace).build(); - TestEnv env = TestUtil.getTestEnv("test_keyspace", topology); - return env; - } - - public static TestSetup suite() { - return new TestSetup(new TestSuite(MapReduceIT.class)) { - - @Override - protected void setUp() throws Exception { - TestUtil.setupTestEnv(testEnv); - // Insert test rows - TestUtil.insertRows(testEnv, 1, NUM_ROWS); - } - - @Override - protected void tearDown() throws Exception { - TestUtil.teardownTestEnv(testEnv); - } - }; - - } -} diff --git a/java/pom.xml b/java/pom.xml index c433b863d81..32f056548da 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -14,8 +14,8 @@ 5.0-SNAPSHOT pom - Vitess Java Client libraries and Hadoop support [Parent] - Java client libraries and Hadoop support for Vitess - a scalable clustering system for MySQL + Vitess Java Client libraries [Parent] + Java client libraries for Vitess - a scalable clustering system for MySQL http://vitess.io 2014 - test-jar - test - org.codehaus.mojo From c73d6dd3369ae3793e9eefee489daae75b7d6e22 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 8 Mar 2020 22:37:02 -0700 Subject: [PATCH 297/825] deprecation: delete UpdateStream code Signed-off-by: Sugu Sougoumarane --- .../vtgateclienttest/goclienttest/callerid.go | 4 - go/cmd/vtgateclienttest/goclienttest/echo.go | 22 - .../vtgateclienttest/goclienttest/errors.go | 26 - go/cmd/vtgateclienttest/services/callerid.go | 7 - go/cmd/vtgateclienttest/services/echo.go | 22 - go/cmd/vtgateclienttest/services/errors.go | 7 - go/cmd/vtgateclienttest/services/fallback.go | 4 - go/vt/proto/query/query.pb.go | 507 +++++++----------- go/vt/proto/queryservice/queryservice.pb.go | 144 ++--- go/vt/proto/vtgate/vtgate.pb.go | 384 ++++--------- go/vt/proto/vtgateservice/vtgateservice.pb.go | 140 ++--- go/vt/vitessdriver/fakeserver_test.go | 5 - go/vt/vtcombo/tablet_map.go | 6 - go/vt/vtctl/query.go | 60 --- go/vt/vtgate/fakerpcvtgateconn/conn.go | 5 - go/vt/vtgate/grpcvtgateconn/conn.go | 31 -- go/vt/vtgate/grpcvtgateconn/suite_test.go | 209 -------- go/vt/vtgate/grpcvtgateservice/server.go | 22 - go/vt/vtgate/resolver.go | 39 -- go/vt/vtgate/scatter_conn.go | 6 - go/vt/vtgate/vtgate.go | 35 -- go/vt/vtgate/vtgateconn/vtgateconn.go | 18 - go/vt/vtgate/vtgateservice/interface.go | 1 - go/vt/vttablet/grpcqueryservice/server.go | 15 - go/vt/vttablet/grpctabletconn/conn.go | 43 -- go/vt/vttablet/queryservice/queryservice.go | 3 - go/vt/vttablet/queryservice/wrapped.go | 7 - go/vt/vttablet/sandboxconn/sandboxconn.go | 5 - .../tabletconntest/fakequeryservice.go | 72 --- .../vttablet/tabletconntest/tabletconntest.go | 94 ---- go/vt/vttablet/tabletserver/tabletserver.go | 65 +-- proto/query.proto | 22 - proto/queryservice.proto | 3 - proto/vtgate.proto | 43 -- proto/vtgateservice.proto | 4 - 35 files changed, 387 insertions(+), 1693 deletions(-) diff --git a/go/cmd/vtgateclienttest/goclienttest/callerid.go b/go/cmd/vtgateclienttest/goclienttest/callerid.go index e46ec5e5a92..98aff6ed552 100644 --- a/go/cmd/vtgateclienttest/goclienttest/callerid.go +++ b/go/cmd/vtgateclienttest/goclienttest/callerid.go @@ -92,10 +92,6 @@ func testCallerID(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgateconn err = getStreamError(conn.StreamExecuteKeyRanges(ctx, query, "", nil, nil, topodatapb.TabletType_MASTER, nil)) checkCallerIDError(t, "StreamExecuteKeyRanges", err) - - // test UpdateStream forwards the callerID - err = getUpdateStreamError(conn.UpdateStream(ctx, "", query, nil, topodatapb.TabletType_MASTER, 0, nil)) - checkCallerIDError(t, "UpdateStream", err) } func checkCallerIDError(t *testing.T, name string, err error) { diff --git a/go/cmd/vtgateclienttest/goclienttest/echo.go b/go/cmd/vtgateclienttest/goclienttest/echo.go index a3ba6e30cba..31236ce911d 100644 --- a/go/cmd/vtgateclienttest/goclienttest/echo.go +++ b/go/cmd/vtgateclienttest/goclienttest/echo.go @@ -99,8 +99,6 @@ var ( } optionsEcho = "include_event_token:true compare_event_token:<" + eventTokenEcho + "> included_fields:TYPE_ONLY " extrasEcho = "event_token:<" + eventTokenEcho + "> fresher:true " - - updateStreamEcho = "map[callerId:" + callerIDEcho + " event:" + eventTokenEcho + " keyRange:" + keyRangeZeroEcho + " keyspace:conn_ks shard:echo://" + query + " tabletType:REPLICA timestamp:0]" ) // testEcho exercises the test cases provided by the "echo" service. @@ -108,7 +106,6 @@ func testEcho(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgateconn.VTG testEchoExecute(t, conn, session) testEchoStreamExecute(t, conn, session) testEchoTransactionExecute(t, conn) - testEchoUpdateStream(t, conn) } func testEchoExecute(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgateconn.VTGateSession) { @@ -401,25 +398,6 @@ func testEchoTransactionExecute(t *testing.T, conn *vtgateconn.VTGateConn) { }) } -func testEchoUpdateStream(t *testing.T, conn *vtgateconn.VTGateConn) { - var stream vtgateconn.UpdateStreamReader - var err error - - ctx := callerid.NewContext(context.Background(), callerID, nil) - - stream, err = conn.UpdateStream(ctx, "conn_ks", echoPrefix+query, keyRanges[0], tabletType, 0, eventToken) - if err != nil { - t.Fatal(err) - } - se, _, err := stream.Recv() - if err != nil { - t.Fatal(err) - } - if se.EventToken.Position != updateStreamEcho { - t.Errorf("UpdateStream(0) =\n%v, want\n%v", se.EventToken.Position, updateStreamEcho) - } -} - // getEcho extracts the echoed field values from a query result. func getEcho(qr *sqltypes.Result) map[string]sqltypes.Value { values := map[string]sqltypes.Value{} diff --git a/go/cmd/vtgateclienttest/goclienttest/errors.go b/go/cmd/vtgateclienttest/goclienttest/errors.go index ec989698eb4..93327e12dae 100644 --- a/go/cmd/vtgateclienttest/goclienttest/errors.go +++ b/go/cmd/vtgateclienttest/goclienttest/errors.go @@ -52,7 +52,6 @@ func testErrors(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgateconn.V testExecuteErrors(t, conn, session) testStreamExecuteErrors(t, conn, session) testTransactionExecuteErrors(t, conn) - testUpdateStreamErrors(t, conn) } func testExecuteErrors(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgateconn.VTGateSession) { @@ -123,14 +122,6 @@ func testStreamExecuteErrors(t *testing.T, conn *vtgateconn.VTGateConn, session }) } -func testUpdateStreamErrors(t *testing.T, conn *vtgateconn.VTGateConn) { - ctx := context.Background() - - checkStreamExecuteErrors(t, func(query string) error { - return getUpdateStreamError(conn.UpdateStream(ctx, "", query, nil, tabletType, 0, nil)) - }) -} - func testTransactionExecuteErrors(t *testing.T, conn *vtgateconn.VTGateConn) { ctx := context.Background() @@ -195,23 +186,6 @@ func getStreamError(stream sqltypes.ResultStream, err error) error { } } -func getUpdateStreamError(stream vtgateconn.UpdateStreamReader, err error) error { - if err != nil { - return err - } - for { - _, _, err := stream.Recv() - switch err { - case nil: - // keep going - case io.EOF: - return nil - default: - return err - } - } -} - func checkExecuteErrors(t *testing.T, execute func(string) error) { for errStr, errCode := range executeErrors { query := errorPrefix + errStr diff --git a/go/cmd/vtgateclienttest/services/callerid.go b/go/cmd/vtgateclienttest/services/callerid.go index a13830cbaff..ec8a8a16fc5 100644 --- a/go/cmd/vtgateclienttest/services/callerid.go +++ b/go/cmd/vtgateclienttest/services/callerid.go @@ -180,10 +180,3 @@ func (c *callerIDClient) MessageAck(ctx context.Context, keyspace string, name s } return c.fallback.MessageAck(ctx, keyspace, name, ids) } - -func (c *callerIDClient) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - if ok, err := c.checkCallerID(ctx, shard); ok { - return err - } - return c.fallbackClient.UpdateStream(ctx, keyspace, shard, keyRange, tabletType, timestamp, event, callback) -} diff --git a/go/cmd/vtgateclienttest/services/echo.go b/go/cmd/vtgateclienttest/services/echo.go index aeaa4b0c0df..f6725c994a6 100644 --- a/go/cmd/vtgateclienttest/services/echo.go +++ b/go/cmd/vtgateclienttest/services/echo.go @@ -340,28 +340,6 @@ func (c *echoClient) MessageAckKeyspaceIds(ctx context.Context, keyspace string, return c.fallback.MessageAckKeyspaceIds(ctx, keyspace, name, idKeyspaceIDs) } -func (c *echoClient) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - if strings.HasPrefix(shard, EchoPrefix) { - m := map[string]interface{}{ - "callerId": callerid.EffectiveCallerIDFromContext(ctx), - "keyspace": keyspace, - "shard": shard, - "keyRange": keyRange, - "timestamp": timestamp, - "tabletType": tabletType, - "event": event, - } - bytes := printSortedMap(reflect.ValueOf(m)) - callback(&querypb.StreamEvent{ - EventToken: &querypb.EventToken{ - Position: string(bytes), - }, - }, 0) - return nil - } - return c.fallbackClient.UpdateStream(ctx, keyspace, shard, keyRange, tabletType, timestamp, event, callback) -} - func (c *echoClient) VStream(ctx context.Context, tabletType topodatapb.TabletType, vgtid *binlogdatapb.VGtid, filter *binlogdatapb.Filter, callback func([]*binlogdatapb.VEvent) error) error { if strings.HasPrefix(vgtid.ShardGtids[0].Shard, EchoPrefix) { _ = callback([]*binlogdatapb.VEvent{ diff --git a/go/cmd/vtgateclienttest/services/errors.go b/go/cmd/vtgateclienttest/services/errors.go index 53a2684c1e1..e5626cc4fba 100644 --- a/go/cmd/vtgateclienttest/services/errors.go +++ b/go/cmd/vtgateclienttest/services/errors.go @@ -296,10 +296,3 @@ func (c *errorClient) GetSrvKeyspace(ctx context.Context, keyspace string) (*top } return c.fallbackClient.GetSrvKeyspace(ctx, keyspace) } - -func (c *errorClient) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - if err := requestToError(shard); err != nil { - return err - } - return c.fallbackClient.UpdateStream(ctx, keyspace, shard, keyRange, tabletType, timestamp, event, callback) -} diff --git a/go/cmd/vtgateclienttest/services/fallback.go b/go/cmd/vtgateclienttest/services/fallback.go index 5597078e645..9196030b1a7 100644 --- a/go/cmd/vtgateclienttest/services/fallback.go +++ b/go/cmd/vtgateclienttest/services/fallback.go @@ -124,10 +124,6 @@ func (c fallbackClient) VStream(ctx context.Context, tabletType topodatapb.Table return c.fallback.VStream(ctx, tabletType, vgtid, filter, send) } -func (c fallbackClient) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - return c.fallback.UpdateStream(ctx, keyspace, shard, keyRange, tabletType, timestamp, event, callback) -} - func (c fallbackClient) HandlePanic(err *error) { c.fallback.HandlePanic(err) } diff --git a/go/vt/proto/query/query.pb.go b/go/vt/proto/query/query.pb.go index 1a6cd4af77a..120dcb99879 100644 --- a/go/vt/proto/query/query.pb.go +++ b/go/vt/proto/query/query.pb.go @@ -3780,124 +3780,6 @@ func (m *StreamHealthResponse) GetTabletAlias() *topodata.TabletAlias { return nil } -// UpdateStreamRequest is the payload for UpdateStream. At most one of -// position and timestamp can be set. If neither is set, we will start -// streaming from the current binlog position. -type UpdateStreamRequest struct { - EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId,proto3" json:"effective_caller_id,omitempty"` - ImmediateCallerId *VTGateCallerID `protobuf:"bytes,2,opt,name=immediate_caller_id,json=immediateCallerId,proto3" json:"immediate_caller_id,omitempty"` - Target *Target `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` - // If position is set, we will start the streaming from that replication - // position. Incompatible with timestamp. - Position string `protobuf:"bytes,4,opt,name=position,proto3" json:"position,omitempty"` - // If timestamp is set, we will start the streaming from the first - // event in the binlogs that have that timestamp. Incompatible with position. - Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateStreamRequest) Reset() { *m = UpdateStreamRequest{} } -func (m *UpdateStreamRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateStreamRequest) ProtoMessage() {} -func (*UpdateStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{54} -} - -func (m *UpdateStreamRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateStreamRequest.Unmarshal(m, b) -} -func (m *UpdateStreamRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateStreamRequest.Marshal(b, m, deterministic) -} -func (m *UpdateStreamRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateStreamRequest.Merge(m, src) -} -func (m *UpdateStreamRequest) XXX_Size() int { - return xxx_messageInfo_UpdateStreamRequest.Size(m) -} -func (m *UpdateStreamRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateStreamRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateStreamRequest proto.InternalMessageInfo - -func (m *UpdateStreamRequest) GetEffectiveCallerId() *vtrpc.CallerID { - if m != nil { - return m.EffectiveCallerId - } - return nil -} - -func (m *UpdateStreamRequest) GetImmediateCallerId() *VTGateCallerID { - if m != nil { - return m.ImmediateCallerId - } - return nil -} - -func (m *UpdateStreamRequest) GetTarget() *Target { - if m != nil { - return m.Target - } - return nil -} - -func (m *UpdateStreamRequest) GetPosition() string { - if m != nil { - return m.Position - } - return "" -} - -func (m *UpdateStreamRequest) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -// UpdateStreamResponse is returned by UpdateStream -type UpdateStreamResponse struct { - Event *StreamEvent `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateStreamResponse) Reset() { *m = UpdateStreamResponse{} } -func (m *UpdateStreamResponse) String() string { return proto.CompactTextString(m) } -func (*UpdateStreamResponse) ProtoMessage() {} -func (*UpdateStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{55} -} - -func (m *UpdateStreamResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateStreamResponse.Unmarshal(m, b) -} -func (m *UpdateStreamResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateStreamResponse.Marshal(b, m, deterministic) -} -func (m *UpdateStreamResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateStreamResponse.Merge(m, src) -} -func (m *UpdateStreamResponse) XXX_Size() int { - return xxx_messageInfo_UpdateStreamResponse.Size(m) -} -func (m *UpdateStreamResponse) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateStreamResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateStreamResponse proto.InternalMessageInfo - -func (m *UpdateStreamResponse) GetEvent() *StreamEvent { - if m != nil { - return m.Event - } - return nil -} - // TransactionMetadata contains the metadata for a distributed transaction. type TransactionMetadata struct { Dtid string `protobuf:"bytes,1,opt,name=dtid,proto3" json:"dtid,omitempty"` @@ -3913,7 +3795,7 @@ func (m *TransactionMetadata) Reset() { *m = TransactionMetadata{} } func (m *TransactionMetadata) String() string { return proto.CompactTextString(m) } func (*TransactionMetadata) ProtoMessage() {} func (*TransactionMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{56} + return fileDescriptor_5c6ac9b241082464, []int{54} } func (m *TransactionMetadata) XXX_Unmarshal(b []byte) error { @@ -4027,206 +3909,201 @@ func init() { proto.RegisterType((*RealtimeStats)(nil), "query.RealtimeStats") proto.RegisterType((*AggregateStats)(nil), "query.AggregateStats") proto.RegisterType((*StreamHealthResponse)(nil), "query.StreamHealthResponse") - proto.RegisterType((*UpdateStreamRequest)(nil), "query.UpdateStreamRequest") - proto.RegisterType((*UpdateStreamResponse)(nil), "query.UpdateStreamResponse") proto.RegisterType((*TransactionMetadata)(nil), "query.TransactionMetadata") } func init() { proto.RegisterFile("query.proto", fileDescriptor_5c6ac9b241082464) } var fileDescriptor_5c6ac9b241082464 = []byte{ - // 3087 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcb, 0x73, 0x1b, 0x47, - 0x7a, 0xd7, 0xe0, 0x45, 0xe0, 0x03, 0x01, 0x36, 0x9b, 0xa4, 0x04, 0x51, 0x7e, 0xd0, 0x63, 0xcb, - 0x66, 0xe8, 0x84, 0x92, 0x29, 0x59, 0x51, 0x6c, 0x27, 0xd1, 0x10, 0x1c, 0xca, 0xb0, 0x80, 0x01, - 0xd4, 0x18, 0x48, 0x96, 0xca, 0x55, 0x53, 0x43, 0xa0, 0x05, 0x4e, 0x71, 0x80, 0x81, 0x66, 0x86, - 0x94, 0x78, 0x53, 0xe2, 0x38, 0xef, 0x87, 0xf3, 0x74, 0x9c, 0x54, 0x9c, 0x54, 0xe5, 0x90, 0xca, - 0x25, 0x7f, 0x43, 0x2a, 0x87, 0x1c, 0xf7, 0xbe, 0xbb, 0x87, 0x3d, 0x6d, 0xed, 0xcd, 0xb5, 0xa7, - 0x3d, 0xec, 0x61, 0x6b, 0xab, 0x1f, 0x33, 0x18, 0x90, 0xd0, 0xc3, 0xda, 0xbd, 0x50, 0xf6, 0xad, - 0xbf, 0x47, 0xf7, 0xd7, 0xdf, 0xef, 0xfb, 0xe6, 0xeb, 0x6f, 0x66, 0x1a, 0x8a, 0xf7, 0xf7, 0xa9, - 0x7f, 0xb8, 0x3e, 0xf2, 0xbd, 0xd0, 0xc3, 0x59, 0x4e, 0x2c, 0x97, 0x43, 0x6f, 0xe4, 0xf5, 0xec, - 0xd0, 0x16, 0xec, 0xe5, 0xe2, 0x41, 0xe8, 0x8f, 0xba, 0x82, 0x50, 0x3f, 0x53, 0x20, 0x67, 0xda, - 0x7e, 0x9f, 0x86, 0x78, 0x19, 0xf2, 0x7b, 0xf4, 0x30, 0x18, 0xd9, 0x5d, 0x5a, 0x51, 0x56, 0x94, - 0xd5, 0x02, 0x89, 0x69, 0xbc, 0x08, 0xd9, 0x60, 0xd7, 0xf6, 0x7b, 0x95, 0x14, 0x17, 0x08, 0x02, - 0xbf, 0x0b, 0xc5, 0xd0, 0xde, 0x71, 0x69, 0x68, 0x85, 0x87, 0x23, 0x5a, 0x49, 0xaf, 0x28, 0xab, - 0xe5, 0x8d, 0xc5, 0xf5, 0xd8, 0x9e, 0xc9, 0x85, 0xe6, 0xe1, 0x88, 0x12, 0x08, 0xe3, 0x31, 0xc6, - 0x90, 0xe9, 0x52, 0xd7, 0xad, 0x64, 0xf8, 0x5a, 0x7c, 0xac, 0x6e, 0x41, 0xf9, 0x96, 0x79, 0xdd, - 0x0e, 0x69, 0xd5, 0x76, 0x5d, 0xea, 0xd7, 0xb6, 0xd8, 0x76, 0xf6, 0x03, 0xea, 0x0f, 0xed, 0x41, - 0xbc, 0x9d, 0x88, 0xc6, 0xa7, 0x21, 0xd7, 0xf7, 0xbd, 0xfd, 0x51, 0x50, 0x49, 0xad, 0xa4, 0x57, - 0x0b, 0x44, 0x52, 0xea, 0x27, 0x00, 0xfa, 0x01, 0x1d, 0x86, 0xa6, 0xb7, 0x47, 0x87, 0xf8, 0x25, - 0x28, 0x84, 0xce, 0x80, 0x06, 0xa1, 0x3d, 0x18, 0xf1, 0x25, 0xd2, 0x64, 0xcc, 0x78, 0x8c, 0x4b, - 0xcb, 0x90, 0x1f, 0x79, 0x81, 0x13, 0x3a, 0xde, 0x90, 0xfb, 0x53, 0x20, 0x31, 0xad, 0xfe, 0x1e, - 0x64, 0x6f, 0xd9, 0xee, 0x3e, 0xc5, 0xaf, 0x42, 0x86, 0x3b, 0xac, 0x70, 0x87, 0x8b, 0xeb, 0x02, - 0x74, 0xee, 0x27, 0x17, 0xb0, 0xb5, 0x0f, 0x98, 0x26, 0x5f, 0x7b, 0x96, 0x08, 0x42, 0xdd, 0x83, - 0xd9, 0x4d, 0x67, 0xd8, 0xbb, 0x65, 0xfb, 0x0e, 0x03, 0xe3, 0x39, 0x97, 0xc1, 0x6f, 0x40, 0x8e, - 0x0f, 0x82, 0x4a, 0x7a, 0x25, 0xbd, 0x5a, 0xdc, 0x98, 0x95, 0x13, 0xf9, 0xde, 0x88, 0x94, 0xa9, - 0xff, 0xa7, 0x00, 0x6c, 0x7a, 0xfb, 0xc3, 0xde, 0x4d, 0x26, 0xc4, 0x08, 0xd2, 0xc1, 0x7d, 0x57, - 0x02, 0xc9, 0x86, 0xf8, 0x06, 0x94, 0x77, 0x9c, 0x61, 0xcf, 0x3a, 0x90, 0xdb, 0x11, 0x58, 0x16, - 0x37, 0xde, 0x90, 0xcb, 0x8d, 0x27, 0xaf, 0x27, 0x77, 0x1d, 0xe8, 0xc3, 0xd0, 0x3f, 0x24, 0xa5, - 0x9d, 0x24, 0x6f, 0xb9, 0x03, 0xf8, 0xb8, 0x12, 0x33, 0xba, 0x47, 0x0f, 0x23, 0xa3, 0x7b, 0xf4, - 0x10, 0xff, 0x46, 0xd2, 0xa3, 0xe2, 0xc6, 0x42, 0x64, 0x2b, 0x31, 0x57, 0xba, 0xf9, 0x5e, 0xea, - 0xaa, 0xa2, 0xfe, 0x7b, 0x0e, 0xca, 0xfa, 0x43, 0xda, 0xdd, 0x0f, 0x69, 0x73, 0xc4, 0x62, 0x10, - 0xe0, 0x75, 0x58, 0x70, 0x86, 0x5d, 0x77, 0xbf, 0x47, 0x2d, 0xca, 0x42, 0x6d, 0x85, 0x2c, 0xd6, - 0x7c, 0xbd, 0x3c, 0x99, 0x97, 0xa2, 0x44, 0x12, 0x68, 0xb0, 0xd0, 0xf5, 0x06, 0x23, 0xdb, 0x9f, - 0xd4, 0x4f, 0x73, 0xfb, 0xf3, 0xd2, 0xfe, 0x58, 0x9f, 0xcc, 0x4b, 0xed, 0xc4, 0x12, 0x0d, 0x98, - 0x93, 0xeb, 0xf6, 0xac, 0x7b, 0x0e, 0x75, 0x7b, 0x01, 0x4f, 0xdd, 0x72, 0x0c, 0xd5, 0xe4, 0x16, - 0xd7, 0x6b, 0x52, 0x79, 0x9b, 0xeb, 0x92, 0xb2, 0x33, 0x41, 0xe3, 0x35, 0x98, 0xef, 0xba, 0x0e, - 0xdb, 0xca, 0x3d, 0x06, 0xb1, 0xe5, 0x7b, 0x0f, 0x82, 0x4a, 0x96, 0xef, 0x7f, 0x4e, 0x08, 0xb6, - 0x19, 0x9f, 0x78, 0x0f, 0x02, 0xfc, 0x1e, 0xe4, 0x1f, 0x78, 0xfe, 0x9e, 0xeb, 0xd9, 0xbd, 0x4a, - 0x8e, 0xdb, 0x7c, 0x65, 0xba, 0xcd, 0xdb, 0x52, 0x8b, 0xc4, 0xfa, 0x78, 0x15, 0x50, 0x70, 0xdf, - 0xb5, 0x02, 0xea, 0xd2, 0x6e, 0x68, 0xb9, 0xce, 0xc0, 0x09, 0x2b, 0x79, 0xfe, 0x14, 0x94, 0x83, - 0xfb, 0x6e, 0x9b, 0xb3, 0xeb, 0x8c, 0x8b, 0x2d, 0x58, 0x0a, 0x7d, 0x7b, 0x18, 0xd8, 0x5d, 0xb6, - 0x98, 0xe5, 0x04, 0x9e, 0x6b, 0xf3, 0x27, 0xa0, 0xc0, 0x4d, 0xae, 0x4d, 0x37, 0x69, 0x8e, 0xa7, - 0xd4, 0xa2, 0x19, 0x64, 0x31, 0x9c, 0xc2, 0xc5, 0xef, 0xc0, 0x52, 0xb0, 0xe7, 0x8c, 0x2c, 0xbe, - 0x8e, 0x35, 0x72, 0xed, 0xa1, 0xd5, 0xb5, 0xbb, 0xbb, 0xb4, 0x02, 0xdc, 0x6d, 0xcc, 0x84, 0x3c, - 0xd5, 0x5a, 0xae, 0x3d, 0xac, 0x32, 0x89, 0xfa, 0x3e, 0x94, 0x27, 0x71, 0xc4, 0xf3, 0x50, 0x32, - 0xef, 0xb4, 0x74, 0x4b, 0x33, 0xb6, 0x2c, 0x43, 0x6b, 0xe8, 0xe8, 0x14, 0x2e, 0x41, 0x81, 0xb3, - 0x9a, 0x46, 0xfd, 0x0e, 0x52, 0xf0, 0x0c, 0xa4, 0xb5, 0x7a, 0x1d, 0xa5, 0xd4, 0xab, 0x90, 0x8f, - 0x00, 0xc1, 0x73, 0x50, 0xec, 0x18, 0xed, 0x96, 0x5e, 0xad, 0x6d, 0xd7, 0xf4, 0x2d, 0x74, 0x0a, - 0xe7, 0x21, 0xd3, 0xac, 0x9b, 0x2d, 0xa4, 0x88, 0x91, 0xd6, 0x42, 0x29, 0x36, 0x73, 0x6b, 0x53, - 0x43, 0x69, 0xf5, 0xbf, 0x14, 0x58, 0x9c, 0xe6, 0x18, 0x2e, 0xc2, 0xcc, 0x96, 0xbe, 0xad, 0x75, - 0xea, 0x26, 0x3a, 0x85, 0x17, 0x60, 0x8e, 0xe8, 0x2d, 0x5d, 0x33, 0xb5, 0xcd, 0xba, 0x6e, 0x11, - 0x5d, 0xdb, 0x42, 0x0a, 0xc6, 0x50, 0x66, 0x23, 0xab, 0xda, 0x6c, 0x34, 0x6a, 0xa6, 0xa9, 0x6f, - 0xa1, 0x14, 0x5e, 0x04, 0xc4, 0x79, 0x1d, 0x63, 0xcc, 0x4d, 0x63, 0x04, 0xb3, 0x6d, 0x9d, 0xd4, - 0xb4, 0x7a, 0xed, 0x2e, 0x5b, 0x00, 0x65, 0xf0, 0x6b, 0xf0, 0x72, 0xb5, 0x69, 0xb4, 0x6b, 0x6d, - 0x53, 0x37, 0x4c, 0xab, 0x6d, 0x68, 0xad, 0xf6, 0x87, 0x4d, 0x93, 0xaf, 0x2c, 0x9c, 0xcb, 0xe2, - 0x32, 0x80, 0xd6, 0x31, 0x9b, 0x62, 0x1d, 0x94, 0xfb, 0x28, 0x93, 0x57, 0x50, 0x4a, 0xfd, 0x22, - 0x05, 0x59, 0x8e, 0x0f, 0xab, 0xaa, 0x89, 0x5a, 0xc9, 0xc7, 0x71, 0x85, 0x49, 0x3d, 0xa1, 0xc2, - 0xf0, 0xc2, 0x2c, 0x6b, 0x9d, 0x20, 0xf0, 0x39, 0x28, 0x78, 0x7e, 0xdf, 0x12, 0x12, 0x51, 0xa5, - 0xf3, 0x9e, 0xdf, 0xe7, 0xe5, 0x9c, 0x55, 0x48, 0x56, 0xdc, 0x77, 0xec, 0x80, 0xf2, 0xac, 0x2d, - 0x90, 0x98, 0xc6, 0x67, 0x81, 0xe9, 0x59, 0x7c, 0x1f, 0x39, 0x2e, 0x9b, 0xf1, 0xfc, 0xbe, 0xc1, - 0xb6, 0xf2, 0x3a, 0x94, 0xba, 0x9e, 0xbb, 0x3f, 0x18, 0x5a, 0x2e, 0x1d, 0xf6, 0xc3, 0xdd, 0xca, - 0xcc, 0x8a, 0xb2, 0x5a, 0x22, 0xb3, 0x82, 0x59, 0xe7, 0x3c, 0x5c, 0x81, 0x99, 0xee, 0xae, 0xed, - 0x07, 0x54, 0x64, 0x6a, 0x89, 0x44, 0x24, 0xb7, 0x4a, 0xbb, 0xce, 0xc0, 0x76, 0x03, 0x9e, 0x95, - 0x25, 0x12, 0xd3, 0xcc, 0x89, 0x7b, 0xae, 0xdd, 0x0f, 0x78, 0x36, 0x95, 0x88, 0x20, 0xd4, 0xdf, - 0x86, 0x34, 0xf1, 0x1e, 0xb0, 0x25, 0x85, 0xc1, 0xa0, 0xa2, 0xac, 0xa4, 0x57, 0x31, 0x89, 0x48, - 0x76, 0x88, 0xc8, 0x3a, 0x2a, 0xca, 0x6b, 0x54, 0x39, 0x3f, 0x81, 0x59, 0x42, 0x83, 0x7d, 0x37, - 0xd4, 0x1f, 0x86, 0xbe, 0x1d, 0xe0, 0x0d, 0x28, 0x26, 0x2b, 0x87, 0xf2, 0xb8, 0xca, 0x01, 0x74, - 0x5c, 0x32, 0x2a, 0x30, 0x73, 0xcf, 0xa7, 0xc1, 0x2e, 0xf5, 0x65, 0x65, 0x8a, 0x48, 0x56, 0x97, - 0x8b, 0x3c, 0xd5, 0x85, 0x0d, 0x56, 0xcd, 0x65, 0x4d, 0x51, 0x26, 0xaa, 0x39, 0x0f, 0x2a, 0x91, - 0x32, 0x86, 0x1e, 0x2b, 0x13, 0x96, 0x7d, 0xef, 0x1e, 0xed, 0x86, 0x54, 0x1c, 0x5a, 0x19, 0x32, - 0xcb, 0x98, 0x9a, 0xe4, 0xb1, 0xb0, 0x39, 0xc3, 0x80, 0xfa, 0xa1, 0xe5, 0xf4, 0x78, 0x40, 0x33, - 0x24, 0x2f, 0x18, 0xb5, 0x1e, 0x7e, 0x05, 0x32, 0xbc, 0xd0, 0x64, 0xb8, 0x15, 0x90, 0x56, 0x88, - 0xf7, 0x80, 0x70, 0x3e, 0x7e, 0x1b, 0x72, 0x94, 0xfb, 0xcb, 0x83, 0x3a, 0x2e, 0xcd, 0x49, 0x28, - 0x88, 0x54, 0x51, 0x3f, 0x80, 0x59, 0xee, 0xc3, 0x6d, 0xdb, 0x1f, 0x3a, 0xc3, 0x3e, 0x3f, 0xd1, - 0xbd, 0x9e, 0xc8, 0xbd, 0x12, 0xe1, 0x63, 0x06, 0xc1, 0x80, 0x06, 0x81, 0xdd, 0xa7, 0xf2, 0x84, - 0x8d, 0x48, 0xf5, 0x3f, 0xd2, 0x50, 0x6c, 0x87, 0x3e, 0xb5, 0x07, 0x1c, 0x3d, 0xfc, 0x01, 0x40, - 0x10, 0xda, 0x21, 0x1d, 0xd0, 0x61, 0x18, 0xc1, 0xf0, 0x92, 0x34, 0x9f, 0xd0, 0x5b, 0x6f, 0x47, - 0x4a, 0x24, 0xa1, 0x7f, 0x34, 0x3c, 0xa9, 0x67, 0x08, 0xcf, 0xf2, 0x57, 0x29, 0x28, 0xc4, 0xab, - 0x61, 0x0d, 0xf2, 0x5d, 0x3b, 0xa4, 0x7d, 0xcf, 0x3f, 0x94, 0x67, 0xf1, 0xf9, 0x27, 0x59, 0x5f, - 0xaf, 0x4a, 0x65, 0x12, 0x4f, 0xc3, 0x2f, 0x83, 0x68, 0x70, 0x44, 0xea, 0x0b, 0x7f, 0x0b, 0x9c, - 0xc3, 0x93, 0xff, 0x3d, 0xc0, 0x23, 0xdf, 0x19, 0xd8, 0xfe, 0xa1, 0xb5, 0x47, 0x0f, 0xa3, 0x43, - 0x24, 0x3d, 0x25, 0xe0, 0x48, 0xea, 0xdd, 0xa0, 0x87, 0xb2, 0xec, 0x5d, 0x9d, 0x9c, 0x2b, 0x53, - 0xf6, 0x78, 0x18, 0x13, 0x33, 0x79, 0x27, 0x10, 0x44, 0x67, 0x7e, 0x96, 0x67, 0x37, 0x1b, 0xaa, - 0x6f, 0x41, 0x3e, 0xda, 0x3c, 0x2e, 0x40, 0x56, 0xf7, 0x7d, 0xcf, 0x47, 0xa7, 0x78, 0xf5, 0x6b, - 0xd4, 0x45, 0x01, 0xdd, 0xda, 0x62, 0x05, 0xf4, 0x7f, 0x53, 0xf1, 0xc1, 0x4b, 0xe8, 0xfd, 0x7d, - 0x1a, 0x84, 0xf8, 0xf7, 0x61, 0x81, 0xf2, 0x4c, 0x73, 0x0e, 0xa8, 0xd5, 0xe5, 0x5d, 0x1a, 0xcb, - 0x33, 0xf1, 0x38, 0xcc, 0xad, 0x8b, 0xa6, 0x32, 0xea, 0xde, 0xc8, 0x7c, 0xac, 0x2b, 0x59, 0x3d, - 0xac, 0xc3, 0x82, 0x33, 0x18, 0xd0, 0x9e, 0x63, 0x87, 0xc9, 0x05, 0x44, 0xc0, 0x96, 0xa2, 0x26, - 0x66, 0xa2, 0x09, 0x24, 0xf3, 0xf1, 0x8c, 0x78, 0x99, 0xf3, 0x90, 0x0b, 0x79, 0xc3, 0x2a, 0xcf, - 0xf0, 0x52, 0x54, 0xd5, 0x38, 0x93, 0x48, 0x21, 0x7e, 0x0b, 0x44, 0xfb, 0xcb, 0xeb, 0xd7, 0x38, - 0x21, 0xc6, 0x5d, 0x0d, 0x11, 0x72, 0x7c, 0x1e, 0xca, 0x13, 0x87, 0x5f, 0x8f, 0x03, 0x96, 0x26, - 0xa5, 0xe4, 0x49, 0xd6, 0xc3, 0x17, 0x60, 0xc6, 0x13, 0x07, 0x1f, 0xaf, 0x6c, 0xe3, 0x1d, 0x4f, - 0x9e, 0x8a, 0x24, 0xd2, 0x52, 0x7f, 0x17, 0xe6, 0x62, 0x04, 0x83, 0x91, 0x37, 0x0c, 0x28, 0x5e, - 0x83, 0x9c, 0xcf, 0x1f, 0x27, 0x89, 0x1a, 0x96, 0x4b, 0x24, 0xea, 0x01, 0x91, 0x1a, 0x6a, 0x0f, - 0xe6, 0x04, 0xe7, 0xb6, 0x13, 0xee, 0xf2, 0x40, 0xe1, 0xf3, 0x90, 0xa5, 0x6c, 0x70, 0x04, 0x73, - 0xd2, 0xaa, 0x72, 0x39, 0x11, 0xd2, 0x84, 0x95, 0xd4, 0x53, 0xad, 0xfc, 0x34, 0x05, 0x0b, 0x72, - 0x97, 0x9b, 0x76, 0xd8, 0xdd, 0x3d, 0xa1, 0xc1, 0x7e, 0x1b, 0x66, 0x18, 0xdf, 0x89, 0x1f, 0x8c, - 0x29, 0xe1, 0x8e, 0x34, 0x58, 0xc0, 0xed, 0xc0, 0x4a, 0x44, 0x57, 0x36, 0x5f, 0x25, 0x3b, 0x48, - 0x9c, 0xfc, 0x53, 0xf2, 0x22, 0xf7, 0x94, 0xbc, 0x98, 0x79, 0xa6, 0xbc, 0xd8, 0x82, 0xc5, 0x49, - 0xc4, 0x65, 0x72, 0xfc, 0x26, 0xcc, 0x88, 0xa0, 0x44, 0x25, 0x70, 0x5a, 0xdc, 0x22, 0x15, 0xf5, - 0xff, 0x53, 0xb0, 0x28, 0xab, 0xd3, 0xb7, 0xe3, 0x31, 0x4d, 0xe0, 0x9c, 0x7d, 0x16, 0x9c, 0x9f, - 0x31, 0x7e, 0x6a, 0x15, 0x96, 0x8e, 0xe0, 0xf8, 0x1c, 0x0f, 0xeb, 0xd7, 0x0a, 0xcc, 0x6e, 0xd2, - 0xbe, 0x33, 0x3c, 0xa1, 0x51, 0x48, 0x80, 0x9b, 0x79, 0xa6, 0x24, 0xbe, 0x02, 0x25, 0xe9, 0xaf, - 0x44, 0xeb, 0x38, 0xda, 0xca, 0x34, 0xb4, 0x7f, 0xac, 0x40, 0xa9, 0xea, 0x0d, 0x06, 0x4e, 0x78, - 0x42, 0x91, 0x3a, 0xee, 0x67, 0x66, 0x9a, 0x9f, 0x08, 0xca, 0x91, 0x9b, 0x02, 0x20, 0xf5, 0x27, - 0x0a, 0xcc, 0x11, 0xcf, 0x75, 0x77, 0xec, 0xee, 0xde, 0x8b, 0xed, 0x3b, 0x06, 0x34, 0x76, 0x54, - 0x7a, 0xff, 0x73, 0x05, 0xca, 0x2d, 0x9f, 0xb2, 0x17, 0xeb, 0x17, 0xda, 0x79, 0xd6, 0x09, 0xf7, - 0x42, 0xd9, 0x43, 0x14, 0x08, 0x1f, 0xab, 0xf3, 0x30, 0x17, 0xfb, 0x2e, 0xf1, 0xf8, 0x81, 0x02, - 0x4b, 0x22, 0x41, 0xa4, 0xa4, 0x77, 0x42, 0x61, 0x89, 0xfc, 0xcd, 0x24, 0xfc, 0xad, 0xc0, 0xe9, - 0xa3, 0xbe, 0x49, 0xb7, 0x3f, 0x4d, 0xc1, 0x99, 0x28, 0x37, 0x4e, 0xb8, 0xe3, 0xbf, 0x42, 0x3e, - 0x2c, 0x43, 0xe5, 0x38, 0x08, 0x12, 0xa1, 0xcf, 0x53, 0x50, 0xa9, 0xfa, 0xd4, 0x0e, 0x69, 0xa2, - 0x17, 0x79, 0x71, 0x72, 0x03, 0xbf, 0x03, 0xb3, 0x23, 0xdb, 0x0f, 0x9d, 0xae, 0x33, 0xb2, 0xd9, - 0xdb, 0x5e, 0x96, 0xb7, 0x3a, 0x47, 0x16, 0x98, 0x50, 0x51, 0xcf, 0xc1, 0xd9, 0x29, 0x88, 0x48, - 0xbc, 0x7e, 0xa1, 0x00, 0x6e, 0x87, 0xb6, 0x1f, 0x7e, 0x0b, 0x4e, 0x95, 0xa9, 0xc9, 0xb4, 0x04, - 0x0b, 0x13, 0xfe, 0x27, 0x71, 0xa1, 0xe1, 0xb7, 0xe2, 0xc4, 0x79, 0x2c, 0x2e, 0x49, 0xff, 0x25, - 0x2e, 0x3f, 0x52, 0x60, 0xb9, 0xea, 0x89, 0x0f, 0x8b, 0x2f, 0xe4, 0x13, 0xa6, 0xbe, 0x0c, 0xe7, - 0xa6, 0x3a, 0x28, 0x01, 0xf8, 0xa1, 0x02, 0xa7, 0x09, 0xb5, 0x7b, 0x2f, 0xa6, 0xf3, 0x37, 0xe1, - 0xcc, 0x31, 0xe7, 0x64, 0x87, 0x7a, 0x05, 0xf2, 0x03, 0x1a, 0xda, 0x3d, 0x3b, 0xb4, 0xa5, 0x4b, - 0xcb, 0xd1, 0xba, 0x63, 0xed, 0x86, 0xd4, 0x20, 0xb1, 0xae, 0xfa, 0x55, 0x0a, 0x16, 0x78, 0xaf, - 0xfb, 0xdd, 0x8b, 0xd6, 0xf4, 0x77, 0x81, 0xcf, 0x15, 0x58, 0x9c, 0x04, 0x28, 0x7e, 0x27, 0xf8, - 0x75, 0x7f, 0xaf, 0x98, 0x52, 0x10, 0xd2, 0xd3, 0x5a, 0xd0, 0xef, 0xa5, 0xa0, 0x92, 0xdc, 0xd2, - 0x77, 0xdf, 0x36, 0x26, 0xbf, 0x6d, 0x7c, 0xe3, 0x8f, 0x59, 0x5f, 0x28, 0x70, 0x76, 0x0a, 0xa0, - 0xdf, 0x2c, 0xd0, 0x89, 0x2f, 0x1c, 0xa9, 0xa7, 0x7e, 0xe1, 0x78, 0xd6, 0x50, 0x7f, 0x5f, 0x81, - 0xc5, 0x86, 0xf8, 0xb0, 0x2c, 0xde, 0xe3, 0x4f, 0x6e, 0x35, 0xe3, 0xdf, 0x8e, 0x33, 0xe3, 0xdf, - 0x37, 0x6a, 0x15, 0x96, 0x8e, 0xb8, 0xf6, 0x1c, 0xdf, 0x26, 0x7e, 0xa6, 0xc0, 0xbc, 0x5c, 0x45, - 0x3b, 0xb1, 0x8d, 0xc0, 0x14, 0x74, 0xf0, 0x2b, 0x90, 0x76, 0x7a, 0x51, 0x07, 0x39, 0xf9, 0x13, - 0x9c, 0x09, 0xd4, 0x6b, 0x80, 0x93, 0x7e, 0x3f, 0x07, 0x74, 0xbc, 0xb7, 0x62, 0xc0, 0x7f, 0x48, - 0x6d, 0x37, 0x8c, 0x0a, 0x88, 0xfa, 0x9f, 0x29, 0x28, 0x11, 0xc6, 0x71, 0x06, 0xb4, 0x1d, 0xda, - 0x61, 0x80, 0x5f, 0x83, 0xd9, 0x5d, 0xae, 0x62, 0x8d, 0x9f, 0x83, 0x02, 0x29, 0x0a, 0x9e, 0xf8, - 0x78, 0xbb, 0x01, 0x4b, 0x01, 0xed, 0x7a, 0xc3, 0x5e, 0x60, 0xed, 0xd0, 0x5d, 0x67, 0xd8, 0xb3, - 0x06, 0x76, 0x10, 0xca, 0xff, 0x43, 0x25, 0xb2, 0x20, 0x85, 0x9b, 0x5c, 0xd6, 0xe0, 0x22, 0x7c, - 0x11, 0x16, 0x77, 0x9c, 0xa1, 0xeb, 0xf5, 0xad, 0x91, 0x6b, 0x1f, 0x52, 0x3f, 0xb0, 0xba, 0xde, - 0xfe, 0x50, 0x40, 0x95, 0x25, 0x58, 0xc8, 0x5a, 0x42, 0x54, 0x65, 0x12, 0x7c, 0x17, 0xd6, 0xa6, - 0x5a, 0xb1, 0xee, 0x39, 0x6e, 0x48, 0x7d, 0xda, 0xb3, 0x7c, 0x3a, 0x72, 0x9d, 0xae, 0xf8, 0xbd, - 0x2b, 0x9a, 0xa9, 0x37, 0xa7, 0x98, 0xde, 0x96, 0xea, 0x64, 0xac, 0x8d, 0xcf, 0x41, 0xa1, 0x3b, - 0xda, 0xb7, 0xf6, 0xf9, 0x2f, 0x1d, 0x56, 0x56, 0x14, 0x92, 0xef, 0x8e, 0xf6, 0x3b, 0x8c, 0xc6, - 0x08, 0xd2, 0xf7, 0x47, 0xa2, 0x9a, 0x28, 0x84, 0x0d, 0xd5, 0xaf, 0x15, 0x28, 0x6b, 0xfd, 0xbe, - 0x4f, 0xfb, 0x76, 0x28, 0x61, 0xba, 0x08, 0x8b, 0x02, 0x92, 0x43, 0x4b, 0xde, 0x1b, 0x11, 0xfe, - 0x28, 0xc2, 0x1f, 0x29, 0x13, 0xb7, 0x46, 0x84, 0x3f, 0x97, 0xe1, 0xf4, 0xfe, 0x70, 0xea, 0x9c, - 0x14, 0x9f, 0xb3, 0x18, 0x4b, 0x93, 0xb3, 0x7e, 0x07, 0xce, 0x4e, 0x47, 0x61, 0xe0, 0x88, 0x3f, - 0xff, 0x25, 0x72, 0x7a, 0x8a, 0xd3, 0x0d, 0x67, 0xf8, 0x84, 0xa9, 0xf6, 0x43, 0x8e, 0xd7, 0x63, - 0xa6, 0xda, 0x0f, 0xd5, 0xff, 0x8e, 0x3f, 0xc9, 0x46, 0xe9, 0x12, 0x97, 0xc7, 0x28, 0xc7, 0x95, - 0x27, 0xe5, 0x78, 0x05, 0x66, 0x02, 0xea, 0x1f, 0x38, 0xc3, 0x7e, 0xf4, 0xcf, 0x50, 0x92, 0xb8, - 0x0d, 0x6f, 0x4a, 0xdf, 0xe9, 0xc3, 0x90, 0xfa, 0x43, 0xdb, 0x75, 0x0f, 0x2d, 0xf1, 0xe6, 0x38, - 0x0c, 0x69, 0xcf, 0x1a, 0xdf, 0x72, 0x11, 0x25, 0xf2, 0x75, 0xa1, 0xad, 0xc7, 0xca, 0x24, 0xd6, - 0x35, 0xe3, 0xfb, 0x2f, 0xef, 0x43, 0xd9, 0x97, 0x49, 0x6c, 0x05, 0x2c, 0x3c, 0xb2, 0x33, 0x58, - 0x8c, 0x7f, 0xfc, 0x25, 0x32, 0x9c, 0x94, 0xfc, 0x89, 0x84, 0xbf, 0x0a, 0xb3, 0x72, 0x47, 0xb6, - 0xeb, 0xd8, 0xe3, 0x4e, 0xe1, 0xc8, 0xd5, 0x1f, 0x8d, 0x09, 0x89, 0xbc, 0x24, 0xc4, 0x89, 0x8f, - 0x32, 0xf9, 0x1c, 0x9a, 0x61, 0xaf, 0x27, 0x0b, 0x9d, 0x51, 0x8f, 0x67, 0xc6, 0x09, 0x2e, 0xda, - 0xc9, 0xdb, 0x42, 0x99, 0xc9, 0xdb, 0x42, 0x93, 0xb7, 0x8f, 0xb2, 0x47, 0x6e, 0x1f, 0xa9, 0xd7, - 0x60, 0x71, 0xd2, 0x7f, 0x99, 0x2b, 0xab, 0x90, 0xe5, 0xff, 0x29, 0x8f, 0x54, 0xa7, 0xc4, 0x8f, - 0x48, 0x22, 0x14, 0xd4, 0xff, 0x51, 0x60, 0x61, 0x4a, 0xe7, 0x1a, 0xb7, 0xc5, 0x4a, 0xe2, 0xad, - 0xfb, 0xb7, 0x20, 0xcb, 0xff, 0x98, 0xca, 0x8b, 0x00, 0x67, 0x8e, 0x37, 0xbe, 0xfc, 0xef, 0x26, - 0x11, 0x5a, 0xac, 0x9c, 0xf1, 0xb4, 0xe8, 0xf2, 0xd7, 0xee, 0xe8, 0xe0, 0x2d, 0x32, 0x9e, 0x78, - 0x13, 0x3f, 0xfe, 0x1e, 0x9f, 0x79, 0xea, 0x7b, 0xfc, 0xda, 0xdf, 0xa5, 0xa1, 0xd0, 0x38, 0x6c, - 0xdf, 0x77, 0xb7, 0x5d, 0xbb, 0xcf, 0x7f, 0x3f, 0x36, 0x5a, 0xe6, 0x1d, 0x74, 0x0a, 0xcf, 0x43, - 0xc9, 0x68, 0x9a, 0x96, 0xd1, 0xa9, 0xd7, 0xad, 0xed, 0xba, 0x76, 0x1d, 0x29, 0x18, 0xc1, 0x6c, - 0x8b, 0xd4, 0xac, 0x1b, 0xfa, 0x1d, 0xc1, 0x49, 0xe1, 0x05, 0x98, 0xeb, 0x18, 0xb5, 0x9b, 0x1d, - 0x7d, 0xcc, 0xcc, 0xe0, 0x25, 0x98, 0x6f, 0x74, 0xea, 0x66, 0xad, 0x55, 0x4f, 0xb0, 0xf3, 0xb8, - 0x04, 0x85, 0xcd, 0x7a, 0x73, 0x53, 0x90, 0x88, 0xad, 0xdf, 0x31, 0xda, 0xb5, 0xeb, 0x86, 0xbe, - 0x25, 0x58, 0x2b, 0x8c, 0x75, 0x57, 0x27, 0xcd, 0xed, 0x5a, 0x64, 0xf2, 0x1a, 0x46, 0x50, 0xdc, - 0xac, 0x19, 0x1a, 0x91, 0xab, 0x3c, 0x52, 0x70, 0x19, 0x0a, 0xba, 0xd1, 0x69, 0x48, 0x3a, 0x85, - 0x2b, 0xb0, 0xa0, 0x75, 0xcc, 0xa6, 0x55, 0x33, 0xaa, 0x44, 0x6f, 0xe8, 0x86, 0x29, 0x25, 0x19, - 0xbc, 0x00, 0x65, 0xb3, 0xd6, 0xd0, 0xdb, 0xa6, 0xd6, 0x68, 0x49, 0x26, 0xdb, 0x45, 0xbe, 0xad, - 0x47, 0x3a, 0x08, 0x2f, 0xc3, 0x92, 0xd1, 0xb4, 0xe4, 0x1d, 0x12, 0xeb, 0x96, 0x56, 0xef, 0xe8, - 0x52, 0xb6, 0x82, 0xcf, 0x00, 0x6e, 0x1a, 0x56, 0xa7, 0xb5, 0xa5, 0x99, 0xba, 0x65, 0x34, 0x6f, - 0x4b, 0xc1, 0x35, 0x5c, 0x86, 0xfc, 0x78, 0x07, 0x8f, 0x18, 0x0a, 0xa5, 0x96, 0x46, 0xcc, 0xb1, - 0xb3, 0x8f, 0x1e, 0x31, 0xb0, 0xe0, 0x3a, 0x69, 0x76, 0x5a, 0x63, 0xb5, 0x79, 0x28, 0x4a, 0xb0, - 0x24, 0x2b, 0xc3, 0x58, 0x9b, 0x35, 0xa3, 0x1a, 0xef, 0xef, 0x51, 0x7e, 0x39, 0x85, 0x94, 0xb5, - 0x3d, 0xc8, 0xf0, 0x70, 0xe4, 0x21, 0x63, 0x34, 0x0d, 0x1d, 0x9d, 0xc2, 0x73, 0x00, 0xb5, 0x76, - 0xcd, 0x30, 0xf5, 0xeb, 0x44, 0xab, 0x33, 0xb7, 0x39, 0x23, 0x02, 0x90, 0x79, 0x3b, 0x0b, 0x33, - 0xb5, 0xf6, 0x76, 0xbd, 0xa9, 0x99, 0xd2, 0xcd, 0x5a, 0xfb, 0x66, 0xa7, 0x69, 0x32, 0x21, 0xc2, - 0x45, 0xc8, 0xd5, 0xda, 0xa6, 0xfe, 0xb1, 0xc9, 0xfc, 0xe2, 0x32, 0x81, 0x2a, 0x7a, 0x74, 0x6d, - 0xed, 0xcb, 0x34, 0x64, 0xf8, 0x0d, 0xc0, 0x12, 0x14, 0x78, 0xb4, 0xcd, 0x3b, 0x2d, 0x66, 0xb2, - 0x00, 0x99, 0x9a, 0x61, 0x5e, 0x45, 0x7f, 0x90, 0xc2, 0x00, 0xd9, 0x0e, 0x1f, 0xff, 0x61, 0x8e, - 0x8d, 0x6b, 0x86, 0xf9, 0xce, 0x15, 0xf4, 0x69, 0x8a, 0x2d, 0xdb, 0x11, 0xc4, 0x1f, 0x45, 0x82, - 0x8d, 0xcb, 0xe8, 0xb3, 0x58, 0xb0, 0x71, 0x19, 0xfd, 0x71, 0x24, 0xb8, 0xb4, 0x81, 0xfe, 0x24, - 0x16, 0x5c, 0xda, 0x40, 0x7f, 0x1a, 0x09, 0xae, 0x5c, 0x46, 0x7f, 0x16, 0x0b, 0xae, 0x5c, 0x46, - 0x7f, 0x9e, 0x63, 0xbe, 0x70, 0x4f, 0x2e, 0x6d, 0xa0, 0xbf, 0xc8, 0xc7, 0xd4, 0x95, 0xcb, 0xe8, - 0x2f, 0xf3, 0x2c, 0xfe, 0x71, 0x54, 0xd1, 0x5f, 0x21, 0xb6, 0x4d, 0x16, 0x20, 0xf4, 0xd7, 0x7c, - 0xc8, 0x44, 0xe8, 0x6f, 0x10, 0xf3, 0x91, 0x71, 0x39, 0xf9, 0x39, 0x97, 0xdc, 0xd1, 0x35, 0x82, - 0xfe, 0x36, 0x27, 0xae, 0x0c, 0x55, 0x6b, 0x0d, 0xad, 0x8e, 0x30, 0x9f, 0xc1, 0x50, 0xf9, 0xfb, - 0x8b, 0x6c, 0xc8, 0xd2, 0x13, 0xfd, 0x43, 0x8b, 0x19, 0xbc, 0xa5, 0x91, 0xea, 0x87, 0x1a, 0x41, - 0xff, 0x78, 0x91, 0x19, 0xbc, 0xa5, 0x11, 0x89, 0xd7, 0x3f, 0xb5, 0x98, 0x22, 0x17, 0x7d, 0x71, - 0x91, 0x6d, 0x5a, 0xf2, 0xff, 0xb9, 0x85, 0xf3, 0x90, 0xde, 0xac, 0x99, 0xe8, 0x4b, 0x6e, 0x8d, - 0xa5, 0x28, 0xfa, 0x17, 0xc4, 0x98, 0x6d, 0xdd, 0x44, 0xff, 0xca, 0x98, 0x59, 0xb3, 0xd3, 0xaa, - 0xeb, 0xe8, 0x25, 0xb6, 0xb9, 0xeb, 0x7a, 0xb3, 0xa1, 0x9b, 0xe4, 0x0e, 0xfa, 0x37, 0xae, 0xfe, - 0x51, 0xbb, 0x69, 0xa0, 0xaf, 0x10, 0x2e, 0x03, 0xe8, 0x1f, 0xb7, 0x88, 0xde, 0x6e, 0xd7, 0x9a, - 0x06, 0x7a, 0x75, 0x6d, 0x1b, 0xd0, 0xd1, 0x72, 0xc0, 0x1c, 0xe8, 0x18, 0x37, 0x8c, 0xe6, 0x6d, - 0x03, 0x9d, 0x62, 0x44, 0x8b, 0xe8, 0x2d, 0x8d, 0xe8, 0x48, 0xc1, 0x00, 0x39, 0x79, 0x11, 0x29, - 0x85, 0x67, 0x21, 0x4f, 0x9a, 0xf5, 0xfa, 0xa6, 0x56, 0xbd, 0x81, 0xd2, 0x9b, 0xef, 0xc2, 0x9c, - 0xe3, 0xad, 0x1f, 0x38, 0x21, 0x0d, 0x02, 0x71, 0xc7, 0xf4, 0xae, 0x2a, 0x29, 0xc7, 0xbb, 0x20, - 0x46, 0x17, 0xfa, 0xde, 0x85, 0x83, 0xf0, 0x02, 0x97, 0x5e, 0xe0, 0x15, 0x63, 0x27, 0xc7, 0x89, - 0x4b, 0xbf, 0x0c, 0x00, 0x00, 0xff, 0xff, 0xa7, 0xa1, 0x8c, 0xaf, 0xc1, 0x2a, 0x00, 0x00, + // 3039 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcd, 0x73, 0xdb, 0xd6, + 0xb5, 0x37, 0x48, 0x8a, 0x22, 0x0f, 0x45, 0xea, 0xea, 0x4a, 0xb2, 0x69, 0x39, 0x1f, 0x0a, 0x12, + 0x27, 0x7e, 0xca, 0x7b, 0xb2, 0x23, 0x3b, 0x7e, 0x7e, 0x49, 0xde, 0x7b, 0x86, 0x28, 0xc8, 0x61, + 0x4c, 0x82, 0xf4, 0x25, 0x68, 0xc7, 0x9e, 0xcc, 0x60, 0x20, 0xf2, 0x9a, 0xc2, 0x08, 0x24, 0x68, + 0x00, 0x92, 0xad, 0x9d, 0xdb, 0x34, 0xfd, 0xfe, 0x48, 0x3f, 0xd3, 0xb4, 0xd3, 0xb4, 0x33, 0x5d, + 0x74, 0xba, 0xe9, 0xdf, 0xd0, 0xe9, 0xa2, 0xcb, 0xee, 0xdb, 0x2e, 0xba, 0xea, 0x74, 0x97, 0xe9, + 0xaa, 0x8b, 0x2e, 0x3a, 0x9d, 0xfb, 0x01, 0x10, 0x94, 0xe8, 0x8f, 0xb8, 0xdd, 0xd8, 0xc9, 0xee, + 0x9e, 0x8f, 0x7b, 0xcf, 0x3d, 0xbf, 0x73, 0x70, 0xee, 0x01, 0x2e, 0xa0, 0x70, 0x6b, 0x97, 0xfa, + 0xfb, 0xab, 0x43, 0xdf, 0x0b, 0x3d, 0x3c, 0xc5, 0x89, 0xa5, 0x52, 0xe8, 0x0d, 0xbd, 0xae, 0x1d, + 0xda, 0x82, 0xbd, 0x54, 0xd8, 0x0b, 0xfd, 0x61, 0x47, 0x10, 0xea, 0x7b, 0x0a, 0x64, 0x4d, 0xdb, + 0xef, 0xd1, 0x10, 0x2f, 0x41, 0x6e, 0x87, 0xee, 0x07, 0x43, 0xbb, 0x43, 0xcb, 0xca, 0xb2, 0x72, + 0x2a, 0x4f, 0x62, 0x1a, 0x2f, 0xc0, 0x54, 0xb0, 0x6d, 0xfb, 0xdd, 0x72, 0x8a, 0x0b, 0x04, 0x81, + 0x5f, 0x85, 0x42, 0x68, 0x6f, 0xb9, 0x34, 0xb4, 0xc2, 0xfd, 0x21, 0x2d, 0xa7, 0x97, 0x95, 0x53, + 0xa5, 0xb5, 0x85, 0xd5, 0xd8, 0x9e, 0xc9, 0x85, 0xe6, 0xfe, 0x90, 0x12, 0x08, 0xe3, 0x31, 0xc6, + 0x90, 0xe9, 0x50, 0xd7, 0x2d, 0x67, 0xf8, 0x5a, 0x7c, 0xac, 0x6e, 0x40, 0xe9, 0xaa, 0x79, 0xc9, + 0x0e, 0x69, 0xc5, 0x76, 0x5d, 0xea, 0x57, 0x37, 0xd8, 0x76, 0x76, 0x03, 0xea, 0x0f, 0xec, 0x7e, + 0xbc, 0x9d, 0x88, 0xc6, 0x47, 0x21, 0xdb, 0xf3, 0xbd, 0xdd, 0x61, 0x50, 0x4e, 0x2d, 0xa7, 0x4f, + 0xe5, 0x89, 0xa4, 0xd4, 0x77, 0x00, 0xf4, 0x3d, 0x3a, 0x08, 0x4d, 0x6f, 0x87, 0x0e, 0xf0, 0x53, + 0x90, 0x0f, 0x9d, 0x3e, 0x0d, 0x42, 0xbb, 0x3f, 0xe4, 0x4b, 0xa4, 0xc9, 0x88, 0x71, 0x0f, 0x97, + 0x96, 0x20, 0x37, 0xf4, 0x02, 0x27, 0x74, 0xbc, 0x01, 0xf7, 0x27, 0x4f, 0x62, 0x5a, 0xfd, 0x3f, + 0x98, 0xba, 0x6a, 0xbb, 0xbb, 0x14, 0x3f, 0x0b, 0x19, 0xee, 0xb0, 0xc2, 0x1d, 0x2e, 0xac, 0x0a, + 0xd0, 0xb9, 0x9f, 0x5c, 0xc0, 0xd6, 0xde, 0x63, 0x9a, 0x7c, 0xed, 0x19, 0x22, 0x08, 0x75, 0x07, + 0x66, 0xd6, 0x9d, 0x41, 0xf7, 0xaa, 0xed, 0x3b, 0x0c, 0x8c, 0x47, 0x5c, 0x06, 0xbf, 0x00, 0x59, + 0x3e, 0x08, 0xca, 0xe9, 0xe5, 0xf4, 0xa9, 0xc2, 0xda, 0x8c, 0x9c, 0xc8, 0xf7, 0x46, 0xa4, 0x4c, + 0xfd, 0x8d, 0x02, 0xb0, 0xee, 0xed, 0x0e, 0xba, 0x57, 0x98, 0x10, 0x23, 0x48, 0x07, 0xb7, 0x5c, + 0x09, 0x24, 0x1b, 0xe2, 0xcb, 0x50, 0xda, 0x72, 0x06, 0x5d, 0x6b, 0x4f, 0x6e, 0x47, 0x60, 0x59, + 0x58, 0x7b, 0x41, 0x2e, 0x37, 0x9a, 0xbc, 0x9a, 0xdc, 0x75, 0xa0, 0x0f, 0x42, 0x7f, 0x9f, 0x14, + 0xb7, 0x92, 0xbc, 0xa5, 0x36, 0xe0, 0xc3, 0x4a, 0xcc, 0xe8, 0x0e, 0xdd, 0x8f, 0x8c, 0xee, 0xd0, + 0x7d, 0xfc, 0x1f, 0x49, 0x8f, 0x0a, 0x6b, 0xf3, 0x91, 0xad, 0xc4, 0x5c, 0xe9, 0xe6, 0x6b, 0xa9, + 0x0b, 0x8a, 0xfa, 0xd3, 0x2c, 0x94, 0xf4, 0x3b, 0xb4, 0xb3, 0x1b, 0xd2, 0xc6, 0x90, 0xc5, 0x20, + 0xc0, 0xab, 0x30, 0xef, 0x0c, 0x3a, 0xee, 0x6e, 0x97, 0x5a, 0x94, 0x85, 0xda, 0x0a, 0x59, 0xac, + 0xf9, 0x7a, 0x39, 0x32, 0x27, 0x45, 0x89, 0x24, 0xd0, 0x60, 0xbe, 0xe3, 0xf5, 0x87, 0xb6, 0x3f, + 0xae, 0x9f, 0xe6, 0xf6, 0xe7, 0xa4, 0xfd, 0x91, 0x3e, 0x99, 0x93, 0xda, 0x89, 0x25, 0xea, 0x30, + 0x2b, 0xd7, 0xed, 0x5a, 0x37, 0x1d, 0xea, 0x76, 0x03, 0x9e, 0xba, 0xa5, 0x18, 0xaa, 0xf1, 0x2d, + 0xae, 0x56, 0xa5, 0xf2, 0x26, 0xd7, 0x25, 0x25, 0x67, 0x8c, 0xc6, 0x2b, 0x30, 0xd7, 0x71, 0x1d, + 0xb6, 0x95, 0x9b, 0x0c, 0x62, 0xcb, 0xf7, 0x6e, 0x07, 0xe5, 0x29, 0xbe, 0xff, 0x59, 0x21, 0xd8, + 0x64, 0x7c, 0xe2, 0xdd, 0x0e, 0xf0, 0x6b, 0x90, 0xbb, 0xed, 0xf9, 0x3b, 0xae, 0x67, 0x77, 0xcb, + 0x59, 0x6e, 0xf3, 0x99, 0xc9, 0x36, 0xaf, 0x49, 0x2d, 0x12, 0xeb, 0xe3, 0x53, 0x80, 0x82, 0x5b, + 0xae, 0x15, 0x50, 0x97, 0x76, 0x42, 0xcb, 0x75, 0xfa, 0x4e, 0x58, 0xce, 0xf1, 0xa7, 0xa0, 0x14, + 0xdc, 0x72, 0x5b, 0x9c, 0x5d, 0x63, 0x5c, 0x6c, 0xc1, 0x62, 0xe8, 0xdb, 0x83, 0xc0, 0xee, 0xb0, + 0xc5, 0x2c, 0x27, 0xf0, 0x5c, 0x9b, 0x3f, 0x01, 0x79, 0x6e, 0x72, 0x65, 0xb2, 0x49, 0x73, 0x34, + 0xa5, 0x1a, 0xcd, 0x20, 0x0b, 0xe1, 0x04, 0x2e, 0x7e, 0x05, 0x16, 0x83, 0x1d, 0x67, 0x68, 0xf1, + 0x75, 0xac, 0xa1, 0x6b, 0x0f, 0xac, 0x8e, 0xdd, 0xd9, 0xa6, 0x65, 0xe0, 0x6e, 0x63, 0x26, 0xe4, + 0xa9, 0xd6, 0x74, 0xed, 0x41, 0x85, 0x49, 0xd4, 0xd7, 0xa1, 0x34, 0x8e, 0x23, 0x9e, 0x83, 0xa2, + 0x79, 0xbd, 0xa9, 0x5b, 0x9a, 0xb1, 0x61, 0x19, 0x5a, 0x5d, 0x47, 0x47, 0x70, 0x11, 0xf2, 0x9c, + 0xd5, 0x30, 0x6a, 0xd7, 0x91, 0x82, 0xa7, 0x21, 0xad, 0xd5, 0x6a, 0x28, 0xa5, 0x5e, 0x80, 0x5c, + 0x04, 0x08, 0x9e, 0x85, 0x42, 0xdb, 0x68, 0x35, 0xf5, 0x4a, 0x75, 0xb3, 0xaa, 0x6f, 0xa0, 0x23, + 0x38, 0x07, 0x99, 0x46, 0xcd, 0x6c, 0x22, 0x45, 0x8c, 0xb4, 0x26, 0x4a, 0xb1, 0x99, 0x1b, 0xeb, + 0x1a, 0x4a, 0xab, 0xbf, 0x50, 0x60, 0x61, 0x92, 0x63, 0xb8, 0x00, 0xd3, 0x1b, 0xfa, 0xa6, 0xd6, + 0xae, 0x99, 0xe8, 0x08, 0x9e, 0x87, 0x59, 0xa2, 0x37, 0x75, 0xcd, 0xd4, 0xd6, 0x6b, 0xba, 0x45, + 0x74, 0x6d, 0x03, 0x29, 0x18, 0x43, 0x89, 0x8d, 0xac, 0x4a, 0xa3, 0x5e, 0xaf, 0x9a, 0xa6, 0xbe, + 0x81, 0x52, 0x78, 0x01, 0x10, 0xe7, 0xb5, 0x8d, 0x11, 0x37, 0x8d, 0x11, 0xcc, 0xb4, 0x74, 0x52, + 0xd5, 0x6a, 0xd5, 0x1b, 0x6c, 0x01, 0x94, 0xc1, 0xcf, 0xc1, 0xd3, 0x95, 0x86, 0xd1, 0xaa, 0xb6, + 0x4c, 0xdd, 0x30, 0xad, 0x96, 0xa1, 0x35, 0x5b, 0x6f, 0x36, 0x4c, 0xbe, 0xb2, 0x70, 0x6e, 0x0a, + 0x97, 0x00, 0xb4, 0xb6, 0xd9, 0x10, 0xeb, 0xa0, 0xec, 0x5b, 0x99, 0x9c, 0x82, 0x52, 0xea, 0x07, + 0x29, 0x98, 0xe2, 0xf8, 0xb0, 0xaa, 0x9a, 0xa8, 0x95, 0x7c, 0x1c, 0x57, 0x98, 0xd4, 0x7d, 0x2a, + 0x0c, 0x2f, 0xcc, 0xb2, 0xd6, 0x09, 0x02, 0x9f, 0x80, 0xbc, 0xe7, 0xf7, 0x2c, 0x21, 0x11, 0x55, + 0x3a, 0xe7, 0xf9, 0x3d, 0x5e, 0xce, 0x59, 0x85, 0x64, 0xc5, 0x7d, 0xcb, 0x0e, 0x28, 0xcf, 0xda, + 0x3c, 0x89, 0x69, 0x7c, 0x1c, 0x98, 0x9e, 0xc5, 0xf7, 0x91, 0xe5, 0xb2, 0x69, 0xcf, 0xef, 0x19, + 0x6c, 0x2b, 0xcf, 0x43, 0xb1, 0xe3, 0xb9, 0xbb, 0xfd, 0x81, 0xe5, 0xd2, 0x41, 0x2f, 0xdc, 0x2e, + 0x4f, 0x2f, 0x2b, 0xa7, 0x8a, 0x64, 0x46, 0x30, 0x6b, 0x9c, 0x87, 0xcb, 0x30, 0xdd, 0xd9, 0xb6, + 0xfd, 0x80, 0x8a, 0x4c, 0x2d, 0x92, 0x88, 0xe4, 0x56, 0x69, 0xc7, 0xe9, 0xdb, 0x6e, 0xc0, 0xb3, + 0xb2, 0x48, 0x62, 0x9a, 0x39, 0x71, 0xd3, 0xb5, 0x7b, 0x01, 0xcf, 0xa6, 0x22, 0x11, 0x84, 0xfa, + 0xdf, 0x90, 0x26, 0xde, 0x6d, 0xb6, 0xa4, 0x30, 0x18, 0x94, 0x95, 0xe5, 0xf4, 0x29, 0x4c, 0x22, + 0x92, 0x1d, 0x22, 0xb2, 0x8e, 0x8a, 0xf2, 0x1a, 0x55, 0xce, 0x77, 0x60, 0x86, 0xd0, 0x60, 0xd7, + 0x0d, 0xf5, 0x3b, 0xa1, 0x6f, 0x07, 0x78, 0x0d, 0x0a, 0xc9, 0xca, 0xa1, 0xdc, 0xab, 0x72, 0x00, + 0x1d, 0x95, 0x8c, 0x32, 0x4c, 0xdf, 0xf4, 0x69, 0xb0, 0x4d, 0x7d, 0x59, 0x99, 0x22, 0x92, 0xd5, + 0xe5, 0x02, 0x4f, 0x75, 0x61, 0x83, 0x55, 0x73, 0x59, 0x53, 0x94, 0xb1, 0x6a, 0xce, 0x83, 0x4a, + 0xa4, 0x8c, 0xa1, 0xc7, 0xca, 0x84, 0x65, 0xdf, 0xbc, 0x49, 0x3b, 0x21, 0x15, 0x87, 0x56, 0x86, + 0xcc, 0x30, 0xa6, 0x26, 0x79, 0x2c, 0x6c, 0xce, 0x20, 0xa0, 0x7e, 0x68, 0x39, 0x5d, 0x1e, 0xd0, + 0x0c, 0xc9, 0x09, 0x46, 0xb5, 0x8b, 0x9f, 0x81, 0x0c, 0x2f, 0x34, 0x19, 0x6e, 0x05, 0xa4, 0x15, + 0xe2, 0xdd, 0x26, 0x9c, 0x8f, 0x5f, 0x86, 0x2c, 0xe5, 0xfe, 0xf2, 0xa0, 0x8e, 0x4a, 0x73, 0x12, + 0x0a, 0x22, 0x55, 0xd4, 0x37, 0x60, 0x86, 0xfb, 0x70, 0xcd, 0xf6, 0x07, 0xce, 0xa0, 0xc7, 0x4f, + 0x74, 0xaf, 0x2b, 0x72, 0xaf, 0x48, 0xf8, 0x98, 0x41, 0xd0, 0xa7, 0x41, 0x60, 0xf7, 0xa8, 0x3c, + 0x61, 0x23, 0x52, 0xfd, 0x59, 0x1a, 0x0a, 0xad, 0xd0, 0xa7, 0x76, 0x9f, 0xa3, 0x87, 0xdf, 0x00, + 0x08, 0x42, 0x3b, 0xa4, 0x7d, 0x3a, 0x08, 0x23, 0x18, 0x9e, 0x92, 0xe6, 0x13, 0x7a, 0xab, 0xad, + 0x48, 0x89, 0x24, 0xf4, 0x0f, 0x86, 0x27, 0xf5, 0x10, 0xe1, 0x59, 0xfa, 0x28, 0x05, 0xf9, 0x78, + 0x35, 0xac, 0x41, 0xae, 0x63, 0x87, 0xb4, 0xe7, 0xf9, 0xfb, 0xf2, 0x2c, 0x3e, 0x79, 0x3f, 0xeb, + 0xab, 0x15, 0xa9, 0x4c, 0xe2, 0x69, 0xf8, 0x69, 0x10, 0x0d, 0x8e, 0x48, 0x7d, 0xe1, 0x6f, 0x9e, + 0x73, 0x78, 0xf2, 0xbf, 0x06, 0x78, 0xe8, 0x3b, 0x7d, 0xdb, 0xdf, 0xb7, 0x76, 0xe8, 0x7e, 0x74, + 0x88, 0xa4, 0x27, 0x04, 0x1c, 0x49, 0xbd, 0xcb, 0x74, 0x5f, 0x96, 0xbd, 0x0b, 0xe3, 0x73, 0x65, + 0xca, 0x1e, 0x0e, 0x63, 0x62, 0x26, 0xef, 0x04, 0x82, 0xe8, 0xcc, 0x9f, 0xe2, 0xd9, 0xcd, 0x86, + 0xea, 0x4b, 0x90, 0x8b, 0x36, 0x8f, 0xf3, 0x30, 0xa5, 0xfb, 0xbe, 0xe7, 0xa3, 0x23, 0xbc, 0xfa, + 0xd5, 0x6b, 0xa2, 0x80, 0x6e, 0x6c, 0xb0, 0x02, 0xfa, 0xeb, 0x54, 0x7c, 0xf0, 0x12, 0x7a, 0x6b, + 0x97, 0x06, 0x21, 0xfe, 0x7f, 0x98, 0xa7, 0x3c, 0xd3, 0x9c, 0x3d, 0x6a, 0x75, 0x78, 0x97, 0xc6, + 0xf2, 0x4c, 0x3c, 0x0e, 0xb3, 0xab, 0xa2, 0xa9, 0x8c, 0xba, 0x37, 0x32, 0x17, 0xeb, 0x4a, 0x56, + 0x17, 0xeb, 0x30, 0xef, 0xf4, 0xfb, 0xb4, 0xeb, 0xd8, 0x61, 0x72, 0x01, 0x11, 0xb0, 0xc5, 0xa8, + 0x89, 0x19, 0x6b, 0x02, 0xc9, 0x5c, 0x3c, 0x23, 0x5e, 0xe6, 0x24, 0x64, 0x43, 0xde, 0xb0, 0xca, + 0x33, 0xbc, 0x18, 0x55, 0x35, 0xce, 0x24, 0x52, 0x88, 0x5f, 0x02, 0xd1, 0xfe, 0xf2, 0xfa, 0x35, + 0x4a, 0x88, 0x51, 0x57, 0x43, 0x84, 0x1c, 0x9f, 0x84, 0xd2, 0xd8, 0xe1, 0xd7, 0xe5, 0x80, 0xa5, + 0x49, 0x31, 0x79, 0x92, 0x75, 0xf1, 0x69, 0x98, 0xf6, 0xc4, 0xc1, 0xc7, 0x2b, 0xdb, 0x68, 0xc7, + 0xe3, 0xa7, 0x22, 0x89, 0xb4, 0xd4, 0xff, 0x85, 0xd9, 0x18, 0xc1, 0x60, 0xe8, 0x0d, 0x02, 0x8a, + 0x57, 0x20, 0xeb, 0xf3, 0xc7, 0x49, 0xa2, 0x86, 0xe5, 0x12, 0x89, 0x7a, 0x40, 0xa4, 0x86, 0xda, + 0x85, 0x59, 0xc1, 0xb9, 0xe6, 0x84, 0xdb, 0x3c, 0x50, 0xf8, 0x24, 0x4c, 0x51, 0x36, 0x38, 0x80, + 0x39, 0x69, 0x56, 0xb8, 0x9c, 0x08, 0x69, 0xc2, 0x4a, 0xea, 0x81, 0x56, 0xfe, 0x9a, 0x82, 0x79, + 0xb9, 0xcb, 0x75, 0x3b, 0xec, 0x6c, 0x3f, 0xa6, 0xc1, 0x7e, 0x19, 0xa6, 0x19, 0xdf, 0x89, 0x1f, + 0x8c, 0x09, 0xe1, 0x8e, 0x34, 0x58, 0xc0, 0xed, 0xc0, 0x4a, 0x44, 0x57, 0x36, 0x5f, 0x45, 0x3b, + 0x48, 0x9c, 0xfc, 0x13, 0xf2, 0x22, 0xfb, 0x80, 0xbc, 0x98, 0x7e, 0xa8, 0xbc, 0xd8, 0x80, 0x85, + 0x71, 0xc4, 0x65, 0x72, 0xfc, 0x27, 0x4c, 0x8b, 0xa0, 0x44, 0x25, 0x70, 0x52, 0xdc, 0x22, 0x15, + 0xf5, 0xb7, 0x29, 0x58, 0x90, 0xd5, 0xe9, 0xd3, 0xf1, 0x98, 0x26, 0x70, 0x9e, 0x7a, 0x18, 0x9c, + 0x1f, 0x32, 0x7e, 0x6a, 0x05, 0x16, 0x0f, 0xe0, 0xf8, 0x08, 0x0f, 0xeb, 0xc7, 0x0a, 0xcc, 0xac, + 0xd3, 0x9e, 0x33, 0x78, 0x4c, 0xa3, 0x90, 0x00, 0x37, 0xf3, 0x50, 0x49, 0x7c, 0x1e, 0x8a, 0xd2, + 0x5f, 0x89, 0xd6, 0x61, 0xb4, 0x95, 0x49, 0x68, 0xff, 0x59, 0x81, 0x62, 0xc5, 0xeb, 0xf7, 0x9d, + 0xf0, 0x31, 0x45, 0xea, 0xb0, 0x9f, 0x99, 0x49, 0x7e, 0x22, 0x28, 0x45, 0x6e, 0x0a, 0x80, 0xd4, + 0xbf, 0x28, 0x30, 0x4b, 0x3c, 0xd7, 0xdd, 0xb2, 0x3b, 0x3b, 0x4f, 0xb6, 0xef, 0x18, 0xd0, 0xc8, + 0x51, 0xe9, 0xfd, 0xdf, 0x15, 0x28, 0x35, 0x7d, 0xca, 0x5e, 0xac, 0x9f, 0x68, 0xe7, 0x59, 0x27, + 0xdc, 0x0d, 0x65, 0x0f, 0x91, 0x27, 0x7c, 0xac, 0xce, 0xc1, 0x6c, 0xec, 0xbb, 0xc4, 0xe3, 0x0f, + 0x0a, 0x2c, 0x8a, 0x04, 0x91, 0x92, 0xee, 0x63, 0x0a, 0x4b, 0xe4, 0x6f, 0x26, 0xe1, 0x6f, 0x19, + 0x8e, 0x1e, 0xf4, 0x4d, 0xba, 0xfd, 0x6e, 0x0a, 0x8e, 0x45, 0xb9, 0xf1, 0x98, 0x3b, 0xfe, 0x2f, + 0xe4, 0xc3, 0x12, 0x94, 0x0f, 0x83, 0x20, 0x11, 0x7a, 0x3f, 0x05, 0xe5, 0x8a, 0x4f, 0xed, 0x90, + 0x26, 0x7a, 0x91, 0x27, 0x27, 0x37, 0xf0, 0x2b, 0x30, 0x33, 0xb4, 0xfd, 0xd0, 0xe9, 0x38, 0x43, + 0x9b, 0xbd, 0xed, 0x4d, 0xf1, 0x56, 0xe7, 0xc0, 0x02, 0x63, 0x2a, 0xea, 0x09, 0x38, 0x3e, 0x01, + 0x11, 0x89, 0xd7, 0x3f, 0x14, 0xc0, 0xad, 0xd0, 0xf6, 0xc3, 0x4f, 0xc1, 0xa9, 0x32, 0x31, 0x99, + 0x16, 0x61, 0x7e, 0xcc, 0xff, 0x24, 0x2e, 0x34, 0xfc, 0x54, 0x9c, 0x38, 0xf7, 0xc4, 0x25, 0xe9, + 0xbf, 0xc4, 0xe5, 0x4f, 0x0a, 0x2c, 0x55, 0x3c, 0xf1, 0x61, 0xf1, 0x89, 0x7c, 0xc2, 0xd4, 0xa7, + 0xe1, 0xc4, 0x44, 0x07, 0x25, 0x00, 0x7f, 0x54, 0xe0, 0x28, 0xa1, 0x76, 0xf7, 0xc9, 0x74, 0xfe, + 0x0a, 0x1c, 0x3b, 0xe4, 0x9c, 0xec, 0x50, 0xcf, 0x43, 0xae, 0x4f, 0x43, 0xbb, 0x6b, 0x87, 0xb6, + 0x74, 0x69, 0x29, 0x5a, 0x77, 0xa4, 0x5d, 0x97, 0x1a, 0x24, 0xd6, 0x55, 0x3f, 0x4a, 0xc1, 0x3c, + 0xef, 0x75, 0x3f, 0x7b, 0xd1, 0x9a, 0xfc, 0x2e, 0xf0, 0xbe, 0x02, 0x0b, 0xe3, 0x00, 0xc5, 0xef, + 0x04, 0xff, 0xee, 0xef, 0x15, 0x13, 0x0a, 0x42, 0x7a, 0x52, 0x0b, 0xfa, 0xbb, 0x14, 0x94, 0x93, + 0x5b, 0xfa, 0xec, 0xdb, 0xc6, 0xf8, 0xb7, 0x8d, 0x4f, 0xfc, 0x31, 0xeb, 0x03, 0x05, 0x8e, 0x4f, + 0x00, 0xf4, 0x93, 0x05, 0x3a, 0xf1, 0x85, 0x23, 0xf5, 0xc0, 0x2f, 0x1c, 0x0f, 0x1b, 0xea, 0xdf, + 0x2b, 0xb0, 0x50, 0x17, 0x1f, 0x96, 0xc5, 0x7b, 0xfc, 0xe3, 0x5b, 0xcd, 0xf8, 0xb7, 0xe3, 0xcc, + 0xe8, 0xfa, 0x46, 0xad, 0xc0, 0xe2, 0x01, 0xd7, 0x1e, 0xe1, 0xdb, 0xc4, 0xdf, 0x14, 0x98, 0x93, + 0xab, 0x68, 0x8f, 0x6d, 0x23, 0x30, 0x01, 0x1d, 0xfc, 0x0c, 0xa4, 0x9d, 0x6e, 0xd4, 0x41, 0x8e, + 0x5f, 0x82, 0x33, 0x81, 0x7a, 0x11, 0x70, 0xd2, 0xef, 0x47, 0x80, 0x8e, 0xf7, 0x56, 0x0c, 0xf8, + 0x37, 0xa9, 0xed, 0x86, 0x51, 0x01, 0x51, 0x7f, 0x9e, 0x82, 0x22, 0x61, 0x1c, 0xa7, 0x4f, 0x5b, + 0xa1, 0x1d, 0x06, 0xf8, 0x39, 0x98, 0xd9, 0xe6, 0x2a, 0xd6, 0xe8, 0x39, 0xc8, 0x93, 0x82, 0xe0, + 0x89, 0x8f, 0xb7, 0x6b, 0xb0, 0x18, 0xd0, 0x8e, 0x37, 0xe8, 0x06, 0xd6, 0x16, 0xdd, 0x76, 0x06, + 0x5d, 0xab, 0x6f, 0x07, 0xa1, 0xbc, 0x1f, 0x2a, 0x92, 0x79, 0x29, 0x5c, 0xe7, 0xb2, 0x3a, 0x17, + 0xe1, 0x33, 0xb0, 0xb0, 0xe5, 0x0c, 0x5c, 0xaf, 0x67, 0x0d, 0x5d, 0x7b, 0x9f, 0xfa, 0x81, 0xd5, + 0xf1, 0x76, 0x07, 0x02, 0xaa, 0x29, 0x82, 0x85, 0xac, 0x29, 0x44, 0x15, 0x26, 0xc1, 0x37, 0x60, + 0x65, 0xa2, 0x15, 0xeb, 0xa6, 0xe3, 0x86, 0xd4, 0xa7, 0x5d, 0xcb, 0xa7, 0x43, 0xd7, 0xe9, 0x88, + 0xeb, 0x5d, 0xd1, 0x4c, 0xbd, 0x38, 0xc1, 0xf4, 0xa6, 0x54, 0x27, 0x23, 0x6d, 0x7c, 0x02, 0xf2, + 0x9d, 0xe1, 0xae, 0xb5, 0xcb, 0xaf, 0x74, 0x58, 0x59, 0x51, 0x48, 0xae, 0x33, 0xdc, 0x6d, 0x33, + 0x1a, 0x23, 0x48, 0xdf, 0x1a, 0x8a, 0x6a, 0xa2, 0x10, 0x36, 0x54, 0x3f, 0x56, 0xa0, 0xa4, 0xf5, + 0x7a, 0x3e, 0xed, 0xd9, 0xa1, 0x84, 0xe9, 0x0c, 0x2c, 0x08, 0x48, 0xf6, 0x2d, 0xf9, 0xdf, 0x88, + 0xf0, 0x47, 0x11, 0xfe, 0x48, 0x99, 0xf8, 0x6b, 0x44, 0xf8, 0x73, 0x0e, 0x8e, 0xee, 0x0e, 0x26, + 0xce, 0x49, 0xf1, 0x39, 0x0b, 0xb1, 0x34, 0x39, 0xeb, 0x7f, 0xe0, 0xf8, 0x64, 0x14, 0xfa, 0x8e, + 0xb8, 0xf9, 0x2f, 0x92, 0xa3, 0x13, 0x9c, 0xae, 0x3b, 0x83, 0xfb, 0x4c, 0xb5, 0xef, 0x70, 0xbc, + 0xee, 0x31, 0xd5, 0xbe, 0xa3, 0xfe, 0x32, 0xfe, 0x24, 0x1b, 0xa5, 0x4b, 0x5c, 0x1e, 0xa3, 0x1c, + 0x57, 0xee, 0x97, 0xe3, 0x65, 0x98, 0x0e, 0xa8, 0xbf, 0xe7, 0x0c, 0x7a, 0xd1, 0x9d, 0xa1, 0x24, + 0x71, 0x0b, 0x5e, 0x94, 0xbe, 0xd3, 0x3b, 0x21, 0xf5, 0x07, 0xb6, 0xeb, 0xee, 0x5b, 0xe2, 0xcd, + 0x71, 0x10, 0xd2, 0xae, 0x35, 0xfa, 0xcb, 0x45, 0x94, 0xc8, 0xe7, 0x85, 0xb6, 0x1e, 0x2b, 0x93, + 0x58, 0xd7, 0x8c, 0xff, 0x7f, 0x79, 0x1d, 0x4a, 0xbe, 0x4c, 0x62, 0x2b, 0x60, 0xe1, 0x91, 0x9d, + 0xc1, 0x42, 0x7c, 0xf1, 0x97, 0xc8, 0x70, 0x52, 0xf4, 0xc7, 0x12, 0xfe, 0x02, 0xcc, 0xc8, 0x1d, + 0xd9, 0xae, 0x63, 0x8f, 0x3a, 0x85, 0x03, 0xbf, 0xfe, 0x68, 0x4c, 0x48, 0xe4, 0x4f, 0x42, 0x9c, + 0x78, 0x2b, 0x93, 0xcb, 0xa2, 0x69, 0xf5, 0x57, 0x0a, 0xcc, 0x4f, 0x68, 0xbb, 0xe2, 0x9e, 0x4e, + 0x49, 0xbc, 0x32, 0xfe, 0x17, 0x4c, 0xf1, 0xeb, 0x3e, 0x79, 0x8b, 0x7d, 0xec, 0x70, 0xd7, 0xc6, + 0xaf, 0xe6, 0x88, 0xd0, 0x62, 0xcf, 0x22, 0xf7, 0xa9, 0xc3, 0xdf, 0x19, 0xa3, 0x53, 0xa3, 0xc0, + 0x78, 0xe2, 0x35, 0xf2, 0xf0, 0x4b, 0x68, 0xe6, 0x81, 0x2f, 0xa1, 0x2b, 0xdf, 0x49, 0x43, 0xbe, + 0xbe, 0xdf, 0xba, 0xe5, 0x6e, 0xba, 0x76, 0x8f, 0xdf, 0x9d, 0xd5, 0x9b, 0xe6, 0x75, 0x74, 0x04, + 0xcf, 0x41, 0xd1, 0x68, 0x98, 0x96, 0xd1, 0xae, 0xd5, 0xac, 0xcd, 0x9a, 0x76, 0x09, 0x29, 0x18, + 0xc1, 0x4c, 0x93, 0x54, 0xad, 0xcb, 0xfa, 0x75, 0xc1, 0x49, 0xe1, 0x79, 0x98, 0x6d, 0x1b, 0xd5, + 0x2b, 0x6d, 0x7d, 0xc4, 0xcc, 0xe0, 0x45, 0x98, 0xab, 0xb7, 0x6b, 0x66, 0xb5, 0x59, 0x4b, 0xb0, + 0x73, 0xb8, 0x08, 0xf9, 0xf5, 0x5a, 0x63, 0x5d, 0x90, 0x88, 0xad, 0xdf, 0x36, 0x5a, 0xd5, 0x4b, + 0x86, 0xbe, 0x21, 0x58, 0xcb, 0x8c, 0x75, 0x43, 0x27, 0x8d, 0xcd, 0x6a, 0x64, 0xf2, 0x22, 0x46, + 0x50, 0x58, 0xaf, 0x1a, 0x1a, 0x91, 0xab, 0xdc, 0x55, 0x70, 0x09, 0xf2, 0xba, 0xd1, 0xae, 0x4b, + 0x3a, 0x85, 0xcb, 0x30, 0xaf, 0xb5, 0xcd, 0x86, 0x55, 0x35, 0x2a, 0x44, 0xaf, 0xeb, 0x86, 0x29, + 0x25, 0x19, 0x3c, 0x0f, 0x25, 0xb3, 0x5a, 0xd7, 0x5b, 0xa6, 0x56, 0x6f, 0x4a, 0x26, 0xdb, 0x45, + 0xae, 0xa5, 0x47, 0x3a, 0x08, 0x2f, 0xc1, 0xa2, 0xd1, 0xb0, 0xe4, 0x0f, 0x10, 0xd6, 0x55, 0xad, + 0xd6, 0xd6, 0xa5, 0x6c, 0x19, 0x1f, 0x03, 0xdc, 0x30, 0xac, 0x76, 0x73, 0x43, 0x33, 0x75, 0xcb, + 0x68, 0x5c, 0x93, 0x82, 0x8b, 0xb8, 0x04, 0xb9, 0xd1, 0x0e, 0xee, 0x32, 0x14, 0x8a, 0x4d, 0x8d, + 0x98, 0x23, 0x67, 0xef, 0xde, 0x65, 0x60, 0xc1, 0x25, 0xd2, 0x68, 0x37, 0x47, 0x6a, 0x73, 0x50, + 0x90, 0x60, 0x49, 0x56, 0x86, 0xb1, 0xd6, 0xab, 0x46, 0x25, 0xde, 0xdf, 0xdd, 0xdc, 0x52, 0x0a, + 0x29, 0x2b, 0x3b, 0x90, 0xe1, 0xe1, 0xc8, 0x41, 0xc6, 0x68, 0x18, 0x3a, 0x3a, 0x82, 0x67, 0x01, + 0xaa, 0xad, 0xaa, 0x61, 0xea, 0x97, 0x88, 0x56, 0x63, 0x6e, 0x73, 0x46, 0x04, 0x20, 0xf3, 0x76, + 0x06, 0xa6, 0xab, 0xad, 0xcd, 0x5a, 0x43, 0x33, 0xa5, 0x9b, 0xd5, 0xd6, 0x95, 0x76, 0xc3, 0x64, + 0x42, 0x84, 0x0b, 0x90, 0xad, 0xb6, 0x4c, 0xfd, 0x6d, 0x93, 0xf9, 0xc5, 0x65, 0x02, 0x55, 0x74, + 0xf7, 0xe2, 0xca, 0x87, 0x69, 0xc8, 0xf0, 0xdf, 0xd7, 0x8a, 0x90, 0xe7, 0xd1, 0x36, 0xaf, 0x37, + 0x99, 0xc9, 0x3c, 0x64, 0xaa, 0x86, 0x79, 0x01, 0x7d, 0x2e, 0x85, 0x01, 0xa6, 0xda, 0x7c, 0xfc, + 0xf9, 0x2c, 0x1b, 0x57, 0x0d, 0xf3, 0x95, 0xf3, 0xe8, 0xdd, 0x14, 0x5b, 0xb6, 0x2d, 0x88, 0x2f, + 0x44, 0x82, 0xb5, 0x73, 0xe8, 0xbd, 0x58, 0xb0, 0x76, 0x0e, 0x7d, 0x31, 0x12, 0x9c, 0x5d, 0x43, + 0x5f, 0x8a, 0x05, 0x67, 0xd7, 0xd0, 0x97, 0x23, 0xc1, 0xf9, 0x73, 0xe8, 0x2b, 0xb1, 0xe0, 0xfc, + 0x39, 0xf4, 0xd5, 0x2c, 0xf3, 0x85, 0x7b, 0x72, 0x76, 0x0d, 0x7d, 0x2d, 0x17, 0x53, 0xe7, 0xcf, + 0xa1, 0xaf, 0xe7, 0x58, 0xfc, 0xe3, 0xa8, 0xa2, 0x6f, 0x20, 0xb6, 0x4d, 0x16, 0x20, 0xf4, 0x4d, + 0x3e, 0x64, 0x22, 0xf4, 0x2d, 0xc4, 0x7c, 0x64, 0x5c, 0x4e, 0xbe, 0xcf, 0x25, 0xd7, 0x75, 0x8d, + 0xa0, 0x6f, 0x67, 0xc5, 0xff, 0x2e, 0x95, 0x6a, 0x5d, 0xab, 0x21, 0xcc, 0x67, 0x30, 0x54, 0xbe, + 0x7b, 0x86, 0x0d, 0x59, 0x7a, 0xa2, 0xef, 0x35, 0x99, 0xc1, 0xab, 0x1a, 0xa9, 0xbc, 0xa9, 0x11, + 0xf4, 0xfd, 0x33, 0xcc, 0xe0, 0x55, 0x8d, 0x48, 0xbc, 0x7e, 0xd0, 0x64, 0x8a, 0x5c, 0xf4, 0xc1, + 0x19, 0xb6, 0x69, 0xc9, 0xff, 0x61, 0x13, 0xe7, 0x20, 0xbd, 0x5e, 0x35, 0xd1, 0x87, 0xdc, 0x1a, + 0x4b, 0x51, 0xf4, 0x23, 0xc4, 0x98, 0x2d, 0xdd, 0x44, 0x3f, 0x66, 0xcc, 0x29, 0xb3, 0xdd, 0xac, + 0xe9, 0xe8, 0x29, 0xb6, 0xb9, 0x4b, 0x7a, 0xa3, 0xae, 0x9b, 0xe4, 0x3a, 0xfa, 0x09, 0x57, 0x7f, + 0xab, 0xd5, 0x30, 0xd0, 0x47, 0x08, 0x97, 0x00, 0xf4, 0xb7, 0x9b, 0x44, 0x6f, 0xb5, 0xaa, 0x0d, + 0x03, 0x3d, 0xbb, 0xb2, 0x09, 0xe8, 0x60, 0x39, 0x60, 0x0e, 0xb4, 0x8d, 0xcb, 0x46, 0xe3, 0x9a, + 0x81, 0x8e, 0x30, 0xa2, 0x49, 0xf4, 0xa6, 0x46, 0x74, 0xa4, 0x60, 0x80, 0xac, 0xfc, 0x8b, 0x26, + 0x85, 0x67, 0x20, 0x47, 0x1a, 0xb5, 0xda, 0xba, 0x56, 0xb9, 0x8c, 0xd2, 0xeb, 0xaf, 0xc2, 0xac, + 0xe3, 0xad, 0xee, 0x39, 0x21, 0x0d, 0x02, 0xf1, 0x83, 0xe4, 0x0d, 0x55, 0x52, 0x8e, 0x77, 0x5a, + 0x8c, 0x4e, 0xf7, 0xbc, 0xd3, 0x7b, 0xe1, 0x69, 0x2e, 0x3d, 0xcd, 0x2b, 0xc6, 0x56, 0x96, 0x13, + 0x67, 0xff, 0x19, 0x00, 0x00, 0xff, 0xff, 0x59, 0xd0, 0x47, 0x8d, 0x7e, 0x29, 0x00, 0x00, } diff --git a/go/vt/proto/queryservice/queryservice.pb.go b/go/vt/proto/queryservice/queryservice.pb.go index 051d9026b97..822d6ddd6d4 100644 --- a/go/vt/proto/queryservice/queryservice.pb.go +++ b/go/vt/proto/queryservice/queryservice.pb.go @@ -30,43 +30,42 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package func init() { proto.RegisterFile("queryservice.proto", fileDescriptor_4bd2dde8711f22e3) } var fileDescriptor_4bd2dde8711f22e3 = []byte{ - // 566 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x95, 0xdf, 0x4f, 0x13, 0x41, - 0x10, 0xc7, 0xf5, 0x01, 0x6a, 0xa6, 0x05, 0x71, 0x11, 0x95, 0x03, 0xcb, 0x8f, 0x37, 0x63, 0xd2, - 0x1a, 0x35, 0x31, 0x21, 0xf1, 0x81, 0x36, 0x12, 0x0d, 0xf1, 0x57, 0x11, 0x62, 0x34, 0x31, 0xd9, - 0x5e, 0x27, 0xf5, 0xc2, 0xf5, 0xb6, 0xdc, 0xee, 0x15, 0xfd, 0x5b, 0xfd, 0x67, 0x0c, 0x77, 0x37, - 0x73, 0xbb, 0xdb, 0x3b, 0xde, 0xd8, 0xef, 0x77, 0xe6, 0xc3, 0xdc, 0x4e, 0x67, 0x16, 0xc4, 0x55, - 0x86, 0xe9, 0x5f, 0x8d, 0xe9, 0x22, 0x0a, 0xb1, 0x37, 0x4f, 0x95, 0x51, 0xa2, 0x63, 0x6b, 0x41, - 0x3b, 0x3f, 0x15, 0x56, 0xb0, 0x31, 0x8e, 0x92, 0x58, 0x4d, 0x27, 0xd2, 0xc8, 0x42, 0x79, 0xf9, - 0x6f, 0x0d, 0x56, 0xbe, 0xde, 0x44, 0x88, 0x23, 0x68, 0xbd, 0xfb, 0x83, 0x61, 0x66, 0x50, 0x6c, - 0xf5, 0x8a, 0xa4, 0xf2, 0x3c, 0xc2, 0xab, 0x0c, 0xb5, 0x09, 0x1e, 0xf9, 0xb2, 0x9e, 0xab, 0x44, - 0xe3, 0xe1, 0x1d, 0xf1, 0x01, 0x3a, 0xa5, 0x38, 0x90, 0x26, 0xfc, 0x2d, 0x02, 0x37, 0x32, 0x17, - 0x89, 0xb2, 0x53, 0xeb, 0x31, 0xea, 0x13, 0xac, 0x9d, 0x99, 0x14, 0xe5, 0x8c, 0x8a, 0xa1, 0x78, - 0x47, 0x25, 0xd8, 0x6e, 0xbd, 0x49, 0xb4, 0x17, 0x77, 0xc5, 0x6b, 0x58, 0x19, 0xe0, 0x34, 0x4a, - 0xc4, 0x66, 0x19, 0x9a, 0x9f, 0x28, 0xff, 0xa1, 0x2b, 0x72, 0x15, 0x6f, 0x60, 0x75, 0xa8, 0x66, - 0xb3, 0xc8, 0x08, 0x8a, 0x28, 0x8e, 0x94, 0xb7, 0xe5, 0xa9, 0x9c, 0xf8, 0x16, 0xee, 0x8d, 0x54, - 0x1c, 0x8f, 0x65, 0x78, 0x29, 0xe8, 0xbe, 0x48, 0xa0, 0xe4, 0xc7, 0x4b, 0x3a, 0xa7, 0x1f, 0x41, - 0xeb, 0x4b, 0x8a, 0x73, 0x99, 0x56, 0x4d, 0x28, 0xcf, 0x7e, 0x13, 0x58, 0xe6, 0xdc, 0xcf, 0xb0, - 0x5e, 0x94, 0x53, 0x5a, 0x13, 0xb1, 0xeb, 0x54, 0x49, 0x32, 0x91, 0x9e, 0x36, 0xb8, 0x0c, 0x3c, - 0x87, 0x0d, 0x2a, 0x91, 0x91, 0x5d, 0xaf, 0x76, 0x1f, 0xba, 0xd7, 0xe8, 0x33, 0xf6, 0x3b, 0x3c, - 0x18, 0xa6, 0x28, 0x0d, 0x7e, 0x4b, 0x65, 0xa2, 0x65, 0x68, 0x22, 0x95, 0x08, 0xca, 0x5b, 0x72, - 0x08, 0xbc, 0xdf, 0x1c, 0xc0, 0xe4, 0x13, 0x68, 0x9f, 0x19, 0x99, 0x9a, 0xb2, 0x75, 0xdb, 0xfc, - 0xe3, 0x60, 0x8d, 0x68, 0x41, 0x9d, 0xe5, 0x70, 0xd0, 0x70, 0x1f, 0x99, 0x53, 0x69, 0x4b, 0x1c, - 0xdb, 0x62, 0xce, 0x2f, 0xd8, 0x1c, 0xaa, 0x24, 0x8c, 0xb3, 0x89, 0xf3, 0xad, 0x07, 0x7c, 0xf1, - 0x4b, 0x1e, 0x71, 0x0f, 0x6f, 0x0b, 0x61, 0xfe, 0x08, 0xee, 0x8f, 0x50, 0x4e, 0x6c, 0x36, 0x35, - 0xd5, 0xd3, 0x89, 0xdb, 0x6d, 0xb2, 0xed, 0x51, 0xce, 0x87, 0x81, 0xc6, 0x2f, 0xb0, 0x27, 0xc4, - 0x9b, 0xbe, 0x9d, 0x5a, 0xcf, 0x6e, 0xb4, 0xed, 0x14, 0xab, 0x61, 0xaf, 0x26, 0xc7, 0xd9, 0x0f, - 0xfb, 0xcd, 0x01, 0xf6, 0x92, 0xf8, 0x88, 0x5a, 0xcb, 0x29, 0x16, 0x83, 0xcf, 0x4b, 0xc2, 0x51, - 0xfd, 0x25, 0xe1, 0x99, 0xd6, 0x92, 0x18, 0x02, 0x94, 0xe6, 0x71, 0x78, 0x29, 0x9e, 0xb8, 0xf1, - 0xc7, 0x55, 0xbb, 0xb7, 0x6b, 0x1c, 0x2e, 0xea, 0x14, 0x3a, 0x05, 0xfa, 0x3d, 0xca, 0xd8, 0x54, - 0x4b, 0xd0, 0x16, 0xfd, 0x9b, 0x73, 0x3d, 0xab, 0xa2, 0x53, 0xe8, 0x9c, 0xcf, 0x27, 0xd2, 0xd0, - 0x07, 0x12, 0xcc, 0x16, 0x7d, 0x98, 0xeb, 0x59, 0xb0, 0x13, 0x68, 0x5d, 0x30, 0xc7, 0x7a, 0x02, - 0x2e, 0x7c, 0x4e, 0x9d, 0x67, 0x71, 0x46, 0xd0, 0x26, 0x59, 0x5d, 0x6b, 0xd1, 0xad, 0x8b, 0x57, - 0xd7, 0xba, 0xda, 0x05, 0x4d, 0xbe, 0xc5, 0xfc, 0x09, 0xeb, 0xd5, 0xbf, 0xca, 0x62, 0xa3, 0xc5, - 0x41, 0x7d, 0x19, 0x37, 0x5e, 0x35, 0x1e, 0xb7, 0x84, 0x54, 0xf0, 0xc1, 0xf3, 0x1f, 0xcf, 0x16, - 0x91, 0x41, 0xad, 0x7b, 0x91, 0xea, 0x17, 0x7f, 0xf5, 0xa7, 0xaa, 0xbf, 0x30, 0xfd, 0xfc, 0xf5, - 0xeb, 0xdb, 0x2f, 0xe5, 0x78, 0x35, 0xd7, 0x5e, 0xfd, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xf3, - 0x72, 0x91, 0x54, 0x07, 0x00, 0x00, + // 554 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x95, 0x4d, 0x6f, 0xd3, 0x40, + 0x10, 0x86, 0xe1, 0xd0, 0x06, 0x4d, 0xd2, 0x52, 0xb6, 0x14, 0xa8, 0x53, 0xd2, 0x8f, 0x1b, 0x42, + 0x4a, 0x10, 0x20, 0x21, 0x55, 0xe2, 0xd0, 0x44, 0x54, 0x20, 0xc4, 0x57, 0x0a, 0x15, 0x02, 0x09, + 0x69, 0xe3, 0x8c, 0x82, 0x55, 0xc7, 0x9b, 0x7a, 0xd7, 0x29, 0xfc, 0x09, 0x7e, 0x33, 0xaa, 0xed, + 0x19, 0xef, 0x6e, 0xec, 0xde, 0xb2, 0xef, 0x3b, 0xf3, 0x64, 0xbc, 0xe3, 0x19, 0x83, 0xb8, 0xcc, + 0x30, 0xfd, 0xab, 0x31, 0x5d, 0x46, 0x21, 0xf6, 0x17, 0xa9, 0x32, 0x4a, 0x74, 0x6c, 0x2d, 0x68, + 0xe7, 0xa7, 0xc2, 0x0a, 0xb6, 0x26, 0x51, 0x12, 0xab, 0xd9, 0x54, 0x1a, 0x59, 0x28, 0xcf, 0xff, + 0x6d, 0xc0, 0xda, 0x97, 0xeb, 0x08, 0x71, 0x0c, 0xad, 0x37, 0x7f, 0x30, 0xcc, 0x0c, 0x8a, 0x9d, + 0x7e, 0x91, 0x54, 0x9e, 0xc7, 0x78, 0x99, 0xa1, 0x36, 0xc1, 0x03, 0x5f, 0xd6, 0x0b, 0x95, 0x68, + 0x3c, 0xba, 0x25, 0xde, 0x41, 0xa7, 0x14, 0x87, 0xd2, 0x84, 0xbf, 0x45, 0xe0, 0x46, 0xe6, 0x22, + 0x51, 0xba, 0xb5, 0x1e, 0xa3, 0x3e, 0xc2, 0xc6, 0x99, 0x49, 0x51, 0xce, 0xa9, 0x18, 0x8a, 0x77, + 0x54, 0x82, 0xed, 0xd5, 0x9b, 0x44, 0x7b, 0x76, 0x5b, 0xbc, 0x84, 0xb5, 0x21, 0xce, 0xa2, 0x44, + 0x6c, 0x97, 0xa1, 0xf9, 0x89, 0xf2, 0xef, 0xbb, 0x22, 0x57, 0xf1, 0x0a, 0xd6, 0x47, 0x6a, 0x3e, + 0x8f, 0x8c, 0xa0, 0x88, 0xe2, 0x48, 0x79, 0x3b, 0x9e, 0xca, 0x89, 0xaf, 0xe1, 0xce, 0x58, 0xc5, + 0xf1, 0x44, 0x86, 0x17, 0x82, 0xee, 0x8b, 0x04, 0x4a, 0x7e, 0xb8, 0xa2, 0x73, 0xfa, 0x31, 0xb4, + 0x3e, 0xa7, 0xb8, 0x90, 0x69, 0xd5, 0x84, 0xf2, 0xec, 0x37, 0x81, 0x65, 0xce, 0xfd, 0x04, 0x9b, + 0x45, 0x39, 0xa5, 0x35, 0x15, 0x7b, 0x4e, 0x95, 0x24, 0x13, 0xe9, 0x71, 0x83, 0xcb, 0xc0, 0x6f, + 0xb0, 0x45, 0x25, 0x32, 0xb2, 0xe7, 0xd5, 0xee, 0x43, 0xf7, 0x1b, 0x7d, 0xc6, 0x7e, 0x87, 0x7b, + 0xa3, 0x14, 0xa5, 0xc1, 0xaf, 0xa9, 0x4c, 0xb4, 0x0c, 0x4d, 0xa4, 0x12, 0x41, 0x79, 0x2b, 0x0e, + 0x81, 0x0f, 0x9a, 0x03, 0x98, 0x7c, 0x0a, 0xed, 0x33, 0x23, 0x53, 0x53, 0xb6, 0x6e, 0x97, 0x5f, + 0x0e, 0xd6, 0x88, 0x16, 0xd4, 0x59, 0x0e, 0x07, 0x0d, 0xf7, 0x91, 0x39, 0x95, 0xb6, 0xc2, 0xb1, + 0x2d, 0xe6, 0xfc, 0x82, 0xed, 0x91, 0x4a, 0xc2, 0x38, 0x9b, 0x3a, 0xcf, 0x7a, 0xc8, 0x17, 0xbf, + 0xe2, 0x11, 0xf7, 0xe8, 0xa6, 0x10, 0xe6, 0x8f, 0xe1, 0xee, 0x18, 0xe5, 0xd4, 0x66, 0x53, 0x53, + 0x3d, 0x9d, 0xb8, 0xbd, 0x26, 0xdb, 0x1e, 0xe5, 0x7c, 0x18, 0x68, 0xfc, 0x02, 0x7b, 0x42, 0xbc, + 0xe9, 0xeb, 0xd6, 0x7a, 0x76, 0xa3, 0x6d, 0xa7, 0x58, 0x0d, 0xfb, 0x35, 0x39, 0xce, 0x7e, 0x38, + 0x68, 0x0e, 0xb0, 0x97, 0xc4, 0x07, 0xd4, 0x5a, 0xce, 0xb0, 0x18, 0x7c, 0x5e, 0x12, 0x8e, 0xea, + 0x2f, 0x09, 0xcf, 0xb4, 0x96, 0xc4, 0x08, 0xa0, 0x34, 0x4f, 0xc2, 0x0b, 0xf1, 0xc8, 0x8d, 0x3f, + 0xa9, 0xda, 0xbd, 0x5b, 0xe3, 0x70, 0x51, 0xef, 0xa1, 0x53, 0xa0, 0xdf, 0xa2, 0x8c, 0x4d, 0xb5, + 0x04, 0x6d, 0xd1, 0xbf, 0x39, 0xd7, 0xb3, 0x2a, 0x3a, 0x85, 0xd6, 0x79, 0xf9, 0x6c, 0x41, 0xdf, + 0xda, 0xda, 0xe7, 0xee, 0xa3, 0x75, 0x6b, 0x3d, 0x8b, 0x33, 0x86, 0x36, 0xc9, 0xea, 0x4a, 0x8b, + 0x5e, 0x5d, 0xbc, 0xba, 0xd2, 0xd5, 0xf8, 0x36, 0xf9, 0x16, 0xf3, 0x27, 0x6c, 0x56, 0x7f, 0x95, + 0xc5, 0x46, 0x8b, 0xc3, 0xfa, 0x32, 0xae, 0xbd, 0xea, 0x8d, 0xbe, 0x21, 0xa4, 0x82, 0x0f, 0x9f, + 0xfe, 0x78, 0xb2, 0x8c, 0x0c, 0x6a, 0xdd, 0x8f, 0xd4, 0xa0, 0xf8, 0x35, 0x98, 0xa9, 0xc1, 0xd2, + 0x0c, 0xf2, 0x0f, 0xd6, 0xc0, 0xfe, 0xb8, 0x4d, 0xd6, 0x73, 0xed, 0xc5, 0xff, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x16, 0x79, 0x9a, 0xc3, 0x07, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -125,8 +124,6 @@ type QueryClient interface { // StreamHealth runs a streaming RPC to the tablet, that returns the // current health of the tablet on a regular basis. StreamHealth(ctx context.Context, in *query.StreamHealthRequest, opts ...grpc.CallOption) (Query_StreamHealthClient, error) - // UpdateStream asks the server to return a stream of the updates that have been applied to its database. - UpdateStream(ctx context.Context, in *query.UpdateStreamRequest, opts ...grpc.CallOption) (Query_UpdateStreamClient, error) // VStream streams vreplication events. VStream(ctx context.Context, in *binlogdata.VStreamRequest, opts ...grpc.CallOption) (Query_VStreamClient, error) // VStreamRows streams rows from the specified starting point. @@ -383,40 +380,8 @@ func (x *queryStreamHealthClient) Recv() (*query.StreamHealthResponse, error) { return m, nil } -func (c *queryClient) UpdateStream(ctx context.Context, in *query.UpdateStreamRequest, opts ...grpc.CallOption) (Query_UpdateStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[3], "/queryservice.Query/UpdateStream", opts...) - if err != nil { - return nil, err - } - x := &queryUpdateStreamClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Query_UpdateStreamClient interface { - Recv() (*query.UpdateStreamResponse, error) - grpc.ClientStream -} - -type queryUpdateStreamClient struct { - grpc.ClientStream -} - -func (x *queryUpdateStreamClient) Recv() (*query.UpdateStreamResponse, error) { - m := new(query.UpdateStreamResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - func (c *queryClient) VStream(ctx context.Context, in *binlogdata.VStreamRequest, opts ...grpc.CallOption) (Query_VStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[4], "/queryservice.Query/VStream", opts...) + stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[3], "/queryservice.Query/VStream", opts...) if err != nil { return nil, err } @@ -448,7 +413,7 @@ func (x *queryVStreamClient) Recv() (*binlogdata.VStreamResponse, error) { } func (c *queryClient) VStreamRows(ctx context.Context, in *binlogdata.VStreamRowsRequest, opts ...grpc.CallOption) (Query_VStreamRowsClient, error) { - stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[5], "/queryservice.Query/VStreamRows", opts...) + stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[4], "/queryservice.Query/VStreamRows", opts...) if err != nil { return nil, err } @@ -480,7 +445,7 @@ func (x *queryVStreamRowsClient) Recv() (*binlogdata.VStreamRowsResponse, error) } func (c *queryClient) VStreamResults(ctx context.Context, in *binlogdata.VStreamResultsRequest, opts ...grpc.CallOption) (Query_VStreamResultsClient, error) { - stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[6], "/queryservice.Query/VStreamResults", opts...) + stream, err := c.cc.NewStream(ctx, &_Query_serviceDesc.Streams[5], "/queryservice.Query/VStreamResults", opts...) if err != nil { return nil, err } @@ -557,8 +522,6 @@ type QueryServer interface { // StreamHealth runs a streaming RPC to the tablet, that returns the // current health of the tablet on a regular basis. StreamHealth(*query.StreamHealthRequest, Query_StreamHealthServer) error - // UpdateStream asks the server to return a stream of the updates that have been applied to its database. - UpdateStream(*query.UpdateStreamRequest, Query_UpdateStreamServer) error // VStream streams vreplication events. VStream(*binlogdata.VStreamRequest, Query_VStreamServer) error // VStreamRows streams rows from the specified starting point. @@ -628,9 +591,6 @@ func (*UnimplementedQueryServer) MessageAck(ctx context.Context, req *query.Mess func (*UnimplementedQueryServer) StreamHealth(req *query.StreamHealthRequest, srv Query_StreamHealthServer) error { return status.Errorf(codes.Unimplemented, "method StreamHealth not implemented") } -func (*UnimplementedQueryServer) UpdateStream(req *query.UpdateStreamRequest, srv Query_UpdateStreamServer) error { - return status.Errorf(codes.Unimplemented, "method UpdateStream not implemented") -} func (*UnimplementedQueryServer) VStream(req *binlogdata.VStreamRequest, srv Query_VStreamServer) error { return status.Errorf(codes.Unimplemented, "method VStream not implemented") } @@ -996,27 +956,6 @@ func (x *queryStreamHealthServer) Send(m *query.StreamHealthResponse) error { return x.ServerStream.SendMsg(m) } -func _Query_UpdateStream_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(query.UpdateStreamRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(QueryServer).UpdateStream(m, &queryUpdateStreamServer{stream}) -} - -type Query_UpdateStreamServer interface { - Send(*query.UpdateStreamResponse) error - grpc.ServerStream -} - -type queryUpdateStreamServer struct { - grpc.ServerStream -} - -func (x *queryUpdateStreamServer) Send(m *query.UpdateStreamResponse) error { - return x.ServerStream.SendMsg(m) -} - func _Query_VStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(binlogdata.VStreamRequest) if err := stream.RecvMsg(m); err != nil { @@ -1165,11 +1104,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ Handler: _Query_StreamHealth_Handler, ServerStreams: true, }, - { - StreamName: "UpdateStream", - Handler: _Query_UpdateStream_Handler, - ServerStreams: true, - }, { StreamName: "VStream", Handler: _Query_VStream_Handler, diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index 8da164ca2af..4f2b378f521 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -3107,161 +3107,6 @@ func (m *VStreamResponse) GetEvents() []*binlogdata.VEvent { return nil } -// UpdateStreamRequest is the payload to UpdateStream. -type UpdateStreamRequest struct { - // caller_id identifies the caller. This is the effective caller ID, - // set by the application to further identify the caller. - CallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=caller_id,json=callerId,proto3" json:"caller_id,omitempty"` - // keyspace to target the query to. - Keyspace string `protobuf:"bytes,2,opt,name=keyspace,proto3" json:"keyspace,omitempty"` - // shard to target the query to, for unsharded keyspaces. - Shard string `protobuf:"bytes,3,opt,name=shard,proto3" json:"shard,omitempty"` - // KeyRange to target the query to, for sharded keyspaces. - KeyRange *topodata.KeyRange `protobuf:"bytes,4,opt,name=key_range,json=keyRange,proto3" json:"key_range,omitempty"` - // tablet_type is the type of tablets that this request is targeted to. - TabletType topodata.TabletType `protobuf:"varint,5,opt,name=tablet_type,json=tabletType,proto3,enum=topodata.TabletType" json:"tablet_type,omitempty"` - // timestamp is the timestamp to start the stream from. It is - // unused is event is set, and we are only streaming from the shard - // described by event.shard. - Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // event is the event to start the stream from. - // Note it is only used if we are streaming from exactly the same shard - // as this event was coming from. Otherwise we can't use this event, - // and will use the timestamp as a starting point. - Event *query.EventToken `protobuf:"bytes,7,opt,name=event,proto3" json:"event,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateStreamRequest) Reset() { *m = UpdateStreamRequest{} } -func (m *UpdateStreamRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateStreamRequest) ProtoMessage() {} -func (*UpdateStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{43} -} - -func (m *UpdateStreamRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateStreamRequest.Unmarshal(m, b) -} -func (m *UpdateStreamRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateStreamRequest.Marshal(b, m, deterministic) -} -func (m *UpdateStreamRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateStreamRequest.Merge(m, src) -} -func (m *UpdateStreamRequest) XXX_Size() int { - return xxx_messageInfo_UpdateStreamRequest.Size(m) -} -func (m *UpdateStreamRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateStreamRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateStreamRequest proto.InternalMessageInfo - -func (m *UpdateStreamRequest) GetCallerId() *vtrpc.CallerID { - if m != nil { - return m.CallerId - } - return nil -} - -func (m *UpdateStreamRequest) GetKeyspace() string { - if m != nil { - return m.Keyspace - } - return "" -} - -func (m *UpdateStreamRequest) GetShard() string { - if m != nil { - return m.Shard - } - return "" -} - -func (m *UpdateStreamRequest) GetKeyRange() *topodata.KeyRange { - if m != nil { - return m.KeyRange - } - return nil -} - -func (m *UpdateStreamRequest) GetTabletType() topodata.TabletType { - if m != nil { - return m.TabletType - } - return topodata.TabletType_UNKNOWN -} - -func (m *UpdateStreamRequest) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -func (m *UpdateStreamRequest) GetEvent() *query.EventToken { - if m != nil { - return m.Event - } - return nil -} - -// UpdateStreamResponse is streamed by UpdateStream. -type UpdateStreamResponse struct { - // event is one event from the stream. - Event *query.StreamEvent `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"` - // resume_timestamp is the timestamp to resume streaming from if the - // client is interrupted. If the Update Stream only goes to one - // shard, this is equal to event.timestamp. If the Update Stream - // goes to multiple shards and aggregates, this is the minimum value - // of the current timestamp for all shards. - ResumeTimestamp int64 `protobuf:"varint,2,opt,name=resume_timestamp,json=resumeTimestamp,proto3" json:"resume_timestamp,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateStreamResponse) Reset() { *m = UpdateStreamResponse{} } -func (m *UpdateStreamResponse) String() string { return proto.CompactTextString(m) } -func (*UpdateStreamResponse) ProtoMessage() {} -func (*UpdateStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_aab96496ceaf1ebb, []int{44} -} - -func (m *UpdateStreamResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateStreamResponse.Unmarshal(m, b) -} -func (m *UpdateStreamResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateStreamResponse.Marshal(b, m, deterministic) -} -func (m *UpdateStreamResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateStreamResponse.Merge(m, src) -} -func (m *UpdateStreamResponse) XXX_Size() int { - return xxx_messageInfo_UpdateStreamResponse.Size(m) -} -func (m *UpdateStreamResponse) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateStreamResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateStreamResponse proto.InternalMessageInfo - -func (m *UpdateStreamResponse) GetEvent() *query.StreamEvent { - if m != nil { - return m.Event - } - return nil -} - -func (m *UpdateStreamResponse) GetResumeTimestamp() int64 { - if m != nil { - return m.ResumeTimestamp - } - return 0 -} - func init() { proto.RegisterEnum("vtgate.TransactionMode", TransactionMode_name, TransactionMode_value) proto.RegisterEnum("vtgate.CommitOrder", CommitOrder_name, CommitOrder_value) @@ -3310,127 +3155,120 @@ func init() { proto.RegisterType((*GetSrvKeyspaceResponse)(nil), "vtgate.GetSrvKeyspaceResponse") proto.RegisterType((*VStreamRequest)(nil), "vtgate.VStreamRequest") proto.RegisterType((*VStreamResponse)(nil), "vtgate.VStreamResponse") - proto.RegisterType((*UpdateStreamRequest)(nil), "vtgate.UpdateStreamRequest") - proto.RegisterType((*UpdateStreamResponse)(nil), "vtgate.UpdateStreamResponse") } func init() { proto.RegisterFile("vtgate.proto", fileDescriptor_aab96496ceaf1ebb) } var fileDescriptor_aab96496ceaf1ebb = []byte{ - // 1839 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x5b, 0x6f, 0x1b, 0xc7, - 0x15, 0xce, 0xee, 0xf2, 0x7a, 0x96, 0x37, 0x8f, 0x69, 0x87, 0x51, 0x14, 0x9b, 0xdd, 0x54, 0x08, - 0xe3, 0x1a, 0x52, 0xa3, 0xa0, 0x41, 0x51, 0xa4, 0x28, 0x2c, 0x5a, 0x31, 0x88, 0x58, 0x97, 0x8e, - 0x68, 0xbb, 0x2d, 0x5a, 0x2c, 0x56, 0xdc, 0x09, 0xbd, 0x15, 0xb9, 0xcb, 0xec, 0x0c, 0xe9, 0xea, - 0xa5, 0xc8, 0x3f, 0x08, 0xfa, 0x50, 0xa0, 0x08, 0x0a, 0x14, 0x05, 0x0c, 0xf4, 0xa9, 0xaf, 0x05, - 0xda, 0xbe, 0xf4, 0xb9, 0x2f, 0x45, 0xff, 0x42, 0x7f, 0x42, 0x7f, 0x41, 0xb0, 0x33, 0xb3, 0x57, - 0x4b, 0x16, 0x45, 0x5a, 0x06, 0xfd, 0x22, 0xec, 0x9c, 0x33, 0x97, 0x33, 0xdf, 0xf9, 0xe6, 0x9c, - 0xc3, 0x19, 0x41, 0x65, 0xc6, 0x86, 0x16, 0x23, 0x9b, 0x13, 0xdf, 0x63, 0x1e, 0x2a, 0x88, 0xd6, - 0x5a, 0xe3, 0xd8, 0x71, 0x47, 0xde, 0xd0, 0xb6, 0x98, 0x25, 0x34, 0x6b, 0xfa, 0x97, 0x53, 0xe2, - 0x9f, 0xca, 0x46, 0x8d, 0x79, 0x13, 0x2f, 0xa9, 0x9c, 0x31, 0x7f, 0x32, 0x10, 0x0d, 0xe3, 0x79, - 0x1e, 0x8a, 0x47, 0x84, 0x52, 0xc7, 0x73, 0xd1, 0x06, 0xd4, 0x1c, 0xd7, 0x64, 0xbe, 0xe5, 0x52, - 0x6b, 0xc0, 0x1c, 0xcf, 0x6d, 0x29, 0x6d, 0xa5, 0x53, 0xc2, 0x55, 0xc7, 0xed, 0xc7, 0x42, 0xd4, - 0x85, 0x1a, 0x7d, 0x6a, 0xf9, 0xb6, 0x49, 0xc5, 0x38, 0xda, 0x52, 0xdb, 0x5a, 0x47, 0xdf, 0x5e, - 0xdf, 0x94, 0xd6, 0xc9, 0xf9, 0x36, 0x8f, 0x82, 0x5e, 0xb2, 0x81, 0xab, 0x34, 0xd1, 0xa2, 0xe8, - 0x5d, 0x28, 0x53, 0xc7, 0x1d, 0x8e, 0x88, 0x69, 0x1f, 0xb7, 0x34, 0xbe, 0x4c, 0x49, 0x08, 0xee, - 0x1f, 0xa3, 0x5b, 0x00, 0xd6, 0x94, 0x79, 0x03, 0x6f, 0x3c, 0x76, 0x58, 0x2b, 0xc7, 0xb5, 0x09, - 0x09, 0x7a, 0x1f, 0xaa, 0xcc, 0xf2, 0x87, 0x84, 0x99, 0x94, 0xf9, 0x8e, 0x3b, 0x6c, 0xe5, 0xdb, - 0x4a, 0xa7, 0x8c, 0x2b, 0x42, 0x78, 0xc4, 0x65, 0x68, 0x0b, 0x8a, 0xde, 0x84, 0x71, 0xfb, 0x0a, - 0x6d, 0xa5, 0xa3, 0x6f, 0xdf, 0xd8, 0x14, 0xa8, 0xec, 0xfe, 0x86, 0x0c, 0xa6, 0x8c, 0x1c, 0x08, - 0x25, 0x0e, 0x7b, 0xa1, 0x1d, 0x68, 0x24, 0xf6, 0x6e, 0x8e, 0x3d, 0x9b, 0xb4, 0x8a, 0x6d, 0xa5, - 0x53, 0xdb, 0x7e, 0x3b, 0xdc, 0x59, 0x02, 0x86, 0x3d, 0xcf, 0x26, 0xb8, 0xce, 0xd2, 0x02, 0xb4, - 0x05, 0xa5, 0x67, 0x96, 0xef, 0x3a, 0xee, 0x90, 0xb6, 0x4a, 0x1c, 0x95, 0xeb, 0x72, 0xd5, 0x9f, - 0x06, 0x7f, 0x9f, 0x08, 0x1d, 0x8e, 0x3a, 0xa1, 0x9f, 0x40, 0x65, 0xe2, 0x93, 0x18, 0xca, 0xf2, - 0x1c, 0x50, 0xea, 0x13, 0x9f, 0x44, 0x40, 0xde, 0x83, 0xea, 0xc4, 0xa3, 0x2c, 0x9e, 0x01, 0xe6, - 0x98, 0xa1, 0x12, 0x0c, 0x89, 0xa6, 0xf8, 0x2e, 0xd4, 0x46, 0x16, 0x65, 0xa6, 0xe3, 0x52, 0xe2, - 0x33, 0xd3, 0xb1, 0x5b, 0x7a, 0x5b, 0xe9, 0xe4, 0x70, 0x25, 0x90, 0xf6, 0xb8, 0xb0, 0x67, 0xa3, - 0xf7, 0x00, 0xbe, 0xf0, 0xa6, 0xae, 0x6d, 0xfa, 0xde, 0x33, 0xda, 0xaa, 0xf0, 0x1e, 0x65, 0x2e, - 0xc1, 0xde, 0x33, 0xba, 0xf6, 0x4b, 0xa8, 0x24, 0x97, 0x40, 0x1b, 0x50, 0x10, 0xee, 0xe0, 0x24, - 0xd2, 0xb7, 0xab, 0x12, 0x87, 0x3e, 0x17, 0x62, 0xa9, 0x0c, 0x38, 0x97, 0x04, 0xdd, 0xb1, 0x5b, - 0x6a, 0x5b, 0xe9, 0x68, 0xb8, 0x9a, 0x90, 0xf6, 0x6c, 0xe3, 0x3f, 0x2a, 0xd4, 0xa4, 0xdf, 0x30, - 0xf9, 0x72, 0x4a, 0x28, 0x43, 0x77, 0xa1, 0x3c, 0xb0, 0x46, 0x23, 0xe2, 0x07, 0x83, 0xc4, 0x1a, - 0xf5, 0x4d, 0x41, 0xed, 0x2e, 0x97, 0xf7, 0xee, 0xe3, 0x92, 0xe8, 0xd1, 0xb3, 0xd1, 0x87, 0x50, - 0x94, 0x08, 0xf1, 0x05, 0x44, 0xdf, 0x24, 0x40, 0x38, 0xd4, 0xa3, 0x0f, 0x20, 0xcf, 0x4d, 0xe5, - 0xb4, 0xd4, 0xb7, 0xaf, 0x49, 0xc3, 0x77, 0x82, 0xad, 0x72, 0x2f, 0x62, 0xa1, 0x47, 0x3f, 0x00, - 0x9d, 0x59, 0xc7, 0x23, 0xc2, 0x4c, 0x76, 0x3a, 0x21, 0x9c, 0xa7, 0xb5, 0xed, 0xe6, 0x66, 0x74, - 0xdc, 0xfa, 0x5c, 0xd9, 0x3f, 0x9d, 0x10, 0x0c, 0x2c, 0xfa, 0x46, 0x77, 0x01, 0xb9, 0x5e, 0x80, - 0x76, 0xea, 0xa8, 0xe5, 0x39, 0xcb, 0x1b, 0xae, 0xc7, 0x7a, 0xa9, 0xd3, 0xb6, 0x01, 0xb5, 0x13, - 0x72, 0x4a, 0x27, 0xd6, 0x80, 0x98, 0xfc, 0x08, 0x71, 0x36, 0x97, 0x71, 0x35, 0x94, 0x72, 0xd4, - 0x93, 0x6c, 0x2f, 0xce, 0xc3, 0x76, 0xe3, 0x6b, 0x05, 0xea, 0x11, 0xa2, 0x74, 0xe2, 0xb9, 0x94, - 0xa0, 0x0d, 0xc8, 0x13, 0xdf, 0xf7, 0xfc, 0x0c, 0x9c, 0xf8, 0xb0, 0xbb, 0x1b, 0x88, 0xb1, 0xd0, - 0x5e, 0x06, 0xcb, 0x3b, 0x50, 0xf0, 0x09, 0x9d, 0x8e, 0x98, 0x04, 0x13, 0x25, 0x4f, 0x03, 0xe6, - 0x1a, 0x2c, 0x7b, 0x18, 0xff, 0x53, 0xa1, 0x29, 0x2d, 0xe2, 0x7b, 0xa2, 0xab, 0xe3, 0xe9, 0x35, - 0x28, 0x85, 0x70, 0x73, 0x37, 0x97, 0x71, 0xd4, 0x46, 0x37, 0xa1, 0xc0, 0xfd, 0x42, 0x5b, 0xf9, - 0xb6, 0xd6, 0x29, 0x63, 0xd9, 0xca, 0xb2, 0xa3, 0xb0, 0x14, 0x3b, 0x8a, 0xe7, 0xb0, 0x23, 0xe1, - 0xf6, 0xd2, 0x5c, 0x6e, 0xff, 0xbd, 0x02, 0x37, 0x32, 0x20, 0xaf, 0x84, 0xf3, 0xff, 0xaf, 0xc2, - 0x3b, 0xd2, 0xae, 0xcf, 0x25, 0xb2, 0xbd, 0x37, 0x85, 0x01, 0xdf, 0x81, 0x4a, 0x74, 0x44, 0x1d, - 0xc9, 0x83, 0x0a, 0xd6, 0x4f, 0xe2, 0x7d, 0xac, 0x28, 0x19, 0xbe, 0x51, 0x60, 0xed, 0x2c, 0xd0, - 0x57, 0x82, 0x11, 0x5f, 0x69, 0xf0, 0x76, 0x6c, 0x1c, 0xb6, 0xdc, 0x21, 0x79, 0x43, 0xf8, 0xf0, - 0x11, 0xc0, 0x09, 0x39, 0x35, 0x7d, 0x6e, 0x32, 0x67, 0x43, 0xb0, 0xd3, 0xc8, 0xd7, 0xe1, 0x6e, - 0x70, 0xf9, 0x24, 0xdc, 0xd7, 0x8a, 0xf2, 0xe3, 0x0f, 0x0a, 0xb4, 0x5e, 0x74, 0xc1, 0x4a, 0xb0, - 0xe3, 0xef, 0xb9, 0x88, 0x1d, 0xbb, 0x2e, 0x73, 0xd8, 0xe9, 0x1b, 0x13, 0x2d, 0xee, 0x02, 0x22, - 0xdc, 0x62, 0x73, 0xe0, 0x8d, 0xa6, 0x63, 0xd7, 0x74, 0xad, 0x31, 0x91, 0x15, 0x6c, 0x43, 0x68, - 0xba, 0x5c, 0xb1, 0x6f, 0x8d, 0x09, 0xfa, 0x19, 0x5c, 0x97, 0xbd, 0x53, 0x21, 0xa6, 0xc0, 0x49, - 0xd5, 0x09, 0x2d, 0x3d, 0x07, 0x89, 0xcd, 0x50, 0x80, 0xaf, 0x89, 0x49, 0x3e, 0x3f, 0x3f, 0x24, - 0x15, 0x97, 0xa2, 0x5c, 0xe9, 0x62, 0xca, 0x95, 0xe7, 0xa1, 0xdc, 0xda, 0x31, 0x94, 0x42, 0xa3, - 0xd1, 0x6d, 0xc8, 0x71, 0xd3, 0x14, 0x6e, 0x9a, 0x1e, 0x16, 0x90, 0x81, 0x45, 0x5c, 0x81, 0x9a, - 0x90, 0x9f, 0x59, 0xa3, 0x29, 0xe1, 0x8e, 0xab, 0x60, 0xd1, 0x40, 0xb7, 0x41, 0x4f, 0x60, 0xc5, - 0x7d, 0x55, 0xc1, 0x10, 0x47, 0xe3, 0x24, 0xad, 0x13, 0x88, 0xad, 0x04, 0xad, 0xff, 0xab, 0xc2, - 0x75, 0x69, 0xda, 0x8e, 0xc5, 0x06, 0x4f, 0xaf, 0x9c, 0xd2, 0xdf, 0x83, 0x62, 0x60, 0x8d, 0x43, - 0x68, 0x4b, 0xe3, 0x9c, 0x3a, 0x83, 0xd4, 0x61, 0x8f, 0x45, 0x0b, 0xde, 0x0d, 0xa8, 0x59, 0xf4, - 0x8c, 0x62, 0xb7, 0x6a, 0xd1, 0xd7, 0x51, 0xe9, 0x7e, 0xa3, 0x44, 0x75, 0xa5, 0xc4, 0xf4, 0xca, - 0x5c, 0xfd, 0x7d, 0x28, 0x0a, 0x47, 0x86, 0x68, 0xde, 0x94, 0xb6, 0x09, 0x37, 0x3f, 0x71, 0xd8, - 0x53, 0x31, 0x75, 0xd8, 0xcd, 0x70, 0xa1, 0xce, 0x91, 0xe6, 0x7b, 0xe3, 0x70, 0xc7, 0x51, 0x46, - 0xb9, 0x44, 0x94, 0x51, 0xcf, 0xad, 0x4a, 0xb5, 0x64, 0x55, 0x6a, 0xfc, 0x2d, 0xae, 0xb3, 0x38, - 0x18, 0xaf, 0xa9, 0xd2, 0xfe, 0x28, 0x4b, 0xb3, 0xe8, 0x27, 0x75, 0x66, 0xf7, 0xaf, 0x8b, 0x6c, - 0x97, 0xbd, 0x1d, 0x30, 0xfe, 0x18, 0xd7, 0x4a, 0x29, 0xe0, 0xae, 0x8c, 0x4b, 0x77, 0xb3, 0x5c, - 0x3a, 0x2b, 0x6e, 0x44, 0x3c, 0xfa, 0x2d, 0x34, 0x39, 0x92, 0x71, 0x84, 0x7f, 0x85, 0x64, 0xca, - 0x16, 0xb8, 0xda, 0x0b, 0x05, 0xae, 0xf1, 0x2f, 0x15, 0x6e, 0x25, 0xe1, 0x79, 0x9d, 0x45, 0xfc, - 0x27, 0x59, 0x72, 0xad, 0xa7, 0xc8, 0x95, 0x81, 0x64, 0x65, 0x19, 0xf6, 0x67, 0x05, 0x6e, 0x9f, - 0x0b, 0xe1, 0x8a, 0xd0, 0xec, 0x2f, 0x2a, 0x34, 0x8f, 0x98, 0x4f, 0xac, 0xf1, 0x52, 0xb7, 0x31, - 0x11, 0x2b, 0xd5, 0xcb, 0x5d, 0xb1, 0x68, 0xf3, 0xbb, 0x28, 0x93, 0x4a, 0x72, 0x17, 0xa4, 0x92, - 0xfc, 0x5c, 0x57, 0x84, 0x09, 0x5c, 0x0b, 0x2f, 0xc7, 0xd5, 0xe8, 0xc2, 0x8d, 0x0c, 0x50, 0xd2, - 0x85, 0x71, 0x39, 0xa0, 0x5c, 0x58, 0x0e, 0x7c, 0xad, 0xc2, 0x5a, 0x6a, 0x96, 0x65, 0xc2, 0xf5, - 0xdc, 0xa0, 0x27, 0x43, 0x81, 0x76, 0x6e, 0x5e, 0xc9, 0xbd, 0xec, 0xb6, 0x23, 0x3f, 0xa7, 0xa3, - 0x2e, 0x7d, 0x48, 0x7a, 0xf0, 0xee, 0x99, 0x80, 0x2c, 0x00, 0xee, 0x9f, 0x54, 0xb8, 0x9d, 0x9a, - 0x6b, 0xe9, 0x98, 0xf5, 0x4a, 0x10, 0xce, 0x06, 0xdb, 0xdc, 0x85, 0xb7, 0x09, 0x57, 0x06, 0xf6, - 0x3e, 0xb4, 0xcf, 0x07, 0x68, 0x01, 0xc4, 0xff, 0xaa, 0xc2, 0x7b, 0xd9, 0x09, 0x97, 0xf9, 0x61, - 0xff, 0x4a, 0xf0, 0x4e, 0xff, 0x5a, 0xcf, 0x2d, 0xf0, 0x6b, 0xfd, 0xca, 0xf0, 0x7f, 0x08, 0xb7, - 0xce, 0x83, 0x6b, 0x01, 0xf4, 0x7f, 0x0e, 0x95, 0x1d, 0x32, 0x74, 0xdc, 0xc5, 0xb0, 0x4e, 0x3d, - 0xd8, 0xa8, 0xe9, 0x07, 0x1b, 0xe3, 0x47, 0x50, 0x95, 0x53, 0x4b, 0xbb, 0x12, 0x81, 0x52, 0xb9, - 0x20, 0x50, 0x7e, 0xa5, 0x40, 0xb5, 0xcb, 0xdf, 0x75, 0xae, 0xbc, 0x50, 0xb8, 0x09, 0x05, 0x8b, - 0x79, 0x63, 0x67, 0x20, 0x5f, 0x9c, 0x64, 0xcb, 0x68, 0x40, 0x2d, 0xb4, 0x40, 0xd8, 0x6f, 0xfc, - 0x1a, 0xea, 0xd8, 0x1b, 0x8d, 0x8e, 0xad, 0xc1, 0xc9, 0x55, 0x5b, 0x65, 0x20, 0x68, 0xc4, 0x6b, - 0xc9, 0xf5, 0x7f, 0x05, 0xef, 0x60, 0x42, 0xbd, 0xd1, 0x8c, 0x24, 0x4a, 0x8a, 0xc5, 0x2c, 0x41, - 0x90, 0xb3, 0x99, 0x7c, 0x57, 0x29, 0x63, 0xfe, 0x6d, 0xfc, 0x53, 0x81, 0xe6, 0x1e, 0xa1, 0xd4, - 0x1a, 0x12, 0x41, 0xb0, 0xc5, 0xa6, 0x7e, 0x59, 0xcd, 0xd8, 0x84, 0xbc, 0xc8, 0xbc, 0xe2, 0xbc, - 0x89, 0x06, 0xda, 0x82, 0x72, 0x74, 0xd8, 0x78, 0x4e, 0x3e, 0xfb, 0xac, 0x95, 0xc2, 0xb3, 0x16, - 0x58, 0x9f, 0xb8, 0x1f, 0xe1, 0xdf, 0xc6, 0xef, 0x14, 0xb8, 0x26, 0xad, 0xbf, 0xb7, 0xa8, 0x7f, - 0x5e, 0x66, 0x7a, 0xb8, 0xa6, 0x16, 0xaf, 0x89, 0x6e, 0x81, 0x16, 0x06, 0x63, 0x7d, 0xbb, 0x22, - 0x4f, 0xd9, 0x63, 0x6b, 0x34, 0x25, 0x38, 0x50, 0x18, 0x7b, 0x50, 0xe9, 0x25, 0x2a, 0x4d, 0xb4, - 0x0e, 0x6a, 0x64, 0x46, 0xba, 0xbb, 0xea, 0xd8, 0xd9, 0x2b, 0x0a, 0xf5, 0x85, 0x2b, 0x8a, 0x7f, - 0x28, 0xb0, 0x1e, 0x6f, 0x71, 0xe9, 0xc4, 0x74, 0xd9, 0xdd, 0x7e, 0x0a, 0x75, 0xc7, 0x36, 0x5f, - 0x48, 0x43, 0xfa, 0x76, 0x33, 0x64, 0x71, 0x72, 0xb3, 0xb8, 0xea, 0x24, 0x5a, 0xd4, 0x58, 0x87, - 0xb5, 0xb3, 0xc8, 0x2b, 0xa9, 0xfd, 0x31, 0xdc, 0x78, 0x40, 0xd8, 0x91, 0x3f, 0x0b, 0x87, 0x84, - 0x5b, 0x4a, 0x1a, 0xa9, 0xa4, 0x8d, 0x34, 0x30, 0xdc, 0xcc, 0x0e, 0x92, 0x91, 0xe6, 0x87, 0x50, - 0xa1, 0xfe, 0xcc, 0x4c, 0x8d, 0x0c, 0x22, 0x6b, 0x44, 0xaa, 0xe4, 0x20, 0x9d, 0xc6, 0x0d, 0xe3, - 0xdf, 0x0a, 0xd4, 0x1e, 0x2f, 0x43, 0xff, 0x4c, 0x1a, 0x50, 0xe7, 0x4c, 0x03, 0x1f, 0x40, 0x7e, - 0x36, 0x64, 0xf2, 0x66, 0x2a, 0xc8, 0x5a, 0x89, 0xe7, 0xfb, 0xc7, 0x0f, 0x98, 0x63, 0x63, 0xa1, - 0x0f, 0x82, 0xfb, 0x17, 0xce, 0x88, 0x11, 0x3f, 0x3a, 0x29, 0x89, 0x9e, 0x9f, 0x71, 0x0d, 0x96, - 0x3d, 0x8c, 0x1f, 0x43, 0x3d, 0xda, 0x4b, 0x9c, 0x1b, 0xc8, 0x8c, 0xb8, 0x8c, 0xb6, 0x14, 0x99, - 0xd4, 0x92, 0x0b, 0xed, 0x06, 0x2a, 0x2c, 0x7b, 0x18, 0xcf, 0x55, 0xb8, 0xfe, 0x68, 0x62, 0x5b, - 0x6c, 0xd5, 0xe3, 0xc1, 0x82, 0xa9, 0x77, 0x1d, 0xca, 0xcc, 0x19, 0x13, 0xca, 0xac, 0xf1, 0x84, - 0x27, 0x5f, 0x0d, 0xc7, 0x82, 0xc0, 0x23, 0x1c, 0x07, 0x79, 0xa1, 0x14, 0xd6, 0x11, 0x1c, 0xa2, - 0xbe, 0x77, 0x42, 0x5c, 0x2c, 0xf4, 0xc6, 0x09, 0x34, 0xd3, 0x28, 0x49, 0xa8, 0x3b, 0xe1, 0x04, - 0xe9, 0x2c, 0x2c, 0x93, 0x37, 0x47, 0x5a, 0x74, 0x40, 0x1f, 0x42, 0x23, 0x48, 0xc7, 0x63, 0x62, - 0xc6, 0xf6, 0x88, 0x17, 0xef, 0xba, 0x90, 0xf7, 0x43, 0xf1, 0x9d, 0xfb, 0x50, 0xcf, 0xfc, 0xbf, - 0x01, 0xaa, 0x83, 0xfe, 0x68, 0xff, 0xe8, 0x70, 0xb7, 0xdb, 0xfb, 0xac, 0xb7, 0x7b, 0xbf, 0xf1, - 0x16, 0x02, 0x28, 0x1c, 0xf5, 0xf6, 0x1f, 0x3c, 0xdc, 0x6d, 0x28, 0xa8, 0x0c, 0xf9, 0xbd, 0x47, - 0x0f, 0xfb, 0xbd, 0x86, 0x1a, 0x7c, 0xf6, 0x9f, 0x1c, 0x1c, 0x76, 0x1b, 0xda, 0x9d, 0x4f, 0x41, - 0x17, 0xb9, 0xed, 0xc0, 0xb7, 0x89, 0x1f, 0x0c, 0xd8, 0x3f, 0xc0, 0x7b, 0xf7, 0x1e, 0x36, 0xde, - 0x42, 0x45, 0xd0, 0x0e, 0x71, 0x30, 0xb2, 0x04, 0xb9, 0xc3, 0x83, 0xa3, 0x7e, 0x43, 0x45, 0x35, - 0x80, 0x7b, 0x8f, 0xfa, 0x07, 0xdd, 0x83, 0xbd, 0xbd, 0x5e, 0xbf, 0xa1, 0xed, 0x7c, 0x02, 0x75, - 0xc7, 0xdb, 0x9c, 0x39, 0x8c, 0x50, 0x2a, 0xfe, 0x63, 0xe4, 0x17, 0xef, 0xcb, 0x96, 0xe3, 0x6d, - 0x89, 0xaf, 0xad, 0xa1, 0xb7, 0x35, 0x63, 0x5b, 0x5c, 0xbb, 0x25, 0xc2, 0xc3, 0x71, 0x81, 0xb7, - 0x3e, 0xfe, 0x36, 0x00, 0x00, 0xff, 0xff, 0x0c, 0xb7, 0x26, 0xa1, 0xb1, 0x22, 0x00, 0x00, + // 1756 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x5b, 0x8f, 0x1b, 0x49, + 0x15, 0xde, 0x6e, 0xdf, 0x4f, 0xfb, 0x96, 0x8a, 0x93, 0xf5, 0x7a, 0x67, 0x13, 0xd3, 0xcb, 0x68, + 0x4d, 0x88, 0x6c, 0xd6, 0x2b, 0x56, 0x08, 0x2d, 0x42, 0x19, 0x67, 0x36, 0xb2, 0x36, 0x73, 0xa1, + 0xec, 0x24, 0x80, 0x40, 0x56, 0x8f, 0xbb, 0xd6, 0x69, 0xc6, 0xee, 0xf6, 0x76, 0x95, 0x1d, 0xe6, + 0x05, 0xed, 0x3f, 0x58, 0xf1, 0x80, 0x84, 0x56, 0x48, 0x08, 0x09, 0x89, 0x27, 0x5e, 0x91, 0x80, + 0x17, 0x9e, 0x79, 0x41, 0xfc, 0x05, 0x7e, 0x02, 0xbf, 0x60, 0xd5, 0x55, 0xd5, 0x17, 0x77, 0xc6, + 0x19, 0x8f, 0x9d, 0x19, 0x39, 0x2f, 0xa3, 0xae, 0x73, 0xea, 0x72, 0xce, 0x77, 0xbe, 0x3a, 0x75, + 0x5c, 0x35, 0x90, 0x9f, 0xb3, 0x91, 0xc1, 0x48, 0x73, 0xea, 0x3a, 0xcc, 0x41, 0x69, 0xd1, 0xaa, + 0x95, 0x4f, 0x2c, 0x7b, 0xec, 0x8c, 0x4c, 0x83, 0x19, 0x42, 0x53, 0xd3, 0xbe, 0x98, 0x11, 0xf7, + 0x4c, 0x36, 0x8a, 0xcc, 0x99, 0x3a, 0x51, 0xe5, 0x9c, 0xb9, 0xd3, 0xa1, 0x68, 0xe8, 0x7f, 0x4e, + 0x41, 0xa6, 0x47, 0x28, 0xb5, 0x1c, 0x1b, 0xed, 0x42, 0xd1, 0xb2, 0x07, 0xcc, 0x35, 0x6c, 0x6a, + 0x0c, 0x99, 0xe5, 0xd8, 0x55, 0xa5, 0xae, 0x34, 0xb2, 0xb8, 0x60, 0xd9, 0xfd, 0x50, 0x88, 0x3a, + 0x50, 0xa4, 0xcf, 0x0d, 0xd7, 0x1c, 0x50, 0x31, 0x8e, 0x56, 0xd5, 0x7a, 0xa2, 0xa1, 0xb5, 0x77, + 0x9a, 0xd2, 0x3a, 0x39, 0x5f, 0xb3, 0xe7, 0xf5, 0x92, 0x0d, 0x5c, 0xa0, 0x91, 0x16, 0x45, 0xef, + 0x42, 0x8e, 0x5a, 0xf6, 0x68, 0x4c, 0x06, 0xe6, 0x49, 0x35, 0xc1, 0x97, 0xc9, 0x0a, 0xc1, 0xc3, + 0x13, 0x74, 0x07, 0xc0, 0x98, 0x31, 0x67, 0xe8, 0x4c, 0x26, 0x16, 0xab, 0x26, 0xb9, 0x36, 0x22, + 0x41, 0xef, 0x43, 0x81, 0x19, 0xee, 0x88, 0xb0, 0x01, 0x65, 0xae, 0x65, 0x8f, 0xaa, 0xa9, 0xba, + 0xd2, 0xc8, 0xe1, 0xbc, 0x10, 0xf6, 0xb8, 0x0c, 0xb5, 0x20, 0xe3, 0x4c, 0x19, 0xb7, 0x2f, 0x5d, + 0x57, 0x1a, 0x5a, 0xfb, 0x56, 0x53, 0xa0, 0xb2, 0xff, 0x6b, 0x32, 0x9c, 0x31, 0x72, 0x24, 0x94, + 0xd8, 0xef, 0x85, 0xf6, 0xa0, 0x1c, 0xf1, 0x7d, 0x30, 0x71, 0x4c, 0x52, 0xcd, 0xd4, 0x95, 0x46, + 0xb1, 0xfd, 0xb6, 0xef, 0x59, 0x04, 0x86, 0x03, 0xc7, 0x24, 0xb8, 0xc4, 0x16, 0x05, 0xa8, 0x05, + 0xd9, 0x17, 0x86, 0x6b, 0x5b, 0xf6, 0x88, 0x56, 0xb3, 0x1c, 0x95, 0x9b, 0x72, 0xd5, 0x9f, 0x78, + 0x7f, 0x9f, 0x09, 0x1d, 0x0e, 0x3a, 0xa1, 0x1f, 0x43, 0x7e, 0xea, 0x92, 0x10, 0xca, 0xdc, 0x0a, + 0x50, 0x6a, 0x53, 0x97, 0x04, 0x40, 0x3e, 0x80, 0xc2, 0xd4, 0xa1, 0x2c, 0x9c, 0x01, 0x56, 0x98, + 0x21, 0xef, 0x0d, 0x09, 0xa6, 0xf8, 0x36, 0x14, 0xc7, 0x06, 0x65, 0x03, 0xcb, 0xa6, 0xc4, 0x65, + 0x03, 0xcb, 0xac, 0x6a, 0x75, 0xa5, 0x91, 0xc4, 0x79, 0x4f, 0xda, 0xe5, 0xc2, 0xae, 0x89, 0xde, + 0x03, 0xf8, 0xdc, 0x99, 0xd9, 0xe6, 0xc0, 0x75, 0x5e, 0xd0, 0x6a, 0x9e, 0xf7, 0xc8, 0x71, 0x09, + 0x76, 0x5e, 0xd0, 0xda, 0x2f, 0x20, 0x1f, 0x5d, 0x02, 0xed, 0x42, 0x5a, 0x84, 0x83, 0x93, 0x48, + 0x6b, 0x17, 0x24, 0x0e, 0x7d, 0x2e, 0xc4, 0x52, 0xe9, 0x71, 0x2e, 0x0a, 0xba, 0x65, 0x56, 0xd5, + 0xba, 0xd2, 0x48, 0xe0, 0x42, 0x44, 0xda, 0x35, 0xf5, 0xff, 0xa8, 0x50, 0x94, 0x71, 0xc3, 0xe4, + 0x8b, 0x19, 0xa1, 0x0c, 0xdd, 0x87, 0xdc, 0xd0, 0x18, 0x8f, 0x89, 0xeb, 0x0d, 0x12, 0x6b, 0x94, + 0x9a, 0x82, 0xda, 0x1d, 0x2e, 0xef, 0x3e, 0xc4, 0x59, 0xd1, 0xa3, 0x6b, 0xa2, 0xef, 0x40, 0x46, + 0x22, 0xc4, 0x17, 0x10, 0x7d, 0xa3, 0x00, 0x61, 0x5f, 0x8f, 0x3e, 0x80, 0x14, 0x37, 0x95, 0xd3, + 0x52, 0x6b, 0xdf, 0x90, 0x86, 0xef, 0x79, 0xae, 0xf2, 0x28, 0x62, 0xa1, 0x47, 0xdf, 0x07, 0x8d, + 0x19, 0x27, 0x63, 0xc2, 0x06, 0xec, 0x6c, 0x4a, 0x38, 0x4f, 0x8b, 0xed, 0x4a, 0x33, 0xd8, 0x6e, + 0x7d, 0xae, 0xec, 0x9f, 0x4d, 0x09, 0x06, 0x16, 0x7c, 0xa3, 0xfb, 0x80, 0x6c, 0xc7, 0x43, 0x7b, + 0x61, 0xab, 0xa5, 0x38, 0xcb, 0xcb, 0xb6, 0xc3, 0xba, 0x0b, 0xbb, 0x6d, 0x17, 0x8a, 0xa7, 0xe4, + 0x8c, 0x4e, 0x8d, 0x21, 0x19, 0xf0, 0x2d, 0xc4, 0xd9, 0x9c, 0xc3, 0x05, 0x5f, 0xca, 0x51, 0x8f, + 0xb2, 0x3d, 0xb3, 0x0a, 0xdb, 0xf5, 0xaf, 0x14, 0x28, 0x05, 0x88, 0xd2, 0xa9, 0x63, 0x53, 0x82, + 0x76, 0x21, 0x45, 0x5c, 0xd7, 0x71, 0x63, 0x70, 0xe2, 0xe3, 0xce, 0xbe, 0x27, 0xc6, 0x42, 0x7b, + 0x19, 0x2c, 0xef, 0x41, 0xda, 0x25, 0x74, 0x36, 0x66, 0x12, 0x4c, 0x14, 0xdd, 0x0d, 0x98, 0x6b, + 0xb0, 0xec, 0xa1, 0xff, 0x4f, 0x85, 0x8a, 0xb4, 0x88, 0xfb, 0x44, 0xb7, 0x27, 0xd2, 0x35, 0xc8, + 0xfa, 0x70, 0xf3, 0x30, 0xe7, 0x70, 0xd0, 0x46, 0xb7, 0x21, 0xcd, 0xe3, 0x42, 0xab, 0xa9, 0x7a, + 0xa2, 0x91, 0xc3, 0xb2, 0x15, 0x67, 0x47, 0x7a, 0x23, 0x76, 0x64, 0x96, 0xb0, 0x23, 0x12, 0xf6, + 0xec, 0x4a, 0x61, 0xff, 0x9d, 0x02, 0xb7, 0x62, 0x20, 0x6f, 0x45, 0xf0, 0xff, 0xaf, 0xc2, 0x3b, + 0xd2, 0xae, 0xcf, 0x24, 0xb2, 0xdd, 0x37, 0x85, 0x01, 0xdf, 0x82, 0x7c, 0xb0, 0x45, 0x2d, 0xc9, + 0x83, 0x3c, 0xd6, 0x4e, 0x43, 0x3f, 0xb6, 0x94, 0x0c, 0x5f, 0x2b, 0x50, 0x3b, 0x0f, 0xf4, 0xad, + 0x60, 0xc4, 0x97, 0x09, 0x78, 0x3b, 0x34, 0x0e, 0x1b, 0xf6, 0x88, 0xbc, 0x21, 0x7c, 0xf8, 0x10, + 0xe0, 0x94, 0x9c, 0x0d, 0x5c, 0x6e, 0x32, 0x67, 0x83, 0xe7, 0x69, 0x10, 0x6b, 0xdf, 0x1b, 0x9c, + 0x3b, 0xf5, 0xfd, 0xda, 0x52, 0x7e, 0xfc, 0x5e, 0x81, 0xea, 0xcb, 0x21, 0xd8, 0x0a, 0x76, 0xfc, + 0x3d, 0x19, 0xb0, 0x63, 0xdf, 0x66, 0x16, 0x3b, 0x7b, 0x63, 0xb2, 0xc5, 0x7d, 0x40, 0x84, 0x5b, + 0x3c, 0x18, 0x3a, 0xe3, 0xd9, 0xc4, 0x1e, 0xd8, 0xc6, 0x84, 0xc8, 0x0a, 0xb6, 0x2c, 0x34, 0x1d, + 0xae, 0x38, 0x34, 0x26, 0x04, 0xfd, 0x14, 0x6e, 0xca, 0xde, 0x0b, 0x29, 0x26, 0xcd, 0x49, 0xd5, + 0xf0, 0x2d, 0x5d, 0x82, 0x44, 0xd3, 0x17, 0xe0, 0x1b, 0x62, 0x92, 0xcf, 0x96, 0xa7, 0xa4, 0xcc, + 0x46, 0x94, 0xcb, 0x5e, 0x4c, 0xb9, 0xdc, 0x2a, 0x94, 0xab, 0x9d, 0x40, 0xd6, 0x37, 0x1a, 0xdd, + 0x85, 0x24, 0x37, 0x4d, 0xe1, 0xa6, 0x69, 0x7e, 0x01, 0xe9, 0x59, 0xc4, 0x15, 0xa8, 0x02, 0xa9, + 0xb9, 0x31, 0x9e, 0x11, 0x1e, 0xb8, 0x3c, 0x16, 0x0d, 0x74, 0x17, 0xb4, 0x08, 0x56, 0x3c, 0x56, + 0x79, 0x0c, 0x61, 0x36, 0x8e, 0xd2, 0x3a, 0x82, 0xd8, 0x56, 0xd0, 0xfa, 0xbf, 0x2a, 0xdc, 0x94, + 0xa6, 0xed, 0x19, 0x6c, 0xf8, 0xfc, 0xca, 0x29, 0xfd, 0x5d, 0xc8, 0x78, 0xd6, 0x58, 0x84, 0x56, + 0x13, 0x9c, 0x53, 0xe7, 0x90, 0xda, 0xef, 0xb1, 0x6e, 0xc1, 0xbb, 0x0b, 0x45, 0x83, 0x9e, 0x53, + 0xec, 0x16, 0x0c, 0x7a, 0x1d, 0x95, 0xee, 0xd7, 0x4a, 0x50, 0x57, 0x4a, 0x4c, 0xaf, 0x2c, 0xd4, + 0xdf, 0x83, 0x8c, 0x08, 0xa4, 0x8f, 0xe6, 0x6d, 0x69, 0x9b, 0x08, 0xf3, 0x33, 0x8b, 0x3d, 0x17, + 0x53, 0xfb, 0xdd, 0x74, 0x1b, 0x4a, 0x1c, 0x69, 0xee, 0x1b, 0x87, 0x3b, 0xcc, 0x32, 0xca, 0x25, + 0xb2, 0x8c, 0xba, 0xb4, 0x2a, 0x4d, 0x44, 0xab, 0x52, 0xfd, 0x6f, 0x61, 0x9d, 0xc5, 0xc1, 0xb8, + 0xa6, 0x4a, 0xfb, 0xc3, 0x38, 0xcd, 0x82, 0x9f, 0xd4, 0x31, 0xef, 0xaf, 0x8b, 0x6c, 0x97, 0xbd, + 0x1d, 0xd0, 0xff, 0x10, 0xd6, 0x4a, 0x0b, 0xc0, 0x5d, 0x19, 0x97, 0xee, 0xc7, 0xb9, 0x74, 0x5e, + 0xde, 0x08, 0x78, 0xf4, 0x1b, 0xa8, 0x70, 0x24, 0xc3, 0x0c, 0xff, 0x1a, 0xc9, 0x14, 0x2f, 0x70, + 0x13, 0x2f, 0x15, 0xb8, 0xfa, 0xbf, 0x54, 0xb8, 0x13, 0x85, 0xe7, 0x3a, 0x8b, 0xf8, 0x8f, 0xe3, + 0xe4, 0xda, 0x59, 0x20, 0x57, 0x0c, 0x92, 0xad, 0x65, 0xd8, 0x9f, 0x14, 0xb8, 0xbb, 0x14, 0xc2, + 0x2d, 0xa1, 0xd9, 0x5f, 0x54, 0xa8, 0xf4, 0x98, 0x4b, 0x8c, 0xc9, 0x46, 0xb7, 0x31, 0x01, 0x2b, + 0xd5, 0xcb, 0x5d, 0xb1, 0x24, 0x56, 0x0f, 0x51, 0xec, 0x28, 0x49, 0x5e, 0x70, 0x94, 0xa4, 0x56, + 0xba, 0x22, 0x8c, 0xe0, 0x9a, 0x7e, 0x35, 0xae, 0x7a, 0x07, 0x6e, 0xc5, 0x80, 0x92, 0x21, 0x0c, + 0xcb, 0x01, 0xe5, 0xc2, 0x72, 0xe0, 0x2b, 0x15, 0x6a, 0x0b, 0xb3, 0x6c, 0x92, 0xae, 0x57, 0x06, + 0x3d, 0x9a, 0x0a, 0x12, 0x4b, 0xcf, 0x95, 0xe4, 0xab, 0x6e, 0x3b, 0x52, 0x2b, 0x06, 0xea, 0xd2, + 0x9b, 0xa4, 0x0b, 0xef, 0x9e, 0x0b, 0xc8, 0x1a, 0xe0, 0xfe, 0x51, 0x85, 0xbb, 0x0b, 0x73, 0x6d, + 0x9c, 0xb3, 0x5e, 0x0b, 0xc2, 0xf1, 0x64, 0x9b, 0xbc, 0xf0, 0x36, 0xe1, 0xca, 0xc0, 0x3e, 0x84, + 0xfa, 0x72, 0x80, 0xd6, 0x40, 0xfc, 0xaf, 0x2a, 0xbc, 0x17, 0x9f, 0x70, 0x93, 0x1f, 0xf6, 0xaf, + 0x05, 0xef, 0xc5, 0x5f, 0xeb, 0xc9, 0x35, 0x7e, 0xad, 0x5f, 0x19, 0xfe, 0x8f, 0xe1, 0xce, 0x32, + 0xb8, 0xd6, 0x40, 0xff, 0x67, 0x90, 0xdf, 0x23, 0x23, 0xcb, 0x5e, 0x0f, 0xeb, 0x85, 0x07, 0x1b, + 0x75, 0xf1, 0xc1, 0x46, 0xff, 0x21, 0x14, 0xe4, 0xd4, 0xd2, 0xae, 0x48, 0xa2, 0x54, 0x2e, 0x48, + 0x94, 0x5f, 0x2a, 0x50, 0xe8, 0xf0, 0x77, 0x9d, 0x2b, 0x2f, 0x14, 0x6e, 0x43, 0xda, 0x60, 0xce, + 0xc4, 0x1a, 0xca, 0x17, 0x27, 0xd9, 0xd2, 0xcb, 0x50, 0xf4, 0x2d, 0x10, 0xf6, 0xeb, 0xbf, 0x82, + 0x12, 0x76, 0xc6, 0xe3, 0x13, 0x63, 0x78, 0x7a, 0xd5, 0x56, 0xe9, 0x08, 0xca, 0xe1, 0x5a, 0x72, + 0xfd, 0x5f, 0xc2, 0x3b, 0x98, 0x50, 0x67, 0x3c, 0x27, 0x91, 0x92, 0x62, 0x3d, 0x4b, 0x10, 0x24, + 0x4d, 0x26, 0xdf, 0x55, 0x72, 0x98, 0x7f, 0xeb, 0xff, 0x54, 0xa0, 0x72, 0x40, 0x28, 0x35, 0x46, + 0x44, 0x10, 0x6c, 0xbd, 0xa9, 0x5f, 0x55, 0x33, 0x56, 0x20, 0x25, 0x4e, 0x5e, 0xb1, 0xdf, 0x44, + 0x03, 0xb5, 0x20, 0x17, 0x6c, 0x36, 0x7e, 0x26, 0x9f, 0xbf, 0xd7, 0xb2, 0xfe, 0x5e, 0xf3, 0xac, + 0x8f, 0xdc, 0x8f, 0xf0, 0x6f, 0xfd, 0xb7, 0x0a, 0xdc, 0x90, 0xd6, 0x3f, 0x58, 0x37, 0x3e, 0xaf, + 0x32, 0xdd, 0x5f, 0x33, 0x11, 0xae, 0x89, 0xee, 0x40, 0xc2, 0x4f, 0xc6, 0x5a, 0x3b, 0x2f, 0x77, + 0xd9, 0x53, 0x63, 0x3c, 0x23, 0xd8, 0x53, 0xe8, 0x07, 0x90, 0xef, 0x46, 0x2a, 0x4d, 0xb4, 0x03, + 0x6a, 0x60, 0xc6, 0x62, 0x77, 0xd5, 0x32, 0xe3, 0x57, 0x14, 0xea, 0x4b, 0x57, 0x14, 0xff, 0x50, + 0x60, 0x27, 0x74, 0x71, 0xe3, 0x83, 0xe9, 0xb2, 0xde, 0x7e, 0x02, 0x25, 0xcb, 0x1c, 0xbc, 0x74, + 0x0c, 0x69, 0xed, 0x8a, 0xcf, 0xe2, 0xa8, 0xb3, 0xb8, 0x60, 0x45, 0x5a, 0x54, 0xdf, 0x81, 0xda, + 0x79, 0xe4, 0x95, 0xd4, 0xfe, 0x08, 0x6e, 0x3d, 0x22, 0xac, 0xe7, 0xce, 0xfd, 0x21, 0xbe, 0x4b, + 0x51, 0x23, 0x95, 0x45, 0x23, 0x75, 0x0c, 0xb7, 0xe3, 0x83, 0x64, 0xa6, 0xf9, 0x01, 0xe4, 0xa9, + 0x3b, 0x1f, 0x2c, 0x8c, 0xf4, 0x32, 0x6b, 0x40, 0xaa, 0xe8, 0x20, 0x8d, 0x86, 0x0d, 0xfd, 0xdf, + 0x0a, 0x14, 0x9f, 0x6e, 0x42, 0xff, 0xd8, 0x31, 0xa0, 0xae, 0x78, 0x0c, 0x7c, 0x00, 0xa9, 0xf9, + 0x88, 0xc9, 0x9b, 0x29, 0xef, 0xd4, 0x8a, 0x3c, 0xdf, 0x3f, 0x7d, 0xc4, 0x2c, 0x13, 0x0b, 0xbd, + 0x97, 0xdc, 0x3f, 0xb7, 0xc6, 0x8c, 0xb8, 0xc1, 0x4e, 0x89, 0xf4, 0xfc, 0x94, 0x6b, 0xb0, 0xec, + 0xa1, 0xff, 0x08, 0x4a, 0x81, 0x2f, 0xe1, 0xd9, 0x40, 0xe6, 0xc4, 0x66, 0xb4, 0xaa, 0xc8, 0x43, + 0x2d, 0xba, 0xd0, 0xbe, 0xa7, 0xc2, 0xb2, 0xc7, 0xbd, 0x87, 0x50, 0x8a, 0xbd, 0x6d, 0xa3, 0x12, + 0x68, 0x4f, 0x0e, 0x7b, 0xc7, 0xfb, 0x9d, 0xee, 0xa7, 0xdd, 0xfd, 0x87, 0xe5, 0xb7, 0x10, 0x40, + 0xba, 0xd7, 0x3d, 0x7c, 0xf4, 0x78, 0xbf, 0xac, 0xa0, 0x1c, 0xa4, 0x0e, 0x9e, 0x3c, 0xee, 0x77, + 0xcb, 0xaa, 0xf7, 0xd9, 0x7f, 0x76, 0x74, 0xdc, 0x29, 0x27, 0xee, 0x7d, 0x02, 0x9a, 0xc8, 0xa3, + 0x47, 0xae, 0x49, 0x5c, 0x6f, 0xc0, 0xe1, 0x11, 0x3e, 0x78, 0xf0, 0xb8, 0xfc, 0x16, 0xca, 0x40, + 0xe2, 0x18, 0x7b, 0x23, 0xb3, 0x90, 0x3c, 0x3e, 0xea, 0xf5, 0xcb, 0x2a, 0x2a, 0x02, 0x3c, 0x78, + 0xd2, 0x3f, 0xea, 0x1c, 0x1d, 0x1c, 0x74, 0xfb, 0xe5, 0xc4, 0xde, 0xc7, 0x50, 0xb2, 0x9c, 0xe6, + 0xdc, 0x62, 0x84, 0x52, 0xf1, 0xdf, 0x09, 0x3f, 0x7f, 0x5f, 0xb6, 0x2c, 0xa7, 0x25, 0xbe, 0x5a, + 0x23, 0xa7, 0x35, 0x67, 0x2d, 0xae, 0x6d, 0x09, 0x2a, 0x9e, 0xa4, 0x79, 0xeb, 0xa3, 0x6f, 0x02, + 0x00, 0x00, 0xff, 0xff, 0xe2, 0x00, 0xc6, 0xcc, 0x1d, 0x21, 0x00, 0x00, } diff --git a/go/vt/proto/vtgateservice/vtgateservice.pb.go b/go/vt/proto/vtgateservice/vtgateservice.pb.go index 0dd813a77a9..7f8211a5737 100644 --- a/go/vt/proto/vtgateservice/vtgateservice.pb.go +++ b/go/vt/proto/vtgateservice/vtgateservice.pb.go @@ -30,43 +30,42 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package func init() { proto.RegisterFile("vtgateservice.proto", fileDescriptor_601ae27c95081e0f) } var fileDescriptor_601ae27c95081e0f = []byte{ - // 576 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x95, 0xdb, 0x6f, 0x12, 0x41, - 0x14, 0xc6, 0xf5, 0x41, 0x6a, 0x8e, 0xd0, 0x98, 0x69, 0x0b, 0x2d, 0x5e, 0x8b, 0xda, 0x1a, 0x1f, - 0xc0, 0x68, 0x62, 0x62, 0x62, 0x34, 0xa0, 0x8d, 0x31, 0x4d, 0xbd, 0x80, 0x62, 0xd2, 0xc4, 0x87, - 0x61, 0x39, 0xd9, 0x6e, 0x80, 0x5d, 0xd8, 0x19, 0x36, 0xf2, 0xd7, 0xf9, 0xaf, 0x99, 0xee, 0x5c, - 0x76, 0x66, 0x76, 0x80, 0x37, 0xe6, 0xfb, 0xbe, 0xf3, 0x63, 0xe6, 0xec, 0xc9, 0x0c, 0xec, 0x65, - 0x3c, 0xa4, 0x1c, 0x19, 0xa6, 0x59, 0x14, 0x60, 0x7b, 0x9e, 0x26, 0x3c, 0x21, 0x35, 0x4b, 0x6c, - 0x56, 0xc5, 0x52, 0x98, 0xcd, 0x3b, 0x8b, 0x25, 0xa6, 0x2b, 0xb1, 0x78, 0xf5, 0x6f, 0x17, 0x2a, - 0xc3, 0x88, 0x23, 0x63, 0xe4, 0x1d, 0xec, 0x9c, 0xfd, 0xc5, 0x60, 0xc9, 0x91, 0xd4, 0xdb, 0xb2, - 0x42, 0x0a, 0x7d, 0x5c, 0x2c, 0x91, 0xf1, 0x66, 0xa3, 0xa4, 0xb3, 0x79, 0x12, 0x33, 0x6c, 0xdd, - 0x20, 0xe7, 0x50, 0x95, 0x62, 0x8f, 0xf2, 0xe0, 0x8a, 0xdc, 0x73, 0xa2, 0xb9, 0xaa, 0x38, 0xf7, - 0xfd, 0xa6, 0x86, 0x7d, 0x87, 0xda, 0x80, 0xa7, 0x48, 0x67, 0x6a, 0x43, 0xba, 0xc0, 0x92, 0x15, - 0xee, 0xc1, 0x1a, 0x57, 0xf1, 0x5e, 0xde, 0x24, 0x5f, 0xa1, 0x26, 0xe5, 0xc1, 0x15, 0x4d, 0xc7, - 0x8c, 0xb8, 0x5b, 0x10, 0x72, 0x89, 0xe8, 0xb8, 0x7a, 0x87, 0x7f, 0x80, 0x48, 0xeb, 0x1c, 0x57, - 0x6c, 0x4e, 0x03, 0xfc, 0x32, 0x66, 0xe4, 0xd8, 0x29, 0x33, 0x3c, 0x45, 0x6e, 0x6d, 0x8a, 0x68, - 0xfc, 0x6f, 0xb8, 0x5b, 0xf8, 0x7d, 0x1a, 0x87, 0xc8, 0xc8, 0xa3, 0x72, 0xa5, 0x70, 0x14, 0xfa, - 0xf1, 0xfa, 0x80, 0x07, 0x7c, 0x16, 0xf3, 0x88, 0xaf, 0xae, 0x77, 0xed, 0x82, 0xb5, 0xb3, 0x0e, - 0x6c, 0x04, 0x3c, 0x0d, 0xc9, 0x3f, 0xa6, 0xec, 0xf2, 0xb1, 0xef, 0x43, 0xdb, 0xad, 0x6e, 0x6d, - 0x8a, 0x68, 0xfc, 0x14, 0x1a, 0xa6, 0x6f, 0x36, 0xfd, 0xc4, 0x07, 0xf0, 0x74, 0xfe, 0x74, 0x6b, - 0x4e, 0xff, 0xdb, 0x08, 0xf6, 0xac, 0x51, 0x92, 0xa7, 0x69, 0x79, 0xe7, 0xcc, 0x3e, 0xce, 0x93, - 0x8d, 0x19, 0x63, 0x22, 0x17, 0x70, 0x68, 0x45, 0xcc, 0x23, 0x9d, 0x7a, 0x21, 0x9e, 0x33, 0x3d, - 0xdf, 0x1e, 0x34, 0xfe, 0x72, 0x02, 0x75, 0x37, 0x27, 0x67, 0xeb, 0xd9, 0x3a, 0x8e, 0x3d, 0x61, - 0x27, 0xdb, 0x62, 0xc6, 0x9f, 0xbd, 0x81, 0x5b, 0x3d, 0x0c, 0xa3, 0x98, 0xec, 0xab, 0xa2, 0x7c, - 0xa9, 0x50, 0x07, 0x8e, 0xaa, 0x7b, 0xff, 0x16, 0x2a, 0x1f, 0x93, 0xd9, 0x2c, 0xe2, 0x44, 0x47, - 0xc4, 0x5a, 0x55, 0xd6, 0x5d, 0x59, 0x97, 0x7e, 0x80, 0xdb, 0xfd, 0x64, 0x3a, 0x1d, 0xd1, 0x60, - 0x42, 0xf4, 0x55, 0xa5, 0x14, 0x55, 0x7e, 0x58, 0x36, 0xcc, 0x21, 0xee, 0x23, 0x4b, 0xa6, 0x19, - 0xfe, 0x4c, 0x69, 0xcc, 0x68, 0xc0, 0xa3, 0x24, 0x2e, 0x86, 0xb8, 0xec, 0x95, 0x86, 0xd8, 0x17, - 0xd1, 0xf8, 0x6f, 0x50, 0xbb, 0x40, 0xc6, 0x68, 0x88, 0xa2, 0x7f, 0xc5, 0x25, 0x64, 0xc9, 0xc5, - 0x2d, 0x29, 0x6e, 0x6a, 0xc7, 0x34, 0x7a, 0xfc, 0x09, 0x40, 0x9a, 0xdd, 0x60, 0x42, 0x8e, 0x1c, - 0x5a, 0xb7, 0x38, 0xf4, 0x91, 0x8d, 0xea, 0x5a, 0xa7, 0xbe, 0x84, 0x83, 0x42, 0x37, 0xc7, 0xf0, - 0x69, 0x19, 0xe8, 0x99, 0xc1, 0x8d, 0xec, 0x1f, 0xb0, 0xfb, 0x19, 0xf9, 0x20, 0xcd, 0x54, 0x21, - 0xd1, 0x57, 0xab, 0xad, 0x2b, 0xda, 0xc3, 0x75, 0xb6, 0x46, 0xbe, 0x87, 0x9d, 0xa1, 0xec, 0x9f, - 0x1e, 0x85, 0xa1, 0xdd, 0xb9, 0x46, 0x49, 0x37, 0x9a, 0x76, 0x01, 0xd5, 0x5f, 0xf3, 0x31, 0xe5, - 0xea, 0x23, 0xe8, 0x97, 0xca, 0x54, 0x4b, 0x2f, 0x95, 0x6d, 0x16, 0xb8, 0x5e, 0x0f, 0xf6, 0xa3, - 0xa4, 0x9d, 0xe5, 0x6f, 0xa8, 0x78, 0x54, 0xdb, 0x61, 0x3a, 0x0f, 0x2e, 0x5f, 0x48, 0x29, 0x4a, - 0x3a, 0xe2, 0x57, 0x27, 0x4c, 0x3a, 0x19, 0xef, 0xe4, 0x91, 0x8e, 0xf5, 0x40, 0x8f, 0x2a, 0xb9, - 0xf8, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfb, 0xe3, 0xd1, 0x31, 0xcd, 0x07, 0x00, 0x00, + // 556 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x95, 0xdb, 0x6b, 0x13, 0x41, + 0x14, 0xc6, 0xf5, 0xc1, 0x54, 0x8e, 0x8d, 0xc8, 0xb4, 0x4d, 0xda, 0x78, 0x6d, 0xd4, 0x56, 0x7c, + 0x48, 0x44, 0x41, 0x10, 0x44, 0x49, 0xb4, 0x88, 0x14, 0x6f, 0x89, 0x44, 0x28, 0xf8, 0x30, 0xd9, + 0x1e, 0xb6, 0x4b, 0x92, 0x9d, 0x64, 0x67, 0xb2, 0x98, 0xff, 0xda, 0x3f, 0x41, 0xdc, 0xb9, 0xec, + 0xcc, 0xec, 0x24, 0x79, 0xcb, 0x7c, 0xdf, 0x77, 0x7e, 0x3b, 0x73, 0x72, 0x98, 0x81, 0xbd, 0x5c, + 0xc4, 0x54, 0x20, 0xc7, 0x2c, 0x4f, 0x22, 0xec, 0xcc, 0x33, 0x26, 0x18, 0xa9, 0x3b, 0x62, 0x6b, + 0x57, 0x2e, 0xa5, 0xd9, 0xba, 0xb5, 0x58, 0x62, 0xb6, 0x92, 0x8b, 0x97, 0x7f, 0xeb, 0x50, 0x1b, + 0x25, 0x02, 0x39, 0x27, 0x6f, 0x61, 0xe7, 0xec, 0x0f, 0x46, 0x4b, 0x81, 0xa4, 0xd1, 0x51, 0x15, + 0x4a, 0x18, 0xe0, 0x62, 0x89, 0x5c, 0xb4, 0x9a, 0x15, 0x9d, 0xcf, 0x59, 0xca, 0xb1, 0x7d, 0x8d, + 0x9c, 0xc3, 0xae, 0x12, 0xfb, 0x54, 0x44, 0x57, 0xe4, 0xae, 0x17, 0x2d, 0x54, 0xcd, 0xb9, 0x17, + 0x36, 0x0d, 0xec, 0x3b, 0xd4, 0x87, 0x22, 0x43, 0x3a, 0xd3, 0x1b, 0x32, 0x05, 0x8e, 0xac, 0x71, + 0xf7, 0xd7, 0xb8, 0x9a, 0xf7, 0xe2, 0x3a, 0xf9, 0x0a, 0x75, 0x25, 0x0f, 0xaf, 0x68, 0x76, 0xc9, + 0x89, 0xbf, 0x05, 0x29, 0x57, 0x88, 0x9e, 0x6b, 0x76, 0xf8, 0x1b, 0x88, 0xb2, 0xce, 0x71, 0xc5, + 0xe7, 0x34, 0xc2, 0xcf, 0x97, 0x9c, 0x1c, 0x7b, 0x65, 0x96, 0xa7, 0xc9, 0xed, 0x4d, 0x11, 0x83, + 0xff, 0x05, 0x77, 0x4a, 0x7f, 0x40, 0xd3, 0x18, 0x39, 0x79, 0x58, 0xad, 0x94, 0x8e, 0x46, 0x3f, + 0x5a, 0x1f, 0x08, 0x80, 0xcf, 0x52, 0x91, 0x88, 0xd5, 0xff, 0x5d, 0xfb, 0x60, 0xe3, 0xac, 0x03, + 0x5b, 0x81, 0x40, 0x43, 0x8a, 0x3f, 0x53, 0x75, 0xf9, 0x38, 0xf4, 0x47, 0xbb, 0xad, 0x6e, 0x6f, + 0x8a, 0x18, 0xfc, 0x14, 0x9a, 0xb6, 0x6f, 0x37, 0xfd, 0x24, 0x04, 0x08, 0x74, 0xfe, 0x74, 0x6b, + 0xce, 0x7c, 0x6d, 0x0c, 0x7b, 0xce, 0x28, 0xa9, 0xd3, 0xb4, 0x83, 0x73, 0xe6, 0x1e, 0xe7, 0xf1, + 0xc6, 0x8c, 0x35, 0x91, 0x0b, 0x38, 0x74, 0x22, 0xf6, 0x91, 0x4e, 0x83, 0x90, 0xc0, 0x99, 0x9e, + 0x6d, 0x0f, 0x5a, 0x9f, 0x9c, 0x40, 0xc3, 0xcf, 0xa9, 0xd9, 0x7a, 0xba, 0x8e, 0xe3, 0x4e, 0xd8, + 0xc9, 0xb6, 0x98, 0xf5, 0xb1, 0xd7, 0x70, 0xa3, 0x8f, 0x71, 0x92, 0x92, 0x7d, 0x5d, 0x54, 0x2c, + 0x35, 0xea, 0xc0, 0x53, 0x4d, 0xef, 0xdf, 0x40, 0xed, 0x03, 0x9b, 0xcd, 0x12, 0x41, 0x4c, 0x44, + 0xae, 0x75, 0x65, 0xc3, 0x97, 0x4d, 0xe9, 0x7b, 0xb8, 0x39, 0x60, 0xd3, 0xe9, 0x98, 0x46, 0x13, + 0x62, 0xae, 0x2a, 0xad, 0xe8, 0xf2, 0xc3, 0xaa, 0x61, 0x0f, 0xf1, 0x00, 0x39, 0x9b, 0xe6, 0xf8, + 0x33, 0xa3, 0x29, 0xa7, 0x91, 0x48, 0x58, 0x5a, 0x0e, 0x71, 0xd5, 0xab, 0x0c, 0x71, 0x28, 0x62, + 0xf0, 0xdf, 0xa0, 0xfe, 0x05, 0x39, 0xa7, 0x31, 0xca, 0xfe, 0x95, 0x97, 0x90, 0x23, 0x97, 0xb7, + 0xa4, 0xbc, 0xa9, 0x3d, 0xd3, 0xea, 0xf1, 0x47, 0x00, 0x65, 0xf6, 0xa2, 0x09, 0x39, 0xf2, 0x68, + 0xbd, 0xf2, 0xd0, 0x47, 0x2e, 0xaa, 0xe7, 0x9c, 0xfa, 0x02, 0x0e, 0x4a, 0xdd, 0x1e, 0xc3, 0x27, + 0x55, 0x60, 0x60, 0x06, 0x37, 0xb2, 0x7f, 0xc0, 0xed, 0x4f, 0x28, 0x86, 0x59, 0xae, 0x0b, 0x89, + 0xb9, 0x5a, 0x5d, 0x5d, 0xd3, 0x1e, 0xac, 0xb3, 0x0d, 0xf2, 0x1d, 0xec, 0x8c, 0x54, 0xff, 0xcc, + 0x28, 0x8c, 0xdc, 0xce, 0x35, 0x2b, 0x7a, 0xd9, 0xb4, 0x7e, 0x1f, 0xf6, 0x13, 0xd6, 0xc9, 0x8b, + 0x47, 0x4f, 0xbe, 0x82, 0x9d, 0x38, 0x9b, 0x47, 0x17, 0xcf, 0x95, 0x94, 0xb0, 0xae, 0xfc, 0xd5, + 0x8d, 0x59, 0x37, 0x17, 0xdd, 0x22, 0xd2, 0x75, 0x5e, 0xd4, 0x71, 0xad, 0x10, 0x5f, 0xfd, 0x0b, + 0x00, 0x00, 0xff, 0xff, 0x96, 0x13, 0x38, 0x44, 0x7e, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -157,9 +156,6 @@ type VitessClient interface { GetSrvKeyspace(ctx context.Context, in *vtgate.GetSrvKeyspaceRequest, opts ...grpc.CallOption) (*vtgate.GetSrvKeyspaceResponse, error) // VStream streams binlog events from the requested sources. VStream(ctx context.Context, in *vtgate.VStreamRequest, opts ...grpc.CallOption) (Vitess_VStreamClient, error) - // UpdateStream asks the server for a stream of StreamEvent objects. - // API group: Update Stream - UpdateStream(ctx context.Context, in *vtgate.UpdateStreamRequest, opts ...grpc.CallOption) (Vitess_UpdateStreamClient, error) } type vitessClient struct { @@ -497,38 +493,6 @@ func (x *vitessVStreamClient) Recv() (*vtgate.VStreamResponse, error) { return m, nil } -func (c *vitessClient) UpdateStream(ctx context.Context, in *vtgate.UpdateStreamRequest, opts ...grpc.CallOption) (Vitess_UpdateStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &_Vitess_serviceDesc.Streams[6], "/vtgateservice.Vitess/UpdateStream", opts...) - if err != nil { - return nil, err - } - x := &vitessUpdateStreamClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Vitess_UpdateStreamClient interface { - Recv() (*vtgate.UpdateStreamResponse, error) - grpc.ClientStream -} - -type vitessUpdateStreamClient struct { - grpc.ClientStream -} - -func (x *vitessUpdateStreamClient) Recv() (*vtgate.UpdateStreamResponse, error) { - m := new(vtgate.UpdateStreamResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - // VitessServer is the server API for Vitess service. type VitessServer interface { // Execute tries to route the query to the right shard. @@ -607,9 +571,6 @@ type VitessServer interface { GetSrvKeyspace(context.Context, *vtgate.GetSrvKeyspaceRequest) (*vtgate.GetSrvKeyspaceResponse, error) // VStream streams binlog events from the requested sources. VStream(*vtgate.VStreamRequest, Vitess_VStreamServer) error - // UpdateStream asks the server for a stream of StreamEvent objects. - // API group: Update Stream - UpdateStream(*vtgate.UpdateStreamRequest, Vitess_UpdateStreamServer) error } // UnimplementedVitessServer can be embedded to have forward compatible implementations. @@ -679,9 +640,6 @@ func (*UnimplementedVitessServer) GetSrvKeyspace(ctx context.Context, req *vtgat func (*UnimplementedVitessServer) VStream(req *vtgate.VStreamRequest, srv Vitess_VStreamServer) error { return status.Errorf(codes.Unimplemented, "method VStream not implemented") } -func (*UnimplementedVitessServer) UpdateStream(req *vtgate.UpdateStreamRequest, srv Vitess_UpdateStreamServer) error { - return status.Errorf(codes.Unimplemented, "method UpdateStream not implemented") -} func RegisterVitessServer(s *grpc.Server, srv VitessServer) { s.RegisterService(&_Vitess_serviceDesc, srv) @@ -1083,27 +1041,6 @@ func (x *vitessVStreamServer) Send(m *vtgate.VStreamResponse) error { return x.ServerStream.SendMsg(m) } -func _Vitess_UpdateStream_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(vtgate.UpdateStreamRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(VitessServer).UpdateStream(m, &vitessUpdateStreamServer{stream}) -} - -type Vitess_UpdateStreamServer interface { - Send(*vtgate.UpdateStreamResponse) error - grpc.ServerStream -} - -type vitessUpdateStreamServer struct { - grpc.ServerStream -} - -func (x *vitessUpdateStreamServer) Send(m *vtgate.UpdateStreamResponse) error { - return x.ServerStream.SendMsg(m) -} - var _Vitess_serviceDesc = grpc.ServiceDesc{ ServiceName: "vtgateservice.Vitess", HandlerType: (*VitessServer)(nil), @@ -1200,11 +1137,6 @@ var _Vitess_serviceDesc = grpc.ServiceDesc{ Handler: _Vitess_VStream_Handler, ServerStreams: true, }, - { - StreamName: "UpdateStream", - Handler: _Vitess_UpdateStream_Handler, - ServerStreams: true, - }, }, Metadata: "vtgateservice.proto", } diff --git a/go/vt/vitessdriver/fakeserver_test.go b/go/vt/vitessdriver/fakeserver_test.go index 6bd04da762d..73c0731caa2 100644 --- a/go/vt/vitessdriver/fakeserver_test.go +++ b/go/vt/vitessdriver/fakeserver_test.go @@ -226,11 +226,6 @@ func (f *fakeVTGateService) VStream(ctx context.Context, tabletType topodatapb.T return nil } -// UpdateStream is part of the VTGateService interface -func (f *fakeVTGateService) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - return nil -} - // HandlePanic is part of the VTGateService interface func (f *fakeVTGateService) HandlePanic(err *error) { if x := recover(); x != nil { diff --git a/go/vt/vtcombo/tablet_map.go b/go/vt/vtcombo/tablet_map.go index 0b982608576..26f3e2c7d5b 100644 --- a/go/vt/vtcombo/tablet_map.go +++ b/go/vt/vtcombo/tablet_map.go @@ -448,12 +448,6 @@ func (itc *internalTabletConn) StreamHealth(ctx context.Context, callback func(* return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err)) } -// UpdateStream is part of queryservice.QueryService. -func (itc *internalTabletConn) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error { - err := itc.tablet.qsc.QueryService().UpdateStream(ctx, target, position, timestamp, callback) - return tabletconn.ErrorFromGRPC(vterrors.ToGRPC(err)) -} - // VStream is part of queryservice.QueryService. func (itc *internalTabletConn) VStream(ctx context.Context, target *querypb.Target, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { err := itc.tablet.qsc.QueryService().VStream(ctx, target, startPos, filter, send) diff --git a/go/vt/vtctl/query.go b/go/vt/vtctl/query.go index e8919efc6ed..b5b2846c73b 100644 --- a/go/vt/vtctl/query.go +++ b/go/vt/vtctl/query.go @@ -96,11 +96,6 @@ func init() { commandVtTabletStreamHealth, "[-count ] ", "Executes the StreamHealth streaming query to a vttablet process. Will stop after getting answers."}) - addCommand(queriesGroupName, command{ - "VtTabletUpdateStream", - commandVtTabletUpdateStream, - "[-count ] [-position ] [-timestamp ] ", - "Executes the UpdateStream streaming query to a vttablet process. Will stop after getting answers."}) } type bindvars map[string]interface{} @@ -560,61 +555,6 @@ func commandVtTabletStreamHealth(ctx context.Context, wr *wrangler.Wrangler, sub return nil } -func commandVtTabletUpdateStream(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { - if !*enableQueries { - return fmt.Errorf("query commands are disabled (set the -enable_queries flag to enable)") - } - - count := subFlags.Int("count", 1, "number of responses to wait for") - timestamp := subFlags.Int("timestamp", 0, "timestamp to start the stream from") - position := subFlags.String("position", "", "position to start the stream from") - if err := subFlags.Parse(args); err != nil { - return err - } - if subFlags.NArg() != 1 { - return fmt.Errorf("the argument is required for the VtTabletUpdateStream command") - } - tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0)) - if err != nil { - return err - } - tabletInfo, err := wr.TopoServer().GetTablet(ctx, tabletAlias) - if err != nil { - return err - } - - conn, err := tabletconn.GetDialer()(tabletInfo.Tablet, grpcclient.FailFast(false)) - if err != nil { - return fmt.Errorf("cannot connect to tablet %v: %v", tabletAlias, err) - } - - i := 0 - err = conn.UpdateStream(ctx, &querypb.Target{ - Keyspace: tabletInfo.Tablet.Keyspace, - Shard: tabletInfo.Tablet.Shard, - TabletType: tabletInfo.Tablet.Type, - }, *position, int64(*timestamp), func(se *querypb.StreamEvent) error { - data, err := json.Marshal(se) - if err != nil { - wr.Logger().Errorf2(err, "cannot json-marshal structure") - } else { - wr.Logger().Printf("%v\n", string(data)) - } - i++ - if i >= *count { - return io.EOF - } - return nil - }) - if err != nil { - return err - } - if i < *count { - return errors.New("stream ended early") - } - return nil -} - // loggerWriter turns a Logger into a Writer by decorating it with a Write() // method that sends everything to Logger.Printf(). type loggerWriter struct { diff --git a/go/vt/vtgate/fakerpcvtgateconn/conn.go b/go/vt/vtgate/fakerpcvtgateconn/conn.go index e9f84249e87..0ca9b5f42dc 100644 --- a/go/vt/vtgate/fakerpcvtgateconn/conn.go +++ b/go/vt/vtgate/fakerpcvtgateconn/conn.go @@ -316,11 +316,6 @@ func (conn *FakeVTGateConn) VStream(ctx context.Context, tabletType topodatapb.T return nil, fmt.Errorf("NYI") } -// UpdateStream please see vtgateconn.Impl.UpdateStream -func (conn *FakeVTGateConn) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken) (vtgateconn.UpdateStreamReader, error) { - return nil, fmt.Errorf("NYI") -} - // Close please see vtgateconn.Impl.Close func (conn *FakeVTGateConn) Close() { } diff --git a/go/vt/vtgate/grpcvtgateconn/conn.go b/go/vt/vtgate/grpcvtgateconn/conn.go index 63523f45aab..8d4293d03f0 100644 --- a/go/vt/vtgate/grpcvtgateconn/conn.go +++ b/go/vt/vtgate/grpcvtgateconn/conn.go @@ -517,37 +517,6 @@ func (conn *vtgateConn) VStream(ctx context.Context, tabletType topodatapb.Table }, nil } -type updateStreamAdapter struct { - stream vtgateservicepb.Vitess_UpdateStreamClient -} - -func (a *updateStreamAdapter) Recv() (*querypb.StreamEvent, int64, error) { - r, err := a.stream.Recv() - if err != nil { - return nil, 0, vterrors.FromGRPC(err) - } - return r.Event, r.ResumeTimestamp, nil -} - -func (conn *vtgateConn) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken) (vtgateconn.UpdateStreamReader, error) { - req := &vtgatepb.UpdateStreamRequest{ - CallerId: callerid.EffectiveCallerIDFromContext(ctx), - Keyspace: keyspace, - Shard: shard, - KeyRange: keyRange, - TabletType: tabletType, - Timestamp: timestamp, - Event: event, - } - stream, err := conn.c.UpdateStream(ctx, req) - if err != nil { - return nil, vterrors.FromGRPC(err) - } - return &updateStreamAdapter{ - stream: stream, - }, nil -} - func (conn *vtgateConn) Close() { conn.cc.Close() } diff --git a/go/vt/vtgate/grpcvtgateconn/suite_test.go b/go/vt/vtgate/grpcvtgateconn/suite_test.go index 4bbe6e7ffea..150f39dffc4 100644 --- a/go/vt/vtgate/grpcvtgateconn/suite_test.go +++ b/go/vt/vtgate/grpcvtgateconn/suite_test.go @@ -840,83 +840,6 @@ func (f *fakeVTGateService) VStream(ctx context.Context, tabletType topodatapb.T panic("unimplemented") } -// queryUpdateStream contains all the fields we use to test UpdateStream -type queryUpdateStream struct { - Keyspace string - Shard string - KeyRange *topodatapb.KeyRange - TabletType topodatapb.TabletType - Timestamp int64 - Event *querypb.EventToken -} - -func (q *queryUpdateStream) equal(q2 *queryUpdateStream) bool { - return q.Keyspace == q2.Keyspace && - q.Shard == q2.Shard && - proto.Equal(q.KeyRange, q2.KeyRange) && - q.TabletType == q2.TabletType && - q.Timestamp == q2.Timestamp && - proto.Equal(q.Event, q2.Event) -} - -// UpdateStream is part of the VTGateService interface -func (f *fakeVTGateService) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - if f.panics { - panic(fmt.Errorf("test forced panic")) - } - execCase, ok := execMap[shard] - if !ok { - return fmt.Errorf("no match for: %s", shard) - } - f.checkCallerID(ctx, "UpdateStream") - query := &queryUpdateStream{ - Keyspace: keyspace, - Shard: shard, - KeyRange: keyRange, - TabletType: tabletType, - Timestamp: timestamp, - Event: event, - } - if !query.equal(execCase.updateStreamQuery) { - f.t.Errorf("UpdateStream: %+v, want %+v", query, execCase.updateStreamQuery) - return nil - } - if execCase.result != nil { - // The first result only has statement with fields. - result := &querypb.StreamEvent{ - Statements: []*querypb.StreamEvent_Statement{ - { - PrimaryKeyFields: execCase.result.Fields, - }, - }, - } - if err := callback(result, int64(execCase.result.RowsAffected)); err != nil { - return err - } - if f.hasError { - // wait until the client has the response, since all streaming implementation may not - // send previous messages if an error has been triggered. - <-f.errorWait - f.errorWait = make(chan struct{}) // for next test - return errTestVtGateError - } - for _, row := range execCase.result.Rows { - - result := &querypb.StreamEvent{ - Statements: []*querypb.StreamEvent_Statement{ - { - PrimaryKeyValues: sqltypes.RowsToProto3([][]sqltypes.Value{row}), - }, - }, - } - if err := callback(result, int64(execCase.result.RowsAffected)); err != nil { - return err - } - } - } - return nil -} - // CreateFakeServer returns the fake server for the tests func CreateFakeServer(t *testing.T) vtgateservice.VTGateService { return &fakeVTGateService{ @@ -975,7 +898,6 @@ func RunTests(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGat testMessageAck(t, conn) testMessageAckKeyspaceIds(t, conn) testGetSrvKeyspace(t, conn) - testUpdateStream(t, conn) // force a panic at every call, then test that works fs.panics = true @@ -999,7 +921,6 @@ func RunTests(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGat testMessageAckPanic(t, conn) testMessageAckKeyspaceIdsPanic(t, conn) testGetSrvKeyspacePanic(t, conn) - testUpdateStreamPanic(t, conn) fs.panics = false } @@ -1035,7 +956,6 @@ func RunErrorTests(t *testing.T, fakeServer vtgateservice.VTGateService) { testMessageAckError(t, conn) testMessageAckKeyspaceIdsError(t, conn) testGetSrvKeyspaceError(t, conn) - testUpdateStreamError(t, conn, fs) fs.hasError = false } @@ -1938,89 +1858,6 @@ func testGetSrvKeyspacePanic(t *testing.T, conn *vtgateconn.VTGateConn) { expectPanic(t, err) } -func testUpdateStream(t *testing.T, conn *vtgateconn.VTGateConn) { - ctx := newContext() - execCase := execMap["request1"] - stream, err := conn.UpdateStream(ctx, execCase.updateStreamQuery.Keyspace, execCase.updateStreamQuery.Shard, execCase.updateStreamQuery.KeyRange, execCase.updateStreamQuery.TabletType, execCase.updateStreamQuery.Timestamp, execCase.updateStreamQuery.Event) - if err != nil { - t.Fatal(err) - } - var qr querypb.QueryResult - for { - packet, resumeTimestamp, err := stream.Recv() - if err != nil { - if err != io.EOF { - t.Error(err) - } - break - } - qr.RowsAffected = uint64(resumeTimestamp) - if len(packet.Statements[0].PrimaryKeyFields) != 0 { - qr.Fields = packet.Statements[0].PrimaryKeyFields - } - if len(packet.Statements[0].PrimaryKeyValues) != 0 { - qr.Rows = append(qr.Rows, packet.Statements[0].PrimaryKeyValues...) - } - } - - sqr := sqltypes.Proto3ToResult(&qr) - wantResult := *execCase.result - wantResult.InsertID = 0 - wantResult.Extras = nil - if !sqr.Equal(&wantResult) { - t.Errorf("Unexpected result from UpdateStream: got %+v want %+v", sqr, wantResult) - } - - stream, err = conn.UpdateStream(ctx, "", "none", nil, topodatapb.TabletType_RDONLY, 0, nil) - if err != nil { - t.Fatal(err) - } - _, _, err = stream.Recv() - want := "no match for: none" - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("none request: %v, want %v", err, want) - } -} - -func testUpdateStreamError(t *testing.T, conn *vtgateconn.VTGateConn, fake *fakeVTGateService) { - ctx := newContext() - execCase := execMap["request1"] - stream, err := conn.UpdateStream(ctx, execCase.updateStreamQuery.Keyspace, execCase.updateStreamQuery.Shard, execCase.updateStreamQuery.KeyRange, execCase.updateStreamQuery.TabletType, execCase.updateStreamQuery.Timestamp, execCase.updateStreamQuery.Event) - if err != nil { - t.Fatalf("UpdateStream failed: %v", err) - } - qr, _, err := stream.Recv() - if err != nil { - t.Fatalf("UpdateStream failed: cannot read result1: %v", err) - } - - if !sqltypes.FieldsEqual(qr.Statements[0].PrimaryKeyFields, execCase.result.Fields) { - t.Errorf("Unexpected result from UpdateStream: got %#v want %#v", qr.Statements[0].PrimaryKeyFields, execCase.result.Fields) - } - // signal to the server that the first result has been received - close(fake.errorWait) - // After 1 result, we expect to get an error (no more results). - _, _, err = stream.Recv() - if err == nil { - t.Fatalf("UpdateStream channel wasn't closed") - } - verifyError(t, err, "UpdateStream") -} - -func testUpdateStreamPanic(t *testing.T, conn *vtgateconn.VTGateConn) { - ctx := newContext() - execCase := execMap["request1"] - stream, err := conn.UpdateStream(ctx, execCase.updateStreamQuery.Keyspace, execCase.updateStreamQuery.Shard, execCase.updateStreamQuery.KeyRange, execCase.updateStreamQuery.TabletType, execCase.updateStreamQuery.Timestamp, execCase.updateStreamQuery.Event) - if err != nil { - t.Fatal(err) - } - _, _, err = stream.Recv() - if err == nil { - t.Fatalf("Received packets instead of panic?") - } - expectPanic(t, err) -} - var testCallerID = &vtrpcpb.CallerID{ Principal: "test_principal", Component: "test_component", @@ -2045,7 +1882,6 @@ var execMap = map[string]struct { entityIdsQuery *queryExecuteEntityIds batchQueryShard *queryExecuteBatchShards keyspaceIDBatchQuery *queryExecuteBatchKeyspaceIds - updateStreamQuery *queryUpdateStream result *sqltypes.Result outSession *vtgatepb.Session err error @@ -2152,21 +1988,6 @@ var execMap = map[string]struct { AsTransaction: true, Session: nil, }, - updateStreamQuery: &queryUpdateStream{ - Keyspace: "connection_ks", - Shard: "request1", - KeyRange: &topodatapb.KeyRange{ - Start: []byte{0x72}, - End: []byte{0x90}, - }, - TabletType: topodatapb.TabletType_RDONLY, - Timestamp: 123789, - Event: &querypb.EventToken{ - Timestamp: 1234567, - Shard: "request1", - Position: "streaming_position", - }, - }, result: &result1, outSession: nil, }, @@ -2271,21 +2092,6 @@ var execMap = map[string]struct { AsTransaction: false, Session: nil, }, - updateStreamQuery: &queryUpdateStream{ - Keyspace: "connection_ks", - Shard: "errorRequst", - KeyRange: &topodatapb.KeyRange{ - Start: []byte{0x72}, - End: []byte{0x90}, - }, - TabletType: topodatapb.TabletType_RDONLY, - Timestamp: 123789, - Event: &querypb.EventToken{ - Timestamp: 1234567, - Shard: "request1", - Position: "streaming_position", - }, - }, result: nil, outSession: nil, }, @@ -2385,21 +2191,6 @@ var execMap = map[string]struct { TabletType: topodatapb.TabletType_RDONLY, Session: session1, }, - updateStreamQuery: &queryUpdateStream{ - Keyspace: "connection_ks", - Shard: "txRequest", - KeyRange: &topodatapb.KeyRange{ - Start: []byte{0x72}, - End: []byte{0x90}, - }, - TabletType: topodatapb.TabletType_RDONLY, - Timestamp: 123789, - Event: &querypb.EventToken{ - Timestamp: 1234567, - Shard: "request1", - Position: "streaming_position", - }, - }, result: nil, outSession: session2, }, diff --git a/go/vt/vtgate/grpcvtgateservice/server.go b/go/vt/vtgate/grpcvtgateservice/server.go index 5321c9325fc..cbda8f0c5f1 100644 --- a/go/vt/vtgate/grpcvtgateservice/server.go +++ b/go/vt/vtgate/grpcvtgateservice/server.go @@ -480,28 +480,6 @@ func (vtg *VTGate) VStream(request *vtgatepb.VStreamRequest, stream vtgateservic return vterrors.ToGRPC(vtgErr) } -// UpdateStream is the RPC version of vtgateservice.VTGateService method -func (vtg *VTGate) UpdateStream(request *vtgatepb.UpdateStreamRequest, stream vtgateservicepb.Vitess_UpdateStreamServer) (err error) { - defer vtg.server.HandlePanic(&err) - ctx := withCallerIDContext(stream.Context(), request.CallerId) - vtgErr := vtg.server.UpdateStream(ctx, - request.Keyspace, - request.Shard, - request.KeyRange, - request.TabletType, - request.Timestamp, - request.Event, - func(event *querypb.StreamEvent, resumeTimestamp int64) error { - // Send is not safe to call concurrently, but vtgate - // guarantees that it's not. - return stream.Send(&vtgatepb.UpdateStreamResponse{ - Event: event, - ResumeTimestamp: resumeTimestamp, - }) - }) - return vterrors.ToGRPC(vtgErr) -} - func init() { vtgate.RegisterVTGates = append(vtgate.RegisterVTGates, func(vtGate vtgateservice.VTGateService) { if servenv.GRPCCheckServiceMap("vtgateservice") { diff --git a/go/vt/vtgate/resolver.go b/go/vt/vtgate/resolver.go index c3c1cf42299..6dc66af2708 100644 --- a/go/vt/vtgate/resolver.go +++ b/go/vt/vtgate/resolver.go @@ -306,45 +306,6 @@ func (res *Resolver) MessageAckKeyspaceIds(ctx context.Context, keyspace, name s return res.scatterConn.MessageAck(ctx, rss, values, name) } -// UpdateStream streams the events. -// TODO(alainjobart): Implement the multi-shards merge code. -func (res *Resolver) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - var destination key.Destination - if shard != "" { - // If we pass in a shard, resolve the keyspace/shard - // following redirects. - destination = key.DestinationShard(shard) - } else { - // If we pass in a KeyRange, resolve it to one shard - // only for now. - destination = key.DestinationExactKeyRange{KeyRange: keyRange} - } - rss, err := res.resolver.ResolveDestination(ctx, keyspace, tabletType, destination) - if err != nil { - return err - } - if len(rss) != 1 { - return fmt.Errorf("UpdateStream only supports exactly one shard per keyrange at the moment, but provided keyrange %v maps to %v shards", keyRange, len(rss)) - } - - // Just send it to ScatterConn. With just one connection, the - // timestamp to resume from is the one we get. - // Also use the incoming event if the shard matches. - position := "" - if event != nil && event.Shard == shard { - position = event.Position - timestamp = 0 - } - return res.scatterConn.UpdateStream(ctx, rss[0], timestamp, position, func(se *querypb.StreamEvent) error { - var timestamp int64 - if se.EventToken != nil { - timestamp = se.EventToken.Timestamp - se.EventToken.Shard = shard - } - return callback(se, timestamp) - }) -} - // GetGatewayCacheStatus returns a displayable version of the Gateway cache. func (res *Resolver) GetGatewayCacheStatus() gateway.TabletCacheStatusList { return res.scatterConn.GetGatewayCacheStatus() diff --git a/go/vt/vtgate/scatter_conn.go b/go/vt/vtgate/scatter_conn.go index 8e0ebff3342..53ecdce9bbd 100644 --- a/go/vt/vtgate/scatter_conn.go +++ b/go/vt/vtgate/scatter_conn.go @@ -614,12 +614,6 @@ func (stc *ScatterConn) MessageAck(ctx context.Context, rss []*srvtopo.ResolvedS return totalCount, allErrors.AggrError(vterrors.Aggregate) } -// UpdateStream just sends the query to the ResolvedShard, -// and sends the results back. -func (stc *ScatterConn) UpdateStream(ctx context.Context, rs *srvtopo.ResolvedShard, timestamp int64, position string, callback func(*querypb.StreamEvent) error) error { - return rs.QueryService.UpdateStream(ctx, rs.Target, position, timestamp, callback) -} - // Close closes the underlying Gateway. func (stc *ScatterConn) Close() error { return stc.gateway.Close(context.Background()) diff --git a/go/vt/vtgate/vtgate.go b/go/vt/vtgate/vtgate.go index b7a766ddfec..620f486230a 100644 --- a/go/vt/vtgate/vtgate.go +++ b/go/vt/vtgate/vtgate.go @@ -126,7 +126,6 @@ type VTGate struct { logStreamExecuteKeyspaceIds *logutil.ThrottledLogger logStreamExecuteKeyRanges *logutil.ThrottledLogger logStreamExecuteShards *logutil.ThrottledLogger - logUpdateStream *logutil.ThrottledLogger logMessageStream *logutil.ThrottledLogger } @@ -199,7 +198,6 @@ func Init(ctx context.Context, hc discovery.HealthCheck, serv srvtopo.Server, ce logStreamExecuteKeyspaceIds: logutil.NewThrottledLogger("StreamExecuteKeyspaceIds", 5*time.Second), logStreamExecuteKeyRanges: logutil.NewThrottledLogger("StreamExecuteKeyRanges", 5*time.Second), logStreamExecuteShards: logutil.NewThrottledLogger("StreamExecuteShards", 5*time.Second), - logUpdateStream: logutil.NewThrottledLogger("UpdateStream", 5*time.Second), logMessageStream: logutil.NewThrottledLogger("MessageStream", 5*time.Second), } @@ -920,39 +918,6 @@ func (vtg *VTGate) MessageAckKeyspaceIds(ctx context.Context, keyspace string, n return count, formatError(err) } -// UpdateStream is part of the vtgate service API. -// Note we guarantee the callback will not be called concurrently -// by multiple go routines, as the current implementation can only target -// one shard. -func (vtg *VTGate) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - startTime := time.Now() - ltt := topoproto.TabletTypeLString(tabletType) - statsKey := []string{"UpdateStream", keyspace, ltt} - defer vtg.timings.Record(statsKey, startTime) - - err := vtg.resolver.UpdateStream( - ctx, - keyspace, - shard, - keyRange, - tabletType, - timestamp, - event, - callback, - ) - if err != nil { - request := map[string]interface{}{ - "Keyspace": keyspace, - "Shard": shard, - "KeyRange": keyRange, - "TabletType": ltt, - "Timestamp": timestamp, - } - recordAndAnnotateError(err, statsKey, request, vtg.logUpdateStream) - } - return formatError(err) -} - // VStream streams binlog events. func (vtg *VTGate) VStream(ctx context.Context, tabletType topodatapb.TabletType, vgtid *binlogdatapb.VGtid, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { return vtg.vsm.VStream(ctx, tabletType, vgtid, filter, send) diff --git a/go/vt/vtgate/vtgateconn/vtgateconn.go b/go/vt/vtgate/vtgateconn/vtgateconn.go index 470a22824e1..7c764343384 100644 --- a/go/vt/vtgate/vtgateconn/vtgateconn.go +++ b/go/vt/vtgate/vtgateconn/vtgateconn.go @@ -177,21 +177,6 @@ func (conn *VTGateConn) VStream(ctx context.Context, tabletType topodatapb.Table return conn.impl.VStream(ctx, tabletType, vgtid, filter) } -// UpdateStreamReader is returned by UpdateStream. -type UpdateStreamReader interface { - // Recv returns the next result on the stream. - // It will return io.EOF if the stream ended. - Recv() (*querypb.StreamEvent, int64, error) -} - -// UpdateStream executes a streaming query on vtgate. It returns an -// UpdateStreamReader and an error. First check the error. Then you -// can pull values from the UpdateStreamReader until io.EOF, or -// another error. -func (conn *VTGateConn) UpdateStream(ctx context.Context, keyspace, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken) (UpdateStreamReader, error) { - return conn.impl.UpdateStream(ctx, keyspace, shard, keyRange, tabletType, timestamp, event) -} - // VTGateSession exposes the V3 API to the clients. // The object maintains client-side state and is comparable to a native MySQL connection. // For example, if you enable autocommit on a Session object, all subsequent calls will respect this. @@ -379,9 +364,6 @@ type Impl interface { // VStream streams binlogevents VStream(ctx context.Context, tabletType topodatapb.TabletType, vgtid *binlogdatapb.VGtid, filter *binlogdatapb.Filter) (VStreamReader, error) - // UpdateStream asks for a stream of StreamEvent. - UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken) (UpdateStreamReader, error) - // Close must be called for releasing resources. Close() } diff --git a/go/vt/vtgate/vtgateservice/interface.go b/go/vt/vtgate/vtgateservice/interface.go index ae2acf182be..83bcb2919f1 100644 --- a/go/vt/vtgate/vtgateservice/interface.go +++ b/go/vt/vtgate/vtgateservice/interface.go @@ -63,7 +63,6 @@ type VTGateService interface { // Update Stream methods VStream(ctx context.Context, tabletType topodatapb.TabletType, vgtid *binlogdatapb.VGtid, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error - UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error // HandlePanic should be called with defer at the beginning of each // RPC implementation method, before calling any of the previous methods diff --git a/go/vt/vttablet/grpcqueryservice/server.go b/go/vt/vttablet/grpcqueryservice/server.go index 15f78172275..d48037f26a9 100644 --- a/go/vt/vttablet/grpcqueryservice/server.go +++ b/go/vt/vttablet/grpcqueryservice/server.go @@ -331,21 +331,6 @@ func (q *query) StreamHealth(request *querypb.StreamHealthRequest, stream querys return vterrors.ToGRPC(err) } -// UpdateStream is part of the queryservice.QueryServer interface -func (q *query) UpdateStream(request *querypb.UpdateStreamRequest, stream queryservicepb.Query_UpdateStreamServer) (err error) { - defer q.server.HandlePanic(&err) - ctx := callerid.NewContext(callinfo.GRPCCallInfo(stream.Context()), - request.EffectiveCallerId, - request.ImmediateCallerId, - ) - err = q.server.UpdateStream(ctx, request.Target, request.Position, request.Timestamp, func(reply *querypb.StreamEvent) error { - return stream.Send(&querypb.UpdateStreamResponse{ - Event: reply, - }) - }) - return vterrors.ToGRPC(err) -} - // VStream is part of the queryservice.QueryServer interface func (q *query) VStream(request *binlogdatapb.VStreamRequest, stream queryservicepb.Query_VStreamServer) (err error) { defer q.server.HandlePanic(&err) diff --git a/go/vt/vttablet/grpctabletconn/conn.go b/go/vt/vttablet/grpctabletconn/conn.go index baffd9b7c31..478b0c0ff5f 100644 --- a/go/vt/vttablet/grpctabletconn/conn.go +++ b/go/vt/vttablet/grpctabletconn/conn.go @@ -593,49 +593,6 @@ func (conn *gRPCQueryClient) StreamHealth(ctx context.Context, callback func(*qu } } -// UpdateStream starts a streaming query to VTTablet. -func (conn *gRPCQueryClient) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error { - // Please see comments in StreamExecute to see how this works. - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - stream, err := func() (queryservicepb.Query_UpdateStreamClient, error) { - conn.mu.RLock() - defer conn.mu.RUnlock() - if conn.cc == nil { - return nil, tabletconn.ConnClosed - } - - req := &querypb.UpdateStreamRequest{ - Target: target, - EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), - ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), - Position: position, - Timestamp: timestamp, - } - stream, err := conn.c.UpdateStream(ctx, req) - if err != nil { - return nil, tabletconn.ErrorFromGRPC(err) - } - return stream, nil - }() - if err != nil { - return err - } - for { - r, err := stream.Recv() - if err != nil { - return tabletconn.ErrorFromGRPC(err) - } - if err := callback(r.Event); err != nil { - if err == nil || err == io.EOF { - return nil - } - return err - } - } -} - // VStream starts a VReplication stream. func (conn *gRPCQueryClient) VStream(ctx context.Context, target *querypb.Target, position string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { stream, err := func() (queryservicepb.Query_VStreamClient, error) { diff --git a/go/vt/vttablet/queryservice/queryservice.go b/go/vt/vttablet/queryservice/queryservice.go index 37366ca4167..574d5b2d2db 100644 --- a/go/vt/vttablet/queryservice/queryservice.go +++ b/go/vt/vttablet/queryservice/queryservice.go @@ -92,9 +92,6 @@ type QueryService interface { MessageStream(ctx context.Context, target *querypb.Target, name string, callback func(*sqltypes.Result) error) error MessageAck(ctx context.Context, target *querypb.Target, name string, ids []*querypb.Value) (count int64, err error) - // UpdateStream streams updates from the provided position or timestamp. - UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error - // VStream streams VReplication events based on the specified filter. VStream(ctx context.Context, target *querypb.Target, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error diff --git a/go/vt/vttablet/queryservice/wrapped.go b/go/vt/vttablet/queryservice/wrapped.go index 27d47ffc49d..185046dd9df 100644 --- a/go/vt/vttablet/queryservice/wrapped.go +++ b/go/vt/vttablet/queryservice/wrapped.go @@ -235,13 +235,6 @@ func (ws *wrappedService) MessageAck(ctx context.Context, target *querypb.Target return count, err } -func (ws *wrappedService) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error { - return ws.wrapper(ctx, target, ws.impl, "UpdateStream", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (bool, error) { - innerErr := conn.UpdateStream(ctx, target, position, timestamp, callback) - return canRetry(ctx, innerErr), innerErr - }) -} - func (ws *wrappedService) VStream(ctx context.Context, target *querypb.Target, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { return ws.wrapper(ctx, target, ws.impl, "VStream", false, func(ctx context.Context, target *querypb.Target, conn QueryService) (bool, error) { innerErr := conn.VStream(ctx, target, startPos, filter, send) diff --git a/go/vt/vttablet/sandboxconn/sandboxconn.go b/go/vt/vttablet/sandboxconn/sandboxconn.go index add0cb97baa..1f05142e698 100644 --- a/go/vt/vttablet/sandboxconn/sandboxconn.go +++ b/go/vt/vttablet/sandboxconn/sandboxconn.go @@ -332,11 +332,6 @@ func (sbc *SandboxConn) StreamHealth(ctx context.Context, callback func(*querypb return fmt.Errorf("not implemented in test") } -// UpdateStream is part of the QueryService interface. -func (sbc *SandboxConn) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error { - return fmt.Errorf("not implemented in test") -} - // ExpectVStreamStartPos makes the conn verify that that the next vstream request has the right startPos. func (sbc *SandboxConn) ExpectVStreamStartPos(startPos string) { sbc.StartPos = startPos diff --git a/go/vt/vttablet/tabletconntest/fakequeryservice.go b/go/vt/vttablet/tabletconntest/fakequeryservice.go index ba8a61dddc6..76a54fde386 100644 --- a/go/vt/vttablet/tabletconntest/fakequeryservice.go +++ b/go/vt/vttablet/tabletconntest/fakequeryservice.go @@ -48,7 +48,6 @@ type FakeQueryService struct { // these fields are used to simulate and synchronize on panics Panics bool StreamExecutePanicsEarly bool - UpdateStreamPanicsEarly bool PanicWait chan struct{} // ExpectedTransactionID is what transactionID to expect for Execute @@ -706,77 +705,6 @@ func (f *FakeQueryService) StreamHealth(ctx context.Context, callback func(*quer return nil } -const ( - // UpdateStreamPosition is a test update stream position. - UpdateStreamPosition = "update stream position" - - // UpdateStreamTimestamp is a test update stream timestamp. - UpdateStreamTimestamp = 123654 -) - -// UpdateStreamStreamEvent1 is a test update stream event. -var UpdateStreamStreamEvent1 = querypb.StreamEvent{ - Statements: []*querypb.StreamEvent_Statement{ - { - Category: querypb.StreamEvent_Statement_DML, - TableName: "table1", - }, - }, - EventToken: &querypb.EventToken{ - Timestamp: 789654, - Shard: "shard1", - Position: "streaming position 1", - }, -} - -// UpdateStreamStreamEvent2 is a test update stream event. -var UpdateStreamStreamEvent2 = querypb.StreamEvent{ - Statements: []*querypb.StreamEvent_Statement{ - { - Category: querypb.StreamEvent_Statement_DML, - TableName: "table2", - }, - }, - EventToken: &querypb.EventToken{ - Timestamp: 789655, - Shard: "shard1", - Position: "streaming position 2", - }, -} - -// UpdateStream is part of the queryservice.QueryService interface -func (f *FakeQueryService) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error { - if f.Panics && f.UpdateStreamPanicsEarly { - panic(fmt.Errorf("test-triggered panic early")) - } - if position != UpdateStreamPosition { - f.t.Errorf("invalid UpdateStream.position: got %v expected %v", position, UpdateStreamPosition) - } - if timestamp != UpdateStreamTimestamp { - f.t.Errorf("invalid UpdateStream.timestamp: got %v expected %v", timestamp, UpdateStreamTimestamp) - } - f.checkTargetCallerID(ctx, "UpdateStream", target) - if err := callback(&UpdateStreamStreamEvent1); err != nil { - f.t.Errorf("callback1 failed: %v", err) - } - if f.Panics && !f.UpdateStreamPanicsEarly { - // wait until the client gets the response, then panics - <-f.PanicWait - panic(fmt.Errorf("test-triggered panic late")) - } - if f.HasError { - // wait until the client has the response, since all - // streaming implementation may not send previous - // messages if an error has been triggered. - <-f.ErrorWait - return f.TabletError - } - if err := callback(&UpdateStreamStreamEvent2); err != nil { - f.t.Errorf("callback2 failed: %v", err) - } - return nil -} - // VStream is part of the queryservice.QueryService interface func (f *FakeQueryService) VStream(ctx context.Context, target *querypb.Target, position string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { panic("not implemented") diff --git a/go/vt/vttablet/tabletconntest/tabletconntest.go b/go/vt/vttablet/tabletconntest/tabletconntest.go index 2da1b5028ca..6c9743f7b00 100644 --- a/go/vt/vttablet/tabletconntest/tabletconntest.go +++ b/go/vt/vttablet/tabletconntest/tabletconntest.go @@ -770,97 +770,6 @@ func testStreamHealthPanics(t *testing.T, conn queryservice.QueryService, f *Fak }) } -func testUpdateStream(t *testing.T, conn queryservice.QueryService, f *FakeQueryService) { - t.Log("testUpdateStream") - ctx := context.Background() - ctx = callerid.NewContext(ctx, TestCallerID, TestVTGateCallerID) - i := 0 - err := conn.UpdateStream(ctx, TestTarget, UpdateStreamPosition, UpdateStreamTimestamp, func(qr *querypb.StreamEvent) error { - switch i { - case 0: - if !proto.Equal(qr, &UpdateStreamStreamEvent1) { - t.Errorf("Unexpected result1 from UpdateStream: got %v wanted %v", qr, UpdateStreamStreamEvent1) - } - case 1: - if !proto.Equal(qr, &UpdateStreamStreamEvent2) { - t.Errorf("Unexpected result2 from UpdateStream: got %v wanted %v", qr, UpdateStreamStreamEvent2) - } - default: - t.Fatal("callback should not be called any more") - } - i++ - return nil - }) - if err != nil { - t.Fatalf("UpdateStream failed: %v", err) - } -} - -func testUpdateStreamError(t *testing.T, conn queryservice.QueryService, f *FakeQueryService) { - t.Log("testUpdateStreamError") - f.HasError = true - testErrorHelper(t, f, "UpdateStream", func(ctx context.Context) error { - f.ErrorWait = make(chan struct{}) - ctx = callerid.NewContext(ctx, TestCallerID, TestVTGateCallerID) - return conn.UpdateStream(ctx, TestTarget, UpdateStreamPosition, UpdateStreamTimestamp, func(qr *querypb.StreamEvent) error { - // For some errors, the call can be retried. - select { - case <-f.ErrorWait: - return nil - default: - } - if !proto.Equal(qr, &UpdateStreamStreamEvent1) { - t.Errorf("Unexpected result1 from UpdateStream: got %v wanted %v", qr, UpdateStreamStreamEvent1) - } - // signal to the server that the first result has been received - close(f.ErrorWait) - return nil - }) - }) - f.HasError = false -} - -func testUpdateStreamPanics(t *testing.T, conn queryservice.QueryService, f *FakeQueryService) { - t.Log("testUpdateStreamPanics") - // early panic is before sending the Fields, that is returned - // by the UpdateStream call itself, or as the first error - // by ErrFunc - f.UpdateStreamPanicsEarly = true - testPanicHelper(t, f, "UpdateStream.Early", func(ctx context.Context) error { - ctx = callerid.NewContext(ctx, TestCallerID, TestVTGateCallerID) - return conn.UpdateStream(ctx, TestTarget, UpdateStreamPosition, UpdateStreamTimestamp, func(qr *querypb.StreamEvent) error { - return nil - }) - }) - - // late panic is after sending Fields - f.UpdateStreamPanicsEarly = false - testPanicHelper(t, f, "UpdateStream.Late", func(ctx context.Context) error { - f.PanicWait = make(chan struct{}) - ctx = callerid.NewContext(ctx, TestCallerID, TestVTGateCallerID) - i := 0 - return conn.UpdateStream(ctx, TestTarget, UpdateStreamPosition, UpdateStreamTimestamp, func(qr *querypb.StreamEvent) error { - // For some errors, the call can be retried. - select { - case <-f.PanicWait: - return nil - default: - } - switch i { - case 0: - if !proto.Equal(qr, &UpdateStreamStreamEvent1) { - t.Errorf("Unexpected result1 from UpdateStream: got %v wanted %v", qr, UpdateStreamStreamEvent1) - } - close(f.PanicWait) - default: - t.Fatal("callback should not be called any more") - } - i++ - return nil - }) - }) -} - // TestSuite runs all the tests. // If fake.TestingGateway is set, we only test the calls that can go through // a gateway. @@ -885,7 +794,6 @@ func TestSuite(t *testing.T, protocol string, tablet *topodatapb.Tablet, fake *F testBeginExecuteBatch, testMessageStream, testMessageAck, - testUpdateStream, // error test cases testBeginError, @@ -908,7 +816,6 @@ func TestSuite(t *testing.T, protocol string, tablet *topodatapb.Tablet, fake *F testBeginExecuteBatchErrorInExecuteBatch, testMessageStreamError, testMessageAckError, - testUpdateStreamError, // panic test cases testBeginPanics, @@ -929,7 +836,6 @@ func TestSuite(t *testing.T, protocol string, tablet *topodatapb.Tablet, fake *F testBeginExecuteBatchPanics, testMessageStreamPanics, testMessageAckPanics, - testUpdateStreamPanics, } if !fake.TestingGateway { diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index d54f0b70acd..9bc79c1dd82 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -38,7 +38,6 @@ import ( "vitess.io/vitess/go/sync2" "vitess.io/vitess/go/tb" "vitess.io/vitess/go/trace" - "vitess.io/vitess/go/vt/binlog" "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/dbconnpool" @@ -161,16 +160,15 @@ type TabletServer struct { // The following variables should only be accessed within // the context of a startRequest-endRequest. - se *schema.Engine - qe *QueryEngine - te *TxEngine - teCtrl TxPoolController - hw *heartbeat.Writer - hr *heartbeat.Reader - watcher *ReplicationWatcher - vstreamer *vstreamer.Engine - messager *messager.Engine - updateStreamList *binlog.StreamList + se *schema.Engine + qe *QueryEngine + te *TxEngine + teCtrl TxPoolController + hw *heartbeat.Writer + hr *heartbeat.Reader + watcher *ReplicationWatcher + vstreamer *vstreamer.Engine + messager *messager.Engine // checkMySQLThrottler is used to throttle the number of // requests sent to CheckMySQL. @@ -273,7 +271,6 @@ func NewTabletServer(config tabletenv.TabletConfig, topoServer *topo.Server, ali tsv.hr = heartbeat.NewReader(tsv, config) tsv.txThrottler = txthrottler.CreateTxThrottlerFromTabletConfig(topoServer) tsv.watcher = NewReplicationWatcher(tsv.se, config) - tsv.updateStreamList = &binlog.StreamList{} // FIXME(alainjobart) could we move this to the Register method below? // So that vtcombo doesn't even call it once, on the first tablet. // And we can remove the tsOnce variable. @@ -549,7 +546,6 @@ func (tsv *TabletServer) fullStart() (err error) { return err } tsv.hr.Init(tsv.target) - tsv.updateStreamList.Init() tsv.vstreamer.Open(tsv.target.Keyspace, tsv.alias.Cell) return tsv.serveNewType() } @@ -626,7 +622,6 @@ func (tsv *TabletServer) waitForShutdown() { tsv.messager.Close() tsv.teCtrl.StopGently() tsv.qe.streamQList.TerminateAll() - tsv.updateStreamList.Stop() tsv.watcher.Close() tsv.requests.Wait() tsv.txThrottler.Close() @@ -641,7 +636,6 @@ func (tsv *TabletServer) closeAll() { tsv.hw.Close() tsv.teCtrl.StopGently() tsv.watcher.Close() - tsv.updateStreamList.Stop() tsv.qe.Close() tsv.se.Close() tsv.txThrottler.Close() @@ -1773,47 +1767,6 @@ func (tsv *TabletServer) TopoServer() *topo.Server { return tsv.topoServer } -// UpdateStream streams binlog events. -func (tsv *TabletServer) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64, callback func(*querypb.StreamEvent) error) error { - // Parse the position if needed. - var p mysql.Position - var err error - if timestamp == 0 { - if position != "" { - p, err = mysql.DecodePosition(position) - if err != nil { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot parse position: %v", err) - } - } - } else if position != "" { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "at most one of position and timestamp should be specified") - } - - // Validate proper target is used. - if err = tsv.startRequest(ctx, target, false /* isBegin */, false /* allowOnShutdown */); err != nil { - return err - } - defer tsv.endRequest(false) - - s := binlog.NewEventStreamer(tsv.dbconfigs.DbaWithDB(), tsv.se, p, timestamp, callback) - - // Create a cancelable wrapping context. - streamCtx, streamCancel := context.WithCancel(ctx) - i := tsv.updateStreamList.Add(streamCancel) - defer tsv.updateStreamList.Delete(i) - - // And stream with it. - err = s.Stream(streamCtx) - switch err { - case binlog.ErrBinlogUnavailable: - return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%v", err) - case nil, io.EOF: - return nil - default: - return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "%v", err) - } -} - // HandlePanic is part of the queryservice.QueryService interface func (tsv *TabletServer) HandlePanic(err *error) { if x := recover(); x != nil { diff --git a/proto/query.proto b/proto/query.proto index 3a5aaf9cf90..4839fc31f39 100644 --- a/proto/query.proto +++ b/proto/query.proto @@ -799,28 +799,6 @@ message StreamHealthResponse { topodata.TabletAlias tablet_alias = 5; } -// UpdateStreamRequest is the payload for UpdateStream. At most one of -// position and timestamp can be set. If neither is set, we will start -// streaming from the current binlog position. -message UpdateStreamRequest { - vtrpc.CallerID effective_caller_id = 1; - VTGateCallerID immediate_caller_id = 2; - Target target = 3; - - // If position is set, we will start the streaming from that replication - // position. Incompatible with timestamp. - string position = 4; - - // If timestamp is set, we will start the streaming from the first - // event in the binlogs that have that timestamp. Incompatible with position. - int64 timestamp = 5; -} - -// UpdateStreamResponse is returned by UpdateStream -message UpdateStreamResponse { - StreamEvent event = 1; -} - // TransactionState represents the state of a distributed transaction. enum TransactionState { UNKNOWN = 0; diff --git a/proto/queryservice.proto b/proto/queryservice.proto index 9533a9dcd81..a1ccabdfe71 100644 --- a/proto/queryservice.proto +++ b/proto/queryservice.proto @@ -89,9 +89,6 @@ service Query { // current health of the tablet on a regular basis. rpc StreamHealth(query.StreamHealthRequest) returns (stream query.StreamHealthResponse) {}; - // UpdateStream asks the server to return a stream of the updates that have been applied to its database. - rpc UpdateStream(query.UpdateStreamRequest) returns (stream query.UpdateStreamResponse) {}; - // VStream streams vreplication events. rpc VStream(binlogdata.VStreamRequest) returns (stream binlogdata.VStreamResponse) {}; diff --git a/proto/vtgate.proto b/proto/vtgate.proto index eb2ecf67a20..870ae25835f 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -753,46 +753,3 @@ message VStreamRequest { message VStreamResponse { repeated binlogdata.VEvent events = 1; } - -// UpdateStreamRequest is the payload to UpdateStream. -message UpdateStreamRequest { - // caller_id identifies the caller. This is the effective caller ID, - // set by the application to further identify the caller. - vtrpc.CallerID caller_id = 1; - - // keyspace to target the query to. - string keyspace = 2; - - // shard to target the query to, for unsharded keyspaces. - string shard = 3; - - // KeyRange to target the query to, for sharded keyspaces. - topodata.KeyRange key_range = 4; - - // tablet_type is the type of tablets that this request is targeted to. - topodata.TabletType tablet_type = 5; - - // timestamp is the timestamp to start the stream from. It is - // unused is event is set, and we are only streaming from the shard - // described by event.shard. - int64 timestamp = 6; - - // event is the event to start the stream from. - // Note it is only used if we are streaming from exactly the same shard - // as this event was coming from. Otherwise we can't use this event, - // and will use the timestamp as a starting point. - query.EventToken event = 7; -} - -// UpdateStreamResponse is streamed by UpdateStream. -message UpdateStreamResponse { - // event is one event from the stream. - query.StreamEvent event = 1; - - // resume_timestamp is the timestamp to resume streaming from if the - // client is interrupted. If the Update Stream only goes to one - // shard, this is equal to event.timestamp. If the Update Stream - // goes to multiple shards and aggregates, this is the minimum value - // of the current timestamp for all shards. - int64 resume_timestamp = 2; -} diff --git a/proto/vtgateservice.proto b/proto/vtgateservice.proto index 121aaa4b36d..22ea1e9e7c0 100644 --- a/proto/vtgateservice.proto +++ b/proto/vtgateservice.proto @@ -126,8 +126,4 @@ service Vitess { // VStream streams binlog events from the requested sources. rpc VStream(vtgate.VStreamRequest) returns (stream vtgate.VStreamResponse) {}; - - // UpdateStream asks the server for a stream of StreamEvent objects. - // API group: Update Stream - rpc UpdateStream(vtgate.UpdateStreamRequest) returns (stream vtgate.UpdateStreamResponse) {}; } From 481ff933ab3ea5c20b482ad2fc34db199c2c215a Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 10 Mar 2020 16:15:14 -0700 Subject: [PATCH 298/825] tabletserver: remove TxPoolController abstraction It was not necessary, and was obfuscating the fact that it's wrapping TxEngine. Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/tabletserver.go | 60 ++++----------------- 1 file changed, 9 insertions(+), 51 deletions(-) diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 9bc79c1dd82..259d055136d 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -163,7 +163,6 @@ type TabletServer struct { se *schema.Engine qe *QueryEngine te *TxEngine - teCtrl TxPoolController hw *heartbeat.Writer hr *heartbeat.Reader watcher *ReplicationWatcher @@ -207,46 +206,6 @@ func NewServer(topoServer *topo.Server, alias topodatapb.TabletAlias) *TabletSer return NewTabletServer(tabletenv.Config, topoServer, alias) } -// TxPoolController is how the tablet server interacts with the tx-pool. -// It is responsible for keeping it's own state - knowing when different types -// of transactions are allowed, and how to do state transitions. -type TxPoolController interface { - // Stop will stop accepting any new transactions. Transactions are immediately aborted. - Stop() error - - // Will start accepting all transactions. If transitioning from RO mode, transactions - // might need to be rolled back before new transactions can be accepts. - AcceptReadWrite() error - - // Will start accepting read-only transactions, but not full read and write transactions. - // If the engine is currently accepting full read and write transactions, they need to - // given a chance to clean up before they are forcefully rolled back. - AcceptReadOnly() error - - // InitDBConfig must be called before Init. - InitDBConfig(dbcfgs *dbconfigs.DBConfigs) - - // Init must be called once when vttablet starts for setting - // up the metadata tables. - Init() error - - // StopGently will change the state to NotServing but first wait for transactions to wrap up - StopGently() - - // Begin begins a transaction, and returns the associated transaction id and the - // statement(s) used to execute the begin (if any). - // - // Subsequent statements can access the connection through the transaction id. - Begin(ctx context.Context, options *querypb.ExecuteOptions) (int64, string, error) - - // Commit commits the specified transaction, returning the statement used to execute - // the commit or "" in autocommit settings. - Commit(ctx context.Context, transactionID int64) (string, error) - - // Rollback rolls back the specified transaction. - Rollback(ctx context.Context, transactionID int64) error -} - var tsOnce sync.Once var srvTopoServer srvtopo.Server @@ -266,7 +225,6 @@ func NewTabletServer(config tabletenv.TabletConfig, topoServer *topo.Server, ali tsv.se = schema.NewEngine(tsv, config) tsv.qe = NewQueryEngine(tsv, tsv.se, config) tsv.te = NewTxEngine(tsv, config) - tsv.teCtrl = tsv.te tsv.hw = heartbeat.NewWriter(tsv, alias, config) tsv.hr = heartbeat.NewReader(tsv, config) tsv.txThrottler = txthrottler.CreateTxThrottlerFromTabletConfig(topoServer) @@ -378,7 +336,7 @@ func (tsv *TabletServer) InitDBConfig(target querypb.Target, dbcfgs *dbconfigs.D tsv.se.InitDBConfig(tsv.dbconfigs.DbaWithDB()) tsv.qe.InitDBConfig(tsv.dbconfigs) - tsv.teCtrl.InitDBConfig(tsv.dbconfigs) + tsv.te.InitDBConfig(tsv.dbconfigs) tsv.hw.InitDBConfig(tsv.dbconfigs) tsv.hr.InitDBConfig(tsv.dbconfigs) tsv.watcher.InitDBConfig(tsv.dbconfigs) @@ -539,7 +497,7 @@ func (tsv *TabletServer) fullStart() (err error) { if err := tsv.qe.Open(); err != nil { return err } - if err := tsv.teCtrl.Init(); err != nil { + if err := tsv.te.Init(); err != nil { return err } if err := tsv.hw.Init(tsv.target); err != nil { @@ -556,7 +514,7 @@ func (tsv *TabletServer) serveNewType() (err error) { // transactional requests are not allowed. So, we can // be sure that the tx pool won't change after the wait. if tsv.target.TabletType == topodatapb.TabletType_MASTER { - tsv.teCtrl.AcceptReadWrite() + tsv.te.AcceptReadWrite() if err := tsv.txThrottler.Open(tsv.target.Keyspace, tsv.target.Shard); err != nil { return err } @@ -565,7 +523,7 @@ func (tsv *TabletServer) serveNewType() (err error) { tsv.hr.Close() tsv.hw.Open() } else { - tsv.teCtrl.AcceptReadOnly() + tsv.te.AcceptReadOnly() tsv.messager.Close() tsv.hr.Open() tsv.hw.Close() @@ -620,7 +578,7 @@ func (tsv *TabletServer) waitForShutdown() { // will be allowed. They will enable the conclusion of outstanding // transactions. tsv.messager.Close() - tsv.teCtrl.StopGently() + tsv.te.StopGently() tsv.qe.streamQList.TerminateAll() tsv.watcher.Close() tsv.requests.Wait() @@ -634,7 +592,7 @@ func (tsv *TabletServer) closeAll() { tsv.vstreamer.Close() tsv.hr.Close() tsv.hw.Close() - tsv.teCtrl.StopGently() + tsv.te.StopGently() tsv.watcher.Close() tsv.qe.Close() tsv.se.Close() @@ -766,7 +724,7 @@ func (tsv *TabletServer) Begin(ctx context.Context, target *querypb.Target, opti return vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "Transaction throttled") } var beginSQL string - transactionID, beginSQL, err = tsv.teCtrl.Begin(ctx, options) + transactionID, beginSQL, err = tsv.te.Begin(ctx, options) logStats.TransactionID = transactionID // Record the actual statements that were executed in the logStats. @@ -796,7 +754,7 @@ func (tsv *TabletServer) Commit(ctx context.Context, target *querypb.Target, tra logStats.TransactionID = transactionID var commitSQL string - commitSQL, err = tsv.teCtrl.Commit(ctx, transactionID) + commitSQL, err = tsv.te.Commit(ctx, transactionID) // If nothing was actually executed, don't count the operation in // the tablet metrics, and clear out the logStats Method so that @@ -820,7 +778,7 @@ func (tsv *TabletServer) Rollback(ctx context.Context, target *querypb.Target, t func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tabletenv.QueryStats.Record("ROLLBACK", time.Now()) logStats.TransactionID = transactionID - return tsv.teCtrl.Rollback(ctx, transactionID) + return tsv.te.Rollback(ctx, transactionID) }, ) } From 1d66aa041c1103e79f05a55b6fbc4dfdd34930dd Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 10 Mar 2020 19:01:20 -0700 Subject: [PATCH 299/825] deprecation: delete unused event token code There was an aspiration that applications will use an event token to validate how fresh a replica read was. The feature was neither very usable nor used by anyone. This has now been deleted. Signed-off-by: Sugu Sougoumarane --- go/cmd/vtgateclienttest/goclienttest/echo.go | 25 +- go/cmd/vtgateclienttest/services/echo.go | 10 - go/sqltypes/proto3.go | 3 - go/sqltypes/proto3_test.go | 70 --- go/sqltypes/result.go | 63 +- go/sqltypes/result_test.go | 24 - go/vt/proto/query/query.pb.go | 569 ++++++++---------- go/vt/vtgate/engine/ordered_aggregate.go | 1 - go/vt/vtgate/grpcvtgateconn/suite_test.go | 22 +- .../tabletconntest/fakequeryservice.go | 24 +- .../tabletmanager/vreplication/engine_test.go | 4 +- go/vt/vttablet/tabletserver/query_executor.go | 6 +- .../tabletserver/replication_watcher.go | 161 +---- go/vt/vttablet/tabletserver/tabletserver.go | 9 +- proto/query.proto | 30 +- 15 files changed, 295 insertions(+), 726 deletions(-) diff --git a/go/cmd/vtgateclienttest/goclienttest/echo.go b/go/cmd/vtgateclienttest/goclienttest/echo.go index 31236ce911d..0e8f4c1d89d 100644 --- a/go/cmd/vtgateclienttest/goclienttest/echo.go +++ b/go/cmd/vtgateclienttest/goclienttest/echo.go @@ -85,20 +85,10 @@ var ( callerID = callerid.NewEffectiveCallerID("test_principal", "test_component", "test_subcomponent") callerIDEcho = "principal:\"test_principal\" component:\"test_component\" subcomponent:\"test_subcomponent\" " - eventToken = &querypb.EventToken{ - Timestamp: 876543, - Shard: shards[0], - Position: "test_position", - } - eventTokenEcho = "timestamp:876543 shard:\"-80\" position:\"test_position\" " - options = &querypb.ExecuteOptions{ - IncludedFields: querypb.ExecuteOptions_TYPE_ONLY, - IncludeEventToken: true, - CompareEventToken: eventToken, + IncludedFields: querypb.ExecuteOptions_TYPE_ONLY, } - optionsEcho = "include_event_token:true compare_event_token:<" + eventTokenEcho + "> included_fields:TYPE_ONLY " - extrasEcho = "event_token:<" + eventTokenEcho + "> fresher:true " + optionsEcho = "included_fields:TYPE_ONLY " ) // testEcho exercises the test cases provided by the "echo" service. @@ -130,7 +120,6 @@ func testEchoExecute(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgatec "bindVars": bindVarsEcho, "tabletType": tabletTypeEcho, "options": optionsEcho, - "extras": extrasEcho, }) qr, err = conn.ExecuteKeyspaceIds(ctx, echoPrefix+query, keyspace, keyspaceIDs, bindVars, tabletType, options) @@ -142,7 +131,6 @@ func testEchoExecute(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgatec "bindVars": bindVarsEcho, "tabletType": tabletTypeEcho, "options": optionsEcho, - "extras": extrasEcho, }) qr, err = conn.ExecuteKeyRanges(ctx, echoPrefix+query, keyspace, keyRanges, bindVars, tabletType, options) @@ -154,7 +142,6 @@ func testEchoExecute(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgatec "bindVars": bindVarsEcho, "tabletType": tabletTypeEcho, "options": optionsEcho, - "extras": extrasEcho, }) qr, err = conn.ExecuteEntityIds(ctx, echoPrefix+query, keyspace, "column1", entityKeyspaceIDs, bindVars, tabletType, options) @@ -167,7 +154,6 @@ func testEchoExecute(t *testing.T, conn *vtgateconn.VTGateConn, session *vtgatec "bindVars": bindVarsEcho, "tabletType": tabletTypeEcho, "options": optionsEcho, - "extras": extrasEcho, }) var qrs []sqltypes.Result @@ -418,13 +404,6 @@ func checkEcho(t *testing.T, name string, qr *sqltypes.Result, err error, want m } got := getEcho(qr) for k, v := range want { - if k == "extras" { - gotExtras := qr.Extras.String() - if gotExtras != v { - t.Errorf("%v: extras = \n%q, want \n%q", name, gotExtras, v) - } - continue - } if got[k].ToString() != v { t.Errorf("%v: %v = \n%q, want \n%q", name, k, got[k], v) } diff --git a/go/cmd/vtgateclienttest/services/echo.go b/go/cmd/vtgateclienttest/services/echo.go index f6725c994a6..f200d07bdab 100644 --- a/go/cmd/vtgateclienttest/services/echo.go +++ b/go/cmd/vtgateclienttest/services/echo.go @@ -95,16 +95,6 @@ func echoQueryResult(vals map[string]interface{}) *sqltypes.Result { } qr.Rows = [][]sqltypes.Value{row} - if options, ok := vals["options"]; ok { - o := options.(*querypb.ExecuteOptions) - if o != nil && o.CompareEventToken != nil { - qr.Extras = &querypb.ResultExtras{ - Fresher: true, - EventToken: o.CompareEventToken, - } - } - } - return qr } diff --git a/go/sqltypes/proto3.go b/go/sqltypes/proto3.go index c42cc81c3ce..284e27fadbd 100644 --- a/go/sqltypes/proto3.go +++ b/go/sqltypes/proto3.go @@ -90,7 +90,6 @@ func ResultToProto3(qr *Result) *querypb.QueryResult { RowsAffected: qr.RowsAffected, InsertId: qr.InsertID, Rows: RowsToProto3(qr.Rows), - Extras: qr.Extras, } } @@ -105,7 +104,6 @@ func Proto3ToResult(qr *querypb.QueryResult) *Result { RowsAffected: qr.RowsAffected, InsertID: qr.InsertId, Rows: proto3ToRows(qr.Fields, qr.Rows), - Extras: qr.Extras, } } @@ -121,7 +119,6 @@ func CustomProto3ToResult(fields []*querypb.Field, qr *querypb.QueryResult) *Res RowsAffected: qr.RowsAffected, InsertID: qr.InsertId, Rows: proto3ToRows(fields, qr.Rows), - Extras: qr.Extras, } } diff --git a/go/sqltypes/proto3_test.go b/go/sqltypes/proto3_test.go index 0d55fff019d..5cb88ebe353 100644 --- a/go/sqltypes/proto3_test.go +++ b/go/sqltypes/proto3_test.go @@ -49,13 +49,6 @@ func TestResult(t *testing.T) { NULL, NULL, }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard0", - Position: "position0", - }, - }, } p3Result := &querypb.QueryResult{ Fields: fields, @@ -68,13 +61,6 @@ func TestResult(t *testing.T) { Lengths: []int64{2, -1, -1}, Values: []byte("bb"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard0", - Position: "position0", - }, - }, } p3converted := ResultToProto3(sqlResult) if !proto.Equal(p3converted, p3Result) { @@ -125,13 +111,6 @@ func TestResults(t *testing.T) { TestValue(Int64, "1"), TestValue(Float64, "2"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard0", - Position: "position0", - }, - }, }, { Fields: fields2, InsertID: 3, @@ -141,13 +120,6 @@ func TestResults(t *testing.T) { TestValue(Int64, "3"), TestValue(Float64, "4"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard1", - Position: "position1", - }, - }, }} p3Results := []*querypb.QueryResult{{ Fields: fields1, @@ -157,13 +129,6 @@ func TestResults(t *testing.T) { Lengths: []int64{2, 1, 1}, Values: []byte("aa12"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard0", - Position: "position0", - }, - }, }, { Fields: fields2, InsertId: 3, @@ -172,13 +137,6 @@ func TestResults(t *testing.T) { Lengths: []int64{2, 1, 1}, Values: []byte("bb34"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard1", - Position: "position1", - }, - }, }} p3converted := ResultsToProto3(sqlResults) if !Proto3ResultsEqual(p3converted, p3Results) { @@ -224,13 +182,6 @@ func TestQueryReponses(t *testing.T) { TestValue(Int64, "1"), TestValue(Float64, "2"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard0", - Position: "position0", - }, - }, }, QueryError: nil, }, { @@ -243,13 +194,6 @@ func TestQueryReponses(t *testing.T) { TestValue(Int64, "3"), TestValue(Float64, "4"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard1", - Position: "position1", - }, - }, }, QueryError: nil, }, { @@ -269,13 +213,6 @@ func TestQueryReponses(t *testing.T) { Lengths: []int64{2, 1, 1}, Values: []byte("aa12"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard0", - Position: "position0", - }, - }, }, }, { Error: nil, @@ -287,13 +224,6 @@ func TestQueryReponses(t *testing.T) { Lengths: []int64{2, 1, 1}, Values: []byte("bb34"), }}, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "shard1", - Position: "position1", - }, - }, }, }, { Error: &vtrpcpb.RPCError{ diff --git a/go/sqltypes/result.go b/go/sqltypes/result.go index 2f308f6d210..b12378b2ebf 100644 --- a/go/sqltypes/result.go +++ b/go/sqltypes/result.go @@ -25,11 +25,10 @@ import ( // Result represents a query result. type Result struct { - Fields []*querypb.Field `json:"fields"` - RowsAffected uint64 `json:"rows_affected"` - InsertID uint64 `json:"insert_id"` - Rows [][]Value `json:"rows"` - Extras *querypb.ResultExtras `json:"extras"` + Fields []*querypb.Field `json:"fields"` + RowsAffected uint64 `json:"rows_affected"` + InsertID uint64 `json:"insert_id"` + Rows [][]Value `json:"rows"` } // ResultStream is an interface for receiving Result. It is used for @@ -74,18 +73,6 @@ func (result *Result) Copy() *Result { out.Rows = append(out.Rows, CopyRow(r)) } } - if result.Extras != nil { - out.Extras = &querypb.ResultExtras{ - Fresher: result.Extras.Fresher, - } - if result.Extras.EventToken != nil { - out.Extras.EventToken = &querypb.EventToken{ - Timestamp: result.Extras.EventToken.Timestamp, - Shard: result.Extras.EventToken.Shard, - Position: result.Extras.EventToken.Position, - } - } - } return out } @@ -118,18 +105,6 @@ func (result *Result) Truncate(l int) *Result { out.Rows = append(out.Rows, r[:l]) } } - if result.Extras != nil { - out.Extras = &querypb.ResultExtras{ - Fresher: result.Extras.Fresher, - } - if result.Extras.EventToken != nil { - out.Extras.EventToken = &querypb.EventToken{ - Timestamp: result.Extras.EventToken.Timestamp, - Shard: result.Extras.EventToken.Shard, - Position: result.Extras.EventToken.Position, - } - } - } return out } @@ -158,12 +133,11 @@ func (result *Result) Equal(other *Result) bool { return false } - // Compare Fields, RowsAffected, InsertID, Rows, Extras. + // Compare Fields, RowsAffected, InsertID, Rows. return FieldsEqual(result.Fields, other.Fields) && result.RowsAffected == other.RowsAffected && result.InsertID == other.InsertID && - reflect.DeepEqual(result.Rows, other.Rows) && - proto.Equal(result.Extras, other.Extras) + reflect.DeepEqual(result.Rows, other.Rows) } // ResultsEqual compares two arrays of Result. @@ -242,30 +216,5 @@ func (result *Result) AppendResult(src *Result) { if src.InsertID != 0 { result.InsertID = src.InsertID } - if len(result.Rows) == 0 { - // we haven't gotten any result yet, just save the new extras. - result.Extras = src.Extras - } else { - // Merge the EventTokens / Fresher flags within Extras. - if src.Extras == nil { - // We didn't get any from innerq. Have to clear any - // we'd have gotten already. - if result.Extras != nil { - result.Extras.EventToken = nil - result.Extras.Fresher = false - } - } else { - // We may have gotten an EventToken from - // innerqr. If we also got one earlier, merge - // it. If we didn't get one earlier, we - // discard the new one. - if result.Extras != nil { - // Note if any of the two is nil, we get nil. - result.Extras.EventToken = EventTokenMinimum(result.Extras.EventToken, src.Extras.EventToken) - - result.Extras.Fresher = result.Extras.Fresher && src.Extras.Fresher - } - } - } result.Rows = append(result.Rows, src.Rows...) } diff --git a/go/sqltypes/result_test.go b/go/sqltypes/result_test.go index 2ddeaa59b34..bf2d9fd87fe 100644 --- a/go/sqltypes/result_test.go +++ b/go/sqltypes/result_test.go @@ -61,14 +61,6 @@ func TestCopy(t *testing.T) { {TestValue(Int64, "2"), MakeTrusted(VarChar, nil)}, {TestValue(Int64, "3"), TestValue(VarChar, "")}, }, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "sh", - Position: "po", - }, - Fresher: true, - }, } out := in.Copy() if !reflect.DeepEqual(out, in) { @@ -90,14 +82,6 @@ func TestTruncate(t *testing.T) { {TestValue(Int64, "2"), MakeTrusted(VarChar, nil)}, {TestValue(Int64, "3"), TestValue(VarChar, "")}, }, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "sh", - Position: "po", - }, - Fresher: true, - }, } out := in.Truncate(0) @@ -117,14 +101,6 @@ func TestTruncate(t *testing.T) { {TestValue(Int64, "2")}, {TestValue(Int64, "3")}, }, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "sh", - Position: "po", - }, - Fresher: true, - }, } if !reflect.DeepEqual(out, want) { t.Errorf("Truncate(1):\n%v, want\n%v", out, want) diff --git a/go/vt/proto/query/query.pb.go b/go/vt/proto/query/query.pb.go index 120dcb99879..45f9128cebd 100644 --- a/go/vt/proto/query/query.pb.go +++ b/go/vt/proto/query/query.pb.go @@ -488,7 +488,7 @@ func (x StreamEvent_Statement_Category) String() string { } func (StreamEvent_Statement_Category) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{12, 0, 0} + return fileDescriptor_5c6ac9b241082464, []int{11, 0, 0} } // Target describes what the client expects the tablet is. @@ -835,12 +835,6 @@ func (m *BoundQuery) GetBindVariables() map[string]*BindVariable { // ExecuteOptions is passed around for all Execute calls. type ExecuteOptions struct { - // If set, we will try to include an EventToken with the responses. - IncludeEventToken bool `protobuf:"varint,2,opt,name=include_event_token,json=includeEventToken,proto3" json:"include_event_token,omitempty"` - // If set, the fresher field may be set as a result comparison to this token. - // This is a shortcut so the application doesn't need to care about - // comparing EventTokens. - CompareEventToken *EventToken `protobuf:"bytes,3,opt,name=compare_event_token,json=compareEventToken,proto3" json:"compare_event_token,omitempty"` // Controls what fields are returned in Field message responses from mysql, i.e. // field name, table name, etc. This is an optimization for high-QPS queries where // the client knows what it's getting @@ -894,20 +888,6 @@ func (m *ExecuteOptions) XXX_DiscardUnknown() { var xxx_messageInfo_ExecuteOptions proto.InternalMessageInfo -func (m *ExecuteOptions) GetIncludeEventToken() bool { - if m != nil { - return m.IncludeEventToken - } - return false -} - -func (m *ExecuteOptions) GetCompareEventToken() *EventToken { - if m != nil { - return m.CompareEventToken - } - return nil -} - func (m *ExecuteOptions) GetIncludedFields() ExecuteOptions_IncludedFields { if m != nil { return m.IncludedFields @@ -1124,59 +1104,6 @@ func (m *Row) GetValues() []byte { return nil } -// ResultExtras contains optional out-of-band information. Usually the -// extras are requested by adding ExecuteOptions flags. -type ResultExtras struct { - // event_token is populated if the include_event_token flag is set - // in ExecuteOptions. - EventToken *EventToken `protobuf:"bytes,1,opt,name=event_token,json=eventToken,proto3" json:"event_token,omitempty"` - // If set, it means the data returned with this result is fresher - // than the compare_token passed in the ExecuteOptions. - Fresher bool `protobuf:"varint,2,opt,name=fresher,proto3" json:"fresher,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ResultExtras) Reset() { *m = ResultExtras{} } -func (m *ResultExtras) String() string { return proto.CompactTextString(m) } -func (*ResultExtras) ProtoMessage() {} -func (*ResultExtras) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{9} -} - -func (m *ResultExtras) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ResultExtras.Unmarshal(m, b) -} -func (m *ResultExtras) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ResultExtras.Marshal(b, m, deterministic) -} -func (m *ResultExtras) XXX_Merge(src proto.Message) { - xxx_messageInfo_ResultExtras.Merge(m, src) -} -func (m *ResultExtras) XXX_Size() int { - return xxx_messageInfo_ResultExtras.Size(m) -} -func (m *ResultExtras) XXX_DiscardUnknown() { - xxx_messageInfo_ResultExtras.DiscardUnknown(m) -} - -var xxx_messageInfo_ResultExtras proto.InternalMessageInfo - -func (m *ResultExtras) GetEventToken() *EventToken { - if m != nil { - return m.EventToken - } - return nil -} - -func (m *ResultExtras) GetFresher() bool { - if m != nil { - return m.Fresher - } - return false -} - // QueryResult is returned by Execute and ExecuteStream. // // As returned by Execute, len(fields) is always equal to len(row) @@ -1187,21 +1114,20 @@ func (m *ResultExtras) GetFresher() bool { // len(QueryResult[0].fields) is always equal to len(row) (for each // row in rows for each QueryResult in QueryResult[1:]). type QueryResult struct { - Fields []*Field `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` - RowsAffected uint64 `protobuf:"varint,2,opt,name=rows_affected,json=rowsAffected,proto3" json:"rows_affected,omitempty"` - InsertId uint64 `protobuf:"varint,3,opt,name=insert_id,json=insertId,proto3" json:"insert_id,omitempty"` - Rows []*Row `protobuf:"bytes,4,rep,name=rows,proto3" json:"rows,omitempty"` - Extras *ResultExtras `protobuf:"bytes,5,opt,name=extras,proto3" json:"extras,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Fields []*Field `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` + RowsAffected uint64 `protobuf:"varint,2,opt,name=rows_affected,json=rowsAffected,proto3" json:"rows_affected,omitempty"` + InsertId uint64 `protobuf:"varint,3,opt,name=insert_id,json=insertId,proto3" json:"insert_id,omitempty"` + Rows []*Row `protobuf:"bytes,4,rep,name=rows,proto3" json:"rows,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *QueryResult) Reset() { *m = QueryResult{} } func (m *QueryResult) String() string { return proto.CompactTextString(m) } func (*QueryResult) ProtoMessage() {} func (*QueryResult) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{10} + return fileDescriptor_5c6ac9b241082464, []int{9} } func (m *QueryResult) XXX_Unmarshal(b []byte) error { @@ -1250,13 +1176,6 @@ func (m *QueryResult) GetRows() []*Row { return nil } -func (m *QueryResult) GetExtras() *ResultExtras { - if m != nil { - return m.Extras - } - return nil -} - // QueryWarning is used to convey out of band query execution warnings // by storing in the vtgate.Session type QueryWarning struct { @@ -1271,7 +1190,7 @@ func (m *QueryWarning) Reset() { *m = QueryWarning{} } func (m *QueryWarning) String() string { return proto.CompactTextString(m) } func (*QueryWarning) ProtoMessage() {} func (*QueryWarning) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{11} + return fileDescriptor_5c6ac9b241082464, []int{10} } func (m *QueryWarning) XXX_Unmarshal(b []byte) error { @@ -1323,7 +1242,7 @@ func (m *StreamEvent) Reset() { *m = StreamEvent{} } func (m *StreamEvent) String() string { return proto.CompactTextString(m) } func (*StreamEvent) ProtoMessage() {} func (*StreamEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{12} + return fileDescriptor_5c6ac9b241082464, []int{11} } func (m *StreamEvent) XXX_Unmarshal(b []byte) error { @@ -1377,7 +1296,7 @@ func (m *StreamEvent_Statement) Reset() { *m = StreamEvent_Statement{} } func (m *StreamEvent_Statement) String() string { return proto.CompactTextString(m) } func (*StreamEvent_Statement) ProtoMessage() {} func (*StreamEvent_Statement) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{12, 0} + return fileDescriptor_5c6ac9b241082464, []int{11, 0} } func (m *StreamEvent_Statement) XXX_Unmarshal(b []byte) error { @@ -1450,7 +1369,7 @@ func (m *ExecuteRequest) Reset() { *m = ExecuteRequest{} } func (m *ExecuteRequest) String() string { return proto.CompactTextString(m) } func (*ExecuteRequest) ProtoMessage() {} func (*ExecuteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{13} + return fileDescriptor_5c6ac9b241082464, []int{12} } func (m *ExecuteRequest) XXX_Unmarshal(b []byte) error { @@ -1525,7 +1444,7 @@ func (m *ExecuteResponse) Reset() { *m = ExecuteResponse{} } func (m *ExecuteResponse) String() string { return proto.CompactTextString(m) } func (*ExecuteResponse) ProtoMessage() {} func (*ExecuteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{14} + return fileDescriptor_5c6ac9b241082464, []int{13} } func (m *ExecuteResponse) XXX_Unmarshal(b []byte) error { @@ -1570,7 +1489,7 @@ func (m *ResultWithError) Reset() { *m = ResultWithError{} } func (m *ResultWithError) String() string { return proto.CompactTextString(m) } func (*ResultWithError) ProtoMessage() {} func (*ResultWithError) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{15} + return fileDescriptor_5c6ac9b241082464, []int{14} } func (m *ResultWithError) XXX_Unmarshal(b []byte) error { @@ -1623,7 +1542,7 @@ func (m *ExecuteBatchRequest) Reset() { *m = ExecuteBatchRequest{} } func (m *ExecuteBatchRequest) String() string { return proto.CompactTextString(m) } func (*ExecuteBatchRequest) ProtoMessage() {} func (*ExecuteBatchRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{16} + return fileDescriptor_5c6ac9b241082464, []int{15} } func (m *ExecuteBatchRequest) XXX_Unmarshal(b []byte) error { @@ -1705,7 +1624,7 @@ func (m *ExecuteBatchResponse) Reset() { *m = ExecuteBatchResponse{} } func (m *ExecuteBatchResponse) String() string { return proto.CompactTextString(m) } func (*ExecuteBatchResponse) ProtoMessage() {} func (*ExecuteBatchResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{17} + return fileDescriptor_5c6ac9b241082464, []int{16} } func (m *ExecuteBatchResponse) XXX_Unmarshal(b []byte) error { @@ -1750,7 +1669,7 @@ func (m *StreamExecuteRequest) Reset() { *m = StreamExecuteRequest{} } func (m *StreamExecuteRequest) String() string { return proto.CompactTextString(m) } func (*StreamExecuteRequest) ProtoMessage() {} func (*StreamExecuteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{18} + return fileDescriptor_5c6ac9b241082464, []int{17} } func (m *StreamExecuteRequest) XXX_Unmarshal(b []byte) error { @@ -1825,7 +1744,7 @@ func (m *StreamExecuteResponse) Reset() { *m = StreamExecuteResponse{} } func (m *StreamExecuteResponse) String() string { return proto.CompactTextString(m) } func (*StreamExecuteResponse) ProtoMessage() {} func (*StreamExecuteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{19} + return fileDescriptor_5c6ac9b241082464, []int{18} } func (m *StreamExecuteResponse) XXX_Unmarshal(b []byte) error { @@ -1868,7 +1787,7 @@ func (m *BeginRequest) Reset() { *m = BeginRequest{} } func (m *BeginRequest) String() string { return proto.CompactTextString(m) } func (*BeginRequest) ProtoMessage() {} func (*BeginRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{20} + return fileDescriptor_5c6ac9b241082464, []int{19} } func (m *BeginRequest) XXX_Unmarshal(b []byte) error { @@ -1929,7 +1848,7 @@ func (m *BeginResponse) Reset() { *m = BeginResponse{} } func (m *BeginResponse) String() string { return proto.CompactTextString(m) } func (*BeginResponse) ProtoMessage() {} func (*BeginResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{21} + return fileDescriptor_5c6ac9b241082464, []int{20} } func (m *BeginResponse) XXX_Unmarshal(b []byte) error { @@ -1972,7 +1891,7 @@ func (m *CommitRequest) Reset() { *m = CommitRequest{} } func (m *CommitRequest) String() string { return proto.CompactTextString(m) } func (*CommitRequest) ProtoMessage() {} func (*CommitRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{22} + return fileDescriptor_5c6ac9b241082464, []int{21} } func (m *CommitRequest) XXX_Unmarshal(b []byte) error { @@ -2032,7 +1951,7 @@ func (m *CommitResponse) Reset() { *m = CommitResponse{} } func (m *CommitResponse) String() string { return proto.CompactTextString(m) } func (*CommitResponse) ProtoMessage() {} func (*CommitResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{23} + return fileDescriptor_5c6ac9b241082464, []int{22} } func (m *CommitResponse) XXX_Unmarshal(b []byte) error { @@ -2068,7 +1987,7 @@ func (m *RollbackRequest) Reset() { *m = RollbackRequest{} } func (m *RollbackRequest) String() string { return proto.CompactTextString(m) } func (*RollbackRequest) ProtoMessage() {} func (*RollbackRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{24} + return fileDescriptor_5c6ac9b241082464, []int{23} } func (m *RollbackRequest) XXX_Unmarshal(b []byte) error { @@ -2128,7 +2047,7 @@ func (m *RollbackResponse) Reset() { *m = RollbackResponse{} } func (m *RollbackResponse) String() string { return proto.CompactTextString(m) } func (*RollbackResponse) ProtoMessage() {} func (*RollbackResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{25} + return fileDescriptor_5c6ac9b241082464, []int{24} } func (m *RollbackResponse) XXX_Unmarshal(b []byte) error { @@ -2165,7 +2084,7 @@ func (m *PrepareRequest) Reset() { *m = PrepareRequest{} } func (m *PrepareRequest) String() string { return proto.CompactTextString(m) } func (*PrepareRequest) ProtoMessage() {} func (*PrepareRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{26} + return fileDescriptor_5c6ac9b241082464, []int{25} } func (m *PrepareRequest) XXX_Unmarshal(b []byte) error { @@ -2232,7 +2151,7 @@ func (m *PrepareResponse) Reset() { *m = PrepareResponse{} } func (m *PrepareResponse) String() string { return proto.CompactTextString(m) } func (*PrepareResponse) ProtoMessage() {} func (*PrepareResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{27} + return fileDescriptor_5c6ac9b241082464, []int{26} } func (m *PrepareResponse) XXX_Unmarshal(b []byte) error { @@ -2268,7 +2187,7 @@ func (m *CommitPreparedRequest) Reset() { *m = CommitPreparedRequest{} } func (m *CommitPreparedRequest) String() string { return proto.CompactTextString(m) } func (*CommitPreparedRequest) ProtoMessage() {} func (*CommitPreparedRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{28} + return fileDescriptor_5c6ac9b241082464, []int{27} } func (m *CommitPreparedRequest) XXX_Unmarshal(b []byte) error { @@ -2328,7 +2247,7 @@ func (m *CommitPreparedResponse) Reset() { *m = CommitPreparedResponse{} func (m *CommitPreparedResponse) String() string { return proto.CompactTextString(m) } func (*CommitPreparedResponse) ProtoMessage() {} func (*CommitPreparedResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{29} + return fileDescriptor_5c6ac9b241082464, []int{28} } func (m *CommitPreparedResponse) XXX_Unmarshal(b []byte) error { @@ -2365,7 +2284,7 @@ func (m *RollbackPreparedRequest) Reset() { *m = RollbackPreparedRequest func (m *RollbackPreparedRequest) String() string { return proto.CompactTextString(m) } func (*RollbackPreparedRequest) ProtoMessage() {} func (*RollbackPreparedRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{30} + return fileDescriptor_5c6ac9b241082464, []int{29} } func (m *RollbackPreparedRequest) XXX_Unmarshal(b []byte) error { @@ -2432,7 +2351,7 @@ func (m *RollbackPreparedResponse) Reset() { *m = RollbackPreparedRespon func (m *RollbackPreparedResponse) String() string { return proto.CompactTextString(m) } func (*RollbackPreparedResponse) ProtoMessage() {} func (*RollbackPreparedResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{31} + return fileDescriptor_5c6ac9b241082464, []int{30} } func (m *RollbackPreparedResponse) XXX_Unmarshal(b []byte) error { @@ -2469,7 +2388,7 @@ func (m *CreateTransactionRequest) Reset() { *m = CreateTransactionReque func (m *CreateTransactionRequest) String() string { return proto.CompactTextString(m) } func (*CreateTransactionRequest) ProtoMessage() {} func (*CreateTransactionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{32} + return fileDescriptor_5c6ac9b241082464, []int{31} } func (m *CreateTransactionRequest) XXX_Unmarshal(b []byte) error { @@ -2536,7 +2455,7 @@ func (m *CreateTransactionResponse) Reset() { *m = CreateTransactionResp func (m *CreateTransactionResponse) String() string { return proto.CompactTextString(m) } func (*CreateTransactionResponse) ProtoMessage() {} func (*CreateTransactionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{33} + return fileDescriptor_5c6ac9b241082464, []int{32} } func (m *CreateTransactionResponse) XXX_Unmarshal(b []byte) error { @@ -2573,7 +2492,7 @@ func (m *StartCommitRequest) Reset() { *m = StartCommitRequest{} } func (m *StartCommitRequest) String() string { return proto.CompactTextString(m) } func (*StartCommitRequest) ProtoMessage() {} func (*StartCommitRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{34} + return fileDescriptor_5c6ac9b241082464, []int{33} } func (m *StartCommitRequest) XXX_Unmarshal(b []byte) error { @@ -2640,7 +2559,7 @@ func (m *StartCommitResponse) Reset() { *m = StartCommitResponse{} } func (m *StartCommitResponse) String() string { return proto.CompactTextString(m) } func (*StartCommitResponse) ProtoMessage() {} func (*StartCommitResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{35} + return fileDescriptor_5c6ac9b241082464, []int{34} } func (m *StartCommitResponse) XXX_Unmarshal(b []byte) error { @@ -2677,7 +2596,7 @@ func (m *SetRollbackRequest) Reset() { *m = SetRollbackRequest{} } func (m *SetRollbackRequest) String() string { return proto.CompactTextString(m) } func (*SetRollbackRequest) ProtoMessage() {} func (*SetRollbackRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{36} + return fileDescriptor_5c6ac9b241082464, []int{35} } func (m *SetRollbackRequest) XXX_Unmarshal(b []byte) error { @@ -2744,7 +2663,7 @@ func (m *SetRollbackResponse) Reset() { *m = SetRollbackResponse{} } func (m *SetRollbackResponse) String() string { return proto.CompactTextString(m) } func (*SetRollbackResponse) ProtoMessage() {} func (*SetRollbackResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{37} + return fileDescriptor_5c6ac9b241082464, []int{36} } func (m *SetRollbackResponse) XXX_Unmarshal(b []byte) error { @@ -2780,7 +2699,7 @@ func (m *ConcludeTransactionRequest) Reset() { *m = ConcludeTransactionR func (m *ConcludeTransactionRequest) String() string { return proto.CompactTextString(m) } func (*ConcludeTransactionRequest) ProtoMessage() {} func (*ConcludeTransactionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{38} + return fileDescriptor_5c6ac9b241082464, []int{37} } func (m *ConcludeTransactionRequest) XXX_Unmarshal(b []byte) error { @@ -2840,7 +2759,7 @@ func (m *ConcludeTransactionResponse) Reset() { *m = ConcludeTransaction func (m *ConcludeTransactionResponse) String() string { return proto.CompactTextString(m) } func (*ConcludeTransactionResponse) ProtoMessage() {} func (*ConcludeTransactionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{39} + return fileDescriptor_5c6ac9b241082464, []int{38} } func (m *ConcludeTransactionResponse) XXX_Unmarshal(b []byte) error { @@ -2876,7 +2795,7 @@ func (m *ReadTransactionRequest) Reset() { *m = ReadTransactionRequest{} func (m *ReadTransactionRequest) String() string { return proto.CompactTextString(m) } func (*ReadTransactionRequest) ProtoMessage() {} func (*ReadTransactionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{40} + return fileDescriptor_5c6ac9b241082464, []int{39} } func (m *ReadTransactionRequest) XXX_Unmarshal(b []byte) error { @@ -2937,7 +2856,7 @@ func (m *ReadTransactionResponse) Reset() { *m = ReadTransactionResponse func (m *ReadTransactionResponse) String() string { return proto.CompactTextString(m) } func (*ReadTransactionResponse) ProtoMessage() {} func (*ReadTransactionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{41} + return fileDescriptor_5c6ac9b241082464, []int{40} } func (m *ReadTransactionResponse) XXX_Unmarshal(b []byte) error { @@ -2981,7 +2900,7 @@ func (m *BeginExecuteRequest) Reset() { *m = BeginExecuteRequest{} } func (m *BeginExecuteRequest) String() string { return proto.CompactTextString(m) } func (*BeginExecuteRequest) ProtoMessage() {} func (*BeginExecuteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{42} + return fileDescriptor_5c6ac9b241082464, []int{41} } func (m *BeginExecuteRequest) XXX_Unmarshal(b []byte) error { @@ -3055,7 +2974,7 @@ func (m *BeginExecuteResponse) Reset() { *m = BeginExecuteResponse{} } func (m *BeginExecuteResponse) String() string { return proto.CompactTextString(m) } func (*BeginExecuteResponse) ProtoMessage() {} func (*BeginExecuteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{43} + return fileDescriptor_5c6ac9b241082464, []int{42} } func (m *BeginExecuteResponse) XXX_Unmarshal(b []byte) error { @@ -3114,7 +3033,7 @@ func (m *BeginExecuteBatchRequest) Reset() { *m = BeginExecuteBatchReque func (m *BeginExecuteBatchRequest) String() string { return proto.CompactTextString(m) } func (*BeginExecuteBatchRequest) ProtoMessage() {} func (*BeginExecuteBatchRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{44} + return fileDescriptor_5c6ac9b241082464, []int{43} } func (m *BeginExecuteBatchRequest) XXX_Unmarshal(b []byte) error { @@ -3195,7 +3114,7 @@ func (m *BeginExecuteBatchResponse) Reset() { *m = BeginExecuteBatchResp func (m *BeginExecuteBatchResponse) String() string { return proto.CompactTextString(m) } func (*BeginExecuteBatchResponse) ProtoMessage() {} func (*BeginExecuteBatchResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{45} + return fileDescriptor_5c6ac9b241082464, []int{44} } func (m *BeginExecuteBatchResponse) XXX_Unmarshal(b []byte) error { @@ -3253,7 +3172,7 @@ func (m *MessageStreamRequest) Reset() { *m = MessageStreamRequest{} } func (m *MessageStreamRequest) String() string { return proto.CompactTextString(m) } func (*MessageStreamRequest) ProtoMessage() {} func (*MessageStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{46} + return fileDescriptor_5c6ac9b241082464, []int{45} } func (m *MessageStreamRequest) XXX_Unmarshal(b []byte) error { @@ -3314,7 +3233,7 @@ func (m *MessageStreamResponse) Reset() { *m = MessageStreamResponse{} } func (m *MessageStreamResponse) String() string { return proto.CompactTextString(m) } func (*MessageStreamResponse) ProtoMessage() {} func (*MessageStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{47} + return fileDescriptor_5c6ac9b241082464, []int{46} } func (m *MessageStreamResponse) XXX_Unmarshal(b []byte) error { @@ -3359,7 +3278,7 @@ func (m *MessageAckRequest) Reset() { *m = MessageAckRequest{} } func (m *MessageAckRequest) String() string { return proto.CompactTextString(m) } func (*MessageAckRequest) ProtoMessage() {} func (*MessageAckRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{48} + return fileDescriptor_5c6ac9b241082464, []int{47} } func (m *MessageAckRequest) XXX_Unmarshal(b []byte) error { @@ -3430,7 +3349,7 @@ func (m *MessageAckResponse) Reset() { *m = MessageAckResponse{} } func (m *MessageAckResponse) String() string { return proto.CompactTextString(m) } func (*MessageAckResponse) ProtoMessage() {} func (*MessageAckResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{49} + return fileDescriptor_5c6ac9b241082464, []int{48} } func (m *MessageAckResponse) XXX_Unmarshal(b []byte) error { @@ -3469,7 +3388,7 @@ func (m *StreamHealthRequest) Reset() { *m = StreamHealthRequest{} } func (m *StreamHealthRequest) String() string { return proto.CompactTextString(m) } func (*StreamHealthRequest) ProtoMessage() {} func (*StreamHealthRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{50} + return fileDescriptor_5c6ac9b241082464, []int{49} } func (m *StreamHealthRequest) XXX_Unmarshal(b []byte) error { @@ -3529,7 +3448,7 @@ func (m *RealtimeStats) Reset() { *m = RealtimeStats{} } func (m *RealtimeStats) String() string { return proto.CompactTextString(m) } func (*RealtimeStats) ProtoMessage() {} func (*RealtimeStats) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{51} + return fileDescriptor_5c6ac9b241082464, []int{50} } func (m *RealtimeStats) XXX_Unmarshal(b []byte) error { @@ -3618,7 +3537,7 @@ func (m *AggregateStats) Reset() { *m = AggregateStats{} } func (m *AggregateStats) String() string { return proto.CompactTextString(m) } func (*AggregateStats) ProtoMessage() {} func (*AggregateStats) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{52} + return fileDescriptor_5c6ac9b241082464, []int{51} } func (m *AggregateStats) XXX_Unmarshal(b []byte) error { @@ -3724,7 +3643,7 @@ func (m *StreamHealthResponse) Reset() { *m = StreamHealthResponse{} } func (m *StreamHealthResponse) String() string { return proto.CompactTextString(m) } func (*StreamHealthResponse) ProtoMessage() {} func (*StreamHealthResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{53} + return fileDescriptor_5c6ac9b241082464, []int{52} } func (m *StreamHealthResponse) XXX_Unmarshal(b []byte) error { @@ -3795,7 +3714,7 @@ func (m *TransactionMetadata) Reset() { *m = TransactionMetadata{} } func (m *TransactionMetadata) String() string { return proto.CompactTextString(m) } func (*TransactionMetadata) ProtoMessage() {} func (*TransactionMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_5c6ac9b241082464, []int{54} + return fileDescriptor_5c6ac9b241082464, []int{53} } func (m *TransactionMetadata) XXX_Unmarshal(b []byte) error { @@ -3863,7 +3782,6 @@ func init() { proto.RegisterType((*ExecuteOptions)(nil), "query.ExecuteOptions") proto.RegisterType((*Field)(nil), "query.Field") proto.RegisterType((*Row)(nil), "query.Row") - proto.RegisterType((*ResultExtras)(nil), "query.ResultExtras") proto.RegisterType((*QueryResult)(nil), "query.QueryResult") proto.RegisterType((*QueryWarning)(nil), "query.QueryWarning") proto.RegisterType((*StreamEvent)(nil), "query.StreamEvent") @@ -3915,195 +3833,192 @@ func init() { func init() { proto.RegisterFile("query.proto", fileDescriptor_5c6ac9b241082464) } var fileDescriptor_5c6ac9b241082464 = []byte{ - // 3039 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcd, 0x73, 0xdb, 0xd6, - 0xb5, 0x37, 0x48, 0x8a, 0x22, 0x0f, 0x45, 0xea, 0xea, 0x4a, 0xb2, 0x69, 0x39, 0x1f, 0x0a, 0x12, - 0x27, 0x7e, 0xca, 0x7b, 0xb2, 0x23, 0x3b, 0x7e, 0x7e, 0x49, 0xde, 0x7b, 0x86, 0x28, 0xc8, 0x61, - 0x4c, 0x82, 0xf4, 0x25, 0x68, 0xc7, 0x9e, 0xcc, 0x60, 0x20, 0xf2, 0x9a, 0xc2, 0x08, 0x24, 0x68, - 0x00, 0x92, 0xad, 0x9d, 0xdb, 0x34, 0xfd, 0xfe, 0x48, 0x3f, 0xd3, 0xb4, 0xd3, 0xb4, 0x33, 0x5d, - 0x74, 0xba, 0xe9, 0xdf, 0xd0, 0xe9, 0xa2, 0xcb, 0xee, 0xdb, 0x2e, 0xba, 0xea, 0x74, 0x97, 0xe9, - 0xaa, 0x8b, 0x2e, 0x3a, 0x9d, 0xfb, 0x01, 0x10, 0x94, 0xe8, 0x8f, 0xb8, 0xdd, 0xd8, 0xc9, 0xee, - 0x9e, 0x8f, 0x7b, 0xcf, 0x3d, 0xbf, 0x73, 0x70, 0xee, 0x01, 0x2e, 0xa0, 0x70, 0x6b, 0x97, 0xfa, - 0xfb, 0xab, 0x43, 0xdf, 0x0b, 0x3d, 0x3c, 0xc5, 0x89, 0xa5, 0x52, 0xe8, 0x0d, 0xbd, 0xae, 0x1d, - 0xda, 0x82, 0xbd, 0x54, 0xd8, 0x0b, 0xfd, 0x61, 0x47, 0x10, 0xea, 0x7b, 0x0a, 0x64, 0x4d, 0xdb, - 0xef, 0xd1, 0x10, 0x2f, 0x41, 0x6e, 0x87, 0xee, 0x07, 0x43, 0xbb, 0x43, 0xcb, 0xca, 0xb2, 0x72, - 0x2a, 0x4f, 0x62, 0x1a, 0x2f, 0xc0, 0x54, 0xb0, 0x6d, 0xfb, 0xdd, 0x72, 0x8a, 0x0b, 0x04, 0x81, - 0x5f, 0x85, 0x42, 0x68, 0x6f, 0xb9, 0x34, 0xb4, 0xc2, 0xfd, 0x21, 0x2d, 0xa7, 0x97, 0x95, 0x53, - 0xa5, 0xb5, 0x85, 0xd5, 0xd8, 0x9e, 0xc9, 0x85, 0xe6, 0xfe, 0x90, 0x12, 0x08, 0xe3, 0x31, 0xc6, - 0x90, 0xe9, 0x50, 0xd7, 0x2d, 0x67, 0xf8, 0x5a, 0x7c, 0xac, 0x6e, 0x40, 0xe9, 0xaa, 0x79, 0xc9, - 0x0e, 0x69, 0xc5, 0x76, 0x5d, 0xea, 0x57, 0x37, 0xd8, 0x76, 0x76, 0x03, 0xea, 0x0f, 0xec, 0x7e, - 0xbc, 0x9d, 0x88, 0xc6, 0x47, 0x21, 0xdb, 0xf3, 0xbd, 0xdd, 0x61, 0x50, 0x4e, 0x2d, 0xa7, 0x4f, - 0xe5, 0x89, 0xa4, 0xd4, 0x77, 0x00, 0xf4, 0x3d, 0x3a, 0x08, 0x4d, 0x6f, 0x87, 0x0e, 0xf0, 0x53, - 0x90, 0x0f, 0x9d, 0x3e, 0x0d, 0x42, 0xbb, 0x3f, 0xe4, 0x4b, 0xa4, 0xc9, 0x88, 0x71, 0x0f, 0x97, - 0x96, 0x20, 0x37, 0xf4, 0x02, 0x27, 0x74, 0xbc, 0x01, 0xf7, 0x27, 0x4f, 0x62, 0x5a, 0xfd, 0x3f, - 0x98, 0xba, 0x6a, 0xbb, 0xbb, 0x14, 0x3f, 0x0b, 0x19, 0xee, 0xb0, 0xc2, 0x1d, 0x2e, 0xac, 0x0a, - 0xd0, 0xb9, 0x9f, 0x5c, 0xc0, 0xd6, 0xde, 0x63, 0x9a, 0x7c, 0xed, 0x19, 0x22, 0x08, 0x75, 0x07, - 0x66, 0xd6, 0x9d, 0x41, 0xf7, 0xaa, 0xed, 0x3b, 0x0c, 0x8c, 0x47, 0x5c, 0x06, 0xbf, 0x00, 0x59, - 0x3e, 0x08, 0xca, 0xe9, 0xe5, 0xf4, 0xa9, 0xc2, 0xda, 0x8c, 0x9c, 0xc8, 0xf7, 0x46, 0xa4, 0x4c, - 0xfd, 0x8d, 0x02, 0xb0, 0xee, 0xed, 0x0e, 0xba, 0x57, 0x98, 0x10, 0x23, 0x48, 0x07, 0xb7, 0x5c, - 0x09, 0x24, 0x1b, 0xe2, 0xcb, 0x50, 0xda, 0x72, 0x06, 0x5d, 0x6b, 0x4f, 0x6e, 0x47, 0x60, 0x59, - 0x58, 0x7b, 0x41, 0x2e, 0x37, 0x9a, 0xbc, 0x9a, 0xdc, 0x75, 0xa0, 0x0f, 0x42, 0x7f, 0x9f, 0x14, - 0xb7, 0x92, 0xbc, 0xa5, 0x36, 0xe0, 0xc3, 0x4a, 0xcc, 0xe8, 0x0e, 0xdd, 0x8f, 0x8c, 0xee, 0xd0, - 0x7d, 0xfc, 0x1f, 0x49, 0x8f, 0x0a, 0x6b, 0xf3, 0x91, 0xad, 0xc4, 0x5c, 0xe9, 0xe6, 0x6b, 0xa9, - 0x0b, 0x8a, 0xfa, 0xd3, 0x2c, 0x94, 0xf4, 0x3b, 0xb4, 0xb3, 0x1b, 0xd2, 0xc6, 0x90, 0xc5, 0x20, - 0xc0, 0xab, 0x30, 0xef, 0x0c, 0x3a, 0xee, 0x6e, 0x97, 0x5a, 0x94, 0x85, 0xda, 0x0a, 0x59, 0xac, - 0xf9, 0x7a, 0x39, 0x32, 0x27, 0x45, 0x89, 0x24, 0xd0, 0x60, 0xbe, 0xe3, 0xf5, 0x87, 0xb6, 0x3f, - 0xae, 0x9f, 0xe6, 0xf6, 0xe7, 0xa4, 0xfd, 0x91, 0x3e, 0x99, 0x93, 0xda, 0x89, 0x25, 0xea, 0x30, - 0x2b, 0xd7, 0xed, 0x5a, 0x37, 0x1d, 0xea, 0x76, 0x03, 0x9e, 0xba, 0xa5, 0x18, 0xaa, 0xf1, 0x2d, - 0xae, 0x56, 0xa5, 0xf2, 0x26, 0xd7, 0x25, 0x25, 0x67, 0x8c, 0xc6, 0x2b, 0x30, 0xd7, 0x71, 0x1d, - 0xb6, 0x95, 0x9b, 0x0c, 0x62, 0xcb, 0xf7, 0x6e, 0x07, 0xe5, 0x29, 0xbe, 0xff, 0x59, 0x21, 0xd8, - 0x64, 0x7c, 0xe2, 0xdd, 0x0e, 0xf0, 0x6b, 0x90, 0xbb, 0xed, 0xf9, 0x3b, 0xae, 0x67, 0x77, 0xcb, - 0x59, 0x6e, 0xf3, 0x99, 0xc9, 0x36, 0xaf, 0x49, 0x2d, 0x12, 0xeb, 0xe3, 0x53, 0x80, 0x82, 0x5b, - 0xae, 0x15, 0x50, 0x97, 0x76, 0x42, 0xcb, 0x75, 0xfa, 0x4e, 0x58, 0xce, 0xf1, 0xa7, 0xa0, 0x14, - 0xdc, 0x72, 0x5b, 0x9c, 0x5d, 0x63, 0x5c, 0x6c, 0xc1, 0x62, 0xe8, 0xdb, 0x83, 0xc0, 0xee, 0xb0, - 0xc5, 0x2c, 0x27, 0xf0, 0x5c, 0x9b, 0x3f, 0x01, 0x79, 0x6e, 0x72, 0x65, 0xb2, 0x49, 0x73, 0x34, - 0xa5, 0x1a, 0xcd, 0x20, 0x0b, 0xe1, 0x04, 0x2e, 0x7e, 0x05, 0x16, 0x83, 0x1d, 0x67, 0x68, 0xf1, - 0x75, 0xac, 0xa1, 0x6b, 0x0f, 0xac, 0x8e, 0xdd, 0xd9, 0xa6, 0x65, 0xe0, 0x6e, 0x63, 0x26, 0xe4, - 0xa9, 0xd6, 0x74, 0xed, 0x41, 0x85, 0x49, 0xd4, 0xd7, 0xa1, 0x34, 0x8e, 0x23, 0x9e, 0x83, 0xa2, - 0x79, 0xbd, 0xa9, 0x5b, 0x9a, 0xb1, 0x61, 0x19, 0x5a, 0x5d, 0x47, 0x47, 0x70, 0x11, 0xf2, 0x9c, - 0xd5, 0x30, 0x6a, 0xd7, 0x91, 0x82, 0xa7, 0x21, 0xad, 0xd5, 0x6a, 0x28, 0xa5, 0x5e, 0x80, 0x5c, - 0x04, 0x08, 0x9e, 0x85, 0x42, 0xdb, 0x68, 0x35, 0xf5, 0x4a, 0x75, 0xb3, 0xaa, 0x6f, 0xa0, 0x23, - 0x38, 0x07, 0x99, 0x46, 0xcd, 0x6c, 0x22, 0x45, 0x8c, 0xb4, 0x26, 0x4a, 0xb1, 0x99, 0x1b, 0xeb, - 0x1a, 0x4a, 0xab, 0xbf, 0x50, 0x60, 0x61, 0x92, 0x63, 0xb8, 0x00, 0xd3, 0x1b, 0xfa, 0xa6, 0xd6, - 0xae, 0x99, 0xe8, 0x08, 0x9e, 0x87, 0x59, 0xa2, 0x37, 0x75, 0xcd, 0xd4, 0xd6, 0x6b, 0xba, 0x45, - 0x74, 0x6d, 0x03, 0x29, 0x18, 0x43, 0x89, 0x8d, 0xac, 0x4a, 0xa3, 0x5e, 0xaf, 0x9a, 0xa6, 0xbe, - 0x81, 0x52, 0x78, 0x01, 0x10, 0xe7, 0xb5, 0x8d, 0x11, 0x37, 0x8d, 0x11, 0xcc, 0xb4, 0x74, 0x52, - 0xd5, 0x6a, 0xd5, 0x1b, 0x6c, 0x01, 0x94, 0xc1, 0xcf, 0xc1, 0xd3, 0x95, 0x86, 0xd1, 0xaa, 0xb6, - 0x4c, 0xdd, 0x30, 0xad, 0x96, 0xa1, 0x35, 0x5b, 0x6f, 0x36, 0x4c, 0xbe, 0xb2, 0x70, 0x6e, 0x0a, - 0x97, 0x00, 0xb4, 0xb6, 0xd9, 0x10, 0xeb, 0xa0, 0xec, 0x5b, 0x99, 0x9c, 0x82, 0x52, 0xea, 0x07, - 0x29, 0x98, 0xe2, 0xf8, 0xb0, 0xaa, 0x9a, 0xa8, 0x95, 0x7c, 0x1c, 0x57, 0x98, 0xd4, 0x7d, 0x2a, - 0x0c, 0x2f, 0xcc, 0xb2, 0xd6, 0x09, 0x02, 0x9f, 0x80, 0xbc, 0xe7, 0xf7, 0x2c, 0x21, 0x11, 0x55, - 0x3a, 0xe7, 0xf9, 0x3d, 0x5e, 0xce, 0x59, 0x85, 0x64, 0xc5, 0x7d, 0xcb, 0x0e, 0x28, 0xcf, 0xda, - 0x3c, 0x89, 0x69, 0x7c, 0x1c, 0x98, 0x9e, 0xc5, 0xf7, 0x91, 0xe5, 0xb2, 0x69, 0xcf, 0xef, 0x19, - 0x6c, 0x2b, 0xcf, 0x43, 0xb1, 0xe3, 0xb9, 0xbb, 0xfd, 0x81, 0xe5, 0xd2, 0x41, 0x2f, 0xdc, 0x2e, - 0x4f, 0x2f, 0x2b, 0xa7, 0x8a, 0x64, 0x46, 0x30, 0x6b, 0x9c, 0x87, 0xcb, 0x30, 0xdd, 0xd9, 0xb6, - 0xfd, 0x80, 0x8a, 0x4c, 0x2d, 0x92, 0x88, 0xe4, 0x56, 0x69, 0xc7, 0xe9, 0xdb, 0x6e, 0xc0, 0xb3, - 0xb2, 0x48, 0x62, 0x9a, 0x39, 0x71, 0xd3, 0xb5, 0x7b, 0x01, 0xcf, 0xa6, 0x22, 0x11, 0x84, 0xfa, - 0xdf, 0x90, 0x26, 0xde, 0x6d, 0xb6, 0xa4, 0x30, 0x18, 0x94, 0x95, 0xe5, 0xf4, 0x29, 0x4c, 0x22, - 0x92, 0x1d, 0x22, 0xb2, 0x8e, 0x8a, 0xf2, 0x1a, 0x55, 0xce, 0x77, 0x60, 0x86, 0xd0, 0x60, 0xd7, - 0x0d, 0xf5, 0x3b, 0xa1, 0x6f, 0x07, 0x78, 0x0d, 0x0a, 0xc9, 0xca, 0xa1, 0xdc, 0xab, 0x72, 0x00, - 0x1d, 0x95, 0x8c, 0x32, 0x4c, 0xdf, 0xf4, 0x69, 0xb0, 0x4d, 0x7d, 0x59, 0x99, 0x22, 0x92, 0xd5, - 0xe5, 0x02, 0x4f, 0x75, 0x61, 0x83, 0x55, 0x73, 0x59, 0x53, 0x94, 0xb1, 0x6a, 0xce, 0x83, 0x4a, - 0xa4, 0x8c, 0xa1, 0xc7, 0xca, 0x84, 0x65, 0xdf, 0xbc, 0x49, 0x3b, 0x21, 0x15, 0x87, 0x56, 0x86, - 0xcc, 0x30, 0xa6, 0x26, 0x79, 0x2c, 0x6c, 0xce, 0x20, 0xa0, 0x7e, 0x68, 0x39, 0x5d, 0x1e, 0xd0, - 0x0c, 0xc9, 0x09, 0x46, 0xb5, 0x8b, 0x9f, 0x81, 0x0c, 0x2f, 0x34, 0x19, 0x6e, 0x05, 0xa4, 0x15, - 0xe2, 0xdd, 0x26, 0x9c, 0x8f, 0x5f, 0x86, 0x2c, 0xe5, 0xfe, 0xf2, 0xa0, 0x8e, 0x4a, 0x73, 0x12, - 0x0a, 0x22, 0x55, 0xd4, 0x37, 0x60, 0x86, 0xfb, 0x70, 0xcd, 0xf6, 0x07, 0xce, 0xa0, 0xc7, 0x4f, - 0x74, 0xaf, 0x2b, 0x72, 0xaf, 0x48, 0xf8, 0x98, 0x41, 0xd0, 0xa7, 0x41, 0x60, 0xf7, 0xa8, 0x3c, - 0x61, 0x23, 0x52, 0xfd, 0x59, 0x1a, 0x0a, 0xad, 0xd0, 0xa7, 0x76, 0x9f, 0xa3, 0x87, 0xdf, 0x00, - 0x08, 0x42, 0x3b, 0xa4, 0x7d, 0x3a, 0x08, 0x23, 0x18, 0x9e, 0x92, 0xe6, 0x13, 0x7a, 0xab, 0xad, - 0x48, 0x89, 0x24, 0xf4, 0x0f, 0x86, 0x27, 0xf5, 0x10, 0xe1, 0x59, 0xfa, 0x28, 0x05, 0xf9, 0x78, - 0x35, 0xac, 0x41, 0xae, 0x63, 0x87, 0xb4, 0xe7, 0xf9, 0xfb, 0xf2, 0x2c, 0x3e, 0x79, 0x3f, 0xeb, - 0xab, 0x15, 0xa9, 0x4c, 0xe2, 0x69, 0xf8, 0x69, 0x10, 0x0d, 0x8e, 0x48, 0x7d, 0xe1, 0x6f, 0x9e, - 0x73, 0x78, 0xf2, 0xbf, 0x06, 0x78, 0xe8, 0x3b, 0x7d, 0xdb, 0xdf, 0xb7, 0x76, 0xe8, 0x7e, 0x74, - 0x88, 0xa4, 0x27, 0x04, 0x1c, 0x49, 0xbd, 0xcb, 0x74, 0x5f, 0x96, 0xbd, 0x0b, 0xe3, 0x73, 0x65, - 0xca, 0x1e, 0x0e, 0x63, 0x62, 0x26, 0xef, 0x04, 0x82, 0xe8, 0xcc, 0x9f, 0xe2, 0xd9, 0xcd, 0x86, - 0xea, 0x4b, 0x90, 0x8b, 0x36, 0x8f, 0xf3, 0x30, 0xa5, 0xfb, 0xbe, 0xe7, 0xa3, 0x23, 0xbc, 0xfa, - 0xd5, 0x6b, 0xa2, 0x80, 0x6e, 0x6c, 0xb0, 0x02, 0xfa, 0xeb, 0x54, 0x7c, 0xf0, 0x12, 0x7a, 0x6b, - 0x97, 0x06, 0x21, 0xfe, 0x7f, 0x98, 0xa7, 0x3c, 0xd3, 0x9c, 0x3d, 0x6a, 0x75, 0x78, 0x97, 0xc6, - 0xf2, 0x4c, 0x3c, 0x0e, 0xb3, 0xab, 0xa2, 0xa9, 0x8c, 0xba, 0x37, 0x32, 0x17, 0xeb, 0x4a, 0x56, - 0x17, 0xeb, 0x30, 0xef, 0xf4, 0xfb, 0xb4, 0xeb, 0xd8, 0x61, 0x72, 0x01, 0x11, 0xb0, 0xc5, 0xa8, - 0x89, 0x19, 0x6b, 0x02, 0xc9, 0x5c, 0x3c, 0x23, 0x5e, 0xe6, 0x24, 0x64, 0x43, 0xde, 0xb0, 0xca, - 0x33, 0xbc, 0x18, 0x55, 0x35, 0xce, 0x24, 0x52, 0x88, 0x5f, 0x02, 0xd1, 0xfe, 0xf2, 0xfa, 0x35, - 0x4a, 0x88, 0x51, 0x57, 0x43, 0x84, 0x1c, 0x9f, 0x84, 0xd2, 0xd8, 0xe1, 0xd7, 0xe5, 0x80, 0xa5, - 0x49, 0x31, 0x79, 0x92, 0x75, 0xf1, 0x69, 0x98, 0xf6, 0xc4, 0xc1, 0xc7, 0x2b, 0xdb, 0x68, 0xc7, - 0xe3, 0xa7, 0x22, 0x89, 0xb4, 0xd4, 0xff, 0x85, 0xd9, 0x18, 0xc1, 0x60, 0xe8, 0x0d, 0x02, 0x8a, - 0x57, 0x20, 0xeb, 0xf3, 0xc7, 0x49, 0xa2, 0x86, 0xe5, 0x12, 0x89, 0x7a, 0x40, 0xa4, 0x86, 0xda, - 0x85, 0x59, 0xc1, 0xb9, 0xe6, 0x84, 0xdb, 0x3c, 0x50, 0xf8, 0x24, 0x4c, 0x51, 0x36, 0x38, 0x80, - 0x39, 0x69, 0x56, 0xb8, 0x9c, 0x08, 0x69, 0xc2, 0x4a, 0xea, 0x81, 0x56, 0xfe, 0x9a, 0x82, 0x79, - 0xb9, 0xcb, 0x75, 0x3b, 0xec, 0x6c, 0x3f, 0xa6, 0xc1, 0x7e, 0x19, 0xa6, 0x19, 0xdf, 0x89, 0x1f, - 0x8c, 0x09, 0xe1, 0x8e, 0x34, 0x58, 0xc0, 0xed, 0xc0, 0x4a, 0x44, 0x57, 0x36, 0x5f, 0x45, 0x3b, - 0x48, 0x9c, 0xfc, 0x13, 0xf2, 0x22, 0xfb, 0x80, 0xbc, 0x98, 0x7e, 0xa8, 0xbc, 0xd8, 0x80, 0x85, - 0x71, 0xc4, 0x65, 0x72, 0xfc, 0x27, 0x4c, 0x8b, 0xa0, 0x44, 0x25, 0x70, 0x52, 0xdc, 0x22, 0x15, - 0xf5, 0xb7, 0x29, 0x58, 0x90, 0xd5, 0xe9, 0xd3, 0xf1, 0x98, 0x26, 0x70, 0x9e, 0x7a, 0x18, 0x9c, - 0x1f, 0x32, 0x7e, 0x6a, 0x05, 0x16, 0x0f, 0xe0, 0xf8, 0x08, 0x0f, 0xeb, 0xc7, 0x0a, 0xcc, 0xac, - 0xd3, 0x9e, 0x33, 0x78, 0x4c, 0xa3, 0x90, 0x00, 0x37, 0xf3, 0x50, 0x49, 0x7c, 0x1e, 0x8a, 0xd2, - 0x5f, 0x89, 0xd6, 0x61, 0xb4, 0x95, 0x49, 0x68, 0xff, 0x59, 0x81, 0x62, 0xc5, 0xeb, 0xf7, 0x9d, - 0xf0, 0x31, 0x45, 0xea, 0xb0, 0x9f, 0x99, 0x49, 0x7e, 0x22, 0x28, 0x45, 0x6e, 0x0a, 0x80, 0xd4, - 0xbf, 0x28, 0x30, 0x4b, 0x3c, 0xd7, 0xdd, 0xb2, 0x3b, 0x3b, 0x4f, 0xb6, 0xef, 0x18, 0xd0, 0xc8, - 0x51, 0xe9, 0xfd, 0xdf, 0x15, 0x28, 0x35, 0x7d, 0xca, 0x5e, 0xac, 0x9f, 0x68, 0xe7, 0x59, 0x27, - 0xdc, 0x0d, 0x65, 0x0f, 0x91, 0x27, 0x7c, 0xac, 0xce, 0xc1, 0x6c, 0xec, 0xbb, 0xc4, 0xe3, 0x0f, - 0x0a, 0x2c, 0x8a, 0x04, 0x91, 0x92, 0xee, 0x63, 0x0a, 0x4b, 0xe4, 0x6f, 0x26, 0xe1, 0x6f, 0x19, - 0x8e, 0x1e, 0xf4, 0x4d, 0xba, 0xfd, 0x6e, 0x0a, 0x8e, 0x45, 0xb9, 0xf1, 0x98, 0x3b, 0xfe, 0x2f, - 0xe4, 0xc3, 0x12, 0x94, 0x0f, 0x83, 0x20, 0x11, 0x7a, 0x3f, 0x05, 0xe5, 0x8a, 0x4f, 0xed, 0x90, - 0x26, 0x7a, 0x91, 0x27, 0x27, 0x37, 0xf0, 0x2b, 0x30, 0x33, 0xb4, 0xfd, 0xd0, 0xe9, 0x38, 0x43, - 0x9b, 0xbd, 0xed, 0x4d, 0xf1, 0x56, 0xe7, 0xc0, 0x02, 0x63, 0x2a, 0xea, 0x09, 0x38, 0x3e, 0x01, - 0x11, 0x89, 0xd7, 0x3f, 0x14, 0xc0, 0xad, 0xd0, 0xf6, 0xc3, 0x4f, 0xc1, 0xa9, 0x32, 0x31, 0x99, - 0x16, 0x61, 0x7e, 0xcc, 0xff, 0x24, 0x2e, 0x34, 0xfc, 0x54, 0x9c, 0x38, 0xf7, 0xc4, 0x25, 0xe9, - 0xbf, 0xc4, 0xe5, 0x4f, 0x0a, 0x2c, 0x55, 0x3c, 0xf1, 0x61, 0xf1, 0x89, 0x7c, 0xc2, 0xd4, 0xa7, - 0xe1, 0xc4, 0x44, 0x07, 0x25, 0x00, 0x7f, 0x54, 0xe0, 0x28, 0xa1, 0x76, 0xf7, 0xc9, 0x74, 0xfe, - 0x0a, 0x1c, 0x3b, 0xe4, 0x9c, 0xec, 0x50, 0xcf, 0x43, 0xae, 0x4f, 0x43, 0xbb, 0x6b, 0x87, 0xb6, - 0x74, 0x69, 0x29, 0x5a, 0x77, 0xa4, 0x5d, 0x97, 0x1a, 0x24, 0xd6, 0x55, 0x3f, 0x4a, 0xc1, 0x3c, - 0xef, 0x75, 0x3f, 0x7b, 0xd1, 0x9a, 0xfc, 0x2e, 0xf0, 0xbe, 0x02, 0x0b, 0xe3, 0x00, 0xc5, 0xef, - 0x04, 0xff, 0xee, 0xef, 0x15, 0x13, 0x0a, 0x42, 0x7a, 0x52, 0x0b, 0xfa, 0xbb, 0x14, 0x94, 0x93, - 0x5b, 0xfa, 0xec, 0xdb, 0xc6, 0xf8, 0xb7, 0x8d, 0x4f, 0xfc, 0x31, 0xeb, 0x03, 0x05, 0x8e, 0x4f, - 0x00, 0xf4, 0x93, 0x05, 0x3a, 0xf1, 0x85, 0x23, 0xf5, 0xc0, 0x2f, 0x1c, 0x0f, 0x1b, 0xea, 0xdf, - 0x2b, 0xb0, 0x50, 0x17, 0x1f, 0x96, 0xc5, 0x7b, 0xfc, 0xe3, 0x5b, 0xcd, 0xf8, 0xb7, 0xe3, 0xcc, - 0xe8, 0xfa, 0x46, 0xad, 0xc0, 0xe2, 0x01, 0xd7, 0x1e, 0xe1, 0xdb, 0xc4, 0xdf, 0x14, 0x98, 0x93, - 0xab, 0x68, 0x8f, 0x6d, 0x23, 0x30, 0x01, 0x1d, 0xfc, 0x0c, 0xa4, 0x9d, 0x6e, 0xd4, 0x41, 0x8e, - 0x5f, 0x82, 0x33, 0x81, 0x7a, 0x11, 0x70, 0xd2, 0xef, 0x47, 0x80, 0x8e, 0xf7, 0x56, 0x0c, 0xf8, - 0x37, 0xa9, 0xed, 0x86, 0x51, 0x01, 0x51, 0x7f, 0x9e, 0x82, 0x22, 0x61, 0x1c, 0xa7, 0x4f, 0x5b, - 0xa1, 0x1d, 0x06, 0xf8, 0x39, 0x98, 0xd9, 0xe6, 0x2a, 0xd6, 0xe8, 0x39, 0xc8, 0x93, 0x82, 0xe0, - 0x89, 0x8f, 0xb7, 0x6b, 0xb0, 0x18, 0xd0, 0x8e, 0x37, 0xe8, 0x06, 0xd6, 0x16, 0xdd, 0x76, 0x06, - 0x5d, 0xab, 0x6f, 0x07, 0xa1, 0xbc, 0x1f, 0x2a, 0x92, 0x79, 0x29, 0x5c, 0xe7, 0xb2, 0x3a, 0x17, - 0xe1, 0x33, 0xb0, 0xb0, 0xe5, 0x0c, 0x5c, 0xaf, 0x67, 0x0d, 0x5d, 0x7b, 0x9f, 0xfa, 0x81, 0xd5, - 0xf1, 0x76, 0x07, 0x02, 0xaa, 0x29, 0x82, 0x85, 0xac, 0x29, 0x44, 0x15, 0x26, 0xc1, 0x37, 0x60, - 0x65, 0xa2, 0x15, 0xeb, 0xa6, 0xe3, 0x86, 0xd4, 0xa7, 0x5d, 0xcb, 0xa7, 0x43, 0xd7, 0xe9, 0x88, - 0xeb, 0x5d, 0xd1, 0x4c, 0xbd, 0x38, 0xc1, 0xf4, 0xa6, 0x54, 0x27, 0x23, 0x6d, 0x7c, 0x02, 0xf2, - 0x9d, 0xe1, 0xae, 0xb5, 0xcb, 0xaf, 0x74, 0x58, 0x59, 0x51, 0x48, 0xae, 0x33, 0xdc, 0x6d, 0x33, - 0x1a, 0x23, 0x48, 0xdf, 0x1a, 0x8a, 0x6a, 0xa2, 0x10, 0x36, 0x54, 0x3f, 0x56, 0xa0, 0xa4, 0xf5, - 0x7a, 0x3e, 0xed, 0xd9, 0xa1, 0x84, 0xe9, 0x0c, 0x2c, 0x08, 0x48, 0xf6, 0x2d, 0xf9, 0xdf, 0x88, - 0xf0, 0x47, 0x11, 0xfe, 0x48, 0x99, 0xf8, 0x6b, 0x44, 0xf8, 0x73, 0x0e, 0x8e, 0xee, 0x0e, 0x26, - 0xce, 0x49, 0xf1, 0x39, 0x0b, 0xb1, 0x34, 0x39, 0xeb, 0x7f, 0xe0, 0xf8, 0x64, 0x14, 0xfa, 0x8e, - 0xb8, 0xf9, 0x2f, 0x92, 0xa3, 0x13, 0x9c, 0xae, 0x3b, 0x83, 0xfb, 0x4c, 0xb5, 0xef, 0x70, 0xbc, - 0xee, 0x31, 0xd5, 0xbe, 0xa3, 0xfe, 0x32, 0xfe, 0x24, 0x1b, 0xa5, 0x4b, 0x5c, 0x1e, 0xa3, 0x1c, - 0x57, 0xee, 0x97, 0xe3, 0x65, 0x98, 0x0e, 0xa8, 0xbf, 0xe7, 0x0c, 0x7a, 0xd1, 0x9d, 0xa1, 0x24, - 0x71, 0x0b, 0x5e, 0x94, 0xbe, 0xd3, 0x3b, 0x21, 0xf5, 0x07, 0xb6, 0xeb, 0xee, 0x5b, 0xe2, 0xcd, - 0x71, 0x10, 0xd2, 0xae, 0x35, 0xfa, 0xcb, 0x45, 0x94, 0xc8, 0xe7, 0x85, 0xb6, 0x1e, 0x2b, 0x93, - 0x58, 0xd7, 0x8c, 0xff, 0x7f, 0x79, 0x1d, 0x4a, 0xbe, 0x4c, 0x62, 0x2b, 0x60, 0xe1, 0x91, 0x9d, - 0xc1, 0x42, 0x7c, 0xf1, 0x97, 0xc8, 0x70, 0x52, 0xf4, 0xc7, 0x12, 0xfe, 0x02, 0xcc, 0xc8, 0x1d, - 0xd9, 0xae, 0x63, 0x8f, 0x3a, 0x85, 0x03, 0xbf, 0xfe, 0x68, 0x4c, 0x48, 0xe4, 0x4f, 0x42, 0x9c, - 0x78, 0x2b, 0x93, 0xcb, 0xa2, 0x69, 0xf5, 0x57, 0x0a, 0xcc, 0x4f, 0x68, 0xbb, 0xe2, 0x9e, 0x4e, - 0x49, 0xbc, 0x32, 0xfe, 0x17, 0x4c, 0xf1, 0xeb, 0x3e, 0x79, 0x8b, 0x7d, 0xec, 0x70, 0xd7, 0xc6, - 0xaf, 0xe6, 0x88, 0xd0, 0x62, 0xcf, 0x22, 0xf7, 0xa9, 0xc3, 0xdf, 0x19, 0xa3, 0x53, 0xa3, 0xc0, - 0x78, 0xe2, 0x35, 0xf2, 0xf0, 0x4b, 0x68, 0xe6, 0x81, 0x2f, 0xa1, 0x2b, 0xdf, 0x49, 0x43, 0xbe, - 0xbe, 0xdf, 0xba, 0xe5, 0x6e, 0xba, 0x76, 0x8f, 0xdf, 0x9d, 0xd5, 0x9b, 0xe6, 0x75, 0x74, 0x04, - 0xcf, 0x41, 0xd1, 0x68, 0x98, 0x96, 0xd1, 0xae, 0xd5, 0xac, 0xcd, 0x9a, 0x76, 0x09, 0x29, 0x18, - 0xc1, 0x4c, 0x93, 0x54, 0xad, 0xcb, 0xfa, 0x75, 0xc1, 0x49, 0xe1, 0x79, 0x98, 0x6d, 0x1b, 0xd5, - 0x2b, 0x6d, 0x7d, 0xc4, 0xcc, 0xe0, 0x45, 0x98, 0xab, 0xb7, 0x6b, 0x66, 0xb5, 0x59, 0x4b, 0xb0, - 0x73, 0xb8, 0x08, 0xf9, 0xf5, 0x5a, 0x63, 0x5d, 0x90, 0x88, 0xad, 0xdf, 0x36, 0x5a, 0xd5, 0x4b, - 0x86, 0xbe, 0x21, 0x58, 0xcb, 0x8c, 0x75, 0x43, 0x27, 0x8d, 0xcd, 0x6a, 0x64, 0xf2, 0x22, 0x46, - 0x50, 0x58, 0xaf, 0x1a, 0x1a, 0x91, 0xab, 0xdc, 0x55, 0x70, 0x09, 0xf2, 0xba, 0xd1, 0xae, 0x4b, - 0x3a, 0x85, 0xcb, 0x30, 0xaf, 0xb5, 0xcd, 0x86, 0x55, 0x35, 0x2a, 0x44, 0xaf, 0xeb, 0x86, 0x29, - 0x25, 0x19, 0x3c, 0x0f, 0x25, 0xb3, 0x5a, 0xd7, 0x5b, 0xa6, 0x56, 0x6f, 0x4a, 0x26, 0xdb, 0x45, - 0xae, 0xa5, 0x47, 0x3a, 0x08, 0x2f, 0xc1, 0xa2, 0xd1, 0xb0, 0xe4, 0x0f, 0x10, 0xd6, 0x55, 0xad, - 0xd6, 0xd6, 0xa5, 0x6c, 0x19, 0x1f, 0x03, 0xdc, 0x30, 0xac, 0x76, 0x73, 0x43, 0x33, 0x75, 0xcb, - 0x68, 0x5c, 0x93, 0x82, 0x8b, 0xb8, 0x04, 0xb9, 0xd1, 0x0e, 0xee, 0x32, 0x14, 0x8a, 0x4d, 0x8d, - 0x98, 0x23, 0x67, 0xef, 0xde, 0x65, 0x60, 0xc1, 0x25, 0xd2, 0x68, 0x37, 0x47, 0x6a, 0x73, 0x50, - 0x90, 0x60, 0x49, 0x56, 0x86, 0xb1, 0xd6, 0xab, 0x46, 0x25, 0xde, 0xdf, 0xdd, 0xdc, 0x52, 0x0a, - 0x29, 0x2b, 0x3b, 0x90, 0xe1, 0xe1, 0xc8, 0x41, 0xc6, 0x68, 0x18, 0x3a, 0x3a, 0x82, 0x67, 0x01, - 0xaa, 0xad, 0xaa, 0x61, 0xea, 0x97, 0x88, 0x56, 0x63, 0x6e, 0x73, 0x46, 0x04, 0x20, 0xf3, 0x76, - 0x06, 0xa6, 0xab, 0xad, 0xcd, 0x5a, 0x43, 0x33, 0xa5, 0x9b, 0xd5, 0xd6, 0x95, 0x76, 0xc3, 0x64, - 0x42, 0x84, 0x0b, 0x90, 0xad, 0xb6, 0x4c, 0xfd, 0x6d, 0x93, 0xf9, 0xc5, 0x65, 0x02, 0x55, 0x74, - 0xf7, 0xe2, 0xca, 0x87, 0x69, 0xc8, 0xf0, 0xdf, 0xd7, 0x8a, 0x90, 0xe7, 0xd1, 0x36, 0xaf, 0x37, - 0x99, 0xc9, 0x3c, 0x64, 0xaa, 0x86, 0x79, 0x01, 0x7d, 0x2e, 0x85, 0x01, 0xa6, 0xda, 0x7c, 0xfc, - 0xf9, 0x2c, 0x1b, 0x57, 0x0d, 0xf3, 0x95, 0xf3, 0xe8, 0xdd, 0x14, 0x5b, 0xb6, 0x2d, 0x88, 0x2f, - 0x44, 0x82, 0xb5, 0x73, 0xe8, 0xbd, 0x58, 0xb0, 0x76, 0x0e, 0x7d, 0x31, 0x12, 0x9c, 0x5d, 0x43, - 0x5f, 0x8a, 0x05, 0x67, 0xd7, 0xd0, 0x97, 0x23, 0xc1, 0xf9, 0x73, 0xe8, 0x2b, 0xb1, 0xe0, 0xfc, - 0x39, 0xf4, 0xd5, 0x2c, 0xf3, 0x85, 0x7b, 0x72, 0x76, 0x0d, 0x7d, 0x2d, 0x17, 0x53, 0xe7, 0xcf, - 0xa1, 0xaf, 0xe7, 0x58, 0xfc, 0xe3, 0xa8, 0xa2, 0x6f, 0x20, 0xb6, 0x4d, 0x16, 0x20, 0xf4, 0x4d, - 0x3e, 0x64, 0x22, 0xf4, 0x2d, 0xc4, 0x7c, 0x64, 0x5c, 0x4e, 0xbe, 0xcf, 0x25, 0xd7, 0x75, 0x8d, - 0xa0, 0x6f, 0x67, 0xc5, 0xff, 0x2e, 0x95, 0x6a, 0x5d, 0xab, 0x21, 0xcc, 0x67, 0x30, 0x54, 0xbe, - 0x7b, 0x86, 0x0d, 0x59, 0x7a, 0xa2, 0xef, 0x35, 0x99, 0xc1, 0xab, 0x1a, 0xa9, 0xbc, 0xa9, 0x11, - 0xf4, 0xfd, 0x33, 0xcc, 0xe0, 0x55, 0x8d, 0x48, 0xbc, 0x7e, 0xd0, 0x64, 0x8a, 0x5c, 0xf4, 0xc1, - 0x19, 0xb6, 0x69, 0xc9, 0xff, 0x61, 0x13, 0xe7, 0x20, 0xbd, 0x5e, 0x35, 0xd1, 0x87, 0xdc, 0x1a, - 0x4b, 0x51, 0xf4, 0x23, 0xc4, 0x98, 0x2d, 0xdd, 0x44, 0x3f, 0x66, 0xcc, 0x29, 0xb3, 0xdd, 0xac, - 0xe9, 0xe8, 0x29, 0xb6, 0xb9, 0x4b, 0x7a, 0xa3, 0xae, 0x9b, 0xe4, 0x3a, 0xfa, 0x09, 0x57, 0x7f, - 0xab, 0xd5, 0x30, 0xd0, 0x47, 0x08, 0x97, 0x00, 0xf4, 0xb7, 0x9b, 0x44, 0x6f, 0xb5, 0xaa, 0x0d, - 0x03, 0x3d, 0xbb, 0xb2, 0x09, 0xe8, 0x60, 0x39, 0x60, 0x0e, 0xb4, 0x8d, 0xcb, 0x46, 0xe3, 0x9a, - 0x81, 0x8e, 0x30, 0xa2, 0x49, 0xf4, 0xa6, 0x46, 0x74, 0xa4, 0x60, 0x80, 0xac, 0xfc, 0x8b, 0x26, - 0x85, 0x67, 0x20, 0x47, 0x1a, 0xb5, 0xda, 0xba, 0x56, 0xb9, 0x8c, 0xd2, 0xeb, 0xaf, 0xc2, 0xac, - 0xe3, 0xad, 0xee, 0x39, 0x21, 0x0d, 0x02, 0xf1, 0x83, 0xe4, 0x0d, 0x55, 0x52, 0x8e, 0x77, 0x5a, - 0x8c, 0x4e, 0xf7, 0xbc, 0xd3, 0x7b, 0xe1, 0x69, 0x2e, 0x3d, 0xcd, 0x2b, 0xc6, 0x56, 0x96, 0x13, - 0x67, 0xff, 0x19, 0x00, 0x00, 0xff, 0xff, 0x59, 0xd0, 0x47, 0x8d, 0x7e, 0x29, 0x00, 0x00, + // 2983 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0x4b, 0x70, 0xdb, 0xd6, + 0xb9, 0x36, 0xf8, 0x12, 0xf9, 0x53, 0xa4, 0x8e, 0x8e, 0x24, 0x9b, 0x96, 0xf3, 0x50, 0x90, 0x38, + 0xd1, 0xd5, 0xbd, 0x57, 0x76, 0x64, 0xc7, 0xd7, 0x37, 0x49, 0x5b, 0x43, 0x14, 0xe4, 0xd0, 0x26, + 0x41, 0xfa, 0x10, 0xb4, 0x63, 0x4f, 0x67, 0x30, 0x10, 0x79, 0x4c, 0x61, 0x04, 0x02, 0x34, 0x00, + 0xc9, 0xd6, 0xce, 0x6d, 0x9a, 0xbe, 0x1f, 0xe9, 0x33, 0x4d, 0x33, 0xcd, 0x74, 0xa6, 0x8b, 0x4e, + 0x37, 0x5d, 0x77, 0xd9, 0xe9, 0xa2, 0xcb, 0xee, 0xdb, 0x2e, 0xba, 0xea, 0x74, 0x97, 0xe9, 0xaa, + 0x8b, 0x2e, 0x3a, 0x9d, 0xf3, 0x00, 0x48, 0x59, 0x8c, 0xed, 0xb8, 0xdd, 0xd8, 0xc9, 0xee, 0xfc, + 0x8f, 0xf3, 0xf8, 0xbe, 0xf3, 0xe3, 0x3f, 0x3f, 0x80, 0x03, 0xc5, 0x5b, 0xbb, 0x34, 0xd8, 0x5f, + 0x1d, 0x06, 0x7e, 0xe4, 0xe3, 0x2c, 0x17, 0x16, 0xcb, 0x91, 0x3f, 0xf4, 0x7b, 0x76, 0x64, 0x0b, + 0xf5, 0x62, 0x71, 0x2f, 0x0a, 0x86, 0x5d, 0x21, 0xa8, 0x6f, 0x2b, 0x90, 0x33, 0xed, 0xa0, 0x4f, + 0x23, 0xbc, 0x08, 0xf9, 0x1d, 0xba, 0x1f, 0x0e, 0xed, 0x2e, 0xad, 0x28, 0x4b, 0xca, 0x72, 0x81, + 0x24, 0x32, 0x9e, 0x87, 0x6c, 0xb8, 0x6d, 0x07, 0xbd, 0x4a, 0x8a, 0x1b, 0x84, 0x80, 0x5f, 0x81, + 0x62, 0x64, 0x6f, 0xb9, 0x34, 0xb2, 0xa2, 0xfd, 0x21, 0xad, 0xa4, 0x97, 0x94, 0xe5, 0xf2, 0xda, + 0xfc, 0x6a, 0x32, 0x9f, 0xc9, 0x8d, 0xe6, 0xfe, 0x90, 0x12, 0x88, 0x92, 0x36, 0xc6, 0x90, 0xe9, + 0x52, 0xd7, 0xad, 0x64, 0xf8, 0x58, 0xbc, 0xad, 0x6e, 0x40, 0xf9, 0xaa, 0x79, 0xd1, 0x8e, 0x68, + 0xd5, 0x76, 0x5d, 0x1a, 0xd4, 0x36, 0xd8, 0x72, 0x76, 0x43, 0x1a, 0x78, 0xf6, 0x20, 0x59, 0x4e, + 0x2c, 0xe3, 0xa3, 0x90, 0xeb, 0x07, 0xfe, 0xee, 0x30, 0xac, 0xa4, 0x96, 0xd2, 0xcb, 0x05, 0x22, + 0x25, 0xf5, 0xf3, 0x00, 0xfa, 0x1e, 0xf5, 0x22, 0xd3, 0xdf, 0xa1, 0x1e, 0x7e, 0x0a, 0x0a, 0x91, + 0x33, 0xa0, 0x61, 0x64, 0x0f, 0x86, 0x7c, 0x88, 0x34, 0x19, 0x29, 0x3e, 0x02, 0xd2, 0x22, 0xe4, + 0x87, 0x7e, 0xe8, 0x44, 0x8e, 0xef, 0x71, 0x3c, 0x05, 0x92, 0xc8, 0xea, 0x67, 0x21, 0x7b, 0xd5, + 0x76, 0x77, 0x29, 0x7e, 0x16, 0x32, 0x1c, 0xb0, 0xc2, 0x01, 0x17, 0x57, 0x05, 0xe9, 0x1c, 0x27, + 0x37, 0xb0, 0xb1, 0xf7, 0x98, 0x27, 0x1f, 0x7b, 0x9a, 0x08, 0x41, 0xdd, 0x81, 0xe9, 0x75, 0xc7, + 0xeb, 0x5d, 0xb5, 0x03, 0x87, 0x91, 0xf1, 0x88, 0xc3, 0xe0, 0x17, 0x20, 0xc7, 0x1b, 0x61, 0x25, + 0xbd, 0x94, 0x5e, 0x2e, 0xae, 0x4d, 0xcb, 0x8e, 0x7c, 0x6d, 0x44, 0xda, 0xd4, 0xdf, 0x2a, 0x00, + 0xeb, 0xfe, 0xae, 0xd7, 0xbb, 0xc2, 0x8c, 0x18, 0x41, 0x3a, 0xbc, 0xe5, 0x4a, 0x22, 0x59, 0x13, + 0x5f, 0x86, 0xf2, 0x96, 0xe3, 0xf5, 0xac, 0x3d, 0xb9, 0x1c, 0xc1, 0x65, 0x71, 0xed, 0x05, 0x39, + 0xdc, 0xa8, 0xf3, 0xea, 0xf8, 0xaa, 0x43, 0xdd, 0x8b, 0x82, 0x7d, 0x52, 0xda, 0x1a, 0xd7, 0x2d, + 0x76, 0x00, 0x1f, 0x76, 0x62, 0x93, 0xee, 0xd0, 0xfd, 0x78, 0xd2, 0x1d, 0xba, 0x8f, 0xff, 0x6b, + 0x1c, 0x51, 0x71, 0x6d, 0x2e, 0x9e, 0x6b, 0xac, 0xaf, 0x84, 0xf9, 0x6a, 0xea, 0xbc, 0xa2, 0xfe, + 0x3a, 0x0b, 0x65, 0xfd, 0x0e, 0xed, 0xee, 0x46, 0xb4, 0x39, 0x64, 0x7b, 0x10, 0xe2, 0x06, 0xcc, + 0x38, 0x5e, 0xd7, 0xdd, 0xed, 0xd1, 0x9e, 0x75, 0xd3, 0xa1, 0x6e, 0x2f, 0xe4, 0x71, 0x54, 0x4e, + 0xd6, 0x7d, 0xd0, 0x7f, 0xb5, 0x26, 0x9d, 0x37, 0xb9, 0x2f, 0x29, 0x3b, 0x07, 0x64, 0xbc, 0x02, + 0xb3, 0x5d, 0xd7, 0xa1, 0x5e, 0x64, 0xdd, 0x64, 0x78, 0xad, 0xc0, 0xbf, 0x1d, 0x56, 0xb2, 0x4b, + 0xca, 0x72, 0x9e, 0xcc, 0x08, 0xc3, 0x26, 0xd3, 0x13, 0xff, 0x76, 0x88, 0x5f, 0x85, 0xfc, 0x6d, + 0x3f, 0xd8, 0x71, 0x7d, 0xbb, 0x57, 0xc9, 0xf1, 0x39, 0x9f, 0x99, 0x3c, 0xe7, 0x35, 0xe9, 0x45, + 0x12, 0x7f, 0xbc, 0x0c, 0x28, 0xbc, 0xe5, 0x5a, 0x21, 0x75, 0x69, 0x37, 0xb2, 0x5c, 0x67, 0xe0, + 0x44, 0x95, 0x3c, 0x0f, 0xc9, 0x72, 0x78, 0xcb, 0x6d, 0x73, 0x75, 0x9d, 0x69, 0xb1, 0x05, 0x0b, + 0x51, 0x60, 0x7b, 0xa1, 0xdd, 0x65, 0x83, 0x59, 0x4e, 0xe8, 0xbb, 0x36, 0x0f, 0xc7, 0x02, 0x9f, + 0x72, 0x65, 0xf2, 0x94, 0xe6, 0xa8, 0x4b, 0x2d, 0xee, 0x41, 0xe6, 0xa3, 0x09, 0x5a, 0xfc, 0x32, + 0x2c, 0x84, 0x3b, 0xce, 0xd0, 0xe2, 0xe3, 0x58, 0x43, 0xd7, 0xf6, 0xac, 0xae, 0xdd, 0xdd, 0xa6, + 0x15, 0xe0, 0xb0, 0x31, 0x33, 0xf2, 0x7d, 0x6f, 0xb9, 0xb6, 0x57, 0x65, 0x16, 0xf5, 0x35, 0x28, + 0x1f, 0xe4, 0x11, 0xcf, 0x42, 0xc9, 0xbc, 0xde, 0xd2, 0x2d, 0xcd, 0xd8, 0xb0, 0x0c, 0xad, 0xa1, + 0xa3, 0x23, 0xb8, 0x04, 0x05, 0xae, 0x6a, 0x1a, 0xf5, 0xeb, 0x48, 0xc1, 0x53, 0x90, 0xd6, 0xea, + 0x75, 0x94, 0x52, 0xcf, 0x43, 0x3e, 0x26, 0x04, 0xcf, 0x40, 0xb1, 0x63, 0xb4, 0x5b, 0x7a, 0xb5, + 0xb6, 0x59, 0xd3, 0x37, 0xd0, 0x11, 0x9c, 0x87, 0x4c, 0xb3, 0x6e, 0xb6, 0x90, 0x22, 0x5a, 0x5a, + 0x0b, 0xa5, 0x58, 0xcf, 0x8d, 0x75, 0x0d, 0xa5, 0xd5, 0x5f, 0x28, 0x30, 0x3f, 0x09, 0x18, 0x2e, + 0xc2, 0xd4, 0x86, 0xbe, 0xa9, 0x75, 0xea, 0x26, 0x3a, 0x82, 0xe7, 0x60, 0x86, 0xe8, 0x2d, 0x5d, + 0x33, 0xb5, 0xf5, 0xba, 0x6e, 0x11, 0x5d, 0xdb, 0x40, 0x0a, 0xc6, 0x50, 0x66, 0x2d, 0xab, 0xda, + 0x6c, 0x34, 0x6a, 0xa6, 0xa9, 0x6f, 0xa0, 0x14, 0x9e, 0x07, 0xc4, 0x75, 0x1d, 0x63, 0xa4, 0x4d, + 0x63, 0x04, 0xd3, 0x6d, 0x9d, 0xd4, 0xb4, 0x7a, 0xed, 0x06, 0x1b, 0x00, 0x65, 0xf0, 0x73, 0xf0, + 0x74, 0xb5, 0x69, 0xb4, 0x6b, 0x6d, 0x53, 0x37, 0x4c, 0xab, 0x6d, 0x68, 0xad, 0xf6, 0x1b, 0x4d, + 0x93, 0x8f, 0x2c, 0xc0, 0x65, 0x71, 0x19, 0x40, 0xeb, 0x98, 0x4d, 0x31, 0x0e, 0xca, 0x5d, 0xca, + 0xe4, 0x15, 0x94, 0xba, 0x94, 0xc9, 0xa7, 0x50, 0xfa, 0x52, 0x26, 0x9f, 0x46, 0x19, 0xf5, 0xdd, + 0x14, 0x64, 0x39, 0x57, 0x2c, 0xdd, 0x8d, 0x25, 0x31, 0xde, 0x4e, 0x1e, 0xfd, 0xd4, 0x7d, 0x1e, + 0x7d, 0x9e, 0x31, 0x65, 0x12, 0x12, 0x02, 0x3e, 0x01, 0x05, 0x3f, 0xe8, 0x5b, 0xc2, 0x22, 0xd2, + 0x67, 0xde, 0x0f, 0xfa, 0x3c, 0xcf, 0xb2, 0xd4, 0xc5, 0xb2, 0xee, 0x96, 0x1d, 0x52, 0x1e, 0xc1, + 0x05, 0x92, 0xc8, 0xf8, 0x38, 0x30, 0x3f, 0x8b, 0xaf, 0x23, 0xc7, 0x6d, 0x53, 0x7e, 0xd0, 0x37, + 0xd8, 0x52, 0x9e, 0x87, 0x52, 0xd7, 0x77, 0x77, 0x07, 0x9e, 0xe5, 0x52, 0xaf, 0x1f, 0x6d, 0x57, + 0xa6, 0x96, 0x94, 0xe5, 0x12, 0x99, 0x16, 0xca, 0x3a, 0xd7, 0xe1, 0x0a, 0x4c, 0x75, 0xb7, 0xed, + 0x20, 0xa4, 0x22, 0x6a, 0x4b, 0x24, 0x16, 0xf9, 0xac, 0xb4, 0xeb, 0x0c, 0x6c, 0x37, 0xe4, 0x11, + 0x5a, 0x22, 0x89, 0xcc, 0x40, 0xdc, 0x74, 0xed, 0x7e, 0xc8, 0x23, 0xab, 0x44, 0x84, 0xa0, 0xfe, + 0x1f, 0xa4, 0x89, 0x7f, 0x9b, 0x0d, 0x29, 0x26, 0x0c, 0x2b, 0xca, 0x52, 0x7a, 0x19, 0x93, 0x58, + 0x64, 0xd9, 0x5d, 0x26, 0x38, 0x91, 0xf7, 0xe2, 0x94, 0xf6, 0xbe, 0x02, 0x45, 0x1e, 0x98, 0x84, + 0x86, 0xbb, 0x6e, 0xc4, 0x12, 0xa1, 0xcc, 0x00, 0xca, 0x81, 0x44, 0xc8, 0x69, 0x27, 0xd2, 0xc6, + 0xf0, 0xb1, 0x87, 0xda, 0xb2, 0x6f, 0xde, 0xa4, 0xdd, 0x88, 0x8a, 0x7c, 0x9f, 0x21, 0xd3, 0x4c, + 0xa9, 0x49, 0x1d, 0x23, 0xd6, 0xf1, 0x42, 0x1a, 0x44, 0x96, 0xd3, 0xe3, 0x94, 0x67, 0x48, 0x5e, + 0x28, 0x6a, 0x3d, 0xfc, 0x0c, 0x64, 0x78, 0x5a, 0xc8, 0xf0, 0x59, 0x40, 0xce, 0x42, 0xfc, 0xdb, + 0x84, 0xeb, 0x2f, 0x65, 0xf2, 0x59, 0x94, 0x53, 0x5f, 0x87, 0x69, 0xbe, 0xb8, 0x6b, 0x76, 0xe0, + 0x39, 0x5e, 0x9f, 0x9f, 0x72, 0x7e, 0x4f, 0x6c, 0x7b, 0x89, 0xf0, 0x36, 0xc3, 0x3c, 0xa0, 0x61, + 0x68, 0xf7, 0xa9, 0x3c, 0x75, 0x62, 0x51, 0xfd, 0x59, 0x1a, 0x8a, 0xed, 0x28, 0xa0, 0xf6, 0x80, + 0x1f, 0x60, 0xf8, 0x75, 0x80, 0x30, 0xb2, 0x23, 0x3a, 0xa0, 0x5e, 0x14, 0xe3, 0x7b, 0x4a, 0xce, + 0x3c, 0xe6, 0xb7, 0xda, 0x8e, 0x9d, 0xc8, 0x98, 0x3f, 0x5e, 0x83, 0x22, 0x65, 0x66, 0x2b, 0x62, + 0x07, 0xa1, 0x4c, 0xb6, 0xb3, 0x71, 0xe6, 0x48, 0x4e, 0x48, 0x02, 0x34, 0x69, 0x2f, 0x7e, 0x90, + 0x82, 0x42, 0x32, 0x1a, 0xd6, 0x20, 0xdf, 0xb5, 0x23, 0xda, 0xf7, 0x83, 0x7d, 0x79, 0x3e, 0x9d, + 0xbc, 0xdf, 0xec, 0xab, 0x55, 0xe9, 0x4c, 0x92, 0x6e, 0xf8, 0x69, 0x10, 0x87, 0xbe, 0x88, 0x3a, + 0x81, 0xb7, 0xc0, 0x35, 0x3c, 0xee, 0x5e, 0x05, 0x3c, 0x0c, 0x9c, 0x81, 0x1d, 0xec, 0x5b, 0x3b, + 0x74, 0x3f, 0xce, 0xe5, 0xe9, 0x09, 0x3b, 0x89, 0xa4, 0xdf, 0x65, 0xba, 0x2f, 0xb3, 0xcf, 0xf9, + 0x83, 0x7d, 0x65, 0xb4, 0x1c, 0xde, 0x9f, 0xb1, 0x9e, 0xfc, 0x74, 0x0c, 0xe3, 0x73, 0x30, 0xcb, + 0x03, 0x8b, 0x35, 0xd5, 0x97, 0x20, 0x1f, 0x2f, 0x1e, 0x17, 0x20, 0xab, 0x07, 0x81, 0x1f, 0xa0, + 0x23, 0x3c, 0x09, 0x35, 0xea, 0x22, 0x8f, 0x6d, 0x6c, 0xb0, 0x3c, 0xf6, 0x9b, 0x54, 0x72, 0x18, + 0x11, 0x7a, 0x6b, 0x97, 0x86, 0x11, 0xfe, 0x1c, 0xcc, 0x51, 0x1e, 0x42, 0xce, 0x1e, 0xb5, 0xba, + 0xbc, 0x72, 0x61, 0x01, 0xa4, 0x70, 0xbe, 0x67, 0x56, 0x45, 0xa1, 0x15, 0x57, 0x34, 0x64, 0x36, + 0xf1, 0x95, 0xaa, 0x1e, 0xd6, 0x61, 0xce, 0x19, 0x0c, 0x68, 0xcf, 0xb1, 0xa3, 0xf1, 0x01, 0xc4, + 0x86, 0x2d, 0xc4, 0x07, 0xfb, 0x81, 0xc2, 0x88, 0xcc, 0x26, 0x3d, 0x92, 0x61, 0x4e, 0x42, 0x2e, + 0xe2, 0x45, 0x1c, 0x8f, 0xdd, 0xe2, 0x5a, 0x29, 0x4e, 0x28, 0x5c, 0x49, 0xa4, 0x11, 0xbf, 0x04, + 0xa2, 0x24, 0xe4, 0xa9, 0x63, 0x14, 0x10, 0xa3, 0x93, 0x9e, 0x08, 0x3b, 0x3e, 0x09, 0xe5, 0x03, + 0x67, 0x50, 0x8f, 0x13, 0x96, 0x26, 0xa5, 0xf1, 0x03, 0xa5, 0x87, 0x4f, 0xc1, 0x94, 0x2f, 0xce, + 0x1f, 0x9e, 0x54, 0x46, 0x2b, 0x3e, 0x78, 0x38, 0x91, 0xd8, 0x4b, 0xfd, 0x0c, 0xcc, 0x24, 0x0c, + 0x86, 0x43, 0xdf, 0x0b, 0x29, 0x5e, 0x81, 0x5c, 0xc0, 0x1f, 0x67, 0xc9, 0x1a, 0x96, 0x43, 0x8c, + 0x3d, 0xe8, 0x44, 0x7a, 0xa8, 0x3d, 0x98, 0x11, 0x9a, 0x6b, 0x4e, 0xb4, 0xcd, 0x37, 0x0a, 0x9f, + 0x84, 0x2c, 0x65, 0x8d, 0x7b, 0x38, 0x27, 0xad, 0x2a, 0xb7, 0x13, 0x61, 0x1d, 0x9b, 0x25, 0xf5, + 0xc0, 0x59, 0xfe, 0x96, 0x82, 0x39, 0xb9, 0xca, 0x75, 0x3b, 0xea, 0x6e, 0x3f, 0xa6, 0x9b, 0xfd, + 0xdf, 0x30, 0xc5, 0xf4, 0x4e, 0xf2, 0x60, 0x4c, 0xd8, 0xee, 0xd8, 0x83, 0x6d, 0xb8, 0x1d, 0x5a, + 0x63, 0xbb, 0x2b, 0x6b, 0xa0, 0x92, 0x1d, 0x8e, 0x1d, 0xc0, 0x13, 0xe2, 0x22, 0xf7, 0x80, 0xb8, + 0x98, 0x7a, 0xa8, 0xb8, 0xd8, 0x80, 0xf9, 0x83, 0x8c, 0xcb, 0xe0, 0xf8, 0x1f, 0x98, 0x12, 0x9b, + 0x12, 0xa7, 0xc0, 0x49, 0xfb, 0x16, 0xbb, 0xa8, 0xbf, 0x4b, 0xc1, 0xbc, 0xcc, 0x4e, 0x9f, 0x8c, + 0xc7, 0x74, 0x8c, 0xe7, 0xec, 0xc3, 0xf0, 0xfc, 0x90, 0xfb, 0xa7, 0x56, 0x61, 0xe1, 0x1e, 0x1e, + 0x1f, 0xe1, 0x61, 0xfd, 0x50, 0x81, 0xe9, 0x75, 0xda, 0x77, 0xbc, 0xc7, 0x74, 0x17, 0xc6, 0xc8, + 0xcd, 0x3c, 0x54, 0x10, 0x9f, 0x83, 0x92, 0xc4, 0x2b, 0xd9, 0x3a, 0xcc, 0xb6, 0x32, 0x89, 0xed, + 0xbf, 0x28, 0x50, 0xaa, 0xfa, 0x83, 0x81, 0x13, 0x3d, 0xa6, 0x4c, 0x1d, 0xc6, 0x99, 0x99, 0x84, + 0x13, 0x41, 0x39, 0x86, 0x29, 0x08, 0x52, 0xff, 0xaa, 0xc0, 0x0c, 0xf1, 0x5d, 0x77, 0xcb, 0xee, + 0xee, 0x3c, 0xd9, 0xd8, 0x31, 0xa0, 0x11, 0x50, 0x89, 0xfe, 0x1f, 0x0a, 0x94, 0x5b, 0x01, 0x1d, + 0xda, 0x01, 0x7d, 0xa2, 0xc1, 0xb3, 0x4a, 0xb8, 0x17, 0xc9, 0x1a, 0xa2, 0x40, 0x78, 0x5b, 0x9d, + 0x85, 0x99, 0x04, 0xbb, 0xe4, 0xe3, 0x8f, 0x0a, 0x2c, 0x88, 0x00, 0x91, 0x96, 0xde, 0x63, 0x4a, + 0x4b, 0x8c, 0x37, 0x33, 0x86, 0xb7, 0x02, 0x47, 0xef, 0xc5, 0x26, 0x61, 0xbf, 0x95, 0x82, 0x63, + 0x71, 0x6c, 0x3c, 0xe6, 0xc0, 0xff, 0x8d, 0x78, 0x58, 0x84, 0xca, 0x61, 0x12, 0x24, 0x43, 0xef, + 0xa4, 0xa0, 0x52, 0x0d, 0xa8, 0x1d, 0xd1, 0xb1, 0x5a, 0xe4, 0xc9, 0x89, 0x0d, 0xfc, 0x32, 0x4c, + 0x0f, 0xed, 0x20, 0x72, 0xba, 0xce, 0xd0, 0x66, 0x6f, 0x7b, 0x59, 0x5e, 0xea, 0xdc, 0x33, 0xc0, + 0x01, 0x17, 0xf5, 0x04, 0x1c, 0x9f, 0xc0, 0x88, 0xe4, 0xeb, 0x9f, 0x0a, 0xe0, 0x76, 0x64, 0x07, + 0xd1, 0x27, 0xe0, 0x54, 0x99, 0x18, 0x4c, 0x0b, 0x30, 0x77, 0x00, 0xff, 0x38, 0x2f, 0x34, 0xfa, + 0x44, 0x9c, 0x38, 0x1f, 0xc9, 0xcb, 0x38, 0x7e, 0xc9, 0xcb, 0x9f, 0x15, 0x58, 0xac, 0xfa, 0xe2, + 0xfb, 0xde, 0x13, 0xf9, 0x84, 0xa9, 0x4f, 0xc3, 0x89, 0x89, 0x00, 0x25, 0x01, 0x7f, 0x52, 0xe0, + 0x28, 0xa1, 0x76, 0xef, 0xc9, 0x04, 0x7f, 0x05, 0x8e, 0x1d, 0x02, 0x27, 0x2b, 0xd4, 0x73, 0x90, + 0x1f, 0xd0, 0xc8, 0xee, 0xd9, 0x91, 0x2d, 0x21, 0x2d, 0xc6, 0xe3, 0x8e, 0xbc, 0x1b, 0xd2, 0x83, + 0x24, 0xbe, 0xea, 0x07, 0x29, 0x98, 0xe3, 0xb5, 0xee, 0xa7, 0x2f, 0x5a, 0x93, 0xdf, 0x05, 0xde, + 0x51, 0x60, 0xfe, 0x20, 0x41, 0xc9, 0x3b, 0xc1, 0x7f, 0xfa, 0x7b, 0xc5, 0x84, 0x84, 0x90, 0x9e, + 0x54, 0x82, 0xfe, 0x3e, 0x05, 0x95, 0xf1, 0x25, 0x7d, 0xfa, 0x6d, 0xe3, 0xe0, 0xb7, 0x8d, 0x8f, + 0xfd, 0x31, 0xeb, 0x5d, 0x05, 0x8e, 0x4f, 0x20, 0xf4, 0xe3, 0x6d, 0xf4, 0xd8, 0x17, 0x8e, 0xd4, + 0x03, 0xbf, 0x70, 0x3c, 0xec, 0x56, 0xff, 0x41, 0x81, 0xf9, 0x86, 0xf8, 0xb0, 0x2c, 0xde, 0xe3, + 0x1f, 0xdf, 0x6c, 0xc6, 0xbf, 0x1d, 0x67, 0x46, 0x7f, 0x4e, 0xd4, 0x2a, 0x2c, 0xdc, 0x03, 0xed, + 0x11, 0xbe, 0x4d, 0xfc, 0x5d, 0x81, 0x59, 0x39, 0x8a, 0xf6, 0xd8, 0x16, 0x02, 0x13, 0xd8, 0xc1, + 0xcf, 0x40, 0xda, 0xe9, 0xc5, 0x15, 0xe4, 0xc1, 0x1f, 0xc3, 0xcc, 0xa0, 0x5e, 0x00, 0x3c, 0x8e, + 0xfb, 0x11, 0xa8, 0xe3, 0xb5, 0x15, 0x23, 0xfe, 0x0d, 0x6a, 0xbb, 0x51, 0x9c, 0x40, 0xd4, 0x9f, + 0xa7, 0xa0, 0x44, 0x98, 0xc6, 0x19, 0xd0, 0x76, 0x64, 0x47, 0x21, 0x7e, 0x0e, 0xa6, 0xb7, 0xb9, + 0x8b, 0x35, 0x7a, 0x0e, 0x0a, 0xa4, 0x28, 0x74, 0xe2, 0xe3, 0xed, 0x1a, 0x2c, 0x84, 0xb4, 0xeb, + 0x7b, 0xbd, 0xd0, 0xda, 0xa2, 0xdb, 0x8e, 0xd7, 0xb3, 0x06, 0x76, 0x18, 0xd1, 0x80, 0x33, 0x56, + 0x22, 0x73, 0xd2, 0xb8, 0xce, 0x6d, 0x0d, 0x6e, 0xc2, 0xa7, 0x61, 0x7e, 0xcb, 0xf1, 0x5c, 0xbf, + 0x6f, 0x0d, 0x5d, 0x7b, 0x9f, 0x06, 0xa1, 0xd5, 0xf5, 0x77, 0x3d, 0x41, 0x55, 0x96, 0x60, 0x61, + 0x6b, 0x09, 0x53, 0x95, 0x59, 0xf0, 0x0d, 0x58, 0x99, 0x38, 0x8b, 0x75, 0xd3, 0x71, 0x23, 0x1a, + 0xd0, 0x9e, 0x15, 0xd0, 0xa1, 0xeb, 0x74, 0xc5, 0x5f, 0x56, 0x51, 0x4c, 0xbd, 0x38, 0x61, 0xea, + 0x4d, 0xe9, 0x4e, 0x46, 0xde, 0xf8, 0x04, 0x14, 0xba, 0xc3, 0x5d, 0x6b, 0x97, 0xff, 0xd2, 0x61, + 0x69, 0x45, 0x21, 0xf9, 0xee, 0x70, 0xb7, 0xc3, 0x64, 0x8c, 0x20, 0x7d, 0x6b, 0x28, 0xb2, 0x89, + 0x42, 0x58, 0x53, 0xfd, 0x50, 0x81, 0xb2, 0xd6, 0xef, 0x07, 0xb4, 0x6f, 0x47, 0x92, 0xa6, 0xd3, + 0x30, 0x2f, 0x28, 0xd9, 0xb7, 0xe4, 0x5d, 0x0a, 0x81, 0x47, 0x11, 0x78, 0xa4, 0x4d, 0xdc, 0xa4, + 0x10, 0x78, 0xce, 0xc2, 0xd1, 0x5d, 0x6f, 0x62, 0x9f, 0x14, 0xef, 0x33, 0x9f, 0x58, 0xc7, 0x7b, + 0xfd, 0x3f, 0x1c, 0x9f, 0xcc, 0xc2, 0xc0, 0x11, 0x37, 0x1d, 0x4a, 0xe4, 0xe8, 0x04, 0xd0, 0x0d, + 0xc7, 0xbb, 0x4f, 0x57, 0xfb, 0x0e, 0xe7, 0xeb, 0x23, 0xba, 0xda, 0x77, 0xd4, 0x5f, 0x26, 0x9f, + 0x64, 0xe3, 0x70, 0x49, 0xd2, 0x63, 0x1c, 0xe3, 0xca, 0xfd, 0x62, 0xbc, 0x02, 0x53, 0x21, 0x0d, + 0xf6, 0x1c, 0xaf, 0xcf, 0xc1, 0xe5, 0x49, 0x2c, 0xe2, 0x36, 0xbc, 0x28, 0xb1, 0xd3, 0x3b, 0x11, + 0x0d, 0x3c, 0xdb, 0x75, 0xf7, 0x2d, 0xf1, 0xe6, 0xe8, 0x45, 0xb4, 0x67, 0x8d, 0x6e, 0x7e, 0x88, + 0x14, 0xf9, 0xbc, 0xf0, 0xd6, 0x13, 0x67, 0x92, 0xf8, 0x9a, 0xc9, 0x9d, 0x90, 0xd7, 0xa0, 0x1c, + 0xc8, 0x20, 0xb6, 0x42, 0xb6, 0x3d, 0xb2, 0x32, 0x98, 0x8f, 0xff, 0x29, 0x8d, 0x47, 0x38, 0x29, + 0x05, 0x07, 0x02, 0xfe, 0x3c, 0x4c, 0xcb, 0x15, 0xd9, 0xae, 0x63, 0x8f, 0x2a, 0x85, 0x7b, 0xae, + 0xc3, 0x68, 0xcc, 0x48, 0xe4, 0xc5, 0x19, 0x2e, 0x5c, 0xca, 0xe4, 0x73, 0x68, 0x4a, 0xfd, 0x95, + 0x02, 0x73, 0x13, 0xca, 0xae, 0xa4, 0xa6, 0x53, 0xc6, 0x5e, 0x19, 0xff, 0x17, 0xb2, 0xfc, 0x77, + 0x9f, 0xfc, 0x81, 0x7c, 0xec, 0x70, 0xd5, 0xc6, 0x7f, 0xcd, 0x11, 0xe1, 0xc5, 0x9e, 0x45, 0x8e, + 0xa9, 0xcb, 0xdf, 0x19, 0xe3, 0x53, 0xa3, 0xc8, 0x74, 0xe2, 0x35, 0xf2, 0xf0, 0x4b, 0x68, 0xe6, + 0x81, 0x2f, 0xa1, 0x2b, 0xdf, 0x4b, 0x43, 0xa1, 0xb1, 0xdf, 0xbe, 0xe5, 0x6e, 0xba, 0x76, 0x9f, + 0xff, 0x3b, 0x6b, 0xb4, 0xcc, 0xeb, 0xe8, 0x08, 0x9e, 0x85, 0x92, 0xd1, 0x34, 0x2d, 0xa3, 0x53, + 0xaf, 0x5b, 0x9b, 0x75, 0xed, 0x22, 0x52, 0x30, 0x82, 0xe9, 0x16, 0xa9, 0x59, 0x97, 0xf5, 0xeb, + 0x42, 0x93, 0xc2, 0x73, 0x30, 0xd3, 0x31, 0x6a, 0x57, 0x3a, 0xfa, 0x48, 0x99, 0xc1, 0x0b, 0x30, + 0xdb, 0xe8, 0xd4, 0xcd, 0x5a, 0xab, 0x3e, 0xa6, 0xce, 0xe3, 0x12, 0x14, 0xd6, 0xeb, 0xcd, 0x75, + 0x21, 0x22, 0x36, 0x7e, 0xc7, 0x68, 0xd7, 0x2e, 0x1a, 0xfa, 0x86, 0x50, 0x2d, 0x31, 0xd5, 0x0d, + 0x9d, 0x34, 0x37, 0x6b, 0xf1, 0x94, 0x17, 0x30, 0x82, 0xe2, 0x7a, 0xcd, 0xd0, 0x88, 0x1c, 0xe5, + 0xae, 0x82, 0xcb, 0x50, 0xd0, 0x8d, 0x4e, 0x43, 0xca, 0x29, 0x5c, 0x81, 0x39, 0xad, 0x63, 0x36, + 0xad, 0x9a, 0x51, 0x25, 0x7a, 0x43, 0x37, 0x4c, 0x69, 0xc9, 0xe0, 0x39, 0x28, 0x9b, 0xb5, 0x86, + 0xde, 0x36, 0xb5, 0x46, 0x4b, 0x2a, 0xd9, 0x2a, 0xf2, 0x6d, 0x3d, 0xf6, 0x41, 0x78, 0x11, 0x16, + 0x8c, 0xa6, 0x25, 0xef, 0x21, 0x58, 0x57, 0xb5, 0x7a, 0x47, 0x97, 0xb6, 0x25, 0x7c, 0x0c, 0x70, + 0xd3, 0xb0, 0x3a, 0xad, 0x0d, 0xcd, 0xd4, 0x2d, 0xa3, 0x79, 0x4d, 0x1a, 0x2e, 0xe0, 0x32, 0xe4, + 0x47, 0x2b, 0xb8, 0xcb, 0x58, 0x28, 0xb5, 0x34, 0x62, 0x8e, 0xc0, 0xde, 0xbd, 0xcb, 0xc8, 0x82, + 0x8b, 0xa4, 0xd9, 0x69, 0x8d, 0xdc, 0x66, 0xa1, 0x28, 0xc9, 0x92, 0xaa, 0x0c, 0x53, 0xad, 0xd7, + 0x8c, 0x6a, 0xb2, 0xbe, 0xbb, 0xf9, 0xc5, 0x14, 0x52, 0x56, 0x76, 0x20, 0xc3, 0xb7, 0x23, 0x0f, + 0x19, 0xa3, 0x69, 0xe8, 0xe8, 0x08, 0x9e, 0x01, 0xa8, 0xb5, 0x6b, 0x86, 0xa9, 0x5f, 0x24, 0x5a, + 0x9d, 0xc1, 0xe6, 0x8a, 0x98, 0x40, 0x86, 0x76, 0x1a, 0xa6, 0x6a, 0xed, 0xcd, 0x7a, 0x53, 0x33, + 0x25, 0xcc, 0x5a, 0xfb, 0x4a, 0xa7, 0x69, 0x32, 0x23, 0xc2, 0x45, 0xc8, 0xd5, 0xda, 0xa6, 0xfe, + 0xa6, 0xc9, 0x70, 0x71, 0x9b, 0x60, 0x15, 0xdd, 0xbd, 0xb0, 0xf2, 0x5e, 0x1a, 0x32, 0xfc, 0x4a, + 0x57, 0x09, 0x0a, 0x7c, 0xb7, 0xcd, 0xeb, 0x2d, 0x36, 0x65, 0x01, 0x32, 0x35, 0xc3, 0x3c, 0x8f, + 0xbe, 0x90, 0xc2, 0x00, 0xd9, 0x0e, 0x6f, 0x7f, 0x31, 0xc7, 0xda, 0x35, 0xc3, 0x7c, 0xf9, 0x1c, + 0x7a, 0x2b, 0xc5, 0x86, 0xed, 0x08, 0xe1, 0x4b, 0xb1, 0x61, 0xed, 0x2c, 0x7a, 0x3b, 0x31, 0xac, + 0x9d, 0x45, 0x5f, 0x8e, 0x0d, 0x67, 0xd6, 0xd0, 0x57, 0x12, 0xc3, 0x99, 0x35, 0xf4, 0xd5, 0xd8, + 0x70, 0xee, 0x2c, 0xfa, 0x5a, 0x62, 0x38, 0x77, 0x16, 0x7d, 0x3d, 0xc7, 0xb0, 0x70, 0x24, 0x67, + 0xd6, 0xd0, 0x37, 0xf2, 0x89, 0x74, 0xee, 0x2c, 0xfa, 0x66, 0x9e, 0xed, 0x7f, 0xb2, 0xab, 0xe8, + 0x5b, 0x88, 0x2d, 0x93, 0x6d, 0x10, 0xfa, 0x36, 0x6f, 0x32, 0x13, 0xfa, 0x0e, 0x62, 0x18, 0x99, + 0x96, 0x8b, 0xef, 0x70, 0xcb, 0x75, 0x5d, 0x23, 0xe8, 0xbb, 0x39, 0x71, 0xed, 0xa4, 0x5a, 0x6b, + 0x68, 0x75, 0x84, 0x79, 0x0f, 0xc6, 0xca, 0xf7, 0x4f, 0xb3, 0x26, 0x0b, 0x4f, 0xf4, 0x83, 0x16, + 0x9b, 0xf0, 0xaa, 0x46, 0xaa, 0x6f, 0x68, 0x04, 0xfd, 0xf0, 0x34, 0x9b, 0xf0, 0xaa, 0x46, 0x24, + 0x5f, 0x3f, 0x6a, 0x31, 0x47, 0x6e, 0x7a, 0xf7, 0x34, 0x5b, 0xb4, 0xd4, 0xff, 0xb8, 0x85, 0xf3, + 0x90, 0x5e, 0xaf, 0x99, 0xe8, 0x3d, 0x3e, 0x1b, 0x0b, 0x51, 0xf4, 0x13, 0xc4, 0x94, 0x6d, 0xdd, + 0x44, 0xef, 0x33, 0x65, 0xd6, 0xec, 0xb4, 0xea, 0x3a, 0x7a, 0x8a, 0x2d, 0xee, 0xa2, 0xde, 0x6c, + 0xe8, 0x26, 0xb9, 0x8e, 0x7e, 0xca, 0xdd, 0x2f, 0xb5, 0x9b, 0x06, 0xfa, 0x00, 0xe1, 0x32, 0x80, + 0xfe, 0x66, 0x8b, 0xe8, 0xed, 0x76, 0xad, 0x69, 0xa0, 0x67, 0x57, 0x36, 0x01, 0xdd, 0x9b, 0x0e, + 0x18, 0x80, 0x8e, 0x71, 0xd9, 0x68, 0x5e, 0x33, 0xd0, 0x11, 0x26, 0xb4, 0x88, 0xde, 0xd2, 0x88, + 0x8e, 0x14, 0x0c, 0x90, 0x93, 0x97, 0x59, 0x52, 0x78, 0x1a, 0xf2, 0xa4, 0x59, 0xaf, 0xaf, 0x6b, + 0xd5, 0xcb, 0x28, 0xbd, 0xfe, 0x0a, 0xcc, 0x38, 0xfe, 0xea, 0x9e, 0x13, 0xd1, 0x30, 0x14, 0x97, + 0x06, 0x6f, 0xa8, 0x52, 0x72, 0xfc, 0x53, 0xa2, 0x75, 0xaa, 0xef, 0x9f, 0xda, 0x8b, 0x4e, 0x71, + 0xeb, 0x29, 0x9e, 0x31, 0xb6, 0x72, 0x5c, 0x38, 0xf3, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x69, + 0x36, 0x21, 0xf2, 0x92, 0x28, 0x00, 0x00, } diff --git a/go/vt/vtgate/engine/ordered_aggregate.go b/go/vt/vtgate/engine/ordered_aggregate.go index 9ec98b35d22..0cf008afb73 100644 --- a/go/vt/vtgate/engine/ordered_aggregate.go +++ b/go/vt/vtgate/engine/ordered_aggregate.go @@ -156,7 +156,6 @@ func (oa *OrderedAggregate) execute(vcursor VCursor, bindVars map[string]*queryp out := &sqltypes.Result{ Fields: oa.convertFields(result.Fields), Rows: make([][]sqltypes.Value, 0, len(result.Rows)), - Extras: result.Extras, } // This code is similar to the one in StreamExecute. var current []sqltypes.Value diff --git a/go/vt/vtgate/grpcvtgateconn/suite_test.go b/go/vt/vtgate/grpcvtgateconn/suite_test.go index 150f39dffc4..d762191ad20 100644 --- a/go/vt/vtgate/grpcvtgateconn/suite_test.go +++ b/go/vt/vtgate/grpcvtgateconn/suite_test.go @@ -1275,7 +1275,6 @@ func testStreamExecute(t *testing.T, session *vtgateconn.VTGateSession) { wantResult := *execCase.result wantResult.RowsAffected = 0 wantResult.InsertID = 0 - wantResult.Extras = nil if !qr.Equal(&wantResult) { t.Errorf("Unexpected result from StreamExecute: got %+v want %+v", qr, wantResult) } @@ -1356,7 +1355,6 @@ func testStreamExecuteShards(t *testing.T, conn *vtgateconn.VTGateConn) { wantResult := *execCase.result wantResult.RowsAffected = 0 wantResult.InsertID = 0 - wantResult.Extras = nil if !qr.Equal(&wantResult) { t.Errorf("Unexpected result from StreamExecuteShards: got %+v want %+v", qr, wantResult) } @@ -1437,7 +1435,6 @@ func testStreamExecuteKeyRanges(t *testing.T, conn *vtgateconn.VTGateConn) { wantResult := *execCase.result wantResult.RowsAffected = 0 wantResult.InsertID = 0 - wantResult.Extras = nil if !qr.Equal(&wantResult) { t.Errorf("Unexpected result from StreamExecuteKeyRanges: got %+v want %+v", qr, wantResult) } @@ -1518,7 +1515,6 @@ func testStreamExecuteKeyspaceIds(t *testing.T, conn *vtgateconn.VTGateConn) { wantResult := *execCase.result wantResult.RowsAffected = 0 wantResult.InsertID = 0 - wantResult.Extras = nil if !qr.Equal(&wantResult) { t.Errorf("Unexpected result from StreamExecuteKeyspaceIds: got %+v want %+v", qr, wantResult) } @@ -1865,13 +1861,7 @@ var testCallerID = &vtrpcpb.CallerID{ } var testExecuteOptions = &querypb.ExecuteOptions{ - IncludedFields: querypb.ExecuteOptions_TYPE_ONLY, - IncludeEventToken: true, - CompareEventToken: &querypb.EventToken{ - Timestamp: 135, - Shard: "shrd", - Position: "pstn", - }, + IncludedFields: querypb.ExecuteOptions_TYPE_ONLY, } var execMap = map[string]struct { @@ -2196,15 +2186,6 @@ var execMap = map[string]struct { }, } -var extras = querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 123, - Shard: "sh", - Position: "po", - }, - Fresher: true, -} - var result1 = sqltypes.Result{ Fields: []*querypb.Field{ { @@ -2228,7 +2209,6 @@ var result1 = sqltypes.Result{ sqltypes.NewInt32(3), }, }, - Extras: &extras, } // streamResultFields is only the fields, sent as the first packet diff --git a/go/vt/vttablet/tabletconntest/fakequeryservice.go b/go/vt/vttablet/tabletconntest/fakequeryservice.go index 76a54fde386..a1ad9c39cb7 100644 --- a/go/vt/vttablet/tabletconntest/fakequeryservice.go +++ b/go/vt/vttablet/tabletconntest/fakequeryservice.go @@ -91,13 +91,7 @@ var TestVTGateCallerID = &querypb.VTGateCallerID{ // TestExecuteOptions is a test execute options. var TestExecuteOptions = &querypb.ExecuteOptions{ - IncludedFields: querypb.ExecuteOptions_TYPE_ONLY, - IncludeEventToken: true, - CompareEventToken: &querypb.EventToken{ - Timestamp: 9876, - Shard: "ssss", - Position: "pppp", - }, + IncludedFields: querypb.ExecuteOptions_TYPE_ONLY, ClientFoundRows: true, } @@ -385,14 +379,6 @@ var ExecuteQueryResult = sqltypes.Result{ sqltypes.TestValue(sqltypes.Char, "row2 value2"), }, }, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 456321, - Shard: "test_shard", - Position: "test_position", - }, - Fresher: true, - }, } // Execute is part of the queryservice.QueryService interface @@ -529,14 +515,6 @@ var ExecuteBatchQueryResultList = []sqltypes.Result{ sqltypes.TestValue(sqltypes.Int8, "2"), }, }, - Extras: &querypb.ResultExtras{ - EventToken: &querypb.EventToken{ - Timestamp: 456322, - Shard: "test_shard2", - Position: "test_position2", - }, - Fresher: true, - }, }, { Fields: []*querypb.Field{ diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go index 6377be170a1..0d1981f8246 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go @@ -365,7 +365,7 @@ func TestWaitForPosError(t *testing.T) { dbClient.ExpectRequest("select pos, state, message from _vt.vreplication where id=1", &sqltypes.Result{Rows: [][]sqltypes.Value{{}}}, nil) err = vre.WaitForPos(context.Background(), 1, "MariaDB/0-1-1084") - want = "unexpected result: &{[] 0 0 [[]] }" + want = "unexpected result: &{[] 0 0 [[]]}" if err == nil || err.Error() != want { t.Errorf("WaitForPos: %v, want %v", err, want) } @@ -376,7 +376,7 @@ func TestWaitForPosError(t *testing.T) { sqltypes.NewVarBinary("MariaDB/0-1-1083"), }}}, nil) err = vre.WaitForPos(context.Background(), 1, "MariaDB/0-1-1084") - want = `unexpected result: &{[] 0 0 [[VARBINARY("MariaDB/0-1-1083")] [VARBINARY("MariaDB/0-1-1083")]] }` + want = `unexpected result: &{[] 0 0 [[VARBINARY("MariaDB/0-1-1083")] [VARBINARY("MariaDB/0-1-1083")]]}` if err == nil || err.Error() != want { t.Errorf("WaitForPos: %v, want %v", err, want) } diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index e15c428708b..68b5b9cba83 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -104,11 +104,7 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { case planbuilder.PlanSelectImpossible: if qre.plan.Fields != nil { return &sqltypes.Result{ - Fields: qre.plan.Fields, - RowsAffected: 0, - InsertID: 0, - Rows: nil, - Extras: nil, + Fields: qre.plan.Fields, }, nil } } diff --git a/go/vt/vttablet/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go index 3a65508615a..9d2d13b0f12 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher.go +++ b/go/vt/vttablet/tabletserver/replication_watcher.go @@ -17,183 +17,80 @@ limitations under the License. package tabletserver import ( - "sync" "time" "golang.org/x/net/context" - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/stats" - "vitess.io/vitess/go/vt/binlog" - "vitess.io/vitess/go/vt/binlog/eventtoken" - "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" ) +// VStreamer defines the functions of VStreamer +// that the replicationWatcher needs. +type VStreamer interface { + Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error +} + // ReplicationWatcher is a tabletserver service that watches the -// replication stream. It can tell you the current event token, -// and it will trigger schema reloads if a DDL is encountered. +// replication stream. It will trigger schema reloads if a DDL +// is encountered. type ReplicationWatcher struct { - dbconfigs *dbconfigs.DBConfigs - - // Life cycle management vars - isOpen bool - cancel context.CancelFunc - wg sync.WaitGroup - watchReplication bool - se *schema.Engine + vs VStreamer - mu sync.Mutex - eventToken *querypb.EventToken + cancel context.CancelFunc } -var replOnce sync.Once - // NewReplicationWatcher creates a new ReplicationWatcher. -func NewReplicationWatcher(se *schema.Engine, config tabletenv.TabletConfig) *ReplicationWatcher { - rpw := &ReplicationWatcher{ +func NewReplicationWatcher(vs VStreamer, config tabletenv.TabletConfig) *ReplicationWatcher { + return &ReplicationWatcher{ + vs: vs, watchReplication: config.WatchReplication, - se: se, } - replOnce.Do(func() { - stats.Publish("EventTokenPosition", stats.StringFunc(func() string { - if e := rpw.EventToken(); e != nil { - return e.Position - } - return "" - })) - stats.NewGaugeFunc( - "EventTokenTimestamp", - "Replication watcher event token timestamp", - func() int64 { - if e := rpw.EventToken(); e != nil { - return e.Timestamp - } - return 0 - }) - }) - return rpw -} - -// InitDBConfig must be called before Open. -func (rpw *ReplicationWatcher) InitDBConfig(dbcfgs *dbconfigs.DBConfigs) { - rpw.dbconfigs = dbcfgs } // Open starts the ReplicationWatcher service. func (rpw *ReplicationWatcher) Open() { - if rpw.isOpen || !rpw.watchReplication { + if rpw.cancel != nil || !rpw.watchReplication { return } + ctx, cancel := context.WithCancel(tabletenv.LocalContext()) rpw.cancel = cancel - rpw.wg.Add(1) - go rpw.Process(ctx, rpw.dbconfigs) - rpw.isOpen = true + go rpw.Process(ctx) } // Close stops the ReplicationWatcher service. func (rpw *ReplicationWatcher) Close() { - if !rpw.isOpen { + if rpw.cancel == nil { return } rpw.cancel() - rpw.wg.Wait() - rpw.isOpen = false + rpw.cancel = nil } // Process processes the replication stream. -func (rpw *ReplicationWatcher) Process(ctx context.Context, dbconfigs *dbconfigs.DBConfigs) { - defer func() { - tabletenv.LogError() - rpw.wg.Done() - }() +func (rpw *ReplicationWatcher) Process(ctx context.Context) { + defer tabletenv.LogError() - for { - log.Infof("Starting a binlog Streamer from current replication position to monitor binlogs") - streamer := binlog.NewStreamer(dbconfigs.DbaWithDB(), rpw.se, nil /*clientCharset*/, mysql.Position{}, 0 /*timestamp*/, func(eventToken *querypb.EventToken, statements []binlog.FullBinlogStatement) error { - // Save the event token. - rpw.mu.Lock() - rpw.eventToken = eventToken - rpw.mu.Unlock() - - // If it's a DDL, trigger a schema reload. - for _, statement := range statements { - if statement.Statement.Category != binlogdatapb.BinlogTransaction_Statement_BL_DDL { - continue - } - err := rpw.se.Reload(ctx) - log.Infof("Streamer triggered a schema reload, with result: %v", err) - return nil - } + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "/.*", + }}, + } + for { + err := rpw.vs.Stream(ctx, "current", filter, func(events []*binlogdatapb.VEvent) error { return nil }) - - if err := streamer.Stream(ctx); err != nil { - log.Infof("Streamer stopped: %v", err) - } - select { case <-ctx.Done(): return case <-time.After(5 * time.Second): } + log.Infof("VStream ended: %v, retrying in 5 seconds", err) + time.Sleep(5 * time.Second) } } - -// ComputeExtras returns the requested ResultExtras based on the supplied options. -func (rpw *ReplicationWatcher) ComputeExtras(options *querypb.ExecuteOptions) *querypb.ResultExtras { - if options == nil { - // No options passed in. - return nil - } - - if !options.IncludeEventToken && options.CompareEventToken == nil { - // The flags that make extras exist are not there. - return nil - } - - et := rpw.EventToken() - if et == nil { - return nil - } - - var extras *querypb.ResultExtras - - // See if we need to fill in EventToken. - if options.IncludeEventToken { - extras = &querypb.ResultExtras{ - EventToken: et, - } - } - - // See if we need to compare. - if options.CompareEventToken != nil { - if eventtoken.Fresher(et, options.CompareEventToken) >= 0 { - // For a query, we are fresher if greater or equal - // to the provided compare_event_token. - if extras == nil { - extras = &querypb.ResultExtras{ - Fresher: true, - } - } else { - extras.Fresher = true - } - } - } - return extras -} - -// EventToken returns the current event token. -func (rpw *ReplicationWatcher) EventToken() *querypb.EventToken { - rpw.mu.Lock() - defer rpw.mu.Unlock() - return rpw.eventToken -} diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 259d055136d..9e3a443624e 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -228,7 +228,6 @@ func NewTabletServer(config tabletenv.TabletConfig, topoServer *topo.Server, ali tsv.hw = heartbeat.NewWriter(tsv, alias, config) tsv.hr = heartbeat.NewReader(tsv, config) tsv.txThrottler = txthrottler.CreateTxThrottlerFromTabletConfig(topoServer) - tsv.watcher = NewReplicationWatcher(tsv.se, config) // FIXME(alainjobart) could we move this to the Register method below? // So that vtcombo doesn't even call it once, on the first tablet. // And we can remove the tsOnce variable. @@ -252,6 +251,7 @@ func NewTabletServer(config tabletenv.TabletConfig, topoServer *topo.Server, ali }) // TODO(sougou): move this up once the stats naming problem is fixed. tsv.vstreamer = vstreamer.NewEngine(srvTopoServer, tsv.se) + tsv.watcher = NewReplicationWatcher(tsv.vstreamer, config) tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer, config) return tsv } @@ -339,7 +339,6 @@ func (tsv *TabletServer) InitDBConfig(target querypb.Target, dbcfgs *dbconfigs.D tsv.te.InitDBConfig(tsv.dbconfigs) tsv.hw.InitDBConfig(tsv.dbconfigs) tsv.hr.InitDBConfig(tsv.dbconfigs) - tsv.watcher.InitDBConfig(tsv.dbconfigs) tsv.vstreamer.InitDBConfig(tsv.dbconfigs.DbaWithDB()) return nil } @@ -505,6 +504,7 @@ func (tsv *TabletServer) fullStart() (err error) { } tsv.hr.Init(tsv.target) tsv.vstreamer.Open(tsv.target.Keyspace, tsv.alias.Cell) + tsv.watcher.Open() return tsv.serveNewType() } @@ -518,7 +518,6 @@ func (tsv *TabletServer) serveNewType() (err error) { if err := tsv.txThrottler.Open(tsv.target.Keyspace, tsv.target.Shard); err != nil { return err } - tsv.watcher.Close() tsv.messager.Open() tsv.hr.Close() tsv.hw.Open() @@ -562,6 +561,7 @@ func (tsv *TabletServer) StopService() { log.Infof("Executing complete shutdown.") tsv.waitForShutdown() + tsv.watcher.Close() tsv.vstreamer.Close() tsv.qe.Close() tsv.se.Close() @@ -589,6 +589,7 @@ func (tsv *TabletServer) waitForShutdown() { // It forcibly shuts down everything. func (tsv *TabletServer) closeAll() { tsv.messager.Close() + tsv.watcher.Close() tsv.vstreamer.Close() tsv.hr.Close() tsv.hw.Close() @@ -986,12 +987,10 @@ func (tsv *TabletServer) qreExecute(ctx context.Context, query string, comments tsv: tsv, tabletType: tabletType, } - extras := tsv.watcher.ComputeExtras(options) result, err = qre.Execute() if err != nil { return nil, err } - result.Extras = extras result = result.StripMetadata(sqltypes.IncludeFieldsOrDefault(options)) return result, nil diff --git a/proto/query.proto b/proto/query.proto index 4839fc31f39..ff4bf43b265 100644 --- a/proto/query.proto +++ b/proto/query.proto @@ -234,17 +234,11 @@ message BoundQuery { // ExecuteOptions is passed around for all Execute calls. message ExecuteOptions { - // This used to be exclude_field_names, which was replaced by + // 1 used to be exclude_field_names, which was replaced by // IncludedFields enum below - reserved 1; - - // If set, we will try to include an EventToken with the responses. - bool include_event_token = 2; - - // If set, the fresher field may be set as a result comparison to this token. - // This is a shortcut so the application doesn't need to care about - // comparing EventTokens. - EventToken compare_event_token = 3; + // 2 used to be include_event_token + // 3 used to be compare_event_token + reserved 1, 2, 3; enum IncludedFields { TYPE_AND_NAME = 0; @@ -345,18 +339,6 @@ message Row { bytes values = 2; } -// ResultExtras contains optional out-of-band information. Usually the -// extras are requested by adding ExecuteOptions flags. -message ResultExtras { - // event_token is populated if the include_event_token flag is set - // in ExecuteOptions. - EventToken event_token = 1; - - // If set, it means the data returned with this result is fresher - // than the compare_token passed in the ExecuteOptions. - bool fresher = 2; -} - // QueryResult is returned by Execute and ExecuteStream. // // As returned by Execute, len(fields) is always equal to len(row) @@ -367,11 +349,13 @@ message ResultExtras { // len(QueryResult[0].fields) is always equal to len(row) (for each // row in rows for each QueryResult in QueryResult[1:]). message QueryResult { + // This used to be ResultExtras. + reserved 5; + repeated Field fields = 1; uint64 rows_affected = 2; uint64 insert_id = 3; repeated Row rows = 4; - ResultExtras extras = 5; } // QueryWarning is used to convey out of band query execution warnings From 8647051ec265d1c77c3ce2f62de1b2d6e72ee054 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 11 Mar 2020 17:40:04 -0700 Subject: [PATCH 300/825] tabletserver: simplify some code Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/query_executor.go | 15 +---- .../tabletserver/replication_watcher.go | 1 + go/vt/vttablet/tabletserver/tabletserver.go | 62 ++++++++----------- 3 files changed, 29 insertions(+), 49 deletions(-) diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 68b5b9cba83..a7a695f3232 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -139,7 +139,7 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { case planbuilder.PlanSet: return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) case planbuilder.PlanPassSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible: - return qre.execDirect(conn) + return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, false) default: // handled above: // planbuilder.PlanNextval @@ -522,19 +522,6 @@ func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) { }, nil } -// execDirect is for reads inside transactions. Always send to MySQL. -func (qre *QueryExecutor) execDirect(conn *TxConnection) (*sqltypes.Result, error) { - if qre.tsv.qe.enableQueryPlanFieldCaching && qre.plan.Fields != nil { - result, err := qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, false) - if err != nil { - return nil, err - } - result.Fields = qre.plan.Fields - return result, nil - } - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, false) -} - // execSelect sends a query to mysql only if another identical query is not running. Otherwise, it waits and // reuses the result. If the plan is missng field info, it sends the query to mysql requesting full info. func (qre *QueryExecutor) execSelect() (*sqltypes.Result, error) { diff --git a/go/vt/vttablet/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go index 9d2d13b0f12..9d6966a9c03 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher.go +++ b/go/vt/vttablet/tabletserver/replication_watcher.go @@ -82,6 +82,7 @@ func (rpw *ReplicationWatcher) Process(ctx context.Context) { } for { + // VStreamer will reload the schema when it encounters a DDL. err := rpw.vs.Stream(ctx, "current", filter, func(events []*binlogdatapb.VEvent) error { return nil }) diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 9e3a443624e..b63b73b519d 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -192,14 +192,10 @@ type TabletServer struct { alias topodatapb.TabletAlias } -// RegisterFunction is a callback type to be called when we -// Register() a TabletServer -type RegisterFunction func(Controller) - // RegisterFunctions is a list of all the // RegisterFunction that will be called upon // Register() on a TabletServer -var RegisterFunctions []RegisterFunction +var RegisterFunctions []func(Controller) // NewServer creates a new TabletServer based on the command line flags. func NewServer(topoServer *topo.Server, alias topodatapb.TabletAlias) *TabletServer { @@ -717,12 +713,11 @@ func (tsv *TabletServer) Begin(ctx context.Context, target *querypb.Target, opti err = tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "Begin", "begin", nil, - target, options, true /* isBegin */, false, /* allowOnShutdown */ + target, options, false, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { startTime := time.Now() if tsv.txThrottler.Throttle() { - // TODO(erez): I think this should be RESOURCE_EXHAUSTED. - return vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "Transaction throttled") + return vterrors.Errorf(vtrpcpb.Code_RESOURCE_EXHAUSTED, "Transaction throttled") } var beginSQL string transactionID, beginSQL, err = tsv.te.Begin(ctx, options) @@ -749,7 +744,7 @@ func (tsv *TabletServer) Commit(ctx context.Context, target *querypb.Target, tra return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "Commit", "commit", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { startTime := time.Now() logStats.TransactionID = transactionID @@ -775,7 +770,7 @@ func (tsv *TabletServer) Rollback(ctx context.Context, target *querypb.Target, t return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "Rollback", "rollback", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { defer tabletenv.QueryStats.Record("ROLLBACK", time.Now()) logStats.TransactionID = transactionID @@ -789,7 +784,7 @@ func (tsv *TabletServer) Prepare(ctx context.Context, target *querypb.Target, tr return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "Prepare", "prepare", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -806,7 +801,7 @@ func (tsv *TabletServer) CommitPrepared(ctx context.Context, target *querypb.Tar return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "CommitPrepared", "commit_prepared", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -823,7 +818,7 @@ func (tsv *TabletServer) RollbackPrepared(ctx context.Context, target *querypb.T return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "RollbackPrepared", "rollback_prepared", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -840,7 +835,7 @@ func (tsv *TabletServer) CreateTransaction(ctx context.Context, target *querypb. return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "CreateTransaction", "create_transaction", nil, - target, nil, true /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -858,7 +853,7 @@ func (tsv *TabletServer) StartCommit(ctx context.Context, target *querypb.Target return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "StartCommit", "start_commit", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -876,7 +871,7 @@ func (tsv *TabletServer) SetRollback(ctx context.Context, target *querypb.Target return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "SetRollback", "set_rollback", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -894,7 +889,7 @@ func (tsv *TabletServer) ConcludeTransaction(ctx context.Context, target *queryp return tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "ConcludeTransaction", "conclude_transaction", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -911,7 +906,7 @@ func (tsv *TabletServer) ReadTransaction(ctx context.Context, target *querypb.Ta err = tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "ReadTransaction", "read_transaction", nil, - target, nil, false /* isBegin */, true, /* allowOnShutdown */ + target, nil, true, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { txe := &TxExecutor{ ctx: ctx, @@ -935,7 +930,7 @@ func (tsv *TabletServer) Execute(ctx context.Context, target *querypb.Target, sq err = tsv.execRequest( ctx, tsv.QueryTimeout.Get(), "Execute", sql, bindVariables, - target, options, false /* isBegin */, allowOnShutdown, + target, options, allowOnShutdown, func(ctx context.Context, logStats *tabletenv.LogStats) error { if bindVariables == nil { bindVariables = make(map[string]*querypb.BindVariable) @@ -1003,7 +998,7 @@ func (tsv *TabletServer) StreamExecute(ctx context.Context, target *querypb.Targ return tsv.execRequest( ctx, 0, "StreamExecute", sql, bindVariables, - target, options, false /* isBegin */, false, /* allowOnShutdown */ + target, options, false, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { if bindVariables == nil { bindVariables = make(map[string]*querypb.BindVariable) @@ -1066,10 +1061,10 @@ func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Targe // tsv.convertAndLogError. That's because the methods which returned "err", // e.g. tsv.Execute(), already called that function and therefore already // converted and logged the error. - if err = tsv.startRequest(ctx, target, false /* isBegin */, allowOnShutdown); err != nil { + if err = tsv.startRequest(ctx, target, allowOnShutdown); err != nil { return nil, err } - defer tsv.endRequest(false) + defer tsv.endRequest() defer tsv.handlePanicAndSendLogStats("batch", nil, nil) if options == nil { @@ -1159,7 +1154,7 @@ func (tsv *TabletServer) beginWaitForSameRangeTransactions(ctx context.Context, // -queryserver-config-txpool-timeout (defaults to 1s) to limit the waiting. ctx, tsv.QueryTimeout.Get(), "", "waitForSameRangeTransactions", nil, - target, options, true /* isBegin */, false, /* allowOnShutdown */ + target, options, false, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { k, table := tsv.computeTxSerializerKey(ctx, logStats, sql, bindVariables) if k == "" { @@ -1233,7 +1228,7 @@ func (tsv *TabletServer) MessageStream(ctx context.Context, target *querypb.Targ return tsv.execRequest( ctx, 0, "MessageStream", "stream", nil, - target, nil, false /* isBegin */, false, /* allowOnShutdown */ + target, nil, false, /* allowOnShutdown */ func(ctx context.Context, logStats *tabletenv.LogStats) error { plan, err := tsv.qe.GetMessageStreamPlan(name) if err != nil { @@ -1285,10 +1280,10 @@ func (tsv *TabletServer) PurgeMessages(ctx context.Context, target *querypb.Targ } func (tsv *TabletServer) execDML(ctx context.Context, target *querypb.Target, queryGenerator func() (string, map[string]*querypb.BindVariable, error)) (count int64, err error) { - if err = tsv.startRequest(ctx, target, true /* isBegin */, false /* allowOnShutdown */); err != nil { + if err = tsv.startRequest(ctx, target, false /* allowOnShutdown */); err != nil { return 0, err } - defer tsv.endRequest(true) + defer tsv.endRequest() defer tsv.handlePanicAndSendLogStats("ack", nil, nil) query, bv, err := queryGenerator() @@ -1356,7 +1351,7 @@ func (tsv *TabletServer) VStreamResults(ctx context.Context, target *querypb.Tar func (tsv *TabletServer) execRequest( ctx context.Context, timeout time.Duration, requestName, sql string, bindVariables map[string]*querypb.BindVariable, - target *querypb.Target, options *querypb.ExecuteOptions, isBegin, allowOnShutdown bool, + target *querypb.Target, options *querypb.ExecuteOptions, allowOnShutdown bool, exec func(ctx context.Context, logStats *tabletenv.LogStats) error, ) (err error) { span, ctx := trace.NewSpan(ctx, "TabletServer."+requestName) @@ -1376,14 +1371,14 @@ func (tsv *TabletServer) execRequest( logStats.OriginalSQL = sql logStats.BindVariables = bindVariables defer tsv.handlePanicAndSendLogStats(sql, bindVariables, logStats) - if err = tsv.startRequest(ctx, target, isBegin, allowOnShutdown); err != nil { + if err = tsv.startRequest(ctx, target, allowOnShutdown); err != nil { return err } ctx, cancel := withTimeout(ctx, timeout, options) defer func() { cancel() - tsv.endRequest(isBegin) + tsv.endRequest() }() err = exec(ctx, logStats) @@ -1740,11 +1735,8 @@ func (tsv *TabletServer) Close(ctx context.Context) error { // the request (a waitgroup) as started. Every startRequest requires // one and only one corresponding endRequest. When the service shuts // down, StopService will wait on this waitgroup to ensure that there -// are no requests in flight. For begin requests, isBegin must be set -// to true, which increments an additional waitgroup. During state -// transitions, this waitgroup will be checked to make sure that no -// such statements are in-flight while we resolve the tx pool. -func (tsv *TabletServer) startRequest(ctx context.Context, target *querypb.Target, isBegin, allowOnShutdown bool) (err error) { +// are no requests in flight. +func (tsv *TabletServer) startRequest(ctx context.Context, target *querypb.Target, allowOnShutdown bool) (err error) { tsv.mu.Lock() defer tsv.mu.Unlock() if tsv.state == StateServing { @@ -1781,7 +1773,7 @@ ok: } // endRequest unregisters the current request (a waitgroup) as done. -func (tsv *TabletServer) endRequest(isBegin bool) { +func (tsv *TabletServer) endRequest() { tsv.requests.Done() } From cbd81a39906b78cf88bf4e0a6a133f6b0e9c7aaf Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 13 Mar 2020 17:42:45 -0700 Subject: [PATCH 301/825] deprecation: deprecate DML_PK Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/planbuilder/dml.go | 146 ++++-------------- .../tabletserver/planbuilder/query_gen.go | 18 +-- go/vt/vttablet/tabletserver/schema/schema.go | 13 +- 3 files changed, 52 insertions(+), 125 deletions(-) diff --git a/go/vt/vttablet/tabletserver/planbuilder/dml.go b/go/vt/vttablet/tabletserver/planbuilder/dml.go index 54be33c5486..16d0baacae8 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/dml.go +++ b/go/vt/vttablet/tabletserver/planbuilder/dml.go @@ -48,21 +48,16 @@ func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan plan.Reason = ReasonTable return plan, nil } - table, tableErr := plan.setTable(tableName, tables) - - // Updates aren't supported on topics - if tableErr == nil && table.IsTopic() { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "updates not allowed on topics") - } + table, _ := plan.setTable(tableName, tables) - // In passthrough dml mode, allow the operation even if the - // table is unknown in the schema. - if PassthroughDMLs { + // Drop to pass-through mode if table cannot be indentified. + if PassthroughDMLs || table == nil { return plan, nil } - if tableErr != nil { - return nil, tableErr + // Updates aren't supported on topics + if table.IsTopic() { + return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "updates not allowed on topics") } // Store the WHERE clause as string for the hot row protection (txserializer). @@ -70,32 +65,12 @@ func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan buf.Myprintf("%v", upd.Where) plan.WhereClause = buf.ParsedQuery() - if !table.HasPrimary() { - log.Warningf("no primary key for table %s", tableName) - plan.Reason = ReasonTableNoIndex + if hasPKConstraint(upd.Where, table) { return plan, nil } - plan.SecondaryPKValues, err = analyzeUpdateExpressions(upd.Exprs, table.Indexes[0]) - if err != nil { - if err == ErrTooComplex { - plan.Reason = ReasonPKChange - return plan, nil - } - return nil, err - } - plan.OuterQuery = GenerateUpdateOuterQuery(upd, aliased, nil) - if pkValues := analyzeWhere(upd.Where, table.Indexes[0]); pkValues != nil { - // Also, there should be no limit clause. - if upd.Limit == nil { - plan.PlanID = PlanDMLPK - plan.PKValues = pkValues - return plan, nil - } - } - plan.PlanID = PlanDMLSubquery plan.Subquery = GenerateUpdateSubquery(upd, table, aliased) return plan, nil @@ -111,31 +86,28 @@ func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan plan.Reason = ReasonMultiTable return plan, nil } + aliased, ok := del.TableExprs[0].(*sqlparser.AliasedTableExpr) if !ok { plan.Reason = ReasonMultiTable return plan, nil } + tableName := sqlparser.GetTableName(aliased.Expr) if tableName.IsEmpty() { plan.Reason = ReasonTable return plan, nil } - table, tableErr := plan.setTable(tableName, tables) + table, _ := plan.setTable(tableName, tables) - // Deletes aren't supported on topics - if tableErr == nil && table.IsTopic() { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "deletes not allowed on topics") - } - - // In passthrough dml mode, allow the operation even if the - // table is unknown in the schema. - if PassthroughDMLs { + // Drop to pass-through mode if table cannot be indentified. + if PassthroughDMLs || table == nil { return plan, nil } - if tableErr != nil { - return nil, tableErr + // Deletes aren't supported on topics + if table.IsTopic() { + return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "deletes not allowed on topics") } // Store the WHERE clause as string for the hot row protection (txserializer). @@ -143,23 +115,12 @@ func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan buf.Myprintf("%v", del.Where) plan.WhereClause = buf.ParsedQuery() - if !table.HasPrimary() { - log.Warningf("no primary key for table %s", tableName) - plan.Reason = ReasonTableNoIndex + if hasPKConstraint(del.Where, table) { return plan, nil } plan.OuterQuery = GenerateDeleteOuterQuery(del, aliased) - if pkValues := analyzeWhere(del.Where, table.Indexes[0]); pkValues != nil { - // Also, there should be no limit clause. - if del.Limit == nil { - plan.PlanID = PlanDMLPK - plan.PKValues = pkValues - return plan, nil - } - } - plan.PlanID = PlanDMLSubquery plan.Subquery = GenerateDeleteSubquery(del, table, aliased) return plan, nil @@ -172,27 +133,6 @@ func analyzeSet(set *sqlparser.Set) (plan *Plan) { } } -func analyzeUpdateExpressions(exprs sqlparser.UpdateExprs, pkIndex *schema.Index) (pkValues []sqltypes.PlanValue, err error) { - for _, expr := range exprs { - index := pkIndex.FindColumn(expr.Name.Name) - if index == -1 { - continue - } - if !sqlparser.IsValue(expr.Expr) { - return nil, ErrTooComplex - } - if pkValues == nil { - pkValues = make([]sqltypes.PlanValue, len(pkIndex.Columns)) - } - var err error - pkValues[index], err = sqlparser.NewPlanValue(expr.Expr) - if err != nil { - return nil, err - } - } - return pkValues, nil -} - func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ PlanID: PlanPassSelect, @@ -253,28 +193,22 @@ func analyzeFrom(tableExprs sqlparser.TableExprs) sqlparser.TableIdent { return sqlparser.GetTableName(node.Expr) } -func analyzeWhere(node *sqlparser.Where, pkIndex *schema.Index) []sqltypes.PlanValue { - if node == nil { - return nil +func hasPKConstraint(node *sqlparser.Where, table *schema.Table) bool { + if node == nil || len(table.PKColumns) == 0 { + return false } - conditions := analyzeBoolean(node.Expr) - if conditions == nil { - return nil - } - return getPKValues(conditions, pkIndex) + conditions := splitBool(node.Expr) + return matchPKs(conditions, table) } -func analyzeBoolean(node sqlparser.Expr) (conditions []*sqlparser.ComparisonExpr) { +func splitBool(node sqlparser.Expr) (conditions []*sqlparser.ComparisonExpr) { switch node := node.(type) { case *sqlparser.AndExpr: - left := analyzeBoolean(node.Left) - right := analyzeBoolean(node.Right) - if left == nil || right == nil { - return nil - } + left := splitBool(node.Left) + right := splitBool(node.Right) return append(left, right...) case *sqlparser.ParenExpr: - return analyzeBoolean(node.Expr) + return splitBool(node.Expr) case *sqlparser.ComparisonExpr: switch { case node.Operator == sqlparser.EqualStr: @@ -290,35 +224,21 @@ func analyzeBoolean(node sqlparser.Expr) (conditions []*sqlparser.ComparisonExpr return nil } -func getPKValues(conditions []*sqlparser.ComparisonExpr, pkIndex *schema.Index) []sqltypes.PlanValue { - pkValues := make([]sqltypes.PlanValue, len(pkIndex.Columns)) - inClauseSeen := false +func matchPKs(conditions []*sqlparser.ComparisonExpr, table *schema.Table) bool { + pkValues := make([]bool, len(table.PKColumns)) for _, condition := range conditions { - if condition.Operator == sqlparser.InStr { - if inClauseSeen { - return nil - } - inClauseSeen = true - } - index := pkIndex.FindColumn(condition.Left.(*sqlparser.ColName).Name) + index := table.FindPKColumn(condition.Left.(*sqlparser.ColName).Name) if index == -1 { - return nil - } - if !pkValues[index].IsNull() { - return nil - } - var err error - pkValues[index], err = sqlparser.NewPlanValue(condition.Right) - if err != nil { - return nil + continue } + pkValues[index] = true } for _, v := range pkValues { - if v.IsNull() { - return nil + if !v { + return false } } - return pkValues + return true } func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan *Plan, err error) { diff --git a/go/vt/vttablet/tabletserver/planbuilder/query_gen.go b/go/vt/vttablet/tabletserver/planbuilder/query_gen.go index 0418d2bb969..6dd37591ebf 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/query_gen.go +++ b/go/vt/vttablet/tabletserver/planbuilder/query_gen.go @@ -98,42 +98,38 @@ func GenerateDeleteOuterQuery(del *sqlparser.Delete, aliased *sqlparser.AliasedT // GenerateUpdateSubquery generates the subquery for updates. func GenerateUpdateSubquery(upd *sqlparser.Update, table *schema.Table, aliased *sqlparser.AliasedTableExpr) *sqlparser.ParsedQuery { return GenerateSubquery( - table.Indexes[0].Columns, + table, aliased, upd.Where, upd.OrderBy, upd.Limit, - true, ) } // GenerateDeleteSubquery generates the subquery for deletes. func GenerateDeleteSubquery(del *sqlparser.Delete, table *schema.Table, aliased *sqlparser.AliasedTableExpr) *sqlparser.ParsedQuery { return GenerateSubquery( - table.Indexes[0].Columns, + table, aliased, del.Where, del.OrderBy, del.Limit, - true, ) } // GenerateSubquery generates a subquery based on the input parameters. -func GenerateSubquery(columns []sqlparser.ColIdent, table *sqlparser.AliasedTableExpr, where *sqlparser.Where, order sqlparser.OrderBy, limit *sqlparser.Limit, forUpdate bool) *sqlparser.ParsedQuery { +func GenerateSubquery(table *schema.Table, tableName *sqlparser.AliasedTableExpr, where *sqlparser.Where, order sqlparser.OrderBy, limit *sqlparser.Limit) *sqlparser.ParsedQuery { buf := sqlparser.NewTrackedBuffer(nil) if limit == nil { limit = execLimit } buf.WriteString("select ") prefix := "" - for _, c := range columns { - buf.Myprintf("%s%v", prefix, c) + for _, colnum := range table.PKColumns { + buf.Myprintf("%s%v", prefix, table.Columns[colnum].Name) prefix = ", " } - buf.Myprintf(" from %v%v%v%v", table, where, order, limit) - if forUpdate { - buf.Myprintf(sqlparser.ForUpdateStr) - } + buf.Myprintf(" from %v%v%v%v", tableName, where, order, limit) + buf.Myprintf(sqlparser.ForUpdateStr) return buf.ParsedQuery() } diff --git a/go/vt/vttablet/tabletserver/schema/schema.go b/go/vt/vttablet/tabletserver/schema/schema.go index 3f6bce060a9..c943420d5a8 100644 --- a/go/vt/vttablet/tabletserver/schema/schema.go +++ b/go/vt/vttablet/tabletserver/schema/schema.go @@ -184,6 +184,17 @@ func (ta *Table) FindColumn(name sqlparser.ColIdent) int { return -1 } +// FindPKColumn finds a pk column in the table. It returns the index if found. +// Otherwise, it returns -1. +func (ta *Table) FindPKColumn(name sqlparser.ColIdent) int { + for i, colnum := range ta.PKColumns { + if ta.Columns[colnum].Name.Equal(name) { + return i + } + } + return -1 +} + // GetPKColumn returns the pk column specified by the index. func (ta *Table) GetPKColumn(index int) *TableColumn { return &ta.Columns[ta.PKColumns[index]] @@ -212,7 +223,7 @@ func (ta *Table) SetMysqlStats(tr, dl, il, df, mdl sqltypes.Value) { // HasPrimary returns true if the table has a primary key. func (ta *Table) HasPrimary() bool { - return len(ta.Indexes) != 0 && ta.Indexes[0].Name.EqualString("primary") + return len(ta.PKColumns) != 0 } // IsTopic returns true if TopicInfo is not nil. From 585fa2a99a11fd6b7da18d7b57ffc5e9a8299c69 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 14 Mar 2020 19:08:54 -0700 Subject: [PATCH 302/825] deprecation: implement DMLLimit Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/planbuilder/dml.go | 200 ++++-------------- .../vttablet/tabletserver/planbuilder/plan.go | 21 +- go/vt/vttablet/tabletserver/query_executor.go | 127 ++--------- .../tabletserver/query_executor_test.go | 133 ------------ go/vt/vttablet/tabletserver/tabletserver.go | 2 +- 5 files changed, 71 insertions(+), 412 deletions(-) diff --git a/go/vt/vttablet/tabletserver/planbuilder/dml.go b/go/vt/vttablet/tabletserver/planbuilder/dml.go index 16d0baacae8..205f980548e 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/dml.go +++ b/go/vt/vttablet/tabletserver/planbuilder/dml.go @@ -28,36 +28,8 @@ import ( func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ - PlanID: PlanPassDML, - FullQuery: GenerateFullQuery(upd), - } - - if len(upd.TableExprs) > 1 { - plan.Reason = ReasonMultiTable - return plan, nil - } - - aliased, ok := upd.TableExprs[0].(*sqlparser.AliasedTableExpr) - if !ok { - plan.Reason = ReasonMultiTable - return plan, nil - } - - tableName := sqlparser.GetTableName(aliased.Expr) - if tableName.IsEmpty() { - plan.Reason = ReasonTable - return plan, nil - } - table, _ := plan.setTable(tableName, tables) - - // Drop to pass-through mode if table cannot be indentified. - if PassthroughDMLs || table == nil { - return plan, nil - } - - // Updates aren't supported on topics - if table.IsTopic() { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "updates not allowed on topics") + PlanID: PlanPassDML, + Table: lookupTable(upd.TableExprs, tables), } // Store the WHERE clause as string for the hot row protection (txserializer). @@ -65,49 +37,22 @@ func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan buf.Myprintf("%v", upd.Where) plan.WhereClause = buf.ParsedQuery() - if hasPKConstraint(upd.Where, table) { + if PassthroughDMLs || upd.Limit != nil { + plan.FullQuery = GenerateFullQuery(upd) return plan, nil } - plan.OuterQuery = GenerateUpdateOuterQuery(upd, aliased, nil) - - plan.PlanID = PlanDMLSubquery - plan.Subquery = GenerateUpdateSubquery(upd, table, aliased) + plan.PlanID = PlanDMLLimit + upd.Limit = execLimit + plan.FullQuery = GenerateFullQuery(upd) + upd.Limit = nil return plan, nil } func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ - PlanID: PlanPassDML, - FullQuery: GenerateFullQuery(del), - } - - if len(del.TableExprs) > 1 { - plan.Reason = ReasonMultiTable - return plan, nil - } - - aliased, ok := del.TableExprs[0].(*sqlparser.AliasedTableExpr) - if !ok { - plan.Reason = ReasonMultiTable - return plan, nil - } - - tableName := sqlparser.GetTableName(aliased.Expr) - if tableName.IsEmpty() { - plan.Reason = ReasonTable - return plan, nil - } - table, _ := plan.setTable(tableName, tables) - - // Drop to pass-through mode if table cannot be indentified. - if PassthroughDMLs || table == nil { - return plan, nil - } - - // Deletes aren't supported on topics - if table.IsTopic() { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "deletes not allowed on topics") + PlanID: PlanPassDML, + Table: lookupTable(del.TableExprs, tables), } // Store the WHERE clause as string for the hot row protection (txserializer). @@ -115,14 +60,14 @@ func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan buf.Myprintf("%v", del.Where) plan.WhereClause = buf.ParsedQuery() - if hasPKConstraint(del.Where, table) { + if PassthroughDMLs || del.Limit != nil { + plan.FullQuery = GenerateFullQuery(del) return plan, nil } - - plan.OuterQuery = GenerateDeleteOuterQuery(del, aliased) - - plan.PlanID = PlanDMLSubquery - plan.Subquery = GenerateDeleteSubquery(del, table, aliased) + plan.PlanID = PlanDMLLimit + del.Limit = execLimit + plan.FullQuery = GenerateFullQuery(del) + del.Limit = nil return plan, nil } @@ -136,6 +81,7 @@ func analyzeSet(set *sqlparser.Set) (plan *Plan) { func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ PlanID: PlanPassSelect, + Table: lookupTable(sel.From, tables), FieldQuery: GenerateFieldQuery(sel), FullQuery: GenerateLimitQuery(sel), } @@ -143,20 +89,6 @@ func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan plan.PlanID = PlanSelectLock } - tableName := analyzeFrom(sel.From) - if tableName.IsEmpty() { - return plan, nil - } - table, err := plan.setTable(tableName, tables) - if err != nil { - return nil, err - } - - // Selects aren't supported on topics - if table.IsTopic() { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "selects not allowed on topics") - } - if sel.Where != nil { comp, ok := sel.Where.Expr.(*sqlparser.ComparisonExpr) if ok && comp.IsImpossible() { @@ -167,8 +99,8 @@ func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan // Check if it's a NEXT VALUE statement. if nextVal, ok := sel.SelectExprs[0].(sqlparser.Nextval); ok { - if table.Type != schema.Sequence { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s is not a sequence", tableName) + if plan.Table == nil || plan.Table.Type != schema.Sequence { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s is not a sequence", sqlparser.String(sel.From)) } plan.PlanID = PlanNextval v, err := sqlparser.NewPlanValue(nextVal.Expr) @@ -182,65 +114,6 @@ func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan return plan, nil } -func analyzeFrom(tableExprs sqlparser.TableExprs) sqlparser.TableIdent { - if len(tableExprs) > 1 { - return sqlparser.NewTableIdent("") - } - node, ok := tableExprs[0].(*sqlparser.AliasedTableExpr) - if !ok { - return sqlparser.NewTableIdent("") - } - return sqlparser.GetTableName(node.Expr) -} - -func hasPKConstraint(node *sqlparser.Where, table *schema.Table) bool { - if node == nil || len(table.PKColumns) == 0 { - return false - } - conditions := splitBool(node.Expr) - return matchPKs(conditions, table) -} - -func splitBool(node sqlparser.Expr) (conditions []*sqlparser.ComparisonExpr) { - switch node := node.(type) { - case *sqlparser.AndExpr: - left := splitBool(node.Left) - right := splitBool(node.Right) - return append(left, right...) - case *sqlparser.ParenExpr: - return splitBool(node.Expr) - case *sqlparser.ComparisonExpr: - switch { - case node.Operator == sqlparser.EqualStr: - if sqlparser.IsColName(node.Left) && sqlparser.IsValue(node.Right) { - return []*sqlparser.ComparisonExpr{node} - } - case node.Operator == sqlparser.InStr: - if sqlparser.IsColName(node.Left) && sqlparser.IsSimpleTuple(node.Right) { - return []*sqlparser.ComparisonExpr{node} - } - } - } - return nil -} - -func matchPKs(conditions []*sqlparser.ComparisonExpr, table *schema.Table) bool { - pkValues := make([]bool, len(table.PKColumns)) - for _, condition := range conditions { - index := table.FindPKColumn(condition.Left.(*sqlparser.ColName).Name) - if index == -1 { - continue - } - pkValues[index] = true - } - for _, v := range pkValues { - if !v { - return false - } - } - return true -} - func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ PlanID: PlanPassDML, @@ -256,14 +129,17 @@ func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan plan.Reason = ReasonTable return plan, nil } - table, tableErr := plan.setTable(tableName, tables) + plan.Table = tables[tableName.String()] + if plan.Table == nil { + return plan, nil + } switch { - case tableErr == nil && table.Type == schema.Message: + case plan.Table.Type == schema.Message: // message inserts need to continue being strict, even in passthrough dml mode, // because field defaults are set here - case tableErr == nil && table.IsTopic(): + case plan.Table.IsTopic(): plan.PlanID = PlanInsertTopic plan.Reason = ReasonTopic return plan, nil @@ -272,22 +148,19 @@ func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan // In passthrough dml mode, allow the operation even if the // table is unknown in the schema. return plan, nil - - case tableErr != nil: - return nil, tableErr } - if !table.HasPrimary() { + if !plan.Table.HasPrimary() { log.Warningf("no primary key for table %s", tableName) plan.Reason = ReasonTableNoIndex return plan, nil } - switch table.Type { + switch plan.Table.Type { case schema.NoType, schema.Sequence: // For now, allow sequence inserts. - return analyzeInsertNoType(ins, plan, table) + return analyzeInsertNoType(ins, plan, plan.Table) case schema.Message: - return analyzeInsertMessage(ins, plan, table) + return analyzeInsertMessage(ins, plan, plan.Table) } panic("unreachable") } @@ -572,6 +445,21 @@ func analyzeOnDupExpressions(ins *sqlparser.Insert, pkIndex *schema.Index) (pkVa return pkValues, true } +func lookupTable(tableExprs sqlparser.TableExprs, tables map[string]*schema.Table) *schema.Table { + if len(tableExprs) > 1 { + return nil + } + aliased, ok := tableExprs[0].(*sqlparser.AliasedTableExpr) + if !ok { + return nil + } + tableName := sqlparser.GetTableName(aliased.Expr) + if tableName.IsEmpty() { + return nil + } + return tables[tableName.String()] +} + // extractColumnValues extracts the values of a column into a PlanValue. func extractColumnValues(rowList sqlparser.Values, colnum int) (sqltypes.PlanValue, bool) { pv := sqltypes.PlanValue{Values: make([]sqltypes.PlanValue, len(rowList))} diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan.go b/go/vt/vttablet/tabletserver/planbuilder/plan.go index 87e119f3c52..a682b7f9aa2 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan.go @@ -57,11 +57,8 @@ const ( // and is valid in all replication modes. // Otherwise is only allowed in row based replication mode PlanPassDML - // PlanDMLPK is an update or delete with an equality where clause(s) - // on primary key(s). - PlanDMLPK - // PlanDMLSubquery is an update or delete with a subselect statement - PlanDMLSubquery + // PlanDMLLimit is an update or delete with a limit. + PlanDMLLimit // PlanInsertPK is insert statement where the PK value is // supplied with the query. PlanInsertPK @@ -97,8 +94,7 @@ var planName = [NumPlans]string{ "SELECT_LOCK", "NEXTVAL", "PASS_DML", - "DML_PK", - "DML_SUBQUERY", + "DML_LIMIT", "INSERT_PK", "INSERT_SUBQUERY", "UPSERT_PK", @@ -240,13 +236,6 @@ func (plan *Plan) TableName() sqlparser.TableIdent { return tableName } -func (plan *Plan) setTable(tableName sqlparser.TableIdent, tables map[string]*schema.Table) (*schema.Table, error) { - if plan.Table = tables[tableName.String()]; plan.Table == nil { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "table %s not found in schema", tableName) - } - return plan.Table, nil -} - // Build builds a plan based on the schema. func Build(statement sqlparser.Statement, tables map[string]*schema.Table) (*Plan, error) { var plan *Plan @@ -314,9 +303,7 @@ func BuildStreaming(sql string, tables map[string]*schema.Table) (*Plan, error) if stmt.Lock != "" { return nil, vterrors.New(vtrpcpb.Code_FAILED_PRECONDITION, "select with lock not allowed for streaming") } - if tableName := analyzeFrom(stmt.From); !tableName.IsEmpty() { - plan.setTable(tableName, tables) - } + plan.Table = lookupTable(stmt.From, tables) case *sqlparser.OtherRead, *sqlparser.Show, *sqlparser.Union: // pass default: diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index a7a695f3232..c6491bfde5b 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -128,10 +128,8 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { return qre.execInsertMessage(conn) case planbuilder.PlanInsertSubquery: return qre.execInsertSubquery(conn) - case planbuilder.PlanDMLPK: - return qre.execDMLPK(conn) - case planbuilder.PlanDMLSubquery: - return qre.execDMLSubquery(conn) + case planbuilder.PlanDMLLimit: + return qre.execDMLLimit(conn) case planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: return qre.execSQL(conn, qre.query, true) case planbuilder.PlanUpsertPK: @@ -174,9 +172,7 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { fallthrough case planbuilder.PlanInsertSubquery: fallthrough - case planbuilder.PlanDMLPK: - fallthrough - case planbuilder.PlanDMLSubquery: + case planbuilder.PlanDMLLimit: fallthrough case planbuilder.PlanUpsertPK: if !qre.tsv.qe.autoCommit.Get() { @@ -278,10 +274,8 @@ func (qre *QueryExecutor) execDmlAutoCommit() (reply *sqltypes.Result, err error return qre.execInsertMessage(conn) case planbuilder.PlanInsertSubquery: reply, err = qre.execInsertSubquery(conn) - case planbuilder.PlanDMLPK: - reply, err = qre.execDMLPK(conn) - case planbuilder.PlanDMLSubquery: - reply, err = qre.execDMLSubquery(conn) + case planbuilder.PlanDMLLimit: + reply, err = qre.execDMLLimit(conn) case planbuilder.PlanUpsertPK: reply, err = qre.execUpsertPK(conn) default: @@ -597,109 +591,34 @@ func (qre *QueryExecutor) execInsertPKRows(conn *TxConnection, extras map[string } func (qre *QueryExecutor) execUpsertPK(conn *TxConnection) (*sqltypes.Result, error) { - // For RBR, upserts are passed through. - if qre.tsv.qe.binlogFormat == connpool.BinlogFormatRow { - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) - } - - // For statement or mixed mode, we have to split into two ops. - pkRows, err := buildValueList(qre.plan.Table, qre.plan.PKValues, qre.bindVars) - if err != nil { - return nil, err - } - // We do not need to build the secondary list for the insert part. - // But the part that updates will build it if it gets executed, - // because it's the one that can change the primary keys. - bsc := buildStreamComment(qre.plan.Table, pkRows, nil) - result, err := qre.txFetch(conn, qre.plan.OuterQuery, qre.bindVars, nil, bsc, true, true) - if err == nil { - return result, nil - } - sqlErr, ok := err.(*mysql.SQLError) - if !ok { - return result, err - } - if sqlErr.Number() != mysql.ERDupEntry { - return nil, err - } - // If the error didn't match pk, just return the error without updating. - if !strings.Contains(sqlErr.Error(), "'PRIMARY'") { - return nil, err - } - // At this point, we know the insert failed due to a duplicate pk row. - // So, we just update the row. - result, err = qre.execDMLPKRows(conn, qre.plan.UpsertQuery, pkRows) - if err != nil { - return nil, err - } - // Follow MySQL convention. RowsAffected must be 2 if a row was updated. - if result.RowsAffected == 1 { - result.RowsAffected = 2 - } - return result, err + return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) } -func (qre *QueryExecutor) execDMLPK(conn *TxConnection) (*sqltypes.Result, error) { - pkRows, err := buildValueList(qre.plan.Table, qre.plan.PKValues, qre.bindVars) +func (qre *QueryExecutor) execDMLLimit(conn *TxConnection) (*sqltypes.Result, error) { + maxrows := qre.tsv.qe.maxResultSize.Get() + qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) + result, err := qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) if err != nil { return nil, err } - return qre.execDMLPKRows(conn, qre.plan.OuterQuery, pkRows) -} - -func (qre *QueryExecutor) execDMLSubquery(conn *TxConnection) (*sqltypes.Result, error) { - innerResult, err := qre.txFetch(conn, qre.plan.Subquery, qre.bindVars, nil, "", true, false) - if err != nil { + if err := qre.verifyRowCount(int64(result.RowsAffected), maxrows); err != nil { return nil, err } - return qre.execDMLPKRows(conn, qre.plan.OuterQuery, innerResult.Rows) + return result, nil } -func (qre *QueryExecutor) execDMLPKRows(conn *TxConnection, query *sqlparser.ParsedQuery, pkRows [][]sqltypes.Value) (*sqltypes.Result, error) { - if len(pkRows) == 0 { - return &sqltypes.Result{RowsAffected: 0}, nil - } - secondaryList, err := buildSecondaryList(qre.plan.Table, pkRows, qre.plan.SecondaryPKValues, qre.bindVars) - if err != nil { - return nil, err +func (qre *QueryExecutor) verifyRowCount(count, maxrows int64) error { + if count > maxrows { + callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) + return mysql.NewSQLError(mysql.ERVitessMaxRowsExceeded, mysql.SSUnknownSQLState, "caller id: %s: row count exceeded %d", callerID.Username, maxrows) } - - result := &sqltypes.Result{} - maxRows := int(qre.tsv.qe.maxDMLRows.Get()) - for i := 0; i < len(pkRows); i += maxRows { - end := i + maxRows - if end >= len(pkRows) { - end = len(pkRows) - } - pkRows := pkRows[i:end] - secondaryList := secondaryList - if secondaryList != nil { - secondaryList = secondaryList[i:end] - } - var bsc string - // Build comments only if we're not in RBR mode. - if qre.tsv.qe.binlogFormat != connpool.BinlogFormatRow { - bsc = buildStreamComment(qre.plan.Table, pkRows, secondaryList) - } - extras := map[string]sqlparser.Encodable{ - "#pk": &sqlparser.TupleEqualityList{ - Columns: qre.plan.Table.Indexes[0].Columns, - Rows: pkRows, - }, - } - r, err := qre.txFetch(conn, query, qre.bindVars, extras, bsc, true, true) - if err != nil { - return nil, err - } - - // UPDATEs can return InsertID when LAST_INSERT_ID(expr) is used. In - // this case it should be the same for all rows. - result.InsertID = r.InsertID - - // DMLs should all return RowsAffected. - result.RowsAffected += r.RowsAffected + warnThreshold := qre.tsv.qe.warnResultSize.Get() + if warnThreshold > 0 && count > warnThreshold { + callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) + tabletenv.Warnings.Add("ResultsExceeded", 1) + log.Warningf("CallerID: %s row count %v exceeds warning threshold %v: %q", callerID.Username, count, warnThreshold, queryAsString(qre.plan.FullQuery.Query, qre.bindVars)) } - return result, nil + return nil } func (qre *QueryExecutor) execSet() (*sqltypes.Result, error) { @@ -820,8 +739,6 @@ func (qre *QueryExecutor) streamFetch(conn *connpool.DBConn, parsedQuery *sqlpar } func (qre *QueryExecutor) generateFinalSQL(parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, extras map[string]sqlparser.Encodable, buildStreamComment string) (string, string, error) { - bindVars["#maxLimit"] = sqltypes.Int64BindVariable(qre.getLimit(parsedQuery)) - var buf strings.Builder buf.WriteString(qre.marginComments.Leading) diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 782cda8a8c6..3053b65b3b6 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -725,139 +725,6 @@ func TestQueryExecutorPlanUpsertPkAutoCommit(t *testing.T) { } } -func TestQueryExecutorPlanDmlPk(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanDMLPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{"update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanDmlPkTransactionIsolation(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - db.AddQuery("set transaction isolation level SERIALIZABLE", &sqltypes.Result{}) - txid := newTransaction(tsv, &querypb.ExecuteOptions{ - TransactionIsolation: querypb.ExecuteOptions_SERIALIZABLE, - }) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanDMLPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{"update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanDmlPkRBR(t *testing.T) { - // RBR test is almost identical to the non-RBR test, except that - // the _stream comments are suppressed for RBR. - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set name = 2 where pk in (1)" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanDMLPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{query} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanDmlAutoCommit(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanDMLPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -func TestQueryExecutorPlanDmlAutoCommitTransactionIsolation(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - - qre.options = &querypb.ExecuteOptions{ - TransactionIsolation: querypb.ExecuteOptions_READ_UNCOMMITTED, - } - db.AddQuery("set transaction isolation level READ UNCOMMITTED", &sqltypes.Result{}) - - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanDMLPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - func TestQueryExecutorPlanDmlSubQuery(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index b63b73b519d..00eb4a8bced 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -1188,7 +1188,7 @@ func (tsv *TabletServer) computeTxSerializerKey(ctx context.Context, logStats *t return "", "" } - if plan.PlanID != planbuilder.PlanDMLPK && plan.PlanID != planbuilder.PlanDMLSubquery { + if plan.PlanID != planbuilder.PlanPassDML && plan.PlanID != planbuilder.PlanDMLLimit { // Serialize only UPDATE or DELETE queries. return "", "" } From 4ee0b11ce287ce4eeba2e46e90b3a7550a01e1eb Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 14 Mar 2020 20:34:55 -0700 Subject: [PATCH 303/825] deprecation: delete lot of SBR code Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/codex.go | 169 ------ go/vt/vttablet/tabletserver/codex_test.go | 264 --------- .../vttablet/tabletserver/planbuilder/dml.go | 362 ++---------- .../vttablet/tabletserver/planbuilder/plan.go | 35 +- .../tabletserver/planbuilder/plan_test.go | 46 +- go/vt/vttablet/tabletserver/query_engine.go | 10 + go/vt/vttablet/tabletserver/query_executor.go | 207 ++----- .../tabletserver/query_executor_test.go | 541 ------------------ go/vt/vttablet/tabletserver/queryz.go | 10 +- go/vt/vttablet/tabletserver/queryz_test.go | 4 - .../vttablet/tabletserver/rules/rules_test.go | 10 +- 11 files changed, 154 insertions(+), 1504 deletions(-) delete mode 100644 go/vt/vttablet/tabletserver/codex.go delete mode 100644 go/vt/vttablet/tabletserver/codex_test.go diff --git a/go/vt/vttablet/tabletserver/codex.go b/go/vt/vttablet/tabletserver/codex.go deleted file mode 100644 index de3b6bf7dfd..00000000000 --- a/go/vt/vttablet/tabletserver/codex.go +++ /dev/null @@ -1,169 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package tabletserver - -import ( - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" -) - -// buildValueList builds the set of PK reference rows used to drive the next query. -// It uses the PK values supplied in the original query and bind variables. -// The generated reference rows are validated for type match against the PK of the table. -func buildValueList(table *schema.Table, pkValues []sqltypes.PlanValue, bindVars map[string]*querypb.BindVariable) ([][]sqltypes.Value, error) { - rows, err := sqltypes.ResolveRows(pkValues, bindVars) - if err != nil { - return nil, err - } - // Iterate by columns. - for j := range pkValues { - typ := table.GetPKColumn(j).Type - for i := range rows { - rows[i][j], err = sqltypes.Cast(rows[i][j], typ) - if err != nil { - return nil, err - } - } - } - return rows, nil -} - -// buildSecondaryList is used for handling ON DUPLICATE DMLs, or those that change the PK. -func buildSecondaryList(table *schema.Table, pkList [][]sqltypes.Value, secondaryList []sqltypes.PlanValue, bindVars map[string]*querypb.BindVariable) ([][]sqltypes.Value, error) { - if secondaryList == nil { - return nil, nil - } - secondaryRows, err := sqltypes.ResolveRows(secondaryList, bindVars) - if err != nil { - return nil, err - } - rows := make([][]sqltypes.Value, len(pkList)) - for i, row := range pkList { - // If secondaryRows has only one row, then that - // row should be duplicated for every row in pkList. - // Otherwise, we use the individual values. - var changedValues []sqltypes.Value - if len(secondaryRows) == 1 { - changedValues = secondaryRows[0] - } else { - changedValues = secondaryRows[i] - } - rows[i] = make([]sqltypes.Value, len(row)) - for j, value := range row { - if changedValues[j].IsNull() { - rows[i][j] = value - } else { - rows[i][j] = changedValues[j] - } - } - } - return rows, nil -} - -// resolveNumber extracts a number from a bind variable or sql value. -func resolveNumber(pv sqltypes.PlanValue, bindVars map[string]*querypb.BindVariable) (int64, error) { - v, err := pv.ResolveValue(bindVars) - if err != nil { - return 0, err - } - ret, err := sqltypes.ToInt64(v) - if err != nil { - return 0, err - } - return ret, nil -} - -func validateRow(table *schema.Table, columnNumbers []int, row []sqltypes.Value) error { - if len(row) != len(columnNumbers) { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "data inconsistency %d vs %d", len(row), len(columnNumbers)) - } - for j, value := range row { - if err := validateValue(&table.Columns[columnNumbers[j]], value); err != nil { - return err - } - } - - return nil -} - -func validateValue(col *schema.TableColumn, value sqltypes.Value) error { - if value.IsNull() { - return nil - } - if sqltypes.IsIntegral(col.Type) { - if !value.IsIntegral() { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "type mismatch, expecting numeric type for %v for column: %v", value, col) - } - } else if col.Type == sqltypes.VarBinary { - if !value.IsQuoted() { - return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "type mismatch, expecting string type for %v for column: %v", value, col) - } - } - return nil -} - -func buildStreamComment(table *schema.Table, pkValueList [][]sqltypes.Value, secondaryList [][]sqltypes.Value) string { - buf := sqlparser.NewTrackedBuffer(nil) - buf.Myprintf(" /* _stream %v (", table.Name) - // We assume the first index exists, and is the pk - for _, pkName := range table.Indexes[0].Columns { - buf.Myprintf("%v ", pkName) - } - buf.WriteString(")") - buildPKValueList(buf, table, pkValueList) - buildPKValueList(buf, table, secondaryList) - buf.WriteString("; */") - return buf.String() -} - -func buildPKValueList(buf *sqlparser.TrackedBuffer, table *schema.Table, pkValueList [][]sqltypes.Value) { - for _, pkValues := range pkValueList { - buf.WriteString(" (") - for _, pkValue := range pkValues { - pkValue.EncodeASCII(buf) - buf.WriteString(" ") - } - buf.WriteString(")") - } -} - -func applyFilterWithPKDefaults(table *schema.Table, columnNumbers []int, input []sqltypes.Value) (output []sqltypes.Value) { - output = make([]sqltypes.Value, len(columnNumbers)) - for colIndex, colPointer := range columnNumbers { - if colPointer >= 0 { - output[colIndex] = input[colPointer] - } else { - output[colIndex] = table.GetPKColumn(colIndex).Default - } - } - return output -} - -// unicoded returns a valid UTF-8 string that json won't reject -func unicoded(in string) (out string) { - for i, v := range in { - if v == 0xFFFD { - return in[:i] - } - } - return in -} diff --git a/go/vt/vttablet/tabletserver/codex_test.go b/go/vt/vttablet/tabletserver/codex_test.go deleted file mode 100644 index f8ee20f5c3f..00000000000 --- a/go/vt/vttablet/tabletserver/codex_test.go +++ /dev/null @@ -1,264 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package tabletserver - -import ( - "reflect" - "testing" - - "vitess.io/vitess/go/sqltypes" - - querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" -) - -func TestCodexBuildValuesList(t *testing.T) { - table := createTable("Table", - []string{"pk1", "pk2", "col1"}, - []querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32}, - []string{"pk1", "pk2"}) - bindVars := map[string]*querypb.BindVariable{ - "key": sqltypes.Int64BindVariable(10), - } - tcases := []struct { - pkValues []sqltypes.PlanValue - out [][]sqltypes.Value - err string - }{{ - pkValues: []sqltypes.PlanValue{{ - Key: "key", - }, { - Value: sqltypes.NewVarBinary("aa"), - }}, - out: [][]sqltypes.Value{ - {sqltypes.NewInt64(10), sqltypes.NewVarBinary("aa")}, - }, - }, { - pkValues: []sqltypes.PlanValue{{ - Key: "nokey", - }}, - err: "missing bind var nokey", - }, { - pkValues: []sqltypes.PlanValue{{ - Value: sqltypes.NewVarChar("aa"), - }}, - err: `strconv.ParseInt: parsing "aa": invalid syntax`, - }} - for _, tc := range tcases { - got, err := buildValueList(table, tc.pkValues, bindVars) - if tc.err != "" { - if err == nil || err.Error() != tc.err { - t.Errorf("buildValueList(%v) error: %v, want %s", tc.pkValues, err, tc.err) - } - continue - } - if err != nil { - t.Errorf("buildValueList(%v) error: %v", tc.pkValues, err) - } - if !reflect.DeepEqual(got, tc.out) { - t.Errorf("buildValueList(%v): %v, want %s", tc.pkValues, got, tc.out) - } - } -} - -func TestBuildSecondaryList(t *testing.T) { - table := createTable("Table", - []string{"pk1", "pk2", "col1"}, - []querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32}, - []string{"pk1", "pk2"}) - bindVars := map[string]*querypb.BindVariable{ - "key": sqltypes.Int64BindVariable(10), - } - r := sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "pk1|pk2", - "int64|varchar", - ), - "1|aa", - "2|bb", - ) - pkList := r.Rows - tcases := []struct { - secondaryList []sqltypes.PlanValue - out [][]sqltypes.Value - err string - }{{ - secondaryList: nil, - out: nil, - }, { - secondaryList: []sqltypes.PlanValue{{}, { - Value: sqltypes.NewVarBinary("cc"), - }}, - out: [][]sqltypes.Value{ - {sqltypes.NewInt64(1), sqltypes.NewVarBinary("cc")}, - {sqltypes.NewInt64(2), sqltypes.NewVarBinary("cc")}, - }, - }, { - secondaryList: []sqltypes.PlanValue{{ - Key: "nokey", - }}, - err: "missing bind var nokey", - }} - for _, tc := range tcases { - got, err := buildSecondaryList(table, pkList, tc.secondaryList, bindVars) - if tc.err != "" { - if err == nil || err.Error() != tc.err { - t.Errorf("buildSecondaryList(%v) error: %v, want %s", tc.secondaryList, err, tc.err) - } - continue - } - if err != nil { - t.Errorf("buildSecondaryList(%v) error: %v", tc.secondaryList, err) - } - if !reflect.DeepEqual(got, tc.out) { - t.Errorf("buildSecondaryList(%v): %v, want %s", tc.secondaryList, got, tc.out) - } - } -} - -func TestResolveNumber(t *testing.T) { - bindVars := map[string]*querypb.BindVariable{ - "key": sqltypes.Int64BindVariable(10), - } - tcases := []struct { - pv sqltypes.PlanValue - out int64 - err string - }{{ - pv: sqltypes.PlanValue{Key: "key"}, - out: 10, - }, { - pv: sqltypes.PlanValue{Key: "nokey"}, - err: "missing bind var nokey", - }, { - pv: sqltypes.PlanValue{Value: sqltypes.NewVarChar("aa")}, - err: "could not parse value: 'aa'", - }} - for _, tc := range tcases { - got, err := resolveNumber(tc.pv, bindVars) - if tc.err != "" { - if err == nil || err.Error() != tc.err { - t.Errorf("resolveNumber(%v) error: %v, want %s", tc.pv, err, tc.err) - } - continue - } - if err != nil { - t.Errorf("resolveNumber(%v) error: %v", tc.pv, err) - } - if got != tc.out { - t.Errorf("resolveNumber(%v): %d, want %d", tc.pv, got, tc.out) - } - } -} - -func TestCodexBuildStreamComment(t *testing.T) { - pk1 := "pk1" - pk2 := "pk2" - table := createTable("Table", - []string{"pk1", "pk2", "col1"}, - []querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32}, - []string{pk1, pk2}) - - // set pk2 = 'xyz' where pk1=1 and pk2 = 'abc' - bindVars := map[string]*querypb.BindVariable{} - pk1Val := sqltypes.NewInt64(1) - pk2Val := sqltypes.NewVarChar("abc") - pkValues := []sqltypes.PlanValue{{Value: pk1Val}, {Value: pk2Val}} - pkList, _ := buildValueList(table, pkValues, bindVars) - pk2SecVal := sqltypes.NewVarChar("xyz") - secondaryPKValues := []sqltypes.PlanValue{{}, {Value: pk2SecVal}} - secondaryList, _ := buildSecondaryList(table, pkList, secondaryPKValues, bindVars) - want := " /* _stream `Table` (pk1 pk2 ) (1 'YWJj' ) (1 'eHl6' ); */" - got := buildStreamComment(table, pkList, secondaryList) - if !reflect.DeepEqual(got, want) { - t.Fatalf("case 1 failed, got\n%s, want\n%s", got, want) - } -} - -func TestCodexValidateRow(t *testing.T) { - table := createTable("Table", - []string{"pk1", "pk2", "col1"}, - []querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32}, - []string{"pk1", "pk2"}) - // #columns and #rows do not match - err := validateRow(table, []int{1}, []sqltypes.Value{}) - if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT { - t.Errorf("validateRow: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) - } - // column 0 is int type but row is in string type - err = validateRow(table, []int{0}, []sqltypes.Value{sqltypes.NewVarBinary("str")}) - if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT { - t.Errorf("validateRow: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) - } -} - -func TestCodexApplyFilterWithPKDefaults(t *testing.T) { - testUtils := newTestUtils() - table := createTable("Table", - []string{"pk1", "pk2", "col1"}, - []querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32}, - []string{"pk1", "pk2"}) - output := applyFilterWithPKDefaults(table, []int{-1}, []sqltypes.Value{}) - if len(output) != 1 { - t.Fatalf("expect to only one output but got: %v", output) - } - val, err := sqltypes.ToInt64(output[0]) - if err != nil { - t.Fatalf("should not get an error, but got err: %v", err) - } - testUtils.checkEqual(t, int64(0), val) -} - -func TestCodexUnicoded(t *testing.T) { - testUtils := newTestUtils() - in := "test" - out := unicoded(in) - testUtils.checkEqual(t, in, out) - in = "tes\xFFFDt" - out = unicoded(in) - testUtils.checkEqual(t, "tes", out) -} - -func createTable(name string, colNames []string, colTypes []querypb.Type, pKeys []string) *schema.Table { - table := schema.NewTable(name) - for i, colName := range colNames { - colType := colTypes[i] - defaultVal := sqltypes.Value{} - if sqltypes.IsIntegral(colType) { - defaultVal = sqltypes.NewInt64(0) - } else if colType == sqltypes.VarBinary { - defaultVal = sqltypes.NewVarBinary("") - } - table.AddColumn(colName, colType, defaultVal, "") - } - setPK(table, pKeys) - return table -} - -func setPK(ta *schema.Table, colnames []string) error { - if len(ta.Indexes) != 0 { - panic("setPK must be called before adding other indexes") - } - pkIndex := ta.AddIndex("PRIMARY", true) - for _, colname := range colnames { - pkIndex.AddColumn(colname, 1) - } - ta.Done() - return nil -} diff --git a/go/vt/vttablet/tabletserver/planbuilder/dml.go b/go/vt/vttablet/tabletserver/planbuilder/dml.go index 205f980548e..155cc76e3bd 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/dml.go +++ b/go/vt/vttablet/tabletserver/planbuilder/dml.go @@ -17,8 +17,6 @@ limitations under the License. package planbuilder import ( - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -26,6 +24,42 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) +func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { + plan = &Plan{ + PlanID: PlanPassSelect, + Table: lookupTable(sel.From, tables), + FieldQuery: GenerateFieldQuery(sel), + FullQuery: GenerateLimitQuery(sel), + } + if sel.Lock != "" { + plan.PlanID = PlanSelectLock + } + + if sel.Where != nil { + comp, ok := sel.Where.Expr.(*sqlparser.ComparisonExpr) + if ok && comp.IsImpossible() { + plan.PlanID = PlanSelectImpossible + return plan, nil + } + } + + // Check if it's a NEXT VALUE statement. + if nextVal, ok := sel.SelectExprs[0].(sqlparser.Nextval); ok { + if plan.Table == nil || plan.Table.Type != schema.Sequence { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s is not a sequence", sqlparser.String(sel.From)) + } + plan.PlanID = PlanNextval + v, err := sqlparser.NewPlanValue(nextVal.Expr) + if err != nil { + return nil, err + } + plan.NextCount = v + plan.FieldQuery = nil + plan.FullQuery = nil + } + return plan, nil +} + func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ PlanID: PlanPassDML, @@ -71,64 +105,13 @@ func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan return plan, nil } -func analyzeSet(set *sqlparser.Set) (plan *Plan) { - return &Plan{ - PlanID: PlanSet, - FullQuery: GenerateFullQuery(set), - } -} - -func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { - plan = &Plan{ - PlanID: PlanPassSelect, - Table: lookupTable(sel.From, tables), - FieldQuery: GenerateFieldQuery(sel), - FullQuery: GenerateLimitQuery(sel), - } - if sel.Lock != "" { - plan.PlanID = PlanSelectLock - } - - if sel.Where != nil { - comp, ok := sel.Where.Expr.(*sqlparser.ComparisonExpr) - if ok && comp.IsImpossible() { - plan.PlanID = PlanSelectImpossible - return plan, nil - } - } - - // Check if it's a NEXT VALUE statement. - if nextVal, ok := sel.SelectExprs[0].(sqlparser.Nextval); ok { - if plan.Table == nil || plan.Table.Type != schema.Sequence { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s is not a sequence", sqlparser.String(sel.From)) - } - plan.PlanID = PlanNextval - v, err := sqlparser.NewPlanValue(nextVal.Expr) - if err != nil { - return nil, err - } - plan.PKValues = []sqltypes.PlanValue{v} - plan.FieldQuery = nil - plan.FullQuery = nil - } - return plan, nil -} - func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ PlanID: PlanPassDML, FullQuery: GenerateFullQuery(ins), } - if ins.Action == sqlparser.ReplaceStr { - plan.Reason = ReasonReplace - return plan, nil - } tableName := sqlparser.GetTableName(ins.Table) - if tableName.IsEmpty() { - plan.Reason = ReasonTable - return plan, nil - } plan.Table = tables[tableName.String()] if plan.Table == nil { return plan, nil @@ -136,153 +119,11 @@ func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan switch { case plan.Table.Type == schema.Message: - // message inserts need to continue being strict, even in passthrough dml mode, - // because field defaults are set here - + return analyzeInsertMessage(ins, plan, plan.Table) case plan.Table.IsTopic(): plan.PlanID = PlanInsertTopic - plan.Reason = ReasonTopic - return plan, nil - - case PassthroughDMLs: - // In passthrough dml mode, allow the operation even if the - // table is unknown in the schema. - return plan, nil - } - - if !plan.Table.HasPrimary() { - log.Warningf("no primary key for table %s", tableName) - plan.Reason = ReasonTableNoIndex - return plan, nil - } - switch plan.Table.Type { - case schema.NoType, schema.Sequence: - // For now, allow sequence inserts. - return analyzeInsertNoType(ins, plan, plan.Table) - case schema.Message: - return analyzeInsertMessage(ins, plan, plan.Table) - } - panic("unreachable") -} - -func analyzeInsertNoType(ins *sqlparser.Insert, plan *Plan, table *schema.Table) (*Plan, error) { - // Populate column list from schema if it wasn't specified. - if len(ins.Columns) == 0 { - for _, col := range table.Columns { - ins.Columns = append(ins.Columns, col.Name) - } - } - pkColumnNumbers := getInsertPKColumns(ins.Columns, table) - - if sel, ok := ins.Rows.(sqlparser.SelectStatement); ok { - if ins.OnDup != nil { - // Upserts not allowed for subqueries. - // http://bugs.mysql.com/bug.php?id=58637 - plan.Reason = ReasonUpsertSubquery - return plan, nil - } - plan.PlanID = PlanInsertSubquery - plan.OuterQuery = GenerateInsertOuterQuery(ins) - plan.Subquery = GenerateLimitQuery(sel) - for _, col := range ins.Columns { - colIndex := table.FindColumn(col) - if colIndex == -1 { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "column %v not found in table %s", col, table.Name) - } - plan.ColumnNumbers = append(plan.ColumnNumbers, colIndex) - } - plan.SubqueryPKColumns = pkColumnNumbers - return plan, nil - } - - // If it's not a sqlparser.SelectStatement, it's Values. - rowList := ins.Rows.(sqlparser.Values) - for i := range rowList { - if len(rowList[i]) == 0 { - for _, col := range table.Columns { - expr, err := sqlparser.ExprFromValue(col.Default) - if err != nil { - return nil, vterrors.Wrap(err, "could not create default row for insert without row values") - } - rowList[i] = append(rowList[i], expr) - } - continue - } - if len(rowList[i]) != len(ins.Columns) { - return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "column count doesn't match value count") - } - } - plan.PKValues = getInsertPKValues(pkColumnNumbers, rowList, table) - if plan.PKValues == nil { - plan.Reason = ReasonComplexExpr - return plan, nil - } - - if ins.OnDup == nil { - plan.PlanID = PlanInsertPK - plan.OuterQuery = sqlparser.NewParsedQuery(ins) - return plan, nil - } - - // Compute secondary pk values if OnDup changes them. - var ok bool - plan.SecondaryPKValues, ok = analyzeOnDupExpressions(ins, table.Indexes[0]) - if !ok { - plan.Reason = ReasonPKChange - return plan, nil - } - - // If the table only has one unique key then it is safe to pass through - // a simple upsert unmodified even if there are multiple rows in the - // statement. The action is same as a regular insert except that we - // may have to publish possible PK changes by OnDup, which would be - // recorded in SecondaryPKValues. - if table.UniqueIndexes() <= 1 { - plan.PlanID = PlanInsertPK - plan.OuterQuery = sqlparser.NewParsedQuery(ins) return plan, nil } - - // Otherwise multiple rows are unsupported - if len(rowList) > 1 { - plan.Reason = ReasonUpsertMultiRow - return plan, nil - } - plan.PlanID = PlanUpsertPK - newins := *ins - newins.Ignore = "" - newins.OnDup = nil - plan.OuterQuery = sqlparser.NewParsedQuery(&newins) - tableAlias := &sqlparser.AliasedTableExpr{Expr: ins.Table} - upd := &sqlparser.Update{ - Comments: ins.Comments, - TableExprs: sqlparser.TableExprs{tableAlias}, - Exprs: sqlparser.UpdateExprs(ins.OnDup), - } - - // We need to replace 'values' expressions with the actual values they reference. - var formatErr error - plan.UpsertQuery = GenerateUpdateOuterQuery(upd, tableAlias, func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { - if node, ok := node.(*sqlparser.ValuesFuncExpr); ok { - if !node.Name.Qualifier.IsEmpty() && node.Name.Qualifier != ins.Table { - formatErr = vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, - "could not find qualified column %v in table %v", - sqlparser.String(node.Name), sqlparser.String(ins.Table)) - return - } - colnum := ins.Columns.FindColumn(node.Name.Name) - if colnum == -1 { - formatErr = vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "could not find column %v", node.Name) - return - } - buf.Myprintf("(%v)", rowList[0][colnum]) - return - } - node.Format(buf) - }) - if formatErr != nil { - return nil, formatErr - } return plan, nil } @@ -346,103 +187,16 @@ func analyzeInsertMessage(ins *sqlparser.Insert, plan *Plan, table *schema.Table return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s must be specified for message insert", col.String()) } - pkColumnNumbers := getInsertPKColumns(ins.Columns, table) - plan.PKValues = getInsertPKValues(pkColumnNumbers, rowList, table) - if plan.PKValues == nil { - // Dead code. The previous checks already catch this condition. - plan.Reason = ReasonComplexExpr - return plan, nil - } plan.PlanID = PlanInsertMessage - plan.OuterQuery = sqlparser.NewParsedQuery(ins) + plan.FullQuery = GenerateFullQuery(ins) return plan, nil } -func getInsertPKColumns(columns sqlparser.Columns, table *schema.Table) (pkColumnNumbers []int) { - pkIndex := table.Indexes[0] - pkColumnNumbers = make([]int, len(pkIndex.Columns)) - for i := range pkColumnNumbers { - pkColumnNumbers[i] = -1 - } - for i, column := range columns { - index := pkIndex.FindColumn(column) - if index == -1 { - continue - } - pkColumnNumbers[index] = i - } - return pkColumnNumbers -} - -func addVal(ins *sqlparser.Insert, col sqlparser.ColIdent, expr sqlparser.Expr) int { - ins.Columns = append(ins.Columns, col) - rows := ins.Rows.(sqlparser.Values) - for i := range rows { - rows[i] = append(rows[i], expr) - } - return len(ins.Columns) - 1 -} - -func copyVal(ins *sqlparser.Insert, col sqlparser.ColIdent, colIndex int) int { - ins.Columns = append(ins.Columns, col) - rows := ins.Rows.(sqlparser.Values) - for i := range rows { - rows[i] = append(rows[i], rows[i][colIndex]) - } - return len(ins.Columns) - 1 -} - -func getInsertPKValues(pkColumnNumbers []int, rowList sqlparser.Values, table *schema.Table) []sqltypes.PlanValue { - pkValues := make([]sqltypes.PlanValue, len(pkColumnNumbers)) - // We iterate by columns (j, i). - for j, columnNumber := range pkColumnNumbers { - if columnNumber == -1 { - // No value was specified. Use the default from the schema for all rows. - pkValues[j] = sqltypes.PlanValue{Value: table.GetPKColumn(j).Default} - continue - } - var ok bool - pkValues[j], ok = extractColumnValues(rowList, columnNumber) - if !ok { - return nil - } - } - return pkValues -} - -// analyzeOnDupExpressions analyzes the OnDup and returns the list for any pk value changes. -func analyzeOnDupExpressions(ins *sqlparser.Insert, pkIndex *schema.Index) (pkValues []sqltypes.PlanValue, ok bool) { - rowList := ins.Rows.(sqlparser.Values) - for _, expr := range ins.OnDup { - index := pkIndex.FindColumn(expr.Name.Name) - if index == -1 { - continue - } - - if pkValues == nil { - pkValues = make([]sqltypes.PlanValue, len(pkIndex.Columns)) - } - if vf, ok := expr.Expr.(*sqlparser.ValuesFuncExpr); ok { - if !vf.Name.Qualifier.IsEmpty() && vf.Name.Qualifier != ins.Table { - return nil, false - } - insertCol := ins.Columns.FindColumn(vf.Name.Name) - if insertCol == -1 { - return nil, false - } - pkValues[index], ok = extractColumnValues(rowList, insertCol) - if !ok { - return nil, false - } - continue - } - - pkValues[index], ok = extractSingleValue(expr.Expr) - if !ok { - return nil, false - } +func analyzeSet(set *sqlparser.Set) (plan *Plan) { + return &Plan{ + PlanID: PlanSet, + FullQuery: GenerateFullQuery(set), } - return pkValues, true } func lookupTable(tableExprs sqlparser.TableExprs, tables map[string]*schema.Table) *schema.Table { @@ -460,28 +214,20 @@ func lookupTable(tableExprs sqlparser.TableExprs, tables map[string]*schema.Tabl return tables[tableName.String()] } -// extractColumnValues extracts the values of a column into a PlanValue. -func extractColumnValues(rowList sqlparser.Values, colnum int) (sqltypes.PlanValue, bool) { - pv := sqltypes.PlanValue{Values: make([]sqltypes.PlanValue, len(rowList))} - for i := 0; i < len(rowList); i++ { - var ok bool - pv.Values[i], ok = extractSingleValue(rowList[i][colnum]) - if !ok { - return pv, false - } +func addVal(ins *sqlparser.Insert, col sqlparser.ColIdent, expr sqlparser.Expr) int { + ins.Columns = append(ins.Columns, col) + rows := ins.Rows.(sqlparser.Values) + for i := range rows { + rows[i] = append(rows[i], expr) } - return pv, true + return len(ins.Columns) - 1 } -func extractSingleValue(expr sqlparser.Expr) (sqltypes.PlanValue, bool) { - pv := sqltypes.PlanValue{} - if !sqlparser.IsNull(expr) && !sqlparser.IsValue(expr) { - return pv, false - } - var err error - pv, err = sqlparser.NewPlanValue(expr) - if err != nil { - return pv, false +func copyVal(ins *sqlparser.Insert, col sqlparser.ColIdent, colIndex int) int { + ins.Columns = append(ins.Columns, col) + rows := ins.Rows.(sqlparser.Values) + for i := range rows { + rows[i] = append(rows[i], rows[i][colIndex]) } - return pv, true + return len(ins.Columns) - 1 } diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan.go b/go/vt/vttablet/tabletserver/planbuilder/plan.go index a682b7f9aa2..f2ab88c2c4a 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan.go @@ -59,13 +59,6 @@ const ( PlanPassDML // PlanDMLLimit is an update or delete with a limit. PlanDMLLimit - // PlanInsertPK is insert statement where the PK value is - // supplied with the query. - PlanInsertPK - // PlanInsertSubquery is same as PlanDMLSubquery but for inserts. - PlanInsertSubquery - // PlanUpsertPK is for insert ... on duplicate key constructs. - PlanUpsertPK // PlanInsertTopic is for inserting into message topics. PlanInsertTopic // PlanInsertMessage is for inserting into message tables. @@ -95,9 +88,6 @@ var planName = [NumPlans]string{ "NEXTVAL", "PASS_DML", "DML_LIMIT", - "INSERT_PK", - "INSERT_SUBQUERY", - "UPSERT_PK", "INSERT_TOPIC", "INSERT_MESSAGE", "SET", @@ -185,7 +175,6 @@ func (rt ReasonType) MarshalJSON() ([]byte, error) { // Plan is built for selects and DMLs. type Plan struct { PlanID PlanType - Reason ReasonType Table *schema.Table // NewName is the new name of the table. Set for DDLs which create or change the table. NewName sqlparser.TableIdent @@ -199,32 +188,12 @@ type Plan struct { // FullQuery will be set for all plans. FullQuery *sqlparser.ParsedQuery - // For PK plans, only OuterQuery is set. - // For SUBQUERY plans, Subquery is also set. - OuterQuery *sqlparser.ParsedQuery - Subquery *sqlparser.ParsedQuery - UpsertQuery *sqlparser.ParsedQuery - - // PlanInsertSubquery: columns to be inserted. - ColumnNumbers []int - - // PKValues is an sqltypes.Value if it's sourced - // from the query. If it's a bind var then it's - // a string including the ':' prefix(es). - // PlanDMLPK: where clause values. - // PlanInsertPK: values clause. - // PlanNextVal: increment. - PKValues []sqltypes.PlanValue - - // For update: set clause if pk is changing. - SecondaryPKValues []sqltypes.PlanValue + // NextCount stores the count for "select next". + NextCount sqltypes.PlanValue // 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 } // TableName returns the table name for the plan. diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go index 020fffc9d0d..1026e6c99e8 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go @@ -30,7 +30,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/tableacl" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" @@ -40,35 +39,24 @@ import ( // This is only for testing. func (p *Plan) MarshalJSON() ([]byte, error) { mplan := struct { - PlanID PlanType - Reason ReasonType `json:",omitempty"` - TableName sqlparser.TableIdent `json:",omitempty"` - Permissions []Permission `json:",omitempty"` - FieldQuery *sqlparser.ParsedQuery `json:",omitempty"` - FullQuery *sqlparser.ParsedQuery `json:",omitempty"` - OuterQuery *sqlparser.ParsedQuery `json:",omitempty"` - Subquery *sqlparser.ParsedQuery `json:",omitempty"` - UpsertQuery *sqlparser.ParsedQuery `json:",omitempty"` - ColumnNumbers []int `json:",omitempty"` - PKValues []sqltypes.PlanValue `json:",omitempty"` - SecondaryPKValues []sqltypes.PlanValue `json:",omitempty"` - WhereClause *sqlparser.ParsedQuery `json:",omitempty"` - SubqueryPKColumns []int `json:",omitempty"` + PlanID PlanType + TableName sqlparser.TableIdent `json:",omitempty"` + Permissions []Permission `json:",omitempty"` + FieldQuery *sqlparser.ParsedQuery `json:",omitempty"` + FullQuery *sqlparser.ParsedQuery `json:",omitempty"` + NextCount string `json:",omitempty"` + WhereClause *sqlparser.ParsedQuery `json:",omitempty"` }{ - PlanID: p.PlanID, - Reason: p.Reason, - TableName: p.TableName(), - Permissions: p.Permissions, - 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, + PlanID: p.PlanID, + TableName: p.TableName(), + Permissions: p.Permissions, + FieldQuery: p.FieldQuery, + FullQuery: p.FullQuery, + WhereClause: p.WhereClause, + } + if !p.NextCount.IsNull() { + b, _ := p.NextCount.MarshalJSON() + mplan.NextCount = string(b) } return json.Marshal(&mplan) } diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index 16d82967940..fe468b67d07 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -619,3 +619,13 @@ func (qe *QueryEngine) handleHTTPConsolidations(response http.ResponseWriter, re response.Write([]byte(fmt.Sprintf("%v: %s\n", v.Count, query))) } } + +// unicoded returns a valid UTF-8 string that json won't reject +func unicoded(in string) (out string) { + for i, v := range in { + if v == 0xFFFD { + return in[:i] + } + } + return in +} diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index c6491bfde5b..949d533619b 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -39,7 +39,7 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/proto/topodata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) @@ -54,7 +54,7 @@ type QueryExecutor struct { ctx context.Context logStats *tabletenv.LogStats tsv *TabletServer - tabletType topodata.TabletType + tabletType topodatapb.TabletType } var sequenceFields = []*querypb.Field{ @@ -117,32 +117,26 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { } defer conn.Recycle() switch qre.plan.PlanID { - case planbuilder.PlanPassDML: - if !qre.tsv.qe.allowUnsafeDMLs && (qre.tsv.qe.binlogFormat != connpool.BinlogFormatRow) { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: cannot identify primary key of statement") - } - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) - case planbuilder.PlanInsertPK: - return qre.execInsertPK(conn) + case planbuilder.PlanPassDML, planbuilder.PlanSet: + return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) case planbuilder.PlanInsertMessage: return qre.execInsertMessage(conn) - case planbuilder.PlanInsertSubquery: - return qre.execInsertSubquery(conn) case planbuilder.PlanDMLLimit: return qre.execDMLLimit(conn) case planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: - return qre.execSQL(conn, qre.query, true) - case planbuilder.PlanUpsertPK: - return qre.execUpsertPK(conn) - case planbuilder.PlanSet: - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) + return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, false) case planbuilder.PlanPassSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible: - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, false) + maxrows := qre.getSelectLimit() + qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) + qr, err := qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, false) + if err != nil { + return nil, err + } + if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { + return nil, err + } + return qr, nil default: - // handled above: - // planbuilder.PlanNextval - // planbuilder.PlanDDL - // not valid for Execute: // planbuilder.PlanSelectStream // planbuilder.PlanMessageStream: @@ -151,39 +145,26 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { } else { switch qre.plan.PlanID { case planbuilder.PlanPassSelect, planbuilder.PlanSelectImpossible: - return qre.execSelect() + maxrows := qre.getSelectLimit() + qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) + qr, err := qre.execSelect() + if err != nil { + return nil, err + } + if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { + return nil, err + } + return qr, nil case planbuilder.PlanSelectLock: return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) - case planbuilder.PlanSet: - return qre.execSet() - case planbuilder.PlanOtherRead: - conn, connErr := qre.getConn() - if connErr != nil { - return nil, connErr - } - defer conn.Recycle() - return qre.execSQL(conn, qre.query, true) - - case planbuilder.PlanPassDML: - fallthrough - case planbuilder.PlanInsertPK: - fallthrough - case planbuilder.PlanInsertMessage: - fallthrough - case planbuilder.PlanInsertSubquery: - fallthrough - case planbuilder.PlanDMLLimit: - fallthrough - case planbuilder.PlanUpsertPK: + case planbuilder.PlanSet, planbuilder.PlanOtherRead: + return qre.execOther() + case planbuilder.PlanPassDML, planbuilder.PlanInsertMessage, planbuilder.PlanDMLLimit: if !qre.tsv.qe.autoCommit.Get() { return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) } return qre.execDmlAutoCommit() default: - // handled above: - // planbuilder.PlanNextval - // planbuilder.PlanDDL - // not valid for Execute: // planbuilder.PlanSelectStream // planbuilder.PlanMessageStream: @@ -228,7 +209,7 @@ func (qre *QueryExecutor) Stream(callback func(*sqltypes.Result) error) error { qre.tsv.qe.streamQList.Add(qd) defer qre.tsv.qe.streamQList.Remove(qd) - return qre.streamFetch(conn, qre.plan.FullQuery, qre.bindVars, "", callback) + return qre.streamFetch(conn, qre.plan.FullQuery, qre.bindVars, callback) } // MessageStream streams messages from a message table. @@ -264,20 +245,11 @@ func (qre *QueryExecutor) execDmlAutoCommit() (reply *sqltypes.Result, err error return qre.execAsTransaction(func(conn *TxConnection) (reply *sqltypes.Result, err error) { switch qre.plan.PlanID { case planbuilder.PlanPassDML: - if !qre.tsv.qe.allowUnsafeDMLs && (qre.tsv.qe.binlogFormat != connpool.BinlogFormatRow) { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: cannot identify primary key of statement") - } - reply, err = qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) - case planbuilder.PlanInsertPK: - reply, err = qre.execInsertPK(conn) + reply, err = qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) case planbuilder.PlanInsertMessage: return qre.execInsertMessage(conn) - case planbuilder.PlanInsertSubquery: - reply, err = qre.execInsertSubquery(conn) case planbuilder.PlanDMLLimit: reply, err = qre.execDMLLimit(conn) - case planbuilder.PlanUpsertPK: - reply, err = qre.execUpsertPK(conn) default: return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported query: %s", qre.query) } @@ -398,7 +370,7 @@ func (qre *QueryExecutor) execDDL() (*sqltypes.Result, error) { sql := qre.query var err error if qre.plan.FullQuery != nil { - sql, _, err = qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars, nil, "") + sql, _, err = qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) if err != nil { return nil, err } @@ -444,7 +416,7 @@ func (qre *QueryExecutor) execDDL() (*sqltypes.Result, error) { } func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) { - inc, err := resolveNumber(qre.plan.PKValues[0], qre.bindVars) + inc, err := resolveNumber(qre.plan.NextCount, qre.bindVars) if err != nil { return nil, err } @@ -534,70 +506,18 @@ func (qre *QueryExecutor) execSelect() (*sqltypes.Result, error) { return nil, err } defer conn.Recycle() - return qre.dbConnFetch(conn, qre.plan.FullQuery, qre.bindVars, "", true) -} - -func (qre *QueryExecutor) execInsertPK(conn *TxConnection) (*sqltypes.Result, error) { - pkRows, err := buildValueList(qre.plan.Table, qre.plan.PKValues, qre.bindVars) - if err != nil { - return nil, err - } - return qre.execInsertPKRows(conn, nil, pkRows) + return qre.dbConnFetch(conn, qre.plan.FullQuery, qre.bindVars) } func (qre *QueryExecutor) execInsertMessage(conn *TxConnection) (*sqltypes.Result, error) { qre.bindVars["#time_now"] = sqltypes.Int64BindVariable(time.Now().UnixNano()) - return qre.execInsertPK(conn) -} - -func (qre *QueryExecutor) execInsertSubquery(conn *TxConnection) (*sqltypes.Result, error) { - innerResult, err := qre.txFetch(conn, qre.plan.Subquery, qre.bindVars, nil, "", true, false) - if err != nil { - return nil, err - } - innerRows := innerResult.Rows - if len(innerRows) == 0 { - return &sqltypes.Result{RowsAffected: 0}, nil - } - if len(qre.plan.ColumnNumbers) != len(innerRows[0]) { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Subquery length does not match column list") - } - pkRows := make([][]sqltypes.Value, len(innerRows)) - for i, innerRow := range innerRows { - pkRows[i] = applyFilterWithPKDefaults(qre.plan.Table, qre.plan.SubqueryPKColumns, innerRow) - } - // Validating first row is sufficient - if err := validateRow(qre.plan.Table, qre.plan.Table.PKColumns, pkRows[0]); err != nil { - return nil, err - } - - extras := map[string]sqlparser.Encodable{ - "#values": sqlparser.InsertValues(innerRows), - } - return qre.execInsertPKRows(conn, extras, pkRows) -} - -func (qre *QueryExecutor) execInsertPKRows(conn *TxConnection, extras map[string]sqlparser.Encodable, pkRows [][]sqltypes.Value) (*sqltypes.Result, error) { - var bsc string - // Build comments only if we're not in RBR mode. - if qre.tsv.qe.binlogFormat != connpool.BinlogFormatRow { - secondaryList, err := buildSecondaryList(qre.plan.Table, pkRows, qre.plan.SecondaryPKValues, qre.bindVars) - if err != nil { - return nil, err - } - bsc = buildStreamComment(qre.plan.Table, pkRows, secondaryList) - } - return qre.txFetch(conn, qre.plan.OuterQuery, qre.bindVars, extras, bsc, true, true) -} - -func (qre *QueryExecutor) execUpsertPK(conn *TxConnection) (*sqltypes.Result, error) { - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) + return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) } func (qre *QueryExecutor) execDMLLimit(conn *TxConnection) (*sqltypes.Result, error) { maxrows := qre.tsv.qe.maxResultSize.Get() qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) - result, err := qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, nil, "", true, true) + result, err := qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) if err != nil { return nil, err } @@ -616,18 +536,18 @@ func (qre *QueryExecutor) verifyRowCount(count, maxrows int64) error { if warnThreshold > 0 && count > warnThreshold { callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) tabletenv.Warnings.Add("ResultsExceeded", 1) - log.Warningf("CallerID: %s row count %v exceeds warning threshold %v: %q", callerID.Username, count, warnThreshold, queryAsString(qre.plan.FullQuery.Query, qre.bindVars)) + log.Warningf("caller id: %s row count %v exceeds warning threshold %v: %q", callerID.Username, count, warnThreshold, queryAsString(qre.plan.FullQuery.Query, qre.bindVars)) } return nil } -func (qre *QueryExecutor) execSet() (*sqltypes.Result, error) { +func (qre *QueryExecutor) execOther() (*sqltypes.Result, error) { conn, err := qre.getConn() if err != nil { return nil, err } defer conn.Recycle() - return qre.dbConnFetch(conn, qre.plan.FullQuery, qre.bindVars, "", false) + return qre.dbConnFetch(conn, qre.plan.FullQuery, qre.bindVars) } func (qre *QueryExecutor) getConn() (*connpool.DBConn, error) { @@ -663,12 +583,12 @@ func (qre *QueryExecutor) getStreamConn() (*connpool.DBConn, error) { } func (qre *QueryExecutor) qFetch(logStats *tabletenv.LogStats, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - sql, sqlWithoutComments, err := qre.generateFinalSQL(parsedQuery, bindVars, nil, "") + sql, sqlWithoutComments, err := qre.generateFinalSQL(parsedQuery, bindVars) if err != nil { return nil, err } // Check tablet type. - if qre.tsv.qe.enableConsolidator || (qre.tsv.qe.enableConsolidatorReplicas && qre.tabletType != topodata.TabletType_MASTER) { + if qre.tsv.qe.enableConsolidator || (qre.tsv.qe.enableConsolidatorReplicas && qre.tabletType != topodatapb.TabletType_MASTER) { q, original := qre.tsv.qe.consolidator.Create(string(sqlWithoutComments)) if original { defer q.Broadcast() @@ -704,12 +624,12 @@ func (qre *QueryExecutor) qFetch(logStats *tabletenv.LogStats, parsedQuery *sqlp } // txFetch fetches from a TxConnection. -func (qre *QueryExecutor) txFetch(conn *TxConnection, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, extras map[string]sqlparser.Encodable, buildStreamComment string, wantfields, record bool) (*sqltypes.Result, error) { - sql, _, err := qre.generateFinalSQL(parsedQuery, bindVars, extras, buildStreamComment) +func (qre *QueryExecutor) txFetch(conn *TxConnection, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, record bool) (*sqltypes.Result, error) { + sql, _, err := qre.generateFinalSQL(parsedQuery, bindVars) if err != nil { return nil, err } - qr, err := qre.execSQL(conn, sql, wantfields) + qr, err := qre.execSQL(conn, sql, true) if err != nil { return nil, err } @@ -721,48 +641,45 @@ func (qre *QueryExecutor) txFetch(conn *TxConnection, parsedQuery *sqlparser.Par } // dbConnFetch fetches from a connpool.DBConn. -func (qre *QueryExecutor) dbConnFetch(conn *connpool.DBConn, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, buildStreamComment string, wantfields bool) (*sqltypes.Result, error) { - sql, _, err := qre.generateFinalSQL(parsedQuery, bindVars, nil, buildStreamComment) +func (qre *QueryExecutor) dbConnFetch(conn *connpool.DBConn, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { + sql, _, err := qre.generateFinalSQL(parsedQuery, bindVars) if err != nil { return nil, err } - return qre.execSQL(conn, sql, wantfields) + return qre.execSQL(conn, sql, true) } // streamFetch performs a streaming fetch. -func (qre *QueryExecutor) streamFetch(conn *connpool.DBConn, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, buildStreamComment string, callback func(*sqltypes.Result) error) error { - sql, _, err := qre.generateFinalSQL(parsedQuery, bindVars, nil, buildStreamComment) +func (qre *QueryExecutor) streamFetch(conn *connpool.DBConn, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, callback func(*sqltypes.Result) error) error { + sql, _, err := qre.generateFinalSQL(parsedQuery, bindVars) if err != nil { return err } return qre.execStreamSQL(conn, sql, callback) } -func (qre *QueryExecutor) generateFinalSQL(parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, extras map[string]sqlparser.Encodable, buildStreamComment string) (string, string, error) { +func (qre *QueryExecutor) generateFinalSQL(parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable) (string, string, error) { var buf strings.Builder buf.WriteString(qre.marginComments.Leading) - query, err := parsedQuery.GenerateQuery(bindVars, extras) + query, err := parsedQuery.GenerateQuery(bindVars, nil) if err != nil { return "", "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s", err) } buf.WriteString(query) - if buildStreamComment != "" { - buf.WriteString(buildStreamComment) - } withoutComments := buf.String() buf.WriteString(qre.marginComments.Trailing) fullSQL := buf.String() return fullSQL, withoutComments, nil } -func (qre *QueryExecutor) getLimit(query *sqlparser.ParsedQuery) int64 { +func (qre *QueryExecutor) getSelectLimit() int64 { maxRows := qre.tsv.qe.maxResultSize.Get() sqlLimit := qre.options.GetSqlSelectLimit() - if sqlLimit > 0 && sqlLimit < maxRows && strings.HasPrefix(sqlparser.StripLeadingComments(query.Query), "select") { + if sqlLimit > 0 && sqlLimit < maxRows { return sqlLimit } - return maxRows + 1 + return maxRows } // poolConn is an abstraction for reusing code in execSQL. @@ -775,14 +692,7 @@ func (qre *QueryExecutor) execSQL(conn poolConn, sql string, wantfields bool) (* defer span.Finish() defer qre.logStats.AddRewrittenSQL(sql, time.Now()) - res, err := conn.Exec(ctx, sql, int(qre.tsv.qe.maxResultSize.Get()), wantfields) - warnThreshold := qre.tsv.qe.warnResultSize.Get() - if res != nil && warnThreshold > 0 && int64(len(res.Rows)) > warnThreshold { - callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) - tabletenv.Warnings.Add("ResultsExceeded", 1) - log.Warningf("CallerID: %s Results returned (%v) exceeds warning threshold (%v): %q", callerID.Username, len(res.Rows), warnThreshold, sql) - } - return res, err + return conn.Exec(ctx, sql, int(qre.tsv.qe.maxResultSize.Get()), wantfields) } func (qre *QueryExecutor) execStreamSQL(conn *connpool.DBConn, sql string, callback func(*sqltypes.Result) error) error { @@ -802,3 +712,12 @@ func (qre *QueryExecutor) execStreamSQL(conn *connpool.DBConn, sql string, callb } return nil } + +// resolveNumber extracts a number from a bind variable or sql value. +func resolveNumber(pv sqltypes.PlanValue, bindVars map[string]*querypb.BindVariable) (int64, error) { + v, err := pv.ResolveValue(bindVars) + if err != nil { + return 0, err + } + return sqltypes.ToInt64(v) +} diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 3053b65b3b6..ba6ce759293 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -267,26 +267,6 @@ func TestQueryExecutorPlanPassDmlReplaceInto(t *testing.T) { testCommitHelper(t, tsv, qre) } -func TestQueryExecutorPlanInsertPk(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - db.AddQuery("insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) - want := &sqltypes.Result{} - query := "insert into test_table(pk) values(1)" - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanInsertPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - func TestQueryExecutorPlanInsertMessage(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() @@ -307,527 +287,6 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { } } -func TestQueryExecutorPlanInsertSubQueryAutoCommmit(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "insert into test_table(pk) select pk from test_table where pk = 2" - want := &sqltypes.Result{} - db.AddQuery(query, want) - selectQuery := "select pk from test_table where pk = 2 limit 10001" - db.AddQuery(selectQuery, &sqltypes.Result{ - Fields: []*querypb.Field{{ - Name: "pk", - Type: sqltypes.Int32, - }}, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt32(2)}, - }, - }) - - insertQuery := "insert into test_table(pk) values (2) /* _stream test_table (pk ) (2 ); */" - - db.AddQuery(insertQuery, &sqltypes.Result{}) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanInsertSubquery, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -func TestQueryExecutorPlanInsertSubQuery(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "insert into test_table(pk) select pk from test_table where pk = 2" - want := &sqltypes.Result{} - db.AddQuery(query, want) - selectQuery := "select pk from test_table where pk = 2 limit 10001" - db.AddQuery(selectQuery, &sqltypes.Result{ - Fields: []*querypb.Field{{ - Name: "pk", - Type: sqltypes.Int32, - }}, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt32(2)}, - }, - }) - - insertQuery := "insert into test_table(pk) values (2) /* _stream test_table (pk ) (2 ); */" - - db.AddQuery(insertQuery, &sqltypes.Result{}) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanInsertSubquery, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{"insert into test_table(pk) values (2) /* _stream test_table (pk ) (2 ); */"} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanInsertSubQueryRBR(t *testing.T) { - // RBR test is almost identical to the non-RBR test, except that - // the _stream comments are suppressed for RBR. - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "insert into test_table(pk) select pk from test_table where pk = 2" - want := &sqltypes.Result{} - db.AddQuery(query, want) - selectQuery := "select pk from test_table where pk = 2 limit 10001" - db.AddQuery(selectQuery, &sqltypes.Result{ - Fields: []*querypb.Field{{ - Name: "pk", - Type: sqltypes.Int32, - }}, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt32(2)}, - }, - }) - - insertQuery := "insert into test_table(pk) values (2)" - - db.AddQuery(insertQuery, &sqltypes.Result{}) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanInsertSubquery, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{"insert into test_table(pk) values (2)"} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanUpsertPk(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - db.AddQuery("insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) - want := &sqltypes.Result{} - query := "insert into test_table(pk) values(1) on duplicate key update val=1" - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanUpsertPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{"insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */"} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - - db.AddRejectedQuery("insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", errRejected) - txid = newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, query, txid) - defer testCommitHelper(t, tsv, qre) - _, err = qre.Execute() - wantErr := "rejected" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Errorf("qre.Execute() = %v, want %v", err, wantErr) - } - if gotqueries = fetchRecordedQueries(qre); gotqueries != nil { - t.Errorf("queries: %v, want nil", gotqueries) - } - - db.AddRejectedQuery( - "insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", - mysql.NewSQLError(mysql.ERDupEntry, mysql.SSDupKey, "err"), - ) - db.AddQuery("update test_table(pk) set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) - txid = newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, query, txid) - defer testCommitHelper(t, tsv, qre) - _, err = qre.Execute() - wantErr = "err (errno 1062) (sqlstate 23000)" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Errorf("qre.Execute() = %v, want %v", err, wantErr) - } - if gotqueries = fetchRecordedQueries(qre); gotqueries != nil { - t.Errorf("queries: %v, want nil", gotqueries) - } - - db.AddRejectedQuery( - "insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", - mysql.NewSQLError(mysql.ERDupEntry, mysql.SSDupKey, "ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'"), - ) - db.AddQuery( - "update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", - &sqltypes.Result{RowsAffected: 1}, - ) - txid = newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, query, txid) - defer testCommitHelper(t, tsv, qre) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - want = &sqltypes.Result{ - RowsAffected: 2, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("got: %v, want: %v", got, want) - } - wantqueries = []string{"update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */"} - gotqueries = fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - - // Test pk change. - db.AddRejectedQuery( - "insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", - mysql.NewSQLError(mysql.ERDupEntry, mysql.SSDupKey, "ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'"), - ) - db.AddQuery( - "update test_table set pk = 2 where pk in (1) /* _stream test_table (pk ) (1 ) (2 ); */", - &sqltypes.Result{RowsAffected: 1}, - ) - txid = newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, "insert into test_table(pk) values (1) on duplicate key update pk=2", txid) - defer testCommitHelper(t, tsv, qre) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - want = &sqltypes.Result{ - RowsAffected: 2, - } - if !reflect.DeepEqual(got, want) { - t.Errorf("got: %v, want: %v", got, want) - } - wantqueries = []string{"update test_table set pk = 2 where pk in (1) /* _stream test_table (pk ) (1 ) (2 ); */"} - gotqueries = fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanUpsertPkSingleUnique(t *testing.T) { - db := setUpQueryExecutorTestWithOneUniqueKey(t) - defer db.Close() - query := "insert into test_table(pk) values (1) on duplicate key update val = 1 /* _stream test_table (pk ) (1 ); */" - db.AddQuery(query, &sqltypes.Result{}) - db.AddRejectedQuery("insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", errRejected) - want := &sqltypes.Result{} - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query[0:strings.Index(query, " /*")], txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanInsertPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{query} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - - // PK changed by upsert. - query = "insert into test_table(pk) values (1), (2), (3) on duplicate key update pk = 5 /* _stream test_table (pk ) (1 ) (2 ) (3 ) (5 ) (5 ) (5 ); */" - db.AddQuery(query, &sqltypes.Result{}) - want = &sqltypes.Result{} - ctx = context.Background() - tsv = newTestTabletServer(ctx, noFlags, db) - txid = newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, query[0:strings.Index(query, " /*")], txid) - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanInsertPK, qre.plan.PlanID) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries = []string{query} - gotqueries = fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - - // PK changed by using values. - query = "insert into test_table(pk, name) values (1, 4), (2, 5), (3, 6) on duplicate key update pk = values(name) /* _stream test_table (pk ) (1 ) (2 ) (3 ) (4 ) (5 ) (6 ); */" - db.AddQuery(query, &sqltypes.Result{}) - want = &sqltypes.Result{} - ctx = context.Background() - tsv = newTestTabletServer(ctx, noFlags, db) - txid = newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, query[0:strings.Index(query, " /*")], txid) - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanInsertPK, qre.plan.PlanID) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries = []string{query} - gotqueries = fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - - query = "insert into test_table(pk) values (1), (2), (3) on duplicate key update val = 5 /* _stream test_table (pk ) (1 ) (2 ) (3 ); */" - db.AddQuery(query, &sqltypes.Result{}) - want = &sqltypes.Result{} - ctx = context.Background() - tsv = newTestTabletServer(ctx, noFlags, db) - txid = newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, query[0:strings.Index(query, " /*")], txid) - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanInsertPK, qre.plan.PlanID) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries = []string{query} - gotqueries = fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanUpsertPkRBR(t *testing.T) { - // For UPSERT, the query just becomes a pass-through in RBR mode. - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "insert into test_table(pk) values (1) on duplicate key update val = 1" - db.AddQuery(query, &sqltypes.Result{}) - want := &sqltypes.Result{} - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanUpsertPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{query} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanUpsertPkAutoCommit(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - db.AddQuery("insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) - want := &sqltypes.Result{} - query := "insert into test_table(pk) values(1) on duplicate key update val=1" - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanUpsertPK, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - - db.AddRejectedQuery("insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", errRejected) - _, err = qre.Execute() - wantErr := "rejected" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Fatalf("qre.Execute() = %v, want %v", err, wantErr) - } - - db.AddRejectedQuery( - "insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", - mysql.NewSQLError(mysql.ERDupEntry, mysql.SSDupKey, "err"), - ) - db.AddQuery("update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) - _, err = qre.Execute() - wantErr = "err (errno 1062) (sqlstate 23000)" - if err == nil || !strings.Contains(err.Error(), wantErr) { - t.Fatalf("qre.Execute() = %v, want %v", err, wantErr) - } - - db.AddRejectedQuery( - "insert into test_table(pk) values (1) /* _stream test_table (pk ) (1 ); */", - mysql.NewSQLError(mysql.ERDupEntry, mysql.SSDupKey, "ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'"), - ) - db.AddQuery( - "update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", - &sqltypes.Result{RowsAffected: 1}, - ) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - want = &sqltypes.Result{ - RowsAffected: 2, - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -func TestQueryExecutorPlanDmlSubQuery(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set addr = 3 where name = 1" - expandedQuery := "select pk from test_table where name = 1 limit 10001 for update" - want := &sqltypes.Result{} - db.AddQuery(query, want) - db.AddQuery(expandedQuery, &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int32}, - }, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt32(2)}, - }, - }) - updateQuery := "update test_table set addr = 3 where pk in (2) /* _stream test_table (pk ) (2 ); */" - db.AddQuery(updateQuery, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanDMLSubquery, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{updateQuery} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanDmlSubQueryRBR(t *testing.T) { - // RBR test is almost identical to the non-RBR test, except that - // the _stream comments are suppressed for RBR. - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set addr = 3 where name = 1" - expandedQuery := "select pk from test_table where name = 1 limit 10001 for update" - want := &sqltypes.Result{} - db.AddQuery(query, want) - db.AddQuery(expandedQuery, &sqltypes.Result{ - Fields: []*querypb.Field{ - {Type: sqltypes.Int32}, - }, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt32(2)}, - }, - }) - updateQuery := "update test_table set addr = 3 where pk in (2)" - db.AddQuery(updateQuery, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanDMLSubquery, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{updateQuery} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } -} - -func TestQueryExecutorPlanDmlSubQueryAutoCommit(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set addr = 3 where name = 1" - expandedQuery := "select pk from test_table where name = 1 limit 10001 for update" - want := &sqltypes.Result{} - db.AddQuery(query, want) - db.AddQuery(expandedQuery, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanDMLSubquery, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - func TestQueryExecutorPlanOtherWithinATransaction(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() diff --git a/go/vt/vttablet/tabletserver/queryz.go b/go/vt/vttablet/tabletserver/queryz.go index dea2b1dfd0a..ef37e77c5cf 100644 --- a/go/vt/vttablet/tabletserver/queryz.go +++ b/go/vt/vttablet/tabletserver/queryz.go @@ -36,7 +36,6 @@ var (

- @@ -54,7 +53,6 @@ var ( - @@ -74,7 +72,6 @@ type queryzRow struct { Query string Table string Plan planbuilder.PlanType - Reason planbuilder.ReasonType Count int64 tm time.Duration mysqlTime time.Duration @@ -150,10 +147,9 @@ func queryzHandler(qe *QueryEngine, w http.ResponseWriter, r *http.Request) { continue } Value := &queryzRow{ - Query: logz.Wrappable(sqlparser.TruncateForUI(v)), - Table: plan.TableName().String(), - Plan: plan.PlanID, - Reason: plan.Reason, + Query: logz.Wrappable(sqlparser.TruncateForUI(v)), + Table: plan.TableName().String(), + Plan: plan.PlanID, } Value.Count, Value.tm, Value.mysqlTime, Value.Rows, Value.Errors = plan.Stats() var timepq time.Duration diff --git a/go/vt/vttablet/tabletserver/queryz_test.go b/go/vt/vttablet/tabletserver/queryz_test.go index f639ed7f061..78f5256a199 100644 --- a/go/vt/vttablet/tabletserver/queryz_test.go +++ b/go/vt/vttablet/tabletserver/queryz_test.go @@ -41,7 +41,6 @@ func TestQueryzHandler(t *testing.T) { Plan: &planbuilder.Plan{ Table: &schema.Table{Name: sqlparser.NewTableIdent("test_table")}, PlanID: planbuilder.PlanPassSelect, - Reason: planbuilder.ReasonTable, }, } plan1.AddStats(10, 2*time.Second, 1*time.Second, 2, 0) @@ -51,7 +50,6 @@ func TestQueryzHandler(t *testing.T) { Plan: &planbuilder.Plan{ Table: &schema.Table{Name: sqlparser.NewTableIdent("test_table")}, PlanID: planbuilder.PlanDDL, - Reason: planbuilder.ReasonDefault, }, } plan2.AddStats(1, 2*time.Millisecond, 1*time.Millisecond, 1, 0) @@ -61,7 +59,6 @@ func TestQueryzHandler(t *testing.T) { Plan: &planbuilder.Plan{ Table: &schema.Table{Name: sqlparser.NewTableIdent("")}, PlanID: planbuilder.PlanOtherRead, - Reason: planbuilder.ReasonDefault, }, } plan3.AddStats(1, 75*time.Millisecond, 50*time.Millisecond, 1, 0) @@ -72,7 +69,6 @@ func TestQueryzHandler(t *testing.T) { Plan: &planbuilder.Plan{ Table: &schema.Table{Name: sqlparser.NewTableIdent("")}, PlanID: planbuilder.PlanOtherRead, - Reason: planbuilder.ReasonDefault, }, } plan4.AddStats(1, 1*time.Millisecond, 1*time.Millisecond, 1, 0) diff --git a/go/vt/vttablet/tabletserver/rules/rules_test.go b/go/vt/vttablet/tabletserver/rules/rules_test.go index 31eb8f2cf1c..309d93fb9fa 100644 --- a/go/vt/vttablet/tabletserver/rules/rules_test.go +++ b/go/vt/vttablet/tabletserver/rules/rules_test.go @@ -185,7 +185,7 @@ func TestFilterByPlan(t *testing.T) { t.Errorf("qrs1:\n%s, want\n%s", got, want) } - qrs1 = qrs.FilterByPlan("select", planbuilder.PlanInsertPK, "a") + qrs1 = qrs.FilterByPlan("select", planbuilder.PlanPassDML, "a") want = compacted(`[{ "Description":"rule 3", "Name":"r3", @@ -201,7 +201,7 @@ func TestFilterByPlan(t *testing.T) { t.Errorf("qrs1:\n%s, want\n%s", got, want) } - qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanInsertPK, "a") + qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanPassDML, "a") if qrs1.rules != nil { t.Errorf("want nil, got non-nil") } @@ -220,7 +220,7 @@ func TestFilterByPlan(t *testing.T) { qr5 := NewQueryRule("rule 5", "r5", QRFail) qrs.Add(qr5) - qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanInsertPK, "a") + qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanPassDML, "a") want = compacted(`[{ "Description":"rule 5", "Name":"r5", @@ -258,12 +258,12 @@ func TestQueryRule(t *testing.T) { } qr.AddPlanCond(planbuilder.PlanPassSelect) - qr.AddPlanCond(planbuilder.PlanInsertPK) + qr.AddPlanCond(planbuilder.PlanPassDML) if qr.plans[0] != planbuilder.PlanPassSelect { t.Errorf("want PASS_SELECT, got %s", qr.plans[0].String()) } - if qr.plans[1] != planbuilder.PlanInsertPK { + if qr.plans[1] != planbuilder.PlanPassDML { t.Errorf("want INSERT_PK, got %s", qr.plans[1].String()) } From 11e4978fc19b88dd275684071dfbf039f9f2e600 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 15 Mar 2020 16:22:05 -0700 Subject: [PATCH 304/825] deprecation: refactor code for readability Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/query_executor.go | 188 ++++++++---------- 1 file changed, 82 insertions(+), 106 deletions(-) diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 949d533619b..264efe34ace 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -116,61 +116,89 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { return nil, err } defer conn.Recycle() - switch qre.plan.PlanID { - case planbuilder.PlanPassDML, planbuilder.PlanSet: - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) - case planbuilder.PlanInsertMessage: - return qre.execInsertMessage(conn) - case planbuilder.PlanDMLLimit: - return qre.execDMLLimit(conn) - case planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, false) - case planbuilder.PlanPassSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible: - maxrows := qre.getSelectLimit() - qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) - qr, err := qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, false) - if err != nil { - return nil, err - } - if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { - return nil, err - } - return qr, nil - default: - // not valid for Execute: - // planbuilder.PlanSelectStream - // planbuilder.PlanMessageStream: - return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) + return qre.txConnExec(conn) + } + + switch qre.plan.PlanID { + case planbuilder.PlanPassSelect, planbuilder.PlanSelectImpossible: + maxrows := qre.getSelectLimit() + qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) + qr, err := qre.execSelect() + if err != nil { + return nil, err } - } else { - switch qre.plan.PlanID { - case planbuilder.PlanPassSelect, planbuilder.PlanSelectImpossible: - maxrows := qre.getSelectLimit() - qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) - qr, err := qre.execSelect() - if err != nil { - return nil, err - } - if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { - return nil, err - } - return qr, nil - case planbuilder.PlanSelectLock: + if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { + return nil, err + } + return qr, nil + case planbuilder.PlanSelectLock: + return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) + case planbuilder.PlanSet, planbuilder.PlanOtherRead: + return qre.execOther() + case planbuilder.PlanPassDML, planbuilder.PlanInsertMessage, planbuilder.PlanDMLLimit: + if !qre.tsv.qe.autoCommit.Get() { return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) - case planbuilder.PlanSet, planbuilder.PlanOtherRead: - return qre.execOther() - case planbuilder.PlanPassDML, planbuilder.PlanInsertMessage, planbuilder.PlanDMLLimit: - if !qre.tsv.qe.autoCommit.Get() { - return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) - } - return qre.execDmlAutoCommit() - default: - // not valid for Execute: - // planbuilder.PlanSelectStream - // planbuilder.PlanMessageStream: - return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) } + return qre.execAsTransaction(qre.txConnExec) + } + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) +} + +func (qre *QueryExecutor) execAsTransaction(f func(conn *TxConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { + conn, beginSQL, err := qre.tsv.te.txPool.LocalBegin(qre.ctx, qre.options) + if err != nil { + return nil, err + } + defer qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) + if beginSQL != "" { + qre.logStats.AddRewrittenSQL(beginSQL, time.Now()) + } + + reply, err = f(conn) + + start := time.Now() + if err != nil { + qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) + qre.logStats.AddRewrittenSQL("rollback", start) + return nil, err + } + commitSQL, err := qre.tsv.te.txPool.LocalCommit(qre.ctx, conn) + + // As above LocalCommit is a no-op for autocommmit so don't log anything. + if commitSQL != "" { + qre.logStats.AddRewrittenSQL(commitSQL, start) + } + + if err != nil { + return nil, err + } + return reply, nil +} + +func (qre *QueryExecutor) txConnExec(conn *TxConnection) (*sqltypes.Result, error) { + switch qre.plan.PlanID { + case planbuilder.PlanPassDML, planbuilder.PlanSet: + return qre.txFetch(conn, true) + case planbuilder.PlanInsertMessage: + qre.bindVars["#time_now"] = sqltypes.Int64BindVariable(time.Now().UnixNano()) + return qre.txFetch(conn, true) + case planbuilder.PlanDMLLimit: + return qre.execDMLLimit(conn) + case planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: + return qre.txFetch(conn, false) + case planbuilder.PlanPassSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible: + maxrows := qre.getSelectLimit() + qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) + qr, err := qre.txFetch(conn, false) + if err != nil { + return nil, err + } + if err := qre.verifyRowCount(int64(len(qr.Rows)), maxrows); err != nil { + return nil, err + } + return qr, nil } + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) } // Stream performs a streaming query execution. @@ -241,53 +269,6 @@ func (qre *QueryExecutor) MessageStream(callback func(*sqltypes.Result) error) e return nil } -func (qre *QueryExecutor) execDmlAutoCommit() (reply *sqltypes.Result, err error) { - return qre.execAsTransaction(func(conn *TxConnection) (reply *sqltypes.Result, err error) { - switch qre.plan.PlanID { - case planbuilder.PlanPassDML: - reply, err = qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) - case planbuilder.PlanInsertMessage: - return qre.execInsertMessage(conn) - case planbuilder.PlanDMLLimit: - reply, err = qre.execDMLLimit(conn) - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported query: %s", qre.query) - } - return reply, err - }) -} - -func (qre *QueryExecutor) execAsTransaction(f func(conn *TxConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { - conn, beginSQL, err := qre.tsv.te.txPool.LocalBegin(qre.ctx, qre.options) - if err != nil { - return nil, err - } - defer qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) - if beginSQL != "" { - qre.logStats.AddRewrittenSQL(beginSQL, time.Now()) - } - - reply, err = f(conn) - - start := time.Now() - if err != nil { - qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) - qre.logStats.AddRewrittenSQL("rollback", start) - return nil, err - } - commitSQL, err := qre.tsv.te.txPool.LocalCommit(qre.ctx, conn) - - // As above LocalCommit is a no-op for autocommmit so don't log anything. - if commitSQL != "" { - qre.logStats.AddRewrittenSQL(commitSQL, start) - } - - if err != nil { - return nil, err - } - return reply, nil -} - // checkPermissions returns an error if the query does not pass all checks // (query blacklisting, table ACL). func (qre *QueryExecutor) checkPermissions() error { @@ -509,15 +490,10 @@ func (qre *QueryExecutor) execSelect() (*sqltypes.Result, error) { return qre.dbConnFetch(conn, qre.plan.FullQuery, qre.bindVars) } -func (qre *QueryExecutor) execInsertMessage(conn *TxConnection) (*sqltypes.Result, error) { - qre.bindVars["#time_now"] = sqltypes.Int64BindVariable(time.Now().UnixNano()) - return qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) -} - func (qre *QueryExecutor) execDMLLimit(conn *TxConnection) (*sqltypes.Result, error) { maxrows := qre.tsv.qe.maxResultSize.Get() qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) - result, err := qre.txFetch(conn, qre.plan.FullQuery, qre.bindVars, true) + result, err := qre.txFetch(conn, true) if err != nil { return nil, err } @@ -624,8 +600,8 @@ func (qre *QueryExecutor) qFetch(logStats *tabletenv.LogStats, parsedQuery *sqlp } // txFetch fetches from a TxConnection. -func (qre *QueryExecutor) txFetch(conn *TxConnection, parsedQuery *sqlparser.ParsedQuery, bindVars map[string]*querypb.BindVariable, record bool) (*sqltypes.Result, error) { - sql, _, err := qre.generateFinalSQL(parsedQuery, bindVars) +func (qre *QueryExecutor) txFetch(conn *TxConnection, record bool) (*sqltypes.Result, error) { + sql, _, err := qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) if err != nil { return nil, err } From f5a5dc54afc07b9a1f18f0d095fee2f2c4744061 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 15 Mar 2020 16:31:45 -0700 Subject: [PATCH 305/825] deprecation: support direct autocommit Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/endtoend/framework/server.go | 1 - go/vt/vttablet/tabletserver/query_engine.go | 2 -- go/vt/vttablet/tabletserver/query_executor.go | 21 ++++++++++++------- .../vttablet/tabletserver/tabletenv/config.go | 5 ++--- go/vt/vttablet/tabletserver/tabletserver.go | 7 ------- .../tabletserver/tabletserver_flaky_test.go | 5 ----- 6 files changed, 15 insertions(+), 26 deletions(-) diff --git a/go/vt/vttablet/endtoend/framework/server.go b/go/vt/vttablet/endtoend/framework/server.go index 9d235ea946b..f51d26eae03 100644 --- a/go/vt/vttablet/endtoend/framework/server.go +++ b/go/vt/vttablet/endtoend/framework/server.go @@ -65,7 +65,6 @@ func StartServer(connParams, connAppDebugParams mysql.ConnParams, dbName string) dbcfgs := dbconfigs.NewTestDBConfigs(connParams, connAppDebugParams, dbName) config := tabletenv.DefaultQsConfig - config.EnableAutoCommit = true config.StrictTableACL = true config.TwoPCEnable = true config.TwoPCAbandonAge = 1 diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index fe468b67d07..7329621f32f 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -150,7 +150,6 @@ type QueryEngine struct { queryPoolWaiters sync2.AtomicInt64 queryPoolWaiterCap sync2.AtomicInt64 binlogFormat connpool.BinlogFormat - autoCommit sync2.AtomicBool maxResultSize sync2.AtomicInt64 warnResultSize sync2.AtomicInt64 maxDMLRows sync2.AtomicInt64 @@ -217,7 +216,6 @@ func NewQueryEngine(checker connpool.MySQLChecker, se *schema.Engine, config tab config.HotRowProtectionConcurrentTransactions) qe.streamQList = NewQueryList() - qe.autoCommit.Set(config.EnableAutoCommit) qe.strictTableACL = config.StrictTableACL qe.enableTableACLDryRun = config.EnableTableACLDryRun diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 264efe34ace..69ba5062f55 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -135,16 +135,21 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) case planbuilder.PlanSet, planbuilder.PlanOtherRead: return qre.execOther() - case planbuilder.PlanPassDML, planbuilder.PlanInsertMessage, planbuilder.PlanDMLLimit: - if !qre.tsv.qe.autoCommit.Get() { - return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) - } - return qre.execAsTransaction(qre.txConnExec) + case planbuilder.PlanPassDML, planbuilder.PlanInsertMessage: + return qre.execAsTransaction(true /* autocommit */, qre.txConnExec) + case planbuilder.PlanDMLLimit: + return qre.execAsTransaction(false /* autocommit */, qre.txConnExec) } return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) } -func (qre *QueryExecutor) execAsTransaction(f func(conn *TxConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { +func (qre *QueryExecutor) execAsTransaction(autocommit bool, f func(conn *TxConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { + if autocommit { + if qre.options == nil { + qre.options = &querypb.ExecuteOptions{} + } + qre.options.TransactionIsolation = querypb.ExecuteOptions_AUTOCOMMIT + } conn, beginSQL, err := qre.tsv.te.txPool.LocalBegin(qre.ctx, qre.options) if err != nil { return nil, err @@ -385,7 +390,7 @@ func (qre *QueryExecutor) execDDL() (*sqltypes.Result, error) { return result, nil } - result, err := qre.execAsTransaction(func(conn *TxConnection) (*sqltypes.Result, error) { + result, err := qre.execAsTransaction(true /* autocommit */, func(conn *TxConnection) (*sqltypes.Result, error) { return qre.execSQL(conn, sql, true) }) @@ -410,7 +415,7 @@ func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) { t.SequenceInfo.Lock() defer t.SequenceInfo.Unlock() if t.SequenceInfo.NextVal == 0 || t.SequenceInfo.NextVal+inc > t.SequenceInfo.LastVal { - _, err := qre.execAsTransaction(func(conn *TxConnection) (*sqltypes.Result, error) { + _, err := qre.execAsTransaction(false /* autocommit */, func(conn *TxConnection) (*sqltypes.Result, error) { query := fmt.Sprintf("select next_id, cache from %s where id = 0 for update", sqlparser.String(tableName)) qr, err := qre.execSQL(conn, query, false) if err != nil { diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index 454ae05c29f..1034c7a8ccd 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -45,6 +45,7 @@ var ( // Placeholder for deprecated variable. // TODO(sougou): deprecate the flag after release 7.0. deprecatedMessagePoolPrefillParallelism int + deprecatedAutocommit bool ) func init() { @@ -82,7 +83,7 @@ func init() { flag.BoolVar(&Config.TerseErrors, "queryserver-config-terse-errors", DefaultQsConfig.TerseErrors, "prevent bind vars from escaping in returned errors") flag.StringVar(&Config.PoolNamePrefix, "pool-name-prefix", DefaultQsConfig.PoolNamePrefix, "pool name prefix, vttablet has several pools and each of them has a name. This config specifies the prefix of these pool names") flag.BoolVar(&Config.WatchReplication, "watch_replication_stream", false, "When enabled, vttablet will stream the MySQL replication stream from the local server, and use it to support the include_event_token ExecuteOptions.") - flag.BoolVar(&Config.EnableAutoCommit, "enable-autocommit", DefaultQsConfig.EnableAutoCommit, "if the flag is on, a DML outsides a transaction will be auto committed. This flag is deprecated and is unsafe. Instead, use the VTGate provided autocommit feature.") + flag.BoolVar(&deprecatedAutocommit, "enable-autocommit", true, "This flag is deprecated. Autocommit is always allowed.") flag.BoolVar(&Config.TwoPCEnable, "twopc_enable", DefaultQsConfig.TwoPCEnable, "if the flag is on, 2pc is enabled. Other 2pc flags must be supplied.") flag.StringVar(&Config.TwoPCCoordinatorAddress, "twopc_coordinator_address", DefaultQsConfig.TwoPCCoordinatorAddress, "address of the (VTGate) process(es) that will be used to notify of abandoned transactions.") flag.Float64Var(&Config.TwoPCAbandonAge, "twopc_abandon_age", DefaultQsConfig.TwoPCAbandonAge, "time in seconds. Any unresolved transaction older than this time will be sent to the coordinator to be resolved.") @@ -160,7 +161,6 @@ type TabletConfig struct { TxPoolWaiterCap int StrictTableACL bool TerseErrors bool - EnableAutoCommit bool EnableTableACLDryRun bool PoolNamePrefix string TableACLExemptACL string @@ -237,7 +237,6 @@ var DefaultQsConfig = TabletConfig{ StreamBufferSize: 32 * 1024, StrictTableACL: false, TerseErrors: false, - EnableAutoCommit: false, EnableTableACLDryRun: false, PoolNamePrefix: "", TableACLExemptACL: "", diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 00eb4a8bced..27f5db98624 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -1077,7 +1077,6 @@ func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Targe // Setting ExecuteOptions_AUTOCOMMIT will get a connection out of the // pool without actually begin/commit the transaction. if (options.TransactionIsolation == querypb.ExecuteOptions_DEFAULT) && - tsv.qe.autoCommit.Get() && asTransaction && tsv.qe.passthroughDMLs.Get() { options.TransactionIsolation = querypb.ExecuteOptions_AUTOCOMMIT @@ -1885,12 +1884,6 @@ func (tsv *TabletServer) QueryPlanCacheCap() int { return int(tsv.qe.QueryPlanCacheCap()) } -// SetAutoCommit sets autocommit on or off. -// This function should only be used for testing. -func (tsv *TabletServer) SetAutoCommit(auto bool) { - tsv.qe.autoCommit.Set(auto) -} - // SetMaxResultSize changes the max result size to the specified value. // This function should only be used for testing. func (tsv *TabletServer) SetMaxResultSize(val int) { diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index 90ed7bf7274..7730c747b8a 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -2572,11 +2572,6 @@ func TestConfigChanges(t *testing.T) { t.Errorf("tsv.qe.QueryPlanCacheCap: %d, want %d", val, newSize) } - tsv.SetAutoCommit(true) - if val := tsv.qe.autoCommit.Get(); !val { - t.Errorf("tsv.qe.autoCommit.Get: %v, want true", val) - } - tsv.SetMaxResultSize(newSize) if val := tsv.MaxResultSize(); val != newSize { t.Errorf("MaxResultSize: %d, want %d", val, newSize) From 0794095eba95943eb10d72d9d9860494c1584178 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 15 Mar 2020 20:14:08 -0700 Subject: [PATCH 306/825] deprecation: tests for planbuilder Signed-off-by: Sugu Sougoumarane --- .../planbuilder/{dml.go => builder.go} | 28 +- .../vttablet/tabletserver/planbuilder/plan.go | 124 +- .../tabletserver/planbuilder/query_gen.go | 69 - .../planbuilder/testdata/exec_cases.txt | 2047 +++-------------- .../planbuilder/testdata/stream_cases.txt | 10 +- go/vt/vttablet/tabletserver/query_executor.go | 12 +- .../tabletserver/query_executor_test.go | 36 +- go/vt/vttablet/tabletserver/querylogz_test.go | 2 +- go/vt/vttablet/tabletserver/queryz_test.go | 2 +- go/vt/vttablet/tabletserver/rules/map_test.go | 6 +- .../vttablet/tabletserver/rules/rules_test.go | 28 +- go/vt/vttablet/tabletserver/tabletserver.go | 4 +- 12 files changed, 405 insertions(+), 1963 deletions(-) rename go/vt/vttablet/tabletserver/planbuilder/{dml.go => builder.go} (93%) diff --git a/go/vt/vttablet/tabletserver/planbuilder/dml.go b/go/vt/vttablet/tabletserver/planbuilder/builder.go similarity index 93% rename from go/vt/vttablet/tabletserver/planbuilder/dml.go rename to go/vt/vttablet/tabletserver/planbuilder/builder.go index 155cc76e3bd..74fb0e4e42d 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/dml.go +++ b/go/vt/vttablet/tabletserver/planbuilder/builder.go @@ -26,7 +26,7 @@ import ( func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ - PlanID: PlanPassSelect, + PlanID: PlanSelect, Table: lookupTable(sel.From, tables), FieldQuery: GenerateFieldQuery(sel), FullQuery: GenerateLimitQuery(sel), @@ -62,21 +62,23 @@ func analyzeSelect(sel *sqlparser.Select, tables map[string]*schema.Table) (plan func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ - PlanID: PlanPassDML, + PlanID: PlanUpdate, Table: lookupTable(upd.TableExprs, tables), } // 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 upd.Where != nil { + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", upd.Where) + plan.WhereClause = buf.ParsedQuery() + } if PassthroughDMLs || upd.Limit != nil { plan.FullQuery = GenerateFullQuery(upd) return plan, nil } - plan.PlanID = PlanDMLLimit + plan.PlanID = PlanUpdateLimit upd.Limit = execLimit plan.FullQuery = GenerateFullQuery(upd) upd.Limit = nil @@ -85,20 +87,22 @@ func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ - PlanID: PlanPassDML, + PlanID: PlanDelete, Table: lookupTable(del.TableExprs, tables), } // 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 del.Where != nil { + buf := sqlparser.NewTrackedBuffer(nil) + buf.Myprintf("%v", del.Where) + plan.WhereClause = buf.ParsedQuery() + } if PassthroughDMLs || del.Limit != nil { plan.FullQuery = GenerateFullQuery(del) return plan, nil } - plan.PlanID = PlanDMLLimit + plan.PlanID = PlanDeleteLimit del.Limit = execLimit plan.FullQuery = GenerateFullQuery(del) del.Limit = nil @@ -107,7 +111,7 @@ func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan *Plan, err error) { plan = &Plan{ - PlanID: PlanPassDML, + PlanID: PlanInsert, FullQuery: GenerateFullQuery(ins), } diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan.go b/go/vt/vttablet/tabletserver/planbuilder/plan.go index f2ab88c2c4a..deabf41f49a 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan.go @@ -18,7 +18,6 @@ package planbuilder import ( "encoding/json" - "fmt" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" @@ -30,11 +29,9 @@ import ( ) var ( - // ErrTooComplex indicates given sql query is too complex. - ErrTooComplex = vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "Complex") - execLimit = &sqlparser.Limit{Rowcount: sqlparser.NewValArg([]byte(":#maxLimit"))} + execLimit = &sqlparser.Limit{Rowcount: sqlparser.NewValArg([]byte(":#maxLimit"))} - // PassthroughDMLs will return PlanPassDML for all update or delete statements + // PassthroughDMLs will return plans that pass-through the DMLs without changing them. PassthroughDMLs = false ) @@ -43,60 +40,47 @@ var ( // PlanType indicates a query plan type. type PlanType int +// The following are PlanType values. const ( - // PlanPassSelect is pass through select statements. This is the - // default plan for select statements. - PlanPassSelect PlanType = iota - // PlanSelectLock is for a select that locks. + PlanSelect PlanType = iota PlanSelectLock - // PlanNextval is for NEXTVAL. PlanNextval - // PlanPassDML is pass through update & delete statements. This is - // the default plan for update and delete statements. - // If PassthroughDMLs is true, then it is used for all DML statements - // and is valid in all replication modes. - // Otherwise is only allowed in row based replication mode - PlanPassDML - // PlanDMLLimit is an update or delete with a limit. - PlanDMLLimit - // PlanInsertTopic is for inserting into message topics. + PlanSelectImpossible + PlanInsert PlanInsertTopic - // PlanInsertMessage is for inserting into message tables. PlanInsertMessage - // PlanSet is for SET statements. - PlanSet - // PlanDDL is for DDL statements. + PlanUpdate + PlanUpdateLimit + PlanDelete + PlanDeleteLimit PlanDDL - // PlanSelectStream is used for streaming queries. - PlanSelectStream - // PlanOtherRead is for SHOW, DESCRIBE & EXPLAIN statements. + PlanSet PlanOtherRead - // PlanOtherAdmin is for REPAIR, OPTIMIZE and TRUNCATE statements. PlanOtherAdmin - // PlanMessageStream is used for streaming messages. + PlanSelectStream PlanMessageStream - // PlanSelectImpossible is used for where or having clauses that can never be true. - PlanSelectImpossible - // NumPlans stores the total number of plans NumPlans ) // Must exactly match order of plan constants. var planName = [NumPlans]string{ - "PASS_SELECT", - "SELECT_LOCK", - "NEXTVAL", - "PASS_DML", - "DML_LIMIT", - "INSERT_TOPIC", - "INSERT_MESSAGE", - "SET", + "Select", + "SelectLock", + "Nextval", + "SelectImpossible", + "Insert", + "InsertTopic", + "InsertMessage", + "Update", + "UpdateLimit", + "Delete", + "DeleteLimit", "DDL", - "SELECT_STREAM", - "OTHER_READ", - "OTHER_ADMIN", - "MESSAGE_STREAM", - "SELECT_IMPOSSIBLE", + "Set", + "OtherRead", + "OtherAdmin", + "SelectStream", + "MessageStream", } func (pt PlanType) String() string { @@ -118,7 +102,7 @@ func PlanByName(s string) (pt PlanType, ok bool) { // IsSelect returns true if PlanType is about a select query. func (pt PlanType) IsSelect() bool { - return pt == PlanPassSelect || pt == PlanSelectLock || pt == PlanSelectImpossible + return pt == PlanSelect || pt == PlanSelectLock || pt == PlanSelectImpossible } // MarshalJSON returns a json string for PlanType. @@ -128,56 +112,10 @@ func (pt PlanType) MarshalJSON() ([]byte, error) { //_______________________________________________ -// ReasonType indicates why a query plan fails to build -type ReasonType int - -// Reason codes give a hint about why a certain plan was chosen. -const ( - ReasonDefault ReasonType = iota - ReasonTable - ReasonTableNoIndex - ReasonPKChange - ReasonComplexExpr - ReasonUpsertSubquery - ReasonUpsertMultiRow - ReasonReplace - ReasonMultiTable - ReasonTopic - NumReasons -) - -// Must exactly match order of reason constants. -var reasonName = [NumReasons]string{ - "DEFAULT", - "TABLE", - "TABLE_NOINDEX", - "PK_CHANGE", - "COMPLEX_EXPR", - "UPSERT_SUBQUERY", - "UPSERT_MULTI_ROW", - "REPLACE", - "MULTI_TABLE", - "TOPIC", -} - -// String returns a string representation of a ReasonType. -func (rt ReasonType) String() string { - return reasonName[rt] -} - -// MarshalJSON returns a json string for ReasonType. -func (rt ReasonType) MarshalJSON() ([]byte, error) { - return ([]byte)(fmt.Sprintf("\"%s\"", rt.String())), nil -} - -//_______________________________________________ - -// Plan is built for selects and DMLs. +// Plan contains the parameters for executing a request. type Plan struct { PlanID PlanType Table *schema.Table - // NewName is the new name of the table. Set for DDLs which create or change the table. - NewName sqlparser.TableIdent // Permissions stores the permissions for the tables accessed in the query. Permissions []Permission @@ -217,7 +155,7 @@ func Build(statement sqlparser.Statement, tables map[string]*schema.Table) (*Pla switch stmt := statement.(type) { case *sqlparser.Union: plan, err = &Plan{ - PlanID: PlanPassSelect, + PlanID: PlanSelect, FieldQuery: GenerateFieldQuery(stmt), FullQuery: GenerateLimitQuery(stmt), }, nil diff --git a/go/vt/vttablet/tabletserver/planbuilder/query_gen.go b/go/vt/vttablet/tabletserver/planbuilder/query_gen.go index 6dd37591ebf..8001ace3447 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/query_gen.go +++ b/go/vt/vttablet/tabletserver/planbuilder/query_gen.go @@ -18,7 +18,6 @@ package planbuilder import ( "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" ) // GenerateFullQuery generates the full query from the ast. @@ -65,71 +64,3 @@ func GenerateLimitQuery(selStmt sqlparser.SelectStatement) *sqlparser.ParsedQuer buf.Myprintf("%v", selStmt) return buf.ParsedQuery() } - -// GenerateInsertOuterQuery generates the outer query for inserts. -func GenerateInsertOuterQuery(ins *sqlparser.Insert) *sqlparser.ParsedQuery { - buf := sqlparser.NewTrackedBuffer(nil) - buf.Myprintf("%s %v%sinto %v%v values %a", - ins.Action, - ins.Comments, - ins.Ignore, - ins.Table, - ins.Columns, - ":#values", - ) - return buf.ParsedQuery() -} - -// GenerateUpdateOuterQuery generates the outer query for updates. -// If there is no custom formatting needed, formatter can be nil. -func GenerateUpdateOuterQuery(upd *sqlparser.Update, aliased *sqlparser.AliasedTableExpr, formatter sqlparser.NodeFormatter) *sqlparser.ParsedQuery { - buf := sqlparser.NewTrackedBuffer(formatter) - buf.Myprintf("update %v%v set %v where %a%v", upd.Comments, aliased.RemoveHints(), upd.Exprs, ":#pk", upd.OrderBy) - return buf.ParsedQuery() -} - -// GenerateDeleteOuterQuery generates the outer query for deletes. -func GenerateDeleteOuterQuery(del *sqlparser.Delete, aliased *sqlparser.AliasedTableExpr) *sqlparser.ParsedQuery { - buf := sqlparser.NewTrackedBuffer(nil) - buf.Myprintf("delete %vfrom %v where %a%v", del.Comments, aliased.RemoveHints(), ":#pk", del.OrderBy) - return buf.ParsedQuery() -} - -// GenerateUpdateSubquery generates the subquery for updates. -func GenerateUpdateSubquery(upd *sqlparser.Update, table *schema.Table, aliased *sqlparser.AliasedTableExpr) *sqlparser.ParsedQuery { - return GenerateSubquery( - table, - aliased, - upd.Where, - upd.OrderBy, - upd.Limit, - ) -} - -// GenerateDeleteSubquery generates the subquery for deletes. -func GenerateDeleteSubquery(del *sqlparser.Delete, table *schema.Table, aliased *sqlparser.AliasedTableExpr) *sqlparser.ParsedQuery { - return GenerateSubquery( - table, - aliased, - del.Where, - del.OrderBy, - del.Limit, - ) -} - -// GenerateSubquery generates a subquery based on the input parameters. -func GenerateSubquery(table *schema.Table, tableName *sqlparser.AliasedTableExpr, where *sqlparser.Where, order sqlparser.OrderBy, limit *sqlparser.Limit) *sqlparser.ParsedQuery { - buf := sqlparser.NewTrackedBuffer(nil) - if limit == nil { - limit = execLimit - } - buf.WriteString("select ") - prefix := "" - for _, colnum := range table.PKColumns { - buf.Myprintf("%s%v", prefix, table.Columns[colnum].Name) - prefix = ", " - } - buf.Myprintf(" from %v%v%v%v", tableName, where, order, limit) - buf.Myprintf(sqlparser.ForUpdateStr) - return buf.ParsedQuery() -} diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index d26044b6c73..3397ff6ee84 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -1,7 +1,7 @@ # union "select * from a union select * from b" { - "PlanID": "PASS_SELECT", + "PlanID": "Select", "TableName": "", "Permissions": [ { @@ -20,7 +20,7 @@ # union with limit "select * from a union select * from b limit 10" { - "PlanID": "PASS_SELECT", + "PlanID": "Select", "TableName": "", "Permissions": [ { @@ -36,10 +36,10 @@ "FullQuery": "select * from a union select * from b limit 10" } -# distinct -"select distinct * from a" +# with no where clause +"select * from a" { - "PlanID": "PASS_SELECT", + "PlanID": "Select", "TableName": "a", "Permissions": [ { @@ -48,28 +48,13 @@ } ], "FieldQuery": "select * from a where 1 != 1", - "FullQuery": "select distinct * from a limit :#maxLimit" -} - -# group by -"select * from a group by b" -{ - "PlanID": "PASS_SELECT", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 0 - } - ], - "FieldQuery": "select * from a where 1 != 1 group by b", - "FullQuery": "select * from a group by b limit :#maxLimit" + "FullQuery": "select * from a limit :#maxLimit" } -# having -"select * from a having b=1" +# select with a regular where clause +"select * from a where id=1" { - "PlanID": "PASS_SELECT", + "PlanID": "Select", "TableName": "a", "Permissions": [ { @@ -78,13 +63,13 @@ } ], "FieldQuery": "select * from a where 1 != 1", - "FullQuery": "select * from a having b = 1 limit :#maxLimit" + "FullQuery": "select * from a where id = 1 limit :#maxLimit" } -# limit +# select with limit "select * from a limit 5" { - "PlanID": "PASS_SELECT", + "PlanID": "Select", "TableName": "a", "Permissions": [ { @@ -99,7 +84,7 @@ # limit with offset arg "select * from a limit 10, 5" { - "PlanID": "PASS_SELECT", + "PlanID": "Select", "TableName": "a", "Permissions": [ { @@ -111,10 +96,10 @@ "FullQuery": "select * from a limit 10, 5" } -# limit with offset keyword -"select * from a limit 5 offset 10" +# select impossible +"select * from a where 1 != 1" { - "PlanID": "PASS_SELECT", + "PlanID": "SelectImpossible", "TableName": "a", "Permissions": [ { @@ -123,100 +108,13 @@ } ], "FieldQuery": "select * from a where 1 != 1", - "FullQuery": "select * from a limit 10, 5" -} - -# cross-db -"select * from a.b" -{ - "PlanID": "PASS_SELECT", - "TableName": "", - "Permissions": [ - { - "TableName": "b", - "Role": 0 - } - ], - "FieldQuery": "select * from a.b where 1 != 1", - "FullQuery": "select * from a.b limit :#maxLimit" -} - -# multi-table -"select * from a,b" -{ - "PlanID": "PASS_SELECT", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 0 - }, - { - "TableName": "b", - "Role": 0 - } - ], - "FieldQuery": "select * from a, b where 1 != 1", - "FullQuery": "select * from a, b limit :#maxLimit" -} - -# multi-table (join) -"select * from a join b" -{ - "PlanID": "PASS_SELECT", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 0 - }, - { - "TableName": "b", - "Role": 0 - } - ], - "FieldQuery": "select * from a join b where 1 != 1", - "FullQuery": "select * from a join b limit :#maxLimit" -} - -# multi-table (right join) -"select * from a right join b on c = d" -{ - "PlanID": "PASS_SELECT", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 0 - }, - { - "TableName": "b", - "Role": 0 - } - ], - "FieldQuery": "select * from a right join b on c = d where 1 != 1", - "FullQuery": "select * from a right join b on c = d limit :#maxLimit" -} - -# Parenthesized table -"select * from (b)" -{ - "PlanID": "PASS_SELECT", - "TableName": "", - "Permissions": [ - { - "TableName": "b", - "Role": 0 - } - ], - "FieldQuery": "select * from (b) where 1 != 1", - "FullQuery": "select * from (b) limit :#maxLimit" + "FullQuery": "select * from a where 1 != 1 limit :#maxLimit" } # bind in select list "select :bv from a" { - "PlanID": "PASS_SELECT", + "PlanID": "Select", "TableName": "a", "Permissions": [ { @@ -227,55 +125,10 @@ "FullQuery": "select :bv from a limit :#maxLimit" } -# simple -"select eid from a" -{ - "PlanID": "PASS_SELECT", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 0 - } - ], - "FieldQuery": "select eid from a where 1 != 1", - "FullQuery": "select eid from a limit :#maxLimit" -} - -# as -"select eid as foo from a" -{ - "PlanID": "PASS_SELECT", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 0 - } - ], - "FieldQuery": "select eid as foo from a where 1 != 1", - "FullQuery": "select eid as foo from a limit :#maxLimit" -} - -# * -"select * from a" -{ - "PlanID": "PASS_SELECT", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 0 - } - ], - "FieldQuery": "select * from a where 1 != 1", - "FullQuery": "select * from a limit :#maxLimit" -} - -# c.eid -"select c.eid from a as c" +# bind in select list and impossible +"select :bv from a where 1 != 1" { - "PlanID": "PASS_SELECT", + "PlanID": "SelectImpossible", "TableName": "a", "Permissions": [ { @@ -283,1370 +136,158 @@ "Role": 0 } ], - "FieldQuery": "select c.eid from a as c where 1 != 1", - "FullQuery": "select c.eid from a as c limit :#maxLimit" + "FullQuery": "select :bv from a where 1 != 1 limit :#maxLimit" } -# for update -"select eid from a for update" +# single value sequence +"select next value from seq" { - "PlanID": "SELECT_LOCK", - "TableName": "a", + "PlanID": "Nextval", + "TableName": "seq", "Permissions": [ { - "TableName": "a", + "TableName": "seq", "Role": 0 } ], - "FieldQuery": "select eid from a where 1 != 1", - "FullQuery": "select eid from a limit :#maxLimit for update" + "NextCount": "1" } -# lock in share mode -"select eid from a lock in share mode" +# sequence with number +"select next 10 values from seq" { - "PlanID": "SELECT_LOCK", - "TableName": "a", + "PlanID": "Nextval", + "TableName": "seq", "Permissions": [ { - "TableName": "a", + "TableName": "seq", "Role": 0 } ], - "FieldQuery": "select eid from a where 1 != 1", - "FullQuery": "select eid from a limit :#maxLimit lock in share mode" -} - -# insert cross-db -"insert into b.a (eid, id) values (1, :a)" -{ - "PlanID": "PASS_DML", - "Reason": "TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into b.a(eid, id) values (1, :a)" -} - -# insert cross-db -options:PassthroughDMLs -"insert into b.a (eid, id) values (1, :a)" -{ - "PlanID": "PASS_DML", - "Reason": "TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into b.a(eid, id) values (1, :a)" -} - -# insert with bind value -"insert into a (eid, id) values (1, :a)" -{ - "PlanID": "INSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, :a)", - "OuterQuery": "insert into a(eid, id) values (1, :a)", - "PKValues": [[1], [":a"]] -} - -# insert with bind value -options:PassthroughDMLs -"insert into a (eid, id) values (1, :a)" -{ - "PlanID": "PASS_DML", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, :a)" -} - -# default number -"insert into a (id) values (1)" -{ - "PlanID": "INSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(id) values (1)", - "OuterQuery": "insert into a(id) values (1)", - "PKValues": [0, [1]] -} - -# default string -"insert into d(id) values(1)" -{ - "PlanID": "INSERT_PK", - "TableName": "d", - "Permissions": [ - { - "TableName": "d", - "Role": 1 - } - ], - "FullQuery": "insert into d(id) values (1)", - "OuterQuery": "insert into d(id) values (1)", - "PKValues": ["0"] -} - -# default without INTO -"insert d(id) values(1)" -{ - "PlanID": "INSERT_PK", - "TableName": "d", - "Permissions": [ - { - "TableName": "d", - "Role": 1 - } - ], - "FullQuery": "insert into d(id) values (1)", - "OuterQuery": "insert into d(id) values (1)", - "PKValues": ["0"] -} - -# mismatch -"insert into a (eid, id) values (1)" -"column count doesn't match value count" - -# negative number -"insert into a (eid, id) values (-1, 2)" -{ - "PlanID": "INSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (-1, 2)", - "OuterQuery": "insert into a(eid, id) values (-1, 2)", - "PKValues": [[-1], [2]] -} - -# positive number -"insert into a (eid, id) values (+1, 2)" -{ - "PlanID": "INSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, 2)", - "OuterQuery": "insert into a(eid, id) values (1, 2)", - "PKValues": [[1], [2]] -} - -# non-trivial unary -"insert into a (eid, id) values (~1, 2)" -{ - "PlanID": "PASS_DML", - "Reason": "COMPLEX_EXPR", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (~1, 2)" + "NextCount": "10" } -# complex -"insert into a (eid, id) values (1+1, 2)" -{ - "PlanID": "PASS_DML", - "Reason": "COMPLEX_EXPR", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1 + 1, 2)" -} -# complex -"insert into a (eid, id) values (0x04, 2)" +# sequence with bindvar +"select next :a values from seq" { - "PlanID": "PASS_DML", - "Reason": "COMPLEX_EXPR", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (0x04, 2)" -} - -# no index -"insert into c (eid, id) values (1, 2)" -{ - "PlanID": "PASS_DML", - "Reason": "TABLE_NOINDEX", - "TableName": "c", - "Permissions": [ - { - "TableName": "c", - "Role": 1 - } - ], - "FullQuery": "insert into c(eid, id) values (1, 2)" -} - -# no column list -"insert into a values (1, 2, 'name', 'foo', 'camelcase')" -{ - "PlanID": "INSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a values (1, 2, 'name', 'foo', 'camelcase')", - "OuterQuery": "insert into a(eid, id, name, foo, CamelCase) values (1, 2, 'name', 'foo', 'camelcase')", - "PKValues": [[1], [2]] -} - -# upsert multiple unique index -"insert into a (eid, id) values (1, 2) on duplicate key update name = func(a)" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, 2) on duplicate key update name = func(a)", - "OuterQuery": "insert into a(eid, id) values (1, 2)", - "UpsertQuery": "update a set name = func(a) where :#pk", - "PKValues": [[1], [2]] -} - -# upsert multiple unique index -options:PassthroughDMLs -"insert into a (eid, id) values (1, 2) on duplicate key update name = func(a)" -{ - "PlanID": "PASS_DML", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, 2) on duplicate key update name = func(a)" -} - -# upsert single unique index -"insert into b (eid, id) values (1, 2) on duplicate key update name = func(a)" -{ - "PlanID": "INSERT_PK", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "insert into b(eid, id) values (1, 2) on duplicate key update name = func(a)", - "OuterQuery": "insert into b(eid, id) values (1, 2) on duplicate key update name = func(a)", - "PKValues": [[1], [2]] -} - -# upsert pk change -"insert into a (eid, id) values (1, 2) on duplicate key update eid = 2" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, 2) on duplicate key update eid = 2", - "OuterQuery": "insert into a(eid, id) values (1, 2)", - "UpsertQuery": "update a set eid = 2 where :#pk", - "PKValues": [[1], [2]], - "SecondaryPKValues": [2, null] -} - -# upsert with values() func -"insert into a (eid, id, name) values (1, 2, 'foo') on duplicate key update name = values(name)" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id, name) values (1, 2, 'foo') on duplicate key update name = values(name)", - "OuterQuery": "insert into a(eid, id, name) values (1, 2, 'foo')", - "UpsertQuery": "update a set name = ('foo') where :#pk", - "PKValues": [[1], [2]] -} - -# upsert with values() func and qualified column -"insert into a (eid, id, name) values (1, 2, 'foo') on duplicate key update name = values(a.name)" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id, name) values (1, 2, 'foo') on duplicate key update name = values(a.name)", - "OuterQuery": "insert into a(eid, id, name) values (1, 2, 'foo')", - "UpsertQuery": "update a set name = ('foo') where :#pk", - "PKValues": [[1], [2]] -} - -# upsert with values() func and qualified column with improper table -"insert into a (eid, id, name) values (1, 2, 'foo') on duplicate key update name = values(c.name)" -"could not find qualified column c.name in table a" - -# upsert with values() func and qualified column and qualified table -"insert into b.a (eid, id, name) values (1, 2, 'foo') on duplicate key update name = values(b.a.name)" -{ - "PlanID": "PASS_DML", - "Reason": "TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into b.a(eid, id, name) values (1, 2, 'foo') on duplicate key update name = values(b.a.name)" -} - -# upsert with values() inside another func -"insert into a (eid, id, name) values (1, 2, 'foo') on duplicate key update name = concat(values(name), 'foo')" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id, name) values (1, 2, 'foo') on duplicate key update name = concat(values(name), 'foo')", - "OuterQuery": "insert into a(eid, id, name) values (1, 2, 'foo')", - "UpsertQuery": "update a set name = concat(('foo'), 'foo') where :#pk", - "PKValues": [[1], [2]] -} - -# upsert with values() and simple expression -"insert into a (eid, id, name) values (1, 2, 3) on duplicate key update name = values(name) + 5" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id, name) values (1, 2, 3) on duplicate key update name = values(name) + 5", - "OuterQuery": "insert into a(eid, id, name) values (1, 2, 3)", - "UpsertQuery": "update a set name = (3) + 5 where :#pk", - "PKValues": [[1], [2]] -} - -# upsert with bindvars in values() -"insert into a (eid, id, name) values (1, :id, :name) on duplicate key update name = values(name), id = values(id)" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id, name) values (1, :id, :name) on duplicate key update name = values(name), id = values(id)", - "OuterQuery": "insert into a(eid, id, name) values (1, :id, :name)", - "UpsertQuery": "update a set name = (:name), id = (:id) where :#pk", - "PKValues": [[1], [":id"]], - "SecondaryPKValues": [null, [":id"]] -} - -# complex upsert with values() -"insert into a (eid, id) values (1+1, 2) on duplicate key update eid = values(eid) + 1" -{ - "PlanID": "PASS_DML", - "Reason": "COMPLEX_EXPR", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1 + 1, 2) on duplicate key update eid = values(eid) + 1" -} - -# upsert where values for pk references a complex expression -"insert into a (eid, id, name) values (1, 2, 1+1) on duplicate key update eid = values(name)" -{ - "PlanID": "PASS_DML", - "Reason": "PK_CHANGE", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id, name) values (1, 2, 1 + 1) on duplicate key update eid = values(name)", - "PKValues": [[1],[2]] -} - -# upsert where values for pk references a non-existent column -"insert into a (eid, id) values (1, 2) on duplicate key update eid = values(name)" -{ - "PlanID": "PASS_DML", - "Reason": "PK_CHANGE", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, 2) on duplicate key update eid = values(name)", - "PKValues": [[1],[2]] -} - -# upsert with mismatch values() -"insert into a (eid, id) values (1, 2) on duplicate key update eid = values(eid), name = values(name)" -"could not find column &{ name { }}" - -# upsert pk change, with values() func -"insert into a (eid, id) values (1, 2) on duplicate key update eid = values(eid), id = values(id)" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id) values (1, 2) on duplicate key update eid = values(eid), id = values(id)", - "OuterQuery": "insert into a(eid, id) values (1, 2)", - "UpsertQuery": "update a set eid = (1), id = (2) where :#pk", - "PKValues": [[1], [2]], - "SecondaryPKValues": [[1], [2]] -} - -# upsert pk change, with mixed expr and values() func -"insert into a (eid, id, name) values (1, 2, 'foo') on duplicate key update eid = 2, id = values(id), name = func()" -{ - "PlanID": "UPSERT_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(eid, id, name) values (1, 2, 'foo') on duplicate key update eid = 2, id = values(id), name = func()", - "OuterQuery": "insert into a(eid, id, name) values (1, 2, 'foo')", - "UpsertQuery": "update a set eid = 2, id = (2), name = func() where :#pk", - "PKValues": [[1], [2]], - "SecondaryPKValues": [2, [2]] -} - -# upsert complex pk change -"insert into a (id, eid) values (1, 2) on duplicate key update eid = func(a)" -{ - "PlanID": "PASS_DML", - "Reason": "PK_CHANGE", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(id, eid) values (1, 2) on duplicate key update eid = func(a)", - "PKValues": [[2], [1]] -} - -# upsert multi-row with multiple unique indexes -"insert into a (id, eid) values (1, 2), (2, 3) on duplicate key update name = func(a)" -{ - "PlanID": "PASS_DML", - "Reason": "UPSERT_MULTI_ROW", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "insert into a(id, eid) values (1, 2), (2, 3) on duplicate key update name = func(a)", - "PKValues": [[2,3],[1,2]] -} - -# upsert multi-row with 0-1 unique indexes -"insert into b (id, eid) values (1, 2), (2, 3) on duplicate key update name = func(a)" -{ - "PlanID": "INSERT_PK", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "insert into b(id, eid) values (1, 2), (2, 3) on duplicate key update name = func(a)", - "OuterQuery": "insert into b(id, eid) values (1, 2), (2, 3) on duplicate key update name = func(a)", - "PKValues": [[2,3],[1,2]] -} - -# upsert multi-row with 0-1 unique indexes and pk change -"insert into b (id, eid) values (1, 2), (2, 3) on duplicate key update id = 1" -{ - "PlanID": "INSERT_PK", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "insert into b(id, eid) values (1, 2), (2, 3) on duplicate key update id = 1", - "OuterQuery": "insert into b(id, eid) values (1, 2), (2, 3) on duplicate key update id = 1", - "PKValues": [[2,3],[1,2]], - "SecondaryPKValues": [null, 1] -} - -# upsert multi-row with 0-1 unique indexes and pk change, using values, and a different column source -"insert into b (id, eid) values (1, 2), (3, 4) on duplicate key update id = values(eid)" -{ - "PlanID": "INSERT_PK", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "insert into b(id, eid) values (1, 2), (3, 4) on duplicate key update id = values(eid)", - "OuterQuery": "insert into b(id, eid) values (1, 2), (3, 4) on duplicate key update id = values(eid)", - "PKValues": [[2,4],[1,3]], - "SecondaryPKValues": [null, [2, 4]] -} - -# upsert subquery -"insert into b (id, eid) select * from a on duplicate key update name = func(a)" -{ - "PlanID": "PASS_DML", - "Reason": "UPSERT_SUBQUERY", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - }, - { - "TableName": "a", - "Role": 0 - } - ], - "FullQuery": "insert into b(id, eid) select * from a on duplicate key update name = func(a)" -} - -# subquery -"insert into b (eid, id) select * from a" -{ - "PlanID": "INSERT_SUBQUERY", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - }, - { - "TableName": "a", - "Role": 0 - } - ], - "FullQuery": "insert into b(eid, id) select * from a", - "OuterQuery": "insert into b(eid, id) values :#values", - "Subquery": "select * from a limit :#maxLimit", - "ColumnNumbers": [0, 1], - "SubqueryPKColumns": [0, 1] -} - -# subquery with no column list -"insert into b select * from a" -{ - "PlanID": "INSERT_SUBQUERY", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - }, - { - "TableName": "a", - "Role": 0 - } - ], - "FullQuery": "insert into b select * from a", - "OuterQuery": "insert into b(eid, id) values :#values", - "Subquery": "select * from a limit :#maxLimit", - "ColumnNumbers": [0, 1], - "SubqueryPKColumns": [0, 1] -} - -# multi-row -"insert into b (eid, id) values (1, 2), (3, 4)" -{ - "PlanID": "INSERT_PK", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "insert into b(eid, id) values (1, 2), (3, 4)", - "OuterQuery": "insert into b(eid, id) values (1, 2), (3, 4)", - "PKValues": [[1, 3], [2, 4]] -} - -# topic insert with time_scheduled specified -"insert into test_topic(time_scheduled, id, message) values(1, 2, 'aa')" -{ - "PlanID": "INSERT_TOPIC", - "Reason": "TOPIC", - "TableName": "test_topic", - "Permissions": [ - { - "TableName": "test_topic", - "Role": 1 - } - ], - "FullQuery": "insert into test_topic(time_scheduled, id, message) values (1, 2, 'aa')" -} - -# topic update -"update test_topic set time_next = 1 where id = 1" -"updates not allowed on topics" - -# topic delete -"delete from test_topic where id = 1" -"deletes not allowed on topics" - -# message insert with time_scheduled specified -"insert into msg(time_scheduled, id, message) values(1, 2, 'aa')" -{ - "PlanID": "INSERT_MESSAGE", - "TableName": "msg", - "Permissions": [ - { - "TableName": "msg", - "Role": 1 - } - ], - "FullQuery": "insert into msg(time_scheduled, id, message) values (1, 2, 'aa')", - "OuterQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0)", - "PKValues": [[1], [2]] -} - -# message insert with time_scheduled specified with PassthroughDMLs -options:PassthroughDMLs -"insert into msg(time_scheduled, id, message) values(1, 2, 'aa')" -{ - "PlanID": "INSERT_MESSAGE", - "TableName": "msg", - "Permissions": [ - { - "TableName": "msg", - "Role": 1 - } - ], - "FullQuery": "insert into msg(time_scheduled, id, message) values (1, 2, 'aa')", - "OuterQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0)", - "PKValues": [[1], [2]] -} - -# message insert with no time_schedule -"insert into msg(id, message) values(2, 'aa')" -{ - "PlanID": "INSERT_MESSAGE", - "TableName": "msg", - "Permissions": [ - { - "TableName": "msg", - "Role": 1 - } - ], - "FullQuery": "insert into msg(id, message) values (2, 'aa')", - "OuterQuery": "insert into msg(id, message, time_scheduled, time_next, time_created, epoch) values (2, 'aa', :#time_now, :#time_now, :#time_now, 0)", - "PKValues": [[":#time_now"], [2]] -} - -# message insert with no time_schedule with PassthroughDMLs -options:PassthroughDMLs -"insert into msg(id, message) values(2, 'aa')" -{ - "PlanID": "INSERT_MESSAGE", - "TableName": "msg", - "Permissions": [ - { - "TableName": "msg", - "Role": 1 - } - ], - "FullQuery": "insert into msg(id, message) values (2, 'aa')", - "OuterQuery": "insert into msg(id, message, time_scheduled, time_next, time_created, epoch) values (2, 'aa', :#time_now, :#time_now, :#time_now, 0)", - "PKValues": [[":#time_now"], [2]] -} - -# message multi-value insert -"insert into msg(time_scheduled, id, message) values(1, 2, 'aa'), (3, 4, 'bb')" -{ - "PlanID": "INSERT_MESSAGE", - "TableName": "msg", - "Permissions": [ - { - "TableName": "msg", - "Role": 1 - } - ], - "FullQuery": "insert into msg(time_scheduled, id, message) values (1, 2, 'aa'), (3, 4, 'bb')", - "OuterQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0), (3, 4, 'bb', 3, :#time_now, 0)", - "PKValues": [ - [ - 1, - 3 - ], - [ - 2, - 4 - ] - ] -} - -# message multi-value upsert -"insert into msg(time_scheduled, id, message) values(1, 2, 'aa'), (3, 4, 'bb') on duplicate key update message = values(message)" -{ - "PlanID": "INSERT_MESSAGE", - "TableName": "msg", - "Permissions": [ - { - "TableName": "msg", - "Role": 1 - } - ], - "FullQuery": "insert into msg(time_scheduled, id, message) values (1, 2, 'aa'), (3, 4, 'bb') on duplicate key update message = values(message)", - "OuterQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0), (3, 4, 'bb', 3, :#time_now, 0) on duplicate key update message = values(message)", - "PKValues": [ - [ - 1, - 3 - ], - [ - 2, - 4 - ] - ] -} - -# message insert subquery -"insert into msg(time_scheduled, id, message) select * from a" -"subquery not allowed for message table: msg" - -# message insert without column list -"insert into msg values(1)" -"column list must be specified for message table insert: msg" - -# message column count mismatch -"insert into msg(id, message) values(1)" -"column count doesn't match value count" - -# message insert time_next -"insert into msg(id, message, time_next) values(2, 'aa', 3)" -"time_next must not be specified for message insert" - -# message insert time_created -"insert into msg(id, message, time_created) values(2, 'aa', 3)" -"time_created must not be specified for message insert" - -# message insert epoch -"insert into msg(id, message, epoch) values(2, 'aa', 3)" -"epoch must not be specified for message insert" - -# message insert time_acked -"insert into msg(id, message, time_acked) values(2, 'aa', 3)" -"time_acked must not be specified for message insert" - -# message insert id missing -"insert into msg(message) values('aa')" -"id must be specified for message insert" - -# multi-row -"replace into b (eid, id) values (1, 2), (3, 4)" -{ - "PlanID": "PASS_DML", - "Reason":"REPLACE", - "TableName": "", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "replace into b(eid, id) values (1, 2), (3, 4)" -} - -# multi-row -options:PassthroughDMLs -"replace into b (eid, id) values (1, 2), (3, 4)" -{ - "PlanID": "PASS_DML", - "Reason": "REPLACE", - "TableName": "", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "replace into b(eid, id) values (1, 2), (3, 4)" -} - -# single-row with set -"replace into b set eid = 1, id = 2" -{ - "PlanID": "PASS_DML", - "Reason":"REPLACE", - "TableName": "", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "replace into b(eid, id) values (1, 2)" -} - -# replace subquery -"replace into b (eid, id) select * from a" -{ - "PlanID": "PASS_DML", - "Reason":"REPLACE", - "TableName": "", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - }, - { - "TableName": "a", - "Role": 0 - } - ], - "FullQuery": "replace into b(eid, id) select * from a" -} - -# subquery with no column list -"replace into b select * from a" -{ - "PlanID": "PASS_DML", - "Reason":"REPLACE", - "TableName": "", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - }, - { - "TableName": "a", - "Role": 0 - } - ], - "FullQuery": "replace into b select * from a" -} - -# update limit with pk -"update d set foo='foo' where name in ('a', 'b') limit 1" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "d", - "Permissions": [ - { - "TableName": "d", - "Role": 1 - } - ], - "FullQuery": "update d set foo = 'foo' where name in ('a', 'b') limit 1", - "OuterQuery": "update d set foo = 'foo' where :#pk", - "Subquery": "select name from d where name in ('a', 'b') limit 1 for update", - "WhereClause": "where name in ('a', 'b')" -} - -# update limit with pk -options:PassthroughDMLs -"update d set foo='foo' where name in ('a', 'b') limit 1" -{ - "PlanID": "PASS_DML", - "TableName": "d", - "Permissions": [ - { - "TableName": "d", - "Role": 1 - } - ], - "FullQuery": "update d set foo = 'foo' where name in ('a', 'b') limit 1" -} - -# update cross-db -"update b.a set name='foo' where eid=1 and id=1" -{ - "PlanID": "PASS_DML", - "Reason": "TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "update b.a set name = 'foo' where eid = 1 and id = 1" -} - -# update cross-db -options:PassthroughDMLs -"update b.a set name='foo' where eid=1 and id=1" -{ - "PlanID": "PASS_DML", - "Reason": "TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "update b.a set name = 'foo' where eid = 1 and id = 1" -} - -# update unknown table -"update bogus set name='foo' where id=1" -"table bogus not found in schema" - -# update unknown table -options:PassthroughDMLs -"update bogus set name='foo' where id=1" -{ - "PlanID": "PASS_DML", - "TableName": "", - "Permissions": [ - { - "TableName": "bogus", - "Role": 1 - } - ], - "FullQuery": "update bogus set name = 'foo' where id = 1" -} - -# multi-table update -"update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test'" -{ - "PlanID": "PASS_DML", - "Reason": "MULTI_TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - }, - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test'" -} - -# multi-table update -"update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test'" -{ - "PlanID": "PASS_DML", - "Reason": "MULTI_TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - }, - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test'" -} - -# multi-table update -options:PassthroughDMLs -"update a join b on a.id = b.id set a.name = 'foo' where b.var = 'test'" -{ - "PlanID": "PASS_DML", - "Reason": "MULTI_TABLE", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - }, - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "update a join b on a.id = b.id set a.name = 'foo' where b.var = 'test'" -} - -# pk changed -"update b set eid=1" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "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], - "WhereClause": "" -} - -# type mismatch -"update b set eid=18446744073709551616" -"strconv.ParseUint: parsing "18446744073709551616": value out of range" - -# complex pk change -"update b set eid=foo()" -{ - "PlanID": "PASS_DML", - "Reason": "PK_CHANGE", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "update b set eid = foo()", - "WhereClause": "" -} - -# complex pk change -options:PassthroughDMLs -"update b set eid=foo()" -{ - "PlanID": "PASS_DML", - "TableName": "b", - "Permissions": [ - { - "TableName": "b", - "Role": 1 - } - ], - "FullQuery": "update b set eid = foo()" -} - -# update subquery -"update a set name='foo'" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "update a set name = 'foo'", - "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a limit :#maxLimit for update", - "WhereClause": "" -} - -# update complex where clause -"update a set name='foo' where eid+1=1" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "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", - "WhereClause": "where eid + 1 = 1" -} - -# pk -"update a set name='foo' where eid=1 and id=1" -{ - "PlanID": "DML_PK", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "FullQuery": "update a set name = 'foo' where eid = 1 and id = 1", - "OuterQuery": "update a set name = 'foo' where :#pk", - "PKValues": [1, 1], - "WhereClause": "where eid = 1 and id = 1" -} - -# partial pk -"update a set name='foo' where eid=1" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "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", - "WhereClause": "where eid = 1" -} - -# bad pk -"update a set name='foo' where eid=1.0 and id=1" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "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", - "WhereClause": "where eid = 1.0 and id = 1" -} - -# partial pk with limit -"update a set name='foo' where eid=1 limit 10" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "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", - "WhereClause": "where eid = 1" -} - -# non-pk -"update a set name='foo' where eid=1 and name='foo'" -{ - "PlanID": "DML_SUBQUERY", - "TableName": "a", - "Permissions": [ - { - "TableName": "a", - "Role": 1 - } - ], - "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", - "WhereClause": "where eid = 1 and name = 'foo'" -} - -# no index -"update c set eid=1" -{ - "PlanID": "PASS_DML", - "Reason": "TABLE_NOINDEX", - "TableName": "c", + "PlanID": "Nextval", + "TableName": "seq", "Permissions": [ { - "TableName": "c", - "Role": 1 + "TableName": "seq", + "Role": 0 } ], - "FullQuery": "update c set eid = 1", - "WhereClause": "" + "NextCount": "\":a\"" } -# complex expression in where -"update a set name='foo' where eid+1=1 and id=1" +# squence with bad value +"select next 12345667852342342342323423423 values from seq" +"strconv.ParseUint: parsing "12345667852342342342323423423": value out of range" + +# nextval on non-sequence table +"select next value from a" +"a is not a sequence" + +# nextval on non-existent table +"select next value from id" +"id is not a sequence" + +# for update +"select eid from a for update" { - "PlanID":"DML_SUBQUERY", - "TableName":"a", + "PlanID": "SelectLock", + "TableName": "a", "Permissions": [ { "TableName": "a", - "Role": 1 + "Role": 0 } ], - "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", - "WhereClause": "where eid + 1 = 1 and id = 1" + "FieldQuery": "select eid from a where 1 != 1", + "FullQuery": "select eid from a limit :#maxLimit for update" } -# parenthesized expressions in where -"update a set name='foo' where (eid=1) and id=1" +# lock in share mode +"select eid from a lock in share mode" { - "PlanID": "DML_PK", + "PlanID": "SelectLock", "TableName": "a", "Permissions": [ { "TableName": "a", - "Role": 1 + "Role": 0 } ], - "FullQuery": "update a set name = 'foo' where (eid = 1) and id = 1", - "OuterQuery": "update a set name = 'foo' where :#pk", - "PKValues": [1, 1], - "WhereClause": "where (eid = 1) and id = 1" + "FieldQuery": "select eid from a where 1 != 1", + "FullQuery": "select eid from a limit :#maxLimit lock in share mode" } -# in clause expression in where -"update a set name='foo' where eid in (1, 2) and id=1" +# normal insert +"insert into a(eid, id) values (1, 2)" { - "PlanID":"DML_PK", - "TableName":"a", + "PlanID": "Insert", + "TableName": "a", "Permissions": [ { "TableName": "a", "Role": 1 } ], - "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], - "WhereClause": "where eid in (1, 2) and id = 1" + "FullQuery": "insert into a(eid, id) values (1, 2)" } -# 'like' expression in where -"update d set foo='foo' where name like 'a%'" +# insert cross-db +"insert into b.a (eid, id) values (1, 2)" { - "PlanID": "DML_SUBQUERY", - "TableName": "d", + "PlanID": "Insert", + "TableName": "", "Permissions": [ { - "TableName": "d", + "TableName": "a", "Role": 1 } ], - "FullQuery": "update d set foo = 'foo' where name like 'a%'", - "OuterQuery": "update d set foo = 'foo' where :#pk", - "Subquery": "select name from d where name like 'a%' limit :#maxLimit for update", - "WhereClause": "where name like 'a%'" + "FullQuery": "insert into b.a(eid, id) values (1, 2)" } -# double in clause -"update a set name='foo' where eid in (1, 2) and id in (1, 2)" +# insert with bind value +"insert into a (eid, id) values (1, :a)" { - "PlanID":"DML_SUBQUERY", - "TableName":"a", + "PlanID": "Insert", + "TableName": "a", "Permissions": [ { "TableName": "a", "Role": 1 } ], - "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", - "WhereClause": "where eid in (1, 2) and id in (1, 2)" + "FullQuery": "insert into a(eid, id) values (1, :a)" } -# double use of pk -"update a set name='foo' where eid=1 and eid=2" +# insert with subquery +"insert into b (eid, id) select * from a" { - "PlanID":"DML_SUBQUERY", - "TableName":"a", + "PlanID": "Insert", + "TableName": "b", "Permissions": [ { - "TableName": "a", + "TableName": "b", "Role": 1 + }, + { + "TableName": "a", + "Role": 0 } ], - "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", - "WhereClause": "where eid = 1 and eid = 2" + "FullQuery": "insert into b(eid, id) select * from a" } -# partial pk with order by -"update a set name = 'foo' where eid=1 order by id desc" +# upsert +"insert into a (eid, id) values (1, 2) on duplicate key update name = func(a)" { - "PlanID": "DML_SUBQUERY", + "PlanID": "Insert", "TableName": "a", "Permissions": [ { @@ -1654,181 +295,189 @@ options:PassthroughDMLs "Role": 1 } ], - "FullQuery": "update a set name = 'foo' where eid = 1 order by id desc", - "OuterQuery": "update a set name = 'foo' where :#pk order by id desc", - "Subquery": "select eid, id from a where eid = 1 order by id desc limit :#maxLimit for update", - "WhereClause": "where eid = 1" + "FullQuery": "insert into a(eid, id) values (1, 2) on duplicate key update name = func(a)" } -# update with index hint -# note that you won't find a corresponding test for delete because the grammar doesn't allow it. -"update a use index(b) set name = 'foo' where eid=1" +# topic insert with time_scheduled specified +"insert into test_topic(time_scheduled, id, message) values(1, 2, 'aa')" { - "PlanID": "DML_SUBQUERY", - "TableName": "a", + "PlanID": "InsertTopic", + "TableName": "test_topic", "Permissions": [ { - "TableName": "a", + "TableName": "test_topic", "Role": 1 } ], - "FullQuery": "update a use index (b) set name = 'foo' where eid = 1", - "OuterQuery": "update a set name = 'foo' where :#pk", - "Subquery": "select eid, id from a use index (b) where eid = 1 limit :#maxLimit for update", - "WhereClause": "where eid = 1" + "FullQuery": "insert into test_topic(time_scheduled, id, message) values (1, 2, 'aa')" } -# delete limit with pk -"delete from d where name in ('a', 'b') limit 1" +# message insert with time_scheduled specified +"insert into msg(time_scheduled, id, message) values(1, 2, 'aa')" { - "PlanID": "DML_SUBQUERY", - "TableName": "d", + "PlanID": "InsertMessage", + "TableName": "msg", "Permissions": [ { - "TableName": "d", + "TableName": "msg", "Role": 1 } ], - "FullQuery": "delete from d where name in ('a', 'b') limit 1", - "OuterQuery": "delete from d where :#pk", - "Subquery": "select name from d where name in ('a', 'b') limit 1 for update", - "WhereClause": "where name in ('a', 'b')" + "FullQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0)" } -# delete limit with pk +# message insert with time_scheduled specified with PassthroughDMLs options:PassthroughDMLs -"delete from d where name in ('a', 'b') limit 1" +"insert into msg(time_scheduled, id, message) values(1, 2, 'aa')" { - "PlanID": "PASS_DML", - "TableName": "d", + "PlanID": "InsertMessage", + "TableName": "msg", "Permissions": [ { - "TableName": "d", + "TableName": "msg", "Role": 1 } ], - "FullQuery": "delete from d where name in ('a', 'b') limit 1" + "FullQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0)" } -# delete cross-db -"delete from b.a where eid=1 and id=1" +# message insert with no time_schedule +"insert into msg(id, message) values(2, 'aa')" { - "PlanID": "PASS_DML", - "Reason": "TABLE", - "TableName": "", + "PlanID": "InsertMessage", + "TableName": "msg", "Permissions": [ { - "TableName": "a", + "TableName": "msg", "Role": 1 } ], - "FullQuery": "delete from b.a where eid = 1 and id = 1" + "FullQuery": "insert into msg(id, message, time_scheduled, time_next, time_created, epoch) values (2, 'aa', :#time_now, :#time_now, :#time_now, 0)" } -# delete with no where clause -"delete from a" +# message insert with no time_schedule with PassthroughDMLs +options:PassthroughDMLs +"insert into msg(id, message) values(2, 'aa')" { - "PlanID": "DML_SUBQUERY", - "TableName": "a", + "PlanID": "InsertMessage", + "TableName": "msg", "Permissions": [ { - "TableName": "a", + "TableName": "msg", "Role": 1 } ], - "FullQuery": "delete from a", - "OuterQuery": "delete from a where :#pk", - "Subquery": "select eid, id from a limit :#maxLimit for update", - "WhereClause": "" + "FullQuery": "insert into msg(id, message, time_scheduled, time_next, time_created, epoch) values (2, 'aa', :#time_now, :#time_now, :#time_now, 0)" } -# delete complex where clause -"delete from a where eid+1=1" +# message multi-value insert +"insert into msg(time_scheduled, id, message) values(1, 2, 'aa'), (3, 4, 'bb')" { - "PlanID": "DML_SUBQUERY", - "TableName": "a", + "PlanID": "InsertMessage", + "TableName": "msg", "Permissions": [ { - "TableName": "a", + "TableName": "msg", "Role": 1 } ], - "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", - "WhereClause": "where eid + 1 = 1" + "FullQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0), (3, 4, 'bb', 3, :#time_now, 0)" } -# pk -"delete from a where eid=1 and id=1" +# message multi-value upsert +"insert into msg(time_scheduled, id, message) values(1, 2, 'aa'), (3, 4, 'bb') on duplicate key update message = values(message)" { - "PlanID": "DML_PK", - "TableName": "a", + "PlanID": "InsertMessage", + "TableName": "msg", "Permissions": [ { - "TableName": "a", + "TableName": "msg", "Role": 1 } ], - "FullQuery": "delete from a where eid = 1 and id = 1", - "OuterQuery": "delete from a where :#pk", - "PKValues": [1, 1], - "WhereClause": "where eid = 1 and id = 1" + "FullQuery": "insert into msg(time_scheduled, id, message, time_next, time_created, epoch) values (1, 2, 'aa', 1, :#time_now, 0), (3, 4, 'bb', 3, :#time_now, 0) on duplicate key update message = values(message)" } -# pk -options:PassthroughDMLs -"delete from a where eid=1 and id=1" +# message insert subquery +"insert into msg(time_scheduled, id, message) select * from a" +"subquery not allowed for message table: msg" + +# message insert without column list +"insert into msg values(1)" +"column list must be specified for message table insert: msg" + +# message column count mismatch +"insert into msg(id, message) values(1)" +"column count doesn't match value count" + +# message insert time_next +"insert into msg(id, message, time_next) values(2, 'aa', 3)" +"time_next must not be specified for message insert" + +# message insert time_created +"insert into msg(id, message, time_created) values(2, 'aa', 3)" +"time_created must not be specified for message insert" + +# message insert epoch +"insert into msg(id, message, epoch) values(2, 'aa', 3)" +"epoch must not be specified for message insert" + +# message insert time_acked +"insert into msg(id, message, time_acked) values(2, 'aa', 3)" +"time_acked must not be specified for message insert" + +# message insert id missing +"insert into msg(message) values('aa')" +"id must be specified for message insert" + +# replace +"replace into b (eid, id) values (1, 2), (3, 4)" { - "PlanID": "PASS_DML", - "TableName": "a", + "PlanID": "Insert", + "TableName": "b", "Permissions": [ { - "TableName": "a", + "TableName": "b", "Role": 1 } ], - "FullQuery": "delete from a where eid = 1 and id = 1" + "FullQuery": "replace into b(eid, id) values (1, 2), (3, 4)" } -# partial pk -"delete from a where eid=1" +# update with no where clause +"update d set foo='foo'" { - "PlanID": "DML_SUBQUERY", - "TableName": "a", + "PlanID": "UpdateLimit", + "TableName": "d", "Permissions": [ { - "TableName": "a", + "TableName": "d", "Role": 1 } ], - "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", - "WhereClause": "where eid = 1" + "FullQuery": "update d set foo = 'foo' limit :#maxLimit" } -# partial pk with order by -"delete from a where eid=1 order by id desc" +# normal update +"update d set foo='foo' where name in ('a', 'b')" { - "PlanID": "DML_SUBQUERY", - "TableName": "a", + "PlanID": "UpdateLimit", + "TableName": "d", "Permissions": [ { - "TableName": "a", + "TableName": "d", "Role": 1 } ], - "FullQuery": "delete from a where eid = 1 order by id desc", - "OuterQuery": "delete from a where :#pk order by id desc", - "Subquery": "select eid, id from a where eid = 1 order by id desc limit :#maxLimit for update", - "WhereClause": "where eid = 1" + "FullQuery": "update d set foo = 'foo' where name in ('a', 'b') limit :#maxLimit", + "WhereClause": "where name in ('a', 'b')" } -# 'like' expression -"delete from d where name like 'a%'" +# normal update +options:PassthroughDMLs +"update d set foo='foo' where name in ('a', 'b')" { - "PlanID": "DML_SUBQUERY", + "PlanID": "Update", "TableName": "d", "Permissions": [ { @@ -1836,279 +485,232 @@ options:PassthroughDMLs "Role": 1 } ], - "FullQuery": "delete from d where name like 'a%'", - "OuterQuery": "delete from d where :#pk", - "Subquery": "select name from d where name like 'a%' limit :#maxLimit for update", - "WhereClause": "where name like 'a%'" + "FullQuery": "update d set foo = 'foo' where name in ('a', 'b')", + "WhereClause": "where name in ('a', 'b')" } -# bad pk value delete -"delete from a where eid=1.0 and id=1" +# cross-db update +"update a.b set foo='foo' where name in ('a', 'b')" { - "PlanID": "DML_SUBQUERY", - "TableName": "a", + "PlanID": "UpdateLimit", + "TableName": "", "Permissions": [ { - "TableName": "a", + "TableName": "b", "Role": 1 } ], - "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", - "WhereClause": "where eid = 1.0 and id = 1" + "FullQuery": "update a.b set foo = 'foo' where name in ('a', 'b') limit :#maxLimit", + "WhereClause": "where name in ('a', 'b')" } -# non-pk -"delete from a where eid=1 and name='foo'" +# update unknown table +"update bogus set name='foo' where id=1" { - "PlanID": "DML_SUBQUERY", - "TableName": "a", + "PlanID": "UpdateLimit", + "TableName": "", "Permissions": [ { - "TableName": "a", + "TableName": "bogus", "Role": 1 } ], - "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", - "WhereClause": "where eid = 1 and name = 'foo'" + "FullQuery": "update bogus set name = 'foo' where id = 1 limit :#maxLimit", + "WhereClause": "where id = 1" } -# no index -"delete from c" +# update unknown table +options:PassthroughDMLs +"update bogus set name='foo' where id=1" { - "PlanID": "PASS_DML", - "Reason": "TABLE_NOINDEX", - "TableName": "c", + "PlanID": "Update", + "TableName": "", "Permissions": [ { - "TableName": "c", + "TableName": "bogus", "Role": 1 } ], - "FullQuery": "delete from c", - "WhereClause": "" + "FullQuery": "update bogus set name = 'foo' where id = 1", + "WhereClause": "where id = 1" } -# delete complex expression in where -"delete from a where eid+1=1 and id=1" +# multi-table update +"update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test'" { - "PlanID":"DML_SUBQUERY", - "TableName":"a", + "PlanID": "UpdateLimit", + "TableName": "", "Permissions": [ { "TableName": "a", "Role": 1 - } - ], - "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", - "WhereClause": "where eid + 1 = 1 and id = 1" -} - -# parenthesized expressions in where -"delete from a where (eid=1) and id=1" -{ - "PlanID": "DML_PK", - "TableName": "a", - "Permissions": [ + }, { - "TableName": "a", + "TableName": "b", "Role": 1 } ], - "FullQuery": "delete from a where (eid = 1) and id = 1", - "OuterQuery": "delete from a where :#pk", - "PKValues": [1, 1], - "WhereClause": "where (eid = 1) and id = 1" + "FullQuery": "update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test' limit :#maxLimit", + "WhereClause": "where a.id = b.id and b.var = 'test'" } -# delete in clause expression in where -"delete from a where eid in (1, 2) and id=1" +# multi-table update +options:PassthroughDMLs +"update a join b on a.id = b.id set a.name = 'foo' where b.var = 'test'" { - "PlanID":"DML_PK", - "TableName":"a", + "PlanID": "Update", + "TableName": "", "Permissions": [ { "TableName": "a", "Role": 1 + }, + { + "TableName": "b", + "Role": 1 } ], - "FullQuery":"delete from a where eid in (1, 2) and id = 1", - "OuterQuery":"delete from a where :#pk", - "PKValues":[[1,2],1], - "WhereClause": "where eid in (1, 2) and id = 1" + "FullQuery": "update a join b on a.id = b.id set a.name = 'foo' where b.var = 'test'", + "WhereClause": "where b.var = 'test'" } -# delete double in clause -"delete from a where eid in (1, 2) and id in (1, 2)" + +# update with limit +"update a set name='foo' limit 1" { - "PlanID":"DML_SUBQUERY", - "TableName":"a", + "PlanID": "Update", + "TableName": "a", "Permissions": [ { "TableName": "a", "Role": 1 } ], - "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", - "WhereClause": "where eid in (1, 2) and id in (1, 2)" + "FullQuery": "update a set name = 'foo' limit 1" } -# delete double use of pk -"delete from a where eid=1 and eid=2" +# update with limit +options:PassthroughDMLs +"update a set name='foo' limit 1" { - "PlanID":"DML_SUBQUERY", - "TableName":"a", + "PlanID": "Update", + "TableName": "a", "Permissions": [ { "TableName": "a", "Role": 1 } ], - "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", - "WhereClause": "where eid = 1 and eid = 2" + "FullQuery": "update a set name = 'foo' limit 1" } -# multi-table delete -"delete a, b from a, b, c where a.id = b.id and b.id = c.id and c.name = 'foo'" +# delete with no where clause +"delete from a" { - "PlanID": "PASS_DML", - "Reason": "MULTI_TABLE", - "TableName": "", + "PlanID": "DeleteLimit", + "TableName": "a", "Permissions": [ { "TableName": "a", "Role": 1 - }, - { - "TableName": "b", - "Role": 1 - }, - { - "TableName": "c", - "Role": 1 } ], - "FullQuery": "delete a, b from a, b, c where a.id = b.id and b.id = c.id and c.name = 'foo'" + "FullQuery": "delete from a limit :#maxLimit" } -# multi-table delete with join -"delete a from a join b on a.id = b.id where a.name = 'foo'" +# normal delete +"delete from d where name in ('a', 'b')" { - "PlanID": "PASS_DML", - "Reason": "MULTI_TABLE", - "TableName": "", + "PlanID": "DeleteLimit", + "TableName": "d", "Permissions": [ { - "TableName": "a", - "Role": 1 - }, - { - "TableName": "b", + "TableName": "d", "Role": 1 } ], - "FullQuery": "delete a from a join b on a.id = b.id where a.name = 'foo'" + "FullQuery": "delete from d where name in ('a', 'b') limit :#maxLimit", + "WhereClause": "where name in ('a', 'b')" } -# single value sequence -"select next value from seq" +# normal delete +options:PassthroughDMLs +"delete from d where name in ('a', 'b')" { - "PlanID": "NEXTVAL", - "TableName": "seq", + "PlanID": "Delete", + "TableName": "d", "Permissions": [ { - "TableName": "seq", - "Role": 0 + "TableName": "d", + "Role": 1 } ], - "PKValues":[1] + "FullQuery": "delete from d where name in ('a', 'b')", + "WhereClause": "where name in ('a', 'b')" } -# sequence with number -"select next 10 values from seq" +# delete unknown table +"delete from bogus" { - "PlanID": "NEXTVAL", - "TableName": "seq", + "PlanID": "DeleteLimit", + "TableName": "", "Permissions": [ { - "TableName": "seq", - "Role": 0 + "TableName": "bogus", + "Role": 1 } ], - "PKValues":[10] + "FullQuery": "delete from bogus limit :#maxLimit" } -# sequence with bindvar -"select next :a values from seq" +# delete unknown table +options:PassthroughDMLs +"delete from bogus" { - "PlanID": "NEXTVAL", - "TableName": "seq", + "PlanID": "Delete", + "TableName": "", "Permissions": [ { - "TableName": "seq", - "Role": 0 + "TableName": "bogus", + "Role": 1 } ], - "PKValues":[":a"] + "FullQuery": "delete from bogus" } -# insert no values autoinc -"insert into auto values ()" +# delete with limit +"delete from a limit 10" { - "PlanID": "INSERT_PK", - "TableName": "auto", + "PlanID": "Delete", + "TableName": "a", "Permissions": [ { - "TableName": "auto", + "TableName": "a", "Role": 1 } ], - "FullQuery": "insert into auto values ()", - "OuterQuery": "insert into auto(id) values (null)", - "PKValues":[ - [null] - ] + "FullQuery": "delete from a limit 10" } -# insert no values defaults -"insert into with_defaults values ()" +# delete with limit +options:PassthroughDMLs +"delete from a limit 10" { - "PlanID": "INSERT_PK", - "TableName": "with_defaults", + "PlanID": "Delete", + "TableName": "a", "Permissions": [ { - "TableName": "with_defaults", + "TableName": "a", "Role": 1 } ], - "FullQuery": "insert into with_defaults values ()", - "OuterQuery": "insert into with_defaults(aid, bid, cid) values (3, -2, null)", - "PKValues":[ - 3 - ] + "FullQuery": "delete from a limit 10" } -# nextval on non-sequence table -"select next value from a" -"a is not a sequence" - -# nextval on non-existent table -"select next value from id" -"table id not found in schema" - # int "set a=1" { - "PlanID": "SET", + "PlanID": "Set", "TableName": "", "FullQuery": "set a = 1" } @@ -2116,7 +718,7 @@ options:PassthroughDMLs # float "set a=1.2" { - "PlanID": "SET", + "PlanID": "Set", "TableName": "", "FullQuery": "set a = 1.2" } @@ -2124,7 +726,7 @@ options:PassthroughDMLs # string "set a='b'" { - "PlanID": "SET", + "PlanID": "Set", "TableName": "", "FullQuery": "set a = 'b'" } @@ -2132,7 +734,7 @@ options:PassthroughDMLs # multi "set a=1, b=2" { - "PlanID": "SET", + "PlanID": "Set", "TableName": "", "FullQuery": "set a = 1, b = 2" } @@ -2295,58 +897,38 @@ options:PassthroughDMLs # show "show a" { - "PlanID": "OTHER_READ", + "PlanID": "OtherRead", "TableName": "" } # describe "describe a" { - "PlanID": "OTHER_READ", + "PlanID": "OtherRead", "TableName": "" } # explain "explain a" { - "PlanID": "OTHER_READ", + "PlanID": "OtherRead", "TableName": "" } # repair "repair a" { - "PlanID": "OTHER_ADMIN", + "PlanID": "OtherAdmin", "TableName": "" } # optimize "optimize a" { - "PlanID": "OTHER_ADMIN", + "PlanID": "OtherAdmin", "TableName": "" } -# table not found select -"select * from aaaa" -"table aaaa not found in schema" - -# table not found update -"update aaaa set a=1" -"table aaaa not found in schema" - -# table not found update -"delete from aaaa" -"table aaaa not found in schema" - -# table not found update -"insert into aaaa values(1)" -"table aaaa not found in schema" - -# column not found insert with subquery -"insert into a(missing) select * from b" -"column missing not found in table a" - # syntax error "syntax error" "syntax error at position 7 near 'syntax'" @@ -2354,18 +936,3 @@ options:PassthroughDMLs # named locks are unsafe with server-side connection pooling "select get_lock('foo') from dual" "get_lock() not allowed" - -# select DISTINCT ((1,2),(1,2)) from dual; -"select DISTINCT ((1,2),(1,2)) from dual" -{ - "PlanID": "PASS_SELECT", - "TableName": "dual", - "Permissions": [ - { - "TableName": "dual", - "Role": 0 - } - ], - "FieldQuery": "select ((1, 2), (1, 2)) from dual where 1 != 1", - "FullQuery": "select distinct ((1, 2), (1, 2)) from dual limit :#maxLimit" -} diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/stream_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/stream_cases.txt index fb32bfd3dc1..1a5cce0146c 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/stream_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/stream_cases.txt @@ -1,7 +1,7 @@ # select "select * from a" { - "PlanID": "SELECT_STREAM", + "PlanID": "SelectStream", "TableName": "a", "Permissions":[{"TableName":"a","Role":0}], "FullQuery": "select * from a" @@ -10,7 +10,7 @@ # select join "select * from a join b" { - "PlanID": "SELECT_STREAM", + "PlanID": "SelectStream", "TableName": "", "Permissions":[{"TableName":"a","Role":0},{"TableName":"b","Role":0}], "FullQuery": "select * from a join b" @@ -23,7 +23,7 @@ # union "select * from a union select * from b" { - "PlanID": "SELECT_STREAM", + "PlanID": "SelectStream", "TableName": "", "Permissions":[{"TableName":"a","Role":0},{"TableName":"b","Role":0}], "FullQuery": "select * from a union select * from b" @@ -32,7 +32,7 @@ # show "show tables" { - "PlanID": "SELECT_STREAM", + "PlanID": "SelectStream", "TableName": "", "FullQuery": "show tables" } @@ -40,7 +40,7 @@ # other "desc foo" { - "PlanID": "SELECT_STREAM", + "PlanID": "SelectStream", "TableName": "", "FullQuery": "otherread" } diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 69ba5062f55..2958648f628 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -120,7 +120,7 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { } switch qre.plan.PlanID { - case planbuilder.PlanPassSelect, planbuilder.PlanSelectImpossible: + case planbuilder.PlanSelect, planbuilder.PlanSelectImpossible: maxrows := qre.getSelectLimit() qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) qr, err := qre.execSelect() @@ -135,9 +135,9 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) case planbuilder.PlanSet, planbuilder.PlanOtherRead: return qre.execOther() - case planbuilder.PlanPassDML, planbuilder.PlanInsertMessage: + case planbuilder.PlanInsert, planbuilder.PlanUpdate, planbuilder.PlanDelete, planbuilder.PlanInsertMessage: return qre.execAsTransaction(true /* autocommit */, qre.txConnExec) - case planbuilder.PlanDMLLimit: + case planbuilder.PlanUpdateLimit, planbuilder.PlanDeleteLimit: return qre.execAsTransaction(false /* autocommit */, qre.txConnExec) } return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) @@ -182,16 +182,16 @@ func (qre *QueryExecutor) execAsTransaction(autocommit bool, f func(conn *TxConn func (qre *QueryExecutor) txConnExec(conn *TxConnection) (*sqltypes.Result, error) { switch qre.plan.PlanID { - case planbuilder.PlanPassDML, planbuilder.PlanSet: + case planbuilder.PlanInsert, planbuilder.PlanUpdate, planbuilder.PlanDelete, planbuilder.PlanSet: return qre.txFetch(conn, true) case planbuilder.PlanInsertMessage: qre.bindVars["#time_now"] = sqltypes.Int64BindVariable(time.Now().UnixNano()) return qre.txFetch(conn, true) - case planbuilder.PlanDMLLimit: + case planbuilder.PlanUpdateLimit, planbuilder.PlanDeleteLimit: return qre.execDMLLimit(conn) case planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: return qre.txFetch(conn, false) - case planbuilder.PlanPassSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible: + case planbuilder.PlanSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible: maxrows := qre.getSelectLimit() qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) qr, err := qre.txFetch(conn, false) diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index ba6ce759293..1563a7e4aff 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -80,7 +80,7 @@ func TestQueryExecutorPlanPassDmlRBR(t *testing.T) { txid := newTransaction(tsv, nil) qre := newTestQueryExecutor(ctx, tsv, query, txid) tsv.qe.binlogFormat = connpool.BinlogFormatRow - checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -121,7 +121,7 @@ func TestQueryExecutorPassthroughDml(t *testing.T) { txid := newTransaction(tsv, nil) qre := newTestQueryExecutor(ctx, tsv, query, txid) - checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -172,7 +172,7 @@ func TestQueryExecutorPlanPassDmlAutoCommitRBR(t *testing.T) { defer tsv.StopService() qre := newTestQueryExecutor(ctx, tsv, query, 0) tsv.qe.binlogFormat = connpool.BinlogFormatRow - checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -205,7 +205,7 @@ func TestQueryExecutorPassthroughDmlAutoCommit(t *testing.T) { tsv.qe.binlogFormat = connpool.BinlogFormatRow qre := newTestQueryExecutor(ctx, tsv, query, 0) - checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -244,7 +244,7 @@ func TestQueryExecutorPlanPassDmlReplaceInto(t *testing.T) { txid := newTransaction(tsv, nil) qre := newTestQueryExecutor(ctx, tsv, query, txid) tsv.qe.binlogFormat = connpool.BinlogFormatRow - checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanInsert, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -338,7 +338,7 @@ func TestQueryExecutorPlanPassSelectWithInATransaction(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, txid) defer tsv.StopService() defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -389,7 +389,7 @@ func TestQueryExecutorPlanPassSelect(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -441,7 +441,7 @@ func TestQueryExecutorPlanPassSelectSqlSelectLimit(t *testing.T) { SqlSelectLimit: 20, } defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -743,7 +743,7 @@ func TestQueryExecutorTableAcl(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("got: %v, want nil", err) @@ -787,7 +787,7 @@ func TestQueryExecutorTableAclNoPermission(t *testing.T) { // without enabling Config.StrictTableAcl tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("got: %v, want nil", err) @@ -801,7 +801,7 @@ func TestQueryExecutorTableAclNoPermission(t *testing.T) { tsv = newTestTabletServer(ctx, enableStrictTableACL, db) qre = newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) // query should fail because current user do not have read permissions _, err = qre.Execute() if err == nil { @@ -898,7 +898,7 @@ func TestQueryExecutorTableAclExemptACL(t *testing.T) { tsv := newTestTabletServer(ctx, enableStrictTableACL, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) // query should fail because current user do not have read permissions _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED { @@ -965,7 +965,7 @@ func TestQueryExecutorTableAclDryRun(t *testing.T) { tableACLStatsKey := strings.Join([]string{ "test_table", "group02", - planbuilder.PlanPassSelect.String(), + planbuilder.PlanSelect.String(), username, }, ".") // enable Config.StrictTableAcl @@ -973,7 +973,7 @@ func TestQueryExecutorTableAclDryRun(t *testing.T) { tsv.qe.enableTableACLDryRun = true qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) beforeCount := tabletenv.TableaclPseudoDenied.Counts()[tableACLStatsKey] // query should fail because current user do not have read permissions _, err := qre.Execute() @@ -1008,7 +1008,7 @@ func TestQueryExecutorBlacklistQRFail(t *testing.T) { alterRule.SetIPCond(bannedAddr) alterRule.SetUserCond(bannedUser) alterRule.SetQueryCond("select.*") - alterRule.AddPlanCond(planbuilder.PlanPassSelect) + alterRule.AddPlanCond(planbuilder.PlanSelect) alterRule.AddTableCond("test_table") rulesName := "blacklistedRulesQRFail" @@ -1032,7 +1032,7 @@ func TestQueryExecutorBlacklistQRFail(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) // execute should fail because query has been blacklisted _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT { @@ -1062,7 +1062,7 @@ func TestQueryExecutorBlacklistQRRetry(t *testing.T) { alterRule.SetIPCond(bannedAddr) alterRule.SetUserCond(bannedUser) alterRule.SetQueryCond("select.*") - alterRule.AddPlanCond(planbuilder.PlanPassSelect) + alterRule.AddPlanCond(planbuilder.PlanSelect) alterRule.AddTableCond("test_table") rulesName := "blacklistedRulesQRRetry" @@ -1086,7 +1086,7 @@ func TestQueryExecutorBlacklistQRRetry(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) + checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_FAILED_PRECONDITION { t.Fatalf("tsv.qe.queryRuleSources.SetRules: %v, want %v", code, vtrpcpb.Code_FAILED_PRECONDITION) diff --git a/go/vt/vttablet/tabletserver/querylogz_test.go b/go/vt/vttablet/tabletserver/querylogz_test.go index e829b23a144..793562864bd 100644 --- a/go/vt/vttablet/tabletserver/querylogz_test.go +++ b/go/vt/vttablet/tabletserver/querylogz_test.go @@ -47,7 +47,7 @@ func TestQuerylogzHandlerInvalidLogStats(t *testing.T) { func TestQuerylogzHandler(t *testing.T) { req, _ := http.NewRequest("GET", "/querylogz?timeout=10&limit=1", nil) logStats := tabletenv.NewLogStats(context.Background(), "Execute") - logStats.PlanType = planbuilder.PlanPassSelect.String() + logStats.PlanType = planbuilder.PlanSelect.String() logStats.OriginalSQL = "select name from test_table limit 1000" logStats.RowsAffected = 1000 logStats.NumberOfQueries = 1 diff --git a/go/vt/vttablet/tabletserver/queryz_test.go b/go/vt/vttablet/tabletserver/queryz_test.go index 78f5256a199..9d1f8fc44f3 100644 --- a/go/vt/vttablet/tabletserver/queryz_test.go +++ b/go/vt/vttablet/tabletserver/queryz_test.go @@ -40,7 +40,7 @@ func TestQueryzHandler(t *testing.T) { plan1 := &TabletPlan{ Plan: &planbuilder.Plan{ Table: &schema.Table{Name: sqlparser.NewTableIdent("test_table")}, - PlanID: planbuilder.PlanPassSelect, + PlanID: planbuilder.PlanSelect, }, } plan1.AddStats(10, 2*time.Second, 1*time.Second, 2, 0) diff --git a/go/vt/vttablet/tabletserver/rules/map_test.go b/go/vt/vttablet/tabletserver/rules/map_test.go index ffb533cf5b8..ffe80b1156b 100644 --- a/go/vt/vttablet/tabletserver/rules/map_test.go +++ b/go/vt/vttablet/tabletserver/rules/map_test.go @@ -174,7 +174,7 @@ func TestMapFilterByPlan(t *testing.T) { qri.SetRules(customQueryRules, otherRules) // Test filter by blacklist rule - qrs = qri.FilterByPlan("select * from bannedtable2", planbuilder.PlanPassSelect, "bannedtable2") + qrs = qri.FilterByPlan("select * from bannedtable2", planbuilder.PlanSelect, "bannedtable2") if l := len(qrs.rules); l != 1 { t.Errorf("Select from bannedtable matches %d rules, but we expect %d", l, 1) } @@ -183,7 +183,7 @@ func TestMapFilterByPlan(t *testing.T) { } // Test filter by custom rule - qrs = qri.FilterByPlan("select cid from t_customer limit 10", planbuilder.PlanPassSelect, "t_customer") + qrs = qri.FilterByPlan("select cid from t_customer limit 10", planbuilder.PlanSelect, "t_customer") if l := len(qrs.rules); l != 1 { t.Errorf("Select from t_customer matches %d rules, but we expect %d", l, 1) } @@ -197,7 +197,7 @@ func TestMapFilterByPlan(t *testing.T) { qr.AddBindVarCond("bindvar1", true, false, QRNoOp, nil) otherRules.Add(qr) qri.SetRules(customQueryRules, otherRules) - qrs = qri.FilterByPlan("select * from bannedtable2", planbuilder.PlanPassSelect, "bannedtable2") + qrs = qri.FilterByPlan("select * from bannedtable2", planbuilder.PlanSelect, "bannedtable2") if l := len(qrs.rules); l != 2 { t.Errorf("Insert into bannedtable2 matches %d rules: %v, but we expect %d rules to be matched", l, qrs.rules, 2) } diff --git a/go/vt/vttablet/tabletserver/rules/rules_test.go b/go/vt/vttablet/tabletserver/rules/rules_test.go index 309d93fb9fa..09377833e4a 100644 --- a/go/vt/vttablet/tabletserver/rules/rules_test.go +++ b/go/vt/vttablet/tabletserver/rules/rules_test.go @@ -81,7 +81,7 @@ func TestQueryRules(t *testing.T) { func TestCopy(t *testing.T) { qrs1 := New() qr1 := NewQueryRule("rule 1", "r1", QRFail) - qr1.AddPlanCond(planbuilder.PlanPassSelect) + qr1.AddPlanCond(planbuilder.PlanSelect) qr1.AddTableCond("aa") qr1.AddBindVarCond("a", true, false, QRNoOp, nil) @@ -107,11 +107,11 @@ func TestFilterByPlan(t *testing.T) { qr1 := NewQueryRule("rule 1", "r1", QRFail) qr1.SetIPCond("123") qr1.SetQueryCond("select") - qr1.AddPlanCond(planbuilder.PlanPassSelect) + qr1.AddPlanCond(planbuilder.PlanSelect) qr1.AddBindVarCond("a", true, false, QRNoOp, nil) qr2 := NewQueryRule("rule 2", "r2", QRFail) - qr2.AddPlanCond(planbuilder.PlanPassSelect) + qr2.AddPlanCond(planbuilder.PlanSelect) qr2.AddPlanCond(planbuilder.PlanSelectLock) qr2.AddBindVarCond("a", true, false, QRNoOp, nil) @@ -128,7 +128,7 @@ func TestFilterByPlan(t *testing.T) { qrs.Add(qr3) qrs.Add(qr4) - qrs1 := qrs.FilterByPlan("select", planbuilder.PlanPassSelect, "a") + qrs1 := qrs.FilterByPlan("select", planbuilder.PlanSelect, "a") want := compacted(`[{ "Description":"rule 1", "Name":"r1", @@ -163,7 +163,7 @@ func TestFilterByPlan(t *testing.T) { t.Errorf("qrs1:\n%s, want\n%s", got, want) } - qrs1 = qrs.FilterByPlan("insert", planbuilder.PlanPassSelect, "a") + qrs1 = qrs.FilterByPlan("insert", planbuilder.PlanSelect, "a") want = compacted(`[{ "Description":"rule 2", "Name":"r2", @@ -185,7 +185,7 @@ func TestFilterByPlan(t *testing.T) { t.Errorf("qrs1:\n%s, want\n%s", got, want) } - qrs1 = qrs.FilterByPlan("select", planbuilder.PlanPassDML, "a") + qrs1 = qrs.FilterByPlan("select", planbuilder.PlanInsert, "a") want = compacted(`[{ "Description":"rule 3", "Name":"r3", @@ -201,12 +201,12 @@ func TestFilterByPlan(t *testing.T) { t.Errorf("qrs1:\n%s, want\n%s", got, want) } - qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanPassDML, "a") + qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanInsert, "a") if qrs1.rules != nil { t.Errorf("want nil, got non-nil") } - qrs1 = qrs.FilterByPlan("table", planbuilder.PlanPassDML, "b") + qrs1 = qrs.FilterByPlan("table", planbuilder.PlanInsert, "b") want = compacted(`[{ "Description":"rule 4", "Name":"r4", @@ -220,7 +220,7 @@ func TestFilterByPlan(t *testing.T) { qr5 := NewQueryRule("rule 5", "r5", QRFail) qrs.Add(qr5) - qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanPassDML, "a") + qrs1 = qrs.FilterByPlan("sel", planbuilder.PlanInsert, "a") want = compacted(`[{ "Description":"rule 5", "Name":"r5", @@ -232,7 +232,7 @@ func TestFilterByPlan(t *testing.T) { } qrsnil1 := New() - if qrsnil2 := qrsnil1.FilterByPlan("", planbuilder.PlanPassSelect, "a"); qrsnil2.rules != nil { + if qrsnil2 := qrsnil1.FilterByPlan("", planbuilder.PlanSelect, "a"); qrsnil2.rules != nil { t.Errorf("want nil, got non-nil") } } @@ -257,13 +257,13 @@ func TestQueryRule(t *testing.T) { t.Errorf("want error") } - qr.AddPlanCond(planbuilder.PlanPassSelect) - qr.AddPlanCond(planbuilder.PlanPassDML) + qr.AddPlanCond(planbuilder.PlanSelect) + qr.AddPlanCond(planbuilder.PlanInsert) - if qr.plans[0] != planbuilder.PlanPassSelect { + if qr.plans[0] != planbuilder.PlanSelect { t.Errorf("want PASS_SELECT, got %s", qr.plans[0].String()) } - if qr.plans[1] != planbuilder.PlanPassDML { + if qr.plans[1] != planbuilder.PlanInsert { t.Errorf("want INSERT_PK, got %s", qr.plans[1].String()) } diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 27f5db98624..a9a753d17eb 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -1187,7 +1187,9 @@ func (tsv *TabletServer) computeTxSerializerKey(ctx context.Context, logStats *t return "", "" } - if plan.PlanID != planbuilder.PlanPassDML && plan.PlanID != planbuilder.PlanDMLLimit { + switch plan.PlanID { + case planbuilder.PlanUpdate, planbuilder.PlanUpdateLimit, + planbuilder.PlanDelete, planbuilder.PlanDeleteLimit: // Serialize only UPDATE or DELETE queries. return "", "" } From f75cfdbdeb68323d1b1f0a962ba8439f708d2f3f Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Mon, 9 Mar 2020 22:17:59 -0700 Subject: [PATCH 307/825] deprecation: delete obsolete binlog test Signed-off-by: Sugu Sougoumarane --- go/test/endtoend/binlog/binlog_test.go | 369 ------------------------- test/config.json | 9 - 2 files changed, 378 deletions(-) delete mode 100644 go/test/endtoend/binlog/binlog_test.go diff --git a/go/test/endtoend/binlog/binlog_test.go b/go/test/endtoend/binlog/binlog_test.go deleted file mode 100644 index 5247b5c9633..00000000000 --- a/go/test/endtoend/binlog/binlog_test.go +++ /dev/null @@ -1,369 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - This file contains integration tests for the go/vt/binlog package. - It sets up filtered replication between two shards and checks how data flows - through binlog streamer. -*/ - -package binlog - -import ( - "context" - "encoding/json" - "flag" - "fmt" - "os" - "os/exec" - "path" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/test/endtoend/cluster" - "vitess.io/vitess/go/vt/proto/query" -) - -var ( - localCluster *cluster.LocalProcessCluster - cell = "zone1" - hostname = "localhost" - keyspaceName = "ks" - tableName = "test_table" - sqlSchema = ` - create table %s( - id bigint(20) unsigned auto_increment, - msg varchar(64), - primary key (id), - index by_msg (msg) - ) Engine=InnoDB -` - insertSql = `insert into %s(id,msg) values(%d, "%s")` - commonTabletArg = []string{ - "-vreplication_healthcheck_topology_refresh", "1s", - "-vreplication_healthcheck_retry_delay", "1s", - "-vreplication_retry_delay", "1s", - "-degraded_threshold", "5s", - "-lock_tables_timeout", "5s", - "-watch_replication_stream", - "-enable_replication_reporter", - "-serving_state_grace_period", "1s", - "-binlog_player_protocol", "grpc", - "-enable-autocommit", - } - vSchema = ` - { - "sharded": false, - "vindexes": { - "hash_index": { - "type": "hash" - } - }, - "tables": { - "%s": { - "column_vindexes": [ - { - "column": "id", - "name": "hash_index" - } - ] - } - } - } -` - srcMaster *cluster.Vttablet - srcReplica *cluster.Vttablet - srcRdonly *cluster.Vttablet - - destMaster *cluster.Vttablet - destReplica *cluster.Vttablet - destRdonly *cluster.Vttablet -) - -func TestMain(m *testing.M) { - defer cluster.PanicHandler(nil) - flag.Parse() - - exitcode, err := func() (int, error) { - localCluster = cluster.NewCluster(cell, hostname) - defer localCluster.Teardown() - - localCluster.Keyspaces = append(localCluster.Keyspaces, cluster.Keyspace{ - Name: keyspaceName, - }) - - // Start topo server - if err := localCluster.StartTopo(); err != nil { - return 1, err - } - - srcMaster = localCluster.GetVttabletInstance("master", 0, "") - srcReplica = localCluster.GetVttabletInstance("replica", 0, "") - srcRdonly = localCluster.GetVttabletInstance("rdonly", 0, "") - - destMaster = localCluster.GetVttabletInstance("master", 0, "") - destReplica = localCluster.GetVttabletInstance("replica", 0, "") - destRdonly = localCluster.GetVttabletInstance("rdonly", 0, "") - - var mysqlProcs []*exec.Cmd - for _, tablet := range []*cluster.Vttablet{srcMaster, srcReplica, srcRdonly, destMaster, destReplica, destRdonly} { - tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory) - tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort, - tablet.GrpcPort, - tablet.TabletUID, - cell, - "", - keyspaceName, - localCluster.VtctldProcess.Port, - tablet.Type, - localCluster.TopoPort, - hostname, - localCluster.TmpDirectory, - commonTabletArg, - true, - ) - tablet.VttabletProcess.SupportsBackup = true - proc, err := tablet.MysqlctlProcess.StartProcess() - if err != nil { - return 1, err - } - mysqlProcs = append(mysqlProcs, proc) - } - for _, proc := range mysqlProcs { - if err := proc.Wait(); err != nil { - return 1, err - } - } - - if err := localCluster.VtctlProcess.CreateKeyspace(keyspaceName); err != nil { - return 1, err - } - - shard1 := cluster.Shard{ - Name: "0", - Vttablets: []*cluster.Vttablet{srcMaster, srcReplica, srcRdonly}, - } - for idx := range shard1.Vttablets { - shard1.Vttablets[idx].VttabletProcess.Shard = shard1.Name - } - localCluster.Keyspaces[0].Shards = append(localCluster.Keyspaces[0].Shards, shard1) - - shard2 := cluster.Shard{ - Name: "-", - Vttablets: []*cluster.Vttablet{destMaster, destReplica, destRdonly}, - } - for idx := range shard2.Vttablets { - shard2.Vttablets[idx].VttabletProcess.Shard = shard2.Name - } - localCluster.Keyspaces[0].Shards = append(localCluster.Keyspaces[0].Shards, shard2) - - for _, tablet := range shard1.Vttablets { - if err := localCluster.VtctlclientProcess.InitTablet(tablet, cell, keyspaceName, hostname, shard1.Name); err != nil { - return 1, err - } - if err := tablet.VttabletProcess.Setup(); err != nil { - return 1, err - } - } - if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard1.Name, cell, srcMaster.TabletUID); err != nil { - return 1, err - } - - if err := localCluster.VtctlclientProcess.ApplySchema(keyspaceName, fmt.Sprintf(sqlSchema, tableName)); err != nil { - return 1, err - } - if err := localCluster.VtctlclientProcess.ApplyVSchema(keyspaceName, fmt.Sprintf(vSchema, tableName)); err != nil { - return 1, err - } - - // run a health check on source replica so it responds to discovery - // (for binlog players) and on the source rdonlys (for workers) - for _, tablet := range []string{srcReplica.Alias, srcRdonly.Alias} { - if err := localCluster.VtctlclientProcess.ExecuteCommand("RunHealthCheck", tablet); err != nil { - return 1, err - } - } - - // Create destination shard (won't be serving as there is no DB) - for _, tablet := range shard2.Vttablets { - if err := localCluster.VtctlclientProcess.InitTablet(tablet, cell, keyspaceName, hostname, shard2.Name); err != nil { - return 1, err - } - if err := tablet.VttabletProcess.Setup(); err != nil { - return 1, err - } - } - - if err := localCluster.VtctlclientProcess.InitShardMaster(keyspaceName, shard2.Name, cell, destMaster.TabletUID); err != nil { - return 1, err - } - _ = localCluster.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", keyspaceName) - // Copy schema - if err := localCluster.VtctlclientProcess.ExecuteCommand("CopySchemaShard", srcReplica.Alias, fmt.Sprintf("%s/%s", keyspaceName, shard2.Name)); err != nil { - return 1, err - } - - // run the clone worker (this is a degenerate case, source and destination - // both have the full keyrange. Happens to work correctly). - localCluster.VtworkerProcess = *cluster.VtworkerProcessInstance(localCluster.GetAndReservePort(), - localCluster.GetAndReservePort(), - localCluster.TopoPort, - localCluster.Hostname, - localCluster.TmpDirectory) - localCluster.VtworkerProcess.Cell = cell - if err := localCluster.VtworkerProcess.ExecuteVtworkerCommand(localCluster.VtworkerProcess.Port, - localCluster.VtworkerProcess.GrpcPort, "--use_v3_resharding_mode=true", - "SplitClone", - "--chunk_count", "10", - "--min_rows_per_chunk", "1", - "--exclude_tables", "unrelated", - "--min_healthy_rdonly_tablets", "1", - fmt.Sprintf("%s/%s", keyspaceName, shard1.Name)); err != nil { - return 1, err - } - if err := destMaster.VttabletProcess.WaitForBinLogPlayerCount(1); err != nil { - return 1, err - } - // Wait for dst_replica to be ready. - if err := destReplica.VttabletProcess.WaitForBinlogServerState("Enabled"); err != nil { - return 1, err - } - return m.Run(), nil - }() - if err != nil { - fmt.Printf("%v\n", err) - os.Exit(1) - } else { - os.Exit(exitcode) - } -} - -// Insert something that will replicate incorrectly if the charset is not -// propagated through binlog streamer to the destination. - -// Vitess tablets default to using utf8, so we insert something crazy and -// pretend it's latin1. If the binlog player doesn't also pretend it's -// latin1, it will be inserted as utf8, which will change its value. -func TestCharset(t *testing.T) { - defer cluster.PanicHandler(t) - position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) - - _, err := queryTablet(t, *srcMaster, fmt.Sprintf(insertSql, tableName, 1, "Šṛ́rỏé"), "latin1") - require.Nil(t, err) - println("Waiting to get rows in dest master tablet") - waitForReplicaEvent(t, position, "1", *destReplica) - - verifyData(t, 1, "latin1", `[UINT64(1) VARCHAR("Šṛ́rỏé")]`) -} - -// Enable binlog_checksum, which will also force a log rotation that should -// cause binlog streamer to notice the new checksum setting. -func TestChecksumEnabled(t *testing.T) { - defer cluster.PanicHandler(t) - position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) - _, err := queryTablet(t, *destReplica, "SET @@global.binlog_checksum=1", "") - require.Nil(t, err) - - // Insert something and make sure it comes through intact. - _, err = queryTablet(t, *srcMaster, fmt.Sprintf(insertSql, tableName, 2, "value - 2"), "") - require.Nil(t, err) - - // Look for it using update stream to see if binlog streamer can talk to - // dest_replica, which now has binlog_checksum enabled. - waitForReplicaEvent(t, position, "2", *destReplica) - - verifyData(t, 2, "", `[UINT64(2) VARCHAR("value - 2")]`) -} - -// Disable binlog_checksum to make sure we can also talk to a server without -// checksums enabled, in case they are enabled by default -func TestChecksumDisabled(t *testing.T) { - defer cluster.PanicHandler(t) - position, _ := cluster.GetMasterPosition(t, *destReplica, hostname) - - _, err := queryTablet(t, *destReplica, "SET @@global.binlog_checksum=0", "") - require.Nil(t, err) - - // Insert something and make sure it comes through intact. - _, err = queryTablet(t, *srcMaster, fmt.Sprintf(insertSql, tableName, 3, "value - 3"), "") - require.Nil(t, err) - - // Look for it using update stream to see if binlog streamer can talk to - // dest_replica, which now has binlog_checksum disabled. - waitForReplicaEvent(t, position, "3", *destReplica) - - verifyData(t, 3, "", `[UINT64(3) VARCHAR("value - 3")]`) -} - -// Wait for a replica event with the given SQL string. -func waitForReplicaEvent(t *testing.T, position string, pkKey string, vttablet cluster.Vttablet) { - timeout := time.Now().Add(10 * time.Second) - for time.Now().Before(timeout) { - println("fetching with position " + position) - output, err := localCluster.VtctlclientProcess.ExecuteCommandWithOutput("VtTabletUpdateStream", "-position", position, "-count", "1", vttablet.Alias) - require.Nil(t, err) - - var binlogStreamEvent query.StreamEvent - err = json.Unmarshal([]byte(output), &binlogStreamEvent) - require.Nil(t, err) - for _, statement := range binlogStreamEvent.Statements { - if isCurrentRowPresent(*statement, pkKey) { - return - } - } - time.Sleep(300 * time.Millisecond) - position = binlogStreamEvent.EventToken.Position - } -} - -func isCurrentRowPresent(statement query.StreamEvent_Statement, pkKey string) bool { - if statement.TableName == tableName && - statement.PrimaryKeyFields[0].Name == "id" && - fmt.Sprintf("%s", statement.PrimaryKeyValues[0].Values) == pkKey { - return true - } - return false -} - -func verifyData(t *testing.T, id uint64, charset string, expectedOutput string) { - data, err := queryTablet(t, *destMaster, fmt.Sprintf("select id, msg from %s where id = %d", tableName, id), charset) - require.Nil(t, err) - assert.NotNil(t, data.Rows) - rowFound := assert.Equal(t, len(data.Rows), 1) - assert.Equal(t, len(data.Fields), 2) - if rowFound { - assert.Equal(t, fmt.Sprintf("%v", data.Rows[0]), expectedOutput) - } -} - -func queryTablet(t *testing.T, vttablet cluster.Vttablet, query string, charset string) (*sqltypes.Result, error) { - dbParams := mysql.ConnParams{ - Uname: "vt_dba", - UnixSocket: path.Join(vttablet.VttabletProcess.Directory, "mysql.sock"), - - DbName: fmt.Sprintf("vt_%s", keyspaceName), - } - if charset != "" { - dbParams.Charset = charset - } - ctx := context.Background() - dbConn, err := mysql.Connect(ctx, &dbParams) - require.Nil(t, err) - defer dbConn.Close() - return dbConn.ExecuteFetch(query, 1000, true) -} diff --git a/test/config.json b/test/config.json index 6f9ec4b7a81..f1b664e60c8 100644 --- a/test/config.json +++ b/test/config.json @@ -121,15 +121,6 @@ "RetryMax": 0, "Tags": [] }, - "binlog": { - "File": "unused.go", - "Args": ["vitess.io/vitess/go/test/endtoend/binlog"], - "Command": [], - "Manual": false, - "Shard": 12, - "RetryMax": 0, - "Tags": [] - }, "cellalias": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/cellalias"], From c6af1148c280c53281138178a07b8c2c4c72d393 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Mon, 16 Mar 2020 13:25:55 -0700 Subject: [PATCH 308/825] deprecation: improve DDL pass-through Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/planbuilder/ddl.go | 54 ------------------- .../vttablet/tabletserver/planbuilder/plan.go | 2 +- .../tabletserver/planbuilder/plan_test.go | 12 ----- .../planbuilder/testdata/exec_cases.txt | 27 ---------- go/vt/vttablet/tabletserver/query_executor.go | 44 +++------------ go/vt/vttablet/tabletserver/tx_pool.go | 3 ++ 6 files changed, 11 insertions(+), 131 deletions(-) delete mode 100644 go/vt/vttablet/tabletserver/planbuilder/ddl.go diff --git a/go/vt/vttablet/tabletserver/planbuilder/ddl.go b/go/vt/vttablet/tabletserver/planbuilder/ddl.go deleted file mode 100644 index 68b0449db19..00000000000 --- a/go/vt/vttablet/tabletserver/planbuilder/ddl.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package planbuilder - -import ( - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" -) - -// DDLPlan provides a plan for DDLs. -type DDLPlan struct { - Action string -} - -// DDLParse parses a DDL and produces a DDLPlan. -func DDLParse(sql string) (plan *DDLPlan) { - statement, err := sqlparser.Parse(sql) - if err != nil { - return &DDLPlan{Action: ""} - } - stmt, ok := statement.(*sqlparser.DDL) - if !ok { - return &DDLPlan{Action: ""} - } - return &DDLPlan{ - Action: stmt.Action, - } -} - -func analyzeDDL(ddl *sqlparser.DDL, tables map[string]*schema.Table) *Plan { - // TODO(sougou): Add support for sequences. - plan := &Plan{ - PlanID: PlanDDL, - } - // this can become a whitelist of fully supported ddl actions as support grows - if ddl.PartitionSpec != nil { - plan.FullQuery = GenerateFullQuery(ddl) - } - return plan -} diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan.go b/go/vt/vttablet/tabletserver/planbuilder/plan.go index deabf41f49a..a9b662a79c8 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan.go @@ -170,7 +170,7 @@ func Build(statement sqlparser.Statement, tables map[string]*schema.Table) (*Pla case *sqlparser.Set: plan, err = analyzeSet(stmt), nil case *sqlparser.DDL: - plan, err = analyzeDDL(stmt, tables), nil + plan = &Plan{PlanID: PlanDDL} case *sqlparser.Show: plan, err = &Plan{PlanID: PlanOtherRead}, nil case *sqlparser.OtherRead: diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go index 1026e6c99e8..7b4c7adea91 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan_test.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan_test.go @@ -161,18 +161,6 @@ func TestStreamPlan(t *testing.T) { } } -func TestDDLPlan(t *testing.T) { - for tcase := range iterateExecFile("ddl_cases.txt") { - plan := DDLParse(tcase.input) - expected := make(map[string]interface{}) - err := json.Unmarshal([]byte(tcase.output), &expected) - if err != nil { - t.Fatalf("Error marshalling %v", plan) - } - matchString(t, tcase.lineno, expected["Action"], plan.Action) - } -} - func TestMessageStreamingPlan(t *testing.T) { testSchema := loadSchema("schema_test.json") plan, err := BuildMessageStreaming("msg", testSchema) diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index 3397ff6ee84..32a47b833f9 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -867,33 +867,6 @@ options:PassthroughDMLs ] } -# reorganize partition with bind -"alter table a reorganize partition b into (partition c values less than (:bv), partition d values less than (maxvalue))" -{ - "PlanID": "DDL", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 2 - } - ], - "FullQuery": "alter table a reorganize partition b into (partition c values less than (:bv), partition d values less than (maxvalue))" -} - -# partition -"alter table a partition by range (id) (partition p0 values less than (10), partition p1 values less than (maxvalue))" -{ - "PlanID": "DDL", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 2 - } - ] -} - # show "show a" { diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 2958648f628..66d3451f242 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -97,8 +97,6 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { } switch qre.plan.PlanID { - case planbuilder.PlanDDL: - return qre.execDDL() case planbuilder.PlanNextval: return qre.execNextval() case planbuilder.PlanSelectImpossible: @@ -202,6 +200,8 @@ func (qre *QueryExecutor) txConnExec(conn *TxConnection) (*sqltypes.Result, erro return nil, err } return qr, nil + case planbuilder.PlanDDL: + return qre.execDDL(conn) } return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) } @@ -352,20 +352,7 @@ func (qre *QueryExecutor) checkAccess(authorized *tableacl.ACLResult, tableName return nil } -func (qre *QueryExecutor) execDDL() (*sqltypes.Result, error) { - sql := qre.query - var err error - if qre.plan.FullQuery != nil { - sql, _, err = qre.generateFinalSQL(qre.plan.FullQuery, qre.bindVars) - if err != nil { - return nil, err - } - } - ddlPlan := planbuilder.DDLParse(sql) - if ddlPlan.Action == "" { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "DDL is not understood") - } - +func (qre *QueryExecutor) execDDL(conn *TxConnection) (*sqltypes.Result, error) { defer func() { err := qre.tsv.se.Reload(qre.ctx) if err != nil { @@ -373,31 +360,14 @@ func (qre *QueryExecutor) execDDL() (*sqltypes.Result, error) { } }() - if qre.transactionID != 0 { - conn, err := qre.tsv.te.txPool.Get(qre.transactionID, "DDL begin again") - if err != nil { - return nil, err - } - defer conn.Recycle() - result, err := qre.execSQL(conn, sql, true) - if err != nil { - return nil, err - } - err = conn.BeginAgain(qre.ctx) - if err != nil { - return nil, err - } - return result, nil + result, err := qre.execSQL(conn, qre.query, true) + if err != nil { + return nil, err } - - result, err := qre.execAsTransaction(true /* autocommit */, func(conn *TxConnection) (*sqltypes.Result, error) { - return qre.execSQL(conn, sql, true) - }) - + err = conn.BeginAgain(qre.ctx) if err != nil { return nil, err } - return result, nil } diff --git a/go/vt/vttablet/tabletserver/tx_pool.go b/go/vt/vttablet/tabletserver/tx_pool.go index 1bb5723bc9d..4d597e0108c 100644 --- a/go/vt/vttablet/tabletserver/tx_pool.go +++ b/go/vt/vttablet/tabletserver/tx_pool.go @@ -458,6 +458,9 @@ func (txc *TxConnection) Exec(ctx context.Context, query string, maxrows int, wa // BeginAgain commits the existing transaction and begins a new one func (txc *TxConnection) BeginAgain(ctx context.Context) error { + if txc.Autocommit { + return nil + } if _, err := txc.DBConn.Exec(ctx, "commit", 1, false); err != nil { return err } From b1165fb332daeb3e08ed90e8be9a89b2b753b582 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Fri, 13 Mar 2020 15:30:11 +0530 Subject: [PATCH 309/825] Removed python test files dependency Signed-off-by: Arindam Nayak --- py/setup.py | 23 - py/util/__init__.py | 13 - py/util/grpc_with_metadata.py | 75 - py/util/static_auth_client.py | 27 - py/vtctl/__init__.py | 13 - py/vtctl/grpc_vtctl_client.py | 80 - py/vtctl/vtctl_client.py | 151 - py/vtdb/__init__.py | 36 - py/vtdb/base_cursor.py | 234 - py/vtdb/cursorv3.py | 45 - py/vtdb/dbapi.py | 50 - py/vtdb/dbexceptions.py | 108 - py/vtdb/event_token.py | 79 - py/vtdb/field_types.py | 124 - py/vtdb/grpc_vtgate_client.py | 393 -- py/vtdb/keyrange.py | 60 - py/vtdb/keyrange_constants.py | 47 - py/vtdb/keyspace.py | 95 - py/vtdb/prefer_vtroot_imports.py | 53 - py/vtdb/proto3_encoding.py | 758 ---- py/vtdb/times.py | 104 - py/vtdb/topology.py | 25 - py/vtdb/vtdb_logger.py | 70 - py/vtdb/vtgate_client.py | 469 -- py/vtdb/vtgate_client_testsuite.py | 670 --- py/vtdb/vtgate_cursor.py | 289 -- py/vtdb/vtgate_utils.py | 225 - py/vtdb/vtrouting.py | 280 -- py/vtproto/__init__.py | 0 py/vtproto/automation_pb2.py | 627 --- py/vtproto/automation_pb2_grpc.py | 3 - py/vtproto/automationservice_pb2.py | 66 - py/vtproto/automationservice_pb2_grpc.py | 64 - py/vtproto/binlogdata_pb2.py | 1660 ------- py/vtproto/binlogdata_pb2_grpc.py | 3 - py/vtproto/binlogservice_pb2.py | 66 - py/vtproto/binlogservice_pb2_grpc.py | 65 - py/vtproto/logutil_pb2.py | 139 - py/vtproto/logutil_pb2_grpc.py | 3 - py/vtproto/mysqlctl_pb2.py | 425 -- py/vtproto/mysqlctl_pb2_grpc.py | 114 - py/vtproto/query_pb2.py | 4022 ----------------- py/vtproto/query_pb2_grpc.py | 3 - py/vtproto/queryservice_pb2.py | 265 -- py/vtproto/queryservice_pb2_grpc.py | 445 -- py/vtproto/replicationdata_pb2.py | 112 - py/vtproto/replicationdata_pb2_grpc.py | 3 - py/vtproto/tableacl_pb2.py | 138 - py/vtproto/tableacl_pb2_grpc.py | 3 - py/vtproto/tabletmanagerdata_pb2.py | 3971 ---------------- py/vtproto/tabletmanagerdata_pb2_grpc.py | 3 - py/vtproto/tabletmanagerservice_pb2.py | 453 -- py/vtproto/tabletmanagerservice_pb2_grpc.py | 841 ---- py/vtproto/throttlerdata_pb2.py | 656 --- py/vtproto/throttlerdata_pb2_grpc.py | 3 - py/vtproto/throttlerservice_pb2.py | 93 - py/vtproto/throttlerservice_pb2_grpc.py | 122 - py/vtproto/topodata_pb2.py | 1293 ------ py/vtproto/topodata_pb2_grpc.py | 3 - py/vtproto/vschema_pb2.py | 697 --- py/vtproto/vschema_pb2_grpc.py | 3 - py/vtproto/vtctldata_pb2.py | 254 -- py/vtproto/vtctldata_pb2_grpc.py | 3 - py/vtproto/vtctlservice_pb2.py | 57 - py/vtproto/vtctlservice_pb2_grpc.py | 46 - py/vtproto/vtgate_pb2.py | 3241 ------------- py/vtproto/vtgate_pb2_grpc.py | 3 - py/vtproto/vtgateservice_pb2.py | 256 -- py/vtproto/vtgateservice_pb2_grpc.py | 459 -- py/vtproto/vtrpc_pb2.py | 322 -- py/vtproto/vtrpc_pb2_grpc.py | 3 - py/vtproto/vttest_pb2.py | 206 - py/vtproto/vttest_pb2_grpc.py | 3 - py/vtproto/vttime_pb2.py | 77 - py/vtproto/vttime_pb2_grpc.py | 3 - py/vtproto/vtworkerdata_pb2.py | 112 - py/vtproto/vtworkerdata_pb2_grpc.py | 3 - py/vtproto/vtworkerservice_pb2.py | 57 - py/vtproto/vtworkerservice_pb2_grpc.py | 47 - py/vtproto/workflow_pb2.py | 449 -- py/vtproto/workflow_pb2_grpc.py | 3 - py/vttest/__init__.py | 18 - py/vttest/environment.py | 116 - py/vttest/init_data_options.py | 34 - py/vttest/local_database.py | 469 -- py/vttest/mysql_db.py | 53 - py/vttest/mysql_db_mysqlctl.py | 92 - py/vttest/mysql_flavor.py | 114 - py/vttest/run_local_database.py | 212 - py/vttest/sharding_utils.py | 80 - py/vttest/vt_processes.py | 231 - test/backup.py | 603 --- test/backup_mysqlctld.py | 25 - test/backup_only.py | 392 -- test/backup_transform.py | 313 -- test/backup_transform_mysqlctld.py | 25 - test/base_sharding.py | 433 -- test/binlog.py | 237 - test/cache_invalidation.py | 696 --- test/cell_aliases.py | 311 -- test/cell_no_aliases.py | 24 - test/encrypted_replication.py | 123 - test/encrypted_transport.py | 327 -- test/environment.py | 296 -- test/grpc_protocols_flavor.py | 78 - test/horizontal_resharding_workflow.py | 105 - test/initial_sharding.py | 646 --- test/initial_sharding_bytes.py | 29 - test/initial_sharding_multi.py | 796 ---- test/initial_sharding_multi_split_diff.py | 29 - test/keyrange_test.py | 172 - test/keyspace_test.py | 410 -- test/keyspace_util.py | 149 - test/legacy_resharding.py | 653 --- test/merge_sharding.py | 465 -- test/merge_sharding_bytes.py | 28 - test/messaging.py | 315 -- test/mysql_flavor.py | 276 -- test/mysql_server_test.py | 304 -- test/mysqlctl.py | 108 - test/prepared_statement_test.py | 321 -- test/protocols_flavor.py | 100 - test/python_client_test.py | 108 - test/recovery.py | 534 --- test/reparent.py | 785 ---- test/resharding.py | 1281 ------ test/resharding_bytes.py | 29 - test/resharding_multi_split_diff.py | 25 - test/resharding_rbr.py | 25 - test/schema.py | 377 -- test/schema_swap_test.py | 499 -- test/sharded.py | 193 - test/sharded_recovery.py | 648 --- test/tablet.py | 881 ---- test/tabletmanager.py | 914 ---- test/tabletmanager2.py | 223 - test/topo_flavor/__init__.py | 15 - test/topo_flavor/consul.py | 92 - test/topo_flavor/etcd2.py | 127 - test/topo_flavor/server.py | 82 - test/topo_flavor/zk2.py | 101 - test/update_stream.py | 672 --- test/update_stream_rbr.py | 24 - test/utils.py | 1337 ------ test/vertical_split.py | 678 --- test/vertical_split_rbr.py | 25 - test/vschema.py | 167 - test/vtbackup.py | 223 - test/vtctld_test.py | 213 - test/vtctld_web_test.py | 556 --- test/vtgate_buffer.py | 422 -- test/vtgate_gateway_flavor/__init__.py | 15 - .../vtgate_gateway_flavor/discoverygateway.py | 40 - test/vtgate_gateway_flavor/gateway.py | 74 - test/vtgate_utils_test.py | 103 - test/vtgatev2_test.py | 1723 ------- test/vtgatev3_test.py | 2034 --------- test/vttest_sample_test.py | 285 -- test/vttest_schema/default/test_table.sql | 6 - .../test_keyspace/test_table.sql | 6 - test/worker.py | 705 --- test/xb_recovery.py | 25 - test/xtrabackup.py | 25 - test/xtrabackup_xbstream.py | 27 - third_party/py/mock-1.0.1.tar.gz | Bin 818644 -> 0 bytes 165 files changed, 53096 deletions(-) delete mode 100755 py/setup.py delete mode 100644 py/util/__init__.py delete mode 100644 py/util/grpc_with_metadata.py delete mode 100644 py/util/static_auth_client.py delete mode 100644 py/vtctl/__init__.py delete mode 100644 py/vtctl/grpc_vtctl_client.py delete mode 100644 py/vtctl/vtctl_client.py delete mode 100644 py/vtdb/__init__.py delete mode 100644 py/vtdb/base_cursor.py delete mode 100644 py/vtdb/cursorv3.py delete mode 100644 py/vtdb/dbapi.py delete mode 100755 py/vtdb/dbexceptions.py delete mode 100644 py/vtdb/event_token.py delete mode 100755 py/vtdb/field_types.py delete mode 100644 py/vtdb/grpc_vtgate_client.py delete mode 100644 py/vtdb/keyrange.py delete mode 100644 py/vtdb/keyrange_constants.py delete mode 100644 py/vtdb/keyspace.py delete mode 100644 py/vtdb/prefer_vtroot_imports.py delete mode 100644 py/vtdb/proto3_encoding.py delete mode 100755 py/vtdb/times.py delete mode 100644 py/vtdb/topology.py delete mode 100644 py/vtdb/vtdb_logger.py delete mode 100644 py/vtdb/vtgate_client.py delete mode 100755 py/vtdb/vtgate_client_testsuite.py delete mode 100644 py/vtdb/vtgate_cursor.py delete mode 100644 py/vtdb/vtgate_utils.py delete mode 100644 py/vtdb/vtrouting.py delete mode 100644 py/vtproto/__init__.py delete mode 100644 py/vtproto/automation_pb2.py delete mode 100644 py/vtproto/automation_pb2_grpc.py delete mode 100644 py/vtproto/automationservice_pb2.py delete mode 100644 py/vtproto/automationservice_pb2_grpc.py delete mode 100644 py/vtproto/binlogdata_pb2.py delete mode 100644 py/vtproto/binlogdata_pb2_grpc.py delete mode 100644 py/vtproto/binlogservice_pb2.py delete mode 100644 py/vtproto/binlogservice_pb2_grpc.py delete mode 100644 py/vtproto/logutil_pb2.py delete mode 100644 py/vtproto/logutil_pb2_grpc.py delete mode 100644 py/vtproto/mysqlctl_pb2.py delete mode 100644 py/vtproto/mysqlctl_pb2_grpc.py delete mode 100644 py/vtproto/query_pb2.py delete mode 100644 py/vtproto/query_pb2_grpc.py delete mode 100644 py/vtproto/queryservice_pb2.py delete mode 100644 py/vtproto/queryservice_pb2_grpc.py delete mode 100644 py/vtproto/replicationdata_pb2.py delete mode 100644 py/vtproto/replicationdata_pb2_grpc.py delete mode 100644 py/vtproto/tableacl_pb2.py delete mode 100644 py/vtproto/tableacl_pb2_grpc.py delete mode 100644 py/vtproto/tabletmanagerdata_pb2.py delete mode 100644 py/vtproto/tabletmanagerdata_pb2_grpc.py delete mode 100644 py/vtproto/tabletmanagerservice_pb2.py delete mode 100644 py/vtproto/tabletmanagerservice_pb2_grpc.py delete mode 100644 py/vtproto/throttlerdata_pb2.py delete mode 100644 py/vtproto/throttlerdata_pb2_grpc.py delete mode 100644 py/vtproto/throttlerservice_pb2.py delete mode 100644 py/vtproto/throttlerservice_pb2_grpc.py delete mode 100644 py/vtproto/topodata_pb2.py delete mode 100644 py/vtproto/topodata_pb2_grpc.py delete mode 100644 py/vtproto/vschema_pb2.py delete mode 100644 py/vtproto/vschema_pb2_grpc.py delete mode 100644 py/vtproto/vtctldata_pb2.py delete mode 100644 py/vtproto/vtctldata_pb2_grpc.py delete mode 100644 py/vtproto/vtctlservice_pb2.py delete mode 100644 py/vtproto/vtctlservice_pb2_grpc.py delete mode 100644 py/vtproto/vtgate_pb2.py delete mode 100644 py/vtproto/vtgate_pb2_grpc.py delete mode 100644 py/vtproto/vtgateservice_pb2.py delete mode 100644 py/vtproto/vtgateservice_pb2_grpc.py delete mode 100644 py/vtproto/vtrpc_pb2.py delete mode 100644 py/vtproto/vtrpc_pb2_grpc.py delete mode 100644 py/vtproto/vttest_pb2.py delete mode 100644 py/vtproto/vttest_pb2_grpc.py delete mode 100644 py/vtproto/vttime_pb2.py delete mode 100644 py/vtproto/vttime_pb2_grpc.py delete mode 100644 py/vtproto/vtworkerdata_pb2.py delete mode 100644 py/vtproto/vtworkerdata_pb2_grpc.py delete mode 100644 py/vtproto/vtworkerservice_pb2.py delete mode 100644 py/vtproto/vtworkerservice_pb2_grpc.py delete mode 100644 py/vtproto/workflow_pb2.py delete mode 100644 py/vtproto/workflow_pb2_grpc.py delete mode 100644 py/vttest/__init__.py delete mode 100644 py/vttest/environment.py delete mode 100644 py/vttest/init_data_options.py delete mode 100644 py/vttest/local_database.py delete mode 100644 py/vttest/mysql_db.py delete mode 100644 py/vttest/mysql_db_mysqlctl.py delete mode 100644 py/vttest/mysql_flavor.py delete mode 100755 py/vttest/run_local_database.py delete mode 100644 py/vttest/sharding_utils.py delete mode 100644 py/vttest/vt_processes.py delete mode 100755 test/backup.py delete mode 100755 test/backup_mysqlctld.py delete mode 100755 test/backup_only.py delete mode 100755 test/backup_transform.py delete mode 100755 test/backup_transform_mysqlctld.py delete mode 100644 test/base_sharding.py delete mode 100755 test/binlog.py delete mode 100755 test/cache_invalidation.py delete mode 100755 test/cell_aliases.py delete mode 100755 test/cell_no_aliases.py delete mode 100755 test/encrypted_replication.py delete mode 100755 test/encrypted_transport.py delete mode 100644 test/environment.py delete mode 100644 test/grpc_protocols_flavor.py delete mode 100644 test/horizontal_resharding_workflow.py delete mode 100755 test/initial_sharding.py delete mode 100755 test/initial_sharding_bytes.py delete mode 100755 test/initial_sharding_multi.py delete mode 100755 test/initial_sharding_multi_split_diff.py delete mode 100755 test/keyrange_test.py delete mode 100755 test/keyspace_test.py delete mode 100644 test/keyspace_util.py delete mode 100755 test/legacy_resharding.py delete mode 100755 test/merge_sharding.py delete mode 100755 test/merge_sharding_bytes.py delete mode 100755 test/messaging.py delete mode 100644 test/mysql_flavor.py delete mode 100755 test/mysql_server_test.py delete mode 100755 test/mysqlctl.py delete mode 100755 test/prepared_statement_test.py delete mode 100644 test/protocols_flavor.py delete mode 100755 test/python_client_test.py delete mode 100755 test/recovery.py delete mode 100755 test/reparent.py delete mode 100755 test/resharding.py delete mode 100755 test/resharding_bytes.py delete mode 100755 test/resharding_multi_split_diff.py delete mode 100755 test/resharding_rbr.py delete mode 100755 test/schema.py delete mode 100755 test/schema_swap_test.py delete mode 100755 test/sharded.py delete mode 100755 test/sharded_recovery.py delete mode 100644 test/tablet.py delete mode 100755 test/tabletmanager.py delete mode 100755 test/tabletmanager2.py delete mode 100644 test/topo_flavor/__init__.py delete mode 100644 test/topo_flavor/consul.py delete mode 100644 test/topo_flavor/etcd2.py delete mode 100644 test/topo_flavor/server.py delete mode 100644 test/topo_flavor/zk2.py delete mode 100755 test/update_stream.py delete mode 100755 test/update_stream_rbr.py delete mode 100644 test/utils.py delete mode 100755 test/vertical_split.py delete mode 100755 test/vertical_split_rbr.py delete mode 100755 test/vschema.py delete mode 100644 test/vtbackup.py delete mode 100755 test/vtctld_test.py delete mode 100755 test/vtctld_web_test.py delete mode 100755 test/vtgate_buffer.py delete mode 100644 test/vtgate_gateway_flavor/__init__.py delete mode 100644 test/vtgate_gateway_flavor/discoverygateway.py delete mode 100644 test/vtgate_gateway_flavor/gateway.py delete mode 100755 test/vtgate_utils_test.py delete mode 100755 test/vtgatev2_test.py delete mode 100755 test/vtgatev3_test.py delete mode 100755 test/vttest_sample_test.py delete mode 100644 test/vttest_schema/default/test_table.sql delete mode 100644 test/vttest_schema/test_keyspace/test_table.sql delete mode 100755 test/worker.py delete mode 100755 test/xb_recovery.py delete mode 100755 test/xtrabackup.py delete mode 100755 test/xtrabackup_xbstream.py delete mode 100644 third_party/py/mock-1.0.1.tar.gz diff --git a/py/setup.py b/py/setup.py deleted file mode 100755 index 9ea1921ff9f..00000000000 --- a/py/setup.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This is the setup script for the submodules in the Vitess python client. -""" - -from distutils.core import setup - - -setup(name="vitess", - packages=["vtctl", "vtdb", "vtproto", "vttest", "util"], - platforms="Any", - ) diff --git a/py/util/__init__.py b/py/util/__init__.py deleted file mode 100644 index fbc9e3ecdd1..00000000000 --- a/py/util/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/py/util/grpc_with_metadata.py b/py/util/grpc_with_metadata.py deleted file mode 100644 index a2a2d219cca..00000000000 --- a/py/util/grpc_with_metadata.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This file contains the grpc channel decorator to have metadata set per call -""" - -class GRPCWithMetadataCallable: - def __init__(self, callable, getMetadata): - """Constructor. - Args: - callable: Underlying channel - getMetadata: method to call to get metadata for the grpc request. - """ - self.callable = callable - self.getMetadata = getMetadata - - def __call__(self, - request, - timeout=None, - metadata=None, - credentials=None): - call_metadata = self.getMetadata() - if metadata is not None and call_metadata is not None: - call_metadata = metadata + call_metadata - return self.callable(request, timeout, metadata=call_metadata, credentials=credentials) - -class GRPCWithMetadataChannel: - """This class provides a decorator for grpc channels where the caller can set up - metadata to be attached to all calls to the underlying stub. - """ - def __init__(self, channel, getMetadata): - self.channel = channel - self.getMetadata = getMetadata - - def unary_unary(self, - method, - request_serializer=None, - response_deserializer=None): - return GRPCWithMetadataCallable( - self.channel.unary_unary(method, request_serializer, response_deserializer), - self.getMetadata) - - def unary_stream(self, - method, - request_serializer=None, - response_deserializer=None): - return GRPCWithMetadataCallable( - self.channel.unary_stream(method, request_serializer, response_deserializer), - self.getMetadata) - - def stream_unary(self, - method, - request_serializer=None, - response_deserializer=None): - return GRPCWithMetadataCallable( - self.channel.stream_unary(method, request_serializer, response_deserializer), - self.getMetadata) - - def stream_stream(self, - method, - request_serializer=None, - response_deserializer=None): - return GRPCWithMetadataCallable( - self.channel.stream_stream(method, request_serializer, response_deserializer), - self.getMetadata) diff --git a/py/util/static_auth_client.py b/py/util/static_auth_client.py deleted file mode 100644 index f2e0235da29..00000000000 --- a/py/util/static_auth_client.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json - -class StaticAuthClientCreds(): - """Metadata wrapper for StaticAuthClientCreds.""" - - def __init__(self, auth_static_client_creds): - self._credentials = auth_static_client_creds - with open(self._credentials) as data_file: - self._data = json.load(data_file) - - def metadata(self): - return (('username', self._data['Username']), ('password', self._data['Password']),) - diff --git a/py/vtctl/__init__.py b/py/vtctl/__init__.py deleted file mode 100644 index fbc9e3ecdd1..00000000000 --- a/py/vtctl/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/py/vtctl/grpc_vtctl_client.py b/py/vtctl/grpc_vtctl_client.py deleted file mode 100644 index d9a958e8650..00000000000 --- a/py/vtctl/grpc_vtctl_client.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This file contains the grpc implementation of the vtctl client. -""" - -import datetime -from urlparse import urlparse - -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import - -import grpc - -import vtctl_client - -from vtproto import vtctldata_pb2 -from vtproto import vtctlservice_pb2_grpc -from util import static_auth_client -from util import grpc_with_metadata - - -class GRPCVtctlClient(vtctl_client.VtctlClient): - """GRPCVtctlClient is the gRPC implementation of VtctlClient. - - It is registered as 'grpc' protocol. - """ - - def __init__(self, addr, timeout, auth_static_client_creds=None): - self.addr = addr - self.timeout = timeout - self.stub = None - self.auth_static_client_creds = auth_static_client_creds - - def __str__(self): - return '' % self.addr - - def dial(self): - if self.stub: - self.stub.close() - - p = urlparse('http://' + self.addr) - channel = grpc.insecure_channel('%s:%s' % (p.hostname, p.port)) - if self.auth_static_client_creds is not None: - channel = grpc_with_metadata.GRPCWithMetadataChannel( - channel, - self.get_auth_static_client_creds) - self.stub = vtctlservice_pb2_grpc.VtctlStub(channel) - - def close(self): - self.stub = None - - def is_closed(self): - return self.stub is None - - def get_auth_static_client_creds(self): - return static_auth_client.StaticAuthClientCreds( - self.auth_static_client_creds).metadata() - - def execute_vtctl_command(self, args, action_timeout=30.0): - req = vtctldata_pb2.ExecuteVtctlCommandRequest( - args=args, - action_timeout=long(action_timeout * 1e9)) - it = self.stub.ExecuteVtctlCommand(req, action_timeout) - for response in it: - t = datetime.datetime.utcfromtimestamp(response.event.time.seconds) - yield vtctl_client.Event(t, response.event.level, response.event.file, - response.event.line, response.event.value) - - -vtctl_client.register_conn_class('grpc', GRPCVtctlClient) diff --git a/py/vtctl/vtctl_client.py b/py/vtctl/vtctl_client.py deleted file mode 100644 index 663da1e4470..00000000000 --- a/py/vtctl/vtctl_client.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This module defines the vtctl client interface. -""" - -import logging - -# mapping from protocol to python class. The protocol matches the string -# used by vtctlclient as a -vtctl_client_protocol parameter. -vtctl_client_conn_classes = dict() - - -def register_conn_class(protocol, c): - """Used by implementations to register themselves. - - Args: - protocol: short string to document the protocol. - c: class to register. - """ - vtctl_client_conn_classes[protocol] = c - - -def connect(protocol, *pargs, **kargs): - """connect will return a dialed VtctlClient connection to a vtctl server. - - Args: - protocol: the registered protocol to use. - *pargs: passed to the registered protocol __init__ method. - **kargs: passed to the registered protocol __init__ method. - - Returns: - A dialed VtctlClient. - - Raises: - ValueError: if the protocol is unknown. - """ - if protocol not in vtctl_client_conn_classes: - raise ValueError('Unknown vtctl protocol', protocol) - conn = vtctl_client_conn_classes[protocol](*pargs, **kargs) - conn.dial() - return conn - - -class Event(object): - """Event is streamed by VtctlClient. - - Eventually, we will just use the proto3 definition for logutil.proto/Event. - """ - - INFO = 0 - WARNING = 1 - ERROR = 2 - CONSOLE = 3 - - def __init__(self, time, level, file, line, value): - self.time = time - self.level = level - self.file = file - self.line = line - self.value = value - - -class VtctlClient(object): - """VtctlClient is the interface for the vtctl client implementations. - - All implementations must implement all these methods. - If something goes wrong with the connection, this object will be thrown out. - """ - - def __init__(self, addr, timeout): - """Initialize a vtctl connection. - - Args: - addr: server address. Can be protocol dependent. - timeout: connection timeout (float, in seconds). - """ - pass - - def dial(self): - """Dial to the server. If successful, call close() to close the connection. - """ - pass - - def close(self): - """Close the connection. This object may be re-used again by calling dial(). - """ - pass - - def is_closed(self): - """Checks the connection status. - - Returns: - True if this connection is closed. - """ - pass - - def execute_vtctl_command(self, args, action_timeout=30.0): - """Executes a remote command on the vtctl server. - - Args: - args: Command line to run. - action_timeout: total timeout for the action (float, in seconds). - - Returns: - This is a generator method that yields Event objects. - """ - pass - - -def execute_vtctl_command(client, args, action_timeout=30.0, - info_to_debug=False): - """This is a helper method that executes a remote vtctl command. - - It logs the output to the logging module, and returns the console output. - - Args: - client: VtctlClient object to use. - args: Command line to run. - action_timeout: total timeout for the action (float, in seconds). - info_to_debug: if set, changes the info messages to debug. - - Returns: - The console output of the action. - """ - - console_result = '' - for e in client.execute_vtctl_command(args, action_timeout=action_timeout): - if e.level == Event.INFO: - if info_to_debug: - logging.debug('%s', e.value) - else: - logging.info('%s', e.value) - elif e.level == Event.WARNING: - logging.warning('%s', e.value) - elif e.level == Event.ERROR: - logging.error('%s', e.value) - elif e.level == Event.CONSOLE: - console_result += e.value - - return console_result diff --git a/py/vtdb/__init__.py b/py/vtdb/__init__.py deleted file mode 100644 index 278ebfd69c6..00000000000 --- a/py/vtdb/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -"""This file provides the PEP0249 compliant variables for this module. - -See https://www.python.org/dev/peps/pep-0249 for more information on these. -""" - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Follows the Python Database API 2.0. -apilevel = '2.0' - -# Threads may share the module, but not connections. -# (we store session information in the connection now, that should be in the -# cursor but are not for historical reasons). -threadsafety = 2 - -# Named style, e.g. ...WHERE name=:name. -# -# Note we also provide a function in dbapi to convert from 'pyformat' -# to 'named', and prune unused bind variables in the SQL query. -# -# Also, we use an extension to bind variables to handle lists: -# Using the '::name' syntax (instead of ':name') will indicate a list bind -# variable. The type then has to be a list, set or tuple. -paramstyle = 'named' diff --git a/py/vtdb/base_cursor.py b/py/vtdb/base_cursor.py deleted file mode 100644 index be753062414..00000000000 --- a/py/vtdb/base_cursor.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Base classes for cursors. - -These classes centralize common code. -""" - -from vtdb import dbexceptions - - -class BasePEP0249Cursor(object): - """Cursor with common PEP0249 implementations.""" - - def __init__(self): - self._clear_common_state() - self._conn = None - - def callproc(self): - """For PEP 0249.""" - raise dbexceptions.NotSupportedError - - def executemany(self, sql, params_list): - """For PEP 0249.""" - _ = sql, params_list - raise dbexceptions.NotSupportedError - - def nextset(self): - """For PEP 0249.""" - raise dbexceptions.NotSupportedError - - def setinputsizes(self, sizes): - """For PEP 0249.""" - _ = sizes - - def setoutputsize(self, size, column=None): - """For PEP 0249.""" - _ = size, column - - @property - def rownumber(self): - return self.index - - def __iter__(self): - """For PEP 0249: To make cursors compatible to the iteration protocol.""" - return self - - def next(self): - """For PEP 0249.""" - val = self.fetchone() - if val is None: - raise StopIteration - return val - - def close(self): - """For PEP 0249.""" - raise NotImplementedError - - def fetchone(self): - """For PEP 0249.""" - raise NotImplementedError - - def fetchmany(self, size=None): - """For PEP 0249.""" - raise NotImplementedError - - def fetchall(self): - """For PEP 0249.""" - raise NotImplementedError - - def _clear_common_state(self): - self.index = 0 - - @property - def connection(self): - if not self._conn: - raise dbexceptions.ProgrammingError( - 'Cannot use closed cursor %s.' % self.__class__) - return self._conn - - -class BaseListCursor(BasePEP0249Cursor): - """Base cursor where results are stored as a list. - - Execute call should return a (results, rowcount, lastrowid, - description) tuple. The fetch commands traverse self.results. - """ - arraysize = 1 - - def __init__(self, single_db=False, twopc=False): - super(BaseListCursor, self).__init__() - self._clear_list_state() - self.effective_caller_id = None - self.single_db = single_db - self.twopc = twopc - - def _clear_list_state(self): - self._clear_common_state() - self.description = None - self.lastrowid = None - self.rowcount = None - self.results = None - - def set_effective_caller_id(self, effective_caller_id): - """Set the effective caller id that will be used in upcoming calls.""" - self.effective_caller_id = effective_caller_id - - def begin(self): - return self.connection.begin( - effective_caller_id=self.effective_caller_id, - single_db=self.single_db) - - def commit(self): - return self.connection.commit(self.twopc) - - def rollback(self): - return self.connection.rollback() - - def _check_fetch(self): - if self.results is None: - raise dbexceptions.ProgrammingError('Fetch called before execute.') - - def _handle_transaction_sql(self, sql): - sql_check = sql.strip().lower() - if sql_check == 'begin': - self.begin() - return True - elif sql_check == 'commit': - self.commit() - return True - elif sql_check == 'rollback': - self.rollback() - return True - else: - return False - - def close(self): - self._clear_list_state() - self._conn = None - - def fetchone(self): - self._check_fetch() - if self.index >= len(self.results): - return None - self.index += 1 - return self.results[self.index - 1] - - def fetchmany(self, size=None): - self._check_fetch() - if self.index >= len(self.results): - return [] - if size is None: - size = self.arraysize - res = self.results[self.index:self.index + size] - self.index += size - return res - - def fetchall(self): - self._check_fetch() - return self.fetchmany(len(self.results) - self.index) - - -class BaseStreamCursor(BasePEP0249Cursor): - """Base cursor where results are returned as a generator. - - This supports large queries. An execute call returns a (generator, - description) pair. The fetch functions read items from the generator - until it is exhausted. - """ - - arraysize = 1 - - def __init__(self): - super(BaseStreamCursor, self).__init__() - self._clear_stream_state() - self.effective_caller_id = None - - def set_effective_caller_id(self, effective_caller_id): - """Set the effective caller id that will be used in upcoming calls.""" - self.effective_caller_id = effective_caller_id - - def _clear_stream_state(self): - self._clear_common_state() - self.description = None - self.generator = None - - def fetchone(self): - if self.description is None: - raise dbexceptions.ProgrammingError('Fetch called before execute.') - self.index += 1 - try: - return self.generator.next() - except StopIteration: - return None - - # fetchmany can be called until it returns no rows. Returning less rows - # than what we asked for is also an indication we ran out, but the cursor - # API in PEP249 is silent about that. - def fetchmany(self, size=None): - if size is None: - size = self.arraysize - result = [] - for _ in xrange(size): - row = self.fetchone() - if row is None: - break - result.append(row) - return result - - def fetchall(self): - result = [] - while True: - row = self.fetchone() - if row is None: - break - result.append(row) - return result - - def close(self): - if self.generator: - self.generator.close() - self._clear_stream_state() - self._conn = None diff --git a/py/vtdb/cursorv3.py b/py/vtdb/cursorv3.py deleted file mode 100644 index c5d502a5ef6..00000000000 --- a/py/vtdb/cursorv3.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from vtdb import base_cursor - - -class Cursor(base_cursor.BaseListCursor): - - def __init__(self, connection, tablet_type, single_db=False, twopc=False): - super(Cursor, self).__init__(single_db=single_db, twopc=twopc) - self._conn = connection - self.tablet_type = tablet_type - - def execute(self, sql, bind_variables): - self._clear_list_state() - if self._handle_transaction_sql(sql): - return - self.results, self.rowcount, self.lastrowid, self.description = ( - self.connection._execute(sql, bind_variables, self.tablet_type)) - return self.rowcount - - -class StreamCursor(base_cursor.BaseStreamCursor): - - def __init__(self, connection, tablet_type): - super(StreamCursor, self).__init__() - self._conn = connection - self.tablet_type = tablet_type - - def execute(self, sql, bind_variables, **kargs): - self._clear_stream_state() - self.generator, self.description = self.connection._stream_execute( - sql, bind_variables, self.tablet_type) - return 0 diff --git a/py/vtdb/dbapi.py b/py/vtdb/dbapi.py deleted file mode 100644 index 56c9febbd73..00000000000 --- a/py/vtdb/dbapi.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from vtdb import dbexceptions - - -# A simple class to trap and re-export only variables referenced from -# the sql statement since bind dictionaries can be *very* noisy. This -# is a by-product of converting the DB-API %(name)s syntax to our -# :name syntax. -class BindVarsProxy(object): - - def __init__(self, bind_vars): - self.bind_vars = bind_vars - self.accessed_keys = set() - - def __getitem__(self, name): - var = self.bind_vars[name] - self.bind_vars[name] - self.accessed_keys.add(name) - if isinstance(var, (list, set, tuple)): - return '::%s' % name - - return ':%s' % name - - def export_bind_vars(self): - return dict([(k, self.bind_vars[k]) for k in self.accessed_keys]) - - -# convert bind style from %(name)s to :name and export only the -# variables bound. -def prepare_query_bind_vars(query, bind_vars): - bind_vars_proxy = BindVarsProxy(bind_vars) - try: - query %= bind_vars_proxy - except KeyError as e: - raise dbexceptions.InterfaceError(e[0], query, bind_vars) - - return query, bind_vars_proxy.export_bind_vars() diff --git a/py/vtdb/dbexceptions.py b/py/vtdb/dbexceptions.py deleted file mode 100755 index 430c710f7f8..00000000000 --- a/py/vtdb/dbexceptions.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import exceptions - - -class Error(exceptions.StandardError): - pass - - -class DatabaseError(exceptions.StandardError): - pass - - -class DataError(DatabaseError): - pass - - -class Warning(exceptions.StandardError): - pass - - -class InterfaceError(Error): - pass - - -class InternalError(DatabaseError): - pass - - -class OperationalError(DatabaseError): - pass - - -class ProgrammingError(DatabaseError): - pass - - -class NotSupportedError(ProgrammingError): - pass - - -class IntegrityError(DatabaseError): - pass - - -class PartialCommitError(IntegrityError): - pass - - -# Below errors are VT specific - - -# Retry means a simple and immediate reconnect to the same host/port -# will likely fix things. This is initiated by a graceful restart on -# the server side. In general this can be handled transparently -# unless the error is within a transaction. -class RetryError(OperationalError): - pass - - -# This failure is "permanent" - retrying on this host is futile. Push the error -# up in case the upper layers can gracefully recover by reresolving a suitable -# endpoint. -class FatalError(OperationalError): - pass - - -# This failure is operational in the sense that we must teardown the -# connection to ensure future RPCs are handled correctly. -class TimeoutError(OperationalError): - pass - - -class TxPoolFull(DatabaseError): - pass - - -# TransientError is raised for an error that is expected to go away soon. These -# errors should be retried. Examples: when a client exceedes allocated quota on -# a server, or when there's a backlog of requests and new ones are temporarily -# being rejected. -class TransientError(DatabaseError): - pass - - -# TODO(aaijazi): These are deprecated. They will be replaced by TransientError. -# ThrottledError is raised when client exceeds allocated quota on the server -class ThrottledError(DatabaseError): - pass - - -# QueryNotServed is raised when a pre-condition has failed. For instance, -# an update stream query cannot be served because there aren't enough -# binlogs on the server. -class QueryNotServed(DatabaseError): - pass diff --git a/py/vtdb/event_token.py b/py/vtdb/event_token.py deleted file mode 100644 index f393b8b9e77..00000000000 --- a/py/vtdb/event_token.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Helper methods to compare EventToken objects. -""" - - -MARIADB_FLAVOR = 'MariaDB' -MYSQL_FLAVOR = 'MySQL56' - - -def fresher(ev1, ev2): - """Returns a comparison of the tokens. - - If we can't figure it out, this method will return -1, meaning ev1 - is considered older in doubt. - - Args: - ev1: the first event token. - ev2: the second event token. - - Returns: - value: a negative number of ev1ev2. - - """ - if ev1 is None or ev2 is None: - # Either one is None, we don't know. - return -1 - - if ev1.timestamp != ev2.timestamp: - # The timestamp is enough to set them apart. - return ev1.timestamp - ev2.timestamp - - if ev1.shard and ev1.shard == ev2.shard: - # They come from the same shard, we can parse them and compare. - if not ev1.position or not ev2.position: - # We do not know. - return -1 - - # Split them up. - parts1 = ev1.position.split('/', 1) - parts2 = ev2.position.split('/', 1) - if len(parts1) != 2 or len(parts2) != 2: - # This should never happen, but let's just handle it. - return -1 - if parts1[0] != parts2[0]: - # This should never happen, but let's just handle it. - return -1 - pos1 = parts1[1] - pos2 = parts2[1] - - if parts1[0] == MARIADB_FLAVOR: - # Both GTIDSets should be of the form --. - # We just compare the sequence as int. - parts1 = pos1.split('-', 2) - parts2 = pos2.split('-', 2) - if len(parts1) != 3 or len(parts2) != 3: - # This should never happen, but let's just handle it. - return -1 - - return int(parts1[2]) - int(parts2[2]) - - elif parts1[0] == MYSQL_FLAVOR: - # Not implemented yet. - pass - - # We do not know. - return -1 diff --git a/py/vtdb/field_types.py b/py/vtdb/field_types.py deleted file mode 100755 index e1e234aede5..00000000000 --- a/py/vtdb/field_types.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from array import array -import datetime -from decimal import Decimal -from vtdb import times - -# These numbers should exactly match values defined in -# dist/mysql-5.1.52/include/mysql/mysql_com.h -VT_DECIMAL = 0 -VT_TINY = 1 -VT_SHORT = 2 -VT_LONG = 3 -VT_FLOAT = 4 -VT_DOUBLE = 5 -VT_NULL = 6 -VT_TIMESTAMP = 7 -VT_LONGLONG = 8 -VT_INT24 = 9 -VT_DATE = 10 -VT_TIME = 11 -VT_DATETIME = 12 -VT_YEAR = 13 -VT_NEWDATE = 14 -VT_BIT = 16 -VT_NEWDECIMAL = 246 -VT_ENUM = 247 -VT_SET = 248 -VT_TINY_BLOB = 249 -VT_MEDIUM_BLOB = 250 -VT_LONG_BLOB = 251 -VT_BLOB = 252 -VT_VAR_STRING = 253 -VT_STRING = 254 -VT_GEOMETRY = 255 - - -class DBAPITypeObject(object): - - def __init__(self, *values): - self.values = values - - def __cmp__(self, other): - if other in self.values: - return 0 - return 1 - - -# FIXME(msolomon) why do we have these values if they aren't referenced? -STRING = DBAPITypeObject(VT_ENUM, VT_VAR_STRING, VT_STRING) -BINARY = DBAPITypeObject(VT_TINY_BLOB, VT_MEDIUM_BLOB, VT_LONG_BLOB, VT_BLOB) -NUMBER = DBAPITypeObject( - VT_DECIMAL, VT_TINY, VT_SHORT, VT_LONG, VT_FLOAT, VT_DOUBLE, VT_LONGLONG, - VT_INT24, VT_YEAR, VT_NEWDECIMAL) -DATETIME = DBAPITypeObject( - VT_TIMESTAMP, VT_DATE, VT_TIME, VT_DATETIME, VT_NEWDATE) -ROWID = DBAPITypeObject() - -conversions = { - VT_DECIMAL: Decimal, - VT_TINY: int, - VT_SHORT: int, - VT_LONG: long, - VT_FLOAT: float, - VT_DOUBLE: float, - VT_TIMESTAMP: times.DateTimeOrNone, - VT_LONGLONG: long, - VT_INT24: int, - VT_DATE: times.DateOrNone, - VT_TIME: times.TimeDeltaOrNone, - VT_DATETIME: times.DateTimeOrNone, - VT_YEAR: int, - VT_NEWDATE: times.DateOrNone, - VT_NEWDECIMAL: Decimal, -} - - -# This is a temporary workaround till we figure out how to support -# native lists in our API. -class List(list): - pass - -NoneType = type(None) - -# FIXME(msolomon) we could make a SqlLiteral ABC and just type check. -# That doesn't seem dramatically better than __sql_literal__ but it might -# be move self-documenting. - - -def convert_bind_vars(bind_variables): - new_vars = {} - if bind_variables is None: - return new_vars - for key, val in bind_variables.iteritems(): - if hasattr(val, '__sql_literal__'): - new_vars[key] = val.__sql_literal__() - elif isinstance(val, datetime.datetime): - new_vars[key] = times.DateTimeToString(val) - elif isinstance(val, datetime.date): - new_vars[key] = times.DateToString(val) - elif isinstance(val, set): - new_vars[key] = sorted(val) - elif isinstance(val, tuple): - new_vars[key] = list(val) - elif isinstance(val, (int, long, float, str, list, NoneType)): - new_vars[key] = val - else: - # NOTE(msolomon) begrudgingly I allow this - we just have too much code - # that relies on this. - # This accidentally solves our hideous dependency on mx.DateTime. - new_vars[key] = str(val) - return new_vars diff --git a/py/vtdb/grpc_vtgate_client.py b/py/vtdb/grpc_vtgate_client.py deleted file mode 100644 index 41348d66753..00000000000 --- a/py/vtdb/grpc_vtgate_client.py +++ /dev/null @@ -1,393 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""A simple, direct connection to the vtgate proxy server, using gRPC. -""" - -import logging -import re -from urlparse import urlparse - -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import - -import grpc - -from vtproto import vtgate_pb2 -from vtproto import vtgateservice_pb2_grpc - -from vtdb import dbexceptions -from vtdb import proto3_encoding -from vtdb import vtdb_logger -from vtdb import vtgate_client -from vtdb import vtgate_cursor -from vtdb import vtgate_utils -from util import static_auth_client -from util import grpc_with_metadata - - -_errno_pattern = re.compile(r'\(errno (\d+)\)', re.IGNORECASE) - - -class GRPCVTGateConnection(vtgate_client.VTGateClient, - proto3_encoding.Proto3Connection): - """A direct gRPC connection to the vtgate query service, using proto3. - """ - - def __init__(self, addr, timeout, - root_certificates=None, private_key=None, certificate_chain=None, - auth_static_client_creds=None, - **kwargs): - """Creates a new GRPCVTGateConnection. - - Args: - addr: address to connect to. - timeout: connection time out. - root_certificates: PEM_encoded root certificates. - private_key: PEM-encoded private key. - certificate_chain: PEM-encoded certificate chain. - auth_static_client_creds: basic auth credentials file path. - **kwargs: passed up. - """ - super(GRPCVTGateConnection, self).__init__(addr, timeout, **kwargs) - self.stub = None - self.root_certificates = root_certificates - self.private_key = private_key - self.certificate_chain = certificate_chain - self.auth_static_client_creds = auth_static_client_creds - self.logger_object = vtdb_logger.get_logger() - - def dial(self): - if self.stub: - self.stub.close() - - p = urlparse('http://' + self.addr) - target = '%s:%s' % (p.hostname, p.port) - - if self.root_certificates or self.private_key or self.certificate_chain: - creds = grpc.ssl_channel_credentials( - self.root_certificates, self.private_key, self.certificate_chain) - channel = grpc.secure_channel(target, creds) - else: - channel = grpc.insecure_channel(target) - if self.auth_static_client_creds is not None: - channel = grpc_with_metadata.GRPCWithMetadataChannel( - channel, - self.get_auth_static_client_creds) - self.stub = vtgateservice_pb2_grpc.VitessStub(channel) - - def close(self): - """close closes the server connection and frees up associated resources. - - The stub object is managed by the gRPC library, removing references - to it will just close the channel. - """ - if self.session and self.session.in_transaction: - # If the endpoint is not responding, this would exception out, - # just when we want to not connect to the endpoint any more. - # Let's swallow that exception. - try: - self.rollback() - except dbexceptions.DatabaseError: - pass - self.stub = None - - def is_closed(self): - return self.stub is None - - def get_auth_static_client_creds(self): - return static_auth_client.StaticAuthClientCreds( - self.auth_static_client_creds).metadata() - - def cursor(self, *pargs, **kwargs): - cursorclass = kwargs.pop('cursorclass', None) or vtgate_cursor.VTGateCursor - return cursorclass(self, *pargs, **kwargs) - - def begin(self, effective_caller_id=None, single_db=False): - try: - request = self.begin_request(effective_caller_id, single_db) - response = self.stub.Begin(request, self.timeout) - self.update_session(response) - except (grpc.RpcError, vtgate_utils.VitessError) as e: - raise _convert_exception(e, 'Begin') - - def commit(self, twopc=False): - try: - request = self.commit_request(twopc) - self.stub.Commit(request, self.timeout) - except (grpc.RpcError, vtgate_utils.VitessError) as e: - raise _convert_exception(e, 'Commit') - finally: - self.session = None - - def rollback(self): - try: - request = self.rollback_request() - self.stub.Rollback(request, self.timeout) - except (grpc.RpcError, vtgate_utils.VitessError) as e: - raise _convert_exception(e, 'Rollback') - finally: - self.session = None - - @vtgate_utils.exponential_backoff_retry((dbexceptions.ThrottledError, - dbexceptions.TransientError)) - def _execute( - self, sql, bind_variables, tablet_type, keyspace_name=None, - shards=None, keyspace_ids=None, keyranges=None, - entity_keyspace_id_map=None, entity_column_name=None, - not_in_transaction=False, effective_caller_id=None, - include_event_token=False, compare_event_token=None, **kwargs): - - # FIXME(alainjobart): keyspace should be in routing_kwargs, - # as it's not used for v3. - - try: - request, routing_kwargs, method_name = self.execute_request_and_name( - sql, bind_variables, tablet_type, - keyspace_name, shards, keyspace_ids, keyranges, - entity_column_name, entity_keyspace_id_map, - not_in_transaction, effective_caller_id, include_event_token, - compare_event_token) - method = getattr(self.stub, method_name) - response = method(request, self.timeout) - return self.process_execute_response(method_name, response) - - except (grpc.RpcError, vtgate_utils.VitessError) as e: - self.logger_object.log_private_data(bind_variables) - raise _convert_exception( - e, method_name, - sql=sql, keyspace=keyspace_name, tablet_type=tablet_type, - not_in_transaction=not_in_transaction, - **routing_kwargs) - - @vtgate_utils.exponential_backoff_retry((dbexceptions.ThrottledError, - dbexceptions.TransientError)) - def _execute_batch( - self, sql_list, bind_variables_list, keyspace_list, keyspace_ids_list, - shards_list, tablet_type, as_transaction, effective_caller_id=None, - **kwargs): - - try: - request, method_name = self.execute_batch_request_and_name( - sql_list, bind_variables_list, keyspace_list, - keyspace_ids_list, shards_list, - tablet_type, as_transaction, effective_caller_id) - method = getattr(self.stub, method_name) - response = method(request, self.timeout) - return self.process_execute_batch_response(method_name, response) - - except (grpc.RpcError, vtgate_utils.VitessError) as e: - self.logger_object.log_private_data(bind_variables_list) - raise _convert_exception( - e, method_name, - sqls=sql_list, tablet_type=tablet_type, - as_transaction=as_transaction) - - @vtgate_utils.exponential_backoff_retry((dbexceptions.ThrottledError, - dbexceptions.TransientError)) - def _stream_execute( - self, sql, bind_variables, tablet_type, keyspace_name=None, - shards=None, keyspace_ids=None, keyranges=None, - effective_caller_id=None, - **kwargs): - - try: - request, routing_kwargs, method_name = ( - self.stream_execute_request_and_name( - sql, bind_variables, tablet_type, - keyspace_name, - shards, - keyspace_ids, - keyranges, - effective_caller_id)) - method = getattr(self.stub, method_name) - it = method(request, self.timeout) - first_response = it.next() - except (grpc.RpcError, vtgate_utils.VitessError) as e: - self.logger_object.log_private_data(bind_variables) - raise _convert_exception( - e, method_name, - sql=sql, keyspace=keyspace_name, tablet_type=tablet_type, - **routing_kwargs) - - fields, convs = self.build_conversions(first_response.result.fields) - - def row_generator(): - try: - for response in it: - for row in response.result.rows: - yield tuple(proto3_encoding.make_row(row, convs)) - except Exception: - logging.exception('gRPC low-level error') - raise - - return row_generator(), fields - - def get_srv_keyspace(self, name): - try: - request = vtgate_pb2.GetSrvKeyspaceRequest( - keyspace=name, - ) - response = self.stub.GetSrvKeyspace(request, self.timeout) - return self.keyspace_from_response(name, response) - - except (grpc.RpcError, vtgate_utils.VitessError) as e: - raise _convert_exception(e, keyspace=name) - - @vtgate_utils.exponential_backoff_retry((dbexceptions.ThrottledError, - dbexceptions.TransientError)) - def update_stream( - self, keyspace_name, tablet_type, - timestamp=None, event=None, - shard=None, key_range=None, - effective_caller_id=None, - **kwargs): - - try: - request = self.update_stream_request( - keyspace_name, shard, key_range, tablet_type, - timestamp, event, effective_caller_id) - it = self.stub.UpdateStream(request, self.timeout) - except (grpc.RpcError, vtgate_utils.VitessError) as e: - raise _convert_exception( - e, 'UpdateStream', - keyspace=keyspace_name, tablet_type=tablet_type) - - def row_generator(): - try: - for response in it: - yield (response.event, response.resume_timestamp) - except Exception as e: - raise _convert_exception(e) - - return row_generator() - - @vtgate_utils.exponential_backoff_retry((dbexceptions.ThrottledError, - dbexceptions.TransientError)) - def message_stream( - self, keyspace, name, - shard=None, key_range=None, - effective_caller_id=None, - **kwargs): - - try: - request = self.message_stream_request( - keyspace, shard, key_range, - name, effective_caller_id) - it = self.stub.MessageStream(request, self.timeout) - first_response = it.next() - except (grpc.RpcError, vtgate_utils.VitessError) as e: - raise _convert_exception( - e, 'MessageStream', name=name, - keyspace=keyspace) - - fields, convs = self.build_conversions(first_response.result.fields) - - def row_generator(): - try: - for response in it: - for row in response.result.rows: - yield tuple(proto3_encoding.make_row(row, convs)) - except Exception: - logging.exception('gRPC low-level error') - raise - - return row_generator(), fields - - @vtgate_utils.exponential_backoff_retry((dbexceptions.ThrottledError, - dbexceptions.TransientError)) - def message_ack( - self, - name, ids, - keyspace=None, effective_caller_id=None, - **kwargs): - - try: - request = self.message_ack_request( - keyspace, name, ids, effective_caller_id) - response = self.stub.MessageAck(request, self.timeout) - except (grpc.RpcError, vtgate_utils.VitessError) as e: - raise _convert_exception( - e, 'MessageAck', name=name, ids=ids, - keyspace=keyspace) - - return response.result.rows_affected - - def get_warnings(self): - if self.session: - return self.session.warnings - return [] - -def _convert_exception(exc, *args, **kwargs): - """This parses the protocol exceptions to the api interface exceptions. - - This also logs the exception and increments the appropriate error counters. - - Args: - exc: raw protocol exception. - *args: additional args from the raising site. - **kwargs: additional keyword args from the raising site. - They will be converted into a single string, and added as an extra - arg to the exception. - - Returns: - Api interface exceptions - dbexceptions with new args. - """ - kwargs_as_str = vtgate_utils.convert_exception_kwargs(kwargs) - exc.args += args - if kwargs_as_str: - exc.args += kwargs_as_str, - new_args = (type(exc).__name__,) + exc.args - if isinstance(exc, vtgate_utils.VitessError): - new_exc = exc.convert_to_dbexception(new_args) - elif isinstance(exc, grpc.RpcError): - # Most RpcErrors should also implement Call so we can get details. - if isinstance(exc, grpc.Call): - code = exc.code() - details = exc.details() - if code == grpc.StatusCode.DEADLINE_EXCEEDED: - new_exc = dbexceptions.TimeoutError(new_args) - elif code == grpc.StatusCode.UNAVAILABLE: - if vtgate_utils.throttler_err_re.search(details): - return dbexceptions.ThrottledError(new_args) - else: - return dbexceptions.TransientError(details, new_args) - elif code == grpc.StatusCode.ALREADY_EXISTS: - new_exc = _prune_integrity_error(details, new_args) - elif code == grpc.StatusCode.FAILED_PRECONDITION: - return dbexceptions.QueryNotServed(details, new_args) - elif code == grpc.StatusCode.INVALID_ARGUMENT: - return dbexceptions.ProgrammingError(details, new_args) - else: - # Other RPC error that we don't specifically handle. - new_exc = dbexceptions.DatabaseError(new_args + (code, details)) - else: - # RPC error that doesn't provide code and details. - # Don't let gRPC-specific errors leak beyond this package. - new_exc = dbexceptions.DatabaseError(new_args + (exc,)) - else: - new_exc = exc - vtgate_utils.log_exception( - new_exc, - keyspace=kwargs.get('keyspace'), tablet_type=kwargs.get('tablet_type')) - return new_exc - - -def _prune_integrity_error(msg, exc_args): - """Prunes an integrity error message and returns an IntegrityError.""" - parts = _errno_pattern.split(msg) - pruned_msg = msg[:msg.find(parts[2])] - exc_args = (pruned_msg,) + tuple(exc_args[1:]) - return dbexceptions.IntegrityError(exc_args) - -vtgate_client.register_conn_class('grpc', GRPCVTGateConnection) diff --git a/py/vtdb/keyrange.py b/py/vtdb/keyrange.py deleted file mode 100644 index cc63f22573d..00000000000 --- a/py/vtdb/keyrange.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Contains the definition of the KeyRange object. -""" - -from vtdb import dbexceptions -from vtdb import keyrange_constants - - -class KeyRange(object): - """Definition of KeyRange object. - - Vitess uses range based sharding. KeyRange denotes the range - for the sharding key. - - Attributes: - Start: start of the keyrange. - End: end of the keyrange. - """ - - Start = None - End = None - - def __init__(self, kr): - if isinstance(kr, str): - if kr == keyrange_constants.NON_PARTIAL_KEYRANGE: - self.Start = keyrange_constants.MIN_KEY - self.End = keyrange_constants.MAX_KEY - return - else: - kr = kr.split('-') - if not isinstance(kr, tuple) and not isinstance(kr, list) or len(kr) != 2: - raise dbexceptions.ProgrammingError( - 'keyrange must be a list or tuple or a '-' separated str %s' % kr) - self.Start = kr[0].strip().decode('hex') - self.End = kr[1].strip().decode('hex') - - def __str__(self): - if (self.Start == keyrange_constants.MIN_KEY and - self.End == keyrange_constants.MAX_KEY): - return keyrange_constants.NON_PARTIAL_KEYRANGE - return '%s-%s' % (self.Start.encode('hex'), self.End.encode('hex')) - - def __repr__(self): - if (self.Start == keyrange_constants.MIN_KEY and - self.End == keyrange_constants.MAX_KEY): - return 'KeyRange(%r)' % keyrange_constants.NON_PARTIAL_KEYRANGE - return 'KeyRange(%r-%r)' % (self.Start, self.End) diff --git a/py/vtdb/keyrange_constants.py b/py/vtdb/keyrange_constants.py deleted file mode 100644 index 59913d91916..00000000000 --- a/py/vtdb/keyrange_constants.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Constants related to keyspaces and shard names.""" - -# Keyrange that spans the entire space, used -# for unsharded database. -NON_PARTIAL_KEYRANGE = '' -MIN_KEY = '' -MAX_KEY = '' - -KIT_UNSET = '' -KIT_UINT64 = 'uint64' -KIT_BYTES = 'bytes' - -# Map from proto3 integer keyspace id type to lower case string version -PROTO3_KIT_TO_STRING = { - 0: KIT_UNSET, - 1: KIT_UINT64, - 2: KIT_BYTES, -} - -# Map from proto3 integer tablet type value to the lower case string -# (Eventually we will use the proto3 version of this) -PROTO3_TABLET_TYPE_TO_STRING = { - 0: 'unknown', - 1: 'master', - 2: 'replica', - 3: 'rdonly', - 4: 'spare', - 5: 'experimental', - 6: 'backup', - 7: 'restore', - 8: 'worker', - 9: 'scrap', -} diff --git a/py/vtdb/keyspace.py b/py/vtdb/keyspace.py deleted file mode 100644 index 69754129aaa..00000000000 --- a/py/vtdb/keyspace.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""A Vitess keyspace represents a sharded MySQL database.""" - -import struct - -from vtdb import keyrange_constants - - -pack_keyspace_id = struct.Struct('!Q').pack - - -class Keyspace(object): - """Represent the SrvKeyspace object from the toposerver. - - Provide functions to extract sharding information from the same. - """ - - # load this object from a SrvKeyspace object generated by vt - def __init__(self, name, data): - self.name = name - self.partitions = data.get('Partitions', {}) - self.sharding_col_name = data.get('ShardingColumnName', '') - self.sharding_col_type = data.get( - 'ShardingColumnType', keyrange_constants.KIT_UNSET) - self.served_from = data.get('ServedFrom', None) - - def get_shards(self, db_type): - if not db_type: - raise ValueError('db_type is not set') - try: - return self.partitions[db_type]['ShardReferences'] - except KeyError: - return [] - - def get_shard_count(self, db_type): - if not db_type: - raise ValueError('db_type is not set') - shards = self.get_shards(db_type) - return len(shards) - - def get_shard_names(self, db_type): - if not db_type: - raise ValueError('db_type is not set') - shards = self.get_shards(db_type) - return [shard['Name'] for shard in shards] - - def keyspace_id_to_shard_name_for_db_type(self, keyspace_id, db_type): - """Finds the shard for a keyspace_id. - - WARNING: this only works for KIT_UINT64 keyspace ids. - - Args: - keyspace_id: A uint64 keyspace_id. - db_type: Str tablet type (master, rdonly, or replica). - - Returns: - Shard name. - - Raises: - ValueError: On invalid keyspace_id. - """ - if not keyspace_id: - raise ValueError('keyspace_id is not set') - if not db_type: - raise ValueError('db_type is not set') - # Pack this into big-endian and do a byte-wise comparison. - pkid = pack_keyspace_id(keyspace_id) - shards = self.get_shards(db_type) - for shard in shards: - if 'KeyRange' not in shard or not shard['KeyRange']: - # this keyrange is covering the full space - return shard['Name'] - if _shard_contain_kid(pkid, - shard['KeyRange']['Start'], - shard['KeyRange']['End']): - return shard['Name'] - raise ValueError( - 'cannot find shard for keyspace_id %s in %s' % (keyspace_id, shards)) - - -def _shard_contain_kid(pkid, start, end): - return start <= pkid and (end == keyrange_constants.MAX_KEY or pkid < end) diff --git a/py/vtdb/prefer_vtroot_imports.py b/py/vtdb/prefer_vtroot_imports.py deleted file mode 100644 index 6456a7e8233..00000000000 --- a/py/vtdb/prefer_vtroot_imports.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Reorder sys.path to put $VTROOT/dist/* paths before others. - -This ensures libraries installed there will be preferred over other versions -that may be present at the system level. We do this at runtime because -regardless of what we set in the PYTHONPATH environment variable, the system -dist-packages folder gets prepended sometimes. - -To use this, just import it before importing packages that you want to make -sure are overridden from $VTROOT/dist. - -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import -""" - -import os -import sys - - -def _prefer_vtroot_imports(): - """Reorder sys.path to put $VTROOT/dist before others.""" - - vtroot = os.environ.get('VTROOT') - if not vtroot: - # VTROOT is not set. Don't try anything. - return - dist = os.path.join(vtroot, 'dist') - - dist_paths = [] - other_paths = [] - - for path in sys.path: - if path: - if path.startswith(dist): - dist_paths.append(path) - else: - other_paths.append(path) - - sys.path = [''] + dist_paths + other_paths - -_prefer_vtroot_imports() diff --git a/py/vtdb/proto3_encoding.py b/py/vtdb/proto3_encoding.py deleted file mode 100644 index 7bf53ccc6ca..00000000000 --- a/py/vtdb/proto3_encoding.py +++ /dev/null @@ -1,758 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Utility module for proto3-python conversions. - -This module defines the conversion functions from proto3 to python, -and utility methods / classes to convert requests / responses for any -python connector using the proto3 requests / responses. -""" - -import datetime -from decimal import Decimal - -from vtproto import query_pb2 -from vtproto import topodata_pb2 -from vtproto import vtgate_pb2 -from vtproto import vtrpc_pb2 - -from vtdb import field_types -from vtdb import keyrange_constants -from vtdb import keyspace -from vtdb import times -from vtdb import vtgate_utils - -# conversions is a map of type to the conversion function that needs -# to be used to convert the incoming array of bytes to the -# corresponding native python type. -# If a type doesn't need conversion, it's not in the map. -conversions = { - query_pb2.INT8: int, - query_pb2.UINT8: int, - query_pb2.INT16: int, - query_pb2.UINT16: int, - query_pb2.INT24: int, - query_pb2.UINT24: int, - query_pb2.INT32: int, - query_pb2.UINT32: int, - query_pb2.INT64: int, - query_pb2.UINT64: long, - query_pb2.FLOAT32: float, - query_pb2.FLOAT64: float, - query_pb2.TIMESTAMP: times.DateTimeOrNone, - query_pb2.DATE: times.DateOrNone, - query_pb2.TIME: times.TimeDeltaOrNone, - query_pb2.DATETIME: times.DateTimeOrNone, - query_pb2.YEAR: int, - query_pb2.DECIMAL: Decimal, - # query_pb2.TEXT: no conversion - # query_pb2.BLOB: no conversion - # query_pb2.VARCHAR: no conversion - # query_pb2.VARBINARY: no conversion - # query_pb2.CHAR: no conversion - # query_pb2.BINARY: no conversion - # query_pb2.BIT: no conversion - # query_pb2.ENUM: no conversion - # query_pb2.SET: no conversion - # query_pb2.TUPLE: no conversion -} - -# legacy_code_to_code_map maps legacy error codes -# to the new code that matches grpc's canonical error codes. -legacy_code_to_code_map = { - vtrpc_pb2.SUCCESS_LEGACY: vtrpc_pb2.OK, - vtrpc_pb2.CANCELLED_LEGACY: vtrpc_pb2.CANCELED, - vtrpc_pb2.UNKNOWN_ERROR_LEGACY: vtrpc_pb2.UNKNOWN, - vtrpc_pb2.BAD_INPUT_LEGACY: vtrpc_pb2.INVALID_ARGUMENT, - vtrpc_pb2.DEADLINE_EXCEEDED_LEGACY: vtrpc_pb2.DEADLINE_EXCEEDED, - vtrpc_pb2.INTEGRITY_ERROR_LEGACY: vtrpc_pb2.ALREADY_EXISTS, - vtrpc_pb2.PERMISSION_DENIED_LEGACY: vtrpc_pb2.PERMISSION_DENIED, - vtrpc_pb2.RESOURCE_EXHAUSTED_LEGACY: vtrpc_pb2.RESOURCE_EXHAUSTED, - vtrpc_pb2.QUERY_NOT_SERVED_LEGACY: vtrpc_pb2.FAILED_PRECONDITION, - vtrpc_pb2.NOT_IN_TX_LEGACY: vtrpc_pb2.ABORTED, - vtrpc_pb2.INTERNAL_ERROR_LEGACY: vtrpc_pb2.INTERNAL, - vtrpc_pb2.TRANSIENT_ERROR_LEGACY: vtrpc_pb2.UNAVAILABLE, - vtrpc_pb2.UNAUTHENTICATED_LEGACY: vtrpc_pb2.UNAUTHENTICATED, -} - - -INT_UPPERBOUND_PLUS_ONE = 1<<63 - - -def make_row(row, convs): - """Builds a python native row from proto3 row, and conversion array. - - Args: - row: proto3 query.Row object - convs: conversion function array - - Returns: - an array of converted rows. - """ - converted_row = [] - offset = 0 - for i, l in enumerate(row.lengths): - if l == -1: - converted_row.append(None) - elif convs[i]: - converted_row.append(convs[i](row.values[offset:offset+l])) - offset += l - else: - converted_row.append(row.values[offset:offset+l]) - offset += l - return converted_row - - -def build_value(v): - """Build a proto value from any valid input.""" - val = query_pb2.Value() - convert_value(v, val) - return val - - -def convert_value(value, proto_value, allow_lists=False): - """Convert a variable from python type to proto type+value. - - Args: - value: the python value. - proto_value: the proto3 object, needs a type and value field. - allow_lists: allows the use of python lists. - """ - if isinstance(value, bool): - proto_value.type = query_pb2.INT64 - proto_value.value = str(int(value)) - elif isinstance(value, int): - proto_value.type = query_pb2.INT64 - proto_value.value = str(value) - elif isinstance(value, long): - if value < INT_UPPERBOUND_PLUS_ONE: - proto_value.type = query_pb2.INT64 - else: - proto_value.type = query_pb2.UINT64 - proto_value.value = str(value) - elif isinstance(value, float): - proto_value.type = query_pb2.FLOAT64 - proto_value.value = str(value) - elif hasattr(value, '__sql_literal__'): - proto_value.type = query_pb2.VARBINARY - proto_value.value = str(value.__sql_literal__()) - elif isinstance(value, datetime.datetime): - proto_value.type = query_pb2.VARBINARY - proto_value.value = times.DateTimeToString(value) - elif isinstance(value, datetime.date): - proto_value.type = query_pb2.VARBINARY - proto_value.value = times.DateToString(value) - elif isinstance(value, str): - proto_value.type = query_pb2.VARBINARY - proto_value.value = value - elif isinstance(value, field_types.NoneType): - proto_value.type = query_pb2.NULL_TYPE - elif allow_lists and isinstance(value, (set, tuple, list)): - # this only works for bind variables, not for entities. - proto_value.type = query_pb2.TUPLE - for v in list(value): - proto_v = proto_value.values.add() - convert_value(v, proto_v) - else: - proto_value.type = query_pb2.VARBINARY - proto_value.value = str(value) - - -def convert_bind_vars(bind_variables, request_bind_variables): - """Convert binding variables to proto3. - - Args: - bind_variables: a map of strings to python native types. - request_bind_variables: the proto3 object to add bind variables to. - """ - if not bind_variables: - return - for key, val in bind_variables.iteritems(): - convert_value(val, request_bind_variables[key], allow_lists=True) - - -def convert_stream_event_statement(statement): - """Converts encoded rows inside a StreamEvent.Statement to native types. - - Args: - statement: the StreamEvent.Statement object. - - Returns: - fields: array of names for the primary key columns. - rows: array of tuples for each primary key value. - """ - fields = [] - rows = [] - if statement.primary_key_fields: - convs = [] - for field in statement.primary_key_fields: - fields.append(field.name) - convs.append(conversions.get(field.type)) - - for r in statement.primary_key_values: - row = tuple(make_row(r, convs)) - rows.append(row) - - return fields, rows - - -class Proto3Connection(object): - """A base class for proto3-based python connectors. - - It assumes the derived object will contain a proto3 self.session object. - """ - - def __init__(self): - self._effective_caller_id = None - self.event_token = None - self.fresher = None - - def _add_caller_id(self, request, caller_id): - """Adds the vtgate_client.CallerID to the proto3 request, if any. - - Args: - request: proto3 request (any of the {,stream,batch} execute queries). - caller_id: vtgate_client.CallerID object. - """ - if caller_id: - if caller_id.principal: - request.caller_id.principal = caller_id.principal - if caller_id.component: - request.caller_id.component = caller_id.component - if caller_id.subcomponent: - request.caller_id.subcomponent = caller_id.subcomponent - - def _add_session(self, request): - """Adds self.session to the request, if any. - - Args: - request: the proto3 request to add session to. - """ - if self.session: - request.session.CopyFrom(self.session) - - def update_session(self, response): - """Updates the current session from the response, if it has one. - - Args: - response: a proto3 response that may contain a session object. - """ - if response.HasField('session') and response.session: - self.session = response.session - - def _convert_entity_ids(self, entity_keyspace_ids, request_eki): - """Convert external entity id map to ProtoBuffer. - - Args: - entity_keyspace_ids: map of entity_keyspace_id. - request_eki: destination proto3 list. - """ - for xid, kid in entity_keyspace_ids.iteritems(): - eid = request_eki.add() - eid.keyspace_id = kid - convert_value(xid, eid, allow_lists=False) - - def _add_key_ranges(self, request, key_ranges): - """Adds the provided keyrange.KeyRange objects to the proto3 request. - - Args: - request: proto3 request. - key_ranges: list of keyrange.KeyRange objects. - """ - for kr in key_ranges: - encoded_kr = request.key_ranges.add() - encoded_kr.start = kr.Start - encoded_kr.end = kr.End - - def _extract_rpc_error(self, exec_method, error): - """Raises a VitessError for a proto3 vtrpc.RPCError structure, if set. - - Args: - exec_method: name of the method to use in VitessError. - error: vtrpc.RPCError structure. - - Raises: - vtgate_utils.VitessError: if an error was set. - """ - if error.code: - raise vtgate_utils.VitessError(exec_method, error.code, error.message) - elif error.legacy_code: - raise vtgate_utils.VitessError( - exec_method, - legacy_code_to_code_map[error.legacy_code], - error.message) - - def build_conversions(self, qr_fields): - """Builds an array of fields and conversions from a result fields. - - Args: - qr_fields: query result fields - - Returns: - fields: array of fields - convs: conversions to use. - """ - fields = [] - convs = [] - for field in qr_fields: - fields.append((field.name, field.type)) - convs.append(conversions.get(field.type)) - return fields, convs - - def _get_rowset_from_query_result(self, query_result): - """Builds a python rowset from proto3 response. - - Args: - query_result: proto3 query.QueryResult object. - - Returns: - Array of rows - Number of modified rows - Last insert ID - Fields array of (name, type) tuples. - """ - if not query_result: - return [], 0, 0, [] - fields, convs = self.build_conversions(query_result.fields) - results = [] - for row in query_result.rows: - results.append(tuple(make_row(row, convs))) - rowcount = query_result.rows_affected - lastrowid = query_result.insert_id - return results, rowcount, lastrowid, fields - - def begin_request(self, effective_caller_id, single_db): - """Builds a vtgate_pb2.BeginRequest object. - - Also remembers the effective caller id for next call to - commit_request or rollback_request. - - Args: - effective_caller_id: optional vtgate_client.CallerID. - single_db: True if single db transaction is needed. - - Returns: - A vtgate_pb2.BeginRequest object. - """ - request = vtgate_pb2.BeginRequest() - request.single_db = single_db - self._add_caller_id(request, effective_caller_id) - self._effective_caller_id = effective_caller_id - return request - - def commit_request(self, twopc): - """Builds a vtgate_pb2.CommitRequest object. - - Uses the effective_caller_id saved from begin_request(). - It will also clear the saved effective_caller_id. - - Args: - twopc: perform 2-phase commit. - - Returns: - A vtgate_pb2.CommitRequest object. - """ - request = vtgate_pb2.CommitRequest() - request.atomic = twopc - self._add_caller_id(request, self._effective_caller_id) - self._add_session(request) - self._effective_caller_id = None - return request - - def rollback_request(self): - """Builds a vtgate_pb2.RollbackRequest object. - - Uses the effective_caller_id saved from begin_request(). - It will also clear the saved effective_caller_id. - - Returns: - A vtgate_pb2.RollbackRequest object. - """ - request = vtgate_pb2.RollbackRequest() - self._add_caller_id(request, self._effective_caller_id) - self._add_session(request) - self._effective_caller_id = None - return request - - def execute_request_and_name(self, sql, bind_variables, tablet_type, - keyspace_name, - shards, - keyspace_ids, - key_ranges, - entity_column_name, entity_keyspace_id_map, - not_in_transaction, effective_caller_id, - include_event_token, compare_event_token): - """Builds the right vtgate_pb2 Request and method for an _execute call. - - Args: - sql: the query to run. Bind Variables in there should be in python format. - bind_variables: python map of bind variables. - tablet_type: string tablet type. - keyspace_name: keyspace to apply the query to. - shards: array of strings representing the shards. - keyspace_ids: array of keyspace ids. - key_ranges: array of keyrange.KeyRange objects. - entity_column_name: the column name to vary. - entity_keyspace_id_map: map of external id to keyspace id. - not_in_transaction: do not create a transaction to a new shard. - effective_caller_id: optional vtgate_client.CallerID. - include_event_token: boolean on whether to ask for event token. - compare_event_token: set the result extras fresher based on this token. - - Returns: - A vtgate_pb2.XXXRequest object. - A dict that contains the routing parameters. - The name of the remote method called. - """ - - if shards is not None: - request = vtgate_pb2.ExecuteShardsRequest(keyspace=keyspace_name) - request.shards.extend(shards) - routing_kwargs = {'shards': shards} - method_name = 'ExecuteShards' - - elif keyspace_ids is not None: - request = vtgate_pb2.ExecuteKeyspaceIdsRequest(keyspace=keyspace_name) - request.keyspace_ids.extend(keyspace_ids) - routing_kwargs = {'keyspace_ids': keyspace_ids} - method_name = 'ExecuteKeyspaceIds' - - elif key_ranges is not None: - request = vtgate_pb2.ExecuteKeyRangesRequest(keyspace=keyspace_name) - self._add_key_ranges(request, key_ranges) - routing_kwargs = {'keyranges': key_ranges} - method_name = 'ExecuteKeyRanges' - - elif entity_keyspace_id_map is not None: - request = vtgate_pb2.ExecuteEntityIdsRequest( - keyspace=keyspace_name, - entity_column_name=entity_column_name) - self._convert_entity_ids(entity_keyspace_id_map, - request.entity_keyspace_ids) - routing_kwargs = {'entity_keyspace_id_map': entity_keyspace_id_map, - 'entity_column_name': entity_column_name} - method_name = 'ExecuteEntityIds' - - else: - request = vtgate_pb2.ExecuteRequest() - if keyspace_name: - request.keyspace_shard = keyspace_name - routing_kwargs = {} - method_name = 'Execute' - - request.query.sql = sql - convert_bind_vars(bind_variables, request.query.bind_variables) - request.tablet_type = topodata_pb2.TabletType.Value(tablet_type.upper()) - request.not_in_transaction = not_in_transaction - self._add_caller_id(request, effective_caller_id) - self._add_session(request) - if include_event_token: - request.options.include_event_token = True - if compare_event_token: - request.options.compare_event_token.CopyFrom(compare_event_token) - self.event_token = None - self.fresher = None - return request, routing_kwargs, method_name - - def process_execute_response(self, exec_method, response): - """Processes an Execute* response, and returns the rowset. - - Args: - exec_method: name of the method called. - response: proto3 response returned. - Returns: - results: list of rows. - rowcount: how many rows were affected. - lastrowid: auto-increment value for the last row inserted. - fields: describes the field names and types. - """ - self.update_session(response) - self._extract_rpc_error(exec_method, response.error) - if response.result.extras: - self.event_token = response.result.extras.event_token - self.fresher = response.result.extras.fresher - return self._get_rowset_from_query_result(response.result) - - def execute_batch_request_and_name(self, sql_list, bind_variables_list, - keyspace_list, - keyspace_ids_list, shards_list, - tablet_type, as_transaction, - effective_caller_id): - """Builds the right vtgate_pb2 ExecuteBatch query. - - Args: - sql_list: list os SQL statements. - bind_variables_list: list of bind variables. - keyspace_list: list of keyspaces. - keyspace_ids_list: list of list of keyspace_ids. - shards_list: list of shards. - tablet_type: target tablet type. - as_transaction: execute all statements in a single transaction. - effective_caller_id: optional vtgate_client.CallerID. - - Returns: - A proper vtgate_pb2.ExecuteBatchXXX object. - The name of the remote method to call. - """ - if keyspace_ids_list and keyspace_ids_list[0]: - request = vtgate_pb2.ExecuteBatchKeyspaceIdsRequest() - for sql, bind_variables, keyspace_name, keyspace_ids in zip( - sql_list, bind_variables_list, keyspace_list, keyspace_ids_list): - query = request.queries.add(keyspace=keyspace_name) - query.query.sql = sql - convert_bind_vars(bind_variables, query.query.bind_variables) - query.keyspace_ids.extend(keyspace_ids) - method_name = 'ExecuteBatchKeyspaceIds' - else: - request = vtgate_pb2.ExecuteBatchShardsRequest() - for sql, bind_variables, keyspace_name, shards in zip( - sql_list, bind_variables_list, keyspace_list, shards_list): - query = request.queries.add(keyspace=keyspace_name) - query.query.sql = sql - convert_bind_vars(bind_variables, query.query.bind_variables) - query.shards.extend(shards) - method_name = 'ExecuteBatchShards' - - request.tablet_type = topodata_pb2.TabletType.Value(tablet_type.upper()) - request.as_transaction = as_transaction - self._add_caller_id(request, effective_caller_id) - self._add_session(request) - return request, method_name - - def process_execute_batch_response(self, exec_method, response): - """Processes an ExecuteBatch* response, and returns the rowsets. - - Args: - exec_method: name of the method called. - response: proto3 response returned. - - Returns: - rowsets: array of tuples as would be returned by an execute method. - """ - self.update_session(response) - self._extract_rpc_error(exec_method, response.error) - - rowsets = [] - for result in response.results: - rowset = self._get_rowset_from_query_result(result) - rowsets.append(rowset) - return rowsets - - def update_stream_request(self, - keyspace_name, - shard, - key_range, - tablet_type, - timestamp, - event, - effective_caller_id): - """Builds the right vtgate_pb2 UpdateStreamRequest. - - Args: - keyspace_name: keyspace to apply the query to. - shard: shard to ask for. - key_range: keyrange.KeyRange object. - tablet_type: string tablet type. - timestamp: when to start the stream from. - event: alternate way to describe where to start the stream from. - effective_caller_id: optional vtgate_client.CallerID. - - Returns: - A vtgate_pb2.UpdateStreamRequest object. - """ - request = vtgate_pb2.UpdateStreamRequest(keyspace=keyspace_name, - tablet_type=tablet_type, - shard=shard) - if timestamp: - request.timestamp = timestamp - if event: - if event.timestamp: - request.event.timestamp = event.timestamp - if event.shard: - request.event.shard = event.shard - if event.position: - request.event.position = event.position - if key_range: - request.key_range.start = key_range.Start - request.key_range.end = key_range.End - self._add_caller_id(request, effective_caller_id) - return request - - def message_stream_request(self, - keyspace_name, - shard, - key_range, - name, - effective_caller_id): - """Builds the right vtgate_pb2 MessageStreamRequest. - - Args: - keyspace_name: keyspace to apply the query to. - shard: shard to ask for. - key_range: keyrange.KeyRange object. - name: message table name. - effective_caller_id: optional vtgate_client.CallerID. - - Returns: - A vtgate_pb2.MessageStreamRequest object. - """ - request = vtgate_pb2.MessageStreamRequest(keyspace=keyspace_name, - name=name, - shard=shard) - if key_range: - request.key_range.start = key_range.Start - request.key_range.end = key_range.End - self._add_caller_id(request, effective_caller_id) - return request - - def message_ack_request(self, - keyspace_name, - name, - ids, - effective_caller_id): - """Builds the right vtgate_pb2 MessageAckRequest. - - Args: - keyspace_name: keyspace to apply the query to. - name: message table name. - ids: list of message ids. - effective_caller_id: optional vtgate_client.CallerID. - - Returns: - A vtgate_pb2.MessageAckRequest object. - """ - vals = [] - for v in ids: - vals.append(build_value(v)) - request = vtgate_pb2.MessageAckRequest(keyspace=keyspace_name, - name=name, - ids=vals) - self._add_caller_id(request, effective_caller_id) - return request - - def stream_execute_request_and_name(self, sql, bind_variables, tablet_type, - keyspace_name, - shards, - keyspace_ids, - key_ranges, - effective_caller_id): - """Builds the right vtgate_pb2 Request and method for a _stream_execute. - - Args: - sql: the query to run. Bind Variables in there should be in python format. - bind_variables: python map of bind variables. - tablet_type: string tablet type. - keyspace_name: keyspace to apply the query to. - shards: array of strings representing the shards. - keyspace_ids: array of keyspace ids. - key_ranges: array of keyrange.KeyRange objects. - effective_caller_id: optional vtgate_client.CallerID. - - Returns: - A vtgate_pb2.StreamExecuteXXXXRequest object. - A dict that contains the routing parameters. - The name of the remote method called. - """ - - if shards is not None: - request = vtgate_pb2.StreamExecuteShardsRequest(keyspace=keyspace_name) - request.shards.extend(shards) - routing_kwargs = {'shards': shards} - method_name = 'StreamExecuteShards' - - elif keyspace_ids is not None: - request = vtgate_pb2.StreamExecuteKeyspaceIdsRequest( - keyspace=keyspace_name) - request.keyspace_ids.extend(keyspace_ids) - routing_kwargs = {'keyspace_ids': keyspace_ids} - method_name = 'StreamExecuteKeyspaceIds' - - elif key_ranges is not None: - request = vtgate_pb2.StreamExecuteKeyRangesRequest(keyspace=keyspace_name) - self._add_key_ranges(request, key_ranges) - routing_kwargs = {'keyranges': key_ranges} - method_name = 'StreamExecuteKeyRanges' - - else: - request = vtgate_pb2.StreamExecuteRequest() - if keyspace_name: - request.keyspace_shard = keyspace_name - routing_kwargs = {} - method_name = 'StreamExecute' - - request.query.sql = sql - convert_bind_vars(bind_variables, request.query.bind_variables) - request.tablet_type = topodata_pb2.TabletType.Value(tablet_type.upper()) - self._add_caller_id(request, effective_caller_id) - return request, routing_kwargs, method_name - - def srv_keyspace_proto3_to_old(self, sk): - """Converts a proto3 SrvKeyspace. - - Args: - sk: proto3 SrvKeyspace. - - Returns: - dict with converted values. - """ - result = {} - - if sk.sharding_column_name: - result['ShardingColumnName'] = sk.sharding_column_name - - if sk.sharding_column_type == 1: - result['ShardingColumnType'] = keyrange_constants.KIT_UINT64 - elif sk.sharding_column_type == 2: - result['ShardingColumnType'] = keyrange_constants.KIT_BYTES - - sfmap = {} - for sf in sk.served_from: - tt = keyrange_constants.PROTO3_TABLET_TYPE_TO_STRING[sf.tablet_type] - sfmap[tt] = sf.keyspace - result['ServedFrom'] = sfmap - - if sk.partitions: - pmap = {} - for p in sk.partitions: - tt = keyrange_constants.PROTO3_TABLET_TYPE_TO_STRING[p.served_type] - srs = [] - for sr in p.shard_references: - result_sr = { - 'Name': sr.name, - } - if sr.key_range: - result_sr['KeyRange'] = { - 'Start': sr.key_range.start, - 'End': sr.key_range.end, - } - srs.append(result_sr) - pmap[tt] = { - 'ShardReferences': srs, - } - result['Partitions'] = pmap - - return result - - def keyspace_from_response(self, name, response): - """Builds a Keyspace object from the response of a GetSrvKeyspace call. - - Args: - name: keyspace name. - response: a GetSrvKeyspaceResponse object. - - Returns: - A keyspace.Keyspace object. - """ - return keyspace.Keyspace( - name, - self.srv_keyspace_proto3_to_old(response.srv_keyspace)) diff --git a/py/vtdb/times.py b/py/vtdb/times.py deleted file mode 100755 index a45abe6c018..00000000000 --- a/py/vtdb/times.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# times module -# -# This module provides some Date and Time interface for vtdb -# -# Use Python datetime module to handle date and time columns. - -from datetime import date -from datetime import datetime -from datetime import time -from datetime import timedelta -from math import modf -from time import localtime - -# FIXME(msolomon) what are these aliasesf for? -Date = date -Time = time -TimeDelta = timedelta -Timestamp = datetime - -DateTimeDeltaType = timedelta -DateTimeType = datetime - - -# Convert UNIX ticks into a date instance. -def DateFromTicks(ticks): - return date(*localtime(ticks)[:3]) - - -# Convert UNIX ticks into a time instance. -def TimeFromTicks(ticks): - return time(*localtime(ticks)[3:6]) - - -# Convert UNIX ticks into a datetime instance. -def TimestampFromTicks(ticks): - return datetime(*localtime(ticks)[:6]) - - -def DateTimeOrNone(s): - if ' ' in s: - sep = ' ' - elif 'T' in s: - sep = 'T' - else: - return DateOrNone(s) - - try: - d, t = s.split(sep, 1) - return datetime(*[int(x) for x in d.split('-')+t.split(':')]) - except Exception: - return DateOrNone(s) - - -def TimeDeltaOrNone(s): - try: - h, m, s = s.split(':') - td = timedelta( - hours=int(h), minutes=int(m), seconds=int(float(s)), - microseconds=int(modf(float(s))[0]*1000000)) - if h < 0: - return -td - else: - return td - except Exception: - return None - - -def TimeOrNone(s): - try: - h, m, s = s.split(':') - return time( - hour=int(h), minute=int(m), second=int(float(s)), - microsecond=int(modf(float(s))[0]*1000000)) - except Exception: - return None - - -def DateOrNone(s): - try: - return date(*[int(x) for x in s.split('-', 2)]) - except Exception: - return None - - -def DateToString(d): - return d.isoformat() - - -def DateTimeToString(dt): - return dt.isoformat(' ') diff --git a/py/vtdb/topology.py b/py/vtdb/topology.py deleted file mode 100644 index d0acbb8db08..00000000000 --- a/py/vtdb/topology.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Deprecated module that holds keyspace / sharding methods.""" - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# DEPRECATED module, just one hardcoded function left, so vtrouting.py -# is not changed yet. Will be cleaned up soon. - -from vtdb import keyrange_constants - - -def get_sharding_col(keyspace_name): - _ = keyspace_name - return 'keyspace_id', keyrange_constants.KIT_UINT64 diff --git a/py/vtdb/vtdb_logger.py b/py/vtdb/vtdb_logger.py deleted file mode 100644 index b203af52381..00000000000 --- a/py/vtdb/vtdb_logger.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This module defines the VtdbLogger interface, to report suspicious events. -""" - -import logging - - -class VtdbLogger(object): - """VtdbLogger's methods are called whenever something worth noting happens. - - The default behavior of the class is to log using the logging module. - Registering a new implementation allows the client code to report the - conditions to any custom reporting mechanism. - - We use this in the following cases: - - error reporting (an exception happened) - - performance logging (calls to other services took that long) - """ - # - # vtclient callbacks - # - - # Integrity Error is called when mysql throws an IntegrityError on a query. - # This is thrown by both vtclient and vtgatev2. - def integrity_error(self, e): - logging.warning('integrity_error: %s', e) - - # vtclient_exception is called when a FatalError is raised by - # vtclient (that error is sent back to the application, the retries - # happen at a lower level). e can be one of - # dbexceptions.{RetryError, FatalError, TxPoolFull} - # or a more generic dbexceptions.OperationalError - def vtclient_exception(self, keyspace_name, shard_name, db_type, e): - logging.warning('vtclient_exception for %s.%s.%s: %s', keyspace_name, - shard_name, db_type, e) - - # - # vtgatev2 callbacks - # - - def log_private_data(self, private_data): - logging.info('Additional exception data %s', private_data) - - def warning(self, msg, *args, **kwargs): - logging.warning(msg, *args, **kwargs) - - -# registration mechanism for VtdbLogger -_vtdb_logger = VtdbLogger() - - -def register_vtdb_logger(logger): - global _vtdb_logger - _vtdb_logger = logger - - -def get_logger(): - return _vtdb_logger diff --git a/py/vtdb/vtgate_client.py b/py/vtdb/vtgate_client.py deleted file mode 100644 index 6c946d24ed4..00000000000 --- a/py/vtdb/vtgate_client.py +++ /dev/null @@ -1,469 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This module defines the vtgate client interface. -""" - -from vtdb import vtgate_cursor - -# mapping from protocol to python class. -vtgate_client_conn_classes = dict() - - -def register_conn_class(protocol, c): - """Used by implementations to register themselves. - - Args: - protocol: short string to document the protocol. - c: class to register. - """ - vtgate_client_conn_classes[protocol] = c - - -def connect(protocol, vtgate_addrs, timeout, *pargs, **kargs): - """connect will return a dialed VTGateClient connection to a vtgate server. - - FIXME(alainjobart): exceptions raised are not consistent. - - Args: - protocol: the registered protocol to use. - vtgate_addrs: single or multiple vtgate server addresses to connect to. - Which address is actually used depends on the load balancing - capabilities of the underlying protocol used. - timeout: connection timeout, float in seconds. - *pargs: passed to the registered protocol __init__ method. - **kargs: passed to the registered protocol __init__ method. - - Returns: - A dialed VTGateClient. - - Raises: - dbexceptions.OperationalError: if we are unable to establish the connection - (for instance, no available instance). - dbexceptions.Error: if vtgate_addrs have the wrong type. - ValueError: If the protocol is unknown, or vtgate_addrs are malformed. - """ - if protocol not in vtgate_client_conn_classes: - raise ValueError('Unknown vtgate_client protocol', protocol) - conn = vtgate_client_conn_classes[protocol]( - vtgate_addrs, timeout, *pargs, **kargs) - conn.dial() - return conn - - -# Note: Eventually, this object will be replaced by a proto3 CallerID -# object when all vitess customers have migrated to proto3. -class CallerID(object): - """An object with principal, component, and subcomponent fields.""" - - def __init__(self, principal=None, component=None, subcomponent=None): - self.principal = principal - self.component = component - self.subcomponent = subcomponent - - def __repr__(self): - return repr((self.principal, self.component, self.subcomponent)) - - -class VTGateClient(object): - """VTGateClient is the interface for the vtgate client implementations. - - All implementations must implement all these methods. - If something goes wrong with the connection, this object will be thrown out. - - FIXME(alainjobart) transactional state (the Session object) is currently - maintained by this object. It should be maintained by the cursor, and just - returned / passed in with every method that makes sense. - """ - - def __init__(self, addr, timeout, *pargs, **kwargs): - """Initialize a vtgate connection. - - Args: - addr: server address. Can be protocol dependent. - timeout: connection timeout (float, in seconds). - *pargs: passed to super constructor. - **kwargs: passed to super constructor. - """ - super(VTGateClient, self).__init__(*pargs, **kwargs) - self.addr = addr - self.timeout = timeout - # self.session is used by vtgate_utils.exponential_backoff_retry. - # implementations should use it to store the session object. - self.session = None - - def dial(self): - """Dial to the server. - - If successful, call close() to close the connection. - """ - raise NotImplementedError('Child class needs to implement this') - - def close(self): - """Close the connection. - - This object may be re-used again by calling dial(). - """ - raise NotImplementedError('Child class needs to implement this') - - def is_closed(self): - """Checks the connection status. - - Returns: - True if this connection is closed. - """ - raise NotImplementedError('Child class needs to implement this') - - def cursor(self, *pargs, **kwargs): - """Creates a cursor instance associated with this connection. - - Args: - *pargs: passed to the cursor constructor. - **kwargs: passed to the cursor constructor. - - Returns: - A new cursor to use on this connection. - """ - cursorclass = kwargs.pop('cursorclass', None) or vtgate_cursor.VTGateCursor - return cursorclass(self, *pargs, **kwargs) - - def begin(self, effective_caller_id=None, single_db=False): - """Starts a transaction. - - FIXME(alainjobart): instead of storing the Session as member variable, - should return it and let the cursor store it. - - Args: - effective_caller_id: CallerID Object. - single_db: True if single db transaction is needed. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.IntegrityError: integrity of an index would not be - guaranteed with this statement. - dbexceptions.DatabaseError: generic database error. - dbexceptions.ProgrammingError: the supplied statements are invalid, - this is probably an error in the code. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def commit(self, twopc=False): - """Commits the current transaction. - - FIXME(alainjobart): should take the session in. - - Args: - twopc: perform 2-phase commit. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.IntegrityError: integrity of an index would not be - guaranteed with this statement. - dbexceptions.DatabaseError: generic database error. - dbexceptions.ProgrammingError: the supplied statements are invalid, - this is probably an error in the code. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def rollback(self): - """Rolls the current transaction back. - - FIXME(alainjobart): should take the session in. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.IntegrityError: integrity of an index would not be - guaranteed with this statement. - dbexceptions.DatabaseError: generic database error. - dbexceptions.ProgrammingError: the supplied statements are invalid, - this is probably an error in the code. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def _execute(self, sql, bind_variables, tablet_type, - keyspace_name=None, - shards=None, - keyspace_ids=None, - keyranges=None, - entity_keyspace_id_map=None, entity_column_name=None, - not_in_transaction=False, effective_caller_id=None, - include_event_token=False, compare_event_token=None, - **kwargs): - """Executes the given sql. - - FIXME(alainjobart): should take the session in. - - Args: - sql: query to execute. - bind_variables: map of bind variables for the query. - tablet_type: the (string) version of the tablet type. - keyspace_name: if specified, the keyspace to send the query to. - Required if any of the routing parameters is used. - Not required only if using vtgate v3 API. - shards: if specified, use this list of shards names to route the query. - Incompatible with keyspace_ids, keyranges, entity_keyspace_id_map, - entity_column_name. - Requires keyspace. - keyspace_ids: if specified, use this list to route the query. - Incompatible with shards, keyranges, entity_keyspace_id_map, - entity_column_name. - Requires keyspace. - keyranges: if specified, use this list to route the query. - Incompatible with shards, keyspace_ids, entity_keyspace_id_map, - entity_column_name. - Requires keyspace. - entity_keyspace_id_map: if specified, use this map to route the query. - Incompatible with shards, keyspace_ids, keyranges. - Requires keyspace, entity_column_name. - entity_column_name: if specified, use this value to route the query. - Incompatible with shards, keyspace_ids, keyranges. - Requires keyspace, entity_keyspace_id_map. - not_in_transaction: force this execute to be outside the current - transaction, if any. - effective_caller_id: CallerID object. - include_event_token: if true, the flag will be sent to vtgate. - The member variable event_token will be set with the result. - compare_event_token: set the result extras fresher based on this token. - The member variable fresher will be set with the result. - **kwargs: implementation specific parameters. - - Returns: - results: list of rows. - rowcount: how many rows were affected. - lastrowid: auto-increment value for the last row inserted. - fields: describes the field names and types. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.IntegrityError: integrity of an index would not be - guaranteed with this statement. - dbexceptions.DatabaseError: generic database error. - dbexceptions.ProgrammingError: the supplied statements are invalid, - this is probably an error in the code. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def _execute_batch( - self, sql_list, bind_variables_list, tablet_type, - keyspace_list=None, shards_list=None, keyspace_ids_list=None, - as_transaction=False, effective_caller_id=None, **kwargs): - """Executes a list of sql queries. - - These follow the same routing rules as _execute. - - FIXME(alainjobart): should take the session in. - - Args: - sql_list: list of SQL queries to execute. - bind_variables_list: bind variables to associated with each query. - tablet_type: the (string) version of the tablet type. - keyspace_list: if specified, the keyspaces to send the queries to. - Required if any of the routing parameters is used. - Not required only if using vtgate v3 API. - shards_list: if specified, use this list of shards names (per sql query) - to route each query. - Incompatible with keyspace_ids_list. - Requires keyspace_list. - keyspace_ids_list: if specified, use this list of keyspace_ids (per sql - query) to route each query. - Incompatible with shards_list. - Requires keyspace_list. - as_transaction: starts and commits a transaction around the statements. - effective_caller_id: CallerID object. - **kwargs: implementation specific parameters. - - Returns: - results: an array of (results, rowcount, lastrowid, fields) tuples, - one for each query. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.IntegrityError: integrity of an index would not be - guaranteed with this statement. - dbexceptions.DatabaseError: generic database error. - dbexceptions.ProgrammingError: the supplied statements are invalid, - this is probably an error in the code. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def _stream_execute( - self, sql, bind_variables, tablet_type, keyspace=None, shards=None, - keyspace_ids=None, keyranges=None, effective_caller_id=None, **kwargs): - """Executes the given sql, in streaming mode. - - FIXME(alainjobart): the return values are weird (historical reasons) - and unused for now. We should use them, and not store the current - streaming status in the connection, but in the cursor. - - Args: - sql: query to execute. - bind_variables: map of bind variables for the query. - tablet_type: the (string) version of the tablet type. - keyspace: if specified, the keyspace to send the query to. - Required if any of the routing parameters is used. - Not required only if using vtgate v3 API. - shards: if specified, use this list of shards names to route the query. - Incompatible with keyspace_ids, keyranges. - Requires keyspace. - keyspace_ids: if specified, use this list to route the query. - Incompatible with shards, keyranges. - Requires keyspace. - keyranges: if specified, use this list to route the query. - Incompatible with shards, keyspace_ids. - Requires keyspace. - effective_caller_id: CallerID object. - **kwargs: implementation specific parameters. - - Returns: - A (row generator, fields) pair. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.IntegrityError: integrity of an index would not be - guaranteed with this statement. - dbexceptions.DatabaseError: generic database error. - dbexceptions.ProgrammingError: the supplied statements are invalid, - this is probably an error in the code. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def get_srv_keyspace(self, keyspace): - """Returns a SrvKeyspace object. - - Args: - keyspace: name of the keyspace to retrieve. - - Returns: - srv_keyspace: a keyspace.Keyspace object. - - Raises: - TBD - """ - raise NotImplementedError('Child class needs to implement this') - - def update_stream(self, - keyspace_name, tablet_type, - timestamp=None, event=None, - shard=None, key_range=None, - effective_caller_id=None, - **kwargs): - """Asks for an update stream. - - Args: - keyspace_name: the keyspace to get updates from. - tablet_type: the (proto3) version of the tablet type. - timestamp: when to start the stream from. Unused if event is set, - and event.shard matches the only shard we stream from. - event: query_pb2.EventToken to start streaming from. Used only if its - shard field matches the single shard we're going to stream from. - shard: the shard name to listen for. - Incompatible with key_range. - key_range: the key range to listen for. - Incompatible with shard. - effective_caller_id: CallerID object. - **kwargs: implementation specific parameters. - - Returns: - A row generator that returns tuples (event, resume timestamp). - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.DatabaseError: generic database error. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def message_stream(self, - keyspace, name, - shard=None, key_range=None, - effective_caller_id=None, - **kwargs): - """Asks for a message stream. - - Args: - keyspace: the keyspace of the message table. - name: the name of the message table. - shard: the shard name to listen for. - Incompatible with key_range. - key_range: the key range to listen for. - Incompatible with shard. - effective_caller_id: CallerID object. - **kwargs: implementation specific parameters. - - Returns: - A (row generator, fields) pair. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.DatabaseError: generic database error. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def message_ack(self, - name, ids, - keyspace=None, effective_caller_id=None, - **kwargs): - """Acks a list of messages. - - Args: - name: the name of the message table. - ids: list of message ids to ack. - keyspace: the keyspace of the message table. - Not required if table can be auto-resolved. - effective_caller_id: CallerID object. - **kwargs: implementation specific parameters. - - Returns: - The number of rows acked. - - Raises: - dbexceptions.TimeoutError: for connection timeout. - dbexceptions.TransientError: the server is overloaded, and this query - is asked to back off. - dbexceptions.DatabaseError: generic database error. - dbexceptions.FatalError: this query should not be retried. - """ - raise NotImplementedError('Child class needs to implement this') - - def get_warnings(self): - """Get warnings from the previous query - - Returns: - The list of warnings. - - """ - raise NotImplementedError('Child class needs to implement this') - diff --git a/py/vtdb/vtgate_client_testsuite.py b/py/vtdb/vtgate_client_testsuite.py deleted file mode 100755 index eada034fc94..00000000000 --- a/py/vtdb/vtgate_client_testsuite.py +++ /dev/null @@ -1,670 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This is the test suite for a vtgate_client implementation. -""" - -import struct - -from google.protobuf import text_format - -from vtdb import dbexceptions -from vtdb import keyrange -from vtdb import keyrange_constants -from vtdb import vtgate_client -from vtdb import vtgate_cursor -from vtproto import query_pb2 -from vtproto import topodata_pb2 - - -class TestPythonClientBase(object): - """Base class for Python client tests.""" - - # conn must be opened as a vtgate_client connection. - # This is not done by this class. - conn = None - - # A packed keyspace_id from the middle of the full keyrange. - KEYSPACE_ID_0X80 = struct.Struct('!Q').pack(0x80 << 56) - - def _open_v3_cursor(self): - return self.conn.cursor(keyspace=None, tablet_type='master') - - def _open_shards_cursor(self): - return self.conn.cursor( - tablet_type='master', keyspace='keyspace', shards=['-80']) - - def _open_keyspace_ids_cursor(self): - return self.conn.cursor( - tablet_type='master', keyspace='keyspace', - keyspace_ids=[self.KEYSPACE_ID_0X80]) - - def _open_keyranges_cursor(self): - kr = keyrange.KeyRange(keyrange_constants.NON_PARTIAL_KEYRANGE) - return self.conn.cursor( - tablet_type='master', keyspace='keyspace', keyranges=[kr]) - - def _open_batch_cursor(self): - return self.conn.cursor(tablet_type='master', keyspace=None) - - def _open_stream_v3_cursor(self): - return self.conn.cursor( - tablet_type='master', keyspace=None, - cursorclass=vtgate_cursor.StreamVTGateCursor) - - def _open_stream_shards_cursor(self): - return self.conn.cursor( - tablet_type='master', keyspace='keyspace', shards=['-80'], - cursorclass=vtgate_cursor.StreamVTGateCursor) - - def _open_stream_keyspace_ids_cursor(self): - return self.conn.cursor( - tablet_type='master', keyspace='keyspace', - keyspace_ids=[self.KEYSPACE_ID_0X80], - cursorclass=vtgate_cursor.StreamVTGateCursor) - - def _open_stream_keyranges_cursor(self): - kr = keyrange.KeyRange(keyrange_constants.NON_PARTIAL_KEYRANGE) - return self.conn.cursor( - tablet_type='master', keyspace='keyspace', keyranges=[kr], - cursorclass=vtgate_cursor.StreamVTGateCursor) - - -class TestErrors(TestPythonClientBase): - """Test cases to verify that the Python client can handle errors correctly.""" - - def _verify_exception_for_execute(self, query, exception): - """Verify that we raise a specific exception for all Execute calls. - - Args: - query: query string to use for execute calls. - exception: exception class that we expect the execute call to raise. - """ - - # Execute test - cursor = self._open_v3_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # ExecuteShards test - cursor = self._open_shards_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # ExecuteKeyspaceIds test - cursor = self._open_keyspace_ids_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # ExecuteKeyRanges test - cursor = self._open_keyranges_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # ExecuteEntityIds test - cursor = self.conn.cursor(tablet_type='master', keyspace='keyspace') - with self.assertRaises(exception): - cursor.execute( - query, {}, - entity_keyspace_id_map={1: self.KEYSPACE_ID_0X80}, - entity_column_name='user_id') - cursor.close() - - # ExecuteBatchKeyspaceIds test - cursor = self._open_batch_cursor() - with self.assertRaises(exception): - cursor.executemany( - sql=None, - params_list=[ - dict( - sql=query, - bind_variables={}, - keyspace='keyspace', - keyspace_ids=[self.KEYSPACE_ID_0X80])]) - cursor.close() - - # ExecuteBatchShards test - cursor = self._open_batch_cursor() - with self.assertRaises(exception): - cursor.executemany( - sql=None, - params_list=[ - dict( - sql=query, - bind_variables={}, - keyspace='keyspace', - shards=['0'])]) - cursor.close() - - def _verify_exception_for_stream_execute(self, query, exception): - """Verify that we raise a specific exception for all StreamExecute calls. - - Args: - query: query string to use for StreamExecute calls. - exception: exception class that we expect StreamExecute to raise. - """ - # StreamExecute test - cursor = self._open_stream_v3_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # StreamExecuteShards test - cursor = self._open_stream_shards_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # StreamExecuteKeyspaceIds test - cursor = self._open_stream_keyspace_ids_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # StreamExecuteKeyRanges test - cursor = self._open_stream_keyranges_cursor() - with self.assertRaises(exception): - cursor.execute(query, {}) - cursor.close() - - # UpdateStream test - with self.assertRaises(exception): - for _, _ in self.conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - shard=query): - pass - - def test_partial_integrity_errors(self): - """Raise an IntegrityError when Execute returns a partial error.""" - # Special query that makes vtgateclienttest return a partial error. - self._verify_exception_for_execute( - 'partialerror://integrity error', - dbexceptions.IntegrityError) - - def _verify_exception_for_all_execute_methods(self, query, exception): - self._verify_exception_for_execute(query, exception) - self._verify_exception_for_stream_execute(query, exception) - - def test_integrity_error(self): - """Test we raise dbexceptions.IntegrityError.""" - self._verify_exception_for_all_execute_methods( - 'error://integrity error', - dbexceptions.IntegrityError) - - def test_transient_error(self): - """Test we raise dbexceptions.TransientError for Execute calls.""" - # Special query that makes vtgateclienttest return a TransientError. - self._verify_exception_for_all_execute_methods( - 'error://transient error', - dbexceptions.TransientError) - - def test_throttled_error(self): - """Test we raise dbexceptions.ThrottledError.""" - # Special query that makes vtgateclienttest return a ThrottledError. - self._verify_exception_for_all_execute_methods( - 'error://throttled error', - dbexceptions.ThrottledError) - - def test_query_not_served_error(self): - """Test we raise dbexceptions.QueryNotServed.""" - # Special query that makes vtgateclienttest return QueryNotServed. - self._verify_exception_for_all_execute_methods( - 'error://query not served', - dbexceptions.QueryNotServed) - - def test_programming_error(self): - """Test we raise dbexceptions.ProgrammingError.""" - # Special query that makes vtgateclienttest return a ProgrammingError. - self._verify_exception_for_all_execute_methods( - 'error://bad input', - dbexceptions.ProgrammingError) - - def test_error(self): - """Test a regular server error raises the right exception.""" - error_request = 'error://unknown error' - error_caller_id = vtgate_client.CallerID(principal=error_request) - - # Begin test - with self.assertRaisesRegexp(dbexceptions.DatabaseError, 'forced error'): - self.conn.begin(error_caller_id) - - # Commit test - with self.assertRaisesRegexp(dbexceptions.DatabaseError, 'forced error'): - self.conn.begin(error_caller_id) - - # Rollback test - with self.assertRaisesRegexp(dbexceptions.DatabaseError, 'forced error'): - self.conn.begin(error_caller_id) - - # GetSrvKeyspace test - with self.assertRaisesRegexp(dbexceptions.DatabaseError, 'forced error'): - self.conn.get_srv_keyspace(error_request) - - -class TestSuccess(TestPythonClientBase): - """Success test cases for the Python client.""" - - def test_success_get_srv_keyspace(self): - """Test we get the right results from get_srv_keyspace. - - We only test the successful cases. - """ - - # big has one big shard - big = self.conn.get_srv_keyspace('big') - self.assertEquals(big.name, 'big') - self.assertEquals(big.sharding_col_name, 'sharding_column_name') - self.assertEquals(big.sharding_col_type, keyrange_constants.KIT_UINT64) - self.assertEquals(big.served_from, {'master': 'other_keyspace'}) - self.assertEquals(big.get_shards('replica'), - [{'Name': 'shard0', - 'KeyRange': { - 'Start': '\x40\x00\x00\x00\x00\x00\x00\x00', - 'End': '\x80\x00\x00\x00\x00\x00\x00\x00', - }}]) - self.assertEquals(big.get_shard_count('replica'), 1) - self.assertEquals(big.get_shard_count('rdonly'), 0) - self.assertEquals(big.get_shard_names('replica'), ['shard0']) - self.assertEquals(big.keyspace_id_to_shard_name_for_db_type( - 0x6000000000000000, 'replica'), 'shard0') - with self.assertRaises(ValueError): - big.keyspace_id_to_shard_name_for_db_type(0x2000000000000000, 'replica') - - # small has no shards - small = self.conn.get_srv_keyspace('small') - self.assertEquals(small.name, 'small') - self.assertEquals(small.sharding_col_name, '') - self.assertEquals(small.sharding_col_type, keyrange_constants.KIT_UNSET) - self.assertEquals(small.served_from, {}) - self.assertEquals(small.get_shards('replica'), []) - self.assertEquals(small.get_shard_count('replica'), 0) - with self.assertRaises(ValueError): - small.keyspace_id_to_shard_name_for_db_type(0x6000000000000000, 'replica') - - -class TestCallerId(TestPythonClientBase): - """Caller ID test cases for the Python client.""" - - def test_effective_caller_id(self): - """Test that the passed in effective_caller_id is parsed correctly. - - Pass a special sql query that sends the expected - effective_caller_id through different vtgate interfaces. Make sure - the good_effective_caller_id works, and the - bad_effective_caller_id raises a DatabaseError. - """ - - # Special query that makes vtgateclienttest match effective_caller_id. - effective_caller_id_test_query = ( - 'callerid://{"principal":"pr", "component":"co", "subcomponent":"su"}') - good_effective_caller_id = vtgate_client.CallerID( - principal='pr', component='co', subcomponent='su') - bad_effective_caller_id = vtgate_client.CallerID( - principal='pr_wrong', component='co_wrong', subcomponent='su_wrong') - - def check_good_and_bad_effective_caller_ids(cursor, cursor_execute_method): - cursor.set_effective_caller_id(good_effective_caller_id) - with self.assertRaises(dbexceptions.DatabaseError) as cm: - cursor_execute_method(cursor) - self.assertIn('SUCCESS:', str(cm.exception)) - - cursor.set_effective_caller_id(bad_effective_caller_id) - with self.assertRaises(dbexceptions.DatabaseError) as cm: - cursor_execute_method(cursor) - self.assertNotIn('SUCCESS:', str(cm.exception)) - - # test Execute - def cursor_execute_method(cursor): - cursor.execute(effective_caller_id_test_query, {}) - - check_good_and_bad_effective_caller_ids( - self._open_v3_cursor(), cursor_execute_method) - - # test ExecuteShards - def cursor_execute_shards_method(cursor): - cursor.execute(effective_caller_id_test_query, {}) - - check_good_and_bad_effective_caller_ids( - self._open_shards_cursor(), cursor_execute_shards_method) - - # test ExecuteKeyspaceIds - def cursor_execute_keyspace_ids_method(cursor): - cursor.execute(effective_caller_id_test_query, {}) - - check_good_and_bad_effective_caller_ids( - self._open_keyspace_ids_cursor(), cursor_execute_keyspace_ids_method) - - # test ExecuteKeyRanges - def cursor_execute_key_ranges_method(cursor): - cursor.execute(effective_caller_id_test_query, {}) - - check_good_and_bad_effective_caller_ids( - self._open_keyranges_cursor(), cursor_execute_key_ranges_method) - - # test ExecuteEntityIds - def cursor_execute_entity_ids_method(cursor): - cursor.execute( - effective_caller_id_test_query, {}, - entity_keyspace_id_map={1: self.KEYSPACE_ID_0X80}, - entity_column_name='user_id') - - check_good_and_bad_effective_caller_ids( - self.conn.cursor(tablet_type='master', keyspace='keyspace'), - cursor_execute_entity_ids_method) - - # test ExecuteBatchKeyspaceIds - def cursor_execute_batch_keyspace_ids_method(cursor): - cursor.executemany( - sql=None, - params_list=[dict( - sql=effective_caller_id_test_query, bind_variables={}, - keyspace='keyspace', - keyspace_ids=[self.KEYSPACE_ID_0X80])]) - - check_good_and_bad_effective_caller_ids( - self._open_batch_cursor(), cursor_execute_batch_keyspace_ids_method) - - # test ExecuteBatchShards - def cursor_execute_batch_shard_method(cursor): - cursor.executemany( - sql=None, - params_list=[dict( - sql=effective_caller_id_test_query, bind_variables={}, - keyspace='keyspace', - shards=['0'])]) - - check_good_and_bad_effective_caller_ids( - self._open_batch_cursor(), cursor_execute_batch_shard_method) - - # test StreamExecute - def cursor_stream_execute_v3_method(cursor): - cursor.execute(sql=effective_caller_id_test_query, bind_variables={}) - - check_good_and_bad_effective_caller_ids( - self._open_stream_v3_cursor(), - cursor_stream_execute_v3_method) - - # test StreamExecuteShards - def cursor_stream_execute_shards_method(cursor): - cursor.execute(sql=effective_caller_id_test_query, bind_variables={}) - - check_good_and_bad_effective_caller_ids( - self._open_stream_shards_cursor(), - cursor_stream_execute_shards_method) - - # test StreamExecuteKeyspaceIds - def cursor_stream_execute_keyspace_ids_method(cursor): - cursor.execute(sql=effective_caller_id_test_query, bind_variables={}) - - check_good_and_bad_effective_caller_ids( - self._open_stream_keyspace_ids_cursor(), - cursor_stream_execute_keyspace_ids_method) - - # test StreamExecuteKeyRanges - def cursor_stream_execute_keyranges_method(cursor): - cursor.execute(sql=effective_caller_id_test_query, bind_variables={}) - - check_good_and_bad_effective_caller_ids( - self._open_stream_keyranges_cursor(), - cursor_stream_execute_keyranges_method) - - -class TestEcho(TestPythonClientBase): - """Send queries to the server, check the returned result matches.""" - - echo_prefix = 'echo://' - - query = ( - u'test query with bind variables: :int :float :bytes, unicode: ' - u'\u6211\u80fd\u541e\u4e0b\u73bb\u7483\u800c\u4e0d\u50b7\u8eab\u9ad4' - ).encode('utf-8') - query_echo = ( - u'test query with bind variables: :int :float :bytes, unicode: ' - u'\u6211\u80fd\u541e\u4e0b\u73bb\u7483\u800c\u4e0d\u50b7\u8eab\u9ad4' - ).encode('utf-8') - keyspace = 'test_keyspace' - - shards = ['-80', '80-'] - shards_echo = '[-80 80-]' - - keyspace_ids = ['\x01\x02\x03\x04', '\x05\x06\x07\x08'] - keyspace_ids_echo = '[[1 2 3 4] [5 6 7 8]]' - - # FIXME(alainjobart) using a map for the entities makes it impossible to - # guarantee the order of the entities in the query. It is really an API - # problem here? For this test, however, I'll just use a single value for now - entity_keyspace_ids = { - 123: '\x01\x02\x03', - # 2.0: '\x04\x05\x06', - # '\x01\x02\x03': '\x07\x08\x09', - } -# entity_keyspace_ids_echo = ('[type:INT64 value:"123" ' -# 'keyspace_id:"\\001\\002\\003" ' -# 'type:FLOAT64 value:"2" ' -# 'keyspace_id:"\\004\\005\\006" ' -# 'type:VARBINARY value:"\\001\\002\\003" ' -# 'keyspace_id:"\\007\\010\\t" ]') - entity_keyspace_ids_echo = ('[type:INT64 value:"123" ' - 'keyspace_id:"\\001\\002\\003" ]') - - key_ranges = [keyrange.KeyRange('01020304-05060708')] - key_ranges_echo = '[start:"\\001\\002\\003\\004" end:"\\005\\006\\007\\010" ]' - - tablet_type = 'replica' - tablet_type_echo = 'REPLICA' - - bind_variables = { - 'int': 123, - 'float': 2.1, - 'bytes': '\x01\x02\x03', - 'bool': True, - } - bind_variables_echo = ('map[bool:type:INT64 value:"1" ' - 'bytes:type:VARBINARY value:"\\001\\002\\003" ' - 'float:type:FLOAT64 value:"2.1" ' - 'int:type:INT64 value:"123" ]') - - caller_id = vtgate_client.CallerID( - principal='test_principal', - component='test_component', - subcomponent='test_subcomponent') - caller_id_echo = ('principal:"test_principal" component:"test_component"' - ' subcomponent:"test_subcomponent" ') - - event_token = query_pb2.EventToken(timestamp=123, - shard=shards[0], - position='test_pos') - options_echo = ('include_event_token:true compare_event_token:' - ' ') - - session_echo = ('autocommit:true target_string:"@replica" ' - 'options: > ') - - def test_echo_execute(self): - """This test calls the echo method.""" - - # Execute - cursor = self.conn.cursor(tablet_type=self.tablet_type, keyspace=None) - cursor.set_effective_caller_id(self.caller_id) - cursor.execute(self.echo_prefix+self.query, self.bind_variables, - include_event_token=True, - compare_event_token=self.event_token) - self._check_echo(cursor, { - 'callerId': self.caller_id_echo, - # FIXME(alainjobart) change this to query_echo once v3 understand binds - 'query': self.echo_prefix+self.query, - 'bindVars': self.bind_variables_echo, - 'session': self.session_echo, - }) - cursor.close() - - # ExecuteShards - cursor = self.conn.cursor( - tablet_type=self.tablet_type, keyspace=self.keyspace, - shards=self.shards) - cursor.set_effective_caller_id(self.caller_id) - cursor.execute(self.echo_prefix+self.query, self.bind_variables, - include_event_token=True, - compare_event_token=self.event_token) - self._check_echo(cursor, { - 'callerId': self.caller_id_echo, - 'query': self.echo_prefix+self.query_echo, - 'keyspace': self.keyspace, - 'shards': self.shards_echo, - 'bindVars': self.bind_variables_echo, - 'tabletType': self.tablet_type_echo, - 'options': self.options_echo, - 'fresher': True, - 'eventToken': self.event_token, - }) - cursor.close() - - # ExecuteKeyspaceIds - cursor = self.conn.cursor( - tablet_type=self.tablet_type, keyspace=self.keyspace, - keyspace_ids=self.keyspace_ids) - cursor.set_effective_caller_id(self.caller_id) - cursor.execute(self.echo_prefix+self.query, self.bind_variables, - include_event_token=True, - compare_event_token=self.event_token) - self._check_echo(cursor, { - 'callerId': self.caller_id_echo, - 'query': self.echo_prefix+self.query_echo, - 'keyspace': self.keyspace, - 'keyspaceIds': self.keyspace_ids_echo, - 'bindVars': self.bind_variables_echo, - 'tabletType': self.tablet_type_echo, - 'options': self.options_echo, - 'fresher': True, - 'eventToken': self.event_token, - }) - cursor.close() - - # ExecuteKeyRanges - cursor = self.conn.cursor( - tablet_type=self.tablet_type, keyspace=self.keyspace, - keyranges=self.key_ranges) - cursor.set_effective_caller_id(self.caller_id) - cursor.execute(self.echo_prefix+self.query, self.bind_variables, - include_event_token=True, - compare_event_token=self.event_token) - self._check_echo(cursor, { - 'callerId': self.caller_id_echo, - 'query': self.echo_prefix+self.query_echo, - 'keyspace': self.keyspace, - 'keyRanges': self.key_ranges_echo, - 'bindVars': self.bind_variables_echo, - 'tabletType': self.tablet_type_echo, - }) - cursor.close() - - # ExecuteEntityIds - cursor = self.conn.cursor( - tablet_type=self.tablet_type, keyspace=self.keyspace) - cursor.set_effective_caller_id(self.caller_id) - cursor.execute(self.echo_prefix+self.query, self.bind_variables, - entity_keyspace_id_map=self.entity_keyspace_ids, - entity_column_name='column1', - include_event_token=True, - compare_event_token=self.event_token) - self._check_echo(cursor, { - 'callerId': self.caller_id_echo, - 'query': self.echo_prefix+self.query_echo, - 'keyspace': self.keyspace, - 'entityColumnName': 'column1', - 'entityIds': self.entity_keyspace_ids_echo, - 'bindVars': self.bind_variables_echo, - 'tabletType': self.tablet_type_echo, - 'options': self.options_echo, - 'fresher': True, - 'eventToken': self.event_token, - }) - cursor.close() - - # ExecuteBatchShards - cursor = self.conn.cursor( - tablet_type=self.tablet_type, keyspace=None, - as_transaction=True) - cursor.set_effective_caller_id(self.caller_id) - cursor.executemany(sql=None, - params_list=[ - dict( - sql=self.echo_prefix+self.query, - bind_variables=self.bind_variables, - keyspace=self.keyspace, - shards=self.shards)]) - self._check_echo(cursor, { - 'callerId': self.caller_id_echo, - 'query': self.echo_prefix+self.query_echo, - 'keyspace': self.keyspace, - 'shards': self.shards_echo, - 'bindVars': self.bind_variables_echo, - 'tabletType': self.tablet_type_echo, - 'asTransaction': 'true', - }) - cursor.close() - - # ExecuteBatchKeyspaceIds - cursor = self.conn.cursor( - tablet_type=self.tablet_type, keyspace=None, - as_transaction=True) - cursor.set_effective_caller_id(self.caller_id) - cursor.executemany(sql=None, - params_list=[ - dict( - sql=self.echo_prefix+self.query, - bind_variables=self.bind_variables, - keyspace=self.keyspace, - keyspace_ids=self.keyspace_ids)]) - self._check_echo(cursor, { - 'callerId': self.caller_id_echo, - 'query': self.echo_prefix+self.query_echo, - 'keyspace': self.keyspace, - 'keyspaceIds': self.keyspace_ids_echo, - 'bindVars': self.bind_variables_echo, - 'tabletType': self.tablet_type_echo, - 'asTransaction': 'true', - }) - cursor.close() - - def _get_echo(self, cursor): - result = {} - data = cursor.fetchall() - for i, (n, _) in enumerate(cursor.description): - result[n] = data[0][i] - return result - - def _check_echo(self, cursor, values): - """_check_echo makes sure the echo result is correct.""" - got = self._get_echo(cursor) - for k, v in values.iteritems(): - if k == 'fresher': - self.assertTrue(self.conn.fresher) - elif k == 'eventToken': - self.assertEqual(text_format.MessageToString(self.conn.event_token), - text_format.MessageToString(v)) - else: - self.assertEqual(got[k], v, 'item %s is different in result: got %s' - ' expected %s' % (k, got[k], v)) - - # Check NULL and empty string. - self.assertEqual(got['null'], None) - self.assertEqual(got['emptyString'], '') diff --git a/py/vtdb/vtgate_cursor.py b/py/vtdb/vtgate_cursor.py deleted file mode 100644 index e19ff97407f..00000000000 --- a/py/vtdb/vtgate_cursor.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""VTGateCursor, and StreamVTGateCursor.""" - -import itertools -import operator -import re - -from vtdb import base_cursor -from vtdb import dbexceptions - -write_sql_pattern = re.compile(r'\s*(insert|update|delete)', re.IGNORECASE) - - -def ascii_lower(string): - """Lower-case, but only in the ASCII range.""" - return string.encode('utf8').lower().decode('utf8') - - -class VTGateCursorMixin(object): - - def connection_list(self): - return [self._conn] - - def is_writable(self): - return self._writable - - -class VTGateCursor(base_cursor.BaseListCursor, VTGateCursorMixin): - """A cursor for execute statements to VTGate. - - Results are stored as a list. - """ - - def __init__( - self, connection, tablet_type, keyspace=None, - shards=None, keyspace_ids=None, keyranges=None, - writable=False, as_transaction=False, single_db=False, - twopc=False): - """Init VTGateCursor. - - Args: - connection: A PEP0249 connection object. - tablet_type: Str tablet_type. - keyspace: Str keyspace or None if batch API will be used. - shards: List of strings. - keyspace_ids: Struct('!Q').packed keyspace IDs. - keyranges: Str keyranges. - writable: True if writable. - as_transaction: True if an executemany call is its own transaction. - single_db: True if single db transaction is needed. - twopc: True if 2-phase commit is needed. - """ - super(VTGateCursor, self).__init__(single_db=single_db, twopc=twopc) - self._conn = connection - self._writable = writable - self.description = None - self.index = None - self.keyspace = keyspace - self.shards = shards - self.keyspace_ids = keyspace_ids - self.keyranges = keyranges - self.lastrowid = None - self.results = None - self.routing = None - self.rowcount = 0 - self.tablet_type = tablet_type - self.as_transaction = as_transaction - self._clear_batch_state() - - # pass kwargs here in case higher level APIs need to push more data through - # for instance, a key value for shard mapping - def execute(self, sql, bind_variables, **kwargs): - """Perform a query, return the number of rows affected.""" - self._clear_list_state() - self._clear_batch_state() - if self._handle_transaction_sql(sql): - return - entity_keyspace_id_map = kwargs.pop('entity_keyspace_id_map', None) - entity_column_name = kwargs.pop('entity_column_name', None) - write_query = bool(write_sql_pattern.match(sql)) - # NOTE: This check may also be done at higher layers but adding it - # here for completion. - if write_query: - if not self.is_writable(): - raise dbexceptions.ProgrammingError('DML on a non-writable cursor', sql) - if entity_keyspace_id_map: - raise dbexceptions.ProgrammingError( - 'entity_keyspace_id_map is not allowed for write queries') - # FIXME(alainjobart): the entity_keyspace_id_map should be in the - # cursor, same as keyspace_ids, shards, keyranges, to avoid this hack. - if entity_keyspace_id_map: - shards = None - keyspace_ids = None - keyranges = None - else: - shards = self.shards - keyspace_ids = self.keyspace_ids - keyranges = self.keyranges - self.results, self.rowcount, self.lastrowid, self.description = ( - self.connection._execute( # pylint: disable=protected-access - sql, - bind_variables, - tablet_type=self.tablet_type, - keyspace_name=self.keyspace, - shards=shards, - keyspace_ids=keyspace_ids, - keyranges=keyranges, - entity_keyspace_id_map=entity_keyspace_id_map, - entity_column_name=entity_column_name, - not_in_transaction=not self.is_writable(), - effective_caller_id=self.effective_caller_id, - **kwargs)) - return self.rowcount - - def fetch_aggregate_function(self, func): - return func(row[0] for row in self.fetchall()) - - def fetch_aggregate(self, order_by_columns, limit): - """Fetch from many shards, sort, then remove sort columns. - - A scatter query may return up to limit rows. Sort all results - manually order them, and return the first rows. - - This is a special-use function. - - Args: - order_by_columns: The ORDER BY clause. Each element is either a - column, [column, 'ASC'], or [column, 'DESC']. - limit: Int limit. - - Returns: - Smallest rows, with up to limit items. First len(order_by_columns) - columns are stripped. - """ - sort_columns = [] - desc_columns = [] - for order_clause in order_by_columns: - if isinstance(order_clause, (tuple, list)): - sort_columns.append(order_clause[0]) - if ascii_lower(order_clause[1]) == 'desc': - desc_columns.append(order_clause[0]) - else: - sort_columns.append(order_clause) - # sort the rows and then trim off the prepended sort columns - - if sort_columns: - sorted_rows = list(sort_row_list_by_columns( - self.fetchall(), sort_columns, desc_columns))[:limit] - else: - sorted_rows = itertools.islice(self.fetchall(), limit) - neutered_rows = [row[len(order_by_columns):] for row in sorted_rows] - return neutered_rows - - def _clear_batch_state(self): - """Clear state that allows traversal to next query's results.""" - self.result_sets = [] - self.result_set_index = None - - def close(self): - super(VTGateCursor, self).close() - self._clear_batch_state() - - def executemany(self, sql, params_list, **kwargs): - """Execute multiple statements in one batch. - - This adds len(params_list) result_sets to self.result_sets. Each - result_set is a (results, rowcount, lastrowid, fields) tuple. - - Each call overwrites the old result_sets. After execution, nextset() - is called to move the fetch state to the start of the first - result set. - - Args: - sql: The sql text, with %(format)s-style tokens. May be None. - params_list: A list of the keyword params that are normally sent - to execute. Either the sql arg or params['sql'] must be defined. - **kwargs: passed as is to connection._execute_batch. - """ - if sql: - sql_list = [sql] * len(params_list) - else: - sql_list = [params.get('sql') for params in params_list] - bind_variables_list = [params['bind_variables'] for params in params_list] - keyspace_list = [params['keyspace'] for params in params_list] - keyspace_ids_list = [params.get('keyspace_ids') for params in params_list] - shards_list = [params.get('shards') for params in params_list] - self._clear_batch_state() - # Find other _execute_batch calls in test code. - self.result_sets = self.connection._execute_batch( # pylint: disable=protected-access - sql_list, bind_variables_list, keyspace_list, keyspace_ids_list, - shards_list, - self.tablet_type, self.as_transaction, self.effective_caller_id, - **kwargs) - self.nextset() - - def nextset(self): - """Move the fetch state to the start of the next result set. - - self.(results, rowcount, lastrowid, description) will be set to - the next result_set, and the fetch-commands will work on this - result set. - - Returns: - True if another result set exists, False if not. - """ - if self.result_set_index is None: - self.result_set_index = 0 - else: - self.result_set_index += 1 - - self._clear_list_state() - if self.result_set_index < len(self.result_sets): - self.results, self.rowcount, self.lastrowid, self.description = ( - self.result_sets[self.result_set_index]) - return True - else: - self._clear_batch_state() - return None - - -class StreamVTGateCursor(base_cursor.BaseStreamCursor, VTGateCursorMixin): - - """A cursor for streaming statements to VTGate. - - Results are returned as a generator. - """ - - def __init__( - self, connection, tablet_type, keyspace=None, - shards=None, keyspace_ids=None, - keyranges=None, writable=False): - super(StreamVTGateCursor, self).__init__() - self._conn = connection - self._writable = writable - self.keyspace = keyspace - self.shards = shards - self.keyspace_ids = keyspace_ids - self.keyranges = keyranges - self.routing = None - self.tablet_type = tablet_type - - def is_writable(self): - return self._writable - - # pass kwargs here in case higher level APIs need to push more data through - # for instance, a key value for shard mapping - def execute(self, sql, bind_variables, **kwargs): - """Start a streaming query.""" - if self._writable: - raise dbexceptions.ProgrammingError('Streaming query cannot be writable') - self._clear_stream_state() - self.generator, self.description = self.connection._stream_execute( # pylint: disable=protected-access - sql, - bind_variables, - tablet_type=self.tablet_type, - keyspace_name=self.keyspace, - shards=self.shards, - keyspace_ids=self.keyspace_ids, - keyranges=self.keyranges, - not_in_transaction=not self.is_writable(), - effective_caller_id=self.effective_caller_id, - **kwargs) - return 0 - - -def sort_row_list_by_columns(row_list, sort_columns=(), desc_columns=()): - """Sort by leading sort columns by stable-sorting in reverse-index order.""" - for column_index, column_name in reversed( - [x for x in enumerate(sort_columns)]): - og = operator.itemgetter(column_index) - if not isinstance(row_list, list): - row_list = sorted( - row_list, key=og, reverse=bool(column_name in desc_columns)) - else: - row_list.sort(key=og, reverse=bool(column_name in desc_columns)) - return row_list diff --git a/py/vtdb/vtgate_utils.py b/py/vtdb/vtgate_utils.py deleted file mode 100644 index be92599578f..00000000000 --- a/py/vtdb/vtgate_utils.py +++ /dev/null @@ -1,225 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Simple utility values, methods, and classes.""" - -import logging -import re -import time - -from vtdb import dbexceptions -from vtdb import vtdb_logger -from vtproto import vtrpc_pb2 - -INITIAL_DELAY_MS = 5 -NUM_RETRIES = 3 -MAX_DELAY_MS = 100 -BACKOFF_MULTIPLIER = 2 - - -# This pattern is used in transient error messages to differentiate -# between a transient error and a throttling error. -throttler_err_re = re.compile( - r'exceeded (.*) quota, rate limiting', re.IGNORECASE) - - -def log_exception(exc, keyspace=None, tablet_type=None): - """This method logs the exception. - - Args: - exc: exception raised by calling code - keyspace: keyspace for the exception - tablet_type: tablet_type for the exception - """ - logger_object = vtdb_logger.get_logger() - - shard_name = None - if isinstance(exc, dbexceptions.IntegrityError): - logger_object.integrity_error(exc) - else: - logger_object.vtclient_exception(keyspace, shard_name, tablet_type, exc) - - -def exponential_backoff_retry( - retry_exceptions, - initial_delay_ms=INITIAL_DELAY_MS, - num_retries=NUM_RETRIES, - backoff_multiplier=BACKOFF_MULTIPLIER, - max_delay_ms=MAX_DELAY_MS): - """Decorator for exponential backoff retry. - - Log and raise exception if unsuccessful. - Do not retry while in a session. - - Args: - retry_exceptions: tuple of exceptions to check. - initial_delay_ms: initial delay between retries in ms. - num_retries: number max number of retries. - backoff_multiplier: multiplier for each retry e.g. 2 will double the - retry delay. - max_delay_ms: upper bound on retry delay. - - Returns: - A decorator method that returns wrapped method. - """ - def decorator(method): - """Returns wrapper that calls method and retries on retry_exceptions.""" - def wrapper(self, *args, **kwargs): - attempt = 0 - delay = initial_delay_ms - - while True: - try: - return method(self, *args, **kwargs) - except retry_exceptions as e: - attempt += 1 - if attempt > num_retries or self.session: - # In this case it is hard to discern keyspace - # and tablet_type from exception. - log_exception(e) - raise e - logging.error( - 'retryable error: %s, retrying in %d ms, attempt %d of %d', e, - delay, attempt, num_retries) - time.sleep(delay/1000.0) - delay *= backoff_multiplier - delay = min(max_delay_ms, delay) - return wrapper - return decorator - - -class VitessError(Exception): - """VitessError is raised by an RPC with a server-side application error. - - VitessErrors have an error code and message. - - The individual protocols are responsible for getting the code and message - from their protocol-specific encoding, and creating this error. - Then this error can be converted to the right dbexception. - """ - - _errno_pattern = re.compile(r'\(errno (\d+)\)') - - def __init__(self, method_name, code, message): - """Initializes a VitessError from a code and message. - - Args: - method_name: RPC method name, as a string, that was called. - code: integer that represents the error code. From vtrpc_pb2.Code. - message: string representation of the error. - """ - self.method_name = method_name - self.code = code - self.message = message - # Make self.args reflect the error components - super(VitessError, self).__init__(message, method_name, code) - - def __str__(self): - """Print the error nicely, converting the proto error enum to its name.""" - return '%s returned %s with message: %s' % ( - self.method_name, vtrpc_pb2.Code.Name(self.code), self.message) - - def convert_to_dbexception(self, args): - """Converts from a VitessError to the appropriate dbexceptions class. - - Args: - args: argument tuple to use to create the new exception. - - Returns: - An exception from dbexceptions. - """ - # FIXME(alainjobart): this is extremely confusing: self.message is only - # used for integrity errors, and nothing else. The other cases - # have to provide the message in the args. - if self.code == vtrpc_pb2.UNAVAILABLE: - if throttler_err_re.search(self.message): - return dbexceptions.ThrottledError(args) - return dbexceptions.TransientError(args) - if self.code == vtrpc_pb2.FAILED_PRECONDITION: - return dbexceptions.QueryNotServed(args) - if self.code == vtrpc_pb2.ALREADY_EXISTS: - # Prune the error message to truncate after the mysql errno, since - # the error message may contain the query string with bind variables. - msg = self.message.lower() - parts = self._errno_pattern.split(msg) - pruned_msg = msg[:msg.find(parts[2])] - new_args = (pruned_msg,) + tuple(args[1:]) - return dbexceptions.IntegrityError(new_args) - if self.code == vtrpc_pb2.INVALID_ARGUMENT: - return dbexceptions.ProgrammingError(args) - return dbexceptions.DatabaseError(args) - - -def unique_join(str_list, delim='|'): - return delim.join(sorted(set(str(item) for item in str_list))) - - -def keyspace_id_prefix(packed_keyspace_id): - """Return the first str byte of packed_keyspace_id if it exists.""" - return '%02x' % ord(packed_keyspace_id[0]) - - -def keyspace_id_prefixes(packed_keyspace_ids): - """Return the first str byte of each packed_keyspace_id if it exists.""" - return unique_join(keyspace_id_prefix(pkid) for pkid in packed_keyspace_ids) - - -def convert_exception_kwarg(key, value): - if value is None: - return key, value - if key in ( - 'entity_column_name', - 'keyspace', - 'num_queries', - 'sql', - 'tablet_type'): - return key, value - elif key == 'entity_keyspace_id_map': - return 'entity_keyspace_ids', keyspace_id_prefixes( - value.values()) - elif key in ( - 'keyspace_ids', - 'merged_keyspace_ids'): - return key, keyspace_id_prefixes(value) - elif key in ( - 'keyranges', - 'keyspaces', - 'sqls'): - return key, unique_join(value) - elif key in ( - 'not_in_transaction', - 'as_transaction'): - return key, str(value) - else: - return key, 'unknown' - - -def convert_exception_kwargs(kwargs): - """Convert kwargs into a readable str. - - Args: - kwargs: A (str: value) dict. - - Returns: - A comma-delimited string of converted, truncated key=value pairs. - All non-None kwargs are included in alphabetical order. - """ - new_kwargs = {} - for key, value in kwargs.iteritems(): - new_key, new_value = convert_exception_kwarg(key, value) - new_kwargs[new_key] = new_value - return ', '.join( - ('%s=%s' % (k, v))[:256] - for (k, v) in sorted(new_kwargs.iteritems()) - if v is not None) diff --git a/py/vtdb/vtrouting.py b/py/vtdb/vtrouting.py deleted file mode 100644 index 3efb74fd51a..00000000000 --- a/py/vtdb/vtrouting.py +++ /dev/null @@ -1,280 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Library for computing the parallization and routing for streaming queries. - -The client computes the keyranges to parallelize over -based on the desired parallization and shard count. -Please note that the shard count maybe greater than equal -to actual shards. It is suggested to keep this value -at the upsharded count in case a resharding event is planned. -These keyranges can then be used to derived the routing -information. The routing information object is supposed to -be opaque to the client but VTGate uses it to route queries. -It is also used to compute the associated where clause to be added -to the query, which is needed to prevent returning duplicate result -from two parallelized queries running over the same shard. - -API usage - - -task_map = vtrouting.create_parallel_task_keyrange_map(num_tasks, shard_count) -keyrange_list = task_map.keyrange_list - -for db_keyrange in keyrange_list: - vt_routing_info = vtrouting.create_vt_routing_info(db_keyrange, 'ruser') - -During query construction - - -where_clause, bind_vars = vt_routing_info.update_where_clause( - where_clause, bind_vars) -""" - -from vtdb import dbexceptions -from vtdb import keyrange -from vtdb import keyrange_constants -from vtdb import topology - - -class TaskKeyrangeMap(object): - """Class for computing keyranges for parallel tasks. - - Attributes: - num_tasks: number of parallel queries. - """ - - def __init__(self, num_tasks): - self.num_tasks = num_tasks - self.keyrange_list = [] - self.compute_kr_list() - - def compute_kr_list(self): - """compute the keyrange list for parallel queries. - """ - kr_chunks = [] - # we only support 256 shards for now - min_key_hex = int('00', base=16) - max_key_hex = int('100', base=16) - kr = min_key_hex - span = (max_key_hex - min_key_hex)/self.num_tasks - kr_chunks.append('') - for i in xrange(self.num_tasks): - kr += span - kr_chunks.append('%.2x' % kr) - kr_chunks[-1] = '' - for i in xrange(len(kr_chunks) - 1): - start = kr_chunks[i] - end = kr_chunks[i+1] - kr = keyrange.KeyRange((start, end,)) - self.keyrange_list.append(str(kr)) - - -class VTRoutingInfo(object): - """Class to encompass the keyrange and compute the where clause with the same. - - Attributes: - db_keyrange: the keyrange to route the query to. - extra_where_clause: where clause with the sharding key for the keyspace. - extra_bind_vars: bind var dictionary with the sharding key for the keyspace. - """ - - def __init__(self, db_keyrange, where_clause, bind_vars): - self.db_keyrange = db_keyrange - self.extra_where_clause = where_clause - self.extra_bind_vars = bind_vars - - def update_where_clause(self, where_clause, bind_vars): - """Updates the existing where clause of the query with the routing info. - - Args: - where_clause: the where_clause of the query being constructed. - bind_vars: bind_vars of the query being constructed. - - Returns: - updated where_clause and bind_vars with the sharding key - of the keyspace and the db_keyrange. - Example - - Assuming sharding key of keyspace 'test_keyspace' is - 'keyspace_id'. - returned_where_clause = where_clause AND keyspace_id <= - returned_bind_vars = {'keyspace_id': } - """ - if self.extra_where_clause: - if where_clause: - where_clause += ' AND ' + self.extra_where_clause - else: - where_clause = self.extra_where_clause - if self.extra_bind_vars: - bind_vars.update(self.extra_bind_vars) - return where_clause, bind_vars - - -def create_parallel_task_keyrange_map(num_tasks, shard_count): - """Compute the task map for a streaming query. - - Args: - num_tasks: desired parallelization of queries. - shard_count: configurable global shard count for keyspace. - This can be greater than actual shards and should be set to - the desired upsharded value before a resharding event. - This is so that the keyrange->shard map for an inflight - query is always unique. - - Returns: - TaskKeyrangeMap object - """ - if num_tasks % shard_count != 0: - raise dbexceptions.ProgrammingError( - 'tasks %d should be multiple of shard_count %d' % (num_tasks, - shard_count)) - return TaskKeyrangeMap(num_tasks) - - -def create_vt_routing_info(key_range, keyspace_name): - """Creates VTRoutingInfo object for the given key_range in keyspace. - - Args: - key_range: keyrange for query routing. - keyspace_name: target keyspace for the query. This is used to derive - sharding column type and column name information for the where clause. - - Returns: - VTRoutingInfo object. - """ - col_name, col_type = topology.get_sharding_col(keyspace_name) - if not col_name or col_type == keyrange_constants.KIT_UNSET: - return VTRoutingInfo(key_range, '', {}) - where_clause, bind_vars = _create_where_clause_for_keyrange(key_range, - col_name, - col_type) - return VTRoutingInfo(key_range, where_clause, bind_vars) - - -def _true_int_kr_value(kr_value): - """This returns the true value of keyrange for comparison with keyspace_id. - - We abbreviate the keyranges for ease of use. - To obtain true value for comparison with keyspace id, - create true hex value for that keyrange by right padding and conversion. - Args: - kr_value: short keyranges as used by vitess. - Returns: - complete hex value of keyrange. - """ - if kr_value == '': - return None - kr_value += (16-len(kr_value))*'0' - if not kr_value.startswith('0x'): - kr_value = '0x' + kr_value - return int(kr_value, base=16) - - -def _create_where_clause_for_keyrange( - key_range, keyspace_col_name='keyspace_id', - keyspace_col_type=keyrange_constants.KIT_UINT64): - """Compute the where clause and bind_vars for a given keyrange. - - Args: - key_range: keyrange for the query. - keyspace_col_name: keyspace column name of keyspace. - keyspace_col_type: keyspace column type of keyspace. - Returns: - where clause for the keyrange. - """ - - if isinstance(key_range, str): - # If the key_range is for unsharded db, there is no - # where clause to add to or bind_vars to add to. - if key_range == keyrange_constants.NON_PARTIAL_KEYRANGE: - return '', {} - key_range = key_range.split('-') - - if (not isinstance(key_range, tuple) and not isinstance(key_range, list) or - len(key_range) != 2): - raise dbexceptions.ProgrammingError( - 'key_range must be list or tuple or \'-\' separated str %s' % key_range) - - if keyspace_col_type == keyrange_constants.KIT_UINT64: - return _create_where_clause_for_int_keyspace(key_range, keyspace_col_name) - elif keyspace_col_type == keyrange_constants.KIT_BYTES: - return _create_where_clause_for_str_keyspace(key_range, keyspace_col_name) - else: - raise dbexceptions.ProgrammingError( - 'Illegal type for keyspace_col_type %d' % keyspace_col_type) - - -def _create_where_clause_for_str_keyspace(key_range, keyspace_col_name): - """This creates the where clause and bind_vars if keyspace_id col is a str. - - The comparison is done using mysql hex function and byte level comparison - with the key_range values. - - Args: - key_range: keyrange for the query. - keyspace_col_name: keyspace column name for the keyspace. - - Returns: - This returns the where clause when the keyspace column type is string. - """ - kr_min = key_range[0].strip() - kr_max = key_range[1].strip() - - where_clause = '' - bind_vars = {} - i = 0 - if kr_min != keyrange_constants.MIN_KEY: - bind_name = '%s%d' % (keyspace_col_name, i) - where_clause = 'hex(%s) >= ' % keyspace_col_name + '%(' + bind_name + ')s' - i += 1 - bind_vars[bind_name] = kr_min - if kr_max != keyrange_constants.MAX_KEY: - if where_clause: - where_clause += ' AND ' - bind_name = '%s%d' % (keyspace_col_name, i) - where_clause += 'hex(%s) < ' % keyspace_col_name + '%(' + bind_name + ')s' - bind_vars[bind_name] = kr_max - return where_clause, bind_vars - - -def _create_where_clause_for_int_keyspace(key_range, keyspace_col_name): - """This creates the where clause and bind_vars if keyspace_id col is a int. - - The comparison is done using numeric comparison on the int values hence - the true 64-bit int values are generated for the key_range in the bind_vars. - - Args: - key_range: keyrange for the query. - keyspace_col_name: keyspace column name for the keyspace. - - Returns: - This returns the where clause when the keyspace column type is uint64. - """ - kr_min = _true_int_kr_value(key_range[0]) - kr_max = _true_int_kr_value(key_range[1]) - - where_clause = '' - bind_vars = {} - i = 0 - if kr_min is not None: - bind_name = '%s%d' % (keyspace_col_name, i) - where_clause = '%s >= ' % keyspace_col_name + '%(' + bind_name + ')s' - i += 1 - bind_vars[bind_name] = kr_min - if kr_max is not None: - if where_clause: - where_clause += ' AND ' - bind_name = '%s%d' % (keyspace_col_name, i) - where_clause += '%s < ' % keyspace_col_name + '%(' + bind_name + ')s' - bind_vars[bind_name] = kr_max - return where_clause, bind_vars diff --git a/py/vtproto/__init__.py b/py/vtproto/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/py/vtproto/automation_pb2.py b/py/vtproto/automation_pb2.py deleted file mode 100644 index eb5021f86ca..00000000000 --- a/py/vtproto/automation_pb2.py +++ /dev/null @@ -1,627 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: automation.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='automation.proto', - package='automation', - syntax='proto3', - serialized_options=_b('Z\'vitess.io/vitess/go/vt/proto/automation'), - serialized_pb=_b('\n\x10\x61utomation.proto\x12\nautomation\"\x90\x01\n\x10\x43lusterOperation\x12\n\n\x02id\x18\x01 \x01(\t\x12/\n\x0cserial_tasks\x18\x02 \x03(\x0b\x32\x19.automation.TaskContainer\x12\x30\n\x05state\x18\x03 \x01(\x0e\x32!.automation.ClusterOperationState\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"N\n\rTaskContainer\x12(\n\x0eparallel_tasks\x18\x01 \x03(\x0b\x32\x10.automation.Task\x12\x13\n\x0b\x63oncurrency\x18\x02 \x01(\x05\"\xce\x01\n\x04Task\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\nparameters\x18\x02 \x03(\x0b\x32 .automation.Task.ParametersEntry\x12\n\n\x02id\x18\x03 \x01(\t\x12$\n\x05state\x18\x04 \x01(\x0e\x32\x15.automation.TaskState\x12\x0e\n\x06output\x18\x05 \x01(\t\x12\r\n\x05\x65rror\x18\x06 \x01(\t\x1a\x31\n\x0fParametersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xb1\x01\n\x1e\x45nqueueClusterOperationRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12N\n\nparameters\x18\x02 \x03(\x0b\x32:.automation.EnqueueClusterOperationRequest.ParametersEntry\x1a\x31\n\x0fParametersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"-\n\x1f\x45nqueueClusterOperationResponse\x12\n\n\x02id\x18\x01 \x01(\t\"-\n\x1fGetClusterOperationStateRequest\x12\n\n\x02id\x18\x01 \x01(\t\"T\n GetClusterOperationStateResponse\x12\x30\n\x05state\x18\x01 \x01(\x0e\x32!.automation.ClusterOperationState\"/\n!GetClusterOperationDetailsRequest\x12\n\n\x02id\x18\x01 \x01(\t\"V\n\"GetClusterOperationDetailsResponse\x12\x30\n\ncluster_op\x18\x02 \x01(\x0b\x32\x1c.automation.ClusterOperation*\x9a\x01\n\x15\x43lusterOperationState\x12#\n\x1fUNKNOWN_CLUSTER_OPERATION_STATE\x10\x00\x12!\n\x1d\x43LUSTER_OPERATION_NOT_STARTED\x10\x01\x12\x1d\n\x19\x43LUSTER_OPERATION_RUNNING\x10\x02\x12\x1a\n\x16\x43LUSTER_OPERATION_DONE\x10\x03*K\n\tTaskState\x12\x16\n\x12UNKNOWN_TASK_STATE\x10\x00\x12\x0f\n\x0bNOT_STARTED\x10\x01\x12\x0b\n\x07RUNNING\x10\x02\x12\x08\n\x04\x44ONE\x10\x03\x42)Z\'vitess.io/vitess/go/vt/proto/automationb\x06proto3') -) - -_CLUSTEROPERATIONSTATE = _descriptor.EnumDescriptor( - name='ClusterOperationState', - full_name='automation.ClusterOperationState', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN_CLUSTER_OPERATION_STATE', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLUSTER_OPERATION_NOT_STARTED', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLUSTER_OPERATION_RUNNING', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLUSTER_OPERATION_DONE', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=966, - serialized_end=1120, -) -_sym_db.RegisterEnumDescriptor(_CLUSTEROPERATIONSTATE) - -ClusterOperationState = enum_type_wrapper.EnumTypeWrapper(_CLUSTEROPERATIONSTATE) -_TASKSTATE = _descriptor.EnumDescriptor( - name='TaskState', - full_name='automation.TaskState', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN_TASK_STATE', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_STARTED', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RUNNING', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DONE', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=1122, - serialized_end=1197, -) -_sym_db.RegisterEnumDescriptor(_TASKSTATE) - -TaskState = enum_type_wrapper.EnumTypeWrapper(_TASKSTATE) -UNKNOWN_CLUSTER_OPERATION_STATE = 0 -CLUSTER_OPERATION_NOT_STARTED = 1 -CLUSTER_OPERATION_RUNNING = 2 -CLUSTER_OPERATION_DONE = 3 -UNKNOWN_TASK_STATE = 0 -NOT_STARTED = 1 -RUNNING = 2 -DONE = 3 - - - -_CLUSTEROPERATION = _descriptor.Descriptor( - name='ClusterOperation', - full_name='automation.ClusterOperation', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='automation.ClusterOperation.id', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='serial_tasks', full_name='automation.ClusterOperation.serial_tasks', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='automation.ClusterOperation.state', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='error', full_name='automation.ClusterOperation.error', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=33, - serialized_end=177, -) - - -_TASKCONTAINER = _descriptor.Descriptor( - name='TaskContainer', - full_name='automation.TaskContainer', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='parallel_tasks', full_name='automation.TaskContainer.parallel_tasks', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='concurrency', full_name='automation.TaskContainer.concurrency', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=179, - serialized_end=257, -) - - -_TASK_PARAMETERSENTRY = _descriptor.Descriptor( - name='ParametersEntry', - full_name='automation.Task.ParametersEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='automation.Task.ParametersEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='automation.Task.ParametersEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=417, - serialized_end=466, -) - -_TASK = _descriptor.Descriptor( - name='Task', - full_name='automation.Task', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='automation.Task.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='parameters', full_name='automation.Task.parameters', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id', full_name='automation.Task.id', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='automation.Task.state', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='output', full_name='automation.Task.output', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='error', full_name='automation.Task.error', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_TASK_PARAMETERSENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=260, - serialized_end=466, -) - - -_ENQUEUECLUSTEROPERATIONREQUEST_PARAMETERSENTRY = _descriptor.Descriptor( - name='ParametersEntry', - full_name='automation.EnqueueClusterOperationRequest.ParametersEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='automation.EnqueueClusterOperationRequest.ParametersEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='automation.EnqueueClusterOperationRequest.ParametersEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=417, - serialized_end=466, -) - -_ENQUEUECLUSTEROPERATIONREQUEST = _descriptor.Descriptor( - name='EnqueueClusterOperationRequest', - full_name='automation.EnqueueClusterOperationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='automation.EnqueueClusterOperationRequest.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='parameters', full_name='automation.EnqueueClusterOperationRequest.parameters', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_ENQUEUECLUSTEROPERATIONREQUEST_PARAMETERSENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=469, - serialized_end=646, -) - - -_ENQUEUECLUSTEROPERATIONRESPONSE = _descriptor.Descriptor( - name='EnqueueClusterOperationResponse', - full_name='automation.EnqueueClusterOperationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='automation.EnqueueClusterOperationResponse.id', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=648, - serialized_end=693, -) - - -_GETCLUSTEROPERATIONSTATEREQUEST = _descriptor.Descriptor( - name='GetClusterOperationStateRequest', - full_name='automation.GetClusterOperationStateRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='automation.GetClusterOperationStateRequest.id', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=695, - serialized_end=740, -) - - -_GETCLUSTEROPERATIONSTATERESPONSE = _descriptor.Descriptor( - name='GetClusterOperationStateResponse', - full_name='automation.GetClusterOperationStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='state', full_name='automation.GetClusterOperationStateResponse.state', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=742, - serialized_end=826, -) - - -_GETCLUSTEROPERATIONDETAILSREQUEST = _descriptor.Descriptor( - name='GetClusterOperationDetailsRequest', - full_name='automation.GetClusterOperationDetailsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='automation.GetClusterOperationDetailsRequest.id', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=828, - serialized_end=875, -) - - -_GETCLUSTEROPERATIONDETAILSRESPONSE = _descriptor.Descriptor( - name='GetClusterOperationDetailsResponse', - full_name='automation.GetClusterOperationDetailsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='cluster_op', full_name='automation.GetClusterOperationDetailsResponse.cluster_op', index=0, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=877, - serialized_end=963, -) - -_CLUSTEROPERATION.fields_by_name['serial_tasks'].message_type = _TASKCONTAINER -_CLUSTEROPERATION.fields_by_name['state'].enum_type = _CLUSTEROPERATIONSTATE -_TASKCONTAINER.fields_by_name['parallel_tasks'].message_type = _TASK -_TASK_PARAMETERSENTRY.containing_type = _TASK -_TASK.fields_by_name['parameters'].message_type = _TASK_PARAMETERSENTRY -_TASK.fields_by_name['state'].enum_type = _TASKSTATE -_ENQUEUECLUSTEROPERATIONREQUEST_PARAMETERSENTRY.containing_type = _ENQUEUECLUSTEROPERATIONREQUEST -_ENQUEUECLUSTEROPERATIONREQUEST.fields_by_name['parameters'].message_type = _ENQUEUECLUSTEROPERATIONREQUEST_PARAMETERSENTRY -_GETCLUSTEROPERATIONSTATERESPONSE.fields_by_name['state'].enum_type = _CLUSTEROPERATIONSTATE -_GETCLUSTEROPERATIONDETAILSRESPONSE.fields_by_name['cluster_op'].message_type = _CLUSTEROPERATION -DESCRIPTOR.message_types_by_name['ClusterOperation'] = _CLUSTEROPERATION -DESCRIPTOR.message_types_by_name['TaskContainer'] = _TASKCONTAINER -DESCRIPTOR.message_types_by_name['Task'] = _TASK -DESCRIPTOR.message_types_by_name['EnqueueClusterOperationRequest'] = _ENQUEUECLUSTEROPERATIONREQUEST -DESCRIPTOR.message_types_by_name['EnqueueClusterOperationResponse'] = _ENQUEUECLUSTEROPERATIONRESPONSE -DESCRIPTOR.message_types_by_name['GetClusterOperationStateRequest'] = _GETCLUSTEROPERATIONSTATEREQUEST -DESCRIPTOR.message_types_by_name['GetClusterOperationStateResponse'] = _GETCLUSTEROPERATIONSTATERESPONSE -DESCRIPTOR.message_types_by_name['GetClusterOperationDetailsRequest'] = _GETCLUSTEROPERATIONDETAILSREQUEST -DESCRIPTOR.message_types_by_name['GetClusterOperationDetailsResponse'] = _GETCLUSTEROPERATIONDETAILSRESPONSE -DESCRIPTOR.enum_types_by_name['ClusterOperationState'] = _CLUSTEROPERATIONSTATE -DESCRIPTOR.enum_types_by_name['TaskState'] = _TASKSTATE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ClusterOperation = _reflection.GeneratedProtocolMessageType('ClusterOperation', (_message.Message,), dict( - DESCRIPTOR = _CLUSTEROPERATION, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.ClusterOperation) - )) -_sym_db.RegisterMessage(ClusterOperation) - -TaskContainer = _reflection.GeneratedProtocolMessageType('TaskContainer', (_message.Message,), dict( - DESCRIPTOR = _TASKCONTAINER, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.TaskContainer) - )) -_sym_db.RegisterMessage(TaskContainer) - -Task = _reflection.GeneratedProtocolMessageType('Task', (_message.Message,), dict( - - ParametersEntry = _reflection.GeneratedProtocolMessageType('ParametersEntry', (_message.Message,), dict( - DESCRIPTOR = _TASK_PARAMETERSENTRY, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.Task.ParametersEntry) - )) - , - DESCRIPTOR = _TASK, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.Task) - )) -_sym_db.RegisterMessage(Task) -_sym_db.RegisterMessage(Task.ParametersEntry) - -EnqueueClusterOperationRequest = _reflection.GeneratedProtocolMessageType('EnqueueClusterOperationRequest', (_message.Message,), dict( - - ParametersEntry = _reflection.GeneratedProtocolMessageType('ParametersEntry', (_message.Message,), dict( - DESCRIPTOR = _ENQUEUECLUSTEROPERATIONREQUEST_PARAMETERSENTRY, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.EnqueueClusterOperationRequest.ParametersEntry) - )) - , - DESCRIPTOR = _ENQUEUECLUSTEROPERATIONREQUEST, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.EnqueueClusterOperationRequest) - )) -_sym_db.RegisterMessage(EnqueueClusterOperationRequest) -_sym_db.RegisterMessage(EnqueueClusterOperationRequest.ParametersEntry) - -EnqueueClusterOperationResponse = _reflection.GeneratedProtocolMessageType('EnqueueClusterOperationResponse', (_message.Message,), dict( - DESCRIPTOR = _ENQUEUECLUSTEROPERATIONRESPONSE, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.EnqueueClusterOperationResponse) - )) -_sym_db.RegisterMessage(EnqueueClusterOperationResponse) - -GetClusterOperationStateRequest = _reflection.GeneratedProtocolMessageType('GetClusterOperationStateRequest', (_message.Message,), dict( - DESCRIPTOR = _GETCLUSTEROPERATIONSTATEREQUEST, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.GetClusterOperationStateRequest) - )) -_sym_db.RegisterMessage(GetClusterOperationStateRequest) - -GetClusterOperationStateResponse = _reflection.GeneratedProtocolMessageType('GetClusterOperationStateResponse', (_message.Message,), dict( - DESCRIPTOR = _GETCLUSTEROPERATIONSTATERESPONSE, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.GetClusterOperationStateResponse) - )) -_sym_db.RegisterMessage(GetClusterOperationStateResponse) - -GetClusterOperationDetailsRequest = _reflection.GeneratedProtocolMessageType('GetClusterOperationDetailsRequest', (_message.Message,), dict( - DESCRIPTOR = _GETCLUSTEROPERATIONDETAILSREQUEST, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.GetClusterOperationDetailsRequest) - )) -_sym_db.RegisterMessage(GetClusterOperationDetailsRequest) - -GetClusterOperationDetailsResponse = _reflection.GeneratedProtocolMessageType('GetClusterOperationDetailsResponse', (_message.Message,), dict( - DESCRIPTOR = _GETCLUSTEROPERATIONDETAILSRESPONSE, - __module__ = 'automation_pb2' - # @@protoc_insertion_point(class_scope:automation.GetClusterOperationDetailsResponse) - )) -_sym_db.RegisterMessage(GetClusterOperationDetailsResponse) - - -DESCRIPTOR._options = None -_TASK_PARAMETERSENTRY._options = None -_ENQUEUECLUSTEROPERATIONREQUEST_PARAMETERSENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/automation_pb2_grpc.py b/py/vtproto/automation_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/automation_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/automationservice_pb2.py b/py/vtproto/automationservice_pb2.py deleted file mode 100644 index cb754003010..00000000000 --- a/py/vtproto/automationservice_pb2.py +++ /dev/null @@ -1,66 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: automationservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import automation_pb2 as automation__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='automationservice.proto', - package='automationservice', - syntax='proto3', - serialized_options=_b('Z.vitess.io/vitess/go/vt/proto/automationservice'), - serialized_pb=_b('\n\x17\x61utomationservice.proto\x12\x11\x61utomationservice\x1a\x10\x61utomation.proto2\x81\x02\n\nAutomation\x12t\n\x17\x45nqueueClusterOperation\x12*.automation.EnqueueClusterOperationRequest\x1a+.automation.EnqueueClusterOperationResponse\"\x00\x12}\n\x1aGetClusterOperationDetails\x12-.automation.GetClusterOperationDetailsRequest\x1a..automation.GetClusterOperationDetailsResponse\"\x00\x42\x30Z.vitess.io/vitess/go/vt/proto/automationserviceb\x06proto3') - , - dependencies=[automation__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_AUTOMATION = _descriptor.ServiceDescriptor( - name='Automation', - full_name='automationservice.Automation', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=65, - serialized_end=322, - methods=[ - _descriptor.MethodDescriptor( - name='EnqueueClusterOperation', - full_name='automationservice.Automation.EnqueueClusterOperation', - index=0, - containing_service=None, - input_type=automation__pb2._ENQUEUECLUSTEROPERATIONREQUEST, - output_type=automation__pb2._ENQUEUECLUSTEROPERATIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='GetClusterOperationDetails', - full_name='automationservice.Automation.GetClusterOperationDetails', - index=1, - containing_service=None, - input_type=automation__pb2._GETCLUSTEROPERATIONDETAILSREQUEST, - output_type=automation__pb2._GETCLUSTEROPERATIONDETAILSRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_AUTOMATION) - -DESCRIPTOR.services_by_name['Automation'] = _AUTOMATION - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/automationservice_pb2_grpc.py b/py/vtproto/automationservice_pb2_grpc.py deleted file mode 100644 index 79bdb7aaaeb..00000000000 --- a/py/vtproto/automationservice_pb2_grpc.py +++ /dev/null @@ -1,64 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import automation_pb2 as automation__pb2 - - -class AutomationStub(object): - # missing associated documentation comment in .proto file - pass - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.EnqueueClusterOperation = channel.unary_unary( - '/automationservice.Automation/EnqueueClusterOperation', - request_serializer=automation__pb2.EnqueueClusterOperationRequest.SerializeToString, - response_deserializer=automation__pb2.EnqueueClusterOperationResponse.FromString, - ) - self.GetClusterOperationDetails = channel.unary_unary( - '/automationservice.Automation/GetClusterOperationDetails', - request_serializer=automation__pb2.GetClusterOperationDetailsRequest.SerializeToString, - response_deserializer=automation__pb2.GetClusterOperationDetailsResponse.FromString, - ) - - -class AutomationServicer(object): - # missing associated documentation comment in .proto file - pass - - def EnqueueClusterOperation(self, request, context): - """Start a cluster operation. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GetClusterOperationDetails(self, request, context): - """TODO(mberlin): Polling this is bad. Implement a subscribe mechanism to wait for changes? - Get all details of an active cluster operation. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_AutomationServicer_to_server(servicer, server): - rpc_method_handlers = { - 'EnqueueClusterOperation': grpc.unary_unary_rpc_method_handler( - servicer.EnqueueClusterOperation, - request_deserializer=automation__pb2.EnqueueClusterOperationRequest.FromString, - response_serializer=automation__pb2.EnqueueClusterOperationResponse.SerializeToString, - ), - 'GetClusterOperationDetails': grpc.unary_unary_rpc_method_handler( - servicer.GetClusterOperationDetails, - request_deserializer=automation__pb2.GetClusterOperationDetailsRequest.FromString, - response_serializer=automation__pb2.GetClusterOperationDetailsResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'automationservice.Automation', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/binlogdata_pb2.py b/py/vtproto/binlogdata_pb2.py deleted file mode 100644 index 33984799c48..00000000000 --- a/py/vtproto/binlogdata_pb2.py +++ /dev/null @@ -1,1660 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: binlogdata.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import vtrpc_pb2 as vtrpc__pb2 -import query_pb2 as query__pb2 -import topodata_pb2 as topodata__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='binlogdata.proto', - package='binlogdata', - syntax='proto3', - serialized_options=_b('Z\'vitess.io/vitess/go/vt/proto/binlogdata'), - serialized_pb=_b('\n\x10\x62inlogdata.proto\x12\nbinlogdata\x1a\x0bvtrpc.proto\x1a\x0bquery.proto\x1a\x0etopodata.proto\"7\n\x07\x43harset\x12\x0e\n\x06\x63lient\x18\x01 \x01(\x05\x12\x0c\n\x04\x63onn\x18\x02 \x01(\x05\x12\x0e\n\x06server\x18\x03 \x01(\x05\"\xb5\x03\n\x11\x42inlogTransaction\x12;\n\nstatements\x18\x01 \x03(\x0b\x32\'.binlogdata.BinlogTransaction.Statement\x12&\n\x0b\x65vent_token\x18\x04 \x01(\x0b\x32\x11.query.EventToken\x1a\xae\x02\n\tStatement\x12\x42\n\x08\x63\x61tegory\x18\x01 \x01(\x0e\x32\x30.binlogdata.BinlogTransaction.Statement.Category\x12$\n\x07\x63harset\x18\x02 \x01(\x0b\x32\x13.binlogdata.Charset\x12\x0b\n\x03sql\x18\x03 \x01(\x0c\"\xa9\x01\n\x08\x43\x61tegory\x12\x13\n\x0f\x42L_UNRECOGNIZED\x10\x00\x12\x0c\n\x08\x42L_BEGIN\x10\x01\x12\r\n\tBL_COMMIT\x10\x02\x12\x0f\n\x0b\x42L_ROLLBACK\x10\x03\x12\x15\n\x11\x42L_DML_DEPRECATED\x10\x04\x12\n\n\x06\x42L_DDL\x10\x05\x12\n\n\x06\x42L_SET\x10\x06\x12\r\n\tBL_INSERT\x10\x07\x12\r\n\tBL_UPDATE\x10\x08\x12\r\n\tBL_DELETE\x10\tJ\x04\x08\x02\x10\x03J\x04\x08\x03\x10\x04\"v\n\x15StreamKeyRangeRequest\x12\x10\n\x08position\x18\x01 \x01(\t\x12%\n\tkey_range\x18\x02 \x01(\x0b\x32\x12.topodata.KeyRange\x12$\n\x07\x63harset\x18\x03 \x01(\x0b\x32\x13.binlogdata.Charset\"S\n\x16StreamKeyRangeResponse\x12\x39\n\x12\x62inlog_transaction\x18\x01 \x01(\x0b\x32\x1d.binlogdata.BinlogTransaction\"]\n\x13StreamTablesRequest\x12\x10\n\x08position\x18\x01 \x01(\t\x12\x0e\n\x06tables\x18\x02 \x03(\t\x12$\n\x07\x63harset\x18\x03 \x01(\x0b\x32\x13.binlogdata.Charset\"Q\n\x14StreamTablesResponse\x12\x39\n\x12\x62inlog_transaction\x18\x01 \x01(\x0b\x32\x1d.binlogdata.BinlogTransaction\"%\n\x04Rule\x12\r\n\x05match\x18\x01 \x01(\t\x12\x0e\n\x06\x66ilter\x18\x02 \x01(\t\"\x9c\x01\n\x06\x46ilter\x12\x1f\n\x05rules\x18\x01 \x03(\x0b\x32\x10.binlogdata.Rule\x12\x39\n\x0e\x66ieldEventMode\x18\x02 \x01(\x0e\x32!.binlogdata.Filter.FieldEventMode\"6\n\x0e\x46ieldEventMode\x12\x13\n\x0f\x45RR_ON_MISMATCH\x10\x00\x12\x0f\n\x0b\x42\x45ST_EFFORT\x10\x01\"\x8f\x02\n\x0c\x42inlogSource\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12\r\n\x05shard\x18\x02 \x01(\t\x12)\n\x0btablet_type\x18\x03 \x01(\x0e\x32\x14.topodata.TabletType\x12%\n\tkey_range\x18\x04 \x01(\x0b\x32\x12.topodata.KeyRange\x12\x0e\n\x06tables\x18\x05 \x03(\t\x12\"\n\x06\x66ilter\x18\x06 \x01(\x0b\x32\x12.binlogdata.Filter\x12\'\n\x06on_ddl\x18\x07 \x01(\x0e\x32\x17.binlogdata.OnDDLAction\x12\x16\n\x0e\x65xternal_mysql\x18\x08 \x01(\t\x12\x17\n\x0fstop_after_copy\x18\t \x01(\x08\"B\n\tRowChange\x12\x1a\n\x06\x62\x65\x66ore\x18\x01 \x01(\x0b\x32\n.query.Row\x12\x19\n\x05\x61\x66ter\x18\x02 \x01(\x0b\x32\n.query.Row\"J\n\x08RowEvent\x12\x12\n\ntable_name\x18\x01 \x01(\t\x12*\n\x0brow_changes\x18\x02 \x03(\x0b\x32\x15.binlogdata.RowChange\">\n\nFieldEvent\x12\x12\n\ntable_name\x18\x01 \x01(\t\x12\x1c\n\x06\x66ields\x18\x02 \x03(\x0b\x32\x0c.query.Field\":\n\tShardGtid\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12\r\n\x05shard\x18\x02 \x01(\t\x12\x0c\n\x04gtid\x18\x03 \x01(\t\"3\n\x05VGtid\x12*\n\x0bshard_gtids\x18\x01 \x03(\x0b\x32\x15.binlogdata.ShardGtid\"0\n\rKeyspaceShard\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12\r\n\x05shard\x18\x02 \x01(\t\"\xe7\x01\n\x07Journal\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x31\n\x0emigration_type\x18\x02 \x01(\x0e\x32\x19.binlogdata.MigrationType\x12\x0e\n\x06tables\x18\x03 \x03(\t\x12\x16\n\x0elocal_position\x18\x04 \x01(\t\x12*\n\x0bshard_gtids\x18\x05 \x03(\x0b\x32\x15.binlogdata.ShardGtid\x12/\n\x0cparticipants\x18\x06 \x03(\x0b\x32\x19.binlogdata.KeyspaceShard\x12\x18\n\x10source_workflows\x18\x07 \x03(\t\"\x9d\x02\n\x06VEvent\x12$\n\x04type\x18\x01 \x01(\x0e\x32\x16.binlogdata.VEventType\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\x12\x0c\n\x04gtid\x18\x03 \x01(\t\x12\x0b\n\x03\x64\x64l\x18\x04 \x01(\t\x12\'\n\trow_event\x18\x05 \x01(\x0b\x32\x14.binlogdata.RowEvent\x12+\n\x0b\x66ield_event\x18\x06 \x01(\x0b\x32\x16.binlogdata.FieldEvent\x12 \n\x05vgtid\x18\x07 \x01(\x0b\x32\x11.binlogdata.VGtid\x12$\n\x07journal\x18\x08 \x01(\x0b\x32\x13.binlogdata.Journal\x12\x0b\n\x03\x64ml\x18\t \x01(\t\x12\x14\n\x0c\x63urrent_time\x18\x14 \x01(\x03\"\xc7\x01\n\x0eVStreamRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x10\n\x08position\x18\x04 \x01(\t\x12\"\n\x06\x66ilter\x18\x05 \x01(\x0b\x32\x12.binlogdata.Filter\"5\n\x0fVStreamResponse\x12\"\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x12.binlogdata.VEvent\"\xc8\x01\n\x12VStreamRowsRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\r\n\x05query\x18\x04 \x01(\t\x12\"\n\x06lastpk\x18\x05 \x01(\x0b\x32\x12.query.QueryResult\"\x97\x01\n\x13VStreamRowsResponse\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.query.Field\x12\x1e\n\x08pkfields\x18\x02 \x03(\x0b\x32\x0c.query.Field\x12\x0c\n\x04gtid\x18\x03 \x01(\t\x12\x18\n\x04rows\x18\x04 \x03(\x0b\x32\n.query.Row\x12\x1a\n\x06lastpk\x18\x05 \x01(\x0b\x32\n.query.Row\"\xa7\x01\n\x15VStreamResultsRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\r\n\x05query\x18\x04 \x01(\t\"^\n\x16VStreamResultsResponse\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.query.Field\x12\x0c\n\x04gtid\x18\x03 \x01(\t\x12\x18\n\x04rows\x18\x04 \x03(\x0b\x32\n.query.Row*>\n\x0bOnDDLAction\x12\n\n\x06IGNORE\x10\x00\x12\x08\n\x04STOP\x10\x01\x12\x08\n\x04\x45XEC\x10\x02\x12\x0f\n\x0b\x45XEC_IGNORE\x10\x03*\xd1\x01\n\nVEventType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04GTID\x10\x01\x12\t\n\x05\x42\x45GIN\x10\x02\x12\n\n\x06\x43OMMIT\x10\x03\x12\x0c\n\x08ROLLBACK\x10\x04\x12\x07\n\x03\x44\x44L\x10\x05\x12\n\n\x06INSERT\x10\x06\x12\x0b\n\x07REPLACE\x10\x07\x12\n\n\x06UPDATE\x10\x08\x12\n\n\x06\x44\x45LETE\x10\t\x12\x07\n\x03SET\x10\n\x12\t\n\x05OTHER\x10\x0b\x12\x07\n\x03ROW\x10\x0c\x12\t\n\x05\x46IELD\x10\r\x12\r\n\tHEARTBEAT\x10\x0e\x12\t\n\x05VGTID\x10\x0f\x12\x0b\n\x07JOURNAL\x10\x10*\'\n\rMigrationType\x12\n\n\x06TABLES\x10\x00\x12\n\n\x06SHARDS\x10\x01\x42)Z\'vitess.io/vitess/go/vt/proto/binlogdatab\x06proto3') - , - dependencies=[vtrpc__pb2.DESCRIPTOR,query__pb2.DESCRIPTOR,topodata__pb2.DESCRIPTOR,]) - -_ONDDLACTION = _descriptor.EnumDescriptor( - name='OnDDLAction', - full_name='binlogdata.OnDDLAction', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='IGNORE', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='STOP', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXEC', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXEC_IGNORE', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=3199, - serialized_end=3261, -) -_sym_db.RegisterEnumDescriptor(_ONDDLACTION) - -OnDDLAction = enum_type_wrapper.EnumTypeWrapper(_ONDDLACTION) -_VEVENTTYPE = _descriptor.EnumDescriptor( - name='VEventType', - full_name='binlogdata.VEventType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='GTID', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BEGIN', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='COMMIT', index=3, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ROLLBACK', index=4, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DDL', index=5, number=5, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INSERT', index=6, number=6, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='REPLACE', index=7, number=7, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UPDATE', index=8, number=8, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DELETE', index=9, number=9, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SET', index=10, number=10, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OTHER', index=11, number=11, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ROW', index=12, number=12, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FIELD', index=13, number=13, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='HEARTBEAT', index=14, number=14, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='VGTID', index=15, number=15, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='JOURNAL', index=16, number=16, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=3264, - serialized_end=3473, -) -_sym_db.RegisterEnumDescriptor(_VEVENTTYPE) - -VEventType = enum_type_wrapper.EnumTypeWrapper(_VEVENTTYPE) -_MIGRATIONTYPE = _descriptor.EnumDescriptor( - name='MigrationType', - full_name='binlogdata.MigrationType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='TABLES', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SHARDS', index=1, number=1, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=3475, - serialized_end=3514, -) -_sym_db.RegisterEnumDescriptor(_MIGRATIONTYPE) - -MigrationType = enum_type_wrapper.EnumTypeWrapper(_MIGRATIONTYPE) -IGNORE = 0 -STOP = 1 -EXEC = 2 -EXEC_IGNORE = 3 -UNKNOWN = 0 -GTID = 1 -BEGIN = 2 -COMMIT = 3 -ROLLBACK = 4 -DDL = 5 -INSERT = 6 -REPLACE = 7 -UPDATE = 8 -DELETE = 9 -SET = 10 -OTHER = 11 -ROW = 12 -FIELD = 13 -HEARTBEAT = 14 -VGTID = 15 -JOURNAL = 16 -TABLES = 0 -SHARDS = 1 - - -_BINLOGTRANSACTION_STATEMENT_CATEGORY = _descriptor.EnumDescriptor( - name='Category', - full_name='binlogdata.BinlogTransaction.Statement.Category', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='BL_UNRECOGNIZED', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_BEGIN', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_COMMIT', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_ROLLBACK', index=3, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_DML_DEPRECATED', index=4, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_DDL', index=5, number=5, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_SET', index=6, number=6, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_INSERT', index=7, number=7, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_UPDATE', index=8, number=8, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BL_DELETE', index=9, number=9, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=388, - serialized_end=557, -) -_sym_db.RegisterEnumDescriptor(_BINLOGTRANSACTION_STATEMENT_CATEGORY) - -_FILTER_FIELDEVENTMODE = _descriptor.EnumDescriptor( - name='FieldEventMode', - full_name='binlogdata.Filter.FieldEventMode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='ERR_ON_MISMATCH', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BEST_EFFORT', index=1, number=1, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=1096, - serialized_end=1150, -) -_sym_db.RegisterEnumDescriptor(_FILTER_FIELDEVENTMODE) - - -_CHARSET = _descriptor.Descriptor( - name='Charset', - full_name='binlogdata.Charset', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='client', full_name='binlogdata.Charset.client', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='conn', full_name='binlogdata.Charset.conn', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='server', full_name='binlogdata.Charset.server', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=74, - serialized_end=129, -) - - -_BINLOGTRANSACTION_STATEMENT = _descriptor.Descriptor( - name='Statement', - full_name='binlogdata.BinlogTransaction.Statement', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='category', full_name='binlogdata.BinlogTransaction.Statement.category', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='charset', full_name='binlogdata.BinlogTransaction.Statement.charset', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sql', full_name='binlogdata.BinlogTransaction.Statement.sql', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _BINLOGTRANSACTION_STATEMENT_CATEGORY, - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=255, - serialized_end=557, -) - -_BINLOGTRANSACTION = _descriptor.Descriptor( - name='BinlogTransaction', - full_name='binlogdata.BinlogTransaction', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='statements', full_name='binlogdata.BinlogTransaction.statements', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='event_token', full_name='binlogdata.BinlogTransaction.event_token', index=1, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_BINLOGTRANSACTION_STATEMENT, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=132, - serialized_end=569, -) - - -_STREAMKEYRANGEREQUEST = _descriptor.Descriptor( - name='StreamKeyRangeRequest', - full_name='binlogdata.StreamKeyRangeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='binlogdata.StreamKeyRangeRequest.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='binlogdata.StreamKeyRangeRequest.key_range', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='charset', full_name='binlogdata.StreamKeyRangeRequest.charset', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=571, - serialized_end=689, -) - - -_STREAMKEYRANGERESPONSE = _descriptor.Descriptor( - name='StreamKeyRangeResponse', - full_name='binlogdata.StreamKeyRangeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='binlog_transaction', full_name='binlogdata.StreamKeyRangeResponse.binlog_transaction', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=691, - serialized_end=774, -) - - -_STREAMTABLESREQUEST = _descriptor.Descriptor( - name='StreamTablesRequest', - full_name='binlogdata.StreamTablesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='binlogdata.StreamTablesRequest.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tables', full_name='binlogdata.StreamTablesRequest.tables', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='charset', full_name='binlogdata.StreamTablesRequest.charset', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=776, - serialized_end=869, -) - - -_STREAMTABLESRESPONSE = _descriptor.Descriptor( - name='StreamTablesResponse', - full_name='binlogdata.StreamTablesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='binlog_transaction', full_name='binlogdata.StreamTablesResponse.binlog_transaction', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=871, - serialized_end=952, -) - - -_RULE = _descriptor.Descriptor( - name='Rule', - full_name='binlogdata.Rule', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='match', full_name='binlogdata.Rule.match', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='filter', full_name='binlogdata.Rule.filter', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=954, - serialized_end=991, -) - - -_FILTER = _descriptor.Descriptor( - name='Filter', - full_name='binlogdata.Filter', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='rules', full_name='binlogdata.Filter.rules', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='fieldEventMode', full_name='binlogdata.Filter.fieldEventMode', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _FILTER_FIELDEVENTMODE, - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=994, - serialized_end=1150, -) - - -_BINLOGSOURCE = _descriptor.Descriptor( - name='BinlogSource', - full_name='binlogdata.BinlogSource', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspace', full_name='binlogdata.BinlogSource.keyspace', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='binlogdata.BinlogSource.shard', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='binlogdata.BinlogSource.tablet_type', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='binlogdata.BinlogSource.key_range', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tables', full_name='binlogdata.BinlogSource.tables', index=4, - number=5, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='filter', full_name='binlogdata.BinlogSource.filter', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='on_ddl', full_name='binlogdata.BinlogSource.on_ddl', index=6, - number=7, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='external_mysql', full_name='binlogdata.BinlogSource.external_mysql', index=7, - number=8, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='stop_after_copy', full_name='binlogdata.BinlogSource.stop_after_copy', index=8, - number=9, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1153, - serialized_end=1424, -) - - -_ROWCHANGE = _descriptor.Descriptor( - name='RowChange', - full_name='binlogdata.RowChange', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='before', full_name='binlogdata.RowChange.before', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='after', full_name='binlogdata.RowChange.after', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1426, - serialized_end=1492, -) - - -_ROWEVENT = _descriptor.Descriptor( - name='RowEvent', - full_name='binlogdata.RowEvent', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='table_name', full_name='binlogdata.RowEvent.table_name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='row_changes', full_name='binlogdata.RowEvent.row_changes', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1494, - serialized_end=1568, -) - - -_FIELDEVENT = _descriptor.Descriptor( - name='FieldEvent', - full_name='binlogdata.FieldEvent', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='table_name', full_name='binlogdata.FieldEvent.table_name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='fields', full_name='binlogdata.FieldEvent.fields', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1570, - serialized_end=1632, -) - - -_SHARDGTID = _descriptor.Descriptor( - name='ShardGtid', - full_name='binlogdata.ShardGtid', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspace', full_name='binlogdata.ShardGtid.keyspace', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='binlogdata.ShardGtid.shard', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='gtid', full_name='binlogdata.ShardGtid.gtid', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1634, - serialized_end=1692, -) - - -_VGTID = _descriptor.Descriptor( - name='VGtid', - full_name='binlogdata.VGtid', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='shard_gtids', full_name='binlogdata.VGtid.shard_gtids', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1694, - serialized_end=1745, -) - - -_KEYSPACESHARD = _descriptor.Descriptor( - name='KeyspaceShard', - full_name='binlogdata.KeyspaceShard', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspace', full_name='binlogdata.KeyspaceShard.keyspace', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='binlogdata.KeyspaceShard.shard', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1747, - serialized_end=1795, -) - - -_JOURNAL = _descriptor.Descriptor( - name='Journal', - full_name='binlogdata.Journal', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='binlogdata.Journal.id', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='migration_type', full_name='binlogdata.Journal.migration_type', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tables', full_name='binlogdata.Journal.tables', index=2, - number=3, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='local_position', full_name='binlogdata.Journal.local_position', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard_gtids', full_name='binlogdata.Journal.shard_gtids', index=4, - number=5, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='participants', full_name='binlogdata.Journal.participants', index=5, - number=6, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_workflows', full_name='binlogdata.Journal.source_workflows', index=6, - number=7, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1798, - serialized_end=2029, -) - - -_VEVENT = _descriptor.Descriptor( - name='VEvent', - full_name='binlogdata.VEvent', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='binlogdata.VEvent.type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp', full_name='binlogdata.VEvent.timestamp', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='gtid', full_name='binlogdata.VEvent.gtid', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ddl', full_name='binlogdata.VEvent.ddl', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='row_event', full_name='binlogdata.VEvent.row_event', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='field_event', full_name='binlogdata.VEvent.field_event', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='vgtid', full_name='binlogdata.VEvent.vgtid', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='journal', full_name='binlogdata.VEvent.journal', index=7, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dml', full_name='binlogdata.VEvent.dml', index=8, - number=9, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='current_time', full_name='binlogdata.VEvent.current_time', index=9, - number=20, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2032, - serialized_end=2317, -) - - -_VSTREAMREQUEST = _descriptor.Descriptor( - name='VStreamRequest', - full_name='binlogdata.VStreamRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='binlogdata.VStreamRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='binlogdata.VStreamRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='binlogdata.VStreamRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='position', full_name='binlogdata.VStreamRequest.position', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='filter', full_name='binlogdata.VStreamRequest.filter', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2320, - serialized_end=2519, -) - - -_VSTREAMRESPONSE = _descriptor.Descriptor( - name='VStreamResponse', - full_name='binlogdata.VStreamResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='events', full_name='binlogdata.VStreamResponse.events', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2521, - serialized_end=2574, -) - - -_VSTREAMROWSREQUEST = _descriptor.Descriptor( - name='VStreamRowsRequest', - full_name='binlogdata.VStreamRowsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='binlogdata.VStreamRowsRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='binlogdata.VStreamRowsRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='binlogdata.VStreamRowsRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='binlogdata.VStreamRowsRequest.query', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='lastpk', full_name='binlogdata.VStreamRowsRequest.lastpk', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2577, - serialized_end=2777, -) - - -_VSTREAMROWSRESPONSE = _descriptor.Descriptor( - name='VStreamRowsResponse', - full_name='binlogdata.VStreamRowsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='fields', full_name='binlogdata.VStreamRowsResponse.fields', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='pkfields', full_name='binlogdata.VStreamRowsResponse.pkfields', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='gtid', full_name='binlogdata.VStreamRowsResponse.gtid', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rows', full_name='binlogdata.VStreamRowsResponse.rows', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='lastpk', full_name='binlogdata.VStreamRowsResponse.lastpk', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2780, - serialized_end=2931, -) - - -_VSTREAMRESULTSREQUEST = _descriptor.Descriptor( - name='VStreamResultsRequest', - full_name='binlogdata.VStreamResultsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='binlogdata.VStreamResultsRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='binlogdata.VStreamResultsRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='binlogdata.VStreamResultsRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='binlogdata.VStreamResultsRequest.query', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2934, - serialized_end=3101, -) - - -_VSTREAMRESULTSRESPONSE = _descriptor.Descriptor( - name='VStreamResultsResponse', - full_name='binlogdata.VStreamResultsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='fields', full_name='binlogdata.VStreamResultsResponse.fields', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='gtid', full_name='binlogdata.VStreamResultsResponse.gtid', index=1, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rows', full_name='binlogdata.VStreamResultsResponse.rows', index=2, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3103, - serialized_end=3197, -) - -_BINLOGTRANSACTION_STATEMENT.fields_by_name['category'].enum_type = _BINLOGTRANSACTION_STATEMENT_CATEGORY -_BINLOGTRANSACTION_STATEMENT.fields_by_name['charset'].message_type = _CHARSET -_BINLOGTRANSACTION_STATEMENT.containing_type = _BINLOGTRANSACTION -_BINLOGTRANSACTION_STATEMENT_CATEGORY.containing_type = _BINLOGTRANSACTION_STATEMENT -_BINLOGTRANSACTION.fields_by_name['statements'].message_type = _BINLOGTRANSACTION_STATEMENT -_BINLOGTRANSACTION.fields_by_name['event_token'].message_type = query__pb2._EVENTTOKEN -_STREAMKEYRANGEREQUEST.fields_by_name['key_range'].message_type = topodata__pb2._KEYRANGE -_STREAMKEYRANGEREQUEST.fields_by_name['charset'].message_type = _CHARSET -_STREAMKEYRANGERESPONSE.fields_by_name['binlog_transaction'].message_type = _BINLOGTRANSACTION -_STREAMTABLESREQUEST.fields_by_name['charset'].message_type = _CHARSET -_STREAMTABLESRESPONSE.fields_by_name['binlog_transaction'].message_type = _BINLOGTRANSACTION -_FILTER.fields_by_name['rules'].message_type = _RULE -_FILTER.fields_by_name['fieldEventMode'].enum_type = _FILTER_FIELDEVENTMODE -_FILTER_FIELDEVENTMODE.containing_type = _FILTER -_BINLOGSOURCE.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_BINLOGSOURCE.fields_by_name['key_range'].message_type = topodata__pb2._KEYRANGE -_BINLOGSOURCE.fields_by_name['filter'].message_type = _FILTER -_BINLOGSOURCE.fields_by_name['on_ddl'].enum_type = _ONDDLACTION -_ROWCHANGE.fields_by_name['before'].message_type = query__pb2._ROW -_ROWCHANGE.fields_by_name['after'].message_type = query__pb2._ROW -_ROWEVENT.fields_by_name['row_changes'].message_type = _ROWCHANGE -_FIELDEVENT.fields_by_name['fields'].message_type = query__pb2._FIELD -_VGTID.fields_by_name['shard_gtids'].message_type = _SHARDGTID -_JOURNAL.fields_by_name['migration_type'].enum_type = _MIGRATIONTYPE -_JOURNAL.fields_by_name['shard_gtids'].message_type = _SHARDGTID -_JOURNAL.fields_by_name['participants'].message_type = _KEYSPACESHARD -_VEVENT.fields_by_name['type'].enum_type = _VEVENTTYPE -_VEVENT.fields_by_name['row_event'].message_type = _ROWEVENT -_VEVENT.fields_by_name['field_event'].message_type = _FIELDEVENT -_VEVENT.fields_by_name['vgtid'].message_type = _VGTID -_VEVENT.fields_by_name['journal'].message_type = _JOURNAL -_VSTREAMREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_VSTREAMREQUEST.fields_by_name['immediate_caller_id'].message_type = query__pb2._VTGATECALLERID -_VSTREAMREQUEST.fields_by_name['target'].message_type = query__pb2._TARGET -_VSTREAMREQUEST.fields_by_name['filter'].message_type = _FILTER -_VSTREAMRESPONSE.fields_by_name['events'].message_type = _VEVENT -_VSTREAMROWSREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_VSTREAMROWSREQUEST.fields_by_name['immediate_caller_id'].message_type = query__pb2._VTGATECALLERID -_VSTREAMROWSREQUEST.fields_by_name['target'].message_type = query__pb2._TARGET -_VSTREAMROWSREQUEST.fields_by_name['lastpk'].message_type = query__pb2._QUERYRESULT -_VSTREAMROWSRESPONSE.fields_by_name['fields'].message_type = query__pb2._FIELD -_VSTREAMROWSRESPONSE.fields_by_name['pkfields'].message_type = query__pb2._FIELD -_VSTREAMROWSRESPONSE.fields_by_name['rows'].message_type = query__pb2._ROW -_VSTREAMROWSRESPONSE.fields_by_name['lastpk'].message_type = query__pb2._ROW -_VSTREAMRESULTSREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_VSTREAMRESULTSREQUEST.fields_by_name['immediate_caller_id'].message_type = query__pb2._VTGATECALLERID -_VSTREAMRESULTSREQUEST.fields_by_name['target'].message_type = query__pb2._TARGET -_VSTREAMRESULTSRESPONSE.fields_by_name['fields'].message_type = query__pb2._FIELD -_VSTREAMRESULTSRESPONSE.fields_by_name['rows'].message_type = query__pb2._ROW -DESCRIPTOR.message_types_by_name['Charset'] = _CHARSET -DESCRIPTOR.message_types_by_name['BinlogTransaction'] = _BINLOGTRANSACTION -DESCRIPTOR.message_types_by_name['StreamKeyRangeRequest'] = _STREAMKEYRANGEREQUEST -DESCRIPTOR.message_types_by_name['StreamKeyRangeResponse'] = _STREAMKEYRANGERESPONSE -DESCRIPTOR.message_types_by_name['StreamTablesRequest'] = _STREAMTABLESREQUEST -DESCRIPTOR.message_types_by_name['StreamTablesResponse'] = _STREAMTABLESRESPONSE -DESCRIPTOR.message_types_by_name['Rule'] = _RULE -DESCRIPTOR.message_types_by_name['Filter'] = _FILTER -DESCRIPTOR.message_types_by_name['BinlogSource'] = _BINLOGSOURCE -DESCRIPTOR.message_types_by_name['RowChange'] = _ROWCHANGE -DESCRIPTOR.message_types_by_name['RowEvent'] = _ROWEVENT -DESCRIPTOR.message_types_by_name['FieldEvent'] = _FIELDEVENT -DESCRIPTOR.message_types_by_name['ShardGtid'] = _SHARDGTID -DESCRIPTOR.message_types_by_name['VGtid'] = _VGTID -DESCRIPTOR.message_types_by_name['KeyspaceShard'] = _KEYSPACESHARD -DESCRIPTOR.message_types_by_name['Journal'] = _JOURNAL -DESCRIPTOR.message_types_by_name['VEvent'] = _VEVENT -DESCRIPTOR.message_types_by_name['VStreamRequest'] = _VSTREAMREQUEST -DESCRIPTOR.message_types_by_name['VStreamResponse'] = _VSTREAMRESPONSE -DESCRIPTOR.message_types_by_name['VStreamRowsRequest'] = _VSTREAMROWSREQUEST -DESCRIPTOR.message_types_by_name['VStreamRowsResponse'] = _VSTREAMROWSRESPONSE -DESCRIPTOR.message_types_by_name['VStreamResultsRequest'] = _VSTREAMRESULTSREQUEST -DESCRIPTOR.message_types_by_name['VStreamResultsResponse'] = _VSTREAMRESULTSRESPONSE -DESCRIPTOR.enum_types_by_name['OnDDLAction'] = _ONDDLACTION -DESCRIPTOR.enum_types_by_name['VEventType'] = _VEVENTTYPE -DESCRIPTOR.enum_types_by_name['MigrationType'] = _MIGRATIONTYPE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Charset = _reflection.GeneratedProtocolMessageType('Charset', (_message.Message,), dict( - DESCRIPTOR = _CHARSET, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.Charset) - )) -_sym_db.RegisterMessage(Charset) - -BinlogTransaction = _reflection.GeneratedProtocolMessageType('BinlogTransaction', (_message.Message,), dict( - - Statement = _reflection.GeneratedProtocolMessageType('Statement', (_message.Message,), dict( - DESCRIPTOR = _BINLOGTRANSACTION_STATEMENT, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.BinlogTransaction.Statement) - )) - , - DESCRIPTOR = _BINLOGTRANSACTION, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.BinlogTransaction) - )) -_sym_db.RegisterMessage(BinlogTransaction) -_sym_db.RegisterMessage(BinlogTransaction.Statement) - -StreamKeyRangeRequest = _reflection.GeneratedProtocolMessageType('StreamKeyRangeRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMKEYRANGEREQUEST, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.StreamKeyRangeRequest) - )) -_sym_db.RegisterMessage(StreamKeyRangeRequest) - -StreamKeyRangeResponse = _reflection.GeneratedProtocolMessageType('StreamKeyRangeResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMKEYRANGERESPONSE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.StreamKeyRangeResponse) - )) -_sym_db.RegisterMessage(StreamKeyRangeResponse) - -StreamTablesRequest = _reflection.GeneratedProtocolMessageType('StreamTablesRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMTABLESREQUEST, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.StreamTablesRequest) - )) -_sym_db.RegisterMessage(StreamTablesRequest) - -StreamTablesResponse = _reflection.GeneratedProtocolMessageType('StreamTablesResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMTABLESRESPONSE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.StreamTablesResponse) - )) -_sym_db.RegisterMessage(StreamTablesResponse) - -Rule = _reflection.GeneratedProtocolMessageType('Rule', (_message.Message,), dict( - DESCRIPTOR = _RULE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.Rule) - )) -_sym_db.RegisterMessage(Rule) - -Filter = _reflection.GeneratedProtocolMessageType('Filter', (_message.Message,), dict( - DESCRIPTOR = _FILTER, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.Filter) - )) -_sym_db.RegisterMessage(Filter) - -BinlogSource = _reflection.GeneratedProtocolMessageType('BinlogSource', (_message.Message,), dict( - DESCRIPTOR = _BINLOGSOURCE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.BinlogSource) - )) -_sym_db.RegisterMessage(BinlogSource) - -RowChange = _reflection.GeneratedProtocolMessageType('RowChange', (_message.Message,), dict( - DESCRIPTOR = _ROWCHANGE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.RowChange) - )) -_sym_db.RegisterMessage(RowChange) - -RowEvent = _reflection.GeneratedProtocolMessageType('RowEvent', (_message.Message,), dict( - DESCRIPTOR = _ROWEVENT, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.RowEvent) - )) -_sym_db.RegisterMessage(RowEvent) - -FieldEvent = _reflection.GeneratedProtocolMessageType('FieldEvent', (_message.Message,), dict( - DESCRIPTOR = _FIELDEVENT, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.FieldEvent) - )) -_sym_db.RegisterMessage(FieldEvent) - -ShardGtid = _reflection.GeneratedProtocolMessageType('ShardGtid', (_message.Message,), dict( - DESCRIPTOR = _SHARDGTID, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.ShardGtid) - )) -_sym_db.RegisterMessage(ShardGtid) - -VGtid = _reflection.GeneratedProtocolMessageType('VGtid', (_message.Message,), dict( - DESCRIPTOR = _VGTID, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VGtid) - )) -_sym_db.RegisterMessage(VGtid) - -KeyspaceShard = _reflection.GeneratedProtocolMessageType('KeyspaceShard', (_message.Message,), dict( - DESCRIPTOR = _KEYSPACESHARD, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.KeyspaceShard) - )) -_sym_db.RegisterMessage(KeyspaceShard) - -Journal = _reflection.GeneratedProtocolMessageType('Journal', (_message.Message,), dict( - DESCRIPTOR = _JOURNAL, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.Journal) - )) -_sym_db.RegisterMessage(Journal) - -VEvent = _reflection.GeneratedProtocolMessageType('VEvent', (_message.Message,), dict( - DESCRIPTOR = _VEVENT, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VEvent) - )) -_sym_db.RegisterMessage(VEvent) - -VStreamRequest = _reflection.GeneratedProtocolMessageType('VStreamRequest', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMREQUEST, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VStreamRequest) - )) -_sym_db.RegisterMessage(VStreamRequest) - -VStreamResponse = _reflection.GeneratedProtocolMessageType('VStreamResponse', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMRESPONSE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VStreamResponse) - )) -_sym_db.RegisterMessage(VStreamResponse) - -VStreamRowsRequest = _reflection.GeneratedProtocolMessageType('VStreamRowsRequest', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMROWSREQUEST, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VStreamRowsRequest) - )) -_sym_db.RegisterMessage(VStreamRowsRequest) - -VStreamRowsResponse = _reflection.GeneratedProtocolMessageType('VStreamRowsResponse', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMROWSRESPONSE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VStreamRowsResponse) - )) -_sym_db.RegisterMessage(VStreamRowsResponse) - -VStreamResultsRequest = _reflection.GeneratedProtocolMessageType('VStreamResultsRequest', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMRESULTSREQUEST, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VStreamResultsRequest) - )) -_sym_db.RegisterMessage(VStreamResultsRequest) - -VStreamResultsResponse = _reflection.GeneratedProtocolMessageType('VStreamResultsResponse', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMRESULTSRESPONSE, - __module__ = 'binlogdata_pb2' - # @@protoc_insertion_point(class_scope:binlogdata.VStreamResultsResponse) - )) -_sym_db.RegisterMessage(VStreamResultsResponse) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/binlogdata_pb2_grpc.py b/py/vtproto/binlogdata_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/binlogdata_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/binlogservice_pb2.py b/py/vtproto/binlogservice_pb2.py deleted file mode 100644 index 15b916eeb5f..00000000000 --- a/py/vtproto/binlogservice_pb2.py +++ /dev/null @@ -1,66 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: binlogservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import binlogdata_pb2 as binlogdata__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='binlogservice.proto', - package='binlogservice', - syntax='proto3', - serialized_options=_b('Z*vitess.io/vitess/go/vt/proto/binlogservice'), - serialized_pb=_b('\n\x13\x62inlogservice.proto\x12\rbinlogservice\x1a\x10\x62inlogdata.proto2\xc2\x01\n\x0cUpdateStream\x12[\n\x0eStreamKeyRange\x12!.binlogdata.StreamKeyRangeRequest\x1a\".binlogdata.StreamKeyRangeResponse\"\x00\x30\x01\x12U\n\x0cStreamTables\x12\x1f.binlogdata.StreamTablesRequest\x1a .binlogdata.StreamTablesResponse\"\x00\x30\x01\x42,Z*vitess.io/vitess/go/vt/proto/binlogserviceb\x06proto3') - , - dependencies=[binlogdata__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_UPDATESTREAM = _descriptor.ServiceDescriptor( - name='UpdateStream', - full_name='binlogservice.UpdateStream', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=57, - serialized_end=251, - methods=[ - _descriptor.MethodDescriptor( - name='StreamKeyRange', - full_name='binlogservice.UpdateStream.StreamKeyRange', - index=0, - containing_service=None, - input_type=binlogdata__pb2._STREAMKEYRANGEREQUEST, - output_type=binlogdata__pb2._STREAMKEYRANGERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StreamTables', - full_name='binlogservice.UpdateStream.StreamTables', - index=1, - containing_service=None, - input_type=binlogdata__pb2._STREAMTABLESREQUEST, - output_type=binlogdata__pb2._STREAMTABLESRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_UPDATESTREAM) - -DESCRIPTOR.services_by_name['UpdateStream'] = _UPDATESTREAM - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/binlogservice_pb2_grpc.py b/py/vtproto/binlogservice_pb2_grpc.py deleted file mode 100644 index 47892e53daa..00000000000 --- a/py/vtproto/binlogservice_pb2_grpc.py +++ /dev/null @@ -1,65 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import binlogdata_pb2 as binlogdata__pb2 - - -class UpdateStreamStub(object): - """UpdateStream is the RPC version of binlog.UpdateStream. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.StreamKeyRange = channel.unary_stream( - '/binlogservice.UpdateStream/StreamKeyRange', - request_serializer=binlogdata__pb2.StreamKeyRangeRequest.SerializeToString, - response_deserializer=binlogdata__pb2.StreamKeyRangeResponse.FromString, - ) - self.StreamTables = channel.unary_stream( - '/binlogservice.UpdateStream/StreamTables', - request_serializer=binlogdata__pb2.StreamTablesRequest.SerializeToString, - response_deserializer=binlogdata__pb2.StreamTablesResponse.FromString, - ) - - -class UpdateStreamServicer(object): - """UpdateStream is the RPC version of binlog.UpdateStream. - """ - - def StreamKeyRange(self, request, context): - """StreamKeyRange returns the binlog transactions related to - the specified Keyrange. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StreamTables(self, request, context): - """StreamTables returns the binlog transactions related to - the specified Tables. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_UpdateStreamServicer_to_server(servicer, server): - rpc_method_handlers = { - 'StreamKeyRange': grpc.unary_stream_rpc_method_handler( - servicer.StreamKeyRange, - request_deserializer=binlogdata__pb2.StreamKeyRangeRequest.FromString, - response_serializer=binlogdata__pb2.StreamKeyRangeResponse.SerializeToString, - ), - 'StreamTables': grpc.unary_stream_rpc_method_handler( - servicer.StreamTables, - request_deserializer=binlogdata__pb2.StreamTablesRequest.FromString, - response_serializer=binlogdata__pb2.StreamTablesResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'binlogservice.UpdateStream', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/logutil_pb2.py b/py/vtproto/logutil_pb2.py deleted file mode 100644 index 860905721dc..00000000000 --- a/py/vtproto/logutil_pb2.py +++ /dev/null @@ -1,139 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: logutil.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import vttime_pb2 as vttime__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='logutil.proto', - package='logutil', - syntax='proto3', - serialized_options=_b('Z$vitess.io/vitess/go/vt/proto/logutil'), - serialized_pb=_b('\n\rlogutil.proto\x12\x07logutil\x1a\x0cvttime.proto\"m\n\x05\x45vent\x12\x1a\n\x04time\x18\x01 \x01(\x0b\x32\x0c.vttime.Time\x12\x1d\n\x05level\x18\x02 \x01(\x0e\x32\x0e.logutil.Level\x12\x0c\n\x04\x66ile\x18\x03 \x01(\t\x12\x0c\n\x04line\x18\x04 \x01(\x03\x12\r\n\x05value\x18\x05 \x01(\t*6\n\x05Level\x12\x08\n\x04INFO\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\t\n\x05\x45RROR\x10\x02\x12\x0b\n\x07\x43ONSOLE\x10\x03\x42&Z$vitess.io/vitess/go/vt/proto/logutilb\x06proto3') - , - dependencies=[vttime__pb2.DESCRIPTOR,]) - -_LEVEL = _descriptor.EnumDescriptor( - name='Level', - full_name='logutil.Level', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='INFO', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='WARNING', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ERROR', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CONSOLE', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=151, - serialized_end=205, -) -_sym_db.RegisterEnumDescriptor(_LEVEL) - -Level = enum_type_wrapper.EnumTypeWrapper(_LEVEL) -INFO = 0 -WARNING = 1 -ERROR = 2 -CONSOLE = 3 - - - -_EVENT = _descriptor.Descriptor( - name='Event', - full_name='logutil.Event', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='time', full_name='logutil.Event.time', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='level', full_name='logutil.Event.level', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='file', full_name='logutil.Event.file', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='line', full_name='logutil.Event.line', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='logutil.Event.value', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=40, - serialized_end=149, -) - -_EVENT.fields_by_name['time'].message_type = vttime__pb2._TIME -_EVENT.fields_by_name['level'].enum_type = _LEVEL -DESCRIPTOR.message_types_by_name['Event'] = _EVENT -DESCRIPTOR.enum_types_by_name['Level'] = _LEVEL -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), dict( - DESCRIPTOR = _EVENT, - __module__ = 'logutil_pb2' - # @@protoc_insertion_point(class_scope:logutil.Event) - )) -_sym_db.RegisterMessage(Event) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/logutil_pb2_grpc.py b/py/vtproto/logutil_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/logutil_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/mysqlctl_pb2.py b/py/vtproto/mysqlctl_pb2.py deleted file mode 100644 index 5af5c27283b..00000000000 --- a/py/vtproto/mysqlctl_pb2.py +++ /dev/null @@ -1,425 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: mysqlctl.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='mysqlctl.proto', - package='mysqlctl', - syntax='proto3', - serialized_options=_b('Z%vitess.io/vitess/go/vt/proto/mysqlctl'), - serialized_pb=_b('\n\x0emysqlctl.proto\x12\x08mysqlctl\"#\n\x0cStartRequest\x12\x13\n\x0bmysqld_args\x18\x01 \x03(\t\"\x0f\n\rStartResponse\"*\n\x0fShutdownRequest\x12\x17\n\x0fwait_for_mysqld\x18\x01 \x01(\x08\"\x12\n\x10ShutdownResponse\"\x18\n\x16RunMysqlUpgradeRequest\"\x19\n\x17RunMysqlUpgradeResponse\"\x15\n\x13ReinitConfigRequest\"\x16\n\x14ReinitConfigResponse\"\x16\n\x14RefreshConfigRequest\"\x17\n\x15RefreshConfigResponse2\x8a\x03\n\x08MysqlCtl\x12:\n\x05Start\x12\x16.mysqlctl.StartRequest\x1a\x17.mysqlctl.StartResponse\"\x00\x12\x43\n\x08Shutdown\x12\x19.mysqlctl.ShutdownRequest\x1a\x1a.mysqlctl.ShutdownResponse\"\x00\x12X\n\x0fRunMysqlUpgrade\x12 .mysqlctl.RunMysqlUpgradeRequest\x1a!.mysqlctl.RunMysqlUpgradeResponse\"\x00\x12O\n\x0cReinitConfig\x12\x1d.mysqlctl.ReinitConfigRequest\x1a\x1e.mysqlctl.ReinitConfigResponse\"\x00\x12R\n\rRefreshConfig\x12\x1e.mysqlctl.RefreshConfigRequest\x1a\x1f.mysqlctl.RefreshConfigResponse\"\x00\x42\'Z%vitess.io/vitess/go/vt/proto/mysqlctlb\x06proto3') -) - - - - -_STARTREQUEST = _descriptor.Descriptor( - name='StartRequest', - full_name='mysqlctl.StartRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='mysqld_args', full_name='mysqlctl.StartRequest.mysqld_args', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=28, - serialized_end=63, -) - - -_STARTRESPONSE = _descriptor.Descriptor( - name='StartResponse', - full_name='mysqlctl.StartResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=65, - serialized_end=80, -) - - -_SHUTDOWNREQUEST = _descriptor.Descriptor( - name='ShutdownRequest', - full_name='mysqlctl.ShutdownRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='wait_for_mysqld', full_name='mysqlctl.ShutdownRequest.wait_for_mysqld', index=0, - number=1, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=82, - serialized_end=124, -) - - -_SHUTDOWNRESPONSE = _descriptor.Descriptor( - name='ShutdownResponse', - full_name='mysqlctl.ShutdownResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=126, - serialized_end=144, -) - - -_RUNMYSQLUPGRADEREQUEST = _descriptor.Descriptor( - name='RunMysqlUpgradeRequest', - full_name='mysqlctl.RunMysqlUpgradeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=146, - serialized_end=170, -) - - -_RUNMYSQLUPGRADERESPONSE = _descriptor.Descriptor( - name='RunMysqlUpgradeResponse', - full_name='mysqlctl.RunMysqlUpgradeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=172, - serialized_end=197, -) - - -_REINITCONFIGREQUEST = _descriptor.Descriptor( - name='ReinitConfigRequest', - full_name='mysqlctl.ReinitConfigRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=199, - serialized_end=220, -) - - -_REINITCONFIGRESPONSE = _descriptor.Descriptor( - name='ReinitConfigResponse', - full_name='mysqlctl.ReinitConfigResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=222, - serialized_end=244, -) - - -_REFRESHCONFIGREQUEST = _descriptor.Descriptor( - name='RefreshConfigRequest', - full_name='mysqlctl.RefreshConfigRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=246, - serialized_end=268, -) - - -_REFRESHCONFIGRESPONSE = _descriptor.Descriptor( - name='RefreshConfigResponse', - full_name='mysqlctl.RefreshConfigResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=270, - serialized_end=293, -) - -DESCRIPTOR.message_types_by_name['StartRequest'] = _STARTREQUEST -DESCRIPTOR.message_types_by_name['StartResponse'] = _STARTRESPONSE -DESCRIPTOR.message_types_by_name['ShutdownRequest'] = _SHUTDOWNREQUEST -DESCRIPTOR.message_types_by_name['ShutdownResponse'] = _SHUTDOWNRESPONSE -DESCRIPTOR.message_types_by_name['RunMysqlUpgradeRequest'] = _RUNMYSQLUPGRADEREQUEST -DESCRIPTOR.message_types_by_name['RunMysqlUpgradeResponse'] = _RUNMYSQLUPGRADERESPONSE -DESCRIPTOR.message_types_by_name['ReinitConfigRequest'] = _REINITCONFIGREQUEST -DESCRIPTOR.message_types_by_name['ReinitConfigResponse'] = _REINITCONFIGRESPONSE -DESCRIPTOR.message_types_by_name['RefreshConfigRequest'] = _REFRESHCONFIGREQUEST -DESCRIPTOR.message_types_by_name['RefreshConfigResponse'] = _REFRESHCONFIGRESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -StartRequest = _reflection.GeneratedProtocolMessageType('StartRequest', (_message.Message,), dict( - DESCRIPTOR = _STARTREQUEST, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.StartRequest) - )) -_sym_db.RegisterMessage(StartRequest) - -StartResponse = _reflection.GeneratedProtocolMessageType('StartResponse', (_message.Message,), dict( - DESCRIPTOR = _STARTRESPONSE, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.StartResponse) - )) -_sym_db.RegisterMessage(StartResponse) - -ShutdownRequest = _reflection.GeneratedProtocolMessageType('ShutdownRequest', (_message.Message,), dict( - DESCRIPTOR = _SHUTDOWNREQUEST, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.ShutdownRequest) - )) -_sym_db.RegisterMessage(ShutdownRequest) - -ShutdownResponse = _reflection.GeneratedProtocolMessageType('ShutdownResponse', (_message.Message,), dict( - DESCRIPTOR = _SHUTDOWNRESPONSE, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.ShutdownResponse) - )) -_sym_db.RegisterMessage(ShutdownResponse) - -RunMysqlUpgradeRequest = _reflection.GeneratedProtocolMessageType('RunMysqlUpgradeRequest', (_message.Message,), dict( - DESCRIPTOR = _RUNMYSQLUPGRADEREQUEST, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.RunMysqlUpgradeRequest) - )) -_sym_db.RegisterMessage(RunMysqlUpgradeRequest) - -RunMysqlUpgradeResponse = _reflection.GeneratedProtocolMessageType('RunMysqlUpgradeResponse', (_message.Message,), dict( - DESCRIPTOR = _RUNMYSQLUPGRADERESPONSE, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.RunMysqlUpgradeResponse) - )) -_sym_db.RegisterMessage(RunMysqlUpgradeResponse) - -ReinitConfigRequest = _reflection.GeneratedProtocolMessageType('ReinitConfigRequest', (_message.Message,), dict( - DESCRIPTOR = _REINITCONFIGREQUEST, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.ReinitConfigRequest) - )) -_sym_db.RegisterMessage(ReinitConfigRequest) - -ReinitConfigResponse = _reflection.GeneratedProtocolMessageType('ReinitConfigResponse', (_message.Message,), dict( - DESCRIPTOR = _REINITCONFIGRESPONSE, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.ReinitConfigResponse) - )) -_sym_db.RegisterMessage(ReinitConfigResponse) - -RefreshConfigRequest = _reflection.GeneratedProtocolMessageType('RefreshConfigRequest', (_message.Message,), dict( - DESCRIPTOR = _REFRESHCONFIGREQUEST, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.RefreshConfigRequest) - )) -_sym_db.RegisterMessage(RefreshConfigRequest) - -RefreshConfigResponse = _reflection.GeneratedProtocolMessageType('RefreshConfigResponse', (_message.Message,), dict( - DESCRIPTOR = _REFRESHCONFIGRESPONSE, - __module__ = 'mysqlctl_pb2' - # @@protoc_insertion_point(class_scope:mysqlctl.RefreshConfigResponse) - )) -_sym_db.RegisterMessage(RefreshConfigResponse) - - -DESCRIPTOR._options = None - -_MYSQLCTL = _descriptor.ServiceDescriptor( - name='MysqlCtl', - full_name='mysqlctl.MysqlCtl', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=296, - serialized_end=690, - methods=[ - _descriptor.MethodDescriptor( - name='Start', - full_name='mysqlctl.MysqlCtl.Start', - index=0, - containing_service=None, - input_type=_STARTREQUEST, - output_type=_STARTRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Shutdown', - full_name='mysqlctl.MysqlCtl.Shutdown', - index=1, - containing_service=None, - input_type=_SHUTDOWNREQUEST, - output_type=_SHUTDOWNRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='RunMysqlUpgrade', - full_name='mysqlctl.MysqlCtl.RunMysqlUpgrade', - index=2, - containing_service=None, - input_type=_RUNMYSQLUPGRADEREQUEST, - output_type=_RUNMYSQLUPGRADERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ReinitConfig', - full_name='mysqlctl.MysqlCtl.ReinitConfig', - index=3, - containing_service=None, - input_type=_REINITCONFIGREQUEST, - output_type=_REINITCONFIGRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='RefreshConfig', - full_name='mysqlctl.MysqlCtl.RefreshConfig', - index=4, - containing_service=None, - input_type=_REFRESHCONFIGREQUEST, - output_type=_REFRESHCONFIGRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_MYSQLCTL) - -DESCRIPTOR.services_by_name['MysqlCtl'] = _MYSQLCTL - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/mysqlctl_pb2_grpc.py b/py/vtproto/mysqlctl_pb2_grpc.py deleted file mode 100644 index 62b61e21a1c..00000000000 --- a/py/vtproto/mysqlctl_pb2_grpc.py +++ /dev/null @@ -1,114 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import mysqlctl_pb2 as mysqlctl__pb2 - - -class MysqlCtlStub(object): - """MysqlCtl is the service definition - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.Start = channel.unary_unary( - '/mysqlctl.MysqlCtl/Start', - request_serializer=mysqlctl__pb2.StartRequest.SerializeToString, - response_deserializer=mysqlctl__pb2.StartResponse.FromString, - ) - self.Shutdown = channel.unary_unary( - '/mysqlctl.MysqlCtl/Shutdown', - request_serializer=mysqlctl__pb2.ShutdownRequest.SerializeToString, - response_deserializer=mysqlctl__pb2.ShutdownResponse.FromString, - ) - self.RunMysqlUpgrade = channel.unary_unary( - '/mysqlctl.MysqlCtl/RunMysqlUpgrade', - request_serializer=mysqlctl__pb2.RunMysqlUpgradeRequest.SerializeToString, - response_deserializer=mysqlctl__pb2.RunMysqlUpgradeResponse.FromString, - ) - self.ReinitConfig = channel.unary_unary( - '/mysqlctl.MysqlCtl/ReinitConfig', - request_serializer=mysqlctl__pb2.ReinitConfigRequest.SerializeToString, - response_deserializer=mysqlctl__pb2.ReinitConfigResponse.FromString, - ) - self.RefreshConfig = channel.unary_unary( - '/mysqlctl.MysqlCtl/RefreshConfig', - request_serializer=mysqlctl__pb2.RefreshConfigRequest.SerializeToString, - response_deserializer=mysqlctl__pb2.RefreshConfigResponse.FromString, - ) - - -class MysqlCtlServicer(object): - """MysqlCtl is the service definition - """ - - def Start(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Shutdown(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def RunMysqlUpgrade(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ReinitConfig(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def RefreshConfig(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_MysqlCtlServicer_to_server(servicer, server): - rpc_method_handlers = { - 'Start': grpc.unary_unary_rpc_method_handler( - servicer.Start, - request_deserializer=mysqlctl__pb2.StartRequest.FromString, - response_serializer=mysqlctl__pb2.StartResponse.SerializeToString, - ), - 'Shutdown': grpc.unary_unary_rpc_method_handler( - servicer.Shutdown, - request_deserializer=mysqlctl__pb2.ShutdownRequest.FromString, - response_serializer=mysqlctl__pb2.ShutdownResponse.SerializeToString, - ), - 'RunMysqlUpgrade': grpc.unary_unary_rpc_method_handler( - servicer.RunMysqlUpgrade, - request_deserializer=mysqlctl__pb2.RunMysqlUpgradeRequest.FromString, - response_serializer=mysqlctl__pb2.RunMysqlUpgradeResponse.SerializeToString, - ), - 'ReinitConfig': grpc.unary_unary_rpc_method_handler( - servicer.ReinitConfig, - request_deserializer=mysqlctl__pb2.ReinitConfigRequest.FromString, - response_serializer=mysqlctl__pb2.ReinitConfigResponse.SerializeToString, - ), - 'RefreshConfig': grpc.unary_unary_rpc_method_handler( - servicer.RefreshConfig, - request_deserializer=mysqlctl__pb2.RefreshConfigRequest.FromString, - response_serializer=mysqlctl__pb2.RefreshConfigResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'mysqlctl.MysqlCtl', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/query_pb2.py b/py/vtproto/query_pb2.py deleted file mode 100644 index 78d45df8f25..00000000000 --- a/py/vtproto/query_pb2.py +++ /dev/null @@ -1,4022 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: query.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import topodata_pb2 as topodata__pb2 -import vtrpc_pb2 as vtrpc__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='query.proto', - package='query', - syntax='proto3', - serialized_options=_b('\n\017io.vitess.protoZ\"vitess.io/vitess/go/vt/proto/query'), - serialized_pb=_b('\n\x0bquery.proto\x12\x05query\x1a\x0etopodata.proto\x1a\x0bvtrpc.proto\"b\n\x06Target\x12\x10\n\x08keyspace\x18\x01 \x01(\t\x12\r\n\x05shard\x18\x02 \x01(\t\x12)\n\x0btablet_type\x18\x03 \x01(\x0e\x32\x14.topodata.TabletType\x12\x0c\n\x04\x63\x65ll\x18\x04 \x01(\t\"2\n\x0eVTGateCallerID\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x0e\n\x06groups\x18\x02 \x03(\t\"@\n\nEventToken\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\r\n\x05shard\x18\x02 \x01(\t\x12\x10\n\x08position\x18\x03 \x01(\t\"1\n\x05Value\x12\x19\n\x04type\x18\x01 \x01(\x0e\x32\x0b.query.Type\x12\r\n\x05value\x18\x02 \x01(\x0c\"V\n\x0c\x42indVariable\x12\x19\n\x04type\x18\x01 \x01(\x0e\x32\x0b.query.Type\x12\r\n\x05value\x18\x02 \x01(\x0c\x12\x1c\n\x06values\x18\x03 \x03(\x0b\x32\x0c.query.Value\"\xa2\x01\n\nBoundQuery\x12\x0b\n\x03sql\x18\x01 \x01(\t\x12<\n\x0e\x62ind_variables\x18\x02 \x03(\x0b\x32$.query.BoundQuery.BindVariablesEntry\x1aI\n\x12\x42indVariablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.query.BindVariable:\x02\x38\x01\"\x94\x05\n\x0e\x45xecuteOptions\x12\x1b\n\x13include_event_token\x18\x02 \x01(\x08\x12.\n\x13\x63ompare_event_token\x18\x03 \x01(\x0b\x32\x11.query.EventToken\x12=\n\x0fincluded_fields\x18\x04 \x01(\x0e\x32$.query.ExecuteOptions.IncludedFields\x12\x19\n\x11\x63lient_found_rows\x18\x05 \x01(\x08\x12\x30\n\x08workload\x18\x06 \x01(\x0e\x32\x1e.query.ExecuteOptions.Workload\x12\x18\n\x10sql_select_limit\x18\x08 \x01(\x03\x12I\n\x15transaction_isolation\x18\t \x01(\x0e\x32*.query.ExecuteOptions.TransactionIsolation\x12\x1d\n\x15skip_query_plan_cache\x18\n \x01(\x08\";\n\x0eIncludedFields\x12\x11\n\rTYPE_AND_NAME\x10\x00\x12\r\n\tTYPE_ONLY\x10\x01\x12\x07\n\x03\x41LL\x10\x02\"8\n\x08Workload\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x08\n\x04OLTP\x10\x01\x12\x08\n\x04OLAP\x10\x02\x12\x07\n\x03\x44\x42\x41\x10\x03\"\xa7\x01\n\x14TransactionIsolation\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x13\n\x0fREPEATABLE_READ\x10\x01\x12\x12\n\x0eREAD_COMMITTED\x10\x02\x12\x14\n\x10READ_UNCOMMITTED\x10\x03\x12\x10\n\x0cSERIALIZABLE\x10\x04\x12!\n\x1d\x43ONSISTENT_SNAPSHOT_READ_ONLY\x10\x05\x12\x0e\n\nAUTOCOMMIT\x10\x06J\x04\x08\x01\x10\x02\"\xbf\x01\n\x05\x46ield\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x04type\x18\x02 \x01(\x0e\x32\x0b.query.Type\x12\r\n\x05table\x18\x03 \x01(\t\x12\x11\n\torg_table\x18\x04 \x01(\t\x12\x10\n\x08\x64\x61tabase\x18\x05 \x01(\t\x12\x10\n\x08org_name\x18\x06 \x01(\t\x12\x15\n\rcolumn_length\x18\x07 \x01(\r\x12\x0f\n\x07\x63harset\x18\x08 \x01(\r\x12\x10\n\x08\x64\x65\x63imals\x18\t \x01(\r\x12\r\n\x05\x66lags\x18\n \x01(\r\"&\n\x03Row\x12\x0f\n\x07lengths\x18\x01 \x03(\x12\x12\x0e\n\x06values\x18\x02 \x01(\x0c\"G\n\x0cResultExtras\x12&\n\x0b\x65vent_token\x18\x01 \x01(\x0b\x32\x11.query.EventToken\x12\x0f\n\x07\x66resher\x18\x02 \x01(\x08\"\x94\x01\n\x0bQueryResult\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.query.Field\x12\x15\n\rrows_affected\x18\x02 \x01(\x04\x12\x11\n\tinsert_id\x18\x03 \x01(\x04\x12\x18\n\x04rows\x18\x04 \x03(\x0b\x32\n.query.Row\x12#\n\x06\x65xtras\x18\x05 \x01(\x0b\x32\x13.query.ResultExtras\"-\n\x0cQueryWarning\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xca\x02\n\x0bStreamEvent\x12\x30\n\nstatements\x18\x01 \x03(\x0b\x32\x1c.query.StreamEvent.Statement\x12&\n\x0b\x65vent_token\x18\x02 \x01(\x0b\x32\x11.query.EventToken\x1a\xe0\x01\n\tStatement\x12\x37\n\x08\x63\x61tegory\x18\x01 \x01(\x0e\x32%.query.StreamEvent.Statement.Category\x12\x12\n\ntable_name\x18\x02 \x01(\t\x12(\n\x12primary_key_fields\x18\x03 \x03(\x0b\x32\x0c.query.Field\x12&\n\x12primary_key_values\x18\x04 \x03(\x0b\x32\n.query.Row\x12\x0b\n\x03sql\x18\x05 \x01(\x0c\"\'\n\x08\x43\x61tegory\x12\t\n\x05\x45rror\x10\x00\x12\x07\n\x03\x44ML\x10\x01\x12\x07\n\x03\x44\x44L\x10\x02\"\xf3\x01\n\x0e\x45xecuteRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12 \n\x05query\x18\x04 \x01(\x0b\x32\x11.query.BoundQuery\x12\x16\n\x0etransaction_id\x18\x05 \x01(\x03\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"5\n\x0f\x45xecuteResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"U\n\x0fResultWithError\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12\"\n\x06result\x18\x02 \x01(\x0b\x32\x12.query.QueryResult\"\x92\x02\n\x13\x45xecuteBatchRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\"\n\x07queries\x18\x04 \x03(\x0b\x32\x11.query.BoundQuery\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12\x16\n\x0etransaction_id\x18\x06 \x01(\x03\x12&\n\x07options\x18\x07 \x01(\x0b\x32\x15.query.ExecuteOptions\";\n\x14\x45xecuteBatchResponse\x12#\n\x07results\x18\x01 \x03(\x0b\x32\x12.query.QueryResult\"\xf9\x01\n\x14StreamExecuteRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12 \n\x05query\x18\x04 \x01(\x0b\x32\x11.query.BoundQuery\x12&\n\x07options\x18\x05 \x01(\x0b\x32\x15.query.ExecuteOptions\x12\x16\n\x0etransaction_id\x18\x06 \x01(\x03\";\n\x15StreamExecuteResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xb7\x01\n\x0c\x42\x65ginRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.query.ExecuteOptions\"\'\n\rBeginResponse\x12\x16\n\x0etransaction_id\x18\x01 \x01(\x03\"\xa8\x01\n\rCommitRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x04 \x01(\x03\"\x10\n\x0e\x43ommitResponse\"\xaa\x01\n\x0fRollbackRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x04 \x01(\x03\"\x12\n\x10RollbackResponse\"\xb7\x01\n\x0ePrepareRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x04 \x01(\x03\x12\x0c\n\x04\x64tid\x18\x05 \x01(\t\"\x11\n\x0fPrepareResponse\"\xa6\x01\n\x15\x43ommitPreparedRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x0c\n\x04\x64tid\x18\x04 \x01(\t\"\x18\n\x16\x43ommitPreparedResponse\"\xc0\x01\n\x17RollbackPreparedRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x04 \x01(\x03\x12\x0c\n\x04\x64tid\x18\x05 \x01(\t\"\x1a\n\x18RollbackPreparedResponse\"\xce\x01\n\x18\x43reateTransactionRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x0c\n\x04\x64tid\x18\x04 \x01(\t\x12#\n\x0cparticipants\x18\x05 \x03(\x0b\x32\r.query.Target\"\x1b\n\x19\x43reateTransactionResponse\"\xbb\x01\n\x12StartCommitRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x04 \x01(\x03\x12\x0c\n\x04\x64tid\x18\x05 \x01(\t\"\x15\n\x13StartCommitResponse\"\xbb\x01\n\x12SetRollbackRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x16\n\x0etransaction_id\x18\x04 \x01(\x03\x12\x0c\n\x04\x64tid\x18\x05 \x01(\t\"\x15\n\x13SetRollbackResponse\"\xab\x01\n\x1a\x43oncludeTransactionRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x0c\n\x04\x64tid\x18\x04 \x01(\t\"\x1d\n\x1b\x43oncludeTransactionResponse\"\xa7\x01\n\x16ReadTransactionRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x0c\n\x04\x64tid\x18\x04 \x01(\t\"G\n\x17ReadTransactionResponse\x12,\n\x08metadata\x18\x01 \x01(\x0b\x32\x1a.query.TransactionMetadata\"\xe0\x01\n\x13\x42\x65ginExecuteRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12 \n\x05query\x18\x04 \x01(\x0b\x32\x11.query.BoundQuery\x12&\n\x07options\x18\x05 \x01(\x0b\x32\x15.query.ExecuteOptions\"r\n\x14\x42\x65ginExecuteResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12\"\n\x06result\x18\x02 \x01(\x0b\x32\x12.query.QueryResult\x12\x16\n\x0etransaction_id\x18\x03 \x01(\x03\"\xff\x01\n\x18\x42\x65ginExecuteBatchRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\"\n\x07queries\x18\x04 \x03(\x0b\x32\x11.query.BoundQuery\x12\x16\n\x0e\x61s_transaction\x18\x05 \x01(\x08\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\"x\n\x19\x42\x65ginExecuteBatchResponse\x12\x1e\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x0f.vtrpc.RPCError\x12#\n\x07results\x18\x02 \x03(\x0b\x32\x12.query.QueryResult\x12\x16\n\x0etransaction_id\x18\x03 \x01(\x03\"\xa5\x01\n\x14MessageStreamRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x0c\n\x04name\x18\x04 \x01(\t\";\n\x15MessageStreamResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xbd\x01\n\x11MessageAckRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x19\n\x03ids\x18\x05 \x03(\x0b\x32\x0c.query.Value\"8\n\x12MessageAckResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\xe7\x02\n\x11SplitQueryRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12 \n\x05query\x18\x04 \x01(\x0b\x32\x11.query.BoundQuery\x12\x14\n\x0csplit_column\x18\x05 \x03(\t\x12\x13\n\x0bsplit_count\x18\x06 \x01(\x03\x12\x1f\n\x17num_rows_per_query_part\x18\x08 \x01(\x03\x12\x35\n\talgorithm\x18\t \x01(\x0e\x32\".query.SplitQueryRequest.Algorithm\",\n\tAlgorithm\x12\x10\n\x0c\x45QUAL_SPLITS\x10\x00\x12\r\n\tFULL_SCAN\x10\x01\"A\n\nQuerySplit\x12 \n\x05query\x18\x01 \x01(\x0b\x32\x11.query.BoundQuery\x12\x11\n\trow_count\x18\x02 \x01(\x03\"8\n\x12SplitQueryResponse\x12\"\n\x07queries\x18\x01 \x03(\x0b\x32\x11.query.QuerySplit\"\x15\n\x13StreamHealthRequest\"\xb6\x01\n\rRealtimeStats\x12\x14\n\x0chealth_error\x18\x01 \x01(\t\x12\x1d\n\x15seconds_behind_master\x18\x02 \x01(\r\x12\x1c\n\x14\x62inlog_players_count\x18\x03 \x01(\x05\x12\x32\n*seconds_behind_master_filtered_replication\x18\x04 \x01(\x03\x12\x11\n\tcpu_usage\x18\x05 \x01(\x01\x12\x0b\n\x03qps\x18\x06 \x01(\x01\"\x94\x01\n\x0e\x41ggregateStats\x12\x1c\n\x14healthy_tablet_count\x18\x01 \x01(\x05\x12\x1e\n\x16unhealthy_tablet_count\x18\x02 \x01(\x05\x12!\n\x19seconds_behind_master_min\x18\x03 \x01(\r\x12!\n\x19seconds_behind_master_max\x18\x04 \x01(\r\"\xd7\x01\n\x14StreamHealthResponse\x12\x1d\n\x06target\x18\x01 \x01(\x0b\x32\r.query.Target\x12\x0f\n\x07serving\x18\x02 \x01(\x08\x12.\n&tablet_externally_reparented_timestamp\x18\x03 \x01(\x03\x12,\n\x0erealtime_stats\x18\x04 \x01(\x0b\x32\x14.query.RealtimeStats\x12+\n\x0ctablet_alias\x18\x05 \x01(\x0b\x32\x15.topodata.TabletAliasJ\x04\x08\x06\x10\x07\"\xbb\x01\n\x13UpdateStreamRequest\x12,\n\x13\x65\x66\x66\x65\x63tive_caller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12\x32\n\x13immediate_caller_id\x18\x02 \x01(\x0b\x32\x15.query.VTGateCallerID\x12\x1d\n\x06target\x18\x03 \x01(\x0b\x32\r.query.Target\x12\x10\n\x08position\x18\x04 \x01(\t\x12\x11\n\ttimestamp\x18\x05 \x01(\x03\"9\n\x14UpdateStreamResponse\x12!\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x12.query.StreamEvent\"\x86\x01\n\x13TransactionMetadata\x12\x0c\n\x04\x64tid\x18\x01 \x01(\t\x12&\n\x05state\x18\x02 \x01(\x0e\x32\x17.query.TransactionState\x12\x14\n\x0ctime_created\x18\x03 \x01(\x03\x12#\n\x0cparticipants\x18\x04 \x03(\x0b\x32\r.query.Target*\x92\x03\n\tMySqlFlag\x12\t\n\x05\x45MPTY\x10\x00\x12\x11\n\rNOT_NULL_FLAG\x10\x01\x12\x10\n\x0cPRI_KEY_FLAG\x10\x02\x12\x13\n\x0fUNIQUE_KEY_FLAG\x10\x04\x12\x15\n\x11MULTIPLE_KEY_FLAG\x10\x08\x12\r\n\tBLOB_FLAG\x10\x10\x12\x11\n\rUNSIGNED_FLAG\x10 \x12\x11\n\rZEROFILL_FLAG\x10@\x12\x10\n\x0b\x42INARY_FLAG\x10\x80\x01\x12\x0e\n\tENUM_FLAG\x10\x80\x02\x12\x18\n\x13\x41UTO_INCREMENT_FLAG\x10\x80\x04\x12\x13\n\x0eTIMESTAMP_FLAG\x10\x80\x08\x12\r\n\x08SET_FLAG\x10\x80\x10\x12\x1a\n\x15NO_DEFAULT_VALUE_FLAG\x10\x80 \x12\x17\n\x12ON_UPDATE_NOW_FLAG\x10\x80@\x12\x0e\n\x08NUM_FLAG\x10\x80\x80\x02\x12\x13\n\rPART_KEY_FLAG\x10\x80\x80\x01\x12\x10\n\nGROUP_FLAG\x10\x80\x80\x02\x12\x11\n\x0bUNIQUE_FLAG\x10\x80\x80\x04\x12\x11\n\x0b\x42INCMP_FLAG\x10\x80\x80\x08\x1a\x02\x10\x01*k\n\x04\x46lag\x12\x08\n\x04NONE\x10\x00\x12\x0f\n\nISINTEGRAL\x10\x80\x02\x12\x0f\n\nISUNSIGNED\x10\x80\x04\x12\x0c\n\x07ISFLOAT\x10\x80\x08\x12\r\n\x08ISQUOTED\x10\x80\x10\x12\x0b\n\x06ISTEXT\x10\x80 \x12\r\n\x08ISBINARY\x10\x80@*\x99\x03\n\x04Type\x12\r\n\tNULL_TYPE\x10\x00\x12\t\n\x04INT8\x10\x81\x02\x12\n\n\x05UINT8\x10\x82\x06\x12\n\n\x05INT16\x10\x83\x02\x12\x0b\n\x06UINT16\x10\x84\x06\x12\n\n\x05INT24\x10\x85\x02\x12\x0b\n\x06UINT24\x10\x86\x06\x12\n\n\x05INT32\x10\x87\x02\x12\x0b\n\x06UINT32\x10\x88\x06\x12\n\n\x05INT64\x10\x89\x02\x12\x0b\n\x06UINT64\x10\x8a\x06\x12\x0c\n\x07\x46LOAT32\x10\x8b\x08\x12\x0c\n\x07\x46LOAT64\x10\x8c\x08\x12\x0e\n\tTIMESTAMP\x10\x8d\x10\x12\t\n\x04\x44\x41TE\x10\x8e\x10\x12\t\n\x04TIME\x10\x8f\x10\x12\r\n\x08\x44\x41TETIME\x10\x90\x10\x12\t\n\x04YEAR\x10\x91\x06\x12\x0b\n\x07\x44\x45\x43IMAL\x10\x12\x12\t\n\x04TEXT\x10\x93\x30\x12\t\n\x04\x42LOB\x10\x94P\x12\x0c\n\x07VARCHAR\x10\x95\x30\x12\x0e\n\tVARBINARY\x10\x96P\x12\t\n\x04\x43HAR\x10\x97\x30\x12\x0b\n\x06\x42INARY\x10\x98P\x12\x08\n\x03\x42IT\x10\x99\x10\x12\t\n\x04\x45NUM\x10\x9a\x10\x12\x08\n\x03SET\x10\x9b\x10\x12\t\n\x05TUPLE\x10\x1c\x12\r\n\x08GEOMETRY\x10\x9d\x10\x12\t\n\x04JSON\x10\x9e\x10\x12\x0e\n\nEXPRESSION\x10\x1f*F\n\x10TransactionState\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0b\n\x07PREPARE\x10\x01\x12\n\n\x06\x43OMMIT\x10\x02\x12\x0c\n\x08ROLLBACK\x10\x03\x42\x35\n\x0fio.vitess.protoZ\"vitess.io/vitess/go/vt/proto/queryb\x06proto3') - , - dependencies=[topodata__pb2.DESCRIPTOR,vtrpc__pb2.DESCRIPTOR,]) - -_MYSQLFLAG = _descriptor.EnumDescriptor( - name='MySqlFlag', - full_name='query.MySqlFlag', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='EMPTY', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_NULL_FLAG', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PRI_KEY_FLAG', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNIQUE_KEY_FLAG', index=3, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MULTIPLE_KEY_FLAG', index=4, number=8, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BLOB_FLAG', index=5, number=16, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNSIGNED_FLAG', index=6, number=32, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ZEROFILL_FLAG', index=7, number=64, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BINARY_FLAG', index=8, number=128, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ENUM_FLAG', index=9, number=256, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUTO_INCREMENT_FLAG', index=10, number=512, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TIMESTAMP_FLAG', index=11, number=1024, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SET_FLAG', index=12, number=2048, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NO_DEFAULT_VALUE_FLAG', index=13, number=4096, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ON_UPDATE_NOW_FLAG', index=14, number=8192, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NUM_FLAG', index=15, number=32768, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PART_KEY_FLAG', index=16, number=16384, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='GROUP_FLAG', index=17, number=32768, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNIQUE_FLAG', index=18, number=65536, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BINCMP_FLAG', index=19, number=131072, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=_b('\020\001'), - serialized_start=8110, - serialized_end=8512, -) -_sym_db.RegisterEnumDescriptor(_MYSQLFLAG) - -MySqlFlag = enum_type_wrapper.EnumTypeWrapper(_MYSQLFLAG) -_FLAG = _descriptor.EnumDescriptor( - name='Flag', - full_name='query.Flag', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NONE', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ISINTEGRAL', index=1, number=256, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ISUNSIGNED', index=2, number=512, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ISFLOAT', index=3, number=1024, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ISQUOTED', index=4, number=2048, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ISTEXT', index=5, number=4096, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ISBINARY', index=6, number=8192, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=8514, - serialized_end=8621, -) -_sym_db.RegisterEnumDescriptor(_FLAG) - -Flag = enum_type_wrapper.EnumTypeWrapper(_FLAG) -_TYPE = _descriptor.EnumDescriptor( - name='Type', - full_name='query.Type', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NULL_TYPE', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INT8', index=1, number=257, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UINT8', index=2, number=770, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INT16', index=3, number=259, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UINT16', index=4, number=772, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INT24', index=5, number=261, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UINT24', index=6, number=774, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INT32', index=7, number=263, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UINT32', index=8, number=776, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INT64', index=9, number=265, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UINT64', index=10, number=778, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FLOAT32', index=11, number=1035, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FLOAT64', index=12, number=1036, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TIMESTAMP', index=13, number=2061, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DATE', index=14, number=2062, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TIME', index=15, number=2063, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DATETIME', index=16, number=2064, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='YEAR', index=17, number=785, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DECIMAL', index=18, number=18, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TEXT', index=19, number=6163, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BLOB', index=20, number=10260, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='VARCHAR', index=21, number=6165, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='VARBINARY', index=22, number=10262, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CHAR', index=23, number=6167, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BINARY', index=24, number=10264, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BIT', index=25, number=2073, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ENUM', index=26, number=2074, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SET', index=27, number=2075, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TUPLE', index=28, number=28, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='GEOMETRY', index=29, number=2077, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='JSON', index=30, number=2078, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPRESSION', index=31, number=31, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=8624, - serialized_end=9033, -) -_sym_db.RegisterEnumDescriptor(_TYPE) - -Type = enum_type_wrapper.EnumTypeWrapper(_TYPE) -_TRANSACTIONSTATE = _descriptor.EnumDescriptor( - name='TransactionState', - full_name='query.TransactionState', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PREPARE', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='COMMIT', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ROLLBACK', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=9035, - serialized_end=9105, -) -_sym_db.RegisterEnumDescriptor(_TRANSACTIONSTATE) - -TransactionState = enum_type_wrapper.EnumTypeWrapper(_TRANSACTIONSTATE) -EMPTY = 0 -NOT_NULL_FLAG = 1 -PRI_KEY_FLAG = 2 -UNIQUE_KEY_FLAG = 4 -MULTIPLE_KEY_FLAG = 8 -BLOB_FLAG = 16 -UNSIGNED_FLAG = 32 -ZEROFILL_FLAG = 64 -BINARY_FLAG = 128 -ENUM_FLAG = 256 -AUTO_INCREMENT_FLAG = 512 -TIMESTAMP_FLAG = 1024 -SET_FLAG = 2048 -NO_DEFAULT_VALUE_FLAG = 4096 -ON_UPDATE_NOW_FLAG = 8192 -NUM_FLAG = 32768 -PART_KEY_FLAG = 16384 -GROUP_FLAG = 32768 -UNIQUE_FLAG = 65536 -BINCMP_FLAG = 131072 -NONE = 0 -ISINTEGRAL = 256 -ISUNSIGNED = 512 -ISFLOAT = 1024 -ISQUOTED = 2048 -ISTEXT = 4096 -ISBINARY = 8192 -NULL_TYPE = 0 -INT8 = 257 -UINT8 = 770 -INT16 = 259 -UINT16 = 772 -INT24 = 261 -UINT24 = 774 -INT32 = 263 -UINT32 = 776 -INT64 = 265 -UINT64 = 778 -FLOAT32 = 1035 -FLOAT64 = 1036 -TIMESTAMP = 2061 -DATE = 2062 -TIME = 2063 -DATETIME = 2064 -YEAR = 785 -DECIMAL = 18 -TEXT = 6163 -BLOB = 10260 -VARCHAR = 6165 -VARBINARY = 10262 -CHAR = 6167 -BINARY = 10264 -BIT = 2073 -ENUM = 2074 -SET = 2075 -TUPLE = 28 -GEOMETRY = 2077 -JSON = 2078 -EXPRESSION = 31 -UNKNOWN = 0 -PREPARE = 1 -COMMIT = 2 -ROLLBACK = 3 - - -_EXECUTEOPTIONS_INCLUDEDFIELDS = _descriptor.EnumDescriptor( - name='IncludedFields', - full_name='query.ExecuteOptions.IncludedFields', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='TYPE_AND_NAME', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TYPE_ONLY', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ALL', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=941, - serialized_end=1000, -) -_sym_db.RegisterEnumDescriptor(_EXECUTEOPTIONS_INCLUDEDFIELDS) - -_EXECUTEOPTIONS_WORKLOAD = _descriptor.EnumDescriptor( - name='Workload', - full_name='query.ExecuteOptions.Workload', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNSPECIFIED', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OLTP', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OLAP', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DBA', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=1002, - serialized_end=1058, -) -_sym_db.RegisterEnumDescriptor(_EXECUTEOPTIONS_WORKLOAD) - -_EXECUTEOPTIONS_TRANSACTIONISOLATION = _descriptor.EnumDescriptor( - name='TransactionIsolation', - full_name='query.ExecuteOptions.TransactionIsolation', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='DEFAULT', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='REPEATABLE_READ', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='READ_COMMITTED', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='READ_UNCOMMITTED', index=3, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SERIALIZABLE', index=4, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CONSISTENT_SNAPSHOT_READ_ONLY', index=5, number=5, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUTOCOMMIT', index=6, number=6, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=1061, - serialized_end=1228, -) -_sym_db.RegisterEnumDescriptor(_EXECUTEOPTIONS_TRANSACTIONISOLATION) - -_STREAMEVENT_STATEMENT_CATEGORY = _descriptor.EnumDescriptor( - name='Category', - full_name='query.StreamEvent.Statement.Category', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='Error', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DML', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DDL', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=2033, - serialized_end=2072, -) -_sym_db.RegisterEnumDescriptor(_STREAMEVENT_STATEMENT_CATEGORY) - -_SPLITQUERYREQUEST_ALGORITHM = _descriptor.EnumDescriptor( - name='Algorithm', - full_name='query.SplitQueryRequest.Algorithm', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='EQUAL_SPLITS', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FULL_SCAN', index=1, number=1, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=6975, - serialized_end=7019, -) -_sym_db.RegisterEnumDescriptor(_SPLITQUERYREQUEST_ALGORITHM) - - -_TARGET = _descriptor.Descriptor( - name='Target', - full_name='query.Target', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspace', full_name='query.Target.keyspace', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='query.Target.shard', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='query.Target.tablet_type', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cell', full_name='query.Target.cell', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=51, - serialized_end=149, -) - - -_VTGATECALLERID = _descriptor.Descriptor( - name='VTGateCallerID', - full_name='query.VTGateCallerID', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='username', full_name='query.VTGateCallerID.username', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='groups', full_name='query.VTGateCallerID.groups', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=151, - serialized_end=201, -) - - -_EVENTTOKEN = _descriptor.Descriptor( - name='EventToken', - full_name='query.EventToken', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='timestamp', full_name='query.EventToken.timestamp', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='query.EventToken.shard', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='position', full_name='query.EventToken.position', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=203, - serialized_end=267, -) - - -_VALUE = _descriptor.Descriptor( - name='Value', - full_name='query.Value', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='query.Value.type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='query.Value.value', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=269, - serialized_end=318, -) - - -_BINDVARIABLE = _descriptor.Descriptor( - name='BindVariable', - full_name='query.BindVariable', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='query.BindVariable.type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='query.BindVariable.value', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='values', full_name='query.BindVariable.values', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=320, - serialized_end=406, -) - - -_BOUNDQUERY_BINDVARIABLESENTRY = _descriptor.Descriptor( - name='BindVariablesEntry', - full_name='query.BoundQuery.BindVariablesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='query.BoundQuery.BindVariablesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='query.BoundQuery.BindVariablesEntry.value', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=498, - serialized_end=571, -) - -_BOUNDQUERY = _descriptor.Descriptor( - name='BoundQuery', - full_name='query.BoundQuery', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='sql', full_name='query.BoundQuery.sql', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='bind_variables', full_name='query.BoundQuery.bind_variables', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_BOUNDQUERY_BINDVARIABLESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=409, - serialized_end=571, -) - - -_EXECUTEOPTIONS = _descriptor.Descriptor( - name='ExecuteOptions', - full_name='query.ExecuteOptions', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='include_event_token', full_name='query.ExecuteOptions.include_event_token', index=0, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='compare_event_token', full_name='query.ExecuteOptions.compare_event_token', index=1, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='included_fields', full_name='query.ExecuteOptions.included_fields', index=2, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='client_found_rows', full_name='query.ExecuteOptions.client_found_rows', index=3, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='workload', full_name='query.ExecuteOptions.workload', index=4, - number=6, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sql_select_limit', full_name='query.ExecuteOptions.sql_select_limit', index=5, - number=8, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_isolation', full_name='query.ExecuteOptions.transaction_isolation', index=6, - number=9, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='skip_query_plan_cache', full_name='query.ExecuteOptions.skip_query_plan_cache', index=7, - number=10, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _EXECUTEOPTIONS_INCLUDEDFIELDS, - _EXECUTEOPTIONS_WORKLOAD, - _EXECUTEOPTIONS_TRANSACTIONISOLATION, - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=574, - serialized_end=1234, -) - - -_FIELD = _descriptor.Descriptor( - name='Field', - full_name='query.Field', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='query.Field.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='type', full_name='query.Field.type', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='table', full_name='query.Field.table', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='org_table', full_name='query.Field.org_table', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='database', full_name='query.Field.database', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='org_name', full_name='query.Field.org_name', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='column_length', full_name='query.Field.column_length', index=6, - number=7, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='charset', full_name='query.Field.charset', index=7, - number=8, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='decimals', full_name='query.Field.decimals', index=8, - number=9, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='flags', full_name='query.Field.flags', index=9, - number=10, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1237, - serialized_end=1428, -) - - -_ROW = _descriptor.Descriptor( - name='Row', - full_name='query.Row', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='lengths', full_name='query.Row.lengths', index=0, - number=1, type=18, cpp_type=2, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='values', full_name='query.Row.values', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1430, - serialized_end=1468, -) - - -_RESULTEXTRAS = _descriptor.Descriptor( - name='ResultExtras', - full_name='query.ResultExtras', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='event_token', full_name='query.ResultExtras.event_token', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='fresher', full_name='query.ResultExtras.fresher', index=1, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1470, - serialized_end=1541, -) - - -_QUERYRESULT = _descriptor.Descriptor( - name='QueryResult', - full_name='query.QueryResult', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='fields', full_name='query.QueryResult.fields', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rows_affected', full_name='query.QueryResult.rows_affected', index=1, - number=2, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='insert_id', full_name='query.QueryResult.insert_id', index=2, - number=3, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rows', full_name='query.QueryResult.rows', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='extras', full_name='query.QueryResult.extras', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1544, - serialized_end=1692, -) - - -_QUERYWARNING = _descriptor.Descriptor( - name='QueryWarning', - full_name='query.QueryWarning', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='code', full_name='query.QueryWarning.code', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='message', full_name='query.QueryWarning.message', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1694, - serialized_end=1739, -) - - -_STREAMEVENT_STATEMENT = _descriptor.Descriptor( - name='Statement', - full_name='query.StreamEvent.Statement', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='category', full_name='query.StreamEvent.Statement.category', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='table_name', full_name='query.StreamEvent.Statement.table_name', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='primary_key_fields', full_name='query.StreamEvent.Statement.primary_key_fields', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='primary_key_values', full_name='query.StreamEvent.Statement.primary_key_values', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sql', full_name='query.StreamEvent.Statement.sql', index=4, - number=5, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _STREAMEVENT_STATEMENT_CATEGORY, - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1848, - serialized_end=2072, -) - -_STREAMEVENT = _descriptor.Descriptor( - name='StreamEvent', - full_name='query.StreamEvent', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='statements', full_name='query.StreamEvent.statements', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='event_token', full_name='query.StreamEvent.event_token', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_STREAMEVENT_STATEMENT, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1742, - serialized_end=2072, -) - - -_EXECUTEREQUEST = _descriptor.Descriptor( - name='ExecuteRequest', - full_name='query.ExecuteRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.ExecuteRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.ExecuteRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.ExecuteRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='query.ExecuteRequest.query', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.ExecuteRequest.transaction_id', index=4, - number=5, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='query.ExecuteRequest.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2075, - serialized_end=2318, -) - - -_EXECUTERESPONSE = _descriptor.Descriptor( - name='ExecuteResponse', - full_name='query.ExecuteResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='query.ExecuteResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2320, - serialized_end=2373, -) - - -_RESULTWITHERROR = _descriptor.Descriptor( - name='ResultWithError', - full_name='query.ResultWithError', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='query.ResultWithError.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='query.ResultWithError.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2375, - serialized_end=2460, -) - - -_EXECUTEBATCHREQUEST = _descriptor.Descriptor( - name='ExecuteBatchRequest', - full_name='query.ExecuteBatchRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.ExecuteBatchRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.ExecuteBatchRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.ExecuteBatchRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='queries', full_name='query.ExecuteBatchRequest.queries', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='as_transaction', full_name='query.ExecuteBatchRequest.as_transaction', index=4, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.ExecuteBatchRequest.transaction_id', index=5, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='query.ExecuteBatchRequest.options', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2463, - serialized_end=2737, -) - - -_EXECUTEBATCHRESPONSE = _descriptor.Descriptor( - name='ExecuteBatchResponse', - full_name='query.ExecuteBatchResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='results', full_name='query.ExecuteBatchResponse.results', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2739, - serialized_end=2798, -) - - -_STREAMEXECUTEREQUEST = _descriptor.Descriptor( - name='StreamExecuteRequest', - full_name='query.StreamExecuteRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.StreamExecuteRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.StreamExecuteRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.StreamExecuteRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='query.StreamExecuteRequest.query', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='query.StreamExecuteRequest.options', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.StreamExecuteRequest.transaction_id', index=5, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2801, - serialized_end=3050, -) - - -_STREAMEXECUTERESPONSE = _descriptor.Descriptor( - name='StreamExecuteResponse', - full_name='query.StreamExecuteResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='query.StreamExecuteResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3052, - serialized_end=3111, -) - - -_BEGINREQUEST = _descriptor.Descriptor( - name='BeginRequest', - full_name='query.BeginRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.BeginRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.BeginRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.BeginRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='query.BeginRequest.options', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3114, - serialized_end=3297, -) - - -_BEGINRESPONSE = _descriptor.Descriptor( - name='BeginResponse', - full_name='query.BeginResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.BeginResponse.transaction_id', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3299, - serialized_end=3338, -) - - -_COMMITREQUEST = _descriptor.Descriptor( - name='CommitRequest', - full_name='query.CommitRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.CommitRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.CommitRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.CommitRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.CommitRequest.transaction_id', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3341, - serialized_end=3509, -) - - -_COMMITRESPONSE = _descriptor.Descriptor( - name='CommitResponse', - full_name='query.CommitResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3511, - serialized_end=3527, -) - - -_ROLLBACKREQUEST = _descriptor.Descriptor( - name='RollbackRequest', - full_name='query.RollbackRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.RollbackRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.RollbackRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.RollbackRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.RollbackRequest.transaction_id', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3530, - serialized_end=3700, -) - - -_ROLLBACKRESPONSE = _descriptor.Descriptor( - name='RollbackResponse', - full_name='query.RollbackResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3702, - serialized_end=3720, -) - - -_PREPAREREQUEST = _descriptor.Descriptor( - name='PrepareRequest', - full_name='query.PrepareRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.PrepareRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.PrepareRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.PrepareRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.PrepareRequest.transaction_id', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.PrepareRequest.dtid', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3723, - serialized_end=3906, -) - - -_PREPARERESPONSE = _descriptor.Descriptor( - name='PrepareResponse', - full_name='query.PrepareResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3908, - serialized_end=3925, -) - - -_COMMITPREPAREDREQUEST = _descriptor.Descriptor( - name='CommitPreparedRequest', - full_name='query.CommitPreparedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.CommitPreparedRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.CommitPreparedRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.CommitPreparedRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.CommitPreparedRequest.dtid', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3928, - serialized_end=4094, -) - - -_COMMITPREPAREDRESPONSE = _descriptor.Descriptor( - name='CommitPreparedResponse', - full_name='query.CommitPreparedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4096, - serialized_end=4120, -) - - -_ROLLBACKPREPAREDREQUEST = _descriptor.Descriptor( - name='RollbackPreparedRequest', - full_name='query.RollbackPreparedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.RollbackPreparedRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.RollbackPreparedRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.RollbackPreparedRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.RollbackPreparedRequest.transaction_id', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.RollbackPreparedRequest.dtid', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4123, - serialized_end=4315, -) - - -_ROLLBACKPREPAREDRESPONSE = _descriptor.Descriptor( - name='RollbackPreparedResponse', - full_name='query.RollbackPreparedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4317, - serialized_end=4343, -) - - -_CREATETRANSACTIONREQUEST = _descriptor.Descriptor( - name='CreateTransactionRequest', - full_name='query.CreateTransactionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.CreateTransactionRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.CreateTransactionRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.CreateTransactionRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.CreateTransactionRequest.dtid', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='participants', full_name='query.CreateTransactionRequest.participants', index=4, - number=5, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4346, - serialized_end=4552, -) - - -_CREATETRANSACTIONRESPONSE = _descriptor.Descriptor( - name='CreateTransactionResponse', - full_name='query.CreateTransactionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4554, - serialized_end=4581, -) - - -_STARTCOMMITREQUEST = _descriptor.Descriptor( - name='StartCommitRequest', - full_name='query.StartCommitRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.StartCommitRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.StartCommitRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.StartCommitRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.StartCommitRequest.transaction_id', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.StartCommitRequest.dtid', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4584, - serialized_end=4771, -) - - -_STARTCOMMITRESPONSE = _descriptor.Descriptor( - name='StartCommitResponse', - full_name='query.StartCommitResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4773, - serialized_end=4794, -) - - -_SETROLLBACKREQUEST = _descriptor.Descriptor( - name='SetRollbackRequest', - full_name='query.SetRollbackRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.SetRollbackRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.SetRollbackRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.SetRollbackRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.SetRollbackRequest.transaction_id', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.SetRollbackRequest.dtid', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4797, - serialized_end=4984, -) - - -_SETROLLBACKRESPONSE = _descriptor.Descriptor( - name='SetRollbackResponse', - full_name='query.SetRollbackResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4986, - serialized_end=5007, -) - - -_CONCLUDETRANSACTIONREQUEST = _descriptor.Descriptor( - name='ConcludeTransactionRequest', - full_name='query.ConcludeTransactionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.ConcludeTransactionRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.ConcludeTransactionRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.ConcludeTransactionRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.ConcludeTransactionRequest.dtid', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5010, - serialized_end=5181, -) - - -_CONCLUDETRANSACTIONRESPONSE = _descriptor.Descriptor( - name='ConcludeTransactionResponse', - full_name='query.ConcludeTransactionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5183, - serialized_end=5212, -) - - -_READTRANSACTIONREQUEST = _descriptor.Descriptor( - name='ReadTransactionRequest', - full_name='query.ReadTransactionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.ReadTransactionRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.ReadTransactionRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.ReadTransactionRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='query.ReadTransactionRequest.dtid', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5215, - serialized_end=5382, -) - - -_READTRANSACTIONRESPONSE = _descriptor.Descriptor( - name='ReadTransactionResponse', - full_name='query.ReadTransactionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='metadata', full_name='query.ReadTransactionResponse.metadata', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5384, - serialized_end=5455, -) - - -_BEGINEXECUTEREQUEST = _descriptor.Descriptor( - name='BeginExecuteRequest', - full_name='query.BeginExecuteRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.BeginExecuteRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.BeginExecuteRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.BeginExecuteRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='query.BeginExecuteRequest.query', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='query.BeginExecuteRequest.options', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5458, - serialized_end=5682, -) - - -_BEGINEXECUTERESPONSE = _descriptor.Descriptor( - name='BeginExecuteResponse', - full_name='query.BeginExecuteResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='query.BeginExecuteResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='query.BeginExecuteResponse.result', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.BeginExecuteResponse.transaction_id', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5684, - serialized_end=5798, -) - - -_BEGINEXECUTEBATCHREQUEST = _descriptor.Descriptor( - name='BeginExecuteBatchRequest', - full_name='query.BeginExecuteBatchRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.BeginExecuteBatchRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.BeginExecuteBatchRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.BeginExecuteBatchRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='queries', full_name='query.BeginExecuteBatchRequest.queries', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='as_transaction', full_name='query.BeginExecuteBatchRequest.as_transaction', index=4, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='query.BeginExecuteBatchRequest.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5801, - serialized_end=6056, -) - - -_BEGINEXECUTEBATCHRESPONSE = _descriptor.Descriptor( - name='BeginExecuteBatchResponse', - full_name='query.BeginExecuteBatchResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='query.BeginExecuteBatchResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='results', full_name='query.BeginExecuteBatchResponse.results', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='query.BeginExecuteBatchResponse.transaction_id', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6058, - serialized_end=6178, -) - - -_MESSAGESTREAMREQUEST = _descriptor.Descriptor( - name='MessageStreamRequest', - full_name='query.MessageStreamRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.MessageStreamRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.MessageStreamRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.MessageStreamRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='query.MessageStreamRequest.name', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6181, - serialized_end=6346, -) - - -_MESSAGESTREAMRESPONSE = _descriptor.Descriptor( - name='MessageStreamResponse', - full_name='query.MessageStreamResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='query.MessageStreamResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6348, - serialized_end=6407, -) - - -_MESSAGEACKREQUEST = _descriptor.Descriptor( - name='MessageAckRequest', - full_name='query.MessageAckRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.MessageAckRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.MessageAckRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.MessageAckRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='query.MessageAckRequest.name', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ids', full_name='query.MessageAckRequest.ids', index=4, - number=5, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6410, - serialized_end=6599, -) - - -_MESSAGEACKRESPONSE = _descriptor.Descriptor( - name='MessageAckResponse', - full_name='query.MessageAckResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='query.MessageAckResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6601, - serialized_end=6657, -) - - -_SPLITQUERYREQUEST = _descriptor.Descriptor( - name='SplitQueryRequest', - full_name='query.SplitQueryRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.SplitQueryRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.SplitQueryRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.SplitQueryRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='query.SplitQueryRequest.query', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='split_column', full_name='query.SplitQueryRequest.split_column', index=4, - number=5, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='split_count', full_name='query.SplitQueryRequest.split_count', index=5, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_rows_per_query_part', full_name='query.SplitQueryRequest.num_rows_per_query_part', index=6, - number=8, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='algorithm', full_name='query.SplitQueryRequest.algorithm', index=7, - number=9, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _SPLITQUERYREQUEST_ALGORITHM, - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6660, - serialized_end=7019, -) - - -_QUERYSPLIT = _descriptor.Descriptor( - name='QuerySplit', - full_name='query.QuerySplit', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='query.QuerySplit.query', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='row_count', full_name='query.QuerySplit.row_count', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7021, - serialized_end=7086, -) - - -_SPLITQUERYRESPONSE = _descriptor.Descriptor( - name='SplitQueryResponse', - full_name='query.SplitQueryResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='queries', full_name='query.SplitQueryResponse.queries', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7088, - serialized_end=7144, -) - - -_STREAMHEALTHREQUEST = _descriptor.Descriptor( - name='StreamHealthRequest', - full_name='query.StreamHealthRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7146, - serialized_end=7167, -) - - -_REALTIMESTATS = _descriptor.Descriptor( - name='RealtimeStats', - full_name='query.RealtimeStats', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='health_error', full_name='query.RealtimeStats.health_error', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='seconds_behind_master', full_name='query.RealtimeStats.seconds_behind_master', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='binlog_players_count', full_name='query.RealtimeStats.binlog_players_count', index=2, - number=3, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='seconds_behind_master_filtered_replication', full_name='query.RealtimeStats.seconds_behind_master_filtered_replication', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cpu_usage', full_name='query.RealtimeStats.cpu_usage', index=4, - number=5, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='qps', full_name='query.RealtimeStats.qps', index=5, - number=6, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7170, - serialized_end=7352, -) - - -_AGGREGATESTATS = _descriptor.Descriptor( - name='AggregateStats', - full_name='query.AggregateStats', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='healthy_tablet_count', full_name='query.AggregateStats.healthy_tablet_count', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unhealthy_tablet_count', full_name='query.AggregateStats.unhealthy_tablet_count', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='seconds_behind_master_min', full_name='query.AggregateStats.seconds_behind_master_min', index=2, - number=3, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='seconds_behind_master_max', full_name='query.AggregateStats.seconds_behind_master_max', index=3, - number=4, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7355, - serialized_end=7503, -) - - -_STREAMHEALTHRESPONSE = _descriptor.Descriptor( - name='StreamHealthResponse', - full_name='query.StreamHealthResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='target', full_name='query.StreamHealthResponse.target', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='serving', full_name='query.StreamHealthResponse.serving', index=1, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_externally_reparented_timestamp', full_name='query.StreamHealthResponse.tablet_externally_reparented_timestamp', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='realtime_stats', full_name='query.StreamHealthResponse.realtime_stats', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_alias', full_name='query.StreamHealthResponse.tablet_alias', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7506, - serialized_end=7721, -) - - -_UPDATESTREAMREQUEST = _descriptor.Descriptor( - name='UpdateStreamRequest', - full_name='query.UpdateStreamRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='effective_caller_id', full_name='query.UpdateStreamRequest.effective_caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='immediate_caller_id', full_name='query.UpdateStreamRequest.immediate_caller_id', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target', full_name='query.UpdateStreamRequest.target', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='position', full_name='query.UpdateStreamRequest.position', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp', full_name='query.UpdateStreamRequest.timestamp', index=4, - number=5, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7724, - serialized_end=7911, -) - - -_UPDATESTREAMRESPONSE = _descriptor.Descriptor( - name='UpdateStreamResponse', - full_name='query.UpdateStreamResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='event', full_name='query.UpdateStreamResponse.event', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7913, - serialized_end=7970, -) - - -_TRANSACTIONMETADATA = _descriptor.Descriptor( - name='TransactionMetadata', - full_name='query.TransactionMetadata', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='dtid', full_name='query.TransactionMetadata.dtid', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='query.TransactionMetadata.state', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='time_created', full_name='query.TransactionMetadata.time_created', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='participants', full_name='query.TransactionMetadata.participants', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7973, - serialized_end=8107, -) - -_TARGET.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_VALUE.fields_by_name['type'].enum_type = _TYPE -_BINDVARIABLE.fields_by_name['type'].enum_type = _TYPE -_BINDVARIABLE.fields_by_name['values'].message_type = _VALUE -_BOUNDQUERY_BINDVARIABLESENTRY.fields_by_name['value'].message_type = _BINDVARIABLE -_BOUNDQUERY_BINDVARIABLESENTRY.containing_type = _BOUNDQUERY -_BOUNDQUERY.fields_by_name['bind_variables'].message_type = _BOUNDQUERY_BINDVARIABLESENTRY -_EXECUTEOPTIONS.fields_by_name['compare_event_token'].message_type = _EVENTTOKEN -_EXECUTEOPTIONS.fields_by_name['included_fields'].enum_type = _EXECUTEOPTIONS_INCLUDEDFIELDS -_EXECUTEOPTIONS.fields_by_name['workload'].enum_type = _EXECUTEOPTIONS_WORKLOAD -_EXECUTEOPTIONS.fields_by_name['transaction_isolation'].enum_type = _EXECUTEOPTIONS_TRANSACTIONISOLATION -_EXECUTEOPTIONS_INCLUDEDFIELDS.containing_type = _EXECUTEOPTIONS -_EXECUTEOPTIONS_WORKLOAD.containing_type = _EXECUTEOPTIONS -_EXECUTEOPTIONS_TRANSACTIONISOLATION.containing_type = _EXECUTEOPTIONS -_FIELD.fields_by_name['type'].enum_type = _TYPE -_RESULTEXTRAS.fields_by_name['event_token'].message_type = _EVENTTOKEN -_QUERYRESULT.fields_by_name['fields'].message_type = _FIELD -_QUERYRESULT.fields_by_name['rows'].message_type = _ROW -_QUERYRESULT.fields_by_name['extras'].message_type = _RESULTEXTRAS -_STREAMEVENT_STATEMENT.fields_by_name['category'].enum_type = _STREAMEVENT_STATEMENT_CATEGORY -_STREAMEVENT_STATEMENT.fields_by_name['primary_key_fields'].message_type = _FIELD -_STREAMEVENT_STATEMENT.fields_by_name['primary_key_values'].message_type = _ROW -_STREAMEVENT_STATEMENT.containing_type = _STREAMEVENT -_STREAMEVENT_STATEMENT_CATEGORY.containing_type = _STREAMEVENT_STATEMENT -_STREAMEVENT.fields_by_name['statements'].message_type = _STREAMEVENT_STATEMENT -_STREAMEVENT.fields_by_name['event_token'].message_type = _EVENTTOKEN -_EXECUTEREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_EXECUTEREQUEST.fields_by_name['target'].message_type = _TARGET -_EXECUTEREQUEST.fields_by_name['query'].message_type = _BOUNDQUERY -_EXECUTEREQUEST.fields_by_name['options'].message_type = _EXECUTEOPTIONS -_EXECUTERESPONSE.fields_by_name['result'].message_type = _QUERYRESULT -_RESULTWITHERROR.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_RESULTWITHERROR.fields_by_name['result'].message_type = _QUERYRESULT -_EXECUTEBATCHREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEBATCHREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_EXECUTEBATCHREQUEST.fields_by_name['target'].message_type = _TARGET -_EXECUTEBATCHREQUEST.fields_by_name['queries'].message_type = _BOUNDQUERY -_EXECUTEBATCHREQUEST.fields_by_name['options'].message_type = _EXECUTEOPTIONS -_EXECUTEBATCHRESPONSE.fields_by_name['results'].message_type = _QUERYRESULT -_STREAMEXECUTEREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_STREAMEXECUTEREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_STREAMEXECUTEREQUEST.fields_by_name['target'].message_type = _TARGET -_STREAMEXECUTEREQUEST.fields_by_name['query'].message_type = _BOUNDQUERY -_STREAMEXECUTEREQUEST.fields_by_name['options'].message_type = _EXECUTEOPTIONS -_STREAMEXECUTERESPONSE.fields_by_name['result'].message_type = _QUERYRESULT -_BEGINREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_BEGINREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_BEGINREQUEST.fields_by_name['target'].message_type = _TARGET -_BEGINREQUEST.fields_by_name['options'].message_type = _EXECUTEOPTIONS -_COMMITREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_COMMITREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_COMMITREQUEST.fields_by_name['target'].message_type = _TARGET -_ROLLBACKREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_ROLLBACKREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_ROLLBACKREQUEST.fields_by_name['target'].message_type = _TARGET -_PREPAREREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_PREPAREREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_PREPAREREQUEST.fields_by_name['target'].message_type = _TARGET -_COMMITPREPAREDREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_COMMITPREPAREDREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_COMMITPREPAREDREQUEST.fields_by_name['target'].message_type = _TARGET -_ROLLBACKPREPAREDREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_ROLLBACKPREPAREDREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_ROLLBACKPREPAREDREQUEST.fields_by_name['target'].message_type = _TARGET -_CREATETRANSACTIONREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_CREATETRANSACTIONREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_CREATETRANSACTIONREQUEST.fields_by_name['target'].message_type = _TARGET -_CREATETRANSACTIONREQUEST.fields_by_name['participants'].message_type = _TARGET -_STARTCOMMITREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_STARTCOMMITREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_STARTCOMMITREQUEST.fields_by_name['target'].message_type = _TARGET -_SETROLLBACKREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_SETROLLBACKREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_SETROLLBACKREQUEST.fields_by_name['target'].message_type = _TARGET -_CONCLUDETRANSACTIONREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_CONCLUDETRANSACTIONREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_CONCLUDETRANSACTIONREQUEST.fields_by_name['target'].message_type = _TARGET -_READTRANSACTIONREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_READTRANSACTIONREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_READTRANSACTIONREQUEST.fields_by_name['target'].message_type = _TARGET -_READTRANSACTIONRESPONSE.fields_by_name['metadata'].message_type = _TRANSACTIONMETADATA -_BEGINEXECUTEREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_BEGINEXECUTEREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_BEGINEXECUTEREQUEST.fields_by_name['target'].message_type = _TARGET -_BEGINEXECUTEREQUEST.fields_by_name['query'].message_type = _BOUNDQUERY -_BEGINEXECUTEREQUEST.fields_by_name['options'].message_type = _EXECUTEOPTIONS -_BEGINEXECUTERESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_BEGINEXECUTERESPONSE.fields_by_name['result'].message_type = _QUERYRESULT -_BEGINEXECUTEBATCHREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_BEGINEXECUTEBATCHREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_BEGINEXECUTEBATCHREQUEST.fields_by_name['target'].message_type = _TARGET -_BEGINEXECUTEBATCHREQUEST.fields_by_name['queries'].message_type = _BOUNDQUERY -_BEGINEXECUTEBATCHREQUEST.fields_by_name['options'].message_type = _EXECUTEOPTIONS -_BEGINEXECUTEBATCHRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_BEGINEXECUTEBATCHRESPONSE.fields_by_name['results'].message_type = _QUERYRESULT -_MESSAGESTREAMREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_MESSAGESTREAMREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_MESSAGESTREAMREQUEST.fields_by_name['target'].message_type = _TARGET -_MESSAGESTREAMRESPONSE.fields_by_name['result'].message_type = _QUERYRESULT -_MESSAGEACKREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_MESSAGEACKREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_MESSAGEACKREQUEST.fields_by_name['target'].message_type = _TARGET -_MESSAGEACKREQUEST.fields_by_name['ids'].message_type = _VALUE -_MESSAGEACKRESPONSE.fields_by_name['result'].message_type = _QUERYRESULT -_SPLITQUERYREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_SPLITQUERYREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_SPLITQUERYREQUEST.fields_by_name['target'].message_type = _TARGET -_SPLITQUERYREQUEST.fields_by_name['query'].message_type = _BOUNDQUERY -_SPLITQUERYREQUEST.fields_by_name['algorithm'].enum_type = _SPLITQUERYREQUEST_ALGORITHM -_SPLITQUERYREQUEST_ALGORITHM.containing_type = _SPLITQUERYREQUEST -_QUERYSPLIT.fields_by_name['query'].message_type = _BOUNDQUERY -_SPLITQUERYRESPONSE.fields_by_name['queries'].message_type = _QUERYSPLIT -_STREAMHEALTHRESPONSE.fields_by_name['target'].message_type = _TARGET -_STREAMHEALTHRESPONSE.fields_by_name['realtime_stats'].message_type = _REALTIMESTATS -_STREAMHEALTHRESPONSE.fields_by_name['tablet_alias'].message_type = topodata__pb2._TABLETALIAS -_UPDATESTREAMREQUEST.fields_by_name['effective_caller_id'].message_type = vtrpc__pb2._CALLERID -_UPDATESTREAMREQUEST.fields_by_name['immediate_caller_id'].message_type = _VTGATECALLERID -_UPDATESTREAMREQUEST.fields_by_name['target'].message_type = _TARGET -_UPDATESTREAMRESPONSE.fields_by_name['event'].message_type = _STREAMEVENT -_TRANSACTIONMETADATA.fields_by_name['state'].enum_type = _TRANSACTIONSTATE -_TRANSACTIONMETADATA.fields_by_name['participants'].message_type = _TARGET -DESCRIPTOR.message_types_by_name['Target'] = _TARGET -DESCRIPTOR.message_types_by_name['VTGateCallerID'] = _VTGATECALLERID -DESCRIPTOR.message_types_by_name['EventToken'] = _EVENTTOKEN -DESCRIPTOR.message_types_by_name['Value'] = _VALUE -DESCRIPTOR.message_types_by_name['BindVariable'] = _BINDVARIABLE -DESCRIPTOR.message_types_by_name['BoundQuery'] = _BOUNDQUERY -DESCRIPTOR.message_types_by_name['ExecuteOptions'] = _EXECUTEOPTIONS -DESCRIPTOR.message_types_by_name['Field'] = _FIELD -DESCRIPTOR.message_types_by_name['Row'] = _ROW -DESCRIPTOR.message_types_by_name['ResultExtras'] = _RESULTEXTRAS -DESCRIPTOR.message_types_by_name['QueryResult'] = _QUERYRESULT -DESCRIPTOR.message_types_by_name['QueryWarning'] = _QUERYWARNING -DESCRIPTOR.message_types_by_name['StreamEvent'] = _STREAMEVENT -DESCRIPTOR.message_types_by_name['ExecuteRequest'] = _EXECUTEREQUEST -DESCRIPTOR.message_types_by_name['ExecuteResponse'] = _EXECUTERESPONSE -DESCRIPTOR.message_types_by_name['ResultWithError'] = _RESULTWITHERROR -DESCRIPTOR.message_types_by_name['ExecuteBatchRequest'] = _EXECUTEBATCHREQUEST -DESCRIPTOR.message_types_by_name['ExecuteBatchResponse'] = _EXECUTEBATCHRESPONSE -DESCRIPTOR.message_types_by_name['StreamExecuteRequest'] = _STREAMEXECUTEREQUEST -DESCRIPTOR.message_types_by_name['StreamExecuteResponse'] = _STREAMEXECUTERESPONSE -DESCRIPTOR.message_types_by_name['BeginRequest'] = _BEGINREQUEST -DESCRIPTOR.message_types_by_name['BeginResponse'] = _BEGINRESPONSE -DESCRIPTOR.message_types_by_name['CommitRequest'] = _COMMITREQUEST -DESCRIPTOR.message_types_by_name['CommitResponse'] = _COMMITRESPONSE -DESCRIPTOR.message_types_by_name['RollbackRequest'] = _ROLLBACKREQUEST -DESCRIPTOR.message_types_by_name['RollbackResponse'] = _ROLLBACKRESPONSE -DESCRIPTOR.message_types_by_name['PrepareRequest'] = _PREPAREREQUEST -DESCRIPTOR.message_types_by_name['PrepareResponse'] = _PREPARERESPONSE -DESCRIPTOR.message_types_by_name['CommitPreparedRequest'] = _COMMITPREPAREDREQUEST -DESCRIPTOR.message_types_by_name['CommitPreparedResponse'] = _COMMITPREPAREDRESPONSE -DESCRIPTOR.message_types_by_name['RollbackPreparedRequest'] = _ROLLBACKPREPAREDREQUEST -DESCRIPTOR.message_types_by_name['RollbackPreparedResponse'] = _ROLLBACKPREPAREDRESPONSE -DESCRIPTOR.message_types_by_name['CreateTransactionRequest'] = _CREATETRANSACTIONREQUEST -DESCRIPTOR.message_types_by_name['CreateTransactionResponse'] = _CREATETRANSACTIONRESPONSE -DESCRIPTOR.message_types_by_name['StartCommitRequest'] = _STARTCOMMITREQUEST -DESCRIPTOR.message_types_by_name['StartCommitResponse'] = _STARTCOMMITRESPONSE -DESCRIPTOR.message_types_by_name['SetRollbackRequest'] = _SETROLLBACKREQUEST -DESCRIPTOR.message_types_by_name['SetRollbackResponse'] = _SETROLLBACKRESPONSE -DESCRIPTOR.message_types_by_name['ConcludeTransactionRequest'] = _CONCLUDETRANSACTIONREQUEST -DESCRIPTOR.message_types_by_name['ConcludeTransactionResponse'] = _CONCLUDETRANSACTIONRESPONSE -DESCRIPTOR.message_types_by_name['ReadTransactionRequest'] = _READTRANSACTIONREQUEST -DESCRIPTOR.message_types_by_name['ReadTransactionResponse'] = _READTRANSACTIONRESPONSE -DESCRIPTOR.message_types_by_name['BeginExecuteRequest'] = _BEGINEXECUTEREQUEST -DESCRIPTOR.message_types_by_name['BeginExecuteResponse'] = _BEGINEXECUTERESPONSE -DESCRIPTOR.message_types_by_name['BeginExecuteBatchRequest'] = _BEGINEXECUTEBATCHREQUEST -DESCRIPTOR.message_types_by_name['BeginExecuteBatchResponse'] = _BEGINEXECUTEBATCHRESPONSE -DESCRIPTOR.message_types_by_name['MessageStreamRequest'] = _MESSAGESTREAMREQUEST -DESCRIPTOR.message_types_by_name['MessageStreamResponse'] = _MESSAGESTREAMRESPONSE -DESCRIPTOR.message_types_by_name['MessageAckRequest'] = _MESSAGEACKREQUEST -DESCRIPTOR.message_types_by_name['MessageAckResponse'] = _MESSAGEACKRESPONSE -DESCRIPTOR.message_types_by_name['SplitQueryRequest'] = _SPLITQUERYREQUEST -DESCRIPTOR.message_types_by_name['QuerySplit'] = _QUERYSPLIT -DESCRIPTOR.message_types_by_name['SplitQueryResponse'] = _SPLITQUERYRESPONSE -DESCRIPTOR.message_types_by_name['StreamHealthRequest'] = _STREAMHEALTHREQUEST -DESCRIPTOR.message_types_by_name['RealtimeStats'] = _REALTIMESTATS -DESCRIPTOR.message_types_by_name['AggregateStats'] = _AGGREGATESTATS -DESCRIPTOR.message_types_by_name['StreamHealthResponse'] = _STREAMHEALTHRESPONSE -DESCRIPTOR.message_types_by_name['UpdateStreamRequest'] = _UPDATESTREAMREQUEST -DESCRIPTOR.message_types_by_name['UpdateStreamResponse'] = _UPDATESTREAMRESPONSE -DESCRIPTOR.message_types_by_name['TransactionMetadata'] = _TRANSACTIONMETADATA -DESCRIPTOR.enum_types_by_name['MySqlFlag'] = _MYSQLFLAG -DESCRIPTOR.enum_types_by_name['Flag'] = _FLAG -DESCRIPTOR.enum_types_by_name['Type'] = _TYPE -DESCRIPTOR.enum_types_by_name['TransactionState'] = _TRANSACTIONSTATE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Target = _reflection.GeneratedProtocolMessageType('Target', (_message.Message,), dict( - DESCRIPTOR = _TARGET, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.Target) - )) -_sym_db.RegisterMessage(Target) - -VTGateCallerID = _reflection.GeneratedProtocolMessageType('VTGateCallerID', (_message.Message,), dict( - DESCRIPTOR = _VTGATECALLERID, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.VTGateCallerID) - )) -_sym_db.RegisterMessage(VTGateCallerID) - -EventToken = _reflection.GeneratedProtocolMessageType('EventToken', (_message.Message,), dict( - DESCRIPTOR = _EVENTTOKEN, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.EventToken) - )) -_sym_db.RegisterMessage(EventToken) - -Value = _reflection.GeneratedProtocolMessageType('Value', (_message.Message,), dict( - DESCRIPTOR = _VALUE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.Value) - )) -_sym_db.RegisterMessage(Value) - -BindVariable = _reflection.GeneratedProtocolMessageType('BindVariable', (_message.Message,), dict( - DESCRIPTOR = _BINDVARIABLE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BindVariable) - )) -_sym_db.RegisterMessage(BindVariable) - -BoundQuery = _reflection.GeneratedProtocolMessageType('BoundQuery', (_message.Message,), dict( - - BindVariablesEntry = _reflection.GeneratedProtocolMessageType('BindVariablesEntry', (_message.Message,), dict( - DESCRIPTOR = _BOUNDQUERY_BINDVARIABLESENTRY, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BoundQuery.BindVariablesEntry) - )) - , - DESCRIPTOR = _BOUNDQUERY, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BoundQuery) - )) -_sym_db.RegisterMessage(BoundQuery) -_sym_db.RegisterMessage(BoundQuery.BindVariablesEntry) - -ExecuteOptions = _reflection.GeneratedProtocolMessageType('ExecuteOptions', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEOPTIONS, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ExecuteOptions) - )) -_sym_db.RegisterMessage(ExecuteOptions) - -Field = _reflection.GeneratedProtocolMessageType('Field', (_message.Message,), dict( - DESCRIPTOR = _FIELD, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.Field) - )) -_sym_db.RegisterMessage(Field) - -Row = _reflection.GeneratedProtocolMessageType('Row', (_message.Message,), dict( - DESCRIPTOR = _ROW, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.Row) - )) -_sym_db.RegisterMessage(Row) - -ResultExtras = _reflection.GeneratedProtocolMessageType('ResultExtras', (_message.Message,), dict( - DESCRIPTOR = _RESULTEXTRAS, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ResultExtras) - )) -_sym_db.RegisterMessage(ResultExtras) - -QueryResult = _reflection.GeneratedProtocolMessageType('QueryResult', (_message.Message,), dict( - DESCRIPTOR = _QUERYRESULT, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.QueryResult) - )) -_sym_db.RegisterMessage(QueryResult) - -QueryWarning = _reflection.GeneratedProtocolMessageType('QueryWarning', (_message.Message,), dict( - DESCRIPTOR = _QUERYWARNING, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.QueryWarning) - )) -_sym_db.RegisterMessage(QueryWarning) - -StreamEvent = _reflection.GeneratedProtocolMessageType('StreamEvent', (_message.Message,), dict( - - Statement = _reflection.GeneratedProtocolMessageType('Statement', (_message.Message,), dict( - DESCRIPTOR = _STREAMEVENT_STATEMENT, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StreamEvent.Statement) - )) - , - DESCRIPTOR = _STREAMEVENT, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StreamEvent) - )) -_sym_db.RegisterMessage(StreamEvent) -_sym_db.RegisterMessage(StreamEvent.Statement) - -ExecuteRequest = _reflection.GeneratedProtocolMessageType('ExecuteRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ExecuteRequest) - )) -_sym_db.RegisterMessage(ExecuteRequest) - -ExecuteResponse = _reflection.GeneratedProtocolMessageType('ExecuteResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTERESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ExecuteResponse) - )) -_sym_db.RegisterMessage(ExecuteResponse) - -ResultWithError = _reflection.GeneratedProtocolMessageType('ResultWithError', (_message.Message,), dict( - DESCRIPTOR = _RESULTWITHERROR, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ResultWithError) - )) -_sym_db.RegisterMessage(ResultWithError) - -ExecuteBatchRequest = _reflection.GeneratedProtocolMessageType('ExecuteBatchRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ExecuteBatchRequest) - )) -_sym_db.RegisterMessage(ExecuteBatchRequest) - -ExecuteBatchResponse = _reflection.GeneratedProtocolMessageType('ExecuteBatchResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ExecuteBatchResponse) - )) -_sym_db.RegisterMessage(ExecuteBatchResponse) - -StreamExecuteRequest = _reflection.GeneratedProtocolMessageType('StreamExecuteRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTEREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StreamExecuteRequest) - )) -_sym_db.RegisterMessage(StreamExecuteRequest) - -StreamExecuteResponse = _reflection.GeneratedProtocolMessageType('StreamExecuteResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTERESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StreamExecuteResponse) - )) -_sym_db.RegisterMessage(StreamExecuteResponse) - -BeginRequest = _reflection.GeneratedProtocolMessageType('BeginRequest', (_message.Message,), dict( - DESCRIPTOR = _BEGINREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BeginRequest) - )) -_sym_db.RegisterMessage(BeginRequest) - -BeginResponse = _reflection.GeneratedProtocolMessageType('BeginResponse', (_message.Message,), dict( - DESCRIPTOR = _BEGINRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BeginResponse) - )) -_sym_db.RegisterMessage(BeginResponse) - -CommitRequest = _reflection.GeneratedProtocolMessageType('CommitRequest', (_message.Message,), dict( - DESCRIPTOR = _COMMITREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.CommitRequest) - )) -_sym_db.RegisterMessage(CommitRequest) - -CommitResponse = _reflection.GeneratedProtocolMessageType('CommitResponse', (_message.Message,), dict( - DESCRIPTOR = _COMMITRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.CommitResponse) - )) -_sym_db.RegisterMessage(CommitResponse) - -RollbackRequest = _reflection.GeneratedProtocolMessageType('RollbackRequest', (_message.Message,), dict( - DESCRIPTOR = _ROLLBACKREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.RollbackRequest) - )) -_sym_db.RegisterMessage(RollbackRequest) - -RollbackResponse = _reflection.GeneratedProtocolMessageType('RollbackResponse', (_message.Message,), dict( - DESCRIPTOR = _ROLLBACKRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.RollbackResponse) - )) -_sym_db.RegisterMessage(RollbackResponse) - -PrepareRequest = _reflection.GeneratedProtocolMessageType('PrepareRequest', (_message.Message,), dict( - DESCRIPTOR = _PREPAREREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.PrepareRequest) - )) -_sym_db.RegisterMessage(PrepareRequest) - -PrepareResponse = _reflection.GeneratedProtocolMessageType('PrepareResponse', (_message.Message,), dict( - DESCRIPTOR = _PREPARERESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.PrepareResponse) - )) -_sym_db.RegisterMessage(PrepareResponse) - -CommitPreparedRequest = _reflection.GeneratedProtocolMessageType('CommitPreparedRequest', (_message.Message,), dict( - DESCRIPTOR = _COMMITPREPAREDREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.CommitPreparedRequest) - )) -_sym_db.RegisterMessage(CommitPreparedRequest) - -CommitPreparedResponse = _reflection.GeneratedProtocolMessageType('CommitPreparedResponse', (_message.Message,), dict( - DESCRIPTOR = _COMMITPREPAREDRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.CommitPreparedResponse) - )) -_sym_db.RegisterMessage(CommitPreparedResponse) - -RollbackPreparedRequest = _reflection.GeneratedProtocolMessageType('RollbackPreparedRequest', (_message.Message,), dict( - DESCRIPTOR = _ROLLBACKPREPAREDREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.RollbackPreparedRequest) - )) -_sym_db.RegisterMessage(RollbackPreparedRequest) - -RollbackPreparedResponse = _reflection.GeneratedProtocolMessageType('RollbackPreparedResponse', (_message.Message,), dict( - DESCRIPTOR = _ROLLBACKPREPAREDRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.RollbackPreparedResponse) - )) -_sym_db.RegisterMessage(RollbackPreparedResponse) - -CreateTransactionRequest = _reflection.GeneratedProtocolMessageType('CreateTransactionRequest', (_message.Message,), dict( - DESCRIPTOR = _CREATETRANSACTIONREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.CreateTransactionRequest) - )) -_sym_db.RegisterMessage(CreateTransactionRequest) - -CreateTransactionResponse = _reflection.GeneratedProtocolMessageType('CreateTransactionResponse', (_message.Message,), dict( - DESCRIPTOR = _CREATETRANSACTIONRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.CreateTransactionResponse) - )) -_sym_db.RegisterMessage(CreateTransactionResponse) - -StartCommitRequest = _reflection.GeneratedProtocolMessageType('StartCommitRequest', (_message.Message,), dict( - DESCRIPTOR = _STARTCOMMITREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StartCommitRequest) - )) -_sym_db.RegisterMessage(StartCommitRequest) - -StartCommitResponse = _reflection.GeneratedProtocolMessageType('StartCommitResponse', (_message.Message,), dict( - DESCRIPTOR = _STARTCOMMITRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StartCommitResponse) - )) -_sym_db.RegisterMessage(StartCommitResponse) - -SetRollbackRequest = _reflection.GeneratedProtocolMessageType('SetRollbackRequest', (_message.Message,), dict( - DESCRIPTOR = _SETROLLBACKREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.SetRollbackRequest) - )) -_sym_db.RegisterMessage(SetRollbackRequest) - -SetRollbackResponse = _reflection.GeneratedProtocolMessageType('SetRollbackResponse', (_message.Message,), dict( - DESCRIPTOR = _SETROLLBACKRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.SetRollbackResponse) - )) -_sym_db.RegisterMessage(SetRollbackResponse) - -ConcludeTransactionRequest = _reflection.GeneratedProtocolMessageType('ConcludeTransactionRequest', (_message.Message,), dict( - DESCRIPTOR = _CONCLUDETRANSACTIONREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ConcludeTransactionRequest) - )) -_sym_db.RegisterMessage(ConcludeTransactionRequest) - -ConcludeTransactionResponse = _reflection.GeneratedProtocolMessageType('ConcludeTransactionResponse', (_message.Message,), dict( - DESCRIPTOR = _CONCLUDETRANSACTIONRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ConcludeTransactionResponse) - )) -_sym_db.RegisterMessage(ConcludeTransactionResponse) - -ReadTransactionRequest = _reflection.GeneratedProtocolMessageType('ReadTransactionRequest', (_message.Message,), dict( - DESCRIPTOR = _READTRANSACTIONREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ReadTransactionRequest) - )) -_sym_db.RegisterMessage(ReadTransactionRequest) - -ReadTransactionResponse = _reflection.GeneratedProtocolMessageType('ReadTransactionResponse', (_message.Message,), dict( - DESCRIPTOR = _READTRANSACTIONRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.ReadTransactionResponse) - )) -_sym_db.RegisterMessage(ReadTransactionResponse) - -BeginExecuteRequest = _reflection.GeneratedProtocolMessageType('BeginExecuteRequest', (_message.Message,), dict( - DESCRIPTOR = _BEGINEXECUTEREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BeginExecuteRequest) - )) -_sym_db.RegisterMessage(BeginExecuteRequest) - -BeginExecuteResponse = _reflection.GeneratedProtocolMessageType('BeginExecuteResponse', (_message.Message,), dict( - DESCRIPTOR = _BEGINEXECUTERESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BeginExecuteResponse) - )) -_sym_db.RegisterMessage(BeginExecuteResponse) - -BeginExecuteBatchRequest = _reflection.GeneratedProtocolMessageType('BeginExecuteBatchRequest', (_message.Message,), dict( - DESCRIPTOR = _BEGINEXECUTEBATCHREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BeginExecuteBatchRequest) - )) -_sym_db.RegisterMessage(BeginExecuteBatchRequest) - -BeginExecuteBatchResponse = _reflection.GeneratedProtocolMessageType('BeginExecuteBatchResponse', (_message.Message,), dict( - DESCRIPTOR = _BEGINEXECUTEBATCHRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.BeginExecuteBatchResponse) - )) -_sym_db.RegisterMessage(BeginExecuteBatchResponse) - -MessageStreamRequest = _reflection.GeneratedProtocolMessageType('MessageStreamRequest', (_message.Message,), dict( - DESCRIPTOR = _MESSAGESTREAMREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.MessageStreamRequest) - )) -_sym_db.RegisterMessage(MessageStreamRequest) - -MessageStreamResponse = _reflection.GeneratedProtocolMessageType('MessageStreamResponse', (_message.Message,), dict( - DESCRIPTOR = _MESSAGESTREAMRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.MessageStreamResponse) - )) -_sym_db.RegisterMessage(MessageStreamResponse) - -MessageAckRequest = _reflection.GeneratedProtocolMessageType('MessageAckRequest', (_message.Message,), dict( - DESCRIPTOR = _MESSAGEACKREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.MessageAckRequest) - )) -_sym_db.RegisterMessage(MessageAckRequest) - -MessageAckResponse = _reflection.GeneratedProtocolMessageType('MessageAckResponse', (_message.Message,), dict( - DESCRIPTOR = _MESSAGEACKRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.MessageAckResponse) - )) -_sym_db.RegisterMessage(MessageAckResponse) - -SplitQueryRequest = _reflection.GeneratedProtocolMessageType('SplitQueryRequest', (_message.Message,), dict( - DESCRIPTOR = _SPLITQUERYREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.SplitQueryRequest) - )) -_sym_db.RegisterMessage(SplitQueryRequest) - -QuerySplit = _reflection.GeneratedProtocolMessageType('QuerySplit', (_message.Message,), dict( - DESCRIPTOR = _QUERYSPLIT, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.QuerySplit) - )) -_sym_db.RegisterMessage(QuerySplit) - -SplitQueryResponse = _reflection.GeneratedProtocolMessageType('SplitQueryResponse', (_message.Message,), dict( - DESCRIPTOR = _SPLITQUERYRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.SplitQueryResponse) - )) -_sym_db.RegisterMessage(SplitQueryResponse) - -StreamHealthRequest = _reflection.GeneratedProtocolMessageType('StreamHealthRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMHEALTHREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StreamHealthRequest) - )) -_sym_db.RegisterMessage(StreamHealthRequest) - -RealtimeStats = _reflection.GeneratedProtocolMessageType('RealtimeStats', (_message.Message,), dict( - DESCRIPTOR = _REALTIMESTATS, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.RealtimeStats) - )) -_sym_db.RegisterMessage(RealtimeStats) - -AggregateStats = _reflection.GeneratedProtocolMessageType('AggregateStats', (_message.Message,), dict( - DESCRIPTOR = _AGGREGATESTATS, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.AggregateStats) - )) -_sym_db.RegisterMessage(AggregateStats) - -StreamHealthResponse = _reflection.GeneratedProtocolMessageType('StreamHealthResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMHEALTHRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.StreamHealthResponse) - )) -_sym_db.RegisterMessage(StreamHealthResponse) - -UpdateStreamRequest = _reflection.GeneratedProtocolMessageType('UpdateStreamRequest', (_message.Message,), dict( - DESCRIPTOR = _UPDATESTREAMREQUEST, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.UpdateStreamRequest) - )) -_sym_db.RegisterMessage(UpdateStreamRequest) - -UpdateStreamResponse = _reflection.GeneratedProtocolMessageType('UpdateStreamResponse', (_message.Message,), dict( - DESCRIPTOR = _UPDATESTREAMRESPONSE, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.UpdateStreamResponse) - )) -_sym_db.RegisterMessage(UpdateStreamResponse) - -TransactionMetadata = _reflection.GeneratedProtocolMessageType('TransactionMetadata', (_message.Message,), dict( - DESCRIPTOR = _TRANSACTIONMETADATA, - __module__ = 'query_pb2' - # @@protoc_insertion_point(class_scope:query.TransactionMetadata) - )) -_sym_db.RegisterMessage(TransactionMetadata) - - -DESCRIPTOR._options = None -_MYSQLFLAG._options = None -_BOUNDQUERY_BINDVARIABLESENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/query_pb2_grpc.py b/py/vtproto/query_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/query_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/queryservice_pb2.py b/py/vtproto/queryservice_pb2.py deleted file mode 100644 index b7082eaf642..00000000000 --- a/py/vtproto/queryservice_pb2.py +++ /dev/null @@ -1,265 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: queryservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import query_pb2 as query__pb2 -import binlogdata_pb2 as binlogdata__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='queryservice.proto', - package='queryservice', - syntax='proto3', - serialized_options=_b('Z)vitess.io/vitess/go/vt/proto/queryservice'), - serialized_pb=_b('\n\x12queryservice.proto\x12\x0cqueryservice\x1a\x0bquery.proto\x1a\x10\x62inlogdata.proto2\xa0\x0e\n\x05Query\x12:\n\x07\x45xecute\x12\x15.query.ExecuteRequest\x1a\x16.query.ExecuteResponse\"\x00\x12I\n\x0c\x45xecuteBatch\x12\x1a.query.ExecuteBatchRequest\x1a\x1b.query.ExecuteBatchResponse\"\x00\x12N\n\rStreamExecute\x12\x1b.query.StreamExecuteRequest\x1a\x1c.query.StreamExecuteResponse\"\x00\x30\x01\x12\x34\n\x05\x42\x65gin\x12\x13.query.BeginRequest\x1a\x14.query.BeginResponse\"\x00\x12\x37\n\x06\x43ommit\x12\x14.query.CommitRequest\x1a\x15.query.CommitResponse\"\x00\x12=\n\x08Rollback\x12\x16.query.RollbackRequest\x1a\x17.query.RollbackResponse\"\x00\x12:\n\x07Prepare\x12\x15.query.PrepareRequest\x1a\x16.query.PrepareResponse\"\x00\x12O\n\x0e\x43ommitPrepared\x12\x1c.query.CommitPreparedRequest\x1a\x1d.query.CommitPreparedResponse\"\x00\x12U\n\x10RollbackPrepared\x12\x1e.query.RollbackPreparedRequest\x1a\x1f.query.RollbackPreparedResponse\"\x00\x12X\n\x11\x43reateTransaction\x12\x1f.query.CreateTransactionRequest\x1a .query.CreateTransactionResponse\"\x00\x12\x46\n\x0bStartCommit\x12\x19.query.StartCommitRequest\x1a\x1a.query.StartCommitResponse\"\x00\x12\x46\n\x0bSetRollback\x12\x19.query.SetRollbackRequest\x1a\x1a.query.SetRollbackResponse\"\x00\x12^\n\x13\x43oncludeTransaction\x12!.query.ConcludeTransactionRequest\x1a\".query.ConcludeTransactionResponse\"\x00\x12R\n\x0fReadTransaction\x12\x1d.query.ReadTransactionRequest\x1a\x1e.query.ReadTransactionResponse\"\x00\x12I\n\x0c\x42\x65ginExecute\x12\x1a.query.BeginExecuteRequest\x1a\x1b.query.BeginExecuteResponse\"\x00\x12X\n\x11\x42\x65ginExecuteBatch\x12\x1f.query.BeginExecuteBatchRequest\x1a .query.BeginExecuteBatchResponse\"\x00\x12N\n\rMessageStream\x12\x1b.query.MessageStreamRequest\x1a\x1c.query.MessageStreamResponse\"\x00\x30\x01\x12\x43\n\nMessageAck\x12\x18.query.MessageAckRequest\x1a\x19.query.MessageAckResponse\"\x00\x12\x43\n\nSplitQuery\x12\x18.query.SplitQueryRequest\x1a\x19.query.SplitQueryResponse\"\x00\x12K\n\x0cStreamHealth\x12\x1a.query.StreamHealthRequest\x1a\x1b.query.StreamHealthResponse\"\x00\x30\x01\x12K\n\x0cUpdateStream\x12\x1a.query.UpdateStreamRequest\x1a\x1b.query.UpdateStreamResponse\"\x00\x30\x01\x12\x46\n\x07VStream\x12\x1a.binlogdata.VStreamRequest\x1a\x1b.binlogdata.VStreamResponse\"\x00\x30\x01\x12R\n\x0bVStreamRows\x12\x1e.binlogdata.VStreamRowsRequest\x1a\x1f.binlogdata.VStreamRowsResponse\"\x00\x30\x01\x12[\n\x0eVStreamResults\x12!.binlogdata.VStreamResultsRequest\x1a\".binlogdata.VStreamResultsResponse\"\x00\x30\x01\x42+Z)vitess.io/vitess/go/vt/proto/queryserviceb\x06proto3') - , - dependencies=[query__pb2.DESCRIPTOR,binlogdata__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_QUERY = _descriptor.ServiceDescriptor( - name='Query', - full_name='queryservice.Query', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=68, - serialized_end=1892, - methods=[ - _descriptor.MethodDescriptor( - name='Execute', - full_name='queryservice.Query.Execute', - index=0, - containing_service=None, - input_type=query__pb2._EXECUTEREQUEST, - output_type=query__pb2._EXECUTERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteBatch', - full_name='queryservice.Query.ExecuteBatch', - index=1, - containing_service=None, - input_type=query__pb2._EXECUTEBATCHREQUEST, - output_type=query__pb2._EXECUTEBATCHRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StreamExecute', - full_name='queryservice.Query.StreamExecute', - index=2, - containing_service=None, - input_type=query__pb2._STREAMEXECUTEREQUEST, - output_type=query__pb2._STREAMEXECUTERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Begin', - full_name='queryservice.Query.Begin', - index=3, - containing_service=None, - input_type=query__pb2._BEGINREQUEST, - output_type=query__pb2._BEGINRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Commit', - full_name='queryservice.Query.Commit', - index=4, - containing_service=None, - input_type=query__pb2._COMMITREQUEST, - output_type=query__pb2._COMMITRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Rollback', - full_name='queryservice.Query.Rollback', - index=5, - containing_service=None, - input_type=query__pb2._ROLLBACKREQUEST, - output_type=query__pb2._ROLLBACKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Prepare', - full_name='queryservice.Query.Prepare', - index=6, - containing_service=None, - input_type=query__pb2._PREPAREREQUEST, - output_type=query__pb2._PREPARERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='CommitPrepared', - full_name='queryservice.Query.CommitPrepared', - index=7, - containing_service=None, - input_type=query__pb2._COMMITPREPAREDREQUEST, - output_type=query__pb2._COMMITPREPAREDRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='RollbackPrepared', - full_name='queryservice.Query.RollbackPrepared', - index=8, - containing_service=None, - input_type=query__pb2._ROLLBACKPREPAREDREQUEST, - output_type=query__pb2._ROLLBACKPREPAREDRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='CreateTransaction', - full_name='queryservice.Query.CreateTransaction', - index=9, - containing_service=None, - input_type=query__pb2._CREATETRANSACTIONREQUEST, - output_type=query__pb2._CREATETRANSACTIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StartCommit', - full_name='queryservice.Query.StartCommit', - index=10, - containing_service=None, - input_type=query__pb2._STARTCOMMITREQUEST, - output_type=query__pb2._STARTCOMMITRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SetRollback', - full_name='queryservice.Query.SetRollback', - index=11, - containing_service=None, - input_type=query__pb2._SETROLLBACKREQUEST, - output_type=query__pb2._SETROLLBACKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ConcludeTransaction', - full_name='queryservice.Query.ConcludeTransaction', - index=12, - containing_service=None, - input_type=query__pb2._CONCLUDETRANSACTIONREQUEST, - output_type=query__pb2._CONCLUDETRANSACTIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ReadTransaction', - full_name='queryservice.Query.ReadTransaction', - index=13, - containing_service=None, - input_type=query__pb2._READTRANSACTIONREQUEST, - output_type=query__pb2._READTRANSACTIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='BeginExecute', - full_name='queryservice.Query.BeginExecute', - index=14, - containing_service=None, - input_type=query__pb2._BEGINEXECUTEREQUEST, - output_type=query__pb2._BEGINEXECUTERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='BeginExecuteBatch', - full_name='queryservice.Query.BeginExecuteBatch', - index=15, - containing_service=None, - input_type=query__pb2._BEGINEXECUTEBATCHREQUEST, - output_type=query__pb2._BEGINEXECUTEBATCHRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='MessageStream', - full_name='queryservice.Query.MessageStream', - index=16, - containing_service=None, - input_type=query__pb2._MESSAGESTREAMREQUEST, - output_type=query__pb2._MESSAGESTREAMRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='MessageAck', - full_name='queryservice.Query.MessageAck', - index=17, - containing_service=None, - input_type=query__pb2._MESSAGEACKREQUEST, - output_type=query__pb2._MESSAGEACKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SplitQuery', - full_name='queryservice.Query.SplitQuery', - index=18, - containing_service=None, - input_type=query__pb2._SPLITQUERYREQUEST, - output_type=query__pb2._SPLITQUERYRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StreamHealth', - full_name='queryservice.Query.StreamHealth', - index=19, - containing_service=None, - input_type=query__pb2._STREAMHEALTHREQUEST, - output_type=query__pb2._STREAMHEALTHRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='UpdateStream', - full_name='queryservice.Query.UpdateStream', - index=20, - containing_service=None, - input_type=query__pb2._UPDATESTREAMREQUEST, - output_type=query__pb2._UPDATESTREAMRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='VStream', - full_name='queryservice.Query.VStream', - index=21, - containing_service=None, - input_type=binlogdata__pb2._VSTREAMREQUEST, - output_type=binlogdata__pb2._VSTREAMRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='VStreamRows', - full_name='queryservice.Query.VStreamRows', - index=22, - containing_service=None, - input_type=binlogdata__pb2._VSTREAMROWSREQUEST, - output_type=binlogdata__pb2._VSTREAMROWSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='VStreamResults', - full_name='queryservice.Query.VStreamResults', - index=23, - containing_service=None, - input_type=binlogdata__pb2._VSTREAMRESULTSREQUEST, - output_type=binlogdata__pb2._VSTREAMRESULTSRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_QUERY) - -DESCRIPTOR.services_by_name['Query'] = _QUERY - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/queryservice_pb2_grpc.py b/py/vtproto/queryservice_pb2_grpc.py deleted file mode 100644 index f8d31b3ce3e..00000000000 --- a/py/vtproto/queryservice_pb2_grpc.py +++ /dev/null @@ -1,445 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import binlogdata_pb2 as binlogdata__pb2 -import query_pb2 as query__pb2 - - -class QueryStub(object): - """Query defines the tablet query service, implemented by vttablet. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.Execute = channel.unary_unary( - '/queryservice.Query/Execute', - request_serializer=query__pb2.ExecuteRequest.SerializeToString, - response_deserializer=query__pb2.ExecuteResponse.FromString, - ) - self.ExecuteBatch = channel.unary_unary( - '/queryservice.Query/ExecuteBatch', - request_serializer=query__pb2.ExecuteBatchRequest.SerializeToString, - response_deserializer=query__pb2.ExecuteBatchResponse.FromString, - ) - self.StreamExecute = channel.unary_stream( - '/queryservice.Query/StreamExecute', - request_serializer=query__pb2.StreamExecuteRequest.SerializeToString, - response_deserializer=query__pb2.StreamExecuteResponse.FromString, - ) - self.Begin = channel.unary_unary( - '/queryservice.Query/Begin', - request_serializer=query__pb2.BeginRequest.SerializeToString, - response_deserializer=query__pb2.BeginResponse.FromString, - ) - self.Commit = channel.unary_unary( - '/queryservice.Query/Commit', - request_serializer=query__pb2.CommitRequest.SerializeToString, - response_deserializer=query__pb2.CommitResponse.FromString, - ) - self.Rollback = channel.unary_unary( - '/queryservice.Query/Rollback', - request_serializer=query__pb2.RollbackRequest.SerializeToString, - response_deserializer=query__pb2.RollbackResponse.FromString, - ) - self.Prepare = channel.unary_unary( - '/queryservice.Query/Prepare', - request_serializer=query__pb2.PrepareRequest.SerializeToString, - response_deserializer=query__pb2.PrepareResponse.FromString, - ) - self.CommitPrepared = channel.unary_unary( - '/queryservice.Query/CommitPrepared', - request_serializer=query__pb2.CommitPreparedRequest.SerializeToString, - response_deserializer=query__pb2.CommitPreparedResponse.FromString, - ) - self.RollbackPrepared = channel.unary_unary( - '/queryservice.Query/RollbackPrepared', - request_serializer=query__pb2.RollbackPreparedRequest.SerializeToString, - response_deserializer=query__pb2.RollbackPreparedResponse.FromString, - ) - self.CreateTransaction = channel.unary_unary( - '/queryservice.Query/CreateTransaction', - request_serializer=query__pb2.CreateTransactionRequest.SerializeToString, - response_deserializer=query__pb2.CreateTransactionResponse.FromString, - ) - self.StartCommit = channel.unary_unary( - '/queryservice.Query/StartCommit', - request_serializer=query__pb2.StartCommitRequest.SerializeToString, - response_deserializer=query__pb2.StartCommitResponse.FromString, - ) - self.SetRollback = channel.unary_unary( - '/queryservice.Query/SetRollback', - request_serializer=query__pb2.SetRollbackRequest.SerializeToString, - response_deserializer=query__pb2.SetRollbackResponse.FromString, - ) - self.ConcludeTransaction = channel.unary_unary( - '/queryservice.Query/ConcludeTransaction', - request_serializer=query__pb2.ConcludeTransactionRequest.SerializeToString, - response_deserializer=query__pb2.ConcludeTransactionResponse.FromString, - ) - self.ReadTransaction = channel.unary_unary( - '/queryservice.Query/ReadTransaction', - request_serializer=query__pb2.ReadTransactionRequest.SerializeToString, - response_deserializer=query__pb2.ReadTransactionResponse.FromString, - ) - self.BeginExecute = channel.unary_unary( - '/queryservice.Query/BeginExecute', - request_serializer=query__pb2.BeginExecuteRequest.SerializeToString, - response_deserializer=query__pb2.BeginExecuteResponse.FromString, - ) - self.BeginExecuteBatch = channel.unary_unary( - '/queryservice.Query/BeginExecuteBatch', - request_serializer=query__pb2.BeginExecuteBatchRequest.SerializeToString, - response_deserializer=query__pb2.BeginExecuteBatchResponse.FromString, - ) - self.MessageStream = channel.unary_stream( - '/queryservice.Query/MessageStream', - request_serializer=query__pb2.MessageStreamRequest.SerializeToString, - response_deserializer=query__pb2.MessageStreamResponse.FromString, - ) - self.MessageAck = channel.unary_unary( - '/queryservice.Query/MessageAck', - request_serializer=query__pb2.MessageAckRequest.SerializeToString, - response_deserializer=query__pb2.MessageAckResponse.FromString, - ) - self.SplitQuery = channel.unary_unary( - '/queryservice.Query/SplitQuery', - request_serializer=query__pb2.SplitQueryRequest.SerializeToString, - response_deserializer=query__pb2.SplitQueryResponse.FromString, - ) - self.StreamHealth = channel.unary_stream( - '/queryservice.Query/StreamHealth', - request_serializer=query__pb2.StreamHealthRequest.SerializeToString, - response_deserializer=query__pb2.StreamHealthResponse.FromString, - ) - self.UpdateStream = channel.unary_stream( - '/queryservice.Query/UpdateStream', - request_serializer=query__pb2.UpdateStreamRequest.SerializeToString, - response_deserializer=query__pb2.UpdateStreamResponse.FromString, - ) - self.VStream = channel.unary_stream( - '/queryservice.Query/VStream', - request_serializer=binlogdata__pb2.VStreamRequest.SerializeToString, - response_deserializer=binlogdata__pb2.VStreamResponse.FromString, - ) - self.VStreamRows = channel.unary_stream( - '/queryservice.Query/VStreamRows', - request_serializer=binlogdata__pb2.VStreamRowsRequest.SerializeToString, - response_deserializer=binlogdata__pb2.VStreamRowsResponse.FromString, - ) - self.VStreamResults = channel.unary_stream( - '/queryservice.Query/VStreamResults', - request_serializer=binlogdata__pb2.VStreamResultsRequest.SerializeToString, - response_deserializer=binlogdata__pb2.VStreamResultsResponse.FromString, - ) - - -class QueryServicer(object): - """Query defines the tablet query service, implemented by vttablet. - """ - - def Execute(self, request, context): - """Execute executes the specified SQL query (might be in a - transaction context, if Query.transaction_id is set). - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteBatch(self, request, context): - """ExecuteBatch executes a list of queries, and returns the result - for each query. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StreamExecute(self, request, context): - """StreamExecute executes a streaming query. Use this method if the - query returns a large number of rows. The first QueryResult will - contain the Fields, subsequent QueryResult messages will contain - the rows. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Begin(self, request, context): - """Begin a transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Commit(self, request, context): - """Commit a transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Rollback(self, request, context): - """Rollback a transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Prepare(self, request, context): - """Prepare preares a transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def CommitPrepared(self, request, context): - """CommitPrepared commits a prepared transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def RollbackPrepared(self, request, context): - """RollbackPrepared rolls back a prepared transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def CreateTransaction(self, request, context): - """CreateTransaction creates the metadata for a 2pc transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StartCommit(self, request, context): - """StartCommit initiates a commit for a 2pc transaction. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetRollback(self, request, context): - """SetRollback marks the 2pc transaction for rollback. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ConcludeTransaction(self, request, context): - """ConcludeTransaction marks the 2pc transaction as resolved. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ReadTransaction(self, request, context): - """ReadTransaction returns the 2pc transaction info. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def BeginExecute(self, request, context): - """BeginExecute executes a begin and the specified SQL query. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def BeginExecuteBatch(self, request, context): - """BeginExecuteBatch executes a begin and a list of queries. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MessageStream(self, request, context): - """MessageStream streams messages from a message table. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MessageAck(self, request, context): - """MessageAck acks messages for a table. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SplitQuery(self, request, context): - """SplitQuery is the API to facilitate MapReduce-type iterations - over large data sets (like full table dumps). - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StreamHealth(self, request, context): - """StreamHealth runs a streaming RPC to the tablet, that returns the - current health of the tablet on a regular basis. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def UpdateStream(self, request, context): - """UpdateStream asks the server to return a stream of the updates that have been applied to its database. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def VStream(self, request, context): - """VStream streams vreplication events. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def VStreamRows(self, request, context): - """VStreamRows streams rows from the specified starting point. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def VStreamResults(self, request, context): - """VStreamResults streams results along with the gtid of the snapshot. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_QueryServicer_to_server(servicer, server): - rpc_method_handlers = { - 'Execute': grpc.unary_unary_rpc_method_handler( - servicer.Execute, - request_deserializer=query__pb2.ExecuteRequest.FromString, - response_serializer=query__pb2.ExecuteResponse.SerializeToString, - ), - 'ExecuteBatch': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteBatch, - request_deserializer=query__pb2.ExecuteBatchRequest.FromString, - response_serializer=query__pb2.ExecuteBatchResponse.SerializeToString, - ), - 'StreamExecute': grpc.unary_stream_rpc_method_handler( - servicer.StreamExecute, - request_deserializer=query__pb2.StreamExecuteRequest.FromString, - response_serializer=query__pb2.StreamExecuteResponse.SerializeToString, - ), - 'Begin': grpc.unary_unary_rpc_method_handler( - servicer.Begin, - request_deserializer=query__pb2.BeginRequest.FromString, - response_serializer=query__pb2.BeginResponse.SerializeToString, - ), - 'Commit': grpc.unary_unary_rpc_method_handler( - servicer.Commit, - request_deserializer=query__pb2.CommitRequest.FromString, - response_serializer=query__pb2.CommitResponse.SerializeToString, - ), - 'Rollback': grpc.unary_unary_rpc_method_handler( - servicer.Rollback, - request_deserializer=query__pb2.RollbackRequest.FromString, - response_serializer=query__pb2.RollbackResponse.SerializeToString, - ), - 'Prepare': grpc.unary_unary_rpc_method_handler( - servicer.Prepare, - request_deserializer=query__pb2.PrepareRequest.FromString, - response_serializer=query__pb2.PrepareResponse.SerializeToString, - ), - 'CommitPrepared': grpc.unary_unary_rpc_method_handler( - servicer.CommitPrepared, - request_deserializer=query__pb2.CommitPreparedRequest.FromString, - response_serializer=query__pb2.CommitPreparedResponse.SerializeToString, - ), - 'RollbackPrepared': grpc.unary_unary_rpc_method_handler( - servicer.RollbackPrepared, - request_deserializer=query__pb2.RollbackPreparedRequest.FromString, - response_serializer=query__pb2.RollbackPreparedResponse.SerializeToString, - ), - 'CreateTransaction': grpc.unary_unary_rpc_method_handler( - servicer.CreateTransaction, - request_deserializer=query__pb2.CreateTransactionRequest.FromString, - response_serializer=query__pb2.CreateTransactionResponse.SerializeToString, - ), - 'StartCommit': grpc.unary_unary_rpc_method_handler( - servicer.StartCommit, - request_deserializer=query__pb2.StartCommitRequest.FromString, - response_serializer=query__pb2.StartCommitResponse.SerializeToString, - ), - 'SetRollback': grpc.unary_unary_rpc_method_handler( - servicer.SetRollback, - request_deserializer=query__pb2.SetRollbackRequest.FromString, - response_serializer=query__pb2.SetRollbackResponse.SerializeToString, - ), - 'ConcludeTransaction': grpc.unary_unary_rpc_method_handler( - servicer.ConcludeTransaction, - request_deserializer=query__pb2.ConcludeTransactionRequest.FromString, - response_serializer=query__pb2.ConcludeTransactionResponse.SerializeToString, - ), - 'ReadTransaction': grpc.unary_unary_rpc_method_handler( - servicer.ReadTransaction, - request_deserializer=query__pb2.ReadTransactionRequest.FromString, - response_serializer=query__pb2.ReadTransactionResponse.SerializeToString, - ), - 'BeginExecute': grpc.unary_unary_rpc_method_handler( - servicer.BeginExecute, - request_deserializer=query__pb2.BeginExecuteRequest.FromString, - response_serializer=query__pb2.BeginExecuteResponse.SerializeToString, - ), - 'BeginExecuteBatch': grpc.unary_unary_rpc_method_handler( - servicer.BeginExecuteBatch, - request_deserializer=query__pb2.BeginExecuteBatchRequest.FromString, - response_serializer=query__pb2.BeginExecuteBatchResponse.SerializeToString, - ), - 'MessageStream': grpc.unary_stream_rpc_method_handler( - servicer.MessageStream, - request_deserializer=query__pb2.MessageStreamRequest.FromString, - response_serializer=query__pb2.MessageStreamResponse.SerializeToString, - ), - 'MessageAck': grpc.unary_unary_rpc_method_handler( - servicer.MessageAck, - request_deserializer=query__pb2.MessageAckRequest.FromString, - response_serializer=query__pb2.MessageAckResponse.SerializeToString, - ), - 'SplitQuery': grpc.unary_unary_rpc_method_handler( - servicer.SplitQuery, - request_deserializer=query__pb2.SplitQueryRequest.FromString, - response_serializer=query__pb2.SplitQueryResponse.SerializeToString, - ), - 'StreamHealth': grpc.unary_stream_rpc_method_handler( - servicer.StreamHealth, - request_deserializer=query__pb2.StreamHealthRequest.FromString, - response_serializer=query__pb2.StreamHealthResponse.SerializeToString, - ), - 'UpdateStream': grpc.unary_stream_rpc_method_handler( - servicer.UpdateStream, - request_deserializer=query__pb2.UpdateStreamRequest.FromString, - response_serializer=query__pb2.UpdateStreamResponse.SerializeToString, - ), - 'VStream': grpc.unary_stream_rpc_method_handler( - servicer.VStream, - request_deserializer=binlogdata__pb2.VStreamRequest.FromString, - response_serializer=binlogdata__pb2.VStreamResponse.SerializeToString, - ), - 'VStreamRows': grpc.unary_stream_rpc_method_handler( - servicer.VStreamRows, - request_deserializer=binlogdata__pb2.VStreamRowsRequest.FromString, - response_serializer=binlogdata__pb2.VStreamRowsResponse.SerializeToString, - ), - 'VStreamResults': grpc.unary_stream_rpc_method_handler( - servicer.VStreamResults, - request_deserializer=binlogdata__pb2.VStreamResultsRequest.FromString, - response_serializer=binlogdata__pb2.VStreamResultsResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'queryservice.Query', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/replicationdata_pb2.py b/py/vtproto/replicationdata_pb2.py deleted file mode 100644 index 972a3970aee..00000000000 --- a/py/vtproto/replicationdata_pb2.py +++ /dev/null @@ -1,112 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: replicationdata.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='replicationdata.proto', - package='replicationdata', - syntax='proto3', - serialized_options=_b('Z,vitess.io/vitess/go/vt/proto/replicationdata'), - serialized_pb=_b('\n\x15replicationdata.proto\x12\x0freplicationdata\"\xb6\x01\n\x06Status\x12\x10\n\x08position\x18\x01 \x01(\t\x12\x18\n\x10slave_io_running\x18\x02 \x01(\x08\x12\x19\n\x11slave_sql_running\x18\x03 \x01(\x08\x12\x1d\n\x15seconds_behind_master\x18\x04 \x01(\r\x12\x13\n\x0bmaster_host\x18\x05 \x01(\t\x12\x13\n\x0bmaster_port\x18\x06 \x01(\x05\x12\x1c\n\x14master_connect_retry\x18\x07 \x01(\x05\x42.Z,vitess.io/vitess/go/vt/proto/replicationdatab\x06proto3') -) - - - - -_STATUS = _descriptor.Descriptor( - name='Status', - full_name='replicationdata.Status', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='replicationdata.Status.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='slave_io_running', full_name='replicationdata.Status.slave_io_running', index=1, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='slave_sql_running', full_name='replicationdata.Status.slave_sql_running', index=2, - number=3, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='seconds_behind_master', full_name='replicationdata.Status.seconds_behind_master', index=3, - number=4, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='master_host', full_name='replicationdata.Status.master_host', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='master_port', full_name='replicationdata.Status.master_port', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='master_connect_retry', full_name='replicationdata.Status.master_connect_retry', index=6, - number=7, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=43, - serialized_end=225, -) - -DESCRIPTOR.message_types_by_name['Status'] = _STATUS -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Status = _reflection.GeneratedProtocolMessageType('Status', (_message.Message,), dict( - DESCRIPTOR = _STATUS, - __module__ = 'replicationdata_pb2' - # @@protoc_insertion_point(class_scope:replicationdata.Status) - )) -_sym_db.RegisterMessage(Status) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/replicationdata_pb2_grpc.py b/py/vtproto/replicationdata_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/replicationdata_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/tableacl_pb2.py b/py/vtproto/tableacl_pb2.py deleted file mode 100644 index 793bfdd5886..00000000000 --- a/py/vtproto/tableacl_pb2.py +++ /dev/null @@ -1,138 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: tableacl.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='tableacl.proto', - package='tableacl', - syntax='proto3', - serialized_options=_b('Z%vitess.io/vitess/go/vt/proto/tableacl'), - serialized_pb=_b('\n\x0etableacl.proto\x12\x08tableacl\"q\n\x0eTableGroupSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1f\n\x17table_names_or_prefixes\x18\x02 \x03(\t\x12\x0f\n\x07readers\x18\x03 \x03(\t\x12\x0f\n\x07writers\x18\x04 \x03(\t\x12\x0e\n\x06\x61\x64mins\x18\x05 \x03(\t\"8\n\x06\x43onfig\x12.\n\x0ctable_groups\x18\x01 \x03(\x0b\x32\x18.tableacl.TableGroupSpecB\'Z%vitess.io/vitess/go/vt/proto/tableaclb\x06proto3') -) - - - - -_TABLEGROUPSPEC = _descriptor.Descriptor( - name='TableGroupSpec', - full_name='tableacl.TableGroupSpec', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='tableacl.TableGroupSpec.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='table_names_or_prefixes', full_name='tableacl.TableGroupSpec.table_names_or_prefixes', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='readers', full_name='tableacl.TableGroupSpec.readers', index=2, - number=3, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='writers', full_name='tableacl.TableGroupSpec.writers', index=3, - number=4, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='admins', full_name='tableacl.TableGroupSpec.admins', index=4, - number=5, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=28, - serialized_end=141, -) - - -_CONFIG = _descriptor.Descriptor( - name='Config', - full_name='tableacl.Config', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='table_groups', full_name='tableacl.Config.table_groups', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=143, - serialized_end=199, -) - -_CONFIG.fields_by_name['table_groups'].message_type = _TABLEGROUPSPEC -DESCRIPTOR.message_types_by_name['TableGroupSpec'] = _TABLEGROUPSPEC -DESCRIPTOR.message_types_by_name['Config'] = _CONFIG -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -TableGroupSpec = _reflection.GeneratedProtocolMessageType('TableGroupSpec', (_message.Message,), dict( - DESCRIPTOR = _TABLEGROUPSPEC, - __module__ = 'tableacl_pb2' - # @@protoc_insertion_point(class_scope:tableacl.TableGroupSpec) - )) -_sym_db.RegisterMessage(TableGroupSpec) - -Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), dict( - DESCRIPTOR = _CONFIG, - __module__ = 'tableacl_pb2' - # @@protoc_insertion_point(class_scope:tableacl.Config) - )) -_sym_db.RegisterMessage(Config) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/tableacl_pb2_grpc.py b/py/vtproto/tableacl_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/tableacl_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/tabletmanagerdata_pb2.py b/py/vtproto/tabletmanagerdata_pb2.py deleted file mode 100644 index 0b246f994d9..00000000000 --- a/py/vtproto/tabletmanagerdata_pb2.py +++ /dev/null @@ -1,3971 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: tabletmanagerdata.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import query_pb2 as query__pb2 -import topodata_pb2 as topodata__pb2 -import replicationdata_pb2 as replicationdata__pb2 -import logutil_pb2 as logutil__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='tabletmanagerdata.proto', - package='tabletmanagerdata', - syntax='proto3', - serialized_options=_b('Z.vitess.io/vitess/go/vt/proto/tabletmanagerdata'), - serialized_pb=_b('\n\x17tabletmanagerdata.proto\x12\x11tabletmanagerdata\x1a\x0bquery.proto\x1a\x0etopodata.proto\x1a\x15replicationdata.proto\x1a\rlogutil.proto\"\xb1\x01\n\x0fTableDefinition\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06schema\x18\x02 \x01(\t\x12\x0f\n\x07\x63olumns\x18\x03 \x03(\t\x12\x1b\n\x13primary_key_columns\x18\x04 \x03(\t\x12\x0c\n\x04type\x18\x05 \x01(\t\x12\x13\n\x0b\x64\x61ta_length\x18\x06 \x01(\x04\x12\x11\n\trow_count\x18\x07 \x01(\x04\x12\x1c\n\x06\x66ields\x18\x08 \x03(\x0b\x32\x0c.query.Field\"{\n\x10SchemaDefinition\x12\x17\n\x0f\x64\x61tabase_schema\x18\x01 \x01(\t\x12=\n\x11table_definitions\x18\x02 \x03(\x0b\x32\".tabletmanagerdata.TableDefinition\x12\x0f\n\x07version\x18\x03 \x01(\t\"\x8b\x01\n\x12SchemaChangeResult\x12:\n\rbefore_schema\x18\x01 \x01(\x0b\x32#.tabletmanagerdata.SchemaDefinition\x12\x39\n\x0c\x61\x66ter_schema\x18\x02 \x01(\x0b\x32#.tabletmanagerdata.SchemaDefinition\"\xc1\x01\n\x0eUserPermission\x12\x0c\n\x04host\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\x12\x19\n\x11password_checksum\x18\x03 \x01(\x04\x12\x45\n\nprivileges\x18\x04 \x03(\x0b\x32\x31.tabletmanagerdata.UserPermission.PrivilegesEntry\x1a\x31\n\x0fPrivilegesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xae\x01\n\x0c\x44\x62Permission\x12\x0c\n\x04host\x18\x01 \x01(\t\x12\n\n\x02\x64\x62\x18\x02 \x01(\t\x12\x0c\n\x04user\x18\x03 \x01(\t\x12\x43\n\nprivileges\x18\x04 \x03(\x0b\x32/.tabletmanagerdata.DbPermission.PrivilegesEntry\x1a\x31\n\x0fPrivilegesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x83\x01\n\x0bPermissions\x12;\n\x10user_permissions\x18\x01 \x03(\x0b\x32!.tabletmanagerdata.UserPermission\x12\x37\n\x0e\x64\x62_permissions\x18\x02 \x03(\x0b\x32\x1f.tabletmanagerdata.DbPermission\"\x1e\n\x0bPingRequest\x12\x0f\n\x07payload\x18\x01 \x01(\t\"\x1f\n\x0cPingResponse\x12\x0f\n\x07payload\x18\x01 \x01(\t\" \n\x0cSleepRequest\x12\x10\n\x08\x64uration\x18\x01 \x01(\x03\"\x0f\n\rSleepResponse\"\xaf\x01\n\x12\x45xecuteHookRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\nparameters\x18\x02 \x03(\t\x12\x46\n\textra_env\x18\x03 \x03(\x0b\x32\x33.tabletmanagerdata.ExecuteHookRequest.ExtraEnvEntry\x1a/\n\rExtraEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"J\n\x13\x45xecuteHookResponse\x12\x13\n\x0b\x65xit_status\x18\x01 \x01(\x03\x12\x0e\n\x06stdout\x18\x02 \x01(\t\x12\x0e\n\x06stderr\x18\x03 \x01(\t\"Q\n\x10GetSchemaRequest\x12\x0e\n\x06tables\x18\x01 \x03(\t\x12\x15\n\rinclude_views\x18\x02 \x01(\x08\x12\x16\n\x0e\x65xclude_tables\x18\x03 \x03(\t\"S\n\x11GetSchemaResponse\x12>\n\x11schema_definition\x18\x01 \x01(\x0b\x32#.tabletmanagerdata.SchemaDefinition\"\x17\n\x15GetPermissionsRequest\"M\n\x16GetPermissionsResponse\x12\x33\n\x0bpermissions\x18\x01 \x01(\x0b\x32\x1e.tabletmanagerdata.Permissions\"\x14\n\x12SetReadOnlyRequest\"\x15\n\x13SetReadOnlyResponse\"\x15\n\x13SetReadWriteRequest\"\x16\n\x14SetReadWriteResponse\">\n\x11\x43hangeTypeRequest\x12)\n\x0btablet_type\x18\x01 \x01(\x0e\x32\x14.topodata.TabletType\"\x14\n\x12\x43hangeTypeResponse\"\x15\n\x13RefreshStateRequest\"\x16\n\x14RefreshStateResponse\"\x17\n\x15RunHealthCheckRequest\"\x18\n\x16RunHealthCheckResponse\"+\n\x18IgnoreHealthErrorRequest\x12\x0f\n\x07pattern\x18\x01 \x01(\t\"\x1b\n\x19IgnoreHealthErrorResponse\",\n\x13ReloadSchemaRequest\x12\x15\n\rwait_position\x18\x01 \x01(\t\"\x16\n\x14ReloadSchemaResponse\")\n\x16PreflightSchemaRequest\x12\x0f\n\x07\x63hanges\x18\x01 \x03(\t\"X\n\x17PreflightSchemaResponse\x12=\n\x0e\x63hange_results\x18\x01 \x03(\x0b\x32%.tabletmanagerdata.SchemaChangeResult\"\xc2\x01\n\x12\x41pplySchemaRequest\x12\x0b\n\x03sql\x18\x01 \x01(\t\x12\r\n\x05\x66orce\x18\x02 \x01(\x08\x12\x19\n\x11\x61llow_replication\x18\x03 \x01(\x08\x12:\n\rbefore_schema\x18\x04 \x01(\x0b\x32#.tabletmanagerdata.SchemaDefinition\x12\x39\n\x0c\x61\x66ter_schema\x18\x05 \x01(\x0b\x32#.tabletmanagerdata.SchemaDefinition\"\x8c\x01\n\x13\x41pplySchemaResponse\x12:\n\rbefore_schema\x18\x01 \x01(\x0b\x32#.tabletmanagerdata.SchemaDefinition\x12\x39\n\x0c\x61\x66ter_schema\x18\x02 \x01(\x0b\x32#.tabletmanagerdata.SchemaDefinition\"\x13\n\x11LockTablesRequest\"\x14\n\x12LockTablesResponse\"\x15\n\x13UnlockTablesRequest\"\x16\n\x14UnlockTablesResponse\"|\n\x18\x45xecuteFetchAsDbaRequest\x12\r\n\x05query\x18\x01 \x01(\x0c\x12\x0f\n\x07\x64\x62_name\x18\x02 \x01(\t\x12\x10\n\x08max_rows\x18\x03 \x01(\x04\x12\x17\n\x0f\x64isable_binlogs\x18\x04 \x01(\x08\x12\x15\n\rreload_schema\x18\x05 \x01(\x08\"?\n\x19\x45xecuteFetchAsDbaResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"h\n\x1d\x45xecuteFetchAsAllPrivsRequest\x12\r\n\x05query\x18\x01 \x01(\x0c\x12\x0f\n\x07\x64\x62_name\x18\x02 \x01(\t\x12\x10\n\x08max_rows\x18\x03 \x01(\x04\x12\x15\n\rreload_schema\x18\x04 \x01(\x08\"D\n\x1e\x45xecuteFetchAsAllPrivsResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\";\n\x18\x45xecuteFetchAsAppRequest\x12\r\n\x05query\x18\x01 \x01(\x0c\x12\x10\n\x08max_rows\x18\x02 \x01(\x04\"?\n\x19\x45xecuteFetchAsAppResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"\x14\n\x12SlaveStatusRequest\">\n\x13SlaveStatusResponse\x12\'\n\x06status\x18\x01 \x01(\x0b\x32\x17.replicationdata.Status\"\x17\n\x15MasterPositionRequest\"*\n\x16MasterPositionResponse\x12\x10\n\x08position\x18\x01 \x01(\t\"*\n\x16WaitForPositionRequest\x12\x10\n\x08position\x18\x01 \x01(\t\"\x19\n\x17WaitForPositionResponse\"\x12\n\x10StopSlaveRequest\"\x13\n\x11StopSlaveResponse\"A\n\x17StopSlaveMinimumRequest\x12\x10\n\x08position\x18\x01 \x01(\t\x12\x14\n\x0cwait_timeout\x18\x02 \x01(\x03\",\n\x18StopSlaveMinimumResponse\x12\x10\n\x08position\x18\x01 \x01(\t\"\x13\n\x11StartSlaveRequest\"\x14\n\x12StartSlaveResponse\"E\n\x1bStartSlaveUntilAfterRequest\x12\x10\n\x08position\x18\x01 \x01(\t\x12\x14\n\x0cwait_timeout\x18\x02 \x01(\x03\"\x1e\n\x1cStartSlaveUntilAfterResponse\"8\n!TabletExternallyReparentedRequest\x12\x13\n\x0b\x65xternal_id\x18\x01 \x01(\t\"$\n\"TabletExternallyReparentedResponse\" \n\x1eTabletExternallyElectedRequest\"!\n\x1fTabletExternallyElectedResponse\"\x12\n\x10GetSlavesRequest\"\"\n\x11GetSlavesResponse\x12\r\n\x05\x61\x64\x64rs\x18\x01 \x03(\t\"\x19\n\x17ResetReplicationRequest\"\x1a\n\x18ResetReplicationResponse\"(\n\x17VReplicationExecRequest\x12\r\n\x05query\x18\x01 \x01(\t\">\n\x18VReplicationExecResponse\x12\"\n\x06result\x18\x01 \x01(\x0b\x32\x12.query.QueryResult\"=\n\x1dVReplicationWaitForPosRequest\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x10\n\x08position\x18\x02 \x01(\t\" \n\x1eVReplicationWaitForPosResponse\"\x13\n\x11InitMasterRequest\"&\n\x12InitMasterResponse\x12\x10\n\x08position\x18\x01 \x01(\t\"\x99\x01\n\x1ePopulateReparentJournalRequest\x12\x17\n\x0ftime_created_ns\x18\x01 \x01(\x03\x12\x13\n\x0b\x61\x63tion_name\x18\x02 \x01(\t\x12+\n\x0cmaster_alias\x18\x03 \x01(\x0b\x32\x15.topodata.TabletAlias\x12\x1c\n\x14replication_position\x18\x04 \x01(\t\"!\n\x1fPopulateReparentJournalResponse\"p\n\x10InitSlaveRequest\x12%\n\x06parent\x18\x01 \x01(\x0b\x32\x15.topodata.TabletAlias\x12\x1c\n\x14replication_position\x18\x02 \x01(\t\x12\x17\n\x0ftime_created_ns\x18\x03 \x01(\x03\"\x13\n\x11InitSlaveResponse\"\x15\n\x13\x44\x65moteMasterRequest\"(\n\x14\x44\x65moteMasterResponse\x12\x10\n\x08position\x18\x01 \x01(\t\"\x19\n\x17UndoDemoteMasterRequest\"\x1a\n\x18UndoDemoteMasterResponse\"3\n\x1fPromoteSlaveWhenCaughtUpRequest\x12\x10\n\x08position\x18\x01 \x01(\t\"4\n PromoteSlaveWhenCaughtUpResponse\x12\x10\n\x08position\x18\x01 \x01(\t\"\x19\n\x17SlaveWasPromotedRequest\"\x1a\n\x18SlaveWasPromotedResponse\"\x84\x01\n\x10SetMasterRequest\x12%\n\x06parent\x18\x01 \x01(\x0b\x32\x15.topodata.TabletAlias\x12\x17\n\x0ftime_created_ns\x18\x02 \x01(\x03\x12\x15\n\rwait_position\x18\x04 \x01(\t\x12\x19\n\x11\x66orce_start_slave\x18\x03 \x01(\x08\"\x13\n\x11SetMasterResponse\"A\n\x18SlaveWasRestartedRequest\x12%\n\x06parent\x18\x01 \x01(\x0b\x32\x15.topodata.TabletAlias\"\x1b\n\x19SlaveWasRestartedResponse\"$\n\"StopReplicationAndGetStatusRequest\"N\n#StopReplicationAndGetStatusResponse\x12\'\n\x06status\x18\x01 \x01(\x0b\x32\x17.replicationdata.Status\"\x15\n\x13PromoteSlaveRequest\"(\n\x14PromoteSlaveResponse\x12\x10\n\x08position\x18\x01 \x01(\t\"9\n\rBackupRequest\x12\x13\n\x0b\x63oncurrency\x18\x01 \x01(\x03\x12\x13\n\x0b\x61llowMaster\x18\x02 \x01(\x08\"/\n\x0e\x42\x61\x63kupResponse\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.logutil.Event\"\x1a\n\x18RestoreFromBackupRequest\":\n\x19RestoreFromBackupResponse\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.logutil.EventB0Z.vitess.io/vitess/go/vt/proto/tabletmanagerdatab\x06proto3') - , - dependencies=[query__pb2.DESCRIPTOR,topodata__pb2.DESCRIPTOR,replicationdata__pb2.DESCRIPTOR,logutil__pb2.DESCRIPTOR,]) - - - - -_TABLEDEFINITION = _descriptor.Descriptor( - name='TableDefinition', - full_name='tabletmanagerdata.TableDefinition', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='tabletmanagerdata.TableDefinition.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='schema', full_name='tabletmanagerdata.TableDefinition.schema', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='columns', full_name='tabletmanagerdata.TableDefinition.columns', index=2, - number=3, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='primary_key_columns', full_name='tabletmanagerdata.TableDefinition.primary_key_columns', index=3, - number=4, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='type', full_name='tabletmanagerdata.TableDefinition.type', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='data_length', full_name='tabletmanagerdata.TableDefinition.data_length', index=5, - number=6, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='row_count', full_name='tabletmanagerdata.TableDefinition.row_count', index=6, - number=7, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='fields', full_name='tabletmanagerdata.TableDefinition.fields', index=7, - number=8, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=114, - serialized_end=291, -) - - -_SCHEMADEFINITION = _descriptor.Descriptor( - name='SchemaDefinition', - full_name='tabletmanagerdata.SchemaDefinition', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='database_schema', full_name='tabletmanagerdata.SchemaDefinition.database_schema', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='table_definitions', full_name='tabletmanagerdata.SchemaDefinition.table_definitions', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='version', full_name='tabletmanagerdata.SchemaDefinition.version', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=293, - serialized_end=416, -) - - -_SCHEMACHANGERESULT = _descriptor.Descriptor( - name='SchemaChangeResult', - full_name='tabletmanagerdata.SchemaChangeResult', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='before_schema', full_name='tabletmanagerdata.SchemaChangeResult.before_schema', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='after_schema', full_name='tabletmanagerdata.SchemaChangeResult.after_schema', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=419, - serialized_end=558, -) - - -_USERPERMISSION_PRIVILEGESENTRY = _descriptor.Descriptor( - name='PrivilegesEntry', - full_name='tabletmanagerdata.UserPermission.PrivilegesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='tabletmanagerdata.UserPermission.PrivilegesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='tabletmanagerdata.UserPermission.PrivilegesEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=705, - serialized_end=754, -) - -_USERPERMISSION = _descriptor.Descriptor( - name='UserPermission', - full_name='tabletmanagerdata.UserPermission', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='host', full_name='tabletmanagerdata.UserPermission.host', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='user', full_name='tabletmanagerdata.UserPermission.user', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='password_checksum', full_name='tabletmanagerdata.UserPermission.password_checksum', index=2, - number=3, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='privileges', full_name='tabletmanagerdata.UserPermission.privileges', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_USERPERMISSION_PRIVILEGESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=561, - serialized_end=754, -) - - -_DBPERMISSION_PRIVILEGESENTRY = _descriptor.Descriptor( - name='PrivilegesEntry', - full_name='tabletmanagerdata.DbPermission.PrivilegesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='tabletmanagerdata.DbPermission.PrivilegesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='tabletmanagerdata.DbPermission.PrivilegesEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=705, - serialized_end=754, -) - -_DBPERMISSION = _descriptor.Descriptor( - name='DbPermission', - full_name='tabletmanagerdata.DbPermission', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='host', full_name='tabletmanagerdata.DbPermission.host', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='db', full_name='tabletmanagerdata.DbPermission.db', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='user', full_name='tabletmanagerdata.DbPermission.user', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='privileges', full_name='tabletmanagerdata.DbPermission.privileges', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_DBPERMISSION_PRIVILEGESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=757, - serialized_end=931, -) - - -_PERMISSIONS = _descriptor.Descriptor( - name='Permissions', - full_name='tabletmanagerdata.Permissions', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='user_permissions', full_name='tabletmanagerdata.Permissions.user_permissions', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='db_permissions', full_name='tabletmanagerdata.Permissions.db_permissions', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=934, - serialized_end=1065, -) - - -_PINGREQUEST = _descriptor.Descriptor( - name='PingRequest', - full_name='tabletmanagerdata.PingRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='tabletmanagerdata.PingRequest.payload', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1067, - serialized_end=1097, -) - - -_PINGRESPONSE = _descriptor.Descriptor( - name='PingResponse', - full_name='tabletmanagerdata.PingResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='payload', full_name='tabletmanagerdata.PingResponse.payload', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1099, - serialized_end=1130, -) - - -_SLEEPREQUEST = _descriptor.Descriptor( - name='SleepRequest', - full_name='tabletmanagerdata.SleepRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='duration', full_name='tabletmanagerdata.SleepRequest.duration', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1132, - serialized_end=1164, -) - - -_SLEEPRESPONSE = _descriptor.Descriptor( - name='SleepResponse', - full_name='tabletmanagerdata.SleepResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1166, - serialized_end=1181, -) - - -_EXECUTEHOOKREQUEST_EXTRAENVENTRY = _descriptor.Descriptor( - name='ExtraEnvEntry', - full_name='tabletmanagerdata.ExecuteHookRequest.ExtraEnvEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='tabletmanagerdata.ExecuteHookRequest.ExtraEnvEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='tabletmanagerdata.ExecuteHookRequest.ExtraEnvEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1312, - serialized_end=1359, -) - -_EXECUTEHOOKREQUEST = _descriptor.Descriptor( - name='ExecuteHookRequest', - full_name='tabletmanagerdata.ExecuteHookRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='tabletmanagerdata.ExecuteHookRequest.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='parameters', full_name='tabletmanagerdata.ExecuteHookRequest.parameters', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='extra_env', full_name='tabletmanagerdata.ExecuteHookRequest.extra_env', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_EXECUTEHOOKREQUEST_EXTRAENVENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1184, - serialized_end=1359, -) - - -_EXECUTEHOOKRESPONSE = _descriptor.Descriptor( - name='ExecuteHookResponse', - full_name='tabletmanagerdata.ExecuteHookResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='exit_status', full_name='tabletmanagerdata.ExecuteHookResponse.exit_status', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='stdout', full_name='tabletmanagerdata.ExecuteHookResponse.stdout', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='stderr', full_name='tabletmanagerdata.ExecuteHookResponse.stderr', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1361, - serialized_end=1435, -) - - -_GETSCHEMAREQUEST = _descriptor.Descriptor( - name='GetSchemaRequest', - full_name='tabletmanagerdata.GetSchemaRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tables', full_name='tabletmanagerdata.GetSchemaRequest.tables', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='include_views', full_name='tabletmanagerdata.GetSchemaRequest.include_views', index=1, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='exclude_tables', full_name='tabletmanagerdata.GetSchemaRequest.exclude_tables', index=2, - number=3, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1437, - serialized_end=1518, -) - - -_GETSCHEMARESPONSE = _descriptor.Descriptor( - name='GetSchemaResponse', - full_name='tabletmanagerdata.GetSchemaResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='schema_definition', full_name='tabletmanagerdata.GetSchemaResponse.schema_definition', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1520, - serialized_end=1603, -) - - -_GETPERMISSIONSREQUEST = _descriptor.Descriptor( - name='GetPermissionsRequest', - full_name='tabletmanagerdata.GetPermissionsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1605, - serialized_end=1628, -) - - -_GETPERMISSIONSRESPONSE = _descriptor.Descriptor( - name='GetPermissionsResponse', - full_name='tabletmanagerdata.GetPermissionsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='permissions', full_name='tabletmanagerdata.GetPermissionsResponse.permissions', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1630, - serialized_end=1707, -) - - -_SETREADONLYREQUEST = _descriptor.Descriptor( - name='SetReadOnlyRequest', - full_name='tabletmanagerdata.SetReadOnlyRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1709, - serialized_end=1729, -) - - -_SETREADONLYRESPONSE = _descriptor.Descriptor( - name='SetReadOnlyResponse', - full_name='tabletmanagerdata.SetReadOnlyResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1731, - serialized_end=1752, -) - - -_SETREADWRITEREQUEST = _descriptor.Descriptor( - name='SetReadWriteRequest', - full_name='tabletmanagerdata.SetReadWriteRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1754, - serialized_end=1775, -) - - -_SETREADWRITERESPONSE = _descriptor.Descriptor( - name='SetReadWriteResponse', - full_name='tabletmanagerdata.SetReadWriteResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1777, - serialized_end=1799, -) - - -_CHANGETYPEREQUEST = _descriptor.Descriptor( - name='ChangeTypeRequest', - full_name='tabletmanagerdata.ChangeTypeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tablet_type', full_name='tabletmanagerdata.ChangeTypeRequest.tablet_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1801, - serialized_end=1863, -) - - -_CHANGETYPERESPONSE = _descriptor.Descriptor( - name='ChangeTypeResponse', - full_name='tabletmanagerdata.ChangeTypeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1865, - serialized_end=1885, -) - - -_REFRESHSTATEREQUEST = _descriptor.Descriptor( - name='RefreshStateRequest', - full_name='tabletmanagerdata.RefreshStateRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1887, - serialized_end=1908, -) - - -_REFRESHSTATERESPONSE = _descriptor.Descriptor( - name='RefreshStateResponse', - full_name='tabletmanagerdata.RefreshStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1910, - serialized_end=1932, -) - - -_RUNHEALTHCHECKREQUEST = _descriptor.Descriptor( - name='RunHealthCheckRequest', - full_name='tabletmanagerdata.RunHealthCheckRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1934, - serialized_end=1957, -) - - -_RUNHEALTHCHECKRESPONSE = _descriptor.Descriptor( - name='RunHealthCheckResponse', - full_name='tabletmanagerdata.RunHealthCheckResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1959, - serialized_end=1983, -) - - -_IGNOREHEALTHERRORREQUEST = _descriptor.Descriptor( - name='IgnoreHealthErrorRequest', - full_name='tabletmanagerdata.IgnoreHealthErrorRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='pattern', full_name='tabletmanagerdata.IgnoreHealthErrorRequest.pattern', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1985, - serialized_end=2028, -) - - -_IGNOREHEALTHERRORRESPONSE = _descriptor.Descriptor( - name='IgnoreHealthErrorResponse', - full_name='tabletmanagerdata.IgnoreHealthErrorResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2030, - serialized_end=2057, -) - - -_RELOADSCHEMAREQUEST = _descriptor.Descriptor( - name='ReloadSchemaRequest', - full_name='tabletmanagerdata.ReloadSchemaRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='wait_position', full_name='tabletmanagerdata.ReloadSchemaRequest.wait_position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2059, - serialized_end=2103, -) - - -_RELOADSCHEMARESPONSE = _descriptor.Descriptor( - name='ReloadSchemaResponse', - full_name='tabletmanagerdata.ReloadSchemaResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2105, - serialized_end=2127, -) - - -_PREFLIGHTSCHEMAREQUEST = _descriptor.Descriptor( - name='PreflightSchemaRequest', - full_name='tabletmanagerdata.PreflightSchemaRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='changes', full_name='tabletmanagerdata.PreflightSchemaRequest.changes', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2129, - serialized_end=2170, -) - - -_PREFLIGHTSCHEMARESPONSE = _descriptor.Descriptor( - name='PreflightSchemaResponse', - full_name='tabletmanagerdata.PreflightSchemaResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='change_results', full_name='tabletmanagerdata.PreflightSchemaResponse.change_results', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2172, - serialized_end=2260, -) - - -_APPLYSCHEMAREQUEST = _descriptor.Descriptor( - name='ApplySchemaRequest', - full_name='tabletmanagerdata.ApplySchemaRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='sql', full_name='tabletmanagerdata.ApplySchemaRequest.sql', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='force', full_name='tabletmanagerdata.ApplySchemaRequest.force', index=1, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='allow_replication', full_name='tabletmanagerdata.ApplySchemaRequest.allow_replication', index=2, - number=3, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='before_schema', full_name='tabletmanagerdata.ApplySchemaRequest.before_schema', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='after_schema', full_name='tabletmanagerdata.ApplySchemaRequest.after_schema', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2263, - serialized_end=2457, -) - - -_APPLYSCHEMARESPONSE = _descriptor.Descriptor( - name='ApplySchemaResponse', - full_name='tabletmanagerdata.ApplySchemaResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='before_schema', full_name='tabletmanagerdata.ApplySchemaResponse.before_schema', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='after_schema', full_name='tabletmanagerdata.ApplySchemaResponse.after_schema', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2460, - serialized_end=2600, -) - - -_LOCKTABLESREQUEST = _descriptor.Descriptor( - name='LockTablesRequest', - full_name='tabletmanagerdata.LockTablesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2602, - serialized_end=2621, -) - - -_LOCKTABLESRESPONSE = _descriptor.Descriptor( - name='LockTablesResponse', - full_name='tabletmanagerdata.LockTablesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2623, - serialized_end=2643, -) - - -_UNLOCKTABLESREQUEST = _descriptor.Descriptor( - name='UnlockTablesRequest', - full_name='tabletmanagerdata.UnlockTablesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2645, - serialized_end=2666, -) - - -_UNLOCKTABLESRESPONSE = _descriptor.Descriptor( - name='UnlockTablesResponse', - full_name='tabletmanagerdata.UnlockTablesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2668, - serialized_end=2690, -) - - -_EXECUTEFETCHASDBAREQUEST = _descriptor.Descriptor( - name='ExecuteFetchAsDbaRequest', - full_name='tabletmanagerdata.ExecuteFetchAsDbaRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='tabletmanagerdata.ExecuteFetchAsDbaRequest.query', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='db_name', full_name='tabletmanagerdata.ExecuteFetchAsDbaRequest.db_name', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_rows', full_name='tabletmanagerdata.ExecuteFetchAsDbaRequest.max_rows', index=2, - number=3, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='disable_binlogs', full_name='tabletmanagerdata.ExecuteFetchAsDbaRequest.disable_binlogs', 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='reload_schema', full_name='tabletmanagerdata.ExecuteFetchAsDbaRequest.reload_schema', index=4, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2692, - serialized_end=2816, -) - - -_EXECUTEFETCHASDBARESPONSE = _descriptor.Descriptor( - name='ExecuteFetchAsDbaResponse', - full_name='tabletmanagerdata.ExecuteFetchAsDbaResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='tabletmanagerdata.ExecuteFetchAsDbaResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2818, - serialized_end=2881, -) - - -_EXECUTEFETCHASALLPRIVSREQUEST = _descriptor.Descriptor( - name='ExecuteFetchAsAllPrivsRequest', - full_name='tabletmanagerdata.ExecuteFetchAsAllPrivsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='tabletmanagerdata.ExecuteFetchAsAllPrivsRequest.query', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='db_name', full_name='tabletmanagerdata.ExecuteFetchAsAllPrivsRequest.db_name', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_rows', full_name='tabletmanagerdata.ExecuteFetchAsAllPrivsRequest.max_rows', index=2, - number=3, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='reload_schema', full_name='tabletmanagerdata.ExecuteFetchAsAllPrivsRequest.reload_schema', 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2883, - serialized_end=2987, -) - - -_EXECUTEFETCHASALLPRIVSRESPONSE = _descriptor.Descriptor( - name='ExecuteFetchAsAllPrivsResponse', - full_name='tabletmanagerdata.ExecuteFetchAsAllPrivsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='tabletmanagerdata.ExecuteFetchAsAllPrivsResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2989, - serialized_end=3057, -) - - -_EXECUTEFETCHASAPPREQUEST = _descriptor.Descriptor( - name='ExecuteFetchAsAppRequest', - full_name='tabletmanagerdata.ExecuteFetchAsAppRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='tabletmanagerdata.ExecuteFetchAsAppRequest.query', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_rows', full_name='tabletmanagerdata.ExecuteFetchAsAppRequest.max_rows', index=1, - number=2, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3059, - serialized_end=3118, -) - - -_EXECUTEFETCHASAPPRESPONSE = _descriptor.Descriptor( - name='ExecuteFetchAsAppResponse', - full_name='tabletmanagerdata.ExecuteFetchAsAppResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='tabletmanagerdata.ExecuteFetchAsAppResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3120, - serialized_end=3183, -) - - -_SLAVESTATUSREQUEST = _descriptor.Descriptor( - name='SlaveStatusRequest', - full_name='tabletmanagerdata.SlaveStatusRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3185, - serialized_end=3205, -) - - -_SLAVESTATUSRESPONSE = _descriptor.Descriptor( - name='SlaveStatusResponse', - full_name='tabletmanagerdata.SlaveStatusResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='tabletmanagerdata.SlaveStatusResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3207, - serialized_end=3269, -) - - -_MASTERPOSITIONREQUEST = _descriptor.Descriptor( - name='MasterPositionRequest', - full_name='tabletmanagerdata.MasterPositionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3271, - serialized_end=3294, -) - - -_MASTERPOSITIONRESPONSE = _descriptor.Descriptor( - name='MasterPositionResponse', - full_name='tabletmanagerdata.MasterPositionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.MasterPositionResponse.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3296, - serialized_end=3338, -) - - -_WAITFORPOSITIONREQUEST = _descriptor.Descriptor( - name='WaitForPositionRequest', - full_name='tabletmanagerdata.WaitForPositionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.WaitForPositionRequest.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3340, - serialized_end=3382, -) - - -_WAITFORPOSITIONRESPONSE = _descriptor.Descriptor( - name='WaitForPositionResponse', - full_name='tabletmanagerdata.WaitForPositionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3384, - serialized_end=3409, -) - - -_STOPSLAVEREQUEST = _descriptor.Descriptor( - name='StopSlaveRequest', - full_name='tabletmanagerdata.StopSlaveRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3411, - serialized_end=3429, -) - - -_STOPSLAVERESPONSE = _descriptor.Descriptor( - name='StopSlaveResponse', - full_name='tabletmanagerdata.StopSlaveResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3431, - serialized_end=3450, -) - - -_STOPSLAVEMINIMUMREQUEST = _descriptor.Descriptor( - name='StopSlaveMinimumRequest', - full_name='tabletmanagerdata.StopSlaveMinimumRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.StopSlaveMinimumRequest.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='wait_timeout', full_name='tabletmanagerdata.StopSlaveMinimumRequest.wait_timeout', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3452, - serialized_end=3517, -) - - -_STOPSLAVEMINIMUMRESPONSE = _descriptor.Descriptor( - name='StopSlaveMinimumResponse', - full_name='tabletmanagerdata.StopSlaveMinimumResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.StopSlaveMinimumResponse.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3519, - serialized_end=3563, -) - - -_STARTSLAVEREQUEST = _descriptor.Descriptor( - name='StartSlaveRequest', - full_name='tabletmanagerdata.StartSlaveRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3565, - serialized_end=3584, -) - - -_STARTSLAVERESPONSE = _descriptor.Descriptor( - name='StartSlaveResponse', - full_name='tabletmanagerdata.StartSlaveResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3586, - serialized_end=3606, -) - - -_STARTSLAVEUNTILAFTERREQUEST = _descriptor.Descriptor( - name='StartSlaveUntilAfterRequest', - full_name='tabletmanagerdata.StartSlaveUntilAfterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.StartSlaveUntilAfterRequest.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='wait_timeout', full_name='tabletmanagerdata.StartSlaveUntilAfterRequest.wait_timeout', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3608, - serialized_end=3677, -) - - -_STARTSLAVEUNTILAFTERRESPONSE = _descriptor.Descriptor( - name='StartSlaveUntilAfterResponse', - full_name='tabletmanagerdata.StartSlaveUntilAfterResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3679, - serialized_end=3709, -) - - -_TABLETEXTERNALLYREPARENTEDREQUEST = _descriptor.Descriptor( - name='TabletExternallyReparentedRequest', - full_name='tabletmanagerdata.TabletExternallyReparentedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='external_id', full_name='tabletmanagerdata.TabletExternallyReparentedRequest.external_id', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3711, - serialized_end=3767, -) - - -_TABLETEXTERNALLYREPARENTEDRESPONSE = _descriptor.Descriptor( - name='TabletExternallyReparentedResponse', - full_name='tabletmanagerdata.TabletExternallyReparentedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3769, - serialized_end=3805, -) - - -_TABLETEXTERNALLYELECTEDREQUEST = _descriptor.Descriptor( - name='TabletExternallyElectedRequest', - full_name='tabletmanagerdata.TabletExternallyElectedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3807, - serialized_end=3839, -) - - -_TABLETEXTERNALLYELECTEDRESPONSE = _descriptor.Descriptor( - name='TabletExternallyElectedResponse', - full_name='tabletmanagerdata.TabletExternallyElectedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3841, - serialized_end=3874, -) - - -_GETSLAVESREQUEST = _descriptor.Descriptor( - name='GetSlavesRequest', - full_name='tabletmanagerdata.GetSlavesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3876, - serialized_end=3894, -) - - -_GETSLAVESRESPONSE = _descriptor.Descriptor( - name='GetSlavesResponse', - full_name='tabletmanagerdata.GetSlavesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='addrs', full_name='tabletmanagerdata.GetSlavesResponse.addrs', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3896, - serialized_end=3930, -) - - -_RESETREPLICATIONREQUEST = _descriptor.Descriptor( - name='ResetReplicationRequest', - full_name='tabletmanagerdata.ResetReplicationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3932, - serialized_end=3957, -) - - -_RESETREPLICATIONRESPONSE = _descriptor.Descriptor( - name='ResetReplicationResponse', - full_name='tabletmanagerdata.ResetReplicationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3959, - serialized_end=3985, -) - - -_VREPLICATIONEXECREQUEST = _descriptor.Descriptor( - name='VReplicationExecRequest', - full_name='tabletmanagerdata.VReplicationExecRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='tabletmanagerdata.VReplicationExecRequest.query', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3987, - serialized_end=4027, -) - - -_VREPLICATIONEXECRESPONSE = _descriptor.Descriptor( - name='VReplicationExecResponse', - full_name='tabletmanagerdata.VReplicationExecResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='tabletmanagerdata.VReplicationExecResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4029, - serialized_end=4091, -) - - -_VREPLICATIONWAITFORPOSREQUEST = _descriptor.Descriptor( - name='VReplicationWaitForPosRequest', - full_name='tabletmanagerdata.VReplicationWaitForPosRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='tabletmanagerdata.VReplicationWaitForPosRequest.id', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.VReplicationWaitForPosRequest.position', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4093, - serialized_end=4154, -) - - -_VREPLICATIONWAITFORPOSRESPONSE = _descriptor.Descriptor( - name='VReplicationWaitForPosResponse', - full_name='tabletmanagerdata.VReplicationWaitForPosResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4156, - serialized_end=4188, -) - - -_INITMASTERREQUEST = _descriptor.Descriptor( - name='InitMasterRequest', - full_name='tabletmanagerdata.InitMasterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4190, - serialized_end=4209, -) - - -_INITMASTERRESPONSE = _descriptor.Descriptor( - name='InitMasterResponse', - full_name='tabletmanagerdata.InitMasterResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.InitMasterResponse.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4211, - serialized_end=4249, -) - - -_POPULATEREPARENTJOURNALREQUEST = _descriptor.Descriptor( - name='PopulateReparentJournalRequest', - full_name='tabletmanagerdata.PopulateReparentJournalRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='time_created_ns', full_name='tabletmanagerdata.PopulateReparentJournalRequest.time_created_ns', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='action_name', full_name='tabletmanagerdata.PopulateReparentJournalRequest.action_name', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='master_alias', full_name='tabletmanagerdata.PopulateReparentJournalRequest.master_alias', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='replication_position', full_name='tabletmanagerdata.PopulateReparentJournalRequest.replication_position', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4252, - serialized_end=4405, -) - - -_POPULATEREPARENTJOURNALRESPONSE = _descriptor.Descriptor( - name='PopulateReparentJournalResponse', - full_name='tabletmanagerdata.PopulateReparentJournalResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4407, - serialized_end=4440, -) - - -_INITSLAVEREQUEST = _descriptor.Descriptor( - name='InitSlaveRequest', - full_name='tabletmanagerdata.InitSlaveRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='parent', full_name='tabletmanagerdata.InitSlaveRequest.parent', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='replication_position', full_name='tabletmanagerdata.InitSlaveRequest.replication_position', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='time_created_ns', full_name='tabletmanagerdata.InitSlaveRequest.time_created_ns', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4442, - serialized_end=4554, -) - - -_INITSLAVERESPONSE = _descriptor.Descriptor( - name='InitSlaveResponse', - full_name='tabletmanagerdata.InitSlaveResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4556, - serialized_end=4575, -) - - -_DEMOTEMASTERREQUEST = _descriptor.Descriptor( - name='DemoteMasterRequest', - full_name='tabletmanagerdata.DemoteMasterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4577, - serialized_end=4598, -) - - -_DEMOTEMASTERRESPONSE = _descriptor.Descriptor( - name='DemoteMasterResponse', - full_name='tabletmanagerdata.DemoteMasterResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.DemoteMasterResponse.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4600, - serialized_end=4640, -) - - -_UNDODEMOTEMASTERREQUEST = _descriptor.Descriptor( - name='UndoDemoteMasterRequest', - full_name='tabletmanagerdata.UndoDemoteMasterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4642, - serialized_end=4667, -) - - -_UNDODEMOTEMASTERRESPONSE = _descriptor.Descriptor( - name='UndoDemoteMasterResponse', - full_name='tabletmanagerdata.UndoDemoteMasterResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4669, - serialized_end=4695, -) - - -_PROMOTESLAVEWHENCAUGHTUPREQUEST = _descriptor.Descriptor( - name='PromoteSlaveWhenCaughtUpRequest', - full_name='tabletmanagerdata.PromoteSlaveWhenCaughtUpRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.PromoteSlaveWhenCaughtUpRequest.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4697, - serialized_end=4748, -) - - -_PROMOTESLAVEWHENCAUGHTUPRESPONSE = _descriptor.Descriptor( - name='PromoteSlaveWhenCaughtUpResponse', - full_name='tabletmanagerdata.PromoteSlaveWhenCaughtUpResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.PromoteSlaveWhenCaughtUpResponse.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4750, - serialized_end=4802, -) - - -_SLAVEWASPROMOTEDREQUEST = _descriptor.Descriptor( - name='SlaveWasPromotedRequest', - full_name='tabletmanagerdata.SlaveWasPromotedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4804, - serialized_end=4829, -) - - -_SLAVEWASPROMOTEDRESPONSE = _descriptor.Descriptor( - name='SlaveWasPromotedResponse', - full_name='tabletmanagerdata.SlaveWasPromotedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4831, - serialized_end=4857, -) - - -_SETMASTERREQUEST = _descriptor.Descriptor( - name='SetMasterRequest', - full_name='tabletmanagerdata.SetMasterRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='parent', full_name='tabletmanagerdata.SetMasterRequest.parent', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='time_created_ns', full_name='tabletmanagerdata.SetMasterRequest.time_created_ns', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='wait_position', full_name='tabletmanagerdata.SetMasterRequest.wait_position', index=2, - 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='force_start_slave', full_name='tabletmanagerdata.SetMasterRequest.force_start_slave', index=3, - number=3, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4860, - serialized_end=4992, -) - - -_SETMASTERRESPONSE = _descriptor.Descriptor( - name='SetMasterResponse', - full_name='tabletmanagerdata.SetMasterResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4994, - serialized_end=5013, -) - - -_SLAVEWASRESTARTEDREQUEST = _descriptor.Descriptor( - name='SlaveWasRestartedRequest', - full_name='tabletmanagerdata.SlaveWasRestartedRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='parent', full_name='tabletmanagerdata.SlaveWasRestartedRequest.parent', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5015, - serialized_end=5080, -) - - -_SLAVEWASRESTARTEDRESPONSE = _descriptor.Descriptor( - name='SlaveWasRestartedResponse', - full_name='tabletmanagerdata.SlaveWasRestartedResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5082, - serialized_end=5109, -) - - -_STOPREPLICATIONANDGETSTATUSREQUEST = _descriptor.Descriptor( - name='StopReplicationAndGetStatusRequest', - full_name='tabletmanagerdata.StopReplicationAndGetStatusRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5111, - serialized_end=5147, -) - - -_STOPREPLICATIONANDGETSTATUSRESPONSE = _descriptor.Descriptor( - name='StopReplicationAndGetStatusResponse', - full_name='tabletmanagerdata.StopReplicationAndGetStatusResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='status', full_name='tabletmanagerdata.StopReplicationAndGetStatusResponse.status', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5149, - serialized_end=5227, -) - - -_PROMOTESLAVEREQUEST = _descriptor.Descriptor( - name='PromoteSlaveRequest', - full_name='tabletmanagerdata.PromoteSlaveRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5229, - serialized_end=5250, -) - - -_PROMOTESLAVERESPONSE = _descriptor.Descriptor( - name='PromoteSlaveResponse', - full_name='tabletmanagerdata.PromoteSlaveResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='position', full_name='tabletmanagerdata.PromoteSlaveResponse.position', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5252, - serialized_end=5292, -) - - -_BACKUPREQUEST = _descriptor.Descriptor( - name='BackupRequest', - full_name='tabletmanagerdata.BackupRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='concurrency', full_name='tabletmanagerdata.BackupRequest.concurrency', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='allowMaster', full_name='tabletmanagerdata.BackupRequest.allowMaster', index=1, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5294, - serialized_end=5351, -) - - -_BACKUPRESPONSE = _descriptor.Descriptor( - name='BackupResponse', - full_name='tabletmanagerdata.BackupResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='event', full_name='tabletmanagerdata.BackupResponse.event', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5353, - serialized_end=5400, -) - - -_RESTOREFROMBACKUPREQUEST = _descriptor.Descriptor( - name='RestoreFromBackupRequest', - full_name='tabletmanagerdata.RestoreFromBackupRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5402, - serialized_end=5428, -) - - -_RESTOREFROMBACKUPRESPONSE = _descriptor.Descriptor( - name='RestoreFromBackupResponse', - full_name='tabletmanagerdata.RestoreFromBackupResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='event', full_name='tabletmanagerdata.RestoreFromBackupResponse.event', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5430, - serialized_end=5488, -) - -_TABLEDEFINITION.fields_by_name['fields'].message_type = query__pb2._FIELD -_SCHEMADEFINITION.fields_by_name['table_definitions'].message_type = _TABLEDEFINITION -_SCHEMACHANGERESULT.fields_by_name['before_schema'].message_type = _SCHEMADEFINITION -_SCHEMACHANGERESULT.fields_by_name['after_schema'].message_type = _SCHEMADEFINITION -_USERPERMISSION_PRIVILEGESENTRY.containing_type = _USERPERMISSION -_USERPERMISSION.fields_by_name['privileges'].message_type = _USERPERMISSION_PRIVILEGESENTRY -_DBPERMISSION_PRIVILEGESENTRY.containing_type = _DBPERMISSION -_DBPERMISSION.fields_by_name['privileges'].message_type = _DBPERMISSION_PRIVILEGESENTRY -_PERMISSIONS.fields_by_name['user_permissions'].message_type = _USERPERMISSION -_PERMISSIONS.fields_by_name['db_permissions'].message_type = _DBPERMISSION -_EXECUTEHOOKREQUEST_EXTRAENVENTRY.containing_type = _EXECUTEHOOKREQUEST -_EXECUTEHOOKREQUEST.fields_by_name['extra_env'].message_type = _EXECUTEHOOKREQUEST_EXTRAENVENTRY -_GETSCHEMARESPONSE.fields_by_name['schema_definition'].message_type = _SCHEMADEFINITION -_GETPERMISSIONSRESPONSE.fields_by_name['permissions'].message_type = _PERMISSIONS -_CHANGETYPEREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_PREFLIGHTSCHEMARESPONSE.fields_by_name['change_results'].message_type = _SCHEMACHANGERESULT -_APPLYSCHEMAREQUEST.fields_by_name['before_schema'].message_type = _SCHEMADEFINITION -_APPLYSCHEMAREQUEST.fields_by_name['after_schema'].message_type = _SCHEMADEFINITION -_APPLYSCHEMARESPONSE.fields_by_name['before_schema'].message_type = _SCHEMADEFINITION -_APPLYSCHEMARESPONSE.fields_by_name['after_schema'].message_type = _SCHEMADEFINITION -_EXECUTEFETCHASDBARESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_EXECUTEFETCHASALLPRIVSRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_EXECUTEFETCHASAPPRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_SLAVESTATUSRESPONSE.fields_by_name['status'].message_type = replicationdata__pb2._STATUS -_VREPLICATIONEXECRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_POPULATEREPARENTJOURNALREQUEST.fields_by_name['master_alias'].message_type = topodata__pb2._TABLETALIAS -_INITSLAVEREQUEST.fields_by_name['parent'].message_type = topodata__pb2._TABLETALIAS -_SETMASTERREQUEST.fields_by_name['parent'].message_type = topodata__pb2._TABLETALIAS -_SLAVEWASRESTARTEDREQUEST.fields_by_name['parent'].message_type = topodata__pb2._TABLETALIAS -_STOPREPLICATIONANDGETSTATUSRESPONSE.fields_by_name['status'].message_type = replicationdata__pb2._STATUS -_BACKUPRESPONSE.fields_by_name['event'].message_type = logutil__pb2._EVENT -_RESTOREFROMBACKUPRESPONSE.fields_by_name['event'].message_type = logutil__pb2._EVENT -DESCRIPTOR.message_types_by_name['TableDefinition'] = _TABLEDEFINITION -DESCRIPTOR.message_types_by_name['SchemaDefinition'] = _SCHEMADEFINITION -DESCRIPTOR.message_types_by_name['SchemaChangeResult'] = _SCHEMACHANGERESULT -DESCRIPTOR.message_types_by_name['UserPermission'] = _USERPERMISSION -DESCRIPTOR.message_types_by_name['DbPermission'] = _DBPERMISSION -DESCRIPTOR.message_types_by_name['Permissions'] = _PERMISSIONS -DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST -DESCRIPTOR.message_types_by_name['PingResponse'] = _PINGRESPONSE -DESCRIPTOR.message_types_by_name['SleepRequest'] = _SLEEPREQUEST -DESCRIPTOR.message_types_by_name['SleepResponse'] = _SLEEPRESPONSE -DESCRIPTOR.message_types_by_name['ExecuteHookRequest'] = _EXECUTEHOOKREQUEST -DESCRIPTOR.message_types_by_name['ExecuteHookResponse'] = _EXECUTEHOOKRESPONSE -DESCRIPTOR.message_types_by_name['GetSchemaRequest'] = _GETSCHEMAREQUEST -DESCRIPTOR.message_types_by_name['GetSchemaResponse'] = _GETSCHEMARESPONSE -DESCRIPTOR.message_types_by_name['GetPermissionsRequest'] = _GETPERMISSIONSREQUEST -DESCRIPTOR.message_types_by_name['GetPermissionsResponse'] = _GETPERMISSIONSRESPONSE -DESCRIPTOR.message_types_by_name['SetReadOnlyRequest'] = _SETREADONLYREQUEST -DESCRIPTOR.message_types_by_name['SetReadOnlyResponse'] = _SETREADONLYRESPONSE -DESCRIPTOR.message_types_by_name['SetReadWriteRequest'] = _SETREADWRITEREQUEST -DESCRIPTOR.message_types_by_name['SetReadWriteResponse'] = _SETREADWRITERESPONSE -DESCRIPTOR.message_types_by_name['ChangeTypeRequest'] = _CHANGETYPEREQUEST -DESCRIPTOR.message_types_by_name['ChangeTypeResponse'] = _CHANGETYPERESPONSE -DESCRIPTOR.message_types_by_name['RefreshStateRequest'] = _REFRESHSTATEREQUEST -DESCRIPTOR.message_types_by_name['RefreshStateResponse'] = _REFRESHSTATERESPONSE -DESCRIPTOR.message_types_by_name['RunHealthCheckRequest'] = _RUNHEALTHCHECKREQUEST -DESCRIPTOR.message_types_by_name['RunHealthCheckResponse'] = _RUNHEALTHCHECKRESPONSE -DESCRIPTOR.message_types_by_name['IgnoreHealthErrorRequest'] = _IGNOREHEALTHERRORREQUEST -DESCRIPTOR.message_types_by_name['IgnoreHealthErrorResponse'] = _IGNOREHEALTHERRORRESPONSE -DESCRIPTOR.message_types_by_name['ReloadSchemaRequest'] = _RELOADSCHEMAREQUEST -DESCRIPTOR.message_types_by_name['ReloadSchemaResponse'] = _RELOADSCHEMARESPONSE -DESCRIPTOR.message_types_by_name['PreflightSchemaRequest'] = _PREFLIGHTSCHEMAREQUEST -DESCRIPTOR.message_types_by_name['PreflightSchemaResponse'] = _PREFLIGHTSCHEMARESPONSE -DESCRIPTOR.message_types_by_name['ApplySchemaRequest'] = _APPLYSCHEMAREQUEST -DESCRIPTOR.message_types_by_name['ApplySchemaResponse'] = _APPLYSCHEMARESPONSE -DESCRIPTOR.message_types_by_name['LockTablesRequest'] = _LOCKTABLESREQUEST -DESCRIPTOR.message_types_by_name['LockTablesResponse'] = _LOCKTABLESRESPONSE -DESCRIPTOR.message_types_by_name['UnlockTablesRequest'] = _UNLOCKTABLESREQUEST -DESCRIPTOR.message_types_by_name['UnlockTablesResponse'] = _UNLOCKTABLESRESPONSE -DESCRIPTOR.message_types_by_name['ExecuteFetchAsDbaRequest'] = _EXECUTEFETCHASDBAREQUEST -DESCRIPTOR.message_types_by_name['ExecuteFetchAsDbaResponse'] = _EXECUTEFETCHASDBARESPONSE -DESCRIPTOR.message_types_by_name['ExecuteFetchAsAllPrivsRequest'] = _EXECUTEFETCHASALLPRIVSREQUEST -DESCRIPTOR.message_types_by_name['ExecuteFetchAsAllPrivsResponse'] = _EXECUTEFETCHASALLPRIVSRESPONSE -DESCRIPTOR.message_types_by_name['ExecuteFetchAsAppRequest'] = _EXECUTEFETCHASAPPREQUEST -DESCRIPTOR.message_types_by_name['ExecuteFetchAsAppResponse'] = _EXECUTEFETCHASAPPRESPONSE -DESCRIPTOR.message_types_by_name['SlaveStatusRequest'] = _SLAVESTATUSREQUEST -DESCRIPTOR.message_types_by_name['SlaveStatusResponse'] = _SLAVESTATUSRESPONSE -DESCRIPTOR.message_types_by_name['MasterPositionRequest'] = _MASTERPOSITIONREQUEST -DESCRIPTOR.message_types_by_name['MasterPositionResponse'] = _MASTERPOSITIONRESPONSE -DESCRIPTOR.message_types_by_name['WaitForPositionRequest'] = _WAITFORPOSITIONREQUEST -DESCRIPTOR.message_types_by_name['WaitForPositionResponse'] = _WAITFORPOSITIONRESPONSE -DESCRIPTOR.message_types_by_name['StopSlaveRequest'] = _STOPSLAVEREQUEST -DESCRIPTOR.message_types_by_name['StopSlaveResponse'] = _STOPSLAVERESPONSE -DESCRIPTOR.message_types_by_name['StopSlaveMinimumRequest'] = _STOPSLAVEMINIMUMREQUEST -DESCRIPTOR.message_types_by_name['StopSlaveMinimumResponse'] = _STOPSLAVEMINIMUMRESPONSE -DESCRIPTOR.message_types_by_name['StartSlaveRequest'] = _STARTSLAVEREQUEST -DESCRIPTOR.message_types_by_name['StartSlaveResponse'] = _STARTSLAVERESPONSE -DESCRIPTOR.message_types_by_name['StartSlaveUntilAfterRequest'] = _STARTSLAVEUNTILAFTERREQUEST -DESCRIPTOR.message_types_by_name['StartSlaveUntilAfterResponse'] = _STARTSLAVEUNTILAFTERRESPONSE -DESCRIPTOR.message_types_by_name['TabletExternallyReparentedRequest'] = _TABLETEXTERNALLYREPARENTEDREQUEST -DESCRIPTOR.message_types_by_name['TabletExternallyReparentedResponse'] = _TABLETEXTERNALLYREPARENTEDRESPONSE -DESCRIPTOR.message_types_by_name['TabletExternallyElectedRequest'] = _TABLETEXTERNALLYELECTEDREQUEST -DESCRIPTOR.message_types_by_name['TabletExternallyElectedResponse'] = _TABLETEXTERNALLYELECTEDRESPONSE -DESCRIPTOR.message_types_by_name['GetSlavesRequest'] = _GETSLAVESREQUEST -DESCRIPTOR.message_types_by_name['GetSlavesResponse'] = _GETSLAVESRESPONSE -DESCRIPTOR.message_types_by_name['ResetReplicationRequest'] = _RESETREPLICATIONREQUEST -DESCRIPTOR.message_types_by_name['ResetReplicationResponse'] = _RESETREPLICATIONRESPONSE -DESCRIPTOR.message_types_by_name['VReplicationExecRequest'] = _VREPLICATIONEXECREQUEST -DESCRIPTOR.message_types_by_name['VReplicationExecResponse'] = _VREPLICATIONEXECRESPONSE -DESCRIPTOR.message_types_by_name['VReplicationWaitForPosRequest'] = _VREPLICATIONWAITFORPOSREQUEST -DESCRIPTOR.message_types_by_name['VReplicationWaitForPosResponse'] = _VREPLICATIONWAITFORPOSRESPONSE -DESCRIPTOR.message_types_by_name['InitMasterRequest'] = _INITMASTERREQUEST -DESCRIPTOR.message_types_by_name['InitMasterResponse'] = _INITMASTERRESPONSE -DESCRIPTOR.message_types_by_name['PopulateReparentJournalRequest'] = _POPULATEREPARENTJOURNALREQUEST -DESCRIPTOR.message_types_by_name['PopulateReparentJournalResponse'] = _POPULATEREPARENTJOURNALRESPONSE -DESCRIPTOR.message_types_by_name['InitSlaveRequest'] = _INITSLAVEREQUEST -DESCRIPTOR.message_types_by_name['InitSlaveResponse'] = _INITSLAVERESPONSE -DESCRIPTOR.message_types_by_name['DemoteMasterRequest'] = _DEMOTEMASTERREQUEST -DESCRIPTOR.message_types_by_name['DemoteMasterResponse'] = _DEMOTEMASTERRESPONSE -DESCRIPTOR.message_types_by_name['UndoDemoteMasterRequest'] = _UNDODEMOTEMASTERREQUEST -DESCRIPTOR.message_types_by_name['UndoDemoteMasterResponse'] = _UNDODEMOTEMASTERRESPONSE -DESCRIPTOR.message_types_by_name['PromoteSlaveWhenCaughtUpRequest'] = _PROMOTESLAVEWHENCAUGHTUPREQUEST -DESCRIPTOR.message_types_by_name['PromoteSlaveWhenCaughtUpResponse'] = _PROMOTESLAVEWHENCAUGHTUPRESPONSE -DESCRIPTOR.message_types_by_name['SlaveWasPromotedRequest'] = _SLAVEWASPROMOTEDREQUEST -DESCRIPTOR.message_types_by_name['SlaveWasPromotedResponse'] = _SLAVEWASPROMOTEDRESPONSE -DESCRIPTOR.message_types_by_name['SetMasterRequest'] = _SETMASTERREQUEST -DESCRIPTOR.message_types_by_name['SetMasterResponse'] = _SETMASTERRESPONSE -DESCRIPTOR.message_types_by_name['SlaveWasRestartedRequest'] = _SLAVEWASRESTARTEDREQUEST -DESCRIPTOR.message_types_by_name['SlaveWasRestartedResponse'] = _SLAVEWASRESTARTEDRESPONSE -DESCRIPTOR.message_types_by_name['StopReplicationAndGetStatusRequest'] = _STOPREPLICATIONANDGETSTATUSREQUEST -DESCRIPTOR.message_types_by_name['StopReplicationAndGetStatusResponse'] = _STOPREPLICATIONANDGETSTATUSRESPONSE -DESCRIPTOR.message_types_by_name['PromoteSlaveRequest'] = _PROMOTESLAVEREQUEST -DESCRIPTOR.message_types_by_name['PromoteSlaveResponse'] = _PROMOTESLAVERESPONSE -DESCRIPTOR.message_types_by_name['BackupRequest'] = _BACKUPREQUEST -DESCRIPTOR.message_types_by_name['BackupResponse'] = _BACKUPRESPONSE -DESCRIPTOR.message_types_by_name['RestoreFromBackupRequest'] = _RESTOREFROMBACKUPREQUEST -DESCRIPTOR.message_types_by_name['RestoreFromBackupResponse'] = _RESTOREFROMBACKUPRESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -TableDefinition = _reflection.GeneratedProtocolMessageType('TableDefinition', (_message.Message,), dict( - DESCRIPTOR = _TABLEDEFINITION, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.TableDefinition) - )) -_sym_db.RegisterMessage(TableDefinition) - -SchemaDefinition = _reflection.GeneratedProtocolMessageType('SchemaDefinition', (_message.Message,), dict( - DESCRIPTOR = _SCHEMADEFINITION, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SchemaDefinition) - )) -_sym_db.RegisterMessage(SchemaDefinition) - -SchemaChangeResult = _reflection.GeneratedProtocolMessageType('SchemaChangeResult', (_message.Message,), dict( - DESCRIPTOR = _SCHEMACHANGERESULT, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SchemaChangeResult) - )) -_sym_db.RegisterMessage(SchemaChangeResult) - -UserPermission = _reflection.GeneratedProtocolMessageType('UserPermission', (_message.Message,), dict( - - PrivilegesEntry = _reflection.GeneratedProtocolMessageType('PrivilegesEntry', (_message.Message,), dict( - DESCRIPTOR = _USERPERMISSION_PRIVILEGESENTRY, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.UserPermission.PrivilegesEntry) - )) - , - DESCRIPTOR = _USERPERMISSION, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.UserPermission) - )) -_sym_db.RegisterMessage(UserPermission) -_sym_db.RegisterMessage(UserPermission.PrivilegesEntry) - -DbPermission = _reflection.GeneratedProtocolMessageType('DbPermission', (_message.Message,), dict( - - PrivilegesEntry = _reflection.GeneratedProtocolMessageType('PrivilegesEntry', (_message.Message,), dict( - DESCRIPTOR = _DBPERMISSION_PRIVILEGESENTRY, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.DbPermission.PrivilegesEntry) - )) - , - DESCRIPTOR = _DBPERMISSION, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.DbPermission) - )) -_sym_db.RegisterMessage(DbPermission) -_sym_db.RegisterMessage(DbPermission.PrivilegesEntry) - -Permissions = _reflection.GeneratedProtocolMessageType('Permissions', (_message.Message,), dict( - DESCRIPTOR = _PERMISSIONS, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.Permissions) - )) -_sym_db.RegisterMessage(Permissions) - -PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), dict( - DESCRIPTOR = _PINGREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PingRequest) - )) -_sym_db.RegisterMessage(PingRequest) - -PingResponse = _reflection.GeneratedProtocolMessageType('PingResponse', (_message.Message,), dict( - DESCRIPTOR = _PINGRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PingResponse) - )) -_sym_db.RegisterMessage(PingResponse) - -SleepRequest = _reflection.GeneratedProtocolMessageType('SleepRequest', (_message.Message,), dict( - DESCRIPTOR = _SLEEPREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SleepRequest) - )) -_sym_db.RegisterMessage(SleepRequest) - -SleepResponse = _reflection.GeneratedProtocolMessageType('SleepResponse', (_message.Message,), dict( - DESCRIPTOR = _SLEEPRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SleepResponse) - )) -_sym_db.RegisterMessage(SleepResponse) - -ExecuteHookRequest = _reflection.GeneratedProtocolMessageType('ExecuteHookRequest', (_message.Message,), dict( - - ExtraEnvEntry = _reflection.GeneratedProtocolMessageType('ExtraEnvEntry', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEHOOKREQUEST_EXTRAENVENTRY, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteHookRequest.ExtraEnvEntry) - )) - , - DESCRIPTOR = _EXECUTEHOOKREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteHookRequest) - )) -_sym_db.RegisterMessage(ExecuteHookRequest) -_sym_db.RegisterMessage(ExecuteHookRequest.ExtraEnvEntry) - -ExecuteHookResponse = _reflection.GeneratedProtocolMessageType('ExecuteHookResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEHOOKRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteHookResponse) - )) -_sym_db.RegisterMessage(ExecuteHookResponse) - -GetSchemaRequest = _reflection.GeneratedProtocolMessageType('GetSchemaRequest', (_message.Message,), dict( - DESCRIPTOR = _GETSCHEMAREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.GetSchemaRequest) - )) -_sym_db.RegisterMessage(GetSchemaRequest) - -GetSchemaResponse = _reflection.GeneratedProtocolMessageType('GetSchemaResponse', (_message.Message,), dict( - DESCRIPTOR = _GETSCHEMARESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.GetSchemaResponse) - )) -_sym_db.RegisterMessage(GetSchemaResponse) - -GetPermissionsRequest = _reflection.GeneratedProtocolMessageType('GetPermissionsRequest', (_message.Message,), dict( - DESCRIPTOR = _GETPERMISSIONSREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.GetPermissionsRequest) - )) -_sym_db.RegisterMessage(GetPermissionsRequest) - -GetPermissionsResponse = _reflection.GeneratedProtocolMessageType('GetPermissionsResponse', (_message.Message,), dict( - DESCRIPTOR = _GETPERMISSIONSRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.GetPermissionsResponse) - )) -_sym_db.RegisterMessage(GetPermissionsResponse) - -SetReadOnlyRequest = _reflection.GeneratedProtocolMessageType('SetReadOnlyRequest', (_message.Message,), dict( - DESCRIPTOR = _SETREADONLYREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SetReadOnlyRequest) - )) -_sym_db.RegisterMessage(SetReadOnlyRequest) - -SetReadOnlyResponse = _reflection.GeneratedProtocolMessageType('SetReadOnlyResponse', (_message.Message,), dict( - DESCRIPTOR = _SETREADONLYRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SetReadOnlyResponse) - )) -_sym_db.RegisterMessage(SetReadOnlyResponse) - -SetReadWriteRequest = _reflection.GeneratedProtocolMessageType('SetReadWriteRequest', (_message.Message,), dict( - DESCRIPTOR = _SETREADWRITEREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SetReadWriteRequest) - )) -_sym_db.RegisterMessage(SetReadWriteRequest) - -SetReadWriteResponse = _reflection.GeneratedProtocolMessageType('SetReadWriteResponse', (_message.Message,), dict( - DESCRIPTOR = _SETREADWRITERESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SetReadWriteResponse) - )) -_sym_db.RegisterMessage(SetReadWriteResponse) - -ChangeTypeRequest = _reflection.GeneratedProtocolMessageType('ChangeTypeRequest', (_message.Message,), dict( - DESCRIPTOR = _CHANGETYPEREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ChangeTypeRequest) - )) -_sym_db.RegisterMessage(ChangeTypeRequest) - -ChangeTypeResponse = _reflection.GeneratedProtocolMessageType('ChangeTypeResponse', (_message.Message,), dict( - DESCRIPTOR = _CHANGETYPERESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ChangeTypeResponse) - )) -_sym_db.RegisterMessage(ChangeTypeResponse) - -RefreshStateRequest = _reflection.GeneratedProtocolMessageType('RefreshStateRequest', (_message.Message,), dict( - DESCRIPTOR = _REFRESHSTATEREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.RefreshStateRequest) - )) -_sym_db.RegisterMessage(RefreshStateRequest) - -RefreshStateResponse = _reflection.GeneratedProtocolMessageType('RefreshStateResponse', (_message.Message,), dict( - DESCRIPTOR = _REFRESHSTATERESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.RefreshStateResponse) - )) -_sym_db.RegisterMessage(RefreshStateResponse) - -RunHealthCheckRequest = _reflection.GeneratedProtocolMessageType('RunHealthCheckRequest', (_message.Message,), dict( - DESCRIPTOR = _RUNHEALTHCHECKREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.RunHealthCheckRequest) - )) -_sym_db.RegisterMessage(RunHealthCheckRequest) - -RunHealthCheckResponse = _reflection.GeneratedProtocolMessageType('RunHealthCheckResponse', (_message.Message,), dict( - DESCRIPTOR = _RUNHEALTHCHECKRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.RunHealthCheckResponse) - )) -_sym_db.RegisterMessage(RunHealthCheckResponse) - -IgnoreHealthErrorRequest = _reflection.GeneratedProtocolMessageType('IgnoreHealthErrorRequest', (_message.Message,), dict( - DESCRIPTOR = _IGNOREHEALTHERRORREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.IgnoreHealthErrorRequest) - )) -_sym_db.RegisterMessage(IgnoreHealthErrorRequest) - -IgnoreHealthErrorResponse = _reflection.GeneratedProtocolMessageType('IgnoreHealthErrorResponse', (_message.Message,), dict( - DESCRIPTOR = _IGNOREHEALTHERRORRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.IgnoreHealthErrorResponse) - )) -_sym_db.RegisterMessage(IgnoreHealthErrorResponse) - -ReloadSchemaRequest = _reflection.GeneratedProtocolMessageType('ReloadSchemaRequest', (_message.Message,), dict( - DESCRIPTOR = _RELOADSCHEMAREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ReloadSchemaRequest) - )) -_sym_db.RegisterMessage(ReloadSchemaRequest) - -ReloadSchemaResponse = _reflection.GeneratedProtocolMessageType('ReloadSchemaResponse', (_message.Message,), dict( - DESCRIPTOR = _RELOADSCHEMARESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ReloadSchemaResponse) - )) -_sym_db.RegisterMessage(ReloadSchemaResponse) - -PreflightSchemaRequest = _reflection.GeneratedProtocolMessageType('PreflightSchemaRequest', (_message.Message,), dict( - DESCRIPTOR = _PREFLIGHTSCHEMAREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PreflightSchemaRequest) - )) -_sym_db.RegisterMessage(PreflightSchemaRequest) - -PreflightSchemaResponse = _reflection.GeneratedProtocolMessageType('PreflightSchemaResponse', (_message.Message,), dict( - DESCRIPTOR = _PREFLIGHTSCHEMARESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PreflightSchemaResponse) - )) -_sym_db.RegisterMessage(PreflightSchemaResponse) - -ApplySchemaRequest = _reflection.GeneratedProtocolMessageType('ApplySchemaRequest', (_message.Message,), dict( - DESCRIPTOR = _APPLYSCHEMAREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ApplySchemaRequest) - )) -_sym_db.RegisterMessage(ApplySchemaRequest) - -ApplySchemaResponse = _reflection.GeneratedProtocolMessageType('ApplySchemaResponse', (_message.Message,), dict( - DESCRIPTOR = _APPLYSCHEMARESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ApplySchemaResponse) - )) -_sym_db.RegisterMessage(ApplySchemaResponse) - -LockTablesRequest = _reflection.GeneratedProtocolMessageType('LockTablesRequest', (_message.Message,), dict( - DESCRIPTOR = _LOCKTABLESREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.LockTablesRequest) - )) -_sym_db.RegisterMessage(LockTablesRequest) - -LockTablesResponse = _reflection.GeneratedProtocolMessageType('LockTablesResponse', (_message.Message,), dict( - DESCRIPTOR = _LOCKTABLESRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.LockTablesResponse) - )) -_sym_db.RegisterMessage(LockTablesResponse) - -UnlockTablesRequest = _reflection.GeneratedProtocolMessageType('UnlockTablesRequest', (_message.Message,), dict( - DESCRIPTOR = _UNLOCKTABLESREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.UnlockTablesRequest) - )) -_sym_db.RegisterMessage(UnlockTablesRequest) - -UnlockTablesResponse = _reflection.GeneratedProtocolMessageType('UnlockTablesResponse', (_message.Message,), dict( - DESCRIPTOR = _UNLOCKTABLESRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.UnlockTablesResponse) - )) -_sym_db.RegisterMessage(UnlockTablesResponse) - -ExecuteFetchAsDbaRequest = _reflection.GeneratedProtocolMessageType('ExecuteFetchAsDbaRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEFETCHASDBAREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteFetchAsDbaRequest) - )) -_sym_db.RegisterMessage(ExecuteFetchAsDbaRequest) - -ExecuteFetchAsDbaResponse = _reflection.GeneratedProtocolMessageType('ExecuteFetchAsDbaResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEFETCHASDBARESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteFetchAsDbaResponse) - )) -_sym_db.RegisterMessage(ExecuteFetchAsDbaResponse) - -ExecuteFetchAsAllPrivsRequest = _reflection.GeneratedProtocolMessageType('ExecuteFetchAsAllPrivsRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEFETCHASALLPRIVSREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteFetchAsAllPrivsRequest) - )) -_sym_db.RegisterMessage(ExecuteFetchAsAllPrivsRequest) - -ExecuteFetchAsAllPrivsResponse = _reflection.GeneratedProtocolMessageType('ExecuteFetchAsAllPrivsResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEFETCHASALLPRIVSRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteFetchAsAllPrivsResponse) - )) -_sym_db.RegisterMessage(ExecuteFetchAsAllPrivsResponse) - -ExecuteFetchAsAppRequest = _reflection.GeneratedProtocolMessageType('ExecuteFetchAsAppRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEFETCHASAPPREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteFetchAsAppRequest) - )) -_sym_db.RegisterMessage(ExecuteFetchAsAppRequest) - -ExecuteFetchAsAppResponse = _reflection.GeneratedProtocolMessageType('ExecuteFetchAsAppResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEFETCHASAPPRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ExecuteFetchAsAppResponse) - )) -_sym_db.RegisterMessage(ExecuteFetchAsAppResponse) - -SlaveStatusRequest = _reflection.GeneratedProtocolMessageType('SlaveStatusRequest', (_message.Message,), dict( - DESCRIPTOR = _SLAVESTATUSREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SlaveStatusRequest) - )) -_sym_db.RegisterMessage(SlaveStatusRequest) - -SlaveStatusResponse = _reflection.GeneratedProtocolMessageType('SlaveStatusResponse', (_message.Message,), dict( - DESCRIPTOR = _SLAVESTATUSRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SlaveStatusResponse) - )) -_sym_db.RegisterMessage(SlaveStatusResponse) - -MasterPositionRequest = _reflection.GeneratedProtocolMessageType('MasterPositionRequest', (_message.Message,), dict( - DESCRIPTOR = _MASTERPOSITIONREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.MasterPositionRequest) - )) -_sym_db.RegisterMessage(MasterPositionRequest) - -MasterPositionResponse = _reflection.GeneratedProtocolMessageType('MasterPositionResponse', (_message.Message,), dict( - DESCRIPTOR = _MASTERPOSITIONRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.MasterPositionResponse) - )) -_sym_db.RegisterMessage(MasterPositionResponse) - -WaitForPositionRequest = _reflection.GeneratedProtocolMessageType('WaitForPositionRequest', (_message.Message,), dict( - DESCRIPTOR = _WAITFORPOSITIONREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.WaitForPositionRequest) - )) -_sym_db.RegisterMessage(WaitForPositionRequest) - -WaitForPositionResponse = _reflection.GeneratedProtocolMessageType('WaitForPositionResponse', (_message.Message,), dict( - DESCRIPTOR = _WAITFORPOSITIONRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.WaitForPositionResponse) - )) -_sym_db.RegisterMessage(WaitForPositionResponse) - -StopSlaveRequest = _reflection.GeneratedProtocolMessageType('StopSlaveRequest', (_message.Message,), dict( - DESCRIPTOR = _STOPSLAVEREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StopSlaveRequest) - )) -_sym_db.RegisterMessage(StopSlaveRequest) - -StopSlaveResponse = _reflection.GeneratedProtocolMessageType('StopSlaveResponse', (_message.Message,), dict( - DESCRIPTOR = _STOPSLAVERESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StopSlaveResponse) - )) -_sym_db.RegisterMessage(StopSlaveResponse) - -StopSlaveMinimumRequest = _reflection.GeneratedProtocolMessageType('StopSlaveMinimumRequest', (_message.Message,), dict( - DESCRIPTOR = _STOPSLAVEMINIMUMREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StopSlaveMinimumRequest) - )) -_sym_db.RegisterMessage(StopSlaveMinimumRequest) - -StopSlaveMinimumResponse = _reflection.GeneratedProtocolMessageType('StopSlaveMinimumResponse', (_message.Message,), dict( - DESCRIPTOR = _STOPSLAVEMINIMUMRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StopSlaveMinimumResponse) - )) -_sym_db.RegisterMessage(StopSlaveMinimumResponse) - -StartSlaveRequest = _reflection.GeneratedProtocolMessageType('StartSlaveRequest', (_message.Message,), dict( - DESCRIPTOR = _STARTSLAVEREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StartSlaveRequest) - )) -_sym_db.RegisterMessage(StartSlaveRequest) - -StartSlaveResponse = _reflection.GeneratedProtocolMessageType('StartSlaveResponse', (_message.Message,), dict( - DESCRIPTOR = _STARTSLAVERESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StartSlaveResponse) - )) -_sym_db.RegisterMessage(StartSlaveResponse) - -StartSlaveUntilAfterRequest = _reflection.GeneratedProtocolMessageType('StartSlaveUntilAfterRequest', (_message.Message,), dict( - DESCRIPTOR = _STARTSLAVEUNTILAFTERREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StartSlaveUntilAfterRequest) - )) -_sym_db.RegisterMessage(StartSlaveUntilAfterRequest) - -StartSlaveUntilAfterResponse = _reflection.GeneratedProtocolMessageType('StartSlaveUntilAfterResponse', (_message.Message,), dict( - DESCRIPTOR = _STARTSLAVEUNTILAFTERRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StartSlaveUntilAfterResponse) - )) -_sym_db.RegisterMessage(StartSlaveUntilAfterResponse) - -TabletExternallyReparentedRequest = _reflection.GeneratedProtocolMessageType('TabletExternallyReparentedRequest', (_message.Message,), dict( - DESCRIPTOR = _TABLETEXTERNALLYREPARENTEDREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.TabletExternallyReparentedRequest) - )) -_sym_db.RegisterMessage(TabletExternallyReparentedRequest) - -TabletExternallyReparentedResponse = _reflection.GeneratedProtocolMessageType('TabletExternallyReparentedResponse', (_message.Message,), dict( - DESCRIPTOR = _TABLETEXTERNALLYREPARENTEDRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.TabletExternallyReparentedResponse) - )) -_sym_db.RegisterMessage(TabletExternallyReparentedResponse) - -TabletExternallyElectedRequest = _reflection.GeneratedProtocolMessageType('TabletExternallyElectedRequest', (_message.Message,), dict( - DESCRIPTOR = _TABLETEXTERNALLYELECTEDREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.TabletExternallyElectedRequest) - )) -_sym_db.RegisterMessage(TabletExternallyElectedRequest) - -TabletExternallyElectedResponse = _reflection.GeneratedProtocolMessageType('TabletExternallyElectedResponse', (_message.Message,), dict( - DESCRIPTOR = _TABLETEXTERNALLYELECTEDRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.TabletExternallyElectedResponse) - )) -_sym_db.RegisterMessage(TabletExternallyElectedResponse) - -GetSlavesRequest = _reflection.GeneratedProtocolMessageType('GetSlavesRequest', (_message.Message,), dict( - DESCRIPTOR = _GETSLAVESREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.GetSlavesRequest) - )) -_sym_db.RegisterMessage(GetSlavesRequest) - -GetSlavesResponse = _reflection.GeneratedProtocolMessageType('GetSlavesResponse', (_message.Message,), dict( - DESCRIPTOR = _GETSLAVESRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.GetSlavesResponse) - )) -_sym_db.RegisterMessage(GetSlavesResponse) - -ResetReplicationRequest = _reflection.GeneratedProtocolMessageType('ResetReplicationRequest', (_message.Message,), dict( - DESCRIPTOR = _RESETREPLICATIONREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ResetReplicationRequest) - )) -_sym_db.RegisterMessage(ResetReplicationRequest) - -ResetReplicationResponse = _reflection.GeneratedProtocolMessageType('ResetReplicationResponse', (_message.Message,), dict( - DESCRIPTOR = _RESETREPLICATIONRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.ResetReplicationResponse) - )) -_sym_db.RegisterMessage(ResetReplicationResponse) - -VReplicationExecRequest = _reflection.GeneratedProtocolMessageType('VReplicationExecRequest', (_message.Message,), dict( - DESCRIPTOR = _VREPLICATIONEXECREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.VReplicationExecRequest) - )) -_sym_db.RegisterMessage(VReplicationExecRequest) - -VReplicationExecResponse = _reflection.GeneratedProtocolMessageType('VReplicationExecResponse', (_message.Message,), dict( - DESCRIPTOR = _VREPLICATIONEXECRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.VReplicationExecResponse) - )) -_sym_db.RegisterMessage(VReplicationExecResponse) - -VReplicationWaitForPosRequest = _reflection.GeneratedProtocolMessageType('VReplicationWaitForPosRequest', (_message.Message,), dict( - DESCRIPTOR = _VREPLICATIONWAITFORPOSREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.VReplicationWaitForPosRequest) - )) -_sym_db.RegisterMessage(VReplicationWaitForPosRequest) - -VReplicationWaitForPosResponse = _reflection.GeneratedProtocolMessageType('VReplicationWaitForPosResponse', (_message.Message,), dict( - DESCRIPTOR = _VREPLICATIONWAITFORPOSRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.VReplicationWaitForPosResponse) - )) -_sym_db.RegisterMessage(VReplicationWaitForPosResponse) - -InitMasterRequest = _reflection.GeneratedProtocolMessageType('InitMasterRequest', (_message.Message,), dict( - DESCRIPTOR = _INITMASTERREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.InitMasterRequest) - )) -_sym_db.RegisterMessage(InitMasterRequest) - -InitMasterResponse = _reflection.GeneratedProtocolMessageType('InitMasterResponse', (_message.Message,), dict( - DESCRIPTOR = _INITMASTERRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.InitMasterResponse) - )) -_sym_db.RegisterMessage(InitMasterResponse) - -PopulateReparentJournalRequest = _reflection.GeneratedProtocolMessageType('PopulateReparentJournalRequest', (_message.Message,), dict( - DESCRIPTOR = _POPULATEREPARENTJOURNALREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PopulateReparentJournalRequest) - )) -_sym_db.RegisterMessage(PopulateReparentJournalRequest) - -PopulateReparentJournalResponse = _reflection.GeneratedProtocolMessageType('PopulateReparentJournalResponse', (_message.Message,), dict( - DESCRIPTOR = _POPULATEREPARENTJOURNALRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PopulateReparentJournalResponse) - )) -_sym_db.RegisterMessage(PopulateReparentJournalResponse) - -InitSlaveRequest = _reflection.GeneratedProtocolMessageType('InitSlaveRequest', (_message.Message,), dict( - DESCRIPTOR = _INITSLAVEREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.InitSlaveRequest) - )) -_sym_db.RegisterMessage(InitSlaveRequest) - -InitSlaveResponse = _reflection.GeneratedProtocolMessageType('InitSlaveResponse', (_message.Message,), dict( - DESCRIPTOR = _INITSLAVERESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.InitSlaveResponse) - )) -_sym_db.RegisterMessage(InitSlaveResponse) - -DemoteMasterRequest = _reflection.GeneratedProtocolMessageType('DemoteMasterRequest', (_message.Message,), dict( - DESCRIPTOR = _DEMOTEMASTERREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.DemoteMasterRequest) - )) -_sym_db.RegisterMessage(DemoteMasterRequest) - -DemoteMasterResponse = _reflection.GeneratedProtocolMessageType('DemoteMasterResponse', (_message.Message,), dict( - DESCRIPTOR = _DEMOTEMASTERRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.DemoteMasterResponse) - )) -_sym_db.RegisterMessage(DemoteMasterResponse) - -UndoDemoteMasterRequest = _reflection.GeneratedProtocolMessageType('UndoDemoteMasterRequest', (_message.Message,), dict( - DESCRIPTOR = _UNDODEMOTEMASTERREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.UndoDemoteMasterRequest) - )) -_sym_db.RegisterMessage(UndoDemoteMasterRequest) - -UndoDemoteMasterResponse = _reflection.GeneratedProtocolMessageType('UndoDemoteMasterResponse', (_message.Message,), dict( - DESCRIPTOR = _UNDODEMOTEMASTERRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.UndoDemoteMasterResponse) - )) -_sym_db.RegisterMessage(UndoDemoteMasterResponse) - -PromoteSlaveWhenCaughtUpRequest = _reflection.GeneratedProtocolMessageType('PromoteSlaveWhenCaughtUpRequest', (_message.Message,), dict( - DESCRIPTOR = _PROMOTESLAVEWHENCAUGHTUPREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PromoteSlaveWhenCaughtUpRequest) - )) -_sym_db.RegisterMessage(PromoteSlaveWhenCaughtUpRequest) - -PromoteSlaveWhenCaughtUpResponse = _reflection.GeneratedProtocolMessageType('PromoteSlaveWhenCaughtUpResponse', (_message.Message,), dict( - DESCRIPTOR = _PROMOTESLAVEWHENCAUGHTUPRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PromoteSlaveWhenCaughtUpResponse) - )) -_sym_db.RegisterMessage(PromoteSlaveWhenCaughtUpResponse) - -SlaveWasPromotedRequest = _reflection.GeneratedProtocolMessageType('SlaveWasPromotedRequest', (_message.Message,), dict( - DESCRIPTOR = _SLAVEWASPROMOTEDREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SlaveWasPromotedRequest) - )) -_sym_db.RegisterMessage(SlaveWasPromotedRequest) - -SlaveWasPromotedResponse = _reflection.GeneratedProtocolMessageType('SlaveWasPromotedResponse', (_message.Message,), dict( - DESCRIPTOR = _SLAVEWASPROMOTEDRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SlaveWasPromotedResponse) - )) -_sym_db.RegisterMessage(SlaveWasPromotedResponse) - -SetMasterRequest = _reflection.GeneratedProtocolMessageType('SetMasterRequest', (_message.Message,), dict( - DESCRIPTOR = _SETMASTERREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SetMasterRequest) - )) -_sym_db.RegisterMessage(SetMasterRequest) - -SetMasterResponse = _reflection.GeneratedProtocolMessageType('SetMasterResponse', (_message.Message,), dict( - DESCRIPTOR = _SETMASTERRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SetMasterResponse) - )) -_sym_db.RegisterMessage(SetMasterResponse) - -SlaveWasRestartedRequest = _reflection.GeneratedProtocolMessageType('SlaveWasRestartedRequest', (_message.Message,), dict( - DESCRIPTOR = _SLAVEWASRESTARTEDREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SlaveWasRestartedRequest) - )) -_sym_db.RegisterMessage(SlaveWasRestartedRequest) - -SlaveWasRestartedResponse = _reflection.GeneratedProtocolMessageType('SlaveWasRestartedResponse', (_message.Message,), dict( - DESCRIPTOR = _SLAVEWASRESTARTEDRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.SlaveWasRestartedResponse) - )) -_sym_db.RegisterMessage(SlaveWasRestartedResponse) - -StopReplicationAndGetStatusRequest = _reflection.GeneratedProtocolMessageType('StopReplicationAndGetStatusRequest', (_message.Message,), dict( - DESCRIPTOR = _STOPREPLICATIONANDGETSTATUSREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StopReplicationAndGetStatusRequest) - )) -_sym_db.RegisterMessage(StopReplicationAndGetStatusRequest) - -StopReplicationAndGetStatusResponse = _reflection.GeneratedProtocolMessageType('StopReplicationAndGetStatusResponse', (_message.Message,), dict( - DESCRIPTOR = _STOPREPLICATIONANDGETSTATUSRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.StopReplicationAndGetStatusResponse) - )) -_sym_db.RegisterMessage(StopReplicationAndGetStatusResponse) - -PromoteSlaveRequest = _reflection.GeneratedProtocolMessageType('PromoteSlaveRequest', (_message.Message,), dict( - DESCRIPTOR = _PROMOTESLAVEREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PromoteSlaveRequest) - )) -_sym_db.RegisterMessage(PromoteSlaveRequest) - -PromoteSlaveResponse = _reflection.GeneratedProtocolMessageType('PromoteSlaveResponse', (_message.Message,), dict( - DESCRIPTOR = _PROMOTESLAVERESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.PromoteSlaveResponse) - )) -_sym_db.RegisterMessage(PromoteSlaveResponse) - -BackupRequest = _reflection.GeneratedProtocolMessageType('BackupRequest', (_message.Message,), dict( - DESCRIPTOR = _BACKUPREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.BackupRequest) - )) -_sym_db.RegisterMessage(BackupRequest) - -BackupResponse = _reflection.GeneratedProtocolMessageType('BackupResponse', (_message.Message,), dict( - DESCRIPTOR = _BACKUPRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.BackupResponse) - )) -_sym_db.RegisterMessage(BackupResponse) - -RestoreFromBackupRequest = _reflection.GeneratedProtocolMessageType('RestoreFromBackupRequest', (_message.Message,), dict( - DESCRIPTOR = _RESTOREFROMBACKUPREQUEST, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.RestoreFromBackupRequest) - )) -_sym_db.RegisterMessage(RestoreFromBackupRequest) - -RestoreFromBackupResponse = _reflection.GeneratedProtocolMessageType('RestoreFromBackupResponse', (_message.Message,), dict( - DESCRIPTOR = _RESTOREFROMBACKUPRESPONSE, - __module__ = 'tabletmanagerdata_pb2' - # @@protoc_insertion_point(class_scope:tabletmanagerdata.RestoreFromBackupResponse) - )) -_sym_db.RegisterMessage(RestoreFromBackupResponse) - - -DESCRIPTOR._options = None -_USERPERMISSION_PRIVILEGESENTRY._options = None -_DBPERMISSION_PRIVILEGESENTRY._options = None -_EXECUTEHOOKREQUEST_EXTRAENVENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/tabletmanagerdata_pb2_grpc.py b/py/vtproto/tabletmanagerdata_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/tabletmanagerdata_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/tabletmanagerservice_pb2.py b/py/vtproto/tabletmanagerservice_pb2.py deleted file mode 100644 index 56729dea692..00000000000 --- a/py/vtproto/tabletmanagerservice_pb2.py +++ /dev/null @@ -1,453 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: tabletmanagerservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import tabletmanagerdata_pb2 as tabletmanagerdata__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='tabletmanagerservice.proto', - package='tabletmanagerservice', - syntax='proto3', - serialized_options=_b('Z1vitess.io/vitess/go/vt/proto/tabletmanagerservice'), - serialized_pb=_b('\n\x1atabletmanagerservice.proto\x12\x14tabletmanagerservice\x1a\x17tabletmanagerdata.proto2\xab%\n\rTabletManager\x12I\n\x04Ping\x12\x1e.tabletmanagerdata.PingRequest\x1a\x1f.tabletmanagerdata.PingResponse\"\x00\x12L\n\x05Sleep\x12\x1f.tabletmanagerdata.SleepRequest\x1a .tabletmanagerdata.SleepResponse\"\x00\x12^\n\x0b\x45xecuteHook\x12%.tabletmanagerdata.ExecuteHookRequest\x1a&.tabletmanagerdata.ExecuteHookResponse\"\x00\x12X\n\tGetSchema\x12#.tabletmanagerdata.GetSchemaRequest\x1a$.tabletmanagerdata.GetSchemaResponse\"\x00\x12g\n\x0eGetPermissions\x12(.tabletmanagerdata.GetPermissionsRequest\x1a).tabletmanagerdata.GetPermissionsResponse\"\x00\x12^\n\x0bSetReadOnly\x12%.tabletmanagerdata.SetReadOnlyRequest\x1a&.tabletmanagerdata.SetReadOnlyResponse\"\x00\x12\x61\n\x0cSetReadWrite\x12&.tabletmanagerdata.SetReadWriteRequest\x1a\'.tabletmanagerdata.SetReadWriteResponse\"\x00\x12[\n\nChangeType\x12$.tabletmanagerdata.ChangeTypeRequest\x1a%.tabletmanagerdata.ChangeTypeResponse\"\x00\x12\x61\n\x0cRefreshState\x12&.tabletmanagerdata.RefreshStateRequest\x1a\'.tabletmanagerdata.RefreshStateResponse\"\x00\x12g\n\x0eRunHealthCheck\x12(.tabletmanagerdata.RunHealthCheckRequest\x1a).tabletmanagerdata.RunHealthCheckResponse\"\x00\x12p\n\x11IgnoreHealthError\x12+.tabletmanagerdata.IgnoreHealthErrorRequest\x1a,.tabletmanagerdata.IgnoreHealthErrorResponse\"\x00\x12\x61\n\x0cReloadSchema\x12&.tabletmanagerdata.ReloadSchemaRequest\x1a\'.tabletmanagerdata.ReloadSchemaResponse\"\x00\x12j\n\x0fPreflightSchema\x12).tabletmanagerdata.PreflightSchemaRequest\x1a*.tabletmanagerdata.PreflightSchemaResponse\"\x00\x12^\n\x0b\x41pplySchema\x12%.tabletmanagerdata.ApplySchemaRequest\x1a&.tabletmanagerdata.ApplySchemaResponse\"\x00\x12[\n\nLockTables\x12$.tabletmanagerdata.LockTablesRequest\x1a%.tabletmanagerdata.LockTablesResponse\"\x00\x12\x61\n\x0cUnlockTables\x12&.tabletmanagerdata.UnlockTablesRequest\x1a\'.tabletmanagerdata.UnlockTablesResponse\"\x00\x12p\n\x11\x45xecuteFetchAsDba\x12+.tabletmanagerdata.ExecuteFetchAsDbaRequest\x1a,.tabletmanagerdata.ExecuteFetchAsDbaResponse\"\x00\x12\x7f\n\x16\x45xecuteFetchAsAllPrivs\x12\x30.tabletmanagerdata.ExecuteFetchAsAllPrivsRequest\x1a\x31.tabletmanagerdata.ExecuteFetchAsAllPrivsResponse\"\x00\x12p\n\x11\x45xecuteFetchAsApp\x12+.tabletmanagerdata.ExecuteFetchAsAppRequest\x1a,.tabletmanagerdata.ExecuteFetchAsAppResponse\"\x00\x12^\n\x0bSlaveStatus\x12%.tabletmanagerdata.SlaveStatusRequest\x1a&.tabletmanagerdata.SlaveStatusResponse\"\x00\x12g\n\x0eMasterPosition\x12(.tabletmanagerdata.MasterPositionRequest\x1a).tabletmanagerdata.MasterPositionResponse\"\x00\x12j\n\x0fWaitForPosition\x12).tabletmanagerdata.WaitForPositionRequest\x1a*.tabletmanagerdata.WaitForPositionResponse\"\x00\x12X\n\tStopSlave\x12#.tabletmanagerdata.StopSlaveRequest\x1a$.tabletmanagerdata.StopSlaveResponse\"\x00\x12m\n\x10StopSlaveMinimum\x12*.tabletmanagerdata.StopSlaveMinimumRequest\x1a+.tabletmanagerdata.StopSlaveMinimumResponse\"\x00\x12[\n\nStartSlave\x12$.tabletmanagerdata.StartSlaveRequest\x1a%.tabletmanagerdata.StartSlaveResponse\"\x00\x12y\n\x14StartSlaveUntilAfter\x12..tabletmanagerdata.StartSlaveUntilAfterRequest\x1a/.tabletmanagerdata.StartSlaveUntilAfterResponse\"\x00\x12\x8b\x01\n\x1aTabletExternallyReparented\x12\x34.tabletmanagerdata.TabletExternallyReparentedRequest\x1a\x35.tabletmanagerdata.TabletExternallyReparentedResponse\"\x00\x12\x82\x01\n\x17TabletExternallyElected\x12\x31.tabletmanagerdata.TabletExternallyElectedRequest\x1a\x32.tabletmanagerdata.TabletExternallyElectedResponse\"\x00\x12X\n\tGetSlaves\x12#.tabletmanagerdata.GetSlavesRequest\x1a$.tabletmanagerdata.GetSlavesResponse\"\x00\x12m\n\x10VReplicationExec\x12*.tabletmanagerdata.VReplicationExecRequest\x1a+.tabletmanagerdata.VReplicationExecResponse\"\x00\x12\x7f\n\x16VReplicationWaitForPos\x12\x30.tabletmanagerdata.VReplicationWaitForPosRequest\x1a\x31.tabletmanagerdata.VReplicationWaitForPosResponse\"\x00\x12m\n\x10ResetReplication\x12*.tabletmanagerdata.ResetReplicationRequest\x1a+.tabletmanagerdata.ResetReplicationResponse\"\x00\x12[\n\nInitMaster\x12$.tabletmanagerdata.InitMasterRequest\x1a%.tabletmanagerdata.InitMasterResponse\"\x00\x12\x82\x01\n\x17PopulateReparentJournal\x12\x31.tabletmanagerdata.PopulateReparentJournalRequest\x1a\x32.tabletmanagerdata.PopulateReparentJournalResponse\"\x00\x12X\n\tInitSlave\x12#.tabletmanagerdata.InitSlaveRequest\x1a$.tabletmanagerdata.InitSlaveResponse\"\x00\x12\x61\n\x0c\x44\x65moteMaster\x12&.tabletmanagerdata.DemoteMasterRequest\x1a\'.tabletmanagerdata.DemoteMasterResponse\"\x00\x12m\n\x10UndoDemoteMaster\x12*.tabletmanagerdata.UndoDemoteMasterRequest\x1a+.tabletmanagerdata.UndoDemoteMasterResponse\"\x00\x12\x85\x01\n\x18PromoteSlaveWhenCaughtUp\x12\x32.tabletmanagerdata.PromoteSlaveWhenCaughtUpRequest\x1a\x33.tabletmanagerdata.PromoteSlaveWhenCaughtUpResponse\"\x00\x12m\n\x10SlaveWasPromoted\x12*.tabletmanagerdata.SlaveWasPromotedRequest\x1a+.tabletmanagerdata.SlaveWasPromotedResponse\"\x00\x12X\n\tSetMaster\x12#.tabletmanagerdata.SetMasterRequest\x1a$.tabletmanagerdata.SetMasterResponse\"\x00\x12p\n\x11SlaveWasRestarted\x12+.tabletmanagerdata.SlaveWasRestartedRequest\x1a,.tabletmanagerdata.SlaveWasRestartedResponse\"\x00\x12\x8e\x01\n\x1bStopReplicationAndGetStatus\x12\x35.tabletmanagerdata.StopReplicationAndGetStatusRequest\x1a\x36.tabletmanagerdata.StopReplicationAndGetStatusResponse\"\x00\x12\x61\n\x0cPromoteSlave\x12&.tabletmanagerdata.PromoteSlaveRequest\x1a\'.tabletmanagerdata.PromoteSlaveResponse\"\x00\x12Q\n\x06\x42\x61\x63kup\x12 .tabletmanagerdata.BackupRequest\x1a!.tabletmanagerdata.BackupResponse\"\x00\x30\x01\x12r\n\x11RestoreFromBackup\x12+.tabletmanagerdata.RestoreFromBackupRequest\x1a,.tabletmanagerdata.RestoreFromBackupResponse\"\x00\x30\x01\x42\x33Z1vitess.io/vitess/go/vt/proto/tabletmanagerserviceb\x06proto3') - , - dependencies=[tabletmanagerdata__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_TABLETMANAGER = _descriptor.ServiceDescriptor( - name='TabletManager', - full_name='tabletmanagerservice.TabletManager', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=78, - serialized_end=4857, - methods=[ - _descriptor.MethodDescriptor( - name='Ping', - full_name='tabletmanagerservice.TabletManager.Ping', - index=0, - containing_service=None, - input_type=tabletmanagerdata__pb2._PINGREQUEST, - output_type=tabletmanagerdata__pb2._PINGRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Sleep', - full_name='tabletmanagerservice.TabletManager.Sleep', - index=1, - containing_service=None, - input_type=tabletmanagerdata__pb2._SLEEPREQUEST, - output_type=tabletmanagerdata__pb2._SLEEPRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteHook', - full_name='tabletmanagerservice.TabletManager.ExecuteHook', - index=2, - containing_service=None, - input_type=tabletmanagerdata__pb2._EXECUTEHOOKREQUEST, - output_type=tabletmanagerdata__pb2._EXECUTEHOOKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='GetSchema', - full_name='tabletmanagerservice.TabletManager.GetSchema', - index=3, - containing_service=None, - input_type=tabletmanagerdata__pb2._GETSCHEMAREQUEST, - output_type=tabletmanagerdata__pb2._GETSCHEMARESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='GetPermissions', - full_name='tabletmanagerservice.TabletManager.GetPermissions', - index=4, - containing_service=None, - input_type=tabletmanagerdata__pb2._GETPERMISSIONSREQUEST, - output_type=tabletmanagerdata__pb2._GETPERMISSIONSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SetReadOnly', - full_name='tabletmanagerservice.TabletManager.SetReadOnly', - index=5, - containing_service=None, - input_type=tabletmanagerdata__pb2._SETREADONLYREQUEST, - output_type=tabletmanagerdata__pb2._SETREADONLYRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SetReadWrite', - full_name='tabletmanagerservice.TabletManager.SetReadWrite', - index=6, - containing_service=None, - input_type=tabletmanagerdata__pb2._SETREADWRITEREQUEST, - output_type=tabletmanagerdata__pb2._SETREADWRITERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ChangeType', - full_name='tabletmanagerservice.TabletManager.ChangeType', - index=7, - containing_service=None, - input_type=tabletmanagerdata__pb2._CHANGETYPEREQUEST, - output_type=tabletmanagerdata__pb2._CHANGETYPERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='RefreshState', - full_name='tabletmanagerservice.TabletManager.RefreshState', - index=8, - containing_service=None, - input_type=tabletmanagerdata__pb2._REFRESHSTATEREQUEST, - output_type=tabletmanagerdata__pb2._REFRESHSTATERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='RunHealthCheck', - full_name='tabletmanagerservice.TabletManager.RunHealthCheck', - index=9, - containing_service=None, - input_type=tabletmanagerdata__pb2._RUNHEALTHCHECKREQUEST, - output_type=tabletmanagerdata__pb2._RUNHEALTHCHECKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='IgnoreHealthError', - full_name='tabletmanagerservice.TabletManager.IgnoreHealthError', - index=10, - containing_service=None, - input_type=tabletmanagerdata__pb2._IGNOREHEALTHERRORREQUEST, - output_type=tabletmanagerdata__pb2._IGNOREHEALTHERRORRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ReloadSchema', - full_name='tabletmanagerservice.TabletManager.ReloadSchema', - index=11, - containing_service=None, - input_type=tabletmanagerdata__pb2._RELOADSCHEMAREQUEST, - output_type=tabletmanagerdata__pb2._RELOADSCHEMARESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='PreflightSchema', - full_name='tabletmanagerservice.TabletManager.PreflightSchema', - index=12, - containing_service=None, - input_type=tabletmanagerdata__pb2._PREFLIGHTSCHEMAREQUEST, - output_type=tabletmanagerdata__pb2._PREFLIGHTSCHEMARESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ApplySchema', - full_name='tabletmanagerservice.TabletManager.ApplySchema', - index=13, - containing_service=None, - input_type=tabletmanagerdata__pb2._APPLYSCHEMAREQUEST, - output_type=tabletmanagerdata__pb2._APPLYSCHEMARESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='LockTables', - full_name='tabletmanagerservice.TabletManager.LockTables', - index=14, - containing_service=None, - input_type=tabletmanagerdata__pb2._LOCKTABLESREQUEST, - output_type=tabletmanagerdata__pb2._LOCKTABLESRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='UnlockTables', - full_name='tabletmanagerservice.TabletManager.UnlockTables', - index=15, - containing_service=None, - input_type=tabletmanagerdata__pb2._UNLOCKTABLESREQUEST, - output_type=tabletmanagerdata__pb2._UNLOCKTABLESRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteFetchAsDba', - full_name='tabletmanagerservice.TabletManager.ExecuteFetchAsDba', - index=16, - containing_service=None, - input_type=tabletmanagerdata__pb2._EXECUTEFETCHASDBAREQUEST, - output_type=tabletmanagerdata__pb2._EXECUTEFETCHASDBARESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteFetchAsAllPrivs', - full_name='tabletmanagerservice.TabletManager.ExecuteFetchAsAllPrivs', - index=17, - containing_service=None, - input_type=tabletmanagerdata__pb2._EXECUTEFETCHASALLPRIVSREQUEST, - output_type=tabletmanagerdata__pb2._EXECUTEFETCHASALLPRIVSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteFetchAsApp', - full_name='tabletmanagerservice.TabletManager.ExecuteFetchAsApp', - index=18, - containing_service=None, - input_type=tabletmanagerdata__pb2._EXECUTEFETCHASAPPREQUEST, - output_type=tabletmanagerdata__pb2._EXECUTEFETCHASAPPRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SlaveStatus', - full_name='tabletmanagerservice.TabletManager.SlaveStatus', - index=19, - containing_service=None, - input_type=tabletmanagerdata__pb2._SLAVESTATUSREQUEST, - output_type=tabletmanagerdata__pb2._SLAVESTATUSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='MasterPosition', - full_name='tabletmanagerservice.TabletManager.MasterPosition', - index=20, - containing_service=None, - input_type=tabletmanagerdata__pb2._MASTERPOSITIONREQUEST, - output_type=tabletmanagerdata__pb2._MASTERPOSITIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='WaitForPosition', - full_name='tabletmanagerservice.TabletManager.WaitForPosition', - index=21, - containing_service=None, - input_type=tabletmanagerdata__pb2._WAITFORPOSITIONREQUEST, - output_type=tabletmanagerdata__pb2._WAITFORPOSITIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StopSlave', - full_name='tabletmanagerservice.TabletManager.StopSlave', - index=22, - containing_service=None, - input_type=tabletmanagerdata__pb2._STOPSLAVEREQUEST, - output_type=tabletmanagerdata__pb2._STOPSLAVERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StopSlaveMinimum', - full_name='tabletmanagerservice.TabletManager.StopSlaveMinimum', - index=23, - containing_service=None, - input_type=tabletmanagerdata__pb2._STOPSLAVEMINIMUMREQUEST, - output_type=tabletmanagerdata__pb2._STOPSLAVEMINIMUMRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StartSlave', - full_name='tabletmanagerservice.TabletManager.StartSlave', - index=24, - containing_service=None, - input_type=tabletmanagerdata__pb2._STARTSLAVEREQUEST, - output_type=tabletmanagerdata__pb2._STARTSLAVERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StartSlaveUntilAfter', - full_name='tabletmanagerservice.TabletManager.StartSlaveUntilAfter', - index=25, - containing_service=None, - input_type=tabletmanagerdata__pb2._STARTSLAVEUNTILAFTERREQUEST, - output_type=tabletmanagerdata__pb2._STARTSLAVEUNTILAFTERRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='TabletExternallyReparented', - full_name='tabletmanagerservice.TabletManager.TabletExternallyReparented', - index=26, - containing_service=None, - input_type=tabletmanagerdata__pb2._TABLETEXTERNALLYREPARENTEDREQUEST, - output_type=tabletmanagerdata__pb2._TABLETEXTERNALLYREPARENTEDRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='TabletExternallyElected', - full_name='tabletmanagerservice.TabletManager.TabletExternallyElected', - index=27, - containing_service=None, - input_type=tabletmanagerdata__pb2._TABLETEXTERNALLYELECTEDREQUEST, - output_type=tabletmanagerdata__pb2._TABLETEXTERNALLYELECTEDRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='GetSlaves', - full_name='tabletmanagerservice.TabletManager.GetSlaves', - index=28, - containing_service=None, - input_type=tabletmanagerdata__pb2._GETSLAVESREQUEST, - output_type=tabletmanagerdata__pb2._GETSLAVESRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='VReplicationExec', - full_name='tabletmanagerservice.TabletManager.VReplicationExec', - index=29, - containing_service=None, - input_type=tabletmanagerdata__pb2._VREPLICATIONEXECREQUEST, - output_type=tabletmanagerdata__pb2._VREPLICATIONEXECRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='VReplicationWaitForPos', - full_name='tabletmanagerservice.TabletManager.VReplicationWaitForPos', - index=30, - containing_service=None, - input_type=tabletmanagerdata__pb2._VREPLICATIONWAITFORPOSREQUEST, - output_type=tabletmanagerdata__pb2._VREPLICATIONWAITFORPOSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ResetReplication', - full_name='tabletmanagerservice.TabletManager.ResetReplication', - index=31, - containing_service=None, - input_type=tabletmanagerdata__pb2._RESETREPLICATIONREQUEST, - output_type=tabletmanagerdata__pb2._RESETREPLICATIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='InitMaster', - full_name='tabletmanagerservice.TabletManager.InitMaster', - index=32, - containing_service=None, - input_type=tabletmanagerdata__pb2._INITMASTERREQUEST, - output_type=tabletmanagerdata__pb2._INITMASTERRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='PopulateReparentJournal', - full_name='tabletmanagerservice.TabletManager.PopulateReparentJournal', - index=33, - containing_service=None, - input_type=tabletmanagerdata__pb2._POPULATEREPARENTJOURNALREQUEST, - output_type=tabletmanagerdata__pb2._POPULATEREPARENTJOURNALRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='InitSlave', - full_name='tabletmanagerservice.TabletManager.InitSlave', - index=34, - containing_service=None, - input_type=tabletmanagerdata__pb2._INITSLAVEREQUEST, - output_type=tabletmanagerdata__pb2._INITSLAVERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='DemoteMaster', - full_name='tabletmanagerservice.TabletManager.DemoteMaster', - index=35, - containing_service=None, - input_type=tabletmanagerdata__pb2._DEMOTEMASTERREQUEST, - output_type=tabletmanagerdata__pb2._DEMOTEMASTERRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='UndoDemoteMaster', - full_name='tabletmanagerservice.TabletManager.UndoDemoteMaster', - index=36, - containing_service=None, - input_type=tabletmanagerdata__pb2._UNDODEMOTEMASTERREQUEST, - output_type=tabletmanagerdata__pb2._UNDODEMOTEMASTERRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='PromoteSlaveWhenCaughtUp', - full_name='tabletmanagerservice.TabletManager.PromoteSlaveWhenCaughtUp', - index=37, - containing_service=None, - input_type=tabletmanagerdata__pb2._PROMOTESLAVEWHENCAUGHTUPREQUEST, - output_type=tabletmanagerdata__pb2._PROMOTESLAVEWHENCAUGHTUPRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SlaveWasPromoted', - full_name='tabletmanagerservice.TabletManager.SlaveWasPromoted', - index=38, - containing_service=None, - input_type=tabletmanagerdata__pb2._SLAVEWASPROMOTEDREQUEST, - output_type=tabletmanagerdata__pb2._SLAVEWASPROMOTEDRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SetMaster', - full_name='tabletmanagerservice.TabletManager.SetMaster', - index=39, - containing_service=None, - input_type=tabletmanagerdata__pb2._SETMASTERREQUEST, - output_type=tabletmanagerdata__pb2._SETMASTERRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SlaveWasRestarted', - full_name='tabletmanagerservice.TabletManager.SlaveWasRestarted', - index=40, - containing_service=None, - input_type=tabletmanagerdata__pb2._SLAVEWASRESTARTEDREQUEST, - output_type=tabletmanagerdata__pb2._SLAVEWASRESTARTEDRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StopReplicationAndGetStatus', - full_name='tabletmanagerservice.TabletManager.StopReplicationAndGetStatus', - index=41, - containing_service=None, - input_type=tabletmanagerdata__pb2._STOPREPLICATIONANDGETSTATUSREQUEST, - output_type=tabletmanagerdata__pb2._STOPREPLICATIONANDGETSTATUSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='PromoteSlave', - full_name='tabletmanagerservice.TabletManager.PromoteSlave', - index=42, - containing_service=None, - input_type=tabletmanagerdata__pb2._PROMOTESLAVEREQUEST, - output_type=tabletmanagerdata__pb2._PROMOTESLAVERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Backup', - full_name='tabletmanagerservice.TabletManager.Backup', - index=43, - containing_service=None, - input_type=tabletmanagerdata__pb2._BACKUPREQUEST, - output_type=tabletmanagerdata__pb2._BACKUPRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='RestoreFromBackup', - full_name='tabletmanagerservice.TabletManager.RestoreFromBackup', - index=44, - containing_service=None, - input_type=tabletmanagerdata__pb2._RESTOREFROMBACKUPREQUEST, - output_type=tabletmanagerdata__pb2._RESTOREFROMBACKUPRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_TABLETMANAGER) - -DESCRIPTOR.services_by_name['TabletManager'] = _TABLETMANAGER - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/tabletmanagerservice_pb2_grpc.py b/py/vtproto/tabletmanagerservice_pb2_grpc.py deleted file mode 100644 index 95019dd89ed..00000000000 --- a/py/vtproto/tabletmanagerservice_pb2_grpc.py +++ /dev/null @@ -1,841 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import tabletmanagerdata_pb2 as tabletmanagerdata__pb2 - - -class TabletManagerStub(object): - """TabletManager is a service definition for tabletmanagerdata.TabletManager. - - Various read-only methods - - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.Ping = channel.unary_unary( - '/tabletmanagerservice.TabletManager/Ping', - request_serializer=tabletmanagerdata__pb2.PingRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.PingResponse.FromString, - ) - self.Sleep = channel.unary_unary( - '/tabletmanagerservice.TabletManager/Sleep', - request_serializer=tabletmanagerdata__pb2.SleepRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.SleepResponse.FromString, - ) - self.ExecuteHook = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ExecuteHook', - request_serializer=tabletmanagerdata__pb2.ExecuteHookRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ExecuteHookResponse.FromString, - ) - self.GetSchema = channel.unary_unary( - '/tabletmanagerservice.TabletManager/GetSchema', - request_serializer=tabletmanagerdata__pb2.GetSchemaRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.GetSchemaResponse.FromString, - ) - self.GetPermissions = channel.unary_unary( - '/tabletmanagerservice.TabletManager/GetPermissions', - request_serializer=tabletmanagerdata__pb2.GetPermissionsRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.GetPermissionsResponse.FromString, - ) - self.SetReadOnly = channel.unary_unary( - '/tabletmanagerservice.TabletManager/SetReadOnly', - request_serializer=tabletmanagerdata__pb2.SetReadOnlyRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.SetReadOnlyResponse.FromString, - ) - self.SetReadWrite = channel.unary_unary( - '/tabletmanagerservice.TabletManager/SetReadWrite', - request_serializer=tabletmanagerdata__pb2.SetReadWriteRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.SetReadWriteResponse.FromString, - ) - self.ChangeType = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ChangeType', - request_serializer=tabletmanagerdata__pb2.ChangeTypeRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ChangeTypeResponse.FromString, - ) - self.RefreshState = channel.unary_unary( - '/tabletmanagerservice.TabletManager/RefreshState', - request_serializer=tabletmanagerdata__pb2.RefreshStateRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.RefreshStateResponse.FromString, - ) - self.RunHealthCheck = channel.unary_unary( - '/tabletmanagerservice.TabletManager/RunHealthCheck', - request_serializer=tabletmanagerdata__pb2.RunHealthCheckRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.RunHealthCheckResponse.FromString, - ) - self.IgnoreHealthError = channel.unary_unary( - '/tabletmanagerservice.TabletManager/IgnoreHealthError', - request_serializer=tabletmanagerdata__pb2.IgnoreHealthErrorRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.IgnoreHealthErrorResponse.FromString, - ) - self.ReloadSchema = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ReloadSchema', - request_serializer=tabletmanagerdata__pb2.ReloadSchemaRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ReloadSchemaResponse.FromString, - ) - self.PreflightSchema = channel.unary_unary( - '/tabletmanagerservice.TabletManager/PreflightSchema', - request_serializer=tabletmanagerdata__pb2.PreflightSchemaRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.PreflightSchemaResponse.FromString, - ) - self.ApplySchema = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ApplySchema', - request_serializer=tabletmanagerdata__pb2.ApplySchemaRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ApplySchemaResponse.FromString, - ) - self.LockTables = channel.unary_unary( - '/tabletmanagerservice.TabletManager/LockTables', - request_serializer=tabletmanagerdata__pb2.LockTablesRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.LockTablesResponse.FromString, - ) - self.UnlockTables = channel.unary_unary( - '/tabletmanagerservice.TabletManager/UnlockTables', - request_serializer=tabletmanagerdata__pb2.UnlockTablesRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.UnlockTablesResponse.FromString, - ) - self.ExecuteFetchAsDba = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ExecuteFetchAsDba', - request_serializer=tabletmanagerdata__pb2.ExecuteFetchAsDbaRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ExecuteFetchAsDbaResponse.FromString, - ) - self.ExecuteFetchAsAllPrivs = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ExecuteFetchAsAllPrivs', - request_serializer=tabletmanagerdata__pb2.ExecuteFetchAsAllPrivsRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ExecuteFetchAsAllPrivsResponse.FromString, - ) - self.ExecuteFetchAsApp = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ExecuteFetchAsApp', - request_serializer=tabletmanagerdata__pb2.ExecuteFetchAsAppRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ExecuteFetchAsAppResponse.FromString, - ) - self.SlaveStatus = channel.unary_unary( - '/tabletmanagerservice.TabletManager/SlaveStatus', - request_serializer=tabletmanagerdata__pb2.SlaveStatusRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.SlaveStatusResponse.FromString, - ) - self.MasterPosition = channel.unary_unary( - '/tabletmanagerservice.TabletManager/MasterPosition', - request_serializer=tabletmanagerdata__pb2.MasterPositionRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.MasterPositionResponse.FromString, - ) - self.WaitForPosition = channel.unary_unary( - '/tabletmanagerservice.TabletManager/WaitForPosition', - request_serializer=tabletmanagerdata__pb2.WaitForPositionRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.WaitForPositionResponse.FromString, - ) - self.StopSlave = channel.unary_unary( - '/tabletmanagerservice.TabletManager/StopSlave', - request_serializer=tabletmanagerdata__pb2.StopSlaveRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.StopSlaveResponse.FromString, - ) - self.StopSlaveMinimum = channel.unary_unary( - '/tabletmanagerservice.TabletManager/StopSlaveMinimum', - request_serializer=tabletmanagerdata__pb2.StopSlaveMinimumRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.StopSlaveMinimumResponse.FromString, - ) - self.StartSlave = channel.unary_unary( - '/tabletmanagerservice.TabletManager/StartSlave', - request_serializer=tabletmanagerdata__pb2.StartSlaveRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.StartSlaveResponse.FromString, - ) - self.StartSlaveUntilAfter = channel.unary_unary( - '/tabletmanagerservice.TabletManager/StartSlaveUntilAfter', - request_serializer=tabletmanagerdata__pb2.StartSlaveUntilAfterRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.StartSlaveUntilAfterResponse.FromString, - ) - self.TabletExternallyReparented = channel.unary_unary( - '/tabletmanagerservice.TabletManager/TabletExternallyReparented', - request_serializer=tabletmanagerdata__pb2.TabletExternallyReparentedRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.TabletExternallyReparentedResponse.FromString, - ) - self.TabletExternallyElected = channel.unary_unary( - '/tabletmanagerservice.TabletManager/TabletExternallyElected', - request_serializer=tabletmanagerdata__pb2.TabletExternallyElectedRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.TabletExternallyElectedResponse.FromString, - ) - self.GetSlaves = channel.unary_unary( - '/tabletmanagerservice.TabletManager/GetSlaves', - request_serializer=tabletmanagerdata__pb2.GetSlavesRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.GetSlavesResponse.FromString, - ) - self.VReplicationExec = channel.unary_unary( - '/tabletmanagerservice.TabletManager/VReplicationExec', - request_serializer=tabletmanagerdata__pb2.VReplicationExecRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.VReplicationExecResponse.FromString, - ) - self.VReplicationWaitForPos = channel.unary_unary( - '/tabletmanagerservice.TabletManager/VReplicationWaitForPos', - request_serializer=tabletmanagerdata__pb2.VReplicationWaitForPosRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.VReplicationWaitForPosResponse.FromString, - ) - self.ResetReplication = channel.unary_unary( - '/tabletmanagerservice.TabletManager/ResetReplication', - request_serializer=tabletmanagerdata__pb2.ResetReplicationRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.ResetReplicationResponse.FromString, - ) - self.InitMaster = channel.unary_unary( - '/tabletmanagerservice.TabletManager/InitMaster', - request_serializer=tabletmanagerdata__pb2.InitMasterRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.InitMasterResponse.FromString, - ) - self.PopulateReparentJournal = channel.unary_unary( - '/tabletmanagerservice.TabletManager/PopulateReparentJournal', - request_serializer=tabletmanagerdata__pb2.PopulateReparentJournalRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.PopulateReparentJournalResponse.FromString, - ) - self.InitSlave = channel.unary_unary( - '/tabletmanagerservice.TabletManager/InitSlave', - request_serializer=tabletmanagerdata__pb2.InitSlaveRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.InitSlaveResponse.FromString, - ) - self.DemoteMaster = channel.unary_unary( - '/tabletmanagerservice.TabletManager/DemoteMaster', - request_serializer=tabletmanagerdata__pb2.DemoteMasterRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.DemoteMasterResponse.FromString, - ) - self.UndoDemoteMaster = channel.unary_unary( - '/tabletmanagerservice.TabletManager/UndoDemoteMaster', - request_serializer=tabletmanagerdata__pb2.UndoDemoteMasterRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.UndoDemoteMasterResponse.FromString, - ) - self.PromoteSlaveWhenCaughtUp = channel.unary_unary( - '/tabletmanagerservice.TabletManager/PromoteSlaveWhenCaughtUp', - request_serializer=tabletmanagerdata__pb2.PromoteSlaveWhenCaughtUpRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.PromoteSlaveWhenCaughtUpResponse.FromString, - ) - self.SlaveWasPromoted = channel.unary_unary( - '/tabletmanagerservice.TabletManager/SlaveWasPromoted', - request_serializer=tabletmanagerdata__pb2.SlaveWasPromotedRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.SlaveWasPromotedResponse.FromString, - ) - self.SetMaster = channel.unary_unary( - '/tabletmanagerservice.TabletManager/SetMaster', - request_serializer=tabletmanagerdata__pb2.SetMasterRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.SetMasterResponse.FromString, - ) - self.SlaveWasRestarted = channel.unary_unary( - '/tabletmanagerservice.TabletManager/SlaveWasRestarted', - request_serializer=tabletmanagerdata__pb2.SlaveWasRestartedRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.SlaveWasRestartedResponse.FromString, - ) - self.StopReplicationAndGetStatus = channel.unary_unary( - '/tabletmanagerservice.TabletManager/StopReplicationAndGetStatus', - request_serializer=tabletmanagerdata__pb2.StopReplicationAndGetStatusRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.StopReplicationAndGetStatusResponse.FromString, - ) - self.PromoteSlave = channel.unary_unary( - '/tabletmanagerservice.TabletManager/PromoteSlave', - request_serializer=tabletmanagerdata__pb2.PromoteSlaveRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.PromoteSlaveResponse.FromString, - ) - self.Backup = channel.unary_stream( - '/tabletmanagerservice.TabletManager/Backup', - request_serializer=tabletmanagerdata__pb2.BackupRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.BackupResponse.FromString, - ) - self.RestoreFromBackup = channel.unary_stream( - '/tabletmanagerservice.TabletManager/RestoreFromBackup', - request_serializer=tabletmanagerdata__pb2.RestoreFromBackupRequest.SerializeToString, - response_deserializer=tabletmanagerdata__pb2.RestoreFromBackupResponse.FromString, - ) - - -class TabletManagerServicer(object): - """TabletManager is a service definition for tabletmanagerdata.TabletManager. - - Various read-only methods - - """ - - def Ping(self, request, context): - """Ping returns the input payload - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Sleep(self, request, context): - """Sleep sleeps for the provided duration - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteHook(self, request, context): - """ExecuteHook executes the hook remotely - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GetSchema(self, request, context): - """GetSchema asks the tablet for its schema - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GetPermissions(self, request, context): - """GetPermissions asks the tablet for its permissions - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetReadOnly(self, request, context): - """ - Various read-write methods - - - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetReadWrite(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ChangeType(self, request, context): - """ChangeType asks the remote tablet to change its type - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def RefreshState(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def RunHealthCheck(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def IgnoreHealthError(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ReloadSchema(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def PreflightSchema(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ApplySchema(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def LockTables(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def UnlockTables(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteFetchAsDba(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteFetchAsAllPrivs(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteFetchAsApp(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SlaveStatus(self, request, context): - """ - Replication related methods - - - SlaveStatus returns the current slave status. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MasterPosition(self, request, context): - """MasterPosition returns the current master position - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def WaitForPosition(self, request, context): - """WaitForPosition waits for the position to be reached - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StopSlave(self, request, context): - """StopSlave makes mysql stop its replication - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StopSlaveMinimum(self, request, context): - """StopSlaveMinimum stops the mysql replication after it reaches - the provided minimum point - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StartSlave(self, request, context): - """StartSlave starts the mysql replication - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StartSlaveUntilAfter(self, request, context): - """StartSlave starts the mysql replication until and including - the provided position - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def TabletExternallyReparented(self, request, context): - """TabletExternallyReparented tells a tablet that its underlying MySQL is - currently the master. It is only used in environments (tabletmanagerdata.such as Vitess+MoB) - in which MySQL is reparented by some agent external to Vitess, and then - that agent simply notifies Vitess. - - This call is idempotent with respect to a single target tablet. - However, the tablet assumes there is a cooling-off period following the - initial external reparent from A to B, before this call is repeated on any - tablet other than B. This assumption is configurable with the vttablet flag - "finalize_external_reparent_timeout". - - For more information, see the design doc at go/vt-fast-failover. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def TabletExternallyElected(self, request, context): - """TabletExternallyElected is an notification that may be sent in - anticipation of potentially later sending TabletExternallyReparented. - The tablet can use this extra lead time to prepare to react quickly if - TabletExternallyReparented does follow. - - This call is effectively a no-op if it is not followed by a call to - TabletExternallyReparented, so the external agent doing the reparent can - still change its mind. - - The agent does not need to wait for this call or cancel it before calling - TabletExternallyReparented if the external reparent operation finishes - before TabletExternallyElected returns. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GetSlaves(self, request, context): - """GetSlaves asks for the list of mysql slaves - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def VReplicationExec(self, request, context): - """VReplication API - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def VReplicationWaitForPos(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ResetReplication(self, request, context): - """ - Reparenting related functions - - - ResetReplication makes the target not replicating - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def InitMaster(self, request, context): - """InitMaster initializes the tablet as a master - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def PopulateReparentJournal(self, request, context): - """PopulateReparentJournal tells the tablet to add an entry to its - reparent journal - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def InitSlave(self, request, context): - """InitSlave tells the tablet to reparent to the master unconditionally - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def DemoteMaster(self, request, context): - """DemoteMaster tells the soon-to-be-former master it's gonna change - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def UndoDemoteMaster(self, request, context): - """UndoDemoteMaster reverts all changes made by DemoteMaster - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def PromoteSlaveWhenCaughtUp(self, request, context): - """PromoteSlaveWhenCaughtUp tells the remote tablet to catch up, - and then be the master - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SlaveWasPromoted(self, request, context): - """SlaveWasPromoted tells the remote tablet it is now the master - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetMaster(self, request, context): - """SetMaster tells the slave to reparent - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SlaveWasRestarted(self, request, context): - """SlaveWasRestarted tells the remote tablet its master has changed - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StopReplicationAndGetStatus(self, request, context): - """StopReplicationAndGetStatus stops MySQL replication, and returns the - replication status - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def PromoteSlave(self, request, context): - """PromoteSlave makes the slave the new master - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Backup(self, request, context): - """ - Backup related methods - - - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def RestoreFromBackup(self, request, context): - """RestoreFromBackup deletes all local data and restores it from the latest backup. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_TabletManagerServicer_to_server(servicer, server): - rpc_method_handlers = { - 'Ping': grpc.unary_unary_rpc_method_handler( - servicer.Ping, - request_deserializer=tabletmanagerdata__pb2.PingRequest.FromString, - response_serializer=tabletmanagerdata__pb2.PingResponse.SerializeToString, - ), - 'Sleep': grpc.unary_unary_rpc_method_handler( - servicer.Sleep, - request_deserializer=tabletmanagerdata__pb2.SleepRequest.FromString, - response_serializer=tabletmanagerdata__pb2.SleepResponse.SerializeToString, - ), - 'ExecuteHook': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteHook, - request_deserializer=tabletmanagerdata__pb2.ExecuteHookRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ExecuteHookResponse.SerializeToString, - ), - 'GetSchema': grpc.unary_unary_rpc_method_handler( - servicer.GetSchema, - request_deserializer=tabletmanagerdata__pb2.GetSchemaRequest.FromString, - response_serializer=tabletmanagerdata__pb2.GetSchemaResponse.SerializeToString, - ), - 'GetPermissions': grpc.unary_unary_rpc_method_handler( - servicer.GetPermissions, - request_deserializer=tabletmanagerdata__pb2.GetPermissionsRequest.FromString, - response_serializer=tabletmanagerdata__pb2.GetPermissionsResponse.SerializeToString, - ), - 'SetReadOnly': grpc.unary_unary_rpc_method_handler( - servicer.SetReadOnly, - request_deserializer=tabletmanagerdata__pb2.SetReadOnlyRequest.FromString, - response_serializer=tabletmanagerdata__pb2.SetReadOnlyResponse.SerializeToString, - ), - 'SetReadWrite': grpc.unary_unary_rpc_method_handler( - servicer.SetReadWrite, - request_deserializer=tabletmanagerdata__pb2.SetReadWriteRequest.FromString, - response_serializer=tabletmanagerdata__pb2.SetReadWriteResponse.SerializeToString, - ), - 'ChangeType': grpc.unary_unary_rpc_method_handler( - servicer.ChangeType, - request_deserializer=tabletmanagerdata__pb2.ChangeTypeRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ChangeTypeResponse.SerializeToString, - ), - 'RefreshState': grpc.unary_unary_rpc_method_handler( - servicer.RefreshState, - request_deserializer=tabletmanagerdata__pb2.RefreshStateRequest.FromString, - response_serializer=tabletmanagerdata__pb2.RefreshStateResponse.SerializeToString, - ), - 'RunHealthCheck': grpc.unary_unary_rpc_method_handler( - servicer.RunHealthCheck, - request_deserializer=tabletmanagerdata__pb2.RunHealthCheckRequest.FromString, - response_serializer=tabletmanagerdata__pb2.RunHealthCheckResponse.SerializeToString, - ), - 'IgnoreHealthError': grpc.unary_unary_rpc_method_handler( - servicer.IgnoreHealthError, - request_deserializer=tabletmanagerdata__pb2.IgnoreHealthErrorRequest.FromString, - response_serializer=tabletmanagerdata__pb2.IgnoreHealthErrorResponse.SerializeToString, - ), - 'ReloadSchema': grpc.unary_unary_rpc_method_handler( - servicer.ReloadSchema, - request_deserializer=tabletmanagerdata__pb2.ReloadSchemaRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ReloadSchemaResponse.SerializeToString, - ), - 'PreflightSchema': grpc.unary_unary_rpc_method_handler( - servicer.PreflightSchema, - request_deserializer=tabletmanagerdata__pb2.PreflightSchemaRequest.FromString, - response_serializer=tabletmanagerdata__pb2.PreflightSchemaResponse.SerializeToString, - ), - 'ApplySchema': grpc.unary_unary_rpc_method_handler( - servicer.ApplySchema, - request_deserializer=tabletmanagerdata__pb2.ApplySchemaRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ApplySchemaResponse.SerializeToString, - ), - 'LockTables': grpc.unary_unary_rpc_method_handler( - servicer.LockTables, - request_deserializer=tabletmanagerdata__pb2.LockTablesRequest.FromString, - response_serializer=tabletmanagerdata__pb2.LockTablesResponse.SerializeToString, - ), - 'UnlockTables': grpc.unary_unary_rpc_method_handler( - servicer.UnlockTables, - request_deserializer=tabletmanagerdata__pb2.UnlockTablesRequest.FromString, - response_serializer=tabletmanagerdata__pb2.UnlockTablesResponse.SerializeToString, - ), - 'ExecuteFetchAsDba': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteFetchAsDba, - request_deserializer=tabletmanagerdata__pb2.ExecuteFetchAsDbaRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ExecuteFetchAsDbaResponse.SerializeToString, - ), - 'ExecuteFetchAsAllPrivs': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteFetchAsAllPrivs, - request_deserializer=tabletmanagerdata__pb2.ExecuteFetchAsAllPrivsRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ExecuteFetchAsAllPrivsResponse.SerializeToString, - ), - 'ExecuteFetchAsApp': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteFetchAsApp, - request_deserializer=tabletmanagerdata__pb2.ExecuteFetchAsAppRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ExecuteFetchAsAppResponse.SerializeToString, - ), - 'SlaveStatus': grpc.unary_unary_rpc_method_handler( - servicer.SlaveStatus, - request_deserializer=tabletmanagerdata__pb2.SlaveStatusRequest.FromString, - response_serializer=tabletmanagerdata__pb2.SlaveStatusResponse.SerializeToString, - ), - 'MasterPosition': grpc.unary_unary_rpc_method_handler( - servicer.MasterPosition, - request_deserializer=tabletmanagerdata__pb2.MasterPositionRequest.FromString, - response_serializer=tabletmanagerdata__pb2.MasterPositionResponse.SerializeToString, - ), - 'WaitForPosition': grpc.unary_unary_rpc_method_handler( - servicer.WaitForPosition, - request_deserializer=tabletmanagerdata__pb2.WaitForPositionRequest.FromString, - response_serializer=tabletmanagerdata__pb2.WaitForPositionResponse.SerializeToString, - ), - 'StopSlave': grpc.unary_unary_rpc_method_handler( - servicer.StopSlave, - request_deserializer=tabletmanagerdata__pb2.StopSlaveRequest.FromString, - response_serializer=tabletmanagerdata__pb2.StopSlaveResponse.SerializeToString, - ), - 'StopSlaveMinimum': grpc.unary_unary_rpc_method_handler( - servicer.StopSlaveMinimum, - request_deserializer=tabletmanagerdata__pb2.StopSlaveMinimumRequest.FromString, - response_serializer=tabletmanagerdata__pb2.StopSlaveMinimumResponse.SerializeToString, - ), - 'StartSlave': grpc.unary_unary_rpc_method_handler( - servicer.StartSlave, - request_deserializer=tabletmanagerdata__pb2.StartSlaveRequest.FromString, - response_serializer=tabletmanagerdata__pb2.StartSlaveResponse.SerializeToString, - ), - 'StartSlaveUntilAfter': grpc.unary_unary_rpc_method_handler( - servicer.StartSlaveUntilAfter, - request_deserializer=tabletmanagerdata__pb2.StartSlaveUntilAfterRequest.FromString, - response_serializer=tabletmanagerdata__pb2.StartSlaveUntilAfterResponse.SerializeToString, - ), - 'TabletExternallyReparented': grpc.unary_unary_rpc_method_handler( - servicer.TabletExternallyReparented, - request_deserializer=tabletmanagerdata__pb2.TabletExternallyReparentedRequest.FromString, - response_serializer=tabletmanagerdata__pb2.TabletExternallyReparentedResponse.SerializeToString, - ), - 'TabletExternallyElected': grpc.unary_unary_rpc_method_handler( - servicer.TabletExternallyElected, - request_deserializer=tabletmanagerdata__pb2.TabletExternallyElectedRequest.FromString, - response_serializer=tabletmanagerdata__pb2.TabletExternallyElectedResponse.SerializeToString, - ), - 'GetSlaves': grpc.unary_unary_rpc_method_handler( - servicer.GetSlaves, - request_deserializer=tabletmanagerdata__pb2.GetSlavesRequest.FromString, - response_serializer=tabletmanagerdata__pb2.GetSlavesResponse.SerializeToString, - ), - 'VReplicationExec': grpc.unary_unary_rpc_method_handler( - servicer.VReplicationExec, - request_deserializer=tabletmanagerdata__pb2.VReplicationExecRequest.FromString, - response_serializer=tabletmanagerdata__pb2.VReplicationExecResponse.SerializeToString, - ), - 'VReplicationWaitForPos': grpc.unary_unary_rpc_method_handler( - servicer.VReplicationWaitForPos, - request_deserializer=tabletmanagerdata__pb2.VReplicationWaitForPosRequest.FromString, - response_serializer=tabletmanagerdata__pb2.VReplicationWaitForPosResponse.SerializeToString, - ), - 'ResetReplication': grpc.unary_unary_rpc_method_handler( - servicer.ResetReplication, - request_deserializer=tabletmanagerdata__pb2.ResetReplicationRequest.FromString, - response_serializer=tabletmanagerdata__pb2.ResetReplicationResponse.SerializeToString, - ), - 'InitMaster': grpc.unary_unary_rpc_method_handler( - servicer.InitMaster, - request_deserializer=tabletmanagerdata__pb2.InitMasterRequest.FromString, - response_serializer=tabletmanagerdata__pb2.InitMasterResponse.SerializeToString, - ), - 'PopulateReparentJournal': grpc.unary_unary_rpc_method_handler( - servicer.PopulateReparentJournal, - request_deserializer=tabletmanagerdata__pb2.PopulateReparentJournalRequest.FromString, - response_serializer=tabletmanagerdata__pb2.PopulateReparentJournalResponse.SerializeToString, - ), - 'InitSlave': grpc.unary_unary_rpc_method_handler( - servicer.InitSlave, - request_deserializer=tabletmanagerdata__pb2.InitSlaveRequest.FromString, - response_serializer=tabletmanagerdata__pb2.InitSlaveResponse.SerializeToString, - ), - 'DemoteMaster': grpc.unary_unary_rpc_method_handler( - servicer.DemoteMaster, - request_deserializer=tabletmanagerdata__pb2.DemoteMasterRequest.FromString, - response_serializer=tabletmanagerdata__pb2.DemoteMasterResponse.SerializeToString, - ), - 'UndoDemoteMaster': grpc.unary_unary_rpc_method_handler( - servicer.UndoDemoteMaster, - request_deserializer=tabletmanagerdata__pb2.UndoDemoteMasterRequest.FromString, - response_serializer=tabletmanagerdata__pb2.UndoDemoteMasterResponse.SerializeToString, - ), - 'PromoteSlaveWhenCaughtUp': grpc.unary_unary_rpc_method_handler( - servicer.PromoteSlaveWhenCaughtUp, - request_deserializer=tabletmanagerdata__pb2.PromoteSlaveWhenCaughtUpRequest.FromString, - response_serializer=tabletmanagerdata__pb2.PromoteSlaveWhenCaughtUpResponse.SerializeToString, - ), - 'SlaveWasPromoted': grpc.unary_unary_rpc_method_handler( - servicer.SlaveWasPromoted, - request_deserializer=tabletmanagerdata__pb2.SlaveWasPromotedRequest.FromString, - response_serializer=tabletmanagerdata__pb2.SlaveWasPromotedResponse.SerializeToString, - ), - 'SetMaster': grpc.unary_unary_rpc_method_handler( - servicer.SetMaster, - request_deserializer=tabletmanagerdata__pb2.SetMasterRequest.FromString, - response_serializer=tabletmanagerdata__pb2.SetMasterResponse.SerializeToString, - ), - 'SlaveWasRestarted': grpc.unary_unary_rpc_method_handler( - servicer.SlaveWasRestarted, - request_deserializer=tabletmanagerdata__pb2.SlaveWasRestartedRequest.FromString, - response_serializer=tabletmanagerdata__pb2.SlaveWasRestartedResponse.SerializeToString, - ), - 'StopReplicationAndGetStatus': grpc.unary_unary_rpc_method_handler( - servicer.StopReplicationAndGetStatus, - request_deserializer=tabletmanagerdata__pb2.StopReplicationAndGetStatusRequest.FromString, - response_serializer=tabletmanagerdata__pb2.StopReplicationAndGetStatusResponse.SerializeToString, - ), - 'PromoteSlave': grpc.unary_unary_rpc_method_handler( - servicer.PromoteSlave, - request_deserializer=tabletmanagerdata__pb2.PromoteSlaveRequest.FromString, - response_serializer=tabletmanagerdata__pb2.PromoteSlaveResponse.SerializeToString, - ), - 'Backup': grpc.unary_stream_rpc_method_handler( - servicer.Backup, - request_deserializer=tabletmanagerdata__pb2.BackupRequest.FromString, - response_serializer=tabletmanagerdata__pb2.BackupResponse.SerializeToString, - ), - 'RestoreFromBackup': grpc.unary_stream_rpc_method_handler( - servicer.RestoreFromBackup, - request_deserializer=tabletmanagerdata__pb2.RestoreFromBackupRequest.FromString, - response_serializer=tabletmanagerdata__pb2.RestoreFromBackupResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'tabletmanagerservice.TabletManager', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/throttlerdata_pb2.py b/py/vtproto/throttlerdata_pb2.py deleted file mode 100644 index c531a2912a3..00000000000 --- a/py/vtproto/throttlerdata_pb2.py +++ /dev/null @@ -1,656 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: throttlerdata.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='throttlerdata.proto', - package='throttlerdata', - syntax='proto3', - serialized_options=_b('Z*vitess.io/vitess/go/vt/proto/throttlerdata'), - serialized_pb=_b('\n\x13throttlerdata.proto\x12\rthrottlerdata\"\x11\n\x0fMaxRatesRequest\"{\n\x10MaxRatesResponse\x12\x39\n\x05rates\x18\x01 \x03(\x0b\x32*.throttlerdata.MaxRatesResponse.RatesEntry\x1a,\n\nRatesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x03:\x02\x38\x01\"!\n\x11SetMaxRateRequest\x12\x0c\n\x04rate\x18\x01 \x01(\x03\"#\n\x12SetMaxRateResponse\x12\r\n\x05names\x18\x01 \x03(\t\"\xe8\x03\n\rConfiguration\x12\"\n\x1atarget_replication_lag_sec\x18\x01 \x01(\x03\x12\x1f\n\x17max_replication_lag_sec\x18\x02 \x01(\x03\x12\x14\n\x0cinitial_rate\x18\x03 \x01(\x03\x12\x14\n\x0cmax_increase\x18\x04 \x01(\x01\x12\x1a\n\x12\x65mergency_decrease\x18\x05 \x01(\x01\x12*\n\"min_duration_between_increases_sec\x18\x06 \x01(\x03\x12*\n\"max_duration_between_increases_sec\x18\x07 \x01(\x03\x12*\n\"min_duration_between_decreases_sec\x18\x08 \x01(\x03\x12!\n\x19spread_backlog_across_sec\x18\t \x01(\x03\x12!\n\x19ignore_n_slowest_replicas\x18\n \x01(\x05\x12 \n\x18ignore_n_slowest_rdonlys\x18\x0b \x01(\x05\x12\x1e\n\x16\x61ge_bad_rate_after_sec\x18\x0c \x01(\x03\x12\x19\n\x11\x62\x61\x64_rate_increase\x18\r \x01(\x01\x12#\n\x1bmax_rate_approach_threshold\x18\x0e \x01(\x01\"1\n\x17GetConfigurationRequest\x12\x16\n\x0ethrottler_name\x18\x01 \x01(\t\"\xc4\x01\n\x18GetConfigurationResponse\x12S\n\x0e\x63onfigurations\x18\x01 \x03(\x0b\x32;.throttlerdata.GetConfigurationResponse.ConfigurationsEntry\x1aS\n\x13\x43onfigurationsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12+\n\x05value\x18\x02 \x01(\x0b\x32\x1c.throttlerdata.Configuration:\x02\x38\x01\"\x83\x01\n\x1aUpdateConfigurationRequest\x12\x16\n\x0ethrottler_name\x18\x01 \x01(\t\x12\x33\n\rconfiguration\x18\x02 \x01(\x0b\x32\x1c.throttlerdata.Configuration\x12\x18\n\x10\x63opy_zero_values\x18\x03 \x01(\x08\",\n\x1bUpdateConfigurationResponse\x12\r\n\x05names\x18\x01 \x03(\t\"3\n\x19ResetConfigurationRequest\x12\x16\n\x0ethrottler_name\x18\x01 \x01(\t\"+\n\x1aResetConfigurationResponse\x12\r\n\x05names\x18\x01 \x03(\tB,Z*vitess.io/vitess/go/vt/proto/throttlerdatab\x06proto3') -) - - - - -_MAXRATESREQUEST = _descriptor.Descriptor( - name='MaxRatesRequest', - full_name='throttlerdata.MaxRatesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=38, - serialized_end=55, -) - - -_MAXRATESRESPONSE_RATESENTRY = _descriptor.Descriptor( - name='RatesEntry', - full_name='throttlerdata.MaxRatesResponse.RatesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='throttlerdata.MaxRatesResponse.RatesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='throttlerdata.MaxRatesResponse.RatesEntry.value', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=136, - serialized_end=180, -) - -_MAXRATESRESPONSE = _descriptor.Descriptor( - name='MaxRatesResponse', - full_name='throttlerdata.MaxRatesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='rates', full_name='throttlerdata.MaxRatesResponse.rates', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_MAXRATESRESPONSE_RATESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=57, - serialized_end=180, -) - - -_SETMAXRATEREQUEST = _descriptor.Descriptor( - name='SetMaxRateRequest', - full_name='throttlerdata.SetMaxRateRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='rate', full_name='throttlerdata.SetMaxRateRequest.rate', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=182, - serialized_end=215, -) - - -_SETMAXRATERESPONSE = _descriptor.Descriptor( - name='SetMaxRateResponse', - full_name='throttlerdata.SetMaxRateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='names', full_name='throttlerdata.SetMaxRateResponse.names', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=217, - serialized_end=252, -) - - -_CONFIGURATION = _descriptor.Descriptor( - name='Configuration', - full_name='throttlerdata.Configuration', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='target_replication_lag_sec', full_name='throttlerdata.Configuration.target_replication_lag_sec', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_replication_lag_sec', full_name='throttlerdata.Configuration.max_replication_lag_sec', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='initial_rate', full_name='throttlerdata.Configuration.initial_rate', index=2, - number=3, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_increase', full_name='throttlerdata.Configuration.max_increase', index=3, - number=4, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='emergency_decrease', full_name='throttlerdata.Configuration.emergency_decrease', index=4, - number=5, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='min_duration_between_increases_sec', full_name='throttlerdata.Configuration.min_duration_between_increases_sec', index=5, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_duration_between_increases_sec', full_name='throttlerdata.Configuration.max_duration_between_increases_sec', index=6, - number=7, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='min_duration_between_decreases_sec', full_name='throttlerdata.Configuration.min_duration_between_decreases_sec', index=7, - number=8, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='spread_backlog_across_sec', full_name='throttlerdata.Configuration.spread_backlog_across_sec', index=8, - number=9, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ignore_n_slowest_replicas', full_name='throttlerdata.Configuration.ignore_n_slowest_replicas', index=9, - number=10, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ignore_n_slowest_rdonlys', full_name='throttlerdata.Configuration.ignore_n_slowest_rdonlys', index=10, - number=11, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='age_bad_rate_after_sec', full_name='throttlerdata.Configuration.age_bad_rate_after_sec', index=11, - number=12, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='bad_rate_increase', full_name='throttlerdata.Configuration.bad_rate_increase', index=12, - number=13, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_rate_approach_threshold', full_name='throttlerdata.Configuration.max_rate_approach_threshold', index=13, - number=14, type=1, cpp_type=5, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=255, - serialized_end=743, -) - - -_GETCONFIGURATIONREQUEST = _descriptor.Descriptor( - name='GetConfigurationRequest', - full_name='throttlerdata.GetConfigurationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='throttler_name', full_name='throttlerdata.GetConfigurationRequest.throttler_name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=745, - serialized_end=794, -) - - -_GETCONFIGURATIONRESPONSE_CONFIGURATIONSENTRY = _descriptor.Descriptor( - name='ConfigurationsEntry', - full_name='throttlerdata.GetConfigurationResponse.ConfigurationsEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='throttlerdata.GetConfigurationResponse.ConfigurationsEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='throttlerdata.GetConfigurationResponse.ConfigurationsEntry.value', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=910, - serialized_end=993, -) - -_GETCONFIGURATIONRESPONSE = _descriptor.Descriptor( - name='GetConfigurationResponse', - full_name='throttlerdata.GetConfigurationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='configurations', full_name='throttlerdata.GetConfigurationResponse.configurations', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_GETCONFIGURATIONRESPONSE_CONFIGURATIONSENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=797, - serialized_end=993, -) - - -_UPDATECONFIGURATIONREQUEST = _descriptor.Descriptor( - name='UpdateConfigurationRequest', - full_name='throttlerdata.UpdateConfigurationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='throttler_name', full_name='throttlerdata.UpdateConfigurationRequest.throttler_name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='configuration', full_name='throttlerdata.UpdateConfigurationRequest.configuration', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='copy_zero_values', full_name='throttlerdata.UpdateConfigurationRequest.copy_zero_values', index=2, - number=3, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=996, - serialized_end=1127, -) - - -_UPDATECONFIGURATIONRESPONSE = _descriptor.Descriptor( - name='UpdateConfigurationResponse', - full_name='throttlerdata.UpdateConfigurationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='names', full_name='throttlerdata.UpdateConfigurationResponse.names', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1129, - serialized_end=1173, -) - - -_RESETCONFIGURATIONREQUEST = _descriptor.Descriptor( - name='ResetConfigurationRequest', - full_name='throttlerdata.ResetConfigurationRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='throttler_name', full_name='throttlerdata.ResetConfigurationRequest.throttler_name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1175, - serialized_end=1226, -) - - -_RESETCONFIGURATIONRESPONSE = _descriptor.Descriptor( - name='ResetConfigurationResponse', - full_name='throttlerdata.ResetConfigurationResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='names', full_name='throttlerdata.ResetConfigurationResponse.names', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1228, - serialized_end=1271, -) - -_MAXRATESRESPONSE_RATESENTRY.containing_type = _MAXRATESRESPONSE -_MAXRATESRESPONSE.fields_by_name['rates'].message_type = _MAXRATESRESPONSE_RATESENTRY -_GETCONFIGURATIONRESPONSE_CONFIGURATIONSENTRY.fields_by_name['value'].message_type = _CONFIGURATION -_GETCONFIGURATIONRESPONSE_CONFIGURATIONSENTRY.containing_type = _GETCONFIGURATIONRESPONSE -_GETCONFIGURATIONRESPONSE.fields_by_name['configurations'].message_type = _GETCONFIGURATIONRESPONSE_CONFIGURATIONSENTRY -_UPDATECONFIGURATIONREQUEST.fields_by_name['configuration'].message_type = _CONFIGURATION -DESCRIPTOR.message_types_by_name['MaxRatesRequest'] = _MAXRATESREQUEST -DESCRIPTOR.message_types_by_name['MaxRatesResponse'] = _MAXRATESRESPONSE -DESCRIPTOR.message_types_by_name['SetMaxRateRequest'] = _SETMAXRATEREQUEST -DESCRIPTOR.message_types_by_name['SetMaxRateResponse'] = _SETMAXRATERESPONSE -DESCRIPTOR.message_types_by_name['Configuration'] = _CONFIGURATION -DESCRIPTOR.message_types_by_name['GetConfigurationRequest'] = _GETCONFIGURATIONREQUEST -DESCRIPTOR.message_types_by_name['GetConfigurationResponse'] = _GETCONFIGURATIONRESPONSE -DESCRIPTOR.message_types_by_name['UpdateConfigurationRequest'] = _UPDATECONFIGURATIONREQUEST -DESCRIPTOR.message_types_by_name['UpdateConfigurationResponse'] = _UPDATECONFIGURATIONRESPONSE -DESCRIPTOR.message_types_by_name['ResetConfigurationRequest'] = _RESETCONFIGURATIONREQUEST -DESCRIPTOR.message_types_by_name['ResetConfigurationResponse'] = _RESETCONFIGURATIONRESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -MaxRatesRequest = _reflection.GeneratedProtocolMessageType('MaxRatesRequest', (_message.Message,), dict( - DESCRIPTOR = _MAXRATESREQUEST, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.MaxRatesRequest) - )) -_sym_db.RegisterMessage(MaxRatesRequest) - -MaxRatesResponse = _reflection.GeneratedProtocolMessageType('MaxRatesResponse', (_message.Message,), dict( - - RatesEntry = _reflection.GeneratedProtocolMessageType('RatesEntry', (_message.Message,), dict( - DESCRIPTOR = _MAXRATESRESPONSE_RATESENTRY, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.MaxRatesResponse.RatesEntry) - )) - , - DESCRIPTOR = _MAXRATESRESPONSE, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.MaxRatesResponse) - )) -_sym_db.RegisterMessage(MaxRatesResponse) -_sym_db.RegisterMessage(MaxRatesResponse.RatesEntry) - -SetMaxRateRequest = _reflection.GeneratedProtocolMessageType('SetMaxRateRequest', (_message.Message,), dict( - DESCRIPTOR = _SETMAXRATEREQUEST, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.SetMaxRateRequest) - )) -_sym_db.RegisterMessage(SetMaxRateRequest) - -SetMaxRateResponse = _reflection.GeneratedProtocolMessageType('SetMaxRateResponse', (_message.Message,), dict( - DESCRIPTOR = _SETMAXRATERESPONSE, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.SetMaxRateResponse) - )) -_sym_db.RegisterMessage(SetMaxRateResponse) - -Configuration = _reflection.GeneratedProtocolMessageType('Configuration', (_message.Message,), dict( - DESCRIPTOR = _CONFIGURATION, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.Configuration) - )) -_sym_db.RegisterMessage(Configuration) - -GetConfigurationRequest = _reflection.GeneratedProtocolMessageType('GetConfigurationRequest', (_message.Message,), dict( - DESCRIPTOR = _GETCONFIGURATIONREQUEST, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.GetConfigurationRequest) - )) -_sym_db.RegisterMessage(GetConfigurationRequest) - -GetConfigurationResponse = _reflection.GeneratedProtocolMessageType('GetConfigurationResponse', (_message.Message,), dict( - - ConfigurationsEntry = _reflection.GeneratedProtocolMessageType('ConfigurationsEntry', (_message.Message,), dict( - DESCRIPTOR = _GETCONFIGURATIONRESPONSE_CONFIGURATIONSENTRY, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.GetConfigurationResponse.ConfigurationsEntry) - )) - , - DESCRIPTOR = _GETCONFIGURATIONRESPONSE, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.GetConfigurationResponse) - )) -_sym_db.RegisterMessage(GetConfigurationResponse) -_sym_db.RegisterMessage(GetConfigurationResponse.ConfigurationsEntry) - -UpdateConfigurationRequest = _reflection.GeneratedProtocolMessageType('UpdateConfigurationRequest', (_message.Message,), dict( - DESCRIPTOR = _UPDATECONFIGURATIONREQUEST, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.UpdateConfigurationRequest) - )) -_sym_db.RegisterMessage(UpdateConfigurationRequest) - -UpdateConfigurationResponse = _reflection.GeneratedProtocolMessageType('UpdateConfigurationResponse', (_message.Message,), dict( - DESCRIPTOR = _UPDATECONFIGURATIONRESPONSE, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.UpdateConfigurationResponse) - )) -_sym_db.RegisterMessage(UpdateConfigurationResponse) - -ResetConfigurationRequest = _reflection.GeneratedProtocolMessageType('ResetConfigurationRequest', (_message.Message,), dict( - DESCRIPTOR = _RESETCONFIGURATIONREQUEST, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.ResetConfigurationRequest) - )) -_sym_db.RegisterMessage(ResetConfigurationRequest) - -ResetConfigurationResponse = _reflection.GeneratedProtocolMessageType('ResetConfigurationResponse', (_message.Message,), dict( - DESCRIPTOR = _RESETCONFIGURATIONRESPONSE, - __module__ = 'throttlerdata_pb2' - # @@protoc_insertion_point(class_scope:throttlerdata.ResetConfigurationResponse) - )) -_sym_db.RegisterMessage(ResetConfigurationResponse) - - -DESCRIPTOR._options = None -_MAXRATESRESPONSE_RATESENTRY._options = None -_GETCONFIGURATIONRESPONSE_CONFIGURATIONSENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/throttlerdata_pb2_grpc.py b/py/vtproto/throttlerdata_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/throttlerdata_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/throttlerservice_pb2.py b/py/vtproto/throttlerservice_pb2.py deleted file mode 100644 index 409f04c7ad9..00000000000 --- a/py/vtproto/throttlerservice_pb2.py +++ /dev/null @@ -1,93 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: throttlerservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import throttlerdata_pb2 as throttlerdata__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='throttlerservice.proto', - package='throttlerservice', - syntax='proto3', - serialized_options=_b('Z-vitess.io/vitess/go/vt/proto/throttlerservice'), - serialized_pb=_b('\n\x16throttlerservice.proto\x12\x10throttlerservice\x1a\x13throttlerdata.proto2\xf3\x03\n\tThrottler\x12M\n\x08MaxRates\x12\x1e.throttlerdata.MaxRatesRequest\x1a\x1f.throttlerdata.MaxRatesResponse\"\x00\x12S\n\nSetMaxRate\x12 .throttlerdata.SetMaxRateRequest\x1a!.throttlerdata.SetMaxRateResponse\"\x00\x12\x65\n\x10GetConfiguration\x12&.throttlerdata.GetConfigurationRequest\x1a\'.throttlerdata.GetConfigurationResponse\"\x00\x12n\n\x13UpdateConfiguration\x12).throttlerdata.UpdateConfigurationRequest\x1a*.throttlerdata.UpdateConfigurationResponse\"\x00\x12k\n\x12ResetConfiguration\x12(.throttlerdata.ResetConfigurationRequest\x1a).throttlerdata.ResetConfigurationResponse\"\x00\x42/Z-vitess.io/vitess/go/vt/proto/throttlerserviceb\x06proto3') - , - dependencies=[throttlerdata__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_THROTTLER = _descriptor.ServiceDescriptor( - name='Throttler', - full_name='throttlerservice.Throttler', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=66, - serialized_end=565, - methods=[ - _descriptor.MethodDescriptor( - name='MaxRates', - full_name='throttlerservice.Throttler.MaxRates', - index=0, - containing_service=None, - input_type=throttlerdata__pb2._MAXRATESREQUEST, - output_type=throttlerdata__pb2._MAXRATESRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SetMaxRate', - full_name='throttlerservice.Throttler.SetMaxRate', - index=1, - containing_service=None, - input_type=throttlerdata__pb2._SETMAXRATEREQUEST, - output_type=throttlerdata__pb2._SETMAXRATERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='GetConfiguration', - full_name='throttlerservice.Throttler.GetConfiguration', - index=2, - containing_service=None, - input_type=throttlerdata__pb2._GETCONFIGURATIONREQUEST, - output_type=throttlerdata__pb2._GETCONFIGURATIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='UpdateConfiguration', - full_name='throttlerservice.Throttler.UpdateConfiguration', - index=3, - containing_service=None, - input_type=throttlerdata__pb2._UPDATECONFIGURATIONREQUEST, - output_type=throttlerdata__pb2._UPDATECONFIGURATIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ResetConfiguration', - full_name='throttlerservice.Throttler.ResetConfiguration', - index=4, - containing_service=None, - input_type=throttlerdata__pb2._RESETCONFIGURATIONREQUEST, - output_type=throttlerdata__pb2._RESETCONFIGURATIONRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_THROTTLER) - -DESCRIPTOR.services_by_name['Throttler'] = _THROTTLER - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/throttlerservice_pb2_grpc.py b/py/vtproto/throttlerservice_pb2_grpc.py deleted file mode 100644 index 5cb3b50a547..00000000000 --- a/py/vtproto/throttlerservice_pb2_grpc.py +++ /dev/null @@ -1,122 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import throttlerdata_pb2 as throttlerdata__pb2 - - -class ThrottlerStub(object): - """Throttler defines the throttler RPC calls. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.MaxRates = channel.unary_unary( - '/throttlerservice.Throttler/MaxRates', - request_serializer=throttlerdata__pb2.MaxRatesRequest.SerializeToString, - response_deserializer=throttlerdata__pb2.MaxRatesResponse.FromString, - ) - self.SetMaxRate = channel.unary_unary( - '/throttlerservice.Throttler/SetMaxRate', - request_serializer=throttlerdata__pb2.SetMaxRateRequest.SerializeToString, - response_deserializer=throttlerdata__pb2.SetMaxRateResponse.FromString, - ) - self.GetConfiguration = channel.unary_unary( - '/throttlerservice.Throttler/GetConfiguration', - request_serializer=throttlerdata__pb2.GetConfigurationRequest.SerializeToString, - response_deserializer=throttlerdata__pb2.GetConfigurationResponse.FromString, - ) - self.UpdateConfiguration = channel.unary_unary( - '/throttlerservice.Throttler/UpdateConfiguration', - request_serializer=throttlerdata__pb2.UpdateConfigurationRequest.SerializeToString, - response_deserializer=throttlerdata__pb2.UpdateConfigurationResponse.FromString, - ) - self.ResetConfiguration = channel.unary_unary( - '/throttlerservice.Throttler/ResetConfiguration', - request_serializer=throttlerdata__pb2.ResetConfigurationRequest.SerializeToString, - response_deserializer=throttlerdata__pb2.ResetConfigurationResponse.FromString, - ) - - -class ThrottlerServicer(object): - """Throttler defines the throttler RPC calls. - """ - - def MaxRates(self, request, context): - """MaxRates returns the current max rate for each throttler of the process. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SetMaxRate(self, request, context): - """SetMaxRate allows to change the current max rate for all throttlers - of the process. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GetConfiguration(self, request, context): - """GetConfiguration returns the configuration of the MaxReplicationlag module - for the given throttler or all throttlers if "throttler_name" is empty. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def UpdateConfiguration(self, request, context): - """UpdateConfiguration (partially) updates the configuration of the - MaxReplicationlag module for the given throttler or all throttlers if - "throttler_name" is empty. - If "copy_zero_values" is true, fields with zero values will be copied - as well. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ResetConfiguration(self, request, context): - """ResetConfiguration resets the configuration of the MaxReplicationlag module - to the initial configuration for the given throttler or all throttlers if - "throttler_name" is empty. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_ThrottlerServicer_to_server(servicer, server): - rpc_method_handlers = { - 'MaxRates': grpc.unary_unary_rpc_method_handler( - servicer.MaxRates, - request_deserializer=throttlerdata__pb2.MaxRatesRequest.FromString, - response_serializer=throttlerdata__pb2.MaxRatesResponse.SerializeToString, - ), - 'SetMaxRate': grpc.unary_unary_rpc_method_handler( - servicer.SetMaxRate, - request_deserializer=throttlerdata__pb2.SetMaxRateRequest.FromString, - response_serializer=throttlerdata__pb2.SetMaxRateResponse.SerializeToString, - ), - 'GetConfiguration': grpc.unary_unary_rpc_method_handler( - servicer.GetConfiguration, - request_deserializer=throttlerdata__pb2.GetConfigurationRequest.FromString, - response_serializer=throttlerdata__pb2.GetConfigurationResponse.SerializeToString, - ), - 'UpdateConfiguration': grpc.unary_unary_rpc_method_handler( - servicer.UpdateConfiguration, - request_deserializer=throttlerdata__pb2.UpdateConfigurationRequest.FromString, - response_serializer=throttlerdata__pb2.UpdateConfigurationResponse.SerializeToString, - ), - 'ResetConfiguration': grpc.unary_unary_rpc_method_handler( - servicer.ResetConfiguration, - request_deserializer=throttlerdata__pb2.ResetConfigurationRequest.FromString, - response_serializer=throttlerdata__pb2.ResetConfigurationResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'throttlerservice.Throttler', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/topodata_pb2.py b/py/vtproto/topodata_pb2.py deleted file mode 100644 index 125cc8042ae..00000000000 --- a/py/vtproto/topodata_pb2.py +++ /dev/null @@ -1,1293 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: topodata.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import vttime_pb2 as vttime__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='topodata.proto', - package='topodata', - syntax='proto3', - serialized_options=_b('\n\017io.vitess.protoZ%vitess.io/vitess/go/vt/proto/topodata'), - serialized_pb=_b('\n\x0etopodata.proto\x12\x08topodata\x1a\x0cvttime.proto\"&\n\x08KeyRange\x12\r\n\x05start\x18\x01 \x01(\x0c\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x0c\"(\n\x0bTabletAlias\x12\x0c\n\x04\x63\x65ll\x18\x01 \x01(\t\x12\x0b\n\x03uid\x18\x02 \x01(\r\"\xe4\x03\n\x06Tablet\x12$\n\x05\x61lias\x18\x01 \x01(\x0b\x32\x15.topodata.TabletAlias\x12\x10\n\x08hostname\x18\x02 \x01(\t\x12/\n\x08port_map\x18\x04 \x03(\x0b\x32\x1d.topodata.Tablet.PortMapEntry\x12\x10\n\x08keyspace\x18\x05 \x01(\t\x12\r\n\x05shard\x18\x06 \x01(\t\x12%\n\tkey_range\x18\x07 \x01(\x0b\x32\x12.topodata.KeyRange\x12\"\n\x04type\x18\x08 \x01(\x0e\x32\x14.topodata.TabletType\x12\x18\n\x10\x64\x62_name_override\x18\t \x01(\t\x12(\n\x04tags\x18\n \x03(\x0b\x32\x1a.topodata.Tablet.TagsEntry\x12\x16\n\x0emysql_hostname\x18\x0c \x01(\t\x12\x12\n\nmysql_port\x18\r \x01(\x05\x12,\n\x16master_term_start_time\x18\x0e \x01(\x0b\x32\x0c.vttime.Time\x1a.\n\x0cPortMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01J\x04\x08\x03\x10\x04J\x04\x08\x0b\x10\x0c\"\x81\x05\n\x05Shard\x12+\n\x0cmaster_alias\x18\x01 \x01(\x0b\x32\x15.topodata.TabletAlias\x12,\n\x16master_term_start_time\x18\x08 \x01(\x0b\x32\x0c.vttime.Time\x12%\n\tkey_range\x18\x02 \x01(\x0b\x32\x12.topodata.KeyRange\x12\x30\n\x0cserved_types\x18\x03 \x03(\x0b\x32\x1a.topodata.Shard.ServedType\x12\x32\n\rsource_shards\x18\x04 \x03(\x0b\x32\x1b.topodata.Shard.SourceShard\x12\x36\n\x0ftablet_controls\x18\x06 \x03(\x0b\x32\x1d.topodata.Shard.TabletControl\x12\x19\n\x11is_master_serving\x18\x07 \x01(\x08\x1a\x46\n\nServedType\x12)\n\x0btablet_type\x18\x01 \x01(\x0e\x32\x14.topodata.TabletType\x12\r\n\x05\x63\x65lls\x18\x02 \x03(\t\x1ar\n\x0bSourceShard\x12\x0b\n\x03uid\x18\x01 \x01(\r\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\x0e\n\x06tables\x18\x05 \x03(\t\x1a{\n\rTabletControl\x12)\n\x0btablet_type\x18\x01 \x01(\x0e\x32\x14.topodata.TabletType\x12\r\n\x05\x63\x65lls\x18\x02 \x03(\t\x12\x1a\n\x12\x62lacklisted_tables\x18\x04 \x03(\t\x12\x0e\n\x06\x66rozen\x18\x05 \x01(\x08J\x04\x08\x03\x10\x04J\x04\x08\x05\x10\x06\"\xe0\x02\n\x08Keyspace\x12\x1c\n\x14sharding_column_name\x18\x01 \x01(\t\x12\x36\n\x14sharding_column_type\x18\x02 \x01(\x0e\x32\x18.topodata.KeyspaceIdType\x12\x33\n\x0cserved_froms\x18\x04 \x03(\x0b\x32\x1d.topodata.Keyspace.ServedFrom\x12-\n\rkeyspace_type\x18\x05 \x01(\x0e\x32\x16.topodata.KeyspaceType\x12\x15\n\rbase_keyspace\x18\x06 \x01(\t\x12#\n\rsnapshot_time\x18\x07 \x01(\x0b\x32\x0c.vttime.Time\x1aX\n\nServedFrom\x12)\n\x0btablet_type\x18\x01 \x01(\x0e\x32\x14.topodata.TabletType\x12\r\n\x05\x63\x65lls\x18\x02 \x03(\t\x12\x10\n\x08keyspace\x18\x03 \x01(\tJ\x04\x08\x03\x10\x04\"w\n\x10ShardReplication\x12.\n\x05nodes\x18\x01 \x03(\x0b\x32\x1f.topodata.ShardReplication.Node\x1a\x33\n\x04Node\x12+\n\x0ctablet_alias\x18\x01 \x01(\x0b\x32\x15.topodata.TabletAlias\"E\n\x0eShardReference\x12\x0c\n\x04name\x18\x01 \x01(\t\x12%\n\tkey_range\x18\x02 \x01(\x0b\x32\x12.topodata.KeyRange\"i\n\x12ShardTabletControl\x12\x0c\n\x04name\x18\x01 \x01(\t\x12%\n\tkey_range\x18\x02 \x01(\x0b\x32\x12.topodata.KeyRange\x12\x1e\n\x16query_service_disabled\x18\x03 \x01(\x08\"\xda\x03\n\x0bSrvKeyspace\x12;\n\npartitions\x18\x01 \x03(\x0b\x32\'.topodata.SrvKeyspace.KeyspacePartition\x12\x1c\n\x14sharding_column_name\x18\x02 \x01(\t\x12\x36\n\x14sharding_column_type\x18\x03 \x01(\x0e\x32\x18.topodata.KeyspaceIdType\x12\x35\n\x0bserved_from\x18\x04 \x03(\x0b\x32 .topodata.SrvKeyspace.ServedFrom\x1a\xaf\x01\n\x11KeyspacePartition\x12)\n\x0bserved_type\x18\x01 \x01(\x0e\x32\x14.topodata.TabletType\x12\x32\n\x10shard_references\x18\x02 \x03(\x0b\x32\x18.topodata.ShardReference\x12;\n\x15shard_tablet_controls\x18\x03 \x03(\x0b\x32\x1c.topodata.ShardTabletControl\x1aI\n\nServedFrom\x12)\n\x0btablet_type\x18\x01 \x01(\x0e\x32\x14.topodata.TabletType\x12\x10\n\x08keyspace\x18\x02 \x01(\tJ\x04\x08\x05\x10\x06\"6\n\x08\x43\x65llInfo\x12\x16\n\x0eserver_address\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x02 \x01(\tJ\x04\x08\x03\x10\x04\"\x1b\n\nCellsAlias\x12\r\n\x05\x63\x65lls\x18\x02 \x03(\t*(\n\x0cKeyspaceType\x12\n\n\x06NORMAL\x10\x00\x12\x0c\n\x08SNAPSHOT\x10\x01*2\n\x0eKeyspaceIdType\x12\t\n\x05UNSET\x10\x00\x12\n\n\x06UINT64\x10\x01\x12\t\n\x05\x42YTES\x10\x02*\x90\x01\n\nTabletType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06MASTER\x10\x01\x12\x0b\n\x07REPLICA\x10\x02\x12\n\n\x06RDONLY\x10\x03\x12\t\n\x05\x42\x41TCH\x10\x03\x12\t\n\x05SPARE\x10\x04\x12\x10\n\x0c\x45XPERIMENTAL\x10\x05\x12\n\n\x06\x42\x41\x43KUP\x10\x06\x12\x0b\n\x07RESTORE\x10\x07\x12\x0b\n\x07\x44RAINED\x10\x08\x1a\x02\x10\x01\x42\x38\n\x0fio.vitess.protoZ%vitess.io/vitess/go/vt/proto/topodatab\x06proto3') - , - dependencies=[vttime__pb2.DESCRIPTOR,]) - -_KEYSPACETYPE = _descriptor.EnumDescriptor( - name='KeyspaceType', - full_name='topodata.KeyspaceType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NORMAL', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SNAPSHOT', index=1, number=1, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=2471, - serialized_end=2511, -) -_sym_db.RegisterEnumDescriptor(_KEYSPACETYPE) - -KeyspaceType = enum_type_wrapper.EnumTypeWrapper(_KEYSPACETYPE) -_KEYSPACEIDTYPE = _descriptor.EnumDescriptor( - name='KeyspaceIdType', - full_name='topodata.KeyspaceIdType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNSET', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UINT64', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BYTES', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=2513, - serialized_end=2563, -) -_sym_db.RegisterEnumDescriptor(_KEYSPACEIDTYPE) - -KeyspaceIdType = enum_type_wrapper.EnumTypeWrapper(_KEYSPACEIDTYPE) -_TABLETTYPE = _descriptor.EnumDescriptor( - name='TabletType', - full_name='topodata.TabletType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MASTER', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='REPLICA', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RDONLY', index=3, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BATCH', index=4, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SPARE', index=5, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='EXPERIMENTAL', index=6, number=5, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BACKUP', index=7, number=6, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RESTORE', index=8, number=7, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DRAINED', index=9, number=8, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=_b('\020\001'), - serialized_start=2566, - serialized_end=2710, -) -_sym_db.RegisterEnumDescriptor(_TABLETTYPE) - -TabletType = enum_type_wrapper.EnumTypeWrapper(_TABLETTYPE) -NORMAL = 0 -SNAPSHOT = 1 -UNSET = 0 -UINT64 = 1 -BYTES = 2 -UNKNOWN = 0 -MASTER = 1 -REPLICA = 2 -RDONLY = 3 -BATCH = 3 -SPARE = 4 -EXPERIMENTAL = 5 -BACKUP = 6 -RESTORE = 7 -DRAINED = 8 - - - -_KEYRANGE = _descriptor.Descriptor( - name='KeyRange', - full_name='topodata.KeyRange', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='start', full_name='topodata.KeyRange.start', index=0, - number=1, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='end', full_name='topodata.KeyRange.end', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=42, - serialized_end=80, -) - - -_TABLETALIAS = _descriptor.Descriptor( - name='TabletAlias', - full_name='topodata.TabletAlias', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='cell', full_name='topodata.TabletAlias.cell', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='uid', full_name='topodata.TabletAlias.uid', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=82, - serialized_end=122, -) - - -_TABLET_PORTMAPENTRY = _descriptor.Descriptor( - name='PortMapEntry', - full_name='topodata.Tablet.PortMapEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='topodata.Tablet.PortMapEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='topodata.Tablet.PortMapEntry.value', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=506, - serialized_end=552, -) - -_TABLET_TAGSENTRY = _descriptor.Descriptor( - name='TagsEntry', - full_name='topodata.Tablet.TagsEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='topodata.Tablet.TagsEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='topodata.Tablet.TagsEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=554, - serialized_end=597, -) - -_TABLET = _descriptor.Descriptor( - name='Tablet', - full_name='topodata.Tablet', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='alias', full_name='topodata.Tablet.alias', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='hostname', full_name='topodata.Tablet.hostname', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='port_map', full_name='topodata.Tablet.port_map', index=2, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='topodata.Tablet.keyspace', index=3, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='topodata.Tablet.shard', index=4, - 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='topodata.Tablet.key_range', index=5, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='type', full_name='topodata.Tablet.type', index=6, - number=8, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='db_name_override', full_name='topodata.Tablet.db_name_override', index=7, - number=9, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tags', full_name='topodata.Tablet.tags', index=8, - number=10, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='mysql_hostname', full_name='topodata.Tablet.mysql_hostname', index=9, - number=12, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='mysql_port', full_name='topodata.Tablet.mysql_port', index=10, - number=13, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='master_term_start_time', full_name='topodata.Tablet.master_term_start_time', index=11, - number=14, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_TABLET_PORTMAPENTRY, _TABLET_TAGSENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=125, - serialized_end=609, -) - - -_SHARD_SERVEDTYPE = _descriptor.Descriptor( - name='ServedType', - full_name='topodata.Shard.ServedType', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tablet_type', full_name='topodata.Shard.ServedType.tablet_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cells', full_name='topodata.Shard.ServedType.cells', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=936, - serialized_end=1006, -) - -_SHARD_SOURCESHARD = _descriptor.Descriptor( - name='SourceShard', - full_name='topodata.Shard.SourceShard', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='uid', full_name='topodata.Shard.SourceShard.uid', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='topodata.Shard.SourceShard.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='topodata.Shard.SourceShard.shard', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='topodata.Shard.SourceShard.key_range', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tables', full_name='topodata.Shard.SourceShard.tables', index=4, - number=5, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1008, - serialized_end=1122, -) - -_SHARD_TABLETCONTROL = _descriptor.Descriptor( - name='TabletControl', - full_name='topodata.Shard.TabletControl', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tablet_type', full_name='topodata.Shard.TabletControl.tablet_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cells', full_name='topodata.Shard.TabletControl.cells', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='blacklisted_tables', full_name='topodata.Shard.TabletControl.blacklisted_tables', index=2, - number=4, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='frozen', full_name='topodata.Shard.TabletControl.frozen', index=3, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1124, - serialized_end=1247, -) - -_SHARD = _descriptor.Descriptor( - name='Shard', - full_name='topodata.Shard', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='master_alias', full_name='topodata.Shard.master_alias', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='master_term_start_time', full_name='topodata.Shard.master_term_start_time', index=1, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='topodata.Shard.key_range', index=2, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='served_types', full_name='topodata.Shard.served_types', index=3, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_shards', full_name='topodata.Shard.source_shards', index=4, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_controls', full_name='topodata.Shard.tablet_controls', index=5, - number=6, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_master_serving', full_name='topodata.Shard.is_master_serving', index=6, - number=7, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_SHARD_SERVEDTYPE, _SHARD_SOURCESHARD, _SHARD_TABLETCONTROL, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=612, - serialized_end=1253, -) - - -_KEYSPACE_SERVEDFROM = _descriptor.Descriptor( - name='ServedFrom', - full_name='topodata.Keyspace.ServedFrom', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tablet_type', full_name='topodata.Keyspace.ServedFrom.tablet_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cells', full_name='topodata.Keyspace.ServedFrom.cells', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='topodata.Keyspace.ServedFrom.keyspace', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1514, - serialized_end=1602, -) - -_KEYSPACE = _descriptor.Descriptor( - name='Keyspace', - full_name='topodata.Keyspace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='sharding_column_name', full_name='topodata.Keyspace.sharding_column_name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sharding_column_type', full_name='topodata.Keyspace.sharding_column_type', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='served_froms', full_name='topodata.Keyspace.served_froms', index=2, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace_type', full_name='topodata.Keyspace.keyspace_type', index=3, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='base_keyspace', full_name='topodata.Keyspace.base_keyspace', index=4, - 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='snapshot_time', full_name='topodata.Keyspace.snapshot_time', index=5, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_KEYSPACE_SERVEDFROM, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1256, - serialized_end=1608, -) - - -_SHARDREPLICATION_NODE = _descriptor.Descriptor( - name='Node', - full_name='topodata.ShardReplication.Node', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tablet_alias', full_name='topodata.ShardReplication.Node.tablet_alias', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1678, - serialized_end=1729, -) - -_SHARDREPLICATION = _descriptor.Descriptor( - name='ShardReplication', - full_name='topodata.ShardReplication', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='nodes', full_name='topodata.ShardReplication.nodes', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_SHARDREPLICATION_NODE, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1610, - serialized_end=1729, -) - - -_SHARDREFERENCE = _descriptor.Descriptor( - name='ShardReference', - full_name='topodata.ShardReference', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='topodata.ShardReference.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='topodata.ShardReference.key_range', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1731, - serialized_end=1800, -) - - -_SHARDTABLETCONTROL = _descriptor.Descriptor( - name='ShardTabletControl', - full_name='topodata.ShardTabletControl', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='topodata.ShardTabletControl.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='topodata.ShardTabletControl.key_range', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query_service_disabled', full_name='topodata.ShardTabletControl.query_service_disabled', index=2, - number=3, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1802, - serialized_end=1907, -) - - -_SRVKEYSPACE_KEYSPACEPARTITION = _descriptor.Descriptor( - name='KeyspacePartition', - full_name='topodata.SrvKeyspace.KeyspacePartition', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='served_type', full_name='topodata.SrvKeyspace.KeyspacePartition.served_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard_references', full_name='topodata.SrvKeyspace.KeyspacePartition.shard_references', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard_tablet_controls', full_name='topodata.SrvKeyspace.KeyspacePartition.shard_tablet_controls', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2128, - serialized_end=2303, -) - -_SRVKEYSPACE_SERVEDFROM = _descriptor.Descriptor( - name='ServedFrom', - full_name='topodata.SrvKeyspace.ServedFrom', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='tablet_type', full_name='topodata.SrvKeyspace.ServedFrom.tablet_type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='topodata.SrvKeyspace.ServedFrom.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2305, - serialized_end=2378, -) - -_SRVKEYSPACE = _descriptor.Descriptor( - name='SrvKeyspace', - full_name='topodata.SrvKeyspace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='partitions', full_name='topodata.SrvKeyspace.partitions', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sharding_column_name', full_name='topodata.SrvKeyspace.sharding_column_name', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sharding_column_type', full_name='topodata.SrvKeyspace.sharding_column_type', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='served_from', full_name='topodata.SrvKeyspace.served_from', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_SRVKEYSPACE_KEYSPACEPARTITION, _SRVKEYSPACE_SERVEDFROM, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1910, - serialized_end=2384, -) - - -_CELLINFO = _descriptor.Descriptor( - name='CellInfo', - full_name='topodata.CellInfo', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='server_address', full_name='topodata.CellInfo.server_address', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='root', full_name='topodata.CellInfo.root', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2386, - serialized_end=2440, -) - - -_CELLSALIAS = _descriptor.Descriptor( - name='CellsAlias', - full_name='topodata.CellsAlias', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='cells', full_name='topodata.CellsAlias.cells', index=0, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2442, - serialized_end=2469, -) - -_TABLET_PORTMAPENTRY.containing_type = _TABLET -_TABLET_TAGSENTRY.containing_type = _TABLET -_TABLET.fields_by_name['alias'].message_type = _TABLETALIAS -_TABLET.fields_by_name['port_map'].message_type = _TABLET_PORTMAPENTRY -_TABLET.fields_by_name['key_range'].message_type = _KEYRANGE -_TABLET.fields_by_name['type'].enum_type = _TABLETTYPE -_TABLET.fields_by_name['tags'].message_type = _TABLET_TAGSENTRY -_TABLET.fields_by_name['master_term_start_time'].message_type = vttime__pb2._TIME -_SHARD_SERVEDTYPE.fields_by_name['tablet_type'].enum_type = _TABLETTYPE -_SHARD_SERVEDTYPE.containing_type = _SHARD -_SHARD_SOURCESHARD.fields_by_name['key_range'].message_type = _KEYRANGE -_SHARD_SOURCESHARD.containing_type = _SHARD -_SHARD_TABLETCONTROL.fields_by_name['tablet_type'].enum_type = _TABLETTYPE -_SHARD_TABLETCONTROL.containing_type = _SHARD -_SHARD.fields_by_name['master_alias'].message_type = _TABLETALIAS -_SHARD.fields_by_name['master_term_start_time'].message_type = vttime__pb2._TIME -_SHARD.fields_by_name['key_range'].message_type = _KEYRANGE -_SHARD.fields_by_name['served_types'].message_type = _SHARD_SERVEDTYPE -_SHARD.fields_by_name['source_shards'].message_type = _SHARD_SOURCESHARD -_SHARD.fields_by_name['tablet_controls'].message_type = _SHARD_TABLETCONTROL -_KEYSPACE_SERVEDFROM.fields_by_name['tablet_type'].enum_type = _TABLETTYPE -_KEYSPACE_SERVEDFROM.containing_type = _KEYSPACE -_KEYSPACE.fields_by_name['sharding_column_type'].enum_type = _KEYSPACEIDTYPE -_KEYSPACE.fields_by_name['served_froms'].message_type = _KEYSPACE_SERVEDFROM -_KEYSPACE.fields_by_name['keyspace_type'].enum_type = _KEYSPACETYPE -_KEYSPACE.fields_by_name['snapshot_time'].message_type = vttime__pb2._TIME -_SHARDREPLICATION_NODE.fields_by_name['tablet_alias'].message_type = _TABLETALIAS -_SHARDREPLICATION_NODE.containing_type = _SHARDREPLICATION -_SHARDREPLICATION.fields_by_name['nodes'].message_type = _SHARDREPLICATION_NODE -_SHARDREFERENCE.fields_by_name['key_range'].message_type = _KEYRANGE -_SHARDTABLETCONTROL.fields_by_name['key_range'].message_type = _KEYRANGE -_SRVKEYSPACE_KEYSPACEPARTITION.fields_by_name['served_type'].enum_type = _TABLETTYPE -_SRVKEYSPACE_KEYSPACEPARTITION.fields_by_name['shard_references'].message_type = _SHARDREFERENCE -_SRVKEYSPACE_KEYSPACEPARTITION.fields_by_name['shard_tablet_controls'].message_type = _SHARDTABLETCONTROL -_SRVKEYSPACE_KEYSPACEPARTITION.containing_type = _SRVKEYSPACE -_SRVKEYSPACE_SERVEDFROM.fields_by_name['tablet_type'].enum_type = _TABLETTYPE -_SRVKEYSPACE_SERVEDFROM.containing_type = _SRVKEYSPACE -_SRVKEYSPACE.fields_by_name['partitions'].message_type = _SRVKEYSPACE_KEYSPACEPARTITION -_SRVKEYSPACE.fields_by_name['sharding_column_type'].enum_type = _KEYSPACEIDTYPE -_SRVKEYSPACE.fields_by_name['served_from'].message_type = _SRVKEYSPACE_SERVEDFROM -DESCRIPTOR.message_types_by_name['KeyRange'] = _KEYRANGE -DESCRIPTOR.message_types_by_name['TabletAlias'] = _TABLETALIAS -DESCRIPTOR.message_types_by_name['Tablet'] = _TABLET -DESCRIPTOR.message_types_by_name['Shard'] = _SHARD -DESCRIPTOR.message_types_by_name['Keyspace'] = _KEYSPACE -DESCRIPTOR.message_types_by_name['ShardReplication'] = _SHARDREPLICATION -DESCRIPTOR.message_types_by_name['ShardReference'] = _SHARDREFERENCE -DESCRIPTOR.message_types_by_name['ShardTabletControl'] = _SHARDTABLETCONTROL -DESCRIPTOR.message_types_by_name['SrvKeyspace'] = _SRVKEYSPACE -DESCRIPTOR.message_types_by_name['CellInfo'] = _CELLINFO -DESCRIPTOR.message_types_by_name['CellsAlias'] = _CELLSALIAS -DESCRIPTOR.enum_types_by_name['KeyspaceType'] = _KEYSPACETYPE -DESCRIPTOR.enum_types_by_name['KeyspaceIdType'] = _KEYSPACEIDTYPE -DESCRIPTOR.enum_types_by_name['TabletType'] = _TABLETTYPE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -KeyRange = _reflection.GeneratedProtocolMessageType('KeyRange', (_message.Message,), dict( - DESCRIPTOR = _KEYRANGE, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.KeyRange) - )) -_sym_db.RegisterMessage(KeyRange) - -TabletAlias = _reflection.GeneratedProtocolMessageType('TabletAlias', (_message.Message,), dict( - DESCRIPTOR = _TABLETALIAS, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.TabletAlias) - )) -_sym_db.RegisterMessage(TabletAlias) - -Tablet = _reflection.GeneratedProtocolMessageType('Tablet', (_message.Message,), dict( - - PortMapEntry = _reflection.GeneratedProtocolMessageType('PortMapEntry', (_message.Message,), dict( - DESCRIPTOR = _TABLET_PORTMAPENTRY, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Tablet.PortMapEntry) - )) - , - - TagsEntry = _reflection.GeneratedProtocolMessageType('TagsEntry', (_message.Message,), dict( - DESCRIPTOR = _TABLET_TAGSENTRY, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Tablet.TagsEntry) - )) - , - DESCRIPTOR = _TABLET, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Tablet) - )) -_sym_db.RegisterMessage(Tablet) -_sym_db.RegisterMessage(Tablet.PortMapEntry) -_sym_db.RegisterMessage(Tablet.TagsEntry) - -Shard = _reflection.GeneratedProtocolMessageType('Shard', (_message.Message,), dict( - - ServedType = _reflection.GeneratedProtocolMessageType('ServedType', (_message.Message,), dict( - DESCRIPTOR = _SHARD_SERVEDTYPE, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Shard.ServedType) - )) - , - - SourceShard = _reflection.GeneratedProtocolMessageType('SourceShard', (_message.Message,), dict( - DESCRIPTOR = _SHARD_SOURCESHARD, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Shard.SourceShard) - )) - , - - TabletControl = _reflection.GeneratedProtocolMessageType('TabletControl', (_message.Message,), dict( - DESCRIPTOR = _SHARD_TABLETCONTROL, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Shard.TabletControl) - )) - , - DESCRIPTOR = _SHARD, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Shard) - )) -_sym_db.RegisterMessage(Shard) -_sym_db.RegisterMessage(Shard.ServedType) -_sym_db.RegisterMessage(Shard.SourceShard) -_sym_db.RegisterMessage(Shard.TabletControl) - -Keyspace = _reflection.GeneratedProtocolMessageType('Keyspace', (_message.Message,), dict( - - ServedFrom = _reflection.GeneratedProtocolMessageType('ServedFrom', (_message.Message,), dict( - DESCRIPTOR = _KEYSPACE_SERVEDFROM, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Keyspace.ServedFrom) - )) - , - DESCRIPTOR = _KEYSPACE, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.Keyspace) - )) -_sym_db.RegisterMessage(Keyspace) -_sym_db.RegisterMessage(Keyspace.ServedFrom) - -ShardReplication = _reflection.GeneratedProtocolMessageType('ShardReplication', (_message.Message,), dict( - - Node = _reflection.GeneratedProtocolMessageType('Node', (_message.Message,), dict( - DESCRIPTOR = _SHARDREPLICATION_NODE, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.ShardReplication.Node) - )) - , - DESCRIPTOR = _SHARDREPLICATION, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.ShardReplication) - )) -_sym_db.RegisterMessage(ShardReplication) -_sym_db.RegisterMessage(ShardReplication.Node) - -ShardReference = _reflection.GeneratedProtocolMessageType('ShardReference', (_message.Message,), dict( - DESCRIPTOR = _SHARDREFERENCE, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.ShardReference) - )) -_sym_db.RegisterMessage(ShardReference) - -ShardTabletControl = _reflection.GeneratedProtocolMessageType('ShardTabletControl', (_message.Message,), dict( - DESCRIPTOR = _SHARDTABLETCONTROL, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.ShardTabletControl) - )) -_sym_db.RegisterMessage(ShardTabletControl) - -SrvKeyspace = _reflection.GeneratedProtocolMessageType('SrvKeyspace', (_message.Message,), dict( - - KeyspacePartition = _reflection.GeneratedProtocolMessageType('KeyspacePartition', (_message.Message,), dict( - DESCRIPTOR = _SRVKEYSPACE_KEYSPACEPARTITION, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.SrvKeyspace.KeyspacePartition) - )) - , - - ServedFrom = _reflection.GeneratedProtocolMessageType('ServedFrom', (_message.Message,), dict( - DESCRIPTOR = _SRVKEYSPACE_SERVEDFROM, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.SrvKeyspace.ServedFrom) - )) - , - DESCRIPTOR = _SRVKEYSPACE, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.SrvKeyspace) - )) -_sym_db.RegisterMessage(SrvKeyspace) -_sym_db.RegisterMessage(SrvKeyspace.KeyspacePartition) -_sym_db.RegisterMessage(SrvKeyspace.ServedFrom) - -CellInfo = _reflection.GeneratedProtocolMessageType('CellInfo', (_message.Message,), dict( - DESCRIPTOR = _CELLINFO, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.CellInfo) - )) -_sym_db.RegisterMessage(CellInfo) - -CellsAlias = _reflection.GeneratedProtocolMessageType('CellsAlias', (_message.Message,), dict( - DESCRIPTOR = _CELLSALIAS, - __module__ = 'topodata_pb2' - # @@protoc_insertion_point(class_scope:topodata.CellsAlias) - )) -_sym_db.RegisterMessage(CellsAlias) - - -DESCRIPTOR._options = None -_TABLETTYPE._options = None -_TABLET_PORTMAPENTRY._options = None -_TABLET_TAGSENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/topodata_pb2_grpc.py b/py/vtproto/topodata_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/topodata_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vschema_pb2.py b/py/vtproto/vschema_pb2.py deleted file mode 100644 index 90897a8f264..00000000000 --- a/py/vtproto/vschema_pb2.py +++ /dev/null @@ -1,697 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vschema.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import query_pb2 as query__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vschema.proto', - package='vschema', - syntax='proto3', - serialized_options=_b('Z$vitess.io/vitess/go/vt/proto/vschema'), - serialized_pb=_b('\n\rvschema.proto\x12\x07vschema\x1a\x0bquery.proto\"3\n\x0cRoutingRules\x12#\n\x05rules\x18\x01 \x03(\x0b\x32\x14.vschema.RoutingRule\"4\n\x0bRoutingRule\x12\x12\n\nfrom_table\x18\x01 \x01(\t\x12\x11\n\tto_tables\x18\x02 \x03(\t\"\xa0\x02\n\x08Keyspace\x12\x0f\n\x07sharded\x18\x01 \x01(\x08\x12\x31\n\x08vindexes\x18\x02 \x03(\x0b\x32\x1f.vschema.Keyspace.VindexesEntry\x12-\n\x06tables\x18\x03 \x03(\x0b\x32\x1d.vschema.Keyspace.TablesEntry\x12 \n\x18require_explicit_routing\x18\x04 \x01(\x08\x1a@\n\rVindexesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.vschema.Vindex:\x02\x38\x01\x1a=\n\x0bTablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.vschema.Table:\x02\x38\x01\"\x81\x01\n\x06Vindex\x12\x0c\n\x04type\x18\x01 \x01(\t\x12+\n\x06params\x18\x02 \x03(\x0b\x32\x1b.vschema.Vindex.ParamsEntry\x12\r\n\x05owner\x18\x03 \x01(\t\x1a-\n\x0bParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xca\x01\n\x05Table\x12\x0c\n\x04type\x18\x01 \x01(\t\x12.\n\x0f\x63olumn_vindexes\x18\x02 \x03(\x0b\x32\x15.vschema.ColumnVindex\x12.\n\x0e\x61uto_increment\x18\x03 \x01(\x0b\x32\x16.vschema.AutoIncrement\x12 \n\x07\x63olumns\x18\x04 \x03(\x0b\x32\x0f.vschema.Column\x12\x0e\n\x06pinned\x18\x05 \x01(\t\x12!\n\x19\x63olumn_list_authoritative\x18\x06 \x01(\x08\"=\n\x0c\x43olumnVindex\x12\x0e\n\x06\x63olumn\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07\x63olumns\x18\x03 \x03(\t\"1\n\rAutoIncrement\x12\x0e\n\x06\x63olumn\x18\x01 \x01(\t\x12\x10\n\x08sequence\x18\x02 \x01(\t\"1\n\x06\x43olumn\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x04type\x18\x02 \x01(\x0e\x32\x0b.query.Type\"\xb6\x01\n\nSrvVSchema\x12\x35\n\tkeyspaces\x18\x01 \x03(\x0b\x32\".vschema.SrvVSchema.KeyspacesEntry\x12,\n\rrouting_rules\x18\x02 \x01(\x0b\x32\x15.vschema.RoutingRules\x1a\x43\n\x0eKeyspacesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.vschema.Keyspace:\x02\x38\x01\x42&Z$vitess.io/vitess/go/vt/proto/vschemab\x06proto3') - , - dependencies=[query__pb2.DESCRIPTOR,]) - - - - -_ROUTINGRULES = _descriptor.Descriptor( - name='RoutingRules', - full_name='vschema.RoutingRules', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='rules', full_name='vschema.RoutingRules.rules', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=39, - serialized_end=90, -) - - -_ROUTINGRULE = _descriptor.Descriptor( - name='RoutingRule', - full_name='vschema.RoutingRule', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='from_table', full_name='vschema.RoutingRule.from_table', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='to_tables', full_name='vschema.RoutingRule.to_tables', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=92, - serialized_end=144, -) - - -_KEYSPACE_VINDEXESENTRY = _descriptor.Descriptor( - name='VindexesEntry', - full_name='vschema.Keyspace.VindexesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='vschema.Keyspace.VindexesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='vschema.Keyspace.VindexesEntry.value', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=308, - serialized_end=372, -) - -_KEYSPACE_TABLESENTRY = _descriptor.Descriptor( - name='TablesEntry', - full_name='vschema.Keyspace.TablesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='vschema.Keyspace.TablesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='vschema.Keyspace.TablesEntry.value', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=374, - serialized_end=435, -) - -_KEYSPACE = _descriptor.Descriptor( - name='Keyspace', - full_name='vschema.Keyspace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='sharded', full_name='vschema.Keyspace.sharded', index=0, - number=1, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='vindexes', full_name='vschema.Keyspace.vindexes', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tables', full_name='vschema.Keyspace.tables', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='require_explicit_routing', full_name='vschema.Keyspace.require_explicit_routing', 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_KEYSPACE_VINDEXESENTRY, _KEYSPACE_TABLESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=147, - serialized_end=435, -) - - -_VINDEX_PARAMSENTRY = _descriptor.Descriptor( - name='ParamsEntry', - full_name='vschema.Vindex.ParamsEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='vschema.Vindex.ParamsEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='vschema.Vindex.ParamsEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=522, - serialized_end=567, -) - -_VINDEX = _descriptor.Descriptor( - name='Vindex', - full_name='vschema.Vindex', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='vschema.Vindex.type', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='params', full_name='vschema.Vindex.params', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='owner', full_name='vschema.Vindex.owner', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_VINDEX_PARAMSENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=438, - serialized_end=567, -) - - -_TABLE = _descriptor.Descriptor( - name='Table', - full_name='vschema.Table', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='vschema.Table.type', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='column_vindexes', full_name='vschema.Table.column_vindexes', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='auto_increment', full_name='vschema.Table.auto_increment', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='columns', full_name='vschema.Table.columns', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='pinned', full_name='vschema.Table.pinned', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='column_list_authoritative', full_name='vschema.Table.column_list_authoritative', index=5, - number=6, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=570, - serialized_end=772, -) - - -_COLUMNVINDEX = _descriptor.Descriptor( - name='ColumnVindex', - full_name='vschema.ColumnVindex', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='column', full_name='vschema.ColumnVindex.column', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='vschema.ColumnVindex.name', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='columns', full_name='vschema.ColumnVindex.columns', index=2, - number=3, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=774, - serialized_end=835, -) - - -_AUTOINCREMENT = _descriptor.Descriptor( - name='AutoIncrement', - full_name='vschema.AutoIncrement', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='column', full_name='vschema.AutoIncrement.column', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sequence', full_name='vschema.AutoIncrement.sequence', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=837, - serialized_end=886, -) - - -_COLUMN = _descriptor.Descriptor( - name='Column', - full_name='vschema.Column', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='vschema.Column.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='type', full_name='vschema.Column.type', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=888, - serialized_end=937, -) - - -_SRVVSCHEMA_KEYSPACESENTRY = _descriptor.Descriptor( - name='KeyspacesEntry', - full_name='vschema.SrvVSchema.KeyspacesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='vschema.SrvVSchema.KeyspacesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='vschema.SrvVSchema.KeyspacesEntry.value', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1055, - serialized_end=1122, -) - -_SRVVSCHEMA = _descriptor.Descriptor( - name='SrvVSchema', - full_name='vschema.SrvVSchema', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspaces', full_name='vschema.SrvVSchema.keyspaces', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='routing_rules', full_name='vschema.SrvVSchema.routing_rules', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_SRVVSCHEMA_KEYSPACESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=940, - serialized_end=1122, -) - -_ROUTINGRULES.fields_by_name['rules'].message_type = _ROUTINGRULE -_KEYSPACE_VINDEXESENTRY.fields_by_name['value'].message_type = _VINDEX -_KEYSPACE_VINDEXESENTRY.containing_type = _KEYSPACE -_KEYSPACE_TABLESENTRY.fields_by_name['value'].message_type = _TABLE -_KEYSPACE_TABLESENTRY.containing_type = _KEYSPACE -_KEYSPACE.fields_by_name['vindexes'].message_type = _KEYSPACE_VINDEXESENTRY -_KEYSPACE.fields_by_name['tables'].message_type = _KEYSPACE_TABLESENTRY -_VINDEX_PARAMSENTRY.containing_type = _VINDEX -_VINDEX.fields_by_name['params'].message_type = _VINDEX_PARAMSENTRY -_TABLE.fields_by_name['column_vindexes'].message_type = _COLUMNVINDEX -_TABLE.fields_by_name['auto_increment'].message_type = _AUTOINCREMENT -_TABLE.fields_by_name['columns'].message_type = _COLUMN -_COLUMN.fields_by_name['type'].enum_type = query__pb2._TYPE -_SRVVSCHEMA_KEYSPACESENTRY.fields_by_name['value'].message_type = _KEYSPACE -_SRVVSCHEMA_KEYSPACESENTRY.containing_type = _SRVVSCHEMA -_SRVVSCHEMA.fields_by_name['keyspaces'].message_type = _SRVVSCHEMA_KEYSPACESENTRY -_SRVVSCHEMA.fields_by_name['routing_rules'].message_type = _ROUTINGRULES -DESCRIPTOR.message_types_by_name['RoutingRules'] = _ROUTINGRULES -DESCRIPTOR.message_types_by_name['RoutingRule'] = _ROUTINGRULE -DESCRIPTOR.message_types_by_name['Keyspace'] = _KEYSPACE -DESCRIPTOR.message_types_by_name['Vindex'] = _VINDEX -DESCRIPTOR.message_types_by_name['Table'] = _TABLE -DESCRIPTOR.message_types_by_name['ColumnVindex'] = _COLUMNVINDEX -DESCRIPTOR.message_types_by_name['AutoIncrement'] = _AUTOINCREMENT -DESCRIPTOR.message_types_by_name['Column'] = _COLUMN -DESCRIPTOR.message_types_by_name['SrvVSchema'] = _SRVVSCHEMA -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -RoutingRules = _reflection.GeneratedProtocolMessageType('RoutingRules', (_message.Message,), dict( - DESCRIPTOR = _ROUTINGRULES, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.RoutingRules) - )) -_sym_db.RegisterMessage(RoutingRules) - -RoutingRule = _reflection.GeneratedProtocolMessageType('RoutingRule', (_message.Message,), dict( - DESCRIPTOR = _ROUTINGRULE, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.RoutingRule) - )) -_sym_db.RegisterMessage(RoutingRule) - -Keyspace = _reflection.GeneratedProtocolMessageType('Keyspace', (_message.Message,), dict( - - VindexesEntry = _reflection.GeneratedProtocolMessageType('VindexesEntry', (_message.Message,), dict( - DESCRIPTOR = _KEYSPACE_VINDEXESENTRY, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.Keyspace.VindexesEntry) - )) - , - - TablesEntry = _reflection.GeneratedProtocolMessageType('TablesEntry', (_message.Message,), dict( - DESCRIPTOR = _KEYSPACE_TABLESENTRY, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.Keyspace.TablesEntry) - )) - , - DESCRIPTOR = _KEYSPACE, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.Keyspace) - )) -_sym_db.RegisterMessage(Keyspace) -_sym_db.RegisterMessage(Keyspace.VindexesEntry) -_sym_db.RegisterMessage(Keyspace.TablesEntry) - -Vindex = _reflection.GeneratedProtocolMessageType('Vindex', (_message.Message,), dict( - - ParamsEntry = _reflection.GeneratedProtocolMessageType('ParamsEntry', (_message.Message,), dict( - DESCRIPTOR = _VINDEX_PARAMSENTRY, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.Vindex.ParamsEntry) - )) - , - DESCRIPTOR = _VINDEX, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.Vindex) - )) -_sym_db.RegisterMessage(Vindex) -_sym_db.RegisterMessage(Vindex.ParamsEntry) - -Table = _reflection.GeneratedProtocolMessageType('Table', (_message.Message,), dict( - DESCRIPTOR = _TABLE, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.Table) - )) -_sym_db.RegisterMessage(Table) - -ColumnVindex = _reflection.GeneratedProtocolMessageType('ColumnVindex', (_message.Message,), dict( - DESCRIPTOR = _COLUMNVINDEX, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.ColumnVindex) - )) -_sym_db.RegisterMessage(ColumnVindex) - -AutoIncrement = _reflection.GeneratedProtocolMessageType('AutoIncrement', (_message.Message,), dict( - DESCRIPTOR = _AUTOINCREMENT, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.AutoIncrement) - )) -_sym_db.RegisterMessage(AutoIncrement) - -Column = _reflection.GeneratedProtocolMessageType('Column', (_message.Message,), dict( - DESCRIPTOR = _COLUMN, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.Column) - )) -_sym_db.RegisterMessage(Column) - -SrvVSchema = _reflection.GeneratedProtocolMessageType('SrvVSchema', (_message.Message,), dict( - - KeyspacesEntry = _reflection.GeneratedProtocolMessageType('KeyspacesEntry', (_message.Message,), dict( - DESCRIPTOR = _SRVVSCHEMA_KEYSPACESENTRY, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.SrvVSchema.KeyspacesEntry) - )) - , - DESCRIPTOR = _SRVVSCHEMA, - __module__ = 'vschema_pb2' - # @@protoc_insertion_point(class_scope:vschema.SrvVSchema) - )) -_sym_db.RegisterMessage(SrvVSchema) -_sym_db.RegisterMessage(SrvVSchema.KeyspacesEntry) - - -DESCRIPTOR._options = None -_KEYSPACE_VINDEXESENTRY._options = None -_KEYSPACE_TABLESENTRY._options = None -_VINDEX_PARAMSENTRY._options = None -_SRVVSCHEMA_KEYSPACESENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vschema_pb2_grpc.py b/py/vtproto/vschema_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vschema_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vtctldata_pb2.py b/py/vtproto/vtctldata_pb2.py deleted file mode 100644 index 1af2dc47f3b..00000000000 --- a/py/vtproto/vtctldata_pb2.py +++ /dev/null @@ -1,254 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vtctldata.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import logutil_pb2 as logutil__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vtctldata.proto', - package='vtctldata', - syntax='proto3', - serialized_options=_b('Z&vitess.io/vitess/go/vt/proto/vtctldata'), - serialized_pb=_b('\n\x0fvtctldata.proto\x12\tvtctldata\x1a\rlogutil.proto\"B\n\x1a\x45xecuteVtctlCommandRequest\x12\x0c\n\x04\x61rgs\x18\x01 \x03(\t\x12\x16\n\x0e\x61\x63tion_timeout\x18\x02 \x01(\x03\"<\n\x1b\x45xecuteVtctlCommandResponse\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.logutil.Event\"_\n\x18TableMaterializeSettings\x12\x14\n\x0ctarget_table\x18\x01 \x01(\t\x12\x19\n\x11source_expression\x18\x02 \x01(\t\x12\x12\n\ncreate_ddl\x18\x03 \x01(\t\"\xd3\x01\n\x13MaterializeSettings\x12\x10\n\x08workflow\x18\x01 \x01(\t\x12\x17\n\x0fsource_keyspace\x18\x02 \x01(\t\x12\x17\n\x0ftarget_keyspace\x18\x03 \x01(\t\x12\x17\n\x0fstop_after_copy\x18\x04 \x01(\x08\x12;\n\x0etable_settings\x18\x05 \x03(\x0b\x32#.vtctldata.TableMaterializeSettings\x12\x0c\n\x04\x63\x65ll\x18\x06 \x01(\t\x12\x14\n\x0ctablet_types\x18\x07 \x01(\tB(Z&vitess.io/vitess/go/vt/proto/vtctldatab\x06proto3') - , - dependencies=[logutil__pb2.DESCRIPTOR,]) - - - - -_EXECUTEVTCTLCOMMANDREQUEST = _descriptor.Descriptor( - name='ExecuteVtctlCommandRequest', - full_name='vtctldata.ExecuteVtctlCommandRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='args', full_name='vtctldata.ExecuteVtctlCommandRequest.args', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='action_timeout', full_name='vtctldata.ExecuteVtctlCommandRequest.action_timeout', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=45, - serialized_end=111, -) - - -_EXECUTEVTCTLCOMMANDRESPONSE = _descriptor.Descriptor( - name='ExecuteVtctlCommandResponse', - full_name='vtctldata.ExecuteVtctlCommandResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='event', full_name='vtctldata.ExecuteVtctlCommandResponse.event', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=113, - serialized_end=173, -) - - -_TABLEMATERIALIZESETTINGS = _descriptor.Descriptor( - name='TableMaterializeSettings', - full_name='vtctldata.TableMaterializeSettings', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='target_table', full_name='vtctldata.TableMaterializeSettings.target_table', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_expression', full_name='vtctldata.TableMaterializeSettings.source_expression', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='create_ddl', full_name='vtctldata.TableMaterializeSettings.create_ddl', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=175, - serialized_end=270, -) - - -_MATERIALIZESETTINGS = _descriptor.Descriptor( - name='MaterializeSettings', - full_name='vtctldata.MaterializeSettings', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='workflow', full_name='vtctldata.MaterializeSettings.workflow', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='source_keyspace', full_name='vtctldata.MaterializeSettings.source_keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target_keyspace', full_name='vtctldata.MaterializeSettings.target_keyspace', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='stop_after_copy', full_name='vtctldata.MaterializeSettings.stop_after_copy', 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='table_settings', full_name='vtctldata.MaterializeSettings.table_settings', index=4, - number=5, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cell', full_name='vtctldata.MaterializeSettings.cell', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_types', full_name='vtctldata.MaterializeSettings.tablet_types', index=6, - number=7, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=273, - serialized_end=484, -) - -_EXECUTEVTCTLCOMMANDRESPONSE.fields_by_name['event'].message_type = logutil__pb2._EVENT -_MATERIALIZESETTINGS.fields_by_name['table_settings'].message_type = _TABLEMATERIALIZESETTINGS -DESCRIPTOR.message_types_by_name['ExecuteVtctlCommandRequest'] = _EXECUTEVTCTLCOMMANDREQUEST -DESCRIPTOR.message_types_by_name['ExecuteVtctlCommandResponse'] = _EXECUTEVTCTLCOMMANDRESPONSE -DESCRIPTOR.message_types_by_name['TableMaterializeSettings'] = _TABLEMATERIALIZESETTINGS -DESCRIPTOR.message_types_by_name['MaterializeSettings'] = _MATERIALIZESETTINGS -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ExecuteVtctlCommandRequest = _reflection.GeneratedProtocolMessageType('ExecuteVtctlCommandRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEVTCTLCOMMANDREQUEST, - __module__ = 'vtctldata_pb2' - # @@protoc_insertion_point(class_scope:vtctldata.ExecuteVtctlCommandRequest) - )) -_sym_db.RegisterMessage(ExecuteVtctlCommandRequest) - -ExecuteVtctlCommandResponse = _reflection.GeneratedProtocolMessageType('ExecuteVtctlCommandResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEVTCTLCOMMANDRESPONSE, - __module__ = 'vtctldata_pb2' - # @@protoc_insertion_point(class_scope:vtctldata.ExecuteVtctlCommandResponse) - )) -_sym_db.RegisterMessage(ExecuteVtctlCommandResponse) - -TableMaterializeSettings = _reflection.GeneratedProtocolMessageType('TableMaterializeSettings', (_message.Message,), dict( - DESCRIPTOR = _TABLEMATERIALIZESETTINGS, - __module__ = 'vtctldata_pb2' - # @@protoc_insertion_point(class_scope:vtctldata.TableMaterializeSettings) - )) -_sym_db.RegisterMessage(TableMaterializeSettings) - -MaterializeSettings = _reflection.GeneratedProtocolMessageType('MaterializeSettings', (_message.Message,), dict( - DESCRIPTOR = _MATERIALIZESETTINGS, - __module__ = 'vtctldata_pb2' - # @@protoc_insertion_point(class_scope:vtctldata.MaterializeSettings) - )) -_sym_db.RegisterMessage(MaterializeSettings) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vtctldata_pb2_grpc.py b/py/vtproto/vtctldata_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vtctldata_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vtctlservice_pb2.py b/py/vtproto/vtctlservice_pb2.py deleted file mode 100644 index 5aa94a82ba0..00000000000 --- a/py/vtproto/vtctlservice_pb2.py +++ /dev/null @@ -1,57 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vtctlservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import vtctldata_pb2 as vtctldata__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vtctlservice.proto', - package='vtctlservice', - syntax='proto3', - serialized_options=_b('Z)vitess.io/vitess/go/vt/proto/vtctlservice'), - serialized_pb=_b('\n\x12vtctlservice.proto\x12\x0cvtctlservice\x1a\x0fvtctldata.proto2q\n\x05Vtctl\x12h\n\x13\x45xecuteVtctlCommand\x12%.vtctldata.ExecuteVtctlCommandRequest\x1a&.vtctldata.ExecuteVtctlCommandResponse\"\x00\x30\x01\x42+Z)vitess.io/vitess/go/vt/proto/vtctlserviceb\x06proto3') - , - dependencies=[vtctldata__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_VTCTL = _descriptor.ServiceDescriptor( - name='Vtctl', - full_name='vtctlservice.Vtctl', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=53, - serialized_end=166, - methods=[ - _descriptor.MethodDescriptor( - name='ExecuteVtctlCommand', - full_name='vtctlservice.Vtctl.ExecuteVtctlCommand', - index=0, - containing_service=None, - input_type=vtctldata__pb2._EXECUTEVTCTLCOMMANDREQUEST, - output_type=vtctldata__pb2._EXECUTEVTCTLCOMMANDRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_VTCTL) - -DESCRIPTOR.services_by_name['Vtctl'] = _VTCTL - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vtctlservice_pb2_grpc.py b/py/vtproto/vtctlservice_pb2_grpc.py deleted file mode 100644 index 83891857ed8..00000000000 --- a/py/vtproto/vtctlservice_pb2_grpc.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import vtctldata_pb2 as vtctldata__pb2 - - -class VtctlStub(object): - """Service Vtctl allows you to call vt commands through gRPC. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.ExecuteVtctlCommand = channel.unary_stream( - '/vtctlservice.Vtctl/ExecuteVtctlCommand', - request_serializer=vtctldata__pb2.ExecuteVtctlCommandRequest.SerializeToString, - response_deserializer=vtctldata__pb2.ExecuteVtctlCommandResponse.FromString, - ) - - -class VtctlServicer(object): - """Service Vtctl allows you to call vt commands through gRPC. - """ - - def ExecuteVtctlCommand(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_VtctlServicer_to_server(servicer, server): - rpc_method_handlers = { - 'ExecuteVtctlCommand': grpc.unary_stream_rpc_method_handler( - servicer.ExecuteVtctlCommand, - request_deserializer=vtctldata__pb2.ExecuteVtctlCommandRequest.FromString, - response_serializer=vtctldata__pb2.ExecuteVtctlCommandResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'vtctlservice.Vtctl', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/vtgate_pb2.py b/py/vtproto/vtgate_pb2.py deleted file mode 100644 index b438b2afd4f..00000000000 --- a/py/vtproto/vtgate_pb2.py +++ /dev/null @@ -1,3241 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vtgate.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import binlogdata_pb2 as binlogdata__pb2 -import query_pb2 as query__pb2 -import topodata_pb2 as topodata__pb2 -import vtrpc_pb2 as vtrpc__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vtgate.proto', - package='vtgate', - syntax='proto3', - serialized_options=_b('\n\017io.vitess.protoZ#vitess.io/vitess/go/vt/proto/vtgate'), - serialized_pb=_b('\n\x0cvtgate.proto\x12\x06vtgate\x1a\x10\x62inlogdata.proto\x1a\x0bquery.proto\x1a\x0etopodata.proto\x1a\x0bvtrpc.proto\"\xdf\x03\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\x12\x15\n\rtarget_string\x18\x05 \x01(\t\x12&\n\x07options\x18\x06 \x01(\x0b\x32\x15.query.ExecuteOptions\x12\x31\n\x10transaction_mode\x18\x07 \x01(\x0e\x32\x17.vtgate.TransactionMode\x12%\n\x08warnings\x18\x08 \x03(\x0b\x32\x13.query.QueryWarning\x12\x32\n\x0cpre_sessions\x18\t \x03(\x0b\x32\x1c.vtgate.Session.ShardSession\x12\x33\n\rpost_sessions\x18\n \x03(\x0b\x32\x1c.vtgate.Session.ShardSession\x12\x16\n\x0elast_insert_id\x18\x0b \x01(\x04\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\"\xe9\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\x12 \n\x07session\x18\x06 \x01(\x0b\x32\x0f.vtgate.Session\";\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\"=\n\x0cIdKeyspaceId\x12\x18\n\x02id\x18\x01 \x01(\x0b\x32\x0c.query.Value\x12\x13\n\x0bkeyspace_id\x18\x02 \x01(\x0c\"\x91\x01\n\x1cMessageAckKeyspaceIdsRequest\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-\n\x0fid_keyspace_ids\x18\x04 \x03(\x0b\x32\x14.vtgate.IdKeyspaceId\"\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\"\xa5\x01\n\x0eVStreamRequest\x12\"\n\tcaller_id\x18\x01 \x01(\x0b\x32\x0f.vtrpc.CallerID\x12)\n\x0btablet_type\x18\x02 \x01(\x0e\x32\x14.topodata.TabletType\x12 \n\x05vgtid\x18\x03 \x01(\x0b\x32\x11.binlogdata.VGtid\x12\"\n\x06\x66ilter\x18\x04 \x01(\x0b\x32\x12.binlogdata.Filter\"5\n\x0fVStreamResponse\x12\"\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x12.binlogdata.VEvent\"\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*D\n\x0fTransactionMode\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\n\n\x06SINGLE\x10\x01\x12\t\n\x05MULTI\x10\x02\x12\t\n\x05TWOPC\x10\x03*<\n\x0b\x43ommitOrder\x12\n\n\x06NORMAL\x10\x00\x12\x07\n\x03PRE\x10\x01\x12\x08\n\x04POST\x10\x02\x12\x0e\n\nAUTOCOMMIT\x10\x03\x42\x36\n\x0fio.vitess.protoZ#vitess.io/vitess/go/vt/proto/vtgateb\x06proto3') - , - dependencies=[binlogdata__pb2.DESCRIPTOR,query__pb2.DESCRIPTOR,topodata__pb2.DESCRIPTOR,vtrpc__pb2.DESCRIPTOR,]) - -_TRANSACTIONMODE = _descriptor.EnumDescriptor( - name='TransactionMode', - full_name='vtgate.TransactionMode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='UNSPECIFIED', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SINGLE', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MULTI', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TWOPC', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=7546, - serialized_end=7614, -) -_sym_db.RegisterEnumDescriptor(_TRANSACTIONMODE) - -TransactionMode = enum_type_wrapper.EnumTypeWrapper(_TRANSACTIONMODE) -_COMMITORDER = _descriptor.EnumDescriptor( - name='CommitOrder', - full_name='vtgate.CommitOrder', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NORMAL', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PRE', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='POST', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='AUTOCOMMIT', index=3, number=3, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=7616, - serialized_end=7676, -) -_sym_db.RegisterEnumDescriptor(_COMMITORDER) - -CommitOrder = enum_type_wrapper.EnumTypeWrapper(_COMMITORDER) -UNSPECIFIED = 0 -SINGLE = 1 -MULTI = 2 -TWOPC = 3 -NORMAL = 0 -PRE = 1 -POST = 2 -AUTOCOMMIT = 3 - - - -_SESSION_SHARDSESSION = _descriptor.Descriptor( - name='ShardSession', - full_name='vtgate.Session.ShardSession', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='target', full_name='vtgate.Session.ShardSession.target', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_id', full_name='vtgate.Session.ShardSession.transaction_id', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=495, - serialized_end=564, -) - -_SESSION = _descriptor.Descriptor( - name='Session', - full_name='vtgate.Session', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='in_transaction', full_name='vtgate.Session.in_transaction', index=0, - number=1, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard_sessions', full_name='vtgate.Session.shard_sessions', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='single_db', full_name='vtgate.Session.single_db', index=2, - number=3, 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, - serialized_options=None, file=DESCRIPTOR), - _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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='target_string', full_name='vtgate.Session.target_string', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.Session.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transaction_mode', full_name='vtgate.Session.transaction_mode', index=6, - number=7, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='warnings', full_name='vtgate.Session.warnings', index=7, - number=8, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='pre_sessions', full_name='vtgate.Session.pre_sessions', index=8, - number=9, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='post_sessions', full_name='vtgate.Session.post_sessions', index=9, - number=10, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='last_insert_id', full_name='vtgate.Session.last_insert_id', index=10, - number=11, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_SESSION_SHARDSESSION, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=85, - serialized_end=564, -) - - -_EXECUTEREQUEST = _descriptor.Descriptor( - name='ExecuteRequest', - full_name='vtgate.ExecuteRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.ExecuteRequest.query', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteRequest.tablet_type', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='not_in_transaction', full_name='vtgate.ExecuteRequest.not_in_transaction', index=4, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteRequest.options', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=567, - serialized_end=822, -) - - -_EXECUTERESPONSE = _descriptor.Descriptor( - name='ExecuteResponse', - full_name='vtgate.ExecuteResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.ExecuteResponse.result', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=824, - serialized_end=943, -) - - -_EXECUTESHARDSREQUEST = _descriptor.Descriptor( - name='ExecuteShardsRequest', - full_name='vtgate.ExecuteShardsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteShardsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteShardsRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.ExecuteShardsRequest.query', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.ExecuteShardsRequest.keyspace', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shards', full_name='vtgate.ExecuteShardsRequest.shards', index=4, - number=5, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteShardsRequest.tablet_type', index=5, - number=6, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='not_in_transaction', full_name='vtgate.ExecuteShardsRequest.not_in_transaction', index=6, - number=7, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteShardsRequest.options', index=7, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=946, - serialized_end=1217, -) - - -_EXECUTESHARDSRESPONSE = _descriptor.Descriptor( - name='ExecuteShardsResponse', - full_name='vtgate.ExecuteShardsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteShardsResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteShardsResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.ExecuteShardsResponse.result', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1219, - serialized_end=1344, -) - - -_EXECUTEKEYSPACEIDSREQUEST = _descriptor.Descriptor( - name='ExecuteKeyspaceIdsRequest', - full_name='vtgate.ExecuteKeyspaceIdsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteKeyspaceIdsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteKeyspaceIdsRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.ExecuteKeyspaceIdsRequest.query', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.ExecuteKeyspaceIdsRequest.keyspace', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace_ids', full_name='vtgate.ExecuteKeyspaceIdsRequest.keyspace_ids', index=4, - number=5, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteKeyspaceIdsRequest.tablet_type', index=5, - number=6, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='not_in_transaction', full_name='vtgate.ExecuteKeyspaceIdsRequest.not_in_transaction', index=6, - number=7, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteKeyspaceIdsRequest.options', index=7, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1347, - serialized_end=1629, -) - - -_EXECUTEKEYSPACEIDSRESPONSE = _descriptor.Descriptor( - name='ExecuteKeyspaceIdsResponse', - full_name='vtgate.ExecuteKeyspaceIdsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteKeyspaceIdsResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteKeyspaceIdsResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.ExecuteKeyspaceIdsResponse.result', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1632, - serialized_end=1762, -) - - -_EXECUTEKEYRANGESREQUEST = _descriptor.Descriptor( - name='ExecuteKeyRangesRequest', - full_name='vtgate.ExecuteKeyRangesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteKeyRangesRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteKeyRangesRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.ExecuteKeyRangesRequest.query', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.ExecuteKeyRangesRequest.keyspace', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_ranges', full_name='vtgate.ExecuteKeyRangesRequest.key_ranges', index=4, - number=5, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteKeyRangesRequest.tablet_type', index=5, - number=6, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='not_in_transaction', full_name='vtgate.ExecuteKeyRangesRequest.not_in_transaction', index=6, - number=7, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteKeyRangesRequest.options', index=7, - number=8, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1765, - serialized_end=2063, -) - - -_EXECUTEKEYRANGESRESPONSE = _descriptor.Descriptor( - name='ExecuteKeyRangesResponse', - full_name='vtgate.ExecuteKeyRangesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteKeyRangesResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteKeyRangesResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.ExecuteKeyRangesResponse.result', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2066, - serialized_end=2194, -) - - -_EXECUTEENTITYIDSREQUEST_ENTITYID = _descriptor.Descriptor( - name='EntityId', - full_name='vtgate.ExecuteEntityIdsRequest.EntityId', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='type', full_name='vtgate.ExecuteEntityIdsRequest.EntityId.type', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='vtgate.ExecuteEntityIdsRequest.EntityId.value', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace_id', full_name='vtgate.ExecuteEntityIdsRequest.EntityId.keyspace_id', index=2, - number=3, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2556, - serialized_end=2629, -) - -_EXECUTEENTITYIDSREQUEST = _descriptor.Descriptor( - name='ExecuteEntityIdsRequest', - full_name='vtgate.ExecuteEntityIdsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteEntityIdsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteEntityIdsRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.ExecuteEntityIdsRequest.query', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.ExecuteEntityIdsRequest.keyspace', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='entity_column_name', full_name='vtgate.ExecuteEntityIdsRequest.entity_column_name', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='entity_keyspace_ids', full_name='vtgate.ExecuteEntityIdsRequest.entity_keyspace_ids', index=5, - number=6, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteEntityIdsRequest.tablet_type', index=6, - number=7, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='not_in_transaction', full_name='vtgate.ExecuteEntityIdsRequest.not_in_transaction', index=7, - number=8, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteEntityIdsRequest.options', index=8, - number=9, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_EXECUTEENTITYIDSREQUEST_ENTITYID, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2197, - serialized_end=2629, -) - - -_EXECUTEENTITYIDSRESPONSE = _descriptor.Descriptor( - name='ExecuteEntityIdsResponse', - full_name='vtgate.ExecuteEntityIdsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteEntityIdsResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteEntityIdsResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.ExecuteEntityIdsResponse.result', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2632, - serialized_end=2760, -) - - -_EXECUTEBATCHREQUEST = _descriptor.Descriptor( - name='ExecuteBatchRequest', - full_name='vtgate.ExecuteBatchRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteBatchRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteBatchRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='queries', full_name='vtgate.ExecuteBatchRequest.queries', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteBatchRequest.tablet_type', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='as_transaction', full_name='vtgate.ExecuteBatchRequest.as_transaction', index=4, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteBatchRequest.options', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2763, - serialized_end=3021, -) - - -_EXECUTEBATCHRESPONSE = _descriptor.Descriptor( - name='ExecuteBatchResponse', - full_name='vtgate.ExecuteBatchResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteBatchResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteBatchResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='results', full_name='vtgate.ExecuteBatchResponse.results', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3024, - serialized_end=3153, -) - - -_BOUNDSHARDQUERY = _descriptor.Descriptor( - name='BoundShardQuery', - full_name='vtgate.BoundShardQuery', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.BoundShardQuery.query', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.BoundShardQuery.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shards', full_name='vtgate.BoundShardQuery.shards', index=2, - number=3, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3155, - serialized_end=3240, -) - - -_EXECUTEBATCHSHARDSREQUEST = _descriptor.Descriptor( - name='ExecuteBatchShardsRequest', - full_name='vtgate.ExecuteBatchShardsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteBatchShardsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteBatchShardsRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='queries', full_name='vtgate.ExecuteBatchShardsRequest.queries', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteBatchShardsRequest.tablet_type', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='as_transaction', full_name='vtgate.ExecuteBatchShardsRequest.as_transaction', index=4, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteBatchShardsRequest.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3243, - serialized_end=3489, -) - - -_EXECUTEBATCHSHARDSRESPONSE = _descriptor.Descriptor( - name='ExecuteBatchShardsResponse', - full_name='vtgate.ExecuteBatchShardsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteBatchShardsResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteBatchShardsResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='results', full_name='vtgate.ExecuteBatchShardsResponse.results', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3492, - serialized_end=3623, -) - - -_BOUNDKEYSPACEIDQUERY = _descriptor.Descriptor( - name='BoundKeyspaceIdQuery', - full_name='vtgate.BoundKeyspaceIdQuery', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.BoundKeyspaceIdQuery.query', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.BoundKeyspaceIdQuery.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace_ids', full_name='vtgate.BoundKeyspaceIdQuery.keyspace_ids', index=2, - number=3, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3625, - serialized_end=3721, -) - - -_EXECUTEBATCHKEYSPACEIDSREQUEST = _descriptor.Descriptor( - name='ExecuteBatchKeyspaceIdsRequest', - full_name='vtgate.ExecuteBatchKeyspaceIdsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ExecuteBatchKeyspaceIdsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteBatchKeyspaceIdsRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='queries', full_name='vtgate.ExecuteBatchKeyspaceIdsRequest.queries', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.ExecuteBatchKeyspaceIdsRequest.tablet_type', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='as_transaction', full_name='vtgate.ExecuteBatchKeyspaceIdsRequest.as_transaction', index=4, - number=5, 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, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.ExecuteBatchKeyspaceIdsRequest.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3724, - serialized_end=3980, -) - - -_EXECUTEBATCHKEYSPACEIDSRESPONSE = _descriptor.Descriptor( - name='ExecuteBatchKeyspaceIdsResponse', - full_name='vtgate.ExecuteBatchKeyspaceIdsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='error', full_name='vtgate.ExecuteBatchKeyspaceIdsResponse.error', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.ExecuteBatchKeyspaceIdsResponse.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='results', full_name='vtgate.ExecuteBatchKeyspaceIdsResponse.results', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3983, - serialized_end=4119, -) - - -_STREAMEXECUTEREQUEST = _descriptor.Descriptor( - name='StreamExecuteRequest', - full_name='vtgate.StreamExecuteRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.StreamExecuteRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.StreamExecuteRequest.query', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.StreamExecuteRequest.tablet_type', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.StreamExecuteRequest.options', index=4, - number=5, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.StreamExecuteRequest.session', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4122, - serialized_end=4355, -) - - -_STREAMEXECUTERESPONSE = _descriptor.Descriptor( - name='StreamExecuteResponse', - full_name='vtgate.StreamExecuteResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.StreamExecuteResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4357, - serialized_end=4416, -) - - -_STREAMEXECUTESHARDSREQUEST = _descriptor.Descriptor( - name='StreamExecuteShardsRequest', - full_name='vtgate.StreamExecuteShardsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.StreamExecuteShardsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.StreamExecuteShardsRequest.query', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.StreamExecuteShardsRequest.keyspace', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shards', full_name='vtgate.StreamExecuteShardsRequest.shards', index=3, - number=4, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.StreamExecuteShardsRequest.tablet_type', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.StreamExecuteShardsRequest.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4419, - serialized_end=4634, -) - - -_STREAMEXECUTESHARDSRESPONSE = _descriptor.Descriptor( - name='StreamExecuteShardsResponse', - full_name='vtgate.StreamExecuteShardsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.StreamExecuteShardsResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4636, - serialized_end=4701, -) - - -_STREAMEXECUTEKEYSPACEIDSREQUEST = _descriptor.Descriptor( - name='StreamExecuteKeyspaceIdsRequest', - full_name='vtgate.StreamExecuteKeyspaceIdsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.StreamExecuteKeyspaceIdsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.StreamExecuteKeyspaceIdsRequest.query', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.StreamExecuteKeyspaceIdsRequest.keyspace', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace_ids', full_name='vtgate.StreamExecuteKeyspaceIdsRequest.keyspace_ids', index=3, - number=4, type=12, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.StreamExecuteKeyspaceIdsRequest.tablet_type', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.StreamExecuteKeyspaceIdsRequest.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4704, - serialized_end=4930, -) - - -_STREAMEXECUTEKEYSPACEIDSRESPONSE = _descriptor.Descriptor( - name='StreamExecuteKeyspaceIdsResponse', - full_name='vtgate.StreamExecuteKeyspaceIdsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.StreamExecuteKeyspaceIdsResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=4932, - serialized_end=5002, -) - - -_STREAMEXECUTEKEYRANGESREQUEST = _descriptor.Descriptor( - name='StreamExecuteKeyRangesRequest', - full_name='vtgate.StreamExecuteKeyRangesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.StreamExecuteKeyRangesRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.StreamExecuteKeyRangesRequest.query', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.StreamExecuteKeyRangesRequest.keyspace', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_ranges', full_name='vtgate.StreamExecuteKeyRangesRequest.key_ranges', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.StreamExecuteKeyRangesRequest.tablet_type', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='options', full_name='vtgate.StreamExecuteKeyRangesRequest.options', index=5, - number=6, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5005, - serialized_end=5247, -) - - -_STREAMEXECUTEKEYRANGESRESPONSE = _descriptor.Descriptor( - name='StreamExecuteKeyRangesResponse', - full_name='vtgate.StreamExecuteKeyRangesResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='result', full_name='vtgate.StreamExecuteKeyRangesResponse.result', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5249, - serialized_end=5317, -) - - -_BEGINREQUEST = _descriptor.Descriptor( - name='BeginRequest', - full_name='vtgate.BeginRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.BeginRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='single_db', full_name='vtgate.BeginRequest.single_db', index=1, - number=2, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5319, - serialized_end=5388, -) - - -_BEGINRESPONSE = _descriptor.Descriptor( - name='BeginResponse', - full_name='vtgate.BeginResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.BeginResponse.session', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5390, - serialized_end=5439, -) - - -_COMMITREQUEST = _descriptor.Descriptor( - name='CommitRequest', - full_name='vtgate.CommitRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.CommitRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.CommitRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='atomic', full_name='vtgate.CommitRequest.atomic', index=2, - number=3, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5441, - serialized_end=5542, -) - - -_COMMITRESPONSE = _descriptor.Descriptor( - name='CommitResponse', - full_name='vtgate.CommitResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5544, - serialized_end=5560, -) - - -_ROLLBACKREQUEST = _descriptor.Descriptor( - name='RollbackRequest', - full_name='vtgate.RollbackRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.RollbackRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='session', full_name='vtgate.RollbackRequest.session', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5562, - serialized_end=5649, -) - - -_ROLLBACKRESPONSE = _descriptor.Descriptor( - name='RollbackResponse', - full_name='vtgate.RollbackResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5651, - serialized_end=5669, -) - - -_RESOLVETRANSACTIONREQUEST = _descriptor.Descriptor( - name='ResolveTransactionRequest', - full_name='vtgate.ResolveTransactionRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.ResolveTransactionRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dtid', full_name='vtgate.ResolveTransactionRequest.dtid', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5671, - serialized_end=5748, -) - - -_MESSAGESTREAMREQUEST = _descriptor.Descriptor( - name='MessageStreamRequest', - full_name='vtgate.MessageStreamRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.MessageStreamRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.MessageStreamRequest.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='vtgate.MessageStreamRequest.shard', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='vtgate.MessageStreamRequest.key_range', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='vtgate.MessageStreamRequest.name', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5751, - serialized_end=5895, -) - - -_MESSAGEACKREQUEST = _descriptor.Descriptor( - name='MessageAckRequest', - full_name='vtgate.MessageAckRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.MessageAckRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.MessageAckRequest.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='vtgate.MessageAckRequest.name', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='ids', full_name='vtgate.MessageAckRequest.ids', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=5897, - serialized_end=6011, -) - - -_IDKEYSPACEID = _descriptor.Descriptor( - name='IdKeyspaceId', - full_name='vtgate.IdKeyspaceId', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='vtgate.IdKeyspaceId.id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace_id', full_name='vtgate.IdKeyspaceId.keyspace_id', index=1, - number=2, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6013, - serialized_end=6074, -) - - -_MESSAGEACKKEYSPACEIDSREQUEST = _descriptor.Descriptor( - name='MessageAckKeyspaceIdsRequest', - full_name='vtgate.MessageAckKeyspaceIdsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.MessageAckKeyspaceIdsRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.MessageAckKeyspaceIdsRequest.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='vtgate.MessageAckKeyspaceIdsRequest.name', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='id_keyspace_ids', full_name='vtgate.MessageAckKeyspaceIdsRequest.id_keyspace_ids', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6077, - serialized_end=6222, -) - - -_RESOLVETRANSACTIONRESPONSE = _descriptor.Descriptor( - name='ResolveTransactionResponse', - full_name='vtgate.ResolveTransactionResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6224, - serialized_end=6252, -) - - -_SPLITQUERYREQUEST = _descriptor.Descriptor( - name='SplitQueryRequest', - full_name='vtgate.SplitQueryRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.SplitQueryRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.SplitQueryRequest.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.SplitQueryRequest.query', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='split_column', full_name='vtgate.SplitQueryRequest.split_column', index=3, - number=4, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='split_count', full_name='vtgate.SplitQueryRequest.split_count', index=4, - number=5, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='num_rows_per_query_part', full_name='vtgate.SplitQueryRequest.num_rows_per_query_part', index=5, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='algorithm', full_name='vtgate.SplitQueryRequest.algorithm', index=6, - number=7, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='use_split_query_v2', full_name='vtgate.SplitQueryRequest.use_split_query_v2', index=7, - number=8, 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, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6255, - serialized_end=6521, -) - - -_SPLITQUERYRESPONSE_KEYRANGEPART = _descriptor.Descriptor( - name='KeyRangePart', - full_name='vtgate.SplitQueryResponse.KeyRangePart', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.SplitQueryResponse.KeyRangePart.keyspace', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_ranges', full_name='vtgate.SplitQueryResponse.KeyRangePart.key_ranges', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6595, - serialized_end=6667, -) - -_SPLITQUERYRESPONSE_SHARDPART = _descriptor.Descriptor( - name='ShardPart', - full_name='vtgate.SplitQueryResponse.ShardPart', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.SplitQueryResponse.ShardPart.keyspace', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shards', full_name='vtgate.SplitQueryResponse.ShardPart.shards', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6669, - serialized_end=6714, -) - -_SPLITQUERYRESPONSE_PART = _descriptor.Descriptor( - name='Part', - full_name='vtgate.SplitQueryResponse.Part', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='query', full_name='vtgate.SplitQueryResponse.Part.query', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range_part', full_name='vtgate.SplitQueryResponse.Part.key_range_part', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard_part', full_name='vtgate.SplitQueryResponse.Part.shard_part', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='size', full_name='vtgate.SplitQueryResponse.Part.size', index=3, - number=4, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6717, - serialized_end=6894, -) - -_SPLITQUERYRESPONSE = _descriptor.Descriptor( - name='SplitQueryResponse', - full_name='vtgate.SplitQueryResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='splits', full_name='vtgate.SplitQueryResponse.splits', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_SPLITQUERYRESPONSE_KEYRANGEPART, _SPLITQUERYRESPONSE_SHARDPART, _SPLITQUERYRESPONSE_PART, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6524, - serialized_end=6894, -) - - -_GETSRVKEYSPACEREQUEST = _descriptor.Descriptor( - name='GetSrvKeyspaceRequest', - full_name='vtgate.GetSrvKeyspaceRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.GetSrvKeyspaceRequest.keyspace', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6896, - serialized_end=6937, -) - - -_GETSRVKEYSPACERESPONSE = _descriptor.Descriptor( - name='GetSrvKeyspaceResponse', - full_name='vtgate.GetSrvKeyspaceResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='srv_keyspace', full_name='vtgate.GetSrvKeyspaceResponse.srv_keyspace', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=6939, - serialized_end=7008, -) - - -_VSTREAMREQUEST = _descriptor.Descriptor( - name='VStreamRequest', - full_name='vtgate.VStreamRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.VStreamRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.VStreamRequest.tablet_type', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='vgtid', full_name='vtgate.VStreamRequest.vgtid', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='filter', full_name='vtgate.VStreamRequest.filter', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7011, - serialized_end=7176, -) - - -_VSTREAMRESPONSE = _descriptor.Descriptor( - name='VStreamResponse', - full_name='vtgate.VStreamResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='events', full_name='vtgate.VStreamResponse.events', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7178, - serialized_end=7231, -) - - -_UPDATESTREAMREQUEST = _descriptor.Descriptor( - name='UpdateStreamRequest', - full_name='vtgate.UpdateStreamRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='caller_id', full_name='vtgate.UpdateStreamRequest.caller_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='keyspace', full_name='vtgate.UpdateStreamRequest.keyspace', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shard', full_name='vtgate.UpdateStreamRequest.shard', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key_range', full_name='vtgate.UpdateStreamRequest.key_range', index=3, - number=4, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tablet_type', full_name='vtgate.UpdateStreamRequest.tablet_type', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='timestamp', full_name='vtgate.UpdateStreamRequest.timestamp', index=5, - number=6, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='event', full_name='vtgate.UpdateStreamRequest.event', index=6, - number=7, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7234, - serialized_end=7459, -) - - -_UPDATESTREAMRESPONSE = _descriptor.Descriptor( - name='UpdateStreamResponse', - full_name='vtgate.UpdateStreamResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='event', full_name='vtgate.UpdateStreamResponse.event', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='resume_timestamp', full_name='vtgate.UpdateStreamResponse.resume_timestamp', index=1, - number=2, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=7461, - serialized_end=7544, -) - -_SESSION_SHARDSESSION.fields_by_name['target'].message_type = query__pb2._TARGET -_SESSION_SHARDSESSION.containing_type = _SESSION -_SESSION.fields_by_name['shard_sessions'].message_type = _SESSION_SHARDSESSION -_SESSION.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_SESSION.fields_by_name['transaction_mode'].enum_type = _TRANSACTIONMODE -_SESSION.fields_by_name['warnings'].message_type = query__pb2._QUERYWARNING -_SESSION.fields_by_name['pre_sessions'].message_type = _SESSION_SHARDSESSION -_SESSION.fields_by_name['post_sessions'].message_type = _SESSION_SHARDSESSION -_EXECUTEREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTEREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_EXECUTEREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTEREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTERESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTERESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTERESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_EXECUTESHARDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTESHARDSREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTESHARDSREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_EXECUTESHARDSREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTESHARDSREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTESHARDSRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTESHARDSRESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTESHARDSRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_EXECUTEKEYSPACEIDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEKEYSPACEIDSREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTEKEYSPACEIDSREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_EXECUTEKEYSPACEIDSREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTEKEYSPACEIDSREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTEKEYSPACEIDSRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTEKEYSPACEIDSRESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTEKEYSPACEIDSRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_EXECUTEKEYRANGESREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEKEYRANGESREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTEKEYRANGESREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_EXECUTEKEYRANGESREQUEST.fields_by_name['key_ranges'].message_type = topodata__pb2._KEYRANGE -_EXECUTEKEYRANGESREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTEKEYRANGESREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTEKEYRANGESRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTEKEYRANGESRESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTEKEYRANGESRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_EXECUTEENTITYIDSREQUEST_ENTITYID.fields_by_name['type'].enum_type = query__pb2._TYPE -_EXECUTEENTITYIDSREQUEST_ENTITYID.containing_type = _EXECUTEENTITYIDSREQUEST -_EXECUTEENTITYIDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEENTITYIDSREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTEENTITYIDSREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_EXECUTEENTITYIDSREQUEST.fields_by_name['entity_keyspace_ids'].message_type = _EXECUTEENTITYIDSREQUEST_ENTITYID -_EXECUTEENTITYIDSREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTEENTITYIDSREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTEENTITYIDSRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTEENTITYIDSRESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTEENTITYIDSRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_EXECUTEBATCHREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEBATCHREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTEBATCHREQUEST.fields_by_name['queries'].message_type = query__pb2._BOUNDQUERY -_EXECUTEBATCHREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTEBATCHREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTEBATCHRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTEBATCHRESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTEBATCHRESPONSE.fields_by_name['results'].message_type = query__pb2._RESULTWITHERROR -_BOUNDSHARDQUERY.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_EXECUTEBATCHSHARDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEBATCHSHARDSREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTEBATCHSHARDSREQUEST.fields_by_name['queries'].message_type = _BOUNDSHARDQUERY -_EXECUTEBATCHSHARDSREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTEBATCHSHARDSREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTEBATCHSHARDSRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTEBATCHSHARDSRESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTEBATCHSHARDSRESPONSE.fields_by_name['results'].message_type = query__pb2._QUERYRESULT -_BOUNDKEYSPACEIDQUERY.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_EXECUTEBATCHKEYSPACEIDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_EXECUTEBATCHKEYSPACEIDSREQUEST.fields_by_name['session'].message_type = _SESSION -_EXECUTEBATCHKEYSPACEIDSREQUEST.fields_by_name['queries'].message_type = _BOUNDKEYSPACEIDQUERY -_EXECUTEBATCHKEYSPACEIDSREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_EXECUTEBATCHKEYSPACEIDSREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_EXECUTEBATCHKEYSPACEIDSRESPONSE.fields_by_name['error'].message_type = vtrpc__pb2._RPCERROR -_EXECUTEBATCHKEYSPACEIDSRESPONSE.fields_by_name['session'].message_type = _SESSION -_EXECUTEBATCHKEYSPACEIDSRESPONSE.fields_by_name['results'].message_type = query__pb2._QUERYRESULT -_STREAMEXECUTEREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_STREAMEXECUTEREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_STREAMEXECUTEREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_STREAMEXECUTEREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_STREAMEXECUTEREQUEST.fields_by_name['session'].message_type = _SESSION -_STREAMEXECUTERESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_STREAMEXECUTESHARDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_STREAMEXECUTESHARDSREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_STREAMEXECUTESHARDSREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_STREAMEXECUTESHARDSREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_STREAMEXECUTESHARDSRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_STREAMEXECUTEKEYSPACEIDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_STREAMEXECUTEKEYSPACEIDSREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_STREAMEXECUTEKEYSPACEIDSREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_STREAMEXECUTEKEYSPACEIDSREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_STREAMEXECUTEKEYSPACEIDSRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_STREAMEXECUTEKEYRANGESREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_STREAMEXECUTEKEYRANGESREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_STREAMEXECUTEKEYRANGESREQUEST.fields_by_name['key_ranges'].message_type = topodata__pb2._KEYRANGE -_STREAMEXECUTEKEYRANGESREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_STREAMEXECUTEKEYRANGESREQUEST.fields_by_name['options'].message_type = query__pb2._EXECUTEOPTIONS -_STREAMEXECUTEKEYRANGESRESPONSE.fields_by_name['result'].message_type = query__pb2._QUERYRESULT -_BEGINREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_BEGINRESPONSE.fields_by_name['session'].message_type = _SESSION -_COMMITREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_COMMITREQUEST.fields_by_name['session'].message_type = _SESSION -_ROLLBACKREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_ROLLBACKREQUEST.fields_by_name['session'].message_type = _SESSION -_RESOLVETRANSACTIONREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_MESSAGESTREAMREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_MESSAGESTREAMREQUEST.fields_by_name['key_range'].message_type = topodata__pb2._KEYRANGE -_MESSAGEACKREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_MESSAGEACKREQUEST.fields_by_name['ids'].message_type = query__pb2._VALUE -_IDKEYSPACEID.fields_by_name['id'].message_type = query__pb2._VALUE -_MESSAGEACKKEYSPACEIDSREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_MESSAGEACKKEYSPACEIDSREQUEST.fields_by_name['id_keyspace_ids'].message_type = _IDKEYSPACEID -_SPLITQUERYREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_SPLITQUERYREQUEST.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_SPLITQUERYREQUEST.fields_by_name['algorithm'].enum_type = query__pb2._SPLITQUERYREQUEST_ALGORITHM -_SPLITQUERYRESPONSE_KEYRANGEPART.fields_by_name['key_ranges'].message_type = topodata__pb2._KEYRANGE -_SPLITQUERYRESPONSE_KEYRANGEPART.containing_type = _SPLITQUERYRESPONSE -_SPLITQUERYRESPONSE_SHARDPART.containing_type = _SPLITQUERYRESPONSE -_SPLITQUERYRESPONSE_PART.fields_by_name['query'].message_type = query__pb2._BOUNDQUERY -_SPLITQUERYRESPONSE_PART.fields_by_name['key_range_part'].message_type = _SPLITQUERYRESPONSE_KEYRANGEPART -_SPLITQUERYRESPONSE_PART.fields_by_name['shard_part'].message_type = _SPLITQUERYRESPONSE_SHARDPART -_SPLITQUERYRESPONSE_PART.containing_type = _SPLITQUERYRESPONSE -_SPLITQUERYRESPONSE.fields_by_name['splits'].message_type = _SPLITQUERYRESPONSE_PART -_GETSRVKEYSPACERESPONSE.fields_by_name['srv_keyspace'].message_type = topodata__pb2._SRVKEYSPACE -_VSTREAMREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_VSTREAMREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_VSTREAMREQUEST.fields_by_name['vgtid'].message_type = binlogdata__pb2._VGTID -_VSTREAMREQUEST.fields_by_name['filter'].message_type = binlogdata__pb2._FILTER -_VSTREAMRESPONSE.fields_by_name['events'].message_type = binlogdata__pb2._VEVENT -_UPDATESTREAMREQUEST.fields_by_name['caller_id'].message_type = vtrpc__pb2._CALLERID -_UPDATESTREAMREQUEST.fields_by_name['key_range'].message_type = topodata__pb2._KEYRANGE -_UPDATESTREAMREQUEST.fields_by_name['tablet_type'].enum_type = topodata__pb2._TABLETTYPE -_UPDATESTREAMREQUEST.fields_by_name['event'].message_type = query__pb2._EVENTTOKEN -_UPDATESTREAMRESPONSE.fields_by_name['event'].message_type = query__pb2._STREAMEVENT -DESCRIPTOR.message_types_by_name['Session'] = _SESSION -DESCRIPTOR.message_types_by_name['ExecuteRequest'] = _EXECUTEREQUEST -DESCRIPTOR.message_types_by_name['ExecuteResponse'] = _EXECUTERESPONSE -DESCRIPTOR.message_types_by_name['ExecuteShardsRequest'] = _EXECUTESHARDSREQUEST -DESCRIPTOR.message_types_by_name['ExecuteShardsResponse'] = _EXECUTESHARDSRESPONSE -DESCRIPTOR.message_types_by_name['ExecuteKeyspaceIdsRequest'] = _EXECUTEKEYSPACEIDSREQUEST -DESCRIPTOR.message_types_by_name['ExecuteKeyspaceIdsResponse'] = _EXECUTEKEYSPACEIDSRESPONSE -DESCRIPTOR.message_types_by_name['ExecuteKeyRangesRequest'] = _EXECUTEKEYRANGESREQUEST -DESCRIPTOR.message_types_by_name['ExecuteKeyRangesResponse'] = _EXECUTEKEYRANGESRESPONSE -DESCRIPTOR.message_types_by_name['ExecuteEntityIdsRequest'] = _EXECUTEENTITYIDSREQUEST -DESCRIPTOR.message_types_by_name['ExecuteEntityIdsResponse'] = _EXECUTEENTITYIDSRESPONSE -DESCRIPTOR.message_types_by_name['ExecuteBatchRequest'] = _EXECUTEBATCHREQUEST -DESCRIPTOR.message_types_by_name['ExecuteBatchResponse'] = _EXECUTEBATCHRESPONSE -DESCRIPTOR.message_types_by_name['BoundShardQuery'] = _BOUNDSHARDQUERY -DESCRIPTOR.message_types_by_name['ExecuteBatchShardsRequest'] = _EXECUTEBATCHSHARDSREQUEST -DESCRIPTOR.message_types_by_name['ExecuteBatchShardsResponse'] = _EXECUTEBATCHSHARDSRESPONSE -DESCRIPTOR.message_types_by_name['BoundKeyspaceIdQuery'] = _BOUNDKEYSPACEIDQUERY -DESCRIPTOR.message_types_by_name['ExecuteBatchKeyspaceIdsRequest'] = _EXECUTEBATCHKEYSPACEIDSREQUEST -DESCRIPTOR.message_types_by_name['ExecuteBatchKeyspaceIdsResponse'] = _EXECUTEBATCHKEYSPACEIDSRESPONSE -DESCRIPTOR.message_types_by_name['StreamExecuteRequest'] = _STREAMEXECUTEREQUEST -DESCRIPTOR.message_types_by_name['StreamExecuteResponse'] = _STREAMEXECUTERESPONSE -DESCRIPTOR.message_types_by_name['StreamExecuteShardsRequest'] = _STREAMEXECUTESHARDSREQUEST -DESCRIPTOR.message_types_by_name['StreamExecuteShardsResponse'] = _STREAMEXECUTESHARDSRESPONSE -DESCRIPTOR.message_types_by_name['StreamExecuteKeyspaceIdsRequest'] = _STREAMEXECUTEKEYSPACEIDSREQUEST -DESCRIPTOR.message_types_by_name['StreamExecuteKeyspaceIdsResponse'] = _STREAMEXECUTEKEYSPACEIDSRESPONSE -DESCRIPTOR.message_types_by_name['StreamExecuteKeyRangesRequest'] = _STREAMEXECUTEKEYRANGESREQUEST -DESCRIPTOR.message_types_by_name['StreamExecuteKeyRangesResponse'] = _STREAMEXECUTEKEYRANGESRESPONSE -DESCRIPTOR.message_types_by_name['BeginRequest'] = _BEGINREQUEST -DESCRIPTOR.message_types_by_name['BeginResponse'] = _BEGINRESPONSE -DESCRIPTOR.message_types_by_name['CommitRequest'] = _COMMITREQUEST -DESCRIPTOR.message_types_by_name['CommitResponse'] = _COMMITRESPONSE -DESCRIPTOR.message_types_by_name['RollbackRequest'] = _ROLLBACKREQUEST -DESCRIPTOR.message_types_by_name['RollbackResponse'] = _ROLLBACKRESPONSE -DESCRIPTOR.message_types_by_name['ResolveTransactionRequest'] = _RESOLVETRANSACTIONREQUEST -DESCRIPTOR.message_types_by_name['MessageStreamRequest'] = _MESSAGESTREAMREQUEST -DESCRIPTOR.message_types_by_name['MessageAckRequest'] = _MESSAGEACKREQUEST -DESCRIPTOR.message_types_by_name['IdKeyspaceId'] = _IDKEYSPACEID -DESCRIPTOR.message_types_by_name['MessageAckKeyspaceIdsRequest'] = _MESSAGEACKKEYSPACEIDSREQUEST -DESCRIPTOR.message_types_by_name['ResolveTransactionResponse'] = _RESOLVETRANSACTIONRESPONSE -DESCRIPTOR.message_types_by_name['SplitQueryRequest'] = _SPLITQUERYREQUEST -DESCRIPTOR.message_types_by_name['SplitQueryResponse'] = _SPLITQUERYRESPONSE -DESCRIPTOR.message_types_by_name['GetSrvKeyspaceRequest'] = _GETSRVKEYSPACEREQUEST -DESCRIPTOR.message_types_by_name['GetSrvKeyspaceResponse'] = _GETSRVKEYSPACERESPONSE -DESCRIPTOR.message_types_by_name['VStreamRequest'] = _VSTREAMREQUEST -DESCRIPTOR.message_types_by_name['VStreamResponse'] = _VSTREAMRESPONSE -DESCRIPTOR.message_types_by_name['UpdateStreamRequest'] = _UPDATESTREAMREQUEST -DESCRIPTOR.message_types_by_name['UpdateStreamResponse'] = _UPDATESTREAMRESPONSE -DESCRIPTOR.enum_types_by_name['TransactionMode'] = _TRANSACTIONMODE -DESCRIPTOR.enum_types_by_name['CommitOrder'] = _COMMITORDER -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Session = _reflection.GeneratedProtocolMessageType('Session', (_message.Message,), dict( - - ShardSession = _reflection.GeneratedProtocolMessageType('ShardSession', (_message.Message,), dict( - DESCRIPTOR = _SESSION_SHARDSESSION, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.Session.ShardSession) - )) - , - DESCRIPTOR = _SESSION, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.Session) - )) -_sym_db.RegisterMessage(Session) -_sym_db.RegisterMessage(Session.ShardSession) - -ExecuteRequest = _reflection.GeneratedProtocolMessageType('ExecuteRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteRequest) - )) -_sym_db.RegisterMessage(ExecuteRequest) - -ExecuteResponse = _reflection.GeneratedProtocolMessageType('ExecuteResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTERESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteResponse) - )) -_sym_db.RegisterMessage(ExecuteResponse) - -ExecuteShardsRequest = _reflection.GeneratedProtocolMessageType('ExecuteShardsRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTESHARDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteShardsRequest) - )) -_sym_db.RegisterMessage(ExecuteShardsRequest) - -ExecuteShardsResponse = _reflection.GeneratedProtocolMessageType('ExecuteShardsResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTESHARDSRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteShardsResponse) - )) -_sym_db.RegisterMessage(ExecuteShardsResponse) - -ExecuteKeyspaceIdsRequest = _reflection.GeneratedProtocolMessageType('ExecuteKeyspaceIdsRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEKEYSPACEIDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteKeyspaceIdsRequest) - )) -_sym_db.RegisterMessage(ExecuteKeyspaceIdsRequest) - -ExecuteKeyspaceIdsResponse = _reflection.GeneratedProtocolMessageType('ExecuteKeyspaceIdsResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEKEYSPACEIDSRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteKeyspaceIdsResponse) - )) -_sym_db.RegisterMessage(ExecuteKeyspaceIdsResponse) - -ExecuteKeyRangesRequest = _reflection.GeneratedProtocolMessageType('ExecuteKeyRangesRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEKEYRANGESREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteKeyRangesRequest) - )) -_sym_db.RegisterMessage(ExecuteKeyRangesRequest) - -ExecuteKeyRangesResponse = _reflection.GeneratedProtocolMessageType('ExecuteKeyRangesResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEKEYRANGESRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteKeyRangesResponse) - )) -_sym_db.RegisterMessage(ExecuteKeyRangesResponse) - -ExecuteEntityIdsRequest = _reflection.GeneratedProtocolMessageType('ExecuteEntityIdsRequest', (_message.Message,), dict( - - EntityId = _reflection.GeneratedProtocolMessageType('EntityId', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEENTITYIDSREQUEST_ENTITYID, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteEntityIdsRequest.EntityId) - )) - , - DESCRIPTOR = _EXECUTEENTITYIDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteEntityIdsRequest) - )) -_sym_db.RegisterMessage(ExecuteEntityIdsRequest) -_sym_db.RegisterMessage(ExecuteEntityIdsRequest.EntityId) - -ExecuteEntityIdsResponse = _reflection.GeneratedProtocolMessageType('ExecuteEntityIdsResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEENTITYIDSRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteEntityIdsResponse) - )) -_sym_db.RegisterMessage(ExecuteEntityIdsResponse) - -ExecuteBatchRequest = _reflection.GeneratedProtocolMessageType('ExecuteBatchRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteBatchRequest) - )) -_sym_db.RegisterMessage(ExecuteBatchRequest) - -ExecuteBatchResponse = _reflection.GeneratedProtocolMessageType('ExecuteBatchResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteBatchResponse) - )) -_sym_db.RegisterMessage(ExecuteBatchResponse) - -BoundShardQuery = _reflection.GeneratedProtocolMessageType('BoundShardQuery', (_message.Message,), dict( - DESCRIPTOR = _BOUNDSHARDQUERY, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.BoundShardQuery) - )) -_sym_db.RegisterMessage(BoundShardQuery) - -ExecuteBatchShardsRequest = _reflection.GeneratedProtocolMessageType('ExecuteBatchShardsRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHSHARDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteBatchShardsRequest) - )) -_sym_db.RegisterMessage(ExecuteBatchShardsRequest) - -ExecuteBatchShardsResponse = _reflection.GeneratedProtocolMessageType('ExecuteBatchShardsResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHSHARDSRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteBatchShardsResponse) - )) -_sym_db.RegisterMessage(ExecuteBatchShardsResponse) - -BoundKeyspaceIdQuery = _reflection.GeneratedProtocolMessageType('BoundKeyspaceIdQuery', (_message.Message,), dict( - DESCRIPTOR = _BOUNDKEYSPACEIDQUERY, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.BoundKeyspaceIdQuery) - )) -_sym_db.RegisterMessage(BoundKeyspaceIdQuery) - -ExecuteBatchKeyspaceIdsRequest = _reflection.GeneratedProtocolMessageType('ExecuteBatchKeyspaceIdsRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHKEYSPACEIDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteBatchKeyspaceIdsRequest) - )) -_sym_db.RegisterMessage(ExecuteBatchKeyspaceIdsRequest) - -ExecuteBatchKeyspaceIdsResponse = _reflection.GeneratedProtocolMessageType('ExecuteBatchKeyspaceIdsResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEBATCHKEYSPACEIDSRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ExecuteBatchKeyspaceIdsResponse) - )) -_sym_db.RegisterMessage(ExecuteBatchKeyspaceIdsResponse) - -StreamExecuteRequest = _reflection.GeneratedProtocolMessageType('StreamExecuteRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTEREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteRequest) - )) -_sym_db.RegisterMessage(StreamExecuteRequest) - -StreamExecuteResponse = _reflection.GeneratedProtocolMessageType('StreamExecuteResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTERESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteResponse) - )) -_sym_db.RegisterMessage(StreamExecuteResponse) - -StreamExecuteShardsRequest = _reflection.GeneratedProtocolMessageType('StreamExecuteShardsRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTESHARDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteShardsRequest) - )) -_sym_db.RegisterMessage(StreamExecuteShardsRequest) - -StreamExecuteShardsResponse = _reflection.GeneratedProtocolMessageType('StreamExecuteShardsResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTESHARDSRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteShardsResponse) - )) -_sym_db.RegisterMessage(StreamExecuteShardsResponse) - -StreamExecuteKeyspaceIdsRequest = _reflection.GeneratedProtocolMessageType('StreamExecuteKeyspaceIdsRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTEKEYSPACEIDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteKeyspaceIdsRequest) - )) -_sym_db.RegisterMessage(StreamExecuteKeyspaceIdsRequest) - -StreamExecuteKeyspaceIdsResponse = _reflection.GeneratedProtocolMessageType('StreamExecuteKeyspaceIdsResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTEKEYSPACEIDSRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteKeyspaceIdsResponse) - )) -_sym_db.RegisterMessage(StreamExecuteKeyspaceIdsResponse) - -StreamExecuteKeyRangesRequest = _reflection.GeneratedProtocolMessageType('StreamExecuteKeyRangesRequest', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTEKEYRANGESREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteKeyRangesRequest) - )) -_sym_db.RegisterMessage(StreamExecuteKeyRangesRequest) - -StreamExecuteKeyRangesResponse = _reflection.GeneratedProtocolMessageType('StreamExecuteKeyRangesResponse', (_message.Message,), dict( - DESCRIPTOR = _STREAMEXECUTEKEYRANGESRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.StreamExecuteKeyRangesResponse) - )) -_sym_db.RegisterMessage(StreamExecuteKeyRangesResponse) - -BeginRequest = _reflection.GeneratedProtocolMessageType('BeginRequest', (_message.Message,), dict( - DESCRIPTOR = _BEGINREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.BeginRequest) - )) -_sym_db.RegisterMessage(BeginRequest) - -BeginResponse = _reflection.GeneratedProtocolMessageType('BeginResponse', (_message.Message,), dict( - DESCRIPTOR = _BEGINRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.BeginResponse) - )) -_sym_db.RegisterMessage(BeginResponse) - -CommitRequest = _reflection.GeneratedProtocolMessageType('CommitRequest', (_message.Message,), dict( - DESCRIPTOR = _COMMITREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.CommitRequest) - )) -_sym_db.RegisterMessage(CommitRequest) - -CommitResponse = _reflection.GeneratedProtocolMessageType('CommitResponse', (_message.Message,), dict( - DESCRIPTOR = _COMMITRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.CommitResponse) - )) -_sym_db.RegisterMessage(CommitResponse) - -RollbackRequest = _reflection.GeneratedProtocolMessageType('RollbackRequest', (_message.Message,), dict( - DESCRIPTOR = _ROLLBACKREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.RollbackRequest) - )) -_sym_db.RegisterMessage(RollbackRequest) - -RollbackResponse = _reflection.GeneratedProtocolMessageType('RollbackResponse', (_message.Message,), dict( - DESCRIPTOR = _ROLLBACKRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.RollbackResponse) - )) -_sym_db.RegisterMessage(RollbackResponse) - -ResolveTransactionRequest = _reflection.GeneratedProtocolMessageType('ResolveTransactionRequest', (_message.Message,), dict( - DESCRIPTOR = _RESOLVETRANSACTIONREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ResolveTransactionRequest) - )) -_sym_db.RegisterMessage(ResolveTransactionRequest) - -MessageStreamRequest = _reflection.GeneratedProtocolMessageType('MessageStreamRequest', (_message.Message,), dict( - DESCRIPTOR = _MESSAGESTREAMREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.MessageStreamRequest) - )) -_sym_db.RegisterMessage(MessageStreamRequest) - -MessageAckRequest = _reflection.GeneratedProtocolMessageType('MessageAckRequest', (_message.Message,), dict( - DESCRIPTOR = _MESSAGEACKREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.MessageAckRequest) - )) -_sym_db.RegisterMessage(MessageAckRequest) - -IdKeyspaceId = _reflection.GeneratedProtocolMessageType('IdKeyspaceId', (_message.Message,), dict( - DESCRIPTOR = _IDKEYSPACEID, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.IdKeyspaceId) - )) -_sym_db.RegisterMessage(IdKeyspaceId) - -MessageAckKeyspaceIdsRequest = _reflection.GeneratedProtocolMessageType('MessageAckKeyspaceIdsRequest', (_message.Message,), dict( - DESCRIPTOR = _MESSAGEACKKEYSPACEIDSREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.MessageAckKeyspaceIdsRequest) - )) -_sym_db.RegisterMessage(MessageAckKeyspaceIdsRequest) - -ResolveTransactionResponse = _reflection.GeneratedProtocolMessageType('ResolveTransactionResponse', (_message.Message,), dict( - DESCRIPTOR = _RESOLVETRANSACTIONRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.ResolveTransactionResponse) - )) -_sym_db.RegisterMessage(ResolveTransactionResponse) - -SplitQueryRequest = _reflection.GeneratedProtocolMessageType('SplitQueryRequest', (_message.Message,), dict( - DESCRIPTOR = _SPLITQUERYREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.SplitQueryRequest) - )) -_sym_db.RegisterMessage(SplitQueryRequest) - -SplitQueryResponse = _reflection.GeneratedProtocolMessageType('SplitQueryResponse', (_message.Message,), dict( - - KeyRangePart = _reflection.GeneratedProtocolMessageType('KeyRangePart', (_message.Message,), dict( - DESCRIPTOR = _SPLITQUERYRESPONSE_KEYRANGEPART, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.SplitQueryResponse.KeyRangePart) - )) - , - - ShardPart = _reflection.GeneratedProtocolMessageType('ShardPart', (_message.Message,), dict( - DESCRIPTOR = _SPLITQUERYRESPONSE_SHARDPART, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.SplitQueryResponse.ShardPart) - )) - , - - Part = _reflection.GeneratedProtocolMessageType('Part', (_message.Message,), dict( - DESCRIPTOR = _SPLITQUERYRESPONSE_PART, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.SplitQueryResponse.Part) - )) - , - DESCRIPTOR = _SPLITQUERYRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.SplitQueryResponse) - )) -_sym_db.RegisterMessage(SplitQueryResponse) -_sym_db.RegisterMessage(SplitQueryResponse.KeyRangePart) -_sym_db.RegisterMessage(SplitQueryResponse.ShardPart) -_sym_db.RegisterMessage(SplitQueryResponse.Part) - -GetSrvKeyspaceRequest = _reflection.GeneratedProtocolMessageType('GetSrvKeyspaceRequest', (_message.Message,), dict( - DESCRIPTOR = _GETSRVKEYSPACEREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.GetSrvKeyspaceRequest) - )) -_sym_db.RegisterMessage(GetSrvKeyspaceRequest) - -GetSrvKeyspaceResponse = _reflection.GeneratedProtocolMessageType('GetSrvKeyspaceResponse', (_message.Message,), dict( - DESCRIPTOR = _GETSRVKEYSPACERESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.GetSrvKeyspaceResponse) - )) -_sym_db.RegisterMessage(GetSrvKeyspaceResponse) - -VStreamRequest = _reflection.GeneratedProtocolMessageType('VStreamRequest', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.VStreamRequest) - )) -_sym_db.RegisterMessage(VStreamRequest) - -VStreamResponse = _reflection.GeneratedProtocolMessageType('VStreamResponse', (_message.Message,), dict( - DESCRIPTOR = _VSTREAMRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.VStreamResponse) - )) -_sym_db.RegisterMessage(VStreamResponse) - -UpdateStreamRequest = _reflection.GeneratedProtocolMessageType('UpdateStreamRequest', (_message.Message,), dict( - DESCRIPTOR = _UPDATESTREAMREQUEST, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.UpdateStreamRequest) - )) -_sym_db.RegisterMessage(UpdateStreamRequest) - -UpdateStreamResponse = _reflection.GeneratedProtocolMessageType('UpdateStreamResponse', (_message.Message,), dict( - DESCRIPTOR = _UPDATESTREAMRESPONSE, - __module__ = 'vtgate_pb2' - # @@protoc_insertion_point(class_scope:vtgate.UpdateStreamResponse) - )) -_sym_db.RegisterMessage(UpdateStreamResponse) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vtgate_pb2_grpc.py b/py/vtproto/vtgate_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vtgate_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vtgateservice_pb2.py b/py/vtproto/vtgateservice_pb2.py deleted file mode 100644 index 2c1145f5632..00000000000 --- a/py/vtproto/vtgateservice_pb2.py +++ /dev/null @@ -1,256 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vtgateservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import vtgate_pb2 as vtgate__pb2 -import query_pb2 as query__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vtgateservice.proto', - package='vtgateservice', - syntax='proto3', - serialized_options=_b('\n\024io.vitess.proto.grpcZ*vitess.io/vitess/go/vt/proto/vtgateservice'), - serialized_pb=_b('\n\x13vtgateservice.proto\x12\rvtgateservice\x1a\x0cvtgate.proto\x1a\x0bquery.proto2\x86\x0f\n\x06Vitess\x12<\n\x07\x45xecute\x12\x16.vtgate.ExecuteRequest\x1a\x17.vtgate.ExecuteResponse\"\x00\x12K\n\x0c\x45xecuteBatch\x12\x1b.vtgate.ExecuteBatchRequest\x1a\x1c.vtgate.ExecuteBatchResponse\"\x00\x12P\n\rStreamExecute\x12\x1c.vtgate.StreamExecuteRequest\x1a\x1d.vtgate.StreamExecuteResponse\"\x00\x30\x01\x12N\n\rExecuteShards\x12\x1c.vtgate.ExecuteShardsRequest\x1a\x1d.vtgate.ExecuteShardsResponse\"\x00\x12]\n\x12\x45xecuteKeyspaceIds\x12!.vtgate.ExecuteKeyspaceIdsRequest\x1a\".vtgate.ExecuteKeyspaceIdsResponse\"\x00\x12W\n\x10\x45xecuteKeyRanges\x12\x1f.vtgate.ExecuteKeyRangesRequest\x1a .vtgate.ExecuteKeyRangesResponse\"\x00\x12W\n\x10\x45xecuteEntityIds\x12\x1f.vtgate.ExecuteEntityIdsRequest\x1a .vtgate.ExecuteEntityIdsResponse\"\x00\x12]\n\x12\x45xecuteBatchShards\x12!.vtgate.ExecuteBatchShardsRequest\x1a\".vtgate.ExecuteBatchShardsResponse\"\x00\x12l\n\x17\x45xecuteBatchKeyspaceIds\x12&.vtgate.ExecuteBatchKeyspaceIdsRequest\x1a\'.vtgate.ExecuteBatchKeyspaceIdsResponse\"\x00\x12\x62\n\x13StreamExecuteShards\x12\".vtgate.StreamExecuteShardsRequest\x1a#.vtgate.StreamExecuteShardsResponse\"\x00\x30\x01\x12q\n\x18StreamExecuteKeyspaceIds\x12\'.vtgate.StreamExecuteKeyspaceIdsRequest\x1a(.vtgate.StreamExecuteKeyspaceIdsResponse\"\x00\x30\x01\x12k\n\x16StreamExecuteKeyRanges\x12%.vtgate.StreamExecuteKeyRangesRequest\x1a&.vtgate.StreamExecuteKeyRangesResponse\"\x00\x30\x01\x12\x36\n\x05\x42\x65gin\x12\x14.vtgate.BeginRequest\x1a\x15.vtgate.BeginResponse\"\x00\x12\x39\n\x06\x43ommit\x12\x15.vtgate.CommitRequest\x1a\x16.vtgate.CommitResponse\"\x00\x12?\n\x08Rollback\x12\x17.vtgate.RollbackRequest\x1a\x18.vtgate.RollbackResponse\"\x00\x12]\n\x12ResolveTransaction\x12!.vtgate.ResolveTransactionRequest\x1a\".vtgate.ResolveTransactionResponse\"\x00\x12O\n\rMessageStream\x12\x1c.vtgate.MessageStreamRequest\x1a\x1c.query.MessageStreamResponse\"\x00\x30\x01\x12\x44\n\nMessageAck\x12\x19.vtgate.MessageAckRequest\x1a\x19.query.MessageAckResponse\"\x00\x12Z\n\x15MessageAckKeyspaceIds\x12$.vtgate.MessageAckKeyspaceIdsRequest\x1a\x19.query.MessageAckResponse\"\x00\x12\x45\n\nSplitQuery\x12\x19.vtgate.SplitQueryRequest\x1a\x1a.vtgate.SplitQueryResponse\"\x00\x12Q\n\x0eGetSrvKeyspace\x12\x1d.vtgate.GetSrvKeyspaceRequest\x1a\x1e.vtgate.GetSrvKeyspaceResponse\"\x00\x12>\n\x07VStream\x12\x16.vtgate.VStreamRequest\x1a\x17.vtgate.VStreamResponse\"\x00\x30\x01\x12M\n\x0cUpdateStream\x12\x1b.vtgate.UpdateStreamRequest\x1a\x1c.vtgate.UpdateStreamResponse\"\x00\x30\x01\x42\x42\n\x14io.vitess.proto.grpcZ*vitess.io/vitess/go/vt/proto/vtgateserviceb\x06proto3') - , - dependencies=[vtgate__pb2.DESCRIPTOR,query__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_VITESS = _descriptor.ServiceDescriptor( - name='Vitess', - full_name='vtgateservice.Vitess', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=66, - serialized_end=1992, - methods=[ - _descriptor.MethodDescriptor( - name='Execute', - full_name='vtgateservice.Vitess.Execute', - index=0, - containing_service=None, - input_type=vtgate__pb2._EXECUTEREQUEST, - output_type=vtgate__pb2._EXECUTERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteBatch', - full_name='vtgateservice.Vitess.ExecuteBatch', - index=1, - containing_service=None, - input_type=vtgate__pb2._EXECUTEBATCHREQUEST, - output_type=vtgate__pb2._EXECUTEBATCHRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StreamExecute', - full_name='vtgateservice.Vitess.StreamExecute', - index=2, - containing_service=None, - input_type=vtgate__pb2._STREAMEXECUTEREQUEST, - output_type=vtgate__pb2._STREAMEXECUTERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteShards', - full_name='vtgateservice.Vitess.ExecuteShards', - index=3, - containing_service=None, - input_type=vtgate__pb2._EXECUTESHARDSREQUEST, - output_type=vtgate__pb2._EXECUTESHARDSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteKeyspaceIds', - full_name='vtgateservice.Vitess.ExecuteKeyspaceIds', - index=4, - containing_service=None, - input_type=vtgate__pb2._EXECUTEKEYSPACEIDSREQUEST, - output_type=vtgate__pb2._EXECUTEKEYSPACEIDSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteKeyRanges', - full_name='vtgateservice.Vitess.ExecuteKeyRanges', - index=5, - containing_service=None, - input_type=vtgate__pb2._EXECUTEKEYRANGESREQUEST, - output_type=vtgate__pb2._EXECUTEKEYRANGESRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteEntityIds', - full_name='vtgateservice.Vitess.ExecuteEntityIds', - index=6, - containing_service=None, - input_type=vtgate__pb2._EXECUTEENTITYIDSREQUEST, - output_type=vtgate__pb2._EXECUTEENTITYIDSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteBatchShards', - full_name='vtgateservice.Vitess.ExecuteBatchShards', - index=7, - containing_service=None, - input_type=vtgate__pb2._EXECUTEBATCHSHARDSREQUEST, - output_type=vtgate__pb2._EXECUTEBATCHSHARDSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ExecuteBatchKeyspaceIds', - full_name='vtgateservice.Vitess.ExecuteBatchKeyspaceIds', - index=8, - containing_service=None, - input_type=vtgate__pb2._EXECUTEBATCHKEYSPACEIDSREQUEST, - output_type=vtgate__pb2._EXECUTEBATCHKEYSPACEIDSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StreamExecuteShards', - full_name='vtgateservice.Vitess.StreamExecuteShards', - index=9, - containing_service=None, - input_type=vtgate__pb2._STREAMEXECUTESHARDSREQUEST, - output_type=vtgate__pb2._STREAMEXECUTESHARDSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StreamExecuteKeyspaceIds', - full_name='vtgateservice.Vitess.StreamExecuteKeyspaceIds', - index=10, - containing_service=None, - input_type=vtgate__pb2._STREAMEXECUTEKEYSPACEIDSREQUEST, - output_type=vtgate__pb2._STREAMEXECUTEKEYSPACEIDSRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='StreamExecuteKeyRanges', - full_name='vtgateservice.Vitess.StreamExecuteKeyRanges', - index=11, - containing_service=None, - input_type=vtgate__pb2._STREAMEXECUTEKEYRANGESREQUEST, - output_type=vtgate__pb2._STREAMEXECUTEKEYRANGESRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Begin', - full_name='vtgateservice.Vitess.Begin', - index=12, - containing_service=None, - input_type=vtgate__pb2._BEGINREQUEST, - output_type=vtgate__pb2._BEGINRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Commit', - full_name='vtgateservice.Vitess.Commit', - index=13, - containing_service=None, - input_type=vtgate__pb2._COMMITREQUEST, - output_type=vtgate__pb2._COMMITRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='Rollback', - full_name='vtgateservice.Vitess.Rollback', - index=14, - containing_service=None, - input_type=vtgate__pb2._ROLLBACKREQUEST, - output_type=vtgate__pb2._ROLLBACKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='ResolveTransaction', - full_name='vtgateservice.Vitess.ResolveTransaction', - index=15, - containing_service=None, - input_type=vtgate__pb2._RESOLVETRANSACTIONREQUEST, - output_type=vtgate__pb2._RESOLVETRANSACTIONRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='MessageStream', - full_name='vtgateservice.Vitess.MessageStream', - index=16, - containing_service=None, - input_type=vtgate__pb2._MESSAGESTREAMREQUEST, - output_type=query__pb2._MESSAGESTREAMRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='MessageAck', - full_name='vtgateservice.Vitess.MessageAck', - index=17, - containing_service=None, - input_type=vtgate__pb2._MESSAGEACKREQUEST, - output_type=query__pb2._MESSAGEACKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='MessageAckKeyspaceIds', - full_name='vtgateservice.Vitess.MessageAckKeyspaceIds', - index=18, - containing_service=None, - input_type=vtgate__pb2._MESSAGEACKKEYSPACEIDSREQUEST, - output_type=query__pb2._MESSAGEACKRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='SplitQuery', - full_name='vtgateservice.Vitess.SplitQuery', - index=19, - containing_service=None, - input_type=vtgate__pb2._SPLITQUERYREQUEST, - output_type=vtgate__pb2._SPLITQUERYRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='GetSrvKeyspace', - full_name='vtgateservice.Vitess.GetSrvKeyspace', - index=20, - containing_service=None, - input_type=vtgate__pb2._GETSRVKEYSPACEREQUEST, - output_type=vtgate__pb2._GETSRVKEYSPACERESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='VStream', - full_name='vtgateservice.Vitess.VStream', - index=21, - containing_service=None, - input_type=vtgate__pb2._VSTREAMREQUEST, - output_type=vtgate__pb2._VSTREAMRESPONSE, - serialized_options=None, - ), - _descriptor.MethodDescriptor( - name='UpdateStream', - full_name='vtgateservice.Vitess.UpdateStream', - index=22, - containing_service=None, - input_type=vtgate__pb2._UPDATESTREAMREQUEST, - output_type=vtgate__pb2._UPDATESTREAMRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_VITESS) - -DESCRIPTOR.services_by_name['Vitess'] = _VITESS - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vtgateservice_pb2_grpc.py b/py/vtproto/vtgateservice_pb2_grpc.py deleted file mode 100644 index 593e2dd40da..00000000000 --- a/py/vtproto/vtgateservice_pb2_grpc.py +++ /dev/null @@ -1,459 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import query_pb2 as query__pb2 -import vtgate_pb2 as vtgate__pb2 - - -class VitessStub(object): - """Vitess is the main service to access a Vitess cluster. It is the API that vtgate - exposes to serve all queries. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.Execute = channel.unary_unary( - '/vtgateservice.Vitess/Execute', - request_serializer=vtgate__pb2.ExecuteRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteResponse.FromString, - ) - self.ExecuteBatch = channel.unary_unary( - '/vtgateservice.Vitess/ExecuteBatch', - request_serializer=vtgate__pb2.ExecuteBatchRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteBatchResponse.FromString, - ) - self.StreamExecute = channel.unary_stream( - '/vtgateservice.Vitess/StreamExecute', - request_serializer=vtgate__pb2.StreamExecuteRequest.SerializeToString, - response_deserializer=vtgate__pb2.StreamExecuteResponse.FromString, - ) - self.ExecuteShards = channel.unary_unary( - '/vtgateservice.Vitess/ExecuteShards', - request_serializer=vtgate__pb2.ExecuteShardsRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteShardsResponse.FromString, - ) - self.ExecuteKeyspaceIds = channel.unary_unary( - '/vtgateservice.Vitess/ExecuteKeyspaceIds', - request_serializer=vtgate__pb2.ExecuteKeyspaceIdsRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteKeyspaceIdsResponse.FromString, - ) - self.ExecuteKeyRanges = channel.unary_unary( - '/vtgateservice.Vitess/ExecuteKeyRanges', - request_serializer=vtgate__pb2.ExecuteKeyRangesRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteKeyRangesResponse.FromString, - ) - self.ExecuteEntityIds = channel.unary_unary( - '/vtgateservice.Vitess/ExecuteEntityIds', - request_serializer=vtgate__pb2.ExecuteEntityIdsRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteEntityIdsResponse.FromString, - ) - self.ExecuteBatchShards = channel.unary_unary( - '/vtgateservice.Vitess/ExecuteBatchShards', - request_serializer=vtgate__pb2.ExecuteBatchShardsRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteBatchShardsResponse.FromString, - ) - self.ExecuteBatchKeyspaceIds = channel.unary_unary( - '/vtgateservice.Vitess/ExecuteBatchKeyspaceIds', - request_serializer=vtgate__pb2.ExecuteBatchKeyspaceIdsRequest.SerializeToString, - response_deserializer=vtgate__pb2.ExecuteBatchKeyspaceIdsResponse.FromString, - ) - self.StreamExecuteShards = channel.unary_stream( - '/vtgateservice.Vitess/StreamExecuteShards', - request_serializer=vtgate__pb2.StreamExecuteShardsRequest.SerializeToString, - response_deserializer=vtgate__pb2.StreamExecuteShardsResponse.FromString, - ) - self.StreamExecuteKeyspaceIds = channel.unary_stream( - '/vtgateservice.Vitess/StreamExecuteKeyspaceIds', - request_serializer=vtgate__pb2.StreamExecuteKeyspaceIdsRequest.SerializeToString, - response_deserializer=vtgate__pb2.StreamExecuteKeyspaceIdsResponse.FromString, - ) - self.StreamExecuteKeyRanges = channel.unary_stream( - '/vtgateservice.Vitess/StreamExecuteKeyRanges', - request_serializer=vtgate__pb2.StreamExecuteKeyRangesRequest.SerializeToString, - response_deserializer=vtgate__pb2.StreamExecuteKeyRangesResponse.FromString, - ) - self.Begin = channel.unary_unary( - '/vtgateservice.Vitess/Begin', - request_serializer=vtgate__pb2.BeginRequest.SerializeToString, - response_deserializer=vtgate__pb2.BeginResponse.FromString, - ) - self.Commit = channel.unary_unary( - '/vtgateservice.Vitess/Commit', - request_serializer=vtgate__pb2.CommitRequest.SerializeToString, - response_deserializer=vtgate__pb2.CommitResponse.FromString, - ) - self.Rollback = channel.unary_unary( - '/vtgateservice.Vitess/Rollback', - request_serializer=vtgate__pb2.RollbackRequest.SerializeToString, - response_deserializer=vtgate__pb2.RollbackResponse.FromString, - ) - self.ResolveTransaction = channel.unary_unary( - '/vtgateservice.Vitess/ResolveTransaction', - request_serializer=vtgate__pb2.ResolveTransactionRequest.SerializeToString, - response_deserializer=vtgate__pb2.ResolveTransactionResponse.FromString, - ) - self.MessageStream = channel.unary_stream( - '/vtgateservice.Vitess/MessageStream', - request_serializer=vtgate__pb2.MessageStreamRequest.SerializeToString, - response_deserializer=query__pb2.MessageStreamResponse.FromString, - ) - self.MessageAck = channel.unary_unary( - '/vtgateservice.Vitess/MessageAck', - request_serializer=vtgate__pb2.MessageAckRequest.SerializeToString, - response_deserializer=query__pb2.MessageAckResponse.FromString, - ) - self.MessageAckKeyspaceIds = channel.unary_unary( - '/vtgateservice.Vitess/MessageAckKeyspaceIds', - request_serializer=vtgate__pb2.MessageAckKeyspaceIdsRequest.SerializeToString, - response_deserializer=query__pb2.MessageAckResponse.FromString, - ) - self.SplitQuery = channel.unary_unary( - '/vtgateservice.Vitess/SplitQuery', - request_serializer=vtgate__pb2.SplitQueryRequest.SerializeToString, - response_deserializer=vtgate__pb2.SplitQueryResponse.FromString, - ) - self.GetSrvKeyspace = channel.unary_unary( - '/vtgateservice.Vitess/GetSrvKeyspace', - request_serializer=vtgate__pb2.GetSrvKeyspaceRequest.SerializeToString, - response_deserializer=vtgate__pb2.GetSrvKeyspaceResponse.FromString, - ) - self.VStream = channel.unary_stream( - '/vtgateservice.Vitess/VStream', - request_serializer=vtgate__pb2.VStreamRequest.SerializeToString, - response_deserializer=vtgate__pb2.VStreamResponse.FromString, - ) - self.UpdateStream = channel.unary_stream( - '/vtgateservice.Vitess/UpdateStream', - request_serializer=vtgate__pb2.UpdateStreamRequest.SerializeToString, - response_deserializer=vtgate__pb2.UpdateStreamResponse.FromString, - ) - - -class VitessServicer(object): - """Vitess is the main service to access a Vitess cluster. It is the API that vtgate - exposes to serve all queries. - """ - - def Execute(self, request, context): - """Execute tries to route the query to the right shard. - It depends on the query and bind variables to provide enough - information in conjunction with the vindexes to route the query. - API group: v3 - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteBatch(self, request, context): - """ExecuteBatch tries to route the list of queries on the right shards. - It depends on the query and bind variables to provide enough - information in conjunction with the vindexes to route the query. - API group: v3 - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StreamExecute(self, request, context): - """StreamExecute executes a streaming query based on shards. - It depends on the query and bind variables to provide enough - information in conjunction with the vindexes to route the query. - Use this method if the query returns a large number of rows. - API group: v3 - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteShards(self, request, context): - """ExecuteShards executes the query on the specified shards. - API group: Custom Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteKeyspaceIds(self, request, context): - """ExecuteKeyspaceIds executes the query based on the specified keyspace ids. - API group: Range-based Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteKeyRanges(self, request, context): - """ExecuteKeyRanges executes the query based on the specified key ranges. - API group: Range-based Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteEntityIds(self, request, context): - """ExecuteEntityIds executes the query based on the specified external id to keyspace id map. - API group: Range-based Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteBatchShards(self, request, context): - """ExecuteBatchShards executes the list of queries on the specified shards. - API group: Custom Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ExecuteBatchKeyspaceIds(self, request, context): - """ExecuteBatchKeyspaceIds executes the list of queries based on the specified keyspace ids. - API group: Range-based Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StreamExecuteShards(self, request, context): - """StreamExecuteShards executes a streaming query based on shards. - Use this method if the query returns a large number of rows. - API group: Custom Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StreamExecuteKeyspaceIds(self, request, context): - """StreamExecuteKeyspaceIds executes a streaming query based on keyspace ids. - Use this method if the query returns a large number of rows. - API group: Range-based Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def StreamExecuteKeyRanges(self, request, context): - """StreamExecuteKeyRanges executes a streaming query based on key ranges. - Use this method if the query returns a large number of rows. - API group: Range-based Sharding - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Begin(self, request, context): - """Begin a transaction. - API group: Transactions - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Commit(self, request, context): - """Commit a transaction. - API group: Transactions - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def Rollback(self, request, context): - """Rollback a transaction. - API group: Transactions - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ResolveTransaction(self, request, context): - """ResolveTransaction resolves a transaction. - API group: Transactions - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MessageStream(self, request, context): - """MessageStream streams messages from a message table. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MessageAck(self, request, context): - """MessageAck acks messages for a table. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def MessageAckKeyspaceIds(self, request, context): - """MessageAckKeyspaceIds routes Message Acks using the associated - keyspace ids. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def SplitQuery(self, request, context): - """Split a query into non-overlapping sub queries - API group: Map Reduce - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GetSrvKeyspace(self, request, context): - """GetSrvKeyspace returns a SrvKeyspace object (as seen by this vtgate). - This method is provided as a convenient way for clients to take a - look at the sharding configuration for a Keyspace. Looking at the - sharding information should not be used for routing queries (as the - information may change, use the Execute calls for that). - It is convenient for monitoring applications for instance, or if - using custom sharding. - API group: Topology - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def VStream(self, request, context): - """VStream streams binlog events from the requested sources. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def UpdateStream(self, request, context): - """UpdateStream asks the server for a stream of StreamEvent objects. - API group: Update Stream - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_VitessServicer_to_server(servicer, server): - rpc_method_handlers = { - 'Execute': grpc.unary_unary_rpc_method_handler( - servicer.Execute, - request_deserializer=vtgate__pb2.ExecuteRequest.FromString, - response_serializer=vtgate__pb2.ExecuteResponse.SerializeToString, - ), - 'ExecuteBatch': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteBatch, - request_deserializer=vtgate__pb2.ExecuteBatchRequest.FromString, - response_serializer=vtgate__pb2.ExecuteBatchResponse.SerializeToString, - ), - 'StreamExecute': grpc.unary_stream_rpc_method_handler( - servicer.StreamExecute, - request_deserializer=vtgate__pb2.StreamExecuteRequest.FromString, - response_serializer=vtgate__pb2.StreamExecuteResponse.SerializeToString, - ), - 'ExecuteShards': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteShards, - request_deserializer=vtgate__pb2.ExecuteShardsRequest.FromString, - response_serializer=vtgate__pb2.ExecuteShardsResponse.SerializeToString, - ), - 'ExecuteKeyspaceIds': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteKeyspaceIds, - request_deserializer=vtgate__pb2.ExecuteKeyspaceIdsRequest.FromString, - response_serializer=vtgate__pb2.ExecuteKeyspaceIdsResponse.SerializeToString, - ), - 'ExecuteKeyRanges': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteKeyRanges, - request_deserializer=vtgate__pb2.ExecuteKeyRangesRequest.FromString, - response_serializer=vtgate__pb2.ExecuteKeyRangesResponse.SerializeToString, - ), - 'ExecuteEntityIds': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteEntityIds, - request_deserializer=vtgate__pb2.ExecuteEntityIdsRequest.FromString, - response_serializer=vtgate__pb2.ExecuteEntityIdsResponse.SerializeToString, - ), - 'ExecuteBatchShards': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteBatchShards, - request_deserializer=vtgate__pb2.ExecuteBatchShardsRequest.FromString, - response_serializer=vtgate__pb2.ExecuteBatchShardsResponse.SerializeToString, - ), - 'ExecuteBatchKeyspaceIds': grpc.unary_unary_rpc_method_handler( - servicer.ExecuteBatchKeyspaceIds, - request_deserializer=vtgate__pb2.ExecuteBatchKeyspaceIdsRequest.FromString, - response_serializer=vtgate__pb2.ExecuteBatchKeyspaceIdsResponse.SerializeToString, - ), - 'StreamExecuteShards': grpc.unary_stream_rpc_method_handler( - servicer.StreamExecuteShards, - request_deserializer=vtgate__pb2.StreamExecuteShardsRequest.FromString, - response_serializer=vtgate__pb2.StreamExecuteShardsResponse.SerializeToString, - ), - 'StreamExecuteKeyspaceIds': grpc.unary_stream_rpc_method_handler( - servicer.StreamExecuteKeyspaceIds, - request_deserializer=vtgate__pb2.StreamExecuteKeyspaceIdsRequest.FromString, - response_serializer=vtgate__pb2.StreamExecuteKeyspaceIdsResponse.SerializeToString, - ), - 'StreamExecuteKeyRanges': grpc.unary_stream_rpc_method_handler( - servicer.StreamExecuteKeyRanges, - request_deserializer=vtgate__pb2.StreamExecuteKeyRangesRequest.FromString, - response_serializer=vtgate__pb2.StreamExecuteKeyRangesResponse.SerializeToString, - ), - 'Begin': grpc.unary_unary_rpc_method_handler( - servicer.Begin, - request_deserializer=vtgate__pb2.BeginRequest.FromString, - response_serializer=vtgate__pb2.BeginResponse.SerializeToString, - ), - 'Commit': grpc.unary_unary_rpc_method_handler( - servicer.Commit, - request_deserializer=vtgate__pb2.CommitRequest.FromString, - response_serializer=vtgate__pb2.CommitResponse.SerializeToString, - ), - 'Rollback': grpc.unary_unary_rpc_method_handler( - servicer.Rollback, - request_deserializer=vtgate__pb2.RollbackRequest.FromString, - response_serializer=vtgate__pb2.RollbackResponse.SerializeToString, - ), - 'ResolveTransaction': grpc.unary_unary_rpc_method_handler( - servicer.ResolveTransaction, - request_deserializer=vtgate__pb2.ResolveTransactionRequest.FromString, - response_serializer=vtgate__pb2.ResolveTransactionResponse.SerializeToString, - ), - 'MessageStream': grpc.unary_stream_rpc_method_handler( - servicer.MessageStream, - request_deserializer=vtgate__pb2.MessageStreamRequest.FromString, - response_serializer=query__pb2.MessageStreamResponse.SerializeToString, - ), - 'MessageAck': grpc.unary_unary_rpc_method_handler( - servicer.MessageAck, - request_deserializer=vtgate__pb2.MessageAckRequest.FromString, - response_serializer=query__pb2.MessageAckResponse.SerializeToString, - ), - 'MessageAckKeyspaceIds': grpc.unary_unary_rpc_method_handler( - servicer.MessageAckKeyspaceIds, - request_deserializer=vtgate__pb2.MessageAckKeyspaceIdsRequest.FromString, - response_serializer=query__pb2.MessageAckResponse.SerializeToString, - ), - 'SplitQuery': grpc.unary_unary_rpc_method_handler( - servicer.SplitQuery, - request_deserializer=vtgate__pb2.SplitQueryRequest.FromString, - response_serializer=vtgate__pb2.SplitQueryResponse.SerializeToString, - ), - 'GetSrvKeyspace': grpc.unary_unary_rpc_method_handler( - servicer.GetSrvKeyspace, - request_deserializer=vtgate__pb2.GetSrvKeyspaceRequest.FromString, - response_serializer=vtgate__pb2.GetSrvKeyspaceResponse.SerializeToString, - ), - 'VStream': grpc.unary_stream_rpc_method_handler( - servicer.VStream, - request_deserializer=vtgate__pb2.VStreamRequest.FromString, - response_serializer=vtgate__pb2.VStreamResponse.SerializeToString, - ), - 'UpdateStream': grpc.unary_stream_rpc_method_handler( - servicer.UpdateStream, - request_deserializer=vtgate__pb2.UpdateStreamRequest.FromString, - response_serializer=vtgate__pb2.UpdateStreamResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'vtgateservice.Vitess', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/vtrpc_pb2.py b/py/vtproto/vtrpc_pb2.py deleted file mode 100644 index 39c07d0a732..00000000000 --- a/py/vtproto/vtrpc_pb2.py +++ /dev/null @@ -1,322 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vtrpc.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vtrpc.proto', - package='vtrpc', - syntax='proto3', - serialized_options=_b('\n\017io.vitess.protoZ\"vitess.io/vitess/go/vt/proto/vtrpc'), - serialized_pb=_b('\n\x0bvtrpc.proto\x12\x05vtrpc\"F\n\x08\x43\x61llerID\x12\x11\n\tprincipal\x18\x01 \x01(\t\x12\x11\n\tcomponent\x18\x02 \x01(\t\x12\x14\n\x0csubcomponent\x18\x03 \x01(\t\"c\n\x08RPCError\x12+\n\x0blegacy_code\x18\x01 \x01(\x0e\x32\x16.vtrpc.LegacyErrorCode\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x19\n\x04\x63ode\x18\x03 \x01(\x0e\x32\x0b.vtrpc.Code*\xb6\x02\n\x04\x43ode\x12\x06\n\x02OK\x10\x00\x12\x0c\n\x08\x43\x41NCELED\x10\x01\x12\x0b\n\x07UNKNOWN\x10\x02\x12\x14\n\x10INVALID_ARGUMENT\x10\x03\x12\x15\n\x11\x44\x45\x41\x44LINE_EXCEEDED\x10\x04\x12\r\n\tNOT_FOUND\x10\x05\x12\x12\n\x0e\x41LREADY_EXISTS\x10\x06\x12\x15\n\x11PERMISSION_DENIED\x10\x07\x12\x13\n\x0fUNAUTHENTICATED\x10\x10\x12\x16\n\x12RESOURCE_EXHAUSTED\x10\x08\x12\x17\n\x13\x46\x41ILED_PRECONDITION\x10\t\x12\x0b\n\x07\x41\x42ORTED\x10\n\x12\x10\n\x0cOUT_OF_RANGE\x10\x0b\x12\x11\n\rUNIMPLEMENTED\x10\x0c\x12\x0c\n\x08INTERNAL\x10\r\x12\x0f\n\x0bUNAVAILABLE\x10\x0e\x12\r\n\tDATA_LOSS\x10\x0f*\xe8\x02\n\x0fLegacyErrorCode\x12\x12\n\x0eSUCCESS_LEGACY\x10\x00\x12\x14\n\x10\x43\x41NCELLED_LEGACY\x10\x01\x12\x18\n\x14UNKNOWN_ERROR_LEGACY\x10\x02\x12\x14\n\x10\x42\x41\x44_INPUT_LEGACY\x10\x03\x12\x1c\n\x18\x44\x45\x41\x44LINE_EXCEEDED_LEGACY\x10\x04\x12\x1a\n\x16INTEGRITY_ERROR_LEGACY\x10\x05\x12\x1c\n\x18PERMISSION_DENIED_LEGACY\x10\x06\x12\x1d\n\x19RESOURCE_EXHAUSTED_LEGACY\x10\x07\x12\x1b\n\x17QUERY_NOT_SERVED_LEGACY\x10\x08\x12\x14\n\x10NOT_IN_TX_LEGACY\x10\t\x12\x19\n\x15INTERNAL_ERROR_LEGACY\x10\n\x12\x1a\n\x16TRANSIENT_ERROR_LEGACY\x10\x0b\x12\x1a\n\x16UNAUTHENTICATED_LEGACY\x10\x0c\x42\x35\n\x0fio.vitess.protoZ\"vitess.io/vitess/go/vt/proto/vtrpcb\x06proto3') -) - -_CODE = _descriptor.EnumDescriptor( - name='Code', - full_name='vtrpc.Code', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='OK', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CANCELED', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INVALID_ARGUMENT', index=3, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DEADLINE_EXCEEDED', index=4, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_FOUND', index=5, number=5, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ALREADY_EXISTS', index=6, number=6, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PERMISSION_DENIED', index=7, number=7, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNAUTHENTICATED', index=8, number=16, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RESOURCE_EXHAUSTED', index=9, number=8, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='FAILED_PRECONDITION', index=10, number=9, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ABORTED', index=11, number=10, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='OUT_OF_RANGE', index=12, number=11, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNIMPLEMENTED', index=13, number=12, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INTERNAL', index=14, number=13, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNAVAILABLE', index=15, number=14, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DATA_LOSS', index=16, number=15, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=196, - serialized_end=506, -) -_sym_db.RegisterEnumDescriptor(_CODE) - -Code = enum_type_wrapper.EnumTypeWrapper(_CODE) -_LEGACYERRORCODE = _descriptor.EnumDescriptor( - name='LegacyErrorCode', - full_name='vtrpc.LegacyErrorCode', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='SUCCESS_LEGACY', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CANCELLED_LEGACY', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNKNOWN_ERROR_LEGACY', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='BAD_INPUT_LEGACY', index=3, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DEADLINE_EXCEEDED_LEGACY', index=4, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INTEGRITY_ERROR_LEGACY', index=5, number=5, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='PERMISSION_DENIED_LEGACY', index=6, number=6, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='RESOURCE_EXHAUSTED_LEGACY', index=7, number=7, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='QUERY_NOT_SERVED_LEGACY', index=8, number=8, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='NOT_IN_TX_LEGACY', index=9, number=9, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INTERNAL_ERROR_LEGACY', index=10, number=10, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TRANSIENT_ERROR_LEGACY', index=11, number=11, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='UNAUTHENTICATED_LEGACY', index=12, number=12, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=509, - serialized_end=869, -) -_sym_db.RegisterEnumDescriptor(_LEGACYERRORCODE) - -LegacyErrorCode = enum_type_wrapper.EnumTypeWrapper(_LEGACYERRORCODE) -OK = 0 -CANCELED = 1 -UNKNOWN = 2 -INVALID_ARGUMENT = 3 -DEADLINE_EXCEEDED = 4 -NOT_FOUND = 5 -ALREADY_EXISTS = 6 -PERMISSION_DENIED = 7 -UNAUTHENTICATED = 16 -RESOURCE_EXHAUSTED = 8 -FAILED_PRECONDITION = 9 -ABORTED = 10 -OUT_OF_RANGE = 11 -UNIMPLEMENTED = 12 -INTERNAL = 13 -UNAVAILABLE = 14 -DATA_LOSS = 15 -SUCCESS_LEGACY = 0 -CANCELLED_LEGACY = 1 -UNKNOWN_ERROR_LEGACY = 2 -BAD_INPUT_LEGACY = 3 -DEADLINE_EXCEEDED_LEGACY = 4 -INTEGRITY_ERROR_LEGACY = 5 -PERMISSION_DENIED_LEGACY = 6 -RESOURCE_EXHAUSTED_LEGACY = 7 -QUERY_NOT_SERVED_LEGACY = 8 -NOT_IN_TX_LEGACY = 9 -INTERNAL_ERROR_LEGACY = 10 -TRANSIENT_ERROR_LEGACY = 11 -UNAUTHENTICATED_LEGACY = 12 - - - -_CALLERID = _descriptor.Descriptor( - name='CallerID', - full_name='vtrpc.CallerID', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='principal', full_name='vtrpc.CallerID.principal', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='component', full_name='vtrpc.CallerID.component', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='subcomponent', full_name='vtrpc.CallerID.subcomponent', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=22, - serialized_end=92, -) - - -_RPCERROR = _descriptor.Descriptor( - name='RPCError', - full_name='vtrpc.RPCError', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='legacy_code', full_name='vtrpc.RPCError.legacy_code', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='message', full_name='vtrpc.RPCError.message', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='code', full_name='vtrpc.RPCError.code', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=94, - serialized_end=193, -) - -_RPCERROR.fields_by_name['legacy_code'].enum_type = _LEGACYERRORCODE -_RPCERROR.fields_by_name['code'].enum_type = _CODE -DESCRIPTOR.message_types_by_name['CallerID'] = _CALLERID -DESCRIPTOR.message_types_by_name['RPCError'] = _RPCERROR -DESCRIPTOR.enum_types_by_name['Code'] = _CODE -DESCRIPTOR.enum_types_by_name['LegacyErrorCode'] = _LEGACYERRORCODE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -CallerID = _reflection.GeneratedProtocolMessageType('CallerID', (_message.Message,), dict( - DESCRIPTOR = _CALLERID, - __module__ = 'vtrpc_pb2' - # @@protoc_insertion_point(class_scope:vtrpc.CallerID) - )) -_sym_db.RegisterMessage(CallerID) - -RPCError = _reflection.GeneratedProtocolMessageType('RPCError', (_message.Message,), dict( - DESCRIPTOR = _RPCERROR, - __module__ = 'vtrpc_pb2' - # @@protoc_insertion_point(class_scope:vtrpc.RPCError) - )) -_sym_db.RegisterMessage(RPCError) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vtrpc_pb2_grpc.py b/py/vtproto/vtrpc_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vtrpc_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vttest_pb2.py b/py/vtproto/vttest_pb2.py deleted file mode 100644 index 1c414f959bd..00000000000 --- a/py/vtproto/vttest_pb2.py +++ /dev/null @@ -1,206 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vttest.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vttest.proto', - package='vttest', - syntax='proto3', - serialized_options=_b('Z#vitess.io/vitess/go/vt/proto/vttest'), - serialized_pb=_b('\n\x0cvttest.proto\x12\x06vttest\"/\n\x05Shard\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x18\n\x10\x64\x62_name_override\x18\x02 \x01(\t\"\xb5\x01\n\x08Keyspace\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1d\n\x06shards\x18\x02 \x03(\x0b\x32\r.vttest.Shard\x12\x1c\n\x14sharding_column_name\x18\x03 \x01(\t\x12\x1c\n\x14sharding_column_type\x18\x04 \x01(\t\x12\x13\n\x0bserved_from\x18\x05 \x01(\t\x12\x15\n\rreplica_count\x18\x06 \x01(\x05\x12\x14\n\x0crdonly_count\x18\x07 \x01(\x05\"D\n\x0eVTTestTopology\x12#\n\tkeyspaces\x18\x01 \x03(\x0b\x32\x10.vttest.Keyspace\x12\r\n\x05\x63\x65lls\x18\x02 \x03(\tB%Z#vitess.io/vitess/go/vt/proto/vttestb\x06proto3') -) - - - - -_SHARD = _descriptor.Descriptor( - name='Shard', - full_name='vttest.Shard', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='vttest.Shard.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='db_name_override', full_name='vttest.Shard.db_name_override', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=24, - serialized_end=71, -) - - -_KEYSPACE = _descriptor.Descriptor( - name='Keyspace', - full_name='vttest.Keyspace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='vttest.Keyspace.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shards', full_name='vttest.Keyspace.shards', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sharding_column_name', full_name='vttest.Keyspace.sharding_column_name', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sharding_column_type', full_name='vttest.Keyspace.sharding_column_type', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='served_from', full_name='vttest.Keyspace.served_from', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='replica_count', full_name='vttest.Keyspace.replica_count', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rdonly_count', full_name='vttest.Keyspace.rdonly_count', index=6, - number=7, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=74, - serialized_end=255, -) - - -_VTTESTTOPOLOGY = _descriptor.Descriptor( - name='VTTestTopology', - full_name='vttest.VTTestTopology', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspaces', full_name='vttest.VTTestTopology.keyspaces', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cells', full_name='vttest.VTTestTopology.cells', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=257, - serialized_end=325, -) - -_KEYSPACE.fields_by_name['shards'].message_type = _SHARD -_VTTESTTOPOLOGY.fields_by_name['keyspaces'].message_type = _KEYSPACE -DESCRIPTOR.message_types_by_name['Shard'] = _SHARD -DESCRIPTOR.message_types_by_name['Keyspace'] = _KEYSPACE -DESCRIPTOR.message_types_by_name['VTTestTopology'] = _VTTESTTOPOLOGY -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Shard = _reflection.GeneratedProtocolMessageType('Shard', (_message.Message,), dict( - DESCRIPTOR = _SHARD, - __module__ = 'vttest_pb2' - # @@protoc_insertion_point(class_scope:vttest.Shard) - )) -_sym_db.RegisterMessage(Shard) - -Keyspace = _reflection.GeneratedProtocolMessageType('Keyspace', (_message.Message,), dict( - DESCRIPTOR = _KEYSPACE, - __module__ = 'vttest_pb2' - # @@protoc_insertion_point(class_scope:vttest.Keyspace) - )) -_sym_db.RegisterMessage(Keyspace) - -VTTestTopology = _reflection.GeneratedProtocolMessageType('VTTestTopology', (_message.Message,), dict( - DESCRIPTOR = _VTTESTTOPOLOGY, - __module__ = 'vttest_pb2' - # @@protoc_insertion_point(class_scope:vttest.VTTestTopology) - )) -_sym_db.RegisterMessage(VTTestTopology) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vttest_pb2_grpc.py b/py/vtproto/vttest_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vttest_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vttime_pb2.py b/py/vtproto/vttime_pb2.py deleted file mode 100644 index 7202a865222..00000000000 --- a/py/vtproto/vttime_pb2.py +++ /dev/null @@ -1,77 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vttime.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vttime.proto', - package='vttime', - syntax='proto3', - serialized_options=_b('Z#vitess.io/vitess/go/vt/proto/vttime'), - serialized_pb=_b('\n\x0cvttime.proto\x12\x06vttime\",\n\x04Time\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\x13\n\x0bnanoseconds\x18\x02 \x01(\x05\x42%Z#vitess.io/vitess/go/vt/proto/vttimeb\x06proto3') -) - - - - -_TIME = _descriptor.Descriptor( - name='Time', - full_name='vttime.Time', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='seconds', full_name='vttime.Time.seconds', index=0, - number=1, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='nanoseconds', full_name='vttime.Time.nanoseconds', index=1, - number=2, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=24, - serialized_end=68, -) - -DESCRIPTOR.message_types_by_name['Time'] = _TIME -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Time = _reflection.GeneratedProtocolMessageType('Time', (_message.Message,), dict( - DESCRIPTOR = _TIME, - __module__ = 'vttime_pb2' - # @@protoc_insertion_point(class_scope:vttime.Time) - )) -_sym_db.RegisterMessage(Time) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vttime_pb2_grpc.py b/py/vtproto/vttime_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vttime_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vtworkerdata_pb2.py b/py/vtproto/vtworkerdata_pb2.py deleted file mode 100644 index f319647d24b..00000000000 --- a/py/vtproto/vtworkerdata_pb2.py +++ /dev/null @@ -1,112 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vtworkerdata.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import logutil_pb2 as logutil__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vtworkerdata.proto', - package='vtworkerdata', - syntax='proto3', - serialized_options=_b('Z)vitess.io/vitess/go/vt/proto/vtworkerdata'), - serialized_pb=_b('\n\x12vtworkerdata.proto\x12\x0cvtworkerdata\x1a\rlogutil.proto\"-\n\x1d\x45xecuteVtworkerCommandRequest\x12\x0c\n\x04\x61rgs\x18\x01 \x03(\t\"?\n\x1e\x45xecuteVtworkerCommandResponse\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.logutil.EventB+Z)vitess.io/vitess/go/vt/proto/vtworkerdatab\x06proto3') - , - dependencies=[logutil__pb2.DESCRIPTOR,]) - - - - -_EXECUTEVTWORKERCOMMANDREQUEST = _descriptor.Descriptor( - name='ExecuteVtworkerCommandRequest', - full_name='vtworkerdata.ExecuteVtworkerCommandRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='args', full_name='vtworkerdata.ExecuteVtworkerCommandRequest.args', index=0, - number=1, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=51, - serialized_end=96, -) - - -_EXECUTEVTWORKERCOMMANDRESPONSE = _descriptor.Descriptor( - name='ExecuteVtworkerCommandResponse', - full_name='vtworkerdata.ExecuteVtworkerCommandResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='event', full_name='vtworkerdata.ExecuteVtworkerCommandResponse.event', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=98, - serialized_end=161, -) - -_EXECUTEVTWORKERCOMMANDRESPONSE.fields_by_name['event'].message_type = logutil__pb2._EVENT -DESCRIPTOR.message_types_by_name['ExecuteVtworkerCommandRequest'] = _EXECUTEVTWORKERCOMMANDREQUEST -DESCRIPTOR.message_types_by_name['ExecuteVtworkerCommandResponse'] = _EXECUTEVTWORKERCOMMANDRESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -ExecuteVtworkerCommandRequest = _reflection.GeneratedProtocolMessageType('ExecuteVtworkerCommandRequest', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEVTWORKERCOMMANDREQUEST, - __module__ = 'vtworkerdata_pb2' - # @@protoc_insertion_point(class_scope:vtworkerdata.ExecuteVtworkerCommandRequest) - )) -_sym_db.RegisterMessage(ExecuteVtworkerCommandRequest) - -ExecuteVtworkerCommandResponse = _reflection.GeneratedProtocolMessageType('ExecuteVtworkerCommandResponse', (_message.Message,), dict( - DESCRIPTOR = _EXECUTEVTWORKERCOMMANDRESPONSE, - __module__ = 'vtworkerdata_pb2' - # @@protoc_insertion_point(class_scope:vtworkerdata.ExecuteVtworkerCommandResponse) - )) -_sym_db.RegisterMessage(ExecuteVtworkerCommandResponse) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vtworkerdata_pb2_grpc.py b/py/vtproto/vtworkerdata_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vtworkerdata_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vtproto/vtworkerservice_pb2.py b/py/vtproto/vtworkerservice_pb2.py deleted file mode 100644 index 9c7ad0640ab..00000000000 --- a/py/vtproto/vtworkerservice_pb2.py +++ /dev/null @@ -1,57 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vtworkerservice.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -import vtworkerdata_pb2 as vtworkerdata__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vtworkerservice.proto', - package='vtworkerservice', - syntax='proto3', - serialized_options=_b('Z,vitess.io/vitess/go/vt/proto/vtworkerservice'), - serialized_pb=_b('\n\x15vtworkerservice.proto\x12\x0fvtworkerservice\x1a\x12vtworkerdata.proto2\x83\x01\n\x08Vtworker\x12w\n\x16\x45xecuteVtworkerCommand\x12+.vtworkerdata.ExecuteVtworkerCommandRequest\x1a,.vtworkerdata.ExecuteVtworkerCommandResponse\"\x00\x30\x01\x42.Z,vitess.io/vitess/go/vt/proto/vtworkerserviceb\x06proto3') - , - dependencies=[vtworkerdata__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_VTWORKER = _descriptor.ServiceDescriptor( - name='Vtworker', - full_name='vtworkerservice.Vtworker', - file=DESCRIPTOR, - index=0, - serialized_options=None, - serialized_start=63, - serialized_end=194, - methods=[ - _descriptor.MethodDescriptor( - name='ExecuteVtworkerCommand', - full_name='vtworkerservice.Vtworker.ExecuteVtworkerCommand', - index=0, - containing_service=None, - input_type=vtworkerdata__pb2._EXECUTEVTWORKERCOMMANDREQUEST, - output_type=vtworkerdata__pb2._EXECUTEVTWORKERCOMMANDRESPONSE, - serialized_options=None, - ), -]) -_sym_db.RegisterServiceDescriptor(_VTWORKER) - -DESCRIPTOR.services_by_name['Vtworker'] = _VTWORKER - -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vtworkerservice_pb2_grpc.py b/py/vtproto/vtworkerservice_pb2_grpc.py deleted file mode 100644 index cef8bd35f8a..00000000000 --- a/py/vtproto/vtworkerservice_pb2_grpc.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - -import vtworkerdata_pb2 as vtworkerdata__pb2 - - -class VtworkerStub(object): - """Vtworker contains the vtworker RPC calls. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.ExecuteVtworkerCommand = channel.unary_stream( - '/vtworkerservice.Vtworker/ExecuteVtworkerCommand', - request_serializer=vtworkerdata__pb2.ExecuteVtworkerCommandRequest.SerializeToString, - response_deserializer=vtworkerdata__pb2.ExecuteVtworkerCommandResponse.FromString, - ) - - -class VtworkerServicer(object): - """Vtworker contains the vtworker RPC calls. - """ - - def ExecuteVtworkerCommand(self, request, context): - """ExecuteVtworkerCommand allows to run a vtworker command by specifying the - same arguments as on the command line. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_VtworkerServicer_to_server(servicer, server): - rpc_method_handlers = { - 'ExecuteVtworkerCommand': grpc.unary_stream_rpc_method_handler( - servicer.ExecuteVtworkerCommand, - request_deserializer=vtworkerdata__pb2.ExecuteVtworkerCommandRequest.FromString, - response_serializer=vtworkerdata__pb2.ExecuteVtworkerCommandResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'vtworkerservice.Vtworker', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) diff --git a/py/vtproto/workflow_pb2.py b/py/vtproto/workflow_pb2.py deleted file mode 100644 index c624dfe8fc4..00000000000 --- a/py/vtproto/workflow_pb2.py +++ /dev/null @@ -1,449 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: workflow.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='workflow.proto', - package='workflow', - syntax='proto3', - serialized_options=_b('Z%vitess.io/vitess/go/vt/proto/workflow'), - serialized_pb=_b('\n\x0eworkflow.proto\x12\x08workflow\"\xbc\x01\n\x08Workflow\x12\x0c\n\x04uuid\x18\x01 \x01(\t\x12\x14\n\x0c\x66\x61\x63tory_name\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12&\n\x05state\x18\x04 \x01(\x0e\x32\x17.workflow.WorkflowState\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\x12\r\n\x05\x65rror\x18\x06 \x01(\t\x12\x12\n\nstart_time\x18\x07 \x01(\x03\x12\x10\n\x08\x65nd_time\x18\x08 \x01(\x03\x12\x13\n\x0b\x63reate_time\x18\t \x01(\x03\"\x8f\x02\n\x12WorkflowCheckpoint\x12\x14\n\x0c\x63ode_version\x18\x01 \x01(\x05\x12\x36\n\x05tasks\x18\x02 \x03(\x0b\x32\'.workflow.WorkflowCheckpoint.TasksEntry\x12<\n\x08settings\x18\x03 \x03(\x0b\x32*.workflow.WorkflowCheckpoint.SettingsEntry\x1a<\n\nTasksEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1d\n\x05value\x18\x02 \x01(\x0b\x32\x0e.workflow.Task:\x02\x38\x01\x1a/\n\rSettingsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xac\x01\n\x04Task\x12\n\n\x02id\x18\x01 \x01(\t\x12\"\n\x05state\x18\x02 \x01(\x0e\x32\x13.workflow.TaskState\x12\x32\n\nattributes\x18\x03 \x03(\x0b\x32\x1e.workflow.Task.AttributesEntry\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01*6\n\rWorkflowState\x12\x0e\n\nNotStarted\x10\x00\x12\x0b\n\x07Running\x10\x01\x12\x08\n\x04\x44one\x10\x02*>\n\tTaskState\x12\x12\n\x0eTaskNotStarted\x10\x00\x12\x0f\n\x0bTaskRunning\x10\x01\x12\x0c\n\x08TaskDone\x10\x02\x42\'Z%vitess.io/vitess/go/vt/proto/workflowb\x06proto3') -) - -_WORKFLOWSTATE = _descriptor.EnumDescriptor( - name='WorkflowState', - full_name='workflow.WorkflowState', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NotStarted', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Running', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='Done', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=668, - serialized_end=722, -) -_sym_db.RegisterEnumDescriptor(_WORKFLOWSTATE) - -WorkflowState = enum_type_wrapper.EnumTypeWrapper(_WORKFLOWSTATE) -_TASKSTATE = _descriptor.EnumDescriptor( - name='TaskState', - full_name='workflow.TaskState', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='TaskNotStarted', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TaskRunning', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='TaskDone', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=724, - serialized_end=786, -) -_sym_db.RegisterEnumDescriptor(_TASKSTATE) - -TaskState = enum_type_wrapper.EnumTypeWrapper(_TASKSTATE) -NotStarted = 0 -Running = 1 -Done = 2 -TaskNotStarted = 0 -TaskRunning = 1 -TaskDone = 2 - - - -_WORKFLOW = _descriptor.Descriptor( - name='Workflow', - full_name='workflow.Workflow', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='uuid', full_name='workflow.Workflow.uuid', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='factory_name', full_name='workflow.Workflow.factory_name', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='workflow.Workflow.name', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='workflow.Workflow.state', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='data', full_name='workflow.Workflow.data', index=4, - number=5, type=12, cpp_type=9, label=1, - has_default_value=False, default_value=_b(""), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='error', full_name='workflow.Workflow.error', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='start_time', full_name='workflow.Workflow.start_time', index=6, - number=7, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='end_time', full_name='workflow.Workflow.end_time', index=7, - number=8, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='create_time', full_name='workflow.Workflow.create_time', index=8, - number=9, type=3, cpp_type=2, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=29, - serialized_end=217, -) - - -_WORKFLOWCHECKPOINT_TASKSENTRY = _descriptor.Descriptor( - name='TasksEntry', - full_name='workflow.WorkflowCheckpoint.TasksEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='workflow.WorkflowCheckpoint.TasksEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='workflow.WorkflowCheckpoint.TasksEntry.value', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=382, - serialized_end=442, -) - -_WORKFLOWCHECKPOINT_SETTINGSENTRY = _descriptor.Descriptor( - name='SettingsEntry', - full_name='workflow.WorkflowCheckpoint.SettingsEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='workflow.WorkflowCheckpoint.SettingsEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='workflow.WorkflowCheckpoint.SettingsEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=444, - serialized_end=491, -) - -_WORKFLOWCHECKPOINT = _descriptor.Descriptor( - name='WorkflowCheckpoint', - full_name='workflow.WorkflowCheckpoint', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='code_version', full_name='workflow.WorkflowCheckpoint.code_version', index=0, - number=1, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tasks', full_name='workflow.WorkflowCheckpoint.tasks', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='settings', full_name='workflow.WorkflowCheckpoint.settings', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_WORKFLOWCHECKPOINT_TASKSENTRY, _WORKFLOWCHECKPOINT_SETTINGSENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=220, - serialized_end=491, -) - - -_TASK_ATTRIBUTESENTRY = _descriptor.Descriptor( - name='AttributesEntry', - full_name='workflow.Task.AttributesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='workflow.Task.AttributesEntry.key', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='workflow.Task.AttributesEntry.value', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=617, - serialized_end=666, -) - -_TASK = _descriptor.Descriptor( - name='Task', - full_name='workflow.Task', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='id', full_name='workflow.Task.id', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='workflow.Task.state', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='attributes', full_name='workflow.Task.attributes', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='error', full_name='workflow.Task.error', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_TASK_ATTRIBUTESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=494, - serialized_end=666, -) - -_WORKFLOW.fields_by_name['state'].enum_type = _WORKFLOWSTATE -_WORKFLOWCHECKPOINT_TASKSENTRY.fields_by_name['value'].message_type = _TASK -_WORKFLOWCHECKPOINT_TASKSENTRY.containing_type = _WORKFLOWCHECKPOINT -_WORKFLOWCHECKPOINT_SETTINGSENTRY.containing_type = _WORKFLOWCHECKPOINT -_WORKFLOWCHECKPOINT.fields_by_name['tasks'].message_type = _WORKFLOWCHECKPOINT_TASKSENTRY -_WORKFLOWCHECKPOINT.fields_by_name['settings'].message_type = _WORKFLOWCHECKPOINT_SETTINGSENTRY -_TASK_ATTRIBUTESENTRY.containing_type = _TASK -_TASK.fields_by_name['state'].enum_type = _TASKSTATE -_TASK.fields_by_name['attributes'].message_type = _TASK_ATTRIBUTESENTRY -DESCRIPTOR.message_types_by_name['Workflow'] = _WORKFLOW -DESCRIPTOR.message_types_by_name['WorkflowCheckpoint'] = _WORKFLOWCHECKPOINT -DESCRIPTOR.message_types_by_name['Task'] = _TASK -DESCRIPTOR.enum_types_by_name['WorkflowState'] = _WORKFLOWSTATE -DESCRIPTOR.enum_types_by_name['TaskState'] = _TASKSTATE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Workflow = _reflection.GeneratedProtocolMessageType('Workflow', (_message.Message,), dict( - DESCRIPTOR = _WORKFLOW, - __module__ = 'workflow_pb2' - # @@protoc_insertion_point(class_scope:workflow.Workflow) - )) -_sym_db.RegisterMessage(Workflow) - -WorkflowCheckpoint = _reflection.GeneratedProtocolMessageType('WorkflowCheckpoint', (_message.Message,), dict( - - TasksEntry = _reflection.GeneratedProtocolMessageType('TasksEntry', (_message.Message,), dict( - DESCRIPTOR = _WORKFLOWCHECKPOINT_TASKSENTRY, - __module__ = 'workflow_pb2' - # @@protoc_insertion_point(class_scope:workflow.WorkflowCheckpoint.TasksEntry) - )) - , - - SettingsEntry = _reflection.GeneratedProtocolMessageType('SettingsEntry', (_message.Message,), dict( - DESCRIPTOR = _WORKFLOWCHECKPOINT_SETTINGSENTRY, - __module__ = 'workflow_pb2' - # @@protoc_insertion_point(class_scope:workflow.WorkflowCheckpoint.SettingsEntry) - )) - , - DESCRIPTOR = _WORKFLOWCHECKPOINT, - __module__ = 'workflow_pb2' - # @@protoc_insertion_point(class_scope:workflow.WorkflowCheckpoint) - )) -_sym_db.RegisterMessage(WorkflowCheckpoint) -_sym_db.RegisterMessage(WorkflowCheckpoint.TasksEntry) -_sym_db.RegisterMessage(WorkflowCheckpoint.SettingsEntry) - -Task = _reflection.GeneratedProtocolMessageType('Task', (_message.Message,), dict( - - AttributesEntry = _reflection.GeneratedProtocolMessageType('AttributesEntry', (_message.Message,), dict( - DESCRIPTOR = _TASK_ATTRIBUTESENTRY, - __module__ = 'workflow_pb2' - # @@protoc_insertion_point(class_scope:workflow.Task.AttributesEntry) - )) - , - DESCRIPTOR = _TASK, - __module__ = 'workflow_pb2' - # @@protoc_insertion_point(class_scope:workflow.Task) - )) -_sym_db.RegisterMessage(Task) -_sym_db.RegisterMessage(Task.AttributesEntry) - - -DESCRIPTOR._options = None -_WORKFLOWCHECKPOINT_TASKSENTRY._options = None -_WORKFLOWCHECKPOINT_SETTINGSENTRY._options = None -_TASK_ATTRIBUTESENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/workflow_pb2_grpc.py b/py/vtproto/workflow_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/workflow_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vttest/__init__.py b/py/vttest/__init__.py deleted file mode 100644 index 4d32e37cccb..00000000000 --- a/py/vttest/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from vttest import environment -from vttest import mysql_db_mysqlctl - -environment.mysql_db_class = mysql_db_mysqlctl.MySqlDBMysqlctl diff --git a/py/vttest/environment.py b/py/vttest/environment.py deleted file mode 100644 index b84d14814de..00000000000 --- a/py/vttest/environment.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Contains environment specifications for vttest module. - -This module is meant to be overwritten upon import into a development -tree with the appropriate values. It works as is in the Vitess tree. -""" - -import os -import shutil -import tempfile - -# this is the location of the vtcombo binary -vtcombo_binary = os.path.join(os.environ['VTROOT'], 'bin', 'vtcombo') - -# this is the location of the mysqlctl binary, if mysql_db_mysqlctl is used. -mysqlctl_binary = os.path.join(os.environ['VTROOT'], 'bin', 'mysqlctl') - -# this is the base port set by options. -base_port = None - -# this is the class to use for MySqlDB instances -mysql_db_class = None - - -def get_test_directory(): - """Returns the toplevel directory for the tests. Might create it.""" - directory = tempfile.mkdtemp(prefix='vttest', - dir=os.environ.get('VTDATAROOT', None)) - # Override VTDATAROOT to point to the newly created dir - os.environ['VTDATAROOT'] = directory - os.mkdir(get_logs_directory(directory)) - return directory - - -def get_logs_directory(directory): - """Returns the directory for logs, might be based on directory. - - Args: - directory: the value returned by get_test_directory(). - Returns: - the directory for logs. - """ - return os.path.join(directory, 'logs') - - -def cleanup_test_directory(directory): - """Cleans up the test directory after the test is done. - - Args: - directory: the value returned by get_test_directory(). - """ - shutil.rmtree(directory) - - -def extra_vtcombo_parameters(): - """Returns extra parameters to send to vtcombo.""" - return [ - '-service_map', ','.join([ - 'grpc-vtgateservice', - 'grpc-vtctl', - ]), - ] - - -# pylint: disable=unused-argument -def process_is_healthy(name, addr): - - """Double-checks a process is healthy and ready for RPCs.""" - return True - - -def get_protocol(): - """Returns the protocol used between client and vtcombo.""" - return 'grpc' - - -def get_port(name, protocol=None): - """Returns the port to use for a given process. - - This is only called once per process, so picking an unused port will also - work. - - Args: - name: process name. - protocol: the protocol used. - - Returns: - the port to use. - - Raises: - ValueError: the port name is invalid. - """ - if name == 'vtcombo': - if protocol == 'grpc': - # We can't use the base_port for grpc. - return base_port + 1 - return base_port - elif name == 'mysql': - 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) diff --git a/py/vttest/init_data_options.py b/py/vttest/init_data_options.py deleted file mode 100644 index 04219f036b2..00000000000 --- a/py/vttest/init_data_options.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Stores options used for initializing the database with randomized data. - -The options stored correspond to command line flags. See run_local_database.py -for more details on each option. -""" - - -class InitDataOptions(object): - valid_attrs = set([ - 'rng_seed', - 'min_table_shard_size', - 'max_table_shard_size', - 'null_probability', - ]) - - def __setattr__(self, name, value): - if name not in self.valid_attrs: - raise Exception( - 'InitDataOptions: unsupported attribute: %s' % name) - self.__dict__[name] = value diff --git a/py/vttest/local_database.py b/py/vttest/local_database.py deleted file mode 100644 index 2634aef27a2..00000000000 --- a/py/vttest/local_database.py +++ /dev/null @@ -1,469 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Create a local Vitess database for testing.""" - -import glob -import logging -import os -import random -import re - -from vttest import environment -from vttest import vt_processes - - -class LocalDatabase(object): - """Set up a local Vitess database.""" - - def __init__(self, - topology, - schema_dir, - mysql_only, - init_data_options, - default_schema_dir=None, - extra_my_cnf=None, - snapshot_file=None, - charset='utf8', - mysql_server_bind_address=None): - """Initializes an object of this class. - - Args: - topology: a vttest.VTTestTopology object describing the topology. - schema_dir: see the documentation for the corresponding command line - flag in run_local_database.py - mysql_only: see the documentation for the corresponding command line - flag in run_local_database.py - init_data_options: an object of type InitDataOptions containing - options configuring populating the database with initial random data. - If the value is 'None' then the database will not be initialized - with random data. - default_schema_dir: a directory to use if no keyspace is found in the - schema_dir directory. - extra_my_cnf: additional cnf file to use for the EXTRA_MY_CNF var. - snapshot_file: A MySQL DB snapshot file. - charset: MySQL charset. - mysql_server_bind_address: MySQL server bind address. - """ - - self.topology = topology - self.schema_dir = schema_dir - self.mysql_only = mysql_only - self.init_data_options = init_data_options - self.default_schema_dir = default_schema_dir - self.extra_my_cnf = extra_my_cnf - self.snapshot_file = snapshot_file - self.charset = charset - self.mysql_server_bind_address = mysql_server_bind_address - - def setup(self): - """Create a MySQL instance and all Vitess processes.""" - mysql_port = environment.get_port('mysql') - self.directory = environment.get_test_directory() - self.mysql_db = environment.mysql_db_class( - self.directory, mysql_port, self.extra_my_cnf, self.snapshot_file) - - self.mysql_db.setup() - if not self.snapshot_file: - self.create_databases() - self.load_schema() - if self.init_data_options is not None: - self.rng = random.Random(self.init_data_options.rng_seed) - self.populate_with_random_data() - if self.mysql_only: - return - - vt_processes.start_vt_processes(self.directory, self.topology, - self.mysql_db, self.schema_dir, - charset=self.charset, mysql_server_bind_address=self.mysql_server_bind_address) - - def teardown(self): - """Kill all Vitess processes and wait for them to end. - - MySQLTestDB's wrapper script will take care of mysqld. - """ - if not self.mysql_only: - self.kill() - self.wait() - self.mysql_db.teardown() - environment.cleanup_test_directory(self.directory) - - def kill(self): - """Kill all Vitess processes.""" - vt_processes.kill_vt_processes() - - def wait(self): - """Wait for all Vitess processes to end.""" - vt_processes.wait_vt_processes() - - def vtgate_addr(self): - """Get the host:port for vtgate.""" - if environment.get_protocol() == 'grpc': - return vt_processes.vtcombo_process.grpc_addr() - return vt_processes.vtcombo_process.addr() - - def config(self): - """Returns a dict with enough information to be able to connect.""" - if self.mysql_only: - return self.mysql_db.config() - - 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': - result['grpc_port'] = vt_processes.vtcombo_process.grpc_port - return result - - def mysql_execute(self, queries, db_name=''): - """Execute queries directly on MySQL. - - The queries will be executed in a single transaction. - - Args: - queries: A list of strings. The SQL statements to execute. - db_name: The database name to use. - - Returns: - The results of the last query as a list of row tuples. - """ - conn = self.mysql_db.connect(db_name) - cursor = conn.cursor() - - for query in queries: - cursor.execute(query) - result = cursor.fetchall() - - cursor.close() - # Commit all of the queries. - conn.commit() - conn.close() - return result - - def create_databases(self): - """Create a database for each shard.""" - - cmds = [] - for kpb in self.topology.keyspaces: - if kpb.served_from: - # redirected keyspaces have no underlying database - continue - - for spb in kpb.shards: - db_name = spb.db_name_override - if not db_name: - db_name = 'vt_%s_%s' % (kpb.name, spb.name) - cmds.append('create database `%s`' % db_name) - logging.info('Creating databases') - self.mysql_execute(cmds) - - def load_schema(self): - """Load schema SQL from data files.""" - - if not self.schema_dir: - return - - if not os.path.isdir(self.schema_dir): - raise Exception('schema_dir "%s" is not a directory.' % self.schema_dir) - - for kpb in self.topology.keyspaces: - if kpb.served_from: - # redirected keyspaces have no underlying database - continue - - keyspace = kpb.name - keyspace_dir = os.path.join(self.schema_dir, keyspace) - schema_dir = keyspace_dir - if not os.path.isdir(schema_dir): - schema_dir = self.default_schema_dir - if not schema_dir or not os.path.isdir(schema_dir): - raise Exception( - 'No subdirectory found in schema dir %s for keyspace %s. ' - 'No valid default_schema_dir (set to %s) was found. ' - 'For keyspaces without an initial schema, create the ' - 'directory %s and leave a README file to explain why the ' - 'directory exists. ' - 'Alternatively, disable loading schemas by setting --schema_dir ' - 'to "" or set --default_schema_dir to a valid schema.' % - (self.schema_dir, keyspace, self.default_schema_dir, - keyspace_dir)) - - for filepath in glob.glob(os.path.join(schema_dir, '*.sql')): - logging.info('Loading schema for keyspace %s from file %s', - keyspace, filepath) - cmds = self.get_sql_commands_from_file(filepath, schema_dir) - - # Run the cmds on each shard and cell in the keyspace. - for spb in kpb.shards: - db_name = spb.db_name_override - if not db_name: - db_name = 'vt_%s_%s' % (kpb.name, spb.name) - self.mysql_execute(cmds, db_name=db_name) - - def populate_with_random_data(self): - """Populates all shards with randomly generated data.""" - - for kpb in self.topology.keyspaces: - if kpb.served_from: - # redirected keyspaces have no underlying database - continue - - for spb in kpb.shards: - db_name = spb.db_name_override - if not db_name: - db_name = 'vt_%s_%s' % (kpb.name, spb.name) - self.populate_shard_with_random_data(db_name) - - def populate_shard_with_random_data(self, db_name): - """Populates the given database with randomly generated data. - - Every table in the database is populated. - - Args: - db_name: The shard database name (string). - """ - - tables = self.mysql_execute(['SHOW TABLES'], db_name) - for table in tables: - self.populate_table_with_random_data(db_name, table[0]) - - # The number of rows inserted in a single INSERT statement. - batch_insert_size = 1000 - - def populate_table_with_random_data(self, db_name, table_name): - """Populates the given table with randomly generated data. - - Queries the database for the table schema and then populates - the columns with randomly generated data. - - Args: - db_name: The shard database name (string). - table_name: The name of the table to populate (string). - """ - - field_infos = self.mysql_execute(['DESCRIBE %s' % table_name], db_name) - num_rows = self.rng.randint(self.init_data_options.min_table_shard_size, - self.init_data_options.max_table_shard_size) - rows = [] - for _ in xrange(num_rows): - row = [] - for field_info in field_infos: - field_type = field_info[1] - field_allow_nulls = (field_info[2] == 'YES') - row.append( - self.generate_random_field( - table_name, field_type, field_allow_nulls)) - rows.append(row) - - # Insert 'rows' into the database in batches of size - # self.batch_insert_size - field_names = [field_info[0] for field_info in field_infos] - for index in xrange(0, len(rows), self.batch_insert_size): - self.batch_insert(db_name, - table_name, - field_names, - rows[index:index + self.batch_insert_size]) - - def batch_insert(self, db_name, table_name, field_names, rows): - """Inserts the rows in 'rows' into 'table_name' of database 'db_name'. - - Args: - db_name: The name of the database containing the table. - table_name: The name of the table to populate. - field_names: The list of the field names in the table. - rows: A list of tuples with each tuple containing - the string representations of the fields. - The order of the representation must match the order of the field - names listed in 'field_names'. - """ - - field_names_string = ','.join(field_names) - values_string = ','.join(['(' + ','.join(row) +')' for row in rows]) - # We use "INSERT IGNORE" to ignore duplicate key errors. - insert_query = ('INSERT IGNORE INTO %s (%s) VALUES %s' % - (table_name, field_names_string, values_string)) - logging.info('Executing in database %s: %s', db_name, insert_query) - self.mysql_execute([insert_query], db_name) - - def generate_random_field(self, table_name, field_type, field_allows_nulls): - """Generates a random field string representation. - - By 'string representation' we mean a string that is suitable to be a part - of an 'INSERT INTO' SQL statement. - - Args: - table_name: The name of the table that will contain the generated field - value. Only used for a descriptive exception message in case of - an error. - field_type: The field_type as given by a "DESCRIBE
Query Table PlanReason Count Time MySQL Time{{.Query}} {{.Table}} {{.Plan}}{{.Reason}} {{.Count}} {{.Time}} {{.MysqlTime}}
" SQL statement. - field_allows_nulls: Should be 'true' if this field allows NULLS. - - Returns: - The random field. - - Raises: - Exception: If 'field_type' is not supported. - """ - - value = None - if field_type.startswith('tinyint'): - value = self.random_integer(field_type, 1) - elif field_type.startswith('smallint'): - value = self.random_integer(field_type, 2) - elif field_type.startswith('mediumint'): - value = self.random_integer(field_type, 3) - elif field_type.startswith('int'): - value = self.random_integer(field_type, 4) - elif field_type.startswith('bigint'): - value = self.random_integer(field_type, 8) - elif field_type.startswith('decimal'): - value = self.random_decimal(field_type) - else: - raise Exception('Populating random data in field type: %s is not yet ' - 'supported. (table: %s)' % (field_type, table_name)) - if (field_allows_nulls and - self.true_with_probability(self.init_data_options.null_probability)): - return 'NULL' - return value - - def true_with_probability(self, true_probability): - """Returns a pseudo-random boolean. - - Args: - true_probability: The probability to use for returning 'true'. - Returns: - The value 'true' is with probability 'true_probability'. - """ - - return self.rng.uniform(0, 1) < true_probability - - def random_integer(self, field_type, num_bytes): - num_bits = 8*num_bytes - if field_type.endswith('unsigned'): - return '%d' % (self.rng.randint(0, 2**num_bits-1)) - return '%d' % (self.rng.randint(-2**(num_bits-1), 2**(num_bits-1)-1)) - - decimal_regexp = re.compile(r'decimal\((\d+),(\d+)\)') - - def random_decimal(self, field_type): - match = self.decimal_regexp.match(field_type) - if match is None: - raise Exception("Can't parse 'decimal' field type: %s" % field_type) - num_digits_right = int(match.group(2)) - num_digits_left = int(match.group(1))-num_digits_right - boundary = 10**num_digits_left-1 - rand = self.rng.uniform(-boundary, boundary) - return '%.*f' % (num_digits_right, rand) - - def get_sql_commands_from_file(self, filename, source_root=None): - """Given a file, extract an array of commands from the file. - - Automatically strips out three types of MySQL comment syntax: - '--' at beginning of line: line removed - '-- ': remove everything from here to line's end (note space after dashes) - '#': remove everything from here to line's end - MySQL's handling of C-style /* ... */ comments is weird, so we - leave them alone for now. See the MySQL manual 6.1.6 "Comment Syntax" - for all the weird complications. - - Args: - filename: the SQL source file to use. - source_root: if specified, 'source FILENAME' lines in the SQL file will - source the specified filename relative to source_root. - - Returns: - A list of SQL commands. - """ - fd = open(filename) - lines = fd.readlines() - - inside_single_quotes = 0 - inside_double_quotes = 0 - commands = [] - cmd = '' - for line in lines: - # Strip newline and other trailing whitespace - line = line.rstrip() - - if (not inside_single_quotes and not inside_double_quotes and - line.startswith('--')): - # Line starts with '--', skip line - continue - - i = 0 - next_i = 0 - # Iterate through line, looking for special delimiters - while 1: - i = next_i - if i >= len(line): - break - - # By default, move to next character after this one - next_i = i + 1 - - if line[i] == '\\': - # Next character is literal, skip this and the next character - next_i = i + 2 - - elif line[i] == "'": - if not inside_double_quotes: - inside_single_quotes = not inside_single_quotes - - elif line[i] == '"': - if not inside_single_quotes: - inside_double_quotes = not inside_double_quotes - - elif not inside_single_quotes and not inside_double_quotes: - if line[i] == '#' or line[i:i+3] == '-- ': - # Found unquoted "#" or "-- ", ignore rest of line - line = line[:i] - break - - if line[i] == ';': - # Unquoted semicolon marks end of command - cmd += line[:i] - commands.append(cmd) - cmd = '' - - # Chop off everything before and including the semicolon - line = line[i+1:] - - # Start over at beginning of line - next_i = 0 - - # Reached end of line - if line and not line.isspace(): - if source_root and not cmd and line.startswith('source '): - commands.extend(self.get_sql_commands_from_file( - os.path.join(source_root, line[7:]), - source_root=source_root)) - else: - cmd += line - cmd += '\n' - - # Accept last command even if it doesn't end in semicolon - cmd = cmd.strip() - if cmd: - commands.append(cmd) - - return commands - - def __enter__(self): - self.setup() - return self - - def __exit__(self, exc_type, exc_info, tb): - self.teardown() diff --git a/py/vttest/mysql_db.py b/py/vttest/mysql_db.py deleted file mode 100644 index fe0c4e2037b..00000000000 --- a/py/vttest/mysql_db.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This module defines the interface for the MySQL database. -""" - - -class MySqlDB(object): - """A MySqlDB contains basic info about a MySQL instance.""" - - def __init__(self, directory, port, extra_my_cnf=None, snapshot_file=None): - self._directory = directory - self._port = port - self._extra_my_cnf = extra_my_cnf - self._snapshot_file = snapshot_file - - def setup(self, port): - """Starts the MySQL database.""" - raise NotImplementedError('MySqlDB is the base class.') - - def teardown(self): - """Stops the MySQL database.""" - raise NotImplementedError('MySqlDB is the base class.') - - def username(self): - raise NotImplementedError('MySqlDB is the base class.') - - def password(self): - raise NotImplementedError('MySqlDB is the base class.') - - def hostname(self): - raise NotImplementedError('MySqlDB is the base class.') - - def port(self): - raise NotImplementedError('MySqlDB is the base class.') - - def unix_socket(self): - raise NotImplementedError('MySqlDB is the base class.') - - def config(self): - """Returns the json config to output.""" - raise NotImplementedError('MySqlDB is the base class.') diff --git a/py/vttest/mysql_db_mysqlctl.py b/py/vttest/mysql_db_mysqlctl.py deleted file mode 100644 index c1ecc8d049f..00000000000 --- a/py/vttest/mysql_db_mysqlctl.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This module defines a mysqlctl based MySQL database. -""" - -import os -import subprocess - -import MySQLdb - -from vttest import environment -from vttest import mysql_db -from vttest.mysql_flavor import mysql_flavor - - -class MySqlDBMysqlctl(mysql_db.MySqlDB): - """Contains data and methods to manage a MySQL instance using mysqlctl.""" - - def __init__(self, directory, port, extra_my_cnf, snapshot_file=None): - super(MySqlDBMysqlctl, self).__init__( - directory, port, extra_my_cnf, snapshot_file) - - def setup(self): - cmd = [ - environment.mysqlctl_binary, - '-alsologtostderr', - '-tablet_uid', '1', - '-mysql_port', str(self._port), - 'init', - '-init_db_sql_file', - os.path.join(os.environ['VTROOT'], 'config/init_db.sql'), - ] - env = os.environ - env['VTDATAROOT'] = self._directory - my_cnf = mysql_flavor().my_cnf() - if self._extra_my_cnf: - my_cnf += ':%s' % self._extra_my_cnf - env['EXTRA_MY_CNF'] = my_cnf - result = subprocess.call(cmd, env=env) - if result != 0: - raise Exception('mysqlctl failed', result) - - def teardown(self): - cmd = [ - environment.mysqlctl_binary, - '-alsologtostderr', - '-tablet_uid', '1', - '-mysql_port', str(self._port), - 'shutdown', - ] - result = subprocess.call(cmd) - if result != 0: - raise Exception('mysqlctl failed', result) - - def connect(self, db_name): - return MySQLdb.connect(user='vt_dba', - unix_socket=self.unix_socket(), - db=db_name) - - def username(self): - return 'vt_dba' - - def password(self): - return '' - - def hostname(self): - return '' - - def port(self): - return self._port - - def unix_socket(self): - return os.path.join(self._directory, 'vt_0000000001', 'mysql.sock') - - def config(self): - return { - 'username': self.username(), - 'password': self.password(), - 'socket': self.unix_socket(), - } diff --git a/py/vttest/mysql_flavor.py b/py/vttest/mysql_flavor.py deleted file mode 100644 index c28bf979e02..00000000000 --- a/py/vttest/mysql_flavor.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Define abstractions for various mysql flavors. - -This module is used by mysql_db_mysqlctl.py to handle differences -between various flavors of mysql. -""" - -import logging -import os -import sys - - -# For now, vtroot is only used in this module. If other people -# need this, we should move it to environment. -if "VTROOT" not in os.environ: - sys.stderr.write( - "ERROR: Vitess environment not set up. " - 'Please run "source dev.env" first.\n') - sys.exit(1) - -vtroot = os.environ["VTROOT"] - -class MysqlFlavor(object): - """Base class with default SQL statements.""" - - def my_cnf(self): - """Returns the path to an extra my_cnf file, or None.""" - return None - - -class MariaDB(MysqlFlavor): - """Overrides specific to MariaDB.""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -class MariaDB103(MysqlFlavor): - """Overrides specific to MariaDB 10.3""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -class MySQL56(MysqlFlavor): - """Overrides specific to MySQL 5.6.""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -class MySQL80(MysqlFlavor): - """Overrides specific to MySQL 8.0.""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -__mysql_flavor = None - - -# mysql_flavor is a function because we need something to import before the -# actual __mysql_flavor is initialized, since that doesn't happen until after -# the command-line options are parsed. If we make mysql_flavor a variable and -# import it before it's initialized, the module that imported it won't get the -# updated value when it's later initialized. -def mysql_flavor(): - return __mysql_flavor - - -def set_mysql_flavor(flavor): - global __mysql_flavor - - # Last default is there because the environment variable might be set to "". - flavor = flavor or os.environ.get("MYSQL_FLAVOR", "MySQL56") or "MySQL56" - - # Set the environment variable explicitly in case we're overriding it via - # command-line flag. - os.environ["MYSQL_FLAVOR"] = flavor - - if flavor == "MariaDB": - __mysql_flavor = MariaDB() - elif flavor == "MariaDB103": - __mysql_flavor = MariaDB103() - elif flavor == "MySQL80": - __mysql_flavor = MySQL80() - elif flavor == "MySQL56": - __mysql_flavor = MySQL56() - else: - logging.error("Unknown MYSQL_FLAVOR '%s'", flavor) - exit(1) - - logging.debug("Using MYSQL_FLAVOR=%s", str(flavor)) diff --git a/py/vttest/run_local_database.py b/py/vttest/run_local_database.py deleted file mode 100755 index ba49b6546c1..00000000000 --- a/py/vttest/run_local_database.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -r"""Command-line tool for starting a local Vitess database for testing. - -USAGE: - - $ run_local_database --port 12345 \ - --proto_topo \ - --schema_dir /path/to/schema/dir - -It will run the tool, logging to stderr. On stdout, a small json structure -can be waited on and then parsed by the caller to figure out how to reach -the vtgate process. - -As an alternative to using proto_topo, a local instance can be started by using -additional flags, such as: - - $ run_local_database --port 12345 \ - --schema_dir /path/to/schema/dir \ - --cells cell1,cell2 --keyspaces ks1,ks2 \ - --num_shards 1,2 - -This will create an instance with two keyspaces in two cells, one with a single -shard and another with two shards. - -Once done with the test, send an empty line to this process for it to clean-up, -and then just wait for it to exit. - -""" - -import json -import logging -import optparse -import os -import sys - - -from google.protobuf import text_format - -from vtproto import vttest_pb2 -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import -from vttest import environment -from vttest import init_data_options -from vttest import local_database -from vttest import mysql_flavor -from vttest import sharding_utils - - -def main(cmdline_options): - topology = vttest_pb2.VTTestTopology() - if cmdline_options.proto_topo: - # Text-encoded proto topology object, just parse it. - topology = text_format.Parse(cmdline_options.proto_topo, topology) - if not topology.cells: - topology.cells.append('test') - else: - cells = [] - keyspaces = [] - shard_counts = [] - if cmdline_options.cells: - cells = cmdline_options.cells.split(',') - if cmdline_options.keyspaces: - keyspaces = cmdline_options.keyspaces.split(',') - if cmdline_options.num_shards: - shard_counts = [int(x) for x in cmdline_options.num_shards.split(',')] - - for cell in cells: - topology.cells.append(cell) - for keyspace, num_shards in zip(keyspaces, shard_counts): - ks = topology.keyspaces.add(name=keyspace) - for shard in sharding_utils.get_shard_names(num_shards): - ks.shards.add(name=shard) - ks.replica_count = cmdline_options.replica_count - ks.rdonly_count = cmdline_options.rdonly_count - - environment.base_port = cmdline_options.port - - init_data_opts = None - if cmdline_options.initialize_with_random_data: - init_data_opts = init_data_options.InitDataOptions() - init_data_opts.rng_seed = cmdline_options.rng_seed - init_data_opts.min_table_shard_size = cmdline_options.min_table_shard_size - init_data_opts.max_table_shard_size = cmdline_options.max_table_shard_size - init_data_opts.null_probability = cmdline_options.null_probability - - extra_my_cnf = '' - if cmdline_options.extra_my_cnf: - extra_my_cnf += ':' + cmdline_options.extra_my_cnf - - with local_database.LocalDatabase( - topology, - cmdline_options.schema_dir, - cmdline_options.mysql_only, - init_data_opts, - default_schema_dir=cmdline_options.default_schema_dir, - extra_my_cnf=extra_my_cnf, - charset=cmdline_options.charset, - snapshot_file=cmdline_options.snapshot_file, - mysql_server_bind_address=cmdline_options.mysql_server_bind_address) as local_db: - print json.dumps(local_db.config()) - sys.stdout.flush() - try: - raw_input() - except EOFError: - sys.stderr.write( - 'WARNING: %s: No empty line was received on stdin.' - ' Instead, stdin was closed and the cluster will be shut down now.' - ' Make sure to send the empty line instead to proactively shutdown' - ' the local cluster. For example, did you forget the shutdown in' - ' your test\'s tearDown()?\n' % os.path.basename(__file__)) - -if __name__ == '__main__': - - parser = optparse.OptionParser() - parser.add_option( - '-p', '--port', type='int', - help='Port to use for vtcombo. If this is 0, a random port ' - 'will be chosen.') - parser.add_option( - '-o', '--proto_topo', - help='Define the fake cluster topology as a compact text format encoded' - ' vttest proto. See vttest.proto for more information.') - parser.add_option( - '-s', '--schema_dir', - help='Directory for initial schema files. Within this dir,' - ' there should be a subdir for each keyspace. Within' - ' each keyspace dir, each file is executed as SQL' - ' after the database is created on each shard.' - ' If the directory contains a vschema.json file, it' - ' will be used as the vschema for the V3 API.') - parser.add_option( - '-e', '--default_schema_dir', - help='Default directory for initial schema files. If no schema is found' - ' in schema_dir, default to this location.') - parser.add_option( - '-m', '--mysql_only', action='store_true', - help='If this flag is set only mysql is initialized.' - ' The rest of the vitess components are not started.' - ' Also, the output specifies the mysql unix socket' - ' instead of the vtgate port.') - parser.add_option( - '-r', '--initialize_with_random_data', action='store_true', - help='If this flag is each table-shard will be initialized' - ' with random data. See also the "rng_seed" and "min_shard_size"' - ' and "max_shard_size" flags.') - parser.add_option( - '-d', '--rng_seed', type='int', default=123, - help='The random number generator seed to use when initializing' - ' with random data (see also --initialize_with_random_data).' - ' Multiple runs with the same seed will result with the same' - ' initial data.') - parser.add_option( - '-x', '--min_table_shard_size', type='int', default=1000, - help='The minimum number of initial rows in a table shard. Ignored if' - '--initialize_with_random_data is false. The actual number is chosen' - ' randomly.') - parser.add_option( - '-y', '--max_table_shard_size', type='int', default=10000, - help='The maximum number of initial rows in a table shard. Ignored if' - '--initialize_with_random_data is false. The actual number is chosen' - ' randomly') - parser.add_option( - '-n', '--null_probability', type='float', default=0.1, - help='The probability to initialize a field with "NULL" ' - ' if --initialize_with_random_data is true. Only applies to fields' - ' that can contain NULL values.') - parser.add_option( - '-f', '--extra_my_cnf', - help='extra files to add to the config, separated by ":"') - parser.add_option( - '--mysql_server_bind_address', - help='mysql server bind address ":"') - parser.add_option( - '-v', '--verbose', action='store_true', - help='Display extra error messages.') - parser.add_option('-c', '--cells', default='test', - help='Comma separated list of cells') - parser.add_option('-k', '--keyspaces', default='test_keyspace', - help='Comma separated list of keyspaces') - parser.add_option('--num_shards', default='2', - help='Comma separated shard count (one per keyspace)') - parser.add_option('--replica_count', type='int', default=2, - help='Replica tablets per shard (includes master)') - parser.add_option('--rdonly_count', type='int', default=1, - help='Rdonly tablets per shard') - parser.add_option('--charset', default='utf8', help='MySQL charset') - parser.add_option( - '--snapshot_file', default=None, help='A MySQL DB snapshot file') - (options, args) = parser.parse_args() - if options.verbose: - logging.getLogger().setLevel(logging.DEBUG) - - # This will set the flavor based on the MYSQL_FLAVOR env var, - # or default to MariaDB. - mysql_flavor.set_mysql_flavor(None) - - main(options) diff --git a/py/vttest/sharding_utils.py b/py/vttest/sharding_utils.py deleted file mode 100644 index db9b15ff6b9..00000000000 --- a/py/vttest/sharding_utils.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Sharding utils.""" - - -def get_shard_index(shard_name): - """Returns tuple of shard index, num_shards based on a shard name.""" - if shard_name in ['0', '-']: - return 0, 1 - - shard_begin, shard_end = shard_name.split('-') - num_bytes_used = max(len(shard_begin), len(shard_end)) / 2 - if shard_begin: - shard_begin = int(shard_begin, 16) - else: - shard_begin = 0 - if shard_end: - shard_end = int(shard_end, 16) - else: - shard_end = 1 << num_bytes_used * 8 - shard_width = shard_end - shard_begin - num_shards = (1 << num_bytes_used * 8) / (shard_width) - shard_num = shard_begin / shard_width - return shard_num, num_shards - - -def get_shard_name(shard, num_shards): - """Returns an appropriate shard name, as a string. - - A single shard name is simply 0; otherwise it will attempt to split up 0x100 - into multiple shards. For example, in a two sharded keyspace, shard 0 is - -80, shard 1 is 80-. This function currently only applies to sharding setups - where the shard count is 256 or less, and all shards are equal width. - - Args: - shard: The integer shard index (zero based) - num_shards: Total number of shards (int) - - Returns: - The shard name as a string. - """ - - if num_shards == 1: - return '0' - - shard_width = int(0x100 / num_shards) - - if shard == 0: - return '-%02x' % shard_width - elif shard == num_shards - 1: - return '%02x-' % (shard * shard_width) - else: - return '%02x-%02x' % (shard * shard_width, (shard + 1) * shard_width) - - -def get_shard_names(num_shards): - """Create a generator of shard names. - - Args: - num_shards: Total number of shards (int) - - Returns: - The shard name generator. - """ - return (get_shard_name(x, num_shards) for x in range(num_shards)) diff --git a/py/vttest/vt_processes.py b/py/vttest/vt_processes.py deleted file mode 100644 index 8530c2d5ee2..00000000000 --- a/py/vttest/vt_processes.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Starts the vtcombo process.""" - -import json -import logging -import os -import socket -import subprocess -import time -import urllib - -from google.protobuf import text_format - -from vttest import environment - - -class VtProcess(object): - """Base class for a vt process, vtcombo only now.""" - - START_RETRIES = 5 - - def __init__(self, name, directory, binary, port_name): - self.name = name - self.directory = directory - self.binary = binary - self.extraparams = [] - self.port_name = port_name - self.process = None - - def wait_start(self): - """Start the process and wait for it to respond on HTTP.""" - - for _ in xrange(0, self.START_RETRIES): - self.port = environment.get_port(self.port_name) - if environment.get_protocol() == 'grpc': - self.grpc_port = environment.get_port(self.port_name, protocol='grpc') - else: - self.grpc_port = None - logs_subdirectory = environment.get_logs_directory(self.directory) - cmd = [ - self.binary, - '-port', '%u' % self.port, - '-log_dir', logs_subdirectory, - '-alsologtostderr', - ] - if environment.get_protocol() == 'grpc': - cmd.extend(['-grpc_port', '%u' % self.grpc_port]) - cmd.extend(self.extraparams) - logging.info('Starting process: %s', cmd) - stdout = os.path.join(logs_subdirectory, '%s.%d.log' % - (self.name, self.port)) - self.stdout = open(stdout, 'w') - self.process = subprocess.Popen(cmd, - stdout=self.stdout) - timeout = time.time() + 60.0 - while time.time() < timeout: - if environment.process_is_healthy( - self.name, self.addr()) and self.get_vars(): - logging.info('%s started.', self.name) - return - elif self.process.poll() is not None: - logging.error('%s process exited prematurely.', self.name) - break - time.sleep(0.3) - - logging.error('cannot start %s process on time: %s ', - self.name, socket.getfqdn()) - self.kill() - - raise Exception('Failed %d times to run %s' % ( - self.START_RETRIES, - self.name)) - - def addr(self): - """Return the host:port of the process.""" - return '%s:%u' % (socket.getfqdn(), self.port) - - def grpc_addr(self): - """Get the grpc address of the process. - - Returns: - the grpc host:port of the process. - Only call this is environment.get_protocol() == 'grpc'. - """ - return '%s:%u' % (socket.getfqdn(), self.grpc_port) - - def get_vars(self): - """Return the debug vars.""" - data = None - try: - url = 'http://%s/debug/vars' % self.addr() - f = urllib.urlopen(url) - data = f.read() - f.close() - except IOError: - return None - try: - return json.loads(data) - except ValueError: - logging.error('%s', data) - raise - - def kill(self): - """Kill the process.""" - # These will proceed without error even if the process is already gone. - self.process.terminate() - - def wait(self): - """Wait for the process to end.""" - self.process.wait() - - -class VtcomboProcess(VtProcess): - """Represents a vtcombo subprocess.""" - - QUERYSERVER_PARAMETERS = [ - '-queryserver-config-pool-size', '4', - '-queryserver-config-query-timeout', '300', - '-queryserver-config-schema-reload-time', '60', - '-queryserver-config-stream-pool-size', '4', - '-queryserver-config-transaction-cap', '4', - '-queryserver-config-transaction-timeout', '300', - '-queryserver-config-txpool-timeout', '300', - ] - - def __init__(self, directory, topology, mysql_db, schema_dir, charset, - mysql_server_bind_address=None): - VtProcess.__init__(self, 'vtcombo-%s' % os.environ['USER'], directory, - environment.vtcombo_binary, port_name='vtcombo') - self.extraparams = [ - '-db_charset', charset, - '-db_app_user', mysql_db.username(), - '-db_app_password', mysql_db.password(), - '-db_dba_user', mysql_db.username(), - '-db_dba_password', mysql_db.password(), - '-proto_topo', text_format.MessageToString(topology, as_one_line=True), - '-mycnf_server_id', '1', - '-mycnf_socket_file', mysql_db.unix_socket(), - '-normalize_queries', - ] + self.QUERYSERVER_PARAMETERS + environment.extra_vtcombo_parameters() - if schema_dir: - self.extraparams.extend(['-schema_dir', schema_dir]) - if mysql_db.unix_socket(): - self.extraparams.extend(['-db_socket', mysql_db.unix_socket()]) - else: - self.extraparams.extend( - ['-db_host', mysql_db.hostname(), - '-db_port', str(mysql_db.port())]) - self.vtcombo_mysql_port = environment.get_port('vtcombo_mysql_port') - if mysql_server_bind_address: - # Binding to 0.0.0.0 instead of localhost makes it possible to connect to vtgate from outside a docker container - self.extraparams.extend(['-mysql_server_bind_address', mysql_server_bind_address]) - else: - self.extraparams.extend(['-mysql_server_bind_address', 'localhost']) - self.extraparams.extend( - ['-mysql_auth_server_impl', 'none', - '-mysql_server_port', str(self.vtcombo_mysql_port)]) - - -vtcombo_process = None - - -def start_vt_processes(directory, topology, mysql_db, schema_dir, - charset='utf8', mysql_server_bind_address=None): - """Start the vt processes. - - Args: - directory: the toplevel directory for the processes (logs, ...) - topology: a vttest.VTTestTopology object. - mysql_db: an instance of the mysql_db.MySqlDB class. - schema_dir: the directory that contains the schema / vschema. - charset: the character set for the database connections. - mysql_server_bind_address: MySQL server bind address for vtcombo. - """ - global vtcombo_process - - logging.info('start_vt_processes(directory=%s,vtcombo_binary=%s)', - directory, environment.vtcombo_binary) - vtcombo_process = VtcomboProcess(directory, topology, mysql_db, schema_dir, - charset, mysql_server_bind_address=mysql_server_bind_address) - vtcombo_process.wait_start() - - -def kill_vt_processes(): - """Call kill() on all processes.""" - logging.info('kill_vt_processes()') - if vtcombo_process: - vtcombo_process.kill() - - -def wait_vt_processes(): - """Call wait() on all processes.""" - logging.info('wait_vt_processes()') - if vtcombo_process: - vtcombo_process.wait() - - -def kill_and_wait_vt_processes(): - """Call kill() and then wait() on all processes.""" - kill_vt_processes() - wait_vt_processes() - - -# wait_step is a helper for looping until a condition is true. -# use as follow: -# timeout = 10 -# while True: -# if done: -# break -# timeout = utils.wait_step('condition', timeout) -def wait_step(msg, timeout, sleep_time=1.0): - timeout -= sleep_time - if timeout <= 0: - raise Exception("timeout waiting for condition '%s'" % msg) - logging.debug("Sleeping for %f seconds waiting for condition '%s'", - sleep_time, msg) - time.sleep(sleep_time) - return timeout diff --git a/test/backup.py b/test/backup.py deleted file mode 100755 index 51b7a94eef4..00000000000 --- a/test/backup.py +++ /dev/null @@ -1,603 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import json -import logging -import os -import shutil -import unittest -import datetime - -import MySQLdb - -import environment -import tablet -import utils -from mysql_flavor import mysql_flavor - -use_mysqlctld = False -use_xtrabackup = False -xtrabackup_stripes = 0 -stream_mode = 'tar' -tablet_master = None -tablet_replica1 = None -tablet_replica2 = None -xtrabackup_args = [] - -new_init_db = '' -db_credentials_file = '' - - -def setUpModule(): - global xtrabackup_args - xtrabackup_args = ['-backup_engine_implementation', - 'xtrabackup', - '-xtrabackup_stream_mode', - stream_mode, - '-xtrabackup_user=vt_dba', - '-xtrabackup_stripes=%d' % (xtrabackup_stripes), - '-xtrabackup_backup_flags', - '--password=VtDbaPass'] - - global new_init_db, db_credentials_file - global tablet_master, tablet_replica1, tablet_replica2 - - tablet_master = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - tablet_replica1 = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - tablet_replica2 = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - - try: - environment.topo_server().setup() - - credentials = { - 'vt_dba': ['VtDbaPass'], - 'vt_app': ['VtAppPass'], - 'vt_allprivs': ['VtAllprivsPass'], - 'vt_repl': ['VtReplPass'], - 'vt_filtered': ['VtFilteredPass'], - } - db_credentials_file = environment.tmproot+'/db_credentials.json' - with open(db_credentials_file, 'w') as fd: - fd.write(json.dumps(credentials)) - - # Determine which column is used for user passwords in this MySQL version. - proc = tablet_master.init_mysql() - if use_mysqlctld: - tablet_master.wait_for_mysqlctl_socket() - else: - utils.wait_procs([proc]) - try: - tablet_master.mquery('mysql', 'select password from mysql.user limit 0', - user='root') - password_col = 'password' - except MySQLdb.DatabaseError: - password_col = 'authentication_string' - utils.wait_procs([tablet_master.teardown_mysql()]) - tablet_master.remove_tree(ignore_options=True) - - # Create a new init_db.sql file that sets up passwords for all users. - # Then we use a db-credentials-file with the passwords. - new_init_db = environment.tmproot + '/init_db_with_passwords.sql' - with open(environment.vtroot + '/config/init_db.sql') as fd: - init_db = fd.read() - with open(new_init_db, 'w') as fd: - fd.write(init_db) - fd.write(mysql_flavor().change_passwords(password_col)) - - # start mysql instance external to the test - setup_procs = [ - tablet_master.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica1.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica2.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - ] - if use_mysqlctld: - tablet_master.wait_for_mysqlctl_socket() - tablet_replica1.wait_for_mysqlctl_socket() - tablet_replica2.wait_for_mysqlctl_socket() - else: - utils.wait_procs(setup_procs) - logging.debug("done initializing mysql %s",str(datetime.datetime.now())) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [ - tablet_master.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica1.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica2.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]) - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - tablet_master.remove_tree() - tablet_replica1.remove_tree() - tablet_replica2.remove_tree() - - -class TestBackup(unittest.TestCase): - - def setUp(self): - for t in tablet_master, tablet_replica1: - t.create_db('vt_test_keyspace') - - xtra_args = ['-db-credentials-file', db_credentials_file] - if use_xtrabackup: - xtra_args.extend(xtrabackup_args) - tablet_master.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=xtra_args) - tablet_replica1.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=xtra_args) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_master.tablet_alias]) - - def tearDown(self): - for t in tablet_master, tablet_replica1, tablet_replica2: - t.kill_vttablet() - - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in [tablet_master, tablet_replica1, tablet_replica2]: - t.reset_replication() - t.set_semi_sync_enabled(master=False, slave=False) - t.clean_dbs() - - for backup in self._list_backups(): - self._remove_backup(backup) - - _create_vt_insert_test = '''create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - def _insert_data(self, t, index): - """Add a single row with value 'index' to the given tablet.""" - t.mquery( - 'vt_test_keyspace', - "insert into vt_insert_test (msg) values ('test %s')" % - index, write=True) - - def _check_data(self, t, count, msg): - """Check that the specified tablet has the expected number of rows.""" - timeout = 10 - while True: - try: - result = t.mquery( - 'vt_test_keyspace', 'select count(*) from vt_insert_test') - if result[0][0] == count: - break - except MySQLdb.DatabaseError: - # ignore exceptions, we'll just timeout (the tablet creation - # can take some time to replicate, and we get a 'table vt_insert_test - # does not exist exception in some rare cases) - logging.exception('exception waiting for data to replicate') - timeout = utils.wait_step(msg, timeout) - - def _restore(self, t, tablet_type='replica', extra_args=[]): - """Erase mysql/tablet dir, then start tablet with restore enabled.""" - logging.debug("restoring tablet %s",str(datetime.datetime.now())) - self._reset_tablet_dir(t) - - xtra_args = ['-db-credentials-file', db_credentials_file] - if use_xtrabackup: - xtra_args.extend(xtrabackup_args) - - xtra_args.extend(extra_args) - - t.start_vttablet(wait_for_state='SERVING', - init_tablet_type=tablet_type, - init_keyspace='test_keyspace', - init_shard='0', - supports_backups=True, - extra_args=xtra_args) - - # check semi-sync is enabled for replica, disabled for rdonly. - if tablet_type == 'replica': - t.check_db_var('rpl_semi_sync_slave_enabled', 'ON') - t.check_db_status('rpl_semi_sync_slave_status', 'ON') - else: - t.check_db_var('rpl_semi_sync_slave_enabled', 'OFF') - t.check_db_status('rpl_semi_sync_slave_status', 'OFF') - - def _restore_wait_for_backup(self, t, tablet_type='replica', extra_args=[]): - """Erase mysql/tablet dir, then start tablet with wait_for_restore_interval.""" - self._reset_tablet_dir(t) - - xtra_args = [ - '-db-credentials-file', db_credentials_file, - '-wait_for_backup_interval', '1s', - ] - if use_xtrabackup: - xtra_args.extend(xtrabackup_args) - xtra_args.extend(extra_args) - - t.start_vttablet(wait_for_state=None, - init_tablet_type=tablet_type, - init_keyspace='test_keyspace', - init_shard='0', - supports_backups=True, - extra_args=xtra_args) - - def _reset_tablet_dir(self, t): - """Stop mysql, delete everything including tablet dir, restart mysql.""" - - extra_args = ['-db-credentials-file', db_credentials_file] - - utils.wait_procs([t.teardown_mysql(extra_args=extra_args)]) - # Specify ignore_options because we want to delete the tree even - # if the test's -k / --keep-logs was specified on the command line. - - t.remove_tree(ignore_options=True) - logging.debug("starting mysql %s",str(datetime.datetime.now())) - proc = t.init_mysql(init_db=new_init_db, extra_args=extra_args) - if use_mysqlctld: - t.wait_for_mysqlctl_socket() - else: - utils.wait_procs([proc]) - logging.debug("done starting mysql %s",str(datetime.datetime.now())) - - def _list_backups(self): - """Get a list of backup names for the test shard.""" - backups, _ = utils.run_vtctl(tablet.get_backup_storage_flags() + - ['ListBackups', 'test_keyspace/0'], - mode=utils.VTCTL_VTCTL, trap_output=True) - return backups.splitlines() - - def _remove_backup(self, backup): - """Remove a named backup from the test shard.""" - utils.run_vtctl( - tablet.get_backup_storage_flags() + - ['RemoveBackup', 'test_keyspace/0', backup], - auto_log=True, mode=utils.VTCTL_VTCTL) - - def test_backup_rdonly(self): - self._test_backup('rdonly', False) - - def test_backup_replica(self): - self._test_backup('replica', False) - - def test_backup_master(self): - """Test backup flow. - - test_backup will: - - create a shard with master and replica1 only - - run InitShardMaster - - insert some data - - take a backup on master - - insert more data on the master - - bring up tablet_replica2 after the fact, let it restore the backup - - check all data is right (before+after backup data) - - list the backup, remove it - - """ - # insert data on master, wait for slave to get it - tablet_master.mquery('vt_test_keyspace', self._create_vt_insert_test) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # This will fail, make sure we get the right error. - _, err = utils.run_vtctl(['Backup', tablet_master.tablet_alias], - auto_log=True, expect_fail=True) - self.assertIn('type MASTER cannot take backup. if you really need to do this, rerun the backup command with -allow_master', err) - - # And make sure there is no backup left. - backups = self._list_backups() - self.assertEqual(len(backups), 0, 'invalid backups: %s' % backups) - - # backup the master - utils.run_vtctl(['Backup', '-allow_master=true', tablet_master.tablet_alias], auto_log=True) - - # check that the backup shows up in the listing - backups = self._list_backups() - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), 1) - self.assertTrue(backups[0].endswith(tablet_master.tablet_alias)) - - # insert more data on the master - self._insert_data(tablet_master, 2) - - # now bring up the other slave, letting it restore from backup. - self._restore(tablet_replica2, tablet_type='replica') - - # check the new slave has the data - self._check_data(tablet_replica2, 2, 'replica2 tablet getting data') - - # check that the restored slave has the right local_metadata - result = tablet_replica2.mquery('_vt', 'select * from local_metadata') - metadata = {} - for row in result: - metadata[row[0]] = row[1] - self.assertEqual(metadata['Alias'], 'test_nj-0000062346') - self.assertEqual(metadata['ClusterAlias'], 'test_keyspace.0') - self.assertEqual(metadata['DataCenter'], 'test_nj') - self.assertEqual(metadata['PromotionRule'], 'neutral') - - # remove the backup and check that the list is empty - self._remove_backup(backups[0]) - backups = self._list_backups() - logging.debug('list of backups after remove: %s', backups) - self.assertEqual(len(backups), 0) - - tablet_replica2.kill_vttablet() - - def _test_backup(self, tablet_type, backup_only): - """Test backup flow. - - test_backup will: - - create a shard with master and replica1 only - - run InitShardMaster - - bring up tablet_replica2 concurrently, telling it to wait for a backup - - insert some data - - take a backup - - insert more data on the master - - wait for tablet_replica2 to become SERVING - - check all data is right (before+after backup data) - - list the backup, remove it - - Args: - tablet_type: 'replica' or 'rdonly'. - """ - - # Bring up another replica concurrently, telling it to wait until a backup - # is available instead of starting up empty. - # - # Override the backup engine implementation to a non-existent one for restore. - # This setting should only matter for taking new backups. We should be able - # to restore a previous backup successfully regardless of this setting. - self._restore_wait_for_backup(tablet_replica2, tablet_type=tablet_type, - extra_args=['-backup_engine_implementation', 'fake_implementation']) - - # insert data on master, wait for slave to get it - tablet_master.mquery('vt_test_keyspace', self._create_vt_insert_test) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # backup the slave - alias = tablet_replica1.tablet_alias - logging.debug("taking backup %s",str(datetime.datetime.now())) - - utils.run_vtctl(['Backup', alias], auto_log=True) - - logging.debug("done taking backup %s",str(datetime.datetime.now())) - # end if - - # check that the backup shows up in the listing - backups = self._list_backups() - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), 1) - self.assertTrue(backups[0].endswith(alias)) - - # insert more data on the master - self._insert_data(tablet_master, 2) - - # wait for tablet_replica2 to become serving (after restoring) - utils.pause('wait_for_backup') - tablet_replica2.wait_for_vttablet_state('SERVING') - - # check the new slave has the data - self._check_data(tablet_replica2, 2, 'replica2 tablet getting data') - - # check that the restored slave has the right local_metadata - result = tablet_replica2.mquery('_vt', 'select * from local_metadata') - metadata = {} - for row in result: - metadata[row[0]] = row[1] - self.assertEqual(metadata['Alias'], 'test_nj-0000062346') - self.assertEqual(metadata['ClusterAlias'], 'test_keyspace.0') - self.assertEqual(metadata['DataCenter'], 'test_nj') - if tablet_type == 'replica': - self.assertEqual(metadata['PromotionRule'], 'neutral') - else: - self.assertEqual(metadata['PromotionRule'], 'must_not') - - for backup in backups: - self._remove_backup(backup) - - backups = self._list_backups() - logging.debug('list of backups after remove: %s', backups) - self.assertEqual(len(backups), 0) - - tablet_replica2.kill_vttablet() - - def test_master_slave_same_backup(self): - """Test a master and slave from the same backup. - - Check that a slave and master both restored from the same backup - can replicate successfully. - """ - - # insert data on master, wait for slave to get it - tablet_master.mquery('vt_test_keyspace', self._create_vt_insert_test) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # backup the slave - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # insert more data on the master - self._insert_data(tablet_master, 2) - - # now bring up the other slave, letting it restore from backup. - self._restore(tablet_replica2) - - # check the new slave has the data - self._check_data(tablet_replica2, 2, 'replica2 tablet getting data') - - # Promote replica2 to master. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-new_master', tablet_replica2.tablet_alias]) - - # insert more data on replica2 (current master) - self._insert_data(tablet_replica2, 3) - - # Force replica1 to restore from backup. - tablet_replica1.kill_vttablet() - self._restore(tablet_replica1) - - # wait for replica1 to catch up. - self._check_data(tablet_replica1, 3, - 'replica1 getting data from restored master') - - # This is to test that replicationPosition is processed correctly - # while doing backup/restore after a reparent. - # It is written into the MANIFEST and read back from the MANIFEST. - - # Take another backup on the slave. - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # Insert more data on replica2 (current master). - self._insert_data(tablet_replica2, 4) - - # Force replica1 to restore from backup. - tablet_replica1.kill_vttablet() - self._restore(tablet_replica1) - - # Wait for replica1 to catch up. - self._check_data(tablet_replica1, 4, - 'replica1 getting data from master after reparent+backup+restore') - - tablet_replica2.kill_vttablet() - - def _restore_old_master_test(self, restore_method): - """Test that a former master replicates correctly after being restored. - - - Take a backup. - - Reparent from old master to new master. - - Force old master to restore from a previous backup using restore_method. - - Args: - restore_method: function accepting one parameter of type tablet.Tablet, - this function is called to force a restore on the provided tablet - """ - - # insert data on master, wait for slave to get it - tablet_master.mquery('vt_test_keyspace', self._create_vt_insert_test) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # backup the slave - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # insert more data on the master - self._insert_data(tablet_master, 2) - - # reparent to replica1 - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-new_master', tablet_replica1.tablet_alias]) - - # insert more data on new master - self._insert_data(tablet_replica1, 3) - - # force the old master to restore at the latest backup. - restore_method(tablet_master) - - # wait for it to catch up. - self._check_data(tablet_master, 3, 'former master catches up after restore') - - def test_restore_old_master(self): - def _restore_using_kill(t): - t.kill_vttablet() - self._restore(t) - - self._restore_old_master_test(_restore_using_kill) - - def test_in_place_restore(self): - def _restore_in_place(t): - utils.run_vtctl(['RestoreFromBackup', t.tablet_alias], auto_log=True) - - self._restore_old_master_test(_restore_in_place) - - def test_terminated_restore(self): - stop_restore_msg = 'Copying file 10' - if use_xtrabackup: - stop_restore_msg = 'Restore: Preparing' - def _terminated_restore(t): - for e in utils.vtctld_connection.execute_vtctl_command( - ['RestoreFromBackup', t.tablet_alias]): - logging.info('%s', e.value) - if stop_restore_msg in e.value: - break - - utils.Vtctld().start() - # insert data on master, wait for slave to get it - tablet_master.mquery('vt_test_keyspace', self._create_vt_insert_test) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # backup the slave - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # insert more data on the master - self._insert_data(tablet_master, 2) - - # reparent to replica1 - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-new_master', tablet_replica1.tablet_alias]) - - # insert more data on new master - self._insert_data(tablet_replica1, 3) - - # force the old master to restore at the latest backup, and terminate the restore - # when it is in the middle of copying the files - _terminated_restore(tablet_master) - - # check that restore_file has been created but not deleted - restore_file = os.path.join(tablet_master.tablet_dir, 'restore_in_progress') - self.assertTrue(os.path.isfile(restore_file)) - - # now retry the restore - for e in utils.vtctld_connection.execute_vtctl_command( - ['RestoreFromBackup', tablet_master.tablet_alias]): - logging.info('%s', e.value) - logging.info('waiting for restore to finish') - utils.wait_for_tablet_type(tablet_master.tablet_alias, 'replica', timeout=30) - - # check that restore_file doesn't exist any more - self.assertFalse(os.path.isfile(restore_file)) - - # wait for it to catch up. - self._check_data(tablet_master, 3, 'former master catches up after restore') - - -if __name__ == '__main__': - utils.main() diff --git a/test/backup_mysqlctld.py b/test/backup_mysqlctld.py deleted file mode 100755 index a3ed88ee8be..00000000000 --- a/test/backup_mysqlctld.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Re-runs backup.py with use_mysqlctld=True.""" - -import backup -import utils - -if __name__ == '__main__': - backup.use_mysqlctld = True - utils.main(backup) diff --git a/test/backup_only.py b/test/backup_only.py deleted file mode 100755 index 04f2db4dfdc..00000000000 --- a/test/backup_only.py +++ /dev/null @@ -1,392 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import json -import logging -import os -import unittest -import datetime - -import MySQLdb - -import environment -import tablet -import vtbackup -import utils - -from mysql_flavor import mysql_flavor - -use_mysqlctld = False -use_xtrabackup = False -stream_mode = 'tar' -tablet_master = None -tablet_replica1 = None -tablet_replica2 = None -backup_tablet = None -xtrabackup_args = [] - -new_init_db = '' -db_credentials_file = '' - -def setUpModule(): - global xtrabackup_args - xtrabackup_args = ['-backup_engine_implementation', - 'xtrabackup', - '-xtrabackup_stream_mode', - stream_mode, - '-xtrabackup_user=vt_dba', - '-xtrabackup_backup_flags', - '--password=VtDbaPass'] - - global new_init_db, db_credentials_file - global tablet_master, tablet_replica1, tablet_replica2, backup_tablet - - tablet_master = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - tablet_replica1 = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - tablet_replica2 = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - backup_tablet = vtbackup.Vtbackup(vt_dba_passwd='VtDbaPass') - - try: - environment.topo_server().setup() - - credentials = { - 'vt_dba': ['VtDbaPass'], - 'vt_app': ['VtAppPass'], - 'vt_allprivs': ['VtAllprivsPass'], - 'vt_repl': ['VtReplPass'], - 'vt_filtered': ['VtFilteredPass'], - } - db_credentials_file = environment.tmproot+'/db_credentials.json' - with open(db_credentials_file, 'w') as fd: - fd.write(json.dumps(credentials)) - - # Determine which column is used for user passwords in this MySQL version. - proc = tablet_master.init_mysql() - if use_mysqlctld: - tablet_master.wait_for_mysqlctl_socket() - else: - utils.wait_procs([proc]) - try: - tablet_master.mquery('mysql', 'select password from mysql.user limit 0', - user='root') - password_col = 'password' - except MySQLdb.DatabaseError: - password_col = 'authentication_string' - utils.wait_procs([tablet_master.teardown_mysql()]) - tablet_master.remove_tree(ignore_options=True) - - # Create a new init_db.sql file that sets up passwords for all users. - # Then we use a db-credentials-file with the passwords. - new_init_db = environment.tmproot + '/init_db_with_passwords.sql' - with open(environment.vtroot + '/config/init_db.sql') as fd: - init_db = fd.read() - with open(new_init_db, 'w') as fd: - fd.write(init_db) - fd.write(mysql_flavor().change_passwords(password_col)) - - logging.debug("initializing mysql %s",str(datetime.datetime.now())) - # start mysql instance external to the test - setup_procs = [ - tablet_master.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica1.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica2.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]) - ] - if use_mysqlctld: - tablet_master.wait_for_mysqlctl_socket() - tablet_replica1.wait_for_mysqlctl_socket() - tablet_replica2.wait_for_mysqlctl_socket() - else: - utils.wait_procs(setup_procs) - logging.debug("done initializing mysql %s",str(datetime.datetime.now())) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [ - tablet_master.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica1.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica2.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]) - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - tablet_master.remove_tree() - tablet_replica1.remove_tree() - tablet_replica2.remove_tree() - backup_tablet.remove_tree() - -class TestBackup(unittest.TestCase): - - def setUp(self): - for t in tablet_master, tablet_replica1: - t.create_db('vt_test_keyspace') - - - def tearDown(self): - for t in tablet_master, tablet_replica1, tablet_replica2: - t.kill_vttablet() - - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in [tablet_master, tablet_replica1, tablet_replica2]: - t.reset_replication() - t.set_semi_sync_enabled(master=False, slave=False) - t.clean_dbs() - - for backup in self._list_backups(): - self._remove_backup(backup) - - def _init_tablets(self,init=True,start=True): - xtra_args = ['-db-credentials-file', db_credentials_file] - if use_xtrabackup: - xtra_args.extend(xtrabackup_args) - tablet_master.init_tablet('replica', 'test_keyspace', '0', start=start, - supports_backups=True, - extra_args=xtra_args) - tablet_replica1.init_tablet('replica', 'test_keyspace', '0', start=start, - supports_backups=True, - extra_args=xtra_args) - if init: - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_master.tablet_alias]) - - _create_vt_insert_test = '''create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - def _insert_data(self, t, index): - """Add a single row with value 'index' to the given tablet.""" - t.mquery( - 'vt_test_keyspace', - "insert into vt_insert_test (msg) values ('test %s')" % - index, write=True) - - def _check_data(self, t, count, msg): - """Check that the specified tablet has the expected number of rows.""" - timeout = 10 - while True: - try: - result = t.mquery( - 'vt_test_keyspace', 'select count(*) from vt_insert_test') - if result[0][0] == count: - break - except MySQLdb.DatabaseError: - # ignore exceptions, we'll just timeout (the tablet creation - # can take some time to replicate, and we get a 'table vt_insert_test - # does not exist exception in some rare cases) - logging.exception('exception waiting for data to replicate') - timeout = utils.wait_step(msg, timeout) - - def _restore(self, t, tablet_type='replica',wait_for_state='SERVING'): - """Erase mysql/tablet dir, then start tablet with restore enabled.""" - logging.debug("restoring tablet %s",str(datetime.datetime.now())) - self._reset_tablet_dir(t) - - xtra_args = ['-db-credentials-file', db_credentials_file] - if use_xtrabackup: - xtra_args.extend(xtrabackup_args) - - t.start_vttablet(wait_for_state=wait_for_state, - init_tablet_type=tablet_type, - init_keyspace='test_keyspace', - init_shard='0', - supports_backups=True, - extra_args=xtra_args) - - logging.debug("done restoring tablet %s",str(datetime.datetime.now())) - - def _reset_tablet_dir(self, t): - """Stop mysql, delete everything including tablet dir, restart mysql.""" - - extra_args = ['-db-credentials-file', db_credentials_file] - utils.wait_procs([t.teardown_mysql(extra_args=extra_args)]) - # Specify ignore_options because we want to delete the tree even - # if the test's -k / --keep-logs was specified on the command line. - t.remove_tree(ignore_options=True) - logging.debug("starting mysql %s",str(datetime.datetime.now())) - proc = t.init_mysql(init_db=new_init_db, extra_args=extra_args) - if use_mysqlctld: - t.wait_for_mysqlctl_socket() - else: - utils.wait_procs([proc]) - logging.debug("done starting mysql %s",str(datetime.datetime.now())) - - def _list_backups(self): - """Get a list of backup names for the test shard.""" - backups, _ = utils.run_vtctl(tablet.get_backup_storage_flags() + - ['ListBackups', 'test_keyspace/0'], - mode=utils.VTCTL_VTCTL, trap_output=True) - return backups.splitlines() - - def _remove_backup(self, backup): - """Remove a named backup from the test shard.""" - utils.run_vtctl( - tablet.get_backup_storage_flags() + - ['RemoveBackup', 'test_keyspace/0', backup], - auto_log=True, mode=utils.VTCTL_VTCTL) - - def _backup_only(self, t, initial_backup=False): - """Erase mysql/tablet dir, then start tablet with restore only.""" - logging.debug('starting backup only job') - t.remove_tree(ignore_options=True) - - extra_args = ['-allow_first_backup','-db-credentials-file', db_credentials_file] - if use_xtrabackup: - extra_args.extend(xtrabackup_args) - - if initial_backup: - extra_args.extend(["-initial_backup"]) - - logging.debug("starting backup tablet %s",str(datetime.datetime.now())) - proc = t.start_vtbackup(init_db=new_init_db, - init_keyspace='test_keyspace', - init_shard='0', - extra_args=extra_args) - logging.debug('tablet started waiting for process to end %s',proc) - utils.wait_procs([proc],True) - logging.debug("backup tablet done %s",str(datetime.datetime.now())) - - def test_tablet_initial_backup(self): - self._test_initial_backup() - - # Restore the Shard from the initial backup - self._init_tablets(init=False,start=False) - - # Restore the Tablets - self._restore(tablet_master, tablet_type='replica',wait_for_state="NOT_SERVING") - utils.run_vtctl(['TabletExternallyReparented',tablet_master.tablet_alias]) - self._restore(tablet_replica1, tablet_type='replica') - - # Run the entire backup test - self._test_first_backup('replica', True) - - def _test_initial_backup(self): - """Test Initial Backup Flow - test_initial_backup will: - - Create a shard using vtbackup and --initial-backup - - Create the rest of the cluster restoring from backup - - Externally Reparenting to a master tablet - - Insert Some data - - Verify that the cluster is working - - Take a Second Backup - - Bring up a second replica, and restore from the second backup - - list the backups, remove them - """ - self._backup_only(backup_tablet,initial_backup=True) - backups = self._list_backups() - logging.debug('list of backups after initial: %s', backups) - self.assertEqual(len(backups), 1) - - def test_tablet_backup_only(self): - self._init_tablets() - self._test_first_backup('replica', True) - - def _test_first_backup(self, tablet_type, backup_only): - """Test backup flow. - - test_backup will: - - create a shard with master and replica1 only - - run InitShardMaster - - insert some data - - take a backup - - insert more data on the master - - bring up tablet_replica2 after the fact, let it restore the backup - - check all data is right (before+after backup data) - - list the backup, remove it - - Args: - tablet_type: 'replica' or 'rdonly'. - """ - # insert data on master, wait for slave to get it - backups_count = len(self._list_backups()) - - tablet_master.mquery('vt_test_keyspace', self._create_vt_insert_test) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # backup the slave - alias = tablet_replica1.tablet_alias - logging.debug("taking backup %s",str(datetime.datetime.now())) - if not backup_only: - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - else: - self._backup_only(backup_tablet) - alias = backup_tablet.tablet_alias - logging.debug("done taking backup %s",str(datetime.datetime.now())) - # end if - - # check that the backup shows up in the listing - backups = self._list_backups() - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), backups_count+1) - - # insert more data on the master - self._insert_data(tablet_master, 2) - - # now bring up the other slave, letting it restore from backup. - self._restore(tablet_replica2, tablet_type=tablet_type) - - # check the new slave has the data - self._check_data(tablet_replica2, 2, 'replica2 tablet getting data') - - # check that the restored slave has the right local_metadata - result = tablet_replica2.mquery('_vt', 'select * from local_metadata') - metadata = {} - for row in result: - metadata[row[0]] = row[1] - self.assertEqual(metadata['Alias'], 'test_nj-0000062346') - self.assertEqual(metadata['ClusterAlias'], 'test_keyspace.0') - self.assertEqual(metadata['DataCenter'], 'test_nj') - if tablet_type == 'replica': - self.assertEqual(metadata['PromotionRule'], 'neutral') - else: - self.assertEqual(metadata['PromotionRule'], 'must_not') - - for backup in backups: - self._remove_backup(backup) - - backups = self._list_backups() - logging.debug('list of backups after remove: %s', backups) - self.assertEqual(len(backups), 0) - - tablet_replica2.kill_vttablet() - -if __name__ == '__main__': - utils.main() diff --git a/test/backup_transform.py b/test/backup_transform.py deleted file mode 100755 index 00f3ef9223e..00000000000 --- a/test/backup_transform.py +++ /dev/null @@ -1,313 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import json -import logging -import os -import shutil -import unittest - -import MySQLdb - -import environment -import tablet -import utils -from mysql_flavor import mysql_flavor - -use_mysqlctld = False -tablet_master = None -tablet_replica1 = None -tablet_replica2 = None -new_init_db = '' -db_credentials_file = '' - - -def setUpModule(): - global new_init_db, db_credentials_file - global tablet_master, tablet_replica1, tablet_replica2 - - tablet_master = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - tablet_replica1 = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - tablet_replica2 = tablet.Tablet(use_mysqlctld=use_mysqlctld, - vt_dba_passwd='VtDbaPass') - - try: - environment.topo_server().setup() - - credentials = { - 'vt_dba': ['VtDbaPass'], - 'vt_app': ['VtAppPass'], - 'vt_allprivs': ['VtAllprivsPass'], - 'vt_repl': ['VtReplPass'], - 'vt_filtered': ['VtFilteredPass'], - } - db_credentials_file = environment.tmproot+'/db_credentials.json' - with open(db_credentials_file, 'w') as fd: - fd.write(json.dumps(credentials)) - - # Determine which column is used for user passwords in this MySQL version. - proc = tablet_master.init_mysql() - if use_mysqlctld: - tablet_master.wait_for_mysqlctl_socket() - else: - utils.wait_procs([proc]) - try: - tablet_master.mquery('mysql', 'select password from mysql.user limit 0', - user='root') - password_col = 'password' - except MySQLdb.DatabaseError: - password_col = 'authentication_string' - utils.wait_procs([tablet_master.teardown_mysql()]) - tablet_master.remove_tree(ignore_options=True) - - # Create a new init_db.sql file that sets up passwords for all users. - # Then we use a db-credentials-file with the passwords. - new_init_db = environment.tmproot + '/init_db_with_passwords.sql' - with open(environment.vtroot + '/config/init_db.sql') as fd: - init_db = fd.read() - with open(new_init_db, 'w') as fd: - fd.write(init_db) - fd.write(mysql_flavor().change_passwords(password_col)) - - # start mysql instance external to the test - setup_procs = [ - tablet_master.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica1.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica2.init_mysql(init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]), - ] - if use_mysqlctld: - tablet_master.wait_for_mysqlctl_socket() - tablet_replica1.wait_for_mysqlctl_socket() - tablet_replica2.wait_for_mysqlctl_socket() - else: - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [ - tablet_master.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica1.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]), - tablet_replica2.teardown_mysql(extra_args=['-db-credentials-file', - db_credentials_file]), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - tablet_master.remove_tree() - tablet_replica1.remove_tree() - tablet_replica2.remove_tree() - - -class TestBackupTransform(unittest.TestCase): - - def setUp(self): - for t in tablet_master, tablet_replica1: - t.create_db('vt_test_keyspace') - - tablet_master.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=['-db-credentials-file', - db_credentials_file]) - tablet_replica1.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=['-db-credentials-file', - db_credentials_file]) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_master.tablet_alias]) - - def tearDown(self): - for t in tablet_master, tablet_replica1, tablet_replica2: - t.kill_vttablet() - - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in [tablet_master, tablet_replica1, tablet_replica2]: - t.reset_replication() - t.set_semi_sync_enabled(master=False, slave=False) - t.clean_dbs() - - for backup in self._list_backups(): - self._remove_backup(backup) - - _create_vt_insert_test = '''create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - def _insert_data(self, t, index): - """Add a single row with value 'index' to the given tablet.""" - t.mquery( - 'vt_test_keyspace', - "insert into vt_insert_test (msg) values ('test %s')" % - index, write=True) - - def _check_data(self, t, count, msg): - """Check that the specified tablet has the expected number of rows.""" - timeout = 10 - while True: - try: - result = t.mquery( - 'vt_test_keyspace', 'select count(*) from vt_insert_test') - if result[0][0] == count: - break - except MySQLdb.DatabaseError: - # ignore exceptions, we'll just timeout (the tablet creation - # can take some time to replicate, and we get a 'table vt_insert_test - # does not exist exception in some rare cases) - logging.exception('exception waiting for data to replicate') - timeout = utils.wait_step(msg, timeout) - - def _restore(self, t, tablet_type='replica'): - """Erase mysql/tablet dir, then start tablet with restore enabled.""" - self._reset_tablet_dir(t) - - t.start_vttablet(wait_for_state='SERVING', - init_tablet_type=tablet_type, - init_keyspace='test_keyspace', - init_shard='0', - supports_backups=True, - extra_args=['-db-credentials-file', db_credentials_file]) - - # check semi-sync is enabled for replica, disabled for rdonly. - if tablet_type == 'replica': - t.check_db_var('rpl_semi_sync_slave_enabled', 'ON') - t.check_db_status('rpl_semi_sync_slave_status', 'ON') - else: - t.check_db_var('rpl_semi_sync_slave_enabled', 'OFF') - t.check_db_status('rpl_semi_sync_slave_status', 'OFF') - - def _reset_tablet_dir(self, t): - """Stop mysql, delete everything including tablet dir, restart mysql.""" - extra_args = ['-db-credentials-file', db_credentials_file] - utils.wait_procs([t.teardown_mysql(extra_args=extra_args)]) - # Specify ignore_options because we want to delete the tree even - # if the test's -k / --keep-logs was specified on the command line. - t.remove_tree(ignore_options=True) - proc = t.init_mysql(init_db=new_init_db, extra_args=extra_args) - if use_mysqlctld: - t.wait_for_mysqlctl_socket() - else: - utils.wait_procs([proc]) - - def _list_backups(self): - """Get a list of backup names for the test shard.""" - backups, _ = utils.run_vtctl(tablet.get_backup_storage_flags() + - ['ListBackups', 'test_keyspace/0'], - mode=utils.VTCTL_VTCTL, trap_output=True) - return backups.splitlines() - - def _remove_backup(self, backup): - """Remove a named backup from the test shard.""" - utils.run_vtctl( - tablet.get_backup_storage_flags() + - ['RemoveBackup', 'test_keyspace/0', backup], - auto_log=True, mode=utils.VTCTL_VTCTL) - - def test_backup_transform(self): - """Use a transform, tests we backup and restore properly.""" - - # Insert data on master, make sure slave gets it. - tablet_master.mquery('vt_test_keyspace', self._create_vt_insert_test) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # Restart the replica with the transform parameter. - tablet_replica1.kill_vttablet() - - xtra_args = ['-db-credentials-file', db_credentials_file] - hook_args = ['-backup_storage_hook', - 'test_backup_transform', - '-backup_storage_compress=false'] - xtra_args.extend(hook_args) - - tablet_replica1.start_vttablet(supports_backups=True, - extra_args=xtra_args) - - # Take a backup, it should work. - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # Insert more data on the master. - self._insert_data(tablet_master, 2) - - # Make sure we have the TransformHook in the MANIFEST, and that - # every file starts with 'header'. - backups = self._list_backups() - self.assertEqual(len(backups), 1, 'invalid backups: %s' % backups) - location = os.path.join(environment.tmproot, 'backupstorage', - 'test_keyspace', '0', backups[0]) - with open(os.path.join(location, 'MANIFEST')) as fd: - contents = fd.read() - manifest = json.loads(contents) - self.assertEqual(manifest['TransformHook'], 'test_backup_transform') - self.assertEqual(manifest['SkipCompress'], True) - for i in xrange(len(manifest['FileEntries'])): - name = os.path.join(location, '%d' % i) - with open(name) as fd: - line = fd.readline() - self.assertEqual(line, 'header\n', 'wrong file contents for %s' % name) - - # Then start replica2 from backup, make sure that works. - # Note we don't need to pass in the backup_storage_transform parameter, - # as it is read from the MANIFEST. - self._restore(tablet_replica2) - - # Check the new slave has all the data. - self._check_data(tablet_replica2, 2, 'replica2 tablet getting data') - - def test_backup_transform_error(self): - """Use a transform, force an error, make sure the backup fails.""" - - # Restart the replica with the transform parameter. - tablet_replica1.kill_vttablet() - xtra_args = ['-db-credentials-file', db_credentials_file] - hook_args = ['-backup_storage_hook','test_backup_error'] - xtra_args.extend(hook_args) - tablet_replica1.start_vttablet(supports_backups=True, - extra_args=xtra_args) - - # This will fail, make sure we get the right error. - _, err = utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], - auto_log=True, expect_fail=True) - self.assertIn('backup is not usable, aborting it', err) - - # And make sure there is no backup left. - backups = self._list_backups() - self.assertEqual(len(backups), 0, 'invalid backups: %s' % backups) - -if __name__ == '__main__': - utils.main() diff --git a/test/backup_transform_mysqlctld.py b/test/backup_transform_mysqlctld.py deleted file mode 100755 index 537b3d95a22..00000000000 --- a/test/backup_transform_mysqlctld.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Re-runs backup_transform.py with use_mysqlctld=True.""" - -import backup_transform -import utils - -if __name__ == '__main__': - backup_transform.use_mysqlctld = True - utils.main(backup_transform) diff --git a/test/base_sharding.py b/test/base_sharding.py deleted file mode 100644 index 0978993e039..00000000000 --- a/test/base_sharding.py +++ /dev/null @@ -1,433 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This module contains a base class and utility functions for sharding tests. -""" - -import logging - -import struct - -from vtdb import keyrange_constants - -import utils - - -keyspace_id_type = keyrange_constants.KIT_UINT64 -use_rbr = False -use_multi_split_diff = False -pack_keyspace_id = struct.Struct('!Q').pack - -# fixed_parent_id is used as fixed value for the "parent_id" column in all rows. -# All tests assume a multi-column primary key (parent_id, id) but only adjust -# the "id" column and use this fixed value for "parent_id". -# Since parent_id is fixed, not all test code has to include parent_id in a -# WHERE clause (at the price of a full table scan). -fixed_parent_id = 86 - - -class BaseShardingTest(object): - """This base class uses unittest.TestCase methods to check various things. - - All sharding tests should inherit from this base class, and use the - methods as needed. - """ - - # _insert_value inserts a value in the MySQL database along with the comments - # required for routing. - # NOTE: We assume that the column name for the keyspace_id is called - # 'custom_ksid_col'. This is a regression test which tests for - # places which previously hardcoded the column name to 'keyspace_id'. - def _insert_value(self, tablet_obj, table, mid, msg, keyspace_id): - k = utils.uint64_to_hex(keyspace_id) - tablet_obj.mquery( - tablet_obj.dbname, - ['begin', - 'insert into %s(parent_id, id, msg, custom_ksid_col) ' - 'values(%d, %d, "%s", 0x%x) /* vtgate:: keyspace_id:%s */ ' - '/* id:%d */' % - (table, fixed_parent_id, mid, msg, keyspace_id, k, mid), - 'commit'], - write=True) - - def _insert_multi_value(self, tablet_obj, table, mids, msgs, keyspace_ids): - """Generate multi-shard insert statements.""" - comma_sep = ',' - querystr = ('insert into %s(parent_id, id, msg, custom_ksid_col) values' - %(table)) - values_str = '' - id_str = '/* id:' - ksid_str = '' - - for mid, msg, keyspace_id in zip(mids, msgs, keyspace_ids): - ksid_str += utils.uint64_to_hex(keyspace_id)+comma_sep - values_str += ('(%d, %d, "%s", 0x%x)' % - (fixed_parent_id, mid, msg, keyspace_id) + comma_sep) - id_str += '%d' % (mid) + comma_sep - - values_str = values_str.rstrip(comma_sep) - values_str += '/* vtgate:: keyspace_id:%s */ ' %(ksid_str.rstrip(comma_sep)) - values_str += id_str.rstrip(comma_sep) + '*/' - - querystr += values_str - tablet_obj.mquery( - tablet_obj.dbname, - ['begin', - querystr, - 'commit'], - write=True) - - def _exec_non_annotated_update(self, tablet_obj, table, mids, new_val): - tablet_obj.mquery( - 'vt_test_keyspace', - ['begin', - 'update %s set msg = "%s" where parent_id = %d and id in (%s)' % - (table, new_val, fixed_parent_id, ','.join([str(i) for i in mids])), - 'commit'], - write=True) - - def _exec_non_annotated_delete(self, tablet_obj, table, mids): - tablet_obj.mquery( - 'vt_test_keyspace', - ['begin', - 'delete from %s where parent_id = %d and id in (%s)' % - (table, fixed_parent_id, ','.join([str(i) for i in mids])), - 'commit'], - write=True) - - def _get_value(self, tablet_obj, table, mid): - """Returns the row(s) from the table for the provided id, using MySQL. - - Args: - tablet_obj: the tablet to get data from. - table: the table to query. - mid: id field of the table. - Returns: - A tuple of results. - """ - return tablet_obj.mquery( - tablet_obj.dbname, - 'select parent_id, id, msg, custom_ksid_col from %s ' - 'where parent_id=%d and id=%d' % - (table, fixed_parent_id, mid)) - - def _check_value(self, tablet_obj, table, mid, msg, keyspace_id, - should_be_here=True): - result = self._get_value(tablet_obj, table, mid) - if keyspace_id_type == keyrange_constants.KIT_BYTES: - fmt = '%s' - keyspace_id = pack_keyspace_id(keyspace_id) - else: - fmt = '%x' - if should_be_here: - self.assertEqual(result, ((fixed_parent_id, mid, msg, keyspace_id),), - ('Bad row in tablet %s for id=%d, custom_ksid_col=' + - fmt + ', row=%s') % (tablet_obj.tablet_alias, mid, - keyspace_id, str(result))) - else: - self.assertEqual( - len(result), 0, - ('Extra row in tablet %s for id=%d, custom_ksid_col=' + - fmt + ': %s') % (tablet_obj.tablet_alias, mid, keyspace_id, - str(result))) - - def _is_value_present_and_correct( - self, tablet_obj, table, mid, msg, keyspace_id): - """_is_value_present_and_correct tries to read a value. - - Args: - tablet_obj: the tablet to get data from. - table: the table to query. - mid: the id of the row to query. - msg: expected value of the msg column in the row. - keyspace_id: expected value of the keyspace_id column in the row. - Returns: - True if the value (row) is there and correct. - False if the value is not there. - If the value is not correct, the method will call self.fail. - """ - result = self._get_value(tablet_obj, table, mid) - if not result: - return False - if keyspace_id_type == keyrange_constants.KIT_BYTES: - fmt = '%s' - keyspace_id = pack_keyspace_id(keyspace_id) - else: - fmt = '%x' - self.assertEqual(result, ((fixed_parent_id, mid, msg, keyspace_id),), - ('Bad row in tablet %s for id=%d, ' - 'custom_ksid_col=' + fmt) % ( - tablet_obj.tablet_alias, mid, keyspace_id)) - return True - - def check_binlog_player_vars(self, tablet_obj, source_shards, - seconds_behind_master_max=0): - """Checks the binlog player variables are correctly exported. - - Args: - tablet_obj: the tablet to check. - source_shards: the shards to check we are replicating from. - seconds_behind_master_max: if non-zero, the lag should be smaller than - this value. - """ - v = utils.get_vars(tablet_obj.port) - self.assertIn('VReplicationStreamCount', v) - self.assertEquals(v['VReplicationStreamCount'], len(source_shards)) - self.assertIn('VReplicationSecondsBehindMasterMax', v) - self.assertIn('VReplicationSecondsBehindMaster', v) - self.assertIn('VReplicationSource', v) - shards = v['VReplicationSource'].values() - self.assertEquals(sorted(shards), sorted(source_shards)) - self.assertIn('VReplicationSourceTablet', v) - for uid in v['VReplicationSource']: - self.assertIn(uid, v['VReplicationSourceTablet']) - if seconds_behind_master_max != 0: - self.assertTrue( - v['VReplicationSecondsBehindMasterMax'] < - seconds_behind_master_max, - 'VReplicationSecondsBehindMasterMax is too high: %d > %d' % ( - v['VReplicationSecondsBehindMasterMax'], - seconds_behind_master_max)) - for uid in v['VReplicationSource']: - self.assertTrue( - v['VReplicationSecondsBehindMaster'][uid] < - seconds_behind_master_max, - 'VReplicationSecondsBehindMaster is too high: %d > %d' % ( - v['VReplicationSecondsBehindMaster'][uid], - seconds_behind_master_max)) - - def check_binlog_server_vars(self, tablet_obj, horizontal=True, - min_statements=0, min_transactions=0): - """Checks the binlog server variables are correctly exported. - - Args: - tablet_obj: the tablet to check. - horizontal: true if horizontal split, false for vertical split. - min_statements: check the statement count is greater or equal to this. - min_transactions: check the transaction count is greater or equal to this. - """ - v = utils.get_vars(tablet_obj.port) - if horizontal: - skey = 'UpdateStreamKeyRangeStatements' - tkey = 'UpdateStreamKeyRangeTransactions' - else: - skey = 'UpdateStreamTablesStatements' - tkey = 'UpdateStreamTablesTransactions' - - self.assertIn(skey, v) - self.assertIn(tkey, v) - if min_statements > 0: - self.assertTrue(v[skey] >= min_statements, - 'only got %d < %d statements' % (v[skey], min_statements)) - if min_transactions > 0: - self.assertTrue(v[tkey] >= min_transactions, - 'only got %d < %d transactions' % (v[tkey], - min_transactions)) - - def check_stream_health_equals_binlog_player_vars(self, tablet_obj, count): - """Checks the variables exported by streaming health check match vars. - - Args: - tablet_obj: the tablet to check. - count: number of binlog players to expect. - """ - - blp_stats = utils.get_vars(tablet_obj.port) - self.assertEqual(blp_stats['VReplicationStreamCount'], count) - - # Enforce health check because it's not running by default as - # tablets may not be started with it, or may not run it in time. - utils.run_vtctl(['RunHealthCheck', tablet_obj.tablet_alias]) - stream_health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - tablet_obj.tablet_alias]) - logging.debug('Got health: %s', str(stream_health)) - self.assertNotIn('serving', stream_health) - self.assertIn('realtime_stats', stream_health) - self.assertNotIn('health_error', stream_health['realtime_stats']) - self.assertIn('binlog_players_count', stream_health['realtime_stats']) - self.assertEqual(blp_stats['VReplicationStreamCount'], - stream_health['realtime_stats']['binlog_players_count']) - self.assertEqual(blp_stats['VReplicationSecondsBehindMasterMax'], - stream_health['realtime_stats'].get( - 'seconds_behind_master_filtered_replication', 0)) - - def check_destination_master(self, tablet_obj, source_shards): - """Performs multiple checks on a destination master. - - Combines the following: - - wait_for_binlog_player_count - - check_binlog_player_vars - - check_stream_health_equals_binlog_player_vars - - Args: - tablet_obj: the tablet to check. - source_shards: the shards to check we are replicating from. - """ - tablet_obj.wait_for_binlog_player_count(len(source_shards)) - self.check_binlog_player_vars(tablet_obj, source_shards) - self.check_stream_health_equals_binlog_player_vars(tablet_obj, - len(source_shards)) - - def check_running_binlog_player(self, tablet_obj, query, transaction, - extra_text=None): - """Checks binlog player is running and showing in status. - - Args: - tablet_obj: the tablet to check. - query: number of expected queries. - transaction: number of expected transactions. - extra_text: if present, look for it in status too. - """ - status = tablet_obj.get_status() - self.assertIn('VReplication state: Open', status) - self.assertIn( - '' % (query+transaction, query, - transaction), status) - self.assertIn('', status) - if extra_text: - self.assertIn(extra_text, status) - - def check_no_binlog_player(self, tablet_obj): - """Checks no binlog player is running. - - Also checks the tablet is not showing any binlog player in its status page. - - Args: - tablet_obj: the tablet to check. - """ - tablet_obj.wait_for_binlog_player_count(0) - - def check_throttler_service(self, throttler_server, names, rate): - """Checks that the throttler responds to RPC requests. - - We assume it was enabled by SplitClone with the flag --max_tps 9999. - - Args: - throttler_server: vtworker or vttablet RPC endpoint. Format: host:port - names: Names of the throttlers e.g. BinlogPlayer/0 or /. - rate: Expected initial rate the throttler was started with. - """ - self.check_throttler_service_maxrates(throttler_server, names, rate) - - self.check_throttler_service_configuration(throttler_server, names) - - def check_throttler_service_maxrates(self, throttler_server, names, rate): - """Checks the vtctl ThrottlerMaxRates and ThrottlerSetRate commands.""" - # Avoid flakes by waiting for all throttlers. (Necessary because filtered - # replication on vttablet will register the throttler asynchronously.) - timeout_s = 10 - while True: - stdout, _ = utils.run_vtctl(['ThrottlerMaxRates', '--server', - throttler_server], auto_log=True, - trap_output=True) - if '%d active throttler(s)' % len(names) in stdout: - break - timeout_s = utils.wait_step('all throttlers registered', timeout_s) - for name in names: - self.assertIn('| %s | %d |' % (name, rate), stdout) - self.assertIn('%d active throttler(s)' % len(names), stdout) - - # Check that it's possible to change the max rate on the throttler. - new_rate = 'unlimited' - stdout, _ = utils.run_vtctl(['ThrottlerSetMaxRate', '--server', - throttler_server, new_rate], - auto_log=True, trap_output=True) - self.assertIn('%d active throttler(s)' % len(names), stdout) - stdout, _ = utils.run_vtctl(['ThrottlerMaxRates', '--server', - throttler_server], auto_log=True, - trap_output=True) - for name in names: - self.assertIn('| %s | %s |' % (name, new_rate), stdout) - self.assertIn('%d active throttler(s)' % len(names), stdout) - - def check_throttler_service_configuration(self, throttler_server, names): - """Checks the vtctl (Get|Update|Reset)ThrottlerConfiguration commands.""" - # Verify updating the throttler configuration. - stdout, _ = utils.run_vtctl(['UpdateThrottlerConfiguration', - '--server', throttler_server, - '--copy_zero_values', - 'target_replication_lag_sec:12345 ' - 'max_replication_lag_sec:65789 ' - 'initial_rate:3 ' - 'max_increase:0.4 ' - 'emergency_decrease:0.5 ' - 'min_duration_between_increases_sec:6 ' - 'max_duration_between_increases_sec:7 ' - 'min_duration_between_decreases_sec:8 ' - 'spread_backlog_across_sec:9 ' - 'ignore_n_slowest_replicas:0 ' - 'ignore_n_slowest_rdonlys:0 ' - 'age_bad_rate_after_sec:12 ' - 'bad_rate_increase:0.13 ' - 'max_rate_approach_threshold: 0.9 '], - auto_log=True, trap_output=True) - self.assertIn('%d active throttler(s)' % len(names), stdout) - # Check the updated configuration. - stdout, _ = utils.run_vtctl(['GetThrottlerConfiguration', - '--server', throttler_server], - auto_log=True, trap_output=True) - for name in names: - # The max should be set and have a non-zero value. - # We test only the first field 'target_replication_lag_sec'. - self.assertIn('| %s | target_replication_lag_sec:12345 ' % (name), stdout) - # protobuf omits fields with a zero value in the text output. - self.assertNotIn('ignore_n_slowest_replicas', stdout) - self.assertIn('%d active throttler(s)' % len(names), stdout) - - # Reset clears our configuration values. - stdout, _ = utils.run_vtctl(['ResetThrottlerConfiguration', - '--server', throttler_server], - auto_log=True, trap_output=True) - self.assertIn('%d active throttler(s)' % len(names), stdout) - # Check that the reset configuration no longer has our values. - stdout, _ = utils.run_vtctl(['GetThrottlerConfiguration', - '--server', throttler_server], - auto_log=True, trap_output=True) - for name in names: - # Target lag value should no longer be 12345 and be back to the default. - self.assertNotIn('target_replication_lag_sec:12345', stdout) - self.assertIn('%d active throttler(s)' % len(names), stdout) - - def verify_reconciliation_counters(self, worker_port, online_or_offline, - table, inserts, updates, deletes, equal): - """Checks that the reconciliation Counters have the expected values.""" - worker_vars = utils.get_vars(worker_port) - - i = worker_vars['Worker' + online_or_offline + 'InsertsCounters'] - if inserts == 0: - self.assertNotIn(table, i) - else: - self.assertEqual(i[table], inserts) - - u = worker_vars['Worker' + online_or_offline + 'UpdatesCounters'] - if updates == 0: - self.assertNotIn(table, u) - else: - self.assertEqual(u[table], updates) - - d = worker_vars['Worker' + online_or_offline + 'DeletesCounters'] - if deletes == 0: - self.assertNotIn(table, d) - else: - self.assertEqual(d[table], deletes) - - e = worker_vars['Worker' + online_or_offline + 'EqualRowsCounters'] - if equal == 0: - self.assertNotIn(table, e) - else: - self.assertEqual(e[table], equal) diff --git a/test/binlog.py b/test/binlog.py deleted file mode 100755 index 7e1cc3230a4..00000000000 --- a/test/binlog.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This file contains integration tests for the go/vt/binlog package. -# It sets up filtered replication between two shards and checks how data flows -# through binlog streamer. - -import base64 -import logging -import unittest - -from vtdb import keyrange_constants - -import environment -import tablet -import utils -from mysql_flavor import mysql_flavor - -src_master = tablet.Tablet() -src_replica = tablet.Tablet() -src_rdonly = tablet.Tablet() -dst_master = tablet.Tablet() -dst_replica = tablet.Tablet() -dst_rdonly = tablet.Tablet() - -all_tablets = [src_master, src_replica, src_rdonly, dst_master, dst_replica, - dst_rdonly] - - -def setUpModule(): - try: - environment.topo_server().setup() - - setup_procs = [t.init_mysql(use_rbr=False) for t in all_tablets] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - - # Set up binlog stream from shard 0 to shard 1. - # Modeled after initial_sharding.py. - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - utils.run_vtctl(['SetKeyspaceShardingInfo', '-force', 'test_keyspace', - 'keyspace_id', keyrange_constants.KIT_UINT64]) - - src_master.init_tablet('replica', 'test_keyspace', '0') - src_replica.init_tablet('replica', 'test_keyspace', '0') - src_rdonly.init_tablet('rdonly', 'test_keyspace', '0') - - for t in [src_master, src_replica, src_rdonly]: - t.start_vttablet(wait_for_state=None) - - for t in [src_master, src_replica, src_rdonly]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - src_master.tablet_alias], auto_log=True) - - # Create schema - logging.debug('Creating schema...') - create_table = '''create table test_table( - id bigint auto_increment, - keyspace_id bigint(20) unsigned, - msg varchar(64), - primary key (id), - index by_msg (msg) - ) Engine=InnoDB''' - - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table, - 'test_keyspace'], auto_log=True) - - # run a health check on source replica so it responds to discovery - # (for binlog players) and on the source rdonlys (for workers) - utils.run_vtctl(['RunHealthCheck', src_replica.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', src_rdonly.tablet_alias]) - - # Create destination shard (won't be serving as there is no DB) - dst_master.init_tablet('replica', 'test_keyspace', '-') - dst_replica.init_tablet('replica', 'test_keyspace', '-') - dst_rdonly.init_tablet('rdonly', 'test_keyspace', '-') - dst_master.start_vttablet(wait_for_state='NOT_SERVING') - dst_replica.start_vttablet(wait_for_state='NOT_SERVING') - dst_rdonly.start_vttablet(wait_for_state='NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-', - dst_master.tablet_alias], auto_log=True) - - # copy the schema - utils.run_vtctl(['CopySchemaShard', src_replica.tablet_alias, - 'test_keyspace/-'], auto_log=True) - - # run the clone worker (this is a degenerate case, source and destination - # both have the full keyrange. Happens to work correctly). - logging.debug('Running the clone worker to start binlog stream...') - utils.run_vtworker(['--cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitClone', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/0'], - auto_log=True) - dst_master.wait_for_binlog_player_count(1) - - # Wait for dst_replica to be ready. - dst_replica.wait_for_binlog_server_state('Enabled') - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - tablet.kill_tablets(all_tablets) - - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - for t in all_tablets: - t.remove_tree() - - -class TestBinlog(unittest.TestCase): - - def _wait_for_replica_event(self, position, sql): - """Wait for a replica event with the given SQL string.""" - while True: - event = utils.run_vtctl_json(['VtTabletUpdateStream', - '-position', position, - '-count', '1', - dst_replica.tablet_alias]) - if 'statements' not in event: - logging.debug('skipping event with no statements: %s', event) - for statement in event['statements']: - if 'sql' not in statement: - logging.debug('skipping statement with no sql: %s', statement) - continue - base64sql = statement['sql'] - s = base64.standard_b64decode(base64sql) - logging.debug('found sql: %s', s) - if s == sql: - return - position = event['event_token']['position'] - - def test_charset(self): - start_position = mysql_flavor().master_position(dst_replica) - logging.debug('test_charset: starting @ %s', start_position) - - # Insert something that will replicate incorrectly if the charset is not - # propagated through binlog streamer to the destination. - # - # Vitess tablets default to using utf8, so we insert something crazy and - # pretend it's latin1. If the binlog player doesn't also pretend it's - # latin1, it will be inserted as utf8, which will change its value. - sql = ("INSERT INTO test_table (id, keyspace_id, msg) " - "VALUES (41523, 1, 'Šṛ́rỏé') /* vtgate:: keyspace_id:00000001 */") - src_master.mquery( - 'vt_test_keyspace', - sql, - conn_params={'charset': 'latin1'}, write=True) - self._wait_for_replica_event(start_position, sql) - - # Check the value. - data = dst_master.mquery( - 'vt_test_keyspace', - 'SELECT id, keyspace_id, msg FROM test_table WHERE id=41523 LIMIT 1') - self.assertEqual(len(data), 1, 'No data replicated.') - self.assertEqual(len(data[0]), 3, 'Wrong number of columns.') - self.assertEqual(data[0][2], 'Šṛ́rỏé', - 'Data corrupted due to wrong charset.') - - def test_checksum_enabled(self): - start_position = mysql_flavor().master_position(dst_replica) - logging.debug('test_checksum_enabled: starting @ %s', start_position) - - # Enable binlog_checksum, which will also force a log rotation that should - # cause binlog streamer to notice the new checksum setting. - if not mysql_flavor().enable_binlog_checksum(dst_replica): - logging.debug( - 'skipping checksum test on flavor without binlog_checksum setting') - return - - # Insert something and make sure it comes through intact. - sql = ( - "INSERT INTO test_table (id, keyspace_id, msg) " - "VALUES (19283, 1, 'testing checksum enabled') " - "/* vtgate:: keyspace_id:00000001 */") - src_master.mquery('vt_test_keyspace', sql, write=True) - - # Look for it using update stream to see if binlog streamer can talk to - # dst_replica, which now has binlog_checksum enabled. - self._wait_for_replica_event(start_position, sql) - - def test_checksum_disabled(self): - # Disable binlog_checksum to make sure we can also talk to a server without - # checksums enabled, in case they are enabled by default. - start_position = mysql_flavor().master_position(dst_replica) - logging.debug('test_checksum_disabled: starting @ %s', start_position) - - # For flavors that don't support checksums, this is a no-op. - mysql_flavor().disable_binlog_checksum(dst_replica) - - # Insert something and make sure it comes through intact. - sql = ( - "INSERT INTO test_table (id, keyspace_id, msg) " - "VALUES (58812, 1, 'testing checksum disabled') " - "/* vtgate:: keyspace_id:00000001 */") - src_master.mquery( - 'vt_test_keyspace', sql, write=True) - - # Look for it using update stream to see if binlog streamer can talk to - # dst_replica, which now has binlog_checksum disabled. - self._wait_for_replica_event(start_position, sql) - - -if __name__ == '__main__': - utils.main() diff --git a/test/cache_invalidation.py b/test/cache_invalidation.py deleted file mode 100755 index 595ff7f8e6b..00000000000 --- a/test/cache_invalidation.py +++ /dev/null @@ -1,696 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This test provides a blue print for a service using a caching layer. - -We use an in-memory cache to simplify the implementation, but using a -distributed memcache pool for instance would work the same way. Note -we always use CAS (compare and swap) on the cache values, and ignore -the failures. That way multiple processes trying to affect the cache -won't be an issue. - -It starts an invalidation thread, that mimics what a cache -invalidation process would do. - -It also has an application layer that can also write entries to the -cache if necessary. -""" - -import logging -import threading -import time -import unittest - -import environment -import tablet -import utils -from vtdb import dbexceptions -from vtdb import event_token -from vtdb import proto3_encoding -from vtdb import vtgate_client -from vtproto import topodata_pb2 -from vtproto import query_pb2 - - -master_tablet = tablet.Tablet() -replica_tablet = tablet.Tablet() - -_create_vt_a = '''create table if not exists vt_a ( -id bigint, -name varchar(128), -primary key(id) -) Engine=InnoDB''' - -_create_vt_b = '''create table if not exists vt_b ( -id bigint, -address varchar(128), -primary key(id) -) Engine=InnoDB''' - - -def setUpModule(): - try: - environment.topo_server().setup() - setup_procs = [master_tablet.init_mysql(), - replica_tablet.init_mysql()] - utils.wait_procs(setup_procs) - - # start a vtctld so the vtctl insert commands are just RPCs, not forks. - utils.Vtctld().start() - - # Start up a master mysql and vttablet - logging.debug('Setting up tablets') - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - master_tablet.init_tablet('replica', 'test_keyspace', '0', tablet_index=0) - replica_tablet.init_tablet('replica', 'test_keyspace', '0', tablet_index=1) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - master_tablet.create_db('vt_test_keyspace') - replica_tablet.create_db('vt_test_keyspace') - - master_tablet.start_vttablet(wait_for_state=None) - replica_tablet.start_vttablet(wait_for_state=None) - master_tablet.wait_for_vttablet_state('NOT_SERVING') - replica_tablet.wait_for_vttablet_state('NOT_SERVING') - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - master_tablet.tablet_alias], auto_log=True) - - utils.wait_for_tablet_type(replica_tablet.tablet_alias, 'replica') - master_tablet.wait_for_vttablet_state('SERVING') - replica_tablet.wait_for_vttablet_state('SERVING') - - master_tablet.mquery('vt_test_keyspace', _create_vt_a) - master_tablet.mquery('vt_test_keyspace', _create_vt_b) - - utils.run_vtctl(['ReloadSchemaShard', 'test_keyspace/0']) - utils.run_vtctl(['RebuildVSchemaGraph']) - - utils.VtGate().start(tablets=[master_tablet, replica_tablet]) - utils.vtgate.wait_for_endpoints('test_keyspace.0.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.0.replica', 1) - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - logging.debug('Tearing down the servers and setup') - tablet.kill_tablets([master_tablet, replica_tablet]) - teardown_procs = [master_tablet.teardown_mysql(), - replica_tablet.teardown_mysql()] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - master_tablet.remove_tree() - replica_tablet.remove_tree() - - -class Cache(object): - """Cache is the in-memory cache for objects. - - Its backing store is a dict, indexed by key '
All: %d
Query: %d
' - 'Transaction: %d
-'. - Each value is a dict, with three values: - - 'version': a version number. - - 'token': an EventToken. - - 'value': an optional value. We use an empty tuple if the row is - not in the database. - """ - - def __init__(self): - self.values = {} - self.stats = { - 'cache_miss': 0, - 'cache_hit': 0, - 'add': 0, - 'cas': 0, - 'noop': 0, - } - - def gets(self, table_name, row_id): - """Returns a cache entry if it exists. - - Args: - table_name: the name of the table. - row_id: the row id. - - Returns: - version: entry version, or None if there is no entry. - token: the EventToken for that row. - value: an optional value, or None if not set. - """ - key = '%s-%d' % (table_name, row_id) - if key not in self.values: - self.stats['cache_miss'] += 1 - return None, None, None - self.stats['cache_hit'] += 1 - entry = self.values[key] - return entry['version'], entry['token'], entry['value'] - - def add(self, table_name, row_id, token, value): - """Add a value to the cache, only if it doesn't exist. - - Args: - table_name: the name of the table. - row_id: the row id. - token: the EventToken associated with the read / invalidation. - value: the actual value. - - Raises: - KeyError: if the entry already exists. - """ - key = '%s-%d' % (table_name, row_id) - if key in self.values: - raise KeyError('add failed: key %s already in cache' % key) - self.stats['add'] += 1 - self.values[key] = { - 'version': 1, - 'token': token, - 'value': value, - } - - def cas(self, table_name, row_id, version, token, value): - """Update an entry in the cache. - - Args: - table_name: the name of the table. - row_id: the row id. - version: the existing version to update. - token: the EventToken associated with the read / invalidation. - value: the actual value. - - Raises: - KeyError: if the entry doesn't exist. - Exception: if the version is wrong. - """ - key = '%s-%d' % (table_name, row_id) - if key not in self.values: - raise KeyError('cas failed: key %s not in cache' % key) - if self.values[key]['version'] != version: - raise Exception( - 'cas failed: invalid version %d, have version %d in cache' % - (version, self.values[key]['version'])) - self.stats['cas'] += 1 - self.values[key] = { - 'version': version+1, - 'token': token, - 'value': value, - } - - def noop(self): - """Increments the noop counter.""" - self.stats['noop'] += 1 - - def stats_copy(self): - """Returns a copy of the cache stats.""" - return self.stats.copy() - - def stats_diff(self, before, **kwargs): - """Returns true iff the before stats differ exactly with provided args.""" - for name, value in kwargs.iteritems(): - if self.stats[name] != before[name] + value: - return False - return True - - -class InvalidatorThread(threading.Thread): - - def __init__(self, cache): - threading.Thread.__init__(self) - self.cache = cache - self.done = False - - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - self.conn = vtgate_client.connect(protocol, addr, 30.0) - self.timestamp = long(time.time()) - - self.start() - - def run(self): - while True: - try: - for event, resume_timestamp in self.conn.update_stream( - 'test_keyspace', topodata_pb2.REPLICA, - timestamp=self.timestamp, - shard='0'): - - if self.done: - return - - # Save the timestamp we get, so we can resume from it in case of - # restart. - self.timestamp = resume_timestamp - for statement in event.statements: - if statement.category == 1: # query_pb2.StreamEvent.DML - _, rows = proto3_encoding.convert_stream_event_statement( - statement) - for row in rows: - row_id = row[0] - self.invalidate(statement.table_name, row_id, - event.event_token) - - except dbexceptions.DatabaseError: - logging.exception( - 'InvalidatorThread got exception, continuing from timestamp %d', - self.timestamp) - - def invalidate(self, table_name, row_id, token): - logging.debug('Invalidating %s(%d) - %s:', table_name, row_id, token) - version, cache_event_token, _ = self.cache.gets(table_name, row_id) - if version is None: - logging.debug(' no entry in cache, saving event_token') - self.cache.add(table_name, row_id, token, None) - return - - if event_token.fresher(cache_event_token, token) >= 0: - # For invalidation, a couple things to consider: - # 1. If we can't compare the EventTokens, we want to store the - # invalidation, so it's safer. - # 2. If we have exactly the same EventToken, we do not want to - # store the invalidation. We have either an invalidation or a - # value, in both cases we're fine keeping it. - logging.debug(' invalidation event is older or equal than cache value,' - ' ignoring') - return - - logging.debug(' updating entry in the cache') - self.cache.cas(table_name, row_id, version, token, None) - - def kill(self): - """Kill stops the invalidator. We force an event so we can exit the loop.""" - logging.info('Stopping invalidator') - self.done = True - replica_tablet.mquery('vt_test_keyspace', 'flush logs') - self.join() - self.conn.close() - - -class TestCacheInvalidation(unittest.TestCase): - - def setUp(self): - self.cache = Cache() - self.invalidator = InvalidatorThread(self.cache) - - # Sleep a bit to be sure all binlog reading threads are going, and we - # eat up all previous invalidation messages. - time.sleep(1) - - def tearDown(self): - self.invalidator.kill() - - def _vtgate_connection(self): - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - return vtgate_client.connect(protocol, addr, 30.0) - - def _insert_value_a(self, row_id, name): - logging.debug('Inserting value %d into vt_a', row_id) - conn = self._vtgate_connection() - cursor = conn.cursor(tablet_type='master', keyspace='test_keyspace', - writable=True) - cursor.begin() - insert = 'insert into vt_a (id, name) values (:id, :name)' - bind_variables = { - 'id': row_id, - 'name': name, - } - cursor.execute(insert, bind_variables) - cursor.commit() - conn.close() - - def _get_value(self, table_name, row_id): - """Returns the value for a row, as an array. - - Args: - table_name: the name of the table. - row_id: the row id. - - Returns: - The value for the row as a tuple, or an empty tuple if the row - doesn't exist. - """ - logging.debug('Getting value %d from %s:', row_id, table_name) - - # First look in the cache. - version, cache_event_token, value = self.cache.gets(table_name, row_id) - if value is not None: - logging.debug(' got value from cache: %s', value) - return value - - # It's not in the cache, get it from the database. - conn = self._vtgate_connection() - cursor = conn.cursor(tablet_type='replica', keyspace='test_keyspace') - cursor.execute('select * from %s where id=:id' % table_name, - {'id': row_id}, include_event_token=True, - compare_event_token=cache_event_token) - result = cursor.fetchall() - if not result: - # Not in the database. Use an empty array. - logging.debug(' not in the database') - value = () - else: - value = result[0] - conn.close() - - # If there was no cached version, cache what we got, - # along with the event token. - if version is None: - logging.debug(' adding value to cache: %s', value) - self.cache.add(table_name, row_id, conn.event_token, value) - return value - - # If there was a cached version, and the version we got is older, - # we can't update the cache. - if cache_event_token and not conn.fresher: - logging.debug(' database value is not fresher: %s', value) - self.cache.noop() - return value - - # Save in the cache. - logging.debug(' setting value in cache: %s', value) - self.cache.cas(table_name, row_id, version, conn.event_token, value) - return value - - def wait_for_cache_stats(self, stats, **kwargs): - timeout = 10 - while True: - if self.cache.stats_diff(stats, **kwargs): - return - timeout = utils.wait_step('cache stats update %s' % str(kwargs), timeout) - - def test_cache_invalidation(self): - """Main test case in this suite.""" - - # Insert. - stats = self.cache.stats_copy() - self._insert_value_a(1, 'test_cache_invalidation object') - - # Sleep a bit to be sure the value was propagated. - self.wait_for_cache_stats(stats, cache_miss=1, add=1) - - # Then get the value. We cannot populate the cache here, as the - # timestamp of the latest replication EventToken is the same as - # the invalidation token (only one transaction in the binlogs so - # far). - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 1) - self.assertEqual(result, (1, 'test_cache_invalidation object')) - self.assertTrue(self.cache.stats_diff(stats, cache_hit=1, noop=1)) - - # Insert a second value with a greater timestamp to move the - # current replica EventToken. - time.sleep(1) - stats = self.cache.stats_copy() - self._insert_value_a(2, 'second object') - - # Sleep a bit to be sure the value was propagated. - self.wait_for_cache_stats(stats, cache_miss=1, add=1) - - # This time, when we get the value, the current replica - # EventToken is ahead of the invalidation timestamp of the first - # object, so we should save it in the cache. - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 1) - self.assertEqual(result, (1, 'test_cache_invalidation object')) - self.assertTrue(self.cache.stats_diff(stats, cache_hit=1, cas=1)) - - # Ask again, should be from cache now. - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 1) - self.assertEqual(result, (1, 'test_cache_invalidation object')) - self.assertTrue(self.cache.stats_diff(stats, cache_hit=1, - add=0, cas=0, noop=0)) - - def test_empty_cache_value(self): - """Tests a non-existing value is cached properly.""" - - # Try to read a non-existing value, should get a miss. - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 3) - self.assertEqual(result, ()) - self.assertTrue(self.cache.stats_diff(stats, cache_miss=1, add=1)) - - # Try to read again, should get a hit to an empty value. - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 3) - self.assertEqual(result, ()) - self.assertTrue(self.cache.stats_diff(stats, cache_hit=1, - add=0, cas=0, noop=0)) - - # Now create the value. - stats = self.cache.stats_copy() - self._insert_value_a(3, 'empty cache test object') - - # Wait a bit for cache to get event, make sure we invalidated. - self.wait_for_cache_stats(stats, cache_hit=1, cas=1) - - # Get the value, make sure we got it from DB, with no possible cache update. - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 3) - self.assertEqual(result, (3, 'empty cache test object')) - self.assertTrue(self.cache.stats_diff(stats, cache_hit=1, noop=1)) - - # Insert a second value with a greater timestamp to move the - # current replica EventToken. - time.sleep(1) - stats = self.cache.stats_copy() - self._insert_value_a(4, 'second object') - - # Sleep a bit to be sure the value was propagated. - self.wait_for_cache_stats(stats, cache_miss=1, add=1) - - # This time, when we get the value, the current replica - # EventToken is ahead of the invalidation timestamp of the first - # object, so we should save it in the cache. - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 3) - self.assertEqual(result, (3, 'empty cache test object')) - self.assertTrue(self.cache.stats_diff(stats, cache_hit=1, cas=1)) - - # Ask again, should be from cache now. - stats = self.cache.stats_copy() - result = self._get_value('vt_a', 3) - self.assertEqual(result, (3, 'empty cache test object')) - self.assertTrue(self.cache.stats_diff(stats, cache_hit=1, - add=0, cas=0, noop=0)) - - def test_event_token_fresher(self): - """event_token.fresher test suite.""" - test_cases = [ - { - 'ev1': None, - 'ev2': None, - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=123, - ), - 'ev2': None, - 'expected': -1, - }, { - 'ev1': None, - 'ev2': query_pb2.EventToken( - timestamp=123, - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=123, - ), - 'ev2': query_pb2.EventToken( - timestamp=123, - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=200, - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - ), - 'expected': 100, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - ), - 'ev2': query_pb2.EventToken( - timestamp=200, - ), - 'expected': -100, - }, { - # Test cases with not enough information to compare. - 'ev1': query_pb2.EventToken( - timestamp=100, - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s2', - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='pos1', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='pos2', - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='pos1', # invalid on purpose - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='pos2', # invalid on purpose - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MariaDB/0-1-123', # valid but different - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MySQL56/33333333-3333-3333-3333-333333333333:456-789', - ), - 'expected': -1, - }, { - # MariaDB test cases. - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MariaDB/0-1-200', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MariaDB/0-1-100', - ), - 'expected': 100, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MariaDB/0-1-100', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MariaDB/0-1-200', - ), - 'expected': -100, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MariaDB/0-1-100', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MariaDB/0-1-100', - ), - 'expected': 0, - }, { - # MySQL56 test cases, not supported yet. - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MySQL56/33333333-3333-3333-3333-333333333333:1-200', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MySQL56/33333333-3333-3333-3333-333333333333:1-100', - ), - 'expected': -1, # Should be: 1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MySQL56/33333333-3333-3333-3333-333333333333:1-100', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MySQL56/33333333-3333-3333-3333-333333333333:1-200', - ), - 'expected': -1, - }, { - 'ev1': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MySQL56/33333333-3333-3333-3333-333333333333:1-100', - ), - 'ev2': query_pb2.EventToken( - timestamp=100, - shard='s1', - position='MySQL56/33333333-3333-3333-3333-333333333333:1-100', - ), - 'expected': -1, # Should be: 0, - } - ] - - for tcase in test_cases: - got = event_token.fresher(tcase['ev1'], tcase['ev2']) - self.assertEqual(got, tcase['expected'], - 'got %d but expected %d for Fresher(%s, %s)' % - (got, tcase['expected'], tcase['ev1'], tcase['ev2'])) - - -if __name__ == '__main__': - utils.main() diff --git a/test/cell_aliases.py b/test/cell_aliases.py deleted file mode 100755 index a942bfdc393..00000000000 --- a/test/cell_aliases.py +++ /dev/null @@ -1,311 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This test cell aliases feature - -We start with no aliases and assert that vtgates can't route to replicas/rondly tablets. -Then we add an alias, and these tablets should be routable - -""" - -import threading -import time - -import logging -import unittest - -import base_sharding -import environment -import tablet -import utils - -from vtproto import topodata_pb2 -from vtdb import keyrange_constants -from vtdb import vtgate_client - -use_alias = True - -# initial shards -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet(cell='ny') -shard_0_rdonly = tablet.Tablet(cell='ny') - -#shard_0_replica = tablet.Tablet() -#shard_0_rdonly = tablet.Tablet() -# range 80 - '' -shard_1_master = tablet.Tablet() -#shard_1_replica = tablet.Tablet() -#shard_1_rdonly = tablet.Tablet() - -shard_1_replica = tablet.Tablet(cell='ny') -shard_1_rdonly = tablet.Tablet(cell='ny') - -all_tablets = ([shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica,shard_1_rdonly]) - -vschema = { - 'test_keyspace': '''{ - "sharded": true, - "vindexes": { - "hash_index": { - "type": "hash" - } - }, - "tables": { - "test_table": { - "column_vindexes": [ - { - "column": "custom_ksid_col", - "name": "hash_index" - } - ] - } - } - }''', -} - -def setUpModule(): - try: - environment.topo_server().setup() - setup_procs = [t.init_mysql() - for t in all_tablets] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - -class TestCellsAliases(unittest.TestCase, base_sharding.BaseShardingTest): - - int_type = 265 - - # Gets a vtgate connection - def _get_connection(self, timeout=10.0): - protocol, endpoint = utils.vtgate.rpc_endpoint(python=True) - try: - return vtgate_client.connect(protocol, endpoint, timeout) - except Exception: - logging.exception('Connection to vtgate (timeout=%s) failed.', timeout) - raise - - # executes requetest in tablet type - def _execute_on_tablet_type(self, vtgate_conn, tablet_type, sql, bind_vars): - return vtgate_conn._execute( - sql, bind_vars, tablet_type=tablet_type, keyspace_name=None) - - - # create_schema will create the same schema on the keyspace - # then insert some values - def _create_schema(self): - if base_sharding.keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - # Note that the primary key columns are not defined first on purpose to test - # that a reordered column list is correctly used everywhere in vtworker. - create_table_template = '''create table %s( -custom_ksid_col ''' + t + ''' not null, -msg varchar(64), -id bigint not null, -parent_id bigint not null, -primary key (parent_id, id), -index by_msg (msg) -) Engine=InnoDB''' - - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('test_table'), - 'test_keyspace'], - auto_log=True) - - def _insert_startup_values(self): - self._insert_value(shard_0_master, 'test_table', 1, 'msg1', - 0x1000000000000000) - self._insert_value(shard_1_master, 'test_table', 2, 'msg2', - 0x9000000000000000) - self._insert_value(shard_1_master, 'test_table', 3, 'msg3', - 0xD000000000000000) - - def test_cells_aliases(self): - utils.run_vtctl(['CreateKeyspace', - '--sharding_column_name', 'custom_ksid_col', - '--sharding_column_type', base_sharding.keyspace_id_type, - 'test_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '-80') - shard_0_replica.init_tablet('replica', 'test_keyspace', '-80') - shard_0_rdonly.init_tablet('rdonly', 'test_keyspace', '-80') - shard_1_master.init_tablet('replica', 'test_keyspace', '80-') - shard_1_replica.init_tablet('replica', 'test_keyspace', '80-') - shard_1_rdonly.init_tablet('rdonly', 'test_keyspace', '80-') - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - ks = utils.run_vtctl_json(['GetSrvKeyspace', 'test_nj', 'test_keyspace']) - self.assertEqual(ks['sharding_column_name'], 'custom_ksid_col') - - # we set full_mycnf_args to True as a test in the KIT_BYTES case - full_mycnf_args = (base_sharding.keyspace_id_type == - keyrange_constants.KIT_BYTES) - - # create databases so vttablet can start behaving somewhat normally - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None, full_mycnf_args=full_mycnf_args, - binlog_use_v3_resharding_mode=False) - - # wait for the tablets (replication is not setup, they won't be healthy) - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]: - t.wait_for_vttablet_state('NOT_SERVING') - - # reparent to make the tablets work - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - # check the shards - shards = utils.run_vtctl_json(['FindAllShardsInKeyspace', 'test_keyspace']) - self.assertIn('-80', shards, 'unexpected shards: %s' % str(shards)) - self.assertIn('80-', shards, 'unexpected shards: %s' % str(shards)) - self.assertEqual(len(shards), 2, 'unexpected shards: %s' % str(shards)) - - # create the tables - self._create_schema() - self._insert_startup_values() - - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # Make sure srv keyspace graph looks as expected - utils.check_srv_keyspace( - 'test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - utils.check_srv_keyspace( - 'test_ny', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # Bootstrap vtgate - - utils.apply_vschema(vschema) - - # Adds alias so vtgate can route to replica/rdonly tablets that are not in the same cell, but same alias - if use_alias: - utils.run_vtctl(['AddCellsAlias', '-cells', 'test_nj,test_ny','region_east_coast'], auto_log=True) - - # Check that UpdateCellsAlias is idempotent. - utils.run_vtctl(['UpdateCellsAlias', '-cells', 'test_nj,test_ny','region_east_coast'], auto_log=True) - - tablet_types_to_wait='MASTER,REPLICA' - else: - tablet_types_to_wait='MASTER' - - utils.VtGate().start( - tablets=[shard_0_master, shard_1_master], - tablet_types_to_wait=tablet_types_to_wait, - cells_to_watch='test_nj,test_ny', - ) - utils.vtgate.wait_for_endpoints('test_keyspace.-80.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.master', 1) - - vtgate_conn = self._get_connection() - result = self._execute_on_tablet_type( - vtgate_conn, - 'master', - 'select count(*) from test_table', {}) - self.assertEqual( - result, - ([(3,)], 1, 0, - [('count(*)', self.int_type)])) - - if use_alias: - vtgate_conn = self._get_connection() - result = self._execute_on_tablet_type( - vtgate_conn, - 'master', - 'select count(*) from test_table', {}) - self.assertEqual( - result, - ([(3,)], 1, 0, - [('count(*)', self.int_type)])) - - vtgate_conn = self._get_connection() - result = self._execute_on_tablet_type( - vtgate_conn, - 'replica', - 'select count(*) from test_table', {}) - self.assertEqual( - result, - ([(3,)], 1, 0, - [('count(*)', self.int_type)])) - - vtgate_conn = self._get_connection() - result = self._execute_on_tablet_type( - vtgate_conn, - 'rdonly', - 'select count(*) from test_table', {}) - self.assertEqual( - result, - ([(3,)], 1, 0, - [('count(*)', self.int_type)])) - else: - vtgate_conn = self._get_connection() - try: - self._execute_on_tablet_type( - vtgate_conn, - 'replica', - 'select count(*) from test_table', {}) - self.fail('Expected execute to fail, did not get error') - except Exception as e: - s = str(e) - self.assertIn('80.replica, no valid tablet: node', s) - - vtgate_conn = self._get_connection() - try: - self._execute_on_tablet_type( - vtgate_conn, - 'rdonly', - 'select count(*) from test_table', {}) - self.fail('Expected execute to fail, did not get error') - except Exception as e: - s = str(e) - self.assertIn('80.rdonly, no valid tablet: node', s) - -if __name__ == '__main__': - utils.main() diff --git a/test/cell_no_aliases.py b/test/cell_no_aliases.py deleted file mode 100755 index 5d0668b0b4c..00000000000 --- a/test/cell_no_aliases.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Re-runs cell_aliases.py with no aliases.""" - -import cell_aliases -import utils - -if __name__ == '__main__': - cell_aliases.use_aliases = False - utils.main(cell_aliases) diff --git a/test/encrypted_replication.py b/test/encrypted_replication.py deleted file mode 100755 index d7c61b2cca2..00000000000 --- a/test/encrypted_replication.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import os -import unittest - -import environment -import utils -import tablet - -# single shard / 2 tablets -shard_0_master = tablet.Tablet() -shard_0_slave = tablet.Tablet() - -cert_dir = environment.tmproot + '/certs' - - -def setUpModule(): - try: - environment.topo_server().setup() - - logging.debug('Creating certificates') - os.makedirs(cert_dir) - - utils.run(environment.binary_args('vttlstest') + - ['-root', cert_dir, - 'CreateCA']) - utils.run(environment.binary_args('vttlstest') + - ['-root', cert_dir, - 'CreateSignedCert', - '-common_name', 'Mysql Server', - '-serial', '01', - 'server']) - utils.run(environment.binary_args('vttlstest') + - ['-root', cert_dir, - 'CreateSignedCert', - '-common_name', 'Mysql Client', - '-serial', '02', - 'client']) - - extra_my_cnf = cert_dir + '/secure.cnf' - fd = open(extra_my_cnf, 'w') - fd.write('ssl-ca=' + cert_dir + '/ca-cert.pem\n') - fd.write('ssl-cert=' + cert_dir + '/server-cert.pem\n') - fd.write('ssl-key=' + cert_dir + '/server-key.pem\n') - fd.close() - - setup_procs = [ - shard_0_master.init_mysql(extra_my_cnf=extra_my_cnf), - shard_0_slave.init_mysql(extra_my_cnf=extra_my_cnf), - ] - utils.wait_procs(setup_procs) - - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '0') - shard_0_slave.init_tablet('replica', 'test_keyspace', '0') - - # create databases so vttablet can start behaving normally - shard_0_master.create_db('vt_test_keyspace') - shard_0_slave.create_db('vt_test_keyspace') - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - shard_0_master.kill_vttablet() - shard_0_slave.kill_vttablet() - - teardown_procs = [ - shard_0_master.teardown_mysql(), - shard_0_slave.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_0_master.remove_tree() - shard_0_slave.remove_tree() - - -class TestSecure(unittest.TestCase): - """This test makes sure that we can use SSL replication with Vitess. - """ - - def test_secure(self): - # start the tablets - shard_0_master.start_vttablet(wait_for_state='NOT_SERVING') - shard_0_slave.start_vttablet(wait_for_state='NOT_SERVING', - repl_extra_flags={ - 'flags': '2048', - 'ssl-ca': cert_dir + '/ca-cert.pem', - 'ssl-cert': cert_dir + '/client-cert.pem', - 'ssl-key': cert_dir + '/client-key.pem', - }) - - # Reparent using SSL (this will also check replication works) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - shard_0_master.tablet_alias], auto_log=True) - -if __name__ == '__main__': - utils.main() diff --git a/test/encrypted_transport.py b/test/encrypted_transport.py deleted file mode 100755 index 28e6dbe3177..00000000000 --- a/test/encrypted_transport.py +++ /dev/null @@ -1,327 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This test makes sure encrypted transport over gRPC works. - -The security chains are setup the following way: - -* root CA - * vttablet server CA - * vttablet server instance cert/key - * vttablet client CA - * vttablet client 1 cert/key - * vtgate server CA - * vtgate server instance cert/key (common name is 'localhost') - * vtgate client CA - * vtgate client 1 cert/key - * vtgate client 2 cert/key - -The following table shows all the checks we perform: -process: will check its peer is signed by: for link: - - vttablet vttablet client CA vtgate -> vttablet - vtgate vttablet server CA vtgate -> vttablet - - vtgate vtgate client CA client -> vtgate - client vtgate server CA client -> vtgate - -Additionally, we have the following constraints: -- the client certificate common name is used as immediate -caller ID by vtgate, and forwarded to vttablet. This allows us to use -table ACLs on the vttablet side. -- the vtgate server certificate common name is set to 'localhost' so it matches -the hostname dialed by the vtgate clients. This is not a requirement for the -go client, that can set its expected server name. However, the python gRPC -client doesn't have the ability to set the server name, so they must match. -- the python client needs to have the full chain for the server validation -(that is 'vtgate server CA' + 'root CA'). A go client doesn't. So we read both -below when using the python client, but we only pass the intermediate cert -to the go clients (for vtgate -> vttablet link). -""" - -import logging -import os -import unittest - -from vtdb import vtgate_client -from vtdb import dbexceptions - -import environment -import utils -import tablet - -# single shard / 2 tablets -shard_0_master = tablet.Tablet() -shard_0_slave = tablet.Tablet() - -cert_dir = environment.tmproot + '/certs' -table_acl_config = environment.tmproot + '/table_acl_config.json' - - -def create_signed_cert(ca, serial, name, common_name): - logging.info('Creating signed cert and key %s', common_name) - utils.run(environment.binary_args('vttlstest') + - ['-root', cert_dir, - 'CreateSignedCert', - '-parent', ca, - '-serial', serial, - '-common_name', common_name, - name]) - - -def server_extra_args(name, ca): - return [ - '-grpc_cert', cert_dir + '/' + name + '-cert.pem', - '-grpc_key', cert_dir + '/' + name + '-key.pem', - '-grpc_ca', cert_dir + '/' + ca + '-cert.pem', - ] - - -def tmclient_extra_args(name): - ca = 'vttablet-server' - return [ - '-tablet_manager_grpc_cert', cert_dir + '/' + name + '-cert.pem', - '-tablet_manager_grpc_key', cert_dir + '/' + name + '-key.pem', - '-tablet_manager_grpc_ca', cert_dir + '/' + ca + '-cert.pem', - '-tablet_manager_grpc_server_name', 'vttablet server instance', - ] - - -def tabletconn_extra_args(name): - ca = 'vttablet-server' - return [ - '-tablet_grpc_cert', cert_dir + '/' + name + '-cert.pem', - '-tablet_grpc_key', cert_dir + '/' + name + '-key.pem', - '-tablet_grpc_ca', cert_dir + '/' + ca + '-cert.pem', - '-tablet_grpc_server_name', 'vttablet server instance', - ] - - -def python_client_kwargs(name, ca): - with open(cert_dir + '/ca-cert.pem', 'r') as fd: - root_ca_contents = fd.read() - with open(cert_dir + '/' + ca + '-cert.pem', 'r') as fd: - ca_contents = fd.read() - with open(cert_dir + '/' + name + '-key.pem', 'r') as fd: - key_contents = fd.read() - with open(cert_dir + '/' + name + '-cert.pem', 'r') as fd: - cert_contents = fd.read() - return { - 'root_certificates': root_ca_contents+ca_contents, - 'private_key': key_contents, - 'certificate_chain': cert_contents, - } - - -def setUpModule(): - try: - environment.topo_server().setup() - - logging.debug('Creating certificates') - os.makedirs(cert_dir) - - # Create CA certificate - utils.run(environment.binary_args('vttlstest') + - ['-root', cert_dir, - 'CreateCA']) - - # create all certs - create_signed_cert('ca', '01', 'vttablet-server', 'vttablet server CA') - create_signed_cert('ca', '02', 'vttablet-client', 'vttablet client CA') - create_signed_cert('ca', '03', 'vtgate-server', 'vtgate server CA') - create_signed_cert('ca', '04', 'vtgate-client', 'vtgate client CA') - - create_signed_cert('vttablet-server', '01', 'vttablet-server-instance', - 'vttablet server instance') - - create_signed_cert('vttablet-client', '01', 'vttablet-client-1', - 'vttablet client 1') - - create_signed_cert('vtgate-server', '01', 'vtgate-server-instance', - 'localhost') - - create_signed_cert('vtgate-client', '01', 'vtgate-client-1', - 'vtgate client 1') - create_signed_cert('vtgate-client', '02', 'vtgate-client-2', - 'vtgate client 2') - - # setup all processes - setup_procs = [ - shard_0_master.init_mysql(), - shard_0_slave.init_mysql(), - ] - utils.wait_procs(setup_procs) - - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '0') - shard_0_slave.init_tablet('replica', 'test_keyspace', '0') - - # create databases so vttablet can start behaving normally - shard_0_master.create_db('vt_test_keyspace') - shard_0_slave.create_db('vt_test_keyspace') - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - shard_0_master.kill_vttablet() - shard_0_slave.kill_vttablet() - - teardown_procs = [ - shard_0_master.teardown_mysql(), - shard_0_slave.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_0_master.remove_tree() - shard_0_slave.remove_tree() - - -create_vt_insert_test = '''create table vt_insert_test ( -id bigint auto_increment, -msg varchar(64), -keyspace_id bigint(20) unsigned NOT NULL, -primary key (id) -) Engine=InnoDB''' - - -class TestSecure(unittest.TestCase): - """This test makes sure that we can use full TLS security within Vitess. - """ - - def test_secure(self): - with open(table_acl_config, 'w') as fd: - fd.write("""{ - "table_groups": [ - { - "table_names_or_prefixes": ["vt_insert_test"], - "readers": ["vtgate client 1"], - "writers": ["vtgate client 1"], - "admins": ["vtgate client 1"] - } - ] -} -""") - - # start the tablets - shard_0_master.start_vttablet( - wait_for_state='NOT_SERVING', - table_acl_config=table_acl_config, - extra_args=server_extra_args('vttablet-server-instance', - 'vttablet-client')) - shard_0_slave.start_vttablet( - wait_for_state='NOT_SERVING', - table_acl_config=table_acl_config, - extra_args=server_extra_args('vttablet-server-instance', - 'vttablet-client')) - - # setup replication - utils.run_vtctl(tmclient_extra_args('vttablet-client-1') + [ - 'InitShardMaster', '-force', 'test_keyspace/0', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(tmclient_extra_args('vttablet-client-1') + [ - 'ApplySchema', '-sql', create_vt_insert_test, - 'test_keyspace']) - for t in [shard_0_master, shard_0_slave]: - utils.run_vtctl(tmclient_extra_args('vttablet-client-1') + [ - 'RunHealthCheck', t.tablet_alias]) - - # start vtgate - utils.VtGate().start(extra_args=tabletconn_extra_args('vttablet-client-1')+ - server_extra_args('vtgate-server-instance', - 'vtgate-client')) - - # 'vtgate client 1' is authorized to access vt_insert_test - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - conn = vtgate_client.connect(protocol, addr, 30.0, - **python_client_kwargs('vtgate-client-1', - 'vtgate-server')) - cursor = conn.cursor(tablet_type='master', keyspace='test_keyspace', - shards=['0']) - cursor.execute('select * from vt_insert_test', {}) - conn.close() - - # 'vtgate client 2' is not authorized to access vt_insert_test - conn = vtgate_client.connect(protocol, addr, 30.0, - **python_client_kwargs('vtgate-client-2', - 'vtgate-server')) - try: - cursor = conn.cursor(tablet_type='master', keyspace='test_keyspace', - shards=['0']) - cursor.execute('select * from vt_insert_test', {}) - self.fail('Execute went through') - except dbexceptions.DatabaseError, e: - s = str(e) - self.assertIn('table acl error', s) - self.assertIn('cannot run PASS_SELECT on table', s) - conn.close() - - # now restart vtgate in the mode where we don't use SSL - # for client connections, but we copy effective caller id - # into immediate caller id. - utils.vtgate.kill() - utils.VtGate().start(extra_args=tabletconn_extra_args('vttablet-client-1')+ - ['-grpc_use_effective_callerid']) - - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - conn = vtgate_client.connect(protocol, addr, 30.0) - cursor = conn.cursor(tablet_type='master', keyspace='test_keyspace', - shards=['0']) - - # not passing any immediate caller id should fail as using - # the unsecure user "unsecure_grpc_client" - cursor.set_effective_caller_id(None) - try: - cursor.execute('select * from vt_insert_test', {}) - self.fail('Execute went through') - except dbexceptions.DatabaseError, e: - s = str(e) - self.assertIn('table acl error', s) - self.assertIn('cannot run PASS_SELECT on table', s) - self.assertIn('unsecure_grpc_client', s) - - # 'vtgate client 1' is authorized to access vt_insert_test - cursor.set_effective_caller_id(vtgate_client.CallerID( - principal='vtgate client 1')) - cursor.execute('select * from vt_insert_test', {}) - - # 'vtgate client 2' is not authorized to access vt_insert_test - cursor.set_effective_caller_id(vtgate_client.CallerID( - principal='vtgate client 2')) - try: - cursor.execute('select * from vt_insert_test', {}) - self.fail('Execute went through') - except dbexceptions.DatabaseError, e: - s = str(e) - self.assertIn('table acl error', s) - self.assertIn('cannot run PASS_SELECT on table', s) - - conn.close() - - -if __name__ == '__main__': - utils.main() diff --git a/test/environment.py b/test/environment.py deleted file mode 100644 index eee51cc4523..00000000000 --- a/test/environment.py +++ /dev/null @@ -1,296 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Initialize the test environment.""" - -import logging -import os -import subprocess -import sys - -import protocols_flavor - -# Import the topo implementations that you want registered as options for the -# --topo-server-flavor flag. -# pylint: disable=unused-import -import topo_flavor.zk2 -import topo_flavor.etcd2 -import topo_flavor.consul - -# This imports topo_server into this module, so clients can write -# environment.topo_server(). -# pylint: disable=unused-import -from topo_flavor.server import topo_server - -# Import the VTGate gateway flavors that you want registered as options for the -# --gateway_implementation flag. -# pylint: disable=unused-import -import vtgate_gateway_flavor.discoverygateway - -from selenium import webdriver -from selenium.common.exceptions import WebDriverException -from selenium.webdriver.chrome.options import Options - -from vttest import mysql_flavor - - -# sanity check the environment -if os.getuid() == 1: - sys.stderr.write( - 'ERROR: Vitess and mysqld ' - 'should not be run as root.\n') - sys.exit(1) - -# vtroot is where everything gets installed -vtroot = os.environ['VTROOT'] - -# vtdataroot is where to put all the data files -vtdataroot = os.environ.get('VTDATAROOT', '/vt') - -# vt_mysql_root is where MySQL is installed -vt_mysql_root = os.environ.get( - 'VT_MYSQL_ROOT', os.path.join(vtroot, 'dist', 'mysql')) - -# tmproot is the temporary place to put all test files -tmproot = os.path.join(vtdataroot, 'tmp') - -# vtlogroot is where to put all the log files -vtlogroot = tmproot - -# where to start allocating ports from -vtportstart = int(os.environ.get('VTPORTSTART', '6700')) - -# url in which binaries export their status. -status_url = '/debug/status' - -# location of the curl binary, used for some tests. -curl_bin = '/usr/bin/curl' - -# if set, we will not build the binaries -skip_build = False - -# location of the run_local_database.py file -run_local_database = os.path.join(vtroot, 'py-vtdb', 'vttest', - 'run_local_database.py') - -# url to hit to force the logs to flush. -flush_logs_url = '/debug/flushlogs' - -# set the maximum size for grpc messages to be 5MB (larger than the default of -# 4MB). -grpc_max_message_size = 5 * 1024 * 1024 - - -def setup(): - try: - os.makedirs(tmproot) - except OSError: - # directory already exists - pass - - -# port management: reserve count consecutive ports, returns the first one -def reserve_ports(count): - global vtportstart - result = vtportstart - vtportstart += count - return result - - -def run(args, raise_on_error=True, **kargs): - """simple run command, cannot use utils.run to avoid circular dependencies. - - Args: - args: Variable length argument list. - raise_on_error: if exception should be raised when seeing error. - **kargs: Arbitrary keyword arguments. - - Returns: - None - - Raises: - Exception: when it cannot start subprocess. - """ - try: - logging.debug( - 'run: %s %s', str(args), - ', '.join('%s=%s' % x for x in kargs.iteritems())) - proc = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - **kargs) - stdout, stderr = proc.communicate() - except Exception as e: - raise Exception('Command failed', e, args) - - if proc.returncode: - if raise_on_error: - raise Exception('Command failed: ' + ' '.join(args) + ':\n' + stdout + - stderr) - else: - logging.error('Command failed: %s:\n%s%s', ' '.join(args), stdout, stderr) - return stdout, stderr - - -# compile command line programs, only once -compiled_progs = [] - - -def prog_compile(name): - if skip_build or name in compiled_progs: - return - compiled_progs.append(name) - logging.debug('Compiling %s', name) - run(['go', 'install'], cwd=os.path.join(vtroot, 'go', 'cmd', name)) - - -# binary management: returns the full path for a binary this should -# typically not be used outside this file, unless you want to bypass -# global flag injection (see binary_args) -def binary_path(name): - prog_compile(name) - return os.path.join(vtroot, 'bin', name) - - -# returns flags specific to a given binary -# use this to globally inject flags any time a given command runs -# e.g. - if name == 'vtctl': return ['-extra_arg', 'value'] -# pylint: disable=unused-argument -def binary_flags(name): - return [] - - -# returns binary_path + binary_flags as a list -# this should be used instead of binary_path whenever possible -def binary_args(name): - return [binary_path(name)] + binary_flags(name) - - -# returns binary_path + binary_flags as a string -# this should be used instead of binary_path whenever possible -def binary_argstr(name): - return ' '.join(binary_args(name)) - - -# binary management for the MySQL distribution. -def mysql_binary_path(name): - return os.path.join(vt_mysql_root, 'bin', name) - - -def lameduck_flag(lameduck_period): - return ['-lameduck-period', lameduck_period] - - -# pylint: disable=unused-argument -def add_options(parser): - """Add environment-specific command-line options.""" - pass - - -def setup_protocol_flavor(flavor): - """Imports the right protocols flavor implementation. - - This is a separate method that does dynamic import of the module so the - tests only depend and import the code they will use. - Each protocols flavor implementation will import the modules it needs. - - Args: - flavor: the flavor name to use. - """ - if flavor == 'grpc': - import grpc_protocols_flavor # pylint: disable=g-import-not-at-top - protocols_flavor.set_protocols_flavor( - grpc_protocols_flavor.GRpcProtocolsFlavor()) - - else: - logging.error('Unknown protocols flavor %s', flavor) - exit(1) - - logging.debug('Using protocols flavor \'%s\'', flavor) - - -def reset_mysql_flavor(): - mysql_flavor.set_mysql_flavor(None) - - -def create_webdriver(): - """Creates a webdriver object (local or remote for Travis).""" - - # Set common Options - chrome_options = Options() - chrome_options.add_argument("--disable-gpu") - chrome_options.add_argument("--no-sandbox") - chrome_options.headless = True - - if os.environ.get('CI') == 'true' and os.environ.get('TRAVIS') == 'true': - username = os.environ['SAUCE_USERNAME'] - access_key = os.environ['SAUCE_ACCESS_KEY'] - capabilities = {} - capabilities['tunnel-identifier'] = os.environ['TRAVIS_JOB_NUMBER'] - capabilities['build'] = os.environ['TRAVIS_BUILD_NUMBER'] - capabilities['platform'] = 'Linux' - capabilities['browserName'] = 'chrome' - capabilities['chromeOptions'] = chrome_options - hub_url = '%s:%s@localhost:4445' % (username, access_key) - driver = webdriver.Remote( - desired_capabilities=capabilities, - command_executor='http://%s/wd/hub' % hub_url) - - else: - # Only testing against Chrome for now - os.environ['webdriver.chrome.driver'] = os.path.join(vtroot, 'dist') - service_log_path = os.path.join(tmproot, 'chromedriver.log') - - try: - driver = webdriver.Chrome(service_args=['--verbose'], - service_log_path=service_log_path, - chrome_options=chrome_options - ) - except WebDriverException as e: - if 'Chrome failed to start' not in str(e): - # Not a Chrome issue. Just re-raise the exception. - raise - - # Chrome issue: Dump the log file. - logging.error( - 'webdriver failed to start Chrome.\n' - '\n' - 'See chromedriver.log below for details.\n' - '\n' - 'Original exception:\n' - '\n' - '%s', str(e)) - # Dump the whole log file. This can go over multiple pages because - # webdriver is constantly polling (after Chrome crashed) and logging - # each attempt. - with open(service_log_path, 'r') as f: - logging.error('Content of chromedriver.log:\n%s', f.read()) - logging.error('webdriver failed to start Chrome. Scroll up for' - ' details.') - exit(1) - - driver.set_window_position(0, 0) - driver.set_window_size(1280, 1024) - return driver - - -def set_log_level(verbose): - level = logging.DEBUG - if verbose == 0: - level = logging.WARNING - elif verbose == 1: - level = logging.INFO - logging.getLogger().setLevel(level) diff --git a/test/grpc_protocols_flavor.py b/test/grpc_protocols_flavor.py deleted file mode 100644 index 4ccd7dcabed..00000000000 --- a/test/grpc_protocols_flavor.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Defines which protocols to use for the gRPC flavor.""" - -from grpc.framework.interfaces.face import face - -import protocols_flavor - -# Now imports all the implementations we need. -# We will change this to explicit registration soon. -from vtctl import grpc_vtctl_client # pylint: disable=unused-import -from vtdb import grpc_vtgate_client # pylint: disable=unused-import - - -class GRpcProtocolsFlavor(protocols_flavor.ProtocolsFlavor): - """Definitons to use gRPC everywhere. - """ - - def binlog_player_protocol(self): - return 'grpc' - - def vtctl_client_protocol(self): - return 'grpc' - - def vtctl_python_client_protocol(self): - return 'grpc' - - def vtworker_client_protocol(self): - return 'grpc' - - def tablet_manager_protocol(self): - return 'grpc' - - def tabletconn_protocol(self): - return 'grpc' - - def throttler_client_protocol(self): - return 'grpc' - - def vtgate_protocol(self): - return 'grpc' - - def vtgate_python_protocol(self): - return 'grpc' - - def client_error_exception_type(self): - return face.AbortionError - - def rpc_timeout_message(self): - return 'context deadline exceeded' - - def service_map(self): - return [ - 'grpc-tabletmanager', - 'grpc-throttler', - 'grpc-queryservice', - 'grpc-updatestream', - 'grpc-vtctl', - 'grpc-vtworker', - 'grpc-vtgateservice', - ] - - def vttest_protocol(self): - return 'grpc' diff --git a/test/horizontal_resharding_workflow.py b/test/horizontal_resharding_workflow.py deleted file mode 100644 index 44a8741f8bf..00000000000 --- a/test/horizontal_resharding_workflow.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""End-to-end test for horizontal resharding workflow.""" - -import re -# "unittest" is used indirectly by importing "worker", but pylint does -# not grasp this. -# Import it explicitly to make pylint happy and stop it complaining about -# setUpModule, tearDownModule and the missing module docstring. -import unittest # pylint: disable=unused-import - - -import utils -import worker - - -def setUpModule(): - try: - worker.setUpModule() - utils.Vtctld().start() - except: - tearDownModule() - raise - - -def tearDownModule(): - worker.tearDownModule() - - -class TestHorizontalReshardingWorkflow(worker.TestBaseSplitClone): - """End-to-end test for horizontal resharding workflow. - - This test reuses worker.py, which sets up the environment. - """ - KEYSPACE = 'test_keyspace' - - def test_successful_resharding(self): - """Reshard from 1 to 2 shards by running the workflow.""" - worker_proc, _, worker_rpc_port = utils.run_vtworker_bg( - ['--cell', 'test_nj', '--use_v3_resharding_mode=false'], auto_log=True) - vtworker_endpoint = 'localhost:%d' % worker_rpc_port - - stdout = utils.run_vtctl(['WorkflowCreate', 'horizontal_resharding', - '-keyspace=test_keyspace', - '-vtworkers=%s' % vtworker_endpoint, - '-enable_approvals=false'], - auto_log=True) - workflow_uuid = re.match(r'^uuid: (.*)$', stdout[0]).group(1) - - utils.pause('Now is a good time to look at vtctld UI at: ' - '%s, workflow uuid=%s' % (utils.vtctld.port, workflow_uuid)) - - utils.run_vtctl(['WorkflowWait', workflow_uuid]) - - self.verify() - utils.kill_sub_process(worker_proc, soft=True) - - def verify(self): - self.assert_shard_data_equal(0, worker.shard_master, - worker.shard_0_tablets.replica) - self.assert_shard_data_equal(1, worker.shard_master, - worker.shard_1_tablets.replica) - - # Verify effect of MigrateServedTypes. Dest shards are serving now. - utils.check_srv_keyspace('test_nj', self.KEYSPACE, - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n') - - # source shard: query service must be disabled after MigrateServedTypes. - source_shards = [worker.shard_rdonly1, - worker.shard_replica, - worker.shard_master] - for shard in source_shards: - utils.check_tablet_query_service( - self, shard, serving=False, tablet_control_disabled=True) - - # dest shard -80, 80-: query service must be enabled - # after MigrateServedTypes. - dest_shards = [worker.shard_0_rdonly1, - worker.shard_0_replica, - worker.shard_0_master, - worker.shard_1_rdonly1, - worker.shard_1_replica, - worker.shard_1_master] - for shard in dest_shards: - utils.check_tablet_query_service( - self, - shard, - serving=True, - tablet_control_disabled=False) - -if __name__ == '__main__': - utils.main(test_options=worker.add_test_options) diff --git a/test/initial_sharding.py b/test/initial_sharding.py deleted file mode 100755 index cd561f3bc73..00000000000 --- a/test/initial_sharding.py +++ /dev/null @@ -1,646 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This test simulates the first time a database has to be split. - -- we start with a keyspace with a single shard and a single table -- we add and populate the sharding key -- we set the sharding key in the topology -- we clone into 2 instances -- we enable filtered replication -- we move all serving types -- we remove the source tablets -- we remove the original shard -""" - -import logging -import unittest -from vtdb import keyrange_constants - -import base_sharding -import environment -import tablet -import utils - -# initial shard, covers everything -shard_master = tablet.Tablet() -shard_replica = tablet.Tablet() -shard_rdonly1 = tablet.Tablet() - -# split shards -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -shard_0_rdonly1 = tablet.Tablet() -# range 80 - '' -shard_1_master = tablet.Tablet() -shard_1_replica = tablet.Tablet() -shard_1_rdonly1 = tablet.Tablet() - -all_tablets = [shard_master, shard_replica, shard_rdonly1, - shard_0_master, shard_0_replica, shard_0_rdonly1, - shard_1_master, shard_1_replica, shard_1_rdonly1] - - -def setUpModule(): - try: - environment.topo_server().setup() - setup_procs = [t.init_mysql() for t in all_tablets] - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - - -class TestInitialSharding(unittest.TestCase, base_sharding.BaseShardingTest): - - # create_schema will create the same schema on the keyspace - def _create_schema(self): - # Note that the primary key columns are not defined first on purpose to test - # that a reordered column list is correctly used everywhere in vtworker. - create_table_template = '''create table %s( -msg varchar(64), -id bigint not null, -parent_id bigint not null, -primary key (parent_id, id), -index by_msg (msg) -) Engine=InnoDB''' - - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding1'), - 'test_keyspace'], - auto_log=True) - - def _add_sharding_key_to_schema(self): - if base_sharding.keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - sql = 'alter table %s add custom_ksid_col ' + t - utils.run_vtctl(['ApplySchema', - '-sql=' + sql % ('resharding1'), - 'test_keyspace'], - auto_log=True) - - def _mark_sharding_key_not_null(self): - if base_sharding.keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - sql = 'alter table %s modify custom_ksid_col ' + t + ' not null' - utils.run_vtctl(['ApplySchema', - '-sql=' + sql % ('resharding1'), - 'test_keyspace'], - auto_log=True) - - # _insert_startup_value inserts a value in the MySQL database before it - # is sharded - def _insert_startup_value(self, tablet_obj, table, mid, msg): - tablet_obj.mquery('vt_test_keyspace', [ - 'begin', - 'insert into %s(parent_id, id, msg) values(%d, %d, "%s")' % - (table, base_sharding.fixed_parent_id, mid, msg), - 'commit' - ], write=True) - - def _insert_startup_values(self): - self._insert_startup_value(shard_master, 'resharding1', 1, 'msg1') - self._insert_startup_value(shard_master, 'resharding1', 2, 'msg2') - self._insert_startup_value(shard_master, 'resharding1', 3, 'msg3') - - def _backfill_keyspace_id(self, tablet_obj): - tablet_obj.mquery('vt_test_keyspace', [ - 'begin', - 'update resharding1 set custom_ksid_col=0x1000000000000000 where id=1', - 'update resharding1 set custom_ksid_col=0x9000000000000000 where id=2', - 'update resharding1 set custom_ksid_col=0xD000000000000000 where id=3', - 'commit' - ], write=True) - - def _check_startup_values(self): - # check first value is in the left shard - for t in [shard_0_master, shard_0_replica, shard_0_rdonly1]: - self._check_value(t, 'resharding1', 1, 'msg1', 0x1000000000000000) - for t in [shard_1_master, shard_1_replica, shard_1_rdonly1]: - self._check_value(t, 'resharding1', 1, 'msg1', - 0x1000000000000000, should_be_here=False) - - # check second value is in the right shard - for t in [shard_0_master, shard_0_replica, shard_0_rdonly1]: - self._check_value(t, 'resharding1', 2, 'msg2', 0x9000000000000000, - should_be_here=False) - for t in [shard_1_master, shard_1_replica, shard_1_rdonly1]: - self._check_value(t, 'resharding1', 2, 'msg2', 0x9000000000000000) - - # check third value is in the right shard too - for t in [shard_0_master, shard_0_replica, shard_0_rdonly1]: - self._check_value(t, 'resharding1', 3, 'msg3', 0xD000000000000000, - should_be_here=False) - for t in [shard_1_master, shard_1_replica, shard_1_rdonly1]: - self._check_value(t, 'resharding1', 3, 'msg3', 0xD000000000000000) - - def _insert_lots(self, count, base=0): - for i in xrange(count): - self._insert_value(shard_master, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i) - self._insert_value(shard_master, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i) - - # _check_lots returns how many of the values we have, in percents. - def _check_lots(self, count, base=0): - found = 0 - for i in xrange(count): - if self._is_value_present_and_correct(shard_1_replica, 'resharding1', - 10000 + base + i, 'msg-range1-%d' % - i, 0xA000000000000000 + base + i): - found += 1 - if self._is_value_present_and_correct(shard_1_replica, 'resharding1', - 20000 + base + i, 'msg-range2-%d' % - i, 0xE000000000000000 + base + i): - found += 1 - percent = found * 100 / count / 2 - logging.debug('I have %d%% of the data', percent) - return percent - - def _check_lots_timeout(self, count, threshold, timeout, base=0): - while True: - value = self._check_lots(count, base=base) - if value >= threshold: - return value - timeout = utils.wait_step('enough data went through', timeout) - - # _check_lots_not_present makes sure no data is in the wrong shard - def _check_lots_not_present(self, count, base=0): - for i in xrange(count): - self._check_value(shard_0_replica, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i, - should_be_here=False) - self._check_value(shard_0_replica, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i, - should_be_here=False) - - def test_resharding(self): - # create the keyspace with just one shard - shard_master.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='0', - tablet_index=0) - shard_replica.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='0', - tablet_index=1) - shard_rdonly1.init_tablet( - 'rdonly', - keyspace='test_keyspace', - shard='0', - tablet_index=2) - - for t in [shard_master, shard_replica, shard_rdonly1]: - t.create_db('vt_test_keyspace') - - # replica is not started, InitShardMaster should timeout - shard_master.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - shard_rdonly1.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - - for t in [shard_master, shard_rdonly1]: - t.wait_for_vttablet_state('NOT_SERVING') - - # reparent to make the tablets work - expect fail - # because replica tablet is not up - _, stderr = utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - shard_master.tablet_alias], auto_log=True, expect_fail=True) - - self.assertIn('tablet test_nj-0000062345 ResetReplication failed', stderr) - # start replica - shard_replica.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - - shard_replica.wait_for_vttablet_state('NOT_SERVING') - - # reparent to make the tablets work - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - shard_master.tablet_alias], auto_log=True) - - utils.wait_for_tablet_type(shard_replica.tablet_alias, 'replica') - utils.wait_for_tablet_type(shard_rdonly1.tablet_alias, 'rdonly') - for t in [shard_master, shard_replica, shard_rdonly1]: - t.wait_for_vttablet_state('SERVING') - - # create the tables and add startup values - self._create_schema() - self._insert_startup_values() - - # reload schema on all tablets so we can query them - for t in [shard_master, shard_replica, shard_rdonly1]: - utils.run_vtctl(['ReloadSchema', t.tablet_alias], auto_log=True) - - # We must start vtgate after tablets are up, or else wait until 1min refresh - # (that is the tablet_refresh_interval parameter for discovery gateway) - # we want cache_ttl at zero so we re-read the topology for every test query. - - utils.VtGate().start(cache_ttl='0', tablets=[ - shard_master, shard_replica, shard_rdonly1]) - utils.vtgate.wait_for_endpoints('test_keyspace.0.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.0.replica', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.0.rdonly', 1) - - # check the Map Reduce API works correctly, should use ExecuteShards, - # as we're not sharded yet. - # we have 3 values in the database, asking for 4 splits will get us - # a single query. - sql = 'select id, msg from resharding1' - s = utils.vtgate.split_query(sql, 'test_keyspace', 4) - self.assertEqual(len(s), 1) - self.assertEqual(s[0]['shard_part']['shards'][0], '0') - - # change the schema, backfill keyspace_id, and change schema again - self._add_sharding_key_to_schema() - self._backfill_keyspace_id(shard_master) - self._mark_sharding_key_not_null() - - # now we can be a sharded keyspace (and propagate to SrvKeyspace) - utils.run_vtctl(['SetKeyspaceShardingInfo', 'test_keyspace', - 'custom_ksid_col', base_sharding.keyspace_id_type]) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], - auto_log=True) - - # run a health check on source replica so it responds to discovery - utils.run_vtctl(['RunHealthCheck', shard_replica.tablet_alias]) - - # create the split shards - shard_0_master.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='-80', - tablet_index=0) - shard_0_replica.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='-80', - tablet_index=1) - shard_0_rdonly1.init_tablet( - 'rdonly', - keyspace='test_keyspace', - shard='-80', - tablet_index=2) - shard_1_master.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='80-', - tablet_index=0) - shard_1_replica.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='80-', - tablet_index=1) - shard_1_rdonly1.init_tablet( - 'rdonly', - keyspace='test_keyspace', - shard='80-', - tablet_index=2) - - for t in [shard_0_master, shard_0_replica, shard_0_rdonly1, - shard_1_master, shard_1_replica, shard_1_rdonly1]: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - - for t in [shard_0_master, shard_0_replica, shard_0_rdonly1, - shard_1_master, shard_1_replica, shard_1_rdonly1]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - for t in [shard_0_replica, shard_1_replica]: - utils.wait_for_tablet_type(t.tablet_alias, 'replica') - for t in [shard_0_rdonly1, shard_1_rdonly1]: - utils.wait_for_tablet_type(t.tablet_alias, 'rdonly') - - sharded_tablets = [shard_0_master, shard_0_replica, shard_0_rdonly1, - shard_1_master, shard_1_replica, shard_1_rdonly1] - for t in sharded_tablets: - t.wait_for_vttablet_state('SERVING') - - # must restart vtgate after tablets are up, or else wait until 1min refresh - # we want cache_ttl at zero so we re-read the topology for every test query. - utils.vtgate.kill() - - utils.vtgate = None - utils.VtGate().start(cache_ttl='0', tablets=[ - shard_master, shard_replica, shard_rdonly1, - shard_0_master, shard_0_replica, shard_0_rdonly1, - shard_1_master, shard_1_replica, shard_1_rdonly1]) - var = None - - # Wait for the endpoints, either local or remote. - utils.vtgate.wait_for_endpoints('test_keyspace.0.master', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.0.replica', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.0.rdonly', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.-80.master', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.-80.replica', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.-80.rdonly', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.master', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.replica', 1, var=var) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.rdonly', 1, var=var) - - # check the Map Reduce API works correctly, should use ExecuteKeyRanges now, - # as we are sharded (with just one shard). - # again, we have 3 values in the database, asking for 4 splits will get us - # a single query. - sql = 'select id, msg from resharding1' - s = utils.vtgate.split_query(sql, 'test_keyspace', 4) - self.assertEqual(len(s), 1) - self.assertEqual(s[0]['key_range_part']['keyspace'], 'test_keyspace') - # There must be one empty KeyRange which represents the full keyspace. - self.assertEqual(len(s[0]['key_range_part']['key_ranges']), 1) - self.assertEqual(s[0]['key_range_part']['key_ranges'][0], {}) - - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -\n' - 'Partitions(rdonly): -\n' - 'Partitions(replica): -\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # we need to create the schema, and the worker will do data copying - for keyspace_shard in ('test_keyspace/-80', 'test_keyspace/80-'): - utils.run_vtctl(['CopySchemaShard', - '--exclude_tables', 'unrelated', - shard_rdonly1.tablet_alias, - keyspace_shard], - auto_log=True) - utils.run_vtctl(['RunHealthCheck', shard_rdonly1.tablet_alias]) - - # Run vtworker as daemon for the following SplitClone commands. - worker_proc, worker_port, worker_rpc_port = utils.run_vtworker_bg( - ['--cell', 'test_nj', '--command_display_interval', '10ms', - '--use_v3_resharding_mode=false'], - auto_log=True) - - # Initial clone (online). - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--offline=false', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/0'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 3, 0, 0, 0) - - # Reset vtworker such that we can run the next command. - workerclient_proc = utils.run_vtworker_client_bg(['Reset'], worker_rpc_port) - utils.wait_procs([workerclient_proc]) - - # Modify the destination shard. SplitClone will revert the changes. - # Delete row 1 (provokes an insert). - shard_0_master.mquery('vt_test_keyspace', - 'delete from resharding1 where id=1', write=True) - # Delete row 2 (provokes an insert). - shard_1_master.mquery('vt_test_keyspace', - 'delete from resharding1 where id=2', write=True) - # Update row 3 (provokes an update). - shard_1_master.mquery('vt_test_keyspace', - "update resharding1 set msg='msg-not-3' where id=3", - write=True) - # Insert row 4 (provokes a delete). - self._insert_value(shard_1_master, 'resharding1', 4, 'msg4', - 0xD000000000000000) - - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/0'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 2, 1, 1, 0) - self.verify_reconciliation_counters(worker_port, 'Offline', 'resharding1', - 0, 0, 0, 3) - # Terminate worker daemon because it is no longer needed. - utils.kill_sub_process(worker_proc, soft=True) - - # check the startup values are in the right place - self._check_startup_values() - - # check the schema too - utils.run_vtctl(['ValidateSchemaKeyspace', 'test_keyspace'], auto_log=True) - - # check the binlog players are running - logging.debug('Waiting for binlog players to start on new masters...') - self.check_destination_master(shard_0_master, ['test_keyspace/0']) - self.check_destination_master(shard_1_master, ['test_keyspace/0']) - - # check that binlog server exported the stats vars - self.check_binlog_server_vars(shard_replica, horizontal=True) - - # testing filtered replication: insert a bunch of data on shard 1, - # check we get most of it after a few seconds, wait for binlog server - # timeout, check we get all of it. - logging.debug('Inserting lots of data on source shard') - self._insert_lots(1000) - logging.debug('Checking 80 percent of data is sent quickly') - v = self._check_lots_timeout(1000, 80, 5) - if v != 100: - logging.debug('Checking all data goes through eventually') - self._check_lots_timeout(1000, 100, 20) - logging.debug('Checking no data was sent the wrong way') - self._check_lots_not_present(1000) - self.check_binlog_player_vars(shard_0_master, ['test_keyspace/0'], - seconds_behind_master_max=30) - self.check_binlog_player_vars(shard_1_master, ['test_keyspace/0'], - seconds_behind_master_max=30) - self.check_binlog_server_vars(shard_replica, horizontal=True, - min_statements=1000, min_transactions=1000) - - # use vtworker to compare the data - for t in [shard_0_rdonly1, shard_1_rdonly1]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - if base_sharding.use_multi_split_diff: - logging.debug('Running vtworker MultiSplitDiff for 0') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'MultiSplitDiff', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/0'], - auto_log=True) - else: - logging.debug('Running vtworker SplitDiff for -80') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/-80'], - auto_log=True) - logging.debug('Running vtworker SplitDiff for 80-') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/80-'], - auto_log=True) - - utils.pause('Good time to test vtworker for diffs') - - # get status for the destination master tablet, make sure we have it all - self.check_running_binlog_player(shard_0_master, 2000, 2000) - self.check_running_binlog_player(shard_1_master, 6000, 2000) - - # check we can't migrate the master just yet - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/0', 'master'], - expect_fail=True) - - # now serve rdonly from the split shards - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/0', 'rdonly'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # make sure rdonly tablets are back to serving before hitting vtgate. - for t in [shard_0_rdonly1, shard_1_rdonly1]: - t.wait_for_vttablet_state('SERVING') - - utils.vtgate.wait_for_endpoints('test_keyspace.-80.rdonly', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.rdonly', 1) - - # check the Map Reduce API works correctly, should use ExecuteKeyRanges - # on both destination shards now. - # we ask for 2 splits to only have one per shard - sql = 'select id, msg from resharding1' - timeout = 10.0 - while True: - try: - s = utils.vtgate.split_query(sql, 'test_keyspace', 2) - break - except Exception: # pylint: disable=broad-except - timeout = utils.wait_step( - 'vtgate executes split_query properly', timeout) - self.assertEqual(len(s), 2) - self.assertEqual(s[0]['key_range_part']['keyspace'], 'test_keyspace') - self.assertEqual(s[1]['key_range_part']['keyspace'], 'test_keyspace') - self.assertEqual(len(s[0]['key_range_part']['key_ranges']), 1) - self.assertEqual(len(s[1]['key_range_part']['key_ranges']), 1) - - # then serve replica from the split shards - source_tablet = shard_replica - destination_tablets = [shard_0_replica, shard_1_replica] - - utils.run_vtctl( - ['MigrateServedTypes', 'test_keyspace/0', 'replica'], auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # move replica back and forth - utils.run_vtctl( - ['MigrateServedTypes', '-reverse', 'test_keyspace/0', 'replica'], - auto_log=True) - # After a backwards migration, queryservice should be enabled on - # source and disabled on destinations - utils.check_tablet_query_service(self, source_tablet, True, False) - utils.check_tablet_query_services(self, destination_tablets, False, True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/0', 'replica'], - auto_log=True) - # After a forwards migration, queryservice should be disabled on - # source and enabled on destinations - utils.check_tablet_query_service(self, source_tablet, False, True) - utils.check_tablet_query_services(self, destination_tablets, True, False) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # then serve master from the split shards - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/0', 'master'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # check the binlog players are gone now - self.check_no_binlog_player(shard_0_master) - self.check_no_binlog_player(shard_1_master) - - # make sure we can't delete a shard with tablets - utils.run_vtctl(['DeleteShard', 'test_keyspace/0'], expect_fail=True) - - # remove the original tablets in the original shard - tablet.kill_tablets([shard_master, shard_replica, shard_rdonly1]) - for t in [shard_replica, shard_rdonly1]: - utils.run_vtctl(['DeleteTablet', t.tablet_alias], auto_log=True) - utils.run_vtctl(['DeleteTablet', '-allow_master', - shard_master.tablet_alias], auto_log=True) - - # rebuild the serving graph, all mentions of the old shards should be gone - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # delete the original shard - utils.run_vtctl(['DeleteShard', 'test_keyspace/0'], auto_log=True) - - # kill everything else - tablet.kill_tablets([shard_0_master, shard_0_replica, shard_0_rdonly1, - shard_1_master, shard_1_replica, shard_1_rdonly1]) - - -if __name__ == '__main__': - utils.main() diff --git a/test/initial_sharding_bytes.py b/test/initial_sharding_bytes.py deleted file mode 100755 index 6103372e7be..00000000000 --- a/test/initial_sharding_bytes.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Re-runs initial_sharding.py with a varbinary keyspace_id.""" - -from vtdb import keyrange_constants - -import base_sharding -import initial_sharding -import utils - -# this test is just re-running an entire initial_sharding.py with a -# varbinary keyspace_id -if __name__ == '__main__': - base_sharding.keyspace_id_type = keyrange_constants.KIT_BYTES - utils.main(initial_sharding) diff --git a/test/initial_sharding_multi.py b/test/initial_sharding_multi.py deleted file mode 100755 index 8edad5a73e5..00000000000 --- a/test/initial_sharding_multi.py +++ /dev/null @@ -1,796 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This test simulates the first time a database has to be split -in a multi-vttablet-single-mysql environment - -We have 2 keyspaces. One keyspace is in managing mode. It's vttablets -own the MySQL instances and can reparent, start/stop server, start/stop -replication etc. Other keyspace is in non-managing mode and cannot do -any of these actions. Only TabletExternallyReparented is allowed, but -resharding should still work. - -For each keyspace: -- we start with a keyspace with a single shard and a single table -- we add and populate the sharding key -- we set the sharding key in the topology -- we clone into 2 instances -- we enable filtered replication -- we move all serving types -- we remove the source tablets -- we remove the original shard -""" - -import json -import logging -import unittest -from vtdb import keyrange_constants - -import MySQLdb - -import base_sharding -import environment -import tablet -import utils -from mysql_flavor import mysql_flavor - -# initial shard, covers everything -ks1_shard_master = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks1_shard_replica = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks1_shard_rdonly1 = tablet.Tablet(vt_dba_passwd='VtDbaPass') - -# split shards -# range '' - 80 -ks1_shard_0_master = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks1_shard_0_replica = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks1_shard_0_rdonly1 = tablet.Tablet(vt_dba_passwd='VtDbaPass') -# range 80 - '' -ks1_shard_1_master = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks1_shard_1_replica = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks1_shard_1_rdonly1 = tablet.Tablet(vt_dba_passwd='VtDbaPass') - -ks1_tablets = { - '0': {'master':ks1_shard_master, 'replica':ks1_shard_replica, 'rdonly':ks1_shard_rdonly1}, - '-80': {'master':ks1_shard_0_master, 'replica':ks1_shard_0_replica, 'rdonly':ks1_shard_0_rdonly1}, - '80-': {'master':ks1_shard_1_master, 'replica':ks1_shard_1_replica, 'rdonly':ks1_shard_1_rdonly1} -} - -# initial shard, covers everything -ks2_shard_master = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks2_shard_replica = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks2_shard_rdonly1 = tablet.Tablet(vt_dba_passwd='VtDbaPass') - -# split shards -# range '' - 80 -ks2_shard_0_master = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks2_shard_0_replica = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks2_shard_0_rdonly1 = tablet.Tablet(vt_dba_passwd='VtDbaPass') -# range 80 - '' -ks2_shard_1_master = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks2_shard_1_replica = tablet.Tablet(vt_dba_passwd='VtDbaPass') -ks2_shard_1_rdonly1 = tablet.Tablet(vt_dba_passwd='VtDbaPass') - -ks2_tablets = { - '0': {'master':ks2_shard_master, 'replica':ks2_shard_replica, 'rdonly':ks2_shard_rdonly1}, - '-80': {'master':ks2_shard_0_master, 'replica':ks2_shard_0_replica, 'rdonly':ks2_shard_0_rdonly1}, - '80-': {'master':ks2_shard_1_master, 'replica':ks2_shard_1_replica, 'rdonly':ks2_shard_1_rdonly1} -} - -all_mysql_tablets = [ks1_shard_master, ks1_shard_replica, ks1_shard_rdonly1, - ks1_shard_0_master, ks1_shard_0_replica, ks1_shard_0_rdonly1, - ks1_shard_1_master, ks1_shard_1_replica, ks1_shard_1_rdonly1] - -all_other_tablets = [ks2_shard_master, ks2_shard_replica, ks2_shard_rdonly1, - ks2_shard_0_master, ks2_shard_0_replica, ks2_shard_0_rdonly1, - ks2_shard_1_master, ks2_shard_1_replica, ks2_shard_1_rdonly1] - -def setUpModule(): - global new_init_db, db_credentials_file - - try: - credentials = { - 'vt_dba': ['VtDbaPass'], - 'vt_app': ['VtAppPass'], - 'vt_allprivs': ['VtAllprivsPass'], - 'vt_repl': ['VtReplPass'], - 'vt_filtered': ['VtFilteredPass'], - } - db_credentials_file = environment.tmproot+'/db_credentials.json' - with open(db_credentials_file, 'w') as fd: - fd.write(json.dumps(credentials)) - - # Determine which column is used for user passwords in this MySQL version. - proc = ks1_shard_master.init_mysql() - utils.wait_procs([proc]) - try: - ks1_shard_master.mquery('mysql', 'select password from mysql.user limit 0', - user='root') - password_col = 'password' - except MySQLdb.DatabaseError: - password_col = 'authentication_string' - utils.wait_procs([ks1_shard_master.teardown_mysql()]) - ks1_shard_master.remove_tree(ignore_options=True) - - # Create a new init_db.sql file that sets up passwords for all users. - # Then we use a db-credentials-file with the passwords. - new_init_db = environment.tmproot + '/init_db_with_passwords.sql' - with open(environment.vtroot + '/config/init_db.sql') as fd: - init_db = fd.read() - with open(new_init_db, 'w') as fd: - fd.write(init_db) - fd.write(mysql_flavor().change_passwords(password_col)) - fd.write(''' - -# connecting through a port requires 127.0.0.1 -# --host=localhost will connect through socket -CREATE USER 'vt_dba'@'127.0.0.1' IDENTIFIED BY 'VtDbaPass'; -GRANT ALL ON *.* TO 'vt_dba'@'127.0.0.1'; -GRANT GRANT OPTION ON *.* TO 'vt_dba'@'127.0.0.1'; - -# User for app traffic, with global read-write access. -CREATE USER 'vt_app'@'127.0.0.1' IDENTIFIED BY 'VtAppPass'; -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, - REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, - LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, - SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER - ON *.* TO 'vt_app'@'127.0.0.1'; - -# User for administrative operations that need to be executed as non-SUPER. -# Same permissions as vt_app here. -CREATE USER 'vt_allprivs'@'127.0.0.1' IDENTIFIED BY 'VtAllPrivsPass'; -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, - REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, - LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, - SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER - ON *.* TO 'vt_allprivs'@'127.0.0.1'; - -# User for Vitess filtered replication (binlog player). -# Same permissions as vt_app. -CREATE USER 'vt_filtered'@'127.0.0.1' IDENTIFIED BY 'VtFilteredPass'; -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, - REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, - LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, - SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER - ON *.* TO 'vt_filtered'@'127.0.0.1'; - -FLUSH PRIVILEGES; -''') - setup_procs = [t.init_mysql(use_rbr=True, init_db=new_init_db, - extra_args=['-db-credentials-file', - db_credentials_file]) for t in all_mysql_tablets] - utils.wait_procs(setup_procs) - for i in range(0, len(all_other_tablets)): - all_other_tablets[i].mysql_port = all_mysql_tablets[i].mysql_port - - environment.topo_server().setup() - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql(extra_args=['-db-credentials-file', db_credentials_file]) for t in all_mysql_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_mysql_tablets: - t.remove_tree() - for t in all_other_tablets: - t.remove_tree() - - -class TestInitialSharding(unittest.TestCase, base_sharding.BaseShardingTest): - - # create_schema will create the same schema on the keyspace - def _create_schema(self, keyspace): - # Note that the primary key columns are not defined first on purpose to test - # that a reordered column list is correctly used everywhere in vtworker. - create_table_template = '''create table %s( -msg varchar(64), -id bigint not null, -parent_id bigint not null, -primary key (parent_id, id), -index by_msg (msg) -) Engine=InnoDB''' - - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding1'), - keyspace], - auto_log=True) - - def _add_sharding_key_to_schema(self, keyspace): - if base_sharding.keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - sql = 'alter table %s add custom_ksid_col ' + t - utils.run_vtctl(['ApplySchema', - '-sql=' + sql % ('resharding1'), - keyspace], - auto_log=True) - - def _mark_sharding_key_not_null(self, keyspace): - if base_sharding.keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - sql = 'alter table %s modify custom_ksid_col ' + t + ' not null' - utils.run_vtctl(['ApplySchema', - '-sql=' + sql % ('resharding1'), - keyspace], - auto_log=True) - - # _insert_startup_value inserts a value in the MySQL database before it - # is sharded - def _insert_startup_value(self, keyspace, tablet_obj, table, mid, msg): - tablet_obj.mquery('vt_' + keyspace, [ - 'begin', - 'insert into %s(parent_id, id, msg) values(%d, %d, "%s")' % - (table, base_sharding.fixed_parent_id, mid, msg), - 'commit' - ], write=True) - - def _insert_startup_values(self, keyspace, master_tablet): - self._insert_startup_value(keyspace, master_tablet, 'resharding1', 1, 'msg1') - self._insert_startup_value(keyspace, master_tablet, 'resharding1', 2, 'msg2') - self._insert_startup_value(keyspace, master_tablet, 'resharding1', 3, 'msg3') - - def _backfill_keyspace_id(self, keyspace, tablet_obj): - tablet_obj.mquery('vt_' + keyspace, [ - 'begin', - 'update resharding1 set custom_ksid_col=0x1000000000000000 where id=1', - 'update resharding1 set custom_ksid_col=0x9000000000000000 where id=2', - 'update resharding1 set custom_ksid_col=0xD000000000000000 where id=3', - 'commit' - ], write=True) - - def _check_startup_values(self, keyspace, tablets): - # check first value is in the left shard - for t in tablets['-80'].values(): - self._check_value(t, 'resharding1', 1, 'msg1', 0x1000000000000000) - for t in tablets['80-'].values(): - self._check_value(t, 'resharding1', 1, 'msg1', - 0x1000000000000000, should_be_here=False) - - # check second value is in the right shard - for t in tablets['-80'].values(): - self._check_value(t, 'resharding1', 2, 'msg2', 0x9000000000000000, - should_be_here=False) - for t in tablets['80-'].values(): - self._check_value(t, 'resharding1', 2, 'msg2', 0x9000000000000000) - - # check third value is in the right shard too - for t in tablets['-80'].values(): - self._check_value(t, 'resharding1', 3, 'msg3', 0xD000000000000000, - should_be_here=False) - for t in tablets['80-'].values(): - self._check_value(t, 'resharding1', 3, 'msg3', 0xD000000000000000) - - def _insert_lots(self, keyspace, master_tablet, count, base=0): - for i in xrange(count): - self._insert_value(master_tablet, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i) - self._insert_value(master_tablet, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i) - - # _check_lots returns how many of the values we have, in percents. - def _check_lots(self, replica_tablet, count, base=0): - found = 0 - for i in xrange(count): - if self._is_value_present_and_correct(replica_tablet, 'resharding1', - 10000 + base + i, 'msg-range1-%d' % - i, 0xA000000000000000 + base + i): - found += 1 - if self._is_value_present_and_correct(replica_tablet, 'resharding1', - 20000 + base + i, 'msg-range2-%d' % - i, 0xE000000000000000 + base + i): - found += 1 - percent = found * 100 / count / 2 - logging.debug('I have %d%% of the data', percent) - return percent - - def _check_lots_timeout(self, replica_tablet, count, threshold, timeout, base=0): - while True: - value = self._check_lots(replica_tablet, count, base=base) - if value >= threshold: - return value - timeout = utils.wait_step('enough data went through', timeout) - - # _check_lots_not_present makes sure no data is in the wrong shard - def _check_lots_not_present(self, replica_tablet, count, base=0): - for i in xrange(count): - self._check_value(replica_tablet, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i, - should_be_here=False) - self._check_value(replica_tablet, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i, - should_be_here=False) - - def _test_resharding(self, keyspace, tablet_map, external_mysql=False): - # create the keyspace with just one shard - shard_master = tablet_map['0']['master'] - shard_replica = tablet_map['0']['replica'] - shard_rdonly = tablet_map['0']['rdonly'] - shard_0_master = tablet_map['-80']['master'] - shard_0_replica = tablet_map['-80']['replica'] - shard_0_rdonly = tablet_map['-80']['rdonly'] - shard_1_master = tablet_map['80-']['master'] - shard_1_replica = tablet_map['80-']['replica'] - shard_1_rdonly = tablet_map['80-']['rdonly'] - shard_master.init_tablet( - 'replica', - keyspace=keyspace, - shard='0', - tablet_index=0, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_replica.init_tablet( - 'replica', - keyspace=keyspace, - shard='0', - tablet_index=1, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_rdonly.init_tablet( - 'rdonly', - keyspace=keyspace, - shard='0', - tablet_index=2, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - - for t in [shard_master, shard_replica, shard_rdonly]: - t.create_db('vt_' + keyspace) - - shard_master.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False, - supports_backups=False, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_rdonly.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False, - supports_backups=False, - extra_args=['-db-credentials-file', db_credentials_file]) - - if not external_mysql: - for t in [shard_master, shard_rdonly]: - t.wait_for_vttablet_state('NOT_SERVING') - - # start replica - shard_replica.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False, - supports_backups=False, - extra_args=['-db-credentials-file', db_credentials_file]) - - if not external_mysql: - shard_replica.wait_for_vttablet_state('NOT_SERVING') - - # reparent to make the tablets work - utils.run_vtctl(['InitShardMaster', '-force', keyspace+'/0', - shard_master.tablet_alias], auto_log=True) - - utils.wait_for_tablet_type(shard_replica.tablet_alias, 'replica') - utils.wait_for_tablet_type(shard_rdonly.tablet_alias, 'rdonly') - else: - shard_replica.wait_for_vttablet_state('SERVING') - # default mode is VTCTL_AUTO which makes this command hang - _, stderr = utils.run_vtctl(['TabletExternallyReparented', shard_master.tablet_alias], mode=utils.VTCTL_VTCTL, auto_log=True) - - for t in [shard_master, shard_replica, shard_rdonly]: - t.wait_for_vttablet_state('SERVING') - - # create the tables and add startup values - self._create_schema(keyspace) - self._insert_startup_values(keyspace, shard_master) - - # reload schema on all tablets so we can query them - for t in [shard_master, shard_replica, shard_rdonly]: - utils.run_vtctl(['ReloadSchema', t.tablet_alias], auto_log=True) - - # We must start vtgate after tablets are up, or else wait until 1min refresh - # (that is the tablet_refresh_interval parameter for discovery gateway) - # we want cache_ttl at zero so we re-read the topology for every test query. - - utils.VtGate().start(cache_ttl='0', tablets=[ - shard_master, shard_replica, shard_rdonly]) - utils.vtgate.wait_for_endpoints(keyspace + '.0.master', 1) - utils.vtgate.wait_for_endpoints(keyspace + '.0.replica', 1) - utils.vtgate.wait_for_endpoints(keyspace + '.0.rdonly', 1) - - # check the Map Reduce API works correctly, should use ExecuteShards, - # as we're not sharded yet. - # we have 3 values in the database, asking for 4 splits will get us - # a single query. - sql = 'select id, msg from resharding1' - s = utils.vtgate.split_query(sql, keyspace, 4) - self.assertEqual(len(s), 1) - self.assertEqual(s[0]['shard_part']['shards'][0], '0') - - # change the schema, backfill keyspace_id, and change schema again - self._add_sharding_key_to_schema(keyspace) - self._backfill_keyspace_id(keyspace, shard_master) - self._mark_sharding_key_not_null(keyspace) - - # now we can be a sharded keyspace (and propagate to SrvKeyspace) - utils.run_vtctl(['SetKeyspaceShardingInfo', keyspace, - 'custom_ksid_col', base_sharding.keyspace_id_type]) - utils.run_vtctl(['RebuildKeyspaceGraph', keyspace], - auto_log=True) - - # run a health check on source replica so it responds to discovery - utils.run_vtctl(['RunHealthCheck', shard_replica.tablet_alias]) - - # create the split shards - shard_0_master.init_tablet( - 'replica', - keyspace=keyspace, - shard='-80', - tablet_index=0, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_0_replica.init_tablet( - 'replica', - keyspace=keyspace, - shard='-80', - tablet_index=1, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_0_rdonly.init_tablet( - 'rdonly', - keyspace=keyspace, - shard='-80', - tablet_index=2, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_1_master.init_tablet( - 'replica', - keyspace=keyspace, - shard='80-', - tablet_index=0, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_1_replica.init_tablet( - 'replica', - keyspace=keyspace, - shard='80-', - tablet_index=1, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - shard_1_rdonly.init_tablet( - 'rdonly', - keyspace=keyspace, - shard='80-', - tablet_index=2, - external_mysql=external_mysql, - extra_args=['-db-credentials-file', db_credentials_file]) - - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]: - t.create_db('vt_' + keyspace) - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False, - supports_backups=False, - extra_args=['-db-credentials-file', db_credentials_file]) - - sharded_tablets = [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly] - if not external_mysql: - for t in sharded_tablets: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', keyspace + '/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', keyspace + '/80-', - shard_1_master.tablet_alias], auto_log=True) - - for t in [shard_0_replica, shard_1_replica]: - utils.wait_for_tablet_type(t.tablet_alias, 'replica') - for t in [shard_0_rdonly, shard_1_rdonly]: - utils.wait_for_tablet_type(t.tablet_alias, 'rdonly') - - for t in sharded_tablets: - t.wait_for_vttablet_state('SERVING') - else: - # default mode is VTCTL_AUTO which makes this command hang - _, stderr = utils.run_vtctl(['TabletExternallyReparented', shard_0_master.tablet_alias], mode=utils.VTCTL_VTCTL, auto_log=True) - _, stderr = utils.run_vtctl(['TabletExternallyReparented', shard_1_master.tablet_alias], mode=utils.VTCTL_VTCTL, auto_log=True) - - # must restart vtgate after tablets are up, or else wait until 1min refresh - # we want cache_ttl at zero so we re-read the topology for every test query. - utils.vtgate.kill() - - utils.vtgate = None - utils.VtGate().start(cache_ttl='0', tablets=[ - shard_master, shard_replica, shard_rdonly, - shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]) - var = None - - # Wait for the endpoints, either local or remote. - utils.vtgate.wait_for_endpoints(keyspace + '.0.master', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.0.replica', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.0.rdonly', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.-80.master', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.-80.replica', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.-80.rdonly', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.80-.master', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.80-.replica', 1, var=var) - utils.vtgate.wait_for_endpoints(keyspace + '.80-.rdonly', 1, var=var) - - # check the Map Reduce API works correctly, should use ExecuteKeyRanges now, - # as we are sharded (with just one shard). - # again, we have 3 values in the database, asking for 4 splits will get us - # a single query. - sql = 'select id, msg from resharding1' - s = utils.vtgate.split_query(sql, keyspace, 4) - self.assertEqual(len(s), 1) - self.assertEqual(s[0]['key_range_part']['keyspace'], keyspace) - # There must be one empty KeyRange which represents the full keyspace. - self.assertEqual(len(s[0]['key_range_part']['key_ranges']), 1) - self.assertEqual(s[0]['key_range_part']['key_ranges'][0], {}) - - utils.check_srv_keyspace('test_nj', keyspace, - 'Partitions(master): -\n' - 'Partitions(rdonly): -\n' - 'Partitions(replica): -\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # we need to create the schema, and the worker will do data copying - for keyspace_shard in (keyspace + '/-80', keyspace + '/80-'): - utils.run_vtctl(['CopySchemaShard', - '--exclude_tables', 'unrelated', - shard_rdonly.tablet_alias, - keyspace_shard], - auto_log=True) - utils.run_vtctl(['RunHealthCheck', shard_rdonly.tablet_alias]) - - # Run vtworker as daemon for the following SplitClone commands. - worker_proc, worker_port, worker_rpc_port = utils.run_vtworker_bg( - ['--cell', 'test_nj', '--command_display_interval', '10ms', - '--use_v3_resharding_mode=false'], - auto_log=True) - - # Initial clone (online). - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--offline=false', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - keyspace + '/0'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 3, 0, 0, 0) - - # Reset vtworker such that we can run the next command. - workerclient_proc = utils.run_vtworker_client_bg(['Reset'], worker_rpc_port) - utils.wait_procs([workerclient_proc]) - - # Modify the destination shard. SplitClone will revert the changes. - # Delete row 1 (provokes an insert). - shard_0_master.mquery('vt_' + keyspace, - 'delete from resharding1 where id=1', write=True) - # Delete row 2 (provokes an insert). - shard_1_master.mquery('vt_' + keyspace, - 'delete from resharding1 where id=2', write=True) - # Update row 3 (provokes an update). - shard_1_master.mquery('vt_' + keyspace, - "update resharding1 set msg='msg-not-3' where id=3", - write=True) - # Insert row 4 (provokes a delete). - self._insert_value(shard_1_master, 'resharding1', 4, 'msg4', - 0xD000000000000000) - - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - keyspace + '/0'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 2, 1, 1, 0) - self.verify_reconciliation_counters(worker_port, 'Offline', 'resharding1', - 0, 0, 0, 3) - # Terminate worker daemon because it is no longer needed. - utils.kill_sub_process(worker_proc, soft=True) - - # check the startup values are in the right place - self._check_startup_values(keyspace, tablet_map) - - # check the schema too - utils.run_vtctl(['ValidateSchemaKeyspace', keyspace], auto_log=True) - - # check the binlog players are running - logging.debug('Waiting for binlog players to start on new masters...') - self.check_destination_master(shard_0_master, [keyspace + '/0']) - self.check_destination_master(shard_1_master, [keyspace + '/0']) - - # check that binlog server exported the stats vars - self.check_binlog_server_vars(shard_replica, horizontal=True) - - # testing filtered replication: insert a bunch of data on shard 1, - # check we get most of it after a few seconds, wait for binlog server - # timeout, check we get all of it. - logging.debug('Inserting lots of data on source shard') - self._insert_lots(keyspace, shard_master, 1000) - logging.debug('Checking 80 percent of data is sent quickly') - v = self._check_lots_timeout(shard_1_replica, 1000, 80, 5) - if v != 100: - logging.debug('Checking all data goes through eventually') - self._check_lots_timeout(shard_1_replica, 1000, 100, 20) - logging.debug('Checking no data was sent the wrong way') - self._check_lots_not_present(shard_0_replica, 1000) - self.check_binlog_player_vars(shard_0_master, [keyspace + '/0'], - seconds_behind_master_max=30) - self.check_binlog_player_vars(shard_1_master, [keyspace + '/0'], - seconds_behind_master_max=30) - self.check_binlog_server_vars(shard_replica, horizontal=True, - min_statements=1000, min_transactions=1000) - - # use vtworker to compare the data - for t in [shard_0_rdonly, shard_1_rdonly]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - # get status for the destination master tablet, make sure we have it all - if not external_mysql: - self.check_running_binlog_player(shard_0_master, 2000, 2000) - self.check_running_binlog_player(shard_1_master, 6000, 2000) - else: - self.check_running_binlog_player(shard_0_master, 2002, 2002) - self.check_running_binlog_player(shard_1_master, 6002, 2002) - - - # check we can't migrate the master just yet - utils.run_vtctl(['MigrateServedTypes', keyspace + '/0', 'master'], - expect_fail=True) - - # now serve rdonly from the split shards - utils.run_vtctl(['MigrateServedTypes', keyspace + '/0', 'rdonly'], - auto_log=True) - utils.check_srv_keyspace('test_nj', keyspace, - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # make sure rdonly tablets are back to serving before hitting vtgate. - for t in [shard_0_rdonly, shard_1_rdonly]: - t.wait_for_vttablet_state('SERVING') - - utils.vtgate.wait_for_endpoints(keyspace + '.-80.rdonly', 1) - utils.vtgate.wait_for_endpoints(keyspace + '.80-.rdonly', 1) - - # check the Map Reduce API works correctly, should use ExecuteKeyRanges - # on both destination shards now. - # we ask for 2 splits to only have one per shard - sql = 'select id, msg from resharding1' - timeout = 10.0 - while True: - try: - s = utils.vtgate.split_query(sql, keyspace, 2) - break - except Exception: # pylint: disable=broad-except - timeout = utils.wait_step( - 'vtgate executes split_query properly', timeout) - self.assertEqual(len(s), 2) - self.assertEqual(s[0]['key_range_part']['keyspace'], keyspace) - self.assertEqual(s[1]['key_range_part']['keyspace'], keyspace) - self.assertEqual(len(s[0]['key_range_part']['key_ranges']), 1) - self.assertEqual(len(s[1]['key_range_part']['key_ranges']), 1) - - # then serve replica from the split shards - source_tablet = shard_replica - destination_tablets = [shard_0_replica, shard_1_replica] - - utils.run_vtctl( - ['MigrateServedTypes', keyspace + '/0', 'replica'], auto_log=True) - utils.check_srv_keyspace('test_nj', keyspace, - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # move replica back and forth - utils.run_vtctl( - ['MigrateServedTypes', '-reverse', keyspace + '/0', 'replica'], - auto_log=True) - # After a backwards migration, queryservice should be enabled on - # source and disabled on destinations - utils.check_tablet_query_service(self, source_tablet, True, False) - utils.check_tablet_query_services(self, destination_tablets, False, True) - utils.check_srv_keyspace('test_nj', keyspace, - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - utils.run_vtctl(['MigrateServedTypes', keyspace + '/0', 'replica'], - auto_log=True) - # After a forwards migration, queryservice should be disabled on - # source and enabled on destinations - utils.check_tablet_query_service(self, source_tablet, False, True) - utils.check_tablet_query_services(self, destination_tablets, True, False) - utils.check_srv_keyspace('test_nj', keyspace, - 'Partitions(master): -\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # then serve master from the split shards - utils.run_vtctl(['MigrateServedTypes', keyspace + '/0', 'master'], - auto_log=True) - utils.check_srv_keyspace('test_nj', keyspace, - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # check the binlog players are gone now - self.check_no_binlog_player(shard_0_master) - self.check_no_binlog_player(shard_1_master) - - def kill_all_tablets(self, keyspace, tablet_map): - shard_master = tablet_map['0']['master'] - shard_replica = tablet_map['0']['replica'] - shard_rdonly = tablet_map['0']['rdonly'] - shard_0_master = tablet_map['-80']['master'] - shard_0_replica = tablet_map['-80']['replica'] - shard_0_rdonly = tablet_map['-80']['rdonly'] - shard_1_master = tablet_map['80-']['master'] - shard_1_replica = tablet_map['80-']['replica'] - shard_1_rdonly = tablet_map['80-']['rdonly'] - - # remove the original tablets in the original shard - tablet.kill_tablets([shard_master, shard_replica, shard_rdonly]) - for t in [shard_replica, shard_rdonly]: - utils.run_vtctl(['DeleteTablet', t.tablet_alias], auto_log=True) - utils.run_vtctl(['DeleteTablet', '-allow_master', - shard_master.tablet_alias], auto_log=True) - - # rebuild the serving graph, all mentions of the old shards should be gone - utils.run_vtctl(['RebuildKeyspaceGraph', keyspace], auto_log=True) - - # delete the original shard - utils.run_vtctl(['DeleteShard', keyspace + '/0'], auto_log=True) - - # kill everything else - tablet.kill_tablets([shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]) - - def test_resharding(self): - self._test_resharding('test_keyspace1', ks1_tablets) - self._test_resharding('test_keyspace2', ks2_tablets, True) - self.kill_all_tablets('test_keyspace1', ks1_tablets) - self.kill_all_tablets('test_keyspace2', ks2_tablets) - -if __name__ == '__main__': - utils.main() diff --git a/test/initial_sharding_multi_split_diff.py b/test/initial_sharding_multi_split_diff.py deleted file mode 100755 index c214d98755c..00000000000 --- a/test/initial_sharding_multi_split_diff.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Re-runs initial_sharding.py using multiple-split-diff.""" - -from vtdb import keyrange_constants - -import base_sharding -import initial_sharding -import utils - -# this test is just re-running an entire initial_sharding.py with a -# varbinary keyspace_id -if __name__ == '__main__': - base_sharding.use_multi_split_diff = True - utils.main(initial_sharding) diff --git a/test/keyrange_test.py b/test/keyrange_test.py deleted file mode 100755 index 675607fb791..00000000000 --- a/test/keyrange_test.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import struct -import unittest - -from vtdb import dbexceptions -from vtdb import keyrange -from vtdb import keyrange_constants -from vtdb import vtrouting - -# This unittest tests the computation of task map -# and where clauses for streaming queries. - -pkid_pack = struct.Struct('!Q').pack -int_shard_kid_map = { - '-10': [1, 100, 1000, 100000, 527875958493693904, 626750931627689502, - 345387386794260318, 332484755310826578], - '10-20': [1842642426274125671, 1326307661227634652, 1761124146422844620, - 1661669973250483744], - '20-30': [3361397649937244239, 3303511690915522723, 2444880764308344533, - 2973657788686139039], - '30-40': [3821005920507858605, 4575089859165626432, 3607090456016432961, - 3979558375123453425], - '40-50': [5129057445097465905, 5464969577815708398, 5190676584475132364, - 5762096070688827561], - '50-60': [6419540613918919447, 6867152356089593986, 6601838130703675400, - 6132605084892127391], - '60-70': [7251511061270371980, 7395364497868053835, 7814586147633440734, - 7968977924086033834], - '70-80': [8653665459643609079, 8419099072545971426, 9020726671664230611, - 9064594986161620444], - '80-90': [9767889778372766922, 9742070682920810358, 10296850775085416642, - 9537430901666854108], - '90-a0': [10440455099304929791, 11454183276974683945, 11185910247776122031, - 10460396697869122981], - 'a0-b0': [11935085245138597119, 12115696589214223782, 12639360876311033978, - 12548906240535188165], - 'b0-c0': [13379616110062597001, 12826553979133932576, 13288572810772383281, - 13471801046560785347], - 'c0-d0': [14394342688314745188, 14639660031570920207, 14646353412066152016, - 14186650213447467187], - 'd0-e0': [15397348460895960623, 16014223083986915239, 15058390871463382185, - 15811857963302932363], - 'e0-f0': [17275711019497396001, 16979796627403646478, 16635982235308289704, - 16906674090344806032], - 'f0-': [18229242992218358675, 17623451135465171527, 18333015752598164958, - 17775908119782706671], -} - -# str_shard_kid_map is derived from int_shard_kid_map -# by generating bin-packed strings from the int keyspace_id values. -str_shard_kid_map = dict( - [(shard_name0, [pkid_pack(kid0) for kid0 in kid_list0]) - for shard_name0, kid_list0 in int_shard_kid_map.iteritems()]) - - -class TestKeyRange(unittest.TestCase): - - def test_keyrange_correctness(self): - kr = keyrange.KeyRange('') - self.assertEqual(kr.Start, keyrange_constants.MIN_KEY) - self.assertEqual(kr.End, keyrange_constants.MAX_KEY) - self.assertEqual(str(kr), keyrange_constants.NON_PARTIAL_KEYRANGE) - - kr = keyrange.KeyRange('-') - self.assertEqual(kr.Start, keyrange_constants.MIN_KEY) - self.assertEqual(kr.End, keyrange_constants.MAX_KEY) - self.assertEqual(str(kr), keyrange_constants.NON_PARTIAL_KEYRANGE) - - for kr_str in int_shard_kid_map: - start_raw, end_raw = kr_str.split('-') - kr = keyrange.KeyRange(kr_str) - self.assertEqual(kr.Start, start_raw.strip().decode('hex')) - self.assertEqual(kr.End, end_raw.strip().decode('hex')) - self.assertEqual(str(kr), kr_str) - - def test_incorrect_tasks(self): - global_shard_count = 16 - with self.assertRaises(dbexceptions.ProgrammingError): - vtrouting.create_parallel_task_keyrange_map(4, global_shard_count) - - def test_keyranges_for_tasks(self): - for shard_count in (16, 32, 64): - for num_tasks in (shard_count, shard_count*2, shard_count*4): - stm = vtrouting.create_parallel_task_keyrange_map( - num_tasks, shard_count) - self.assertEqual(len(stm.keyrange_list), num_tasks) - - # This tests that the where clause and bind_vars generated for each shard - # against a few sample values where keyspace_id is an int column. - def test_bind_values_for_int_keyspace(self): - stm = vtrouting.create_parallel_task_keyrange_map(16, 16) - for kr in stm.keyrange_list: - kr_parts = kr.split('-') - where_clause, bind_vars = vtrouting._create_where_clause_for_keyrange(kr) - if len(bind_vars.keys()) == 1: - if kr_parts[0]: - self.assertNotEqual(where_clause.find('>='), -1) - else: - self.assertNotEqual(where_clause.find('<'), -1) - else: - self.assertNotEqual(where_clause.find('>='), -1) - self.assertNotEqual(where_clause.find('AND'), -1) - kid_list = int_shard_kid_map[kr] - for keyspace_id in kid_list: - if len(bind_vars.keys()) == 1: - if kr_parts[0]: - self.assertGreaterEqual(keyspace_id, bind_vars['keyspace_id0']) - else: - self.assertLess(keyspace_id, bind_vars['keyspace_id0']) - else: - self.assertGreaterEqual(keyspace_id, bind_vars['keyspace_id0']) - self.assertLess(keyspace_id, bind_vars['keyspace_id1']) - - # This tests that the where clause and bind_vars generated for each shard - # against a few sample values where keyspace_id is a str column. - # mysql will use the hex function on string keyspace column - # and use byte comparison. Since the exact function is not available, - # the test emulates that by using keyspace_id.encode('hex'). - def test_bind_values_for_str_keyspace(self): - stm = vtrouting.create_parallel_task_keyrange_map(16, 16) - for kr in stm.keyrange_list: - kr_parts = kr.split('-') - where_clause, bind_vars = vtrouting._create_where_clause_for_keyrange( - kr, keyspace_col_type=keyrange_constants.KIT_BYTES) - if len(bind_vars.keys()) == 1: - if kr_parts[0]: - self.assertNotEqual(where_clause.find('>='), -1) - else: - self.assertNotEqual(where_clause.find('<'), -1) - else: - self.assertNotEqual(where_clause.find('>='), -1) - self.assertNotEqual(where_clause.find('AND'), -1) - kid_list = str_shard_kid_map[kr] - for keyspace_id in kid_list: - if len(bind_vars.keys()) == 1: - if kr_parts[0]: - self.assertGreaterEqual( - keyspace_id.encode('hex'), bind_vars['keyspace_id0']) - else: - self.assertLess( - keyspace_id.encode('hex'), bind_vars['keyspace_id0']) - else: - self.assertGreaterEqual( - keyspace_id.encode('hex'), bind_vars['keyspace_id0']) - self.assertLess(keyspace_id.encode('hex'), bind_vars['keyspace_id1']) - - def test_bind_values_for_unsharded_keyspace(self): - stm = vtrouting.create_parallel_task_keyrange_map(1, 1) - self.assertEqual(len(stm.keyrange_list), 1) - where_clause, bind_vars = vtrouting._create_where_clause_for_keyrange( - stm.keyrange_list[0]) - self.assertEqual(where_clause, '') - self.assertEqual(bind_vars, {}) - -if __name__ == '__main__': - unittest.main() diff --git a/test/keyspace_test.py b/test/keyspace_test.py deleted file mode 100755 index 4fedac8f4ef..00000000000 --- a/test/keyspace_test.py +++ /dev/null @@ -1,410 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -import json - -from vtdb import vtgate_client - -import environment -import tablet -import utils - -SHARDED_KEYSPACE = 'test_keyspace_sharded' -UNSHARDED_KEYSPACE = 'test_keyspace_unsharded' - -# shards for SHARDED_KEYSPACE -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -# range 80 - '' -shard_1_master = tablet.Tablet() -shard_1_replica = tablet.Tablet() - -# shard for UNSHARDED_KEYSPACE -unsharded_master = tablet.Tablet() -unsharded_replica = tablet.Tablet() - -shard_names = ['-80', '80-'] -shard_kid_map = { - '-80': [527875958493693904, 626750931627689502, - 345387386794260318, 332484755310826578, - 1842642426274125671, 1326307661227634652, - 1761124146422844620, 1661669973250483744, - 3361397649937244239, 2444880764308344533], - '80-': [9767889778372766922, 9742070682920810358, - 10296850775085416642, 9537430901666854108, - 10440455099304929791, 11454183276974683945, - 11185910247776122031, 10460396697869122981, - 13379616110062597001, 12826553979133932576], -} - -create_vt_insert_test = '''create table vt_insert_test ( -id bigint auto_increment, -msg varchar(64), -keyspace_id bigint(20) unsigned NOT NULL, -primary key (id) -) Engine=InnoDB''' - - -def setUpModule(): - try: - environment.topo_server().setup() - - setup_procs = [ - shard_0_master.init_mysql(), - shard_0_replica.init_mysql(), - shard_1_master.init_mysql(), - shard_1_replica.init_mysql(), - unsharded_master.init_mysql(), - unsharded_replica.init_mysql(), - ] - utils.wait_procs(setup_procs) - setup_tablets() - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - tablet.kill_tablets([shard_0_master, shard_0_replica, - shard_1_master, shard_1_replica]) - teardown_procs = [ - shard_0_master.teardown_mysql(), - shard_0_replica.teardown_mysql(), - shard_1_master.teardown_mysql(), - shard_1_replica.teardown_mysql(), - unsharded_master.teardown_mysql(), - unsharded_replica.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_0_master.remove_tree() - shard_0_replica.remove_tree() - shard_1_master.remove_tree() - shard_1_replica.remove_tree() - unsharded_master.remove_tree() - unsharded_replica.remove_tree() - - -def setup_tablets(): - setup_sharded_keyspace() - setup_unsharded_keyspace() - utils.VtGate().start(tablets=[ - shard_0_master, shard_0_replica, - shard_1_master, shard_1_replica, - unsharded_master, unsharded_replica, - ]) - utils.vtgate.wait_for_endpoints( - '%s.%s.master' % (SHARDED_KEYSPACE, '80-'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.replica' % (SHARDED_KEYSPACE, '80-'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.master' % (SHARDED_KEYSPACE, '-80'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.replica' % (SHARDED_KEYSPACE, '-80'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.master' % (UNSHARDED_KEYSPACE, '0'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.replica' % (UNSHARDED_KEYSPACE, '0'), - 1) - - -def setup_sharded_keyspace(): - utils.run_vtctl(['CreateKeyspace', SHARDED_KEYSPACE]) - utils.run_vtctl(['SetKeyspaceShardingInfo', '-force', SHARDED_KEYSPACE, - 'keyspace_id', 'uint64']) - - shard_0_master.init_tablet( - 'replica', - keyspace=SHARDED_KEYSPACE, - shard='-80', - tablet_index=0) - shard_0_replica.init_tablet( - 'replica', - keyspace=SHARDED_KEYSPACE, - shard='-80', - tablet_index=1) - shard_1_master.init_tablet( - 'replica', - keyspace=SHARDED_KEYSPACE, - shard='80-', - tablet_index=0) - shard_1_replica.init_tablet( - 'replica', - keyspace=SHARDED_KEYSPACE, - shard='80-', - tablet_index=1) - - for t in [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica]: - t.create_db('vt_test_keyspace_sharded') - t.mquery(shard_0_master.dbname, create_vt_insert_test) - t.start_vttablet(wait_for_state=None) - - for t in [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', '%s/-80' % SHARDED_KEYSPACE, - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', '%s/80-' % SHARDED_KEYSPACE, - shard_1_master.tablet_alias], auto_log=True) - - for t in [shard_0_replica, shard_1_replica]: - utils.wait_for_tablet_type(t.tablet_alias, 'replica') - for t in [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica]: - t.wait_for_vttablet_state('SERVING') - - # rebuild to be sure we have the latest data - utils.run_vtctl( - ['RebuildKeyspaceGraph', SHARDED_KEYSPACE], auto_log=True) - utils.check_srv_keyspace('test_nj', SHARDED_KEYSPACE, - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n') - - -def setup_unsharded_keyspace(): - utils.run_vtctl(['CreateKeyspace', UNSHARDED_KEYSPACE]) - utils.run_vtctl(['SetKeyspaceShardingInfo', '-force', UNSHARDED_KEYSPACE, - 'keyspace_id', 'uint64']) - - unsharded_master.init_tablet( - 'replica', - keyspace=UNSHARDED_KEYSPACE, - shard='0', - tablet_index=0) - unsharded_replica.init_tablet( - 'replica', - keyspace=UNSHARDED_KEYSPACE, - shard='0', - tablet_index=1) - - for t in [unsharded_master, unsharded_replica]: - t.create_db('vt_test_keyspace_unsharded') - t.mquery(unsharded_master.dbname, create_vt_insert_test) - t.start_vttablet(wait_for_state=None) - - for t in [unsharded_master, unsharded_replica]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', '%s/0' % UNSHARDED_KEYSPACE, - unsharded_master.tablet_alias], auto_log=True) - - for t in [unsharded_replica]: - utils.wait_for_tablet_type(t.tablet_alias, 'replica') - for t in [unsharded_master, unsharded_replica]: - t.wait_for_vttablet_state('SERVING') - - # rebuild to be sure we have the right version - utils.run_vtctl(['RebuildKeyspaceGraph', UNSHARDED_KEYSPACE], auto_log=True) - utils.check_srv_keyspace('test_nj', UNSHARDED_KEYSPACE, - 'Partitions(master): -\n' - 'Partitions(rdonly): -\n' - 'Partitions(replica): -\n') - - -ALL_DB_TYPES = ['master', 'rdonly', 'replica'] - - -class TestKeyspace(unittest.TestCase): - - def _read_srv_keyspace(self, keyspace_name): - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - conn = vtgate_client.connect(protocol, addr, 30.0) - result = conn.get_srv_keyspace(keyspace_name) - conn.close() - return result - - def test_get_keyspace(self): - ki = utils.run_vtctl_json(['GetKeyspace', UNSHARDED_KEYSPACE]) - self.assertEqual('keyspace_id', ki['sharding_column_name']) - self.assertEqual(1, ki['sharding_column_type']) - - def test_delete_keyspace(self): - utils.run_vtctl(['CreateKeyspace', 'test_delete_keyspace']) - utils.run_vtctl(['CreateShard', 'test_delete_keyspace/0']) - utils.run_vtctl( - ['InitTablet', '-keyspace=test_delete_keyspace', '-shard=0', - 'test_nj-0000000100', 'master']) - - # Can't delete keyspace if there are shards present. - utils.run_vtctl( - ['DeleteKeyspace', 'test_delete_keyspace'], expect_fail=True) - # Can't delete shard if there are tablets present. - utils.run_vtctl(['DeleteShard', '-even_if_serving', - 'test_delete_keyspace/0'], expect_fail=True) - - # Use recursive DeleteShard to remove tablets. - utils.run_vtctl(['DeleteShard', '-even_if_serving', '-recursive', - 'test_delete_keyspace/0']) - # Now non-recursive DeleteKeyspace should work. - utils.run_vtctl(['DeleteKeyspace', 'test_delete_keyspace']) - - # Start over and this time use recursive DeleteKeyspace to do everything. - utils.run_vtctl(['CreateKeyspace', 'test_delete_keyspace']) - utils.run_vtctl(['CreateShard', 'test_delete_keyspace/0']) - utils.run_vtctl( - ['InitTablet', '-port=1234', '-keyspace=test_delete_keyspace', - '-shard=0', 'test_nj-0000000100', 'master']) - - # Create the serving/replication entries and check that they exist, - # so we can later check they're deleted. - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_delete_keyspace']) - utils.run_vtctl( - ['GetShardReplication', 'test_nj', 'test_delete_keyspace/0']) - utils.run_vtctl(['GetSrvKeyspace', 'test_nj', 'test_delete_keyspace']) - - # Recursive DeleteKeyspace - utils.run_vtctl(['DeleteKeyspace', '-recursive', 'test_delete_keyspace']) - - # Check that everything is gone. - utils.run_vtctl(['GetKeyspace', 'test_delete_keyspace'], expect_fail=True) - utils.run_vtctl(['GetShard', 'test_delete_keyspace/0'], expect_fail=True) - utils.run_vtctl(['GetTablet', 'test_nj-0000000100'], expect_fail=True) - utils.run_vtctl( - ['GetShardReplication', 'test_nj', 'test_delete_keyspace/0'], - expect_fail=True) - utils.run_vtctl( - ['GetSrvKeyspace', 'test_nj', 'test_delete_keyspace'], - expect_fail=True) - - def test_remove_keyspace_cell(self): - utils.run_vtctl(['CreateKeyspace', 'test_delete_keyspace']) - utils.run_vtctl(['CreateShard', 'test_delete_keyspace/0']) - utils.run_vtctl(['CreateShard', 'test_delete_keyspace/1']) - utils.run_vtctl( - ['InitTablet', '-port=1234', '-keyspace=test_delete_keyspace', - '-shard=0', 'test_ca-0000000100', 'master']) - utils.run_vtctl( - ['InitTablet', '-port=1234', '-keyspace=test_delete_keyspace', - '-shard=0', 'test_nj-0000000100', 'replica']) - - # Create the serving/replication entries and check that they exist, - # so we can later check they're deleted. - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_delete_keyspace']) - utils.run_vtctl( - ['GetShardReplication', 'test_nj', 'test_delete_keyspace/0']) - utils.run_vtctl(['GetSrvKeyspace', 'test_nj', 'test_delete_keyspace']) - utils.run_vtctl(['GetSrvKeyspace', 'test_ca', 'test_delete_keyspace']) - - # Just remove the shard from one cell (including tablets), - # but leaving the global records and other cells/shards alone. - utils.run_vtctl( - ['RemoveShardCell', '-recursive', 'test_delete_keyspace/0', 'test_nj']) - # Check that the shard is gone from test_nj. - srv_keyspace = utils.run_vtctl_json(['GetSrvKeyspace', 'test_nj', 'test_delete_keyspace']) - for partition in srv_keyspace['partitions']: - self.assertEqual(len(partition['shard_references']), 0, - 'RemoveShardCell should have removed one shard from the target cell: ' + - json.dumps(srv_keyspace)) - # Make sure the shard is still serving in test_ca. - srv_keyspace = utils.run_vtctl_json(['GetSrvKeyspace', 'test_ca', 'test_delete_keyspace']) - for partition in srv_keyspace['partitions']: - self.assertEqual(len(partition['shard_references']), 1, - 'RemoveShardCell should not have changed other cells: ' + - json.dumps(srv_keyspace)) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_delete_keyspace']) - - utils.run_vtctl(['GetKeyspace', 'test_delete_keyspace']) - utils.run_vtctl(['GetShard', 'test_delete_keyspace/0']) - utils.run_vtctl(['GetTablet', 'test_ca-0000000100']) - utils.run_vtctl(['GetTablet', 'test_nj-0000000100'], expect_fail=True) - utils.run_vtctl( - ['GetShardReplication', 'test_ca', 'test_delete_keyspace/0']) - utils.run_vtctl( - ['GetShardReplication', 'test_nj', 'test_delete_keyspace/0'], - expect_fail=True) - utils.run_vtctl(['GetSrvKeyspace', 'test_nj', 'test_delete_keyspace']) - - # Add it back to do another test. - utils.run_vtctl( - ['InitTablet', '-port=1234', '-keyspace=test_delete_keyspace', - '-shard=0', 'test_nj-0000000100', 'replica']) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_delete_keyspace']) - utils.run_vtctl( - ['GetShardReplication', 'test_nj', 'test_delete_keyspace/0']) - - # Now use RemoveKeyspaceCell to remove all shards. - utils.run_vtctl( - ['RemoveKeyspaceCell', '-recursive', 'test_delete_keyspace', - 'test_nj']) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_delete_keyspace']) - - utils.run_vtctl( - ['GetShardReplication', 'test_ca', 'test_delete_keyspace/0']) - utils.run_vtctl( - ['GetShardReplication', 'test_nj', 'test_delete_keyspace/0'], - expect_fail=True) - - # Clean up. - utils.run_vtctl(['DeleteKeyspace', '-recursive', 'test_delete_keyspace']) - - def test_shard_count(self): - sharded_ks = self._read_srv_keyspace(SHARDED_KEYSPACE) - for db_type in ALL_DB_TYPES: - self.assertEqual(sharded_ks.get_shard_count(db_type), 2) - unsharded_ks = self._read_srv_keyspace(UNSHARDED_KEYSPACE) - for db_type in ALL_DB_TYPES: - self.assertEqual(unsharded_ks.get_shard_count(db_type), 1) - - def test_shard_names(self): - sharded_ks = self._read_srv_keyspace(SHARDED_KEYSPACE) - for db_type in ALL_DB_TYPES: - self.assertEqual(sharded_ks.get_shard_names(db_type), ['-80', '80-']) - unsharded_ks = self._read_srv_keyspace(UNSHARDED_KEYSPACE) - for db_type in ALL_DB_TYPES: - self.assertEqual(unsharded_ks.get_shard_names(db_type), ['0']) - - def test_keyspace_id_to_shard_name(self): - # test all keyspace_id in a sharded keyspace go to the right shard - sharded_ks = self._read_srv_keyspace(SHARDED_KEYSPACE) - for sn in shard_names: - for keyspace_id in shard_kid_map[sn]: - self.assertEqual( - sharded_ks.keyspace_id_to_shard_name_for_db_type(keyspace_id, - 'master'), sn) - - # take all keyspace_ids, make sure for unsharded they stay on'0' - unsharded_ks = self._read_srv_keyspace(UNSHARDED_KEYSPACE) - for sn in shard_names: - for keyspace_id in shard_kid_map[sn]: - self.assertEqual( - unsharded_ks.keyspace_id_to_shard_name_for_db_type( - keyspace_id, 'master'), - '0') - - def test_get_srv_keyspace_names(self): - stdout, _ = utils.run_vtctl(['GetSrvKeyspaceNames', 'test_nj'], - trap_output=True) - self.assertEqual( - set(stdout.splitlines()), {SHARDED_KEYSPACE, UNSHARDED_KEYSPACE}) - - -if __name__ == '__main__': - utils.main() diff --git a/test/keyspace_util.py b/test/keyspace_util.py deleted file mode 100644 index d152937ca70..00000000000 --- a/test/keyspace_util.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This module allows you to bring up and tear down keyspaces. -""" - -import os - -import environment -import tablet -import utils - - -class TestEnv(object): - """Main class for this module.""" - - def __init__(self): - self.tablet_map = {} - - def launch( - self, keyspace, shards=None, - replica_count=1, rdonly_count=0, ddls=None, - twopc_coordinator_address=None): - """Launch test environment.""" - - if replica_count < 1: - raise Exception('replica_count=%d < 1; tests now use semi-sync' - ' and must have at least one replica' % replica_count) - self.tablets = [] - self.master_tablets = [] - utils.run_vtctl(['CreateKeyspace', keyspace]) - if not shards or shards[0] == '0': - shards = ['0'] - - # Create tablets and start mysqld. - procs = [] - for shard in shards: - procs.append(self._new_tablet(keyspace, shard, 'master', None)) - for i in xrange(replica_count): - procs.append(self._new_tablet(keyspace, shard, 'replica', i)) - for i in xrange(rdonly_count): - procs.append(self._new_tablet(keyspace, shard, 'rdonly', i)) - utils.wait_procs(procs) - - # init tablets. - for shard in shards: - tablet_index = 0 - self._init_tablet(keyspace, shard, 'master', None, tablet_index) - tablet_index += 1 - for i in xrange(replica_count): - self._init_tablet(keyspace, shard, 'replica', i, tablet_index) - tablet_index += 1 - for i in xrange(rdonly_count): - self._init_tablet(keyspace, shard, 'rdonly', i, tablet_index) - tablet_index += 1 - - # Start tablets. - for shard in shards: - self._start_tablet( - keyspace, shard, 'master', None, twopc_coordinator_address) - for i in xrange(replica_count): - self._start_tablet( - keyspace, shard, 'replica', i, twopc_coordinator_address) - for i in xrange(rdonly_count): - self._start_tablet( - keyspace, shard, 'rdonly', i, twopc_coordinator_address) - - for t in self.tablets: - t.wait_for_vttablet_state('NOT_SERVING') - - for t in self.master_tablets: - utils.run_vtctl(['InitShardMaster', '-force', keyspace+'/'+t.shard, - t.tablet_alias], auto_log=True) - t.tablet_type = 'master' - - for t in self.tablets: - t.wait_for_vttablet_state('SERVING') - - for ddl in ddls: - fname = os.path.join(environment.tmproot, 'ddl.sql') - with open(fname, 'w') as f: - f.write(ddl) - utils.run_vtctl(['ApplySchema', '-sql-file', fname, keyspace]) - - def teardown(self): - all_tablets = self.tablet_map.values() - tablet.kill_tablets(all_tablets) - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - for t in all_tablets: - t.remove_tree() - - def _new_tablet(self, keyspace, shard, tablet_type, index): - """Create a tablet and start mysqld.""" - t = tablet.Tablet() - self.tablets.append(t) - if tablet_type == 'master': - self.master_tablets.append(t) - key = '%s.%s.%s' % (keyspace, shard, tablet_type) - else: - key = '%s.%s.%s.%s' % (keyspace, shard, tablet_type, index) - self.tablet_map[key] = t - return t.init_mysql() - - def _init_tablet(self, keyspace, shard, tablet_type, index, tablet_index): - init_tablet_type = tablet_type - if tablet_type == 'master': - init_tablet_type = 'replica' - key = '%s.%s.%s' % (keyspace, shard, tablet_type) - else: - key = '%s.%s.%s.%s' % (keyspace, shard, tablet_type, index) - t = self.tablet_map[key] - t.init_tablet(init_tablet_type, keyspace, shard, tablet_index=tablet_index) - - def _start_tablet( - self, keyspace, shard, tablet_type, index, twopc_coordinator_address): - """Start a tablet.""" - init_tablet_type = tablet_type - if tablet_type == 'master': - init_tablet_type = 'replica' - key = '%s.%s.%s' % (keyspace, shard, tablet_type) - else: - key = '%s.%s.%s.%s' % (keyspace, shard, tablet_type, index) - t = self.tablet_map[key] - t.create_db('vt_' + keyspace) - extra_args = ['-queryserver-config-schema-reload-time', '1'] - if twopc_coordinator_address: - extra_args.extend([ - '-twopc_enable', - '-twopc_coordinator_address', twopc_coordinator_address, - '-twopc_abandon_age', '3600', - ]) - return t.start_vttablet( - wait_for_state=None, init_tablet_type=init_tablet_type, - init_keyspace=keyspace, init_shard=shard, - extra_args=extra_args) diff --git a/test/legacy_resharding.py b/test/legacy_resharding.py deleted file mode 100755 index 0bc84d27378..00000000000 --- a/test/legacy_resharding.py +++ /dev/null @@ -1,653 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# TODO(mberlin): Remove this file when SplitClone supports merge-sorting -# primary key columns based on the MySQL collation. - -"""This test covers the vtworker LegacySplitClone command. - -The vtworker LegacySplitClone should only be used when it is necessary to -reshard a table that has textual primary key columns (e.g. VARCHAR). -This is the case for the "timestamps" table in this end-to-end test. - -The reason why only LegacySplitClone supports this use case is because the new -resharding clone code (as of https://github.com/vitessio/vitess/pull/1796) -requires to sort rows by their primary key. Whereas LegacySplitClone does a -simple copy and always assumes that the tables on the destination are empty, -the SplitClone command can diff the source and destination tables. In case of -a horizontal resharding this requires merge-sorting multiple destination shards. -Since we currently do not support sorting VARCHAR primary key columns in -SplitClone (due to missing support for MySQL collations), you'll have to resort -to LegacySplitClone only for this use case. - -Note that this file was copied from the original resharding.py file. - -We start with shards -80 and 80-. We then split 80- into 80-c0 and c0-. - -This test is the main resharding test. It not only tests the regular resharding -workflow for an horizontal split, but also a lot of error cases and side -effects, like: -- migrating the traffic one cell at a time. -- migrating rdonly traffic back and forth. -- making sure we can't migrate the master until replica and rdonly are migrated. -- has a background thread to insert data during migration. -- tests a destination shard master failover while replication is running. -- tests a filtered replication source replacement while filtered replication - is running. -- tests 'vtctl SourceShardAdd' and 'vtctl SourceShardDelete'. -- makes sure the key range rules are properly enforced on masters. -""" - -import struct - -import logging -import unittest - -import base_sharding -import environment -import tablet -import utils - -from vtproto import topodata_pb2 -from vtdb import keyrange_constants - -keyspace_id_type = keyrange_constants.KIT_UINT64 -pack_keyspace_id = struct.Struct('!Q').pack - -# initial shards -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -shard_0_ny_rdonly = tablet.Tablet(cell='ny') -# range 80 - '' -shard_1_master = tablet.Tablet() -shard_1_slave1 = tablet.Tablet() -shard_1_slave2 = tablet.Tablet() -shard_1_ny_rdonly = tablet.Tablet(cell='ny') -shard_1_rdonly1 = tablet.Tablet() - -# split shards -# range 80 - c0 -shard_2_master = tablet.Tablet() -shard_2_replica1 = tablet.Tablet() -shard_2_replica2 = tablet.Tablet() -# range c0 - '' -shard_3_master = tablet.Tablet() -shard_3_replica = tablet.Tablet() -shard_3_rdonly1 = tablet.Tablet() - -all_tablets = [shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_1_master, shard_1_slave1, shard_1_slave2, - shard_1_ny_rdonly, shard_1_rdonly1, - shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica, shard_3_rdonly1] - - -def setUpModule(): - try: - environment.topo_server().setup() - setup_procs = [t.init_mysql() for t in all_tablets] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - - -class TestResharding(unittest.TestCase, base_sharding.BaseShardingTest): - - # create_schema will create the same schema on the keyspace - # then insert some values - def _create_schema(self): - if keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - # Note that the primary key columns are not defined first on purpose to test - # that a reordered column list is correctly used everywhere in vtworker. - create_table_template = '''create table %s( -msg varchar(64), -custom_ksid_col ''' + t + ''' not null, -id bigint not null, -parent_id bigint not null, -primary key (parent_id, id), -index by_msg (msg) -) Engine=InnoDB''' - create_view_template = ( - 'create view %s' - '(id, msg, custom_ksid_col) as select id, msg, custom_ksid_col ' - 'from %s') - create_timestamp_table = '''create table timestamps( -name varchar(64), -time_milli bigint(20) unsigned not null, -custom_ksid_col ''' + t + ''' not null, -primary key (name) -) Engine=InnoDB''' - create_unrelated_table = '''create table unrelated( -name varchar(64), -primary key (name) -) Engine=InnoDB''' - - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding1'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding2'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_view_template % ('view1', 'resharding1'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_timestamp_table, - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_unrelated_table, - 'test_keyspace'], - auto_log=True) - - def _insert_startup_values(self): - self._insert_value(shard_0_master, 'resharding1', 1, 'msg1', - 0x1000000000000000) - self._insert_value(shard_1_master, 'resharding1', 2, 'msg2', - 0x9000000000000000) - self._insert_value(shard_1_master, 'resharding1', 3, 'msg3', - 0xD000000000000000) - - def _check_startup_values(self): - # check first value is in the right shard - self._check_value(shard_2_master, 'resharding1', 2, 'msg2', - 0x9000000000000000) - self._check_value(shard_2_replica1, 'resharding1', 2, 'msg2', - 0x9000000000000000) - self._check_value(shard_2_replica2, 'resharding1', 2, 'msg2', - 0x9000000000000000) - self._check_value(shard_3_master, 'resharding1', 2, 'msg2', - 0x9000000000000000, should_be_here=False) - self._check_value(shard_3_replica, 'resharding1', 2, 'msg2', - 0x9000000000000000, should_be_here=False) - self._check_value(shard_3_rdonly1, 'resharding1', 2, 'msg2', - 0x9000000000000000, should_be_here=False) - - # check second value is in the right shard too - self._check_value(shard_2_master, 'resharding1', 3, 'msg3', - 0xD000000000000000, should_be_here=False) - self._check_value(shard_2_replica1, 'resharding1', 3, 'msg3', - 0xD000000000000000, should_be_here=False) - self._check_value(shard_2_replica2, 'resharding1', 3, 'msg3', - 0xD000000000000000, should_be_here=False) - self._check_value(shard_3_master, 'resharding1', 3, 'msg3', - 0xD000000000000000) - self._check_value(shard_3_replica, 'resharding1', 3, 'msg3', - 0xD000000000000000) - self._check_value(shard_3_rdonly1, 'resharding1', 3, 'msg3', - 0xD000000000000000) - - def _insert_lots(self, count, base=0): - for i in xrange(count): - self._insert_value(shard_1_master, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i) - self._insert_value(shard_1_master, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i) - - # _check_lots returns how many of the values we have, in percents. - def _check_lots(self, count, base=0): - found = 0 - for i in xrange(count): - if self._is_value_present_and_correct(shard_2_replica2, 'resharding1', - 10000 + base + i, 'msg-range1-%d' % - i, 0xA000000000000000 + base + i): - found += 1 - if self._is_value_present_and_correct(shard_3_replica, 'resharding1', - 20000 + base + i, 'msg-range2-%d' % - i, 0xE000000000000000 + base + i): - found += 1 - percent = found * 100 / count / 2 - logging.debug('I have %d%% of the data', percent) - return percent - - def _check_lots_timeout(self, count, threshold, timeout, base=0): - while True: - value = self._check_lots(count, base=base) - if value >= threshold: - return value - timeout = utils.wait_step('waiting for %d%% of the data' % threshold, - timeout, sleep_time=1) - - # _check_lots_not_present makes sure no data is in the wrong shard - def _check_lots_not_present(self, count, base=0): - for i in xrange(count): - self._check_value(shard_3_replica, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i, - should_be_here=False) - self._check_value(shard_2_replica2, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i, - should_be_here=False) - - def test_resharding(self): - utils.run_vtctl(['CreateKeyspace', - '--sharding_column_name', 'bad_column', - '--sharding_column_type', 'bytes', - 'test_keyspace']) - utils.run_vtctl(['SetKeyspaceShardingInfo', 'test_keyspace', - 'custom_ksid_col', 'uint64'], expect_fail=True) - utils.run_vtctl(['SetKeyspaceShardingInfo', '-force', - 'test_keyspace', 'custom_ksid_col', keyspace_id_type]) - - shard_0_master.init_tablet('replica', 'test_keyspace', '-80') - shard_0_replica.init_tablet('replica', 'test_keyspace', '-80') - shard_0_ny_rdonly.init_tablet('rdonly', 'test_keyspace', '-80') - shard_1_master.init_tablet('replica', 'test_keyspace', '80-') - shard_1_slave1.init_tablet('replica', 'test_keyspace', '80-') - shard_1_slave2.init_tablet('replica', 'test_keyspace', '80-') - shard_1_ny_rdonly.init_tablet('rdonly', 'test_keyspace', '80-') - shard_1_rdonly1.init_tablet('rdonly', 'test_keyspace', '80-') - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - ks = utils.run_vtctl_json(['GetSrvKeyspace', 'test_nj', 'test_keyspace']) - self.assertEqual(ks['sharding_column_name'], 'custom_ksid_col') - - # we set full_mycnf_args to True as a test in the KIT_BYTES case - full_mycnf_args = keyspace_id_type == keyrange_constants.KIT_BYTES - - # create databases so vttablet can start behaving somewhat normally - for t in [shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_1_master, shard_1_slave1, shard_1_slave2, shard_1_ny_rdonly, - shard_1_rdonly1]: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None, full_mycnf_args=full_mycnf_args, - binlog_use_v3_resharding_mode=False) - - # wait for the tablets (replication is not setup, they won't be healthy) - for t in [shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_1_master, shard_1_slave1, shard_1_slave2, shard_1_ny_rdonly, - shard_1_rdonly1]: - t.wait_for_vttablet_state('NOT_SERVING') - - # reparent to make the tablets work - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - # check the shards - shards = utils.run_vtctl_json(['FindAllShardsInKeyspace', 'test_keyspace']) - self.assertIn('-80', shards, 'unexpected shards: %s' % str(shards)) - self.assertIn('80-', shards, 'unexpected shards: %s' % str(shards)) - self.assertEqual(len(shards), 2, 'unexpected shards: %s' % str(shards)) - - # create the tables - self._create_schema() - self._insert_startup_values() - - # run a health check on source replicas so they respond to discovery - # (for binlog players) and on the source rdonlys (for workers) - for t in [shard_0_replica, shard_1_slave1]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - for t in [shard_0_ny_rdonly, shard_1_ny_rdonly, shard_1_rdonly1]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - # create the split shards - shard_2_master.init_tablet('replica', 'test_keyspace', '80-c0') - shard_2_replica1.init_tablet('replica', 'test_keyspace', '80-c0') - shard_2_replica2.init_tablet('replica', 'test_keyspace', '80-c0') - shard_3_master.init_tablet('replica', 'test_keyspace', 'c0-') - shard_3_replica.init_tablet('replica', 'test_keyspace', 'c0-') - shard_3_rdonly1.init_tablet('rdonly', 'test_keyspace', 'c0-') - - # start vttablet on the split shards (no db created, - # so they're all not serving) - shard_2_master.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - shard_3_master.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - for t in [shard_2_replica1, shard_2_replica2, - shard_3_replica, shard_3_rdonly1]: - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - for t in [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica, shard_3_rdonly1]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-c0', - shard_2_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/c0-', - shard_3_master.tablet_alias], auto_log=True) - - # check the shards - shards = utils.run_vtctl_json(['FindAllShardsInKeyspace', 'test_keyspace']) - for s in ['-80', '80-', '80-c0', 'c0-']: - self.assertIn(s, shards, 'unexpected shards: %s' % str(shards)) - self.assertEqual(len(shards), 4, 'unexpected shards: %s' % str(shards)) - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], - auto_log=True) - utils.check_srv_keyspace( - 'test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # disable shard_1_slave2, so we're sure filtered replication will go - # from shard_1_slave1 - utils.run_vtctl(['ChangeSlaveType', shard_1_slave2.tablet_alias, 'spare']) - shard_1_slave2.wait_for_vttablet_state('NOT_SERVING') - - # we need to create the schema, and the worker will do data copying - for keyspace_shard in ('test_keyspace/80-c0', 'test_keyspace/c0-'): - utils.run_vtctl(['CopySchemaShard', '--exclude_tables', 'unrelated', - shard_1_rdonly1.tablet_alias, keyspace_shard], - auto_log=True) - - # --max_tps is only specified to enable the throttler and ensure that the - # code is executed. But the intent here is not to throttle the test, hence - # the rate limit is set very high. - utils.run_vtworker(['--cell', 'test_nj', - '--command_display_interval', '10ms', - '--use_v3_resharding_mode=false', - 'LegacySplitClone', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - '--max_tps', '9999', - 'test_keyspace/80-'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly1.tablet_alias, - 'rdonly'], auto_log=True) - - # check the startup values are in the right place - self._check_startup_values() - - # check the schema too - utils.run_vtctl(['ValidateSchemaKeyspace', '--exclude_tables=unrelated', - 'test_keyspace'], auto_log=True) - - # check the binlog players are running and exporting vars - self.check_destination_master(shard_2_master, ['test_keyspace/80-']) - self.check_destination_master(shard_3_master, ['test_keyspace/80-']) - - # check that binlog server exported the stats vars - self.check_binlog_server_vars(shard_1_slave1, horizontal=True) - - # Check that the throttler was enabled. - # The stream id is hard-coded as 1, which is the first id generated - # through auto-inc. - self.check_throttler_service(shard_2_master.rpc_endpoint(), - ['BinlogPlayer/1'], 9999) - self.check_throttler_service(shard_3_master.rpc_endpoint(), - ['BinlogPlayer/1'], 9999) - - # testing filtered replication: insert a bunch of data on shard 1, - # check we get most of it after a few seconds, wait for binlog server - # timeout, check we get all of it. - logging.debug('Inserting lots of data on source shard') - self._insert_lots(1000) - logging.debug('Checking 80 percent of data is sent quickly') - v = self._check_lots_timeout(1000, 80, 5) - if v != 100: - # small optimization: only do this check if we don't have all the data - # already anyway. - logging.debug('Checking all data goes through eventually') - self._check_lots_timeout(1000, 100, 20) - logging.debug('Checking no data was sent the wrong way') - self._check_lots_not_present(1000) - self.check_binlog_player_vars(shard_2_master, ['test_keyspace/80-'], - seconds_behind_master_max=30) - self.check_binlog_player_vars(shard_3_master, ['test_keyspace/80-'], - seconds_behind_master_max=30) - self.check_binlog_server_vars(shard_1_slave1, horizontal=True, - min_statements=1000, min_transactions=1000) - - # use vtworker to compare the data (after health-checking the destination - # rdonly tablets so discovery works) - utils.run_vtctl(['RunHealthCheck', shard_3_rdonly1.tablet_alias]) - logging.debug('Running vtworker SplitDiff') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/c0-'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_3_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - - 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, 4000, 2000) - self.check_running_binlog_player(shard_3_master, 4000, 2000) - - # tests a failover switching serving to a different replica - utils.run_vtctl(['ChangeSlaveType', shard_1_slave2.tablet_alias, 'replica']) - utils.run_vtctl(['ChangeSlaveType', shard_1_slave1.tablet_alias, 'spare']) - shard_1_slave2.wait_for_vttablet_state('SERVING') - shard_1_slave1.wait_for_vttablet_state('NOT_SERVING') - utils.run_vtctl(['RunHealthCheck', shard_1_slave2.tablet_alias]) - - # test data goes through again - logging.debug('Inserting lots of data on source shard') - self._insert_lots(1000, base=1000) - logging.debug('Checking 80 percent of data was sent quickly') - self._check_lots_timeout(1000, 80, 5, base=1000) - self.check_binlog_server_vars(shard_1_slave2, horizontal=True, - min_statements=800, min_transactions=800) - - # check we can't migrate the master just yet - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'master'], - expect_fail=True) - - # check query service is off on master 2 and master 3, as filtered - # replication is enabled. Even health check that is enabled on - # master 3 should not interfere (we run it to be sure). - utils.run_vtctl(['RunHealthCheck', shard_3_master.tablet_alias], - auto_log=True) - for master in [shard_2_master, shard_3_master]: - utils.check_tablet_query_service(self, master, False, False) - stream_health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - master.tablet_alias]) - logging.debug('Got health: %s', str(stream_health)) - self.assertIn('realtime_stats', stream_health) - self.assertNotIn('serving', stream_health) - - # check the destination master 3 is healthy, even though its query - # service is not running (if not healthy this would exception out) - shard_3_master.get_healthz() - - # now serve rdonly from the split shards, in test_nj only - utils.run_vtctl(['MigrateServedTypes', '--cells=test_nj', - 'test_keyspace/80-', 'rdonly'], auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_srv_keyspace('test_ny', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_0_ny_rdonly, True, False) - utils.check_tablet_query_service(self, shard_1_ny_rdonly, True, False) - utils.check_tablet_query_service(self, shard_1_rdonly1, False, True) - - # now serve rdonly from the split shards, everywhere - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'rdonly'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_srv_keyspace('test_ny', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_0_ny_rdonly, True, False) - utils.check_tablet_query_service(self, shard_1_ny_rdonly, False, True) - utils.check_tablet_query_service(self, shard_1_rdonly1, False, True) - - # then serve replica from the split shards - destination_shards = ['80-c0', 'c0-'] - - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'replica'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-c0 c0-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_1_slave2, False, True) - - # move replica back and forth - utils.run_vtctl( - ['MigrateServedTypes', '-reverse', 'test_keyspace/80-', 'replica'], - auto_log=True) - # After a backwards migration, queryservice should be enabled on - # source and disabled on destinations - utils.check_tablet_query_service(self, shard_1_slave2, True, False) - # Destination tablets would have query service disabled for other - # reasons than the migration, so check the shard record instead of - # the tablets directly. - utils.check_shard_query_services(self, 'test_nj', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, False) - utils.check_shard_query_services(self, 'test_ny', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, False) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'replica'], - auto_log=True) - # After a forwards migration, queryservice should be disabled on - # source and enabled on destinations - utils.check_tablet_query_service(self, shard_1_slave2, False, True) - # Destination tablets would have query service disabled for other - # reasons than the migration, so check the shard record instead of - # the tablets directly - utils.check_shard_query_services(self, 'test_nj', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, True) - utils.check_shard_query_services(self, 'test_ny', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-c0 c0-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # use vtworker to compare the data again - logging.debug('Running vtworker SplitDiff') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/c0-'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_3_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - - # mock with the SourceShard records to test 'vtctl SourceShardDelete' - # and 'vtctl SourceShardAdd' - utils.run_vtctl(['SourceShardDelete', 'test_keyspace/c0-', '1'], - auto_log=True) - utils.run_vtctl(['SourceShardAdd', '--key_range=80-', - 'test_keyspace/c0-', '1', 'test_keyspace/80-'], - auto_log=True) - - # then serve master from the split shards, make sure the source master's - # query service is now turned off - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'master'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-c0 c0-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-c0 c0-\n', - keyspace_id_type=keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_1_master, False, True) - - # check the binlog players are gone now - self.check_no_binlog_player(shard_2_master) - self.check_no_binlog_player(shard_3_master) - - # delete the original tablets in the original shard - tablet.kill_tablets([shard_1_master, shard_1_slave1, shard_1_slave2, - shard_1_ny_rdonly, shard_1_rdonly1]) - for t in [shard_1_slave1, shard_1_slave2, shard_1_ny_rdonly, - shard_1_rdonly1]: - utils.run_vtctl(['DeleteTablet', t.tablet_alias], auto_log=True) - utils.run_vtctl(['DeleteTablet', '-allow_master', - shard_1_master.tablet_alias], auto_log=True) - - # rebuild the serving graph, all mentions of the old shards should be gone - utils.run_vtctl( - ['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # test RemoveShardCell - utils.run_vtctl( - ['RemoveShardCell', 'test_keyspace/-80', 'test_nj'], auto_log=True, - expect_fail=True) - utils.run_vtctl( - ['RemoveShardCell', 'test_keyspace/80-', 'test_nj'], auto_log=True) - utils.run_vtctl( - ['RemoveShardCell', 'test_keyspace/80-', 'test_ny'], auto_log=True) - shard = utils.run_vtctl_json(['GetShard', 'test_keyspace/80-']) - self.assertTrue('cells' not in shard or not shard['cells']) - - # delete the original shard - utils.run_vtctl(['DeleteShard', 'test_keyspace/80-'], auto_log=True) - - # kill everything - tablet.kill_tablets([shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica, shard_3_rdonly1]) - -if __name__ == '__main__': - utils.main() diff --git a/test/merge_sharding.py b/test/merge_sharding.py deleted file mode 100755 index f733ce30454..00000000000 --- a/test/merge_sharding.py +++ /dev/null @@ -1,465 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This test covers the workflow for a sharding merge. - -We start with 3 shards: -40, 40-80, and 80-. We then merge -40 and 40-80 -into -80. - -Note this test is just testing the full workflow, not corner cases or error -cases. These are mostly done by the other resharding tests. -""" - -import logging -import unittest - -from vtdb import keyrange_constants - -import base_sharding -import environment -import tablet -import utils - - -# initial shards -# shard -40 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -shard_0_rdonly = tablet.Tablet() -# shard 40-80 -shard_1_master = tablet.Tablet() -shard_1_replica = tablet.Tablet() -shard_1_rdonly = tablet.Tablet() -# shard 80- -shard_2_master = tablet.Tablet() -shard_2_replica = tablet.Tablet() -shard_2_rdonly = tablet.Tablet() - -# merged shard -80 -shard_dest_master = tablet.Tablet() -shard_dest_replica = tablet.Tablet() -shard_dest_rdonly = tablet.Tablet() - -all_tablets = [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly, - shard_2_master, shard_2_replica, shard_2_rdonly, - shard_dest_master, shard_dest_replica, shard_dest_rdonly] - - -def setUpModule(): - try: - environment.topo_server().setup() - setup_procs = [t.init_mysql() for t in all_tablets] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - - -class TestMergeSharding(unittest.TestCase, base_sharding.BaseShardingTest): - - # create_schema will create the same schema on the keyspace - # then insert some values - def _create_schema(self): - if base_sharding.keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - # Note that the primary key columns are not defined first on purpose to test - # that a reordered column list is correctly used everywhere in vtworker. - create_table_template = '''create table %s( -msg varchar(64), -custom_ksid_col ''' + t + ''' not null, -id bigint not null, -parent_id bigint not null, -primary key (parent_id, id), -index by_msg (msg) -) Engine=InnoDB''' - create_view_template = ( - 'create view %s' - '(id, msg, custom_ksid_col) as select id, msg, custom_ksid_col ' - 'from %s') - - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding1'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding2'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_view_template % ('view1', 'resharding1'), - 'test_keyspace'], - auto_log=True) - - def _insert_startup_values(self): - # row covered by shard -40 (should be merged). - self._insert_value(shard_0_master, 'resharding1', 1, 'msg1', - 0x1000000000000000) - # row covered by shard 40-80 (should be merged). - self._insert_value(shard_1_master, 'resharding1', 2, 'msg2', - 0x5000000000000000) - # row covered by shard 80- (must not be merged). - self._insert_value(shard_2_master, 'resharding1', 3, 'msg3', - 0xD000000000000000) - - def _check_startup_values(self): - # check first two values are in the right shard - self._check_value(shard_dest_master, 'resharding1', 1, 'msg1', - 0x1000000000000000) - self._check_value(shard_dest_replica, 'resharding1', 1, 'msg1', - 0x1000000000000000) - self._check_value(shard_dest_rdonly, 'resharding1', 1, 'msg1', - 0x1000000000000000) - - self._check_value(shard_dest_master, 'resharding1', 2, 'msg2', - 0x5000000000000000) - self._check_value(shard_dest_replica, 'resharding1', 2, 'msg2', - 0x5000000000000000) - self._check_value(shard_dest_rdonly, 'resharding1', 2, 'msg2', - 0x5000000000000000) - - def _insert_lots(self, count, base=0): - if count > 10000: - self.assertFail('bad count passed in, only support up to 10000') - for i in xrange(count): - self._insert_value(shard_0_master, 'resharding1', 1000000 + base + i, - 'msg-range0-%d' % i, 0x2000000000000000 + base + i) - self._insert_value(shard_1_master, 'resharding1', 1010000 + base + i, - 'msg-range1-%d' % i, 0x6000000000000000 + base + i) - - # _check_lots returns how many of the values we have, in percents. - def _check_lots(self, count, base=0): - found = 0 - for i in xrange(count): - if self._is_value_present_and_correct(shard_dest_replica, 'resharding1', - 1000000 + base + i, - 'msg-range0-%d' % i, - 0x2000000000000000 + base + i): - found += 1 - if self._is_value_present_and_correct(shard_dest_replica, 'resharding1', - 1010000 + base + i, - 'msg-range1-%d' % i, - 0x6000000000000000 + base + i): - found += 1 - percent = found * 100 / count / 2 - logging.debug('I have %d%% of the data', percent) - return percent - - def _check_lots_timeout(self, count, threshold, timeout, base=0): - while True: - value = self._check_lots(count, base=base) - if value >= threshold: - return value - timeout = utils.wait_step('waiting for %d%% of the data' % threshold, - timeout, sleep_time=1) - - def test_merge_sharding(self): - utils.run_vtctl(['CreateKeyspace', - '--sharding_column_name', 'custom_ksid_col', - '--sharding_column_type', base_sharding.keyspace_id_type, - 'test_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '-40') - shard_0_replica.init_tablet('replica', 'test_keyspace', '-40') - shard_0_rdonly.init_tablet('rdonly', 'test_keyspace', '-40') - shard_1_master.init_tablet('replica', 'test_keyspace', '40-80') - shard_1_replica.init_tablet('replica', 'test_keyspace', '40-80') - shard_1_rdonly.init_tablet('rdonly', 'test_keyspace', '40-80') - shard_2_master.init_tablet('replica', 'test_keyspace', '80-') - shard_2_replica.init_tablet('replica', 'test_keyspace', '80-') - shard_2_rdonly.init_tablet('rdonly', 'test_keyspace', '80-') - - # rebuild and check SrvKeyspace - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - ks = utils.run_vtctl_json(['GetSrvKeyspace', 'test_nj', 'test_keyspace']) - self.assertEqual(ks['sharding_column_name'], 'custom_ksid_col') - - # create databases so vttablet can start behaving normally - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly, - shard_2_master, shard_2_replica, shard_2_rdonly]: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - - # won't be serving, no replication state - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly, - shard_2_master, shard_2_replica, shard_2_rdonly]: - t.wait_for_vttablet_state('NOT_SERVING') - - # reparent to make the tablets work - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-40', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/40-80', - shard_1_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_2_master.tablet_alias], auto_log=True) - - # create the tables - self._create_schema() - self._insert_startup_values() - - # run a health check on source replicas so they respond to discovery - # (for binlog players) and on the source rdonlys (for workers) - for t in [shard_0_replica, shard_1_replica]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - for t in [shard_0_rdonly, shard_1_rdonly]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - # create the merge shards - shard_dest_master.init_tablet('replica', 'test_keyspace', '-80') - shard_dest_replica.init_tablet('replica', 'test_keyspace', '-80') - shard_dest_rdonly.init_tablet('rdonly', 'test_keyspace', '-80') - - # start vttablet on the destination shard (no db created, - # so they're all not serving) - for t in [shard_dest_master, shard_dest_replica, shard_dest_rdonly]: - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - for t in [shard_dest_master, shard_dest_replica, shard_dest_rdonly]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_dest_master.tablet_alias], auto_log=True) - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], - auto_log=True) - utils.check_srv_keyspace( - 'test_nj', 'test_keyspace', - 'Partitions(master): -40 40-80 80-\n' - 'Partitions(rdonly): -40 40-80 80-\n' - 'Partitions(replica): -40 40-80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # copy the schema - utils.run_vtctl(['CopySchemaShard', shard_0_rdonly.tablet_alias, - 'test_keyspace/-80'], auto_log=True) - - # copy the data (will also start filtered replication), reset source - # Run vtworker as daemon for the following SplitClone commands. - worker_proc, worker_port, worker_rpc_port = utils.run_vtworker_bg( - ['--cell', 'test_nj', '--command_display_interval', '10ms', - '--use_v3_resharding_mode=false'], - auto_log=True) - - # Initial clone (online). - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--offline=false', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/-80'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 2, 0, 0, 0) - - # Reset vtworker such that we can run the next command. - workerclient_proc = utils.run_vtworker_client_bg(['Reset'], worker_rpc_port) - utils.wait_procs([workerclient_proc]) - - # Modify the destination shard. SplitClone will revert the changes. - # Delete row 1 (provokes an insert). - shard_dest_master.mquery('vt_test_keyspace', - 'delete from resharding1 where id=1', write=True) - # Update row 2 (provokes an update). - shard_dest_master.mquery( - 'vt_test_keyspace', "update resharding1 set msg='msg-not-2' where id=2", - write=True) - # Insert row 0 (provokes a delete). - self._insert_value(shard_dest_master, 'resharding1', 0, 'msg0', - 0x5000000000000000) - - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/-80'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - # Change tablets, which were taken offline, back to rdonly. - utils.run_vtctl(['ChangeSlaveType', shard_0_rdonly.tablet_alias, - 'rdonly'], auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly.tablet_alias, - 'rdonly'], auto_log=True) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 1, 1, 1, 0) - self.verify_reconciliation_counters(worker_port, 'Offline', 'resharding1', - 0, 0, 0, 2) - # Terminate worker daemon because it is no longer needed. - utils.kill_sub_process(worker_proc, soft=True) - - # check the startup values are in the right place - self._check_startup_values() - - # check the schema too - utils.run_vtctl(['ValidateSchemaKeyspace', 'test_keyspace'], auto_log=True) - - # check binlog player variables - self.check_destination_master(shard_dest_master, - ['test_keyspace/-40', 'test_keyspace/40-80']) - - # check that binlog server exported the stats vars - self.check_binlog_server_vars(shard_0_replica, horizontal=True) - self.check_binlog_server_vars(shard_1_replica, horizontal=True) - - # testing filtered replication: insert a bunch of data on shard 0 and 1, - # check we get most of it after a few seconds, wait for binlog server - # timeout, check we get all of it. - logging.debug('Inserting lots of data on source shards') - self._insert_lots(1000) - logging.debug('Checking 80 percent of data is sent quickly') - v = self._check_lots_timeout(1000, 80, 10) - if v != 100: - # small optimization: only do this check if we don't have all the data - # already anyway. - logging.debug('Checking all data goes through eventually') - self._check_lots_timeout(1000, 100, 30) - self.check_binlog_player_vars(shard_dest_master, - ['test_keyspace/-40', 'test_keyspace/40-80'], - seconds_behind_master_max=30) - self.check_binlog_server_vars(shard_0_replica, horizontal=True, - min_statements=1000, min_transactions=1000) - self.check_binlog_server_vars(shard_1_replica, horizontal=True, - min_statements=1000, min_transactions=1000) - - # use vtworker to compare the data (after health-checking the destination - # rdonly tablets so discovery works) - utils.run_vtctl(['RunHealthCheck', shard_dest_rdonly.tablet_alias]) - logging.debug('Running vtworker SplitDiff on first half') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - '--source_uid', '1', - 'test_keyspace/-80'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_0_rdonly.tablet_alias, 'rdonly'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_dest_rdonly.tablet_alias, - 'rdonly'], auto_log=True) - logging.debug('Running vtworker SplitDiff on second half') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - '--source_uid', '2', - 'test_keyspace/-80'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly.tablet_alias, 'rdonly'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_dest_rdonly.tablet_alias, - 'rdonly'], auto_log=True) - - # get status for the destination master tablet, make sure we have it all - self.check_running_binlog_player(shard_dest_master, 3000, 1000) - - # check destination master query service is not running - utils.check_tablet_query_service(self, shard_dest_master, False, False) - stream_health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - shard_dest_master.tablet_alias]) - logging.debug('Got health: %s', str(stream_health)) - self.assertIn('realtime_stats', stream_health) - self.assertNotIn('serving', stream_health) - - # check the destination master 3 is healthy, even though its query - # service is not running (if not healthy this would exception out) - shard_dest_master.get_healthz() - - # now serve rdonly from the split shards - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/-80', 'rdonly'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -40 40-80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -40 40-80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # now serve replica from the split shards - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/-80', 'replica'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -40 40-80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # now serve master from the split shards - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/-80', 'master'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_0_master, False, True) - utils.check_tablet_query_service(self, shard_1_master, False, True) - - # check the binlog players are gone now - self.check_no_binlog_player(shard_dest_master) - - # kill the original tablets in the original shards - tablet.kill_tablets([shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]) - for t in [shard_0_replica, shard_0_rdonly, - shard_1_replica, shard_1_rdonly]: - utils.run_vtctl(['DeleteTablet', t.tablet_alias], auto_log=True) - for t in [shard_0_master, shard_1_master]: - utils.run_vtctl(['DeleteTablet', '-allow_master', t.tablet_alias], - auto_log=True) - - # delete the original shards - utils.run_vtctl(['DeleteShard', 'test_keyspace/-40'], auto_log=True) - utils.run_vtctl(['DeleteShard', 'test_keyspace/40-80'], auto_log=True) - - # rebuild the serving graph, all mentions of the old shards should be gone - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # kill everything else - tablet.kill_tablets([shard_2_master, shard_2_replica, shard_2_rdonly, - shard_dest_master, shard_dest_replica, - shard_dest_rdonly]) - - -if __name__ == '__main__': - utils.main() diff --git a/test/merge_sharding_bytes.py b/test/merge_sharding_bytes.py deleted file mode 100755 index 6fc6dc9e99e..00000000000 --- a/test/merge_sharding_bytes.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Re-runs merge_sharding.py with a varbinary keyspace_id.""" - -from vtdb import keyrange_constants - -import base_sharding -import merge_sharding -import utils - - -if __name__ == '__main__': - base_sharding.keyspace_id_type = keyrange_constants.KIT_BYTES - utils.main(merge_sharding) diff --git a/test/messaging.py b/test/messaging.py deleted file mode 100755 index 31d46faed5c..00000000000 --- a/test/messaging.py +++ /dev/null @@ -1,315 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import logging -import time -import unittest - -import environment -import keyspace_util -import utils - -from vtproto import query_pb2 - -from vtdb import vtgate_client - - -shard_0_master = None -shard_0_replica = None -shard_1_master = None -lookup_master = None - -keyspace_env = None - -create_sharded_message = '''create table sharded_message( -time_scheduled bigint, -id bigint, -time_next bigint, -epoch bigint, -time_created bigint, -time_acked bigint, -message varchar(128), -primary key(time_scheduled, id), -unique index id_idx(id), -index next_idx(time_next, epoch) -) comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1' -''' - -create_unsharded_message = '''create table unsharded_message( -time_scheduled bigint, -id bigint, -time_next bigint, -epoch bigint, -time_created bigint, -time_acked bigint, -message varchar(128), -primary key(time_scheduled, id), -unique index id_idx(id), -index next_idx(time_next, epoch) -) comment 'vitess_message,vt_ack_wait=1,vt_purge_after=3,vt_batch_size=2,vt_cache_size=10,vt_poller_interval=1' -''' - -vschema = { - 'user': '''{ - "sharded": true, - "vindexes": { - "hash_index": { - "type": "hash" - } - }, - "tables": { - "sharded_message": { - "column_vindexes": [ - { - "column": "id", - "name": "hash_index" - } - ] - } - } - }''', - 'lookup': '''{ - "sharded": false, - "tables": { - "unsharded_message": { - "type": "sequence" - } - } - }''', -} - - -def setUpModule(): - global keyspace_env - global shard_0_master - global shard_0_replica - global shard_1_master - global lookup_master - logging.debug('in setUpModule') - - try: - environment.topo_server().setup() - logging.debug('Setting up tablets') - keyspace_env = keyspace_util.TestEnv() - keyspace_env.launch( - 'user', - shards=['-80', '80-'], - ddls=[ - create_sharded_message, - ], - ) - keyspace_env.launch( - 'lookup', - ddls=[ - create_unsharded_message, - ], - ) - shard_0_master = keyspace_env.tablet_map['user.-80.master'] - shard_0_replica = keyspace_env.tablet_map['user.-80.replica.0'] - shard_1_master = keyspace_env.tablet_map['user.80-.master'] - lookup_master = keyspace_env.tablet_map['lookup.0.master'] - - utils.apply_vschema(vschema) - utils.VtGate().start( - tablets=[shard_0_master, shard_0_replica, shard_1_master, lookup_master]) - utils.vtgate.wait_for_endpoints('user.-80.master', 1) - utils.vtgate.wait_for_endpoints('user.-80.replica', 1) - utils.vtgate.wait_for_endpoints('user.80-.master', 1) - utils.vtgate.wait_for_endpoints('lookup.0.master', 1) - except: - tearDownModule() - raise - - -def tearDownModule(): - logging.debug('in tearDownModule') - utils.required_teardown() - if utils.options.skip_teardown: - return - logging.debug('Tearing down the servers and setup') - if keyspace_env: - keyspace_env.teardown() - - environment.topo_server().teardown() - - utils.kill_sub_processes() - utils.remove_tmp_files() - - -def get_connection(timeout=15.0): - protocol, endpoint = utils.vtgate.rpc_endpoint(python=True) - try: - return vtgate_client.connect(protocol, endpoint, timeout) - except Exception: - logging.exception('Connection to vtgate (timeout=%s) failed.', timeout) - raise - - -def get_client_count(tablet): - debugvars = utils.get_vars(tablet.port) - return debugvars['Messages'].get('sharded_message.ClientCount', 0) - - -class TestMessaging(unittest.TestCase): - - def test_sharded(self): - self._test_messaging('sharded_message', 'user') - - def test_unsharded(self): - self._test_messaging('unsharded_message', 'lookup') - - def _test_messaging(self, name, keyspace): - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=None, writable=True) - - query = 'insert into %s(id, message) values(:id, :message)'%(name) - cursor.begin() - # If sharded, these will go to two different shards. - # If unsharded, they'll both be sent as a batch, but there's - # no way to verify this because the vtgate_conn API abstracts - # that part out and issues only one row at a time. However, - # there are unit tests for that part. - cursor.execute(query, {'id': 1, 'message': 'hello world 1'}) - cursor.execute(query, {'id': 4, 'message': 'hello world 4'}) - cursor.commit() - - (it, fields) = vtgate_conn.message_stream( - keyspace, name) - self.assertEqual( - fields, - [ - ('id', query_pb2.INT64), - ('time_scheduled', query_pb2.INT64), - ('message', query_pb2.VARCHAR), - ]) - - # We should get both messages. - result = {} - for _ in xrange(2): - row = it.next() - result[row[0]] = row[2] - self.assertEqual(result, {1: 'hello world 1', 4: 'hello world 4'}) - - # After ack, we should get only one message. - count = vtgate_conn.message_ack(name, [4]) - self.assertEqual(count, 1) - result = {} - for _ in xrange(2): - row = it.next() - result[row[0]] = row[2] - self.assertEqual(result, {1: 'hello world 1'}) - # Only one should be acked. - count = vtgate_conn.message_ack(name, [1, 4]) - self.assertEqual(count, 1) - it.close() - - def test_connections(self): - name = 'sharded_message' - keyspace = 'user' - - self.assertEqual(get_client_count(shard_0_master), 0) - self.assertEqual(get_client_count(shard_1_master), 0) - - vtgate_conn1 = get_connection() - (it1, fields1) = vtgate_conn1.message_stream(keyspace, name) - self.assertEqual(get_client_count(shard_0_master), 1) - self.assertEqual(get_client_count(shard_1_master), 1) - - vtgate_conn2 = get_connection() - (it2, fields2) = vtgate_conn2.message_stream(keyspace, name) - self.assertEqual(get_client_count(shard_0_master), 2) - self.assertEqual(get_client_count(shard_1_master), 2) - - cursor = vtgate_conn1.cursor( - tablet_type='master', keyspace=None, writable=True) - query = 'insert into %s(id, message) values(:id, :message)'%(name) - cursor.begin() - cursor.execute(query, {'id': 2, 'message': 'hello world 2'}) - cursor.execute(query, {'id': 5, 'message': 'hello world 5'}) - cursor.commit() - - # Each connection should get one message. - it1.next() - it2.next() - - # Ack the messages. - count = vtgate_conn1.message_ack(name, [2, 5]) - - # After closing one stream, ensure vttablets have dropped it. - it1.close() - time.sleep(1) - self.assertEqual(get_client_count(shard_0_master), 1) - self.assertEqual(get_client_count(shard_1_master), 1) - - it2.close() - - def test_reparent(self): - name = 'sharded_message' - keyspace = 'user' - - # Start a stream. Use a timeout that's greater than how long - # the test will take to run. - vtgate_conn = get_connection(120) - (it, fields) = vtgate_conn.message_stream(keyspace, name) - self.assertEqual(get_client_count(shard_0_master), 1) - self.assertEqual(get_client_count(shard_0_replica), 0) - self.assertEqual(get_client_count(shard_1_master), 1) - - # Perform a graceful reparent to the replica. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'user/-80', - '-new_master', shard_0_replica.tablet_alias], auto_log=True) - utils.validate_topology() - - # Verify connection has migrated. - # The wait must be at least 6s which is how long vtgate will - # wait before retrying: that is 30s/5 where 30s is the default - # message_stream_grace_period. - time.sleep(10) - self.assertEqual(get_client_count(shard_0_master), 0) - self.assertEqual(get_client_count(shard_0_replica), 1) - self.assertEqual(get_client_count(shard_1_master), 1) - - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=None, writable=True) - query = 'insert into %s(id, message) values(:id, :message)'%(name) - cursor.begin() - cursor.execute(query, {'id': 3, 'message': 'hello world 3'}) - cursor.commit() - - # Receive the message. - it.next() - - # Reparent back to old master. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'user/-80', - '-new_master', shard_0_master.tablet_alias], auto_log=True) - utils.validate_topology() - - time.sleep(10) - self.assertEqual(get_client_count(shard_0_master), 1) - self.assertEqual(get_client_count(shard_0_replica), 0) - self.assertEqual(get_client_count(shard_1_master), 1) - - # Ack the message. - count = vtgate_conn.message_ack(name, [3]) - - -if __name__ == '__main__': - utils.main() diff --git a/test/mysql_flavor.py b/test/mysql_flavor.py deleted file mode 100644 index 8102ad0cfd1..00000000000 --- a/test/mysql_flavor.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Define abstractions for various MySQL flavors.""" - -import environment -import logging -import os -import subprocess - - -class MysqlFlavor(object): - """Base class with default SQL statements.""" - - def demote_master_commands(self): - """Returns commands to stop the current master.""" - return [ - "SET GLOBAL read_only = ON", - "FLUSH TABLES WITH READ LOCK", - "UNLOCK TABLES", - ] - - def promote_slave_commands(self): - """Returns commands to convert a slave to a master.""" - return [ - "STOP SLAVE", - "RESET SLAVE ALL", - "SET GLOBAL read_only = OFF", - ] - - def reset_replication_commands(self): - """Returns commands to reset replication settings.""" - return [ - "STOP SLAVE", - "RESET SLAVE ALL", - "RESET MASTER", - ] - - def change_master_commands(self, host, port, pos): - raise NotImplementedError() - - def set_semi_sync_enabled_commands(self, master=None, slave=None): - """Returns commands to turn semi-sync on/off.""" - cmds = [] - if master is not None: - cmds.append("SET GLOBAL rpl_semi_sync_master_enabled = %d" % master) - if slave is not None: - cmds.append("SET GLOBAL rpl_semi_sync_slave_enabled = %d" % slave) - return cmds - - def extra_my_cnf(self): - """Returns the path to an extra my_cnf file, or None.""" - return None - - def master_position(self, tablet): - """Returns the position from SHOW MASTER STATUS as a string.""" - raise NotImplementedError() - - def position_equal(self, a, b): - """Returns true if position 'a' is equal to 'b'.""" - raise NotImplementedError() - - def position_at_least(self, a, b): - """Returns true if position 'a' is at least as far along as 'b'.""" - raise NotImplementedError() - - def position_after(self, a, b): - """Returns true if position 'a' is after 'b'.""" - return self.position_at_least(a, b) and not self.position_equal(a, b) - - def enable_binlog_checksum(self, tablet): - """Enables binlog_checksum and returns True if the flavor supports it. - - Args: - tablet: A tablet.Tablet object. - - Returns: - False if the flavor doesn't support binlog_checksum. - """ - tablet.mquery("", "SET @@global.binlog_checksum=1") - return True - - def disable_binlog_checksum(self, tablet): - """Disables binlog_checksum if the flavor supports it.""" - tablet.mquery("", "SET @@global.binlog_checksum=0") - - def change_passwords(self, password_col): - """set real passwords for all users""" - return ''' -# Set real passwords for all users. -UPDATE mysql.user SET %s = PASSWORD('RootPass') - WHERE User = 'root' AND Host = 'localhost'; -UPDATE mysql.user SET %s = PASSWORD('VtDbaPass') - WHERE User = 'vt_dba' AND Host = 'localhost'; -UPDATE mysql.user SET %s = PASSWORD('VtAppPass') - WHERE User = 'vt_app' AND Host = 'localhost'; -UPDATE mysql.user SET %s = PASSWORD('VtAllprivsPass') - WHERE User = 'vt_allprivs' AND Host = 'localhost'; -UPDATE mysql.user SET %s = PASSWORD('VtReplPass') - WHERE User = 'vt_repl' AND Host = '%%'; -UPDATE mysql.user SET %s = PASSWORD('VtFilteredPass') - WHERE User = 'vt_filtered' AND Host = 'localhost'; -FLUSH PRIVILEGES; -''' % tuple([password_col] * 6) - -class MariaDB(MysqlFlavor): - """Overrides specific to MariaDB.""" - - def reset_replication_commands(self): - return [ - "STOP SLAVE", - "RESET SLAVE ALL", - "RESET MASTER", - "SET GLOBAL gtid_slave_pos = ''", - ] - - def extra_my_cnf(self): - return "" - - def master_position(self, tablet): - gtid = tablet.mquery("", "SELECT @@GLOBAL.gtid_binlog_pos")[0][0] - return "MariaDB/" + gtid - - def position_equal(self, a, b): - return a == b - - def position_at_least(self, a, b): - # positions are MariaDB/A-B-C and we only compare C - return int(a.split("-")[2]) >= int(b.split("-")[2]) - - def change_master_commands(self, host, port, pos): - gtid = pos.split("/")[1] - return [ - "SET GLOBAL gtid_slave_pos = '%s'" % gtid, - "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, " - "MASTER_USER='vt_repl', MASTER_USE_GTID = slave_pos" % - (host, port)] - - -class MariaDB103(MariaDB): - """Overrides specific to MariaDB 10.3+.""" - - def extra_my_cnf(self): - return "" - -class MySQL56(MysqlFlavor): - """Overrides specific to MySQL 5.6/5.7""" - - def master_position(self, tablet): - gtid = tablet.mquery("", "SELECT @@GLOBAL.gtid_executed")[0][0] - return "MySQL56/" + gtid - - def position_equal(self, a, b): - return subprocess.check_output([ - "mysqlctl", "position", "equal", a, b, - ]).strip() == "true" - - def position_at_least(self, a, b): - return subprocess.check_output([ - "mysqlctl", "position", "at_least", a, b, - ]).strip() == "true" - - def extra_my_cnf(self): - return "" - - def change_master_commands(self, host, port, pos): - gtid = pos.split("/")[1] - return [ - "RESET MASTER", - "SET GLOBAL gtid_purged = '%s'" % gtid, - "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, " - "MASTER_USER='vt_repl', MASTER_AUTO_POSITION = 1" % - (host, port)] - -class MySQL80(MySQL56): - """Overrides specific to MySQL 8.0.""" - def extra_my_cnf(self): - return "" - def change_passwords(self, password_col): - """set real passwords for all users""" - return ''' -# Set real passwords for all users. -ALTER USER 'root'@'localhost' IDENTIFIED BY 'RootPass'; -ALTER USER 'vt_dba'@'localhost' IDENTIFIED BY 'VtDbaPass'; -ALTER USER 'vt_app'@'localhost' IDENTIFIED BY 'VtAppPass'; -ALTER USER 'vt_allprivs'@'localhost' IDENTIFIED BY 'VtAllPrivsPass'; -ALTER USER 'vt_repl'@'%' IDENTIFIED BY 'VtReplPass'; -ALTER USER 'vt_filtered'@'localhost' IDENTIFIED BY 'VtFilteredPass'; -FLUSH PRIVILEGES; -''' - - -# Map of registered MysqlFlavor classes (keyed by an identifier). -flavor_map = {} - -MYSQL_FLAVOR = None - - -# mysql_flavor is a function because we need something to import before the -# variable MYSQL_FLAVOR is initialized, since that doesn't happen until after -# the command-line options are parsed. If we make mysql_flavor a variable and -# import it before it's initialized, the module that imported it won't get the -# updated value when it's later initialized. -def mysql_flavor(): - return MYSQL_FLAVOR - - -def set_mysql_flavor(flavor): - """Set the object that will be returned by mysql_flavor(). - - If flavor is not specified, set it based on MYSQL_FLAVOR environment variable. - - Args: - flavor: String of the MySQL flavor e.g. "MariaDB" or "MySQL56". - """ - - global MYSQL_FLAVOR - - if not flavor: - flavor = os.environ.get("MYSQL_FLAVOR", "MySQL56") - # The environment variable might be set, but equal to "". - if not flavor: - flavor = "MySQL56" - - v = flavor_map.get(flavor, None) - if not v: - logging.error("Unknown MYSQL_FLAVOR '%s'", flavor) - exit(1) - - cls = v["cls"] - env = v["env"] - MYSQL_FLAVOR = cls() - # Set the environment variable explicitly in case we're overriding it via - # command-line flag. - os.environ["MYSQL_FLAVOR"] = env - - logging.debug("Using MySQL flavor: %s, setting MYSQL_FLAVOR=%s (%s)", - str(flavor), env, cls) - - -def register_flavor(flavor, cls, env): - """Register the available MySQL flavors. - - Note: We need the 'env' argument because our internal implementation is - similar to 'MariaDB' (and hence requires MYSQL_FLAVOR=MariaDB) but has its own - flavor class. - - Args: - flavor: Name of the flavor (must be passed to test flag --mysql-flavor). - cls: Class which inherits MysqlFlavor and provides the implementation. - env: Value which will be used for the environment variable MYSQL_FLAVOR. - """ - if flavor in flavor_map: - old_cls = flavor_map[flavor]["cls"] - old_env = flavor_map[flavor]["env"] - logging.error("Cannot register MySQL flavor %s because class %s (env: %s)" - " is already registered for it.", flavor, old_cls, old_env) - exit(1) - - flavor_map[flavor] = {"cls": cls, "env": env} - -register_flavor("MariaDB", MariaDB, "MariaDB") -register_flavor("MariaDB103", MariaDB103, "MariaDB103") -register_flavor("MySQL56", MySQL56, "MySQL56") -register_flavor("MySQL80", MySQL80, "MySQL80") diff --git a/test/mysql_server_test.py b/test/mysql_server_test.py deleted file mode 100755 index 729a519bde2..00000000000 --- a/test/mysql_server_test.py +++ /dev/null @@ -1,304 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Ensures the vtgate MySQL server protocol plugin works as expected. - -We use table ACLs to verify the user name authenticated by the connector is -set properly. -""" - - -import socket -import unittest - -import MySQLdb - -import environment -import utils -import tablet -import warnings - -# single shard / 2 tablets -shard_0_master = tablet.Tablet() -shard_0_slave = tablet.Tablet() - -table_acl_config = environment.tmproot + '/table_acl_config.json' -mysql_auth_server_static = (environment.tmproot + - '/mysql_auth_server_static.json') - - -def setUpModule(): - try: - environment.topo_server().setup() - - # setup all processes - setup_procs = [ - shard_0_master.init_mysql(), - shard_0_slave.init_mysql(), - ] - utils.wait_procs(setup_procs) - - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '0') - shard_0_slave.init_tablet('replica', 'test_keyspace', '0') - - # create databases so vttablet can start behaving normally - shard_0_master.create_db('vt_test_keyspace') - shard_0_slave.create_db('vt_test_keyspace') - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - shard_0_master.kill_vttablet() - shard_0_slave.kill_vttablet() - - teardown_procs = [ - shard_0_master.teardown_mysql(), - shard_0_slave.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_0_master.remove_tree() - shard_0_slave.remove_tree() - - -create_vt_insert_test = '''create table vt_insert_test ( -id bigint auto_increment, -msg varchar(64), -keyspace_id bigint(20) unsigned NOT NULL, -data longblob, -primary key (id) -) Engine=InnoDB''' - - -class TestMySQL(unittest.TestCase): - """This test makes sure the MySQL server connector is correct. - """ - - MYSQL_OPTION_MULTI_STATEMENTS_ON = 0 - MYSQL_OPTION_MULTI_STATEMENTS_OFF = 1 - - def test_mysql_connector(self): - with open(table_acl_config, 'w') as fd: - fd.write("""{ - "table_groups": [ - { - "table_names_or_prefixes": ["vt_insert_test", "dual"], - "readers": ["vtgate client 1"], - "writers": ["vtgate client 1"], - "admins": ["vtgate client 1"] - } - ] -} -""") - - with open(mysql_auth_server_static, 'w') as fd: - fd.write("""{ - "testuser1": { - "Password": "testpassword1", - "UserData": "vtgate client 1" - }, - "testuser2": { - "Password": "testpassword2", - "UserData": "vtgate client 2" - } -} -""") - - # start the tablets - shard_0_master.start_vttablet(wait_for_state='NOT_SERVING', - table_acl_config=table_acl_config) - shard_0_slave.start_vttablet(wait_for_state='NOT_SERVING', - table_acl_config=table_acl_config) - - # setup replication - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['ApplySchema', '-sql', create_vt_insert_test, - 'test_keyspace']) - for t in [shard_0_master, shard_0_slave]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - # start vtgate - utils.VtGate(mysql_server=True).start( - extra_args=['-mysql_auth_server_impl', 'static', - '-mysql_server_query_timeout', '1s', - '-mysql_auth_server_static_file', mysql_auth_server_static]) - # We use gethostbyname('localhost') so we don't presume - # of the IP format (travis is only IP v4, really). - params = dict(host=socket.gethostbyname('localhost'), - port=utils.vtgate.mysql_port, - user='testuser1', - passwd='testpassword1', - db='test_keyspace') - - # 'vtgate client 1' is authorized to access vt_insert_test - conn = MySQLdb.Connect(**params) - cursor = conn.cursor() - cursor.execute('select * from vt_insert_test', {}) - cursor.close() - - # Test multi-statement support. It should only work when - # COM_SET_OPTION has set the options to 0 - conn.set_server_option(self.MYSQL_OPTION_MULTI_STATEMENTS_ON) - cursor = conn.cursor() - cursor.execute("select 1; select 2") - self.assertEquals(((1L,),), cursor.fetchall()) - self.assertEquals(1, cursor.nextset()) - self.assertEquals(((2L,),), cursor.fetchall()) - self.assertEquals(None, cursor.nextset()) - cursor.close() - conn.set_server_option(self.MYSQL_OPTION_MULTI_STATEMENTS_OFF) - - # Multi-statement support should not work without the - # option enabled - cursor = conn.cursor() - try: - cursor.execute("select 1; select 2") - self.fail('Execute went through') - except MySQLdb.OperationalError, e: - s = str(e) - self.assertIn('syntax error', s) - cursor.close() - - # verify that queries work end-to-end with large grpc messages - largeComment = 'L' * ((4 * 1024 * 1024) + 1) - cursor = conn.cursor() - cursor.execute('insert into vt_insert_test (id, msg, keyspace_id, data) values(%s, %s, %s, %s) /* %s */', - (1, 'large blob', 123, 'LLL', largeComment)) - cursor.close() - - cursor = conn.cursor() - cursor.execute('select * from vt_insert_test where id = 1'); - if cursor.rowcount != 1: - self.fail('expected 1 row got ' + str(cursor.rowcount)) - - for (id, msg, keyspace_id, blob) in cursor: - if blob != 'LLL': - self.fail('blob did not match \'LLL\'') - - cursor.close() - - hugeBlob = 'L' * (environment.grpc_max_message_size + 1) - - cursor = conn.cursor() - try: - cursor.execute('insert into vt_insert_test (id, msg, keyspace_id, data) values(%s, %s, %s, %s)', - (2, 'huge blob', 123, hugeBlob)) - self.fail('Execute went through') - except MySQLdb.OperationalError, e: - s = str(e) - self.assertIn('trying to send message larger than max', s) - - conn.close() - - # 'vtgate client' this query should timeout - conn = MySQLdb.Connect(**params) - try: - cursor = conn.cursor() - cursor.execute('SELECT SLEEP(5)', {}) - self.fail('Execute went through') - except MySQLdb.OperationalError, e: - s = str(e) - # 1317 is DeadlineExceeded error code - self.assertIn('1317', s) - conn.close() - - # this query should fail due to the bogus field - conn = MySQLdb.Connect(**params) - try: - cursor = conn.cursor() - cursor.execute('SELECT invalid_field from vt_insert_test', {}) - self.fail('Execute went through') - except MySQLdb.OperationalError, e: - s = str(e) - # 1054 is BadFieldError code - self.assertIn('1054', s) - - # this query should trigger a warning not an error - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - - cursor.execute('SELECT /*vt+ SCATTER_ERRORS_AS_WARNINGS */ invalid_field from vt_insert_test', {}) - if cursor.rowcount != 0: - self.fail('expected 0 rows got ' + str(cursor.rowcount)) - - if len(w) != 1: - print 'unexpected warnings: ', w - - # and the next query should get the warnings - cursor.execute('SHOW WARNINGS', {}) - if cursor.rowcount != 1: - print 'expected 1 warning row, got ' + str(cursor.rowcount) - - for (_, code, message) in cursor: - self.assertEqual(code, 1054) - self.assertIn('errno 1054', message) - self.assertIn('Unknown column', message) - - # test with a query timeout error - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - - cursor.execute('SELECT /*vt+ SCATTER_ERRORS_AS_WARNINGS QUERY_TIMEOUT_MS=1 */ sleep(1) from vt_insert_test', {}) - if cursor.rowcount != 0: - self.fail('expected 0 rows got ' + str(cursor.rowcount)) - - if len(w) != 1: - print 'unexpected warnings: ', w - - cursor.execute('SHOW WARNINGS', {}) - if cursor.rowcount != 1: - print 'expected 1 warning row, got ' + str(cursor.rowcount) - - for (_, code, message) in cursor: - self.assertEqual(code, 1317) - self.assertIn('context deadline exceeded', message) - - # any non-show query clears the warnings - cursor.execute('SELECT 1 from vt_insert_test limit 1', {}) - cursor.execute('SHOW WARNINGS', {}) - if cursor.rowcount != 0: - print 'expected 0 warnings row, got ' + str(cursor.rowcount) - - # 'vtgate client 2' is not authorized to access vt_insert_test - params['user'] = 'testuser2' - params['passwd'] = 'testpassword2' - conn = MySQLdb.Connect(**params) - try: - cursor = conn.cursor() - cursor.execute('select * from vt_insert_test', {}) - self.fail('Execute went through') - except MySQLdb.OperationalError, e: - s = str(e) - self.assertIn('table acl error', s) - self.assertIn('cannot run PASS_SELECT on table', s) - conn.close() - -if __name__ == '__main__': - utils.main() diff --git a/test/mysqlctl.py b/test/mysqlctl.py deleted file mode 100755 index 43ec926176a..00000000000 --- a/test/mysqlctl.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import warnings -import unittest -import environment -import tablet -import utils - -# Dropping a table inexplicably produces a warning despite -# the "IF EXISTS" clause. Squelch these warnings. -warnings.simplefilter('ignore') - -master_tablet = tablet.Tablet() -replica_tablet = tablet.Tablet() - - -def setUpModule(): - try: - environment.topo_server().setup() - utils.Vtctld().start() - - setup_procs = [ - master_tablet.init_mysql(), - replica_tablet.init_mysql(), - ] - utils.wait_procs(setup_procs) - - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - master_tablet.init_tablet('replica', 'test_keyspace', '0') - replica_tablet.init_tablet('replica', 'test_keyspace', '0') - - master_tablet.create_db('vt_test_keyspace') - replica_tablet.create_db('vt_test_keyspace') - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - tablet.kill_tablets([master_tablet, replica_tablet]) - - teardown_procs = [ - master_tablet.teardown_mysql(), - replica_tablet.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - master_tablet.remove_tree() - replica_tablet.remove_tree() - - -class TestMysqlctl(unittest.TestCase): - - def tearDown(self): - tablet.Tablet.check_vttablet_count() - for t in [master_tablet, replica_tablet]: - t.reset_replication() - t.set_semi_sync_enabled(master=False) - t.clean_dbs() - - def test_mysqlctl_restart(self): - utils.pause('mysqld initialized') - utils.wait_procs([master_tablet.shutdown_mysql()]) - utils.wait_procs([master_tablet.start_mysql()]) - - def test_auto_detect(self): - # start up tablets with an empty MYSQL_FLAVOR, which means auto-detect - master_tablet.start_vttablet(wait_for_state=None, - extra_env={'MYSQL_FLAVOR': ''}) - replica_tablet.start_vttablet(wait_for_state=None, - extra_env={'MYSQL_FLAVOR': ''}) - master_tablet.wait_for_vttablet_state('NOT_SERVING') - replica_tablet.wait_for_vttablet_state('NOT_SERVING') - - # reparent tablets, which requires flavor detection - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - master_tablet.tablet_alias], auto_log=True) - - master_tablet.kill_vttablet() - replica_tablet.kill_vttablet() - - -if __name__ == '__main__': - utils.main() diff --git a/test/prepared_statement_test.py b/test/prepared_statement_test.py deleted file mode 100755 index 475b8dfa84d..00000000000 --- a/test/prepared_statement_test.py +++ /dev/null @@ -1,321 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Ensures the vtgate MySQL server protocol plugin works as expected with prepared statements. - -We use table ACLs to verify the user name authenticated by the connector is -set properly. -""" - -import datetime -import socket -import unittest - -import mysql.connector -from mysql.connector import FieldType -from mysql.connector.cursor import MySQLCursorPrepared -from mysql.connector.errors import Error - -import environment -import utils -import tablet -import warnings - -# single shard / 2 tablets -shard_0_master = tablet.Tablet() -shard_0_slave = tablet.Tablet() - -table_acl_config = environment.tmproot + '/table_acl_config.json' -mysql_auth_server_static = (environment.tmproot + - '/mysql_auth_server_static.json') - - -json_example = '''{ - "quiz": { - "sport": { - "q1": { - "question": "Which one is correct team name in NBA?", - "options": [ - "New York Bulls", - "Los Angeles Kings", - "Golden State Warriors", - "Huston Rocket" - ], - "answer": "Huston Rocket" - } - }, - "maths": { - "q1": { - "question": "5 + 7 = ?", - "options": [ - "10", - "11", - "12", - "13" - ], - "answer": "12" - }, - "q2": { - "question": "12 - 8 = ?", - "options": [ - "1", - "2", - "3", - "4" - ], - "answer": "4" - } - } - } -}''' - -insert_stmt = '''insert into vt_prepare_stmt_test values(%s, %s, %s, %s, %s, %s, %s, - %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)''' - -def setUpModule(): - try: - environment.topo_server().setup() - - # setup all processes - setup_procs = [ - shard_0_master.init_mysql(), - shard_0_slave.init_mysql(), - ] - utils.wait_procs(setup_procs) - - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '0') - shard_0_slave.init_tablet('replica', 'test_keyspace', '0') - - # create databases so vttablet can start behaving normally - shard_0_master.create_db('vt_test_keyspace') - shard_0_slave.create_db('vt_test_keyspace') - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - shard_0_master.kill_vttablet() - shard_0_slave.kill_vttablet() - - teardown_procs = [ - shard_0_master.teardown_mysql(), - shard_0_slave.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_0_master.remove_tree() - shard_0_slave.remove_tree() - - -create_vt_prepare_test = '''create table vt_prepare_stmt_test ( -id bigint auto_increment, -msg varchar(64), -keyspace_id bigint(20) unsigned NOT NULL, -tinyint_unsigned TINYINT, -bool_signed BOOL, -smallint_unsigned SMALLINT, -mediumint_unsigned MEDIUMINT, -int_unsigned INT, -float_unsigned FLOAT(10,2), -double_unsigned DOUBLE(16,2), -decimal_unsigned DECIMAL, -t_date DATE, -t_datetime DATETIME, -t_time TIME, -t_timestamp TIMESTAMP, -c8 bit(8) DEFAULT NULL, -c16 bit(16) DEFAULT NULL, -c24 bit(24) DEFAULT NULL, -c32 bit(32) DEFAULT NULL, -c40 bit(40) DEFAULT NULL, -c48 bit(48) DEFAULT NULL, -c56 bit(56) DEFAULT NULL, -c63 bit(63) DEFAULT NULL, -c64 bit(64) DEFAULT NULL, -json_col JSON, -text_col TEXT, -data longblob, -primary key (id) -) Engine=InnoDB''' - - -class TestPreparedStatements(unittest.TestCase): - """This test makes sure that prepared statements is working correctly. - """ - - def test_prepared_statements(self): - with open(table_acl_config, 'w') as fd: - fd.write("""{ - "table_groups": [ - { - "table_names_or_prefixes": ["vt_prepare_stmt_test", "dual"], - "readers": ["vtgate client 1"], - "writers": ["vtgate client 1"], - "admins": ["vtgate client 1"] - } - ] -} -""") - - with open(mysql_auth_server_static, 'w') as fd: - fd.write("""{ - "testuser1": { - "Password": "testpassword1", - "UserData": "vtgate client 1" - }, - "testuser2": { - "Password": "testpassword2", - "UserData": "vtgate client 2" - } -} -""") - - # start the tablets - shard_0_master.start_vttablet(wait_for_state='NOT_SERVING', - table_acl_config=table_acl_config) - shard_0_slave.start_vttablet(wait_for_state='NOT_SERVING', - table_acl_config=table_acl_config) - - # setup replication - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['ApplySchema', '-sql', create_vt_prepare_test, - 'test_keyspace']) - for t in [shard_0_master, shard_0_slave]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - # start vtgate - utils.VtGate(mysql_server=True).start( - extra_args=['-mysql_auth_server_impl', 'static', - '-mysql_server_query_timeout', '1s', - '-mysql_auth_server_static_file', mysql_auth_server_static, - "-mysql_server_version", '8.0.16-7']) - # We use gethostbyname('localhost') so we don't presume - # of the IP format (travis is only IP v4, really). - params = dict(host=socket.gethostbyname('localhost'), - port=utils.vtgate.mysql_port, - user='testuser1', - passwd='testpassword1', - db='test_keyspace', - use_pure=True) - - # 'vtgate client 1' is authorized to access vt_prepare_insert_test - conn = mysql.connector.Connect(**params) - cursor = conn.cursor() - cursor.execute('select * from vt_prepare_stmt_test', {}) - cursor.fetchone() - cursor.close() - - cursor = conn.cursor() - try: - cursor.execute('selet * from vt_prepare_stmt_test', {}) - cursor.close() - except mysql.connector.Error as err: - if err.errno == 1105: - print "Captured the error" - else: - raise - - # Insert several rows using prepared statements - text_value = "text" * 100 # Large text value - largeComment = 'L' * ((4 * 1024) + 1) # Large blob - - # Set up the values for the prepared statement - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - for i in range(1, 100): - insert_values = (i, str(i) + "21", i * 100, 127, 1, 32767, 8388607, 2147483647, 2.55, 64.9,55.5, - datetime.date(2009, 5, 5), datetime.date(2009, 5, 5), datetime.datetime.now().time(), datetime.date(2009, 5, 5), - 1,1,1,1,1,1,1,1,1, json_example, text_value, largeComment) - cursor.execute(insert_stmt, insert_values) - - cursor.fetchone() - cursor.close() - - # Send an invalid table name to ensure that python's mysql client will not fail before entering vtgate - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - try: - cursor.execute('select * from prepare_stmt_test where id = %s', (1,)) - except mysql.connector.Error as err: - if err.errno == 1105: - print "Could not find the table" - else: - raise - - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - cursor.execute('select * from vt_prepare_stmt_test where id = %s', (1,)) - result = cursor.fetchall() - - # Validate the query results. - if cursor.rowcount != 1: - self.fail('expected 1 row got ' + str(cursor.rowcount)) - - if result[0][1] != "121": - self.fail('Received incorrect value, wanted: 121, got ' + result[1]) - - cursor.close() - - # Update a row using prepared statements - updated_text_value = "text_col_msg" - updated_data_value = "updated" - - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - cursor.execute('update vt_prepare_stmt_test set data = %s , text_col = %s where id = %s', (updated_data_value, updated_text_value, 1)) - cursor.close() - - # Validate the update results - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - cursor.execute('select * from vt_prepare_stmt_test where id = %s', (1,)) - result = cursor.fetchone() - if result[-1] != updated_data_value or result[-2] != updated_text_value: - self.fail("Received incorrect values") - cursor.close() - - # Delete from table using prepared statements - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - cursor.execute('delete from vt_prepare_stmt_test where text_col = %s', (text_value,)) - cursor.close() - - # Validate Deletion - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - cursor.execute('select count(*) from vt_prepare_stmt_test') - res = cursor.fetchone() - if res[0] != 1: - self.fail("Delete failed") - cursor.close() - - # resetting the connection - conn.cmd_reset_connection() - cursor = conn.cursor(cursor_class=MySQLCursorPrepared) - cursor.execute('select * from vt_prepare_stmt_test where id = %s', (1,)) - result = cursor.fetchone() - # Should fail since we cleared PreparedData inside the connection - with self.assertRaises(TypeError): - empty_val = result[-2] - -if __name__ == '__main__': - utils.main() diff --git a/test/protocols_flavor.py b/test/protocols_flavor.py deleted file mode 100644 index b4a32eef560..00000000000 --- a/test/protocols_flavor.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Base class for protocols flavor. - -Each set of protocols has a flavor object. The current one is dynamically -imported once. -""" - - -class ProtocolsFlavor(object): - """Base class for protocols flavor.""" - - def binlog_player_protocol(self): - """The binlog player protocol between vttablets, in go.""" - raise NotImplementedError('Not implemented in the base class') - - def vtctl_client_protocol(self): - """The protocol to use for vtctl connections. - - This is just for the go client. - """ - raise NotImplementedError('Not implemented in the base class') - - def vtctl_python_client_protocol(self): - """The protocol to use for vtctl connections. - - This is just for the python client. - """ - raise NotImplementedError('Not implemented in the base class') - - def vtworker_client_protocol(self): - """The protocol to use for vtworker connections.""" - raise NotImplementedError('Not implemented in the base class') - - def tablet_manager_protocol(self): - """The protocol to use for the tablet manager protocol.""" - raise NotImplementedError('Not implemented in the base class') - - def tabletconn_protocol(self): - """The protocol to use for connections from vtctl/vtgate to vttablet.""" - raise NotImplementedError('Not implemented in the base class') - - def throttler_client_protocol(self): - """Client protocol for the resharding throttler. - - This RPC interface is enabled in vtworker and vttablet. - """ - raise NotImplementedError('Not implemented in the base class') - - def vtgate_protocol(self): - """The protocol to use to talk to vtgate, in go.""" - raise NotImplementedError('Not implemented in the base class') - - def vtgate_python_protocol(self): - """The protocol to use to talk to vtgate with python clients.""" - raise NotImplementedError('Not implemented in the base class') - - def client_error_exception_type(self): - """The exception type the RPC client implementation returns for errors.""" - raise NotImplementedError('Not implemented in the base class') - - def rpc_timeout_message(self): - """The error message used by the protocol to indicate a timeout.""" - raise NotImplementedError('Not implemented in the base class') - - def service_map(self): - """A list of entries to enable all relevant protocols in all servers.""" - raise NotImplementedError('Not implemented in the base class') - - def vttest_protocol(self): - """Python protocol to use to talk to a vttest client.""" - raise NotImplementedError('Not implemented in the base class') - - -_protocols_flavor = None - - -def protocols_flavor(): - """Returns the current ProtocolsFlavor object.""" - return _protocols_flavor - - -def set_protocols_flavor(flavor): - """Set the protocols flavor implementation.""" - global _protocols_flavor - _protocols_flavor = flavor diff --git a/test/python_client_test.py b/test/python_client_test.py deleted file mode 100755 index 820902694b3..00000000000 --- a/test/python_client_test.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""This test uses vtgateclienttest to test the vtdb python vtgate client. -""" - -import logging -import unittest - -import environment -from protocols_flavor import protocols_flavor -import utils - -from vtdb import vtgate_client_testsuite -from vtdb import vtgate_client - -vtgateclienttest_process = None -vtgateclienttest_port = None -vtgateclienttest_grpc_port = None - - -def setUpModule(): - global vtgateclienttest_process - global vtgateclienttest_port - global vtgateclienttest_grpc_port - - try: - environment.topo_server().setup() - - vtgateclienttest_port = environment.reserve_ports(1) - args = environment.binary_args('vtgateclienttest') + [ - '-log_dir', environment.vtlogroot, - '-port', str(vtgateclienttest_port), - ] - - if protocols_flavor().vtgate_python_protocol() == 'grpc': - vtgateclienttest_grpc_port = environment.reserve_ports(1) - args.extend(['-grpc_port', str(vtgateclienttest_grpc_port)]) - if protocols_flavor().service_map(): - args.extend(['-service_map', ','.join(protocols_flavor().service_map())]) - - vtgateclienttest_process = utils.run_bg(args) - utils.wait_for_vars('vtgateclienttest', vtgateclienttest_port) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.kill_sub_process(vtgateclienttest_process, soft=True) - if vtgateclienttest_process: - vtgateclienttest_process.wait() - - environment.topo_server().teardown() - - -class TestPythonClientBase(unittest.TestCase): - - def setUp(self): - super(TestPythonClientBase, self).setUp() - protocol = protocols_flavor().vtgate_python_protocol() - if protocol == 'grpc': - addr = 'localhost:%d' % vtgateclienttest_grpc_port - else: - addr = 'localhost:%d' % vtgateclienttest_port - self.conn = vtgate_client.connect(protocol, addr, 30.0) - logging.info( - 'Start: %s, protocol %s.', - '.'.join(self.id().split('.')[-2:]), protocol) - - def tearDown(self): - self.conn.close() - - -class TestErrors(TestPythonClientBase, - vtgate_client_testsuite.TestErrors): - """Test cases to verify that the Python client can handle errors correctly.""" - - -class TestSuccess(TestPythonClientBase, - vtgate_client_testsuite.TestSuccess): - """Success test cases for the Python client.""" - - -class TestCallerId(TestPythonClientBase, - vtgate_client_testsuite.TestCallerId): - """Caller ID test cases for the Python client.""" - - -class TestEcho(TestPythonClientBase, - vtgate_client_testsuite.TestEcho): - """Send queries to the server, check the returned result matches.""" - - -if __name__ == '__main__': - utils.main() diff --git a/test/recovery.py b/test/recovery.py deleted file mode 100755 index a319e5c7de8..00000000000 --- a/test/recovery.py +++ /dev/null @@ -1,534 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from datetime import datetime -import json -import logging -import os -import unittest - -import MySQLdb - -import environment -import tablet -import utils - -from mysql_flavor import mysql_flavor -from vtdb import vtgate_client - -use_xtrabackup = False -xtrabackup_args = [] -stream_mode = 'xbstream' - -# tablets -tablet_master = tablet.Tablet() -tablet_replica1 = tablet.Tablet() -tablet_replica2 = tablet.Tablet() -tablet_replica3 = tablet.Tablet() - -all_tablets = [tablet_master, tablet_replica1, tablet_replica2, tablet_replica3] - -def setUpModule(): - global xtrabackup_args - xtrabackup_args = ['-backup_engine_implementation', - 'xtrabackup', - '-xtrabackup_stream_mode', - stream_mode, - '-xtrabackup_user=vt_dba'] - try: - environment.topo_server().setup() - setup_procs = [t.init_mysql() for t in all_tablets] - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - -def get_connection(timeout=15.0): - protocol, endpoint = utils.vtgate.rpc_endpoint(python=True) - try: - return vtgate_client.connect(protocol, endpoint, timeout) - except Exception: - logging.exception('Connection to vtgate (timeout=%s) failed.', timeout) - raise - -class TestRecovery(unittest.TestCase): - - def setUp(self): - xtra_args = ['-enable_replication_reporter'] - if use_xtrabackup: - xtra_args.extend(xtrabackup_args) - tablet_master.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=xtra_args) - tablet_replica1.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=xtra_args) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_master.tablet_alias]) - - def tearDown(self): - for t in all_tablets: - t.kill_vttablet() - - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in all_tablets: - t.reset_replication() - t.set_semi_sync_enabled(master=False, slave=False) - t.clean_dbs() - - for backup in self._list_backups(): - self._remove_backup(backup) - - _create_vt_insert_test = '''create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - _vschema_json = '''{ - "tables": { - "vt_insert_test": {} - } -}''' - - - def _insert_data(self, t, index): - """Add a single row with value 'index' to the given tablet.""" - t.mquery( - 'vt_test_keyspace', - "insert into vt_insert_test (msg) values ('test %s')" % - index, write=True) - - def _check_data(self, t, count, msg): - """Check that the specified tablet has the expected number of rows.""" - timeout = 10 - while True: - try: - result = t.mquery( - 'vt_test_keyspace', 'select count(*) from vt_insert_test') - if result[0][0] == count: - break - except MySQLdb.DatabaseError: - # ignore exceptions, we'll just timeout (the tablet creation - # can take some time to replicate, and we get a 'table vt_insert_test - # does not exist exception in some rare cases) - logging.exception('exception waiting for data to replicate') - timeout = utils.wait_step(msg, timeout) - - def _restore(self, t, keyspace): - """Erase mysql/tablet dir, then start tablet with restore enabled.""" - self._reset_tablet_dir(t) - - # create a recovery keyspace - utils.run_vtctl(['CreateKeyspace', - '-keyspace_type=SNAPSHOT', - '-base_keyspace=test_keyspace', - '-snapshot_time', - datetime.utcnow().isoformat("T")+"Z", - keyspace]) - # set disable_active_reparents to true and enable_replication_reporter to false - # otherwise replication_reporter will try to restart replication - xtra_args = ['-disable_active_reparents', - '-enable_replication_reporter=false'] - xtra_args.extend(tablet.get_backup_storage_flags()) - if use_xtrabackup: - xtra_args.extend(xtrabackup_args) - - t.start_vttablet(wait_for_state='SERVING', - init_tablet_type='replica', - init_keyspace=keyspace, - init_shard='0', - supports_backups=True, - extra_args=xtra_args) - - def _reset_tablet_dir(self, t): - """Stop mysql, delete everything including tablet dir, restart mysql.""" - utils.wait_procs([t.teardown_mysql()]) - # Specify ignore_options because we want to delete the tree even - # if the test's -k / --keep-logs was specified on the command line. - t.remove_tree(ignore_options=True) - proc = t.init_mysql() - utils.wait_procs([proc]) - - def _list_backups(self): - """Get a list of backup names for the test shard.""" - backups, _ = utils.run_vtctl(tablet.get_backup_storage_flags() + - ['ListBackups', 'test_keyspace/0'], - mode=utils.VTCTL_VTCTL, trap_output=True) - return backups.splitlines() - - def _remove_backup(self, backup): - """Remove a named backup from the test shard.""" - utils.run_vtctl( - tablet.get_backup_storage_flags() + - ['RemoveBackup', 'test_keyspace/0', backup], - auto_log=True, mode=utils.VTCTL_VTCTL) - - def test_basic_recovery(self): - """Test recovery from backup flow. - - test_recovery will: - - create a shard with master and replica1 only - - run InitShardMaster - - insert some data - - take a backup - - insert more data on the master - - create a recovery keyspace - - bring up tablet_replica2 in the new keyspace - - check that new tablet does not have data created after backup - - check that vtgate queries work correctly - - """ - - # insert data on master, wait for replica to get it - utils.run_vtctl(['ApplySchema', - '-sql', self._create_vt_insert_test, - 'test_keyspace'], - auto_log=True) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - master_pos = mysql_flavor().master_position(tablet_master) - # backup the replica - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # check that the backup shows up in the listing - backups = self._list_backups() - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), 1) - self.assertTrue(backups[0].endswith(tablet_replica1.tablet_alias)) - # backup name is of format date.time.tablet_alias - strs = backups[0].split('.') - expectedTime = datetime.strptime(strs[0] + '.' + strs[1], '%Y-%m-%d.%H%M%S') - - # insert more data on the master - self._insert_data(tablet_master, 2) - - utils.run_vtctl(['ApplyVSchema', - '-vschema', self._vschema_json, - 'test_keyspace'], - auto_log=True) - - vs = utils.run_vtctl_json(['GetVSchema', 'test_keyspace']) - logging.debug('test_keyspace vschema: %s', str(vs)) - ks = utils.run_vtctl_json(['GetSrvKeyspace', 'test_nj', 'test_keyspace']) - logging.debug('Serving keyspace before: %s', str(ks)) - vs = utils.run_vtctl_json(['GetSrvVSchema', 'test_nj']) - logging.debug('Serving vschema before recovery: %s', str(vs)) - - # now bring up the recovery keyspace with 1 tablet, letting it restore from backup. - self._restore(tablet_replica2, 'recovery_keyspace') - - vs = utils.run_vtctl_json(['GetSrvVSchema', 'test_nj']) - logging.debug('Serving vschema after recovery: %s', str(vs)) - ks = utils.run_vtctl_json(['GetSrvKeyspace', 'test_nj', 'test_keyspace']) - logging.debug('Serving keyspace after: %s', str(ks)) - vs = utils.run_vtctl_json(['GetVSchema', 'recovery_keyspace']) - logging.debug('recovery_keyspace vschema: %s', str(vs)) - - # check the new replica has only 1 row - self._check_data(tablet_replica2, 1, 'replica2 tablet should not have new data') - - # check that the restored replica has the right local_metadata - result = tablet_replica2.mquery('_vt', 'select * from local_metadata') - metadata = {} - for row in result: - metadata[row[0]] = row[1] - self.assertEqual(metadata['Alias'], 'test_nj-0000062346') - self.assertEqual(metadata['ClusterAlias'], 'recovery_keyspace.0') - self.assertEqual(metadata['DataCenter'], 'test_nj') - self.assertEqual(metadata['RestorePosition'], master_pos) - logging.debug('RestoredBackupTime: %s', str(metadata['RestoredBackupTime'])) - gotTime = datetime.strptime(metadata['RestoredBackupTime'], '%Y-%m-%dT%H:%M:%SZ') - self.assertEqual(gotTime, expectedTime) - - # update original 1st row in master - tablet_master.mquery( - 'vt_test_keyspace', - "update vt_insert_test set msg='new msg' where id=1", write=True) - - # verify that master has new value - result = tablet_master.mquery('vt_test_keyspace', 'select msg from vt_insert_test where id=1') - self.assertEqual(result[0][0], 'new msg') - - # verify that restored replica has old value - result = tablet_replica2.mquery('vt_test_keyspace', 'select msg from vt_insert_test where id=1') - self.assertEqual(result[0][0], 'test 1') - - # start vtgate - vtgate = utils.VtGate() - vtgate.start(tablets=[ - tablet_master, tablet_replica1, tablet_replica2 - ], tablet_types_to_wait='REPLICA') - utils.vtgate.wait_for_endpoints('test_keyspace.0.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.0.replica', 1) - utils.vtgate.wait_for_endpoints('recovery_keyspace.0.replica', 1) - - # check that vtgate doesn't route queries to new tablet - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace=None, writable=True) - - cursor.execute('select count(*) from vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 2) - - cursor.execute('select msg from vt_insert_test where id=1', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 'new msg') - - # check that new keyspace is accessible by using ks.table - cursor.execute('select count(*) from recovery_keyspace.vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 1) - - cursor.execute('select msg from recovery_keyspace.vt_insert_test where id=1', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 'test 1') - - # check that new keyspace is accessible with 'use ks' - cursor.execute('use recovery_keyspace@replica', {}) - cursor.execute('select count(*) from vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 1) - - cursor.execute('select msg from recovery_keyspace.vt_insert_test where id=1', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 'test 1') - - # TODO check that new tablet is accessible with 'use ks:shard' - # this currently does not work through the python client, though it works from mysql client - #cursor.execute('use recovery_keyspace:0@replica', {}) - #cursor.execute('select count(*) from vt_insert_test', {}) - #result = cursor.fetchall() - #if not result: - #self.fail('Result cannot be null') - #else: - #self.assertEqual(result[0][0], 1) - - vtgate_conn.close() - tablet_replica2.kill_vttablet() - vtgate.kill() - - def test_multi_recovery(self): - """Test recovery from backup flow. - - test_multi_recovery will: - - create a shard with master and replica1 only - - run InitShardMaster - - insert some data - - take a backup - - insert more data on the master - - take another backup - - create a recovery keyspace after first backup - - bring up tablet_replica2 in the new keyspace - - check that new tablet does not have data created after backup1 - - create second recovery keyspace after second backup - - bring up tablet_replica3 in second keyspace - - check that new tablet has data created after backup1 but not data created after backup2 - - check that vtgate queries work correctly - - """ - - # insert data on master, wait for replica to get it - utils.run_vtctl(['ApplySchema', - '-sql', self._create_vt_insert_test, - 'test_keyspace'], - auto_log=True) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - - # backup the replica - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # check that the backup shows up in the listing - backups = self._list_backups() - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), 1) - self.assertTrue(backups[0].endswith(tablet_replica1.tablet_alias)) - - # insert more data on the master - self._insert_data(tablet_master, 2) - # wait for it to replicate - self._check_data(tablet_replica1, 2, 'replica1 tablet getting data') - - utils.run_vtctl(['ApplyVSchema', - '-vschema', self._vschema_json, - 'test_keyspace'], - auto_log=True) - - vs = utils.run_vtctl_json(['GetVSchema', 'test_keyspace']) - logging.debug('test_keyspace vschema: %s', str(vs)) - - # now bring up the other replica, letting it restore from backup. - self._restore(tablet_replica2, 'recovery_ks1') - - # we are not asserting on the contents of vschema here - # because the later part of the test (vtgate) will fail - # if the vschema is not copied correctly from the base_keyspace - vs = utils.run_vtctl_json(['GetVSchema', 'recovery_ks1']) - logging.debug('recovery_ks1 vschema: %s', str(vs)) - - # check the new replica does not have the data - self._check_data(tablet_replica2, 1, 'replica2 tablet should not have new data') - - # update original 1st row in master - tablet_master.mquery( - 'vt_test_keyspace', - "update vt_insert_test set msg='new msg 1' where id=1", write=True) - - # verify that master has new value - result = tablet_master.mquery('vt_test_keyspace', 'select msg from vt_insert_test where id=1') - self.assertEqual(result[0][0], 'new msg 1') - - # verify that restored replica has old value - result = tablet_replica2.mquery('vt_test_keyspace', 'select msg from vt_insert_test where id=1') - self.assertEqual(result[0][0], 'test 1') - - # take another backup on the replica - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # insert more data on the master - self._insert_data(tablet_master, 3) - # wait for it to replicate - self._check_data(tablet_replica1, 3, 'replica1 tablet getting data') - - # now bring up the other replica, letting it restore from backup2. - # this also validates that if there are multiple backups, the most recent one is used - self._restore(tablet_replica3, 'recovery_ks2') - - vs = utils.run_vtctl(['GetVSchema', 'recovery_ks2']) - logging.debug('recovery_ks2 vschema: %s', str(vs)) - - # check the new replica does not have the latest data - self._check_data(tablet_replica3, 2, 'replica3 tablet should not have new data') - - # update original 1st row in master again - tablet_master.mquery( - 'vt_test_keyspace', - "update vt_insert_test set msg='new msg 2' where id=1", write=True) - - # verify that master has new value - result = tablet_master.mquery('vt_test_keyspace', 'select msg from vt_insert_test where id=1') - self.assertEqual(result[0][0], 'new msg 2') - - # verify that restored replica has correct value - result = tablet_replica3.mquery('vt_test_keyspace', 'select msg from vt_insert_test where id=1') - self.assertEqual(result[0][0], 'new msg 1') - - # start vtgate - vtgate = utils.VtGate() - vtgate.start(tablets=all_tablets, tablet_types_to_wait='REPLICA') - utils.vtgate.wait_for_endpoints('test_keyspace.0.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.0.replica', 1) - utils.vtgate.wait_for_endpoints('recovery_ks1.0.replica', 1) - utils.vtgate.wait_for_endpoints('recovery_ks2.0.replica', 1) - - # check that vtgate doesn't route queries to new tablet - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace=None, writable=True) - - cursor.execute('select count(*) from vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 3) - - cursor.execute('select msg from vt_insert_test where id=1', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 'new msg 2') - - # check that new keyspace is accessible by using ks.table - cursor.execute('select count(*) from recovery_ks1.vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 1) - - cursor.execute('select msg from recovery_ks1.vt_insert_test where id=1', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 'test 1') - - # check that new keyspace is accessible by using ks.table - cursor.execute('select count(*) from recovery_ks2.vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 2) - - cursor.execute('select msg from recovery_ks2.vt_insert_test where id=1', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 'new msg 1') - - # TODO check that new tablet is accessible with 'use ks:shard' - # this currently does not work through the python client, though it works from mysql client - #cursor.execute('use recovery_ks1:0@replica', {}) - #cursor.execute('select count(*) from vt_insert_test', {}) - #result = cursor.fetchall() - #if not result: - #self.fail('Result cannot be null') - #else: - #self.assertEqual(result[0][0], 1) - - vtgate_conn.close() - vtgate.kill() - -if __name__ == '__main__': - utils.main() diff --git a/test/reparent.py b/test/reparent.py deleted file mode 100755 index 9f883ffdfac..00000000000 --- a/test/reparent.py +++ /dev/null @@ -1,785 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import logging -import time -import unittest - -import environment -import utils -import tablet -from mysql_flavor import mysql_flavor -from protocols_flavor import protocols_flavor - -from vtproto import topodata_pb2 - -tablet_62344 = tablet.Tablet(62344) -tablet_62044 = tablet.Tablet(62044) -tablet_41983 = tablet.Tablet(41983) -tablet_31981 = tablet.Tablet(31981) - - -def setUpModule(): - try: - environment.topo_server().setup() - - # start mysql instance external to the test - setup_procs = [ - tablet_62344.init_mysql(), - tablet_62044.init_mysql(), - tablet_41983.init_mysql(), - tablet_31981.init_mysql(), - ] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [ - tablet_62344.teardown_mysql(), - tablet_62044.teardown_mysql(), - tablet_41983.teardown_mysql(), - tablet_31981.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - tablet_62344.remove_tree() - tablet_62044.remove_tree() - tablet_41983.remove_tree() - tablet_31981.remove_tree() - - -class TestReparent(unittest.TestCase): - - def tearDown(self): - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in [tablet_62344, tablet_62044, tablet_41983, tablet_31981]: - t.reset_replication() - t.set_semi_sync_enabled(master=False) - t.clean_dbs(include_vt=True) - super(TestReparent, self).tearDown() - - _create_vt_insert_test = '''create table vt_insert_test ( - id bigint, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - def _populate_vt_insert_test(self, master_tablet, index): - q = ("insert into vt_insert_test(id, msg) values (%d, 'test %d')" % - (index, index)) - master_tablet.mquery('vt_test_keyspace', q, write=True) - - def _check_vt_insert_test(self, tablet_obj, index): - # wait until it gets the data - timeout = 10.0 - while True: - result = tablet_obj.mquery( - 'vt_test_keyspace', - 'select msg from vt_insert_test where id=%d' % index) - if len(result) == 1: - break - timeout = utils.wait_step('waiting for replication to catch up on %s' % - tablet_obj.tablet_alias, - timeout, sleep_time=0.1) - - def _check_master_tablet(self, t, port=None): - """Makes sure the tablet type is master, and its health check agrees.""" - ti = utils.run_vtctl_json(['GetTablet', t.tablet_alias]) - self.assertEqual(ti['type'], topodata_pb2.MASTER) - if port: - self.assertEqual(ti['port_map']['vt'], port) - - # make sure the health stream is updated - health = utils.run_vtctl_json(['VtTabletStreamHealth', '-count', '1', - t.tablet_alias]) - self.assertIn('serving', health) - self.assertEqual(health['target']['tablet_type'], topodata_pb2.MASTER) - - def test_master_to_spare_state_change_impossible(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62344.init_tablet('master', 'test_keyspace', '0', start=True, - wait_for_start=True) - - utils.run_vtctl(['ChangeSlaveType', tablet_62344.tablet_alias, 'spare'], - expect_fail=True) - tablet_62344.kill_vttablet() - - def test_reparent_down_master(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - tablet_41983.create_db('vt_test_keyspace') - tablet_31981.create_db('vt_test_keyspace') - - # Start up a master mysql and vttablet - tablet_62344.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - - # Create a few slaves for testing reparenting. - tablet_62044.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - tablet_41983.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - tablet_31981.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - - # wait for all tablets to start - for t in [tablet_62344, tablet_62044, tablet_41983, tablet_31981]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent assuming that all the datasets are - # identical. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias], auto_log=True) - utils.validate_topology() - tablet_62344.mquery('vt_test_keyspace', self._create_vt_insert_test) - - # Make the current master agent and database unavailable. - tablet_62344.kill_vttablet() - tablet_62344.shutdown_mysql().wait() - - # Perform a planned reparent operation, will try to contact - # the current master and fail somewhat quickly - _, stderr = utils.run_vtctl(['-wait-time', '5s', - 'PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-new_master', tablet_62044.tablet_alias], - expect_fail=True) - self.assertIn('current master must be healthy to perform planned reparent', stderr) - - # Run forced reparent operation, this should now proceed unimpeded. - utils.run_vtctl(['EmergencyReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-new_master', tablet_62044.tablet_alias], auto_log=True) - - utils.validate_topology() - self._check_master_tablet(tablet_62044) - - # insert data into the new master, check the connected slaves work - self._populate_vt_insert_test(tablet_62044, 2) - self._check_vt_insert_test(tablet_41983, 2) - self._check_vt_insert_test(tablet_31981, 2) - - # bring back the old master as a slave, check that it catches up - tablet_62344.start_mysql().wait() - tablet_62344.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - self._check_vt_insert_test(tablet_62344, 2) - - tablet.kill_tablets( - [tablet_62344, tablet_62044, tablet_41983, tablet_31981]) - - def test_reparent_cross_cell(self, shard_id='0'): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - tablet_41983.create_db('vt_test_keyspace') - tablet_31981.create_db('vt_test_keyspace') - - # Start up a master mysql and vttablet - tablet_62344.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - - # Create a few slaves for testing reparenting. Won't be healthy - # as replication is not running. - tablet_62044.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_41983.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_31981.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - for t in [tablet_62344, tablet_62044, tablet_41983, tablet_31981]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent assuming that all the datasets are - # identical. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/' + shard_id, - tablet_62344.tablet_alias], auto_log=True) - utils.validate_topology(ping_tablets=True) - - self._check_master_tablet(tablet_62344) - - # Perform a graceful reparent operation to another cell. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/' + shard_id, - '-new_master', tablet_31981.tablet_alias], auto_log=True) - utils.validate_topology() - - self._check_master_tablet(tablet_31981) - - tablet.kill_tablets([tablet_62344, tablet_62044, tablet_41983, - tablet_31981]) - - def test_reparent_graceful_range_based(self): - utils.run_vtctl(['CreateKeyspace', - '--sharding_column_name', 'keyspace_id', - '--sharding_column_type', 'uint64', - 'test_keyspace']) - self._test_reparent_graceful('0000000000000000-ffffffffffffffff') - - def test_reparent_graceful(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - self._test_reparent_graceful('0') - - def test_reparent_graceful_recovery(self): - # Test that PRS can perform a graceful recovery - # as long as all tablets are responding. - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - self._test_reparent_graceful('0', confused_master=True) - - def _test_reparent_graceful(self, shard_id, confused_master=False): - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - tablet_41983.create_db('vt_test_keyspace') - tablet_31981.create_db('vt_test_keyspace') - - # Start up a master mysql and vttablet - tablet_62344.init_tablet('replica', 'test_keyspace', shard_id, start=True) - - # Create a few slaves for testing reparenting. - tablet_62044.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_41983.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_31981.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - for t in [tablet_62044, tablet_41983, tablet_31981]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent assuming that all the datasets are - # identical. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/' + shard_id, - tablet_62344.tablet_alias]) - utils.validate_topology(ping_tablets=True) - tablet_62344.mquery('vt_test_keyspace', self._create_vt_insert_test) - - self._check_master_tablet(tablet_62344) - - utils.validate_topology() - - # Run this to make sure it succeeds. - stdout, _ = utils.run_vtctl(['ShardReplicationPositions', - 'test_keyspace/' + shard_id], - trap_output=True) - lines = stdout.splitlines() - self.assertEqual(len(lines), 4) # one master, three slaves - self.assertIn('master', lines[0]) # master first - - # Perform a graceful reparent operation. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/' + shard_id, - '-new_master', tablet_62044.tablet_alias], auto_log=True) - utils.validate_topology() - - if confused_master: - # Simulate a master that forgets it's master and becomes replica. - # PRS should be able to recover by reparenting to the same master again, - # as long as all tablets are available to check that it's safe. - tablet_62044.init_tablet('replica', 'test_keyspace', shard_id, start=False) - utils.run_vtctl(['RefreshState', tablet_62044.tablet_alias]) - - # Perform a graceful reparent to the same master. - # It should be idempotent, and should fix any inconsistencies if necessary. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/' + shard_id, - '-new_master', tablet_62044.tablet_alias], auto_log=True) - utils.validate_topology() - - self._check_master_tablet(tablet_62044) - - # insert data into the new master, check the connected slaves work - self._populate_vt_insert_test(tablet_62044, 1) - self._check_vt_insert_test(tablet_41983, 1) - self._check_vt_insert_test(tablet_62344, 1) - - tablet.kill_tablets([tablet_62344, tablet_62044, tablet_41983, - tablet_31981]) - - # Test address correction. - new_port = environment.reserve_ports(1) - tablet_62044.start_vttablet(port=new_port) - - # Wait until the new address registers. - timeout = 30.0 - while True: - try: - self._check_master_tablet(tablet_62044, port=new_port) - break - except protocols_flavor().client_error_exception_type(): - timeout = utils.wait_step('waiting for new port to register', - timeout, sleep_time=0.1) - - tablet_62044.kill_vttablet() - - # Reparenting should return error if replica vttablet is down - def test_reparent_slave_offline(self, shard_id='0'): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - tablet_41983.create_db('vt_test_keyspace') - tablet_31981.create_db('vt_test_keyspace') - - # Start up a master mysql and vttablet - tablet_62344.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - - # Create a few slaves for testing reparenting. - tablet_62044.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_41983.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_31981.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - - # wait for all tablets to start - for t in [tablet_62344, tablet_62044, tablet_41983, tablet_31981]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent assuming that all the datasets are - # identical. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/' + shard_id, - tablet_62344.tablet_alias]) - utils.validate_topology(ping_tablets=True) - - self._check_master_tablet(tablet_62344) - - # Kill one tablet so we seem offline - tablet_31981.kill_vttablet() - - # Perform a graceful reparent operation. - _, stderr = utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/' + shard_id, - '-new_master', tablet_62044.tablet_alias], expect_fail=True) - self.assertIn('tablet test_ny-0000031981 SetMaster failed', stderr) - - self._check_master_tablet(tablet_62044) - - tablet.kill_tablets([tablet_62344, tablet_62044, tablet_41983]) - - def test_reparent_avoid(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - tablet_31981.create_db('vt_test_keyspace') - - # Start up a master mysql and vttablet - tablet_62344.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - - # Create a few slaves for testing reparenting. Won't be healthy - # as replication is not running. - tablet_62044.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - tablet_31981.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - for t in [tablet_62344, tablet_62044, tablet_31981]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent assuming that all the datasets are - # identical. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias], auto_log=True) - - utils.validate_topology(ping_tablets=True) - self._check_master_tablet(tablet_62344) - - # Perform a reparent operation with avoid_master pointing to non-master. It - # should succeed without doing anything. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-avoid_master', tablet_62044.tablet_alias], auto_log=True) - - utils.validate_topology() - self._check_master_tablet(tablet_62344) - - # Perform a reparent operation with avoid_master pointing to master. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-avoid_master', tablet_62344.tablet_alias], auto_log=True) - - utils.validate_topology() - # 62044 is in the same cell and 31981 is in a different cell, so we must - # land on 62044 - self._check_master_tablet(tablet_62044) - - # If we kill the tablet in the same cell as master then reparent - # -avoid_master will fail. - tablet_62344.kill_vttablet() - _, stderr = utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-avoid_master', tablet_62044.tablet_alias], - auto_log=True, - expect_fail=True) - self.assertIn('cannot find a tablet to reparent to', stderr) - - utils.validate_topology() - self._check_master_tablet(tablet_62044) - - tablet.kill_tablets([tablet_62344, tablet_62044, tablet_41983, - tablet_31981]) - - # assume a different entity is doing the reparent, and telling us it was done - def test_reparent_from_outside(self): - self._test_reparent_from_outside(brutal=False) - - def test_reparent_from_outside_brutal(self): - self._test_reparent_from_outside(brutal=True) - - def _test_reparent_from_outside(self, brutal=False): - """This test will start a master and 3 slaves. - - Then: - - one slave will be the new master - - one slave will be reparented to that new master - - one slave will be busted and dead in the water - and we'll call TabletExternallyReparented. - - Args: - brutal: kills the old master first - """ - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - for t in [tablet_62344, tablet_62044, tablet_41983, tablet_31981]: - t.create_db('vt_test_keyspace') - - # Start up a master mysql and vttablet - tablet_62344.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - - # Create a few slaves for testing reparenting. - tablet_62044.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - tablet_41983.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - tablet_31981.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - - # wait for all tablets to start - for t in [tablet_62344, tablet_62044, tablet_41983, tablet_31981]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Reparent as a starting point - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias], auto_log=True) - - # now manually reparent 1 out of 2 tablets - # 62044 will be the new master - # 31981 won't be re-parented, so it will be busted - - # Shutdown the old master first. - if not brutal: - tablet_62344.mquery('', mysql_flavor().demote_master_commands()) - - # Get the position of the old master and wait for the new one to catch up. - utils.wait_for_replication_pos(tablet_62344, tablet_62044) - - # Promote the new master. - tablet_62044.mquery('', mysql_flavor().promote_slave_commands()) - new_pos = mysql_flavor().master_position(tablet_62044) - logging.debug('New master position: %s', str(new_pos)) - # Use 'localhost' as hostname because Travis CI worker hostnames - # are too long for MySQL replication. - change_master_cmds = mysql_flavor().change_master_commands( - 'localhost', - tablet_62044.mysql_port, - new_pos) - - # 62344 will now be a slave of 62044 - tablet_62344.mquery('', ['RESET MASTER', 'RESET SLAVE'] + - change_master_cmds + - ['START SLAVE']) - - # 41983 will be a slave of 62044 - tablet_41983.mquery('', ['STOP SLAVE'] + - change_master_cmds + - ['START SLAVE']) - - # in brutal mode, we kill the old master first - # and delete its tablet record - if brutal: - tablet_62344.kill_vttablet() - utils.run_vtctl(['DeleteTablet', '-allow_master', - tablet_62344.tablet_alias], auto_log=True) - - base_time = time.time() - - # update topology with the new server - utils.run_vtctl(['TabletExternallyReparented', tablet_62044.tablet_alias], - mode=utils.VTCTL_VTCTL, auto_log=True) - - self._test_reparent_from_outside_check(brutal, base_time) - - if not brutal: - tablet_62344.kill_vttablet() - tablet.kill_tablets([tablet_31981, tablet_62044, tablet_41983]) - - def _test_reparent_from_outside_check(self, brutal, base_time): - - # make sure the shard replication graph is fine - shard_replication = utils.run_vtctl_json(['GetShardReplication', 'test_nj', - 'test_keyspace/0']) - hashed_nodes = {} - for node in shard_replication['nodes']: - key = node['tablet_alias']['cell']+'-'+str(node['tablet_alias']['uid']) - hashed_nodes[key] = True - logging.debug('Got shard replication nodes: %s', str(hashed_nodes)) - expected_nodes = { - 'test_nj-41983': True, - 'test_nj-62044': True, - } - if not brutal: - expected_nodes['test_nj-62344'] = True - self.assertEqual(expected_nodes, hashed_nodes, - 'Got unexpected nodes: %s != %s' % (str(expected_nodes), - str(hashed_nodes))) - - # make sure the master status page says it's the master - tablet_62044_master_status = tablet_62044.get_status() - self.assertIn('Keyspace: test_keyspace Shard: 0 Tablet Type: MASTER', - tablet_62044_master_status) - - # make sure the master health stream says it's the master too - # (health check is disabled on these servers, force it first) - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - tablet_62044.tablet_alias]) - self.assertEqual(health['target']['tablet_type'], topodata_pb2.MASTER) - # have to compare the int version, or the rounding errors can break - self.assertTrue( - health['tablet_externally_reparented_timestamp'] >= int(base_time)) - - def test_reparent_with_down_slave(self, shard_id='0'): - """See if a missing slave can be safely reparented after the fact.""" - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - tablet_41983.create_db('vt_test_keyspace') - tablet_31981.create_db('vt_test_keyspace') - - # Start up a master mysql and vttablet - tablet_62344.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - - # Create a few slaves for testing reparenting. - tablet_62044.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_31981.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - tablet_41983.init_tablet('replica', 'test_keyspace', shard_id, start=True, - wait_for_start=False) - - # wait for all tablets to start - for t in [tablet_62344, tablet_62044, tablet_31981, tablet_41983]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent assuming that all the datasets are identical. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/' + shard_id, - tablet_62344.tablet_alias]) - utils.validate_topology(ping_tablets=True) - tablet_62344.mquery('vt_test_keyspace', self._create_vt_insert_test) - - utils.wait_procs([tablet_41983.shutdown_mysql()]) - - # Perform a graceful reparent operation. It will fail as one tablet is down. - _, stderr = utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/' + shard_id, - '-new_master', tablet_62044.tablet_alias], - expect_fail=True) - self.assertIn('TabletManager.SetMaster on test_nj-0000041983 error', stderr) - - # insert data into the new master, check the connected slaves work - self._populate_vt_insert_test(tablet_62044, 3) - self._check_vt_insert_test(tablet_31981, 3) - self._check_vt_insert_test(tablet_62344, 3) - - # restart mysql on the old slave, should still be connecting to the - # old master - utils.wait_procs([tablet_41983.start_mysql()]) - - utils.pause('check orphan') - - # Use the same PlannedReparentShard command to fix up the tablet. - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/' + shard_id, - '-new_master', tablet_62044.tablet_alias]) - - # wait until it gets the data - self._check_vt_insert_test(tablet_41983, 3) - - tablet.kill_tablets([tablet_62344, tablet_62044, tablet_41983, - tablet_31981]) - - def test_change_type_semi_sync(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # Create new names for tablets, so this test is less confusing. - master = tablet_62344 - replica = tablet_62044 - rdonly1 = tablet_41983 - rdonly2 = tablet_31981 - - # create the database so vttablets start, as they are serving - for t in [master, replica, rdonly1, rdonly2]: - t.create_db('vt_test_keyspace') - - # Start up a soon-to-be master, one replica and two rdonly. - master.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - replica.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - rdonly1.init_tablet('rdonly', 'test_keyspace', '0', start=True, - wait_for_start=False) - rdonly2.init_tablet('rdonly', 'test_keyspace', '0', start=True, - wait_for_start=False) - for t in [master, replica, rdonly1, rdonly2]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent assuming that all the datasets are - # identical. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - master.tablet_alias], auto_log=True) - utils.validate_topology(ping_tablets=True) - self._check_master_tablet(master) - - # Stop replication on rdonly1, to make sure when we make it - # replica it doesn't start again. - # Note we do a similar test for replica -> rdonly below. - utils.run_vtctl(['StopSlave', rdonly1.tablet_alias]) - - # Check semi-sync on slaves. - # The flag is only an indication of the value to use next time - # we turn replication on, so also check the status. - # rdonly1 is not replicating, so its status is off. - replica.check_db_var('rpl_semi_sync_slave_enabled', 'ON') - rdonly1.check_db_var('rpl_semi_sync_slave_enabled', 'OFF') - rdonly2.check_db_var('rpl_semi_sync_slave_enabled', 'OFF') - replica.check_db_status('rpl_semi_sync_slave_status', 'ON') - rdonly1.check_db_status('rpl_semi_sync_slave_status', 'OFF') - rdonly2.check_db_status('rpl_semi_sync_slave_status', 'OFF') - - # Change replica to rdonly while replicating, should turn off semi-sync, - # and restart replication. - utils.run_vtctl(['ChangeSlaveType', replica.tablet_alias, 'rdonly'], - auto_log=True) - replica.check_db_var('rpl_semi_sync_slave_enabled', 'OFF') - replica.check_db_status('rpl_semi_sync_slave_status', 'OFF') - - # Change rdonly1 to replica, should turn on semi-sync, and not start rep. - utils.run_vtctl(['ChangeSlaveType', rdonly1.tablet_alias, 'replica'], - auto_log=True) - rdonly1.check_db_var('rpl_semi_sync_slave_enabled', 'ON') - rdonly1.check_db_status('rpl_semi_sync_slave_status', 'OFF') - slave_io_running = 10 - slave_sql_running = 11 - s = rdonly1.mquery('', 'show slave status') - self.assertEqual(s[0][slave_io_running], 'No') - self.assertEqual(s[0][slave_sql_running], 'No') - - # Now change from replica back to rdonly, make sure replication is - # still not enabled. - utils.run_vtctl(['ChangeSlaveType', rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - rdonly1.check_db_var('rpl_semi_sync_slave_enabled', 'OFF') - rdonly1.check_db_status('rpl_semi_sync_slave_status', 'OFF') - s = rdonly1.mquery('', 'show slave status') - self.assertEqual(s[0][slave_io_running], 'No') - self.assertEqual(s[0][slave_sql_running], 'No') - - # Change rdonly2 to replica, should turn on semi-sync, and restart rep. - utils.run_vtctl(['ChangeSlaveType', rdonly2.tablet_alias, 'replica'], - auto_log=True) - rdonly2.check_db_var('rpl_semi_sync_slave_enabled', 'ON') - rdonly2.check_db_status('rpl_semi_sync_slave_status', 'ON') - - # Clean up. - tablet.kill_tablets([master, replica, rdonly1, rdonly2]) - - def test_reparent_doesnt_hang_if_master_fails(self): - """Makes sure a failed master populate doesn't hang.""" - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as they are serving - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - tablet_41983.create_db('vt_test_keyspace') - tablet_31981.create_db('vt_test_keyspace') - - # Start up vttablet - for t in [tablet_62344, tablet_62044, tablet_31981, tablet_41983]: - t.init_tablet('replica', 'test_keyspace', '0', start=True, - wait_for_start=False) - - # wait for all tablets to start - for t in [tablet_62344, tablet_62044, tablet_31981, tablet_41983]: - t.wait_for_vttablet_state('NOT_SERVING') - - # Force the slaves to reparent. Will create the _vt.reparent_journal table. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias]) - utils.validate_topology(ping_tablets=True) - - # Change the schema of the _vt.reparent_journal table, so that - # inserts into it will fail. That will make the master fail. - tablet_62344.mquery('_vt', 'ALTER TABLE reparent_journal' - ' DROP COLUMN replication_position') - - # Perform a planned reparent operation, the master will fail the - # insert. The slaves should then abort right away. If this fails, - # the test will timeout. - _, stderr = utils.run_vtctl(['-wait-time', '3600s', - 'PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/0', - '-new_master', tablet_62044.tablet_alias], - expect_fail=True) - self.assertIn('master failed to PopulateReparentJournal', - stderr) - - # Clean up the tablets. - tablet.kill_tablets([tablet_62344, tablet_62044, tablet_41983, - tablet_31981]) - -if __name__ == '__main__': - utils.main() diff --git a/test/resharding.py b/test/resharding.py deleted file mode 100755 index 1ecfcba5ee1..00000000000 --- a/test/resharding.py +++ /dev/null @@ -1,1281 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This test covers a resharding scenario of an already sharded keyspace. - -We start with shards -80 and 80-. We then split 80- into 80-c0 and c0-. - -This test is the main resharding test. It not only tests the regular resharding -workflow for an horizontal split, but also a lot of error cases and side -effects, like: -- migrating the traffic one cell at a time. -- migrating rdonly traffic back and forth. -- making sure we can't migrate the master until replica and rdonly are migrated. -- has a background thread to insert data during migration. -- tests a destination shard master failover while replication is running. -- tests a filtered replication source replacement while filtered replication - is running. -- tests 'vtctl SourceShardAdd' and 'vtctl SourceShardDelete'. -- makes sure the key range rules are properly enforced on masters. -""" - -import threading -import time - -import logging -import unittest - -import base_sharding -import environment -import tablet -import utils - -from vtproto import topodata_pb2 -from vtdb import keyrange_constants - -# initial shards -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -shard_0_ny_rdonly = tablet.Tablet(cell='ny') -# range 80 - '' -shard_1_master = tablet.Tablet() -shard_1_slave1 = tablet.Tablet() -shard_1_slave2 = tablet.Tablet() -shard_1_ny_rdonly = tablet.Tablet(cell='ny') -shard_1_rdonly1 = tablet.Tablet() - -# split shards -# range 80 - c0 -shard_2_master = tablet.Tablet() -shard_2_replica1 = tablet.Tablet() -shard_2_replica2 = tablet.Tablet() -shard_2_rdonly1 = tablet.Tablet() -# range c0 - '' -shard_3_master = tablet.Tablet() -shard_3_replica = tablet.Tablet() -shard_3_rdonly1 = tablet.Tablet() - -shard_2_tablets = [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_2_rdonly1] -shard_3_tablets = [shard_3_master, shard_3_replica, shard_3_rdonly1] -all_tablets = ([shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_1_master, shard_1_slave1, shard_1_slave2, - shard_1_ny_rdonly, shard_1_rdonly1] + - shard_2_tablets + shard_3_tablets) - - -def setUpModule(): - try: - environment.topo_server().setup() - 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: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - - -# InsertThread will insert a value into the timestamps table, and then -# every 1/5s will update its value with the current timestamp -class InsertThread(threading.Thread): - - def __init__(self, tablet_obj, thread_name, thread_id, user_id, - keyspace_id): - threading.Thread.__init__(self) - self.tablet = tablet_obj - self.thread_name = thread_name - self.thread_id = thread_id - self.user_id = user_id - self.keyspace_id = keyspace_id - self.str_keyspace_id = utils.uint64_to_hex(keyspace_id) - self.done = False - - self.tablet.mquery( - 'vt_test_keyspace', - ['begin', - 'insert into timestamps(id, time_milli, custom_ksid_col) ' - 'values(%d, %d, 0x%x) ' - '/* vtgate:: keyspace_id:%s */ /* user_id:%d */' % - (self.thread_id, long(time.time() * 1000), self.keyspace_id, - self.str_keyspace_id, self.user_id), - 'commit'], - write=True, user='vt_app') - self.start() - - def run(self): - try: - while not self.done: - self.tablet.mquery( - 'vt_test_keyspace', - ['begin', - 'update timestamps set time_milli=%d ' - 'where id=%d /* vtgate:: keyspace_id:%s */ /* user_id:%d */' % - (long(time.time() * 1000), self.thread_id, - self.str_keyspace_id, self.user_id), - 'commit'], - write=True, user='vt_app') - time.sleep(0.2) - except Exception: # pylint: disable=broad-except - logging.exception('InsertThread got exception.') - - -# MonitorLagThread will get values from a database, and compare the timestamp -# to evaluate lag. Since the qps is really low, and we send binlogs as chunks, -# the latency is pretty high (a few seconds). -class MonitorLagThread(threading.Thread): - - def __init__(self, tablet_obj, thread_name, thread_id): - threading.Thread.__init__(self) - self.tablet = tablet_obj - self.thread_name = thread_name - self.thread_id = thread_id - self.done = False - self.max_lag_ms = 0 - self.lag_sum_ms = 0 - self.sample_count = 0 - self.start() - - def run(self): - try: - while not self.done: - result = self.tablet.mquery( - 'vt_test_keyspace', - 'select time_milli from timestamps where id=%d' % - self.thread_id) - if result: - lag_ms = long(time.time() * 1000) - long(result[0][0]) - logging.debug('MonitorLagThread(%s) got %d ms', - self.thread_name, lag_ms) - self.sample_count += 1 - self.lag_sum_ms += lag_ms - if lag_ms > self.max_lag_ms: - self.max_lag_ms = lag_ms - time.sleep(5.0) - except Exception: # pylint: disable=broad-except - logging.exception('MonitorLagThread got exception.') - - -class TestResharding(unittest.TestCase, base_sharding.BaseShardingTest): - - # create_schema will create the same schema on the keyspace - # then insert some values - def _create_schema(self): - if base_sharding.keyspace_id_type == keyrange_constants.KIT_BYTES: - t = 'varbinary(64)' - else: - t = 'bigint(20) unsigned' - # Note that the primary key columns are not defined first on purpose to test - # that a reordered column list is correctly used everywhere in vtworker. - create_table_template = '''create table %s( -custom_ksid_col ''' + t + ''' not null, -msg varchar(64), -id bigint not null, -parent_id bigint not null, -primary key (parent_id, id), -index by_msg (msg) -) Engine=InnoDB''' - create_table_bindata_template = '''create table %s( -custom_ksid_col ''' + t + ''' not null, -id bigint not null, -parent_id bigint not null, -msg bit(8), -primary key (parent_id, id), -index by_msg (msg) -) Engine=InnoDB''' - create_view_template = ( - 'create view %s' - '(parent_id, id, msg, custom_ksid_col)' - 'as select parent_id, id, msg, custom_ksid_col ' - 'from %s') - create_timestamp_table = '''create table timestamps( -id int not null, -time_milli bigint(20) unsigned not null, -custom_ksid_col ''' + t + ''' not null, -primary key (id) -) Engine=InnoDB''' - # Make sure that clone and diff work with tables which have no primary key. - # RBR only because Vitess requires the primary key for query rewrites if - # it is running with statement based replication. - create_no_pk_table = '''create table no_pk( -custom_ksid_col ''' + t + ''' not null, -msg varchar(64), -id bigint not null, -parent_id bigint not null -) Engine=InnoDB''' - create_unrelated_table = '''create table unrelated( -name varchar(64), -primary key (name) -) Engine=InnoDB''' - - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding1'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_template % ('resharding2'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_bindata_template % ('resharding3'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_view_template % ('view1', 'resharding1'), - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_timestamp_table, - 'test_keyspace'], - auto_log=True) - utils.run_vtctl(['ApplySchema', - '-sql=' + create_unrelated_table, - 'test_keyspace'], - auto_log=True) - if base_sharding.use_rbr: - utils.run_vtctl(['ApplySchema', '-sql=' + create_no_pk_table, - 'test_keyspace'], auto_log=True) - - def _insert_startup_values(self): - self._insert_value(shard_0_master, 'resharding1', 1, 'msg1', - 0x1000000000000000) - self._insert_value(shard_1_master, 'resharding1', 2, 'msg2', - 0x9000000000000000) - self._insert_value(shard_1_master, 'resharding1', 3, 'msg3', - 0xD000000000000000) - self._insert_value(shard_0_master, 'resharding3', 1, 'a', - 0x1000000000000000) - self._insert_value(shard_1_master, 'resharding3', 2, 'b', - 0x9000000000000000) - self._insert_value(shard_1_master, 'resharding3', 3, 'c', - 0xD000000000000000) - if base_sharding.use_rbr: - self._insert_value(shard_1_master, 'no_pk', 1, 'msg1', - 0xA000000000000000) - # TODO(github.com/vitessio/vitess/issues/2880): Add more rows here such - # clone and diff would break when the insertion order on source and - # dest shards is different. - - def _check_startup_values(self): - # check first value is in the right shard - for t in shard_2_tablets: - self._check_value(t, 'resharding1', 2, 'msg2', 0x9000000000000000) - self._check_value(t, 'resharding3', 2, 'b', 0x9000000000000000) - for t in shard_3_tablets: - self._check_value(t, 'resharding1', 2, 'msg2', 0x9000000000000000, - should_be_here=False) - self._check_value(t, 'resharding3', 2, 'b', 0x9000000000000000, - should_be_here=False) - - # check second value is in the right shard too - for t in shard_2_tablets: - self._check_value(t, 'resharding1', 3, 'msg3', 0xD000000000000000, - should_be_here=False) - self._check_value(t, 'resharding3', 3, 'c', 0xD000000000000000, - should_be_here=False) - for t in shard_3_tablets: - self._check_value(t, 'resharding1', 3, 'msg3', 0xD000000000000000) - self._check_value(t, 'resharding3', 3, 'c', 0xD000000000000000) - - if base_sharding.use_rbr: - for t in shard_2_tablets: - self._check_value(t, 'no_pk', 1, 'msg1', 0xA000000000000000) - for t in shard_3_tablets: - self._check_value(t, 'no_pk', 1, 'msg1', 0xA000000000000000, - should_be_here=False) - - def _insert_lots(self, count, base=0): - for i in xrange(count): - self._insert_value(shard_1_master, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i) - self._insert_value(shard_1_master, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i) - - def _exec_multi_shard_dmls(self): - mids = [10000001, 10000002, 10000003] - msg_ids = ['msg-id10000001', 'msg-id10000002', 'msg-id10000003'] - keyspace_ids = [0x9000000000000000, 0xD000000000000000, - 0xE000000000000000] - self._insert_multi_value(shard_1_master, 'resharding1', mids, - msg_ids, keyspace_ids) - - mids = [10000004, 10000005] - msg_ids = ['msg-id10000004', 'msg-id10000005'] - keyspace_ids = [0xD000000000000000, 0xE000000000000000] - self._insert_multi_value(shard_1_master, 'resharding1', mids, - msg_ids, keyspace_ids) - - mids = [10000011, 10000012, 10000013] - msg_ids = ['msg-id10000011', 'msg-id10000012', 'msg-id10000013'] - keyspace_ids = [0x9000000000000000, 0xD000000000000000, 0xE000000000000000] - self._insert_multi_value(shard_1_master, 'resharding1', mids, - msg_ids, keyspace_ids) - - # This update targets two shards. - self._exec_non_annotated_update(shard_1_master, 'resharding1', - [10000011, 10000012], 'update1') - # This update targets one shard. - self._exec_non_annotated_update(shard_1_master, 'resharding1', - [10000013], 'update2') - - mids = [10000014, 10000015, 10000016] - msg_ids = ['msg-id10000014', 'msg-id10000015', 'msg-id10000016'] - keyspace_ids = [0x9000000000000000, 0xD000000000000000, 0xE000000000000000] - self._insert_multi_value(shard_1_master, 'resharding1', mids, - msg_ids, keyspace_ids) - - # This delete targets two shards. - self._exec_non_annotated_delete(shard_1_master, 'resharding1', - [10000014, 10000015]) - - # This delete targets one shard. - self._exec_non_annotated_delete(shard_1_master, 'resharding1', [10000016]) - - # repeat DMLs for table with msg as bit(8) - mids = [10000001, 10000002, 10000003] - keyspace_ids = [0x9000000000000000, 0xD000000000000000, - 0xE000000000000000] - self._insert_multi_value(shard_1_master, 'resharding3', mids, - ['a','b','c'], keyspace_ids) - - mids = [10000004, 10000005] - keyspace_ids = [0xD000000000000000, 0xE000000000000000] - self._insert_multi_value(shard_1_master, 'resharding3', mids, - ['d', 'e'], keyspace_ids) - mids = [10000011, 10000012, 10000013] - keyspace_ids = [0x9000000000000000, 0xD000000000000000, 0xE000000000000000] - - self._insert_multi_value(shard_1_master, 'resharding3', mids, - ['k', 'l', 'm'], keyspace_ids) - - # This update targets two shards. - self._exec_non_annotated_update(shard_1_master, 'resharding3', - [10000011, 10000012], 'g') - - # This update targets one shard. - self._exec_non_annotated_update(shard_1_master, 'resharding3', - [10000013], 'h') - - mids = [10000014, 10000015, 10000016] - keyspace_ids = [0x9000000000000000, 0xD000000000000000, 0xE000000000000000] - self._insert_multi_value(shard_1_master, 'resharding3', mids, - ['n', 'o', 'p'], keyspace_ids) - - # This delete targets two shards. - self._exec_non_annotated_delete(shard_1_master, 'resharding3', - [10000014, 10000015]) - - # This delete targets one shard. - self._exec_non_annotated_delete(shard_1_master, 'resharding3', [10000016]) - - def _check_multi_shard_values(self): - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding1', 10000001, 'msg-id10000001', 0x9000000000000000) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding1', 10000002, 'msg-id10000002', 0xD000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding1', 10000003, 'msg-id10000003', 0xE000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding1', 10000001, 'msg-id10000001', 0x9000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding1', 10000002, 'msg-id10000002', 0xD000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding1', 10000003, 'msg-id10000003', 0xE000000000000000) - - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding1', 10000004, 'msg-id10000004', 0xD000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding1', 10000005, 'msg-id10000005', 0xE000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding1', 10000004, 'msg-id10000004', 0xD000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding1', 10000005, 'msg-id10000005', 0xE000000000000000) - - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding1', 10000011, 'update1', 0x9000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding1', 10000012, 'update1', 0xD000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding1', 10000013, 'update2', 0xE000000000000000) - - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica], - 'resharding1', 10000014, 'msg-id10000014', 0x9000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica], - 'resharding1', 10000015, 'msg-id10000015', 0xD000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica], - 'resharding1', 10000016, 'msg-id10000016', 0xF000000000000000, - should_be_here=False) - - # checks for bit(8) table - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding3', 10000001, 'a', 0x9000000000000000) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding3', 10000002, 'b', 0xD000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding3', 10000003, 'c', 0xE000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding3', 10000001, 'a', 0x9000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding3', 10000002, 'b', 0xD000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding3', 10000003, 'c', 0xE000000000000000) - - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding3', 10000004, 'd', 0xD000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding3', 10000005, 'e', 0xE000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding3', 10000004, 'd', 0xD000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding3', 10000005, 'e', 0xE000000000000000) - - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2], - 'resharding3', 10000011, 'g', 0x9000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding3', 10000012, 'g', 0xD000000000000000) - self._check_multi_dbs( - [shard_3_master, shard_3_replica], - 'resharding3', 10000013, 'h', 0xE000000000000000) - - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica], - 'resharding3', 10000014, 'n', 0x9000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica], - 'resharding3', 10000015, 'o', 0xD000000000000000, - should_be_here=False) - self._check_multi_dbs( - [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_3_master, shard_3_replica], - 'resharding3', 10000016, 'p', 0xF000000000000000, - should_be_here=False) - - # _check_multi_dbs checks the row in multiple dbs. - def _check_multi_dbs(self, dblist, table, mid, msg, keyspace_id, - should_be_here=True): - for db in dblist: - self._check_value(db, table, mid, msg, keyspace_id, should_be_here) - - # _check_lots returns how many of the values we have, in percents. - def _check_lots(self, count, base=0): - found = 0 - for i in xrange(count): - if self._is_value_present_and_correct(shard_2_replica2, 'resharding1', - 10000 + base + i, 'msg-range1-%d' % - i, 0xA000000000000000 + base + i): - found += 1 - if self._is_value_present_and_correct(shard_3_replica, 'resharding1', - 20000 + base + i, 'msg-range2-%d' % - i, 0xE000000000000000 + base + i): - found += 1 - percent = found * 100 / count / 2 - logging.debug('I have %d%% of the data', percent) - return percent - - def _check_lots_timeout(self, count, threshold, timeout, base=0): - while True: - value = self._check_lots(count, base=base) - if value >= threshold: - return value - timeout = utils.wait_step('waiting for %d%% of the data' % threshold, - timeout, sleep_time=1) - - # _check_lots_not_present makes sure no data is in the wrong shard - def _check_lots_not_present(self, count, base=0): - for i in xrange(count): - self._check_value(shard_3_replica, 'resharding1', 10000 + base + i, - 'msg-range1-%d' % i, 0xA000000000000000 + base + i, - should_be_here=False) - self._check_value(shard_2_replica2, 'resharding1', 20000 + base + i, - 'msg-range2-%d' % i, 0xE000000000000000 + base + i, - should_be_here=False) - - def test_resharding(self): - # we're going to reparent and swap these two - global shard_2_master, shard_2_replica1 - - utils.run_vtctl(['CreateKeyspace', - '--sharding_column_name', 'bad_column', - '--sharding_column_type', 'bytes', - 'test_keyspace']) - utils.run_vtctl(['SetKeyspaceShardingInfo', 'test_keyspace', - 'custom_ksid_col', 'uint64'], expect_fail=True) - utils.run_vtctl(['SetKeyspaceShardingInfo', '-force', - 'test_keyspace', - 'custom_ksid_col', base_sharding.keyspace_id_type]) - - shard_0_master.init_tablet('replica', 'test_keyspace', '-80') - shard_0_replica.init_tablet('replica', 'test_keyspace', '-80') - shard_0_ny_rdonly.init_tablet('rdonly', 'test_keyspace', '-80') - shard_1_master.init_tablet('replica', 'test_keyspace', '80-') - shard_1_slave1.init_tablet('replica', 'test_keyspace', '80-') - shard_1_slave2.init_tablet('replica', 'test_keyspace', '80-') - shard_1_ny_rdonly.init_tablet('rdonly', 'test_keyspace', '80-') - shard_1_rdonly1.init_tablet('rdonly', 'test_keyspace', '80-') - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - ks = utils.run_vtctl_json(['GetSrvKeyspace', 'test_nj', 'test_keyspace']) - self.assertEqual(ks['sharding_column_name'], 'custom_ksid_col') - - # we set full_mycnf_args to True as a test in the KIT_BYTES case - full_mycnf_args = (base_sharding.keyspace_id_type == - keyrange_constants.KIT_BYTES) - - # create databases so vttablet can start behaving somewhat normally - for t in [shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_1_master, shard_1_slave1, shard_1_slave2, shard_1_ny_rdonly, - shard_1_rdonly1]: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None, full_mycnf_args=full_mycnf_args, - binlog_use_v3_resharding_mode=False) - - # wait for the tablets (replication is not setup, they won't be healthy) - for t in [shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_1_master, shard_1_slave1, shard_1_slave2, shard_1_ny_rdonly, - shard_1_rdonly1]: - t.wait_for_vttablet_state('NOT_SERVING') - - # reparent to make the tablets work - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - # check the shards - shards = utils.run_vtctl_json(['FindAllShardsInKeyspace', 'test_keyspace']) - self.assertIn('-80', shards, 'unexpected shards: %s' % str(shards)) - self.assertIn('80-', shards, 'unexpected shards: %s' % str(shards)) - self.assertEqual(len(shards), 2, 'unexpected shards: %s' % str(shards)) - - # create the tables - self._create_schema() - self._insert_startup_values() - - # run a health check on source replicas so they respond to discovery - # (for binlog players) and on the source rdonlys (for workers) - for t in [shard_0_replica, shard_1_slave1]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - for t in [shard_0_ny_rdonly, shard_1_ny_rdonly, shard_1_rdonly1]: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - # create the split shards - shard_2_master.init_tablet('replica', 'test_keyspace', '80-c0') - shard_2_replica1.init_tablet('replica', 'test_keyspace', '80-c0') - shard_2_replica2.init_tablet('replica', 'test_keyspace', '80-c0') - shard_2_rdonly1.init_tablet('rdonly', 'test_keyspace', '80-c0') - shard_3_master.init_tablet('replica', 'test_keyspace', 'c0-') - shard_3_replica.init_tablet('replica', 'test_keyspace', 'c0-') - shard_3_rdonly1.init_tablet('rdonly', 'test_keyspace', 'c0-') - - # start vttablet on the split shards (no db created, - # so they're all not serving) - shard_2_master.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - shard_3_master.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - for t in [shard_2_replica1, shard_2_replica2, shard_2_rdonly1, - shard_3_replica, shard_3_rdonly1]: - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=False) - for t in [shard_2_master, shard_2_replica1, shard_2_replica2, - shard_2_rdonly1, - shard_3_master, shard_3_replica, shard_3_rdonly1]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-c0', - shard_2_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/c0-', - shard_3_master.tablet_alias], auto_log=True) - - # check the shards - shards = utils.run_vtctl_json(['FindAllShardsInKeyspace', 'test_keyspace']) - for s in ['-80', '80-', '80-c0', 'c0-']: - self.assertIn(s, shards, 'unexpected shards: %s' % str(shards)) - self.assertEqual(len(shards), 4, 'unexpected shards: %s' % str(shards)) - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], - auto_log=True) - utils.check_srv_keyspace( - 'test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # disable shard_1_slave2, so we're sure filtered replication will go - # from shard_1_slave1 - utils.run_vtctl(['ChangeSlaveType', shard_1_slave2.tablet_alias, 'spare']) - shard_1_slave2.wait_for_vttablet_state('NOT_SERVING') - - # we need to create the schema, and the worker will do data copying - for keyspace_shard in ('test_keyspace/80-c0', 'test_keyspace/c0-'): - utils.run_vtctl(['CopySchemaShard', '--exclude_tables', 'unrelated', - shard_1_rdonly1.tablet_alias, keyspace_shard], - auto_log=True) - - # Run vtworker as daemon for the following SplitClone commands. - worker_proc, worker_port, worker_rpc_port = utils.run_vtworker_bg( - ['--cell', 'test_nj', '--command_display_interval', '10ms', - '--use_v3_resharding_mode=false'], - auto_log=True) - - # Copy the data from the source to the destination shards. - # --max_tps is only specified to enable the throttler and ensure that the - # code is executed. But the intent here is not to throttle the test, hence - # the rate limit is set very high. - # - # Initial clone (online). - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--offline=false', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - '--max_tps', '9999', - 'test_keyspace/80-'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 2, 0, 0, 0) - - # Reset vtworker such that we can run the next command. - workerclient_proc = utils.run_vtworker_client_bg(['Reset'], worker_rpc_port) - utils.wait_procs([workerclient_proc]) - - # Test the correct handling of keyspace_id changes which happen after - # the first clone. - # Let row 2 go to shard 3 instead of shard 2. - shard_1_master.mquery('vt_test_keyspace', - 'update resharding1 set' - ' custom_ksid_col=0xD000000000000000 WHERE id=2', - write=True) - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--offline=false', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - '--max_tps', '9999', - 'test_keyspace/80-'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - # Row 2 will be deleted from shard 2 and inserted to shard 3. - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 1, 0, 1, 1) - self._check_value(shard_2_master, 'resharding1', 2, 'msg2', - 0xD000000000000000, should_be_here=False) - self._check_value(shard_3_master, 'resharding1', 2, 'msg2', - 0xD000000000000000) - # Reset vtworker such that we can run the next command. - workerclient_proc = utils.run_vtworker_client_bg(['Reset'], worker_rpc_port) - utils.wait_procs([workerclient_proc]) - - # Move row 2 back to shard 2 from shard 3 by changing the keyspace_id again. - shard_1_master.mquery('vt_test_keyspace', - 'update resharding1 set' - ' custom_ksid_col=0x9000000000000000 WHERE id=2', - write=True) - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--offline=false', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - '--max_tps', '9999', - 'test_keyspace/80-'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - # Row 2 will be deleted from shard 3 and inserted to shard 2. - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 1, 0, 1, 1) - self._check_value(shard_2_master, 'resharding1', 2, 'msg2', - 0x9000000000000000) - self._check_value(shard_3_master, 'resharding1', 2, 'msg2', - 0x9000000000000000, should_be_here=False) - # Reset vtworker such that we can run the next command. - workerclient_proc = utils.run_vtworker_client_bg(['Reset'], worker_rpc_port) - utils.wait_procs([workerclient_proc]) - - # Modify the destination shard. SplitClone will revert the changes. - # Delete row 2 (provokes an insert). - shard_2_master.mquery('vt_test_keyspace', - 'delete from resharding1 where id=2', write=True) - # Update row 3 (provokes an update). - shard_3_master.mquery('vt_test_keyspace', - "update resharding1 set msg='msg-not-3' where id=3", - write=True) - # Insert row 4 and 5 (provokes a delete). - self._insert_value(shard_3_master, 'resharding1', 4, 'msg4', - 0xD000000000000000) - self._insert_value(shard_3_master, 'resharding1', 5, 'msg5', - 0xD000000000000000) - - workerclient_proc = utils.run_vtworker_client_bg( - ['SplitClone', - '--exclude_tables', 'unrelated', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_rdonly_tablets', '1', - '--max_tps', '9999', - 'test_keyspace/80-'], - worker_rpc_port) - utils.wait_procs([workerclient_proc]) - # Change tablet, which was taken offline, back to rdonly. - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly1.tablet_alias, - 'rdonly'], auto_log=True) - self.verify_reconciliation_counters(worker_port, 'Online', 'resharding1', - 1, 1, 2, 0) - self.verify_reconciliation_counters(worker_port, 'Offline', 'resharding1', - 0, 0, 0, 2) - # Terminate worker daemon because it is no longer needed. - utils.kill_sub_process(worker_proc, soft=True) - - # check the startup values are in the right place - self._check_startup_values() - - # check the schema too - utils.run_vtctl(['ValidateSchemaKeyspace', '--exclude_tables=unrelated', - 'test_keyspace'], auto_log=True) - - # Verify vreplication table entries - result = shard_2_master.mquery('_vt', 'select * from vreplication') - self.assertEqual(len(result), 1) - self.assertEqual(result[0][1], 'SplitClone') - self.assertEqual(result[0][2], - 'keyspace:"test_keyspace" shard:"80-" ' - 'key_range: ') - - result = shard_3_master.mquery('_vt', 'select * from vreplication') - self.assertEqual(len(result), 1) - self.assertEqual(result[0][1], 'SplitClone') - self.assertEqual(result[0][2], - 'keyspace:"test_keyspace" shard:"80-" key_range: ') - - # check the binlog players are running and exporting vars - self.check_destination_master(shard_2_master, ['test_keyspace/80-']) - self.check_destination_master(shard_3_master, ['test_keyspace/80-']) - # When the binlog players/filtered replication is turned on, the query - # service must be turned off on the destination masters. - # The tested behavior is a safeguard to prevent that somebody can - # accidentally modify data on the destination masters while they are not - # migrated yet and the source shards are still the source of truth. - shard_2_master.wait_for_vttablet_state('NOT_SERVING') - shard_3_master.wait_for_vttablet_state('NOT_SERVING') - - # check that binlog server exported the stats vars - self.check_binlog_server_vars(shard_1_slave1, horizontal=True) - - # Check that the throttler was enabled. - # The stream id is hard-coded as 1, which is the first id generated - # through auto-inc. - self.check_throttler_service(shard_2_master.rpc_endpoint(), - ['BinlogPlayer/1'], 9999) - self.check_throttler_service(shard_3_master.rpc_endpoint(), - ['BinlogPlayer/1'], 9999) - - # testing filtered replication: insert a bunch of data on shard 1, - # check we get most of it after a few seconds, wait for binlog server - # timeout, check we get all of it. - logging.debug('Inserting lots of data on source shard') - self._insert_lots(1000) - logging.debug('Executing MultiValue Insert Queries') - self._exec_multi_shard_dmls() - logging.debug('Checking 80 percent of data is sent quickly') - v = self._check_lots_timeout(1000, 80, 5) - if v != 100: - # small optimization: only do this check if we don't have all the data - # already anyway. - logging.debug('Checking all data goes through eventually') - self._check_lots_timeout(1000, 100, 20) - logging.debug('Checking no data was sent the wrong way') - self._check_lots_not_present(1000) - - logging.debug('Checking MultiValue Insert Queries') - self._check_multi_shard_values() - self.check_binlog_player_vars(shard_2_master, ['test_keyspace/80-'], - seconds_behind_master_max=30) - self.check_binlog_player_vars(shard_3_master, ['test_keyspace/80-'], - seconds_behind_master_max=30) - self.check_binlog_server_vars(shard_1_slave1, horizontal=True, - min_statements=1000, min_transactions=1000) - - # use vtworker to compare the data (after health-checking the destination - # rdonly tablets so discovery works) - utils.run_vtctl(['RunHealthCheck', shard_3_rdonly1.tablet_alias]) - - if base_sharding.use_multi_split_diff: - logging.debug('Running vtworker MultiSplitDiff') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'MultiSplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/80-'], - auto_log=True) - else: - logging.debug('Running vtworker SplitDiff') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/c0-'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_3_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - - utils.pause('Good time to test vtworker for diffs') - - # get status for destination master tablets, make sure we have it all - 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, 4036, 2016) - self.check_running_binlog_player(shard_3_master, 4056, 2016) - else: - self.check_running_binlog_player(shard_2_master, 4044, 2016) - self.check_running_binlog_player(shard_3_master, 4048, 2016) - - # start a thread to insert data into shard_1 in the background - # with current time, and monitor the delay - insert_thread_1 = InsertThread(shard_1_master, 'insert_low', 1, 10000, - 0x9000000000000000) - insert_thread_2 = InsertThread(shard_1_master, 'insert_high', 2, 10001, - 0xD000000000000000) - monitor_thread_1 = MonitorLagThread(shard_2_replica2, 'insert_low', 1) - monitor_thread_2 = MonitorLagThread(shard_3_replica, 'insert_high', 2) - - # tests a failover switching serving to a different replica - utils.run_vtctl(['ChangeSlaveType', shard_1_slave2.tablet_alias, 'replica']) - utils.run_vtctl(['ChangeSlaveType', shard_1_slave1.tablet_alias, 'spare']) - shard_1_slave2.wait_for_vttablet_state('SERVING') - shard_1_slave1.wait_for_vttablet_state('NOT_SERVING') - utils.run_vtctl(['RunHealthCheck', shard_1_slave2.tablet_alias]) - - # test data goes through again - logging.debug('Inserting lots of data on source shard') - self._insert_lots(1000, base=1000) - logging.debug('Checking 80 percent of data was sent quickly') - self._check_lots_timeout(1000, 80, 5, base=1000) - self.check_binlog_server_vars(shard_1_slave2, horizontal=True, - min_statements=800, min_transactions=800) - - # check we can't migrate the master just yet - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'master'], - expect_fail=True) - - # check query service is off on master 2 and master 3, as filtered - # replication is enabled. Even health check that is enabled on - # master 3 should not interfere (we run it to be sure). - utils.run_vtctl(['RunHealthCheck', shard_3_master.tablet_alias], - auto_log=True) - for master in [shard_2_master, shard_3_master]: - utils.check_tablet_query_service(self, master, False, False) - stream_health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - master.tablet_alias]) - logging.debug('Got health: %s', str(stream_health)) - self.assertIn('realtime_stats', stream_health) - self.assertNotIn('serving', stream_health) - - # check the destination master 3 is healthy, even though its query - # service is not running (if not healthy this would exception out) - shard_3_master.get_healthz() - - # now serve rdonly from the split shards, in test_nj only - utils.run_vtctl(['MigrateServedTypes', '--cells=test_nj', - 'test_keyspace/80-', 'rdonly'], auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_srv_keyspace('test_ny', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_0_ny_rdonly, True, False) - utils.check_tablet_query_service(self, shard_1_ny_rdonly, True, False) - utils.check_tablet_query_service(self, shard_1_rdonly1, False, True) - - # Shouldn't be able to rebuild keyspace graph while migration is on going - # (i.e there are records that have tablet controls set) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], - auto_log=True, - expect_fail=True, - ) - - # rerun migrate to ensure it doesn't fail - # skip refresh to make it go faster - utils.run_vtctl(['MigrateServedTypes', '--cells=test_nj', - '-skip-refresh-state=true', - 'test_keyspace/80-', 'rdonly'], auto_log=True) - - # now serve rdonly from the split shards, everywhere - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'rdonly'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_srv_keyspace('test_ny', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_0_ny_rdonly, True, False) - utils.check_tablet_query_service(self, shard_1_ny_rdonly, False, True) - utils.check_tablet_query_service(self, shard_1_rdonly1, False, True) - - # rerun migrate to ensure it doesn't fail - # skip refresh to make it go faster - utils.run_vtctl(['MigrateServedTypes', '-skip-refresh-state=true', - 'test_keyspace/80-', 'rdonly'], auto_log=True) - - # then serve replica from the split shards - destination_shards = ['80-c0', 'c0-'] - - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'replica'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-c0 c0-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_1_slave2, False, True) - - # move replica back and forth - utils.run_vtctl( - ['MigrateServedTypes', '-reverse', 'test_keyspace/80-', 'replica'], - auto_log=True) - # After a backwards migration, queryservice should be enabled on - # source and disabled on destinations - utils.check_tablet_query_service(self, shard_1_slave2, True, False) - # Destination tablets would have query service disabled for other - # reasons than the migration, so check the shard record instead of - # the tablets directly. - utils.check_shard_query_services(self, 'test_nj', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, False) - utils.check_shard_query_services(self, 'test_ny', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, False) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'replica'], - auto_log=True) - # After a forwards migration, queryservice should be disabled on - # source and enabled on destinations - utils.check_tablet_query_service(self, shard_1_slave2, False, True) - # Destination tablets would have query service disabled for other - # reasons than the migration, so check the shard record instead of - # the tablets directly - utils.check_shard_query_services(self, 'test_nj', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, True) - utils.check_shard_query_services(self, 'test_ny', 'test_keyspace', destination_shards, - topodata_pb2.REPLICA, True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-c0 c0-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - - # reparent shard_2 to shard_2_replica1, then insert more data and - # see it flow through still - utils.run_vtctl(['PlannedReparentShard', - '-keyspace_shard', 'test_keyspace/80-c0', - '-new_master', shard_2_replica1.tablet_alias]) - - # update our test variables to point at the new master - shard_2_master, shard_2_replica1 = shard_2_replica1, shard_2_master - - utils.pause('check state of _vt.vreplication') - - logging.debug('Inserting lots of data on source shard after reparenting') - self._insert_lots(3000, base=2000) - logging.debug('Checking 80 percent of data was sent fairly quickly') - self._check_lots_timeout(3000, 80, 10, base=2000) - - # use vtworker to compare the data again - if base_sharding.use_multi_split_diff: - logging.debug('Running vtworker MultiSplitDiff') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'MultiSplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/80-'], - auto_log=True) - else: - logging.debug('Running vtworker SplitDiff') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--exclude_tables', 'unrelated', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/c0-'], - auto_log=True) - - utils.run_vtctl(['ChangeSlaveType', shard_1_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - utils.run_vtctl(['ChangeSlaveType', shard_3_rdonly1.tablet_alias, 'rdonly'], - auto_log=True) - - # going to migrate the master now, check the delays - monitor_thread_1.done = True - monitor_thread_2.done = True - insert_thread_1.done = True - insert_thread_2.done = True - logging.debug('DELAY 1: %s max_lag=%d ms avg_lag=%d ms', - monitor_thread_1.thread_name, - monitor_thread_1.max_lag_ms, - monitor_thread_1.lag_sum_ms / monitor_thread_1.sample_count) - logging.debug('DELAY 2: %s max_lag=%d ms avg_lag=%d ms', - monitor_thread_2.thread_name, - monitor_thread_2.max_lag_ms, - monitor_thread_2.lag_sum_ms / monitor_thread_2.sample_count) - - # mock with the SourceShard records to test 'vtctl SourceShardDelete' - # and 'vtctl SourceShardAdd' - utils.run_vtctl(['SourceShardDelete', 'test_keyspace/c0-', '1'], - auto_log=True) - utils.run_vtctl(['SourceShardAdd', '--key_range=80-', - 'test_keyspace/c0-', '1', 'test_keyspace/80-'], - auto_log=True) - - # CancelResharding should fail because migration has started. - utils.run_vtctl(['CancelResharding', 'test_keyspace/80-'], - auto_log=True, expect_fail=True) - - # do a Migrate that will fail waiting for replication - # which should cause the Migrate to be canceled and the source - # master to be serving again. - utils.run_vtctl(['MigrateServedTypes', - '-filtered_replication_wait_time', '0s', - 'test_keyspace/80-', 'master'], - auto_log=True, expect_fail=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-c0 c0-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_1_master, True, False) - - # sabotage master migration and make it fail in an unfinished state. - utils.run_vtctl(['SetShardTabletControl', '-blacklisted_tables=t', - 'test_keyspace/c0-', 'master'], auto_log=True) - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'master'], - auto_log=True, expect_fail=True) - - # Query service is disabled in source shard as failure occurred after point of no return - utils.check_tablet_query_service(self, shard_1_master, False, True) - - # Global topology records should not change as migration did not succeed - shard = utils.run_vtctl_json(['GetShard', 'test_keyspace/80-']) - self.assertEqual(shard['is_master_serving'], True, 'source shards should be set in destination shard') - - shard = utils.run_vtctl_json(['GetShard', 'test_keyspace/c0-']) - self.assertEqual(len(shard['source_shards']), 1, 'source shards should be set in destination shard') - self.assertEqual(shard['is_master_serving'], False, 'source shards should be set in destination shard') - - shard = utils.run_vtctl_json(['GetShard', 'test_keyspace/80-c0']) - self.assertEqual(len(shard['source_shards']), 1, 'source shards should be set in destination shard') - self.assertEqual(shard['is_master_serving'], False, 'source shards should be set in destination shard') - - # remove sabotage, but make it fail early. This should not result - # in the source master serving, because this failure is past the - # point of no return. - utils.run_vtctl(['SetShardTabletControl', '-blacklisted_tables=t', - '-remove', 'test_keyspace/c0-', 'master'], auto_log=True) - utils.run_vtctl(['MigrateServedTypes', - '-filtered_replication_wait_time', '0s', - 'test_keyspace/80-', 'master'], - auto_log=True, expect_fail=True) - - utils.check_tablet_query_service(self, shard_1_master, False, True) - - # do the migration that's expected to succeed - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/80-', 'master'], - auto_log=True) - utils.check_srv_keyspace('test_nj', 'test_keyspace', - 'Partitions(master): -80 80-c0 c0-\n' - 'Partitions(rdonly): -80 80-c0 c0-\n' - 'Partitions(replica): -80 80-c0 c0-\n', - keyspace_id_type=base_sharding.keyspace_id_type, - sharding_column_name='custom_ksid_col') - utils.check_tablet_query_service(self, shard_1_master, False, True) - - # check destination shards are serving - - utils.check_tablet_query_service(self, shard_2_master, True, False) - utils.check_tablet_query_service(self, shard_3_master, True, False) - - # check the binlog players are gone now - self.check_no_binlog_player(shard_2_master) - self.check_no_binlog_player(shard_3_master) - - # test reverse_replication - # start with inserting a row in each destination shard - self._insert_value(shard_2_master, 'resharding2', 2, 'msg2', - 0x9000000000000000) - self._insert_value(shard_3_master, 'resharding2', 3, 'msg3', - 0xD000000000000000) - # ensure the rows are not present yet - self._check_value(shard_1_master, 'resharding2', 2, 'msg2', - 0x9000000000000000, should_be_here=False) - self._check_value(shard_1_master, 'resharding2', 3, 'msg3', - 0xD000000000000000, should_be_here=False) - # repeat the migration with reverse_replication - utils.run_vtctl(['MigrateServedTypes', '-reverse_replication=true', - 'test_keyspace/80-', 'master'], auto_log=True) - # look for the rows in the original master after a short wait - time.sleep(1.0) - self._check_value(shard_1_master, 'resharding2', 2, 'msg2', - 0x9000000000000000) - self._check_value(shard_1_master, 'resharding2', 3, 'msg3', - 0xD000000000000000) - - # retry the migration to ensure it now fails - utils.run_vtctl(['MigrateServedTypes', '-reverse_replication=true', - 'test_keyspace/80-', 'master'], - auto_log=True, expect_fail=True) - - # CancelResharding should now succeed - utils.run_vtctl(['CancelResharding', 'test_keyspace/80-'], auto_log=True) - self.check_no_binlog_player(shard_1_master) - - # delete the original tablets in the original shard - tablet.kill_tablets([shard_1_master, shard_1_slave1, shard_1_slave2, - shard_1_ny_rdonly, shard_1_rdonly1]) - for t in [shard_1_slave1, shard_1_slave2, shard_1_ny_rdonly, - shard_1_rdonly1]: - utils.run_vtctl(['DeleteTablet', t.tablet_alias], auto_log=True) - utils.run_vtctl(['DeleteTablet', '-allow_master', - shard_1_master.tablet_alias], auto_log=True) - - # rebuild the serving graph, all mentions of the old shards should be gone - utils.run_vtctl( - ['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # test RemoveShardCell - utils.run_vtctl( - ['RemoveShardCell', 'test_keyspace/-80', 'test_nj'], auto_log=True, - expect_fail=True) - utils.run_vtctl( - ['RemoveShardCell', 'test_keyspace/80-', 'test_nj'], auto_log=True) - utils.run_vtctl( - ['RemoveShardCell', 'test_keyspace/80-', 'test_ny'], auto_log=True) - shard = utils.run_vtctl_json(['GetShard', 'test_keyspace/80-']) - self.assertTrue('cells' not in shard or not shard['cells']) - - # delete the original shard - utils.run_vtctl(['DeleteShard', 'test_keyspace/80-'], auto_log=True) - - # make sure we can't delete the destination shard now that it's serving - _, stderr = utils.run_vtctl(['DeleteShard', 'test_keyspace/80-c0'], - expect_fail=True) - self.assertIn('is still serving, cannot delete it', stderr) - - # kill everything - tablet.kill_tablets([shard_0_master, shard_0_replica, shard_0_ny_rdonly, - shard_2_master, shard_2_replica1, shard_2_replica2, - shard_2_rdonly1, - shard_3_master, shard_3_replica, shard_3_rdonly1]) - -if __name__ == '__main__': - utils.main() diff --git a/test/resharding_bytes.py b/test/resharding_bytes.py deleted file mode 100755 index 4fccf92d177..00000000000 --- a/test/resharding_bytes.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Re-runs resharding.py with a varbinary keyspace_id.""" - -from vtdb import keyrange_constants - -import base_sharding -import resharding -import utils - -# this test is just re-running an entire resharding.py with a -# varbinary keyspace_id -if __name__ == '__main__': - base_sharding.keyspace_id_type = keyrange_constants.KIT_BYTES - utils.main(resharding) diff --git a/test/resharding_multi_split_diff.py b/test/resharding_multi_split_diff.py deleted file mode 100755 index e4ce4169b12..00000000000 --- a/test/resharding_multi_split_diff.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Re-runs resharding.py with MultiSplitDiff.""" - -import base_sharding -import resharding -import utils - -if __name__ == '__main__': - base_sharding.use_multi_split_diff = True - utils.main(resharding) diff --git a/test/resharding_rbr.py b/test/resharding_rbr.py deleted file mode 100755 index a2037168ab1..00000000000 --- a/test/resharding_rbr.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""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/schema.py b/test/schema.py deleted file mode 100755 index fc1781b646a..00000000000 --- a/test/schema.py +++ /dev/null @@ -1,377 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import os - -import logging -import unittest - -import environment -import tablet -import utils - -shard_0_master = tablet.Tablet() -shard_0_replica1 = tablet.Tablet() -shard_0_replica2 = tablet.Tablet() -shard_0_rdonly = tablet.Tablet() -shard_0_backup = tablet.Tablet() -shard_1_master = tablet.Tablet() -shard_1_replica1 = tablet.Tablet() -shard_2_master = tablet.Tablet() -shard_2_replica1 = tablet.Tablet() - -# shard_2 tablets shouldn't exist yet when _apply_initial_schema() is called. -initial_tablets = [ - shard_0_master, shard_0_replica1, shard_0_replica2, shard_0_rdonly, - shard_0_backup, shard_1_master, shard_1_replica1, -] -shard_2_tablets = [shard_2_master, shard_2_replica1] -all_tablets = initial_tablets + shard_2_tablets - -test_keyspace = 'test_keyspace' -db_name = 'vt_' + test_keyspace - - -def setUpModule(): - try: - environment.topo_server().setup() - - _init_mysql(all_tablets) - - utils.run_vtctl(['CreateKeyspace', test_keyspace]) - - utils.Vtctld().start(enable_schema_change_dir=True) - - except Exception as setup_exception: # pylint: disable=broad-except - try: - tearDownModule() - except Exception as e: # pylint: disable=broad-except - logging.exception('Tearing down a failed setUpModule() failed: %s', e) - raise setup_exception - - -def _init_mysql(tablets): - setup_procs = [] - for t in tablets: - setup_procs.append(t.init_mysql()) - utils.wait_procs(setup_procs) - - -def _setup_shard_2(): - shard_2_master.init_tablet('replica', test_keyspace, '2') - shard_2_replica1.init_tablet('replica', test_keyspace, '2') - - # create databases, start the tablets - for t in shard_2_tablets: - t.create_db(db_name) - t.start_vttablet(wait_for_state=None) - - # wait for the tablets to start - shard_2_master.wait_for_vttablet_state('NOT_SERVING') - shard_2_replica1.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', test_keyspace + '/2', - shard_2_master.tablet_alias], auto_log=True) - utils.run_vtctl(['ValidateKeyspace', '-ping-tablets', test_keyspace]) - - -def _teardown_shard_2(): - tablet.kill_tablets(shard_2_tablets) - - utils.run_vtctl( - ['DeleteShard', '-recursive', '-even_if_serving', 'test_keyspace/2'], - auto_log=True) - - for t in shard_2_tablets: - t.reset_replication() - t.set_semi_sync_enabled(master=False) - t.clean_dbs() - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [] - for t in all_tablets: - teardown_procs.append(t.teardown_mysql()) - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - for t in all_tablets: - t.remove_tree() - - -class TestSchema(unittest.TestCase): - - def setUp(self): - shard_0_master.init_tablet('replica', test_keyspace, '0') - shard_0_replica1.init_tablet('replica', test_keyspace, '0') - shard_0_replica2.init_tablet('replica', test_keyspace, '0') - shard_0_rdonly.init_tablet('rdonly', test_keyspace, '0') - shard_0_backup.init_tablet('backup', test_keyspace, '0') - shard_1_master.init_tablet('replica', test_keyspace, '1') - shard_1_replica1.init_tablet('replica', test_keyspace, '1') - - # create databases, start the tablets - for t in initial_tablets: - t.create_db(db_name) - t.start_vttablet(wait_for_state=None) - - # wait for the tablets to start - for t in initial_tablets: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', test_keyspace + '/0', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', test_keyspace + '/1', - shard_1_master.tablet_alias], auto_log=True) - - def tearDown(self): - # kill all tablets - tablet.kill_tablets(initial_tablets) - - for t in initial_tablets: - t.reset_replication() - t.set_semi_sync_enabled(master=False) - t.clean_dbs() - - utils.run_vtctl(['DeleteShard', '-recursive', '-even_if_serving', - test_keyspace + '/0'], auto_log=True) - utils.run_vtctl(['DeleteShard', '-recursive', '-even_if_serving', - test_keyspace + '/1'], auto_log=True) - - def _check_tables(self, tablet_obj, expected_count): - tables = tablet_obj.mquery(db_name, 'show tables') - self.assertEqual( - len(tables), expected_count, - 'Unexpected table count on %s (not %d): got tables: %s' % - (tablet_obj.tablet_alias, expected_count, str(tables))) - - def _apply_schema(self, keyspace, sql, expect_fail=False): - return utils.run_vtctl(['ApplySchema', - '-sql=' + sql, - keyspace], - expect_fail=expect_fail, auto_log=True) - - def _get_schema(self, tablet_alias): - return utils.run_vtctl_json(['GetSchema', - tablet_alias]) - - def _create_test_table_sql(self, table): - return ( - 'CREATE TABLE %s (\n' - '`id` BIGINT(20) not NULL,\n' - '`msg` varchar(64),\n' - 'PRIMARY KEY (`id`)\n' - ') ENGINE=InnoDB') % table - - def _alter_test_table_sql(self, table, index_column_name): - return ( - 'ALTER TABLE %s\n' - 'ADD COLUMN new_id bigint(20) NOT NULL AUTO_INCREMENT FIRST,\n' - 'DROP PRIMARY KEY,\n' - 'ADD PRIMARY KEY (new_id),\n' - 'ADD INDEX idx_column(%s)\n') % (table, index_column_name) - - def _apply_initial_schema(self): - schema_changes = ';'.join([ - self._create_test_table_sql('vt_select_test01'), - self._create_test_table_sql('vt_select_test02'), - self._create_test_table_sql('vt_select_test03'), - self._create_test_table_sql('vt_select_test04')]) - - # apply schema changes to the test keyspace - self._apply_schema(test_keyspace, schema_changes) - - # check number of tables - self._check_tables(shard_0_master, 4) - self._check_tables(shard_1_master, 4) - - # get schema for each shard - shard_0_schema = self._get_schema(shard_0_master.tablet_alias) - shard_1_schema = self._get_schema(shard_1_master.tablet_alias) - - # all shards should have the same schema - self.assertEqual(shard_0_schema, shard_1_schema) - - def test_schema_changes(self): - self._apply_initial_schema() - - self._apply_schema( - test_keyspace, self._alter_test_table_sql('vt_select_test03', 'msg')) - - shard_0_schema = self._get_schema(shard_0_master.tablet_alias) - shard_1_schema = self._get_schema(shard_1_master.tablet_alias) - - # all shards should have the same schema - self.assertEqual(shard_0_schema, shard_1_schema) - - # test schema changes - os.makedirs(os.path.join(utils.vtctld.schema_change_dir, test_keyspace)) - input_path = os.path.join( - utils.vtctld.schema_change_dir, test_keyspace, 'input') - os.makedirs(input_path) - sql_path = os.path.join(input_path, 'create_test_table_x.sql') - with open(sql_path, 'w') as handler: - handler.write('create table test_table_x (id int)') - - # wait until this sql file being consumed by autoschema - timeout = 10 - while os.path.isfile(sql_path): - timeout = utils.wait_step( - 'waiting for vtctld to pick up schema changes', - timeout, sleep_time=0.2) - - # check number of tables - self._check_tables(shard_0_master, 5) - self._check_tables(shard_1_master, 5) - - def test_schema_changes_drop_and_create(self): - """Tests that a DROP and CREATE table will pass PreflightSchema check. - - PreflightSchema checks each SQL statement separately. When doing so, it must - consider previous statements within the same ApplySchema command. For - example, a CREATE after DROP must not fail: When CREATE is checked, DROP - must have been executed first. - See: https://github.com/vitessio/vitess/issues/1731#issuecomment-222914389 - """ - self._apply_initial_schema() - self._check_tables(shard_0_master, 4) - self._check_tables(shard_1_master, 4) - - drop_and_create = ('DROP TABLE vt_select_test01;\n' + - self._create_test_table_sql('vt_select_test01')) - self._apply_schema(test_keyspace, drop_and_create) - # check number of tables - self._check_tables(shard_0_master, 4) - self._check_tables(shard_1_master, 4) - - def test_schema_changes_preflight_errors_partially(self): - """Tests that some SQL statements fail properly during PreflightSchema.""" - self._apply_initial_schema() - self._check_tables(shard_0_master, 4) - self._check_tables(shard_1_master, 4) - - # Second statement will fail because the table already exists. - create_error = (self._create_test_table_sql('vt_select_test05') + ';\n' + - self._create_test_table_sql('vt_select_test01')) - stdout = self._apply_schema(test_keyspace, create_error, expect_fail=True) - self.assertIn('already exists', ''.join(stdout)) - # check number of tables - self._check_tables(shard_0_master, 4) - self._check_tables(shard_1_master, 4) - - def test_schema_changes_drop_nonexistent_tables(self): - """Tests the PreflightSchema logic for dropping nonexistent tables. - - If a table does not exist, DROP TABLE should error during preflight - because the statement does not change the schema as there is - nothing to drop. - In case of DROP TABLE IF EXISTS though, it should not error as this - is the MySQL behavior the user expects. - """ - self._apply_initial_schema() - self._check_tables(shard_0_master, 4) - self._check_tables(shard_1_master, 4) - - drop_table = ('DROP TABLE nonexistent_table;') - stdout = self._apply_schema(test_keyspace, drop_table, expect_fail=True) - self.assertIn('Unknown table', ''.join(stdout)) - - # This Query may not result in schema change and should be allowed. - drop_if_exists = ('DROP TABLE IF EXISTS nonexistent_table;') - self._apply_schema(test_keyspace, drop_if_exists) - - self._check_tables(shard_0_master, 4) - self._check_tables(shard_1_master, 4) - - def test_vtctl_copyschemashard_use_tablet_as_source(self): - self._test_vtctl_copyschemashard(shard_0_master.tablet_alias) - - def test_vtctl_copyschemashard_use_shard_as_source(self): - self._test_vtctl_copyschemashard('test_keyspace/0') - - def _test_vtctl_copyschemashard(self, source): - # Apply initial schema to the whole keyspace before creating shard 2. - self._apply_initial_schema() - - _setup_shard_2() - - try: - # InitShardMaster creates the db, but there shouldn't be any tables yet. - self._check_tables(shard_2_master, 0) - self._check_tables(shard_2_replica1, 0) - - # Run the command twice to make sure it's idempotent. - for _ in range(2): - utils.run_vtctl(['CopySchemaShard', - source, - 'test_keyspace/2'], - auto_log=True) - - # shard_2_master should look the same as the replica we copied from - self._check_tables(shard_2_master, 4) - utils.wait_for_replication_pos(shard_2_master, shard_2_replica1) - self._check_tables(shard_2_replica1, 4) - shard_0_schema = self._get_schema(shard_0_master.tablet_alias) - shard_2_schema = self._get_schema(shard_2_master.tablet_alias) - self.assertEqual(shard_0_schema, shard_2_schema) - finally: - _teardown_shard_2() - - def test_vtctl_copyschemashard_different_dbs_should_fail(self): - # Apply initial schema to the whole keyspace before creating shard 2. - self._apply_initial_schema() - - _setup_shard_2() - - try: - # InitShardMaster creates the db, but there shouldn't be any tables yet. - self._check_tables(shard_2_master, 0) - self._check_tables(shard_2_replica1, 0) - - # Change the db charset on the destination shard from utf8 to latin1. - # This will make CopySchemaShard fail during its final diff. - # (The different charset won't be corrected on the destination shard - # because we use "CREATE DATABASE IF NOT EXISTS" and this doesn't fail if - # there are differences in the options e.g. the character set.) - shard_2_schema = self._get_schema(shard_2_master.tablet_alias) - self.assertIn('utf8', shard_2_schema['database_schema']) - utils.run_vtctl_json( - ['ExecuteFetchAsDba', '-json', shard_2_master.tablet_alias, - 'ALTER DATABASE vt_test_keyspace CHARACTER SET latin1']) - - _, stderr = utils.run_vtctl(['CopySchemaShard', - 'test_keyspace/0', - 'test_keyspace/2'], - expect_fail=True, - auto_log=True) - self.assertIn('schemas are different', stderr) - - # shard_2_master should have the same number of tables. Only the db - # character set is different. - self._check_tables(shard_2_master, 4) - finally: - _teardown_shard_2() - -if __name__ == '__main__': - utils.main() diff --git a/test/schema_swap_test.py b/test/schema_swap_test.py deleted file mode 100755 index edf381ef6f9..00000000000 --- a/test/schema_swap_test.py +++ /dev/null @@ -1,499 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -import logging -import re -import time -import unittest -import urllib2 - -from vtproto import topodata_pb2 - -import environment -import tablet -import utils - - -# range '' - 80 -shard_0_master = tablet.Tablet(use_mysqlctld=True) -shard_0_replica = tablet.Tablet(use_mysqlctld=True) -shard_0_rdonly = tablet.Tablet(use_mysqlctld=True) -all_shard_0_tablets = (shard_0_master, shard_0_replica, shard_0_rdonly) -# range 80 - '' -shard_1_master = tablet.Tablet(use_mysqlctld=True) -shard_1_replica = tablet.Tablet(use_mysqlctld=True) -shard_1_rdonly = tablet.Tablet(use_mysqlctld=True) -all_shard_1_tablets = (shard_1_master, shard_1_replica, shard_1_rdonly) -# all tablets -all_tablets = all_shard_0_tablets + all_shard_1_tablets - - -def setUpModule(): - try: - environment.topo_server().setup() - - for t in all_tablets: - t.init_mysql() - utils.Vtctld().start() - for t in all_tablets: - t.wait_for_mysqlctl_socket() - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - for t in all_tablets: - t.remove_tree() - - -class TestSchemaSwap(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls._start_tablets('-80', - [shard_0_master, shard_0_replica], - [shard_0_rdonly]) - cls._start_tablets('80-', - [shard_1_master, shard_1_replica], - [shard_1_rdonly]) - - for t in all_tablets: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - for t in all_tablets: - t.wait_for_vttablet_state('SERVING') - - @classmethod - def _start_tablets(cls, shard_name, replica_tablets, rdonly_tablets): - """Start all tablets on a shard. - - Args: - shard_name: string, name of the shard passed to the tablet. - replica_tablets: list of tablet.Tablet, list of tablets that should be - started as replica. - rdonly_tablets: list of tablet.Tablet, list of tablets that should be - started as rdonly. - """ - for t in replica_tablets: - t.start_vttablet(wait_for_state=None, - init_tablet_type='replica', - init_keyspace='test_keyspace', - init_shard=shard_name, - extra_args=utils.vtctld.process_args()) - for t in rdonly_tablets: - t.start_vttablet(wait_for_state=None, - init_tablet_type='rdonly', - init_keyspace='test_keyspace', - init_shard=shard_name, - extra_args=utils.vtctld.process_args()) - - keyspace = 'vt_test_keyspace' - create_table_sql = ('DROP TABLE IF EXISTS test; ' - 'CREATE TABLE test (id int, PRIMARY KEY(id)) ' - 'Engine=InnoDB') - schema_swap_sql = 'ALTER TABLE test ADD COLUMN (t TEXT)' - show_schema_sql = 'SHOW CREATE TABLE test' - final_schema_check_string = '`t` text,' - initial_schema_check_string = 'CREATE TABLE `test`' - - def _get_schema(self, source=shard_0_master): - return source.mquery(self.keyspace, self.show_schema_sql)[0][1] - - def _check_schema(self, contents): - """Check that schema of test table matches the given string.""" - schema_0 = self._get_schema(shard_0_master) - schema_1 = self._get_schema(shard_1_master) - logging.debug('shard 0 schema: %s', schema_0) - logging.debug('shard 1 schema: %s', schema_0) - self.assertEqual(schema_0, schema_1) - self.assertIn(contents, schema_0) - - def _wait_for_schema_propagation(self, - source=shard_0_master, - targets=all_tablets): - """Wait until the current schema has propagated to all tablets.""" - schema = self._get_schema(source) - timeout = 60 # seconds - condition_msg = 'propagation of schema: %s' % schema - for target in targets: - while schema != self._get_schema(target): - timeout = utils.wait_step(condition_msg, timeout) - - def setUp(self): - utils.run_vtctl(['ApplySchema', - '-sql=%s' % self.create_table_sql, - 'test_keyspace'], - auto_log=True) - self._check_schema(self.initial_schema_check_string) - self._wait_for_schema_propagation() - - for t in [shard_0_master, shard_1_master]: - tablet_info = utils.run_vtctl_json(['GetTablet', t.tablet_alias]) - if tablet_info['type'] != topodata_pb2.MASTER: - utils.run_vtctl(['InitShardMaster', '-force', - 'test_keyspace/' + t.shard, t.tablet_alias], - auto_log=True) - tablet_info = utils.run_vtctl_json(['GetTablet', t.tablet_alias]) - self.assertEqual(tablet_info['type'], topodata_pb2.MASTER) - - t.mquery('_vt', "DELETE FROM shard_metadata where name in (" - "'LastStartedSchemaSwap','LastFinishedSchemaSwap'," - "'CurrentSchemaSwapSQL');" - "DELETE FROM local_metadata " - "where name = 'LastAppliedSchemaSwap';") - - self._vtctld_url = 'http://localhost:%d' % utils.vtctld.port - self._wait_for_functional_vtctld() - self._start_vtctld_long_poll() - - def _start_swap(self, sql): - """Start a new schema swap with the given SQL statement.""" - self._swap_error = None - vtctl_res = utils.run_vtctl(['WorkflowCreate', - 'schema_swap', - '-keyspace=test_keyspace', - '-sql=%s' % sql], - auto_log=True) - m = re.match(r'^uuid: (.*)$', vtctl_res[0]) - return m.group(1) - - def _stop_swap(self, swap_uuid): - """Stop the running schema swap with the given uuid.""" - utils.run_vtctl(['WorkflowStop', swap_uuid], auto_log=True) - - def _delete_swap(self, swap_uuid): - """Delete the schema swap with the given uuid.""" - utils.run_vtctl(['WorkflowDelete', swap_uuid], auto_log=True) - - def _fetch_json_from_vtctld(self, url_path): - """Fetch and deserialize a json object from vtctld. - - Args: - url_path: string, a path appended to vtctld address to create a URL that - is used to fetch json object from. - Returns: - deserialized json object returned from vtctld. - """ - full_url = '%s/%s' % (self._vtctld_url, url_path) - f = urllib2.urlopen(full_url) - res_json = f.read() - f.close() - return json.loads(res_json) - - def _start_vtctld_long_poll(self): - """Start long polling of workflow updates from vtctld.""" - poll_update = self._fetch_json_from_vtctld('api/workflow/create') - self._poll_id = poll_update['index'] - return poll_update - - def _wait_for_functional_vtctld(self): - """Wait until vtctld is fully up and is able to respond to polls.""" - while True: - try: - poll_update = self._fetch_json_from_vtctld('api/workflow/create') - if poll_update.get('index') is None: - time.sleep(0.1) - continue - break - except urllib2.HTTPError: - pass - - def _send_retry_vtctld_action(self, swap_uuid): - """Emulate click of the Retry button on the schema swap.""" - req = urllib2.Request('%s/api/workflow/action/%s' % - (self._vtctld_url, self._poll_id)) - req.add_header('Content-Type', 'application/json; charset=utf-8') - resp = urllib2.urlopen(req, '{"path":"/%s","name":"Retry"}' % swap_uuid) - logging.info('Retry response code: %r', resp.getcode()) - - def _strip_logs_from_nodes(self, nodes): - """Strip all the logs from the node hierarchy.""" - for node in nodes: - if node.get('log'): - del node['log'] - if node.get('children'): - self._strip_logs_from_nodes(node['children']) - - def _poll_vtctld(self): - """Do one poll of vtctld for updates to workflow UI. - - If for any reason the poll breaks the method tries to restart the long - polling. - - Returns: - deserialized json object that came from vtctld as the result of poll. Can - be an incremental or a full update. - """ - try: - poll_update = self._fetch_json_from_vtctld('api/workflow/poll/%s' % - self._poll_id) - except urllib2.HTTPError as e: - logging.info('Error polling vtctld, will try to re-create the long poll: ' - '%s', e) - poll_update = self._start_vtctld_long_poll() - - if poll_update.get('nodes'): - # Log contents in the nodes is very big and makes our test logs very hard - # to read without bringing any new information (the history of actions is - # already present in test logs through the logging of incremental polls - # that is done here). Because of that we are stripping all the log - # contents from the nodes hierarchy. - self._strip_logs_from_nodes(poll_update['nodes']) - logging.info('Workflow polling update: %r', poll_update) - - return poll_update - - def _has_swap_done_or_error(self, nodes, swap_uuid): - """Check if the node list has root node of the swap that is finished. - - Args: - nodes: list, list of nodes that came in an update from vtctld. - swap_uuid: string, uuid of the swap to look for. - Returns: - bool, whether the list of nodes had the root node for the swap and the - swap was finished. When True is returned self._swap_error will contain - the error or success message displayed in the swap root node. - """ - for node in nodes: - if node['pathName'] == swap_uuid: - if node['actions'] or node['state'] == 2: - # Button Retry appeared or state is 'Done'. Then the 'message' will - # have the error. - self._swap_error = node['message'] - return True - # Other nodes are not interesting. - break - - return False - - def _wait_for_success_or_error(self, swap_uuid, reset_error=False): - """Wait until schema swap finishes successfully or with error. - - Args: - swap_uuid: string, uuid of the schema swap to wait for. - reset_error: bool, should be set to True when the swap already had an - error and we need to wait for the next one. - Returns: - string, error or success message displayed on the schema swap. - """ - if reset_error: - self._swap_error = None - # Error can have been seen already during execution of - # _wait_for_progress_message(). - if self._swap_error is not None: - return self._swap_error - - while True: - poll_update = self._poll_vtctld() - if not poll_update.get('nodes'): - continue - - if self._has_swap_done_or_error(poll_update['nodes'], swap_uuid): - return self._swap_error - - def _has_progress_message(self, nodes, message): - """Check if any node in the hierarchy has the given progress message.""" - for node in nodes: - if node.get('progressMsg') == message: - return True - children = node.get('children') - if children and self._has_progress_message(children, message): - return True - - return False - - def _wait_for_progress_message(self, swap_uuid, message): - """Wait until at least one node has the given progress message. - - The method returns when some node has the given progress message or when - the given swap has finished successfully or with an error. The latter is - necessary to not wait forever if the swap finishes without ever having the - given progress message. - - Args: - swap_uuid: string, uuid of the swap being waited for. - message: string, progress message to wait for. - """ - while True: - poll_update = self._poll_vtctld() - if not poll_update.get('nodes'): - continue - - if self._has_progress_message(poll_update['nodes'], message): - return - if self._has_swap_done_or_error(poll_update['nodes'], swap_uuid): - return - - def test_successful_swap(self): - """Normal swap running from start to finish, "happy path".""" - swap_uuid = self._start_swap(self.schema_swap_sql) - err = self._wait_for_success_or_error(swap_uuid) - self.assertEqual(err, 'Schema swap is finished') - self._check_schema(self.final_schema_check_string) - self._delete_swap(swap_uuid) - - def test_restarted_swap(self): - """Force a restart of schema swap in the middle.""" - swap_uuid = self._start_swap(self.schema_swap_sql) - # Wait until at least one tablet has the new schema (the progress message is - # '1/3') and then forcefully stop the swap. - self._wait_for_progress_message(swap_uuid, '1/3') - self._stop_swap(swap_uuid) - err = self._wait_for_success_or_error(swap_uuid) - self.assertIn('context canceled', err) - self._delete_swap(swap_uuid) - - # While we are at it try to start new swap with a different SQL statement. - # The swap should fail. - swap_uuid = self._start_swap('ALTER TABLE test ADD COLUMN i int') - err = self._wait_for_success_or_error(swap_uuid) - self.assertIn('different set of SQL statements', err) - self._stop_swap(swap_uuid) - self._delete_swap(swap_uuid) - - # Now restart with the correct statement and should succeed. - swap_uuid = self._start_swap(self.schema_swap_sql) - err = self._wait_for_success_or_error(swap_uuid) - self.assertEqual(err, 'Schema swap is finished') - self._check_schema(self.final_schema_check_string) - self._delete_swap(swap_uuid) - - def _retry_or_restart_swap(self, swap_uuid, use_retry): - """Click Retry button on the swap or fully restart it. - - Args: - swap_uuid: string, uuid of the schema swap to restart. - use_retry: bool, if True then Retry button is clicked, if False then the - swap is restarted completely as a new workflow. - Returns: - string, uuid of the new swap if it's restarted, or swap_uuid if the swap - was retried. - """ - if use_retry: - self._send_retry_vtctld_action(swap_uuid) - else: - self._stop_swap(swap_uuid) - self._delete_swap(swap_uuid) - swap_uuid = self._start_swap(self.schema_swap_sql) - return swap_uuid - - def _test_init_error(self, use_retry): - """Schema swap interrupted by an error during initialization.""" - # By marking the master read-only we cause an error when schema swap tries - # to write shard metadata during initialization. - shard_1_master.mquery('', 'SET GLOBAL read_only = 1') - swap_uuid = self._start_swap(self.schema_swap_sql) - err = self._wait_for_success_or_error(swap_uuid) - self.assertIn('running with the --read-only option', err) - - shard_1_master.mquery('', 'SET GLOBAL read_only = 0') - swap_uuid = self._retry_or_restart_swap(swap_uuid, use_retry=use_retry) - err = self._wait_for_success_or_error(swap_uuid, reset_error=True) - self.assertEqual(err, 'Schema swap is finished') - self._check_schema(self.final_schema_check_string) - self._delete_swap(swap_uuid) - - def test_init_error_with_retry(self): - self._test_init_error(use_retry=True) - - def test_init_error_with_restart(self): - self._test_init_error(use_retry=False) - - def _test_apply_error(self, use_retry): - """Schema swap interrupted while applying seed schema change.""" - # Renaming the test table to cause ALTER TABLE executed during schema swap - # to fail. - logging.debug('running in shard 1: "RENAME TABLE test TO test2"') - shard_1_master.mquery(self.keyspace, 'RENAME TABLE test TO test2') - # self._wait_for_schema_propagation(shard_1_master, all_shard_1_tablets) - swap_uuid = self._start_swap(self.schema_swap_sql) - err = self._wait_for_success_or_error(swap_uuid) - self.assertIn("Table '"+self.keyspace+".test' doesn't exist", err) - - logging.debug('running in shard 1: "RENAME TABLE test2 TO test"') - shard_1_master.mquery(self.keyspace, 'RENAME TABLE test2 TO test') - # self._wait_for_schema_propagation(shard_1_master, all_shard_1_tablets) - swap_uuid = self._retry_or_restart_swap(swap_uuid, use_retry=use_retry) - err = self._wait_for_success_or_error(swap_uuid, reset_error=True) - self.assertEqual(err, 'Schema swap is finished') - self._check_schema(self.final_schema_check_string) - self._delete_swap(swap_uuid) - - def test_apply_error_with_retry(self): - self._test_apply_error(use_retry=True) - - def test_apply_error_with_restart(self): - self._test_apply_error(use_retry=False) - - def _restart_vtctld(self, extra_flags): - """Restart vtctld possibly passing it some additional flags. - - The method makes sure that restarted vtctld has the same listening port as - the one that was before. - - Args: - extra_flags: list of strings, list of additional flags to pass to vtctld - """ - vtctld_port = utils.vtctld.port - utils.vtctld.proc.terminate() - utils.vtctld.proc.wait() - utils.vtctld = None - new_vtctld = utils.Vtctld() - new_vtctld.port = vtctld_port - new_vtctld.start(extra_flags=extra_flags) - self._wait_for_functional_vtctld() - - def test_reparent_error(self): - """Schema swap interrupted by an error during reparent.""" - # With -disable_active_reparents and without 'reparent_away' hook on - # vttablet the attempt to reparent during schema swap will always fail. - self._restart_vtctld(extra_flags=['-disable_active_reparents']) - - swap_uuid = self._start_swap(self.schema_swap_sql) - err = self._wait_for_success_or_error(swap_uuid) - self.assertIn("Error executing 'reparent_away'", err) - - self._restart_vtctld(extra_flags=[]) - # We don't need to restart the swap here because it's automatically - # restarted by vtctld when it's started. - err = self._wait_for_success_or_error(swap_uuid, reset_error=True) - self.assertEqual(err, 'Schema swap is finished') - self._check_schema(self.final_schema_check_string) - self._delete_swap(swap_uuid) - - -if __name__ == '__main__': - utils.main() diff --git a/test/sharded.py b/test/sharded.py deleted file mode 100755 index 280b0ed688a..00000000000 --- a/test/sharded.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests a sharded setup works and routes queries correctly. -""" - -import unittest - -import environment -import tablet -import utils - -# range "" - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -# range 80 - "" -shard_1_master = tablet.Tablet() -shard_1_replica = tablet.Tablet() - - -def setUpModule(): - try: - environment.topo_server().setup() - - setup_procs = [ - shard_0_master.init_mysql(), - shard_0_replica.init_mysql(), - shard_1_master.init_mysql(), - shard_1_replica.init_mysql(), - ] - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [ - shard_0_master.teardown_mysql(), - shard_0_replica.teardown_mysql(), - shard_1_master.teardown_mysql(), - shard_1_replica.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_0_master.remove_tree() - shard_0_replica.remove_tree() - shard_1_master.remove_tree() - shard_1_replica.remove_tree() - -# both shards will have similar tables, but with different column order, -# so we can test column mismatches by doing a 'select *', -# and also check the good case by doing a 'select id, msg' -create_vt_select_test = '''create table vt_select_test ( -id bigint not null, -msg varchar(64), -primary key (id) -) Engine=InnoDB''' - -create_vt_select_test_reverse = '''create table vt_select_test ( -msg varchar(64), -id bigint not null, -primary key (id) -) Engine=InnoDB''' - - -class TestSharded(unittest.TestCase): - - def test_sharding(self): - - utils.run_vtctl(['CreateKeyspace', - '--sharding_column_name', 'keyspace_id', - '--sharding_column_type', 'uint64', - 'test_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '-80') - shard_0_replica.init_tablet('replica', 'test_keyspace', '-80') - shard_1_master.init_tablet('replica', 'test_keyspace', '80-') - shard_1_replica.init_tablet('replica', 'test_keyspace', '80-') - - # create databases, start the tablets, wait for them to start - for t in [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica]: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None) - for t in [shard_0_master, shard_1_master, shard_0_replica, shard_1_replica]: - t.wait_for_vttablet_state('NOT_SERVING') - - # apply the schema on the first shard through vtctl, so all tablets - # are the same. - shard_0_master.mquery('vt_test_keyspace', - create_vt_select_test.replace('\n', ''), write=True) - shard_0_replica.mquery('vt_test_keyspace', - create_vt_select_test.replace('\n', ''), write=True) - - # apply the schema on the second shard. - shard_1_master.mquery( - 'vt_test_keyspace', - create_vt_select_test_reverse.replace('\n', ''), write=True) - shard_1_replica.mquery( - 'vt_test_keyspace', - create_vt_select_test_reverse.replace('\n', ''), write=True) - - for t in [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica]: - utils.run_vtctl(['ReloadSchema', t.tablet_alias]) - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - # insert some values directly (db is RO after minority reparent) - # FIXME(alainjobart) these values don't match the shard map - utils.run_vtctl(['SetReadWrite', shard_0_master.tablet_alias]) - utils.run_vtctl(['SetReadWrite', shard_1_master.tablet_alias]) - shard_0_master.mquery( - 'vt_test_keyspace', - "insert into vt_select_test (id, msg) values (1, 'test 1')", - write=True) - shard_1_master.mquery( - 'vt_test_keyspace', - "insert into vt_select_test (id, msg) values (10, 'test 10')", - write=True) - - utils.validate_topology(ping_tablets=True) - - utils.pause('Before the sql scatter query') - - # make sure the '1' value was written on first shard - rows = shard_0_master.mquery( - 'vt_test_keyspace', 'select id, msg from vt_select_test order by id') - self.assertEqual(rows, ((1, 'test 1'),), - 'wrong mysql_query output: %s' % str(rows)) - - utils.pause('After db writes') - - # throw in some schema validation step - # we created the schema differently, so it should show - utils.run_vtctl(['ValidateSchemaShard', 'test_keyspace/-80']) - utils.run_vtctl(['ValidateSchemaShard', 'test_keyspace/80-']) - _, err = utils.run_vtctl(['ValidateSchemaKeyspace', 'test_keyspace'], - trap_output=True, raise_on_error=False) - if ('schemas differ on table vt_select_test:\n' - 'test_nj-0000062344: CREATE TABLE' not in err): - self.fail('wrong ValidateSchemaKeyspace output: ' + err) - - # validate versions - utils.run_vtctl(['ValidateVersionShard', 'test_keyspace/-80'], - auto_log=True) - utils.run_vtctl(['ValidateVersionKeyspace', 'test_keyspace'], auto_log=True) - - # show and validate permissions - utils.run_vtctl(['GetPermissions', 'test_nj-0000062344'], auto_log=True) - utils.run_vtctl(['ValidatePermissionsShard', 'test_keyspace/-80'], - auto_log=True) - utils.run_vtctl(['ValidatePermissionsKeyspace', 'test_keyspace'], - auto_log=True) - - # connect to the tablets directly, make sure they know / validate - # their own shard - sql = 'select id, msg from vt_select_test order by id' - - qr = shard_0_master.execute(sql) - self.assertEqual(qr['rows'], [[1, 'test 1'],]) - - qr = shard_1_master.execute(sql) - self.assertEqual(qr['rows'], [[10, 'test 10'],]) - - tablet.kill_tablets([shard_0_master, shard_0_replica, shard_1_master, - shard_1_replica]) - -if __name__ == '__main__': - utils.main() diff --git a/test/sharded_recovery.py b/test/sharded_recovery.py deleted file mode 100755 index 26c93cb3693..00000000000 --- a/test/sharded_recovery.py +++ /dev/null @@ -1,648 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import datetime -import json -import logging -import os -import unittest - -import MySQLdb - -import environment -import tablet -import utils - -from vtdb import vtgate_client - -# initial shard, covers everything -tablet_master = tablet.Tablet() -tablet_replica1 = tablet.Tablet() -tablet_rdonly = tablet.Tablet() -# to use for recovery keyspace -tablet_replica2 = tablet.Tablet() -tablet_replica3 = tablet.Tablet() - -# split shards -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -shard_0_rdonly = tablet.Tablet() -# range 80 - '' -shard_1_master = tablet.Tablet() -shard_1_replica = tablet.Tablet() -shard_1_rdonly = tablet.Tablet() - -all_tablets = [tablet_master, tablet_replica1, tablet_replica2, tablet_replica3, tablet_rdonly, - shard_0_master, shard_0_replica, shard_0_rdonly, shard_1_master, shard_1_replica, shard_1_rdonly] - -def setUpModule(): - try: - environment.topo_server().setup() - setup_procs = [t.init_mysql() for t in all_tablets] - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - -def get_connection(timeout=15.0): - protocol, endpoint = utils.vtgate.rpc_endpoint(python=True) - try: - return vtgate_client.connect(protocol, endpoint, timeout) - except Exception: - logging.exception('Connection to vtgate (timeout=%s) failed.', timeout) - raise - -class TestShardedRecovery(unittest.TestCase): - - def setUp(self): - xtra_args = ['-enable_replication_reporter'] - tablet_master.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=xtra_args) - tablet_replica1.init_tablet('replica', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=xtra_args) - tablet_rdonly.init_tablet('rdonly', 'test_keyspace', '0', start=True, - supports_backups=True, - extra_args=xtra_args) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_master.tablet_alias]) - - def tearDown(self): - for t in all_tablets: - t.kill_vttablet() - - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in all_tablets: - t.reset_replication() - t.set_semi_sync_enabled(master=False, slave=False) - t.clean_dbs() - - for shard in ['0', '-80', '80-']: - for backup in self._list_backups(shard): - self._remove_backup(backup, shard) - - _create_vt_insert_test = '''create table vt_insert_test ( - id bigint, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - _vschema_json = '''{ - "sharded": true, - "vindexes": { - "hash": { - "type": "hash" - } - }, - "tables": { - "vt_insert_test": { - "column_vindexes": [ - { - "column": "id", - "name": "hash" - } - ] - } - } -}''' - - def _insert_data(self, t, index): - """Add a single row with value 'index' to the given tablet.""" - t.mquery( - 'vt_test_keyspace', - "insert into vt_insert_test (id, msg) values (%d, 'test %s')" % - (index, index), write=True) - - def _check_data(self, t, count, msg): - """Check that the specified tablet has the expected number of rows.""" - timeout = 10 - while True: - try: - result = t.mquery( - 'vt_test_keyspace', 'select count(*) from vt_insert_test') - if result[0][0] == count: - break - except MySQLdb.DatabaseError: - # ignore exceptions, we'll just timeout (the tablet creation - # can take some time to replicate, and we get a 'table vt_insert_test - # does not exist exception in some rare cases) - logging.exception('exception waiting for data to replicate') - timeout = utils.wait_step(msg, timeout) - - def _restore(self, t, keyspace, shard): - """Erase mysql/tablet dir, then start tablet with restore enabled.""" - self._reset_tablet_dir(t) - - # create a recovery keyspace - utils.run_vtctl(['CreateKeyspace', - '-keyspace_type=SNAPSHOT', - '-base_keyspace=test_keyspace', - '-snapshot_time', - datetime.datetime.utcnow().isoformat("T")+"Z", - keyspace]) - - # set disable_active_reparents to true and enable_replication_reporter to false - # otherwise replication_reporter will try to restart replication - xtra_args = ['-disable_active_reparents', - '-enable_replication_reporter=false'] - xtra_args.extend(tablet.get_backup_storage_flags()) - t.start_vttablet(wait_for_state='SERVING', - init_tablet_type='replica', - init_keyspace=keyspace, - init_shard=shard, - supports_backups=True, - extra_args=xtra_args) - - def _reset_tablet_dir(self, t): - """Stop mysql, delete everything including tablet dir, restart mysql.""" - utils.wait_procs([t.teardown_mysql()]) - # Specify ignore_options because we want to delete the tree even - # if the test's -k / --keep-logs was specified on the command line. - t.remove_tree(ignore_options=True) - proc = t.init_mysql() - utils.wait_procs([proc]) - - def _list_backups(self, shard): - """Get a list of backup names for the test shard.""" - backups, _ = utils.run_vtctl(tablet.get_backup_storage_flags() + - ['ListBackups', 'test_keyspace/%s' % shard], - mode=utils.VTCTL_VTCTL, trap_output=True) - return backups.splitlines() - - def _remove_backup(self, backup, shard): - """Remove a named backup from the test shard.""" - utils.run_vtctl( - tablet.get_backup_storage_flags() + - ['RemoveBackup', 'test_keyspace/%s' % shard, backup], - auto_log=True, mode=utils.VTCTL_VTCTL) - - def test_unsharded_recovery_after_sharding(self): - """Test recovery from backup flow. - - test_recovery will: - - create a shard with master and replica1 only - - run InitShardMaster - - insert some data - - take a backup - - insert more data on the master - - perform a resharding - - create a recovery keyspace - - bring up tablet_replica2 in the new keyspace - - check that new tablet does not have data created after backup - - check that vtgate queries work correctly - - """ - - # insert data on master, wait for replica to get it - utils.run_vtctl(['ApplySchema', - '-sql', self._create_vt_insert_test, - 'test_keyspace'], - auto_log=True) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - # insert more data on the master - self._insert_data(tablet_master, 2) - - # backup the replica - utils.run_vtctl(['Backup', tablet_replica1.tablet_alias], auto_log=True) - - # check that the backup shows up in the listing - backups = self._list_backups('0') - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), 1) - self.assertTrue(backups[0].endswith(tablet_replica1.tablet_alias)) - - # insert more data on the master - self._insert_data(tablet_master, 3) - - utils.run_vtctl(['ApplyVSchema', - '-vschema', self._vschema_json, - 'test_keyspace'], - auto_log=True) - - # create the split shards - shard_0_master.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='-80', - tablet_index=0) - shard_0_replica.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='-80', - tablet_index=1) - shard_0_rdonly.init_tablet( - 'rdonly', - keyspace='test_keyspace', - shard='-80', - tablet_index=2) - shard_1_master.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='80-', - tablet_index=0) - shard_1_replica.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='80-', - tablet_index=1) - shard_1_rdonly.init_tablet( - 'rdonly', - keyspace='test_keyspace', - shard='80-', - tablet_index=2) - - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]: - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=True) - - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - for t in [shard_0_replica, shard_1_replica]: - utils.wait_for_tablet_type(t.tablet_alias, 'replica') - - sharded_tablets = [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly] - for t in sharded_tablets: - t.wait_for_vttablet_state('SERVING') - - # we need to create the schema, and the worker will do data copying - for keyspace_shard in ('test_keyspace/-80', 'test_keyspace/80-'): - utils.run_vtctl(['CopySchemaShard', - 'test_keyspace/0', - keyspace_shard], - auto_log=True) - - utils.run_vtctl( - ['SplitClone', 'test_keyspace', '0', '-80,80-'], auto_log=True) - - utils.run_vtctl( - ['MigrateServedTypes', 'test_keyspace/0', 'rdonly'], auto_log=True) - utils.run_vtctl( - ['MigrateServedTypes', 'test_keyspace/0', 'replica'], auto_log=True) - # then serve master from the split shards - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/0', 'master'], - auto_log=True) - - # remove the original tablets in the original shard - tablet.kill_tablets([tablet_master, tablet_replica1, tablet_rdonly]) - for t in [tablet_replica1, tablet_rdonly]: - utils.run_vtctl(['DeleteTablet', t.tablet_alias], auto_log=True) - utils.run_vtctl(['DeleteTablet', '-allow_master', - tablet_master.tablet_alias], auto_log=True) - - # rebuild the serving graph, all mentions of the old shards should be gone - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # delete the original shard - utils.run_vtctl(['DeleteShard', 'test_keyspace/0'], auto_log=True) - - # now bring up the recovery keyspace and a tablet, letting it restore from backup. - self._restore(tablet_replica2, 'recovery_keyspace', '0') - - # check the new replica does not have the data - self._check_data(tablet_replica2, 2, 'replica2 tablet should not have new data') - - # start vtgate - vtgate = utils.VtGate() - vtgate.start(tablets=[ - shard_0_master, shard_0_replica, shard_1_master, shard_1_replica, tablet_replica2 - ], tablet_types_to_wait='REPLICA') - utils.vtgate.wait_for_endpoints('test_keyspace.-80.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.replica', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.-80.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.replica', 1) - utils.vtgate.wait_for_endpoints('recovery_keyspace.0.replica', 1) - - # check that vtgate doesn't route queries to new tablet - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace=None, writable=True) - - cursor.execute('select count(*) from vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 3) - - # check that new tablet is accessible by using ks.table - cursor.execute('select count(*) from recovery_keyspace.vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 2) - - # check that new tablet is accessible with 'use ks' - cursor.execute('use recovery_keyspace@replica', {}) - cursor.execute('select count(*) from vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 2) - - # TODO check that new tablet is accessible with 'use ks:shard' - # this currently does not work through the python client, though it works from mysql client - #cursor.execute('use recovery_keyspace:0@replica', {}) - #cursor.execute('select count(*) from vt_insert_test', {}) - #result = cursor.fetchall() - #if not result: - #self.fail('Result cannot be null') - #else: - #self.assertEqual(result[0][0], 1) - - vtgate_conn.close() - tablet_replica2.kill_vttablet() - vtgate.kill() - - def test_sharded_recovery(self): - """Test recovery from backup flow. - - test_recovery will: - - create a shard with master and replica1 only - - run InitShardMaster - - insert some data - - perform a resharding - - take a backup of both new shards - - insert more data on the masters of both shards - - create a recovery keyspace - - bring up tablet_replica2 and tablet_replica3 in the new keyspace - - check that new tablets do not have data created after backup - - check that vtgate queries work correctly - - """ - - # insert data on master, wait for replica to get it - utils.run_vtctl(['ApplySchema', - '-sql', self._create_vt_insert_test, - 'test_keyspace'], - auto_log=True) - self._insert_data(tablet_master, 1) - self._check_data(tablet_replica1, 1, 'replica1 tablet getting data') - # insert more data on the master - self._insert_data(tablet_master, 4) - - utils.run_vtctl(['ApplyVSchema', - '-vschema', self._vschema_json, - 'test_keyspace'], - auto_log=True) - - # create the split shards - shard_0_master.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='-80', - tablet_index=0) - shard_0_replica.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='-80', - tablet_index=1) - shard_0_rdonly.init_tablet( - 'rdonly', - keyspace='test_keyspace', - shard='-80', - tablet_index=2) - shard_1_master.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='80-', - tablet_index=0) - shard_1_replica.init_tablet( - 'replica', - keyspace='test_keyspace', - shard='80-', - tablet_index=1) - shard_1_rdonly.init_tablet( - 'rdonly', - keyspace='test_keyspace', - shard='80-', - tablet_index=2) - - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]: - t.start_vttablet(wait_for_state=None, - binlog_use_v3_resharding_mode=True) - - for t in [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - - for t in [shard_0_replica, shard_1_replica]: - utils.wait_for_tablet_type(t.tablet_alias, 'replica') - - sharded_tablets = [shard_0_master, shard_0_replica, shard_0_rdonly, - shard_1_master, shard_1_replica, shard_1_rdonly] - for t in sharded_tablets: - t.wait_for_vttablet_state('SERVING') - - # we need to create the schema, and the worker will do data copying - for keyspace_shard in ('test_keyspace/-80', 'test_keyspace/80-'): - utils.run_vtctl(['CopySchemaShard', - 'test_keyspace/0', - keyspace_shard], - auto_log=True) - - utils.run_vtctl( - ['SplitClone', 'test_keyspace', '0', '-80,80-'], auto_log=True) - - utils.run_vtctl( - ['MigrateServedTypes', 'test_keyspace/0', 'rdonly'], auto_log=True) - utils.run_vtctl( - ['MigrateServedTypes', 'test_keyspace/0', 'replica'], auto_log=True) - # then serve master from the split shards - utils.run_vtctl(['MigrateServedTypes', 'test_keyspace/0', 'master'], - auto_log=True) - - # remove the original tablets in the original shard - tablet.kill_tablets([tablet_master, tablet_replica1, tablet_rdonly]) - for t in [tablet_replica1, tablet_rdonly]: - utils.run_vtctl(['DeleteTablet', t.tablet_alias], auto_log=True) - utils.run_vtctl(['DeleteTablet', '-allow_master', - tablet_master.tablet_alias], auto_log=True) - - # rebuild the serving graph, all mentions of the old shards should be gone - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # delete the original shard - utils.run_vtctl(['DeleteShard', 'test_keyspace/0'], auto_log=True) - - result = shard_0_master.mquery('vt_test_keyspace', "select count(*) from vt_insert_test") - shard_0_count = result[0][0] - logging.debug("Shard -80 has %d rows", shard_0_count) - shard_0_test_id = 0 - if shard_0_count > 0: - result = shard_0_master.mquery('vt_test_keyspace', "select id from vt_insert_test") - shard_0_test_id = result[0][0] - - result = shard_1_master.mquery('vt_test_keyspace', "select count(*) from vt_insert_test") - shard_1_count = result[0][0] - logging.debug("Shard 80- has %d rows", shard_1_count) - shard_1_test_id = 0 - if shard_1_count > 0: - result = shard_1_master.mquery('vt_test_keyspace', "select id from vt_insert_test") - shard_1_test_id = result[0][0] - - # backup the new shards - utils.run_vtctl(['Backup', shard_0_replica.tablet_alias], auto_log=True) - utils.run_vtctl(['Backup', shard_1_replica.tablet_alias], auto_log=True) - - # check that the backup shows up in the listing - backups = self._list_backups('-80') - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), 1) - self.assertTrue(backups[0].endswith(shard_0_replica.tablet_alias)) - - backups = self._list_backups('80-') - logging.debug('list of backups: %s', backups) - self.assertEqual(len(backups), 1) - self.assertTrue(backups[0].endswith(shard_1_replica.tablet_alias)) - - # start vtgate - vtgate = utils.VtGate() - vtgate.start(tablets=[ - shard_0_master, shard_1_master - ], tablet_types_to_wait='MASTER') - utils.vtgate.wait_for_endpoints('test_keyspace.-80.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.master', 1) - - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=None, writable=True) - # insert more data on the masters - for i in [2, 3]: - cursor.execute('insert into vt_insert_test (id, msg) values (:id, :msg)', {'id': i, 'msg': 'test %s' % i}) - - vtgate_conn.close() - vtgate.kill() - - # now bring up the recovery keyspace and 2 tablets, letting it restore from backup. - self._restore(tablet_replica2, 'recovery_keyspace', '-80') - self._restore(tablet_replica3, 'recovery_keyspace', '80-') - - # check the new replicas have the correct number of rows - self._check_data(tablet_replica2, shard_0_count, 'replica2 tablet should not have new data') - self._check_data(tablet_replica3, shard_1_count, 'replica3 tablet should not have new data') - - # start vtgate - vtgate = utils.VtGate() - vtgate.start(tablets=[ - shard_0_master, shard_0_replica, shard_1_master, shard_1_replica, tablet_replica2, tablet_replica3 - ], tablet_types_to_wait='REPLICA') - utils.vtgate.wait_for_endpoints('test_keyspace.-80.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.replica', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.-80.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.80-.replica', 1) - utils.vtgate.wait_for_endpoints('recovery_keyspace.-80.replica', 1) - utils.vtgate.wait_for_endpoints('recovery_keyspace.80-.replica', 1) - - # check that vtgate doesn't route queries to new tablet - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace=None, writable=True) - - cursor.execute('select count(*) from vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 4) - - # check that new keyspace is accessible by using ks.table - cursor.execute('select count(*) from recovery_keyspace.vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 2) - - # check that new keyspace is accessible with 'use ks' - cursor.execute('use recovery_keyspace@replica', {}) - cursor.execute('select count(*) from vt_insert_test', {}) - result = cursor.fetchall() - if not result: - self.fail('Result cannot be null') - else: - self.assertEqual(result[0][0], 2) - - # TODO check that new tablet is accessible with 'use ks:shard' - # this currently does not work through the python client, though it works from mysql client - #cursor.execute('use recovery_keyspace:-80@replica', {}) - #cursor.execute('select count(*) from vt_insert_test', {}) - #result = cursor.fetchall() - #if not result: - # self.fail('Result cannot be null') - #else: - # self.assertEqual(result[0][0], shard_0_count) - #cursor.execute('select id from vt_insert_test', {}) - #result = cursor.fetchall() - #if not result: - # self.fail('Result cannot be null') - #else: - # self.assertEqual(result[0][0], shard_0_test_id) - - #cursor.execute('use recovery_keyspace:80-@replica', {}) - #cursor.execute('select count(*) from vt_insert_test', {}) - #result = cursor.fetchall() - #if not result: - # self.fail('Result cannot be null') - #else: - # self.assertEqual(result[0][0], shard_1_count) - #cursor.execute('use recovery_keyspace:80-@replica', {}) - #cursor.execute('select id from vt_insert_test', {}) - #result = cursor.fetchall() - #if not result: - # self.fail('Result cannot be null') - #else: - # self.assertEqual(result[0][0], shard_1_test_id) - - vtgate_conn.close() - tablet_replica2.kill_vttablet() - tablet_replica3.kill_vttablet() - vtgate.kill() - -if __name__ == '__main__': - utils.main() diff --git a/test/tablet.py b/test/tablet.py deleted file mode 100644 index 714ca3ec022..00000000000 --- a/test/tablet.py +++ /dev/null @@ -1,881 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Manage VTTablet during test.""" - -import json -import logging -import os -import re -import shutil -import sys -import time -import urllib2 -import warnings - -import MySQLdb - -import environment -from mysql_flavor import mysql_flavor -from protocols_flavor import protocols_flavor -from topo_flavor.server import topo_server -from urlparse import urlparse -import utils - -import grpc -from vtproto.tabletmanagerservice_pb2_grpc import TabletManagerStub - -# Dropping a table inexplicably produces a warning despite -# the 'IF EXISTS' clause. Squelch these warnings. -warnings.simplefilter('ignore') - -tablet_cell_map = { - 62344: 'nj', - 62044: 'nj', - 41983: 'nj', - 31981: 'ny', -} - - -def get_backup_storage_flags(): - return ['-backup_storage_implementation', 'file', - '-file_backup_storage_root', - os.path.join(environment.tmproot, 'backupstorage')] - - -def get_all_extra_my_cnf(extra_my_cnf): - all_extra_my_cnf = [environment.vtroot + '/config/mycnf/default-fast.cnf'] - flavor_my_cnf = mysql_flavor().extra_my_cnf() - if flavor_my_cnf: - all_extra_my_cnf.append(flavor_my_cnf) - if extra_my_cnf: - all_extra_my_cnf.append(extra_my_cnf) - return all_extra_my_cnf - - -class Tablet(object): - """This class helps manage a vttablet instance. - - To use it for vttablet, you need to use init_tablet and/or - start_vttablet. - """ - default_uid = 62344 - seq = 0 - tablets_running = 0 - - def __init__(self, tablet_uid=None, port=None, mysql_port=None, cell=None, - use_mysqlctld=False, vt_dba_passwd=None): - self.tablet_uid = tablet_uid or (Tablet.default_uid + Tablet.seq) - self.port = port or (environment.reserve_ports(1)) - self.mysql_port = mysql_port or (environment.reserve_ports(1)) - self.grpc_port = environment.reserve_ports(1) - self.use_mysqlctld = use_mysqlctld - self.vt_dba_passwd = vt_dba_passwd - Tablet.seq += 1 - - if cell: - self.cell = cell - else: - self.cell = tablet_cell_map.get(tablet_uid, 'nj') - self.proc = None - - # filled in during init_mysql - self.mysqlctld_process = None - - # filled in during init_tablet - self.keyspace = None - self.shard = None - self.index = None - self.tablet_index = None - # default to false - self.external_mysql = False - # utility variables - self.tablet_alias = 'test_%s-%010d' % (self.cell, self.tablet_uid) - self.zk_tablet_path = ( - '/zk/test_%s/vt/tablets/%010d' % (self.cell, self.tablet_uid)) - - def __str__(self): - return 'tablet: uid: %d web: http://localhost:%d/ rpc port: %d' % ( - self.tablet_uid, self.port, self.grpc_port) - - def mysqlctl(self, cmd, extra_my_cnf=None, with_ports=False, verbose=False, - extra_args=None): - """Runs a mysqlctl command. - - Args: - cmd: the command to run. - extra_my_cnf: list of extra mycnf files to use - with_ports: if set, sends the tablet and mysql ports to mysqlctl. - verbose: passed to mysqlctld. - extra_args: passed to mysqlctl. - Returns: - the result of run_bg. - """ - extra_env = {} - all_extra_my_cnf = get_all_extra_my_cnf(extra_my_cnf) - if all_extra_my_cnf: - extra_env['EXTRA_MY_CNF'] = ':'.join(all_extra_my_cnf) - args = environment.binary_args('mysqlctl') + [ - '-log_dir', environment.vtlogroot, - '-tablet_uid', str(self.tablet_uid)] - if self.use_mysqlctld: - args.extend( - ['-mysqlctl_socket', os.path.join(self.tablet_dir, 'mysqlctl.sock')]) - if with_ports: - args.extend(['-port', str(self.port), - '-mysql_port', str(self.mysql_port)]) - if verbose: - args.append('-alsologtostderr') - if extra_args: - args.extend(extra_args) - args.extend(cmd) - return utils.run_bg(args, extra_env=extra_env) - - def mysqlctld(self, cmd, extra_my_cnf=None, verbose=False, extra_args=None): - """Runs a mysqlctld command. - - Args: - cmd: the command to run. - extra_my_cnf: list of extra mycnf files to use - verbose: passed to mysqlctld. - extra_args: passed to mysqlctld. - Returns: - the result of run_bg. - """ - extra_env = {} - all_extra_my_cnf = get_all_extra_my_cnf(extra_my_cnf) - if all_extra_my_cnf: - extra_env['EXTRA_MY_CNF'] = ':'.join(all_extra_my_cnf) - args = environment.binary_args('mysqlctld') + [ - '-log_dir', environment.vtlogroot, - '-tablet_uid', str(self.tablet_uid), - '-mysql_port', str(self.mysql_port), - '-socket_file', os.path.join(self.tablet_dir, 'mysqlctl.sock')] - if verbose: - args.append('-alsologtostderr') - if extra_args: - args.extend(extra_args) - 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, - use_rbr=True): - """Init the mysql tablet directory, starts mysqld. - - Either runs 'mysqlctl init', or starts a mysqlctld process. - - Args: - 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 not use_rbr: - if extra_my_cnf: - extra_my_cnf += ':' + environment.vtroot + '/config/mycnf/sbr.cnf' - else: - extra_my_cnf = environment.vtroot + '/config/mycnf/sbr.cnf' - - if not init_db: - init_db = environment.vtroot + '/config/init_db.sql' - - if self.use_mysqlctld: - self.mysqlctld_process = self.mysqlctld(['-init_db_sql_file', init_db], - extra_my_cnf=extra_my_cnf, - extra_args=extra_args) - return self.mysqlctld_process - else: - return self.mysqlctl(['init', '-init_db_sql_file', init_db], - extra_my_cnf=extra_my_cnf, with_ports=True, - extra_args=extra_args) - - def start_mysql(self): - return self.mysqlctl(['start'], with_ports=True) - - def shutdown_mysql(self, extra_args=None): - return self.mysqlctl(['shutdown'], with_ports=True, extra_args=extra_args) - - def teardown_mysql(self, extra_args=None): - if self.use_mysqlctld and self.mysqlctld_process: - # if we use mysqlctld, we just terminate it gracefully, so it kills - # its mysqld. And we return it, so we can wait for it. - utils.kill_sub_process(self.mysqlctld_process, soft=True) - return self.mysqlctld_process - if utils.options.keep_logs: - return self.shutdown_mysql(extra_args=extra_args) - return self.mysqlctl(['teardown', '-force'], extra_args=extra_args) - - def remove_tree(self, ignore_options=False): - if not ignore_options and utils.options.keep_logs: - return - try: - shutil.rmtree(self.tablet_dir) - except OSError as e: - if utils.options.verbose == 2: - print >> sys.stderr, e, self.tablet_dir - - def mysql_connection_parameters(self, dbname, user='vt_dba'): - result = dict(user=user, - db=dbname) - if user == 'vt_dba' and self.vt_dba_passwd: - result['passwd'] = self.vt_dba_passwd - return result - - def connect(self, dbname='', user='vt_dba', **params): - params.update(self.mysql_connection_parameters(dbname, user)) - if 'port' not in params.keys(): - params['unix_socket']=self.tablet_dir + '/mysql.sock' - else: - params['host']='127.0.0.1' - conn = MySQLdb.Connect(**params) - return conn, conn.cursor() - - # Query the MySQL instance directly - def mquery(self, dbname, query, write=False, user='vt_dba', conn_params=None, - log_query=False): - """Runs a query to MySQL directly, using python's SQL driver. - - Args: - dbname: if set, the dbname to use. - query: the SQL query (or queries) to run. - write: if set, wraps the query in a transaction. - user: the db user to use. - conn_params: extra mysql connection parameters. - log_query: if true, the query will be logged. - Returns: - the rows. - """ - if conn_params is None: - conn_params = {} - if self.external_mysql: - conn_params['port']=self.mysql_port - conn, cursor = self.connect(dbname, user=user, **conn_params) - if write: - conn.begin() - if isinstance(query, basestring): - query = [query] - - for q in query: - if log_query: - logging.debug('mysql(%s,%s): %s', self.tablet_uid, dbname, q) - cursor.execute(q) - - if write: - conn.commit() - - try: - return cursor.fetchall() - finally: - cursor.close() - conn.close() - - def assert_table_count(self, dbname, table, n, where=''): - result = self.mquery(dbname, 'select count(*) from ' + table + ' ' + where) - if result[0][0] != n: - raise utils.TestError('expected %d rows in %s' % (n, table), result) - - def reset_replication(self): - self.mquery('', mysql_flavor().reset_replication_commands()) - - def set_semi_sync_enabled(self, master=None, slave=None): - logging.debug('mysql(%s): setting semi-sync mode: master=%s, slave=%s', - self.tablet_uid, master, slave) - self.mquery('', - mysql_flavor().set_semi_sync_enabled_commands(master, slave)) - - def populate(self, dbname, create_sql, insert_sqls=None): - self.create_db(dbname) - if isinstance(create_sql, basestring): - create_sql = [create_sql] - for q in create_sql: - self.mquery(dbname, q) - if insert_sqls: - for q in insert_sqls: - self.mquery(dbname, q, write=True) - - def has_db(self, name): - rows = self.mquery('', 'show databases') - for row in rows: - dbname = row[0] - if dbname == name: - return True - return False - - def drop_db(self, name): - self.mquery('', 'drop database if exists %s' % name) - while self.has_db(name): - logging.debug('%s sleeping while waiting for database drop: %s', - self.tablet_alias, name) - time.sleep(0.3) - self.mquery('', 'drop database if exists %s' % name) - - def create_db(self, name): - self.drop_db(name) - self.mquery('', 'create database %s' % name) - - def clean_dbs(self, include_vt=False): - logging.debug('mysql(%s): removing all databases', self.tablet_uid) - rows = self.mquery('', 'show databases') - for row in rows: - dbname = row[0] - if dbname in ['information_schema', 'performance_schema', 'mysql', 'sys']: - continue - if dbname == '_vt' and not include_vt: - continue - self.drop_db(dbname) - - def wait_check_db_var(self, name, value): - for _ in range(3): - try: - return self.check_db_var(name, value) - except utils.TestError as e: - print >> sys.stderr, 'WARNING: ', e - time.sleep(1.0) - raise e - - def check_db_var(self, name, value): - row = self.get_db_var(name) - if row != (name, value): - raise utils.TestError('variable not set correctly', name, row) - - def get_db_var(self, name): - conn, cursor = self.connect() - try: - cursor.execute("show variables like '%s'" % name) - return cursor.fetchone() - finally: - conn.close() - - def check_db_status(self, name, value): - row = self.get_db_status(name) - if row[1] != value: - raise utils.TestError('status not correct', name, row) - - def get_db_status(self, name): - conn, cursor = self.connect() - try: - cursor.execute("show status like '%s'" % name) - return cursor.fetchone() - finally: - conn.close() - - def update_addrs(self): - args = [ - 'UpdateTabletAddrs', - '-hostname', 'localhost', - '-ip-addr', '127.0.0.1', - '-mysql-port', '%d' % self.mysql_port, - '-vt-port', '%d' % self.port, - self.tablet_alias - ] - return utils.run_vtctl(args) - - def init_tablet(self, tablet_type, keyspace, shard, - tablet_index=None, - start=False, dbname=None, parent=True, wait_for_start=True, - include_mysql_port=True, external_mysql=False, **kwargs): - """Initialize a tablet's record in topology.""" - - self.tablet_type = tablet_type - self.keyspace = keyspace - self.shard = shard - self.tablet_index = tablet_index - self.external_mysql = external_mysql - - self.dbname = dbname or ('vt_' + self.keyspace) - - args = ['InitTablet', - '-hostname', 'localhost', - '-port', str(self.port), - '-grpc_port', str(self.grpc_port), - '-allow_update'] - if include_mysql_port: - args.extend(['-mysql_port', str(self.mysql_port)]) - if parent: - args.append('-parent') - if dbname: - args.extend(['-db_name_override', dbname]) - if keyspace: - args.extend(['-keyspace', keyspace]) - if shard: - args.extend(['-shard', shard]) - args.extend([self.tablet_alias, tablet_type]) - utils.run_vtctl(args) - if start: - if not wait_for_start: - expected_state = None - elif tablet_type == 'master': - expected_state = 'SERVING' - else: - expected_state = 'NOT_SERVING' - self.start_vttablet(wait_for_state=expected_state, **kwargs) - - @property - def tablet_dir(self): - return '%s/vt_%010d' % (environment.vtdataroot, self.tablet_uid) - - def grpc_enabled(self): - return ( - protocols_flavor().tabletconn_protocol() == 'grpc' or - protocols_flavor().tablet_manager_protocol() == 'grpc' or - protocols_flavor().binlog_player_protocol() == 'grpc') - - def semi_sync_enabled(self): - return self.enable_semi_sync - - def flush(self): - utils.curl('http://localhost:%s%s' % - (self.port, environment.flush_logs_url), - stderr=utils.devnull, stdout=utils.devnull) - - def start_vttablet( - self, port=None, - wait_for_state='SERVING', topocustomrule_path=None, - schema_override=None, - repl_extra_flags=None, table_acl_config=None, - lameduck_period=None, security_policy=None, - full_mycnf_args=False, - extra_args=None, extra_env=None, include_mysql_port=True, - init_tablet_type=None, init_keyspace=None, init_shard=None, - # TODO(mberlin): Assign the index automatically and remove this parameter. - tablet_index=None, - init_db_name_override=None, - binlog_use_v3_resharding_mode=True, - supports_backups=True, grace_period='1s', enable_semi_sync=True): - # pylint: disable=g-doc-args - """Starts a vttablet process, and returns it. - - The process is also saved in self.proc, so it's easy to kill as well. - - Returns: - the process that was started. - """ - # pylint: enable=g-doc-args - - args = environment.binary_args('vttablet') - # Use 'localhost' as hostname because Travis CI worker hostnames - # are too long for MySQL replication. - args.extend(['-tablet_hostname', 'localhost']) - args.extend(['-tablet-path', self.tablet_alias]) - args.extend(environment.topo_server().flags()) - args.extend(['-binlog_player_protocol', - protocols_flavor().binlog_player_protocol()]) - args.extend(['-tablet_manager_protocol', - protocols_flavor().tablet_manager_protocol()]) - args.extend(['-tablet_protocol', protocols_flavor().tabletconn_protocol()]) - args.extend(['-vreplication_healthcheck_topology_refresh', '1s']) - args.extend(['-vreplication_healthcheck_retry_delay', '1s']) - args.extend(['-vreplication_retry_delay', '1s']) - args.extend(['-pid_file', os.path.join(self.tablet_dir, 'vttablet.pid')]) - # always enable_replication_reporter with somewhat short values for tests - args.extend(['-health_check_interval', '2s']) - args.extend(['-enable_replication_reporter']) - args.extend(['-degraded_threshold', '5s']) - args.extend(['-lock_tables_timeout', '5s']) - args.extend(['-watch_replication_stream']) - if enable_semi_sync: - args.append('-enable_semi_sync') - # Remember the setting in case a test wants to know it. - self.enable_semi_sync = enable_semi_sync - if self.use_mysqlctld: - args.extend( - ['-mysqlctl_socket', os.path.join(self.tablet_dir, 'mysqlctl.sock')]) - - if self.external_mysql: - args.extend(['-db_host', '127.0.0.1']) - args.extend(['-db_port', str(self.mysql_port)]) - args.append('-disable_active_reparents') - if full_mycnf_args: - # this flag is used to specify all the mycnf_ flags, to make - # sure that code works. - relay_log_path = os.path.join(self.tablet_dir, 'relay-logs', - 'vt-%010d-relay-bin' % self.tablet_uid) - args.extend([ - '-mycnf_server_id', str(self.tablet_uid), - '-mycnf_data_dir', os.path.join(self.tablet_dir, 'data'), - '-mycnf_innodb_data_home_dir', os.path.join(self.tablet_dir, - 'innodb', 'data'), - '-mycnf_innodb_log_group_home_dir', os.path.join(self.tablet_dir, - 'innodb', 'logs'), - '-mycnf_socket_file', os.path.join(self.tablet_dir, 'mysql.sock'), - '-mycnf_error_log_path', os.path.join(self.tablet_dir, 'error.log'), - '-mycnf_slow_log_path', os.path.join(self.tablet_dir, - 'slow-query.log'), - '-mycnf_relay_log_path', relay_log_path, - '-mycnf_relay_log_index_path', relay_log_path + '.index', - '-mycnf_relay_log_info_path', os.path.join(self.tablet_dir, - 'relay-logs', - 'relay-log.info'), - '-mycnf_bin_log_path', os.path.join( - self.tablet_dir, 'bin-logs', 'vt-%010d-bin' % self.tablet_uid), - '-mycnf_master_info_file', os.path.join(self.tablet_dir, - 'master.info'), - '-mycnf_pid_file', os.path.join(self.tablet_dir, 'mysql.pid'), - '-mycnf_tmp_dir', os.path.join(self.tablet_dir, 'tmp'), - '-mycnf_slave_load_tmp_dir', os.path.join(self.tablet_dir, 'tmp'), - ]) - if include_mysql_port: - args.extend(['-mycnf_mysql_port', str(self.mysql_port)]) - - # this is used to run InitTablet as part of the vttablet startup - if init_tablet_type: - self.tablet_type = init_tablet_type - args.extend(['-init_tablet_type', init_tablet_type]) - if init_keyspace: - self.keyspace = init_keyspace - self.shard = init_shard - # tablet_index is required for the update_addr call below. - if self.tablet_index is None: - self.tablet_index = tablet_index - args.extend(['-init_keyspace', init_keyspace, - '-init_shard', init_shard]) - if init_db_name_override: - self.dbname = init_db_name_override - args.extend(['-init_db_name_override', init_db_name_override]) - else: - self.dbname = 'vt_' + init_keyspace - - # Default value for this flag is True. So, add it only if it's false. - if not binlog_use_v3_resharding_mode: - args.extend(['-binlog_use_v3_resharding_mode=false']) - - if supports_backups: - args.extend(['-restore_from_backup'] + get_backup_storage_flags()) - - # When vttablet restores from backup, it will re-generate the .cnf file. - # So we need to have EXTRA_MY_CNF set properly. - # When using mysqlctld, only mysqlctld should need EXTRA_MY_CNF. - # If any test fails without giving EXTRA_MY_CNF to vttablet, - # it means we missed some call that should run remotely on mysqlctld. - if not self.use_mysqlctld: - all_extra_my_cnf = get_all_extra_my_cnf(None) - if all_extra_my_cnf: - if not extra_env: - extra_env = {} - extra_env['EXTRA_MY_CNF'] = ':'.join(all_extra_my_cnf) - - if extra_args: - args.extend(extra_args) - - args.extend(['-port', '%s' % (port or self.port), - '-log_dir', environment.vtlogroot]) - - if topocustomrule_path: - args.extend(['-topocustomrule_path', topocustomrule_path]) - - if schema_override: - args.extend(['-schema-override', schema_override]) - - if table_acl_config: - args.extend(['-table-acl-config', table_acl_config]) - args.extend(['-queryserver-config-strict-table-acl']) - - if protocols_flavor().service_map(): - args.extend(['-service_map', ','.join(protocols_flavor().service_map())]) - if self.grpc_enabled(): - args.extend(['-grpc_port', str(self.grpc_port)]) - args.extend(['-grpc_max_message_size', - str(environment.grpc_max_message_size)]) - if lameduck_period: - args.extend(environment.lameduck_flag(lameduck_period)) - if grace_period: - args.extend(['-serving_state_grace_period', grace_period]) - if security_policy: - args.extend(['-security_policy', security_policy]) - - args.extend(['-enable-autocommit']) - stderr_fd = open( - os.path.join(environment.vtlogroot, 'vttablet-%d.stderr' % - self.tablet_uid), 'w') - # increment count only the first time - if not self.proc: - Tablet.tablets_running += 1 - self.proc = utils.run_bg(args, stderr=stderr_fd, extra_env=extra_env) - - log_message = ( - 'Started vttablet: %s (%s) with pid: %s - Log files: ' - '%s/vttablet.*.{INFO,WARNING,ERROR,FATAL}.*.%s' % - (self.tablet_uid, self.tablet_alias, self.proc.pid, - environment.vtlogroot, self.proc.pid)) - # This may race with the stderr output from the process (though - # that's usually empty). - stderr_fd.write(log_message + '\n') - stderr_fd.close() - logging.debug(log_message) - - # wait for query service to be in the right state - if wait_for_state: - self.wait_for_vttablet_state(wait_for_state, port=port) - - if self.tablet_index is not None: - topo_server().update_addr( - 'test_'+self.cell, self.keyspace, self.shard, - self.tablet_index, (port or self.port)) - - return self.proc - - def wait_for_vttablet_state(self, expected, timeout=60.0, port=None): - expr = re.compile('^' + expected + '$') - while True: - v = utils.get_vars(port or self.port) - last_seen_state = '?' - if v is None: - if self.proc.poll() is not None: - raise utils.TestError( - 'vttablet died while test waiting for state %s' % expected) - logging.debug( - ' vttablet %s not answering at /debug/vars, waiting...', - self.tablet_alias) - else: - if 'TabletStateName' not in v: - logging.debug( - ' vttablet %s not exporting TabletStateName, waiting...', - self.tablet_alias) - else: - s = v['TabletStateName'] - last_seen_state = s - if expr.match(s): - break - else: - logging.debug( - ' vttablet %s in state: %s, expected: %s', self.tablet_alias, s, - expected) - timeout = utils.wait_step( - '%s state %s (last seen state: %s)' % - (self.tablet_alias, expected, last_seen_state), - timeout, sleep_time=0.1) - - def wait_for_mysqlctl_socket(self, timeout=60.0): - mysql_sock = os.path.join(self.tablet_dir, 'mysql.sock') - mysqlctl_sock = os.path.join(self.tablet_dir, 'mysqlctl.sock') - while True: - wait_for = [] - if not os.path.exists(mysql_sock): - wait_for.append(mysql_sock) - if not os.path.exists(mysqlctl_sock): - wait_for.append(mysqlctl_sock) - if not wait_for: - return - timeout = utils.wait_step('waiting for socket files: %s' % str(wait_for), - timeout, sleep_time=2.0) - - def get_status(self): - return utils.get_status(self.port) - - def get_healthz(self): - return urllib2.urlopen('http://localhost:%d/healthz' % self.port).read() - - def kill_vttablet(self, wait=True): - """Sends a SIG_TERM to the tablet. - - Args: - wait: will wait for the process to exit. - Returns: - the subprocess object (use it to get exit code). - """ - logging.debug('killing vttablet: %s, wait: %s', self.tablet_alias, - str(wait)) - proc = self.proc - if proc is not None: - Tablet.tablets_running -= 1 - if proc.poll() is None: - proc.terminate() - if wait: - proc.wait() - self.proc = None - return proc - - def hard_kill_vttablet(self): - logging.debug('hard killing vttablet: %s', self.tablet_alias) - if self.proc is not None: - Tablet.tablets_running -= 1 - if self.proc.poll() is None: - self.proc.kill() - self.proc.wait() - self.proc = None - - def wait_for_binlog_server_state(self, expected, timeout=30.0): - """Wait for the tablet's binlog server to be in the provided state. - - Args: - expected: the state to wait for. - timeout: how long to wait before error. - """ - while True: - v = utils.get_vars(self.port) - if v is None: - if self.proc.poll() is not None: - raise utils.TestError( - 'vttablet died while test waiting for binlog state %s' % - expected) - logging.debug(' vttablet not answering at /debug/vars, waiting...') - else: - if 'UpdateStreamState' not in v: - logging.debug( - ' vttablet not exporting BinlogServerState, waiting...') - else: - s = v['UpdateStreamState'] - if s != expected: - logging.debug(" vttablet's binlog server in state %s != %s", s, - expected) - else: - break - timeout = utils.wait_step( - 'waiting for binlog server state %s' % expected, - timeout, sleep_time=0.5) - logging.debug('tablet %s binlog service is in state %s', - self.tablet_alias, expected) - - def wait_for_binlog_player_count(self, expected, timeout=30.0): - """Wait for a tablet to have binlog players. - - Args: - expected: number of expected binlog players to wait for. - timeout: how long to wait. - """ - while True: - v = utils.get_vars(self.port) - if v is None: - if self.proc.poll() is not None: - raise utils.TestError( - 'vttablet died while test waiting for binlog count %s' % - expected) - logging.debug(' vttablet not answering at /debug/vars, waiting...') - else: - if 'VReplicationStreamCount' not in v: - logging.debug( - ' vttablet not exporting VReplicationStreamCount, waiting...') - else: - s = v['VReplicationStreamCount'] - if s != expected: - logging.debug(" vttablet's binlog player map has count %d != %d", - s, expected) - else: - break - timeout = utils.wait_step( - 'waiting for binlog player count %d' % expected, - timeout, sleep_time=0.5) - logging.debug('tablet %s binlog player has %d players', - self.tablet_alias, expected) - - @classmethod - def check_vttablet_count(cls): - if Tablet.tablets_running > 0: - raise utils.TestError('This test is not killing all its vttablets') - - def execute(self, sql, bindvars=None, transaction_id=None, - execute_options=None, auto_log=True): - """execute uses 'vtctl VtTabletExecute' to execute a command. - - Args: - sql: the command to execute. - bindvars: a dict of bind variables. - transaction_id: the id of the transaction to use if necessary. - execute_options: proto-encoded ExecuteOptions object. - auto_log: passed to run_vtctl. - - Returns: - the result of running vtctl command. - """ - args = [ - 'VtTabletExecute', '-json', - ] - if bindvars: - args.extend(['-bind_variables', json.dumps(bindvars)]) - if transaction_id: - args.extend(['-transaction_id', str(transaction_id)]) - if execute_options: - args.extend(['-options', execute_options]) - args.extend([self.tablet_alias, sql]) - return utils.run_vtctl_json(args, auto_log=auto_log) - - def begin(self, auto_log=True): - """begin uses 'vtctl VtTabletBegin' to start a transaction. - - Args: - auto_log: passed to run_vtctl. - - Returns: - the transaction id. - """ - args = [ - 'VtTabletBegin', - self.tablet_alias, - ] - result = utils.run_vtctl_json(args, auto_log=auto_log) - return result['transaction_id'] - - def commit(self, transaction_id, auto_log=True): - """commit uses 'vtctl VtTabletCommit' to commit a transaction. - - Args: - transaction_id: id of the transaction to roll back. - auto_log: passed to run_vtctl. - - Returns: - the return code for run_vtctl. - """ - args = [ - 'VtTabletCommit', - self.tablet_alias, - str(transaction_id), - ] - return utils.run_vtctl(args, auto_log=auto_log) - - def rollback(self, transaction_id, auto_log=True): - """rollback uses 'vtctl VtTabletRollback' to rollback a transaction. - - Args: - transaction_id: id of the transaction to roll back. - auto_log: passed to run_vtctl. - - Returns: - the return code for run_vtctl. - """ - args = [ - 'VtTabletRollback', - self.tablet_alias, - str(transaction_id), - ] - return utils.run_vtctl(args, auto_log=auto_log) - - def rpc_endpoint(self): - """Returns the protocol and endpoint to use for RPCs.""" - if self.grpc_enabled(): - return 'localhost:%d' % self.grpc_port - return 'localhost:%d' % self.port - - def tablet_manager(self): - """Returns a rpc client able to talk to the TabletManager rpc server in go""" - addr = self.rpc_endpoint() - p = urlparse('http://' + addr) - channel = grpc.insecure_channel('%s:%s' % (p.hostname, p.port)) - return TabletManagerStub(channel) - -def kill_tablets(tablets): - for t in tablets: - logging.debug('killing vttablet: %s', t.tablet_alias) - if t.proc is not None: - Tablet.tablets_running -= 1 - t.proc.terminate() - - for t in tablets: - if t.proc is not None: - t.proc.wait() - t.proc = None - - diff --git a/test/tabletmanager.py b/test/tabletmanager.py deleted file mode 100755 index 4cc5f059ac8..00000000000 --- a/test/tabletmanager.py +++ /dev/null @@ -1,914 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# vim: tabstop=8 expandtab shiftwidth=2 softtabstop=2 - -import json -import logging -import os -import time -import unittest -import urllib -import urllib2 -import re - -import MySQLdb - -import environment -import utils -import tablet -from mysql_flavor import mysql_flavor -from protocols_flavor import protocols_flavor - -from vtproto import topodata_pb2 - -tablet_62344 = tablet.Tablet(62344) -tablet_62044 = tablet.Tablet(62044) - -# regexp to check if the tablet status page reports healthy, -# regardless of actual replication lag -healthy_expr = re.compile(r'Current status: healthy') - - -def setUpModule(): - try: - topo_flavor = environment.topo_server().flavor() - environment.topo_server().setup() - - # start mysql instance external to the test - setup_procs = [ - tablet_62344.init_mysql(), - tablet_62044.init_mysql(), - ] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [ - tablet_62344.teardown_mysql(), - tablet_62044.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - tablet_62344.remove_tree() - tablet_62044.remove_tree() - - -class TestTabletManager(unittest.TestCase): - - def tearDown(self): - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in [tablet_62344, tablet_62044]: - t.reset_replication() - t.set_semi_sync_enabled(master=False) - t.clean_dbs() - - # run twice to check behavior with existing znode data - def test_sanity(self): - self._test_sanity() - self._test_sanity() - - def _test_sanity(self): - # Start up a master mysql and vttablet - utils.run_vtctl(['CreateKeyspace', '-force', 'test_keyspace']) - utils.run_vtctl(['createshard', '-force', 'test_keyspace/0']) - tablet_62344.init_tablet('master', 'test_keyspace', '0', parent=False) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace']) - # if these statements don't run before the tablet it will wedge - # waiting for the db to become accessible. this is more a bug than - # a feature. - tablet_62344.populate('vt_test_keyspace', self._create_vt_select_test, - self._populate_vt_select_test) - - tablet_62344.start_vttablet() - utils.validate_topology() - - - # make sure the query service is started right away. - qr = tablet_62344.execute('select id, msg from vt_select_test') - self.assertEqual(len(qr['rows']), 4, - 'expected 4 rows in vt_select_test: %s' % str(qr)) - self.assertEqual(qr['fields'][0]['name'], 'id') - self.assertEqual(qr['fields'][1]['name'], 'msg') - - # test exclude_field_names to vttablet works as expected. - qr = tablet_62344.execute('select id, msg from vt_select_test', - execute_options='included_fields:TYPE_ONLY ') - self.assertEqual(len(qr['rows']), 4, - 'expected 4 rows in vt_select_test: %s' % str(qr)) - self.assertNotIn('name', qr['fields'][0]) - self.assertNotIn('name', qr['fields'][1]) - - # make sure direct dba queries work - query_result = utils.run_vtctl_json( - ['ExecuteFetchAsDba', '-json', tablet_62344.tablet_alias, - 'select * from vt_test_keyspace.vt_select_test']) - self.assertEqual( - len(query_result['rows']), 4, - 'expected 4 rows in vt_select_test: %s' % str(query_result)) - self.assertEqual( - len(query_result['fields']), 2, - 'expected 2 fields in vt_select_test: %s' % str(query_result)) - - # check Ping / RefreshState / RefreshStateByShard - utils.run_vtctl(['Ping', tablet_62344.tablet_alias]) - utils.run_vtctl(['RefreshState', tablet_62344.tablet_alias]) - utils.run_vtctl(['RefreshStateByShard', 'test_keyspace/0']) - utils.run_vtctl(['RefreshStateByShard', '--cells=test_nj', - 'test_keyspace/0']) - - # Quickly check basic actions. - utils.run_vtctl(['SetReadOnly', tablet_62344.tablet_alias]) - utils.wait_db_read_only(62344) - - utils.run_vtctl(['SetReadWrite', tablet_62344.tablet_alias]) - utils.check_db_read_write(62344) - - utils.validate_topology() - utils.run_vtctl(['ValidateKeyspace', 'test_keyspace']) - # not pinging tablets, as it enables replication checks, and they - # break because we only have a single master, no slaves - utils.run_vtctl(['ValidateShard', '-ping-tablets=false', - 'test_keyspace/0']) - - tablet_62344.kill_vttablet() - - _create_vt_select_test = '''create table vt_select_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - _populate_vt_select_test = [ - "insert into vt_select_test (msg) values ('test %s')" % x - for x in xrange(4)] - - # Test if a vttablet can be pointed at an existing mysql - # We point 62044 at 62344's mysql and try to read from it. - def test_command_line(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - tablet_62044.init_tablet('master', 'test_keyspace', '0') - tablet_62344.populate('vt_test_keyspace', self._create_vt_select_test, - self._populate_vt_select_test) - - # mycnf_server_id prevents vttablet from reading the mycnf - extra_args = [ - '-mycnf_server_id', str(tablet_62044.tablet_uid), - '-db_socket', os.path.join(tablet_62344.tablet_dir, 'mysql.sock')] - # supports_backup=False prevents vttablet from trying to restore - tablet_62044.start_vttablet(extra_args=extra_args, supports_backups=False) - qr = tablet_62044.execute('select id, msg from vt_select_test') - self.assertEqual(len(qr['rows']), 4, - 'expected 4 rows in vt_select_test: %s' % str(qr)) - - # Verify backup fails - try: - utils.run_vtctl(['Backup', tablet_62044.tablet_alias]) - except Exception as e: - self.assertIn('cannot perform backup without my.cnf', str(e)) - else: - self.assertFail('did not get an exception') - - tablet_62044.kill_vttablet() - - def test_actions_and_timeouts(self): - # Start up a master mysql and vttablet - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - tablet_62344.init_tablet('master', 'test_keyspace', '0') - tablet_62344.create_db('vt_test_keyspace') - tablet_62344.start_vttablet() - # validate topology after starting tablet so that tablet has a chance - # to update shard master_alias - timeout = 10 - while True: - shard = utils.run_vtctl_json(['GetShard', 'test_keyspace/0']) - if shard['master_alias']['uid'] == 62344: - break - wait_step('master_alias has been set', timeout) - - utils.validate_topology() - - utils.run_vtctl(['Ping', tablet_62344.tablet_alias]) - - # schedule long action in the background, sleep a little bit to make sure - # it started to run - args = (environment.binary_args('vtctl') + - environment.topo_server().flags() + - ['-tablet_manager_protocol', - protocols_flavor().tablet_manager_protocol(), - '-tablet_protocol', protocols_flavor().tabletconn_protocol(), - '-log_dir', environment.vtlogroot, - 'Sleep', tablet_62344.tablet_alias, '10s']) - bg = utils.run_bg(args) - time.sleep(3) - - # try a frontend RefreshState that should timeout as the tablet is busy - # running the other one - _, stderr = utils.run_vtctl( - ['-wait-time', '3s', 'RefreshState', tablet_62344.tablet_alias], - expect_fail=True) - self.assertIn(protocols_flavor().rpc_timeout_message(), stderr) - - # wait for the background vtctl - bg.wait() - - tablet_62344.kill_vttablet() - - def _run_hook(self, params, expected_status, expected_stdout, - expected_stderr): - hr = utils.run_vtctl_json(['ExecuteHook', tablet_62344.tablet_alias] + - params) - self.assertEqual(hr['ExitStatus'], expected_status) - if isinstance(expected_stdout, basestring): - self.assertEqual(hr['Stdout'], expected_stdout) - else: - found = False - for exp in expected_stdout: - if hr['Stdout'] == exp: - found = True - break - if not found: - self.assertFail( - 'cannot find expected %s in %s' % - (str(expected_stdout), hr['Stdout'])) - if expected_stderr[-1:] == '%': - self.assertEqual( - hr['Stderr'][:len(expected_stderr)-1], - expected_stderr[:len(expected_stderr)-1]) - else: - self.assertEqual(hr['Stderr'], expected_stderr) - - def test_hook(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - # create the database so vttablets start, as it is serving - tablet_62344.create_db('vt_test_keyspace') - - tablet_62344.init_tablet('master', 'test_keyspace', '0', start=True) - - # test a regular program works - self._run_hook(['test.sh', '--flag1', '--param1=hello'], 0, - ['TABLET_ALIAS: test_nj-0000062344\n' - 'PARAM: --flag1\n' - 'PARAM: --param1=hello\n', - 'TABLET_ALIAS: test_nj-0000062344\n' - 'PARAM: --param1=hello\n' - 'PARAM: --flag1\n'], - '') - - # test stderr output - self._run_hook(['test.sh', '--to-stderr'], 0, - 'TABLET_ALIAS: test_nj-0000062344\n' - 'PARAM: --to-stderr\n', - 'ERR: --to-stderr\n') - - # test commands that fail - self._run_hook(['test.sh', '--exit-error'], 1, - 'TABLET_ALIAS: test_nj-0000062344\n' - 'PARAM: --exit-error\n', - 'ERROR: exit status 1\n') - - # test hook that is not present - self._run_hook(['not_here.sh'], -1, - '', - 'missing hook /%') # cannot go further, local path - - # test hook with invalid name - _, err = utils.run_vtctl(['--alsologtostderr', 'ExecuteHook', - tablet_62344.tablet_alias, - '/bin/ls'], - mode=utils.VTCTL_VTCTL, trap_output=True, - raise_on_error=False) - expected = "action failed: ExecuteHook hook name cannot have a '/' in it" - self.assertIn(expected, err) - - tablet_62344.kill_vttablet() - - def test_shard_replication_fix(self): - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - - tablet_62344.create_db('vt_test_keyspace') - tablet_62044.create_db('vt_test_keyspace') - - # one master one replica - tablet_62344.init_tablet('master', 'test_keyspace', '0') - tablet_62044.init_tablet('replica', 'test_keyspace', '0') - - # make sure the replica is in the replication graph - before_bogus = utils.run_vtctl_json(['GetShardReplication', 'test_nj', - 'test_keyspace/0']) - self.assertEqual(2, len(before_bogus['nodes']), - 'wrong shard replication nodes before: %s' % - str(before_bogus)) - - # manually add a bogus entry to the replication graph, and check - # it is removed by ShardReplicationFix - utils.run_vtctl(['ShardReplicationAdd', 'test_keyspace/0', - 'test_nj-0000066666'], auto_log=True) - with_bogus = utils.run_vtctl_json(['GetShardReplication', 'test_nj', - 'test_keyspace/0']) - self.assertEqual(3, len(with_bogus['nodes']), - 'wrong shard replication nodes with bogus: %s' % - str(with_bogus)) - utils.run_vtctl(['ShardReplicationFix', 'test_nj', 'test_keyspace/0'], - auto_log=True) - after_fix = utils.run_vtctl_json(['GetShardReplication', 'test_nj', - 'test_keyspace/0']) - self.assertEqual(2, len(after_fix['nodes']), - 'wrong shard replication nodes after fix: %s' % - str(after_fix)) - - def check_healthz(self, t, expected): - if expected: - self.assertEqual('ok\n', t.get_healthz()) - else: - with self.assertRaises(urllib2.HTTPError): - t.get_healthz() - - def test_health_check(self): - # one master, one replica that starts not initialized - # (for the replica, we let vttablet do the InitTablet) - tablet_62344.init_tablet('replica', 'test_keyspace', '0') - - for t in tablet_62344, tablet_62044: - t.create_db('vt_test_keyspace') - - tablet_62344.start_vttablet(wait_for_state=None) - tablet_62044.start_vttablet(wait_for_state=None, - lameduck_period='5s', - init_tablet_type='replica', - init_keyspace='test_keyspace', - init_shard='0') - - tablet_62344.wait_for_vttablet_state('NOT_SERVING') - tablet_62044.wait_for_vttablet_state('NOT_SERVING') - self.check_healthz(tablet_62044, False) - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias]) - - # make sure the unhealthy slave goes to healthy - tablet_62044.wait_for_vttablet_state('SERVING') - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - self.check_healthz(tablet_62044, True) - - # make sure the master is still master - ti = utils.run_vtctl_json(['GetTablet', tablet_62344.tablet_alias]) - self.assertEqual(ti['type'], topodata_pb2.MASTER, - 'unexpected master type: %s' % ti['type']) - - # stop replication at the mysql level. - tablet_62044.mquery('', 'stop slave') - # vttablet replication_reporter should restart it. - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - # insert something on the master and wait for it on the slave. - tablet_62344.mquery('vt_test_keyspace', [ - 'create table repl_test_table (id int)', - 'insert into repl_test_table values (123)'], write=True) - timeout = 10.0 - while True: - try: - result = tablet_62044.mquery('vt_test_keyspace', - 'select * from repl_test_table') - if result: - self.assertEqual(result[0][0], 123L) - break - except MySQLdb.ProgrammingError: - # Maybe the create table hasn't gone trough yet, we wait more - logging.exception('got this exception waiting for data, ignoring it') - timeout = utils.wait_step( - 'slave replication repaired by replication_reporter', timeout) - - # stop replication, make sure we don't go unhealthy. - # (we have a baseline as well, so the time should be good). - utils.run_vtctl(['StopSlave', tablet_62044.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - self.check_healthz(tablet_62044, True) - - # make sure status web page is healthy - self.assertRegexpMatches(tablet_62044.get_status(), healthy_expr) - - # make sure the health stream is updated - health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - tablet_62044.tablet_alias]) - self.assertTrue(('seconds_behind_master' not in health['realtime_stats']) or - (health['realtime_stats']['seconds_behind_master'] < 30), - 'got unexpected health: %s' % str(health)) - self.assertIn('serving', health) - - # then restart replication, make sure we stay healthy - utils.run_vtctl(['StartSlave', tablet_62044.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - - # make sure status web page is healthy - self.assertRegexpMatches(tablet_62044.get_status(), healthy_expr) - - # now test VtTabletStreamHealth returns the right thing - stdout, _ = utils.run_vtctl(['VtTabletStreamHealth', - '-count', '2', - tablet_62044.tablet_alias], - trap_output=True, auto_log=True) - lines = stdout.splitlines() - self.assertEqual(len(lines), 2) - for line in lines: - logging.debug('Got health: %s', line) - data = json.loads(line) - self.assertIn('realtime_stats', data) - self.assertIn('serving', data) - self.assertTrue(data['serving']) - self.assertNotIn('health_error', data['realtime_stats']) - self.assertNotIn('tablet_externally_reparented_timestamp', data) - self.assertEqual('test_keyspace', data['target']['keyspace']) - self.assertEqual('0', data['target']['shard']) - self.assertEqual(topodata_pb2.REPLICA, data['target']['tablet_type']) - - # Test that VtTabletStreamHealth reports a QPS >0.0. - # Therefore, issue several reads first. - # NOTE: This may be potentially flaky because we'll observe a QPS >0.0 - # exactly "once" for the duration of one sampling interval (5s) and - # after that we'll see 0.0 QPS rates again. If this becomes actually - # flaky, we need to read continuously in a separate thread. - for _ in range(10): - tablet_62044.execute('select 1 from dual') - # This may take up to 5 seconds to become true because we sample the query - # counts for the rates only every 5 seconds (see query_service_stats.go). - timeout = 10 - while True: - health = utils.run_vtctl_json(['VtTabletStreamHealth', '-count', '1', - tablet_62044.tablet_alias]) - if health['realtime_stats'].get('qps', 0.0) > 0.0: - break - timeout = utils.wait_step('QPS >0.0 seen', timeout) - - # kill the tablets - tablet.kill_tablets([tablet_62344, tablet_62044]) - - def test_health_check_drained_state_does_not_shutdown_query_service(self): - # This test is similar to test_health_check, but has the following - # differences: - # - the second tablet is an 'rdonly' and not a 'replica' - # - the second tablet will be set to 'drained' and we expect that - # the query service won't be shutdown - - # Setup master and rdonly tablets. - tablet_62344.init_tablet('replica', 'test_keyspace', '0') - - for t in tablet_62344, tablet_62044: - t.create_db('vt_test_keyspace') - - # Note we only have a master and a rdonly. So we can't enable - # semi-sync in this case, as the rdonly slaves don't semi-sync ack. - tablet_62344.start_vttablet(wait_for_state=None, enable_semi_sync=False) - tablet_62044.start_vttablet(wait_for_state=None, - init_tablet_type='rdonly', - init_keyspace='test_keyspace', - init_shard='0', - enable_semi_sync=False) - - tablet_62344.wait_for_vttablet_state('NOT_SERVING') - tablet_62044.wait_for_vttablet_state('NOT_SERVING') - self.check_healthz(tablet_62044, False) - - # Enable replication. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias]) - - # Trigger healthcheck to save time waiting for the next interval. - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - tablet_62044.wait_for_vttablet_state('SERVING') - self.check_healthz(tablet_62044, True) - - # Change from rdonly to drained and stop replication. (These - # actions are similar to the SplitClone vtworker command - # implementation.) The tablet will stay healthy, and the - # query service is still running. - utils.run_vtctl(['ChangeSlaveType', tablet_62044.tablet_alias, 'drained']) - # Trying to drain the same tablet again, should error - try: - utils.run_vtctl(['ChangeSlaveType', tablet_62044.tablet_alias, 'drained']) - except Exception as e: - s = str(e) - self.assertIn("already drained", s) - utils.run_vtctl(['StopSlave', tablet_62044.tablet_alias]) - # Trigger healthcheck explicitly to avoid waiting for the next interval. - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - utils.wait_for_tablet_type(tablet_62044.tablet_alias, 'drained') - self.check_healthz(tablet_62044, True) - # Query service is still running. - tablet_62044.wait_for_vttablet_state('SERVING') - - # Restart replication. Tablet will become healthy again. - utils.run_vtctl(['ChangeSlaveType', tablet_62044.tablet_alias, 'rdonly']) - utils.run_vtctl(['StartSlave', tablet_62044.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias]) - self.check_healthz(tablet_62044, True) - - # kill the tablets - tablet.kill_tablets([tablet_62344, tablet_62044]) - - def test_no_mysql_healthcheck(self): - """This test starts a vttablet with no mysql port, while mysql is down. - - It makes sure vttablet will start properly and be unhealthy. - Then we start mysql, and make sure vttablet becomes healthy. - """ - # we need replication to be enabled, so the slave tablet can be healthy. - for t in tablet_62344, tablet_62044: - t.create_db('vt_test_keyspace') - pos = mysql_flavor().master_position(tablet_62344) - # Use 'localhost' as hostname because Travis CI worker hostnames - # are too long for MySQL replication. - change_master_cmds = mysql_flavor().change_master_commands( - 'localhost', - tablet_62344.mysql_port, - pos) - tablet_62044.mquery('', ['RESET MASTER', 'RESET SLAVE'] + - change_master_cmds + ['START SLAVE']) - - # now shutdown all mysqld - shutdown_procs = [ - tablet_62344.shutdown_mysql(), - tablet_62044.shutdown_mysql(), - ] - utils.wait_procs(shutdown_procs) - - # start the tablets, wait for them to be NOT_SERVING (mysqld not there) - tablet_62344.init_tablet('master', 'test_keyspace', '0') - tablet_62044.init_tablet('replica', 'test_keyspace', '0', - include_mysql_port=False) - for t in tablet_62344, tablet_62044: - # Since MySQL is down at this point and we want the tablet to start up - # successfully, we have to use supports_backups=False. - t.start_vttablet(wait_for_state=None, supports_backups=False, - full_mycnf_args=True, include_mysql_port=False) - for t in tablet_62344, tablet_62044: - t.wait_for_vttablet_state('NOT_SERVING') - self.check_healthz(t, False) - - # Tell slave to not try to repair replication in healthcheck. - # The StopSlave will ultimately fail because mysqld is not running, - # But vttablet should remember that it's not supposed to fix replication. - utils.run_vtctl(['StopSlave', tablet_62044.tablet_alias], expect_fail=True) - - # The above notice to not fix replication should survive tablet restart. - tablet_62044.kill_vttablet() - tablet_62044.start_vttablet(wait_for_state='NOT_SERVING', - full_mycnf_args=True, include_mysql_port=False, - supports_backups=False) - - # restart mysqld - start_procs = [ - tablet_62344.start_mysql(), - tablet_62044.start_mysql(), - ] - utils.wait_procs(start_procs) - - # the master should still be healthy - utils.run_vtctl(['RunHealthCheck', tablet_62344.tablet_alias], - auto_log=True) - self.check_healthz(tablet_62344, True) - - # the slave will now be healthy, but report a very high replication - # lag, because it can't figure out what it exactly is. - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias], - auto_log=True) - tablet_62044.wait_for_vttablet_state('SERVING') - self.check_healthz(tablet_62044, True) - - health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - tablet_62044.tablet_alias]) - self.assertIn('seconds_behind_master', health['realtime_stats']) - self.assertEqual(health['realtime_stats']['seconds_behind_master'], 7200) - self.assertIn('serving', health) - - # restart replication, wait until health check goes small - # (a value of zero is default and won't be in structure) - utils.run_vtctl(['StartSlave', tablet_62044.tablet_alias]) - timeout = 10 - while True: - utils.run_vtctl(['RunHealthCheck', tablet_62044.tablet_alias], - auto_log=True) - health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - tablet_62044.tablet_alias]) - if 'serving' in health and ( - ('seconds_behind_master' not in health['realtime_stats']) or - (health['realtime_stats']['seconds_behind_master'] < 30)): - break - timeout = utils.wait_step('health delay goes back down', timeout) - - # wait for the tablet to fix its mysql port - for t in tablet_62344, tablet_62044: - # wait for mysql port to show up - timeout = 10 - while True: - ti = utils.run_vtctl_json(['GetTablet', t.tablet_alias]) - if 'mysql' in ti['port_map']: - break - timeout = utils.wait_step('mysql port in tablet record', timeout) - self.assertEqual(ti['port_map']['mysql'], t.mysql_port) - - # all done - tablet.kill_tablets([tablet_62344, tablet_62044]) - - def test_repeated_init_shard_master(self): - """Test that using InitShardMaster can go back and forth between 2 hosts.""" - for t in tablet_62344, tablet_62044: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None, - lameduck_period='5s', - init_tablet_type='replica', - init_keyspace='test_keyspace', - init_shard='0') - - # Tablets are not replicating, so they won't be healthy. - for t in tablet_62344, tablet_62044: - t.wait_for_vttablet_state('NOT_SERVING') - self.check_healthz(t, False) - - # Pick one master out of the two. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias]) - - # Run health check on both, make sure they are both healthy. - # Also make sure the types are correct. - for t in tablet_62344, tablet_62044: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], auto_log=True) - self.check_healthz(t, True) - utils.wait_for_tablet_type(tablet_62344.tablet_alias, 'master', timeout=0) - utils.wait_for_tablet_type(tablet_62044.tablet_alias, 'replica', timeout=0) - - # Pick the other one as master, make sure they are still healthy. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62044.tablet_alias]) - - # Run health check on both, make sure they are both healthy. - # Also make sure the types are correct. - for t in tablet_62344, tablet_62044: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], auto_log=True) - self.check_healthz(t, True) - utils.wait_for_tablet_type(tablet_62344.tablet_alias, 'replica', timeout=0) - utils.wait_for_tablet_type(tablet_62044.tablet_alias, 'master', timeout=0) - - # Come back to the original guy. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - tablet_62344.tablet_alias]) - - # Run health check on both, make sure they are both healthy. - # Also make sure the types are correct. - for t in tablet_62344, tablet_62044: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], auto_log=True) - self.check_healthz(t, True) - utils.wait_for_tablet_type(tablet_62344.tablet_alias, 'master', timeout=0) - utils.wait_for_tablet_type(tablet_62044.tablet_alias, 'replica', timeout=0) - - # And done. - tablet.kill_tablets([tablet_62344, tablet_62044]) - - def test_fallback_security_policy(self): - tablet_62344.create_db('vt_test_keyspace') - tablet_62344.init_tablet('master', 'test_keyspace', '0') - - # Requesting an unregistered security_policy should fall back to deny-all. - tablet_62344.start_vttablet(security_policy='bogus') - - # It should deny ADMIN role. - f = urllib.urlopen('http://localhost:%d/streamqueryz/terminate' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertIn('not allowed', response) - - # It should deny MONITORING role. - f = urllib.urlopen('http://localhost:%d/debug/health' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertIn('not allowed', response) - - # It should deny DEBUGGING role. - f = urllib.urlopen('http://localhost:%d/queryz' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertIn('not allowed', response) - - tablet_62344.kill_vttablet() - - def test_deny_all_security_policy(self): - tablet_62344.create_db('vt_test_keyspace') - tablet_62344.init_tablet('master', 'test_keyspace', '0') - tablet_62344.start_vttablet(security_policy='deny-all') - - # It should deny ADMIN role. - f = urllib.urlopen('http://localhost:%d/streamqueryz/terminate' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertIn('not allowed', response) - - # It should deny MONITORING role. - f = urllib.urlopen('http://localhost:%d/debug/health' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertIn('not allowed', response) - - # It should deny DEBUGGING role. - f = urllib.urlopen('http://localhost:%d/queryz' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertIn('not allowed', response) - - tablet_62344.kill_vttablet() - - def test_read_only_security_policy(self): - tablet_62344.create_db('vt_test_keyspace') - tablet_62344.init_tablet('master', 'test_keyspace', '0') - tablet_62344.start_vttablet(security_policy='read-only') - - # It should deny ADMIN role. - f = urllib.urlopen('http://localhost:%d/streamqueryz/terminate' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertIn('not allowed', response) - - # It should allow MONITORING role. - f = urllib.urlopen('http://localhost:%d/debug/health' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertNotIn('not allowed', response) - - # It should allow DEBUGGING role. - f = urllib.urlopen('http://localhost:%d/queryz' % int(tablet_62344.port)) - response = f.read() - f.close() - self.assertNotIn('not allowed', response) - - tablet_62344.kill_vttablet() - - def test_ignore_health_error(self): - tablet_62344.create_db('vt_test_keyspace') - - # Starts unhealthy because of "no slave status" (not replicating). - tablet_62344.start_vttablet(wait_for_state='NOT_SERVING', - init_tablet_type='replica', - init_keyspace='test_keyspace', - init_shard='0') - - # Force it healthy. - utils.run_vtctl(['IgnoreHealthError', tablet_62344.tablet_alias, - '.*no slave status.*']) - utils.run_vtctl(['RunHealthCheck', tablet_62344.tablet_alias], - auto_log=True) - tablet_62344.wait_for_vttablet_state('SERVING') - self.check_healthz(tablet_62344, True) - - # Turn off the force-healthy. - utils.run_vtctl(['IgnoreHealthError', tablet_62344.tablet_alias, '']) - utils.run_vtctl(['RunHealthCheck', tablet_62344.tablet_alias], - auto_log=True) - tablet_62344.wait_for_vttablet_state('NOT_SERVING') - self.check_healthz(tablet_62344, False) - - tablet_62344.kill_vttablet() - - def test_master_restart_sets_ter_timestamp(self): - """Test that TER timestamp is set when we restart the MASTER vttablet. - - TER = TabletExternallyReparented. - See StreamHealthResponse.tablet_externally_reparented_timestamp for details. - """ - master, replica = tablet_62344, tablet_62044 - tablets = [master, replica] - # Start vttablets. Our future master is initially a REPLICA. - for t in tablets: - t.create_db('vt_test_keyspace') - for t in tablets: - t.start_vttablet(wait_for_state='NOT_SERVING', - init_tablet_type='replica', - init_keyspace='test_keyspace', - init_shard='0') - - # Initialize tablet as MASTER. - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - master.tablet_alias]) - master.wait_for_vttablet_state('SERVING') - - # Capture the current TER. - health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - master.tablet_alias]) - self.assertEqual(topodata_pb2.MASTER, health['target']['tablet_type']) - self.assertIn('tablet_externally_reparented_timestamp', health) - self.assertGreater(health['tablet_externally_reparented_timestamp'], 0, - 'TER on MASTER must be set after InitShardMaster') - - # Restart the MASTER vttablet. - master.kill_vttablet() - master.start_vttablet(wait_for_state='SERVING', - init_tablet_type='replica', - init_keyspace='test_keyspace', - init_shard='0') - - # Make sure that the TER did not change - health_after_restart = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - master.tablet_alias]) - self.assertEqual(topodata_pb2.MASTER, - health_after_restart['target']['tablet_type']) - self.assertIn('tablet_externally_reparented_timestamp', - health_after_restart) - self.assertEqual( - health_after_restart['tablet_externally_reparented_timestamp'], - health['tablet_externally_reparented_timestamp'], - 'When the MASTER vttablet was restarted, the TER timestamp must be set' - ' by reading the old value from the tablet record. Old: %s, New: %s' % (str(health['tablet_externally_reparented_timestamp']), str(health_after_restart['tablet_externally_reparented_timestamp']))) - - # Shutdown. - for t in tablets: - t.kill_vttablet() - - def test_topocustomrule(self): - # Empty rule file. - topocustomrule_file = environment.tmproot+'/rules.json' - with open(topocustomrule_file, 'w') as fd: - fd.write('[]\n') - - # Start up a master mysql and vttablet - utils.run_vtctl(['CreateKeyspace', '-force', 'test_keyspace']) - utils.run_vtctl(['createshard', '-force', 'test_keyspace/0']) - tablet_62344.init_tablet('master', 'test_keyspace', '0', parent=False) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace']) - # Copy config file into topo. - topocustomrule_path = '/keyspaces/test_keyspace/configs/CustomRules' - utils.run_vtctl(['TopoCp', '-to_topo', topocustomrule_file, - topocustomrule_path]) - - # Put some data in, start master. - tablet_62344.populate('vt_test_keyspace', self._create_vt_select_test, - self._populate_vt_select_test) - tablet_62344.start_vttablet(topocustomrule_path=topocustomrule_path) - - utils.validate_topology() - - # make sure the query service is working - qr = tablet_62344.execute('select id, msg from vt_select_test') - self.assertEqual(len(qr['rows']), 4, - 'expected 4 rows in vt_select_test: %s' % str(qr)) - - # Now update the topocustomrule file. - with open(topocustomrule_file, 'w') as fd: - fd.write(''' - [{ - "Name": "rule1", - "Description": "disallow select on table vt_select_test", - "TableNames" : ["vt_select_test"], - "Query" : "(select)|(SELECT)" - }]''') - utils.run_vtctl(['TopoCp', '-to_topo', topocustomrule_file, - topocustomrule_path]) - - # And wait until the query fails with the right error. - timeout = 10.0 - while True: - try: - tablet_62344.execute('select id, msg from vt_select_test') - timeout = utils.wait_step('query rule in place', timeout) - except Exception as e: - print e - expected = ('disallowed due to rule: disallow select' - ' on table vt_select_test') - self.assertIn(expected, str(e)) - break - - # Cleanup. - tablet_62344.kill_vttablet() - - -if __name__ == '__main__': - utils.main() diff --git a/test/tabletmanager2.py b/test/tabletmanager2.py deleted file mode 100755 index 20bf74599ca..00000000000 --- a/test/tabletmanager2.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# vim: tabstop=8 expandtab shiftwidth=2 softtabstop=2 - -import MySQLdb -import logging -import re -import unittest -import os -import environment -import tablet -import utils -import time -from utils import TestError -from vtproto.tabletmanagerdata_pb2 import LockTablesRequest, UnlockTablesRequest, StopSlaveRequest, \ - MasterPositionRequest, StartSlaveUntilAfterRequest - -# regexp to check if the tablet status page reports healthy, -# regardless of actual replication lag -healthy_expr = re.compile(r'Current status: healthy') - -_create_vt_insert_test = '''create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - primary key (id) - ) Engine=InnoDB''' - - -class TestServiceTestOfTabletManager(unittest.TestCase): - """This tests the locking functionality by running rpc calls against the tabletmanager, - in contrast to testing the tabletmanager through the vtcl""" - - replica = tablet.Tablet(62344) - master = tablet.Tablet(62044) - - def setUp(self): - try: - os.makedirs(environment.tmproot) - except OSError: - # directory already exists - pass - - try: - topo_flavor = environment.topo_server().flavor() - environment.topo_server().setup() - - # start mysql instance external to the test - setup_procs = [ - self.replica.init_mysql(), - self.master.init_mysql(), - ] - utils.Vtctld().start() - logging.debug(utils.vtctld_connection) - utils.wait_procs(setup_procs) - - for t in self.master, self.replica: - t.create_db('vt_test_keyspace') - - self.master.init_tablet('replica', 'test_keyspace', '0', start=True) - self.replica.init_tablet('replica', 'test_keyspace', '0', start=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - self.master.tablet_alias]) - self.master.mquery('vt_test_keyspace', _create_vt_insert_test) - for t in [self.master, self.replica]: - t.set_semi_sync_enabled(master=False, slave=False) - except Exception as e: - logging.exception(e) - self.tearDown() - - def tearDown(self): - try: - for t in self.master, self.replica: - t.kill_vttablet() - tablet.Tablet.check_vttablet_count() - environment.topo_server().wipe() - for t in [self.master, self.replica]: - t.reset_replication() - t.set_semi_sync_enabled(master=False, slave=False) - t.clean_dbs() - finally: - utils.required_teardown() - - if utils.options.skip_teardown: - return - - teardown_procs = [ - self.master.teardown_mysql(), - self.replica.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - self.replica.remove_tree() - self.master.remove_tree() - - def _write_data_to_master(self): - """Write a single row to the master""" - self.master.mquery('vt_test_keyspace', "insert into vt_insert_test (msg) values ('test')", write=True) - - def _check_data_on_replica(self, count, msg): - """Check that the specified tablet has the expected number of rows.""" - timeout = 3 - while True: - try: - result = self.replica.mquery( - 'vt_test_keyspace', 'select count(*) from vt_insert_test') - if result[0][0] == count: - break - except MySQLdb.DatabaseError: - # ignore exceptions, we'll just timeout (the tablet creation - # can take some time to replicate, and we get a 'table vt_insert_test - # does not exist exception in some rare cases) - logging.exception('exception waiting for data to replicate') - timeout = utils.wait_step(msg, timeout) - - def test_lock_and_unlock(self): - """Test the lock ability by locking a replica and asserting it does not see changes""" - # first make sure that our writes to the master make it to the replica - self._write_data_to_master() - self._check_data_on_replica(1, "replica getting the data") - - # now lock the replica - tablet_manager = self.replica.tablet_manager() - tablet_manager.LockTables(LockTablesRequest()) - - # make sure that writing to the master does not show up on the replica while locked - self._write_data_to_master() - with self.assertRaises(TestError): - self._check_data_on_replica(2, "the replica should not see these updates") - - # finally, make sure that unlocking the replica leads to the previous write showing up - tablet_manager.UnlockTables(UnlockTablesRequest()) - self._check_data_on_replica(2, "after unlocking the replica, we should see these updates") - - def test_unlock_when_we_dont_have_a_lock(self): - """Unlocking when we do not have a valid lock should lead to an exception being raised""" - # unlock the replica - tablet_manager = self.replica.tablet_manager() - with self.assertRaises(Exception): - tablet_manager.UnlockTables(UnlockTablesRequest()) - - def test_start_slave_until_after(self): - """Test by writing three rows, noting the gtid after each, and then replaying them one by one""" - self.replica.start_vttablet() - self.master.start_vttablet() - - # first we stop replication to the replica, so we can move forward step by step. - replica_tablet_manager = self.replica.tablet_manager() - replica_tablet_manager.StopSlave(StopSlaveRequest()) - - master_tablet_manager = self.master.tablet_manager() - self._write_data_to_master() - pos1 = master_tablet_manager.MasterPosition(MasterPositionRequest()) - - self._write_data_to_master() - pos2 = master_tablet_manager.MasterPosition(MasterPositionRequest()) - - self._write_data_to_master() - pos3 = master_tablet_manager.MasterPosition(MasterPositionRequest()) - - # Now, we'll resume stepwise position by position and make sure that we see the expected data - self._check_data_on_replica(0, "no data has yet reached the replica") - - # timeout is given in nanoseconds. we want to wait no more than 10 seconds - timeout = int(10 * 1e9) - - replica_tablet_manager.StartSlaveUntilAfter( - StartSlaveUntilAfterRequest(position=pos1.position, wait_timeout=timeout)) - self._check_data_on_replica(1, "first row is now visible") - - replica_tablet_manager.StartSlaveUntilAfter( - StartSlaveUntilAfterRequest(position=pos2.position, wait_timeout=timeout)) - self._check_data_on_replica(2, "second row is now visible") - - replica_tablet_manager.StartSlaveUntilAfter( - StartSlaveUntilAfterRequest(position=pos3.position, wait_timeout=timeout)) - self._check_data_on_replica(3, "third row is now visible") - - def test_lock_and_timeout(self): - """Test that the lock times out and updates can be seen even though nothing is unlocked""" - - # first make sure that our writes to the master make it to the replica - self._write_data_to_master() - self._check_data_on_replica(1, "replica getting the data") - - # now lock the replica - tablet_manager = self.replica.tablet_manager() - tablet_manager.LockTables(LockTablesRequest()) - - # make sure that writing to the master does not show up on the replica while locked - self._write_data_to_master() - with self.assertRaises(TestError): - self._check_data_on_replica(2, "the replica should not see these updates") - - # the tests sets the lock timeout to 5 seconds, so sleeping 10 should be safe - time.sleep(10) - - self._check_data_on_replica(2, "the replica should now see these updates") - - # finally, trying to unlock should clearly tell us we did not have the lock - with self.assertRaises(Exception): - tablet_manager.UnlockTables(UnlockTablesRequest()) - - -if __name__ == '__main__': - utils.main() diff --git a/test/topo_flavor/__init__.py b/test/topo_flavor/__init__.py deleted file mode 100644 index 35bf136ccc5..00000000000 --- a/test/topo_flavor/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/test/topo_flavor/consul.py b/test/topo_flavor/consul.py deleted file mode 100644 index 364b37b1f60..00000000000 --- a/test/topo_flavor/consul.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Consul specific configuration.""" - -import json -import os - -import server - - -class ConsulTopoServer(server.TopoServer): - """Implementation of TopoServer for consul.""" - - def setup(self): - import environment # pylint: disable=g-import-not-at-top - import utils # pylint: disable=g-import-not-at-top - - self.port_base = environment.reserve_ports(4) - self.server_addr = 'localhost:%d' % (self.port_base + 1) - - # Write our config file. - self.config_file = os.path.join(environment.vtdataroot, 'consul.json') - config = { - 'ports': { - 'dns': self.port_base, - 'http': self.port_base + 1, - 'serf_lan': self.port_base + 2, - 'serf_wan': self.port_base + 3, - }, - } - with open(self.config_file, 'w') as fd: - fd.write(json.dumps(config)) - - log_base = os.path.join(environment.vtlogroot, 'consul') - self.proc = utils.run_bg([ - 'consul', 'agent', - '-dev', - '-config-file', self.config_file], - stdout=open(log_base + '.stdout', 'a'), - stderr=open(log_base + '.stderr', 'a')) - - # Wait until the daemon is ready. - utils.curl( - 'http://' + self.server_addr + '/v1/kv/?keys', retry_timeout=10) - - # Create the cell configurations using 'vtctl AddCellInfo' - for cell in ['test_nj', 'test_ny', 'test_ca']: - utils.run_vtctl_vtctl(['AddCellInfo', - '-root', cell, - '-server_address', self.server_addr, - cell]) - - def teardown(self): - import utils # pylint: disable=g-import-not-at-top - - utils.kill_sub_process(self.proc) - self.proc.wait() - - def flags(self): - return [ - '-topo_implementation', 'consul', - '-topo_global_server_address', self.server_addr, - '-topo_global_root', 'global', - ] - - def wipe(self): - import utils # pylint: disable=g-import-not-at-top - - utils.curl('http://' + self.server_addr + '/v1/kv/global/keyspaces?recurse', - request='DELETE') - for cell in ['test_nj', 'test_ny', 'test_ca']: - utils.curl('http://' + self.server_addr + '/v1/kv/' + cell + '?recurse', - request='DELETE') - - def update_addr(self, cell, keyspace, shard, tablet_index, port): - pass - -server.flavor_map['consul'] = ConsulTopoServer() diff --git a/test/topo_flavor/etcd2.py b/test/topo_flavor/etcd2.py deleted file mode 100644 index 8396bcc2b77..00000000000 --- a/test/topo_flavor/etcd2.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Etcd2 specific configuration.""" - -import os -import shutil - -import server - - -class Etcd2Cluster(object): - """Sets up a global or cell-local etcd cluster.""" - - def __init__(self, name): - import environment # pylint: disable=g-import-not-at-top - - self.port_base = environment.reserve_ports(2) - - self.name = name - self.hostname = 'localhost' - self.client_port = self.port_base - self.peer_port = self.port_base + 1 - self.client_addr = 'http://%s:%d' % (self.hostname, self.client_port) - self.peer_addr = 'http://%s:%d' % (self.hostname, self.peer_port) - self.api_url = self.client_addr + '/v2' - - dirname = 'etcd_' + self.name - self.data_dir = os.path.join(environment.vtdataroot, dirname) - self.log_base = os.path.join(environment.vtlogroot, dirname) - - self.start() - - def start(self): - import utils # pylint: disable=g-import-not-at-top - - self.proc = utils.run_bg([ - 'etcd', '-name', self.name, - '-advertise-client-urls', self.client_addr, - '-initial-advertise-peer-urls', self.peer_addr, - '-listen-client-urls', self.client_addr, - '-listen-peer-urls', self.peer_addr, - '-initial-cluster', '%s=%s' % (self.name, self.peer_addr), - '-data-dir', self.data_dir], - stdout=open(self.log_base + '.stdout', 'a'), - stderr=open(self.log_base + '.stderr', 'a')) - - def restart(self): - self.stop() - self.start() - - def stop(self): - import utils # pylint: disable=g-import-not-at-top - - utils.kill_sub_process(self.proc) - self.proc.wait() - shutil.rmtree(self.data_dir) - - def wait_until_up(self): - import utils # pylint: disable=g-import-not-at-top - - # Wait for global cluster to come up. - # We create a dummy directory using v2 API, won't be visible to v3. - utils.curl( - self.api_url + '/keys/test', request='PUT', - data='dir=true', retry_timeout=10) - - -class Etcd2TopoServer(server.TopoServer): - """Implementation of TopoServer for etcd2.""" - - clusters = {} - - def setup(self, add_bad_host=False): - for cell in ['global', 'test_ca', 'test_nj', 'test_ny']: - self.clusters[cell] = Etcd2Cluster(cell) - - self.wait_until_up_add_cells() - - def teardown(self): - for cluster in self.clusters.itervalues(): - cluster.stop() - - def flags(self): - return [ - '-topo_implementation', 'etcd2', - '-topo_global_server_address', self.clusters['global'].client_addr, - '-topo_global_root', '/global', - ] - - def wipe(self): - for cluster in self.clusters.itervalues(): - cluster.restart() - - self.wait_until_up_add_cells() - - def update_addr(self, cell, keyspace, shard, tablet_index, port): - pass - - def wait_until_up_add_cells(self): - import utils # pylint: disable=g-import-not-at-top - - for cluster in self.clusters.itervalues(): - cluster.wait_until_up() - - # Add entries in global cell list. - for cell, cluster in self.clusters.iteritems(): - if cell != 'global': - utils.run_vtctl_vtctl(['AddCellInfo', - '-root', '/', - '-server_address', cluster.client_addr, - cell]) - -server.flavor_map['etcd2'] = Etcd2TopoServer() diff --git a/test/topo_flavor/server.py b/test/topo_flavor/server.py deleted file mode 100644 index 24bd8dc7984..00000000000 --- a/test/topo_flavor/server.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""The abstract class for choosing topo server.""" - -import logging - - -class TopoServer(object): - """Base class that defines the required interface.""" - - def setup(self): - """Initialize the topo server.""" - raise NotImplementedError() - - def teardown(self): - """Teardown the topo server.""" - raise NotImplementedError() - - def flags(self): - """Return a list of args that tell a Vitess process to use this topo server. - """ - raise NotImplementedError() - - def wipe(self): - """Wipe the Vitess paths in the topo server.""" - raise NotImplementedError() - - def update_addr(self, cell, keyspace, shard, tablet_index, port): - """Update topo server with additional information.""" - raise NotImplementedError() - - def flavor(self): - """Return the name of this topo server flavor.""" - return self.flavor_name - - -flavor_map = {} - -_server = None - - -def topo_server(): - return _server - - -def set_topo_server_flavor(flavor): - """Set which topo server to use.""" - global _server - - if flavor in flavor_map: - _server = flavor_map[flavor] - logging.debug("Using topo server flavor '%s'", flavor) - elif not flavor: - if len(flavor_map) == 1: - (flavor, _server) = flavor_map.iteritems().next() - logging.debug("Using default topo server flavor '%s'", flavor) - else: - logging.error( - "No --topo-server-flavor specified. Registered flavors: [%s]", - ",".join(flavor_map.keys())) - return - else: - logging.error( - "Unknown topo server flavor '%s'. Registered flavors: [%s]", flavor, - ",".join(flavor_map.keys())) - return - - _server.flavor_name = flavor diff --git a/test/topo_flavor/zk2.py b/test/topo_flavor/zk2.py deleted file mode 100644 index a605f8aea9d..00000000000 --- a/test/topo_flavor/zk2.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""zk2 specific configuration.""" - -import server - - -class Zk2TopoServer(server.TopoServer): - """Implementation of TopoServer for zk2.""" - - def __init__(self): - self.ports_assigned = False - - def assign_ports(self): - """Assign ports if not already assigned.""" - - if self.ports_assigned: - return - - from environment import reserve_ports # pylint: disable=g-import-not-at-top - import utils # pylint: disable=g-import-not-at-top - - self.zk_port_base = reserve_ports(3) - self.hostname = utils.hostname - self.zk_ports = ':'.join(str(self.zk_port_base + i) for i in range(3)) - self.addr = 'localhost:%d' % (self.zk_port_base + 2) - self.ports_assigned = True - - def setup(self): - from environment import run, binary_args, vtlogroot # pylint: disable=g-import-not-at-top,g-multiple-import - import utils # pylint: disable=g-import-not-at-top - - self.assign_ports() - run(binary_args('zkctl') + [ - '-log_dir', vtlogroot, - '-zk.cfg', '1@%s:%s' % (self.hostname, self.zk_ports), - 'init']) - - # Create the cell configurations using 'vtctl AddCellInfo' - utils.run_vtctl_vtctl(['AddCellInfo', - '-root', '/test_nj', - '-server_address', self.addr, - 'test_nj']) - utils.run_vtctl_vtctl(['AddCellInfo', - '-root', '/test_ny', - '-server_address', self.addr, - 'test_ny']) - ca_addr = self.addr - # Use UpdateCellInfo for this one, more coverage. - utils.run_vtctl_vtctl(['UpdateCellInfo', - '-root', '/test_ca', - '-server_address', ca_addr, - 'test_ca']) - - def teardown(self): - from environment import run, binary_args, vtlogroot # pylint: disable=g-import-not-at-top,g-multiple-import - import utils # pylint: disable=g-import-not-at-top - - self.assign_ports() - run(binary_args('zkctl') + [ - '-log_dir', vtlogroot, - '-zk.cfg', '1@%s:%s' % (self.hostname, self.zk_ports), - 'shutdown' if utils.options.keep_logs else 'teardown'], - raise_on_error=False) - - def flags(self): - return [ - '-topo_implementation', 'zk2', - '-topo_global_server_address', self.addr, - '-topo_global_root', '/global', - ] - - def wipe(self): - from environment import run, binary_args # pylint: disable=g-import-not-at-top,g-multiple-import - - # Only delete keyspaces/ in the global topology service, to keep - # the 'cells' directory. So we don't need to re-add the CellInfo records. - run(binary_args('zk') + ['-server', self.addr, 'rm', '-rf', - '/global/keyspaces']) - run(binary_args('zk') + ['-server', self.addr, 'rm', '-rf', '/test_nj/*']) - run(binary_args('zk') + ['-server', self.addr, 'rm', '-rf', '/test_ny/*']) - run(binary_args('zk') + ['-server', self.addr, 'rm', '-rf', '/test_ca/*']) - - def update_addr(self, cell, keyspace, shard, tablet_index, port): - pass - -server.flavor_map['zk2'] = Zk2TopoServer() diff --git a/test/update_stream.py b/test/update_stream.py deleted file mode 100755 index 39c8f88c7c4..00000000000 --- a/test/update_stream.py +++ /dev/null @@ -1,672 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import logging -import time -import unittest - -import environment -import tablet -import utils -from vtdb import dbexceptions -from vtdb import proto3_encoding -from vtdb import vtgate_client -from vtproto import query_pb2 -from vtproto import topodata_pb2 -from mysql_flavor import mysql_flavor -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() - -# master_start_position has the replication position before we start -# doing anything to the master database. It is used by test_ddl to -# make sure we see DDLs. -master_start_position = None - -_create_vt_insert_test = '''create table if not exists vt_insert_test ( -id bigint auto_increment, -msg varchar(64), -primary key (id) -) Engine=InnoDB''' - -_create_vt_a = '''create table if not exists vt_a ( -eid bigint, -id int, -primary key(eid, id) -) Engine=InnoDB''' - -_create_vt_b = '''create table if not exists vt_b ( -eid bigint, -name varchar(128), -foo varbinary(128), -primary key(eid, name) -) Engine=InnoDB''' - - -def _get_master_current_position(): - return mysql_flavor().master_position(master_tablet) - - -def _get_repl_current_position(): - return mysql_flavor().master_position(replica_tablet) - - -def setUpModule(): - global master_start_position - - try: - environment.topo_server().setup() - - # start mysql instance external to the test - 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 - utils.Vtctld().start() - - # Start up a master mysql and vttablet - logging.debug('Setting up tablets') - utils.run_vtctl(['CreateKeyspace', 'test_keyspace']) - master_tablet.init_tablet('replica', 'test_keyspace', '0', tablet_index=0) - replica_tablet.init_tablet('replica', 'test_keyspace', '0', tablet_index=1) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - master_tablet.create_db('vt_test_keyspace') - master_tablet.create_db('other_database') - replica_tablet.create_db('vt_test_keyspace') - replica_tablet.create_db('other_database') - - master_tablet.start_vttablet(wait_for_state=None) - replica_tablet.start_vttablet(wait_for_state=None) - master_tablet.wait_for_vttablet_state('NOT_SERVING') - replica_tablet.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/0', - master_tablet.tablet_alias], auto_log=True) - - utils.wait_for_tablet_type(replica_tablet.tablet_alias, 'replica') - master_tablet.wait_for_vttablet_state('SERVING') - replica_tablet.wait_for_vttablet_state('SERVING') - - # reset counter so tests don't assert - tablet.Tablet.tablets_running = 0 - - master_start_position = _get_master_current_position() - master_tablet.mquery('vt_test_keyspace', _create_vt_insert_test) - master_tablet.mquery('vt_test_keyspace', _create_vt_a) - master_tablet.mquery('vt_test_keyspace', _create_vt_b) - - utils.run_vtctl(['ReloadSchemaKeyspace', 'test_keyspace']) - utils.run_vtctl(['RebuildVSchemaGraph']) - - utils.VtGate().start(tablets=[master_tablet, replica_tablet]) - utils.vtgate.wait_for_endpoints('test_keyspace.0.master', 1) - utils.vtgate.wait_for_endpoints('test_keyspace.0.replica', 1) - - # Wait for the master and slave tablet's ReloadSchema to have worked. - # Note we don't specify a keyspace name, there is only one, vschema - # will just use that single keyspace. - timeout = 10 - while True: - try: - utils.vtgate.execute('select count(1) from vt_insert_test', - tablet_type='master') - utils.vtgate.execute('select count(1) from vt_insert_test', - tablet_type='replica') - break - except protocols_flavor().client_error_exception_type(): - logging.exception('query failed') - timeout = utils.wait_step('slave tablet having correct schema', timeout) - # also re-run ReloadSchema on slave, it case the first one - # didn't get the replicated table. - utils.run_vtctl(['ReloadSchema', replica_tablet.tablet_alias]) - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - logging.debug('Tearing down the servers and setup') - tablet.Tablet.tablets_running = 2 - tablet.kill_tablets([master_tablet, replica_tablet]) - teardown_procs = [master_tablet.teardown_mysql(), - replica_tablet.teardown_mysql()] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - master_tablet.remove_tree() - replica_tablet.remove_tree() - - -class TestUpdateStream(unittest.TestCase): - _populate_vt_insert_test = [ - "insert into vt_insert_test (msg) values ('test %s')" % x - for x in xrange(4)] - - def _populate_vt_a(self, count): - return ['insert into vt_a (eid, id) values (%d, %d)' % (x, x) - for x in xrange(count + 1) if x > 0] - - def _populate_vt_b(self, count): - return [ - "insert into vt_b (eid, name, foo) values (%d, 'name %s', 'foo %s')" % - (x, x, x) for x in xrange(count)] - - def _get_vtgate_stream_conn(self): - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - return vtgate_client.connect(protocol, addr, 30.0) - - def _exec_vt_txn(self, query_list): - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - vtgate_conn = vtgate_client.connect(protocol, addr, 30.0) - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace='test_keyspace', - shards=['0'], writable=True) - cursor.begin() - for query in query_list: - cursor.execute(query, {}) - cursor.commit() - return - - def test_stream_parity(self): - """Tests parity of streams between master and replica for the same writes. - - Also tests transactions are retrieved properly. - """ - - timeout = 30 - while True: - master_position = _get_master_current_position() - replica_position = _get_repl_current_position() - if master_position == replica_position: - break - timeout = utils.wait_step( - '%s == %s' % (master_position, replica_position), - timeout - ) - logging.debug('run_test_stream_parity starting @ %s', - master_position) - self._exec_vt_txn(self._populate_vt_a(15)) - self._exec_vt_txn(self._populate_vt_b(14)) - self._exec_vt_txn(['delete from vt_a']) - self._exec_vt_txn(['delete from vt_b']) - - # get master events - master_conn = self._get_vtgate_stream_conn() - master_events = [] - for event, resume_timestamp in master_conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - event=query_pb2.EventToken(shard='0', position=master_position), - shard='0'): - logging.debug('Got master event(%d): %s', resume_timestamp, event) - master_events.append(event) - if len(master_events) == 4: - break - master_conn.close() - - # get replica events - replica_conn = self._get_vtgate_stream_conn() - replica_events = [] - for event, resume_timestamp in replica_conn.update_stream( - 'test_keyspace', topodata_pb2.REPLICA, - event=query_pb2.EventToken(shard='0', position=replica_position), - shard='0'): - logging.debug('Got slave event(%d): %s', resume_timestamp, event) - replica_events.append(event) - if len(replica_events) == 4: - break - replica_conn.close() - - # and compare - if len(master_events) != len(replica_events): - logging.debug( - 'Test Failed - # of records mismatch, master %s replica %s', - master_events, replica_events) - for master_event, replica_event in zip(master_events, replica_events): - # The timestamp is from when the event was written to the binlogs. - # the master uses the timestamp of when it wrote it originally, - # the slave of when it applied the logs. These can differ and make this - # test flaky. So we just blank them out, easier. We really want to - # compare the replication positions. - master_event.event_token.timestamp = 123 - replica_event.event_token.timestamp = 123 - self.assertEqual( - master_event, replica_event, - "Test failed, data mismatch - master '%s' and replica '%s'" % - (master_event, replica_event)) - logging.debug('Test Writes: PASS') - - def test_ddl(self): - """Asks for all statements since we started, find the DDL.""" - start_position = master_start_position - logging.debug('test_ddl: starting @ %s', start_position) - master_conn = self._get_vtgate_stream_conn() - found = False - for event, _ in master_conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - event=query_pb2.EventToken(shard='0', position=start_position), - shard='0'): - for statement in event.statements: - if statement.sql == _create_vt_insert_test: - found = True - break - break - master_conn.close() - self.assertTrue(found, "didn't get right sql") - - def test_set_insert_id(self): - start_position = _get_master_current_position() - self._exec_vt_txn( - ['SET INSERT_ID=1000000'] + self._populate_vt_insert_test) - logging.debug('test_set_insert_id: starting @ %s', start_position) - master_conn = self._get_vtgate_stream_conn() - expected_id = 1000000 - for event, _ in master_conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - event=query_pb2.EventToken(shard='0', position=start_position), - shard='0'): - for statement in event.statements: - fields, rows = proto3_encoding.convert_stream_event_statement(statement) - self.assertEqual(fields[0], 'id') - self.assertEqual(rows[0][0], expected_id) - expected_id += 1 - break - if expected_id != 1000004: - self.fail('did not get my four values!') - master_conn.close() - - def test_database_filter(self): - start_position = _get_master_current_position() - master_tablet.mquery('other_database', _create_vt_insert_test) - self._exec_vt_txn(self._populate_vt_insert_test) - logging.debug('test_database_filter: starting @ %s', start_position) - master_conn = self._get_vtgate_stream_conn() - for event, _ in master_conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - event=query_pb2.EventToken(shard='0', position=start_position), - shard='0'): - for statement in event.statements: - self.assertNotEqual(statement.category, 2, # query_pb2.StreamEvent.DDL - "query using other_database wasn't filtered out") - break - master_conn.close() - - def test_service_switch(self): - """tests the service switch from disable -> enable -> disable.""" - # make the replica spare - utils.run_vtctl(['ChangeSlaveType', replica_tablet.tablet_alias, 'spare']) - utils.wait_for_tablet_type(replica_tablet.tablet_alias, 'spare') - - # Check UpdateStreamState is disabled. - v = utils.get_vars(replica_tablet.port) - if v['UpdateStreamState'] != 'Disabled': - self.fail("Update stream service should be 'Disabled' but is '%s'" % - v['UpdateStreamState']) - - start_position = _get_repl_current_position() - - # Make sure we can't start a new request to vttablet directly. - _, stderr = utils.run_vtctl(['VtTabletUpdateStream', - '-position', start_position, - replica_tablet.tablet_alias], - expect_fail=True) - self.assertIn('operation not allowed in state NOT_SERVING', stderr) - - # Make sure we can't start a new request through vtgate. - replica_conn = self._get_vtgate_stream_conn() - try: - for event, resume_timestamp in replica_conn.update_stream( - 'test_keyspace', topodata_pb2.REPLICA, - event=query_pb2.EventToken(shard='0', position=start_position), - shard='0'): - self.assertFail('got event(%d): %s' % (resume_timestamp, str(event))) - self.assertFail('update_stream terminated with no exception') - except dbexceptions.DatabaseError as e: - self.assertIn(vtgate_gateway_flavor().no_tablet_found_message(), str(e)) - - # Go back to replica. - utils.run_vtctl( - ['ChangeSlaveType', replica_tablet.tablet_alias, 'replica']) - utils.wait_for_tablet_type(replica_tablet.tablet_alias, 'replica') - - # Check UpdateStreamState is enabled. - v = utils.get_vars(replica_tablet.port) - if v['UpdateStreamState'] != 'Enabled': - self.fail("Update stream service should be 'Enabled' but is '%s'" % - v['UpdateStreamState']) - - def test_event_token(self): - """Checks the background binlog monitor thread works.""" - timeout = 10 - while True: - replica_position = _get_repl_current_position() - value = None - v = utils.get_vars(replica_tablet.port) - if 'EventTokenPosition' in v: - value = v['EventTokenPosition'] - if value == replica_position: - logging.debug('got expected EventTokenPosition vars: %s', value) - ts = v['EventTokenTimestamp'] - now = long(time.time()) - self.assertTrue(ts >= now - 120, - 'EventTokenTimestamp is too old: %d < %d' % - (ts, now-120)) - self.assertTrue(ts <= now, - 'EventTokenTimestamp is too recent: %d > %d' %(ts, now)) - break - timeout = utils.wait_step( - 'EventTokenPosition must be up to date but got %s (expected %s)' % - (value, replica_position), timeout) - - # Replica position can still move forward after this when things are slow. - # Compare only server ids. - replica_position = replica_position.split(":")[0] - - # With vttablet up to date, test a vttablet query returns the EventToken. - qr = replica_tablet.execute('select * from vt_insert_test', - execute_options='include_event_token:true ') - logging.debug('Got result: %s', qr) - self.assertIn('extras', qr) - self.assertIn('event_token', qr['extras']) - pos = qr['extras']['event_token']['position'].split(":")[0] - self.assertEqual(pos, replica_position) - - # Same thing through vtgate - qr = utils.vtgate.execute('select * from vt_insert_test', - tablet_type='replica', - execute_options='include_event_token:true ') - logging.debug('Got result: %s', qr) - self.assertIn('extras', qr) - self.assertIn('event_token', qr['extras']) - pos = qr['extras']['event_token']['position'].split(":")[0] - self.assertEqual(pos, replica_position) - - # Make sure the compare_event_token flag works, by sending a very - # old timestamp, or a timestamp in the future. - qr = replica_tablet.execute( - 'select * from vt_insert_test', - execute_options='compare_event_token: ') - self.assertIn('extras', qr) - self.assertIn('fresher', qr['extras']) - self.assertTrue(qr['extras']['fresher']) - - future_timestamp = long(time.time()) + 100 - qr = replica_tablet.execute( - 'select * from vt_insert_test', - execute_options='compare_event_token: ' % - future_timestamp) - self.assertTrue(qr['extras'] is None) - - # Same thing through vtgate - qr = utils.vtgate.execute( - 'select * from vt_insert_test', tablet_type='replica', - execute_options='compare_event_token: ') - self.assertIn('extras', qr) - self.assertIn('fresher', qr['extras']) - self.assertTrue(qr['extras']['fresher']) - - future_timestamp = long(time.time()) + 100 - qr = utils.vtgate.execute( - 'select * from vt_insert_test', tablet_type='replica', - execute_options='compare_event_token: ' % - future_timestamp) - self.assertTrue(qr['extras'] is None) - - # Make sure the compare_event_token flag works, by sending a very - # old timestamp, or a timestamp in the future, when combined with - # include_event_token flag. - qr = replica_tablet.execute('select * from vt_insert_test', - execute_options='include_event_token:true ' - 'compare_event_token: ') - self.assertIn('extras', qr) - self.assertIn('fresher', qr['extras']) - self.assertTrue(qr['extras']['fresher']) - self.assertIn('event_token', qr['extras']) - pos = qr['extras']['event_token']['position'].split(":")[0] - self.assertEqual(pos, replica_position) - - future_timestamp = long(time.time()) + 100 - qr = replica_tablet.execute('select * from vt_insert_test', - execute_options='include_event_token:true ' - 'compare_event_token: ' % - future_timestamp) - self.assertNotIn('fresher', qr['extras']) - self.assertIn('event_token', qr['extras']) - pos = qr['extras']['event_token']['position'].split(":")[0] - self.assertEqual(pos, replica_position) - - # Same thing through vtgate - qr = utils.vtgate.execute('select * from vt_insert_test', - tablet_type='replica', - execute_options='include_event_token:true ' - 'compare_event_token: ') - self.assertIn('extras', qr) - self.assertIn('fresher', qr['extras']) - self.assertTrue(qr['extras']['fresher']) - self.assertIn('event_token', qr['extras']) - pos = qr['extras']['event_token']['position'].split(":")[0] - self.assertEqual(pos, replica_position) - - future_timestamp = long(time.time()) + 100 - qr = utils.vtgate.execute('select * from vt_insert_test', - tablet_type='replica', - execute_options='include_event_token:true ' - 'compare_event_token: ' % - future_timestamp) - self.assertNotIn('fresher', qr['extras']) - self.assertIn('event_token', qr['extras']) - pos = qr['extras']['event_token']['position'].split(":")[0] - self.assertEqual(pos, replica_position) - - def test_update_stream_interrupt(self): - """Checks that a running query is terminated on going non-serving.""" - # Make sure the replica is replica type. - utils.run_vtctl( - ['ChangeSlaveType', replica_tablet.tablet_alias, 'replica']) - logging.debug('sleeping a bit for the replica action to complete') - utils.wait_for_tablet_type(replica_tablet.tablet_alias, 'replica', 30) - - # Save current position, insert some data. - start_position = _get_repl_current_position() - logging.debug('test_update_stream_interrupt starting @ %s', start_position) - self._exec_vt_txn(self._populate_vt_a(1)) - self._exec_vt_txn(['delete from vt_a']) - - # Start an Update Stream from the slave. When we get the data, go to spare. - # That should interrupt the streaming RPC. - replica_conn = self._get_vtgate_stream_conn() - first = True - txn_count = 0 - try: - for event, resume_timestamp in replica_conn.update_stream( - 'test_keyspace', topodata_pb2.REPLICA, - event=query_pb2.EventToken(shard='0', position=start_position), - shard='0'): - logging.debug('test_update_stream_interrupt got event(%d): %s', - resume_timestamp, event) - if first: - utils.run_vtctl( - ['ChangeSlaveType', replica_tablet.tablet_alias, 'spare']) - utils.wait_for_tablet_type(replica_tablet.tablet_alias, 'spare', 30) - first = False - else: - if event.event_token.position: - txn_count += 1 - - self.assertFail('update_stream terminated with no exception') - except dbexceptions.DatabaseError as e: - self.assertIn('context canceled', str(e)) - self.assertFalse(first) - - logging.debug('Streamed %d transactions before exiting', txn_count) - replica_conn.close() - - def test_log_rotation(self): - start_position = _get_master_current_position() - logging.debug('test_log_rotation: starting @ %s', start_position) - position = start_position - master_tablet.mquery('vt_test_keyspace', 'flush logs') - self._exec_vt_txn(self._populate_vt_a(15)) - self._exec_vt_txn(['delete from vt_a']) - master_conn = self._get_vtgate_stream_conn() - master_txn_count = 0 - logs_correct = False - for event, _ in master_conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - event=query_pb2.EventToken(shard='0', position=start_position), - shard='0'): - if event.event_token.position: - master_txn_count += 1 - position = event.event_token.position - if mysql_flavor().position_after(position, start_position): - logs_correct = True - logging.debug('Log rotation correctly interpreted') - break - if master_txn_count == 2: - self.fail('ran out of logs') - if not logs_correct: - self.fail("Flush logs didn't get properly interpreted") - master_conn.close() - - def test_timestamp_start_current_log(self): - """Test we can start binlog streaming from the current binlog. - - Order of operation: - - Insert something in the binlogs for tablet vt_a then delete it. - - Get the current timestamp. - - Wait for 4 seconds for the timestamp to change for sure. - - Insert something else in vt_b and delete it. - - Then we stream events starting at the original timestamp + 2, we - should get only the vt_b events. - """ - self._test_timestamp_start(rotate_before_sleep=False, - rotate_after_sleep=False) - - def test_timestamp_start_rotated_log_before_sleep(self): - """Test we can start binlog streaming from the current rotated binlog. - - Order of operation: - - Insert something in the binlogs for tablet vt_a then delete it. - - Rotate the logs. - - Get the current timestamp. - - Wait for 4 seconds for the timestamp to change for sure. - - Insert something else in vt_b and delete it. - - Then we stream events starting at the original timestamp + 2, we - should get only the vt_b events. - - In this test case, the current binlogs have a starting time stamp - that is smaller than what we ask for, so it should just stay on it. - """ - self._test_timestamp_start(rotate_before_sleep=True, - rotate_after_sleep=False) - - def test_timestamp_start_rotated_log_after_sleep(self): - """Test we can start binlog streaming from the previous binlog. - - Order of operation: - - Insert something in the binlogs for tablet vt_a then delete it. - - Get the current timestamp. - - Wait for 4 seconds for the timestamp to change for sure. - - Rotate the logs. - - Insert something else in vt_b and delete it. - - Then we stream events starting at the original timestamp + 2, we - should get only the vt_b events. - - In this test case, the current binlogs have a starting time stamp - that is 2s higher than what we ask for, so it should go back to - the previous binlog. - """ - self._test_timestamp_start(rotate_before_sleep=False, - rotate_after_sleep=True) - - def _test_timestamp_start(self, - rotate_before_sleep=False, - rotate_after_sleep=False): - """Common function for timestamp tests.""" - # Insert something in the binlogs for tablet vt_a then delete it. - self._exec_vt_txn(self._populate_vt_a(1)) - self._exec_vt_txn(['delete from vt_a']) - - # (optional) Rotate the logs - if rotate_before_sleep: - master_tablet.mquery('vt_test_keyspace', 'flush logs') - - # Get the current timestamp. - starting_timestamp = long(time.time()) - logging.debug('test_timestamp_start_current_log: starting @ %d', - starting_timestamp) - - # Wait for 4 seconds for the timestamp to change for sure. - time.sleep(4) - - # (optional) Rotate the logs - if rotate_after_sleep: - master_tablet.mquery('vt_test_keyspace', 'flush logs') - - # Insert something else in vt_b and delete it. - self._exec_vt_txn(self._populate_vt_b(1)) - self._exec_vt_txn(['delete from vt_b']) - - # make sure we only get events related to vt_b. - master_conn = self._get_vtgate_stream_conn() - count = 0 - for (event, resume_timestamp) in master_conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - timestamp=starting_timestamp+2, - shard='0'): - logging.debug('_test_timestamp_start: got event: %s @ %d', - str(event), resume_timestamp) - # we might get a couple extra events from the rotation, ignore these. - if not event.statements: - continue - if event.statements[0].category == 0: # Statement.Category.Error - continue - self.assertEqual(event.statements[0].table_name, 'vt_b', - 'got wrong event: %s' % str(event)) - count += 1 - if count == 2: - break - master_conn.close() - - def test_timestamp_start_too_old(self): - """Ask the server to start streaming from a timestamp 4h ago.""" - starting_timestamp = long(time.time()) - 4*60*60 - master_conn = self._get_vtgate_stream_conn() - try: - for (event, resume_timestamp) in master_conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - timestamp=starting_timestamp, - shard='0'): - self.assertFail('got an event: %s %d' % (str(event), resume_timestamp)) - except dbexceptions.QueryNotServed as e: - self.assertIn('cannot find relevant binlogs on this server', - str(e)) - - -if __name__ == '__main__': - utils.main() diff --git a/test/update_stream_rbr.py b/test/update_stream_rbr.py deleted file mode 100755 index 313d2093783..00000000000 --- a/test/update_stream_rbr.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""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/utils.py b/test/utils.py deleted file mode 100644 index 1e3e26e3402..00000000000 --- a/test/utils.py +++ /dev/null @@ -1,1337 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Common import for all tests.""" - -import base64 -import contextlib -import json -import logging -import optparse -import os -import shlex -import shutil -import signal -import socket -import subprocess -import sys -import time -import unittest -import urllib2 - -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import -from vtdb import vtgate_client - -import environment -from mysql_flavor import mysql_flavor -from mysql_flavor import set_mysql_flavor -import MySQLdb -from protocols_flavor import protocols_flavor -from topo_flavor.server import set_topo_server_flavor -from vtctl import vtctl_client -from vtdb import keyrange_constants -from vtgate_gateway_flavor.gateway import set_vtgate_gateway_flavor -from vtgate_gateway_flavor.gateway import vtgate_gateway_flavor -from vtproto import topodata_pb2 - - -options = None -devnull = open('/dev/null', 'w') -try: - hostname = socket.getaddrinfo( - socket.getfqdn(), None, 0, 0, 0, socket.AI_CANONNAME)[0][3] -except socket.gaierror: - # Fallback to 'localhost' if getfqdn() returns this value for "::1" and - # getaddrinfo() cannot resolve it and throws an exception. - # This error scenario was observed on mberlin@'s corp Macbook in 2018. - if socket.getfqdn() == '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa': # pylint: disable=line-too-long - hostname = 'localhost' - else: - raise - - -class TestError(Exception): - pass - - -class Break(Exception): - pass - -environment.setup() - - -class LoggingStream(object): - - def __init__(self): - self.line = '' - - def write(self, value): - if value == '\n': - # we already printed it - self.line = '' - return - self.line += value - logging.info('===== ' + self.line) - if value.endswith('\n'): - self.line = '' - - def writeln(self, value): - self.write(value) - self.line = '' - - def flush(self): - pass - - -def add_options(parser): - environment.add_options(parser) - parser.add_option('-d', '--debug', action='store_true', - help='utils.pause() statements will wait for user input') - parser.add_option('-k', '--keep-logs', action='store_true', - help='Do not delete log files on teardown.') - parser.add_option( - '-q', '--quiet', action='store_const', const=0, dest='verbose', default=1) - parser.add_option( - '-v', '--verbose', action='store_const', const=2, dest='verbose', - default=1) - parser.add_option('--skip-build', action='store_true', - help='Do not build the go binaries when running the test.') - parser.add_option( - '--skip-teardown', action='store_true', - help='Leave the global processes running after the test is done.') - parser.add_option('--mysql-flavor') - parser.add_option('--protocols-flavor', default='grpc') - parser.add_option('--topo-server-flavor', default='zk2') - parser.add_option('--vtgate-gateway-flavor', default='discoverygateway') - - -def set_options(opts): - global options - options = opts - - set_mysql_flavor(options.mysql_flavor) - environment.setup_protocol_flavor(options.protocols_flavor) - set_topo_server_flavor(options.topo_server_flavor) - set_vtgate_gateway_flavor(options.vtgate_gateway_flavor) - environment.skip_build = options.skip_build - - -# main executes the test classes contained in the passed module, or -# __main__ if empty. -def main(mod=None, test_options=None): - """The replacement main method, which parses args and runs tests. - - Args: - mod: module that contains the test methods. - test_options: a function which adds OptionParser options that are specific - to a test file. - """ - if mod is None: - mod = sys.modules['__main__'] - - global options - - parser = optparse.OptionParser(usage='usage: %prog [options] [test_names]') - add_options(parser) - if test_options: - test_options(parser) - (options, args) = parser.parse_args() - - environment.set_log_level(options.verbose) - logging.basicConfig( - format='-- %(asctime)s %(module)s:%(lineno)d %(levelname)s %(message)s') - - set_options(options) - - run_tests(mod, args) - - -def run_tests(mod, args): - try: - suite = unittest.TestSuite() - if not args: - # this will run the setup and teardown - suite.addTests(unittest.TestLoader().loadTestsFromModule(mod)) - else: - if args[0] == 'teardown': - mod.tearDownModule() - - elif args[0] == 'setup': - mod.setUpModule() - - else: - for arg in args: - # this will run the setup and teardown - suite.addTests(unittest.TestLoader().loadTestsFromName(arg, mod)) - - if suite.countTestCases() > 0: - logger = LoggingStream() - result = unittest.TextTestRunner( - stream=logger, verbosity=options.verbose, failfast=True).run(suite) - if not result.wasSuccessful(): - sys.exit(-1) - except KeyboardInterrupt: - logging.warning('======== Tests interrupted, cleaning up ========') - mod.tearDownModule() - # If you interrupt a test, you probably want to stop evaluating the rest. - sys.exit(1) - finally: - if options.keep_logs: - logging.warning('Leaving temporary files behind (--keep-logs), please ' - 'clean up before next run: ' + os.environ['VTDATAROOT']) - - -def remove_tmp_files(): - if options.keep_logs: - return - try: - shutil.rmtree(environment.tmproot) - except OSError as e: - logging.debug('remove_tmp_files: %s', str(e)) - - -def pause(prompt): - if options.debug: - raw_input(prompt) - - -# sub-process management -pid_map = {} -already_killed = [] - - -def _add_proc(proc): - pid_map[proc.pid] = proc - with open(environment.tmproot+'/test-pids', 'a') as f: - print >> f, proc.pid, os.path.basename(proc.args[0]) - - -def required_teardown(): - """Required cleanup steps that can't be skipped with --skip-teardown.""" - # We can't skip closing of gRPC connections, because the Python interpreter - # won't let us die if any connections are left open. - global vtctld_connection, vtctld - if vtctld_connection: - vtctld_connection.close() - vtctld_connection = None - vtctld = None - - -def kill_sub_processes(): - for proc in pid_map.values(): - if proc.pid and proc.returncode is None: - proc.kill() - if not os.path.exists(environment.tmproot+'/test-pids'): - return - with open(environment.tmproot+'/test-pids') as f: - for line in f: - try: - parts = line.strip().split() - pid = int(parts[0]) - proc = pid_map.get(pid) - if not proc or (proc and proc.pid and proc.returncode is None): - if pid not in already_killed: - os.kill(pid, signal.SIGTERM) - except OSError as e: - logging.debug('kill_sub_processes: %s', str(e)) - - -def kill_sub_process(proc, soft=False): - if proc is None: - return - pid = proc.pid - if soft: - proc.terminate() - else: - proc.kill() - if pid and pid in pid_map: - del pid_map[pid] - already_killed.append(pid) - - -# run in foreground, possibly capturing output -def run(cmd, trap_output=False, raise_on_error=True, **kargs): - if isinstance(cmd, str): - args = shlex.split(cmd) - else: - args = cmd - if trap_output: - kargs['stdout'] = subprocess.PIPE - kargs['stderr'] = subprocess.PIPE - logging.debug( - 'run: %s %s', str(cmd), - ', '.join('%s=%s' % x for x in kargs.iteritems())) - proc = subprocess.Popen(args, **kargs) - proc.args = args - stdout, stderr = proc.communicate() - if proc.returncode: - if raise_on_error: - pause('cmd fail: %s, pausing...' % (args)) - raise TestError('cmd fail:', args, proc.returncode, stdout, stderr) - else: - logging.debug('cmd fail: %s %d %s %s', - str(args), proc.returncode, stdout, stderr) - return stdout, stderr - - -# run sub-process, expects failure -def run_fail(cmd, **kargs): - if isinstance(cmd, str): - args = shlex.split(cmd) - else: - args = cmd - kargs['stdout'] = subprocess.PIPE - kargs['stderr'] = subprocess.PIPE - if options.verbose == 2: - logging.debug( - 'run: (expect fail) %s %s', cmd, - ', '.join('%s=%s' % x for x in kargs.iteritems())) - proc = subprocess.Popen(args, **kargs) - proc.args = args - stdout, stderr = proc.communicate() - if proc.returncode == 0 or options.verbose == 2: - logging.info('stdout:\n%sstderr:\n%s', stdout, stderr) - if proc.returncode == 0: - raise TestError('expected fail:', args, stdout, stderr) - return stdout, stderr - - -# run a daemon - kill when this script exits -def run_bg(cmd, **kargs): - if options.verbose == 2: - logging.debug( - 'run: %s %s', cmd, ', '.join('%s=%s' % x for x in kargs.iteritems())) - if 'extra_env' in kargs: - kargs['env'] = os.environ.copy() - if kargs['extra_env']: - kargs['env'].update(kargs['extra_env']) - del kargs['extra_env'] - if isinstance(cmd, str): - args = shlex.split(cmd) - else: - args = cmd - proc = subprocess.Popen(args=args, **kargs) - proc.args = args - _add_proc(proc) - return proc - - -def wait_procs(proc_list, raise_on_error=True): - for proc in proc_list: - pid = proc.pid - if pid: - already_killed.append(pid) - for proc in proc_list: - proc.wait() - for proc in proc_list: - if proc.returncode: - if options.verbose >= 1 and proc.returncode not in (-9,): - sys.stderr.write('proc failed: %s %s\n' % (proc.returncode, proc.args)) - if raise_on_error: - raise subprocess.CalledProcessError(proc.returncode, - ' '.join(proc.args)) - - -def validate_topology(ping_tablets=False): - if ping_tablets: - run_vtctl(['Validate', '-ping-tablets']) - else: - run_vtctl(['Validate']) - - -# wait_step is a helper for looping until a condition is true. -# use as follow: -# timeout = 10 -# while True: -# -# if : -# break -# timeout = utils.wait_step('description of condition', timeout) -def wait_step(msg, timeout, sleep_time=0.1): - timeout -= sleep_time - if timeout <= 0: - raise TestError('timeout waiting for condition "%s"' % msg) - logging.debug('Sleeping for %f seconds waiting for condition "%s"', - sleep_time, msg) - time.sleep(sleep_time) - return timeout - - -# vars helpers -def get_vars(port): - """Returns the dict for vars from a vtxxx process. None if not available.""" - try: - url = 'http://localhost:%d/debug/vars' % int(port) - f = urllib2.urlopen(url) - data = f.read() - f.close() - except urllib2.URLError: - return None - try: - return json.loads(data) - except ValueError: - print data - raise - - -def wait_for_vars(name, port, var=None, key=None, value=None, timeout=10.0): - """Waits for the vars of a process, and optional values. - - Args: - name: nickname for the process. - port: process port to look at. - var: if specified, waits for var in vars. - key: if specified, waits for vars[var][key]==value. - value: if key if specified, waits for vars[var][key]==value. - timeout: how long to wait. - """ - text = 'waiting for http://localhost:%d/debug/vars of %s' % (port, name) - if var: - text += ' value %s' % var - if key: - text += ' key %s:%s' % (key, value) - while True: - display_text = text - v = get_vars(port) - if v: - if var is None: - break - if var in v: - if key is None: - break - if key in v[var]: - if v[var][key] == value: - break - else: - display_text += ' (current value:%s)' % v[var][key] - else: - display_text += ' (no current value)' - else: - display_text += ' (%s not in vars)' % var - else: - display_text += ' (no vars yet)' - timeout = wait_step(display_text, timeout) - - -def poll_for_vars( - name, port, condition_msg, timeout=60.0, condition_fn=None, - require_vars=False): - """Polls for debug variables to exist or match specific conditions. - - This function polls in a tight loop, with no sleeps. This is useful for - variables that are expected to be short-lived (e.g., a 'Done' state - immediately before a process exits). - - Args: - name: the name of the process that we're trying to poll vars from. - port: the port number that we should poll for variables. - condition_msg: string describing the conditions that we're polling for, - used for error messaging. - timeout: number of seconds that we should attempt to poll for. - condition_fn: a function that takes the debug vars dict as input, and - returns a truthy value if it matches the success conditions. - require_vars: True iff we expect the vars to always exist. If - True, and the vars don't exist, we'll raise a TestError. This - can be used to differentiate between a timeout waiting for a - particular condition vs if the process that you're polling has - already exited. - - Raises: - TestError: if the conditions aren't met within the given timeout, or - if vars are required and don't exist. - - Returns: - dict of debug variables - - """ - start_time = time.time() - while True: - if (time.time() - start_time) >= timeout: - raise TestError( - 'Timed out polling for vars from %s; condition "%s" not met' % - (name, condition_msg)) - v = get_vars(port) - if v is None: - if require_vars: - raise TestError( - 'Expected vars to exist on %s, but they do not; ' - 'process probably exited earlier than expected.' % (name,)) - continue - if condition_fn is None: - return v - elif condition_fn(v): - return v - - -def apply_vschema(vschema): - for k, v in vschema.iteritems(): - fname = os.path.join(environment.tmproot, 'vschema.json') - with open(fname, 'w') as f: - f.write(v) - run_vtctl(['ApplyVSchema', '-vschema_file', fname, k]) - - -def wait_for_tablet_type(tablet_alias, expected_type, timeout=10): - """Waits for a given tablet's SlaveType to become the expected value. - - Args: - tablet_alias: Alias of the tablet. - expected_type: Type of the tablet e.g. "replica". - timeout: Timeout in seconds. - - Raises: - TestError: SlaveType did not become expected_type within timeout seconds. - """ - type_as_int = topodata_pb2.TabletType.Value(expected_type.upper()) - while True: - if run_vtctl_json(['GetTablet', tablet_alias])['type'] == type_as_int: - logging.debug('tablet %s went to expected type: %s', - tablet_alias, expected_type) - break - timeout = wait_step( - "%s's SlaveType to be %s" % (tablet_alias, expected_type), - timeout) - - -def wait_for_replication_pos(tablet_a, tablet_b, timeout=60.0): - """Waits for tablet B to catch up to the replication position of tablet A. - - Args: - tablet_a: tablet Object for tablet A. - tablet_b: tablet Object for tablet B. - timeout: Timeout in seconds. - - Raises: - TestError: replication position did not catch up within timeout seconds. - """ - replication_pos_a = mysql_flavor().master_position(tablet_a) - while True: - replication_pos_b = mysql_flavor().master_position(tablet_b) - if mysql_flavor().position_at_least(replication_pos_b, replication_pos_a): - break - timeout = wait_step( - "%s's replication position to catch up %s's; " - 'currently at: %s, waiting to catch up to: %s' % ( - tablet_b.tablet_alias, tablet_a.tablet_alias, replication_pos_b, - replication_pos_a), - timeout, sleep_time=0.1) - -# Save the first running instance of vtgate. It is saved when 'start' -# is called, and cleared when kill is called. -vtgate = None - - -class VtGate(object): - """VtGate object represents a vtgate process.""" - - def __init__(self, port=None, mysql_server=False): - """Creates the Vtgate instance and reserve the ports if necessary.""" - self.port = port or environment.reserve_ports(1) - if protocols_flavor().vtgate_protocol() == 'grpc': - self.grpc_port = environment.reserve_ports(1) - self.proc = None - self.mysql_port = None - if mysql_server: - self.mysql_port = environment.reserve_ports(1) - - def start(self, cell='test_nj', retry_count=2, - topo_impl=None, cache_ttl='1s', - extra_args=None, tablets=None, - tablet_types_to_wait='MASTER,REPLICA', - l2vtgates=None, - cells_to_watch=None): - """Start vtgate. Saves it into the global vtgate variable if not set yet.""" - - args = environment.binary_args('vtgate') + [ - '-port', str(self.port), - '-cell', cell, - '-retry-count', str(retry_count), - '-log_dir', environment.vtlogroot, - '-srv_topo_cache_ttl', cache_ttl, - '-srv_topo_cache_refresh', cache_ttl, - '-tablet_protocol', protocols_flavor().tabletconn_protocol(), - '-stderrthreshold', get_log_level(), - '-normalize_queries', - '-gateway_implementation', vtgate_gateway_flavor().flavor(), - ] - - if cells_to_watch: - args.extend(vtgate_gateway_flavor().flags(cell=cells_to_watch, tablets=tablets)) - else: - args.extend(vtgate_gateway_flavor().flags(cell=cell, tablets=tablets)) - - if l2vtgates: - args.extend(['-l2vtgate_addrs', ','.join(l2vtgates)]) - if tablet_types_to_wait: - args.extend(['-tablet_types_to_wait', tablet_types_to_wait]) - - if protocols_flavor().vtgate_protocol() == 'grpc': - args.extend(['-grpc_port', str(self.grpc_port)]) - args.extend(['-grpc_max_message_size', - str(environment.grpc_max_message_size)]) - if protocols_flavor().service_map(): - args.extend(['-service_map', ','.join(protocols_flavor().service_map())]) - if topo_impl: - args.extend(['-topo_implementation', topo_impl]) - else: - args.extend(environment.topo_server().flags()) - if extra_args: - args.extend(extra_args) - if self.mysql_port: - args.extend(['-mysql_server_port', str(self.mysql_port)]) - - self.proc = run_bg(args) - # We use a longer timeout here, as we may be waiting for the initial - # state of a few tablets. - wait_for_vars('vtgate', self.port, timeout=20.0) - - global vtgate - if not vtgate: - vtgate = self - - def kill(self): - """Terminates the vtgate process, and waits for it to exit. - - If this process is the one saved in the global vtgate variable, - clears it. - - Note if the test is using just one global vtgate process, and - starting it with the test, and killing it at the end of the test, - there is no need to call this kill() method, - utils.kill_sub_processes() will do a good enough job. - - """ - if self.proc is None: - return - kill_sub_process(self.proc, soft=True) - self.proc.wait() - self.proc = None - - global vtgate - if vtgate == self: - vtgate = None - - def addr(self): - """Returns the address of the vtgate process, for web access.""" - return 'localhost:%d' % self.port - - def rpc_endpoint(self, python=False): - """Returns the protocol and endpoint to use for RPCs.""" - if python: - protocol = protocols_flavor().vtgate_python_protocol() - else: - protocol = protocols_flavor().vtgate_protocol() - if protocol == 'grpc': - return protocol, 'localhost:%d' % self.grpc_port - return protocol, self.addr() - - def get_status(self): - """Returns the status page for this process.""" - return get_status(self.port) - - def get_vars(self): - """Returns the vars for this process.""" - return get_vars(self.port) - - def get_vschema(self): - """Returns the used vschema for this process.""" - return urllib2.urlopen('http://localhost:%d/debug/vschema' % - self.port).read() - - @contextlib.contextmanager - def create_connection(self): - """Connects to vtgate and allows to create a cursor to execute queries. - - This method is preferred over the two other methods ("vtclient", "execute") - to execute a query in tests. - - Yields: - A vtgate connection object. - - Example: - with self.vtgate.create_connection() as conn: - c = conn.cursor(keyspace=KEYSPACE, shards=[SHARD], tablet_type='master', - writable=self.writable) - c.execute('SELECT * FROM buffer WHERE id = :id', {'id': 1}) - """ - protocol, endpoint = self.rpc_endpoint(python=True) - # Use a very long timeout to account for slow tests. - conn = vtgate_client.connect(protocol, endpoint, 600.0) - yield conn - conn.close() - - @contextlib.contextmanager - def write_transaction(self, **kwargs): - """Begins a write transaction and commits automatically. - - Note that each transaction contextmanager will create a new connection. - - Args: - **kwargs: vtgate cursor args. See vtgate_cursor.VTGateCursor. - - Yields: - A writable vtgate cursor. - - Example: - with utils.vtgate.write_transaction(keyspace=KEYSPACE, shards=[SHARD], - tablet_type='master') as tx: - tx.execute('INSERT INTO table1 (id, msg) VALUES (:id, :msg)', - {'id': 1, 'msg': 'msg1'}) - """ - with self.create_connection() as conn: - cursor = conn.cursor(writable=True, **kwargs) - cursor.begin() - yield cursor - cursor.commit() - - def vtclient(self, sql, keyspace=None, tablet_type='master', - bindvars=None, streaming=False, - verbose=False, raise_on_error=True, json_output=False): - """Uses the vtclient binary to send a query to vtgate.""" - protocol, addr = self.rpc_endpoint() - args = environment.binary_args('vtclient') + [ - '-server', addr, - '-vtgate_protocol', protocol] - if json_output: - args.append('-json') - if bindvars: - args.extend(['-bind_variables', json.dumps(bindvars)]) - if streaming: - args.append('-streaming') - if keyspace: - args.extend(['-target', '%s@%s' % (keyspace, tablet_type)]) - else: - args.extend(['-target', '@'+tablet_type]) - if verbose: - args.append('-alsologtostderr') - args.append(sql) - - out, err = run(args, raise_on_error=raise_on_error, trap_output=True) - if json_output: - return json.loads(out), err - return out, err - - def execute(self, sql, tablet_type='master', bindvars=None, - execute_options=None): - """Uses 'vtctl VtGateExecute' to execute a command. - - Args: - sql: the command to execute. - tablet_type: the tablet_type to use. - bindvars: a dict of bind variables. - execute_options: proto-encoded ExecuteOptions object. - - Returns: - the result of running vtctl command. - """ - _, addr = self.rpc_endpoint() - args = ['VtGateExecute', '-json', - '-server', addr, - '-target', '@'+tablet_type] - if bindvars: - args.extend(['-bind_variables', json.dumps(bindvars)]) - if execute_options: - args.extend(['-options', execute_options]) - args.append(sql) - return run_vtctl_json(args) - - def execute_shards(self, sql, keyspace, shards, tablet_type='master', - bindvars=None): - """Uses 'vtctl VtGateExecuteShards' to execute a command.""" - _, addr = self.rpc_endpoint() - args = ['VtGateExecuteShards', '-json', - '-server', addr, - '-keyspace', keyspace, - '-shards', shards, - '-tablet_type', tablet_type] - if bindvars: - args.extend(['-bind_variables', json.dumps(bindvars)]) - args.append(sql) - return run_vtctl_json(args) - - def split_query(self, sql, keyspace, split_count, bindvars=None): - """Uses 'vtctl VtGateSplitQuery' to cut a query up in chunks.""" - _, addr = self.rpc_endpoint() - args = ['VtGateSplitQuery', - '-server', addr, - '-keyspace', keyspace, - '-split_count', str(split_count)] - if bindvars: - args.extend(['-bind_variables', json.dumps(bindvars)]) - args.append(sql) - return run_vtctl_json(args) - - def wait_for_endpoints(self, name, count, timeout=20.0, var=None): - """waits until vtgate gets endpoints. - - Args: - name: name of the endpoint, in the form: 'keyspace.shard.type'. - count: how many endpoints to wait for. - timeout: how long to wait. - var: name of the variable to use. if None, defaults to the gateway's. - """ - wait_for_vars('vtgate', self.port, - var=var or vtgate_gateway_flavor().connection_count_vars(), - key=name, value=count, timeout=timeout) - - def verify_no_endpoint(self, name): - """verifies the vtgate doesn't have any enpoint of the given name. - - Args: - name: name of the endpoint, in the form: 'keyspace.shard.type'. - """ - def condition(v): - return (v.get(vtgate_gateway_flavor().connection_count_vars()) - .get(name, None)) is None - - poll_for_vars('l2vtgate', self.port, - 'no endpoint named ' + name, - timeout=5.0, - condition_fn=condition) - - -# vtctl helpers -# The modes are not all equivalent, and we don't really thrive for it. -# If a client needs to rely on vtctl's command line behavior, make -# sure to use mode=utils.VTCTL_VTCTL -VTCTL_AUTO = 0 -VTCTL_VTCTL = 1 -VTCTL_VTCTLCLIENT = 2 -VTCTL_RPC = 3 - - -def run_vtctl(clargs, auto_log=False, expect_fail=False, - mode=VTCTL_AUTO, action_timeout=10, **kwargs): - if mode == VTCTL_AUTO: - if not expect_fail and vtctld: - mode = VTCTL_RPC - else: - mode = VTCTL_VTCTL - - if mode == VTCTL_VTCTL: - return run_vtctl_vtctl(clargs, auto_log=auto_log, - expect_fail=expect_fail, **kwargs) - elif mode == VTCTL_VTCTLCLIENT: - result = vtctld.vtctl_client(clargs) - return result, '' - elif mode == VTCTL_RPC: - if auto_log: - logging.debug('vtctl: %s', ' '.join(clargs)) - result = vtctl_client.execute_vtctl_command(vtctld_connection, clargs, - info_to_debug=True, - action_timeout=action_timeout) - return result, '' - - raise Exception('Unknown mode: %s', mode) - - -def run_vtctl_vtctl(clargs, auto_log=False, expect_fail=False, - **kwargs): - args = environment.binary_args('vtctl') + [ - '-log_dir', environment.vtlogroot, - '-enable_queries', - ] - args.extend(environment.topo_server().flags()) - args.extend(['-tablet_manager_protocol', - protocols_flavor().tablet_manager_protocol()]) - args.extend(['-tablet_protocol', protocols_flavor().tabletconn_protocol()]) - args.extend(['-throttler_client_protocol', - protocols_flavor().throttler_client_protocol()]) - args.extend(['-vtgate_protocol', protocols_flavor().vtgate_protocol()]) - # TODO(b/26388813): Remove the next two lines once vtctl WaitForDrain is - # integrated in the vtctl MigrateServed* commands. - args.extend(['--wait_for_drain_sleep_rdonly', '0s']) - args.extend(['--wait_for_drain_sleep_replica', '0s']) - - if auto_log: - args.append('--stderrthreshold=%s' % get_log_level()) - - if isinstance(clargs, str): - cmd = ' '.join(args) + ' ' + clargs - else: - cmd = args + clargs - - if expect_fail: - return run_fail(cmd, **kwargs) - return run(cmd, **kwargs) - - -# run_vtctl_json runs the provided vtctl command and returns the result -# parsed as json -def run_vtctl_json(clargs, auto_log=True): - stdout, _ = run_vtctl(clargs, trap_output=True, auto_log=auto_log) - return json.loads(stdout) - - -def get_log_level(): - if options.verbose == 2: - return '0' - elif options.verbose == 1: - return '1' - else: - return '2' - - -# vtworker helpers -def run_vtworker(clargs, auto_log=False, expect_fail=False, **kwargs): - """Runs a vtworker process, returning the stdout and stderr.""" - cmd, _, _ = _get_vtworker_cmd(clargs, auto_log) - if expect_fail: - return run_fail(cmd, **kwargs) - return run(cmd, **kwargs) - - -def run_vtworker_bg(clargs, auto_log=False, **kwargs): - """Starts a background vtworker process.""" - cmd, port, rpc_port = _get_vtworker_cmd(clargs, auto_log) - proc = run_bg(cmd, **kwargs), port, rpc_port - wait_for_vars('vtworker', port) - return proc - - -def _get_vtworker_cmd(clargs, auto_log=False): - """Assembles the command that is needed to run a vtworker. - - Args: - clargs: Command line arguments passed to vtworker. - auto_log: If true, set --stderrthreshold according to the test log level. - - Returns: - cmd - list of cmd arguments, can be passed to any `run`-like functions - port - int with the port number that the vtworker is running with - rpc_port - int with the port number of the RPC interface - """ - port = environment.reserve_ports(1) - rpc_port = port - args = environment.binary_args('vtworker') + [ - '-log_dir', environment.vtlogroot, - '-port', str(port), - '-executefetch_retry_time', '1s', - '-tablet_manager_protocol', - protocols_flavor().tablet_manager_protocol(), - '-tablet_protocol', protocols_flavor().tabletconn_protocol(), - ] - args.extend(environment.topo_server().flags()) - if protocols_flavor().service_map(): - args.extend(['-service_map', - ','.join(protocols_flavor().service_map())]) - if protocols_flavor().vtworker_client_protocol() == 'grpc': - rpc_port = environment.reserve_ports(1) - args.extend(['-grpc_port', str(rpc_port)]) - - if auto_log: - args.append('--stderrthreshold=%s' % get_log_level()) - - cmd = args + clargs - return cmd, port, rpc_port - - -# vtworker client helpers -def run_vtworker_client_bg(args, rpc_port): - """Runs vtworkerclient to execute a command on a remote vtworker. - - Args: - args: Full vtworker command. - rpc_port: Port number. - - Returns: - proc: process returned by subprocess.Popen - """ - return run_bg( - environment.binary_args('vtworkerclient') + [ - '-log_dir', environment.vtlogroot, - '-vtworker_client_protocol', - protocols_flavor().vtworker_client_protocol(), - '-server', 'localhost:%d' % rpc_port, - '-stderrthreshold', get_log_level(), - ] + args) - - -def run_automation_server(auto_log=False): - """Starts a background automation_server process. - - Args: - auto_log: True to log. - - Returns: - rpc_port - int with the port number of the RPC interface - """ - rpc_port = environment.reserve_ports(1) - args = environment.binary_args('automation_server') + [ - '-log_dir', environment.vtlogroot, - '-port', str(rpc_port), - '-vtctl_client_protocol', - protocols_flavor().vtctl_client_protocol(), - '-vtworker_client_protocol', - protocols_flavor().vtworker_client_protocol(), - ] - if auto_log: - args.append('--stderrthreshold=%s' % get_log_level()) - - return run_bg(args), rpc_port - - -# mysql helpers -def mysql_query(uid, dbname, query): - conn = MySQLdb.Connect( - user='vt_dba', - unix_socket='%s/vt_%010d/mysql.sock' % (environment.vtdataroot, uid), - db=dbname) - cursor = conn.cursor() - cursor.execute(query) - try: - return cursor.fetchall() - finally: - conn.close() - - -def mysql_write_query(uid, dbname, query): - conn = MySQLdb.Connect( - user='vt_dba', - unix_socket='%s/vt_%010d/mysql.sock' % (environment.vtdataroot, uid), - db=dbname) - cursor = conn.cursor() - conn.begin() - cursor.execute(query) - conn.commit() - try: - return cursor.fetchall() - finally: - conn.close() - - -def check_db_var(uid, name, value): - conn = MySQLdb.Connect( - user='vt_dba', - unix_socket='%s/vt_%010d/mysql.sock' % (environment.vtdataroot, uid)) - cursor = conn.cursor() - cursor.execute("show variables like '%s'" % name) - row = cursor.fetchone() - if row != (name, value): - raise TestError('variable not set correctly', name, row) - conn.close() - - -def check_db_read_only(uid): - return check_db_var(uid, 'read_only', 'ON') - - -def check_db_read_write(uid): - return check_db_var(uid, 'read_only', 'OFF') - - -def wait_db_read_only(uid): - for _ in xrange(3): - try: - check_db_read_only(uid) - return - except TestError as e: - logging.warning('wait_db_read_only: %s', str(e)) - time.sleep(1.0) - raise e - - -def check_srv_keyspace(cell, keyspace, expected, keyspace_id_type='uint64', - sharding_column_name='keyspace_id'): - ks = run_vtctl_json(['GetSrvKeyspace', cell, keyspace]) - result = '' - pmap = {} - for partition in ks['partitions']: - tablet_type = topodata_pb2.TabletType.Name(partition['served_type']).lower() - if tablet_type == 'batch': - tablet_type = 'rdonly' - r = 'Partitions(%s):' % tablet_type - for shard in partition['shard_references']: - s = '' - e = '' - if 'key_range' in shard and shard['key_range']: - if 'start' in shard['key_range']: - s = shard['key_range']['start'] - s = base64.b64decode(s).encode('hex') if s else '' - if 'end' in shard['key_range']: - e = shard['key_range']['end'] - e = base64.b64decode(e).encode('hex') if e else '' - r += ' %s-%s' % (s, e) - pmap[tablet_type] = r + '\n' - for tablet_type in sorted(pmap): - result += pmap[tablet_type] - logging.debug('Cell %s keyspace %s has data:\n%s', cell, keyspace, result) - if expected != result: - raise Exception( - 'Mismatch in srv keyspace for cell %s keyspace %s, expected:\n%' - 's\ngot:\n%s' % ( - cell, keyspace, expected, result)) - if sharding_column_name != ks.get('sharding_column_name'): - raise Exception('Got wrong sharding_column_name in SrvKeyspace: %s' % - str(ks)) - if keyspace_id_type != keyrange_constants.PROTO3_KIT_TO_STRING[ - ks.get('sharding_column_type')]: - raise Exception('Got wrong sharding_column_type in SrvKeyspace: %s' % - str(ks)) - - -def check_shard_query_service( - testcase, cell, keyspace, shard_name, tablet_type, expected_state): - """Checks DisableQueryService in the shard record's TabletControlMap.""" - # We assume that query service should be enabled unless - # DisableQueryService is explicitly True - query_service_enabled = True - ks = run_vtctl_json(['GetSrvKeyspace', cell, keyspace]) - for partition in ks['partitions']: - tablet_type = topodata_pb2.TabletType.Name(partition['served_type']) - if tablet_type != tablet_type: - continue - for shard in partition['shard_tablet_controls']: - if shard['name'] == shard_name: - if shard['query_service_disabled']: - query_service_enabled = False - - testcase.assertEqual( - query_service_enabled, - expected_state, - 'shard %s does not have the correct query service state: ' - 'got %s but expected %s' % - (shard_name, query_service_enabled, expected_state) - ) - - -def check_shard_query_services( - testcase, cell, keyspace, shard_names, tablet_type, expected_state): - for shard_name in shard_names: - check_shard_query_service( - testcase, cell, keyspace, shard_name, tablet_type, expected_state) - - -def check_tablet_query_service( - testcase, tablet, serving, tablet_control_disabled): - """Check that the query service is enabled or disabled on the tablet.""" - tablet_vars = get_vars(tablet.port) - if serving: - expected_state = 'SERVING' - else: - expected_state = 'NOT_SERVING' - testcase.assertEqual( - tablet_vars['TabletStateName'], expected_state, - 'tablet %s (%s/%s, %s) is not in the right serving state: got %s' - ' expected %s' % (tablet.tablet_alias, tablet.keyspace, tablet.shard, - tablet.tablet_type, - tablet_vars['TabletStateName'], expected_state)) - - status = tablet.get_status() - tc_dqs = 'Query Service disabled: TabletControl.DisableQueryService set' - if tablet_control_disabled: - testcase.assertIn(tc_dqs, status) - else: - testcase.assertNotIn(tc_dqs, status) - - if tablet.tablet_type == 'rdonly': - # Run RunHealthCheck to be sure the tablet doesn't change its serving state. - run_vtctl(['RunHealthCheck', tablet.tablet_alias], - auto_log=True) - - tablet_vars = get_vars(tablet.port) - testcase.assertEqual( - tablet_vars['TabletStateName'], expected_state, - 'tablet %s is not in the right serving state after health check: ' - 'got %s expected %s' % - (tablet.tablet_alias, tablet_vars['TabletStateName'], expected_state)) - - -def check_tablet_query_services( - testcase, tablets, serving, tablet_control_disabled): - for tablet in tablets: - check_tablet_query_service( - testcase, tablet, serving, tablet_control_disabled) - - -def get_status(port): - return urllib2.urlopen( - 'http://localhost:%d%s' % (port, environment.status_url)).read() - - -def curl(url, request=None, data=None, background=False, retry_timeout=0, - **kwargs): - args = [environment.curl_bin, '--silent', '--no-buffer', '--location'] - if not background: - args.append('--show-error') - if request: - args.extend(['--request', request]) - if data: - args.extend(['--data', data]) - args.append(url) - - if background: - return run_bg(args, **kwargs) - - if retry_timeout > 0: - while True: - try: - return run(args, trap_output=True, **kwargs) - except TestError as e: - retry_timeout = wait_step( - 'cmd: %s, error: %s' % (str(args), str(e)), retry_timeout) - - return run(args, trap_output=True, **kwargs) - - -class VtctldError(Exception): - pass - -# save the first running instance, and an RPC connection to it, -# so we can use it to run remote vtctl commands -vtctld = None -vtctld_connection = None - - -class Vtctld(object): - - def __init__(self): - self.port = environment.reserve_ports(1) - self.schema_change_dir = os.path.join( - environment.tmproot, 'schema_change_test') - if protocols_flavor().vtctl_client_protocol() == 'grpc': - self.grpc_port = environment.reserve_ports(1) - - def start(self, enable_schema_change_dir=False, extra_flags=None): - # Note the vtctld2 web dir is set to 'dist', which is populated - # when a toplevel 'make build_web' is run. This is meant to test - # the development version of the UI. The real checked-in app is in - # app/. - args = environment.binary_args('vtctld') + [ - '-enable_queries', - '-cell', 'test_nj', - '--log_dir', environment.vtlogroot, - '--port', str(self.port), - '-tablet_manager_protocol', - protocols_flavor().tablet_manager_protocol(), - '-tablet_protocol', protocols_flavor().tabletconn_protocol(), - '-throttler_client_protocol', - protocols_flavor().throttler_client_protocol(), - '-vtgate_protocol', protocols_flavor().vtgate_protocol(), - '-workflow_manager_init', - '-workflow_manager_use_election', - '-schema_swap_delay_between_errors', '1s', - '-wait_for_drain_sleep_rdonly', '1s', - '-wait_for_drain_sleep_replica', '1s', - ] + environment.topo_server().flags() - if extra_flags: - args += extra_flags - # TODO(b/26388813): Remove the next two lines once vtctl WaitForDrain is - # integrated in the vtctl MigrateServed* commands. - args.extend(['--wait_for_drain_sleep_rdonly', '0s']) - args.extend(['--wait_for_drain_sleep_replica', '0s']) - if enable_schema_change_dir: - args += [ - '--schema_change_dir', self.schema_change_dir, - '--schema_change_controller', 'local', - '--schema_change_check_interval', '1', - ] - if protocols_flavor().service_map(): - args.extend(['-service_map', ','.join(protocols_flavor().service_map())]) - if protocols_flavor().vtctl_client_protocol() == 'grpc': - args.extend(['-grpc_port', str(self.grpc_port)]) - stdout_fd = open(os.path.join(environment.tmproot, 'vtctld.stdout'), 'w') - stderr_fd = open(os.path.join(environment.tmproot, 'vtctld.stderr'), 'w') - self.proc = run_bg(args, stdout=stdout_fd, stderr=stderr_fd) - - # wait for the process to listen to RPC - timeout = 30 - while True: - v = get_vars(self.port) - if v: - break - if self.proc.poll() is not None: - raise TestError('vtctld died while starting') - timeout = wait_step('waiting for vtctld to start', timeout, - sleep_time=0.2) - - # save the running instance so vtctl commands can be remote executed now - global vtctld, vtctld_connection - if not vtctld: - vtctld = self - protocol, endpoint = self.rpc_endpoint(python=True) - vtctld_connection = vtctl_client.connect(protocol, endpoint, 30) - - return self.proc - - def rpc_endpoint(self, python=False): - """RPC endpoint to vtctld. - - The RPC endpoint may differ from the webinterface URL e.g. because gRPC - requires a dedicated port. - - Args: - python: boolean, True iff this is for access with Python (as opposed to - Go). - - Returns: - protocol - string e.g. 'grpc' - endpoint - string e.g. 'localhost:15001' - """ - if python: - protocol = protocols_flavor().vtctl_python_client_protocol() - else: - protocol = protocols_flavor().vtctl_client_protocol() - rpc_port = self.port - if protocol == 'grpc': - rpc_port = self.grpc_port - return (protocol, '%s:%d' % (socket.getfqdn(), rpc_port)) - - def process_args(self): - return ['-vtctld_addr', 'http://localhost:%d/' % self.port] - - def vtctl_client(self, args): - if options.verbose == 2: - log_level = 'INFO' - elif options.verbose == 1: - log_level = 'WARNING' - else: - log_level = 'ERROR' - - protocol, endpoint = self.rpc_endpoint() - out, _ = run( - environment.binary_args('vtctlclient') + - ['-vtctl_client_protocol', protocol, - '-server', endpoint, - '-stderrthreshold', log_level] + args, - trap_output=True) - return out - - -def uint64_to_hex(integer): - """Returns the hex representation of an int treated as a 64-bit unsigned int. - - The result is padded by zeros if necessary to fill a 16 character string. - Useful for converting keyspace ids integers. - - Example: - uint64_to_hex(1) == "0000000000000001" - uint64_to_hex(0xDEADBEAF) == "00000000DEADBEEF" - uint64_to_hex(0xDEADBEAFDEADBEAFDEADBEAF) raises an out of range exception. - - Args: - integer: the value to print. - - Returns: - String with the hex representation. - - Raises: - ValueError: if the integer is out of range. - """ - if integer > (1<<64)-1 or integer < 0: - raise ValueError('Integer out of range: %d' % integer) - return '%016X' % integer diff --git a/test/vertical_split.py b/test/vertical_split.py deleted file mode 100755 index fbb61c30c74..00000000000 --- a/test/vertical_split.py +++ /dev/null @@ -1,678 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import time -import unittest - -import base_sharding -import environment -import tablet -import utils - -from vtproto import topodata_pb2 -from vtdb import keyrange -from vtdb import keyrange_constants -from vtdb import vtgate_client - -# source keyspace, with 4 tables -source_master = tablet.Tablet() -source_replica = tablet.Tablet() -source_rdonly1 = tablet.Tablet() -source_rdonly2 = tablet.Tablet() - -# destination keyspace, with just two tables -destination_master = tablet.Tablet() -destination_replica = tablet.Tablet() -destination_rdonly1 = tablet.Tablet() -destination_rdonly2 = tablet.Tablet() - -all_tablets = [source_master, source_replica, source_rdonly1, source_rdonly2, - destination_master, destination_replica, destination_rdonly1, - destination_rdonly2] - - -def setUpModule(): - try: - environment.topo_server().setup() - 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: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - if utils.vtgate: - utils.vtgate.kill() - teardown_procs = [t.teardown_mysql() for t in all_tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - - -class TestVerticalSplit(unittest.TestCase, base_sharding.BaseShardingTest): - - def setUp(self): - self.insert_index = 0 - - self._init_keyspaces_and_tablets() - utils.VtGate().start(cache_ttl='0s', tablets=[ - source_master, source_replica, source_rdonly1, - source_rdonly2, destination_master, - destination_replica, destination_rdonly1, - destination_rdonly2]) - - utils.vtgate.wait_for_endpoints( - '%s.%s.master' % ('source_keyspace', '0'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.replica' % ('source_keyspace', '0'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.rdonly' % ('source_keyspace', '0'), - 2) - utils.vtgate.wait_for_endpoints( - '%s.%s.master' % ('destination_keyspace', '0'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.replica' % ('destination_keyspace', '0'), - 1) - utils.vtgate.wait_for_endpoints( - '%s.%s.rdonly' % ('destination_keyspace', '0'), - 2) - - # create the schema on the source keyspace, add some values - self._insert_initial_values() - - def tearDown(self): - # kill everything - tablet.kill_tablets([source_master, source_replica, source_rdonly1, - source_rdonly2, destination_master, - destination_replica, destination_rdonly1, - destination_rdonly2]) - utils.vtgate.kill() - - def _init_keyspaces_and_tablets(self): - utils.run_vtctl(['CreateKeyspace', 'source_keyspace']) - utils.run_vtctl( - ['CreateKeyspace', '--served_from', - 'master:source_keyspace,replica:source_keyspace,rdonly:' - 'source_keyspace', - 'destination_keyspace']) - - source_master.init_tablet( - 'replica', - keyspace='source_keyspace', - shard='0', - tablet_index=0) - source_replica.init_tablet( - 'replica', - keyspace='source_keyspace', - shard='0', - tablet_index=1) - source_rdonly1.init_tablet( - 'rdonly', - keyspace='source_keyspace', - shard='0', - tablet_index=2) - source_rdonly2.init_tablet( - 'rdonly', - keyspace='source_keyspace', - shard='0', - tablet_index=3) - destination_master.init_tablet( - 'replica', - keyspace='destination_keyspace', - shard='0', - tablet_index=0) - destination_replica.init_tablet( - 'replica', - keyspace='destination_keyspace', - shard='0', - tablet_index=1) - destination_rdonly1.init_tablet( - 'rdonly', - keyspace='destination_keyspace', - shard='0', - tablet_index=2) - destination_rdonly2.init_tablet( - 'rdonly', - keyspace='destination_keyspace', - shard='0', - tablet_index=3) - - utils.run_vtctl( - ['RebuildKeyspaceGraph', 'source_keyspace'], auto_log=True) - utils.run_vtctl( - ['RebuildKeyspaceGraph', 'destination_keyspace'], auto_log=True) - - self._create_source_schema() - - for t in [source_master, source_replica, - destination_master, destination_replica]: - t.start_vttablet(wait_for_state=None) - for t in [source_rdonly1, source_rdonly2, - destination_rdonly1, destination_rdonly2]: - t.start_vttablet(wait_for_state=None) - - # wait for the tablets - master_tablets = [source_master, destination_master] - replica_tablets = [ - source_replica, source_rdonly1, source_rdonly2, - destination_replica, destination_rdonly1, - destination_rdonly2] - for t in master_tablets + replica_tablets: - t.wait_for_vttablet_state('NOT_SERVING') - - # check SrvKeyspace - self._check_srv_keyspace('ServedFrom(master): source_keyspace\n' - 'ServedFrom(rdonly): source_keyspace\n' - 'ServedFrom(replica): source_keyspace\n') - - # reparent to make the tablets work (we use health check, fix their types) - utils.run_vtctl(['InitShardMaster', '-force', 'source_keyspace/0', - source_master.tablet_alias], auto_log=True) - source_master.tablet_type = 'master' - utils.run_vtctl(['InitShardMaster', '-force', 'destination_keyspace/0', - destination_master.tablet_alias], auto_log=True) - destination_master.tablet_type = 'master' - - for t in [source_replica, destination_replica]: - utils.wait_for_tablet_type(t.tablet_alias, 'replica') - for t in [source_rdonly1, source_rdonly2, - destination_rdonly1, destination_rdonly2]: - utils.wait_for_tablet_type(t.tablet_alias, 'rdonly') - - for t in master_tablets + replica_tablets: - t.wait_for_vttablet_state('SERVING') - - def _create_source_schema(self): - create_table_template = '''create table %s( -id bigint not null, -msg varchar(64), -primary key (id), -index by_msg (msg) -) Engine=InnoDB''' - create_view_template = 'create view %s(id, msg) as select id, msg from %s' - # RBR only because Vitess requires the primary key for query rewrites if - # it is running with statement based replication. - create_moving3_no_pk_table = '''create table moving3_no_pk ( -id bigint not null, -msg varchar(64) -) Engine=InnoDB''' - - for t in [source_master, source_replica, source_rdonly1, source_rdonly2]: - t.create_db('vt_source_keyspace') - for n in ['moving1', 'moving2', 'staying1', 'staying2']: - t.mquery(source_master.dbname, create_table_template % (n)) - t.mquery(source_master.dbname, - create_view_template % ('view1', 'moving1')) - if base_sharding.use_rbr: - t.mquery(source_master.dbname, create_moving3_no_pk_table) - - for t in [destination_master, destination_replica, destination_rdonly1, - destination_rdonly2]: - t.create_db('vt_destination_keyspace') - t.mquery(destination_master.dbname, create_table_template % 'extra1') - - def _insert_initial_values(self): - self.moving1_first = self._insert_values('moving1', 100) - self.moving2_first = self._insert_values('moving2', 100) - staying1_first = self._insert_values('staying1', 100) - staying2_first = self._insert_values('staying2', 100) - self._check_values(source_master, 'vt_source_keyspace', 'moving1', - self.moving1_first, 100) - self._check_values(source_master, 'vt_source_keyspace', 'moving2', - self.moving2_first, 100) - self._check_values(source_master, 'vt_source_keyspace', 'staying1', - staying1_first, 100) - self._check_values(source_master, 'vt_source_keyspace', 'staying2', - staying2_first, 100) - self._check_values(source_master, 'vt_source_keyspace', 'view1', - self.moving1_first, 100) - - if base_sharding.use_rbr: - self.moving3_no_pk_first = self._insert_values('moving3_no_pk', 100) - self._check_values(source_master, 'vt_source_keyspace', 'moving3_no_pk', - self.moving3_no_pk_first, 100) - - # Insert data directly because vtgate would redirect us. - destination_master.mquery( - 'vt_destination_keyspace', - "insert into %s (id, msg) values(%d, 'value %d')" % ('extra1', 1, 1), - write=True) - self._check_values(destination_master, 'vt_destination_keyspace', 'extra1', - 1, 1) - - def _vtdb_conn(self): - protocol, addr = utils.vtgate.rpc_endpoint(python=True) - return vtgate_client.connect(protocol, addr, 30.0) - - # insert some values in the source master db, return the first id used - def _insert_values(self, table, count): - result = self.insert_index - conn = self._vtdb_conn() - cursor = conn.cursor( - tablet_type='master', keyspace='source_keyspace', - keyranges=[keyrange.KeyRange(keyrange_constants.NON_PARTIAL_KEYRANGE)], - writable=True) - for _ in xrange(count): - conn.begin() - cursor.execute("insert into %s (id, msg) values(%d, 'value %d')" % ( - table, self.insert_index, self.insert_index), {}) - conn.commit() - self.insert_index += 1 - conn.close() - return result - - def _check_values(self, t, dbname, table, first, count): - logging.debug( - 'Checking %d values from %s/%s starting at %d', count, dbname, - table, first) - rows = t.mquery( - dbname, 'select id, msg from %s where id>=%d order by id limit %d' % - (table, first, count)) - self.assertEqual(count, len(rows), 'got wrong number of rows: %d != %d' % - (len(rows), count)) - for i in xrange(count): - self.assertEqual(first + i, rows[i][0], 'invalid id[%d]: %d != %d' % - (i, first + i, rows[i][0])) - self.assertEqual('value %d' % (first + i), rows[i][1], - "invalid msg[%d]: 'value %d' != '%s'" % - (i, first + i, rows[i][1])) - - def _check_values_timeout(self, t, dbname, table, first, count, - timeout=30): - while True: - try: - self._check_values(t, dbname, table, first, count) - return - except Exception: # pylint: disable=broad-except - timeout -= 1 - if timeout == 0: - raise - logging.debug('Sleeping for 1s waiting for data in %s/%s', dbname, - table) - time.sleep(1) - - def _check_srv_keyspace(self, expected): - cell = 'test_nj' - keyspace = 'destination_keyspace' - ks = utils.run_vtctl_json(['GetSrvKeyspace', cell, keyspace]) - result = '' - if 'served_from' in ks and ks['served_from']: - a = [] - for served_from in sorted(ks['served_from']): - tt = topodata_pb2.TabletType.Name(served_from['tablet_type']).lower() - if tt == 'batch': - tt = 'rdonly' - a.append('ServedFrom(%s): %s\n' % (tt, served_from['keyspace'])) - for line in sorted(a): - result += line - logging.debug('Cell %s keyspace %s has data:\n%s', cell, keyspace, result) - self.assertEqual( - expected, result, - 'Mismatch in srv keyspace for cell %s keyspace %s, expected:\n' - '%s\ngot:\n%s' % ( - cell, keyspace, expected, result)) - self.assertEqual('', ks.get('sharding_column_name', ''), - 'Got a sharding_column_name in SrvKeyspace: %s' % - str(ks)) - self.assertEqual(0, ks.get('sharding_column_type', 0), - 'Got a sharding_column_type in SrvKeyspace: %s' % - str(ks)) - - def _check_blacklisted_tables(self, t, expected): - status = t.get_status() - if expected: - self.assertIn('BlacklistedTables: %s' % ' '.join(expected), status) - else: - self.assertNotIn('BlacklistedTables', status) - - # check we can or cannot access the tables - for table in ['moving1', 'moving2']: - if expected and '/moving/' in expected: - # table is blacklisted, should get the error - _, stderr = utils.run_vtctl(['VtTabletExecute', '-json', - t.tablet_alias, - 'select count(1) from %s' % table], - expect_fail=True) - self.assertIn( - 'disallowed due to rule: enforce blacklisted tables', - stderr) - else: - # table is not blacklisted, should just work - qr = t.execute('select count(1) from %s' % table) - logging.debug('Got %s rows from table %s on tablet %s', - qr['rows'][0][0], table, t.tablet_alias) - - def _check_client_conn_redirection( - self, destination_ks, servedfrom_db_types, - moved_tables=None): - # check that the ServedFrom indirection worked correctly. - if moved_tables is None: - moved_tables = [] - conn = self._vtdb_conn() - for db_type in servedfrom_db_types: - for tbl in moved_tables: - try: - rows = conn._execute( - 'select * from %s' % tbl, {}, tablet_type=db_type, - keyspace_name=destination_ks, - keyranges=[keyrange.KeyRange( - keyrange_constants.NON_PARTIAL_KEYRANGE)]) - logging.debug( - 'Select on %s.%s returned %d rows', db_type, tbl, len(rows)) - except Exception, e: # pylint: disable=broad-except - self.fail('Execute failed w/ exception %s' % str(e)) - - def _check_stats(self): - v = utils.vtgate.get_vars() - self.assertEqual( - v['VttabletCall']['Histograms']['Execute.source_keyspace.0.replica'][ - 'Count'], - 2 - , 'unexpected value for VttabletCall(' - 'Execute.source_keyspace.0.replica) inside %s' % str(v)) - # Verify master reads done by self._check_client_conn_redirection(). - self.assertEqual( - v['VtgateApi']['Histograms'][ - 'ExecuteKeyRanges.destination_keyspace.master']['Count'], - 6, - 'unexpected value for VtgateApi(' - 'ExecuteKeyRanges.destination_keyspace.master) inside %s' % str(v)) - self.assertEqual( - len(v['VtgateApiErrorCounts']), 0, - 'unexpected errors for VtgateApiErrorCounts inside %s' % str(v)) - - def test_vertical_split(self): - utils.run_vtctl(['CopySchemaShard', '--tables', '/moving/,view1', - source_rdonly1.tablet_alias, 'destination_keyspace/0'], - auto_log=True) - - utils.run_vtworker(['--cell', 'test_nj', - '--command_display_interval', '10ms', - '--use_v3_resharding_mode=false', - 'VerticalSplitClone', - '--tables', '/moving/,view1', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_tablets', '1', - 'destination_keyspace/0'], - auto_log=True) - - # test Cancel first - utils.run_vtctl(['CancelResharding', 'destination_keyspace/0'], auto_log=True) - self.check_no_binlog_player(destination_master) - # master should be in serving state after cancel - utils.check_tablet_query_service(self, destination_master, True, False) - - # redo VerticalSplitClone - utils.run_vtworker(['--cell', 'test_nj', - '--command_display_interval', '10ms', - '--use_v3_resharding_mode=false', - 'VerticalSplitClone', - '--tables', '/moving/,view1', - '--chunk_count', '10', - '--min_rows_per_chunk', '1', - '--min_healthy_tablets', '1', - 'destination_keyspace/0'], - auto_log=True) - - # check values are present - self._check_values(destination_master, 'vt_destination_keyspace', 'moving1', - self.moving1_first, 100) - self._check_values(destination_master, 'vt_destination_keyspace', 'moving2', - self.moving2_first, 100) - self._check_values(destination_master, 'vt_destination_keyspace', 'view1', - self.moving1_first, 100) - if base_sharding.use_rbr: - self._check_values(destination_master, 'vt_destination_keyspace', - 'moving3_no_pk', self.moving3_no_pk_first, 100) - - # Verify vreplication table entries - result = destination_master.mquery('_vt', 'select * from vreplication') - self.assertEqual(len(result), 1) - self.assertEqual(result[0][1], 'SplitClone') - self.assertEqual(result[0][2], - 'keyspace:"source_keyspace" shard:"0" tables:"/moving/" tables:"view1" ') - - # check the binlog player is running and exporting vars - self.check_destination_master(destination_master, ['source_keyspace/0']) - - # check that binlog server exported the stats vars - self.check_binlog_server_vars(source_replica, horizontal=False) - - # add values to source, make sure they're replicated - moving1_first_add1 = self._insert_values('moving1', 100) - _ = self._insert_values('staying1', 100) - moving2_first_add1 = self._insert_values('moving2', 100) - self._check_values_timeout(destination_master, 'vt_destination_keyspace', - 'moving1', moving1_first_add1, 100) - self._check_values_timeout(destination_master, 'vt_destination_keyspace', - 'moving2', moving2_first_add1, 100) - self.check_binlog_player_vars(destination_master, ['source_keyspace/0'], - seconds_behind_master_max=30) - self.check_binlog_server_vars(source_replica, horizontal=False, - min_statements=100, min_transactions=100) - - # use vtworker to compare the data - logging.debug('Running vtworker VerticalSplitDiff') - utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'VerticalSplitDiff', - '--min_healthy_rdonly_tablets', '1', - 'destination_keyspace/0'], auto_log=True) - - utils.pause('Good time to test vtworker for diffs') - - # get status for destination master tablet, make sure we have it all - self.check_running_binlog_player(destination_master, 700, 300, - extra_text='moving') - - # check query service is off on destination master, as filtered - # replication is enabled. Even health check should not interfere. - destination_master_vars = utils.get_vars(destination_master.port) - self.assertEqual(destination_master_vars['TabletStateName'], 'NOT_SERVING') - - # check we can't migrate the master just yet - utils.run_vtctl(['MigrateServedFrom', 'destination_keyspace/0', 'master'], - expect_fail=True) - - # migrate rdonly only in test_ny cell, make sure nothing is migrated - # in test_nj - utils.run_vtctl(['MigrateServedFrom', '--cells=test_ny', - 'destination_keyspace/0', 'rdonly'], - auto_log=True) - self._check_srv_keyspace('ServedFrom(master): source_keyspace\n' - 'ServedFrom(rdonly): source_keyspace\n' - 'ServedFrom(replica): source_keyspace\n') - self._check_blacklisted_tables(source_master, None) - self._check_blacklisted_tables(source_replica, None) - self._check_blacklisted_tables(source_rdonly1, None) - self._check_blacklisted_tables(source_rdonly2, None) - - # migrate test_nj only, using command line manual fix command, - # and restore it back. - keyspace_json = utils.run_vtctl_json( - ['GetKeyspace', 'destination_keyspace']) - found = False - for ksf in keyspace_json['served_froms']: - if ksf['tablet_type'] == topodata_pb2.RDONLY: - found = True - self.assertEqual(sorted(ksf['cells']), ['test_ca', 'test_nj']) - self.assertTrue(found) - utils.run_vtctl(['SetKeyspaceServedFrom', '-source=source_keyspace', - '-remove', '-cells=test_nj,test_ca', 'destination_keyspace', - 'rdonly'], auto_log=True) - keyspace_json = utils.run_vtctl_json( - ['GetKeyspace', 'destination_keyspace']) - found = False - for ksf in keyspace_json['served_froms']: - if ksf['tablet_type'] == topodata_pb2.RDONLY: - found = True - self.assertFalse(found) - utils.run_vtctl(['SetKeyspaceServedFrom', '-source=source_keyspace', - 'destination_keyspace', 'rdonly'], - auto_log=True) - keyspace_json = utils.run_vtctl_json( - ['GetKeyspace', 'destination_keyspace']) - found = False - for ksf in keyspace_json['served_froms']: - if ksf['tablet_type'] == topodata_pb2.RDONLY: - found = True - self.assertTrue('cells' not in ksf or not ksf['cells']) - self.assertTrue(found) - - # now serve rdonly from the destination shards - utils.run_vtctl(['MigrateServedFrom', 'destination_keyspace/0', 'rdonly'], - auto_log=True) - self._check_srv_keyspace('ServedFrom(master): source_keyspace\n' - 'ServedFrom(replica): source_keyspace\n') - self._check_blacklisted_tables(source_master, None) - self._check_blacklisted_tables(source_replica, None) - self._check_blacklisted_tables(source_rdonly1, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly2, ['/moving/', 'view1']) - self._check_client_conn_redirection( - 'destination_keyspace', - ['master', 'replica'], ['moving1', 'moving2']) - - # then serve replica from the destination shards - utils.run_vtctl(['MigrateServedFrom', 'destination_keyspace/0', 'replica'], - auto_log=True) - self._check_srv_keyspace('ServedFrom(master): source_keyspace\n') - self._check_blacklisted_tables(source_master, None) - self._check_blacklisted_tables(source_replica, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly1, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly2, ['/moving/', 'view1']) - self._check_client_conn_redirection( - 'destination_keyspace', - ['master'], ['moving1', 'moving2']) - - # move replica back and forth - utils.run_vtctl(['MigrateServedFrom', '-reverse', - 'destination_keyspace/0', 'replica'], auto_log=True) - self._check_srv_keyspace('ServedFrom(master): source_keyspace\n' - 'ServedFrom(replica): source_keyspace\n') - self._check_blacklisted_tables(source_master, None) - self._check_blacklisted_tables(source_replica, None) - self._check_blacklisted_tables(source_rdonly1, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly2, ['/moving/', 'view1']) - utils.run_vtctl(['MigrateServedFrom', 'destination_keyspace/0', 'replica'], - auto_log=True) - self._check_srv_keyspace('ServedFrom(master): source_keyspace\n') - self._check_blacklisted_tables(source_master, None) - self._check_blacklisted_tables(source_replica, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly1, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly2, ['/moving/', 'view1']) - self._check_client_conn_redirection( - 'destination_keyspace', - ['master'], ['moving1', 'moving2']) - - # Cancel should fail now - utils.run_vtctl(['CancelResharding', 'destination_keyspace/0'], - auto_log=True, expect_fail=True) - - # then serve master from the destination shards - utils.run_vtctl(['MigrateServedFrom', 'destination_keyspace/0', 'master'], - auto_log=True) - self._check_srv_keyspace('') - self._check_blacklisted_tables(source_master, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_replica, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly1, ['/moving/', 'view1']) - self._check_blacklisted_tables(source_rdonly2, ['/moving/', 'view1']) - - # check the binlog player is gone now - self.check_no_binlog_player(destination_master) - - # check the stats are correct - self._check_stats() - - # now remove the tables on the source shard. The blacklisted tables - # in the source shard won't match any table, make sure that works. - utils.run_vtctl(['ApplySchema', - '-sql=drop view view1', - 'source_keyspace'], - auto_log=True) - for t in ['moving1', 'moving2']: - utils.run_vtctl(['ApplySchema', - '-sql=drop table %s' % (t), - 'source_keyspace'], - auto_log=True) - for t in [source_master, source_replica, source_rdonly1, source_rdonly2]: - utils.run_vtctl(['ReloadSchema', t.tablet_alias]) - qr = source_master.execute('select count(1) from staying1') - self.assertEqual(len(qr['rows']), 1, - 'cannot read staying1: got %s' % str(qr)) - - # test SetShardTabletControl - self._verify_vtctl_set_shard_tablet_control() - - def _verify_vtctl_set_shard_tablet_control(self): - """Test that manually editing the blacklisted tables works correctly. - - TODO(mberlin): This is more an integration test and should be moved to the - Go codebase eventually. - """ - # check 'vtctl SetShardTabletControl' command works as expected: - # clear the rdonly entry: - utils.run_vtctl(['SetShardTabletControl', '--remove', 'source_keyspace/0', - 'rdonly'], auto_log=True) - self._assert_tablet_controls([topodata_pb2.MASTER, topodata_pb2.REPLICA]) - - # re-add rdonly: - utils.run_vtctl(['SetShardTabletControl', - '--blacklisted_tables=/moving/,view1', - 'source_keyspace/0', 'rdonly'], auto_log=True) - self._assert_tablet_controls([topodata_pb2.MASTER, topodata_pb2.REPLICA, - topodata_pb2.RDONLY]) - - # and then clear all entries: - utils.run_vtctl(['SetShardTabletControl', '--remove', 'source_keyspace/0', - 'rdonly'], auto_log=True) - utils.run_vtctl(['SetShardTabletControl', '--remove', 'source_keyspace/0', - 'replica'], auto_log=True) - utils.run_vtctl(['SetShardTabletControl', '--remove', 'source_keyspace/0', - 'master'], auto_log=True) - shard_json = utils.run_vtctl_json(['GetShard', 'source_keyspace/0']) - self.assertTrue('tablet_controls' not in shard_json or - not shard_json['tablet_controls']) - - def _assert_tablet_controls(self, expected_dbtypes): - shard_json = utils.run_vtctl_json(['GetShard', 'source_keyspace/0']) - self.assertEqual(len(shard_json['tablet_controls']), len(expected_dbtypes)) - - expected_dbtypes_set = set(expected_dbtypes) - for tc in shard_json['tablet_controls']: - self.assertIn(tc['tablet_type'], expected_dbtypes_set) - self.assertEqual(['/moving/', 'view1'], tc['blacklisted_tables']) - expected_dbtypes_set.remove(tc['tablet_type']) - self.assertEqual(0, len(expected_dbtypes_set), - 'Not all expected db types were blacklisted') - - -if __name__ == '__main__': - utils.main() diff --git a/test/vertical_split_rbr.py b/test/vertical_split_rbr.py deleted file mode 100755 index dc332dc5189..00000000000 --- a/test/vertical_split_rbr.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Re-runs resharding.py with RBR on.""" - -import base_sharding -import vertical_split -import utils - -if __name__ == '__main__': - base_sharding.use_rbr = True - utils.main(vertical_split) diff --git a/test/vschema.py b/test/vschema.py deleted file mode 100755 index ed138ddce07..00000000000 --- a/test/vschema.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests vschema creation and manipulation -""" - -import unittest - -import environment -import logging -import tablet -import utils -import keyspace_util - -from vtdb import dbexceptions -from vtdb import vtgate_cursor -from vtdb import vtgate_client - -shard_0_master = None - -keyspace_env = None - -create_vt_user = '''create table vt_user ( -id bigint, -name varchar(64), -primary key (id) -) Engine=InnoDB''' - -create_main = '''create table main ( -id bigint, -val varchar(128), -primary key(id) -) Engine=InnoDB''' - -def setUpModule(): - global keyspace_env - global shard_0_master - - try: - environment.topo_server().setup() - keyspace_env = keyspace_util.TestEnv() - keyspace_env.launch( - 'user', - ddls=[ - create_vt_user, - create_main, - ] - ) - shard_0_master = keyspace_env.tablet_map['user.0.master'] - utils.VtGate().start( - tablets=[shard_0_master], - extra_args=['-vschema_ddl_authorized_users','%'], - ) - utils.vtgate.wait_for_endpoints('user.0.master', 1) - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - if keyspace_env: - keyspace_env.teardown() - - environment.topo_server().teardown() - - utils.kill_sub_processes() - utils.remove_tmp_files() - -def get_connection(timeout=10.0): - protocol, endpoint = utils.vtgate.rpc_endpoint(python=True) - try: - return vtgate_client.connect(protocol, endpoint, timeout) - except Exception: - logging.exception('Connection to vtgate (timeout=%s) failed.', timeout) - raise - -class TestDDLVSchema(unittest.TestCase): - - decimal_type = 18 - int_type = 265 - string_type = 6165 - varbinary_type = 10262 - - def _test_queries(self,cursor,count=4): - for x in xrange(count): - i = x+1 - cursor.begin() - cursor.execute( - 'insert into vt_user (id,name) values (:id,:name)', - {'id': i, 'name': 'test %s' % i}) - cursor.commit() - - # Test select equal - for x in xrange(count): - i = x+1 - cursor.execute('select id, name from vt_user where id = :id', {'id': i}) - self.assertEqual( - (cursor.fetchall(), cursor.rowcount, cursor.lastrowid,cursor.description), - ([(i, 'test %s' % i)], 1L, 0,[('id', self.int_type), ('name', self.string_type)])) - - cursor.begin() - cursor.execute( - 'DELETE FROM vt_user', - {} - ) - cursor.commit() - - def _read_vschema(self, cursor): - # Test Showing Tables - cursor.execute( - 'SHOW VSCHEMA TABLES',{} - ) - self.assertEqual( - [ x[0] for x in cursor.fetchall() ], - [ 'dual', 'main', 'vt_user' ], - ) - - # Test Showing Vindexes - cursor.execute( - 'SHOW VSCHEMA VINDEXES',{} - ) - self.assertEqual( - [ x[0] for x in cursor.fetchall() ], - [ ], - ) - - def _create_vschema(self,cursor): - cursor.begin() - cursor.execute( - 'ALTER VSCHEMA ADD TABLE vt_user',{} - ) - cursor.execute( - 'ALTER VSCHEMA ADD TABLE main',{} - ) - cursor.commit() - - def test_unsharded_vschema(self): - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=None, writable=True) - # Test the blank database with no vschema - self._test_queries(cursor) - - # Use the DDL to create an unsharded vschema and test again - self._create_vschema(cursor) - self._read_vschema(cursor) - self._test_queries(cursor) - -if __name__ == '__main__': - utils.main() diff --git a/test/vtbackup.py b/test/vtbackup.py deleted file mode 100644 index fecaca6612d..00000000000 --- a/test/vtbackup.py +++ /dev/null @@ -1,223 +0,0 @@ -# Copyright 2019 The Vitess Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Manage VTBackup during test.""" - -import os -import sys -import logging -import shutil - -import environment -from mysql_flavor import mysql_flavor -import utils - -tablet_cell_map = { - 62344: 'nj', - 62044: 'nj', - 41983: 'nj', - 31981: 'ny', -} - -def get_backup_storage_flags(): - return ['-backup_storage_implementation', 'file', - '-file_backup_storage_root', - os.path.join(environment.tmproot, 'backupstorage')] - -def get_all_extra_my_cnf(extra_my_cnf): - all_extra_my_cnf = [environment.vtroot + '/config/mycnf/default-fast.cnf'] - flavor_my_cnf = mysql_flavor().extra_my_cnf() - if flavor_my_cnf: - all_extra_my_cnf.append(flavor_my_cnf) - if extra_my_cnf: - all_extra_my_cnf.append(extra_my_cnf) - return all_extra_my_cnf - - -class Vtbackup(object): - """This class helps manage a vtbackup instance. - - To use it for vtbackup, you need to use init_tablet and/or - start_vtbackup. - """ - default_uid = 65534 - seq = 0 - tablets_running = 0 - - def __init__(self, tablet_uid=None, cell=None, vt_dba_passwd=None, - mysql_port=None): - self.tablet_uid = tablet_uid or (Vtbackup.default_uid + Vtbackup.seq) - self.mysql_port = mysql_port or (environment.reserve_ports(1)) - self.vt_dba_passwd = vt_dba_passwd - Vtbackup.seq += 1 - - if cell: - self.cell = cell - else: - self.cell = tablet_cell_map.get(tablet_uid, 'nj') - self.proc = None - - # filled in during init_mysql - self.mysqlctld_process = None - - # filled in during init_tablet - self.keyspace = None - self.shard = None - self.index = None - self.tablet_index = None - self.dbname = None - # default to false - self.external_mysql = False - # utility variables - self.tablet_alias = 'test_%s-%010d' % (self.cell, self.tablet_uid) - - def __str__(self): - return 'vtbackup: uid: %d' % (self.tablet_uid) - - def init_backup(self, keyspace, shard, - tablet_index=None, - start=False, dbname=None, parent=True, wait_for_start=True, - include_mysql_port=True, **kwargs): - """Initialize a vtbackup.""" - - self.keyspace = keyspace - self.shard = shard - self.tablet_index = tablet_index - - self.dbname = dbname or ('vt_' + self.keyspace) - - if start: - self.start_vtbackup(**kwargs) - - @property - def tablet_dir(self): - return '%s/vt_%010d' % (environment.vtdataroot, self.tablet_uid) - - def start_vtbackup(self, - full_mycnf_args=False, - extra_args=None, extra_env=None, include_mysql_port=True, - init_keyspace=None, init_shard=None, - tablet_index=None, - init_db=None, - init_db_name_override=None, - initial_backup=False): - # pylint: disable=g-doc-args - """Starts a vtprocess process, and returns it. - - The process is also saved in self.proc, so it's easy to kill as well. - - Returns: - the process that was started. - """ - # pylint: enable=g-doc-args - - args = environment.binary_args('vtbackup') - # Use 'localhost' as hostname because Travis CI worker hostnames - # are too long for MySQL replication. - args.extend(environment.topo_server().flags()) - args.extend(['-pid_file', os.path.join(self.tablet_dir, 'vtbackup.pid')]) - # always enable_replication_reporter with somewhat short values for tests - - # init_db_sql_file is required to run vtbackup - args.extend([ - "-mysql_port",str(self.mysql_port), - "-init_db_sql_file",init_db - ]) - - # Pass through initial_backup flag to vtbackup - if initial_backup: - args.extend(["-initial_backup"]) - - if full_mycnf_args: - # this flag is used to specify all the mycnf_ flags, to make - # sure that code works. - relay_log_path = os.path.join(self.tablet_dir, 'relay-logs', - 'vt-%010d-relay-bin' % self.tablet_uid) - args.extend([ - '-mycnf_server_id', str(self.tablet_uid), - '-mycnf_data_dir', os.path.join(self.tablet_dir, 'data'), - '-mycnf_innodb_data_home_dir', os.path.join(self.tablet_dir, - 'innodb', 'data'), - '-mycnf_innodb_log_group_home_dir', os.path.join(self.tablet_dir, - 'innodb', 'logs'), - '-mycnf_socket_file', os.path.join(self.tablet_dir, 'mysql.sock'), - '-mycnf_error_log_path', os.path.join(self.tablet_dir, 'error.log'), - '-mycnf_slow_log_path', os.path.join(self.tablet_dir, - 'slow-query.log'), - '-mycnf_relay_log_path', relay_log_path, - '-mycnf_relay_log_index_path', relay_log_path + '.index', - '-mycnf_relay_log_info_path', os.path.join(self.tablet_dir, - 'relay-logs', - 'relay-log.info'), - '-mycnf_bin_log_path', os.path.join( - self.tablet_dir, 'bin-logs', 'vt-%010d-bin' % self.tablet_uid), - '-mycnf_master_info_file', os.path.join(self.tablet_dir, - 'master.info'), - '-mycnf_pid_file', os.path.join(self.tablet_dir, 'mysql.pid'), - '-mycnf_tmp_dir', os.path.join(self.tablet_dir, 'tmp'), - '-mycnf_slave_load_tmp_dir', os.path.join(self.tablet_dir, 'tmp'), - ]) - if include_mysql_port: - args.extend(['-mycnf_mysql_port', str(self.mysql_port)]) - - if init_keyspace: - self.keyspace = init_keyspace - self.shard = init_shard - # tablet_index is required for the update_addr call below. - if self.tablet_index is None: - self.tablet_index = tablet_index - args.extend(['-init_keyspace', init_keyspace, - '-init_shard', init_shard]) - if init_db_name_override: - self.dbname = init_db_name_override - args.extend(['-init_db_name_override', init_db_name_override]) - else: - self.dbname = 'vt_' + init_keyspace - - # Backup Arguments are not optional - args.extend(get_backup_storage_flags()) - - if extra_args: - args.extend(extra_args) - - args.extend(['-log_dir', environment.vtlogroot]) - - stderr_fd = open( - os.path.join(environment.vtlogroot, 'vtbackup-%d.stderr' % - self.tablet_uid), 'w') - # increment count only the first time - if not self.proc: - Vtbackup.tablets_running += 1 - self.proc = utils.run_bg(args, stderr=stderr_fd, extra_env=extra_env) - - log_message = ( - 'Started vtbackup: %s (%s) with pid: %s - Log files: ' - '%s/vtbackup.*.{INFO,WARNING,ERROR,FATAL}.*.%s' % - (self.tablet_uid, self.tablet_alias, self.proc.pid, - environment.vtlogroot, self.proc.pid)) - # This may race with the stderr output from the process (though - # that's usually empty). - stderr_fd.write(log_message + '\n') - stderr_fd.close() - logging.debug(log_message) - - return self.proc - - def remove_tree(self, ignore_options=False): - if not ignore_options and utils.options.keep_logs: - return - try: - shutil.rmtree(self.tablet_dir) - except OSError as e: - if utils.options.verbose == 2: - print >> sys.stderr, e, self.tablet_dir diff --git a/test/vtctld_test.py b/test/vtctld_test.py deleted file mode 100755 index e481ce16f55..00000000000 --- a/test/vtctld_test.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -import logging -import re -import unittest -import urllib2 - -from vtctl import vtctl_client - -import environment -import tablet -import utils - - -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -# range 80 - '' -shard_1_master = tablet.Tablet() -shard_1_replica = tablet.Tablet() -# all tablets -tablets = [shard_0_master, shard_0_replica, shard_1_master, shard_1_replica] - -select_one_table_output = """ -+---+ -| a | -+---+ -| 1 | -+---+ -""".lstrip() - - -def setUpModule(): - try: - environment.topo_server().setup() - - setup_procs = [t.init_mysql() for t in tablets] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in tablets] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - for t in tablets: - t.remove_tree() - - -class TestVtctld(unittest.TestCase): - - @classmethod - def setUpClass(cls): - utils.run_vtctl(['CreateKeyspace', - '--sharding_column_name', 'keyspace_id', - '--sharding_column_type', 'uint64', - 'test_keyspace']) - utils.run_vtctl( - ['CreateKeyspace', '--served_from', - 'master:test_keyspace,replica:test_keyspace,rdonly:test_keyspace', - 'redirected_keyspace']) - - shard_0_master.init_tablet('replica', 'test_keyspace', '-80') - shard_0_replica.init_tablet('replica', 'test_keyspace', '-80') - shard_1_master.init_tablet('replica', 'test_keyspace', '80-') - shard_1_replica.init_tablet('replica', 'test_keyspace', '80-') - - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - utils.run_vtctl(['RebuildKeyspaceGraph', 'redirected_keyspace'], - auto_log=True) - - # start running all the tablets - for t in [shard_0_master, shard_1_master, shard_1_replica]: - t.create_db('vt_test_keyspace') - t.start_vttablet(wait_for_state=None, - extra_args=utils.vtctld.process_args()) - shard_0_replica.create_db('vt_test_keyspace') - shard_0_replica.start_vttablet(extra_args=utils.vtctld.process_args(), - wait_for_state=None) - - # wait for the right states - for t in [shard_0_master, shard_1_master, shard_0_replica, shard_1_replica]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', 'test_keyspace/80-', - shard_1_master.tablet_alias], auto_log=True) - shard_0_replica.wait_for_vttablet_state('SERVING') - - # run checks now - utils.validate_topology() - - def test_api(self): - # for manual tests - utils.pause('Now is a good time to look at vtctld UI at: ' - 'http://localhost:%d/' % utils.vtctld.port) - - # test root works - url = 'http://localhost:%d/api/topodata/' % utils.vtctld.port - f = urllib2.urlopen(url) - root = f.read() - f.close() - result = json.loads(root) - self.assertEqual(result['Error'], '') - self.assertIn('global', result['Children']) - self.assertIn('test_ca', result['Children']) - self.assertIn('test_nj', result['Children']) - self.assertIn('test_ny', result['Children']) - - def test_health_check(self): - url = 'http://localhost:%d/debug/health' % utils.vtctld.port - f = urllib2.urlopen(url) - body = f.read() - f.close() - # test body response for health check is ok - self.assertEqual(body, 'ok') - - def _check_all_tablets(self, result): - lines = result.splitlines() - self.assertEqual(len(lines), len(tablets), 'got lines:\n%s' % lines) - line_map = {} - for line in lines: - parts = line.split() - alias = parts[0] - line_map[alias] = parts - for t in tablets: - if t.tablet_alias not in line_map: - self.assertFalse('tablet %s is not in the result: %s' % ( - t.tablet_alias, str(line_map))) - - def test_vtctl(self): - # standalone RPC client to vtctld - out, _ = utils.run_vtctl( - ['ListAllTablets', 'test_nj'], mode=utils.VTCTL_VTCTLCLIENT) - self._check_all_tablets(out) - - # vtctl querying the topology directly - out, _ = utils.run_vtctl(['ListAllTablets', 'test_nj'], - mode=utils.VTCTL_VTCTL, - trap_output=True, auto_log=True) - self._check_all_tablets(out) - - # python RPC client to vtctld - out, _ = utils.run_vtctl(['ListAllTablets', 'test_nj'], - mode=utils.VTCTL_RPC) - self._check_all_tablets(out) - - def test_tablet_status(self): - # the vttablet that has a health check has a bit more, so using it - shard_0_replica_status = shard_0_replica.get_status() - self.assertTrue( - re.search(r'Polling health information from.+MySQLReplicationLag', - shard_0_replica_status)) - self.assertIn('Alias: = instead of == because we can miss the exact point due to - # slow thread scheduling. - with self.notify_lock: - if (self.notify_after_n_successful_rpcs != 0 and - self.rpcs >= (self.notify_after_n_successful_rpcs + - self.rpcs_so_far)): - self.wait_for_notification.put(True) - self.notify_after_n_successful_rpcs = 0 - except Exception as e: # pylint: disable=broad-except - self.errors += 1 - logging.debug('thread: %s query failed: %s', self.name, str(e)) - - # Wait 10ms seconds between two attempts. - time.sleep(0.01) - - def execute(self, cursor): - raise NotImplementedError('Child class needs to implement this') - - def set_notify_after_n_successful_rpcs(self, n): - with self.notify_lock: - self.notify_after_n_successful_rpcs = n - self.rpcs_so_far = self.rpcs - - def stop(self): - self.quit = True - - -class ReadThread(AbstractVtgateThread): - - def __init__(self, vtgate): - super(ReadThread, self).__init__(vtgate, 'ReadThread') - - def execute(self, cursor): - row_count = cursor.execute('SELECT * FROM buffer WHERE id = :id', - {'id': CRITICAL_READ_ROW_ID}) - logging.debug('read returned %d row(s).', row_count) - - -class UpdateThread(AbstractVtgateThread): - - def __init__(self, vtgate, ignore_error_func=None): - self.ignore_error_func = ignore_error_func - # Value used in next UPDATE query. Increased after every query. - self.i = 1 - self._commit_errors = 0 - super(UpdateThread, self).__init__(vtgate, 'UpdateThread', writable=True) - - def execute(self, cursor): - attempt = self.i - self.i += 1 - try: - commit_started = False - cursor.begin() - # Do not use a bind variable for "msg" to make sure that the value shows - # up in the logs. - row_count = cursor.execute('UPDATE buffer SET msg=\'update %d\' ' - 'WHERE id = :id' % attempt, - {'id': UPDATE_ROW_ID}) - - # Sleep between [0, 1] seconds to prolong the time the transaction is in - # flight. This is more realistic because applications are going to keep - # their transactions open for longer as well. - time.sleep(random.randint(0, 1000) / 1000.0) - - commit_started = True - cursor.commit() - logging.debug('UPDATE %d affected %d row(s).', attempt, row_count) - except Exception as e: # pylint: disable=broad-except - try: - # Rollback to free the transaction in vttablet. - cursor.rollback() - except Exception as e: # pylint: disable=broad-except - logging.warn('rollback failed: %s', str(e)) - - if not commit_started: - logging.debug('UPDATE %d failed before COMMIT. This should not happen.' - ' Re-raising exception.', attempt) - raise - - if self.ignore_error_func and self.ignore_error_func(e): - logging.debug('UPDATE %d failed during COMMIT. But we cannot buffer' - ' this error and ignore it. err: %s', attempt, str(e)) - else: - self._commit_errors += 1 - if self._commit_errors > 1: - raise - logging.debug('UPDATE %d failed during COMMIT. This is okay once' - ' because we do not support buffering it. err: %s', - attempt, str(e)) - - def commit_errors(self): - return self._commit_errors - -master = tablet.Tablet() -replica = tablet.Tablet() -all_tablets = [master, replica] - - -def setUpModule(): - try: - environment.topo_server().setup() - - setup_procs = [t.init_mysql() for t in all_tablets] - utils.Vtctld().start() - utils.wait_procs(setup_procs) - - utils.run_vtctl(['CreateKeyspace', KEYSPACE]) - - # Start tablets. - db_name = 'vt_' + KEYSPACE - for t in all_tablets: - t.create_db(db_name) - master.start_vttablet(wait_for_state=None, - init_tablet_type='replica', - init_keyspace=KEYSPACE, init_shard=SHARD, - tablet_index=0) - replica.start_vttablet(wait_for_state=None, - init_tablet_type='replica', - init_keyspace=KEYSPACE, init_shard=SHARD, - tablet_index=1) - for t in all_tablets: - t.wait_for_vttablet_state('NOT_SERVING') - - # Reparent to choose an initial master and enable replication. - utils.run_vtctl(['InitShardMaster', '-force', '%s/%s' % (KEYSPACE, SHARD), - master.tablet_alias]) - - # Create the schema. - utils.run_vtctl(['ApplySchema', '-sql=' + SCHEMA, KEYSPACE]) - - start_vtgate() - - # Insert two rows for the later threads (critical read, update). - with utils.vtgate.write_transaction(keyspace=KEYSPACE, shards=[SHARD], - tablet_type='master') as tx: - tx.execute('INSERT INTO buffer (id, msg) VALUES (:id, :msg)', - {'id': CRITICAL_READ_ROW_ID, 'msg': 'critical read'}) - tx.execute('INSERT INTO buffer (id, msg) VALUES (:id, :msg)', - {'id': UPDATE_ROW_ID, 'msg': 'update'}) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [t.teardown_mysql() for t in [master, replica]] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - for t in all_tablets: - t.remove_tree() - - -def start_vtgate(): - utils.VtGate().start(extra_args=[ - '-enable_buffer', - # Long timeout in case failover is slow. - '-buffer_window', '10m', - '-buffer_max_failover_duration', '10m', - '-buffer_min_time_between_failovers', '20m'], - tablets=all_tablets) - - -class TestBufferBase(unittest.TestCase): - - def _test_buffer(self, reparent_func, enable_read_thread=True, - ignore_error_func=None): - # Start both threads. - if enable_read_thread: - read_thread = ReadThread(utils.vtgate) - else: - logging.debug('ReadThread explicitly disabled in this test.') - update_thread = UpdateThread(utils.vtgate, ignore_error_func) - - try: - # Verify they got at least 2 RPCs through. - if enable_read_thread: - read_thread.set_notify_after_n_successful_rpcs(2) - update_thread.set_notify_after_n_successful_rpcs(2) - if enable_read_thread: - read_thread.wait_for_notification.get() - update_thread.wait_for_notification.get() - - # Execute the failover. - if enable_read_thread: - read_thread.set_notify_after_n_successful_rpcs(10) - update_thread.set_notify_after_n_successful_rpcs(10) - - reparent_func() - - # Failover is done. Swap master and replica for the next test. - global master, replica - master, replica = replica, master - - if enable_read_thread: - read_thread.wait_for_notification.get() - update_thread.wait_for_notification.get() - except: - # Something went wrong. Kill vtgate first to unblock any buffered requests - # which would further block the two threads. - utils.vtgate.kill() - raise - finally: - # Stop threads. - if enable_read_thread: - read_thread.stop() - update_thread.stop() - if enable_read_thread: - read_thread.join() - update_thread.join() - - # Both threads must not see any error. - if enable_read_thread: - self.assertEqual(0, read_thread.errors) - self.assertEqual(0, update_thread.errors) - # At least one thread should have been buffered. - # TODO(mberlin): This may fail if a failover is too fast. Add retries then. - v = utils.vtgate.get_vars() - labels = '%s.%s' % (KEYSPACE, SHARD) - in_flight_max = v['BufferLastRequestsInFlightMax'].get(labels, 0) - if in_flight_max == 0: - # Missed buffering is okay when we observed the failover during the - # COMMIT (which cannot trigger the buffering). - self.assertGreater(update_thread.commit_errors(), 0, - 'No buffering took place and the update thread saw no' - ' error during COMMIT. But one of it must happen.') - else: - self.assertGreater(in_flight_max, 0) - - # There was a failover and the HealthCheck module must have seen it. - master_promoted_count = v['HealthcheckMasterPromoted'].get(labels, 0) - self.assertGreater(master_promoted_count, 0) - - duration_ms = v['BufferFailoverDurationSumMs'].get(labels, 0) - if duration_ms > 0: - # Buffering was actually started. - logging.debug('Failover was buffered for %d milliseconds.', duration_ms) - # Number of buffering stops must be equal to the number of seen failovers. - buffering_stops = v['BufferStops'].get('%s.NewMasterSeen' % labels, 0) - self.assertEqual(master_promoted_count, buffering_stops) - - def external_reparent(self): - # Demote master. - start = time.time() - master.mquery('', mysql_flavor().demote_master_commands(), log_query=True) - if master.semi_sync_enabled(): - master.set_semi_sync_enabled(master=False) - - # Wait for replica to catch up to master. - utils.wait_for_replication_pos(master, replica) - - # Wait for at least one second to artificially prolong the failover and give - # the buffer a chance to observe it. - d = time.time() - start - min_unavailability_s = 1 - if d < min_unavailability_s: - w = min_unavailability_s - d - logging.debug('Waiting for %.1f seconds because the failover was too fast' - ' (took only %.3f seconds)', w, d) - time.sleep(w) - - # Promote replica to new master. - replica.mquery('', mysql_flavor().promote_slave_commands(), - log_query=True) - if replica.semi_sync_enabled(): - replica.set_semi_sync_enabled(master=True) - old_master = master - new_master = replica - - # Configure old master to use new master. - new_pos = mysql_flavor().master_position(new_master) - logging.debug('New master position: %s', str(new_pos)) - # Use 'localhost' as hostname because Travis CI worker hostnames - # are too long for MySQL replication. - change_master_cmds = mysql_flavor().change_master_commands( - 'localhost', new_master.mysql_port, new_pos) - old_master.mquery('', ['RESET SLAVE'] + change_master_cmds + - ['START SLAVE'], log_query=True) - - # Notify the new vttablet master about the reparent. - utils.run_vtctl(['TabletExternallyReparented', new_master.tablet_alias], - auto_log=True) - - # Wait until the old master becomes a replica before returning. - # Otherwise, that time counts against the timeout of the next test. - # Set the timeout well above 30, because it takes 30 seconds just to cancel - # stuck transactions when demoting the master. - timeout = 60.0 - while True: - try: - health = utils.run_vtctl_json(['VtTabletStreamHealth', - '-count', '1', - old_master.tablet_alias]) - if health['target']['tablet_type'] == topodata_pb2.REPLICA: - break - except: - pass - timeout = utils.wait_step('old master becomes replica', timeout, - sleep_time=1.0) - - -class TestBuffer(TestBufferBase): - - def setUp(self): - utils.vtgate.kill() - # Restart vtgate between each test or the feature - # --buffer_min_time_between_failovers - # will ignore subsequent failovers. - start_vtgate() - - def test_buffer_planned_reparent(self): - def planned_reparent(): - utils.run_vtctl(['PlannedReparentShard', '-keyspace_shard', - '%s/%s' % (KEYSPACE, SHARD), - '-new_master', replica.tablet_alias], auto_log=True) - self._test_buffer(planned_reparent) - - def test_buffer_external_reparent(self): - self._test_buffer(self.external_reparent) - -if __name__ == '__main__': - utils.main() diff --git a/test/vtgate_gateway_flavor/__init__.py b/test/vtgate_gateway_flavor/__init__.py deleted file mode 100644 index 35bf136ccc5..00000000000 --- a/test/vtgate_gateway_flavor/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/test/vtgate_gateway_flavor/discoverygateway.py b/test/vtgate_gateway_flavor/discoverygateway.py deleted file mode 100644 index 79c20ea2158..00000000000 --- a/test/vtgate_gateway_flavor/discoverygateway.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Contain VTGate discovery gateway flavor.""" - -import gateway - - -class DiscoveryGateway(gateway.VTGateGateway): - """Overrides to use discovery gateway.""" - - def flags(self, cell=None, tablets=None): - """Return a list of args that tell a VTGate process to start with.""" - return [ - '-cells_to_watch', cell, - '-tablet_refresh_interval', '2s', - ] - - def connection_count_vars(self): - """Return the vars name containing the number of serving connections.""" - return 'HealthcheckConnections' - - def no_tablet_found_message(self): - """Return the text message that appears in the gateway.""" - return 'no valid tablet' - - -gateway.register_flavor('discoverygateway', DiscoveryGateway) diff --git a/test/vtgate_gateway_flavor/gateway.py b/test/vtgate_gateway_flavor/gateway.py deleted file mode 100644 index 5ab8f4759eb..00000000000 --- a/test/vtgate_gateway_flavor/gateway.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Contain abstract VTGate gateway flavor.""" - -import logging - - -class VTGateGateway(object): - """Base class that defines the required interface.""" - - def flags(self, cell=None, tablets=None): - """Return a list of args that tell a VTGate process to start with.""" - raise NotImplementedError() - - def connection_count_vars(self): - """Return the vars name containing the number of serving connections.""" - raise NotImplementedError() - - def no_tablet_found_message(self): - """Return the text message that appears in the gateway. - - When we ask a gateway implementation to perform an operation and - there is no available tablet for it, this string will appear in - the error message. - """ - raise NotImplementedError() - - def flavor(self): - """Return the name of this topo server flavor.""" - return self.flavor_name - -flavor_map = {} - -_gateway = None - - -def vtgate_gateway_flavor(): - """Return the VTGate gateway flavor instance.""" - return _gateway - - -def set_vtgate_gateway_flavor(flavor): - """Set the VTGate gateway flavor to be used.""" - global _gateway - - if not flavor: - flavor = "discoverygateway" - - cls = flavor_map.get(flavor, None) - if not cls: - logging.error("Unknown VTGate gateway flavor %s", flavor) - exit(1) - - _gateway = cls() - _gateway.flavor_name = flavor - logging.debug("Using VTGate gateway flavor '%s'", flavor) - - -def register_flavor(key, cls): - """Register the available VTGate gateway flavors.""" - flavor_map[key] = cls diff --git a/test/vtgate_utils_test.py b/test/vtgate_utils_test.py deleted file mode 100755 index 2c38b71cfb5..00000000000 --- a/test/vtgate_utils_test.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Tests for vtgate_utils.""" - -import exceptions -import time -import unittest -import utils - -from vtdb import vtgate_utils - - -def setUpModule(): - pass - - -def tearDownModule(): - pass - - -class SomeException(exceptions.Exception): - pass - - -class AnotherException(exceptions.Exception): - pass - - -class FakeVtGateConnection(object): - - def __init__(self): - self.invoked_intervals = [] - # session is used by exponential_backoff_retry - self.session = None - - @vtgate_utils.exponential_backoff_retry( - retry_exceptions=(SomeException, AnotherException)) - def method(self, exc_to_raise): - self.invoked_intervals.append(int(time.time() * 1000)) - if exc_to_raise: - - raise exc_to_raise - - -class TestVtgateUtils(unittest.TestCase): - - def test_retry_exception(self): - fake_conn = FakeVtGateConnection() - with self.assertRaises(SomeException): - fake_conn.method(SomeException('an exception')) - self.assertEquals( - len(fake_conn.invoked_intervals), vtgate_utils.NUM_RETRIES + 1) - previous = fake_conn.invoked_intervals[0] - delay = vtgate_utils.INITIAL_DELAY_MS - for interval in fake_conn.invoked_intervals[1:]: - self.assertTrue(interval - previous >= delay) - previous = interval - delay *= vtgate_utils.BACKOFF_MULTIPLIER - - def test_retry_another_exception(self): - fake_conn = FakeVtGateConnection() - with self.assertRaises(AnotherException): - fake_conn.method(AnotherException('an exception')) - self.assertEquals( - len(fake_conn.invoked_intervals), vtgate_utils.NUM_RETRIES + 1) - - def test_no_retries_inside_txn(self): - fake_conn = FakeVtGateConnection() - fake_conn.session = object() - with self.assertRaises(SomeException): - fake_conn.method(SomeException('an exception')) - self.assertEquals(len(fake_conn.invoked_intervals), 1) - - def test_no_retries_for_non_retryable_exception(self): - fake_conn = FakeVtGateConnection() - with self.assertRaises(exceptions.Exception): - fake_conn.method(exceptions.Exception('an exception')) - self.assertEquals(len(fake_conn.invoked_intervals), 1) - - def test_no_retries_for_no_exception(self): - fake_conn = FakeVtGateConnection() - fake_conn.method(None) - self.assertEquals(len(fake_conn.invoked_intervals), 1) - - -if __name__ == '__main__': - utils.main() diff --git a/test/vtgatev2_test.py b/test/vtgatev2_test.py deleted file mode 100755 index 7f634a965be..00000000000 --- a/test/vtgatev2_test.py +++ /dev/null @@ -1,1723 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import logging -from multiprocessing.pool import ThreadPool -import pprint -import struct -import threading -import time -import traceback -import unittest - -import environment -import tablet -import utils -from vtdb import dbexceptions -from vtdb import keyrange -from vtdb import keyrange_constants -from vtdb import vtdb_logger -from vtdb import vtgate_client -from vtdb import vtgate_cursor - -shard_0_master = tablet.Tablet() -shard_0_replica1 = tablet.Tablet() -shard_0_replica2 = tablet.Tablet() - -shard_1_master = tablet.Tablet() -shard_1_replica1 = tablet.Tablet() -shard_1_replica2 = tablet.Tablet() - -all_tablets = [shard_0_master, shard_0_replica1, shard_0_replica2, - shard_1_master, shard_1_replica1, shard_1_replica2] - -KEYSPACE_NAME = 'test_keyspace' -SHARD_NAMES = ['-80', '80-'] -SHARD_KID_MAP = { - '-80': [ - 527875958493693904, 626750931627689502, - 345387386794260318, 332484755310826578, - 1842642426274125671, 1326307661227634652, - 1761124146422844620, 1661669973250483744, - 3361397649937244239, 2444880764308344533], - '80-': [ - 9767889778372766922, 9742070682920810358, - 10296850775085416642, 9537430901666854108, - 10440455099304929791, 11454183276974683945, - 11185910247776122031, 10460396697869122981, - 13379616110062597001, 12826553979133932576], -} - -CREATE_VT_INSERT_TEST = '''create table vt_insert_test ( - id bigint auto_increment, - msg varchar(64), - keyspace_id bigint(20) unsigned NOT NULL, - primary key (id) -) Engine=InnoDB''' - -CREATE_VT_A = '''create table vt_a ( - eid bigint, - id int, - keyspace_id bigint(20) unsigned NOT NULL, - primary key(eid, id) -) Engine=InnoDB''' - -CREATE_VT_FIELD_TYPES = '''create table vt_field_types ( - id bigint(20) auto_increment, - uint_val bigint(20) unsigned, - str_val varchar(64), - unicode_val varchar(64), - float_val float(5, 1), - keyspace_id bigint(20) unsigned NOT NULL, - primary key(id) -) Engine=InnoDB''' - -CREATE_VT_SEQ = '''create table vt_seq ( - id int, - next_id bigint, - cache bigint, - primary key(id) -) comment 'vitess_sequence' Engine=InnoDB''' - -INIT_VT_SEQ = 'insert into vt_seq values(0, 1, 2)' - - -create_tables = [ - CREATE_VT_INSERT_TEST, - CREATE_VT_A, - CREATE_VT_FIELD_TYPES, - CREATE_VT_SEQ, -] -pack_kid = struct.Struct('!Q').pack - - -class DBRow(object): - - def __init__(self, column_names, row_tuple): - self.__dict__ = dict(zip(column_names, row_tuple)) - - def __repr__(self): - return pprint.pformat(self.__dict__, 4) - - -def setUpModule(): - logging.debug('in setUpModule') - try: - environment.topo_server().setup() - - # start mysql instance external to the test - setup_procs = [shard_0_master.init_mysql(), - shard_0_replica1.init_mysql(), - shard_0_replica2.init_mysql(), - shard_1_master.init_mysql(), - shard_1_replica1.init_mysql(), - shard_1_replica2.init_mysql() - ] - utils.wait_procs(setup_procs) - setup_tablets() - except Exception, e: # pylint: disable=broad-except - logging.exception('error during set up: %s', e) - tearDownModule() - raise - - -def tearDownModule(): - logging.debug('in tearDownModule') - utils.required_teardown() - if utils.options.skip_teardown: - return - logging.debug('Tearing down the servers and setup') - if utils.vtgate: - utils.vtgate.kill() - tablet.kill_tablets([shard_0_master, - shard_0_replica1, shard_0_replica2, - shard_1_master, - shard_1_replica1, shard_1_replica2]) - teardown_procs = [shard_0_master.teardown_mysql(), - shard_0_replica1.teardown_mysql(), - shard_0_replica2.teardown_mysql(), - shard_1_master.teardown_mysql(), - shard_1_replica1.teardown_mysql(), - shard_1_replica2.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_0_master.remove_tree() - shard_0_replica1.remove_tree() - shard_0_replica2.remove_tree() - shard_1_master.remove_tree() - shard_1_replica1.remove_tree() - shard_1_replica2.remove_tree() - - -def setup_tablets(): - """Start up a master mysql and vttablet.""" - - logging.debug('Setting up tablets') - utils.run_vtctl(['CreateKeyspace', KEYSPACE_NAME]) - utils.run_vtctl(['SetKeyspaceShardingInfo', '-force', KEYSPACE_NAME, - 'keyspace_id', 'uint64']) - shard_0_master.init_tablet( - 'replica', - keyspace=KEYSPACE_NAME, - shard='-80', - tablet_index=0) - shard_0_replica1.init_tablet( - 'replica', - keyspace=KEYSPACE_NAME, - shard='-80', - tablet_index=1) - shard_0_replica2.init_tablet( - 'replica', - keyspace=KEYSPACE_NAME, - shard='-80', - tablet_index=2) - shard_1_master.init_tablet( - 'replica', - keyspace=KEYSPACE_NAME, - shard='80-', - tablet_index=0) - shard_1_replica1.init_tablet( - 'replica', - keyspace=KEYSPACE_NAME, - shard='80-', - tablet_index=1) - shard_1_replica2.init_tablet( - 'replica', - keyspace=KEYSPACE_NAME, - shard='80-', - tablet_index=2) - - utils.run_vtctl(['RebuildKeyspaceGraph', KEYSPACE_NAME], auto_log=True) - - for t in [shard_0_master, shard_0_replica1, shard_0_replica2, - shard_1_master, shard_1_replica1, shard_1_replica2]: - t.create_db('vt_test_keyspace') - for create_table in create_tables: - t.mquery(shard_0_master.dbname, create_table) - t.start_vttablet(wait_for_state=None) - - for t in [shard_0_master, shard_0_replica1, shard_0_replica2, - shard_1_master, shard_1_replica1, shard_1_replica2]: - t.wait_for_vttablet_state('NOT_SERVING') - - utils.run_vtctl(['InitShardMaster', '-force', KEYSPACE_NAME+'/-80', - shard_0_master.tablet_alias], auto_log=True) - utils.run_vtctl(['InitShardMaster', '-force', KEYSPACE_NAME+'/80-', - shard_1_master.tablet_alias], auto_log=True) - - for t in [shard_0_master, shard_0_replica1, shard_0_replica2, - shard_1_master, shard_1_replica1, shard_1_replica2]: - t.wait_for_vttablet_state('SERVING') - - utils.run_vtctl( - ['RebuildKeyspaceGraph', KEYSPACE_NAME], auto_log=True) - - utils.check_srv_keyspace( - 'test_nj', KEYSPACE_NAME, - 'Partitions(master): -80 80-\n' - 'Partitions(rdonly): -80 80-\n' - 'Partitions(replica): -80 80-\n') - - - utils.VtGate().start(tablets= - [shard_0_master, shard_0_replica1, shard_0_replica2, - shard_1_master, shard_1_replica1, shard_1_replica2]) - - wait_for_all_tablets() - - -def restart_vtgate(port): - utils.VtGate(port=port).start( - tablets=[shard_0_master, shard_0_replica1, shard_0_replica2, - shard_1_master, shard_1_replica1, shard_1_replica2]) - - -def wait_for_endpoints(name, count): - utils.vtgate.wait_for_endpoints(name, count) - - -def wait_for_all_tablets(): - wait_for_endpoints('%s.%s.master' % (KEYSPACE_NAME, SHARD_NAMES[0]), 1) - wait_for_endpoints('%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[0]), 2) - wait_for_endpoints('%s.%s.master' % (KEYSPACE_NAME, SHARD_NAMES[1]), 1) - wait_for_endpoints('%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[1]), 2) - - -def get_connection(timeout=10.0): - protocol, endpoint = utils.vtgate.rpc_endpoint(python=True) - try: - return vtgate_client.connect(protocol, endpoint, timeout) - except Exception: # pylint: disable=broad-except - logging.exception('Connection to vtgate (timeout=%s) failed.', timeout) - raise - - -def _delete_all(shard_index, table_name): - vtgate_conn = get_connection() - # This write is to set up the test with fresh insert - # and hence performing it directly on the connection. - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from %s' % table_name, {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[keyrange.KeyRange(SHARD_NAMES[shard_index])]) - vtgate_conn.commit() - vtgate_conn.close() - - -def write_rows_to_shard(count, shard_index): - kid_list = SHARD_KID_MAP[SHARD_NAMES[shard_index]] - _delete_all(shard_index, 'vt_insert_test') - vtgate_conn = get_connection() - - for x in xrange(count): - keyspace_id = kid_list[x % len(kid_list)] - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - cursor.execute( - 'insert into vt_insert_test (msg, keyspace_id) ' - 'values (:msg, :keyspace_id)', - {'msg': 'test %s' % x, 'keyspace_id': keyspace_id}) - cursor.commit() - vtgate_conn.close() - - -class BaseTestCase(unittest.TestCase): - - def setUp(self): - super(BaseTestCase, self).setUp() - logging.info('Start: %s.', '.'.join(self.id().split('.')[-2:])) - - -class TestCoreVTGateFunctions(BaseTestCase): - - def setUp(self): - super(TestCoreVTGateFunctions, self).setUp() - self.shard_index = 1 - self.keyrange = keyrange.KeyRange(SHARD_NAMES[self.shard_index]) - self.master_tablet = shard_1_master - self.replica_tablet = shard_1_replica1 - - def test_status(self): - self.assertIn('', utils.vtgate.get_status()) - - def test_connect(self): - vtgate_conn = get_connection() - self.assertNotEqual(vtgate_conn, None) - vtgate_conn.close() - - def test_writes(self): - vtgate_conn = get_connection() - _delete_all(self.shard_index, 'vt_insert_test') - count = 10 - kid_list = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]] - for x in xrange(count): - keyspace_id = kid_list[count%len(kid_list)] - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - cursor.execute( - 'insert into vt_insert_test (msg, keyspace_id) ' - 'values (:msg, :keyspace_id)', - {'msg': 'test %s' % x, 'keyspace_id': keyspace_id}) - cursor.commit() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange]) - rowcount = cursor.execute('select * from vt_insert_test', {}) - self.assertEqual(rowcount, count, 'master fetch works') - vtgate_conn.close() - - def test_query_routing(self): - """Test VtGate routes queries to the right tablets.""" - row_counts = [20, 30] - for shard_index in [0, 1]: - write_rows_to_shard(row_counts[shard_index], shard_index) - vtgate_conn = get_connection() - for shard_index in [0, 1]: - # Fetch all rows in each shard - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[keyrange.KeyRange(SHARD_NAMES[shard_index])]) - rowcount = cursor.execute('select * from vt_insert_test', {}) - # Verify row count - self.assertEqual(rowcount, row_counts[shard_index]) - # Verify keyspace id - for result in cursor.results: - kid = result[2] - self.assertIn(kid, SHARD_KID_MAP[SHARD_NAMES[shard_index]]) - - # Do a cross shard range query and assert all rows are fetched. - # Use this test to also test the vtgate vars are correctly updated. - v = utils.vtgate.get_vars() - key0 = 'Execute.' + KEYSPACE_NAME + '.' + SHARD_NAMES[0] + '.master' - key1 = 'Execute.' + KEYSPACE_NAME + '.' + SHARD_NAMES[1] + '.master' - before0 = v['VttabletCall']['Histograms'][key0]['Count'] - before1 = v['VttabletCall']['Histograms'][key1]['Count'] - - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[keyrange.KeyRange('75-95')]) - rowcount = cursor.execute('select * from vt_insert_test', {}) - self.assertEqual(rowcount, row_counts[0] + row_counts[1]) - vtgate_conn.close() - - v = utils.vtgate.get_vars() - after0 = v['VttabletCall']['Histograms'][key0]['Count'] - after1 = v['VttabletCall']['Histograms'][key1]['Count'] - self.assertEqual(after0 - before0, 1) - self.assertEqual(after1 - before1, 1) - - def test_rollback(self): - vtgate_conn = get_connection() - count = 10 - _delete_all(self.shard_index, 'vt_insert_test') - kid_list = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]] - for x in xrange(count): - keyspace_id = kid_list[x%len(kid_list)] - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - cursor.execute( - 'insert into vt_insert_test (msg, keyspace_id) ' - 'values (:msg, :keyspace_id)', - {'msg': 'test %s' % x, 'keyspace_id': keyspace_id}) - cursor.commit() - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.rollback() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange]) - rowcount = cursor.execute('select * from vt_insert_test', {}) - logging.debug('ROLLBACK TEST rowcount %d count %d', rowcount, count) - self.assertEqual( - rowcount, count, - "Fetched rows(%d) != inserted rows(%d), rollback didn't work" % - (rowcount, count)) - write_rows_to_shard(10, self.shard_index) - vtgate_conn.close() - - def test_execute_entity_ids(self): - vtgate_conn = get_connection() - count = 10 - _delete_all(self.shard_index, 'vt_a') - eid_map = {} - kid_list = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]] - for x in xrange(count): - keyspace_id = kid_list[x%len(kid_list)] - eid_map[x] = pack_kid(keyspace_id) - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - cursor.execute( - 'insert into vt_a (eid, id, keyspace_id) ' - 'values (:eid, :id, :keyspace_id)', - {'eid': x, 'id': x, 'keyspace_id': keyspace_id}) - cursor.commit() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, keyspace_ids=None) - rowcount = cursor.execute( - 'select * from vt_a', {}, - entity_keyspace_id_map=eid_map, entity_column_name='id') - self.assertEqual(rowcount, count, 'entity_ids works') - vtgate_conn.close() - - def test_batch_read(self): - vtgate_conn = get_connection() - count = 10 - _delete_all(self.shard_index, 'vt_insert_test') - shard_name = SHARD_NAMES[self.shard_index] - kid_list = SHARD_KID_MAP[shard_name] - for x in xrange(count): - keyspace_id = kid_list[x%len(kid_list)] - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - cursor.execute( - 'insert into vt_insert_test (msg, keyspace_id) ' - 'values (:msg, :keyspace_id)', - {'msg': 'test %s' % x, 'keyspace_id': keyspace_id}) - cursor.commit() - _delete_all(self.shard_index, 'vt_a') - for x in xrange(count): - keyspace_id = kid_list[x%len(kid_list)] - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - cursor.execute( - 'insert into vt_a (eid, id, keyspace_id) ' - 'values (:eid, :id, :keyspace_id)', - {'eid': x, 'id': x, 'keyspace_id': keyspace_id}) - cursor.commit() - kid_list = [pack_kid(kid) for kid in kid_list] - cursor = vtgate_conn.cursor(tablet_type='master', keyspace=None) - - # Test ExecuteBatchKeyspaceIds - params_list = [ - dict(sql='select msg, keyspace_id from vt_insert_test', - bind_variables={}, - keyspace=KEYSPACE_NAME, keyspace_ids=kid_list, - shards=None), - dict(sql='select eid + 100, id, keyspace_id from vt_a', - bind_variables={}, - keyspace=KEYSPACE_NAME, keyspace_ids=kid_list, - shards=None), - ] - cursor.executemany(sql=None, params_list=params_list) - self.assertEqual(cursor.rowcount, count) - msg_0, msg_1 = (row[0] for row in sorted(cursor.fetchall())[:2]) - self.assertEqual(msg_0, 'test 0') - self.assertEqual(msg_1, 'test 1') - self.assertTrue(cursor.nextset()) - eid_0_plus_100, eid_1_plus_100 = ( - row[0] for row in sorted(cursor.fetchall())[:2]) - self.assertEqual(eid_0_plus_100, 100) - self.assertEqual(eid_1_plus_100, 101) - self.assertFalse(cursor.nextset()) - - # Test ExecuteBatchShards - params_list = [ - dict(sql='select eid, id, keyspace_id from vt_a', - bind_variables={}, - keyspace=KEYSPACE_NAME, - keyspace_ids=None, - shards=[shard_name]), - dict(sql='select eid + 100, id, keyspace_id from vt_a', - bind_variables={}, - keyspace=KEYSPACE_NAME, - keyspace_ids=None, - shards=[shard_name]), - ] - cursor.executemany(sql=None, params_list=params_list) - self.assertEqual(cursor.rowcount, count) - eid_0, eid_1 = (row[0] for row in sorted(cursor.fetchall())[:2]) - self.assertEqual(eid_0, 0) - self.assertEqual(eid_1, 1) - self.assertTrue(cursor.nextset()) - eid_0_plus_100, eid_1_plus_100 = ( - row[0] for row in sorted(cursor.fetchall())[:2]) - self.assertEqual(eid_0_plus_100, 100) - self.assertEqual(eid_1_plus_100, 101) - self.assertFalse(cursor.nextset()) - vtgate_conn.close() - - def test_batch_write(self): - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor(tablet_type='master', keyspace=None) - kid_list = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]] - all_ids = [pack_kid(kid) for kid in kid_list] - count = 10 - cursor.executemany( - sql=None, - params_list=[ - dict(sql='delete from vt_insert_test', bind_variables=None, - keyspace=KEYSPACE_NAME, keyspace_ids=all_ids, - shards=None)]) - - params_list = [] - for x in xrange(count): - keyspace_id = kid_list[x%len(kid_list)] - params_list.append( - dict(sql=None, - bind_variables= - {'msg': 'test %s' % x, 'keyspace_id': keyspace_id}, - keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - shards=None)) - cursor.executemany( - sql='insert into vt_insert_test (msg, keyspace_id) ' - 'values (:msg, :keyspace_id)', - params_list=params_list) - cursor.executemany( - sql=None, - params_list=[ - dict(sql='delete from vt_a', bind_variables=None, - keyspace=KEYSPACE_NAME, keyspace_ids=all_ids, shards=None)]) - params_list = [] - for x in xrange(count): - keyspace_id = kid_list[x%len(kid_list)] - sql = ( - 'insert into vt_a (eid, id, keyspace_id) ' - 'values (:eid, :id, :keyspace_id)') - bind_variables = {'eid': x, 'id': x, 'keyspace_id': keyspace_id} - keyspace = KEYSPACE_NAME - keyspace_ids = [pack_kid(keyspace_id)] - params_list.append(dict( - sql=sql, bind_variables=bind_variables, keyspace=keyspace, - keyspace_ids=keyspace_ids, shards=None)) - cursor.executemany(sql=None, params_list=params_list) - _, rowcount, _, _ = vtgate_conn._execute( - 'select * from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - self.assertEqual(rowcount, count) - _, rowcount, _, _ = vtgate_conn._execute( - 'select * from vt_a', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - self.assertEqual(rowcount, count) - vtgate_conn.close() - - def test_streaming_fetchsubset(self): - count = 30 - write_rows_to_shard(count, self.shard_index) - # Fetch a subset of the total size. - vtgate_conn = get_connection() - - def get_stream_cursor(): - return vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange], - cursorclass=vtgate_cursor.StreamVTGateCursor) - - def fetch_first_10_rows(stream_cursor): - stream_cursor.execute('select msg from vt_insert_test', {}) - rows = stream_cursor.fetchmany(size=10) - self.assertEqual(rows, [('test %d' % x,) for x in xrange(10)]) - - def fetch_next_10_rows(stream_cursor): - rows = stream_cursor.fetchmany(size=10) - self.assertEqual(rows, [('test %d' % x,) for x in xrange(10, 20)]) - - # Open two streaming queries at the same time, fetch some from each, - # and make sure they don't interfere with each other. - stream_cursor_1 = get_stream_cursor() - stream_cursor_2 = get_stream_cursor() - fetch_first_10_rows(stream_cursor_1) - fetch_first_10_rows(stream_cursor_2) - fetch_next_10_rows(stream_cursor_1) - fetch_next_10_rows(stream_cursor_2) - stream_cursor_1.close() - stream_cursor_2.close() - vtgate_conn.close() - - def test_streaming_fetchall(self): - count = 30 - write_rows_to_shard(count, self.shard_index) - # Fetch all. - vtgate_conn = get_connection() - stream_cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange], - cursorclass=vtgate_cursor.StreamVTGateCursor) - stream_cursor.execute('select * from vt_insert_test', {}) - rows = stream_cursor.fetchall() - rowcount = len(list(rows)) - self.assertEqual(rowcount, count) - stream_cursor.close() - vtgate_conn.close() - - def test_streaming_fetchone(self): - count = 30 - write_rows_to_shard(count, self.shard_index) - # Fetch one. - vtgate_conn = get_connection() - stream_cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange], - cursorclass=vtgate_cursor.StreamVTGateCursor) - stream_cursor.execute('select * from vt_insert_test', {}) - rows = stream_cursor.fetchone() - self.assertIsInstance(rows, tuple, 'Received a valid row') - stream_cursor.close() - vtgate_conn.close() - - def test_streaming_multishards(self): - count = 30 - write_rows_to_shard(count, 0) - write_rows_to_shard(count, 1) - vtgate_conn = get_connection() - stream_cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[keyrange.KeyRange( - keyrange_constants.NON_PARTIAL_KEYRANGE)], - cursorclass=vtgate_cursor.StreamVTGateCursor) - stream_cursor.execute('select * from vt_insert_test', {}) - rows = stream_cursor.fetchall() - rowcount = len(list(rows)) - self.assertEqual(rowcount, count * 2) - stream_cursor.close() - vtgate_conn.close() - - def test_streaming_zero_results(self): - vtgate_conn = get_connection() - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - # After deletion, should result zero. - stream_cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange], - cursorclass=vtgate_cursor.StreamVTGateCursor) - stream_cursor.execute('select * from vt_insert_test', {}) - rows = stream_cursor.fetchall() - rowcount = len(list(rows)) - self.assertEqual(rowcount, 0) - vtgate_conn.close() - - def test_interleaving(self): - tablet_type = 'master' - vtgate_conn = get_connection() - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type=tablet_type, keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - kid_list = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]] - count = len(kid_list) - for x in xrange(count): - keyspace_id = kid_list[x] - vtgate_conn._execute( - 'insert into vt_insert_test (msg, keyspace_id) ' - 'values (:msg, :keyspace_id)', - {'msg': 'test %s' % x, 'keyspace_id': keyspace_id}, - tablet_type=tablet_type, keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)]) - vtgate_conn.commit() - vtgate_conn2 = get_connection() - query = ( - 'select keyspace_id from vt_insert_test where keyspace_id = :kid') - thd = threading.Thread(target=self._query_lots, args=( - vtgate_conn2, - query, - {'kid': kid_list[0]}, - KEYSPACE_NAME, - tablet_type, - [pack_kid(kid_list[0])])) - thd.start() - for i in xrange(count): - (result, _, _, _) = vtgate_conn._execute( - query, - {'kid': kid_list[i]}, - tablet_type=tablet_type, keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(kid_list[i])]) - self.assertEqual(result, [(kid_list[i],)]) - if i % 10 == 0: - generator, _ = vtgate_conn._stream_execute( - query, {'kid': kid_list[i]}, - tablet_type=tablet_type, keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(kid_list[i])]) - for result in generator: - self.assertEqual(result, (kid_list[i],)) - thd.join() - vtgate_conn.close() - vtgate_conn2.close() - - def test_sequence(self): - tablet_type = 'master' - try: - vtgate_conn = get_connection() - # Special-cased initialization of sequence to shard 0. - vtgate_conn.begin() - vtgate_conn._execute( - INIT_VT_SEQ, {'keyspace_id': 0}, - tablet_type=tablet_type, keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(0)]) - vtgate_conn.commit() - want = 1 - for _ in xrange(10): - result, _, _, _ = vtgate_conn._execute( - 'select next :n values for vt_seq', {'n': 2}, - tablet_type=tablet_type, keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(0)]) - self.assertEqual(result[0][0], want) - want += 2 - except Exception, e: # pylint: disable=broad-except - self.fail('Failed with error %s %s' % (str(e), traceback.format_exc())) - vtgate_conn.close() - - def test_field_types(self): - vtgate_conn = get_connection() - _delete_all(self.shard_index, 'vt_field_types') - count = 10 - base_uint = int('8' + '0' * 15, base=16) - kid_list = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]] - for x in xrange(1, count): - keyspace_id = kid_list[count % len(kid_list)] - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - cursor.execute( - 'insert into vt_field_types ' - '(uint_val, str_val, unicode_val, float_val, keyspace_id) ' - 'values (:uint_val, :str_val, :unicode_val, ' - ':float_val, :keyspace_id)', - {'uint_val': base_uint + x, 'str_val': 'str_%d' % x, - 'unicode_val': unicode('str_%d' % x), 'float_val': x * 1.2, - 'keyspace_id': keyspace_id}) - cursor.commit() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange]) - rowcount = cursor.execute('select * from vt_field_types', {}) - field_names = [f[0] for f in cursor.description] - self.assertEqual(rowcount, count -1, "rowcount doesn't match") - id_list = [] - uint_val_list = [] - str_val_list = [] - unicode_val_list = [] - float_val_list = [] - for r in cursor.results: - row = DBRow(field_names, r) - id_list.append(row.id) - uint_val_list.append(row.uint_val) - str_val_list.append(row.str_val) - unicode_val_list.append(row.unicode_val) - float_val_list.append(row.float_val) - - # iterable type checks - list, tuple, set are supported. - query = 'select * from vt_field_types where id in ::id_1' - rowcount = cursor.execute(query, {'id_1': id_list}) - self.assertEqual(rowcount, len(id_list), "rowcount doesn't match") - rowcount = cursor.execute(query, {'id_1': tuple(id_list)}) - self.assertEqual(rowcount, len(id_list), "rowcount doesn't match") - rowcount = cursor.execute(query, {'id_1': set(id_list)}) - self.assertEqual(rowcount, len(id_list), "rowcount doesn't match") - for r in cursor.results: - row = DBRow(field_names, r) - self.assertIsInstance(row.id, (int, long)) - - # received field types same as input. - # uint - query = 'select * from vt_field_types where uint_val in ::uint_val_1' - rowcount = cursor.execute(query, {'uint_val_1': uint_val_list}) - self.assertEqual(rowcount, len(uint_val_list), "rowcount doesn't match") - for r in cursor.results: - row = DBRow(field_names, r) - self.assertIsInstance(row.uint_val, long) - self.assertGreaterEqual( - row.uint_val, base_uint, 'uint value not in correct range') - - # str - query = 'select * from vt_field_types where str_val in ::str_val_1' - rowcount = cursor.execute(query, {'str_val_1': str_val_list}) - self.assertEqual(rowcount, len(str_val_list), "rowcount doesn't match") - for r in cursor.results: - row = DBRow(field_names, r) - self.assertIsInstance(row.str_val, str) - - # unicode str - query = ( - 'select * from vt_field_types where unicode_val in ::unicode_val_1') - rowcount = cursor.execute(query, {'unicode_val_1': unicode_val_list}) - self.assertEqual( - rowcount, len(unicode_val_list), "rowcount doesn't match") - for r in cursor.results: - row = DBRow(field_names, r) - self.assertIsInstance(row.unicode_val, basestring) - - # deliberately eliminating the float test since it is flaky due - # to mysql float precision handling. - - vtgate_conn.close() - - def _query_lots( - self, conn, query, bind_vars, keyspace_name, tablet_type, keyspace_ids): - for _ in xrange(500): - result, _, _, _ = conn._execute( - query, bind_vars, - tablet_type=tablet_type, keyspace_name=keyspace_name, - keyspace_ids=keyspace_ids) - self.assertEqual(result, [tuple(bind_vars.values())]) - - def test_vschema_vars(self): - v = utils.vtgate.get_vars() - self.assertIn('VtgateVSchemaCounts', v) - self.assertIn('Reload', v['VtgateVSchemaCounts']) - self.assertGreater(v['VtgateVSchemaCounts']['Reload'], 0) - self.assertNotIn('WatchError', v['VtgateVSchemaCounts']) - self.assertNotIn('Parsing', v['VtgateVSchemaCounts']) - - -class TestFailures(BaseTestCase): - - def setUp(self): - super(TestFailures, self).setUp() - self.shard_index = 1 - self.keyrange = keyrange.KeyRange(SHARD_NAMES[self.shard_index]) - self.master_tablet = shard_1_master - self.replica_tablet = shard_1_replica1 - self.replica_tablet2 = shard_1_replica2 - - def tablet_start(self, tablet_obj, tablet_type, lameduck_period='0.5s', - grace_period=None): - if grace_period is None: - # If grace_period is not specified, use whatever default is defined in - # start_vttablet() itself. - tablet_obj.start_vttablet(lameduck_period=lameduck_period, - init_tablet_type=tablet_type, - init_keyspace=KEYSPACE_NAME, - init_shard=SHARD_NAMES[self.shard_index]) - else: - tablet_obj.start_vttablet(lameduck_period=lameduck_period, - grace_period=grace_period, - init_tablet_type=tablet_type, - init_keyspace=KEYSPACE_NAME, - init_shard=SHARD_NAMES[self.shard_index]) - - def test_status_with_error(self): - """Tests that the status page loads correctly after a VTGate error.""" - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace='INVALID_KEYSPACE', keyspace_ids=['0']) - # We expect to see a DatabaseError due to an invalid keyspace - with self.assertRaises(dbexceptions.DatabaseError): - cursor.execute('select * from vt_insert_test', {}) - vtgate_conn.close() - - # Page should have loaded successfully - self.assertIn('', utils.vtgate.get_status()) - - def test_tablet_restart_read(self): - # Since we're going to kill the tablet, there will be a race between the - # client timeout here and the vtgate->vttablet connection timeout, so we - # increase it for this test. - vtgate_conn = get_connection(timeout=30) - self.replica_tablet.kill_vttablet() - self.replica_tablet2.kill_vttablet() - with self.assertRaises(dbexceptions.DatabaseError): - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - self.tablet_start(self.replica_tablet, 'replica') - self.tablet_start(self.replica_tablet2, 'replica') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - try: - _ = vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - except Exception, e: # pylint: disable=broad-except - self.fail('Communication with shard %s replica failed with error %s' % - (SHARD_NAMES[self.shard_index], str(e))) - vtgate_conn.close() - - def test_vtgate_restart_read(self): - vtgate_conn = get_connection() - port = utils.vtgate.port - utils.vtgate.kill() - with self.assertRaises(dbexceptions.DatabaseError): - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - restart_vtgate(port) - vtgate_conn = get_connection() - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - def test_tablet_restart_stream_execute(self): - # Since we're going to kill the tablet, there will be a race between the - # client timeout here and the vtgate->vttablet connection timeout, so we - # increase it for this test. - vtgate_conn = get_connection(timeout=30) - stream_cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange], - cursorclass=vtgate_cursor.StreamVTGateCursor) - self.replica_tablet.kill_vttablet() - self.replica_tablet2.kill_vttablet() - with self.assertRaises(dbexceptions.DatabaseError): - stream_cursor.execute('select * from vt_insert_test', {}) - self.tablet_start(self.replica_tablet, 'replica') - self.tablet_start(self.replica_tablet2, 'replica') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - try: - stream_cursor.execute('select * from vt_insert_test', {}) - except Exception, e: # pylint: disable=broad-except - self.fail('Communication with shard0 replica failed with error %s' % - str(e)) - vtgate_conn.close() - - def test_vtgate_restart_stream_execute(self): - vtgate_conn = get_connection() - stream_cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange], - cursorclass=vtgate_cursor.StreamVTGateCursor) - port = utils.vtgate.port - utils.vtgate.kill() - with self.assertRaises(dbexceptions.DatabaseError): - stream_cursor.execute('select * from vt_insert_test', {}) - vtgate_conn.close() - - restart_vtgate(port) - vtgate_conn = get_connection() - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - stream_cursor = vtgate_conn.cursor( - tablet_type='replica', keyspace=KEYSPACE_NAME, - keyranges=[self.keyrange], - cursorclass=vtgate_cursor.StreamVTGateCursor) - try: - stream_cursor.execute('select * from vt_insert_test', {}) - except Exception, e: # pylint: disable=broad-except - self.fail('Communication with shard0 replica failed with error %s' % - str(e)) - vtgate_conn.close() - - # vtgate begin doesn't make any back-end connections to - # vttablet so the kill and restart shouldn't have any effect. - def test_tablet_restart_begin(self): - vtgate_conn = get_connection() - self.master_tablet.kill_vttablet() - vtgate_conn.begin() - self.tablet_start(self.master_tablet, 'replica') - wait_for_endpoints( - '%s.%s.master' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - vtgate_conn.begin() - # this succeeds only if retry_count > 0 - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - vtgate_conn.close() - - def test_vtgate_restart_begin(self): - vtgate_conn = get_connection() - port = utils.vtgate.port - utils.vtgate.kill() - with self.assertRaises(dbexceptions.DatabaseError): - vtgate_conn.begin() - restart_vtgate(port) - vtgate_conn = get_connection() - wait_for_endpoints( - '%s.%s.master' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - vtgate_conn.begin() - vtgate_conn.close() - - def test_tablet_fail_write(self): - # Since we're going to kill the tablet, there will be a race between the - # client timeout here and the vtgate->vttablet connection timeout, so we - # increase it for this test. - vtgate_conn = get_connection(timeout=30) - with self.assertRaises(dbexceptions.DatabaseError): - vtgate_conn.begin() - self.master_tablet.kill_vttablet() - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - self.tablet_start(self.master_tablet, 'replica') - wait_for_endpoints( - '%s.%s.master' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - vtgate_conn.close() - - def _get_non_vtgate_errors(self): - v = utils.vtgate.get_vars() - if 'VtgateInfoErrorCounts' not in v: - return 0 - if 'NonVtgateErrors' not in v['VtgateInfoErrorCounts']: - return 0 - return v['VtgateInfoErrorCounts']['NonVtgateErrors'] - - def test_error_on_dml(self): - vtgate_conn = get_connection() - vtgate_conn.begin() - keyspace_id = SHARD_KID_MAP[SHARD_NAMES[ - (self.shard_index+1)%len(SHARD_NAMES) - ]][0] - try: - vtgate_conn._execute( - 'insert into vt_insert_test values(:msg, :keyspace_id)', - {'msg': 'test4', 'keyspace_id': keyspace_id}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - self.fail('Failed to raise DatabaseError exception') - except dbexceptions.DatabaseError: - # FIXME(alainjobart) add a method to get the session to vtgate_client, - # instead of poking into it like this. - logging.info('Shard session: %s', vtgate_conn.session) - transaction_id = vtgate_conn.session.shard_sessions[0].transaction_id - self.assertNotEqual(transaction_id, 0) - except Exception, e: # pylint: disable=broad-except - self.fail('Expected DatabaseError as exception, got %s' % str(e)) - finally: - vtgate_conn.rollback() - vtgate_conn.close() - - def test_vtgate_fail_write(self): - # use a shorter timeout, we know we're going to hit it (twice in fact, - # once for the _execute, once for the close's rollback). - vtgate_conn = get_connection(timeout=5.0) - port = utils.vtgate.port - with self.assertRaises(dbexceptions.DatabaseError): - vtgate_conn.begin() - utils.vtgate.kill() - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - - # Note this tests a connection with an interrupted transaction can - # still be closed. - vtgate_conn.close() - - restart_vtgate(port) - vtgate_conn = get_connection() - wait_for_endpoints( - '%s.%s.master' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from vt_insert_test', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - vtgate_conn.close() - - # test timeout between py client and vtgate - def test_vtgate_timeout(self): - vtgate_conn = get_connection(timeout=3.0) - with self.assertRaises(dbexceptions.TimeoutError): - vtgate_conn._execute( - 'select sleep(4) from dual', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - vtgate_conn = get_connection(timeout=3.0) - with self.assertRaises(dbexceptions.TimeoutError): - vtgate_conn._execute( - 'select sleep(4) from dual', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - # Currently this is causing vttablet to become unreachable at - # the timeout boundary and kill any query being executed - # at the time. Prevent flakiness in other tests by sleeping - # until the query times out. - # TODO(b/17733518) - time.sleep(3) - - # test timeout between vtgate and vttablet - # the timeout is set to 5 seconds - def test_tablet_timeout(self): - # this test only makes sense if there is a shorter/protective timeout - # set for vtgate-vttablet connection. - # TODO(liguo): evaluate if we want such a timeout - return - vtgate_conn = get_connection() # pylint: disable=unreachable - with self.assertRaises(dbexceptions.DatabaseError): - vtgate_conn.begin() - vtgate_conn._execute( - 'select sleep(7) from dual', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - vtgate_conn = get_connection() - with self.assertRaises(dbexceptions.DatabaseError): - vtgate_conn.begin() - vtgate_conn._execute( - 'select sleep(7) from dual', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - # Test the case that no query sent during tablet shuts down (single tablet) - def test_restart_mysql_tablet_idle(self): - self.replica_tablet2.kill_vttablet() - vtgate_conn = get_connection() - utils.wait_procs([self.replica_tablet.shutdown_mysql(),]) - try: - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - self.fail('DatabaseError should have been raised') - except Exception, e: # pylint: disable=broad-except - self.assertIsInstance(e, dbexceptions.DatabaseError) - self.assertNotIsInstance(e, dbexceptions.IntegrityError) - self.assertNotIsInstance(e, dbexceptions.OperationalError) - self.assertNotIsInstance(e, dbexceptions.TimeoutError) - - utils.wait_procs([self.replica_tablet.start_mysql(),]) - # then restart replication, and write data, make sure we go back to healthy - for t in [self.replica_tablet]: - utils.run_vtctl(['StartSlave', t.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], - auto_log=True) - t.wait_for_vttablet_state('SERVING') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - self.tablet_start(self.replica_tablet2, 'replica') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - - # Test the case that there are queries sent during vttablet shuts down, - # and all querys fail because there is only one vttablet. - def test_restart_mysql_tablet_queries(self): - vtgate_conn = get_connection() - utils.wait_procs([self.replica_tablet.shutdown_mysql(),]) - utils.wait_procs([self.replica_tablet2.shutdown_mysql(),]) - try: - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - self.fail('DatabaseError should have been raised') - except Exception, e: # pylint: disable=broad-except - self.assertIsInstance(e, dbexceptions.DatabaseError) - self.assertNotIsInstance(e, dbexceptions.IntegrityError) - self.assertNotIsInstance(e, dbexceptions.OperationalError) - self.assertNotIsInstance(e, dbexceptions.TimeoutError) - utils.wait_procs([self.replica_tablet.start_mysql(),]) - utils.wait_procs([self.replica_tablet2.start_mysql(),]) - # then restart replication, and write data, make sure we go back to healthy - for t in [self.replica_tablet, self.replica_tablet2]: - utils.run_vtctl(['StartSlave', t.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], - auto_log=True) - t.wait_for_vttablet_state('SERVING') - self.replica_tablet2.kill_vttablet() - replica_tablet_proc = self.replica_tablet.kill_vttablet(wait=False) - # send query while vttablet is in lameduck, should fail as no vttablet - time.sleep(0.1) # wait a short while so vtgate gets the health check - try: - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - self.fail('DatabaseError should have been raised') - except Exception, e: # pylint: disable=broad-except - self.assertIsInstance(e, dbexceptions.DatabaseError) - self.assertNotIsInstance(e, dbexceptions.IntegrityError) - self.assertNotIsInstance(e, dbexceptions.OperationalError) - self.assertNotIsInstance(e, dbexceptions.TimeoutError) - # Wait for original tablet to finish before restarting. - replica_tablet_proc.wait() - self.tablet_start(self.replica_tablet, 'replica') - self.tablet_start(self.replica_tablet2, 'replica') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - # as the cached vtgate-tablet conn was marked down, it should succeed - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - # Test the case that there are queries sent during one vttablet shuts down, - # and all queries succeed because there is another vttablet. - def test_restart_mysql_tablet_queries_multi_tablets(self): - vtgate_conn = get_connection() - utils.wait_procs([self.replica_tablet.shutdown_mysql(),]) - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - # should retry on tablet2 and succeed - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - utils.wait_procs([self.replica_tablet.start_mysql(),]) - # then restart replication, and write data, make sure we go back to healthy - for t in [self.replica_tablet]: - utils.run_vtctl(['StartSlave', t.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], - auto_log=True) - t.wait_for_vttablet_state('SERVING') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - # kill tablet2 and leave it in lameduck mode - replica_tablet2_proc = self.replica_tablet2.kill_vttablet(wait=False) - time.sleep(0.1) - # send query while tablet2 is in lameduck, should retry on tablet1 - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_before = int(tablet1_vars['Queries']['TotalCount']) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_after = int(tablet1_vars['Queries']['TotalCount']) - self.assertEqual(t1_query_count_after-t1_query_count_before, 1) - # Wait for tablet2 to go down. - replica_tablet2_proc.wait() - # send another query, should also succeed on tablet1 - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_before = int(tablet1_vars['Queries']['TotalCount']) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_after = int(tablet1_vars['Queries']['TotalCount']) - self.assertEqual(t1_query_count_after-t1_query_count_before, 1) - # start tablet2 - self.tablet_start(self.replica_tablet2, 'replica') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - # query should succeed on either tablet - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_before = int(tablet1_vars['Queries']['TotalCount']) - tablet2_vars = utils.get_vars(self.replica_tablet2.port) - t2_query_count_before = int(tablet2_vars['Queries']['TotalCount']) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_after = int(tablet1_vars['Queries']['TotalCount']) - tablet2_vars = utils.get_vars(self.replica_tablet2.port) - t2_query_count_after = int(tablet2_vars['Queries']['TotalCount']) - self.assertEqual(t1_query_count_after-t1_query_count_before - +t2_query_count_after-t2_query_count_before, 1) - vtgate_conn.close() - - # Test the case that there are queries sent during one vttablet is killed, - # and all queries succeed because there is another vttablet. - def test_kill_mysql_tablet_queries_multi_tablets(self): - vtgate_conn = get_connection() - utils.wait_procs([self.replica_tablet.shutdown_mysql(),]) - # should execute on tablet2 and succeed - tablet2_vars = utils.get_vars(self.replica_tablet2.port) - t2_query_count_before = int(tablet2_vars['Queries']['TotalCount']) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - tablet2_vars = utils.get_vars(self.replica_tablet2.port) - t2_query_count_after = int(tablet2_vars['Queries']['TotalCount']) - self.assertEqual(t2_query_count_after-t2_query_count_before, 1) - # start tablet1 mysql - utils.wait_procs([self.replica_tablet.start_mysql(),]) - # then restart replication, and write data, make sure we go back to healthy - for t in [self.replica_tablet]: - utils.run_vtctl(['StartSlave', t.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], - auto_log=True) - t.wait_for_vttablet_state('SERVING') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - # query should succeed - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - # hard kill tablet2 - self.replica_tablet2.hard_kill_vttablet() - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - # send another query, should succeed on tablet1 - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_before = int(tablet1_vars['Queries']['TotalCount']) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_after = int(tablet1_vars['Queries']['TotalCount']) - self.assertEqual(t1_query_count_after-t1_query_count_before, 1) - # start tablet2 - self.tablet_start(self.replica_tablet2, 'replica') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - # query should succeed on either tablet - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_before = int(tablet1_vars['Queries']['TotalCount']) - tablet2_vars = utils.get_vars(self.replica_tablet2.port) - t2_query_count_before = int(tablet2_vars['Queries']['TotalCount']) - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_after = int(tablet1_vars['Queries']['TotalCount']) - tablet2_vars = utils.get_vars(self.replica_tablet2.port) - t2_query_count_after = int(tablet2_vars['Queries']['TotalCount']) - self.assertEqual(t1_query_count_after-t1_query_count_before - +t2_query_count_after-t2_query_count_before, 1) - vtgate_conn.close() - - def test_bind_vars_in_exception_message(self): - vtgate_conn = get_connection() - keyspace_id = None - - count = 1 - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from vt_a', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[keyrange.KeyRange(SHARD_NAMES[self.shard_index])]) - vtgate_conn.commit() - eid_map = {} - # start transaction - vtgate_conn.begin() - kid_list = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]] - - # kill vttablet - self.master_tablet.kill_vttablet() - - try: - # perform write, this should fail - for x in xrange(count): - keyspace_id = kid_list[x%len(kid_list)] - eid_map[x] = str(keyspace_id) - vtgate_conn._execute( - 'insert into vt_a (eid, id, keyspace_id) ' - 'values (:eid, :id, :keyspace_id)', - {'eid': x, 'id': x, 'keyspace_id': keyspace_id}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)]) - vtgate_conn.commit() - except Exception, e: # pylint: disable=broad-except - # check that bind var value is not present in exception message. - if str(keyspace_id) in str(e): - self.fail('bind_vars present in the exception message') - finally: - vtgate_conn.rollback() - # Start master tablet again - self.tablet_start(self.master_tablet, 'replica') - wait_for_endpoints( - '%s.%s.master' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - vtgate_conn.close() - - def test_fail_fast_when_no_serving_tablets(self): - """Verify VtGate requests fail-fast when tablets are unavailable. - - When there are no SERVING tablets available to serve a request, - VtGate should fail-fast (returning an appropriate error) without - waiting around till the request deadline expires. - """ - tablet_type = 'replica' - keyranges = [keyrange.KeyRange(SHARD_NAMES[self.shard_index])] - query = 'select * from vt_insert_test' - - # Execute a query to warm VtGate's caches for connections and endpoints - get_rtt(KEYSPACE_NAME, query, tablet_type, keyranges) - - # Shutdown mysql and ensure tablet is in NOT_SERVING state - utils.wait_procs([self.replica_tablet.shutdown_mysql(),]) - utils.wait_procs([self.replica_tablet2.shutdown_mysql(),]) - - try: - get_rtt(KEYSPACE_NAME, query, tablet_type, keyranges) - self.replica_tablet.wait_for_vttablet_state('NOT_SERVING') - self.replica_tablet2.wait_for_vttablet_state('NOT_SERVING') - except Exception: # pylint: disable=broad-except - self.fail('unable to set tablet to NOT_SERVING state') - - # Fire off a few requests in parallel - num_requests = 10 - pool = ThreadPool(processes=num_requests) - async_results = [] - for _ in range(num_requests): - async_result = pool.apply_async( - get_rtt, (KEYSPACE_NAME, query, tablet_type, keyranges)) - async_results.append(async_result) - - # Fetch all round trip times and verify max - rt_times = [] - for async_result in async_results: - rt_times.append(async_result.get()) - # The true upper limit is 2 seconds (1s * 2 retries as in - # utils.py). To account for network latencies and other variances, - # we keep an upper bound of 3 here. - self.assertLess( - max(rt_times), 3, - 'at least one request did not fail-fast; round trip times: %s' % - rt_times) - - # Restart tablet and put it back to SERVING state - utils.wait_procs([self.replica_tablet.start_mysql(),]) - utils.wait_procs([self.replica_tablet2.start_mysql(),]) - # then restart replication, and write data, make sure we go back to healthy - for t in [self.replica_tablet, self.replica_tablet2]: - utils.run_vtctl(['StartSlave', t.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', t.tablet_alias], - auto_log=True) - t.wait_for_vttablet_state('SERVING') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - - def test_lameduck_ongoing_query_single(self): - self._test_lameduck_ongoing_query_single(0) - - def test_lameduck_ongoing_query_single_grace_period(self): - self._test_lameduck_ongoing_query_single(2) - - def _test_lameduck_ongoing_query_single(self, grace_period): - # disable the second replica, we'll only use the first one - utils.wait_procs([self.replica_tablet2.shutdown_mysql(),]) - utils.run_vtctl(['RunHealthCheck', self.replica_tablet2.tablet_alias], - auto_log=True) - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - - # re-configure the first tablet with a grace period - self.replica_tablet.kill_vttablet() - self.tablet_start(self.replica_tablet, 'replica', - lameduck_period='5s', - grace_period='%ds'%grace_period) - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - - # make sure query can go through tablet1 - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_before = int(tablet1_vars['Queries']['TotalCount']) - vtgate_conn = get_connection() - try: - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - except Exception, e: # pylint: disable=broad-except - self.fail('Failed with error %s %s' % (str(e), traceback.format_exc())) - tablet1_vars = utils.get_vars(self.replica_tablet.port) - t1_query_count_after = int(tablet1_vars['Queries']['TotalCount']) - self.assertEqual(t1_query_count_after-t1_query_count_before, 1) - # start a long running query - num_requests = 10 - pool = ThreadPool(processes=num_requests) - async_results = [] - for _ in range(5): - async_result = pool.apply_async( - send_long_query, (KEYSPACE_NAME, 'replica', [self.keyrange], 2)) - async_results.append(async_result) - # soft kill vttablet - # **should wait till previous queries are sent out** - time.sleep(1) - replica_tablet_proc = self.replica_tablet.kill_vttablet(wait=False) - # Send query while vttablet is in lameduck. - time.sleep(0.1) - # With discoverygateway, it should fail regardless of grace period, - # because vttablet broadcasts that it's unhealthy, and vtgate should - # remove it immediately. - try: - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - self.fail('DatabaseError should have been raised') - except Exception, e: # pylint: disable=broad-except - self.assertIsInstance(e, dbexceptions.DatabaseError) - self.assertNotIsInstance(e, dbexceptions.IntegrityError) - self.assertNotIsInstance(e, dbexceptions.OperationalError) - self.assertNotIsInstance(e, dbexceptions.TimeoutError) - # Fetch all ongoing query results - query_results = [] - for async_result in async_results: - query_results.append(async_result.get()) - # all should succeed - for query_result in query_results: - self.assertTrue(query_result) - # Wait for the old replica_tablet to exit. - replica_tablet_proc.wait() - # start tablet1 - self.tablet_start(self.replica_tablet, 'replica') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 1) - # send another query, should succeed on tablet1 - vtgate_conn._execute( - 'select 1 from vt_insert_test', {}, - tablet_type='replica', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.close() - - # restart tablet2 - utils.wait_procs([self.replica_tablet2.start_mysql(),]) - utils.run_vtctl(['StartSlave', self.replica_tablet2.tablet_alias]) - utils.run_vtctl(['RunHealthCheck', self.replica_tablet2.tablet_alias], - auto_log=True) - self.replica_tablet2.wait_for_vttablet_state('SERVING') - wait_for_endpoints( - '%s.%s.replica' % (KEYSPACE_NAME, SHARD_NAMES[self.shard_index]), - 2) - - -# Return round trip time for a VtGate query, ignore any errors -def get_rtt(keyspace, query, tablet_type, keyranges): - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type=tablet_type, keyspace=keyspace, keyranges=keyranges) - start = time.time() - try: - cursor.execute(query, {}) - except Exception: # pylint: disable=broad-except - pass - duration = time.time() - start - vtgate_conn.close() - return duration - - -# Send out a long query, return if it succeeds. -def send_long_query(keyspace, tablet_type, keyranges, delay): - try: - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type=tablet_type, keyspace=keyspace, keyranges=keyranges) - query = 'select sleep(%s) from dual' % str(delay) - try: - cursor.execute(query, {}) - except Exception: # pylint: disable=broad-except - return False - vtgate_conn.close() - return True - except Exception: # pylint: disable=broad-except - return False - - -class VTGateTestLogger(vtdb_logger.VtdbLogger): - - def __init__(self): - self._integrity_error_count = 0 - - def integrity_error(self, e): - self._integrity_error_count += 1 - - def get_integrity_error_count(self): - return self._integrity_error_count - - -DML_KEYWORDS = ['insert', 'update', 'delete'] - - -class TestExceptionLogging(BaseTestCase): - - def setUp(self): - super(TestExceptionLogging, self).setUp() - self.shard_index = 1 - self.keyrange = keyrange.KeyRange(SHARD_NAMES[self.shard_index]) - self.master_tablet = shard_1_master - self.replica_tablet = shard_1_replica1 - vtdb_logger.register_vtdb_logger(VTGateTestLogger()) - self.logger = vtdb_logger.get_logger() - - def test_integrity_error_logging(self): - vtgate_conn = get_connection() - - vtgate_conn.begin() - vtgate_conn._execute( - 'delete from vt_a', {}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyranges=[self.keyrange]) - vtgate_conn.commit() - - keyspace_id = SHARD_KID_MAP[SHARD_NAMES[self.shard_index]][0] - - old_error_count = self.logger.get_integrity_error_count() - try: - vtgate_conn.begin() - vtgate_conn._execute( - 'insert into vt_a (eid, id, keyspace_id) ' - 'values (:eid, :id, :keyspace_id)', - {'eid': 1, 'id': 1, 'keyspace_id': keyspace_id}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)]) - vtgate_conn._execute( - 'insert into vt_a (eid, id, keyspace_id) ' - 'values (:eid, :id, :keyspace_id)', - {'eid': 1, 'id': 1, 'keyspace_id': keyspace_id}, - tablet_type='master', keyspace_name=KEYSPACE_NAME, - keyspace_ids=[pack_kid(keyspace_id)]) - vtgate_conn.commit() - except dbexceptions.IntegrityError as e: - parts = str(e).split(',') - exc_msg = parts[0] - for kw in DML_KEYWORDS: - if kw in exc_msg: - self.fail("IntegrityError shouldn't contain the query %s" % exc_msg) - except Exception as e: # pylint: disable=broad-except - self.fail('Expected IntegrityError to be raised, raised %s' % str(e)) - finally: - vtgate_conn.rollback() - # The underlying execute is expected to catch and log the integrity error. - self.assertEqual(self.logger.get_integrity_error_count(), old_error_count+1) - - -if __name__ == '__main__': - utils.main() diff --git a/test/vtgatev3_test.py b/test/vtgatev3_test.py deleted file mode 100755 index 83039e40ef3..00000000000 --- a/test/vtgatev3_test.py +++ /dev/null @@ -1,2034 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from decimal import Decimal -import itertools -import logging -import unittest -import urllib - -import environment -import keyspace_util -import utils -from protocols_flavor import protocols_flavor - -from vtdb import dbexceptions -from vtdb import vtgate_cursor -from vtdb import vtgate_client - - -shard_0_master = None -shard_1_master = None -lookup_master = None - -keyspace_env = None - -create_vt_user = '''create table vt_user ( -id bigint, -name varchar(64), -primary key (id) -) Engine=InnoDB''' - -create_vt_user2 = '''create table vt_user2 ( -id bigint, -name varchar(64), -primary key (id) -) Engine=InnoDB''' - -create_vt_user_extra = '''create table vt_user_extra ( -user_id bigint, -email varchar(64), -primary key (user_id) -) Engine=InnoDB''' - -create_vt_user_extra2 = '''create table vt_user_extra2 ( -user_id bigint, -lastname varchar(64), -address varchar(64), -primary key (user_id) -) Engine=InnoDB''' - -create_vt_multicolvin = '''create table vt_multicolvin ( -kid bigint, -cola varchar(64), -colb varchar(64), -colc varchar(64), -primary key (kid) -) Engine=InnoDB''' - -create_vt_aggr = '''create table vt_aggr ( -id bigint, -name varchar(64), -primary key (id) -) Engine=InnoDB''' - -create_vt_music = '''create table vt_music ( -user_id bigint, -id bigint, -song varchar(64), -primary key (user_id, id) -) Engine=InnoDB''' - -create_vt_music_extra = '''create table vt_music_extra ( -music_id bigint, -user_id bigint, -artist varchar(64), -primary key (music_id) -) Engine=InnoDB''' - -create_upsert = '''create table upsert ( -pk bigint, -owned bigint, -user_id bigint, -col bigint, -primary key (pk) -) Engine=InnoDB''' - -create_join_user = '''create table join_user ( -id bigint, -name varchar(64), -primary key (id) -) Engine=InnoDB''' - -create_join_user_extra = '''create table join_user_extra ( -user_id bigint, -email varchar(64), -primary key (user_id) -) Engine=InnoDB''' - -create_join_name_info = '''create table join_name_info ( -name varchar(128), -info varchar(128), -primary key (name) -) Engine=InnoDB''' - -create_twopc_user = '''create table twopc_user ( -user_id bigint, -val varchar(128), -primary key (user_id) -) Engine=InnoDB''' - -create_vt_user_seq = '''create table vt_user_seq ( - id int, - next_id bigint, - cache bigint, - primary key(id) -) comment 'vitess_sequence' Engine=InnoDB''' - -init_vt_user_seq = 'insert into vt_user_seq values(0, 1, 2)' - -create_vt_music_seq = '''create table vt_music_seq ( - id int, - next_id bigint, - cache bigint, - primary key(id) -) comment 'vitess_sequence' Engine=InnoDB''' - -init_vt_music_seq = 'insert into vt_music_seq values(0, 1, 2)' - -create_vt_main_seq = '''create table vt_main_seq ( - id int, - next_id bigint, - cache bigint, - primary key(id) -) comment 'vitess_sequence' Engine=InnoDB''' - -init_vt_main_seq = 'insert into vt_main_seq values(0, 1, 2)' - -create_name_user2_map = '''create table name_user2_map ( -name varchar(64), -user2_id bigint, -primary key (name, user2_id) -) Engine=InnoDB''' - -create_lastname_user_extra2_map = '''create table lastname_user_extra2_map ( -lastname varchar(64), -user_id bigint, -primary key (lastname, user_id) -) Engine=InnoDB''' - -create_cola_map = '''create table cola_map ( -cola varchar(64), -kid binary(8), -primary key (cola, kid) -) Engine=InnoDB''' - -create_colb_colc_map = '''create table colb_colc_map ( -colb varchar(64), -colc varchar(64), -kid binary(8), -primary key (colb, colc, kid) -) Engine=InnoDB''' - -create_address_user_extra2_map = '''create table address_user_extra2_map ( -address varchar(64), -user_id bigint, -primary key (address) -) Engine=InnoDB''' - -create_music_user_map = '''create table music_user_map ( -music_id bigint, -user_id bigint, -primary key (music_id) -) Engine=InnoDB''' - -create_upsert_primary = '''create table upsert_primary ( -id bigint, -ksnum_id bigint, -primary key (id) -) Engine=InnoDB''' - -create_upsert_owned = '''create table upsert_owned ( -owned bigint, -ksnum_id bigint, -primary key (owned) -) Engine=InnoDB''' - -create_main = '''create table main ( -id bigint, -val varchar(128), -primary key(id) -) Engine=InnoDB''' - -create_twopc_lookup = '''create table twopc_lookup ( -id bigint, -val varchar(128), -primary key (id) -) Engine=InnoDB''' - -vschema = { - 'user': '''{ - "sharded": true, - "vindexes": { - "hash_index": { - "type": "hash" - }, - "unicode_hash": { - "type": "unicode_loose_md5" - }, - "name_user2_map": { - "type": "lookup_hash", - "params": { - "table": "name_user2_map", - "from": "name", - "to": "user2_id" - }, - "owner": "vt_user2" - }, - "lastname_user_extra2_map": { - "type": "lookup_hash", - "params": { - "table": "lastname_user_extra2_map", - "from": "lastname", - "to": "user_id" - }, - "owner": "vt_user_extra2" - }, - "cola_map": { - "type": "lookup", - "params": { - "table": "cola_map", - "from": "cola", - "to": "kid" - }, - "owner": "vt_multicolvin" - }, - "colb_colc_map": { - "type": "lookup", - "params": { - "table": "colb_colc_map", - "from": "colb,colc", - "to": "kid" - }, - "owner": "vt_multicolvin" - }, - "address_user_extra2_map": { - "type": "lookup_hash_unique", - "params": { - "table": "address_user_extra2_map", - "from": "address", - "to": "user_id" - }, - "owner": "vt_user_extra2" - }, - "music_user_map": { - "type": "lookup_hash_unique", - "params": { - "table": "music_user_map", - "from": "music_id", - "to": "user_id" - }, - "owner": "vt_music" - }, - "upsert_primary": { - "type": "lookup_hash_unique", - "params": { - "table": "upsert_primary", - "from": "id", - "to": "ksnum_id" - } - }, - "upsert_owned": { - "type": "lookup_hash_unique", - "params": { - "table": "upsert_owned", - "from": "owned", - "to": "ksnum_id" - }, - "owner": "upsert" - } - }, - "tables": { - "vt_user": { - "column_vindexes": [ - { - "column": "id", - "name": "hash_index" - } - ], - "auto_increment": { - "column": "id", - "sequence": "vt_user_seq" - } - }, - "vt_user2": { - "column_vindexes": [ - { - "column": "id", - "name": "hash_index" - }, - { - "column": "name", - "name": "name_user2_map" - } - ] - }, - "vt_user_extra": { - "column_vindexes": [ - { - "column": "user_id", - "name": "hash_index" - } - ] - }, - "vt_user_extra2": { - "column_vindexes": [ - { - "column": "user_id", - "name": "hash_index" - }, - { - "column": "lastname", - "name": "lastname_user_extra2_map" - }, - { - "column": "address", - "name": "address_user_extra2_map" - } - ] - }, - "vt_multicolvin": { - "column_vindexes": [ - { - "column": "kid", - "name": "hash_index" - }, - { - "column": "cola", - "name": "cola_map" - }, - { - "columns": ["colb", "colc"], - "name": "colb_colc_map" - } - ] - }, - "vt_aggr": { - "column_vindexes": [ - { - "column": "id", - "name": "hash_index" - } - ], - "columns": [ - { - "name": "name", - "type": "VARCHAR" - } - ] - }, - "vt_music": { - "column_vindexes": [ - { - "column": "user_id", - "name": "hash_index" - }, - { - "column": "id", - "name": "music_user_map" - } - ], - "auto_increment": { - "column": "id", - "sequence": "vt_music_seq" - } - }, - "vt_music_extra": { - "column_vindexes": [ - { - "column": "music_id", - "name": "music_user_map" - }, - { - "column": "user_id", - "name": "hash_index" - } - ] - }, - "upsert": { - "column_vindexes": [ - { - "column": "pk", - "name": "upsert_primary" - }, - { - "column": "owned", - "name": "upsert_owned" - }, - { - "column": "user_id", - "name": "hash_index" - } - ] - }, - "join_user": { - "column_vindexes": [ - { - "column": "id", - "name": "hash_index" - } - ] - }, - "join_user_extra": { - "column_vindexes": [ - { - "column": "user_id", - "name": "hash_index" - } - ] - }, - "join_name_info": { - "column_vindexes": [ - { - "column": "name", - "name": "unicode_hash" - } - ] - }, - "twopc_user": { - "column_vindexes": [ - { - "column": "user_id", - "name": "hash_index" - } - ] - } - } - }''', - 'lookup': '''{ - "sharded": false, - "tables": { - "vt_user_seq": { - "type": "sequence" - }, - "vt_music_seq": { - "type": "sequence" - }, - "vt_main_seq": { - "type": "sequence" - }, - "music_user_map": {}, - "cola_map": {}, - "colb_colc_map": {}, - "name_user2_map": {}, - "lastname_user_extra2_map": {}, - "address_user_extra2_map": {}, - "upsert_primary": {}, - "upsert_owned": {}, - "main": { - "auto_increment": { - "column": "id", - "sequence": "vt_main_seq" - } - }, - "twopc_lookup": {} - } - }''', -} - - -def setUpModule(): - global keyspace_env - global shard_0_master - global shard_1_master - global lookup_master - logging.debug('in setUpModule') - - try: - environment.topo_server().setup() - logging.debug('Setting up tablets') - keyspace_env = keyspace_util.TestEnv() - keyspace_env.launch( - 'user', - shards=['-80', '80-'], - ddls=[ - create_vt_user, - create_vt_user2, - create_vt_user_extra, - create_vt_user_extra2, - create_vt_multicolvin, - create_vt_aggr, - create_vt_music, - create_vt_music_extra, - create_upsert, - create_join_user, - create_join_user_extra, - create_join_name_info, - create_twopc_user, - ], - rdonly_count=1, # to test SplitQuery - twopc_coordinator_address='localhost:15028', # enables 2pc - ) - keyspace_env.launch( - 'lookup', - ddls=[ - create_vt_user_seq, - create_vt_music_seq, - create_vt_main_seq, - create_music_user_map, - create_name_user2_map, - create_lastname_user_extra2_map, - create_address_user_extra2_map, - create_cola_map, - create_colb_colc_map, - create_upsert_primary, - create_upsert_owned, - create_main, - create_twopc_lookup, - ], - twopc_coordinator_address='localhost:15028', # enables 2pc - ) - shard_0_master = keyspace_env.tablet_map['user.-80.master'] - shard_1_master = keyspace_env.tablet_map['user.80-.master'] - lookup_master = keyspace_env.tablet_map['lookup.0.master'] - - utils.apply_vschema(vschema) - utils.VtGate().start( - tablets=[shard_0_master, shard_1_master, lookup_master], - extra_args=['-transaction_mode', 'TWOPC']) - utils.vtgate.wait_for_endpoints('user.-80.master', 1) - utils.vtgate.wait_for_endpoints('user.80-.master', 1) - utils.vtgate.wait_for_endpoints('lookup.0.master', 1) - except: - tearDownModule() - raise - - -def tearDownModule(): - logging.debug('in tearDownModule') - utils.required_teardown() - if utils.options.skip_teardown: - return - logging.debug('Tearing down the servers and setup') - if keyspace_env: - keyspace_env.teardown() - - environment.topo_server().teardown() - - utils.kill_sub_processes() - utils.remove_tmp_files() - - -def get_connection(timeout=10.0): - protocol, endpoint = utils.vtgate.rpc_endpoint(python=True) - try: - return vtgate_client.connect(protocol, endpoint, timeout) - except Exception: - logging.exception('Connection to vtgate (timeout=%s) failed.', timeout) - raise - - -class TestVTGateFunctions(unittest.TestCase): - - decimal_type = 18 - int_type = 265 - string_type = 6165 - varbinary_type = 10262 - - def setUp(self): - self.master_tablet = shard_1_master - - def execute_on_master(self, vtgate_conn, sql, bind_vars): - return vtgate_conn._execute( - sql, bind_vars, tablet_type='master', keyspace_name=None) - - def test_health(self): - f = urllib.urlopen('http://localhost:%d/debug/health' % utils.vtgate.port) - response = f.read() - f.close() - self.assertEqual(response, 'ok') - - def test_srv_vschema(self): - """Makes sure the SrvVSchema object is properly built.""" - v = utils.run_vtctl_json(['GetSrvVSchema', 'test_nj']) - self.assertEqual(len(v['keyspaces']), 2, 'wrong vschema: %s' % str(v)) - self.assertIn('user', v['keyspaces']) - self.assertIn('lookup', v['keyspaces']) - - # Now deletes it. - utils.run_vtctl(['DeleteSrvVSchema', 'test_nj']) - _, stderr = utils.run_vtctl(['GetSrvVSchema', 'test_nj'], - expect_fail=True) - self.assertIn('node doesn\'t exist', stderr) - - # And rebuilds it. - utils.run_vtctl(['RebuildVSchemaGraph', '-cells=test_nj']) - v = utils.run_vtctl_json(['GetSrvVSchema', 'test_nj']) - self.assertEqual(len(v['keyspaces']), 2, 'wrong vschema: %s' % str(v)) - self.assertIn('user', v['keyspaces']) - self.assertIn('lookup', v['keyspaces']) - - # Wait for vtgate to re-read it. - timeout = 10 - while True: - vschema_json = utils.vtgate.get_vschema() - if 'lookup' in vschema_json: - break - timeout = utils.wait_step('vtgate re-read vschema', timeout) - - def test_user(self): - count = 4 - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=None, writable=True) - - # Initialize the sequence. - # TODO(sougou): Use DDL when ready. - cursor.begin() - cursor.execute(init_vt_user_seq, {}) - cursor.commit() - - # Test insert - for x in xrange(count): - i = x+1 - cursor.begin() - cursor.execute( - 'insert into vt_user (name) values (:name)', - {'name': 'test %s' % i}) - self.assertEqual( - (cursor.fetchall(), cursor.rowcount, cursor.lastrowid, - cursor.description), - ([], 1L, i, [])) - cursor.commit() - - # Test select equal - for x in xrange(count): - i = x+1 - cursor.execute('select id, name from vt_user where id = :id', {'id': i}) - self.assertEqual( - (cursor.fetchall(), cursor.rowcount, cursor.lastrowid, - cursor.description), - ([(i, 'test %s' % i)], 1L, 0, - [('id', self.int_type), ('name', self.string_type)])) - - # Test case sensitivity - cursor.execute('select Id, Name from vt_user where iD = :id', {'id': 1}) - self.assertEqual( - (cursor.fetchall(), cursor.rowcount, cursor.lastrowid, - cursor.description), - ([(1, 'test 1')], 1L, 0, - [('Id', self.int_type), ('Name', self.string_type)])) - - # test directive timeout - try: - cursor.execute('SELECT /*vt+ QUERY_TIMEOUT_MS=10 */ SLEEP(1)', {}) - self.fail('Execute went through') - except dbexceptions.DatabaseError as e: - s = str(e) - self.assertIn(protocols_flavor().rpc_timeout_message(), s) - - # test directive timeout longer than the query time - cursor.execute('SELECT /*vt+ QUERY_TIMEOUT_MS=2000 */ SLEEP(1)', {}) - self.assertEqual( - (cursor.fetchall(), cursor.rowcount, cursor.lastrowid, - cursor.description), - ([(0,)], 1L, 0, - [(u'SLEEP(1)', self.int_type)])) - - # test shard errors as warnings directive - cursor.execute('SELECT /*vt+ SCATTER_ERRORS_AS_WARNINGS */ bad from vt_user', {}) - print vtgate_conn.get_warnings() - warnings = vtgate_conn.get_warnings() - self.assertEqual(len(warnings), 2) - for warning in warnings: - self.assertEqual(warning.code, 1054) - self.assertIn('errno 1054', warning.message) - self.assertIn('Unknown column', warning.message) - self.assertEqual( - (cursor.fetchall(), cursor.rowcount, cursor.lastrowid, - cursor.description), - ([], 0L, 0, [])) - - # test shard errors as warnings directive with timeout - cursor.execute('SELECT /*vt+ SCATTER_ERRORS_AS_WARNINGS QUERY_TIMEOUT_MS=10 */ SLEEP(1)', {}) - print vtgate_conn.get_warnings() - warnings = vtgate_conn.get_warnings() - self.assertEqual(len(warnings), 1) - for warning in warnings: - self.assertEqual(warning.code, 1317) - self.assertIn('context deadline exceeded', warning.message) - self.assertEqual( - (cursor.fetchall(), cursor.rowcount, cursor.lastrowid, - cursor.description), - ([], 0L, 0, [])) - - # Test insert with no auto-inc - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user (id, name) values (:id, :name)', - {'id': 6, 'name': 'test 6'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - - # Verify values in db - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual(result, ((1L, 'test 1'), (2L, 'test 2'), (3L, 'test 3'))) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual(result, ((4L, 'test 4'), (6L, 'test 6'))) - - # Test MultiValueInsert with no auto-inc - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user (id, name) values (:id0, :name0), (:id1, :name1)', - {'id0': 5, 'name0': 'test 5', 'id1': 7, 'name1': 'test 7'}) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - - # Verify values in db - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual(result, ((1L, 'test 1'), (2L, 'test 2'), (3L, 'test 3'), - (5L, 'test 5'))) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual(result, ((4L, 'test 4'), (6L, 'test 6'), (7L, 'test 7'))) - - # Test IN clause - result = self.execute_on_master( - vtgate_conn, - 'select id, name from vt_user where id in (:a, :b)', {'a': 1, 'b': 4}) - result[0].sort() - self.assertEqual( - result, - ([(1L, 'test 1'), (4L, 'test 4')], 2L, 0, - [('id', self.int_type), ('name', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - 'select id, name from vt_user where id in (:a, :b)', {'a': 1, 'b': 2}) - result[0].sort() - self.assertEqual( - result, - ([(1L, 'test 1'), (2L, 'test 2')], 2L, 0, - [('id', self.int_type), ('name', self.string_type)])) - - # Test scatter - result = vtgate_conn._execute( - 'select id, name from vt_user', - {}, tablet_type='master', keyspace_name=None) - result[0].sort() - self.assertEqual( - result, - ([(1L, 'test 1'), (2L, 'test 2'), (3L, 'test 3'), (4L, 'test 4'), - (5L, 'test 5'), (6L, 'test 6'), (7L, 'test 7')], 7L, 0, - [('id', self.int_type), ('name', self.string_type)])) - - # Test stream over scatter - stream_cursor_1 = vtgate_conn.cursor( - tablet_type='master', keyspace=None, - cursorclass=vtgate_cursor.StreamVTGateCursor) - stream_cursor_1.execute('select id, name from vt_user', {}) - stream_cursor_2 = vtgate_conn.cursor( - tablet_type='master', keyspace=None, - cursorclass=vtgate_cursor.StreamVTGateCursor) - stream_cursor_2.execute('select id, name from vt_user', {}) - self.assertEqual(stream_cursor_1.description, - [('id', self.int_type), ('name', self.string_type)]) - self.assertEqual(stream_cursor_2.description, - [('id', self.int_type), ('name', self.string_type)]) - rows_1 = [] - rows_2 = [] - for row_1, row_2 in itertools.izip(stream_cursor_1, stream_cursor_2): - rows_1.append(row_1) - rows_2.append(row_2) - self.assertEqual( - sorted(rows_1), - [(1L, 'test 1'), (2L, 'test 2'), (3L, 'test 3'), (4L, 'test 4'), - (5L, 'test 5'), (6L, 'test 6'), (7L, 'test 7')]) - self.assertEqual( - sorted(rows_2), - [(1L, 'test 1'), (2L, 'test 2'), (3L, 'test 3'), (4L, 'test 4'), - (5L, 'test 5'), (6L, 'test 6'), (7L, 'test 7')]) - - # Test updates - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_user set name = :name where id = :id', - {'id': 1, 'name': 'test one'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'update vt_user set name = :name where id = :id', - {'id': 4, 'name': 'test four'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual( - result, ((1L, 'test one'), (2L, 'test 2'), (3L, 'test 3'), - (5L, 'test 5'))) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual( - result, ((4L, 'test four'), (6L, 'test 6'), (7L, 'test 7'))) - - # Test deletes - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_user where id = :id', - {'id': 1}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_user where id = :id', - {'id': 4}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual(result, ((2L, 'test 2'), (3L, 'test 3'), (5L, 'test 5'))) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user') - self.assertEqual(result, ((6L, 'test 6'), (7L, 'test 7'))) - - # test passing in the keyspace in the cursor - lcursor = vtgate_conn.cursor( - tablet_type='master', keyspace='lookup', writable=True) - with self.assertRaisesRegexp( - dbexceptions.DatabaseError, '.*table vt_user not found in schema.*'): - lcursor.execute('select id, name from vt_user', {}) - - def test_user2(self): - # user2 is for testing non-unique vindexes - vtgate_conn = get_connection() - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user2 (id, name) values (:id, :name)', - {'id': 1, 'name': 'name1'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user2 (id, name) values (:id, :name)', - {'id': 7, 'name': 'name1'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user2 (id, name) values (:id0, :name0),(:id1, :name1)', - {'id0': 2, 'name0': 'name2', 'id1': 3, 'name1': 'name2'}) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((1L, 'name1'), (2L, 'name2'), (3L, 'name2'))) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((7L, 'name1'),)) - result = lookup_master.mquery( - 'vt_lookup', 'select name, user2_id from name_user2_map') - self.assertEqual(result, (('name1', 1L), ('name1', 7L), ('name2', 2L), - ('name2', 3L))) - - # Test select by id - result = self.execute_on_master( - vtgate_conn, - 'select id, name from vt_user2 where id = :id', {'id': 1}) - self.assertEqual( - result, ([(1, 'name1')], 1L, 0, - [('id', self.int_type), ('name', self.string_type)])) - - # Test select by lookup - result = self.execute_on_master( - vtgate_conn, - 'select id, name from vt_user2 where name = :name', {'name': 'name1'}) - result[0].sort() - self.assertEqual( - result, - ([(1, 'name1'), (7, 'name1')], 2L, 0, - [('id', self.int_type), ('name', self.string_type)])) - - # Test IN clause using non-unique vindex - result = self.execute_on_master( - vtgate_conn, - "select id, name from vt_user2 where name in ('name1', 'name2')", {}) - result[0].sort() - self.assertEqual( - result, - ([(1, 'name1'), (2, 'name2'), (3, 'name2'), (7, 'name1')], 4L, 0, - [('id', self.int_type), ('name', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - "select id, name from vt_user2 where name in ('name1')", {}) - result[0].sort() - self.assertEqual( - result, - ([(1, 'name1'), (7, 'name1')], 2L, 0, - [('id', self.int_type), ('name', self.string_type)])) - - # Test delete - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_user2 where id = :id', - {'id': 1}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_user2 where id = :id', - {'id': 2}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((3L, 'name2'),)) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((7L, 'name1'),)) - result = lookup_master.mquery( - 'vt_lookup', 'select name, user2_id from name_user2_map') - self.assertEqual(result, (('name1', 7L), ('name2', 3L))) - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'delete from vt_user2 where id = :id', - {'id': 7}) - vtgate_conn.commit() - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'delete from vt_user2 where id = :id', - {'id': 3}) - vtgate_conn.commit() - - # Test scatter delete - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user (id, name) values (:id0, :name0),(:id1, :name1)', - {'id0': 22, 'name0': 'name2', 'id1': 33, 'name1': 'name2'}) - self.assertEqual(result, ([], 2L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_user where id > :id', - {'id': 20}) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - - # Test scatter update - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user (id, name) values (:id0, :name0),(:id1, :name1)', - {'id0': 22, 'name0': 'name2', 'id1': 33, 'name1': 'name2'}) - self.assertEqual(result, ([], 2L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'update vt_user set name=:name where id > :id', - {'id': 20, 'name': 'jose'}) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - - def test_user_truncate(self): - vtgate_conn = get_connection() - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user2 (id, name) values (:id, :name)', - {'id': 1, 'name': 'name1'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user2 (id, name) values (:id, :name)', - {'id': 7, 'name': 'name1'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user2 (id, name) values (:id0, :name0),(:id1, :name1)', - {'id0': 2, 'name0': 'name2', 'id1': 3, 'name1': 'name2'}) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((1L, 'name1'), (2L, 'name2'), (3L, 'name2'))) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((7L, 'name1'),)) - result = lookup_master.mquery( - 'vt_lookup', 'select name, user2_id from name_user2_map') - self.assertEqual(result, (('name1', 1L), ('name1', 7L), ('name2', 2L), - ('name2', 3L))) - vtgate_conn.begin() - result = vtgate_conn._execute( - 'truncate vt_user2', - {}, - tablet_type='master', - keyspace_name='user' - ) - vtgate_conn.commit() - lookup_master.mquery('vt_lookup', 'truncate name_user2_map') - self.assertEqual(result, ([], 0L, 0L, [])) - # Test select by id - result = self.execute_on_master( - vtgate_conn, - 'select id, name from vt_user2 where id = :id', {'id': 1}) - self.assertEqual( - result, ([], 0L, 0, [('id', self.int_type), - ('name', self.string_type)])) - - def test_user_extra(self): - # user_extra is for testing unowned functional vindex - count = 4 - vtgate_conn = get_connection() - for x in xrange(count): - i = x+1 - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user_extra (user_id, email) ' - 'values (:user_id, :email)', - {'user_id': i, 'email': 'test %s' % i}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - for x in xrange(count): - i = x+1 - result = self.execute_on_master( - vtgate_conn, - 'select user_id, email from vt_user_extra where user_id = :user_id', - {'user_id': i}) - self.assertEqual( - result, - ([(i, 'test %s' % i)], 1L, 0, - [('user_id', self.int_type), ('email', self.string_type)])) - result = shard_0_master.mquery( - 'vt_user', 'select user_id, email from vt_user_extra') - self.assertEqual(result, ((1L, 'test 1'), (2L, 'test 2'), (3L, 'test 3'))) - result = shard_1_master.mquery( - 'vt_user', 'select user_id, email from vt_user_extra') - self.assertEqual(result, ((4L, 'test 4'),)) - - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_user_extra set email = :email where user_id = :user_id', - {'user_id': 1, 'email': 'test one'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'update vt_user_extra set email = :email where user_id = :user_id', - {'user_id': 4, 'email': 'test four'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery( - 'vt_user', 'select user_id, email from vt_user_extra') - self.assertEqual(result, ((1L, 'test one'), (2L, 'test 2'), (3L, 'test 3'))) - result = shard_1_master.mquery( - 'vt_user', 'select user_id, email from vt_user_extra') - self.assertEqual(result, ((4L, 'test four'),)) - - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_user_extra where user_id = :user_id', - {'user_id': 1}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_user_extra where user_id = :user_id', - {'user_id': 4}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery( - 'vt_user', 'select user_id, email from vt_user_extra') - self.assertEqual(result, ((2L, 'test 2'), (3L, 'test 3'))) - result = shard_1_master.mquery( - 'vt_user', 'select user_id, email from vt_user_extra') - self.assertEqual(result, ()) - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'delete from vt_user_extra where user_id = :user_id', - {'user_id': 2}) - self.execute_on_master( - vtgate_conn, - 'delete from vt_user_extra where user_id = :user_id', - {'user_id': 3}) - vtgate_conn.commit() - - def test_user_scatter_limit(self): - vtgate_conn = get_connection() - # Works when there is no data - result = self.execute_on_master( - vtgate_conn, - 'select id from vt_user2 order by id limit :limit offset :offset ', {'limit': 4, 'offset': 1}) - self.assertEqual( - result, ([], 0L, 0, [('id', self.int_type)])) - - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user2 (id, name) values (:id0, :name0),(:id1, :name1),(:id2, :name2), (:id3, :name3), (:id4, :name4)', - { - 'id0': 1, 'name0': 'name0', - 'id1': 2, 'name1': 'name1', - 'id2': 3, 'name2': 'name2', - 'id3': 4, 'name3': 'name3', - 'id4': 5, 'name4': 'name4', - } - ) - self.assertEqual(result, ([], 5L, 0L, [])) - vtgate_conn.commit() - # Assert that rows are in multiple shards - result = shard_0_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((1L, 'name0'), (2L, 'name1'), (3L, 'name2'), (5L, 'name4'))) - result = shard_1_master.mquery('vt_user', 'select id, name from vt_user2') - self.assertEqual(result, ((4L, 'name3'),)) - - # Works when limit is set - result = self.execute_on_master( - vtgate_conn, - 'select id from vt_user2 order by id limit :limit', {'limit': 2 }) - self.assertEqual( - result, - ([(1,),(2,),], 2L, 0, - [('id', self.int_type)])) - - - # Fetching with offset works - count = 4 - for x in xrange(count): - i = x+1 - result = self.execute_on_master( - vtgate_conn, - 'select id from vt_user2 order by id limit :limit offset :offset ', {'limit': 1, 'offset': x}) - self.assertEqual( - result, - ([(i,)], 1L, 0, - [('id', self.int_type)])) - - # Works when limit is greater than values in the table - result = self.execute_on_master( - vtgate_conn, - 'select id from vt_user2 order by id limit :limit offset :offset ', {'limit': 100, 'offset': 1}) - self.assertEqual( - result, - ([(2,),(3,),(4,),(5,)], 4L, 0, - [('id', self.int_type)])) - - # Works without bind vars - result = self.execute_on_master( - vtgate_conn, - 'select id from vt_user2 order by id limit 1 offset 1', {}) - self.assertEqual( - result, - ([(2,)], 1L, 0, - [('id', self.int_type)])) - - vtgate_conn.begin() - result = vtgate_conn._execute( - 'truncate vt_user2', - {}, - tablet_type='master', - keyspace_name='user' - ) - vtgate_conn.commit() - lookup_master.mquery('vt_lookup', 'truncate name_user2_map') - - def test_user_extra2(self): - # user_extra2 is for testing updates to secondary vindexes - vtgate_conn = get_connection() - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_user_extra2 (user_id, lastname, address) ' - 'values (:user_id, :lastname, :address)', - {'user_id': 5, 'lastname': 'nieves', 'address': 'invernalia'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - - # Updating both vindexes - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_user_extra2 set lastname = :lastname,' - ' address = :address where user_id = :user_id', - {'user_id': 5, 'lastname': 'buendia', 'address': 'macondo'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = self.execute_on_master( - vtgate_conn, - 'select lastname, address from vt_user_extra2 where user_id = 5', {}) - self.assertEqual( - result, - ([('buendia', 'macondo')], 1, 0, - [('lastname', self.string_type), - ('address', self.string_type)])) - result = lookup_master.mquery( - 'vt_lookup', 'select lastname, user_id from lastname_user_extra2_map') - self.assertEqual( - result, - (('buendia', 5L),)) - result = lookup_master.mquery( - 'vt_lookup', 'select address, user_id from address_user_extra2_map') - self.assertEqual( - result, - (('macondo', 5L),)) - - # Updating only one vindex - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_user_extra2 set address = :address where user_id = :user_id', - {'user_id': 5, 'address': 'yoknapatawpha'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = self.execute_on_master( - vtgate_conn, - 'select lastname, address from vt_user_extra2 where user_id = 5', {}) - self.assertEqual( - result, - ([('buendia', 'yoknapatawpha')], 1, 0, - [('lastname', self.string_type), - ('address', self.string_type)])) - result = lookup_master.mquery( - 'vt_lookup', 'select address, user_id from address_user_extra2_map') - self.assertEqual( - result, - (('yoknapatawpha', 5L),)) - result = lookup_master.mquery( - 'vt_lookup', 'select lastname, user_id from lastname_user_extra2_map') - self.assertEqual( - result, - (('buendia', 5L),)) - - # It works when you update to same value on unique index - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_user_extra2 set address = :address where user_id = :user_id', - {'user_id': 5, 'address': 'yoknapatawpha'}) - self.assertEqual(result, ([], 0L, 0L, [])) - vtgate_conn.commit() - result = self.execute_on_master( - vtgate_conn, - 'select lastname, address from vt_user_extra2 where user_id = 5', {}) - self.assertEqual( - result, - ([('buendia', 'yoknapatawpha')], 1, 0, - [('lastname', self.string_type), - ('address', self.string_type)])) - result = lookup_master.mquery( - 'vt_lookup', 'select lastname, user_id from lastname_user_extra2_map') - self.assertEqual( - result, - (('buendia', 5L),)) - result = lookup_master.mquery( - 'vt_lookup', 'select address, user_id from address_user_extra2_map') - self.assertEqual( - result, - (('yoknapatawpha', 5L),)) - - # you can find the record by either vindex - result = self.execute_on_master( - vtgate_conn, - 'select lastname, address from vt_user_extra2' - ' where lastname = "buendia"', {}) - self.assertEqual( - result, - ([('buendia', 'yoknapatawpha')], 1, 0, - [('lastname', self.string_type), - ('address', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - 'select lastname, address from vt_user_extra2' - ' where address = "yoknapatawpha"', {}) - self.assertEqual( - result, - ([('buendia', 'yoknapatawpha')], 1, 0, - [('lastname', self.string_type), - ('address', self.string_type)])) - - def test_multicolvin(self): - # multicolvin tests a table with a multi column vindex - vtgate_conn = get_connection() - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_multicolvin (cola, colb, colc, kid) ' - 'values (:cola, :colb, :colc, :kid)', - {'kid': 5, 'cola': 'cola_value', 'colb': 'colb_value', - 'colc': 'colc_value'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - - # Updating both vindexes - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_multicolvin set cola = :cola, colb = :colb, colc = :colc' - ' where kid = :kid', - {'kid': 5, 'cola': 'cola_newvalue', 'colb': 'colb_newvalue', - 'colc': 'colc_newvalue'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = self.execute_on_master( - vtgate_conn, - 'select cola, colb, colc from vt_multicolvin where kid = 5', {}) - self.assertEqual( - result, - ([('cola_newvalue', 'colb_newvalue', 'colc_newvalue')], 1, 0, - [('cola', self.string_type), - ('colb', self.string_type), - ('colc', self.string_type)])) - result = lookup_master.mquery( - 'vt_lookup', 'select cola from cola_map') - self.assertEqual( - result, - (('cola_newvalue',),)) - result = lookup_master.mquery( - 'vt_lookup', 'select colb, colc from colb_colc_map') - self.assertEqual( - result, - (('colb_newvalue', 'colc_newvalue'),)) - - # Updating only one vindex - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_multicolvin set colb = :colb, colc = :colc where kid = :kid', - {'kid': 5, 'colb': 'colb_newvalue2', 'colc': 'colc_newvalue2'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = self.execute_on_master( - vtgate_conn, 'select colb, colc from vt_multicolvin where kid = 5', {}) - self.assertEqual( - result, - ([('colb_newvalue2', 'colc_newvalue2')], 1, 0, - [('colb', self.string_type), - ('colc', self.string_type)])) - result = lookup_master.mquery( - 'vt_lookup', 'select colb, colc from colb_colc_map') - self.assertEqual( - result, - (('colb_newvalue2', 'colc_newvalue2'),)) - - # Works when inserting multiple rows - vtgate_conn = get_connection() - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_multicolvin (cola, colb, colc, kid) ' - 'values (:cola0, :colb0, :colc0, :kid0),' - ' (:cola1, :colb1, :colc1, :kid1)', - {'kid0': 6, 'cola0': 'cola0_value', 'colb0': 'colb0_value', - 'colc0': 'colc0_value', - 'kid1': 7, 'cola1': 'cola1_value', 'colb1': 'colb1_value', - 'colc1': 'colc1_value' - }) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - - def test_aggr(self): - # test_aggr tests text column aggregation - vtgate_conn = get_connection() - vtgate_conn.begin() - # insert upper and lower-case mixed rows in jumbled order - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_aggr (id, name) values ' - '(10, \'A\'), ' - '(9, \'a\'), ' - '(8, \'b\'), ' - '(7, \'B\'), ' - '(6, \'d\'), ' - '(5, \'c\'), ' - '(4, \'C\'), ' - '(3, \'d\'), ' - '(2, \'e\'), ' - '(1, \'E\')', - {}) - vtgate_conn.commit() - - result = self.execute_on_master( - vtgate_conn, 'select sum(id), name from vt_aggr group by name', {}) - values = [v1 for v1, v2 in result[0]] - print values - self.assertEqual( - [v1 for v1, v2 in result[0]], - [(Decimal('19')), - (Decimal('15')), - (Decimal('9')), - (Decimal('9')), - (Decimal('3'))]) - - def test_music(self): - # music is for testing owned lookup index - vtgate_conn = get_connection() - - # Initialize the sequence. - # TODO(sougou): Use DDL when ready. - vtgate_conn.begin() - self.execute_on_master(vtgate_conn, init_vt_music_seq, {}) - vtgate_conn.commit() - - count = 4 - for x in xrange(count): - i = x+1 - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_music (user_id, song) values (:user_id, :song)', - {'user_id': i, 'song': 'test %s' % i}) - self.assertEqual(result, ([], 1L, i, [])) - vtgate_conn.commit() - for x in xrange(count): - i = x+1 - result = self.execute_on_master( - vtgate_conn, - 'select user_id, id, song from vt_music where id = :id', {'id': i}) - self.assertEqual( - result, - ([(i, i, 'test %s' % i)], 1, 0, - [('user_id', self.int_type), - ('id', self.int_type), - ('song', self.string_type)])) - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_music (user_id, id, song) ' - 'values (:user_id0, :id0, :song0), (:user_id1, :id1, :song1)', - {'user_id0': 5, 'id0': 6, 'song0': 'test 6', 'user_id1': 7, 'id1': 7, - 'song1': 'test 7'}) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery( - 'vt_user', 'select user_id, id, song from vt_music') - self.assertEqual( - result, - ((1L, 1L, 'test 1'), (2L, 2L, 'test 2'), (3L, 3L, 'test 3'), - (5L, 6L, 'test 6'))) - result = shard_1_master.mquery( - 'vt_user', 'select user_id, id, song from vt_music') - self.assertEqual( - result, ((4L, 4L, 'test 4'), (7L, 7L, 'test 7'))) - result = lookup_master.mquery( - 'vt_lookup', 'select music_id, user_id from music_user_map') - self.assertEqual( - result, - ((1L, 1L), (2L, 2L), (3L, 3L), (4L, 4L), (6L, 5L), (7L, 7L))) - - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_music set song = :song where id = :id', - {'id': 6, 'song': 'test six'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'update vt_music set song = :song where id = :id', - {'id': 4, 'song': 'test four'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery( - 'vt_user', 'select user_id, id, song from vt_music') - self.assertEqual( - result, ((1L, 1L, 'test 1'), (2L, 2L, 'test 2'), (3L, 3L, 'test 3'), - (5L, 6L, 'test six'))) - result = shard_1_master.mquery( - 'vt_user', 'select user_id, id, song from vt_music') - self.assertEqual( - result, ((4L, 4L, 'test four'), (7L, 7L, 'test 7'))) - - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_music where id = :id', - {'id': 3}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_music where user_id = :user_id', - {'user_id': 4}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery( - 'vt_user', 'select user_id, id, song from vt_music') - self.assertEqual( - result, ((1L, 1L, 'test 1'), (2L, 2L, 'test 2'), (5L, 6L, 'test six'))) - result = shard_1_master.mquery( - 'vt_user', 'select user_id, id, song from vt_music') - self.assertEqual(result, ((7L, 7L, 'test 7'),)) - result = lookup_master.mquery( - 'vt_lookup', 'select music_id, user_id from music_user_map') - self.assertEqual(result, ((1L, 1L), (2L, 2L), (6L, 5L), (7L, 7L))) - - def test_music_extra(self): - # music_extra is for testing unonwed lookup index - vtgate_conn = get_connection() - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_music_extra (music_id, user_id, artist) ' - 'values (:music_id, :user_id, :artist)', - {'music_id': 1, 'user_id': 1, 'artist': 'test 1'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'insert into vt_music_extra (music_id, artist) ' - 'values (:music_id0, :artist0), (:music_id1, :artist1)', - {'music_id0': 6, 'artist0': 'test 6', 'music_id1': 7, - 'artist1': 'test 7'}) - self.assertEqual(result, ([], 2L, 0L, [])) - vtgate_conn.commit() - result = self.execute_on_master( - vtgate_conn, - 'select music_id, user_id, artist ' - 'from vt_music_extra where music_id = :music_id', - {'music_id': 6}) - self.assertEqual( - result, ([(6L, 5L, 'test 6')], 1, 0, - [('music_id', self.int_type), - ('user_id', self.int_type), - ('artist', self.string_type)])) - result = shard_0_master.mquery( - 'vt_user', 'select music_id, user_id, artist from vt_music_extra') - self.assertEqual(result, ((1L, 1L, 'test 1'), (6L, 5L, 'test 6'))) - result = shard_1_master.mquery( - 'vt_user', 'select music_id, user_id, artist from vt_music_extra') - self.assertEqual(result, ((7L, 7L, 'test 7'),)) - - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'update vt_music_extra set artist = :artist ' - 'where music_id = :music_id', - {'music_id': 6, 'artist': 'test six'}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'update vt_music_extra set artist = :artist ' - 'where music_id = :music_id', - {'music_id': 7, 'artist': 'test seven'}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery( - 'vt_user', 'select music_id, user_id, artist from vt_music_extra') - self.assertEqual(result, ((1L, 1L, 'test 1'), (6L, 5L, 'test six'))) - result = shard_1_master.mquery( - 'vt_user', 'select music_id, user_id, artist from vt_music_extra') - self.assertEqual(result, ((7L, 7L, 'test seven'),)) - - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_music_extra where music_id = :music_id', - {'music_id': 6}) - self.assertEqual(result, ([], 1L, 0L, [])) - result = self.execute_on_master( - vtgate_conn, - 'delete from vt_music_extra where music_id = :music_id', - {'music_id': 7}) - self.assertEqual(result, ([], 1L, 0L, [])) - vtgate_conn.commit() - result = shard_0_master.mquery( - 'vt_user', 'select music_id, user_id, artist from vt_music_extra') - self.assertEqual(result, ((1L, 1L, 'test 1'),)) - result = shard_1_master.mquery( - 'vt_user', 'select music_id, user_id, artist from vt_music_extra') - self.assertEqual(result, ()) - - def test_main_seq(self): - vtgate_conn = get_connection() - - # Initialize the sequence. - # TODO(sougou): Use DDL when ready. - vtgate_conn.begin() - self.execute_on_master(vtgate_conn, init_vt_main_seq, {}) - vtgate_conn.commit() - - count = 4 - for x in xrange(count): - i = x+1 - vtgate_conn.begin() - result = self.execute_on_master( - vtgate_conn, - 'insert into main (val) values (:val)', - {'val': 'test %s' % i}) - self.assertEqual(result, ([], 1L, i, [])) - vtgate_conn.commit() - - result = self.execute_on_master( - vtgate_conn, 'select id, val from main where id = 4', {}) - self.assertEqual( - result, - ([(4, 'test 4')], 1, 0, - [('id', self.int_type), - ('val', self.string_type)])) - - # Now test direct calls to sequence. - result = self.execute_on_master( - vtgate_conn, 'select next 1 values from vt_main_seq', {}) - self.assertEqual( - result, - ([(5,)], 1, 0, - [('nextval', self.int_type)])) - - def test_upsert(self): - vtgate_conn = get_connection() - - # Create lookup entries for primary vindex: - # No entry for 2. upsert_primary is not owned. - # So, we need to pre-create entries that the - # subsequent will insert will use to compute the - # keyspace id. - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'insert into upsert_primary(id, ksnum_id) values' - '(1, 1), (3, 3), (4, 4), (5, 5), (6, 6)', - {}) - vtgate_conn.commit() - - # Create rows on the main table. - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'insert into upsert(pk, owned, user_id, col) values' - '(1, 1, 1, 0), (3, 3, 3, 0), (4, 4, 4, 0), (5, 5, 5, 0), (6, 6, 6, 0)', - {}) - vtgate_conn.commit() - - # Now upsert: 1, 5 and 6 should succeed. - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'insert into upsert(pk, owned, user_id, col) values' - '(1, 1, 1, 1), (2, 2, 2, 2), (3, 1, 1, 3), (4, 4, 1, 4), ' - '(5, 5, 5, 5), (6, 6, 6, 6) ' - 'on duplicate key update col = values(col)', - {}) - vtgate_conn.commit() - - result = self.execute_on_master( - vtgate_conn, - 'select pk, owned, user_id, col from upsert order by pk', - {}) - self.assertEqual( - result[0], - [(1, 1, 1, 1), (3, 3, 3, 0), (4, 4, 4, 0), - (5, 5, 5, 5), (6, 6, 6, 6)]) - - # insert ignore - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'insert into upsert_primary(id, ksnum_id) values(7, 7)', - {}) - vtgate_conn.commit() - # 1 will be sent but will not change existing row. - # 2 will not be sent because there is no keyspace id for it. - # 7 will be sent and will create a row. - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'insert ignore into upsert(pk, owned, user_id, col) values' - '(1, 1, 1, 2), (2, 2, 2, 2), (7, 7, 7, 7)', - {}) - vtgate_conn.commit() - - result = self.execute_on_master( - vtgate_conn, - 'select pk, owned, user_id, col from upsert order by pk', - {}) - self.assertEqual( - result[0], - [(1, 1, 1, 1), (3, 3, 3, 0), (4, 4, 4, 0), - (5, 5, 5, 5), (6, 6, 6, 6), (7, 7, 7, 7)]) - - def test_joins_subqueries(self): - vtgate_conn = get_connection() - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'insert into join_user (id, name) values (:id, :name)', - {'id': 1, 'name': 'name1'}) - self.execute_on_master( - vtgate_conn, - 'insert into join_user_extra (user_id, email) ' - 'values (:user_id, :email)', - {'user_id': 1, 'email': 'email1'}) - self.execute_on_master( - vtgate_conn, - 'insert into join_user_extra (user_id, email) ' - 'values (:user_id, :email)', - {'user_id': 2, 'email': 'email2'}) - self.execute_on_master( - vtgate_conn, - 'insert into join_name_info (name, info) ' - 'values (:name, :info)', - {'name': 'name1', 'info': 'name test'}) - vtgate_conn.commit() - result = self.execute_on_master( - vtgate_conn, - 'select u.id, u.name, e.user_id, e.email ' - 'from join_user u join join_user_extra e where e.user_id = u.id', - {}) - self.assertEqual( - result, - ([(1L, 'name1', 1L, 'email1')], - 1, - 0, - [('id', self.int_type), - ('name', self.string_type), - ('user_id', self.int_type), - ('email', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - 'select u.id, u.name, e.user_id, e.email ' - 'from join_user u join join_user_extra e where e.user_id = u.id+1', - {}) - self.assertEqual( - result, - ([(1L, 'name1', 2L, 'email2')], - 1, - 0, - [('id', self.int_type), - ('name', self.string_type), - ('user_id', self.int_type), - ('email', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - 'select u.id, u.name, e.user_id, e.email ' - 'from join_user u left join join_user_extra e on e.user_id = u.id+1', - {}) - self.assertEqual( - result, - ([(1L, 'name1', 2L, 'email2')], - 1, - 0, - [('id', self.int_type), - ('name', self.string_type), - ('user_id', self.int_type), - ('email', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - 'select u.id, u.name, e.user_id, e.email ' - 'from join_user u left join join_user_extra e on e.user_id = u.id+2', - {}) - self.assertEqual( - result, - ([(1L, 'name1', None, None)], - 1, - 0, - [('id', self.int_type), - ('name', self.string_type), - ('user_id', self.int_type), - ('email', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - 'select u.id, u.name, e.user_id, e.email ' - 'from join_user u join join_user_extra e on e.user_id = u.id+2 ' - 'where u.id = 2', - {}) - self.assertEqual( - result, - ([], - 0, - 0, - [('id', self.int_type), - ('name', self.string_type), - ('user_id', self.int_type), - ('email', self.string_type)])) - result = self.execute_on_master( - vtgate_conn, - 'select u.id, u.name, n.info ' - 'from join_user u join join_name_info n on u.name = n.name ' - 'where u.id = 1', - {}) - self.assertEqual( - result, - ([(1L, 'name1', 'name test')], - 1, - 0, - [('id', self.int_type), - ('name', self.string_type), - ('info', self.string_type)])) - - # test a cross-shard subquery - result = self.execute_on_master( - vtgate_conn, - 'select id, name from join_user ' - 'where id in (select user_id from join_user_extra)', - {}) - self.assertEqual( - result, - ([(1L, 'name1')], - 1, - 0, - [('id', self.int_type), - ('name', self.string_type)])) - vtgate_conn.begin() - self.execute_on_master( - vtgate_conn, - 'delete from join_user where id = :id', - {'id': 1}) - self.execute_on_master( - vtgate_conn, - 'delete from join_user_extra where user_id = :user_id', - {'user_id': 1}) - self.execute_on_master( - vtgate_conn, - 'delete from join_user_extra where user_id = :user_id', - {'user_id': 2}) - vtgate_conn.commit() - - def test_insert_value_required(self): - vtgate_conn = get_connection() - try: - vtgate_conn.begin() - with self.assertRaisesRegexp(dbexceptions.DatabaseError, - '.*could not map.*NULL.*to a keyspace id.*'): - self.execute_on_master( - vtgate_conn, - 'insert into vt_user_extra (email) values (:email)', - {'email': 'test 10'}) - finally: - vtgate_conn.rollback() - - def test_vindex_func(self): - vtgate_conn = get_connection() - result = self.execute_on_master( - vtgate_conn, - 'select id, keyspace_id from hash_index where id = :id', - {'id': 1}) - self.assertEqual( - result, - ([('1', '\x16k@\xb4J\xbaK\xd6')], - 1, - 0, - [('id', self.varbinary_type), - ('keyspace_id', self.varbinary_type)])) - - def test_analyze_table(self): - vtgate_conn = get_connection() - self.execute_on_master( - vtgate_conn, - 'use user', - {}) - result = self.execute_on_master( - vtgate_conn, - 'analyze table vt_user', - {}) - self.assertEqual( - result[0], - [('vt_user.vt_user', 'analyze', 'status', 'OK')]) - - def test_transaction_modes(self): - vtgate_conn = get_connection() - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=None, writable=True, single_db=True) - cursor.begin() - cursor.execute( - 'insert into twopc_user (user_id, val) values(1, \'val\')', {}) - with self.assertRaisesRegexp( - dbexceptions.DatabaseError, '.*multi-db transaction attempted.*'): - cursor.execute( - 'insert into twopc_lookup (id, val) values(1, \'val\')', {}) - - cursor = vtgate_conn.cursor( - tablet_type='master', keyspace=None, writable=True, twopc=True) - cursor.begin() - cursor.execute( - 'insert into twopc_user (user_id, val) values(1, \'val\')', {}) - cursor.execute( - 'insert into twopc_lookup (id, val) values(1, \'val\')', {}) - cursor.commit() - - cursor.execute('select user_id, val from twopc_user where user_id = 1', {}) - self.assertEqual(cursor.fetchall(), [(1, 'val')]) - cursor.execute('select id, val from twopc_lookup where id = 1', {}) - self.assertEqual(cursor.fetchall(), [(1, 'val')]) - - cursor.begin() - cursor.execute('delete from twopc_user where user_id = 1', {}) - cursor.execute('delete from twopc_lookup where id = 1', {}) - cursor.commit() - - cursor.execute('select user_id, val from twopc_user where user_id = 1', {}) - self.assertEqual(cursor.fetchall(), []) - cursor.execute('select id, val from twopc_lookup where id = 1', {}) - self.assertEqual(cursor.fetchall(), []) - - def test_vtclient(self): - """This test uses vtclient to send and receive various queries. - """ - # specify a good default keyspace for the connection here. - utils.vtgate.vtclient( - 'insert into vt_user_extra(user_id, email) values (:v1, :v2)', - keyspace='user', - bindvars=[10, 'test 10']) - - out, _ = utils.vtgate.vtclient( - 'select user_id, email from vt_user_extra where user_id = :v1', - bindvars=[10], json_output=True) - self.assertEqual(out, { - u'fields': [u'user_id', u'email'], - u'rows': [[u'10', u'test 10']], - }) - - utils.vtgate.vtclient( - 'update vt_user_extra set email=:v2 where user_id = :v1', - bindvars=[10, 'test 1000']) - - out, _ = utils.vtgate.vtclient( - 'select user_id, email from vt_user_extra where user_id = :v1', - bindvars=[10], streaming=True, json_output=True) - self.assertEqual(out, { - u'fields': [u'user_id', u'email'], - u'rows': [[u'10', u'test 1000']], - }) - - utils.vtgate.vtclient( - 'delete from vt_user_extra where user_id = :v1', bindvars=[10]) - - out, _ = utils.vtgate.vtclient( - 'select user_id, email from vt_user_extra where user_id = :v1', - bindvars=[10], json_output=True) - self.assertEqual(out, { - u'fields': [u'user_id', u'email'], - u'rows': None, - }) - - # check that specifying an invalid keyspace is propagated and triggers an - # error - _, err = utils.vtgate.vtclient( - 'insert into vt_user_extra(user_id, email) values (:v1, :v2)', - keyspace='invalid', - bindvars=[10, 'test 10'], - raise_on_error=False) - self.assertIn('keyspace invalid not found in vschema', err) - - def test_vtctl_vtgate_execute(self): - """This test uses 'vtctl VtGateExecute' to send and receive various queries. - """ - utils.vtgate.execute( - 'insert into vt_user_extra(user_id, email) values (:user_id, :email)', - bindvars={'user_id': 11, 'email': 'test 11'}) - - qr = utils.vtgate.execute( - 'select user_id, email from vt_user_extra where user_id = :user_id', - bindvars={'user_id': 11}) - logging.debug('Original row: %s', str(qr)) - self.assertEqual(qr['fields'][0]['name'], 'user_id') - self.assertEqual(len(qr['rows']), 1) - v = qr['rows'][0][1] - self.assertEqual(v, 'test 11') - - # test using exclude_field_names works. - qr = utils.vtgate.execute( - 'select user_id, email from vt_user_extra where user_id = :user_id', - bindvars={'user_id': 11}, execute_options='included_fields:TYPE_ONLY ') - logging.debug('Original row: %s', str(qr)) - self.assertNotIn('name', qr['fields'][0]) - self.assertEqual(len(qr['rows']), 1) - v = qr['rows'][0][1] - self.assertEqual(v, 'test 11') - - utils.vtgate.execute( - 'update vt_user_extra set email=:email where user_id = :user_id', - bindvars={'user_id': 11, 'email': 'test 1100'}) - - qr = utils.vtgate.execute( - 'select user_id, email from vt_user_extra where user_id = :user_id', - bindvars={'user_id': 11}) - logging.debug('Modified row: %s', str(qr)) - self.assertEqual(len(qr['rows']), 1) - v = qr['rows'][0][1] - self.assertEqual(v, 'test 1100') - - utils.vtgate.execute( - 'delete from vt_user_extra where user_id = :user_id', - bindvars={'user_id': 11}) - - qr = utils.vtgate.execute( - 'select user_id, email from vt_user_extra where user_id = :user_id', - bindvars={'user_id': 11}) - self.assertEqual(len(qr['rows'] or []), 0) - - def test_split_query(self): - """This test uses 'vtctl VtGateSplitQuery' to validate the Map-Reduce APIs. - - We want to return KeyRange queries. - """ - sql = 'select id, name from vt_user' - s = utils.vtgate.split_query(sql, 'user', 2) - self.assertEqual(len(s), 2) - first_half_queries = 0 - second_half_queries = 0 - for q in s: - self.assertEqual(q['query']['sql'], sql) - self.assertIn('key_range_part', q) - self.assertEqual(len(q['key_range_part']['key_ranges']), 1) - kr = q['key_range_part']['key_ranges'][0] - eighty_in_base64 = 'gA==' - is_first_half = 'start' not in kr and kr['end'] == eighty_in_base64 - is_second_half = 'end' not in kr and kr['start'] == eighty_in_base64 - self.assertTrue(is_first_half or is_second_half, - 'invalid keyrange %s' % str(kr)) - if is_first_half: - first_half_queries += 1 - else: - second_half_queries += 1 - self.assertEqual(first_half_queries, 1, 'invalid split %s' % str(s)) - self.assertEqual(second_half_queries, 1, 'invalid split %s' % str(s)) - - def test_vschema_vars(self): - """Tests the variables exported by vtgate. - - This test needs to run as the last test, as it depends on what happened - previously. - """ - v = utils.vtgate.get_vars() - self.assertIn('VtgateVSchemaCounts', v) - self.assertIn('Reload', v['VtgateVSchemaCounts']) - self.assertGreater(v['VtgateVSchemaCounts']['Reload'], 0) - self.assertNotIn('WatchError', v['VtgateVSchemaCounts'], 0) - self.assertNotIn('Parsing', v['VtgateVSchemaCounts']) - -if __name__ == '__main__': - utils.main() diff --git a/test/vttest_sample_test.py b/test/vttest_sample_test.py deleted file mode 100755 index 73a75990956..00000000000 --- a/test/vttest_sample_test.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Sample test for vttest framework. - -This sample test demonstrates how to setup and teardown a test -database with the associated Vitess processes. It is meant to be used -as a template for unit tests, by developers writing applications -on top of Vitess. The recommended workflow is to have the schema for the -database checked into a source control system, along with the application -layer code. Then unit tests can launch a test cluster for unit tests -(using local_database.py) pointing at that schema, and run all their unit -tests. - -This unit test is written in python, but we don't depend on this at all. -We just execute py/vttest/local_database.py, as we would in any language. -Then we wait for the JSON config string, and we know the address of the -vtgate process. At the end of the test, sending a line to the underlying -process makes it clean up. -""" - -import json -import os -import struct -import subprocess -import time -import urllib - -import unittest - -from google.protobuf import text_format - -from vtproto import topodata_pb2 -from vtproto import vttest_pb2 - -from vtdb import dbexceptions -from vtdb import proto3_encoding -from vtdb import vtgate_client -from vtdb import vtgate_cursor - -import utils -import environment -from protocols_flavor import protocols_flavor - - -pack_kid = struct.Struct('!Q').pack - - -def get_keyspace_id(row_id): - return row_id - - -class TestMysqlctl(unittest.TestCase): - - def test_standalone(self): - """Sample test for run_local_database.py as a standalone process.""" - - topology = vttest_pb2.VTTestTopology() - keyspace = topology.keyspaces.add(name='test_keyspace') - keyspace.replica_count = 2 - keyspace.rdonly_count = 1 - keyspace.shards.add(name='-80') - keyspace.shards.add(name='80-') - topology.keyspaces.add(name='redirect', served_from='test_keyspace') - - # launch a backend database based on the provided topology and schema - port = environment.reserve_ports(1) - args = [environment.run_local_database, - '--port', str(port), - '--proto_topo', text_format.MessageToString(topology, - as_one_line=True), - '--schema_dir', os.path.join(environment.vtroot, 'test', - 'vttest_schema'), - ] - sp = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - config = json.loads(sp.stdout.readline()) - - # gather the vars for the vtgate process - url = 'http://localhost:%d/debug/vars' % config['port'] - f = urllib.urlopen(url) - data = f.read() - f.close() - json_vars = json.loads(data) - self.assertIn('vtcombo', json_vars['cmdline'][0]) - - # build the vtcombo address and protocol - protocol = protocols_flavor().vttest_protocol() - if protocol == 'grpc': - vtgate_addr = 'localhost:%d' % config['grpc_port'] - else: - vtgate_addr = 'localhost:%d' % config['port'] - conn_timeout = 30.0 - utils.pause('Paused test after vtcombo was started.\n' - 'For manual testing, connect to vtgate at: %s ' - 'using protocol: %s.\n' - 'Press enter to continue.' % (vtgate_addr, protocol)) - - # Remember the current timestamp after we sleep for a bit, so we - # can use it for UpdateStream later. - time.sleep(2) - before_insert = long(time.time()) - - # Connect to vtgate. - conn = vtgate_client.connect(protocol, vtgate_addr, conn_timeout) - - # Insert a row. - row_id = 123 - keyspace_id = get_keyspace_id(row_id) - cursor = conn.cursor( - tablet_type='master', keyspace='test_keyspace', - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - insert = ('insert into test_table (id, msg, keyspace_id) values (:id, ' - ':msg, :keyspace_id)') - bind_variables = { - 'id': row_id, - 'msg': 'test %s' % row_id, - 'keyspace_id': keyspace_id, - } - cursor.execute(insert, bind_variables) - cursor.commit() - - # Read the row back. - cursor.execute( - 'select * from test_table where id=:id', {'id': row_id}) - result = cursor.fetchall() - self.assertEqual(result[0][1], 'test 123') - - # try to insert again, see if we get the right integrity error exception - # (this is meant to test vtcombo properly returns exceptions, and to a - # lesser extent that the python client converts it properly) - cursor.begin() - with self.assertRaises(dbexceptions.IntegrityError): - cursor.execute(insert, bind_variables) - cursor.rollback() - - # Insert a bunch of rows with long msg values. - bind_variables['msg'] = 'x' * 64 - id_start = 1000 - rowcount = 500 - cursor.begin() - for i in xrange(id_start, id_start+rowcount): - bind_variables['id'] = i - bind_variables['keyspace_id'] = get_keyspace_id(i) - cursor.execute(insert, bind_variables) - cursor.commit() - cursor.close() - - # Try to fetch a large number of rows, from a rdonly - # (more than one streaming result packet). - stream_cursor = conn.cursor( - tablet_type='rdonly', keyspace='test_keyspace', - keyspace_ids=[pack_kid(keyspace_id)], - cursorclass=vtgate_cursor.StreamVTGateCursor) - stream_cursor.execute('select * from test_table where id >= :id_start', - {'id_start': id_start}) - self.assertEqual(rowcount, len(list(stream_cursor.fetchall()))) - stream_cursor.close() - - # try to read a row using the redirected keyspace, to a replica this time - row_id = 123 - keyspace_id = get_keyspace_id(row_id) - cursor = conn.cursor( - tablet_type='replica', keyspace='redirect', - keyspace_ids=[pack_kid(keyspace_id)]) - cursor.execute( - 'select * from test_table where id=:id', {'id': row_id}) - result = cursor.fetchall() - self.assertEqual(result[0][1], 'test 123') - cursor.close() - - # Try to get the update stream from the connection. This makes - # sure that part works as well. - count = 0 - for (event, _) in conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - timestamp=before_insert, - shard='-80'): - for statement in event.statements: - if statement.table_name == 'test_table': - count += 1 - if count == rowcount + 1: - # We're getting the initial value, plus the 500 updates. - break - - # Insert a sentinel value into the second shard. - row_id = 0x8100000000000000 - keyspace_id = get_keyspace_id(row_id) - cursor = conn.cursor( - tablet_type='master', keyspace='test_keyspace', - keyspace_ids=[pack_kid(keyspace_id)], - writable=True) - cursor.begin() - bind_variables = { - 'id': row_id, - 'msg': 'test %s' % row_id, - 'keyspace_id': keyspace_id, - } - cursor.execute(insert, bind_variables) - cursor.commit() - cursor.close() - - # Try to connect to an update stream on the other shard. - # We may get some random update stream events, but we should not get any - # event that's related to the first shard. Only events related to - # the Insert we just did. - found = False - for (event, _) in conn.update_stream( - 'test_keyspace', topodata_pb2.MASTER, - timestamp=before_insert, - shard='80-'): - for statement in event.statements: - self.assertEqual(statement.table_name, 'test_table') - fields, rows = proto3_encoding.convert_stream_event_statement(statement) - self.assertEqual(fields[0], 'id') - self.assertEqual(rows[0][0], row_id) - found = True - if found: - break - - # Clean up the connection - conn.close() - - # Test we can connect to vtcombo for vtctl actions - protocol = protocols_flavor().vtctl_python_client_protocol() - if protocol == 'grpc': - vtgate_addr = 'localhost:%d' % config['grpc_port'] - else: - vtgate_addr = 'localhost:%d' % config['port'] - out, _ = utils.run( - environment.binary_args('vtctlclient') + - ['-vtctl_client_protocol', protocol, - '-server', vtgate_addr, - '-stderrthreshold', '0', - 'ListAllTablets', 'test', - ], trap_output=True) - num_master = 0 - num_replica = 0 - num_rdonly = 0 - num_dash_80 = 0 - num_80_dash = 0 - for line in out.splitlines(): - parts = line.split() - self.assertEqual(parts[1], 'test_keyspace', - 'invalid keyspace in line: %s' % line) - if parts[3] == 'master': - num_master += 1 - elif parts[3] == 'replica': - num_replica += 1 - elif parts[3] == 'rdonly': - num_rdonly += 1 - else: - self.fail('invalid tablet type in line: %s' % line) - if parts[2] == '-80': - num_dash_80 += 1 - elif parts[2] == '80-': - num_80_dash += 1 - else: - self.fail('invalid shard name in line: %s' % line) - self.assertEqual(num_master, 2) - self.assertEqual(num_replica, 2) - self.assertEqual(num_rdonly, 2) - self.assertEqual(num_dash_80, 3) - self.assertEqual(num_80_dash, 3) - - # and we're done, clean-up process - sp.stdin.write('\n') - sp.wait() - -if __name__ == '__main__': - utils.main() diff --git a/test/vttest_schema/default/test_table.sql b/test/vttest_schema/default/test_table.sql deleted file mode 100644 index 72e6b10edbc..00000000000 --- a/test/vttest_schema/default/test_table.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE TABLE test_table ( - `id` BIGINT(20) UNSIGNED NOT NULL, - `msg` VARCHAR(64), - `keyspace_id` BIGINT(20) UNSIGNED NOT NULL, - PRIMARY KEY (id) -) ENGINE=InnoDB diff --git a/test/vttest_schema/test_keyspace/test_table.sql b/test/vttest_schema/test_keyspace/test_table.sql deleted file mode 100644 index 72e6b10edbc..00000000000 --- a/test/vttest_schema/test_keyspace/test_table.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE TABLE test_table ( - `id` BIGINT(20) UNSIGNED NOT NULL, - `msg` VARCHAR(64), - `keyspace_id` BIGINT(20) UNSIGNED NOT NULL, - PRIMARY KEY (id) -) ENGINE=InnoDB diff --git a/test/worker.py b/test/worker.py deleted file mode 100755 index 0d955bf94f8..00000000000 --- a/test/worker.py +++ /dev/null @@ -1,705 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Tests the robustness and resiliency of vtworkers.""" - -from collections import namedtuple -import urllib -import urllib2 - -import logging -import unittest - -from vtdb import keyrange_constants - -import base_sharding -import environment -import tablet -import utils - - -KEYSPACE_ID_TYPE = keyrange_constants.KIT_UINT64 - - -class ShardTablets(namedtuple('ShardTablets', 'master replicas rdonlys')): - """ShardTablets is a container for all the tablet.Tablets of a shard. - - `master` should be a single Tablet, while `replicas` and `rdonlys` should be - lists of Tablets of the appropriate types. - """ - - @property - def all_tablets(self): - """Returns a list of all the tablets of the shard. - - Does not guarantee any ordering on the returned tablets. - - Returns: - List of all tablets of the shard. - """ - return [self.master] + self.replicas + self.rdonlys - - @property - def replica(self): - """Returns the first replica Tablet instance for the shard, or None.""" - if self.replicas: - return self.replicas[0] - else: - return None - - @property - def rdonly(self): - """Returns the first replica Tablet instance for the shard, or None.""" - if self.rdonlys: - return self.rdonlys[0] - else: - return None - - def __str__(self): - return """master %s -replicas: -%s -rdonlys: -%s -""" % (self.master, - '\n'.join(' %s' % replica for replica in self.replicas), - '\n'.join(' %s' % rdonly for rdonly in self.rdonlys)) - -# initial shard, covers everything -shard_master = tablet.Tablet() -shard_replica = tablet.Tablet() -shard_rdonly1 = tablet.Tablet() - -# split shards -# range '' - 80 -shard_0_master = tablet.Tablet() -shard_0_replica = tablet.Tablet() -shard_0_rdonly1 = tablet.Tablet() -# range 80 - '' -shard_1_master = tablet.Tablet() -shard_1_replica = tablet.Tablet() -shard_1_rdonly1 = tablet.Tablet() - -all_shard_tablets = ShardTablets(shard_master, [shard_replica], [shard_rdonly1]) -shard_0_tablets = ShardTablets( - shard_0_master, [shard_0_replica], [shard_0_rdonly1]) -shard_1_tablets = ShardTablets( - shard_1_master, [shard_1_replica], [shard_1_rdonly1]) - - -def init_keyspace(): - """Creates a `test_keyspace` keyspace with a sharding key.""" - utils.run_vtctl( - ['CreateKeyspace', '-sharding_column_name', 'keyspace_id', - '-sharding_column_type', KEYSPACE_ID_TYPE, 'test_keyspace']) - - -def setUpModule(): - try: - environment.topo_server().setup() - - setup_procs = [ - shard_master.init_mysql(), - shard_replica.init_mysql(), - shard_rdonly1.init_mysql(), - shard_0_master.init_mysql(), - shard_0_replica.init_mysql(), - shard_0_rdonly1.init_mysql(), - shard_1_master.init_mysql(), - shard_1_replica.init_mysql(), - shard_1_rdonly1.init_mysql(), - ] - utils.wait_procs(setup_procs) - init_keyspace() - logging.debug('environment set up with the following shards and tablets:') - logging.debug('=========================================================') - logging.debug('TABLETS: test_keyspace/0:\n%s', all_shard_tablets) - logging.debug('TABLETS: test_keyspace/-80:\n%s', shard_0_tablets) - logging.debug('TABLETS: test_keyspace/80-:\n%s', shard_1_tablets) - except: - tearDownModule() - raise - - -def tearDownModule(): - utils.required_teardown() - if utils.options.skip_teardown: - return - - teardown_procs = [ - shard_master.teardown_mysql(), - shard_replica.teardown_mysql(), - shard_rdonly1.teardown_mysql(), - shard_0_master.teardown_mysql(), - shard_0_replica.teardown_mysql(), - shard_0_rdonly1.teardown_mysql(), - shard_1_master.teardown_mysql(), - shard_1_replica.teardown_mysql(), - shard_1_rdonly1.teardown_mysql(), - ] - utils.wait_procs(teardown_procs, raise_on_error=False) - - environment.topo_server().teardown() - utils.kill_sub_processes() - utils.remove_tmp_files() - - shard_master.remove_tree() - shard_replica.remove_tree() - shard_rdonly1.remove_tree() - shard_0_master.remove_tree() - shard_0_replica.remove_tree() - shard_0_rdonly1.remove_tree() - shard_1_master.remove_tree() - shard_1_replica.remove_tree() - shard_1_rdonly1.remove_tree() - - -class TestBaseSplitClone(unittest.TestCase, base_sharding.BaseShardingTest): - """Abstract test base class for testing the SplitClone worker.""" - - def __init__(self, *args, **kwargs): - super(TestBaseSplitClone, self).__init__(*args, **kwargs) - self.num_insert_rows = utils.options.num_insert_rows - - def run_shard_tablets( - self, shard_name, shard_tablets, create_table=True): - """Handles all the necessary work for initially running a shard's tablets. - - This encompasses the following steps: - 1. (optional) Create db - 2. Starting vttablets and let themselves init them - 3. Waiting for the appropriate vttablet state - 4. Force reparent to the master tablet - 5. RebuildKeyspaceGraph - 7. (optional) Running initial schema setup - - Args: - shard_name: the name of the shard to start tablets in - shard_tablets: an instance of ShardTablets for the given shard - create_table: boolean, True iff we should create a table on the tablets - """ - # Start tablets. - # - # NOTE: The future master has to be started with type 'replica'. - shard_tablets.master.start_vttablet( - wait_for_state=None, init_tablet_type='replica', - init_keyspace='test_keyspace', init_shard=shard_name, - binlog_use_v3_resharding_mode=False) - for t in shard_tablets.replicas: - t.start_vttablet( - wait_for_state=None, init_tablet_type='replica', - init_keyspace='test_keyspace', init_shard=shard_name, - binlog_use_v3_resharding_mode=False) - for t in shard_tablets.rdonlys: - t.start_vttablet( - wait_for_state=None, init_tablet_type='rdonly', - init_keyspace='test_keyspace', init_shard=shard_name, - binlog_use_v3_resharding_mode=False) - - # Block until tablets are up and we can enable replication. - # All tables should be NOT_SERVING until we run InitShardMaster. - for t in shard_tablets.all_tablets: - t.wait_for_vttablet_state('NOT_SERVING') - - # Reparent to choose an initial master and enable replication. - utils.run_vtctl( - ['InitShardMaster', '-force', 'test_keyspace/%s' % shard_name, - shard_tablets.master.tablet_alias], auto_log=True) - timeout = 10 - while True: - shard = utils.run_vtctl_json(['GetShard', 'test_keyspace/%s' % shard_name]) - if shard['master_alias']['uid'] == shard_tablets.master.tablet_uid: - break - wait_step('master_alias has been set', timeout) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - - # Enforce a health check instead of waiting for the next periodic one. - # (saves up to 1 second execution time on average) - for t in shard_tablets.replicas: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - for t in shard_tablets.rdonlys: - utils.run_vtctl(['RunHealthCheck', t.tablet_alias]) - - # Wait for tablet state to change after starting all tablets. This allows - # us to start all tablets at once, instead of sequentially waiting. - # NOTE: Replication has to be enabled first or the health check will - # set a replica or rdonly tablet back to NOT_SERVING. - for t in shard_tablets.all_tablets: - t.wait_for_vttablet_state('SERVING') - - create_table_sql = ( - 'create table worker_test(' - 'id bigint unsigned,' - 'msg varchar(64),' - 'keyspace_id bigint(20) unsigned not null,' - 'primary key (id),' - 'index by_msg (msg)' - ') Engine=InnoDB' - ) - - if create_table: - utils.run_vtctl(['ApplySchema', - '-sql=' + create_table_sql, - 'test_keyspace'], - auto_log=True) - - def copy_schema_to_destination_shards(self): - for keyspace_shard in ('test_keyspace/-80', 'test_keyspace/80-'): - utils.run_vtctl(['CopySchemaShard', - '--exclude_tables', 'unrelated', - shard_rdonly1.tablet_alias, - keyspace_shard], - auto_log=True) - - def _insert_values(self, vttablet, id_offset, msg, keyspace_id, num_values): - """Inserts values into MySQL along with the required routing comments. - - Args: - vttablet: the Tablet instance to modify. - id_offset: offset for the value of `id` column. - msg: the value of `msg` column. - keyspace_id: the value of `keyspace_id` column. - num_values: number of rows to be inserted. - """ - - # For maximum performance, multiple values are inserted in one statement. - # However, when the statements are too long, queries will timeout and - # vttablet will kill them. Therefore, we chunk it into multiple statements. - def chunks(full_list, n): - """Yield successive n-sized chunks from full_list.""" - for i in xrange(0, len(full_list), n): - yield full_list[i:i+n] - - max_chunk_size = 100*1000 - k = utils.uint64_to_hex(keyspace_id) - for chunk in chunks(range(1, num_values+1), max_chunk_size): - logging.debug('Inserting values for range [%d, %d].', chunk[0], chunk[-1]) - values_str = '' - for i in chunk: - if i != chunk[0]: - values_str += ',' - values_str += "(%d, '%s', 0x%x)" % (id_offset + i, msg, keyspace_id) - vttablet.mquery( - 'vt_test_keyspace', [ - 'begin', - 'insert into worker_test(id, msg, keyspace_id) values%s ' - '/* vtgate:: keyspace_id:%s */' % (values_str, k), - 'commit'], - write=True) - - def insert_values(self, vttablet, num_values, num_shards, offset=0, - keyspace_id_range=2**64): - """Inserts simple values, one for each potential shard. - - Each row is given a message that contains the shard number, so we can easily - verify that the source and destination shards have the same data. - - Args: - vttablet: the Tablet instance to modify. - num_values: The number of values to insert. - num_shards: the number of shards that we expect to have. - offset: amount that we should offset the `id`s by. This is useful for - inserting values multiple times. - keyspace_id_range: the number of distinct values that the keyspace id - can have. - """ - shard_width = keyspace_id_range / num_shards - shard_offsets = [i * shard_width for i in xrange(num_shards)] - # TODO(mberlin): Change the "id" column values from the keyspace id to a - # counter starting at 1. The incrementing ids must - # alternate between the two shards. Without this, the - # vtworker chunking won't be well balanced across shards. - for shard_num in xrange(num_shards): - self._insert_values( - vttablet, - shard_offsets[shard_num] + offset, - 'msg-shard-%d' % shard_num, - shard_offsets[shard_num], - num_values) - - def assert_shard_data_equal( - self, shard_num, source_tablet, destination_tablet): - """Asserts source and destination tablets have identical shard data. - - Args: - shard_num: The shard number of the shard that we want to verify. - source_tablet: Tablet instance of the source shard. - destination_tablet: Tablet instance of the destination shard. - """ - select_query = ( - 'select * from worker_test where msg="msg-shard-%s" order by id asc' % - shard_num) - - # Make sure all the right rows made it from the source to the destination - source_rows = source_tablet.mquery('vt_test_keyspace', select_query) - destination_rows = destination_tablet.mquery( - 'vt_test_keyspace', select_query) - self.assertEqual(source_rows, destination_rows) - - # Make sure that there are no extra rows on the destination - count_query = 'select count(*) from worker_test' - destination_count = destination_tablet.mquery( - 'vt_test_keyspace', count_query)[0][0] - self.assertEqual(destination_count, len(destination_rows)) - - def run_split_diff(self, keyspace_shard, source_tablets, destination_tablets): - """Runs a vtworker SplitDiff on the given keyspace/shard. - - Sets all former rdonly slaves back to rdonly. - - Args: - keyspace_shard: keyspace/shard to run SplitDiff on (string) - source_tablets: ShardTablets instance for the source shard - destination_tablets: ShardTablets instance for the destination shard - """ - _ = source_tablets, destination_tablets - logging.debug('Running vtworker SplitDiff for %s', keyspace_shard) - _, _ = utils.run_vtworker( - ['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitDiff', - '--min_healthy_rdonly_tablets', '1', - keyspace_shard], auto_log=True) - - def setUp(self): - """Creates shards, starts the tablets, and inserts some data.""" - try: - self.run_shard_tablets('0', all_shard_tablets) - # create the split shards - self.run_shard_tablets( - '-80', shard_0_tablets, create_table=False) - self.run_shard_tablets( - '80-', shard_1_tablets, create_table=False) - - logging.debug('Start inserting initial data: %s rows', - self.num_insert_rows) - self.insert_values(shard_master, self.num_insert_rows, 2) - logging.debug( - 'Done inserting initial data, waiting for replication to catch up') - utils.wait_for_replication_pos(shard_master, shard_rdonly1) - logging.debug('Replication on source rdonly tablet is caught up') - except: - self.tearDown() - raise - - def tearDown(self): - """Does the minimum to reset topology and tablets to their initial states. - - When benchmarked, this seemed to take around 30% of the time of - (setupModule + tearDownModule). - - FIXME(aaijazi): doing this in parallel greatly reduces the time it takes. - See the kill_tablets method in tablet.py. - """ - - for shard_tablet in [all_shard_tablets, shard_0_tablets, shard_1_tablets]: - for t in shard_tablet.all_tablets: - t.reset_replication() - t.set_semi_sync_enabled(master=False) - t.clean_dbs() - # _vt.vreplication should be dropped to avoid interference between - # test cases - t.mquery('', 'drop table if exists _vt.vreplication') - t.kill_vttablet() - # we allow failures here as some tablets will be gone sometimes - # (the master tablets after an emergency reparent) - utils.run_vtctl(['DeleteTablet', '-allow_master', t.tablet_alias], - auto_log=True, raise_on_error=False) - utils.run_vtctl(['RebuildKeyspaceGraph', 'test_keyspace'], auto_log=True) - for shard in ['0', '-80', '80-']: - utils.run_vtctl( - ['DeleteShard', '-even_if_serving', 'test_keyspace/%s' % shard], - auto_log=True) - - -class TestBaseSplitCloneResiliency(TestBaseSplitClone): - """Tests that the SplitClone worker is resilient to particular failures.""" - - def setUp(self): - try: - super(TestBaseSplitCloneResiliency, self).setUp() - self.copy_schema_to_destination_shards() - except: - self.tearDown() - raise - - def verify_successful_worker_copy_with_reparent(self, mysql_down=False): - """Verifies that vtworker can successfully copy data for a SplitClone. - - Order of operations: - 1. Run a background vtworker - 2. Wait until the worker successfully resolves the destination masters. - 3. Reparent the destination tablets - 4. Wait until the vtworker copy is finished - 5. Verify that the worker was forced to reresolve topology and retry writes - due to the reparent. - 6. Verify that the data was copied successfully to both new shards - - Args: - mysql_down: boolean. If True, we take down the MySQL instances on the - destination masters at first, then bring them back and reparent away. - - Raises: - AssertionError if things didn't go as expected. - """ - worker_proc, worker_port, worker_rpc_port = utils.run_vtworker_bg( - ['--cell', 'test_nj', '--use_v3_resharding_mode=false'], - auto_log=True) - - # --max_tps is only specified to enable the throttler and ensure that the - # code is executed. But the intent here is not to throttle the test, hence - # the rate limit is set very high. - # --chunk_count is 2 because rows are currently ordered by primary key such - # that all rows of the first shard come first and then the second shard. - # TODO(mberlin): Remove --offline=false once vtworker ensures that the - # destination shards are not behind the master's replication - # position. - args = ['SplitClone', - '--offline=false', - '--destination_writer_count', '1', - '--min_healthy_rdonly_tablets', '1', - '--max_tps', '9999'] - # Make the clone as slow as necessary such that there is enough time to - # run PlannedReparent in the meantime. - # TODO(mberlin): Once insert_values is fixed to uniformly distribute the - # rows across shards when sorted by primary key, remove - # --chunk_count 2, --min_rows_per_chunk 1 and set - # --source_reader_count back to 1. - args.extend(['--source_reader_count', '2', - '--chunk_count', '2', - '--min_rows_per_chunk', '1', - '--write_query_max_rows', '1']) - args.append('test_keyspace/0') - workerclient_proc = utils.run_vtworker_client_bg(args, worker_rpc_port) - - if mysql_down: - # vtworker is blocked at this point. This is a good time to test that its - # throttler server is reacting to RPCs. - self.check_throttler_service('localhost:%d' % worker_rpc_port, - ['test_keyspace/-80', 'test_keyspace/80-'], - 9999) - - utils.poll_for_vars( - 'vtworker', worker_port, - 'WorkerState == cloning the data (online)', - condition_fn=lambda v: v.get('WorkerState') == 'cloning the' - ' data (online)') - - logging.debug('Worker is in copy state, Shutting down mysqld on destination masters.') - utils.wait_procs( - [shard_0_master.shutdown_mysql(), - shard_1_master.shutdown_mysql()]) - - # If MySQL is down, we wait until vtworker retried at least once to make - # sure it reached the point where a write failed due to MySQL being down. - # There should be two retries at least, one for each destination shard. - utils.poll_for_vars( - 'vtworker', worker_port, - 'WorkerRetryCount >= 2', - condition_fn=lambda v: v.get('WorkerRetryCount') >= 2) - logging.debug('Worker has retried at least once per shard, starting reparent now') - - # Bring back masters. Since we test with semi-sync now, we need at least - # one replica for the new master. This test is already quite expensive, - # so we bring back the old master and then let it be converted to a - # replica by PRS, rather than leaving the old master down and having a - # third replica up the whole time. - logging.debug('Restarting mysqld on destination masters') - utils.wait_procs( - [shard_0_master.start_mysql(), - shard_1_master.start_mysql()]) - - # Reparent away from the old masters. - utils.run_vtctl( - ['PlannedReparentShard', '-keyspace_shard', 'test_keyspace/-80', - '-new_master', shard_0_replica.tablet_alias], auto_log=True) - utils.run_vtctl( - ['PlannedReparentShard', '-keyspace_shard', 'test_keyspace/80-', - '-new_master', shard_1_replica.tablet_alias], auto_log=True) - - else: - # NOTE: There is a race condition around this: - # It's possible that the SplitClone vtworker command finishes before the - # PlannedReparentShard vtctl command, which we start below, succeeds. - # Then the test would fail because vtworker did not have to retry. - # - # To workaround this, the test takes a parameter to increase the number of - # rows that the worker has to copy (with the idea being to slow the worker - # down). - # You should choose a value for num_insert_rows, such that this test - # passes for your environment (trial-and-error...) - # Make sure that vtworker got past the point where it picked a master - # for each destination shard ("finding targets" state). - utils.poll_for_vars( - 'vtworker', worker_port, - 'WorkerState == cloning the data (online)', - condition_fn=lambda v: v.get('WorkerState') == 'cloning the' - ' data (online)') - logging.debug('Worker is in copy state, starting reparent now') - - utils.run_vtctl( - ['PlannedReparentShard', '-keyspace_shard', 'test_keyspace/-80', - '-new_master', shard_0_replica.tablet_alias], auto_log=True) - utils.run_vtctl( - ['PlannedReparentShard', '-keyspace_shard', 'test_keyspace/80-', - '-new_master', shard_1_replica.tablet_alias], auto_log=True) - - utils.wait_procs([workerclient_proc]) - - # Verify that we were forced to re-resolve and retry. - worker_vars = utils.get_vars(worker_port) - self.assertGreater(worker_vars['WorkerRetryCount'], 1, - "expected vtworker to retry each of the two reparented" - " destination masters at least once, but it didn't") - self.assertNotEqual(worker_vars['WorkerRetryCount'], {}, - "expected vtworker to retry, but it didn't") - utils.kill_sub_process(worker_proc, soft=True) - - # Wait for the destination RDONLYs to catch up or the following offline - # clone will try to insert rows which already exist. - # TODO(mberlin): Remove this once SplitClone supports it natively. - utils.wait_for_replication_pos(shard_0_replica, shard_0_rdonly1) - utils.wait_for_replication_pos(shard_1_replica, shard_1_rdonly1) - # Run final offline clone to enable filtered replication. - _, _ = utils.run_vtworker(['-cell', 'test_nj', - '--use_v3_resharding_mode=false', - 'SplitClone', - '--online=false', - '--min_healthy_rdonly_tablets', '1', - 'test_keyspace/0'], auto_log=True) - - # Make sure that everything is caught up to the same replication point - self.run_split_diff('test_keyspace/-80', all_shard_tablets, shard_0_tablets) - self.run_split_diff('test_keyspace/80-', all_shard_tablets, shard_1_tablets) - - self.assert_shard_data_equal(0, shard_master, shard_0_tablets.replica) - self.assert_shard_data_equal(1, shard_master, shard_1_tablets.replica) - - -class TestReparentDuringWorkerCopy(TestBaseSplitCloneResiliency): - - def __init__(self, *args, **kwargs): - super(TestReparentDuringWorkerCopy, self).__init__(*args, **kwargs) - self.num_insert_rows = utils.options.num_insert_rows_before_reparent_test - - def test_reparent_during_worker_copy(self): - """Simulates a destination reparent during a worker SplitClone copy. - - The SplitClone command should be able to gracefully handle the reparent and - end up with the correct data on the destination. - - Note: this test has a small possibility of flaking, due to the timing issues - involved. It's possible for the worker to finish the copy step before the - reparent succeeds, in which case there are assertions that will fail. This - seems better than having the test silently pass. - """ - self.verify_successful_worker_copy_with_reparent() - - -class TestMysqlDownDuringWorkerCopy(TestBaseSplitCloneResiliency): - - def test_mysql_down_during_worker_copy(self): - """This test simulates MySQL being down on the destination masters.""" - self.verify_successful_worker_copy_with_reparent(mysql_down=True) - - -class TestVtworkerWebinterface(unittest.TestCase): - - def setUp(self): - # Run vtworker without any optional arguments to start in interactive mode. - self.worker_proc, self.worker_port, _ = utils.run_vtworker_bg([]) - - def tearDown(self): - utils.kill_sub_process(self.worker_proc) - - def test_webinterface(self): - worker_base_url = 'http://localhost:%d' % int(self.worker_port) - # Wait for /status to become available. - timeout = 10 - while True: - done = False - try: - urllib2.urlopen(worker_base_url + '/status').read() - done = True - except urllib2.URLError: - pass - if done: - break - timeout = utils.wait_step( - 'worker /status webpage must be available', timeout) - - # Run the command twice to make sure it's idempotent. - for _ in range(2): - # Run Ping command. - try: - urllib2.urlopen( - worker_base_url + '/Debugging/Ping', - data=urllib.urlencode({'message': 'pong'})).read() - raise Exception('Should have thrown an HTTPError for the redirect.') - except urllib2.HTTPError as e: - self.assertEqual(e.code, 307) - # Wait for the Ping command to finish. - utils.poll_for_vars( - 'vtworker', self.worker_port, - 'WorkerState == done', - condition_fn=lambda v: v.get('WorkerState') == 'done') - # Verify that the command logged something and it's available at /status. - status = urllib2.urlopen(worker_base_url + '/status').read() - self.assertIn( - "Ping command was called with message: 'pong'", status, - 'Command did not log output to /status: %s' % status) - - # Reset the job. - urllib2.urlopen(worker_base_url + '/reset').read() - status_after_reset = urllib2.urlopen(worker_base_url + '/status').read() - self.assertIn( - 'This worker is idle.', status_after_reset, - '/status does not indicate that the reset was successful') - - -class TestMinHealthyRdonlyTablets(TestBaseSplitCloneResiliency): - - def split_clone_fails_not_enough_health_rdonly_tablets(self): - """Verify vtworker errors if there aren't enough healthy RDONLY tablets.""" - - _, stderr = utils.run_vtworker( - ['-cell', 'test_nj', - '--wait_for_healthy_rdonly_tablets_timeout', '1s', - '--use_v3_resharding_mode=false', - 'SplitClone', - '--min_healthy_rdonly_tablets', '2', - 'test_keyspace/0'], - auto_log=True, - expect_fail=True) - self.assertIn('findTargets() failed: FindWorkerTablet() failed for' - ' test_nj/test_keyspace/0: not enough healthy RDONLY' - ' tablets to choose from in (test_nj,test_keyspace/0),' - ' have 1 healthy ones, need at least 2', stderr) - - -def add_test_options(parser): - parser.add_option( - '--num_insert_rows', type='int', default=100, - help='The number of rows, per shard, that we should insert before ' - 'resharding for this test.') - parser.add_option( - '--num_insert_rows_before_reparent_test', type='int', default=4500, - help='The number of rows, per shard, that we should insert before ' - 'running TestReparentDuringWorkerCopy (supersedes --num_insert_rows in ' - 'that test). There must be enough rows such that SplitClone takes ' - 'several seconds to run while we run a planned reparent.') - -if __name__ == '__main__': - utils.main(test_options=add_test_options) diff --git a/test/xb_recovery.py b/test/xb_recovery.py deleted file mode 100755 index d73483d707c..00000000000 --- a/test/xb_recovery.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Re-runs recovery.py with use_xtrabackup=True.""" - -import recovery -import utils - -if __name__ == '__main__': - recovery.use_xtrabackup = True - utils.main(recovery) diff --git a/test/xtrabackup.py b/test/xtrabackup.py deleted file mode 100755 index 135bdf60ce8..00000000000 --- a/test/xtrabackup.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Re-runs backup.py with use_xtrabackup=True.""" - -import backup -import utils - -if __name__ == '__main__': - backup.use_xtrabackup = True - utils.main(backup) diff --git a/test/xtrabackup_xbstream.py b/test/xtrabackup_xbstream.py deleted file mode 100755 index 0f027185211..00000000000 --- a/test/xtrabackup_xbstream.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Re-runs backup.py with use_xtrabackup=True.""" - -import backup -import utils - -if __name__ == '__main__': - backup.use_xtrabackup = True - backup.stream_mode = 'xbstream' - backup.xtrabackup_stripes = 8 - utils.main(backup) diff --git a/third_party/py/mock-1.0.1.tar.gz b/third_party/py/mock-1.0.1.tar.gz deleted file mode 100644 index 4fdea77c71c352e5f83c0acb813026e4cd7396f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 818644 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

atF9LDfwyb$p|UR@ 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

)9IxA%dV)~Y|_ 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%0A1A25-QruJDv?#NI|Mf;k=78e<#pQ{5o6WV_ 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傽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

HDd;pS#DWOva(FeF_BETC%5_)Vi4C|0>e5dOPpA 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 z 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 FDga*nhCKiP From 1143fe246cc987303b5eb0bb8cf3927fdb17f5ad Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Fri, 13 Mar 2020 16:35:31 +0530 Subject: [PATCH 310/825] added vttest schema back Signed-off-by: Arindam Nayak --- test/vttest_schema/default/test_table.sql | 6 ++++++ test/vttest_schema/test_keyspace/test_table.sql | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 test/vttest_schema/default/test_table.sql create mode 100644 test/vttest_schema/test_keyspace/test_table.sql diff --git a/test/vttest_schema/default/test_table.sql b/test/vttest_schema/default/test_table.sql new file mode 100644 index 00000000000..72e6b10edbc --- /dev/null +++ b/test/vttest_schema/default/test_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE test_table ( + `id` BIGINT(20) UNSIGNED NOT NULL, + `msg` VARCHAR(64), + `keyspace_id` BIGINT(20) UNSIGNED NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB diff --git a/test/vttest_schema/test_keyspace/test_table.sql b/test/vttest_schema/test_keyspace/test_table.sql new file mode 100644 index 00000000000..72e6b10edbc --- /dev/null +++ b/test/vttest_schema/test_keyspace/test_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE test_table ( + `id` BIGINT(20) UNSIGNED NOT NULL, + `msg` VARCHAR(64), + `keyspace_id` BIGINT(20) UNSIGNED NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB From 113532e51d877c993c7ea12677db323e24e98b2c Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Fri, 13 Mar 2020 16:52:48 +0530 Subject: [PATCH 311/825] Added vttest back Signed-off-by: Arindam Nayak --- py/vttest/__init__.py | 18 ++ py/vttest/environment.py | 116 ++++++++ py/vttest/init_data_options.py | 34 +++ py/vttest/local_database.py | 469 ++++++++++++++++++++++++++++++++ py/vttest/mysql_db.py | 53 ++++ py/vttest/mysql_db_mysqlctl.py | 92 +++++++ py/vttest/mysql_flavor.py | 114 ++++++++ py/vttest/run_local_database.py | 212 +++++++++++++++ py/vttest/sharding_utils.py | 80 ++++++ py/vttest/vt_processes.py | 231 ++++++++++++++++ 10 files changed, 1419 insertions(+) create mode 100644 py/vttest/__init__.py create mode 100644 py/vttest/environment.py create mode 100644 py/vttest/init_data_options.py create mode 100644 py/vttest/local_database.py create mode 100644 py/vttest/mysql_db.py create mode 100644 py/vttest/mysql_db_mysqlctl.py create mode 100644 py/vttest/mysql_flavor.py create mode 100755 py/vttest/run_local_database.py create mode 100644 py/vttest/sharding_utils.py create mode 100644 py/vttest/vt_processes.py diff --git a/py/vttest/__init__.py b/py/vttest/__init__.py new file mode 100644 index 00000000000..4d32e37cccb --- /dev/null +++ b/py/vttest/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from vttest import environment +from vttest import mysql_db_mysqlctl + +environment.mysql_db_class = mysql_db_mysqlctl.MySqlDBMysqlctl diff --git a/py/vttest/environment.py b/py/vttest/environment.py new file mode 100644 index 00000000000..b84d14814de --- /dev/null +++ b/py/vttest/environment.py @@ -0,0 +1,116 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Contains environment specifications for vttest module. + +This module is meant to be overwritten upon import into a development +tree with the appropriate values. It works as is in the Vitess tree. +""" + +import os +import shutil +import tempfile + +# this is the location of the vtcombo binary +vtcombo_binary = os.path.join(os.environ['VTROOT'], 'bin', 'vtcombo') + +# this is the location of the mysqlctl binary, if mysql_db_mysqlctl is used. +mysqlctl_binary = os.path.join(os.environ['VTROOT'], 'bin', 'mysqlctl') + +# this is the base port set by options. +base_port = None + +# this is the class to use for MySqlDB instances +mysql_db_class = None + + +def get_test_directory(): + """Returns the toplevel directory for the tests. Might create it.""" + directory = tempfile.mkdtemp(prefix='vttest', + dir=os.environ.get('VTDATAROOT', None)) + # Override VTDATAROOT to point to the newly created dir + os.environ['VTDATAROOT'] = directory + os.mkdir(get_logs_directory(directory)) + return directory + + +def get_logs_directory(directory): + """Returns the directory for logs, might be based on directory. + + Args: + directory: the value returned by get_test_directory(). + Returns: + the directory for logs. + """ + return os.path.join(directory, 'logs') + + +def cleanup_test_directory(directory): + """Cleans up the test directory after the test is done. + + Args: + directory: the value returned by get_test_directory(). + """ + shutil.rmtree(directory) + + +def extra_vtcombo_parameters(): + """Returns extra parameters to send to vtcombo.""" + return [ + '-service_map', ','.join([ + 'grpc-vtgateservice', + 'grpc-vtctl', + ]), + ] + + +# pylint: disable=unused-argument +def process_is_healthy(name, addr): + + """Double-checks a process is healthy and ready for RPCs.""" + return True + + +def get_protocol(): + """Returns the protocol used between client and vtcombo.""" + return 'grpc' + + +def get_port(name, protocol=None): + """Returns the port to use for a given process. + + This is only called once per process, so picking an unused port will also + work. + + Args: + name: process name. + protocol: the protocol used. + + Returns: + the port to use. + + Raises: + ValueError: the port name is invalid. + """ + if name == 'vtcombo': + if protocol == 'grpc': + # We can't use the base_port for grpc. + return base_port + 1 + return base_port + elif name == 'mysql': + 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) diff --git a/py/vttest/init_data_options.py b/py/vttest/init_data_options.py new file mode 100644 index 00000000000..04219f036b2 --- /dev/null +++ b/py/vttest/init_data_options.py @@ -0,0 +1,34 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Stores options used for initializing the database with randomized data. + +The options stored correspond to command line flags. See run_local_database.py +for more details on each option. +""" + + +class InitDataOptions(object): + valid_attrs = set([ + 'rng_seed', + 'min_table_shard_size', + 'max_table_shard_size', + 'null_probability', + ]) + + def __setattr__(self, name, value): + if name not in self.valid_attrs: + raise Exception( + 'InitDataOptions: unsupported attribute: %s' % name) + self.__dict__[name] = value diff --git a/py/vttest/local_database.py b/py/vttest/local_database.py new file mode 100644 index 00000000000..2634aef27a2 --- /dev/null +++ b/py/vttest/local_database.py @@ -0,0 +1,469 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Create a local Vitess database for testing.""" + +import glob +import logging +import os +import random +import re + +from vttest import environment +from vttest import vt_processes + + +class LocalDatabase(object): + """Set up a local Vitess database.""" + + def __init__(self, + topology, + schema_dir, + mysql_only, + init_data_options, + default_schema_dir=None, + extra_my_cnf=None, + snapshot_file=None, + charset='utf8', + mysql_server_bind_address=None): + """Initializes an object of this class. + + Args: + topology: a vttest.VTTestTopology object describing the topology. + schema_dir: see the documentation for the corresponding command line + flag in run_local_database.py + mysql_only: see the documentation for the corresponding command line + flag in run_local_database.py + init_data_options: an object of type InitDataOptions containing + options configuring populating the database with initial random data. + If the value is 'None' then the database will not be initialized + with random data. + default_schema_dir: a directory to use if no keyspace is found in the + schema_dir directory. + extra_my_cnf: additional cnf file to use for the EXTRA_MY_CNF var. + snapshot_file: A MySQL DB snapshot file. + charset: MySQL charset. + mysql_server_bind_address: MySQL server bind address. + """ + + self.topology = topology + self.schema_dir = schema_dir + self.mysql_only = mysql_only + self.init_data_options = init_data_options + self.default_schema_dir = default_schema_dir + self.extra_my_cnf = extra_my_cnf + self.snapshot_file = snapshot_file + self.charset = charset + self.mysql_server_bind_address = mysql_server_bind_address + + def setup(self): + """Create a MySQL instance and all Vitess processes.""" + mysql_port = environment.get_port('mysql') + self.directory = environment.get_test_directory() + self.mysql_db = environment.mysql_db_class( + self.directory, mysql_port, self.extra_my_cnf, self.snapshot_file) + + self.mysql_db.setup() + if not self.snapshot_file: + self.create_databases() + self.load_schema() + if self.init_data_options is not None: + self.rng = random.Random(self.init_data_options.rng_seed) + self.populate_with_random_data() + if self.mysql_only: + return + + vt_processes.start_vt_processes(self.directory, self.topology, + self.mysql_db, self.schema_dir, + charset=self.charset, mysql_server_bind_address=self.mysql_server_bind_address) + + def teardown(self): + """Kill all Vitess processes and wait for them to end. + + MySQLTestDB's wrapper script will take care of mysqld. + """ + if not self.mysql_only: + self.kill() + self.wait() + self.mysql_db.teardown() + environment.cleanup_test_directory(self.directory) + + def kill(self): + """Kill all Vitess processes.""" + vt_processes.kill_vt_processes() + + def wait(self): + """Wait for all Vitess processes to end.""" + vt_processes.wait_vt_processes() + + def vtgate_addr(self): + """Get the host:port for vtgate.""" + if environment.get_protocol() == 'grpc': + return vt_processes.vtcombo_process.grpc_addr() + return vt_processes.vtcombo_process.addr() + + def config(self): + """Returns a dict with enough information to be able to connect.""" + if self.mysql_only: + return self.mysql_db.config() + + 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': + result['grpc_port'] = vt_processes.vtcombo_process.grpc_port + return result + + def mysql_execute(self, queries, db_name=''): + """Execute queries directly on MySQL. + + The queries will be executed in a single transaction. + + Args: + queries: A list of strings. The SQL statements to execute. + db_name: The database name to use. + + Returns: + The results of the last query as a list of row tuples. + """ + conn = self.mysql_db.connect(db_name) + cursor = conn.cursor() + + for query in queries: + cursor.execute(query) + result = cursor.fetchall() + + cursor.close() + # Commit all of the queries. + conn.commit() + conn.close() + return result + + def create_databases(self): + """Create a database for each shard.""" + + cmds = [] + for kpb in self.topology.keyspaces: + if kpb.served_from: + # redirected keyspaces have no underlying database + continue + + for spb in kpb.shards: + db_name = spb.db_name_override + if not db_name: + db_name = 'vt_%s_%s' % (kpb.name, spb.name) + cmds.append('create database `%s`' % db_name) + logging.info('Creating databases') + self.mysql_execute(cmds) + + def load_schema(self): + """Load schema SQL from data files.""" + + if not self.schema_dir: + return + + if not os.path.isdir(self.schema_dir): + raise Exception('schema_dir "%s" is not a directory.' % self.schema_dir) + + for kpb in self.topology.keyspaces: + if kpb.served_from: + # redirected keyspaces have no underlying database + continue + + keyspace = kpb.name + keyspace_dir = os.path.join(self.schema_dir, keyspace) + schema_dir = keyspace_dir + if not os.path.isdir(schema_dir): + schema_dir = self.default_schema_dir + if not schema_dir or not os.path.isdir(schema_dir): + raise Exception( + 'No subdirectory found in schema dir %s for keyspace %s. ' + 'No valid default_schema_dir (set to %s) was found. ' + 'For keyspaces without an initial schema, create the ' + 'directory %s and leave a README file to explain why the ' + 'directory exists. ' + 'Alternatively, disable loading schemas by setting --schema_dir ' + 'to "" or set --default_schema_dir to a valid schema.' % + (self.schema_dir, keyspace, self.default_schema_dir, + keyspace_dir)) + + for filepath in glob.glob(os.path.join(schema_dir, '*.sql')): + logging.info('Loading schema for keyspace %s from file %s', + keyspace, filepath) + cmds = self.get_sql_commands_from_file(filepath, schema_dir) + + # Run the cmds on each shard and cell in the keyspace. + for spb in kpb.shards: + db_name = spb.db_name_override + if not db_name: + db_name = 'vt_%s_%s' % (kpb.name, spb.name) + self.mysql_execute(cmds, db_name=db_name) + + def populate_with_random_data(self): + """Populates all shards with randomly generated data.""" + + for kpb in self.topology.keyspaces: + if kpb.served_from: + # redirected keyspaces have no underlying database + continue + + for spb in kpb.shards: + db_name = spb.db_name_override + if not db_name: + db_name = 'vt_%s_%s' % (kpb.name, spb.name) + self.populate_shard_with_random_data(db_name) + + def populate_shard_with_random_data(self, db_name): + """Populates the given database with randomly generated data. + + Every table in the database is populated. + + Args: + db_name: The shard database name (string). + """ + + tables = self.mysql_execute(['SHOW TABLES'], db_name) + for table in tables: + self.populate_table_with_random_data(db_name, table[0]) + + # The number of rows inserted in a single INSERT statement. + batch_insert_size = 1000 + + def populate_table_with_random_data(self, db_name, table_name): + """Populates the given table with randomly generated data. + + Queries the database for the table schema and then populates + the columns with randomly generated data. + + Args: + db_name: The shard database name (string). + table_name: The name of the table to populate (string). + """ + + field_infos = self.mysql_execute(['DESCRIBE %s' % table_name], db_name) + num_rows = self.rng.randint(self.init_data_options.min_table_shard_size, + self.init_data_options.max_table_shard_size) + rows = [] + for _ in xrange(num_rows): + row = [] + for field_info in field_infos: + field_type = field_info[1] + field_allow_nulls = (field_info[2] == 'YES') + row.append( + self.generate_random_field( + table_name, field_type, field_allow_nulls)) + rows.append(row) + + # Insert 'rows' into the database in batches of size + # self.batch_insert_size + field_names = [field_info[0] for field_info in field_infos] + for index in xrange(0, len(rows), self.batch_insert_size): + self.batch_insert(db_name, + table_name, + field_names, + rows[index:index + self.batch_insert_size]) + + def batch_insert(self, db_name, table_name, field_names, rows): + """Inserts the rows in 'rows' into 'table_name' of database 'db_name'. + + Args: + db_name: The name of the database containing the table. + table_name: The name of the table to populate. + field_names: The list of the field names in the table. + rows: A list of tuples with each tuple containing + the string representations of the fields. + The order of the representation must match the order of the field + names listed in 'field_names'. + """ + + field_names_string = ','.join(field_names) + values_string = ','.join(['(' + ','.join(row) +')' for row in rows]) + # We use "INSERT IGNORE" to ignore duplicate key errors. + insert_query = ('INSERT IGNORE INTO %s (%s) VALUES %s' % + (table_name, field_names_string, values_string)) + logging.info('Executing in database %s: %s', db_name, insert_query) + self.mysql_execute([insert_query], db_name) + + def generate_random_field(self, table_name, field_type, field_allows_nulls): + """Generates a random field string representation. + + By 'string representation' we mean a string that is suitable to be a part + of an 'INSERT INTO' SQL statement. + + Args: + table_name: The name of the table that will contain the generated field + value. Only used for a descriptive exception message in case of + an error. + field_type: The field_type as given by a "DESCRIBE

-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 z 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

" SQL statement. + field_allows_nulls: Should be 'true' if this field allows NULLS. + + Returns: + The random field. + + Raises: + Exception: If 'field_type' is not supported. + """ + + value = None + if field_type.startswith('tinyint'): + value = self.random_integer(field_type, 1) + elif field_type.startswith('smallint'): + value = self.random_integer(field_type, 2) + elif field_type.startswith('mediumint'): + value = self.random_integer(field_type, 3) + elif field_type.startswith('int'): + value = self.random_integer(field_type, 4) + elif field_type.startswith('bigint'): + value = self.random_integer(field_type, 8) + elif field_type.startswith('decimal'): + value = self.random_decimal(field_type) + else: + raise Exception('Populating random data in field type: %s is not yet ' + 'supported. (table: %s)' % (field_type, table_name)) + if (field_allows_nulls and + self.true_with_probability(self.init_data_options.null_probability)): + return 'NULL' + return value + + def true_with_probability(self, true_probability): + """Returns a pseudo-random boolean. + + Args: + true_probability: The probability to use for returning 'true'. + Returns: + The value 'true' is with probability 'true_probability'. + """ + + return self.rng.uniform(0, 1) < true_probability + + def random_integer(self, field_type, num_bytes): + num_bits = 8*num_bytes + if field_type.endswith('unsigned'): + return '%d' % (self.rng.randint(0, 2**num_bits-1)) + return '%d' % (self.rng.randint(-2**(num_bits-1), 2**(num_bits-1)-1)) + + decimal_regexp = re.compile(r'decimal\((\d+),(\d+)\)') + + def random_decimal(self, field_type): + match = self.decimal_regexp.match(field_type) + if match is None: + raise Exception("Can't parse 'decimal' field type: %s" % field_type) + num_digits_right = int(match.group(2)) + num_digits_left = int(match.group(1))-num_digits_right + boundary = 10**num_digits_left-1 + rand = self.rng.uniform(-boundary, boundary) + return '%.*f' % (num_digits_right, rand) + + def get_sql_commands_from_file(self, filename, source_root=None): + """Given a file, extract an array of commands from the file. + + Automatically strips out three types of MySQL comment syntax: + '--' at beginning of line: line removed + '-- ': remove everything from here to line's end (note space after dashes) + '#': remove everything from here to line's end + MySQL's handling of C-style /* ... */ comments is weird, so we + leave them alone for now. See the MySQL manual 6.1.6 "Comment Syntax" + for all the weird complications. + + Args: + filename: the SQL source file to use. + source_root: if specified, 'source FILENAME' lines in the SQL file will + source the specified filename relative to source_root. + + Returns: + A list of SQL commands. + """ + fd = open(filename) + lines = fd.readlines() + + inside_single_quotes = 0 + inside_double_quotes = 0 + commands = [] + cmd = '' + for line in lines: + # Strip newline and other trailing whitespace + line = line.rstrip() + + if (not inside_single_quotes and not inside_double_quotes and + line.startswith('--')): + # Line starts with '--', skip line + continue + + i = 0 + next_i = 0 + # Iterate through line, looking for special delimiters + while 1: + i = next_i + if i >= len(line): + break + + # By default, move to next character after this one + next_i = i + 1 + + if line[i] == '\\': + # Next character is literal, skip this and the next character + next_i = i + 2 + + elif line[i] == "'": + if not inside_double_quotes: + inside_single_quotes = not inside_single_quotes + + elif line[i] == '"': + if not inside_single_quotes: + inside_double_quotes = not inside_double_quotes + + elif not inside_single_quotes and not inside_double_quotes: + if line[i] == '#' or line[i:i+3] == '-- ': + # Found unquoted "#" or "-- ", ignore rest of line + line = line[:i] + break + + if line[i] == ';': + # Unquoted semicolon marks end of command + cmd += line[:i] + commands.append(cmd) + cmd = '' + + # Chop off everything before and including the semicolon + line = line[i+1:] + + # Start over at beginning of line + next_i = 0 + + # Reached end of line + if line and not line.isspace(): + if source_root and not cmd and line.startswith('source '): + commands.extend(self.get_sql_commands_from_file( + os.path.join(source_root, line[7:]), + source_root=source_root)) + else: + cmd += line + cmd += '\n' + + # Accept last command even if it doesn't end in semicolon + cmd = cmd.strip() + if cmd: + commands.append(cmd) + + return commands + + def __enter__(self): + self.setup() + return self + + def __exit__(self, exc_type, exc_info, tb): + self.teardown() diff --git a/py/vttest/mysql_db.py b/py/vttest/mysql_db.py new file mode 100644 index 00000000000..fe0c4e2037b --- /dev/null +++ b/py/vttest/mysql_db.py @@ -0,0 +1,53 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This module defines the interface for the MySQL database. +""" + + +class MySqlDB(object): + """A MySqlDB contains basic info about a MySQL instance.""" + + def __init__(self, directory, port, extra_my_cnf=None, snapshot_file=None): + self._directory = directory + self._port = port + self._extra_my_cnf = extra_my_cnf + self._snapshot_file = snapshot_file + + def setup(self, port): + """Starts the MySQL database.""" + raise NotImplementedError('MySqlDB is the base class.') + + def teardown(self): + """Stops the MySQL database.""" + raise NotImplementedError('MySqlDB is the base class.') + + def username(self): + raise NotImplementedError('MySqlDB is the base class.') + + def password(self): + raise NotImplementedError('MySqlDB is the base class.') + + def hostname(self): + raise NotImplementedError('MySqlDB is the base class.') + + def port(self): + raise NotImplementedError('MySqlDB is the base class.') + + def unix_socket(self): + raise NotImplementedError('MySqlDB is the base class.') + + def config(self): + """Returns the json config to output.""" + raise NotImplementedError('MySqlDB is the base class.') diff --git a/py/vttest/mysql_db_mysqlctl.py b/py/vttest/mysql_db_mysqlctl.py new file mode 100644 index 00000000000..c1ecc8d049f --- /dev/null +++ b/py/vttest/mysql_db_mysqlctl.py @@ -0,0 +1,92 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This module defines a mysqlctl based MySQL database. +""" + +import os +import subprocess + +import MySQLdb + +from vttest import environment +from vttest import mysql_db +from vttest.mysql_flavor import mysql_flavor + + +class MySqlDBMysqlctl(mysql_db.MySqlDB): + """Contains data and methods to manage a MySQL instance using mysqlctl.""" + + def __init__(self, directory, port, extra_my_cnf, snapshot_file=None): + super(MySqlDBMysqlctl, self).__init__( + directory, port, extra_my_cnf, snapshot_file) + + def setup(self): + cmd = [ + environment.mysqlctl_binary, + '-alsologtostderr', + '-tablet_uid', '1', + '-mysql_port', str(self._port), + 'init', + '-init_db_sql_file', + os.path.join(os.environ['VTROOT'], 'config/init_db.sql'), + ] + env = os.environ + env['VTDATAROOT'] = self._directory + my_cnf = mysql_flavor().my_cnf() + if self._extra_my_cnf: + my_cnf += ':%s' % self._extra_my_cnf + env['EXTRA_MY_CNF'] = my_cnf + result = subprocess.call(cmd, env=env) + if result != 0: + raise Exception('mysqlctl failed', result) + + def teardown(self): + cmd = [ + environment.mysqlctl_binary, + '-alsologtostderr', + '-tablet_uid', '1', + '-mysql_port', str(self._port), + 'shutdown', + ] + result = subprocess.call(cmd) + if result != 0: + raise Exception('mysqlctl failed', result) + + def connect(self, db_name): + return MySQLdb.connect(user='vt_dba', + unix_socket=self.unix_socket(), + db=db_name) + + def username(self): + return 'vt_dba' + + def password(self): + return '' + + def hostname(self): + return '' + + def port(self): + return self._port + + def unix_socket(self): + return os.path.join(self._directory, 'vt_0000000001', 'mysql.sock') + + def config(self): + return { + 'username': self.username(), + 'password': self.password(), + 'socket': self.unix_socket(), + } diff --git a/py/vttest/mysql_flavor.py b/py/vttest/mysql_flavor.py new file mode 100644 index 00000000000..c28bf979e02 --- /dev/null +++ b/py/vttest/mysql_flavor.py @@ -0,0 +1,114 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Define abstractions for various mysql flavors. + +This module is used by mysql_db_mysqlctl.py to handle differences +between various flavors of mysql. +""" + +import logging +import os +import sys + + +# For now, vtroot is only used in this module. If other people +# need this, we should move it to environment. +if "VTROOT" not in os.environ: + sys.stderr.write( + "ERROR: Vitess environment not set up. " + 'Please run "source dev.env" first.\n') + sys.exit(1) + +vtroot = os.environ["VTROOT"] + +class MysqlFlavor(object): + """Base class with default SQL statements.""" + + def my_cnf(self): + """Returns the path to an extra my_cnf file, or None.""" + return None + + +class MariaDB(MysqlFlavor): + """Overrides specific to MariaDB.""" + + def my_cnf(self): + files = [ + os.path.join(vtroot, "config/mycnf/default-fast.cnf"), + ] + return ":".join(files) + +class MariaDB103(MysqlFlavor): + """Overrides specific to MariaDB 10.3""" + + def my_cnf(self): + files = [ + os.path.join(vtroot, "config/mycnf/default-fast.cnf"), + ] + return ":".join(files) + +class MySQL56(MysqlFlavor): + """Overrides specific to MySQL 5.6.""" + + def my_cnf(self): + files = [ + os.path.join(vtroot, "config/mycnf/default-fast.cnf"), + ] + return ":".join(files) + +class MySQL80(MysqlFlavor): + """Overrides specific to MySQL 8.0.""" + + def my_cnf(self): + files = [ + os.path.join(vtroot, "config/mycnf/default-fast.cnf"), + ] + return ":".join(files) + +__mysql_flavor = None + + +# mysql_flavor is a function because we need something to import before the +# actual __mysql_flavor is initialized, since that doesn't happen until after +# the command-line options are parsed. If we make mysql_flavor a variable and +# import it before it's initialized, the module that imported it won't get the +# updated value when it's later initialized. +def mysql_flavor(): + return __mysql_flavor + + +def set_mysql_flavor(flavor): + global __mysql_flavor + + # Last default is there because the environment variable might be set to "". + flavor = flavor or os.environ.get("MYSQL_FLAVOR", "MySQL56") or "MySQL56" + + # Set the environment variable explicitly in case we're overriding it via + # command-line flag. + os.environ["MYSQL_FLAVOR"] = flavor + + if flavor == "MariaDB": + __mysql_flavor = MariaDB() + elif flavor == "MariaDB103": + __mysql_flavor = MariaDB103() + elif flavor == "MySQL80": + __mysql_flavor = MySQL80() + elif flavor == "MySQL56": + __mysql_flavor = MySQL56() + else: + logging.error("Unknown MYSQL_FLAVOR '%s'", flavor) + exit(1) + + logging.debug("Using MYSQL_FLAVOR=%s", str(flavor)) diff --git a/py/vttest/run_local_database.py b/py/vttest/run_local_database.py new file mode 100755 index 00000000000..ba49b6546c1 --- /dev/null +++ b/py/vttest/run_local_database.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +r"""Command-line tool for starting a local Vitess database for testing. + +USAGE: + + $ run_local_database --port 12345 \ + --proto_topo \ + --schema_dir /path/to/schema/dir + +It will run the tool, logging to stderr. On stdout, a small json structure +can be waited on and then parsed by the caller to figure out how to reach +the vtgate process. + +As an alternative to using proto_topo, a local instance can be started by using +additional flags, such as: + + $ run_local_database --port 12345 \ + --schema_dir /path/to/schema/dir \ + --cells cell1,cell2 --keyspaces ks1,ks2 \ + --num_shards 1,2 + +This will create an instance with two keyspaces in two cells, one with a single +shard and another with two shards. + +Once done with the test, send an empty line to this process for it to clean-up, +and then just wait for it to exit. + +""" + +import json +import logging +import optparse +import os +import sys + + +from google.protobuf import text_format + +from vtproto import vttest_pb2 +from vtdb import prefer_vtroot_imports # pylint: disable=unused-import +from vttest import environment +from vttest import init_data_options +from vttest import local_database +from vttest import mysql_flavor +from vttest import sharding_utils + + +def main(cmdline_options): + topology = vttest_pb2.VTTestTopology() + if cmdline_options.proto_topo: + # Text-encoded proto topology object, just parse it. + topology = text_format.Parse(cmdline_options.proto_topo, topology) + if not topology.cells: + topology.cells.append('test') + else: + cells = [] + keyspaces = [] + shard_counts = [] + if cmdline_options.cells: + cells = cmdline_options.cells.split(',') + if cmdline_options.keyspaces: + keyspaces = cmdline_options.keyspaces.split(',') + if cmdline_options.num_shards: + shard_counts = [int(x) for x in cmdline_options.num_shards.split(',')] + + for cell in cells: + topology.cells.append(cell) + for keyspace, num_shards in zip(keyspaces, shard_counts): + ks = topology.keyspaces.add(name=keyspace) + for shard in sharding_utils.get_shard_names(num_shards): + ks.shards.add(name=shard) + ks.replica_count = cmdline_options.replica_count + ks.rdonly_count = cmdline_options.rdonly_count + + environment.base_port = cmdline_options.port + + init_data_opts = None + if cmdline_options.initialize_with_random_data: + init_data_opts = init_data_options.InitDataOptions() + init_data_opts.rng_seed = cmdline_options.rng_seed + init_data_opts.min_table_shard_size = cmdline_options.min_table_shard_size + init_data_opts.max_table_shard_size = cmdline_options.max_table_shard_size + init_data_opts.null_probability = cmdline_options.null_probability + + extra_my_cnf = '' + if cmdline_options.extra_my_cnf: + extra_my_cnf += ':' + cmdline_options.extra_my_cnf + + with local_database.LocalDatabase( + topology, + cmdline_options.schema_dir, + cmdline_options.mysql_only, + init_data_opts, + default_schema_dir=cmdline_options.default_schema_dir, + extra_my_cnf=extra_my_cnf, + charset=cmdline_options.charset, + snapshot_file=cmdline_options.snapshot_file, + mysql_server_bind_address=cmdline_options.mysql_server_bind_address) as local_db: + print json.dumps(local_db.config()) + sys.stdout.flush() + try: + raw_input() + except EOFError: + sys.stderr.write( + 'WARNING: %s: No empty line was received on stdin.' + ' Instead, stdin was closed and the cluster will be shut down now.' + ' Make sure to send the empty line instead to proactively shutdown' + ' the local cluster. For example, did you forget the shutdown in' + ' your test\'s tearDown()?\n' % os.path.basename(__file__)) + +if __name__ == '__main__': + + parser = optparse.OptionParser() + parser.add_option( + '-p', '--port', type='int', + help='Port to use for vtcombo. If this is 0, a random port ' + 'will be chosen.') + parser.add_option( + '-o', '--proto_topo', + help='Define the fake cluster topology as a compact text format encoded' + ' vttest proto. See vttest.proto for more information.') + parser.add_option( + '-s', '--schema_dir', + help='Directory for initial schema files. Within this dir,' + ' there should be a subdir for each keyspace. Within' + ' each keyspace dir, each file is executed as SQL' + ' after the database is created on each shard.' + ' If the directory contains a vschema.json file, it' + ' will be used as the vschema for the V3 API.') + parser.add_option( + '-e', '--default_schema_dir', + help='Default directory for initial schema files. If no schema is found' + ' in schema_dir, default to this location.') + parser.add_option( + '-m', '--mysql_only', action='store_true', + help='If this flag is set only mysql is initialized.' + ' The rest of the vitess components are not started.' + ' Also, the output specifies the mysql unix socket' + ' instead of the vtgate port.') + parser.add_option( + '-r', '--initialize_with_random_data', action='store_true', + help='If this flag is each table-shard will be initialized' + ' with random data. See also the "rng_seed" and "min_shard_size"' + ' and "max_shard_size" flags.') + parser.add_option( + '-d', '--rng_seed', type='int', default=123, + help='The random number generator seed to use when initializing' + ' with random data (see also --initialize_with_random_data).' + ' Multiple runs with the same seed will result with the same' + ' initial data.') + parser.add_option( + '-x', '--min_table_shard_size', type='int', default=1000, + help='The minimum number of initial rows in a table shard. Ignored if' + '--initialize_with_random_data is false. The actual number is chosen' + ' randomly.') + parser.add_option( + '-y', '--max_table_shard_size', type='int', default=10000, + help='The maximum number of initial rows in a table shard. Ignored if' + '--initialize_with_random_data is false. The actual number is chosen' + ' randomly') + parser.add_option( + '-n', '--null_probability', type='float', default=0.1, + help='The probability to initialize a field with "NULL" ' + ' if --initialize_with_random_data is true. Only applies to fields' + ' that can contain NULL values.') + parser.add_option( + '-f', '--extra_my_cnf', + help='extra files to add to the config, separated by ":"') + parser.add_option( + '--mysql_server_bind_address', + help='mysql server bind address ":"') + parser.add_option( + '-v', '--verbose', action='store_true', + help='Display extra error messages.') + parser.add_option('-c', '--cells', default='test', + help='Comma separated list of cells') + parser.add_option('-k', '--keyspaces', default='test_keyspace', + help='Comma separated list of keyspaces') + parser.add_option('--num_shards', default='2', + help='Comma separated shard count (one per keyspace)') + parser.add_option('--replica_count', type='int', default=2, + help='Replica tablets per shard (includes master)') + parser.add_option('--rdonly_count', type='int', default=1, + help='Rdonly tablets per shard') + parser.add_option('--charset', default='utf8', help='MySQL charset') + parser.add_option( + '--snapshot_file', default=None, help='A MySQL DB snapshot file') + (options, args) = parser.parse_args() + if options.verbose: + logging.getLogger().setLevel(logging.DEBUG) + + # This will set the flavor based on the MYSQL_FLAVOR env var, + # or default to MariaDB. + mysql_flavor.set_mysql_flavor(None) + + main(options) diff --git a/py/vttest/sharding_utils.py b/py/vttest/sharding_utils.py new file mode 100644 index 00000000000..db9b15ff6b9 --- /dev/null +++ b/py/vttest/sharding_utils.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +"""Sharding utils.""" + + +def get_shard_index(shard_name): + """Returns tuple of shard index, num_shards based on a shard name.""" + if shard_name in ['0', '-']: + return 0, 1 + + shard_begin, shard_end = shard_name.split('-') + num_bytes_used = max(len(shard_begin), len(shard_end)) / 2 + if shard_begin: + shard_begin = int(shard_begin, 16) + else: + shard_begin = 0 + if shard_end: + shard_end = int(shard_end, 16) + else: + shard_end = 1 << num_bytes_used * 8 + shard_width = shard_end - shard_begin + num_shards = (1 << num_bytes_used * 8) / (shard_width) + shard_num = shard_begin / shard_width + return shard_num, num_shards + + +def get_shard_name(shard, num_shards): + """Returns an appropriate shard name, as a string. + + A single shard name is simply 0; otherwise it will attempt to split up 0x100 + into multiple shards. For example, in a two sharded keyspace, shard 0 is + -80, shard 1 is 80-. This function currently only applies to sharding setups + where the shard count is 256 or less, and all shards are equal width. + + Args: + shard: The integer shard index (zero based) + num_shards: Total number of shards (int) + + Returns: + The shard name as a string. + """ + + if num_shards == 1: + return '0' + + shard_width = int(0x100 / num_shards) + + if shard == 0: + return '-%02x' % shard_width + elif shard == num_shards - 1: + return '%02x-' % (shard * shard_width) + else: + return '%02x-%02x' % (shard * shard_width, (shard + 1) * shard_width) + + +def get_shard_names(num_shards): + """Create a generator of shard names. + + Args: + num_shards: Total number of shards (int) + + Returns: + The shard name generator. + """ + return (get_shard_name(x, num_shards) for x in range(num_shards)) diff --git a/py/vttest/vt_processes.py b/py/vttest/vt_processes.py new file mode 100644 index 00000000000..8530c2d5ee2 --- /dev/null +++ b/py/vttest/vt_processes.py @@ -0,0 +1,231 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Starts the vtcombo process.""" + +import json +import logging +import os +import socket +import subprocess +import time +import urllib + +from google.protobuf import text_format + +from vttest import environment + + +class VtProcess(object): + """Base class for a vt process, vtcombo only now.""" + + START_RETRIES = 5 + + def __init__(self, name, directory, binary, port_name): + self.name = name + self.directory = directory + self.binary = binary + self.extraparams = [] + self.port_name = port_name + self.process = None + + def wait_start(self): + """Start the process and wait for it to respond on HTTP.""" + + for _ in xrange(0, self.START_RETRIES): + self.port = environment.get_port(self.port_name) + if environment.get_protocol() == 'grpc': + self.grpc_port = environment.get_port(self.port_name, protocol='grpc') + else: + self.grpc_port = None + logs_subdirectory = environment.get_logs_directory(self.directory) + cmd = [ + self.binary, + '-port', '%u' % self.port, + '-log_dir', logs_subdirectory, + '-alsologtostderr', + ] + if environment.get_protocol() == 'grpc': + cmd.extend(['-grpc_port', '%u' % self.grpc_port]) + cmd.extend(self.extraparams) + logging.info('Starting process: %s', cmd) + stdout = os.path.join(logs_subdirectory, '%s.%d.log' % + (self.name, self.port)) + self.stdout = open(stdout, 'w') + self.process = subprocess.Popen(cmd, + stdout=self.stdout) + timeout = time.time() + 60.0 + while time.time() < timeout: + if environment.process_is_healthy( + self.name, self.addr()) and self.get_vars(): + logging.info('%s started.', self.name) + return + elif self.process.poll() is not None: + logging.error('%s process exited prematurely.', self.name) + break + time.sleep(0.3) + + logging.error('cannot start %s process on time: %s ', + self.name, socket.getfqdn()) + self.kill() + + raise Exception('Failed %d times to run %s' % ( + self.START_RETRIES, + self.name)) + + def addr(self): + """Return the host:port of the process.""" + return '%s:%u' % (socket.getfqdn(), self.port) + + def grpc_addr(self): + """Get the grpc address of the process. + + Returns: + the grpc host:port of the process. + Only call this is environment.get_protocol() == 'grpc'. + """ + return '%s:%u' % (socket.getfqdn(), self.grpc_port) + + def get_vars(self): + """Return the debug vars.""" + data = None + try: + url = 'http://%s/debug/vars' % self.addr() + f = urllib.urlopen(url) + data = f.read() + f.close() + except IOError: + return None + try: + return json.loads(data) + except ValueError: + logging.error('%s', data) + raise + + def kill(self): + """Kill the process.""" + # These will proceed without error even if the process is already gone. + self.process.terminate() + + def wait(self): + """Wait for the process to end.""" + self.process.wait() + + +class VtcomboProcess(VtProcess): + """Represents a vtcombo subprocess.""" + + QUERYSERVER_PARAMETERS = [ + '-queryserver-config-pool-size', '4', + '-queryserver-config-query-timeout', '300', + '-queryserver-config-schema-reload-time', '60', + '-queryserver-config-stream-pool-size', '4', + '-queryserver-config-transaction-cap', '4', + '-queryserver-config-transaction-timeout', '300', + '-queryserver-config-txpool-timeout', '300', + ] + + def __init__(self, directory, topology, mysql_db, schema_dir, charset, + mysql_server_bind_address=None): + VtProcess.__init__(self, 'vtcombo-%s' % os.environ['USER'], directory, + environment.vtcombo_binary, port_name='vtcombo') + self.extraparams = [ + '-db_charset', charset, + '-db_app_user', mysql_db.username(), + '-db_app_password', mysql_db.password(), + '-db_dba_user', mysql_db.username(), + '-db_dba_password', mysql_db.password(), + '-proto_topo', text_format.MessageToString(topology, as_one_line=True), + '-mycnf_server_id', '1', + '-mycnf_socket_file', mysql_db.unix_socket(), + '-normalize_queries', + ] + self.QUERYSERVER_PARAMETERS + environment.extra_vtcombo_parameters() + if schema_dir: + self.extraparams.extend(['-schema_dir', schema_dir]) + if mysql_db.unix_socket(): + self.extraparams.extend(['-db_socket', mysql_db.unix_socket()]) + else: + self.extraparams.extend( + ['-db_host', mysql_db.hostname(), + '-db_port', str(mysql_db.port())]) + self.vtcombo_mysql_port = environment.get_port('vtcombo_mysql_port') + if mysql_server_bind_address: + # Binding to 0.0.0.0 instead of localhost makes it possible to connect to vtgate from outside a docker container + self.extraparams.extend(['-mysql_server_bind_address', mysql_server_bind_address]) + else: + self.extraparams.extend(['-mysql_server_bind_address', 'localhost']) + self.extraparams.extend( + ['-mysql_auth_server_impl', 'none', + '-mysql_server_port', str(self.vtcombo_mysql_port)]) + + +vtcombo_process = None + + +def start_vt_processes(directory, topology, mysql_db, schema_dir, + charset='utf8', mysql_server_bind_address=None): + """Start the vt processes. + + Args: + directory: the toplevel directory for the processes (logs, ...) + topology: a vttest.VTTestTopology object. + mysql_db: an instance of the mysql_db.MySqlDB class. + schema_dir: the directory that contains the schema / vschema. + charset: the character set for the database connections. + mysql_server_bind_address: MySQL server bind address for vtcombo. + """ + global vtcombo_process + + logging.info('start_vt_processes(directory=%s,vtcombo_binary=%s)', + directory, environment.vtcombo_binary) + vtcombo_process = VtcomboProcess(directory, topology, mysql_db, schema_dir, + charset, mysql_server_bind_address=mysql_server_bind_address) + vtcombo_process.wait_start() + + +def kill_vt_processes(): + """Call kill() on all processes.""" + logging.info('kill_vt_processes()') + if vtcombo_process: + vtcombo_process.kill() + + +def wait_vt_processes(): + """Call wait() on all processes.""" + logging.info('wait_vt_processes()') + if vtcombo_process: + vtcombo_process.wait() + + +def kill_and_wait_vt_processes(): + """Call kill() and then wait() on all processes.""" + kill_vt_processes() + wait_vt_processes() + + +# wait_step is a helper for looping until a condition is true. +# use as follow: +# timeout = 10 +# while True: +# if done: +# break +# timeout = utils.wait_step('condition', timeout) +def wait_step(msg, timeout, sleep_time=1.0): + timeout -= sleep_time + if timeout <= 0: + raise Exception("timeout waiting for condition '%s'" % msg) + logging.debug("Sleeping for %f seconds waiting for condition '%s'", + sleep_time, msg) + time.sleep(sleep_time) + return timeout From c6e996ea16cb4877ddb21efc0ef2de9b42f97186 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Fri, 13 Mar 2020 17:01:05 +0530 Subject: [PATCH 312/825] Added vtproto files back Signed-off-by: Arindam Nayak --- py/vtproto/vttest_pb2.py | 206 ++++++++++++++++++++++++++++++++++ py/vtproto/vttest_pb2_grpc.py | 3 + 2 files changed, 209 insertions(+) create mode 100644 py/vtproto/vttest_pb2.py create mode 100644 py/vtproto/vttest_pb2_grpc.py diff --git a/py/vtproto/vttest_pb2.py b/py/vtproto/vttest_pb2.py new file mode 100644 index 00000000000..1c414f959bd --- /dev/null +++ b/py/vtproto/vttest_pb2.py @@ -0,0 +1,206 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: vttest.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='vttest.proto', + package='vttest', + syntax='proto3', + serialized_options=_b('Z#vitess.io/vitess/go/vt/proto/vttest'), + serialized_pb=_b('\n\x0cvttest.proto\x12\x06vttest\"/\n\x05Shard\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x18\n\x10\x64\x62_name_override\x18\x02 \x01(\t\"\xb5\x01\n\x08Keyspace\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1d\n\x06shards\x18\x02 \x03(\x0b\x32\r.vttest.Shard\x12\x1c\n\x14sharding_column_name\x18\x03 \x01(\t\x12\x1c\n\x14sharding_column_type\x18\x04 \x01(\t\x12\x13\n\x0bserved_from\x18\x05 \x01(\t\x12\x15\n\rreplica_count\x18\x06 \x01(\x05\x12\x14\n\x0crdonly_count\x18\x07 \x01(\x05\"D\n\x0eVTTestTopology\x12#\n\tkeyspaces\x18\x01 \x03(\x0b\x32\x10.vttest.Keyspace\x12\r\n\x05\x63\x65lls\x18\x02 \x03(\tB%Z#vitess.io/vitess/go/vt/proto/vttestb\x06proto3') +) + + + + +_SHARD = _descriptor.Descriptor( + name='Shard', + full_name='vttest.Shard', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='vttest.Shard.name', index=0, + number=1, 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, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='db_name_override', full_name='vttest.Shard.db_name_override', index=1, + number=2, 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, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=24, + serialized_end=71, +) + + +_KEYSPACE = _descriptor.Descriptor( + name='Keyspace', + full_name='vttest.Keyspace', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='vttest.Keyspace.name', index=0, + number=1, 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, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='shards', full_name='vttest.Keyspace.shards', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='sharding_column_name', full_name='vttest.Keyspace.sharding_column_name', index=2, + number=3, 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, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='sharding_column_type', full_name='vttest.Keyspace.sharding_column_type', 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, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='served_from', full_name='vttest.Keyspace.served_from', index=4, + number=5, 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, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='replica_count', full_name='vttest.Keyspace.replica_count', index=5, + number=6, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rdonly_count', full_name='vttest.Keyspace.rdonly_count', index=6, + number=7, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=74, + serialized_end=255, +) + + +_VTTESTTOPOLOGY = _descriptor.Descriptor( + name='VTTestTopology', + full_name='vttest.VTTestTopology', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='keyspaces', full_name='vttest.VTTestTopology.keyspaces', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='cells', full_name='vttest.VTTestTopology.cells', index=1, + number=2, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=257, + serialized_end=325, +) + +_KEYSPACE.fields_by_name['shards'].message_type = _SHARD +_VTTESTTOPOLOGY.fields_by_name['keyspaces'].message_type = _KEYSPACE +DESCRIPTOR.message_types_by_name['Shard'] = _SHARD +DESCRIPTOR.message_types_by_name['Keyspace'] = _KEYSPACE +DESCRIPTOR.message_types_by_name['VTTestTopology'] = _VTTESTTOPOLOGY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Shard = _reflection.GeneratedProtocolMessageType('Shard', (_message.Message,), dict( + DESCRIPTOR = _SHARD, + __module__ = 'vttest_pb2' + # @@protoc_insertion_point(class_scope:vttest.Shard) + )) +_sym_db.RegisterMessage(Shard) + +Keyspace = _reflection.GeneratedProtocolMessageType('Keyspace', (_message.Message,), dict( + DESCRIPTOR = _KEYSPACE, + __module__ = 'vttest_pb2' + # @@protoc_insertion_point(class_scope:vttest.Keyspace) + )) +_sym_db.RegisterMessage(Keyspace) + +VTTestTopology = _reflection.GeneratedProtocolMessageType('VTTestTopology', (_message.Message,), dict( + DESCRIPTOR = _VTTESTTOPOLOGY, + __module__ = 'vttest_pb2' + # @@protoc_insertion_point(class_scope:vttest.VTTestTopology) + )) +_sym_db.RegisterMessage(VTTestTopology) + + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vttest_pb2_grpc.py b/py/vtproto/vttest_pb2_grpc.py new file mode 100644 index 00000000000..a89435267cb --- /dev/null +++ b/py/vtproto/vttest_pb2_grpc.py @@ -0,0 +1,3 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + From d66b93d14b010f43fc5f0935eb9b37f9208c58bb Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Fri, 13 Mar 2020 17:32:38 +0530 Subject: [PATCH 313/825] make java test case, as re-adding files back to required location, not fixing the error Signed-off-by: Arindam Nayak --- py/vttest/run_local_database.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/py/vttest/run_local_database.py b/py/vttest/run_local_database.py index ba49b6546c1..52452713a54 100755 --- a/py/vttest/run_local_database.py +++ b/py/vttest/run_local_database.py @@ -53,7 +53,6 @@ from google.protobuf import text_format from vtproto import vttest_pb2 -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import from vttest import environment from vttest import init_data_options from vttest import local_database @@ -124,8 +123,29 @@ def main(cmdline_options): ' the local cluster. For example, did you forget the shutdown in' ' your test\'s tearDown()?\n' % os.path.basename(__file__)) -if __name__ == '__main__': +def _prefer_vtroot_imports(): + """Reorder sys.path to put $VTROOT/dist before others.""" + + vtroot = os.environ.get('VTROOT') + if not vtroot: + # VTROOT is not set. Don't try anything. + return + dist = os.path.join(vtroot, 'dist') + + dist_paths = [] + other_paths = [] + for path in sys.path: + if path: + if path.startswith(dist): + dist_paths.append(path) + else: + other_paths.append(path) + + sys.path = [''] + dist_paths + other_paths + +if __name__ == '__main__': + _prefer_vtroot_imports() parser = optparse.OptionParser() parser.add_option( '-p', '--port', type='int', From 0c6ea60cb9f32519298427265b8bee6fcf134c9b Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Mon, 16 Mar 2020 13:23:12 +0530 Subject: [PATCH 314/825] fix java test in CI Signed-off-by: Arindam Nayak --- .gitignore | 1 + py/vtdb/__init__.py | 36 ++++++++++++++++++++++ py/vtdb/prefer_vtroot_imports.py | 53 ++++++++++++++++++++++++++++++++ py/vtproto/__init__.py | 0 py/vttest/run_local_database.py | 24 ++------------- 5 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 py/vtdb/__init__.py create mode 100644 py/vtdb/prefer_vtroot_imports.py create mode 100644 py/vtproto/__init__.py diff --git a/.gitignore b/.gitignore index 57701a9c309..d457f289e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ releases /vthook/ /bin/ /vtdataroot/ +venv .scannerwork report \ No newline at end of file diff --git a/py/vtdb/__init__.py b/py/vtdb/__init__.py new file mode 100644 index 00000000000..278ebfd69c6 --- /dev/null +++ b/py/vtdb/__init__.py @@ -0,0 +1,36 @@ +"""This file provides the PEP0249 compliant variables for this module. + +See https://www.python.org/dev/peps/pep-0249 for more information on these. +""" + +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Follows the Python Database API 2.0. +apilevel = '2.0' + +# Threads may share the module, but not connections. +# (we store session information in the connection now, that should be in the +# cursor but are not for historical reasons). +threadsafety = 2 + +# Named style, e.g. ...WHERE name=:name. +# +# Note we also provide a function in dbapi to convert from 'pyformat' +# to 'named', and prune unused bind variables in the SQL query. +# +# Also, we use an extension to bind variables to handle lists: +# Using the '::name' syntax (instead of ':name') will indicate a list bind +# variable. The type then has to be a list, set or tuple. +paramstyle = 'named' diff --git a/py/vtdb/prefer_vtroot_imports.py b/py/vtdb/prefer_vtroot_imports.py new file mode 100644 index 00000000000..6456a7e8233 --- /dev/null +++ b/py/vtdb/prefer_vtroot_imports.py @@ -0,0 +1,53 @@ +# Copyright 2019 The Vitess Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Reorder sys.path to put $VTROOT/dist/* paths before others. + +This ensures libraries installed there will be preferred over other versions +that may be present at the system level. We do this at runtime because +regardless of what we set in the PYTHONPATH environment variable, the system +dist-packages folder gets prepended sometimes. + +To use this, just import it before importing packages that you want to make +sure are overridden from $VTROOT/dist. + +from vtdb import prefer_vtroot_imports # pylint: disable=unused-import +""" + +import os +import sys + + +def _prefer_vtroot_imports(): + """Reorder sys.path to put $VTROOT/dist before others.""" + + vtroot = os.environ.get('VTROOT') + if not vtroot: + # VTROOT is not set. Don't try anything. + return + dist = os.path.join(vtroot, 'dist') + + dist_paths = [] + other_paths = [] + + for path in sys.path: + if path: + if path.startswith(dist): + dist_paths.append(path) + else: + other_paths.append(path) + + sys.path = [''] + dist_paths + other_paths + +_prefer_vtroot_imports() diff --git a/py/vtproto/__init__.py b/py/vtproto/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/py/vttest/run_local_database.py b/py/vttest/run_local_database.py index 52452713a54..ba49b6546c1 100755 --- a/py/vttest/run_local_database.py +++ b/py/vttest/run_local_database.py @@ -53,6 +53,7 @@ from google.protobuf import text_format from vtproto import vttest_pb2 +from vtdb import prefer_vtroot_imports # pylint: disable=unused-import from vttest import environment from vttest import init_data_options from vttest import local_database @@ -123,29 +124,8 @@ def main(cmdline_options): ' the local cluster. For example, did you forget the shutdown in' ' your test\'s tearDown()?\n' % os.path.basename(__file__)) -def _prefer_vtroot_imports(): - """Reorder sys.path to put $VTROOT/dist before others.""" - - vtroot = os.environ.get('VTROOT') - if not vtroot: - # VTROOT is not set. Don't try anything. - return - dist = os.path.join(vtroot, 'dist') - - dist_paths = [] - other_paths = [] - - for path in sys.path: - if path: - if path.startswith(dist): - dist_paths.append(path) - else: - other_paths.append(path) - - sys.path = [''] + dist_paths + other_paths - if __name__ == '__main__': - _prefer_vtroot_imports() + parser = optparse.OptionParser() parser.add_option( '-p', '--port', type='int', From 0cb185928edd33fbbbf60531bd890634b12bbe22 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Mon, 16 Mar 2020 15:06:15 +0530 Subject: [PATCH 315/825] debug timeout testcase Signed-off-by: Arindam Nayak --- .github/workflows/cluster_endtoend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cluster_endtoend.yml b/.github/workflows/cluster_endtoend.yml index 1761630018b..a5cb98fd7d1 100644 --- a/.github/workflows/cluster_endtoend.yml +++ b/.github/workflows/cluster_endtoend.yml @@ -42,5 +42,5 @@ jobs: - name: sharded cluster_endtoend run: | source build.env - eatmydata -- go run test.go -docker=false -print-log -shard ${{matrix.name}} + eatmydata -- go run test.go -docker=false -print-log -follow -shard ${{matrix.name}} From 6bc9380b2ce47f7ac3316204fe26e42b600b0103 Mon Sep 17 00:00:00 2001 From: Arindam Nayak Date: Tue, 17 Mar 2020 16:17:22 +0530 Subject: [PATCH 316/825] Removed java-hadoop test and py-vtdb , PYTHONPATH Signed-off-by: Arindam Nayak --- .dockerignore | 1 - .gitignore | 1 - build.env | 1 - dev.env | 24 - docker/bootstrap/Dockerfile.common | 1 - docker/test/run.sh | 3 +- java/hadoop/pom.xml | 125 ----- .../src/main/java/io/vitess/hadoop/README.md | 96 ---- .../java/io/vitess/hadoop/RowWritable.java | 92 ---- .../main/java/io/vitess/hadoop/SplitQuery.png | Bin 33951 -> 0 bytes .../java/io/vitess/hadoop/VitessConf.java | 129 ----- .../io/vitess/hadoop/VitessInputFormat.java | 126 ----- .../io/vitess/hadoop/VitessInputSplit.java | 69 --- .../io/vitess/hadoop/VitessRecordReader.java | 149 ------ .../java/io/vitess/hadoop/MapReduceIT.java | 250 ---------- java/pom.xml | 1 - py/vtdb/__init__.py | 36 -- py/vtdb/prefer_vtroot_imports.py | 53 -- py/vtproto/__init__.py | 0 py/vtproto/vttest_pb2.py | 206 -------- py/vtproto/vttest_pb2_grpc.py | 3 - py/vttest/__init__.py | 18 - py/vttest/environment.py | 116 ----- py/vttest/init_data_options.py | 34 -- py/vttest/local_database.py | 469 ------------------ py/vttest/mysql_db.py | 53 -- py/vttest/mysql_db_mysqlctl.py | 92 ---- py/vttest/mysql_flavor.py | 114 ----- py/vttest/run_local_database.py | 212 -------- py/vttest/sharding_utils.py | 80 --- py/vttest/vt_processes.py | 231 --------- 31 files changed, 1 insertion(+), 2784 deletions(-) delete mode 100644 java/hadoop/pom.xml delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/README.md delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/SplitQuery.png delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessConf.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java delete mode 100644 java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java delete mode 100644 java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java delete mode 100644 py/vtdb/__init__.py delete mode 100644 py/vtdb/prefer_vtroot_imports.py delete mode 100644 py/vtproto/__init__.py delete mode 100644 py/vtproto/vttest_pb2.py delete mode 100644 py/vtproto/vttest_pb2_grpc.py delete mode 100644 py/vttest/__init__.py delete mode 100644 py/vttest/environment.py delete mode 100644 py/vttest/init_data_options.py delete mode 100644 py/vttest/local_database.py delete mode 100644 py/vttest/mysql_db.py delete mode 100644 py/vttest/mysql_db_mysqlctl.py delete mode 100644 py/vttest/mysql_flavor.py delete mode 100755 py/vttest/run_local_database.py delete mode 100644 py/vttest/sharding_utils.py delete mode 100644 py/vttest/vt_processes.py diff --git a/.dockerignore b/.dockerignore index 58354ff4211..98cca97e5d5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,7 +6,6 @@ java/*/bin php/vendor releases /dist/ -/py-vtdb/ /vthook/ /bin/ /vtdataroot/ diff --git a/.gitignore b/.gitignore index d457f289e2b..041443eeedd 100644 --- a/.gitignore +++ b/.gitignore @@ -78,7 +78,6 @@ releases .vagrant /dist/ -/py-vtdb /vthook/ /bin/ /vtdataroot/ diff --git a/build.env b/build.env index 467749a10f1..fa83ff30ea1 100755 --- a/build.env +++ b/build.env @@ -32,7 +32,6 @@ mkdir -p "$VTDATAROOT" # Set up required soft links. # TODO(mberlin): Which of these can be deleted? -ln -snf "$PWD/py" py-vtdb ln -snf "$PWD/go/vt/zkctl/zksrv.sh" bin/zksrv.sh ln -snf "$PWD/test/vthook-test.sh" vthook/test.sh ln -snf "$PWD/test/vthook-test_backup_error" vthook/test_backup_error diff --git a/dev.env b/dev.env index f256abbd93a..f1e6e71b896 100644 --- a/dev.env +++ b/dev.env @@ -24,30 +24,6 @@ source ./build.env export VTPORTSTART=15000 -# Add all site-packages or dist-packages directories below $VTROOT/dist to $PYTHONPATH. -BACKUP_IFS="$IFS" -IFS=" -" -# Note that the escaped ( ) around the -or expression are important. -# Otherwise, the -print would match *all* files. -for pypath in $(find "$VTROOT/dist" \( -name site-packages -or -name dist-packages \) -print); do - PYTHONPATH=$(prepend_path "$PYTHONPATH" "$pypath") -done -IFS="$BACKUP_IFS" - -PYTHONPATH=$(prepend_path "$PYTHONPATH" "$VTROOT/py-vtdb") -PYTHONPATH=$(prepend_path "$PYTHONPATH" "$VTROOT/dist/selenium") -PYTHONPATH=$(prepend_path "$PYTHONPATH" "$VTROOT/test") -PYTHONPATH=$(prepend_path "$PYTHONPATH" "$VTROOT/test/cluster/sandbox") -export PYTHONPATH - -# Ensure bootstrap.sh uses python2 on systems which default to python3. -command -v python2 >/dev/null && PYTHON=python2 || PYTHON=python -export PYTHON -command -v pip2 >/dev/null && PIP=pip2 || PIP=pip -export PIP -command -v virtualenv2 >/dev/null && VIRTUALENV=virtualenv2 || VIRTUALENV=virtualenv -export VIRTUALENV # Add chromedriver to path for Selenium tests. PATH=$(prepend_path "$PATH" "$VTROOT/dist/chromedriver") diff --git a/docker/bootstrap/Dockerfile.common b/docker/bootstrap/Dockerfile.common index 6a9255b38e7..4f7f22eb3a1 100644 --- a/docker/bootstrap/Dockerfile.common +++ b/docker/bootstrap/Dockerfile.common @@ -40,7 +40,6 @@ RUN mkdir -p /vt/src/vitess.io/vitess/dist && \ ENV VTROOT /vt/src/vitess.io/vitess ENV VTDATAROOT /vt/vtdataroot ENV VTPORTSTART 15000 -ENV PYTHONPATH $VTROOT/dist/grpc/usr/local/lib/python2.7/site-packages:$VTROOT/dist/py-mock-1.0.1/lib/python2.7/site-packages:$VTROOT/py-vtdb:$VTROOT/dist/selenium/lib/python2.7/site-packages ENV PATH $VTROOT/bin:$VTROOT/dist/maven/bin:$VTROOT/dist/chromedriver:$PATH ENV USER vitess diff --git a/docker/test/run.sh b/docker/test/run.sh index df0c6a20f82..539a7fa57e0 100755 --- a/docker/test/run.sh +++ b/docker/test/run.sh @@ -165,14 +165,13 @@ bashcmd="" if [[ -z "$existing_cache_image" ]]; then # Construct "cp" command to copy the source code. - bashcmd=$(append_cmd "$bashcmd" "cp -R /tmp/src/!(vtdataroot|dist|bin|lib|vthook|py-vtdb) . && cp -R /tmp/src/.git .") + bashcmd=$(append_cmd "$bashcmd" "cp -R /tmp/src/!(vtdataroot|dist|bin|lib|vthook) . && cp -R /tmp/src/.git .") fi # Reset the environment if this was an old bootstrap. We can detect this from VTTOP presence. bashcmd=$(append_cmd "$bashcmd" "export VTROOT=/vt/src/vitess.io/vitess") bashcmd=$(append_cmd "$bashcmd" "export VTDATAROOT=/vt/vtdataroot") -bashcmd=$(append_cmd "$bashcmd" "export PYTHONPATH=/vt/src/vitess.io/vitess/dist/grpc/usr/local/lib/python2.7/site-packages:/vt/src/vitess.io/vitess/dist/py-mock-1.0.1/lib/python2.7/site-packages:/vt/src/vitess.io/vitess/py-vtdb:/vt/src/vitess.io/vitess/dist/selenium/lib/python2.7/site-packages") bashcmd=$(append_cmd "$bashcmd" "mkdir -p dist; mkdir -p bin; mkdir -p lib; mkdir -p vthook") bashcmd=$(append_cmd "$bashcmd" "rm -rf /vt/dist; ln -s /vt/src/vitess.io/vitess/dist /vt/dist") diff --git a/java/hadoop/pom.xml b/java/hadoop/pom.xml deleted file mode 100644 index 37e815dde24..00000000000 --- a/java/hadoop/pom.xml +++ /dev/null @@ -1,125 +0,0 @@ - - - 4.0.0 - - io.vitess - vitess-parent - 5.0-SNAPSHOT - - vitess-hadoop - - - - com.google.guava - guava - - - com.google.protobuf - protobuf-java - - - - io.vitess - vitess-client - - - io.vitess - vitess-client - test-jar - test - - - io.vitess - vitess-grpc-client - - - - joda-time - joda-time - - - - org.apache.hadoop - hadoop-common - - - org.apache.hadoop - hadoop-mapreduce-client-core - - - org.apache.hadoop - hadoop-mapreduce-client-jobclient - - test-jar - test - - - - com.google.code.gson - gson - - - - - junit - junit - test - - - - - org.apache.hadoop - hadoop-client - test - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 - - false - true - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.1 - - - - integration-test - verify - - - - - false - true - - io.vitess.client.TestEnv - grpc_port - io.vitess.client.grpc.GrpcClientFactory - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - io.vitess:vitess-grpc-client - - org.apache.hadoop:hadoop-client - - - - - - diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/README.md b/java/hadoop/src/main/java/io/vitess/hadoop/README.md deleted file mode 100644 index 9e4a083bd4c..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# Hadoop Integration - -This package contains the necessary implementations for providing Hadoop support on Vitess. This allows mapreducing over tables stored in Vitess from Hadoop. - -Let's look at an example. Consider a table with the following schema that is stored in Vitess across several shards. - -``` -create table sample_table ( -id bigint auto_increment, -name varchar(64), -keyspace_id bigint(20) unsigned NOT NULL, -primary key (id)) Engine=InnoDB; -``` - -Let's say we want to write a MapReduce job that imports this table from Vitess to HDFS where each row is turned into a CSV record in HDFS. - -We can use [VitessInputFormat](https://github.com/vitessio/vitess/blob/master/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java), an implementation of Hadoop's [InputFormat](https://hadoop.apache.org/docs/stable/api/org/apache/hadoop/mapred/InputFormat.html), for that. With VitessInputFormat, rows from the source table are streamed to the mapper task. Each input record has a [NullWritable](https://hadoop.apache.org/docs/current/api/org/apache/hadoop/io/NullWritable.html) key (no key, really), and [RowWritable](https://github.com/vitessio/vitess/blob/master/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java) as value, which is a writable implementation for the entire row's contents. - -Here is an example implementation of our mapper, which transforms each row into a CSV Text. - -```java -public class TableCsvMapper extends - Mapper { - @Override - public void map(NullWritable key, RowWritable value, Context context) - throws IOException, InterruptedException { - Row row = value.getRow(); - StringBuilder asCsv = new StringBuilder(); - asCsv.append(row.getInt("id")); - asCsv.append(","); - asCsv.append(row.getString("name")); - asCsv.append(","); - asCsv.append(row.getULong("keyspace_id")); - context.write(NullWritable.get(), new Text(asCsv.toString())); - } -} -``` - -The controller code for this MR job is shown below. Note that we are not specifying any sharding/replication related information here. VtGate figures out the right number of shards and replicas to fetch the rows from. The MR author only needs to worry about which rows to fetch (query), how to process them (mapper) and the extent of parallelism (splitCount). - -```java -public static void main(String[] args) { - Configuration conf = new Configuration(); - Job job = Job.getInstance(conf, "map vitess table"); - job.setJarByClass(VitessInputFormat.class); - job.setMapperClass(TableCsvMapper.class); - String vtgateAddresses = "localhost:15011,localhost:15012,localhost:15013"; - String keyspace = "SAMPLE_KEYSPACE"; - String query = "select id, name from sample_table"; - int splitCount = 100; - VitessInputFormat.setInput(job, vtgateAddresses, keyspace, query, splitCount); - job.setMapOutputKeyClass(NullWritable.class); - job.setMapOutputValueClass(Text.class); - ...// set reducer and outputpath and launch the job -} -``` - -Refer [this integration test](https://github.com/vitessio/vitess/blob/master/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java) for a working example a MapReduce job on Vitess. - -## How it Works - -VitessInputFormat relies on VtGate's [SplitQuery](https://github.com/vitessio/vitess/blob/21515f5c1a85c0054ddf7d2ff068702670ab93b5/proto/vtgateservice.proto#L98) RPC to obtain the input splits. This RPC method accepts a SplitQueryRequest which consists of an input query and the desired number of splits (splitCount). SplitQuery returns SplitQueryResult, which has a list of SplitQueryParts. SplitQueryPart consists of a KeyRangeQuery and a size estimate of how many rows this sub-query might return. SplitQueryParts return rows that are mutually exclusive and collectively exhaustive - all rows belonging to the original input query will be returned by one and exactly one SplitQueryPart. - -VitessInputFormat turns each SplitQueryPart into a mapper task. The number of splits generated may not be exactly equal to the desired split count specified in the input. Specifically, if the desired split count is not a multiple of the number of shards, then VtGate will round it up to the next bigger multiple of number of shards. - -In addition to splitting the query, the SplitQuery service also acts as a gatekeeper that rejects queries unsuitable for MapReduce. Queries with potentially expensive operations such as Joins, Group By, inner queries, Distinct, Order By, etc are not allowed. Specifically, only input queries of the following syntax are permitted. - -``` -select [list of columns] from table where [list of simple column filters]; -``` - -There are additional constraints on the table schema to ensure that the sub queries do not result in full table scans. The table must have a primary key (simple or composite) and the leading primary key must be of one of the following types. -``` -VT_TINY, VT_SHORT, VT_LONG, VT_LONGLONG, VT_INT24, VT_FLOAT, VT_DOUBLE -``` - -#### Split Generation - -Here's how SplitQuery works. VtGate forwards the input query to randomly chosen ‘rdonly’ vttablets in each shard with a split count, M = original split count / N, where N is the number of shards. Each vttablet parses the query and rejects it if it does not meet the constraints. If it is a valid query, the tablet fetches the min and max value for the leading primary key column from MySQL. Split the [min, max] range of into M intervals. Construct subqueries by appending where clauses corresponding to PK range intervals to the original query and return it to VtGate. VtGate aggregates the splits received from tablets and constructs KeyRangeQueries by appending KeyRange corresponding to that shard. The following diagram depicts this flow for a sample request of split size 6 on a cluster with two shards. - -![Image](SplitQuery.png) - -## Other Considerations - -1. Specifying splitCount - Each split is a streaming query executed by a single mapper task. splitCount determines the number of mapper tasks that will be created and thus the extent of parallelism. Having too few, but long-running, splits would limit the throughput of the MR job as a whole. Long-running splits also makes retries of individual tasks failures more expensive as compared to leaner splits. On the other side, having too many splits can lead to extra overhead in task setup and connection overhead with VtGate. So, identifying the ideal split count is a balance between the two. - -2. Joining multiple tables - Currently Vitess does not currently mapping over joined tables. However, this can be easily achieved by writing a multi-mapper MapReduce job and performing a reduce-side join in the MR job. - -3. Views - Database Views are not great for full-table scans. If you need to map over a View, consider mapping over the underlying tables instead. - - - - - - - diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java b/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java deleted file mode 100644 index ca91d512ed9..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/RowWritable.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import com.google.common.collect.Lists; -import com.google.common.io.BaseEncoding; -import com.google.gson.Gson; - -import io.vitess.client.cursor.Row; -import io.vitess.proto.Query; -import io.vitess.proto.Query.Field; - -import org.apache.hadoop.io.Writable; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class RowWritable implements Writable { - - private Row row; - - public RowWritable() { - } - - public RowWritable(Row row) { - this.row = row; - } - - public Row get() { - return row; - } - - @Override - public void write(DataOutput out) throws IOException { - out.writeInt(row.getFields().size()); - for (Field field : row.getFields()) { - out.writeUTF(BaseEncoding.base64().encode(field.toByteArray())); - } - out.writeUTF(BaseEncoding.base64().encode(row.getRowProto().toByteArray())); - } - - @Override - public void readFields(DataInput in) throws IOException { - int numFields = in.readInt(); - List fields = Lists.newArrayListWithCapacity(numFields); - for (int i = 0; i < numFields; i++) { - fields.add(Field.parseFrom(BaseEncoding.base64().decode(in.readUTF()))); - } - Query.Row rowProto = Query.Row.parseFrom(BaseEncoding.base64().decode(in.readUTF())); - row = new Row(fields, rowProto); - } - - @Override - public String toString() { - List fields = row.getFields(); - - Map map = new HashMap<>(); - for (int i = 0; i < fields.size(); i++) { - String key = fields.get(i).getName(); - // Prefer the first column if there are duplicate names. - // This matches JDBC ResultSet behavior. - if (!map.containsKey(key)) { - try { - map.put(key, row.getRawValue(i + 1).toStringUtf8()); - } catch (SQLException exc) { - map.put(key, exc.toString()); - } - } - } - - return new Gson().toJson(map); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/SplitQuery.png b/java/hadoop/src/main/java/io/vitess/hadoop/SplitQuery.png deleted file mode 100644 index 1040e0b2ae637a3e17c466733bd807423d4e11e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33951 zcmcG#cU)6l^Dk-xDI!e-sR9B5(xi9k9h4%7RFNjVhENql?;Qn#B2~Hw1QI|(uaP1_ zs-a6s=mA2xi9XMJ-gD0He(qoQpX_Aqz1Ga0nf0BrCNYooH7Lj!$u3>GM4_drYIy0= zW%Q*>SL8{E@gt`uIog*ly}qQSs$?86wb??c3)^^x1?KxWOvk9%3%K{h`dM*h8oSnf zGlw^8ki_ImMCTfau> zEwQAc^$s1aZUEq?<;=3*i0D9;kL7C9$_fw+bjS|7F>l~~-C^b3DncF`5`x)izL3^1 zk2sz-4w(NAxD|S&5tbSg6Z5;h3?n~p_}a2bk5;S}G_DCzJlDzRV?OJkqot)CdrRAy z1J@&o$iY8~zkc)-6%ly?A^qpdI|F(Qd6%7ie+EQFCW(JOBIn=he@%+Wfe&?ds-n0- z|6c!{^cccBqpPcX%U${YKiB_IipU|3`b+bFr2St^kh0%$=jP@nll*)A&lmi_e+Uu? z{~sK%q~}wX+>Np~}BlkBO<*JqtAwyE`{^+yIrUOSmhtnL;`bbhRf% z9As7>^%pyR`y=Q^*OM9RGdmQ8hi*?Snk@|FK`vBe7~wG-GQBbXCZ!t}cmq^?r?dDc z=sAW*7qat=pX@eX;&`1wyPenGvNel}we19jF`gW2gssy8@-N^eq4K-TMIa_{An)5m zD*nV;+vOUNUBPgMDkRN91@luvpHW~Y>>Q8ulwd+2wSA8-ig>Il$<*O1V8s!)sU^<9H`AyWwZ zH@1`3J~(~LZ#}i+x97nZQ;-!v2&QvZB)ae}*+fvt~=;RaZ@^~4c8ITiVYsfTTO6mKjUPd_hJh$U=li-9m$3_?VJ0= z6a}gavGEMzka{6)9IKv$vuw_q;qCse>7K?<@Sk`D$woj&!B@( z$)Tl9AR$U> zSJ^iM5>h9$0m`4)H=q4$!rCNZ%8%;jX+lbEkJD5k2|Ka6e8E)OTWmiR4EM*( z4XEHmJL7hq`BdT{fX;!J21RZh4O%$Gc&Eeh2Mzk(M{iJP3Y%?ynPe3rc4O*$TNvgX z=X4LWOrBVuAin?h6Vp{{GPiG#WM`LK?gV^76F;yDKYiU;F~=JJ72}q{IkPt1;8m}|NLnt1EH`0GKah%44+682iwk9>2dY&iVTGM-Y-aX^0)v5?j7kafx z48uXiKkUnJM^j&We!cwo#T~AhwUP#P3wpOV+b3ru+f&;i`+h2(JBrGSwh1J3?(wdX ztE)j5GBU9D@C4i*6ZK(8^nI(1aIyE_k(*ITBS{xjsEOJuwr|ILuQjpcK6OJ)Tn>KU z_FmXYn9rECp7j#mDwI)GO5xb@kY4{zPhM2F4L6Cn`p1n^7u}XoD*oc@N|VN4w|?=E zd`JT>NfFN@%dBMHKO>@=SJ7m{zTR7W|Fq&W`wZVGgx-Q4%*9A8bHYrDSb7r2Ocqk4!epsVO;1{d`fpTk%8A6$)vw^~Va^Fvf_Qgd1|@c?BAHSbq$0 z$^B*D{y}3ouI*A>pv@HhD-AW3qs=Q1gpa7ypr7f_qac$+3fnAUA8*4NR!5H~B;sA^ zpO8Mk)x|A8k$;nzx`DjrQFL$;ZS_to>($F|J%7L8IsEB6EXsH%pz@f$;kC~Al?POA z&`abXS=Y>b1-CJTZmL>0B>QPT1O&5xMp{gì+dM7 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%0DAlP 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 z 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 getSplitColumns() { - return conf.getStringCollection(SPLIT_COLUMNS); - } - - public void setSplitColumns(Collection splitColumns) { - conf.setStrings(SPLIT_COLUMNS, splitColumns.toArray(new String[0])); - } - - public int getNumRowsPerQueryPart() { - return conf.getInt(NUM_ROWS_PER_QUERY_PART, 100000); - } - - public void setNumRowsPerQueryPart(int numRowsPerQueryPart) { - conf.setInt(NUM_ROWS_PER_QUERY_PART, numRowsPerQueryPart); - } - - public SplitQueryRequest.Algorithm getAlgorithm() { - return conf.getEnum(ALGORITHM, SplitQueryRequest.Algorithm.EQUAL_SPLITS); - } - - public void setAlgorithm(SplitQueryRequest.Algorithm algorithm) { - conf.setEnum(ALGORITHM, algorithm); - } - - public String getRpcFactoryClass() { - return conf.get(RPC_FACTORY_CLASS); - } - - public void setRpcFactoryClass(Class clz) { - conf.set(RPC_FACTORY_CLASS, clz.getName()); - } - - public Query.ExecuteOptions.IncludedFields getIncludedFields() { - return conf.getEnum(INCLUDED_FIELDS, Query.ExecuteOptions.IncludedFields.TYPE_AND_NAME); - } - - public void setIncludedFields(Query.ExecuteOptions.IncludedFields includedFields) { - conf.setEnum(INCLUDED_FIELDS, includedFields); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java b/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java deleted file mode 100644 index 5a9326980d8..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputFormat.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.collect.Lists; - -import io.vitess.client.Context; -import io.vitess.client.RpcClient; -import io.vitess.client.RpcClientFactory; -import io.vitess.client.VTGateBlockingConn; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; -import io.vitess.proto.Vtgate.SplitQueryResponse; - -import org.apache.hadoop.io.NullWritable; -import org.apache.hadoop.mapreduce.InputFormat; -import org.apache.hadoop.mapreduce.InputSplit; -import org.apache.hadoop.mapreduce.Job; -import org.apache.hadoop.mapreduce.JobContext; -import org.apache.hadoop.mapreduce.RecordReader; -import org.apache.hadoop.mapreduce.TaskAttemptContext; - -import org.joda.time.Duration; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Random; - -/** - * {@link VitessInputFormat} is the {@link org.apache.hadoop.mapreduce.InputFormat} for tables in - * Vitess. Input splits ({@link VitessInputSplit}) are fetched from VtGate via an RPC. map() calls - * are supplied with a {@link RowWritable}. - */ -public class VitessInputFormat extends InputFormat { - - @Override - public List getSplits(JobContext context) { - VitessConf conf = new VitessConf(context.getConfiguration()); - List splitResult; - try { - @SuppressWarnings("unchecked") - Class rpcFactoryClass = - (Class) Class.forName(conf.getRpcFactoryClass()); - List addressList = Arrays.asList(conf.getHosts().split(",")); - int index = new Random().nextInt(addressList.size()); - - RpcClient rpcClient = rpcFactoryClass.newInstance().create( - Context.getDefault().withDeadlineAfter(Duration.millis(conf.getTimeoutMs())), - addressList.get(index)); - - try (VTGateBlockingConn vtgate = new VTGateBlockingConn(rpcClient)) { - splitResult = vtgate.splitQuery( - Context.getDefault(), - conf.getKeyspace(), - conf.getInputQuery(), - null /* bind vars */, - conf.getSplitColumns(), - conf.getSplitCount(), - conf.getNumRowsPerQueryPart(), - conf.getAlgorithm()); - } - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException - | IOException exc) { - throw new RuntimeException(exc); - } - - List splits = Lists.newArrayList(); - for (SplitQueryResponse.Part part : splitResult) { - splits.add(new VitessInputSplit(part)); - } - - for (InputSplit split : splits) { - ((VitessInputSplit) split).setLocations(conf.getHosts().split(VitessConf.HOSTS_DELIM)); - } - return splits; - } - - @Override - public RecordReader createRecordReader(InputSplit split, - TaskAttemptContext context) throws IOException, InterruptedException { - return new VitessRecordReader(); - } - - /** - * Sets the necessary configurations for Vitess table input source - */ - public static void setInput( - Job job, - String hosts, - String keyspace, - String query, - Collection splitColumns, - int splitCount, - int numRowsPerQueryPart, - Algorithm algorithm, - Class rpcFactoryClass) { - job.setInputFormatClass(VitessInputFormat.class); - VitessConf vtConf = new VitessConf(job.getConfiguration()); - vtConf.setHosts(checkNotNull(hosts)); - vtConf.setKeyspace(checkNotNull(keyspace)); - vtConf.setInputQuery(checkNotNull(query)); - vtConf.setSplitColumns(checkNotNull(splitColumns)); - vtConf.setSplitCount(splitCount); - vtConf.setNumRowsPerQueryPart(numRowsPerQueryPart); - vtConf.setAlgorithm(checkNotNull(algorithm)); - vtConf.setRpcFactoryClass(checkNotNull(rpcFactoryClass)); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java b/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java deleted file mode 100644 index 251bdf1f763..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/VitessInputSplit.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import com.google.common.io.BaseEncoding; - -import io.vitess.proto.Vtgate.SplitQueryResponse; - -import org.apache.hadoop.io.Writable; -import org.apache.hadoop.mapreduce.InputSplit; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - -public class VitessInputSplit extends InputSplit implements Writable { - - private String[] locations; - private SplitQueryResponse.Part split; - - public VitessInputSplit(SplitQueryResponse.Part split) { - this.split = split; - } - - public VitessInputSplit() { - } - - public SplitQueryResponse.Part getSplit() { - return split; - } - - public void setLocations(String[] locations) { - this.locations = locations; - } - - @Override - public long getLength() throws IOException, InterruptedException { - return split.getSize(); - } - - @Override - public String[] getLocations() throws IOException, InterruptedException { - return locations; - } - - @Override - public void readFields(DataInput input) throws IOException { - split = SplitQueryResponse.Part.parseFrom(BaseEncoding.base64().decode(input.readUTF())); - } - - @Override - public void write(DataOutput output) throws IOException { - output.writeUTF(BaseEncoding.base64().encode(split.toByteArray())); - } -} diff --git a/java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java b/java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java deleted file mode 100644 index 0f649f281e6..00000000000 --- a/java/hadoop/src/main/java/io/vitess/hadoop/VitessRecordReader.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import io.vitess.client.Context; -import io.vitess.client.RpcClient; -import io.vitess.client.RpcClientFactory; -import io.vitess.client.VTGateBlockingConn; -import io.vitess.client.cursor.Cursor; -import io.vitess.client.cursor.Row; -import io.vitess.proto.Query; -import io.vitess.proto.Query.BoundQuery; -import io.vitess.proto.Topodata.TabletType; -import io.vitess.proto.Vtgate.SplitQueryResponse; - -import org.apache.hadoop.io.NullWritable; -import org.apache.hadoop.mapreduce.InputSplit; -import org.apache.hadoop.mapreduce.RecordReader; -import org.apache.hadoop.mapreduce.TaskAttemptContext; - -import org.joda.time.Duration; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -public class VitessRecordReader extends RecordReader { - - private VitessInputSplit split; - private VTGateBlockingConn vtgate; - private VitessConf conf; - private long rowsProcessed = 0; - private Cursor cursor; - private RowWritable currentRow; - private Query.ExecuteOptions.IncludedFields includedFields; - - /** - * Fetch connection parameters from Configuration and open VtGate connection. - */ - @Override - public void initialize(InputSplit split, TaskAttemptContext context) - throws IOException, InterruptedException { - this.split = (VitessInputSplit) split; - conf = new VitessConf(context.getConfiguration()); - try { - @SuppressWarnings("unchecked") - Class rpcFactoryClass = - (Class) Class.forName(conf.getRpcFactoryClass()); - List addressList = Arrays.asList(conf.getHosts().split(",")); - int index = new Random().nextInt(addressList.size()); - - RpcClient rpcClient = rpcFactoryClass.newInstance().create( - Context.getDefault().withDeadlineAfter(Duration.millis(conf.getTimeoutMs())), - addressList.get(index)); - vtgate = new VTGateBlockingConn(rpcClient); - includedFields = conf.getIncludedFields(); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException exc) { - throw new RuntimeException(exc); - } - } - - @Override - public void close() throws IOException { - if (vtgate != null) { - try { - vtgate.close(); - vtgate = null; - } catch (IOException exc) { - throw new RuntimeException(exc); - } - } - } - - @Override - public NullWritable getCurrentKey() throws IOException, InterruptedException { - return NullWritable.get(); - } - - @Override - public RowWritable getCurrentValue() throws IOException, InterruptedException { - return currentRow; - } - - @Override - public float getProgress() throws IOException, InterruptedException { - if (rowsProcessed > split.getLength()) { - return 0.9f; - } - return rowsProcessed / split.getLength(); - } - - /** - * Fetches the next row. If this is the first invocation for the split, execute the streaming - * query. Subsequent calls just advance the iterator. - */ - @Override - public boolean nextKeyValue() throws IOException, InterruptedException { - try { - if (cursor == null) { - SplitQueryResponse.Part splitInfo = split.getSplit(); - if (splitInfo.hasKeyRangePart()) { - BoundQuery query = splitInfo.getQuery(); - SplitQueryResponse.KeyRangePart keyRangePart = splitInfo.getKeyRangePart(); - cursor = vtgate.streamExecuteKeyRanges(Context.getDefault(), query.getSql(), - keyRangePart.getKeyspace(), keyRangePart.getKeyRangesList(), query.getBindVariables(), - TabletType.RDONLY, includedFields); - } else if (splitInfo.hasShardPart()) { - BoundQuery query = splitInfo.getQuery(); - SplitQueryResponse.ShardPart shardPart = splitInfo.getShardPart(); - cursor = vtgate.streamExecuteShards(Context.getDefault(), query.getSql(), - shardPart.getKeyspace(), shardPart.getShardsList(), query.getBindVariables(), - TabletType.RDONLY, includedFields); - } else { - throw new IllegalArgumentException("unknown split info: " + splitInfo); - } - } - Row row = cursor.next(); - if (row == null) { - currentRow = null; - } else { - currentRow = new RowWritable(row); - } - } catch (SQLException exc) { - throw new RuntimeException(exc); - } - - if (currentRow == null) { - return false; - } - rowsProcessed++; - return true; - } -} diff --git a/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java b/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java deleted file mode 100644 index 01e20b6eaf4..00000000000 --- a/java/hadoop/src/test/java/io/vitess/hadoop/MapReduceIT.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2019 The Vitess Authors. - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - - * http://www.apache.org/licenses/LICENSE-2.0 - - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vitess.hadoop; - -import com.google.common.collect.ImmutableList; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import io.vitess.client.TestEnv; -import io.vitess.client.TestUtil; -import io.vitess.proto.Query.SplitQueryRequest.Algorithm; - -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; -import org.apache.hadoop.io.IntWritable; -import org.apache.hadoop.io.NullWritable; -import org.apache.hadoop.mapred.HadoopTestCase; -import org.apache.hadoop.mapreduce.Job; -import org.apache.hadoop.mapreduce.MapReduceTestUtil; -import org.apache.hadoop.mapreduce.Mapper; -import org.apache.hadoop.mapreduce.Reducer; -import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; -import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.sql.SQLException; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import junit.extensions.TestSetup; -import junit.framework.TestSuite; -import vttest.Vttest.Keyspace; -import vttest.Vttest.Shard; -import vttest.Vttest.VTTestTopology; - - -/** - * Integration tests for MapReductions in Vitess. These tests use an in-process Hadoop cluster via - * {@link HadoopTestCase}. These tests are JUnit3 style because of this dependency. Vitess setup for - * these tests require at least one rdonly instance per shard. - */ -public class MapReduceIT extends HadoopTestCase { - - private static final int NUM_ROWS = 40; - - public static TestEnv testEnv = getTestEnv(); - - public MapReduceIT() throws IOException { - super(HadoopTestCase.LOCAL_MR, HadoopTestCase.LOCAL_FS, 1, 1); - } - - /** - * Run a mapper only MR job and verify all the rows in the source table were outputted into HDFS. - */ - public void testDumpTableToHDFS() throws Exception { - // Configurations for the job, output from mapper as Text - Configuration conf = createJobConf(); - Job job = Job.getInstance(conf); - job.setJobName("table"); - job.setJarByClass(VitessInputFormat.class); - job.setMapperClass(TableMapper.class); - VitessInputFormat.setInput( - job, - "localhost:" + testEnv.getPort(), - testEnv.getKeyspace(), - "select id, name, age from vtgate_test", - ImmutableList.of(), - 4 /* splitCount */, - 0 /* numRowsPerQueryPart */, - Algorithm.EQUAL_SPLITS, - TestUtil.getRpcClientFactory().getClass()); - job.setOutputKeyClass(NullWritable.class); - job.setOutputValueClass(RowWritable.class); - job.setOutputFormatClass(TextOutputFormat.class); - job.setNumReduceTasks(0); - - Path outDir = new Path(testEnv.getTestOutputPath(), "mrvitess/output"); - FileSystem fs = FileSystem.get(conf); - if (fs.exists(outDir)) { - fs.delete(outDir, true); - } - FileOutputFormat.setOutputPath(job, outDir); - - job.waitForCompletion(true); - assertTrue(job.isSuccessful()); - - String[] outputLines = MapReduceTestUtil.readOutput(outDir, conf).split("\n"); - // there should be one line per row in the source table - assertEquals(NUM_ROWS, outputLines.length); - Set actualAges = new HashSet<>(); - Set actualNames = new HashSet<>(); - - // Parse and verify we've gotten all the ages and rows. - Gson gson = new Gson(); - for (String line : outputLines) { - String[] parts = line.split("\t"); - actualAges.add(Long.valueOf(parts[0])); - - // Rows are written as JSON since this is TextOutputFormat. - String rowJson = parts[1]; - Type mapType = new TypeToken>() { - }.getType(); - @SuppressWarnings("unchecked") - Map map = (Map) gson.fromJson(rowJson, mapType); - actualNames.add(map.get("name")); - } - - Set expectedAges = new HashSet<>(); - Set expectedNames = new HashSet<>(); - for (long i = 1; i <= NUM_ROWS; i++) { - // Generate values that match TestUtil.insertRows(). - expectedAges.add(i % 10); - expectedNames.add("name_" + i); - } - assertEquals(expectedAges.size(), actualAges.size()); - assertTrue(actualAges.containsAll(expectedAges)); - assertEquals(NUM_ROWS, actualNames.size()); - assertTrue(actualNames.containsAll(expectedNames)); - } - - /** - * Map all rows and aggregate by age at the reducer. - */ - public void testReducerAggregateRows() throws Exception { - Configuration conf = createJobConf(); - - Job job = Job.getInstance(conf); - job.setJobName("table"); - job.setJarByClass(VitessInputFormat.class); - job.setMapperClass(TableMapper.class); - VitessInputFormat.setInput( - job, - "localhost:" + testEnv.getPort(), - testEnv.getKeyspace(), - "select id, name, age from vtgate_test", - ImmutableList.of(), - 1 /* splitCount */, - 0 /* numRowsPerQueryPart */, - Algorithm.EQUAL_SPLITS, - TestUtil.getRpcClientFactory().getClass()); - - job.setMapOutputKeyClass(IntWritable.class); - job.setMapOutputValueClass(RowWritable.class); - - job.setReducerClass(CountReducer.class); - job.setOutputKeyClass(NullWritable.class); - job.setOutputValueClass(IntWritable.class); - job.setOutputFormatClass(TextOutputFormat.class); - - Path outDir = new Path(testEnv.getTestOutputPath(), "mrvitess/output"); - FileSystem fs = FileSystem.get(conf); - if (fs.exists(outDir)) { - fs.delete(outDir, true); - } - FileOutputFormat.setOutputPath(job, outDir); - - job.waitForCompletion(true); - assertTrue(job.isSuccessful()); - - String[] outputLines = MapReduceTestUtil.readOutput(outDir, conf).split("\n"); - // There should be 10 different ages, because age = i % 10. - assertEquals(10, outputLines.length); - // All rows should be accounted for. - int totalRowsReduced = 0; - for (String line : outputLines) { - totalRowsReduced += Integer.parseInt(line); - } - assertEquals(NUM_ROWS, totalRowsReduced); - } - - public static class TableMapper - extends Mapper { - - @Override - public void map(NullWritable key, RowWritable value, Context context) - throws IOException, InterruptedException { - // Tag each record with its age. - try { - context.write(new IntWritable(value.get().getInt("age")), value); - } catch (SQLException e) { - throw new IOException(e); - } - } - } - - public static class CountReducer - extends Reducer { - - @Override - public void reduce(IntWritable key, Iterable values, Context context) - throws IOException, InterruptedException { - // Count how many records there are for each age. - int count = 0; - Iterator itr = values.iterator(); - while (itr.hasNext()) { - count++; - itr.next(); - } - context.write(NullWritable.get(), new IntWritable(count)); - } - } - - /** - * Create env with two shards each having a master, replica, and rdonly. - */ - static TestEnv getTestEnv() { - Keyspace keyspace = Keyspace.newBuilder().setName("test_keyspace") - .addShards(Shard.newBuilder().setName("-80").build()) - .addShards(Shard.newBuilder().setName("80-").build()).build(); - VTTestTopology topology = VTTestTopology.newBuilder().addKeyspaces(keyspace).build(); - TestEnv env = TestUtil.getTestEnv("test_keyspace", topology); - return env; - } - - public static TestSetup suite() { - return new TestSetup(new TestSuite(MapReduceIT.class)) { - - @Override - protected void setUp() throws Exception { - TestUtil.setupTestEnv(testEnv); - // Insert test rows - TestUtil.insertRows(testEnv, 1, NUM_ROWS); - } - - @Override - protected void tearDown() throws Exception { - TestUtil.teardownTestEnv(testEnv); - } - }; - - } -} diff --git a/java/pom.xml b/java/pom.xml index c433b863d81..b8ede24e018 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -24,7 +24,6 @@ client example grpc-client - hadoop jdbc diff --git a/py/vtdb/__init__.py b/py/vtdb/__init__.py deleted file mode 100644 index 278ebfd69c6..00000000000 --- a/py/vtdb/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -"""This file provides the PEP0249 compliant variables for this module. - -See https://www.python.org/dev/peps/pep-0249 for more information on these. -""" - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Follows the Python Database API 2.0. -apilevel = '2.0' - -# Threads may share the module, but not connections. -# (we store session information in the connection now, that should be in the -# cursor but are not for historical reasons). -threadsafety = 2 - -# Named style, e.g. ...WHERE name=:name. -# -# Note we also provide a function in dbapi to convert from 'pyformat' -# to 'named', and prune unused bind variables in the SQL query. -# -# Also, we use an extension to bind variables to handle lists: -# Using the '::name' syntax (instead of ':name') will indicate a list bind -# variable. The type then has to be a list, set or tuple. -paramstyle = 'named' diff --git a/py/vtdb/prefer_vtroot_imports.py b/py/vtdb/prefer_vtroot_imports.py deleted file mode 100644 index 6456a7e8233..00000000000 --- a/py/vtdb/prefer_vtroot_imports.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Reorder sys.path to put $VTROOT/dist/* paths before others. - -This ensures libraries installed there will be preferred over other versions -that may be present at the system level. We do this at runtime because -regardless of what we set in the PYTHONPATH environment variable, the system -dist-packages folder gets prepended sometimes. - -To use this, just import it before importing packages that you want to make -sure are overridden from $VTROOT/dist. - -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import -""" - -import os -import sys - - -def _prefer_vtroot_imports(): - """Reorder sys.path to put $VTROOT/dist before others.""" - - vtroot = os.environ.get('VTROOT') - if not vtroot: - # VTROOT is not set. Don't try anything. - return - dist = os.path.join(vtroot, 'dist') - - dist_paths = [] - other_paths = [] - - for path in sys.path: - if path: - if path.startswith(dist): - dist_paths.append(path) - else: - other_paths.append(path) - - sys.path = [''] + dist_paths + other_paths - -_prefer_vtroot_imports() diff --git a/py/vtproto/__init__.py b/py/vtproto/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/py/vtproto/vttest_pb2.py b/py/vtproto/vttest_pb2.py deleted file mode 100644 index 1c414f959bd..00000000000 --- a/py/vtproto/vttest_pb2.py +++ /dev/null @@ -1,206 +0,0 @@ -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: vttest.proto - -import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='vttest.proto', - package='vttest', - syntax='proto3', - serialized_options=_b('Z#vitess.io/vitess/go/vt/proto/vttest'), - serialized_pb=_b('\n\x0cvttest.proto\x12\x06vttest\"/\n\x05Shard\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x18\n\x10\x64\x62_name_override\x18\x02 \x01(\t\"\xb5\x01\n\x08Keyspace\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1d\n\x06shards\x18\x02 \x03(\x0b\x32\r.vttest.Shard\x12\x1c\n\x14sharding_column_name\x18\x03 \x01(\t\x12\x1c\n\x14sharding_column_type\x18\x04 \x01(\t\x12\x13\n\x0bserved_from\x18\x05 \x01(\t\x12\x15\n\rreplica_count\x18\x06 \x01(\x05\x12\x14\n\x0crdonly_count\x18\x07 \x01(\x05\"D\n\x0eVTTestTopology\x12#\n\tkeyspaces\x18\x01 \x03(\x0b\x32\x10.vttest.Keyspace\x12\r\n\x05\x63\x65lls\x18\x02 \x03(\tB%Z#vitess.io/vitess/go/vt/proto/vttestb\x06proto3') -) - - - - -_SHARD = _descriptor.Descriptor( - name='Shard', - full_name='vttest.Shard', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='vttest.Shard.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='db_name_override', full_name='vttest.Shard.db_name_override', index=1, - number=2, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=24, - serialized_end=71, -) - - -_KEYSPACE = _descriptor.Descriptor( - name='Keyspace', - full_name='vttest.Keyspace', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='name', full_name='vttest.Keyspace.name', index=0, - number=1, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shards', full_name='vttest.Keyspace.shards', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sharding_column_name', full_name='vttest.Keyspace.sharding_column_name', index=2, - number=3, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='sharding_column_type', full_name='vttest.Keyspace.sharding_column_type', 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='served_from', full_name='vttest.Keyspace.served_from', index=4, - number=5, 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, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='replica_count', full_name='vttest.Keyspace.replica_count', index=5, - number=6, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='rdonly_count', full_name='vttest.Keyspace.rdonly_count', index=6, - number=7, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=74, - serialized_end=255, -) - - -_VTTESTTOPOLOGY = _descriptor.Descriptor( - name='VTTestTopology', - full_name='vttest.VTTestTopology', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='keyspaces', full_name='vttest.VTTestTopology.keyspaces', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='cells', full_name='vttest.VTTestTopology.cells', index=1, - number=2, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=257, - serialized_end=325, -) - -_KEYSPACE.fields_by_name['shards'].message_type = _SHARD -_VTTESTTOPOLOGY.fields_by_name['keyspaces'].message_type = _KEYSPACE -DESCRIPTOR.message_types_by_name['Shard'] = _SHARD -DESCRIPTOR.message_types_by_name['Keyspace'] = _KEYSPACE -DESCRIPTOR.message_types_by_name['VTTestTopology'] = _VTTESTTOPOLOGY -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -Shard = _reflection.GeneratedProtocolMessageType('Shard', (_message.Message,), dict( - DESCRIPTOR = _SHARD, - __module__ = 'vttest_pb2' - # @@protoc_insertion_point(class_scope:vttest.Shard) - )) -_sym_db.RegisterMessage(Shard) - -Keyspace = _reflection.GeneratedProtocolMessageType('Keyspace', (_message.Message,), dict( - DESCRIPTOR = _KEYSPACE, - __module__ = 'vttest_pb2' - # @@protoc_insertion_point(class_scope:vttest.Keyspace) - )) -_sym_db.RegisterMessage(Keyspace) - -VTTestTopology = _reflection.GeneratedProtocolMessageType('VTTestTopology', (_message.Message,), dict( - DESCRIPTOR = _VTTESTTOPOLOGY, - __module__ = 'vttest_pb2' - # @@protoc_insertion_point(class_scope:vttest.VTTestTopology) - )) -_sym_db.RegisterMessage(VTTestTopology) - - -DESCRIPTOR._options = None -# @@protoc_insertion_point(module_scope) diff --git a/py/vtproto/vttest_pb2_grpc.py b/py/vtproto/vttest_pb2_grpc.py deleted file mode 100644 index a89435267cb..00000000000 --- a/py/vtproto/vttest_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -import grpc - diff --git a/py/vttest/__init__.py b/py/vttest/__init__.py deleted file mode 100644 index 4d32e37cccb..00000000000 --- a/py/vttest/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from vttest import environment -from vttest import mysql_db_mysqlctl - -environment.mysql_db_class = mysql_db_mysqlctl.MySqlDBMysqlctl diff --git a/py/vttest/environment.py b/py/vttest/environment.py deleted file mode 100644 index b84d14814de..00000000000 --- a/py/vttest/environment.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Contains environment specifications for vttest module. - -This module is meant to be overwritten upon import into a development -tree with the appropriate values. It works as is in the Vitess tree. -""" - -import os -import shutil -import tempfile - -# this is the location of the vtcombo binary -vtcombo_binary = os.path.join(os.environ['VTROOT'], 'bin', 'vtcombo') - -# this is the location of the mysqlctl binary, if mysql_db_mysqlctl is used. -mysqlctl_binary = os.path.join(os.environ['VTROOT'], 'bin', 'mysqlctl') - -# this is the base port set by options. -base_port = None - -# this is the class to use for MySqlDB instances -mysql_db_class = None - - -def get_test_directory(): - """Returns the toplevel directory for the tests. Might create it.""" - directory = tempfile.mkdtemp(prefix='vttest', - dir=os.environ.get('VTDATAROOT', None)) - # Override VTDATAROOT to point to the newly created dir - os.environ['VTDATAROOT'] = directory - os.mkdir(get_logs_directory(directory)) - return directory - - -def get_logs_directory(directory): - """Returns the directory for logs, might be based on directory. - - Args: - directory: the value returned by get_test_directory(). - Returns: - the directory for logs. - """ - return os.path.join(directory, 'logs') - - -def cleanup_test_directory(directory): - """Cleans up the test directory after the test is done. - - Args: - directory: the value returned by get_test_directory(). - """ - shutil.rmtree(directory) - - -def extra_vtcombo_parameters(): - """Returns extra parameters to send to vtcombo.""" - return [ - '-service_map', ','.join([ - 'grpc-vtgateservice', - 'grpc-vtctl', - ]), - ] - - -# pylint: disable=unused-argument -def process_is_healthy(name, addr): - - """Double-checks a process is healthy and ready for RPCs.""" - return True - - -def get_protocol(): - """Returns the protocol used between client and vtcombo.""" - return 'grpc' - - -def get_port(name, protocol=None): - """Returns the port to use for a given process. - - This is only called once per process, so picking an unused port will also - work. - - Args: - name: process name. - protocol: the protocol used. - - Returns: - the port to use. - - Raises: - ValueError: the port name is invalid. - """ - if name == 'vtcombo': - if protocol == 'grpc': - # We can't use the base_port for grpc. - return base_port + 1 - return base_port - elif name == 'mysql': - 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) diff --git a/py/vttest/init_data_options.py b/py/vttest/init_data_options.py deleted file mode 100644 index 04219f036b2..00000000000 --- a/py/vttest/init_data_options.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Stores options used for initializing the database with randomized data. - -The options stored correspond to command line flags. See run_local_database.py -for more details on each option. -""" - - -class InitDataOptions(object): - valid_attrs = set([ - 'rng_seed', - 'min_table_shard_size', - 'max_table_shard_size', - 'null_probability', - ]) - - def __setattr__(self, name, value): - if name not in self.valid_attrs: - raise Exception( - 'InitDataOptions: unsupported attribute: %s' % name) - self.__dict__[name] = value diff --git a/py/vttest/local_database.py b/py/vttest/local_database.py deleted file mode 100644 index 2634aef27a2..00000000000 --- a/py/vttest/local_database.py +++ /dev/null @@ -1,469 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Create a local Vitess database for testing.""" - -import glob -import logging -import os -import random -import re - -from vttest import environment -from vttest import vt_processes - - -class LocalDatabase(object): - """Set up a local Vitess database.""" - - def __init__(self, - topology, - schema_dir, - mysql_only, - init_data_options, - default_schema_dir=None, - extra_my_cnf=None, - snapshot_file=None, - charset='utf8', - mysql_server_bind_address=None): - """Initializes an object of this class. - - Args: - topology: a vttest.VTTestTopology object describing the topology. - schema_dir: see the documentation for the corresponding command line - flag in run_local_database.py - mysql_only: see the documentation for the corresponding command line - flag in run_local_database.py - init_data_options: an object of type InitDataOptions containing - options configuring populating the database with initial random data. - If the value is 'None' then the database will not be initialized - with random data. - default_schema_dir: a directory to use if no keyspace is found in the - schema_dir directory. - extra_my_cnf: additional cnf file to use for the EXTRA_MY_CNF var. - snapshot_file: A MySQL DB snapshot file. - charset: MySQL charset. - mysql_server_bind_address: MySQL server bind address. - """ - - self.topology = topology - self.schema_dir = schema_dir - self.mysql_only = mysql_only - self.init_data_options = init_data_options - self.default_schema_dir = default_schema_dir - self.extra_my_cnf = extra_my_cnf - self.snapshot_file = snapshot_file - self.charset = charset - self.mysql_server_bind_address = mysql_server_bind_address - - def setup(self): - """Create a MySQL instance and all Vitess processes.""" - mysql_port = environment.get_port('mysql') - self.directory = environment.get_test_directory() - self.mysql_db = environment.mysql_db_class( - self.directory, mysql_port, self.extra_my_cnf, self.snapshot_file) - - self.mysql_db.setup() - if not self.snapshot_file: - self.create_databases() - self.load_schema() - if self.init_data_options is not None: - self.rng = random.Random(self.init_data_options.rng_seed) - self.populate_with_random_data() - if self.mysql_only: - return - - vt_processes.start_vt_processes(self.directory, self.topology, - self.mysql_db, self.schema_dir, - charset=self.charset, mysql_server_bind_address=self.mysql_server_bind_address) - - def teardown(self): - """Kill all Vitess processes and wait for them to end. - - MySQLTestDB's wrapper script will take care of mysqld. - """ - if not self.mysql_only: - self.kill() - self.wait() - self.mysql_db.teardown() - environment.cleanup_test_directory(self.directory) - - def kill(self): - """Kill all Vitess processes.""" - vt_processes.kill_vt_processes() - - def wait(self): - """Wait for all Vitess processes to end.""" - vt_processes.wait_vt_processes() - - def vtgate_addr(self): - """Get the host:port for vtgate.""" - if environment.get_protocol() == 'grpc': - return vt_processes.vtcombo_process.grpc_addr() - return vt_processes.vtcombo_process.addr() - - def config(self): - """Returns a dict with enough information to be able to connect.""" - if self.mysql_only: - return self.mysql_db.config() - - 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': - result['grpc_port'] = vt_processes.vtcombo_process.grpc_port - return result - - def mysql_execute(self, queries, db_name=''): - """Execute queries directly on MySQL. - - The queries will be executed in a single transaction. - - Args: - queries: A list of strings. The SQL statements to execute. - db_name: The database name to use. - - Returns: - The results of the last query as a list of row tuples. - """ - conn = self.mysql_db.connect(db_name) - cursor = conn.cursor() - - for query in queries: - cursor.execute(query) - result = cursor.fetchall() - - cursor.close() - # Commit all of the queries. - conn.commit() - conn.close() - return result - - def create_databases(self): - """Create a database for each shard.""" - - cmds = [] - for kpb in self.topology.keyspaces: - if kpb.served_from: - # redirected keyspaces have no underlying database - continue - - for spb in kpb.shards: - db_name = spb.db_name_override - if not db_name: - db_name = 'vt_%s_%s' % (kpb.name, spb.name) - cmds.append('create database `%s`' % db_name) - logging.info('Creating databases') - self.mysql_execute(cmds) - - def load_schema(self): - """Load schema SQL from data files.""" - - if not self.schema_dir: - return - - if not os.path.isdir(self.schema_dir): - raise Exception('schema_dir "%s" is not a directory.' % self.schema_dir) - - for kpb in self.topology.keyspaces: - if kpb.served_from: - # redirected keyspaces have no underlying database - continue - - keyspace = kpb.name - keyspace_dir = os.path.join(self.schema_dir, keyspace) - schema_dir = keyspace_dir - if not os.path.isdir(schema_dir): - schema_dir = self.default_schema_dir - if not schema_dir or not os.path.isdir(schema_dir): - raise Exception( - 'No subdirectory found in schema dir %s for keyspace %s. ' - 'No valid default_schema_dir (set to %s) was found. ' - 'For keyspaces without an initial schema, create the ' - 'directory %s and leave a README file to explain why the ' - 'directory exists. ' - 'Alternatively, disable loading schemas by setting --schema_dir ' - 'to "" or set --default_schema_dir to a valid schema.' % - (self.schema_dir, keyspace, self.default_schema_dir, - keyspace_dir)) - - for filepath in glob.glob(os.path.join(schema_dir, '*.sql')): - logging.info('Loading schema for keyspace %s from file %s', - keyspace, filepath) - cmds = self.get_sql_commands_from_file(filepath, schema_dir) - - # Run the cmds on each shard and cell in the keyspace. - for spb in kpb.shards: - db_name = spb.db_name_override - if not db_name: - db_name = 'vt_%s_%s' % (kpb.name, spb.name) - self.mysql_execute(cmds, db_name=db_name) - - def populate_with_random_data(self): - """Populates all shards with randomly generated data.""" - - for kpb in self.topology.keyspaces: - if kpb.served_from: - # redirected keyspaces have no underlying database - continue - - for spb in kpb.shards: - db_name = spb.db_name_override - if not db_name: - db_name = 'vt_%s_%s' % (kpb.name, spb.name) - self.populate_shard_with_random_data(db_name) - - def populate_shard_with_random_data(self, db_name): - """Populates the given database with randomly generated data. - - Every table in the database is populated. - - Args: - db_name: The shard database name (string). - """ - - tables = self.mysql_execute(['SHOW TABLES'], db_name) - for table in tables: - self.populate_table_with_random_data(db_name, table[0]) - - # The number of rows inserted in a single INSERT statement. - batch_insert_size = 1000 - - def populate_table_with_random_data(self, db_name, table_name): - """Populates the given table with randomly generated data. - - Queries the database for the table schema and then populates - the columns with randomly generated data. - - Args: - db_name: The shard database name (string). - table_name: The name of the table to populate (string). - """ - - field_infos = self.mysql_execute(['DESCRIBE %s' % table_name], db_name) - num_rows = self.rng.randint(self.init_data_options.min_table_shard_size, - self.init_data_options.max_table_shard_size) - rows = [] - for _ in xrange(num_rows): - row = [] - for field_info in field_infos: - field_type = field_info[1] - field_allow_nulls = (field_info[2] == 'YES') - row.append( - self.generate_random_field( - table_name, field_type, field_allow_nulls)) - rows.append(row) - - # Insert 'rows' into the database in batches of size - # self.batch_insert_size - field_names = [field_info[0] for field_info in field_infos] - for index in xrange(0, len(rows), self.batch_insert_size): - self.batch_insert(db_name, - table_name, - field_names, - rows[index:index + self.batch_insert_size]) - - def batch_insert(self, db_name, table_name, field_names, rows): - """Inserts the rows in 'rows' into 'table_name' of database 'db_name'. - - Args: - db_name: The name of the database containing the table. - table_name: The name of the table to populate. - field_names: The list of the field names in the table. - rows: A list of tuples with each tuple containing - the string representations of the fields. - The order of the representation must match the order of the field - names listed in 'field_names'. - """ - - field_names_string = ','.join(field_names) - values_string = ','.join(['(' + ','.join(row) +')' for row in rows]) - # We use "INSERT IGNORE" to ignore duplicate key errors. - insert_query = ('INSERT IGNORE INTO %s (%s) VALUES %s' % - (table_name, field_names_string, values_string)) - logging.info('Executing in database %s: %s', db_name, insert_query) - self.mysql_execute([insert_query], db_name) - - def generate_random_field(self, table_name, field_type, field_allows_nulls): - """Generates a random field string representation. - - By 'string representation' we mean a string that is suitable to be a part - of an 'INSERT INTO' SQL statement. - - Args: - table_name: The name of the table that will contain the generated field - value. Only used for a descriptive exception message in case of - an error. - field_type: The field_type as given by a "DESCRIBE
" SQL statement. - field_allows_nulls: Should be 'true' if this field allows NULLS. - - Returns: - The random field. - - Raises: - Exception: If 'field_type' is not supported. - """ - - value = None - if field_type.startswith('tinyint'): - value = self.random_integer(field_type, 1) - elif field_type.startswith('smallint'): - value = self.random_integer(field_type, 2) - elif field_type.startswith('mediumint'): - value = self.random_integer(field_type, 3) - elif field_type.startswith('int'): - value = self.random_integer(field_type, 4) - elif field_type.startswith('bigint'): - value = self.random_integer(field_type, 8) - elif field_type.startswith('decimal'): - value = self.random_decimal(field_type) - else: - raise Exception('Populating random data in field type: %s is not yet ' - 'supported. (table: %s)' % (field_type, table_name)) - if (field_allows_nulls and - self.true_with_probability(self.init_data_options.null_probability)): - return 'NULL' - return value - - def true_with_probability(self, true_probability): - """Returns a pseudo-random boolean. - - Args: - true_probability: The probability to use for returning 'true'. - Returns: - The value 'true' is with probability 'true_probability'. - """ - - return self.rng.uniform(0, 1) < true_probability - - def random_integer(self, field_type, num_bytes): - num_bits = 8*num_bytes - if field_type.endswith('unsigned'): - return '%d' % (self.rng.randint(0, 2**num_bits-1)) - return '%d' % (self.rng.randint(-2**(num_bits-1), 2**(num_bits-1)-1)) - - decimal_regexp = re.compile(r'decimal\((\d+),(\d+)\)') - - def random_decimal(self, field_type): - match = self.decimal_regexp.match(field_type) - if match is None: - raise Exception("Can't parse 'decimal' field type: %s" % field_type) - num_digits_right = int(match.group(2)) - num_digits_left = int(match.group(1))-num_digits_right - boundary = 10**num_digits_left-1 - rand = self.rng.uniform(-boundary, boundary) - return '%.*f' % (num_digits_right, rand) - - def get_sql_commands_from_file(self, filename, source_root=None): - """Given a file, extract an array of commands from the file. - - Automatically strips out three types of MySQL comment syntax: - '--' at beginning of line: line removed - '-- ': remove everything from here to line's end (note space after dashes) - '#': remove everything from here to line's end - MySQL's handling of C-style /* ... */ comments is weird, so we - leave them alone for now. See the MySQL manual 6.1.6 "Comment Syntax" - for all the weird complications. - - Args: - filename: the SQL source file to use. - source_root: if specified, 'source FILENAME' lines in the SQL file will - source the specified filename relative to source_root. - - Returns: - A list of SQL commands. - """ - fd = open(filename) - lines = fd.readlines() - - inside_single_quotes = 0 - inside_double_quotes = 0 - commands = [] - cmd = '' - for line in lines: - # Strip newline and other trailing whitespace - line = line.rstrip() - - if (not inside_single_quotes and not inside_double_quotes and - line.startswith('--')): - # Line starts with '--', skip line - continue - - i = 0 - next_i = 0 - # Iterate through line, looking for special delimiters - while 1: - i = next_i - if i >= len(line): - break - - # By default, move to next character after this one - next_i = i + 1 - - if line[i] == '\\': - # Next character is literal, skip this and the next character - next_i = i + 2 - - elif line[i] == "'": - if not inside_double_quotes: - inside_single_quotes = not inside_single_quotes - - elif line[i] == '"': - if not inside_single_quotes: - inside_double_quotes = not inside_double_quotes - - elif not inside_single_quotes and not inside_double_quotes: - if line[i] == '#' or line[i:i+3] == '-- ': - # Found unquoted "#" or "-- ", ignore rest of line - line = line[:i] - break - - if line[i] == ';': - # Unquoted semicolon marks end of command - cmd += line[:i] - commands.append(cmd) - cmd = '' - - # Chop off everything before and including the semicolon - line = line[i+1:] - - # Start over at beginning of line - next_i = 0 - - # Reached end of line - if line and not line.isspace(): - if source_root and not cmd and line.startswith('source '): - commands.extend(self.get_sql_commands_from_file( - os.path.join(source_root, line[7:]), - source_root=source_root)) - else: - cmd += line - cmd += '\n' - - # Accept last command even if it doesn't end in semicolon - cmd = cmd.strip() - if cmd: - commands.append(cmd) - - return commands - - def __enter__(self): - self.setup() - return self - - def __exit__(self, exc_type, exc_info, tb): - self.teardown() diff --git a/py/vttest/mysql_db.py b/py/vttest/mysql_db.py deleted file mode 100644 index fe0c4e2037b..00000000000 --- a/py/vttest/mysql_db.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This module defines the interface for the MySQL database. -""" - - -class MySqlDB(object): - """A MySqlDB contains basic info about a MySQL instance.""" - - def __init__(self, directory, port, extra_my_cnf=None, snapshot_file=None): - self._directory = directory - self._port = port - self._extra_my_cnf = extra_my_cnf - self._snapshot_file = snapshot_file - - def setup(self, port): - """Starts the MySQL database.""" - raise NotImplementedError('MySqlDB is the base class.') - - def teardown(self): - """Stops the MySQL database.""" - raise NotImplementedError('MySqlDB is the base class.') - - def username(self): - raise NotImplementedError('MySqlDB is the base class.') - - def password(self): - raise NotImplementedError('MySqlDB is the base class.') - - def hostname(self): - raise NotImplementedError('MySqlDB is the base class.') - - def port(self): - raise NotImplementedError('MySqlDB is the base class.') - - def unix_socket(self): - raise NotImplementedError('MySqlDB is the base class.') - - def config(self): - """Returns the json config to output.""" - raise NotImplementedError('MySqlDB is the base class.') diff --git a/py/vttest/mysql_db_mysqlctl.py b/py/vttest/mysql_db_mysqlctl.py deleted file mode 100644 index c1ecc8d049f..00000000000 --- a/py/vttest/mysql_db_mysqlctl.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This module defines a mysqlctl based MySQL database. -""" - -import os -import subprocess - -import MySQLdb - -from vttest import environment -from vttest import mysql_db -from vttest.mysql_flavor import mysql_flavor - - -class MySqlDBMysqlctl(mysql_db.MySqlDB): - """Contains data and methods to manage a MySQL instance using mysqlctl.""" - - def __init__(self, directory, port, extra_my_cnf, snapshot_file=None): - super(MySqlDBMysqlctl, self).__init__( - directory, port, extra_my_cnf, snapshot_file) - - def setup(self): - cmd = [ - environment.mysqlctl_binary, - '-alsologtostderr', - '-tablet_uid', '1', - '-mysql_port', str(self._port), - 'init', - '-init_db_sql_file', - os.path.join(os.environ['VTROOT'], 'config/init_db.sql'), - ] - env = os.environ - env['VTDATAROOT'] = self._directory - my_cnf = mysql_flavor().my_cnf() - if self._extra_my_cnf: - my_cnf += ':%s' % self._extra_my_cnf - env['EXTRA_MY_CNF'] = my_cnf - result = subprocess.call(cmd, env=env) - if result != 0: - raise Exception('mysqlctl failed', result) - - def teardown(self): - cmd = [ - environment.mysqlctl_binary, - '-alsologtostderr', - '-tablet_uid', '1', - '-mysql_port', str(self._port), - 'shutdown', - ] - result = subprocess.call(cmd) - if result != 0: - raise Exception('mysqlctl failed', result) - - def connect(self, db_name): - return MySQLdb.connect(user='vt_dba', - unix_socket=self.unix_socket(), - db=db_name) - - def username(self): - return 'vt_dba' - - def password(self): - return '' - - def hostname(self): - return '' - - def port(self): - return self._port - - def unix_socket(self): - return os.path.join(self._directory, 'vt_0000000001', 'mysql.sock') - - def config(self): - return { - 'username': self.username(), - 'password': self.password(), - 'socket': self.unix_socket(), - } diff --git a/py/vttest/mysql_flavor.py b/py/vttest/mysql_flavor.py deleted file mode 100644 index c28bf979e02..00000000000 --- a/py/vttest/mysql_flavor.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Define abstractions for various mysql flavors. - -This module is used by mysql_db_mysqlctl.py to handle differences -between various flavors of mysql. -""" - -import logging -import os -import sys - - -# For now, vtroot is only used in this module. If other people -# need this, we should move it to environment. -if "VTROOT" not in os.environ: - sys.stderr.write( - "ERROR: Vitess environment not set up. " - 'Please run "source dev.env" first.\n') - sys.exit(1) - -vtroot = os.environ["VTROOT"] - -class MysqlFlavor(object): - """Base class with default SQL statements.""" - - def my_cnf(self): - """Returns the path to an extra my_cnf file, or None.""" - return None - - -class MariaDB(MysqlFlavor): - """Overrides specific to MariaDB.""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -class MariaDB103(MysqlFlavor): - """Overrides specific to MariaDB 10.3""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -class MySQL56(MysqlFlavor): - """Overrides specific to MySQL 5.6.""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -class MySQL80(MysqlFlavor): - """Overrides specific to MySQL 8.0.""" - - def my_cnf(self): - files = [ - os.path.join(vtroot, "config/mycnf/default-fast.cnf"), - ] - return ":".join(files) - -__mysql_flavor = None - - -# mysql_flavor is a function because we need something to import before the -# actual __mysql_flavor is initialized, since that doesn't happen until after -# the command-line options are parsed. If we make mysql_flavor a variable and -# import it before it's initialized, the module that imported it won't get the -# updated value when it's later initialized. -def mysql_flavor(): - return __mysql_flavor - - -def set_mysql_flavor(flavor): - global __mysql_flavor - - # Last default is there because the environment variable might be set to "". - flavor = flavor or os.environ.get("MYSQL_FLAVOR", "MySQL56") or "MySQL56" - - # Set the environment variable explicitly in case we're overriding it via - # command-line flag. - os.environ["MYSQL_FLAVOR"] = flavor - - if flavor == "MariaDB": - __mysql_flavor = MariaDB() - elif flavor == "MariaDB103": - __mysql_flavor = MariaDB103() - elif flavor == "MySQL80": - __mysql_flavor = MySQL80() - elif flavor == "MySQL56": - __mysql_flavor = MySQL56() - else: - logging.error("Unknown MYSQL_FLAVOR '%s'", flavor) - exit(1) - - logging.debug("Using MYSQL_FLAVOR=%s", str(flavor)) diff --git a/py/vttest/run_local_database.py b/py/vttest/run_local_database.py deleted file mode 100755 index ba49b6546c1..00000000000 --- a/py/vttest/run_local_database.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -r"""Command-line tool for starting a local Vitess database for testing. - -USAGE: - - $ run_local_database --port 12345 \ - --proto_topo \ - --schema_dir /path/to/schema/dir - -It will run the tool, logging to stderr. On stdout, a small json structure -can be waited on and then parsed by the caller to figure out how to reach -the vtgate process. - -As an alternative to using proto_topo, a local instance can be started by using -additional flags, such as: - - $ run_local_database --port 12345 \ - --schema_dir /path/to/schema/dir \ - --cells cell1,cell2 --keyspaces ks1,ks2 \ - --num_shards 1,2 - -This will create an instance with two keyspaces in two cells, one with a single -shard and another with two shards. - -Once done with the test, send an empty line to this process for it to clean-up, -and then just wait for it to exit. - -""" - -import json -import logging -import optparse -import os -import sys - - -from google.protobuf import text_format - -from vtproto import vttest_pb2 -from vtdb import prefer_vtroot_imports # pylint: disable=unused-import -from vttest import environment -from vttest import init_data_options -from vttest import local_database -from vttest import mysql_flavor -from vttest import sharding_utils - - -def main(cmdline_options): - topology = vttest_pb2.VTTestTopology() - if cmdline_options.proto_topo: - # Text-encoded proto topology object, just parse it. - topology = text_format.Parse(cmdline_options.proto_topo, topology) - if not topology.cells: - topology.cells.append('test') - else: - cells = [] - keyspaces = [] - shard_counts = [] - if cmdline_options.cells: - cells = cmdline_options.cells.split(',') - if cmdline_options.keyspaces: - keyspaces = cmdline_options.keyspaces.split(',') - if cmdline_options.num_shards: - shard_counts = [int(x) for x in cmdline_options.num_shards.split(',')] - - for cell in cells: - topology.cells.append(cell) - for keyspace, num_shards in zip(keyspaces, shard_counts): - ks = topology.keyspaces.add(name=keyspace) - for shard in sharding_utils.get_shard_names(num_shards): - ks.shards.add(name=shard) - ks.replica_count = cmdline_options.replica_count - ks.rdonly_count = cmdline_options.rdonly_count - - environment.base_port = cmdline_options.port - - init_data_opts = None - if cmdline_options.initialize_with_random_data: - init_data_opts = init_data_options.InitDataOptions() - init_data_opts.rng_seed = cmdline_options.rng_seed - init_data_opts.min_table_shard_size = cmdline_options.min_table_shard_size - init_data_opts.max_table_shard_size = cmdline_options.max_table_shard_size - init_data_opts.null_probability = cmdline_options.null_probability - - extra_my_cnf = '' - if cmdline_options.extra_my_cnf: - extra_my_cnf += ':' + cmdline_options.extra_my_cnf - - with local_database.LocalDatabase( - topology, - cmdline_options.schema_dir, - cmdline_options.mysql_only, - init_data_opts, - default_schema_dir=cmdline_options.default_schema_dir, - extra_my_cnf=extra_my_cnf, - charset=cmdline_options.charset, - snapshot_file=cmdline_options.snapshot_file, - mysql_server_bind_address=cmdline_options.mysql_server_bind_address) as local_db: - print json.dumps(local_db.config()) - sys.stdout.flush() - try: - raw_input() - except EOFError: - sys.stderr.write( - 'WARNING: %s: No empty line was received on stdin.' - ' Instead, stdin was closed and the cluster will be shut down now.' - ' Make sure to send the empty line instead to proactively shutdown' - ' the local cluster. For example, did you forget the shutdown in' - ' your test\'s tearDown()?\n' % os.path.basename(__file__)) - -if __name__ == '__main__': - - parser = optparse.OptionParser() - parser.add_option( - '-p', '--port', type='int', - help='Port to use for vtcombo. If this is 0, a random port ' - 'will be chosen.') - parser.add_option( - '-o', '--proto_topo', - help='Define the fake cluster topology as a compact text format encoded' - ' vttest proto. See vttest.proto for more information.') - parser.add_option( - '-s', '--schema_dir', - help='Directory for initial schema files. Within this dir,' - ' there should be a subdir for each keyspace. Within' - ' each keyspace dir, each file is executed as SQL' - ' after the database is created on each shard.' - ' If the directory contains a vschema.json file, it' - ' will be used as the vschema for the V3 API.') - parser.add_option( - '-e', '--default_schema_dir', - help='Default directory for initial schema files. If no schema is found' - ' in schema_dir, default to this location.') - parser.add_option( - '-m', '--mysql_only', action='store_true', - help='If this flag is set only mysql is initialized.' - ' The rest of the vitess components are not started.' - ' Also, the output specifies the mysql unix socket' - ' instead of the vtgate port.') - parser.add_option( - '-r', '--initialize_with_random_data', action='store_true', - help='If this flag is each table-shard will be initialized' - ' with random data. See also the "rng_seed" and "min_shard_size"' - ' and "max_shard_size" flags.') - parser.add_option( - '-d', '--rng_seed', type='int', default=123, - help='The random number generator seed to use when initializing' - ' with random data (see also --initialize_with_random_data).' - ' Multiple runs with the same seed will result with the same' - ' initial data.') - parser.add_option( - '-x', '--min_table_shard_size', type='int', default=1000, - help='The minimum number of initial rows in a table shard. Ignored if' - '--initialize_with_random_data is false. The actual number is chosen' - ' randomly.') - parser.add_option( - '-y', '--max_table_shard_size', type='int', default=10000, - help='The maximum number of initial rows in a table shard. Ignored if' - '--initialize_with_random_data is false. The actual number is chosen' - ' randomly') - parser.add_option( - '-n', '--null_probability', type='float', default=0.1, - help='The probability to initialize a field with "NULL" ' - ' if --initialize_with_random_data is true. Only applies to fields' - ' that can contain NULL values.') - parser.add_option( - '-f', '--extra_my_cnf', - help='extra files to add to the config, separated by ":"') - parser.add_option( - '--mysql_server_bind_address', - help='mysql server bind address ":"') - parser.add_option( - '-v', '--verbose', action='store_true', - help='Display extra error messages.') - parser.add_option('-c', '--cells', default='test', - help='Comma separated list of cells') - parser.add_option('-k', '--keyspaces', default='test_keyspace', - help='Comma separated list of keyspaces') - parser.add_option('--num_shards', default='2', - help='Comma separated shard count (one per keyspace)') - parser.add_option('--replica_count', type='int', default=2, - help='Replica tablets per shard (includes master)') - parser.add_option('--rdonly_count', type='int', default=1, - help='Rdonly tablets per shard') - parser.add_option('--charset', default='utf8', help='MySQL charset') - parser.add_option( - '--snapshot_file', default=None, help='A MySQL DB snapshot file') - (options, args) = parser.parse_args() - if options.verbose: - logging.getLogger().setLevel(logging.DEBUG) - - # This will set the flavor based on the MYSQL_FLAVOR env var, - # or default to MariaDB. - mysql_flavor.set_mysql_flavor(None) - - main(options) diff --git a/py/vttest/sharding_utils.py b/py/vttest/sharding_utils.py deleted file mode 100644 index db9b15ff6b9..00000000000 --- a/py/vttest/sharding_utils.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Sharding utils.""" - - -def get_shard_index(shard_name): - """Returns tuple of shard index, num_shards based on a shard name.""" - if shard_name in ['0', '-']: - return 0, 1 - - shard_begin, shard_end = shard_name.split('-') - num_bytes_used = max(len(shard_begin), len(shard_end)) / 2 - if shard_begin: - shard_begin = int(shard_begin, 16) - else: - shard_begin = 0 - if shard_end: - shard_end = int(shard_end, 16) - else: - shard_end = 1 << num_bytes_used * 8 - shard_width = shard_end - shard_begin - num_shards = (1 << num_bytes_used * 8) / (shard_width) - shard_num = shard_begin / shard_width - return shard_num, num_shards - - -def get_shard_name(shard, num_shards): - """Returns an appropriate shard name, as a string. - - A single shard name is simply 0; otherwise it will attempt to split up 0x100 - into multiple shards. For example, in a two sharded keyspace, shard 0 is - -80, shard 1 is 80-. This function currently only applies to sharding setups - where the shard count is 256 or less, and all shards are equal width. - - Args: - shard: The integer shard index (zero based) - num_shards: Total number of shards (int) - - Returns: - The shard name as a string. - """ - - if num_shards == 1: - return '0' - - shard_width = int(0x100 / num_shards) - - if shard == 0: - return '-%02x' % shard_width - elif shard == num_shards - 1: - return '%02x-' % (shard * shard_width) - else: - return '%02x-%02x' % (shard * shard_width, (shard + 1) * shard_width) - - -def get_shard_names(num_shards): - """Create a generator of shard names. - - Args: - num_shards: Total number of shards (int) - - Returns: - The shard name generator. - """ - return (get_shard_name(x, num_shards) for x in range(num_shards)) diff --git a/py/vttest/vt_processes.py b/py/vttest/vt_processes.py deleted file mode 100644 index 8530c2d5ee2..00000000000 --- a/py/vttest/vt_processes.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright 2019 The Vitess Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Starts the vtcombo process.""" - -import json -import logging -import os -import socket -import subprocess -import time -import urllib - -from google.protobuf import text_format - -from vttest import environment - - -class VtProcess(object): - """Base class for a vt process, vtcombo only now.""" - - START_RETRIES = 5 - - def __init__(self, name, directory, binary, port_name): - self.name = name - self.directory = directory - self.binary = binary - self.extraparams = [] - self.port_name = port_name - self.process = None - - def wait_start(self): - """Start the process and wait for it to respond on HTTP.""" - - for _ in xrange(0, self.START_RETRIES): - self.port = environment.get_port(self.port_name) - if environment.get_protocol() == 'grpc': - self.grpc_port = environment.get_port(self.port_name, protocol='grpc') - else: - self.grpc_port = None - logs_subdirectory = environment.get_logs_directory(self.directory) - cmd = [ - self.binary, - '-port', '%u' % self.port, - '-log_dir', logs_subdirectory, - '-alsologtostderr', - ] - if environment.get_protocol() == 'grpc': - cmd.extend(['-grpc_port', '%u' % self.grpc_port]) - cmd.extend(self.extraparams) - logging.info('Starting process: %s', cmd) - stdout = os.path.join(logs_subdirectory, '%s.%d.log' % - (self.name, self.port)) - self.stdout = open(stdout, 'w') - self.process = subprocess.Popen(cmd, - stdout=self.stdout) - timeout = time.time() + 60.0 - while time.time() < timeout: - if environment.process_is_healthy( - self.name, self.addr()) and self.get_vars(): - logging.info('%s started.', self.name) - return - elif self.process.poll() is not None: - logging.error('%s process exited prematurely.', self.name) - break - time.sleep(0.3) - - logging.error('cannot start %s process on time: %s ', - self.name, socket.getfqdn()) - self.kill() - - raise Exception('Failed %d times to run %s' % ( - self.START_RETRIES, - self.name)) - - def addr(self): - """Return the host:port of the process.""" - return '%s:%u' % (socket.getfqdn(), self.port) - - def grpc_addr(self): - """Get the grpc address of the process. - - Returns: - the grpc host:port of the process. - Only call this is environment.get_protocol() == 'grpc'. - """ - return '%s:%u' % (socket.getfqdn(), self.grpc_port) - - def get_vars(self): - """Return the debug vars.""" - data = None - try: - url = 'http://%s/debug/vars' % self.addr() - f = urllib.urlopen(url) - data = f.read() - f.close() - except IOError: - return None - try: - return json.loads(data) - except ValueError: - logging.error('%s', data) - raise - - def kill(self): - """Kill the process.""" - # These will proceed without error even if the process is already gone. - self.process.terminate() - - def wait(self): - """Wait for the process to end.""" - self.process.wait() - - -class VtcomboProcess(VtProcess): - """Represents a vtcombo subprocess.""" - - QUERYSERVER_PARAMETERS = [ - '-queryserver-config-pool-size', '4', - '-queryserver-config-query-timeout', '300', - '-queryserver-config-schema-reload-time', '60', - '-queryserver-config-stream-pool-size', '4', - '-queryserver-config-transaction-cap', '4', - '-queryserver-config-transaction-timeout', '300', - '-queryserver-config-txpool-timeout', '300', - ] - - def __init__(self, directory, topology, mysql_db, schema_dir, charset, - mysql_server_bind_address=None): - VtProcess.__init__(self, 'vtcombo-%s' % os.environ['USER'], directory, - environment.vtcombo_binary, port_name='vtcombo') - self.extraparams = [ - '-db_charset', charset, - '-db_app_user', mysql_db.username(), - '-db_app_password', mysql_db.password(), - '-db_dba_user', mysql_db.username(), - '-db_dba_password', mysql_db.password(), - '-proto_topo', text_format.MessageToString(topology, as_one_line=True), - '-mycnf_server_id', '1', - '-mycnf_socket_file', mysql_db.unix_socket(), - '-normalize_queries', - ] + self.QUERYSERVER_PARAMETERS + environment.extra_vtcombo_parameters() - if schema_dir: - self.extraparams.extend(['-schema_dir', schema_dir]) - if mysql_db.unix_socket(): - self.extraparams.extend(['-db_socket', mysql_db.unix_socket()]) - else: - self.extraparams.extend( - ['-db_host', mysql_db.hostname(), - '-db_port', str(mysql_db.port())]) - self.vtcombo_mysql_port = environment.get_port('vtcombo_mysql_port') - if mysql_server_bind_address: - # Binding to 0.0.0.0 instead of localhost makes it possible to connect to vtgate from outside a docker container - self.extraparams.extend(['-mysql_server_bind_address', mysql_server_bind_address]) - else: - self.extraparams.extend(['-mysql_server_bind_address', 'localhost']) - self.extraparams.extend( - ['-mysql_auth_server_impl', 'none', - '-mysql_server_port', str(self.vtcombo_mysql_port)]) - - -vtcombo_process = None - - -def start_vt_processes(directory, topology, mysql_db, schema_dir, - charset='utf8', mysql_server_bind_address=None): - """Start the vt processes. - - Args: - directory: the toplevel directory for the processes (logs, ...) - topology: a vttest.VTTestTopology object. - mysql_db: an instance of the mysql_db.MySqlDB class. - schema_dir: the directory that contains the schema / vschema. - charset: the character set for the database connections. - mysql_server_bind_address: MySQL server bind address for vtcombo. - """ - global vtcombo_process - - logging.info('start_vt_processes(directory=%s,vtcombo_binary=%s)', - directory, environment.vtcombo_binary) - vtcombo_process = VtcomboProcess(directory, topology, mysql_db, schema_dir, - charset, mysql_server_bind_address=mysql_server_bind_address) - vtcombo_process.wait_start() - - -def kill_vt_processes(): - """Call kill() on all processes.""" - logging.info('kill_vt_processes()') - if vtcombo_process: - vtcombo_process.kill() - - -def wait_vt_processes(): - """Call wait() on all processes.""" - logging.info('wait_vt_processes()') - if vtcombo_process: - vtcombo_process.wait() - - -def kill_and_wait_vt_processes(): - """Call kill() and then wait() on all processes.""" - kill_vt_processes() - wait_vt_processes() - - -# wait_step is a helper for looping until a condition is true. -# use as follow: -# timeout = 10 -# while True: -# if done: -# break -# timeout = utils.wait_step('condition', timeout) -def wait_step(msg, timeout, sleep_time=1.0): - timeout -= sleep_time - if timeout <= 0: - raise Exception("timeout waiting for condition '%s'" % msg) - logging.debug("Sleeping for %f seconds waiting for condition '%s'", - sleep_time, msg) - time.sleep(sleep_time) - return timeout From 7224d444365e6ba8714bcb5b3ca7c7213b90780d Mon Sep 17 00:00:00 2001 From: JohnnyThree Date: Tue, 17 Mar 2020 22:54:37 +0800 Subject: [PATCH 317/825] add 'keyspace exist' check when ApplyVshcema Signed-off-by: JohnnyThree --- go/vt/vtctl/vtctl.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/go/vt/vtctl/vtctl.go b/go/vt/vtctl/vtctl.go index 30b608f5f45..3268d9e95d7 100644 --- a/go/vt/vtctl/vtctl.go +++ b/go/vt/vtctl/vtctl.go @@ -2553,6 +2553,13 @@ func commandApplyVSchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *f return nil } + if _, err := wr.TopoServer().GetKeyspace(ctx, keyspace); err != nil { + if strings.Contains(err.Error(), "node doesn't exist") { + return fmt.Errorf("keyspace(%s) doesn't exist, check if the keyspace is initialized.\n", keyspace) + } + return err + } + if err := wr.TopoServer().SaveVSchema(ctx, keyspace, vs); err != nil { return err } From e7376bef79c18f2bc717ecc8d98209fac790fd47 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 17 Mar 2020 10:23:07 -0700 Subject: [PATCH 318/825] deprecation: delete left over UpdateStream func Signed-off-by: Sugu Sougoumarane --- go/cmd/vtgateclienttest/services/terminal.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/go/cmd/vtgateclienttest/services/terminal.go b/go/cmd/vtgateclienttest/services/terminal.go index 1caf7123922..44c80e26e7d 100644 --- a/go/cmd/vtgateclienttest/services/terminal.go +++ b/go/cmd/vtgateclienttest/services/terminal.go @@ -133,10 +133,6 @@ func (c *terminalClient) VStream(ctx context.Context, tabletType topodatapb.Tabl return errTerminal } -func (c *terminalClient) UpdateStream(ctx context.Context, keyspace string, shard string, keyRange *topodatapb.KeyRange, tabletType topodatapb.TabletType, timestamp int64, event *querypb.EventToken, callback func(*querypb.StreamEvent, int64) error) error { - return errTerminal -} - func (c *terminalClient) HandlePanic(err *error) { if x := recover(); x != nil { log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4)) From 48c654081fa7e977faa34ae81fbc32ad81486207 Mon Sep 17 00:00:00 2001 From: deepthi Date: Tue, 17 Mar 2020 11:47:03 -0700 Subject: [PATCH 319/825] tests: make_parser test errors out on CI Signed-off-by: deepthi --- tools/check_make_parser.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/check_make_parser.sh b/tools/check_make_parser.sh index ef59eb67a9f..8920762108a 100755 --- a/tools/check_make_parser.sh +++ b/tools/check_make_parser.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Validate that the current version of the generated parser matches the output # generated by the version of goyacc installed on the local system. From 2b38b56626b52bee1096496f72f5d1d46dac9eac Mon Sep 17 00:00:00 2001 From: deepthi Date: Tue, 17 Mar 2020 11:42:16 -0700 Subject: [PATCH 320/825] tests: check whether port is available before reserving Signed-off-by: deepthi --- go/test/endtoend/cluster/cluster_process.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index bda184260f5..7f1e2abb3d8 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -22,6 +22,7 @@ import ( "fmt" "io/ioutil" "math/rand" + "net" "os" "os/exec" "os/signal" @@ -576,7 +577,20 @@ func (cluster *LocalProcessCluster) GetAndReservePort() int { if cluster.nextPortForProcess == 0 { cluster.nextPortForProcess = getPort() } - cluster.nextPortForProcess = cluster.nextPortForProcess + 1 + for { + cluster.nextPortForProcess = cluster.nextPortForProcess + 1 + log.Errorf("Attempting to reserve port: %v", cluster.nextPortForProcess) + ln, err := net.Listen("tcp", fmt.Sprintf(":%v", cluster.nextPortForProcess)) + + if err != nil { + log.Errorf("Can't listen on port %v: %s, trying next port", cluster.nextPortForProcess, err) + continue + } + + ln.Close() + log.Errorf("Port %v is available, reserving..", cluster.nextPortForProcess) + break + } return cluster.nextPortForProcess } From a4e107d0a59d59b4e690484550c72f787c9de84e Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 17 Mar 2020 17:53:50 -0700 Subject: [PATCH 321/825] deprecation: tabletserver tests WIP Signed-off-by: Sugu Sougoumarane --- go/mysql/fakesqldb/server.go | 7 - go/vt/vttablet/tabletserver/query_engine.go | 4 - go/vt/vttablet/tabletserver/query_executor.go | 6 +- .../tabletserver/query_executor_test.go | 244 ++++++++++++------ .../tabletserver/schema/load_table.go | 15 -- .../tabletserver/schema/load_table_test.go | 2 - go/vt/vttablet/tabletserver/schema/schema.go | 6 - .../vttablet/tabletserver/tabletenv/config.go | 3 +- go/vt/vttablet/tabletserver/tabletserver.go | 11 +- .../tabletserver/tabletserver_flaky_test.go | 1 - 10 files changed, 175 insertions(+), 124 deletions(-) diff --git a/go/mysql/fakesqldb/server.go b/go/mysql/fakesqldb/server.go index dd29a495a0e..f7c4f904742 100644 --- a/go/mysql/fakesqldb/server.go +++ b/go/mysql/fakesqldb/server.go @@ -287,10 +287,6 @@ func (db *DB) NewConnection(c *mysql.Conn) { db.mu.Lock() defer db.mu.Unlock() - if db.t != nil { - db.t.Logf("NewConnection(%v): client %v", db.name, c.ConnectionID) - } - if db.isConnFail { panic(fmt.Errorf("simulating a connection failure")) } @@ -336,9 +332,6 @@ func (db *DB) HandleQuery(c *mysql.Conn, query string, callback func(*sqltypes.R return callback(&sqltypes.Result{}) } - if db.t != nil { - db.t.Logf("ComQuery(%v): client %v: %v", db.name, c.ConnectionID, query) - } if db.orderMatters { result, err := db.comQueryOrdered(query) if err != nil { diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index 7329621f32f..b996cdbf572 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -153,8 +153,6 @@ type QueryEngine struct { maxResultSize sync2.AtomicInt64 warnResultSize sync2.AtomicInt64 maxDMLRows sync2.AtomicInt64 - passthroughDMLs sync2.AtomicBool - allowUnsafeDMLs bool streamBufferSize sync2.AtomicInt64 // tableaclExemptCount count the number of accesses allowed // based on membership in the superuser ACL @@ -239,8 +237,6 @@ func NewQueryEngine(checker connpool.MySQLChecker, se *schema.Engine, config tab qe.maxDMLRows = sync2.NewAtomicInt64(int64(config.MaxDMLRows)) qe.streamBufferSize = sync2.NewAtomicInt64(int64(config.StreamBufferSize)) - qe.passthroughDMLs = sync2.NewAtomicBool(config.PassthroughDMLs) - qe.allowUnsafeDMLs = config.AllowUnsafeDMLs planbuilder.PassthroughDMLs = config.PassthroughDMLs qe.accessCheckerLogger = logutil.NewThrottledLogger("accessChecker", 1*time.Second) diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 66d3451f242..ee62b338bce 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -131,9 +131,9 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { return qr, nil case planbuilder.PlanSelectLock: return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "%s disallowed outside transaction", qre.plan.PlanID.String()) - case planbuilder.PlanSet, planbuilder.PlanOtherRead: + case planbuilder.PlanSet, planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: return qre.execOther() - case planbuilder.PlanInsert, planbuilder.PlanUpdate, planbuilder.PlanDelete, planbuilder.PlanInsertMessage: + case planbuilder.PlanInsert, planbuilder.PlanUpdate, planbuilder.PlanDelete, planbuilder.PlanInsertMessage, planbuilder.PlanDDL: return qre.execAsTransaction(true /* autocommit */, qre.txConnExec) case planbuilder.PlanUpdateLimit, planbuilder.PlanDeleteLimit: return qre.execAsTransaction(false /* autocommit */, qre.txConnExec) @@ -498,7 +498,7 @@ func (qre *QueryExecutor) execOther() (*sqltypes.Result, error) { return nil, err } defer conn.Recycle() - return qre.dbConnFetch(conn, qre.plan.FullQuery, qre.bindVars) + return qre.execSQL(conn, qre.query, true) } func (qre *QueryExecutor) getConn() (*connpool.DBConn, error) { diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 1563a7e4aff..312e70b0627 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -24,6 +24,8 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/mysql" @@ -47,23 +49,150 @@ import ( vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) -func TestQueryExecutorPlanDDL(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "alter table test_table add zipcode int" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - checkPlanID(t, planbuilder.PlanDDL, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) +func TestQueryExecutorPlans(t *testing.T) { + type dbResponse struct { + query string + result *sqltypes.Result } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) + + dmlResult := &sqltypes.Result{ + RowsAffected: 1, + } + fields := sqltypes.MakeTestFields("a|b", "int64|varchar") + fieldResult := sqltypes.MakeTestResult(fields) + selectResult := sqltypes.MakeTestResult(fields, "1|aaa") + + testcases := []struct { + input string + passThrough bool + dbResponses []dbResponse + resultWant *sqltypes.Result + planWant string + logWant string + }{{ + input: "select * from t where 1 != 1", + dbResponses: []dbResponse{{ + query: "select * from t where 1 != 1", + result: fieldResult, + }}, + resultWant: fieldResult, + planWant: "SelectImpossible", + logWant: "select * from t where 1 != 1", + }, { + input: "select * from t", + dbResponses: []dbResponse{{ + query: "select * from t where 1 != 1", + result: fieldResult, + }, { + query: "select * from t limit 10001", + result: selectResult, + }}, + resultWant: selectResult, + planWant: "Select", + logWant: "select * from t where 1 != 1; select * from t limit 10001", + }, { + input: "set a=1", + dbResponses: []dbResponse{{ + query: "set a=1", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "Set", + logWant: "set a=1", + }, { + input: "show engines", + dbResponses: []dbResponse{{ + query: "show engines", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "OtherRead", + logWant: "show engines", + }, { + input: "repair t", + dbResponses: []dbResponse{{ + query: "repair t", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "OtherAdmin", + logWant: "repair t", + }, { + input: "insert into test_table(a) values(1)", + dbResponses: []dbResponse{{ + query: "insert into test_table(a) values (1)", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "Insert", + logWant: "insert into test_table(a) values (1)", + }, { + input: "update test_table set a=1", + dbResponses: []dbResponse{{ + query: "update test_table set a = 1 limit 10001", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "UpdateLimit", + logWant: "begin; update test_table set a = 1 limit 10001; commit", + }, { + input: "update test_table set a=1", + passThrough: true, + dbResponses: []dbResponse{{ + query: "update test_table set a = 1", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "Update", + logWant: "update test_table set a = 1", + }, { + input: "delete from test_table", + dbResponses: []dbResponse{{ + query: "delete from test_table limit 10001", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "DeleteLimit", + logWant: "begin; delete from test_table limit 10001; commit", + }, { + input: "delete from test_table", + passThrough: true, + dbResponses: []dbResponse{{ + query: "delete from test_table", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "Delete", + logWant: "delete from test_table", + }, { + input: "alter table test_table add zipcode int", + dbResponses: []dbResponse{{ + query: "alter table test_table add zipcode int", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "DDL", + logWant: "alter table test_table add zipcode int", + }} + for _, tcase := range testcases { + func() { + db := setUpQueryExecutorTest(t) + defer db.Close() + for _, dbr := range tcase.dbResponses { + db.AddQuery(dbr.query, dbr.result) + } + ctx := context.Background() + tsv := newTestTabletServer(ctx, noFlags, db) + defer tsv.StopService() + + tsv.SetPassthroughDMLs(tcase.passThrough) + qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) + got, err := qre.Execute() + require.NoError(t, err, tcase.input) + assert.Equal(t, tcase.resultWant, got, tcase.input) + assert.Equal(t, tcase.planWant, qre.logStats.PlanType, tcase.input) + assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) + }() } } @@ -80,7 +209,7 @@ func TestQueryExecutorPlanPassDmlRBR(t *testing.T) { txid := newTransaction(tsv, nil) qre := newTestQueryExecutor(ctx, tsv, query, txid) tsv.qe.binlogFormat = connpool.BinlogFormatRow - checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -121,7 +250,7 @@ func TestQueryExecutorPassthroughDml(t *testing.T) { txid := newTransaction(tsv, nil) qre := newTestQueryExecutor(ctx, tsv, query, txid) - checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -141,23 +270,6 @@ func TestQueryExecutorPassthroughDml(t *testing.T) { if code := vterrors.Code(err); code != vtrpcpb.Code_UNIMPLEMENTED { t.Errorf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) } - - tsv.SetAllowUnsafeDMLs(true) - got, err = qre.Execute() - - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries = []string{query, query} - gotqueries = fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - - testCommitHelper(t, tsv, qre) } func TestQueryExecutorPlanPassDmlAutoCommitRBR(t *testing.T) { @@ -172,7 +284,7 @@ func TestQueryExecutorPlanPassDmlAutoCommitRBR(t *testing.T) { defer tsv.StopService() qre := newTestQueryExecutor(ctx, tsv, query, 0) tsv.qe.binlogFormat = connpool.BinlogFormatRow - checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -205,7 +317,7 @@ func TestQueryExecutorPassthroughDmlAutoCommit(t *testing.T) { tsv.qe.binlogFormat = connpool.BinlogFormatRow qre := newTestQueryExecutor(ctx, tsv, query, 0) - checkPlanID(t, planbuilder.PlanUpdate, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -220,15 +332,6 @@ func TestQueryExecutorPassthroughDmlAutoCommit(t *testing.T) { if code := vterrors.Code(err); code != vtrpcpb.Code_UNIMPLEMENTED { t.Errorf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) } - - tsv.SetAllowUnsafeDMLs(true) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } } func TestQueryExecutorPlanPassDmlReplaceInto(t *testing.T) { @@ -244,7 +347,7 @@ func TestQueryExecutorPlanPassDmlReplaceInto(t *testing.T) { txid := newTransaction(tsv, nil) qre := newTestQueryExecutor(ctx, tsv, query, txid) tsv.qe.binlogFormat = connpool.BinlogFormatRow - checkPlanID(t, planbuilder.PlanInsert, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanInsert, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -277,7 +380,7 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanInsertMessage, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanInsertMessage, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -301,7 +404,7 @@ func TestQueryExecutorPlanOtherWithinATransaction(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, txid) defer tsv.StopService() defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanOtherRead, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanOtherRead, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -338,7 +441,7 @@ func TestQueryExecutorPlanPassSelectWithInATransaction(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, txid) defer tsv.StopService() defer testCommitHelper(t, tsv, qre) - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -367,7 +470,7 @@ func TestQueryExecutorPlanPassSelectWithLockOutsideATransaction(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelectLock, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelectLock, qre.plan.PlanID) _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_FAILED_PRECONDITION { t.Fatalf("qre.Execute: %v, want %v", code, vtrpcpb.Code_FAILED_PRECONDITION) @@ -389,7 +492,7 @@ func TestQueryExecutorPlanPassSelect(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -414,7 +517,7 @@ func TestQueryExecutorPlanSelectImpossible(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelectImpossible, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelectImpossible, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -441,7 +544,7 @@ func TestQueryExecutorPlanPassSelectSqlSelectLimit(t *testing.T) { SqlSelectLimit: 20, } defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -460,7 +563,7 @@ func TestQueryExecutorPlanSet(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) defer tsv.StopService() qre := newTestQueryExecutor(ctx, tsv, setQuery, 0) - checkPlanID(t, planbuilder.PlanSet, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSet, qre.plan.PlanID) // Query will be delegated to MySQL and both Fields and Rows should be // empty arrays in this case. want := &sqltypes.Result{} @@ -503,7 +606,7 @@ func TestQueryExecutorPlanOther(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanOtherRead, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanOtherRead, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("got: %v, want nil", err) @@ -534,7 +637,7 @@ func TestQueryExecutorPlanNextval(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) defer tsv.StopService() qre := newTestQueryExecutor(ctx, tsv, "select next value from seq", 0) - checkPlanID(t, planbuilder.PlanNextval, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanNextval, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) @@ -743,7 +846,7 @@ func TestQueryExecutorTableAcl(t *testing.T) { tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("got: %v, want nil", err) @@ -787,7 +890,7 @@ func TestQueryExecutorTableAclNoPermission(t *testing.T) { // without enabling Config.StrictTableAcl tsv := newTestTabletServer(ctx, noFlags, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("got: %v, want nil", err) @@ -801,7 +904,7 @@ func TestQueryExecutorTableAclNoPermission(t *testing.T) { tsv = newTestTabletServer(ctx, enableStrictTableACL, db) qre = newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) // query should fail because current user do not have read permissions _, err = qre.Execute() if err == nil { @@ -838,7 +941,7 @@ func TestQueryExecutorTableAclDualTableExempt(t *testing.T) { query := "select * from test_table where 1 != 1" qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelectImpossible, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelectImpossible, qre.plan.PlanID) // query should fail because nobody has read access to test_table _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED { @@ -898,7 +1001,7 @@ func TestQueryExecutorTableAclExemptACL(t *testing.T) { tsv := newTestTabletServer(ctx, enableStrictTableACL, db) qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) // query should fail because current user do not have read permissions _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_PERMISSION_DENIED { @@ -973,7 +1076,7 @@ func TestQueryExecutorTableAclDryRun(t *testing.T) { tsv.qe.enableTableACLDryRun = true qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) beforeCount := tabletenv.TableaclPseudoDenied.Counts()[tableACLStatsKey] // query should fail because current user do not have read permissions _, err := qre.Execute() @@ -1032,7 +1135,7 @@ func TestQueryExecutorBlacklistQRFail(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) // execute should fail because query has been blacklisted _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_INVALID_ARGUMENT { @@ -1086,7 +1189,7 @@ func TestQueryExecutorBlacklistQRRetry(t *testing.T) { qre := newTestQueryExecutor(ctx, tsv, query, 0) defer tsv.StopService() - checkPlanID(t, planbuilder.PlanSelect, qre.plan.PlanID) + assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) _, err := qre.Execute() if code := vterrors.Code(err); code != vtrpcpb.Code_FAILED_PRECONDITION { t.Fatalf("tsv.qe.queryRuleSources.SetRules: %v, want %v", code, vtrpcpb.Code_FAILED_PRECONDITION) @@ -1114,7 +1217,6 @@ func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb } else { config.TransactionCap = 100 } - config.EnableAutoCommit = true if flags&enableStrictTableACL > 0 { config.StrictTableACL = true } else { @@ -1205,16 +1307,6 @@ func getTestTableFields() []*querypb.Field { } } -func checkPlanID( - t *testing.T, - expectedPlanID planbuilder.PlanType, - actualPlanID planbuilder.PlanType) { - if expectedPlanID != actualPlanID { - t.Fatalf("expect to get PlanID: %s, but got %s", - expectedPlanID.String(), actualPlanID.String()) - } -} - func getQueryExecutorSupportedQueries(testTableHasMultipleUniqueKeys bool) map[string]*sqltypes.Result { return map[string]*sqltypes.Result{ // queries for twopc diff --git a/go/vt/vttablet/tabletserver/schema/load_table.go b/go/vt/vttablet/tabletserver/schema/load_table.go index b530dcb09da..d8a4a0a7353 100644 --- a/go/vt/vttablet/tabletserver/schema/load_table.go +++ b/go/vt/vttablet/tabletserver/schema/load_table.go @@ -198,21 +198,6 @@ func loadMessageInfo(ta *Table, comment string) error { } } - // Store the position of the id column in the PK - // list. This is required to handle arbitrary updates. - // In such cases, we have to be able to identify the - // affected id and invalidate the message cache. - ta.MessageInfo.IDPKIndex = -1 - for i, j := range ta.PKColumns { - if ta.Columns[j].Name.EqualString("id") { - ta.MessageInfo.IDPKIndex = i - break - } - } - if ta.MessageInfo.IDPKIndex == -1 { - return fmt.Errorf("id column is not part of the primary key for message table: %s", ta.Name.String()) - } - // Load user-defined columns. Any "unrecognized" column is user-defined. for _, c := range ta.Columns { if _, ok := findCols[c.Name.Lowered()]; ok { diff --git a/go/vt/vttablet/tabletserver/schema/load_table_test.go b/go/vt/vttablet/tabletserver/schema/load_table_test.go index d29f1c3b148..5b9fb6ecda9 100644 --- a/go/vt/vttablet/tabletserver/schema/load_table_test.go +++ b/go/vt/vttablet/tabletserver/schema/load_table_test.go @@ -138,7 +138,6 @@ func TestLoadTableMessage(t *testing.T) { Name: sqlparser.NewTableIdent("test_table"), Type: Message, MessageInfo: &MessageInfo{ - IDPKIndex: 1, Fields: []*querypb.Field{{ Name: "id", Type: sqltypes.Int64, @@ -214,7 +213,6 @@ func TestLoadTableMessageTopic(t *testing.T) { Name: sqlparser.NewTableIdent("test_table"), Type: Message, MessageInfo: &MessageInfo{ - IDPKIndex: 1, Fields: []*querypb.Field{{ Name: "id", Type: sqltypes.Int64, diff --git a/go/vt/vttablet/tabletserver/schema/schema.go b/go/vt/vttablet/tabletserver/schema/schema.go index c943420d5a8..b6e7b7bbc02 100644 --- a/go/vt/vttablet/tabletserver/schema/schema.go +++ b/go/vt/vttablet/tabletserver/schema/schema.go @@ -99,12 +99,6 @@ type TopicInfo struct { // MessageInfo contains info specific to message tables. type MessageInfo struct { - // IDPKIndex is the index of the ID column - // in PKvalues. This is used to extract the ID - // value for message tables to discard items - // from the cache. - IDPKIndex int - // Fields stores the field info to be // returned for subscribers. Fields []*querypb.Field diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index 1034c7a8ccd..b6894f1f438 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -46,6 +46,7 @@ var ( // TODO(sougou): deprecate the flag after release 7.0. deprecatedMessagePoolPrefillParallelism int deprecatedAutocommit bool + deprecateAllowUnsafeDMLs bool ) func init() { @@ -65,7 +66,7 @@ func init() { flag.IntVar(&Config.WarnResultSize, "queryserver-config-warn-result-size", DefaultQsConfig.WarnResultSize, "query server result size warning threshold, warn if number of rows returned from vttablet for non-streaming queries exceeds this") flag.IntVar(&Config.MaxDMLRows, "queryserver-config-max-dml-rows", DefaultQsConfig.MaxDMLRows, "query server max dml rows per statement, maximum number of rows allowed to return at a time for an update or delete with either 1) an equality where clauses on primary keys, or 2) a subselect statement. For update and delete statements in above two categories, vttablet will split the original query into multiple small queries based on this configuration value. ") flag.BoolVar(&Config.PassthroughDMLs, "queryserver-config-passthrough-dmls", DefaultQsConfig.PassthroughDMLs, "query server pass through all dml statements without rewriting") - flag.BoolVar(&Config.AllowUnsafeDMLs, "queryserver-config-allowunsafe-dmls", DefaultQsConfig.AllowUnsafeDMLs, "query server allow unsafe dml statements") + flag.BoolVar(&deprecateAllowUnsafeDMLs, "queryserver-config-allowunsafe-dmls", false, "deprecated") flag.IntVar(&Config.StreamBufferSize, "queryserver-config-stream-buffer-size", DefaultQsConfig.StreamBufferSize, "query server stream buffer size, the maximum number of bytes sent from vttablet for each stream call. It's recommended to keep this value in sync with vtgate's stream_buffer_size.") flag.IntVar(&Config.QueryPlanCacheSize, "queryserver-config-query-cache-size", DefaultQsConfig.QueryPlanCacheSize, "query server query cache size, maximum number of queries to be cached. vttablet analyzes every incoming query and generate a query plan, these plans are being cached in a lru cache. This config controls the capacity of the lru cache.") diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index a9a753d17eb..4ddbb1f232c 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -1078,7 +1078,7 @@ func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Targe // pool without actually begin/commit the transaction. if (options.TransactionIsolation == querypb.ExecuteOptions_DEFAULT) && asTransaction && - tsv.qe.passthroughDMLs.Get() { + planbuilder.PassthroughDMLs { options.TransactionIsolation = querypb.ExecuteOptions_AUTOCOMMIT } @@ -1922,14 +1922,7 @@ func (tsv *TabletServer) MaxDMLRows() int { // SetPassthroughDMLs changes the setting to pass through all DMLs // It should only be used for testing func (tsv *TabletServer) SetPassthroughDMLs(val bool) { - planbuilder.PassthroughDMLs = true - tsv.qe.passthroughDMLs.Set(val) -} - -// SetAllowUnsafeDMLs changes the setting to allow unsafe DML statements -// in SBR mode. It should be used only on initialization or for testing. -func (tsv *TabletServer) SetAllowUnsafeDMLs(val bool) { - tsv.qe.allowUnsafeDMLs = val + planbuilder.PassthroughDMLs = val } // SetQueryPoolTimeout changes the timeout to get a connection from the diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index 7730c747b8a..2a38e4acb05 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -1283,7 +1283,6 @@ func TestTabletServerExecuteBatchSqlSucceedInTransaction(t *testing.T) { db.AddRejectedQuery(sql, errRejected) config := testUtils.newQueryServiceConfig() - config.EnableAutoCommit = true tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} From e5c87fc7ba66ce5859c051296349fdd0da2be955 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 17 Mar 2020 23:19:38 -0700 Subject: [PATCH 322/825] deprecation: tabletserver unit tests pass Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/query_executor.go | 6 +- .../tabletserver/query_executor_test.go | 500 ++++-------------- go/vt/vttablet/tabletserver/querylogz_test.go | 7 +- go/vt/vttablet/tabletserver/queryz_test.go | 10 +- go/vt/vttablet/tabletserver/tabletserver.go | 3 +- .../tabletserver/tabletserver_flaky_test.go | 86 +-- .../vttablet/tabletserver/tx_executor_test.go | 4 +- 7 files changed, 157 insertions(+), 459 deletions(-) diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index ee62b338bce..4a1818509f7 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -180,15 +180,15 @@ func (qre *QueryExecutor) execAsTransaction(autocommit bool, f func(conn *TxConn func (qre *QueryExecutor) txConnExec(conn *TxConnection) (*sqltypes.Result, error) { switch qre.plan.PlanID { - case planbuilder.PlanInsert, planbuilder.PlanUpdate, planbuilder.PlanDelete, planbuilder.PlanSet: + case planbuilder.PlanInsert, planbuilder.PlanUpdate, planbuilder.PlanDelete: return qre.txFetch(conn, true) case planbuilder.PlanInsertMessage: qre.bindVars["#time_now"] = sqltypes.Int64BindVariable(time.Now().UnixNano()) return qre.txFetch(conn, true) case planbuilder.PlanUpdateLimit, planbuilder.PlanDeleteLimit: return qre.execDMLLimit(conn) - case planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: - return qre.txFetch(conn, false) + case planbuilder.PlanSet, planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: + return qre.execSQL(conn, qre.query, true) case planbuilder.PlanSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible: maxrows := qre.getSelectLimit() qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1) diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 312e70b0627..3091078cf92 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -38,7 +38,6 @@ import ( "vitess.io/vitess/go/vt/tableacl/simpleacl" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/planbuilder" "vitess.io/vitess/go/vt/vttablet/tabletserver/rules" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -62,34 +61,53 @@ func TestQueryExecutorPlans(t *testing.T) { fieldResult := sqltypes.MakeTestResult(fields) selectResult := sqltypes.MakeTestResult(fields, "1|aaa") + // The queries are run both in and outside a transaction. testcases := []struct { - input string + // input is the input query. + input string + // passThrough specifies if planbuilder.PassthroughDML must be set. passThrough bool + // dbResponses specifes the list of queries and responses to add to the fake db. dbResponses []dbResponse - resultWant *sqltypes.Result - planWant string - logWant string + // resultWant is the result we want. + resultWant *sqltypes.Result + // planWant is the PlanType we want to see built. + planWant string + // logWant is the log of queries we expect to be executed. + logWant string + // inTxWant is the query log we expect if we're in a transation. + // If empty, then we should expect the same as logWant. + inTxWant string }{{ - input: "select * from t where 1 != 1", + input: "select * from t", dbResponses: []dbResponse{{ query: "select * from t where 1 != 1", result: fieldResult, + }, { + query: "select * from t limit 10001", + result: selectResult, }}, - resultWant: fieldResult, - planWant: "SelectImpossible", - logWant: "select * from t where 1 != 1", + resultWant: selectResult, + planWant: "Select", + logWant: "select * from t where 1 != 1; select * from t limit 10001", + // Because the fields would have been cached before, the field query will + // not get re-executed. + inTxWant: "select * from t limit 10001", }, { - input: "select * from t", + input: "select * from t limit 1", dbResponses: []dbResponse{{ query: "select * from t where 1 != 1", result: fieldResult, }, { - query: "select * from t limit 10001", + query: "select * from t limit 1", result: selectResult, }}, resultWant: selectResult, planWant: "Select", - logWant: "select * from t where 1 != 1; select * from t limit 10001", + logWant: "select * from t where 1 != 1; select * from t limit 1", + // Because the fields would have been cached before, the field query will + // not get re-executed. + inTxWant: "select * from t limit 1", }, { input: "set a=1", dbResponses: []dbResponse{{ @@ -126,6 +144,15 @@ func TestQueryExecutorPlans(t *testing.T) { resultWant: dmlResult, planWant: "Insert", logWant: "insert into test_table(a) values (1)", + }, { + input: "replace into test_table(a) values(1)", + dbResponses: []dbResponse{{ + query: "replace into test_table(a) values (1)", + result: dmlResult, + }}, + resultWant: dmlResult, + planWant: "Insert", + logWant: "replace into test_table(a) values (1)", }, { input: "update test_table set a=1", dbResponses: []dbResponse{{ @@ -134,7 +161,10 @@ func TestQueryExecutorPlans(t *testing.T) { }}, resultWant: dmlResult, planWant: "UpdateLimit", - logWant: "begin; update test_table set a = 1 limit 10001; commit", + // The UpdateLimit query will not use autocommit because + // it needs to roll back on failure. + logWant: "begin; update test_table set a = 1 limit 10001; commit", + inTxWant: "update test_table set a = 1 limit 10001", }, { input: "update test_table set a=1", passThrough: true, @@ -153,7 +183,10 @@ func TestQueryExecutorPlans(t *testing.T) { }}, resultWant: dmlResult, planWant: "DeleteLimit", - logWant: "begin; delete from test_table limit 10001; commit", + // The DeleteLimit query will not use autocommit because + // it needs to roll back on failure. + logWant: "begin; delete from test_table limit 10001; commit", + inTxWant: "delete from test_table limit 10001", }, { input: "delete from test_table", passThrough: true, @@ -186,188 +219,92 @@ func TestQueryExecutorPlans(t *testing.T) { defer tsv.StopService() tsv.SetPassthroughDMLs(tcase.passThrough) + + // Test outside a transaction. qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) got, err := qre.Execute() require.NoError(t, err, tcase.input) assert.Equal(t, tcase.resultWant, got, tcase.input) assert.Equal(t, tcase.planWant, qre.logStats.PlanType, tcase.input) assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) - }() - } -} - -func TestQueryExecutorPlanPassDmlRBR(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set pk = foo()" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - // RBR mode - tsv := newTestTabletServer(ctx, noFlags, db) - defer tsv.StopService() - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{query} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - - // Statement mode - tsv.qe.binlogFormat = connpool.BinlogFormatStatement - _, err = qre.Execute() - if code := vterrors.Code(err); code != vtrpcpb.Code_UNIMPLEMENTED { - t.Errorf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) - } - testCommitHelper(t, tsv, qre) -} - -func TestQueryExecutorPassthroughDml(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set pk = foo()" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - // RBR mode - tsv := newTestTabletServer(ctx, noFlags, db) - defer tsv.StopService() - - tsv.SetPassthroughDMLs(true) - defer tsv.SetPassthroughDMLs(false) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{query} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } + // Test inside a transaction. + txid, err := tsv.Begin(ctx, &tsv.target, nil) + require.NoError(t, err) + defer tsv.Commit(ctx, &tsv.target, txid) - // Statement mode also works when allowUnsafeDMLs is true - tsv.qe.binlogFormat = connpool.BinlogFormatStatement - _, err = qre.Execute() - if code := vterrors.Code(err); code != vtrpcpb.Code_UNIMPLEMENTED { - t.Errorf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) + qre = newTestQueryExecutor(ctx, tsv, tcase.input, txid) + got, err = qre.Execute() + require.NoError(t, err, tcase.input) + assert.Equal(t, tcase.resultWant, got, "in tx: %v", tcase.input) + assert.Equal(t, tcase.planWant, qre.logStats.PlanType, "in tx: %v", tcase.input) + want := tcase.logWant + if tcase.inTxWant != "" { + want = tcase.inTxWant + } + assert.Equal(t, want, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) + }() } } -func TestQueryExecutorPlanPassDmlAutoCommitRBR(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set pk = foo()" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - // RBR mode - tsv := newTestTabletServer(ctx, noFlags, db) - defer tsv.StopService() - qre := newTestQueryExecutor(ctx, tsv, query, 0) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - - // Statement mode - tsv.qe.binlogFormat = connpool.BinlogFormatStatement - _, err = qre.Execute() - if code := vterrors.Code(err); code != vtrpcpb.Code_UNIMPLEMENTED { - t.Errorf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) +// TestQueryExecutorSelectImpossible is separate because it's a special case +// because the "in transaction" case is a no-op. +func TestQueryExecutorSelectImpossible(t *testing.T) { + type dbResponse struct { + query string + result *sqltypes.Result } -} - -func TestQueryExecutorPassthroughDmlAutoCommit(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "update test_table set pk = foo()" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - // RBR mode - tsv := newTestTabletServer(ctx, noFlags, db) - defer tsv.StopService() - - tsv.SetPassthroughDMLs(true) - defer tsv.SetPassthroughDMLs(false) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - qre := newTestQueryExecutor(ctx, tsv, query, 0) - assert.Equal(t, planbuilder.PlanUpdate, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } + fields := sqltypes.MakeTestFields("a|b", "int64|varchar") + fieldResult := sqltypes.MakeTestResult(fields) - // Statement mode - tsv.qe.binlogFormat = connpool.BinlogFormatStatement - _, err = qre.Execute() - if code := vterrors.Code(err); code != vtrpcpb.Code_UNIMPLEMENTED { - t.Errorf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) - } -} + testcases := []struct { + input string + dbResponses []dbResponse + resultWant *sqltypes.Result + planWant string + logWant string + inTxWant string + }{{ + input: "select * from t where 1 != 1", + dbResponses: []dbResponse{{ + query: "select * from t where 1 != 1", + result: fieldResult, + }}, + resultWant: fieldResult, + planWant: "SelectImpossible", + logWant: "select * from t where 1 != 1", + inTxWant: "", + }} + for _, tcase := range testcases { + func() { + db := setUpQueryExecutorTest(t) + defer db.Close() + for _, dbr := range tcase.dbResponses { + db.AddQuery(dbr.query, dbr.result) + } + ctx := context.Background() + tsv := newTestTabletServer(ctx, noFlags, db) + defer tsv.StopService() -func TestQueryExecutorPlanPassDmlReplaceInto(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "replace into test_table values (1)" - want := &sqltypes.Result{} - db.AddQuery(query, want) - ctx := context.Background() - // RBR mode - tsv := newTestTabletServer(ctx, noFlags, db) - defer tsv.StopService() - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - tsv.qe.binlogFormat = connpool.BinlogFormatRow - assert.Equal(t, planbuilder.PlanInsert, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - wantqueries := []string{query} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } + qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) + got, err := qre.Execute() + require.NoError(t, err, tcase.input) + assert.Equal(t, tcase.resultWant, got, tcase.input) + assert.Equal(t, tcase.planWant, qre.logStats.PlanType, tcase.input) + assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) + txid, err := tsv.Begin(ctx, &tsv.target, nil) + require.NoError(t, err) + defer tsv.Commit(ctx, &tsv.target, txid) - // Statement mode - tsv.qe.binlogFormat = connpool.BinlogFormatStatement - _, err = qre.Execute() - if code := vterrors.Code(err); code != vtrpcpb.Code_UNIMPLEMENTED { - t.Errorf("qre.Execute: %v, want %v", code, vtrpcpb.Code_INVALID_ARGUMENT) + qre = newTestQueryExecutor(ctx, tsv, tcase.input, txid) + got, err = qre.Execute() + require.NoError(t, err, tcase.input) + assert.Equal(t, tcase.resultWant, got, "in tx: %v", tcase.input) + assert.Equal(t, tcase.planWant, qre.logStats.PlanType, "in tx: %v", tcase.input) + assert.Equal(t, tcase.inTxWant, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) + }() } - testCommitHelper(t, tsv, qre) } func TestQueryExecutorPlanInsertMessage(t *testing.T) { @@ -390,70 +327,6 @@ func TestQueryExecutorPlanInsertMessage(t *testing.T) { } } -func TestQueryExecutorPlanOtherWithinATransaction(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "show test_table" - want := &sqltypes.Result{ - Fields: getTestTableFields(), - } - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - assert.Equal(t, planbuilder.PlanOtherRead, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - if gotqueries := fetchRecordedQueries(qre); gotqueries != nil { - t.Errorf("queries: %v, want nil", gotqueries) - } -} - -func TestQueryExecutorPlanPassSelectWithInATransaction(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - fields := []*querypb.Field{ - {Name: "addr", Type: sqltypes.Int32}, - } - query := "select addr from test_table where pk = 1 limit 1000" - want := &sqltypes.Result{ - Fields: fields, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - {sqltypes.NewInt32(123)}, - }, - } - db.AddQuery(query, want) - db.AddQuery("select addr from test_table where 1 != 1", &sqltypes.Result{ - Fields: fields, - }) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - txid := newTransaction(tsv, nil) - qre := newTestQueryExecutor(ctx, tsv, query, txid) - defer tsv.StopService() - defer testCommitHelper(t, tsv, qre) - assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } - if gotqueries := fetchRecordedQueries(qre); gotqueries != nil { - t.Errorf("queries: %v, want nil", gotqueries) - } -} - func TestQueryExecutorPlanPassSelectWithLockOutsideATransaction(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() @@ -477,145 +350,6 @@ func TestQueryExecutorPlanPassSelectWithLockOutsideATransaction(t *testing.T) { } } -func TestQueryExecutorPlanPassSelect(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "select * from test_table limit 1000" - want := &sqltypes.Result{ - Fields: getTestTableFields(), - } - db.AddQuery(query, want) - db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ - Fields: getTestTableFields(), - }) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -func TestQueryExecutorPlanSelectImpossible(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "select * from test_table where 1 != 1" - want := &sqltypes.Result{ - Fields: getTestTableFields(), - } - db.AddQuery(query, want) - db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{ - Fields: getTestTableFields(), - }) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - assert.Equal(t, planbuilder.PlanSelectImpossible, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -func TestQueryExecutorPlanPassSelectSqlSelectLimit(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "select * from test_table" - expandedQuery := "select * from test_table limit 20" - want := &sqltypes.Result{ - Fields: getTestTableFields(), - } - db.AddQuery(query, want) - db.AddQuery(expandedQuery, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - qre.options = &querypb.ExecuteOptions{ - SqlSelectLimit: 20, - } - defer tsv.StopService() - assert.Equal(t, planbuilder.PlanSelect, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("got: %v, want: %v", got, want) - } -} - -func TestQueryExecutorPlanSet(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - setQuery := "set unknown_key = 1" - db.AddQuery(setQuery, &sqltypes.Result{}) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - defer tsv.StopService() - qre := newTestQueryExecutor(ctx, tsv, setQuery, 0) - assert.Equal(t, planbuilder.PlanSet, qre.plan.PlanID) - // Query will be delegated to MySQL and both Fields and Rows should be - // empty arrays in this case. - want := &sqltypes.Result{} - got, err := qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("qre.Execute() = %v, want: %v", got, want) - } - - // Test inside transaction. - txid := newTransaction(tsv, nil) - qre = newTestQueryExecutor(ctx, tsv, setQuery, txid) - got, err = qre.Execute() - if err != nil { - t.Fatalf("qre.Execute() = %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("qre.Execute() = %v, want: %v", got, want) - } - wantqueries := []string{"set unknown_key = 1"} - gotqueries := fetchRecordedQueries(qre) - if !reflect.DeepEqual(gotqueries, wantqueries) { - t.Errorf("queries: %v, want %v", gotqueries, wantqueries) - } - testCommitHelper(t, tsv, qre) - tsv.StopService() -} - -func TestQueryExecutorPlanOther(t *testing.T) { - db := setUpQueryExecutorTest(t) - defer db.Close() - query := "show test_table" - want := &sqltypes.Result{ - Fields: getTestTableFields(), - } - db.AddQuery(query, want) - ctx := context.Background() - tsv := newTestTabletServer(ctx, noFlags, db) - qre := newTestQueryExecutor(ctx, tsv, query, 0) - defer tsv.StopService() - assert.Equal(t, planbuilder.PlanOtherRead, qre.plan.PlanID) - got, err := qre.Execute() - if err != nil { - t.Fatalf("got: %v, want nil", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("qre.Execute() = %v, want: %v", got, want) - } -} - func TestQueryExecutorPlanNextval(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() @@ -803,7 +537,7 @@ func TestQueryExecutorMessageStreamACL(t *testing.T) { return io.EOF }) - want := `table acl error: "u2" [] cannot run MESSAGE_STREAM on table "msg"` + want := `table acl error: "u2" [] cannot run MessageStream on table "msg"` if err == nil || err.Error() != want { t.Errorf("qre.MessageStream(msg) error: %v, want %s", err, want) } diff --git a/go/vt/vttablet/tabletserver/querylogz_test.go b/go/vt/vttablet/tabletserver/querylogz_test.go index 793562864bd..249f1962f95 100644 --- a/go/vt/vttablet/tabletserver/querylogz_test.go +++ b/go/vt/vttablet/tabletserver/querylogz_test.go @@ -73,7 +73,7 @@ func TestQuerylogzHandler(t *testing.T) { ``, ``, ``, - ``, + ``, ``, ``, ``, @@ -103,7 +103,7 @@ func TestQuerylogzHandler(t *testing.T) { ``, ``, ``, - ``, + ``, ``, ``, ``, @@ -133,7 +133,7 @@ func TestQuerylogzHandler(t *testing.T) { ``, ``, ``, - ``, + ``, ``, ``, ``, @@ -162,6 +162,7 @@ func TestQuerylogzHandler(t *testing.T) { } func checkQuerylogzHasStats(t *testing.T, pattern []string, logStats *tabletenv.LogStats, page []byte) { + t.Helper() matcher := regexp.MustCompile(strings.Join(pattern, `\s*`)) if !matcher.Match(page) { t.Fatalf("querylogz page does not contain stats: %v, pattern: %v, page: %s", logStats, pattern, string(page)) diff --git a/go/vt/vttablet/tabletserver/queryz_test.go b/go/vt/vttablet/tabletserver/queryz_test.go index 9d1f8fc44f3..cfcc26a45e5 100644 --- a/go/vt/vttablet/tabletserver/queryz_test.go +++ b/go/vt/vttablet/tabletserver/queryz_test.go @@ -85,8 +85,7 @@ func TestQueryzHandler(t *testing.T) { ``, ``, ``, - ``, - ``, + ``, ``, ``, ``, @@ -103,7 +102,6 @@ func TestQueryzHandler(t *testing.T) { ``, ``, ``, - ``, ``, ``, ``, @@ -119,8 +117,7 @@ func TestQueryzHandler(t *testing.T) { ``, ``, ``, - ``, - ``, + ``, ``, ``, ``, @@ -136,8 +133,7 @@ func TestQueryzHandler(t *testing.T) { ``, ``, ``, - ``, - ``, + ``, ``, ``, ``, diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 4ddbb1f232c..0aaa4d86b76 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -1188,9 +1188,10 @@ func (tsv *TabletServer) computeTxSerializerKey(ctx context.Context, logStats *t } switch plan.PlanID { + // Serialize only UPDATE or DELETE queries. case planbuilder.PlanUpdate, planbuilder.PlanUpdateLimit, planbuilder.PlanDelete, planbuilder.PlanDeleteLimit: - // Serialize only UPDATE or DELETE queries. + default: return "", "" } diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index 2a38e4acb05..faa2937d95b 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -627,7 +627,7 @@ func TestTabletServerRedoLogIsKeptBetweenRestarts(t *testing.T) { sqltypes.NewVarBinary("dtid0"), sqltypes.NewInt64(RedoStatePrepared), sqltypes.NewVarBinary(""), - sqltypes.NewVarBinary("update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"), + sqltypes.NewVarBinary("update test_table set name = 2 where pk = 1 limit 10001"), }}, }) turnOnTxEngine() @@ -635,7 +635,7 @@ func TestTabletServerRedoLogIsKeptBetweenRestarts(t *testing.T) { t.Errorf("len(tsv.te.preparedPool.conns): %d, want 1", len(tsv.te.preparedPool.conns)) } got := tsv.te.preparedPool.conns["dtid0"].Queries - want := []string{"update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"} + want := []string{"update test_table set name = 2 where pk = 1 limit 10001"} if !reflect.DeepEqual(got, want) { t.Errorf("Prepared queries: %v, want %v", got, want) } @@ -662,7 +662,7 @@ func TestTabletServerRedoLogIsKeptBetweenRestarts(t *testing.T) { sqltypes.NewVarBinary("a:b:10"), sqltypes.NewInt64(RedoStatePrepared), sqltypes.NewVarBinary(""), - sqltypes.NewVarBinary("update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"), + sqltypes.NewVarBinary("update test_table set name = 2 where pk = 1 limit 10001"), }, { sqltypes.NewVarBinary("a:b:20"), sqltypes.NewInt64(RedoStateFailed), @@ -675,7 +675,7 @@ func TestTabletServerRedoLogIsKeptBetweenRestarts(t *testing.T) { t.Errorf("len(tsv.te.preparedPool.conns): %d, want 1", len(tsv.te.preparedPool.conns)) } got = tsv.te.preparedPool.conns["a:b:10"].Queries - want = []string{"update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"} + want = []string{"update test_table set name = 2 where pk = 1 limit 10001"} if !reflect.DeepEqual(got, want) { t.Errorf("Prepared queries: %v, want %v", got, want) } @@ -1098,10 +1098,10 @@ func TestTabletServerExecuteBatch(t *testing.T) { testUtils := newTestUtils() sql := "insert into test_table values (1, 2, 'addr', 'name')" sqlResult := &sqltypes.Result{} - expanedSQL := "insert into test_table(pk, name, addr, name_string) values (1, 2, 'addr', 'name') /* _stream test_table (pk ) (1 ); */" + expandedSQL := "insert into test_table(pk, name, addr, name_string) values (1, 2, 'addr', 'name') /* _stream test_table (pk ) (1 ); */" db.AddQuery(sql, sqlResult) - db.AddQuery(expanedSQL, sqlResult) + db.AddQuery(expandedSQL, sqlResult) config := testUtils.newQueryServiceConfig() tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) @@ -1231,14 +1231,14 @@ func TestTabletServerExecuteBatchSqlExecFailInTransaction(t *testing.T) { testUtils := newTestUtils() sql := "insert into test_table values (1, 2)" sqlResult := &sqltypes.Result{} - expanedSQL := "insert into test_table values (1, 2) /* _stream test_table (pk ) (1 ); */" + expandedSQL := "insert into test_table values (1, 2) /* _stream test_table (pk ) (1 ); */" db.AddQuery(sql, sqlResult) - db.AddQuery(expanedSQL, sqlResult) + db.AddQuery(expandedSQL, sqlResult) // make this query fail db.AddRejectedQuery(sql, errRejected) - db.AddRejectedQuery(expanedSQL, errRejected) + db.AddRejectedQuery(expandedSQL, errRejected) config := testUtils.newQueryServiceConfig() tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) @@ -1268,40 +1268,6 @@ func TestTabletServerExecuteBatchSqlExecFailInTransaction(t *testing.T) { } } -func TestTabletServerExecuteBatchSqlSucceedInTransaction(t *testing.T) { - db := setUpTabletServerTest(t) - defer db.Close() - testUtils := newTestUtils() - sql := "insert into test_table values (1, 2, 'addr', 'name')" - sqlResult := &sqltypes.Result{} - expanedSQL := "insert into test_table(pk, name, addr, name_string) values (1, 2, 'addr', 'name') /* _stream test_table (pk ) (1 ); */" - - db.AddQuery(sql, sqlResult) - db.AddQuery(expanedSQL, sqlResult) - - // cause execution error for this particular sql query - db.AddRejectedQuery(sql, errRejected) - - config := testUtils.newQueryServiceConfig() - tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) - dbcfgs := testUtils.newDBConfigs(db) - target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} - err := tsv.StartService(target, dbcfgs) - if err != nil { - t.Fatalf("StartService failed: %v", err) - } - defer tsv.StopService() - ctx := context.Background() - if _, err := tsv.ExecuteBatch(ctx, &target, []*querypb.BoundQuery{ - { - Sql: sql, - BindVariables: nil, - }, - }, false, 0, nil); err != nil { - t.Fatal(err) - } -} - func TestTabletServerExecuteBatchCallCommitWithoutABegin(t *testing.T) { db := setUpTabletServerTest(t) defer db.Close() @@ -1332,10 +1298,10 @@ func TestExecuteBatchNestedTransaction(t *testing.T) { testUtils := newTestUtils() sql := "insert into test_table values (1, 2)" sqlResult := &sqltypes.Result{} - expanedSQL := "insert into test_table values (1, 2) /* _stream test_table (pk ) (1 ); */" + expandedSQL := "insert into test_table values (1, 2) /* _stream test_table (pk ) (1 ); */" db.AddQuery(sql, sqlResult) - db.AddQuery(expanedSQL, sqlResult) + db.AddQuery(expandedSQL, sqlResult) config := testUtils.newQueryServiceConfig() tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) dbcfgs := testUtils.newDBConfigs(db) @@ -1421,7 +1387,7 @@ func TestSerializeTransactionsSameRow(t *testing.T) { // 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 ); */", + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and name = 1 limit 10001", func() { close(tx1Started) if err := waitForTxSerializationPendingQueries(tsv, "test_table where pk = 1 and name = 1", 2); err != nil { @@ -1546,7 +1512,7 @@ func TestSerializeTransactionsSameRow_ExecuteBatchAsTransaction(t *testing.T) { // Make sure that tx2 and tx3 start only after tx1 is running its Execute(). tx1Started := make(chan struct{}) - db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk in (1) /* _stream test_table (pk ) (1 ); */", + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and name = 1 limit 10001", func() { close(tx1Started) if err := waitForTxSerializationPendingQueries(tsv, "test_table where pk = 1 and name = 1", 2); err != nil { @@ -1663,7 +1629,7 @@ func TestSerializeTransactionsSameRow_ConcurrentTransactions(t *testing.T) { tx1Started := make(chan struct{}) allQueriesPending := make(chan struct{}) - db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk in (1) /* _stream test_table (pk ) (1 ); */", + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and name = 1 limit 10001", func() { close(tx1Started) <-allQueriesPending @@ -1802,7 +1768,7 @@ func TestSerializeTransactionsSameRow_TooManyPendingRequests(t *testing.T) { // 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 ); */", + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and name = 1 limit 10001", func() { close(tx1Started) <-tx2Failed @@ -1891,7 +1857,7 @@ func TestSerializeTransactionsSameRow_TooManyPendingRequests_ExecuteBatchAsTrans // 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 ); */", + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and name = 1 limit 10001", func() { close(tx1Started) <-tx2Failed @@ -1988,7 +1954,7 @@ func TestSerializeTransactionsSameRow_RequestCanceled(t *testing.T) { // 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 ); */", + db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and name = 1 limit 10001", func() { close(tx1Started) // Keep blocking until tx2 was canceled. @@ -2117,9 +2083,9 @@ 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: 'update msg set time_acked" if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("tsv.MessageAck(invalid): %v, want %s", err, want) + t.Errorf("tsv.MessageAck(invalid):\n%v, want\n%s", err, want) } db.AddQuery( @@ -2158,7 +2124,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: 'update msg set time_next" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("tsv.PostponeMessages(invalid):\n%v, want\n%s", err, want) } @@ -2199,7 +2165,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: 'delete from msg where time_scheduled" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("tsv.PurgeMessages(invalid):\n%v, want\n%s", err, want) } @@ -2218,7 +2184,7 @@ func TestPurgeMessages(t *testing.T) { }}, }, ) - db.AddQuery("delete from msg where (time_scheduled = 1 and id = 1) /* _stream msg (time_scheduled id ) (1 1 ); */", &sqltypes.Result{RowsAffected: 1}) + db.AddQuery("delete from msg where time_scheduled < 3 and time_acked is not null limit 500", &sqltypes.Result{RowsAffected: 1}) count, err := tsv.PurgeMessages(ctx, &target, "msg", 3) require.NoError(t, err) if count != 1 { @@ -2616,17 +2582,17 @@ 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 ); */": { + "update test_table set name_string = 'tx1' where pk = 1 and name = 1 limit 10001": { RowsAffected: 1, }, - "update test_table set name_string = 'tx2' where pk in (1) /* _stream test_table (pk ) (1 ); */": { + "update test_table set name_string = 'tx2' where pk = 1 and name = 1 limit 10001": { RowsAffected: 1, }, - "update test_table set name_string = 'tx3' where pk in (1) /* _stream test_table (pk ) (1 ); */": { + "update test_table set name_string = 'tx3' where pk = 1 and name = 1 limit 10001": { RowsAffected: 1, }, // tx3, but with different primary key. - "update test_table set name_string = 'tx3' where pk in (2) /* _stream test_table (pk ) (2 ); */": { + "update test_table set name_string = 'tx3' where pk = 2 and name = 1 limit 10001": { RowsAffected: 1, }, // Complex WHERE clause requires SELECT of primary key first. diff --git a/go/vt/vttablet/tabletserver/tx_executor_test.go b/go/vt/vttablet/tabletserver/tx_executor_test.go index d4c3fef16b3..bc4bcb64b0a 100644 --- a/go/vt/vttablet/tabletserver/tx_executor_test.go +++ b/go/vt/vttablet/tabletserver/tx_executor_test.go @@ -529,7 +529,7 @@ func newTestTxExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db *fa db.AddQueryPattern("insert into `_vt`\\.redo_statement.*", &sqltypes.Result{}) db.AddQuery("delete from `_vt`.redo_state where dtid = 'aa'", &sqltypes.Result{}) db.AddQuery("delete from `_vt`.redo_statement where dtid = 'aa'", &sqltypes.Result{}) - db.AddQuery("update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) + db.AddQuery("update test_table set name = 2 where pk = 1 limit 10001", &sqltypes.Result{}) return &TxExecutor{ ctx: ctx, logStats: logStats, @@ -547,7 +547,7 @@ func newShortAgeExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db * db.AddQueryPattern("insert into `_vt`\\.redo_statement.*", &sqltypes.Result{}) db.AddQuery("delete from `_vt`.redo_state where dtid = 'aa'", &sqltypes.Result{}) db.AddQuery("delete from `_vt`.redo_statement where dtid = 'aa'", &sqltypes.Result{}) - db.AddQuery("update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{}) + db.AddQuery("update test_table set name = 2 where pk = 1 limit 10001", &sqltypes.Result{}) return &TxExecutor{ ctx: ctx, logStats: logStats, From a1e533d9aec1b5267d8e590a9ec95a16d7e9431a Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 18 Mar 2020 13:11:40 -0700 Subject: [PATCH 323/825] deprecation: no need to check binlog format Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/connpool/dbconn.go | 42 +++++-------------- go/vt/vttablet/tabletserver/query_engine.go | 5 ++- .../tabletserver/query_executor_test.go | 12 ------ .../tabletserver/tabletserver_flaky_test.go | 20 --------- 4 files changed, 14 insertions(+), 65 deletions(-) diff --git a/go/vt/vttablet/tabletserver/connpool/dbconn.go b/go/vt/vttablet/tabletserver/connpool/dbconn.go index d5f9a6c20d3..ef39b8e193b 100644 --- a/go/vt/vttablet/tabletserver/connpool/dbconn.go +++ b/go/vt/vttablet/tabletserver/connpool/dbconn.go @@ -227,65 +227,45 @@ var ( getModeSQL = "select @@global.sql_mode" getAutocommit = "select @@autocommit" getAutoIsNull = "select @@sql_auto_is_null" - showBinlog = "show variables like 'binlog_format'" ) // VerifyMode is a helper method to verify mysql is running with // sql_mode = STRICT_TRANS_TABLES or STRICT_ALL_TABLES and autocommit=ON. -// It also returns the current binlog format. -func (dbc *DBConn) VerifyMode(strictTransTables bool) (BinlogFormat, error) { +func (dbc *DBConn) VerifyMode(strictTransTables bool) error { if strictTransTables { qr, err := dbc.conn.ExecuteFetch(getModeSQL, 2, false) if err != nil { - return 0, vterrors.Wrap(err, "could not verify mode") + return vterrors.Wrap(err, "could not verify mode") } if len(qr.Rows) != 1 { - return 0, fmt.Errorf("incorrect rowcount received for %s: %d", getModeSQL, len(qr.Rows)) + return fmt.Errorf("incorrect rowcount received for %s: %d", getModeSQL, len(qr.Rows)) } sqlMode := qr.Rows[0][0].ToString() if !(strings.Contains(sqlMode, "STRICT_TRANS_TABLES") || strings.Contains(sqlMode, "STRICT_ALL_TABLES")) { - return 0, fmt.Errorf("require sql_mode to be STRICT_TRANS_TABLES or STRICT_ALL_TABLES: got '%s'", qr.Rows[0][0].ToString()) + return fmt.Errorf("require sql_mode to be STRICT_TRANS_TABLES or STRICT_ALL_TABLES: got '%s'", qr.Rows[0][0].ToString()) } } qr, err := dbc.conn.ExecuteFetch(getAutocommit, 2, false) if err != nil { - return 0, vterrors.Wrap(err, "could not verify mode") + return vterrors.Wrap(err, "could not verify mode") } if len(qr.Rows) != 1 { - return 0, fmt.Errorf("incorrect rowcount received for %s: %d", getAutocommit, len(qr.Rows)) + return fmt.Errorf("incorrect rowcount received for %s: %d", getAutocommit, len(qr.Rows)) } if !strings.Contains(qr.Rows[0][0].ToString(), "1") { - return 0, fmt.Errorf("require autocommit to be 1: got %s", qr.Rows[0][0].ToString()) + return fmt.Errorf("require autocommit to be 1: got %s", qr.Rows[0][0].ToString()) } qr, err = dbc.conn.ExecuteFetch(getAutoIsNull, 2, false) if err != nil { - return 0, vterrors.Wrap(err, "could not verify mode") + return vterrors.Wrap(err, "could not verify mode") } if len(qr.Rows) != 1 { - return 0, fmt.Errorf("incorrect rowcount received for %s: %d", getAutoIsNull, len(qr.Rows)) + return fmt.Errorf("incorrect rowcount received for %s: %d", getAutoIsNull, len(qr.Rows)) } if !strings.Contains(qr.Rows[0][0].ToString(), "0") { - return 0, fmt.Errorf("require sql_auto_is_null to be 0: got %s", qr.Rows[0][0].ToString()) + return fmt.Errorf("require sql_auto_is_null to be 0: got %s", qr.Rows[0][0].ToString()) } - qr, err = dbc.conn.ExecuteFetch(showBinlog, 10, false) - if err != nil { - return 0, vterrors.Wrap(err, "could not fetch binlog format") - } - if len(qr.Rows) != 1 { - return 0, fmt.Errorf("incorrect rowcount received for %s: %d", showBinlog, len(qr.Rows)) - } - if len(qr.Rows[0]) != 2 { - return 0, fmt.Errorf("incorrect column count received for %s: %d", showBinlog, len(qr.Rows[0])) - } - switch qr.Rows[0][1].ToString() { - case "STATEMENT": - return BinlogFormatStatement, nil - case "ROW": - return BinlogFormatRow, nil - case "MIXED": - return BinlogFormatMixed, nil - } - return 0, fmt.Errorf("unexpected binlog format for %s: %s", showBinlog, qr.Rows[0][1].ToString()) + return nil } // Close closes the DBConn. diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index b996cdbf572..303ecf6657c 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -149,7 +149,6 @@ type QueryEngine struct { connTimeout sync2.AtomicDuration queryPoolWaiters sync2.AtomicInt64 queryPoolWaiterCap sync2.AtomicInt64 - binlogFormat connpool.BinlogFormat maxResultSize sync2.AtomicInt64 warnResultSize sync2.AtomicInt64 maxDMLRows sync2.AtomicInt64 @@ -292,7 +291,9 @@ func (qe *QueryEngine) Open() error { qe.conns.Close() return err } - qe.binlogFormat, err = conn.VerifyMode(qe.strictTransTables) + err = conn.VerifyMode(qe.strictTransTables) + // Recycle needs to happen before error check. + // Otherwise, qe.conns.Close will hang. conn.Recycle() if err != nil { diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 3091078cf92..aa2e38ceffc 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -1105,18 +1105,6 @@ func getQueryExecutorSupportedQueries(testTableHasMultipleUniqueKeys bool) map[s {sqltypes.NewVarBinary("fakedb server")}, }, }, - "show variables like 'binlog_format'": { - Fields: []*querypb.Field{{ - Type: sqltypes.VarChar, - }, { - Type: sqltypes.VarChar, - }}, - RowsAffected: 1, - Rows: [][]sqltypes.Value{{ - sqltypes.NewVarBinary("binlog_format"), - sqltypes.NewVarBinary("STATEMENT"), - }}, - }, mysql.BaseShowTables: { Fields: mysql.BaseShowTablesFields, RowsAffected: 3, diff --git a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go index faa2937d95b..4d2cc6a0b8b 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_flaky_test.go @@ -2595,26 +2595,6 @@ func getSupportedQueries() map[string]*sqltypes.Result { "update test_table set name_string = 'tx3' where pk = 2 and name = 1 limit 10001": { 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.NewVarBinary("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.NewVarBinary("2"), - }}, - }, // queries for twopc sqlTurnoffBinlog: {}, fmt.Sprintf(sqlCreateSidecarDB, "`_vt`"): {}, From d5a97a3942e07dbf7e3c4ce4d5f0bcf0e0b15c16 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 18 Mar 2020 13:23:50 -0700 Subject: [PATCH 324/825] deprecation: fix other tests under tabletserver Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/rules/rules_test.go | 2 +- .../tabletserver/schema/load_table_test.go | 38 ------------------- go/vt/vttablet/tabletserver/schema/schema.go | 2 +- 3 files changed, 2 insertions(+), 40 deletions(-) diff --git a/go/vt/vttablet/tabletserver/rules/rules_test.go b/go/vt/vttablet/tabletserver/rules/rules_test.go index 09377833e4a..3a25ce74fca 100644 --- a/go/vt/vttablet/tabletserver/rules/rules_test.go +++ b/go/vt/vttablet/tabletserver/rules/rules_test.go @@ -532,7 +532,7 @@ func TestImport(t *testing.T) { "RequestIP": "123.123.123", "User": "user", "Query": "query", - "Plans": ["PASS_SELECT", "INSERT_PK"], + "Plans": ["Select", "Insert"], "TableNames":["a", "b"], "BindVarConds": [{ "Name": "bvname1", diff --git a/go/vt/vttablet/tabletserver/schema/load_table_test.go b/go/vt/vttablet/tabletserver/schema/load_table_test.go index 5b9fb6ecda9..ce96c828f2c 100644 --- a/go/vt/vttablet/tabletserver/schema/load_table_test.go +++ b/go/vt/vttablet/tabletserver/schema/load_table_test.go @@ -170,25 +170,6 @@ func TestLoadTableMessage(t *testing.T) { t.Errorf("newTestLoadTable: %v, want %s", err, wanterr) } - // id column must be part of primary key. - for query, result := range getMessageTableQueries() { - db.AddQuery(query, result) - } - db.AddQuery( - "show index from test_table", - &sqltypes.Result{ - Fields: mysql.ShowIndexFromTableFields, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - mysql.ShowIndexFromTableRow("test_table", true, "PRIMARY", 1, "time_scheduled", false), - }, - }) - _, err = newTestLoadTable("USER_TABLE", "vitess_message,vt_ack_wait=30,vt_purge_after=120,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=30", db) - wanterr = "id column is not part of the primary key for message table: test_table" - if err == nil || err.Error() != wanterr { - t.Errorf("newTestLoadTable: %v, want %s", err, wanterr) - } - for query, result := range getTestLoadTableQueries() { db.AddQuery(query, result) } @@ -246,25 +227,6 @@ func TestLoadTableMessageTopic(t *testing.T) { t.Errorf("newTestLoadTable: %v, want %s", err, wanterr) } - // id column must be part of primary key. - for query, result := range getMessageTableQueries() { - db.AddQuery(query, result) - } - db.AddQuery( - "show index from test_table", - &sqltypes.Result{ - Fields: mysql.ShowIndexFromTableFields, - RowsAffected: 1, - Rows: [][]sqltypes.Value{ - mysql.ShowIndexFromTableRow("test_table", true, "PRIMARY", 1, "time_scheduled", false), - }, - }) - _, err = newTestLoadTable("USER_TABLE", "vitess_message,vt_topic=test_topic,vt_ack_wait=30,vt_purge_after=120,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=30", db) - wanterr = "id column is not part of the primary key for message table: test_table" - if err == nil || err.Error() != wanterr { - t.Errorf("newTestLoadTable: %v, want %s", err, wanterr) - } - for query, result := range getTestLoadTableQueries() { db.AddQuery(query, result) } diff --git a/go/vt/vttablet/tabletserver/schema/schema.go b/go/vt/vttablet/tabletserver/schema/schema.go index b6e7b7bbc02..13b2d7282a0 100644 --- a/go/vt/vttablet/tabletserver/schema/schema.go +++ b/go/vt/vttablet/tabletserver/schema/schema.go @@ -140,7 +140,7 @@ func NewTable(name string) *Table { // Done must be called after columns and indexes are added to // the table. It will build additional metadata like PKColumns. func (ta *Table) Done() { - if !ta.HasPrimary() { + if len(ta.Indexes) == 0 { return } pkIndex := ta.Indexes[0] From 48d380ea47ac40ea2c2f8a954212385df105abe1 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Wed, 18 Mar 2020 23:14:25 +0100 Subject: [PATCH 325/825] Update test cases for where clause Signed-off-by: Rohit Nayak --- .../tabletserver/vstreamer/planbuilder.go | 13 ++-- .../vstreamer/planbuilder_test.go | 77 +++++++++++++++---- 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index 6441cc01c2b..c6474d1b8ec 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -37,13 +37,14 @@ import ( type Plan struct { Table *Table + // ColExprs is the list of column expressions to be sent + // in the stream. + ColExprs []ColExpr + // Filters is the list of filters to be applied to the columns // of the table. Filters []Filter - // ColExprs is the list of column expressions to be sent - // in the stream. - ColExprs []ColExpr } type Opcode int @@ -381,9 +382,9 @@ func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) er return err } plan.Filters = append(plan.Filters, Filter{ - Opcode: Equal, - ColNum: colnum, - Value: resolved, + Opcode: Equal, + ColNum: colnum, + Value: resolved, }) case *sqlparser.FuncExpr: if !expr.Name.EqualString("in_keyrange") { diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go index 0ff9bfea5fa..e9ac90e98fa 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go @@ -245,7 +245,14 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("val"), Type: sqltypes.VarBinary, }}, - VindexColumns: []int{0}, + Filters: []Filter {{ + Opcode: VindexMatch, + ColNum: 0, + Value: sqltypes.NULL, + Vindex: nil, + VindexColumns: []int{0}, + KeyRange: nil, + }}, }, }, { inTable: t1, @@ -302,7 +309,14 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("id"), Type: sqltypes.Int64, }}, - VindexColumns: []int{0}, + Filters: []Filter {{ + Opcode: VindexMatch, + ColNum: 0, + Value: sqltypes.NULL, + Vindex: nil, + VindexColumns: []int{0}, + KeyRange: nil, + }}, }, }, { inTable: t1, @@ -317,7 +331,36 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("id"), Type: sqltypes.Int64, }}, - VindexColumns: []int{0}, + Filters: []Filter {{ + Opcode: VindexMatch, + ColNum: 0, + Value: sqltypes.NULL, + Vindex: nil, + VindexColumns: []int{0}, + KeyRange: nil, + }}, + }, + },{ + inTable: t1, + inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select val, id from t1 where id = 1"}, + outPlan: &Plan{ + ColExprs: []ColExpr{{ + ColNum: 1, + Alias: sqlparser.NewColIdent("val"), + Type: sqltypes.VarBinary, + }, { + ColNum: 0, + Alias: sqlparser.NewColIdent("id"), + Type: sqltypes.Int64, + }}, + Filters: []Filter {{ + Opcode: Equal, + ColNum: 0, + Value: sqltypes.NewInt64(1), + Vindex: nil, + VindexColumns: nil, + KeyRange: nil, + }}, }, }, { inTable: t2, @@ -335,7 +378,14 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("id"), Type: sqltypes.Int64, }}, - VindexColumns: []int{0, 1}, + Filters: []Filter {{ + Opcode: VindexMatch, + ColNum: 0, + Value: sqltypes.NULL, + Vindex: nil, + VindexColumns: []int{0,1}, + KeyRange: nil, + }}, }, }, { inTable: regional, @@ -400,14 +450,10 @@ func TestPlanbuilder(t *testing.T) { inTable: t1, inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select *, id from t1"}, outErr: `unsupported: *, id`, - }, { - inTable: t1, - inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select id, val from t1 where id=1"}, - outErr: `unsupported where clause: where id = 1`, - }, { + },{ inTable: t1, inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select id, val from t1 where max(id)"}, - outErr: `unsupported where clause: where max(id)`, + outErr: `unsupported constraint: max(id)`, }, { inTable: t1, inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select id, val from t1 where in_keyrange(id)"}, @@ -463,15 +509,20 @@ func TestPlanbuilder(t *testing.T) { inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select id, val from t1 where in_keyrange(id, 1+1, '-80')"}, outErr: `unsupported: 1 + 1`, }} - for _, tcase := range testcases { plan, err := buildPlan(tcase.inTable, testLocalVSchema, &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{tcase.inRule}, }) if plan != nil { plan.Table = nil - plan.Vindex = nil - plan.KeyRange = nil + for ind, _ := range plan.Filters { + plan.Filters[ind].KeyRange = nil + if plan.Filters[ind]. + Opcode == VindexMatch { + plan.Filters[ind].Value = sqltypes.NULL + } + plan.Filters[ind].Vindex = nil + } if !reflect.DeepEqual(tcase.outPlan, plan) { t.Errorf("Plan(%v, %v):\n%v, want\n%v", tcase.inTable, tcase.inRule, plan, tcase.outPlan) } From 47f391504333d4cceffae729e7c2937e15ceeaf3 Mon Sep 17 00:00:00 2001 From: Dan Kozlowski Date: Wed, 18 Mar 2020 15:29:11 -0700 Subject: [PATCH 326/825] Altering the context for azblob to outlive the AddFile request Azure uses an Async upload thread backed by a pipe to upload files. Since the Flush call doesn't wait for the upload to finish this can cause a context cancelled error. This change uses a seperate context that can be cancelled by a call to Abort() Signed-off-by: Dan Kozlowski --- go/vt/mysqlctl/azblobbackupstorage/azblob.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/go/vt/mysqlctl/azblobbackupstorage/azblob.go b/go/vt/mysqlctl/azblobbackupstorage/azblob.go index 870605a9d2d..a9346506e82 100644 --- a/go/vt/mysqlctl/azblobbackupstorage/azblob.go +++ b/go/vt/mysqlctl/azblobbackupstorage/azblob.go @@ -113,6 +113,8 @@ type AZBlobBackupHandle struct { readOnly bool waitGroup sync.WaitGroup errors concurrency.AllErrorRecorder + ctx context.Context + cancel context.CancelFunc } // Directory implements BackupHandle. @@ -148,7 +150,7 @@ func (bh *AZBlobBackupHandle) AddFile(ctx context.Context, filename string, file go func() { defer bh.waitGroup.Done() - _, err := azblob.UploadStreamToBlockBlob(ctx, reader, blockBlobURL, azblob.UploadStreamToBlockBlobOptions{ + _, err := azblob.UploadStreamToBlockBlob(bh.ctx, reader, blockBlobURL, azblob.UploadStreamToBlockBlobOptions{ BufferSize: azblob.BlockBlobMaxStageBlockBytes, MaxBuffers: *azBlobParallelism, }) @@ -175,6 +177,10 @@ func (bh *AZBlobBackupHandle) AbortBackup(ctx context.Context) error { if bh.readOnly { return fmt.Errorf("AbortBackup cannot be called on read-only backup") } + // Cancel the context of any uploads. + bh.cancel() + + // Remove the backup return bh.bs.RemoveBackup(ctx, bh.dir, bh.name) } @@ -252,11 +258,14 @@ func (bs *AZBlobBackupStorage) ListBackups(ctx context.Context, dir string) ([]b } for _, subdir := range subdirs { + cancelableCtx, cancel := context.WithCancel(ctx) result = append(result, &AZBlobBackupHandle{ bs: bs, dir: strings.Join([]string{dir, subdir}, "/"), name: subdir, readOnly: true, + ctx: cancelableCtx, + cancel: cancel, }) } @@ -265,11 +274,14 @@ func (bs *AZBlobBackupStorage) ListBackups(ctx context.Context, dir string) ([]b // StartBackup implements BackupStorage. func (bs *AZBlobBackupStorage) StartBackup(ctx context.Context, dir, name string) (backupstorage.BackupHandle, error) { + cancelableCtx, cancel := context.WithCancel(ctx) return &AZBlobBackupHandle{ bs: bs, dir: dir, name: name, readOnly: false, + ctx: cancelableCtx, + cancel: cancel, }, nil } From 8e728d4e443baf702b64ed694d01b5a275269d55 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 18 Mar 2020 19:25:20 -0700 Subject: [PATCH 327/825] deprecation: fix tabletserver endtoend tests Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/endtoend/compatibility_test.go | 84 ---------- go/vt/vttablet/endtoend/config_test.go | 100 ------------ go/vt/vttablet/endtoend/misc_test.go | 22 +-- go/vt/vttablet/endtoend/queries_test.go | 148 +++++------------- go/vt/vttablet/endtoend/transaction_test.go | 29 ++-- 5 files changed, 58 insertions(+), 325 deletions(-) diff --git a/go/vt/vttablet/endtoend/compatibility_test.go b/go/vt/vttablet/endtoend/compatibility_test.go index e5837eda5b2..fa4180659aa 100644 --- a/go/vt/vttablet/endtoend/compatibility_test.go +++ b/go/vt/vttablet/endtoend/compatibility_test.go @@ -657,90 +657,6 @@ func TestNull(t *testing.T) { } } -func TestTypeLimits(t *testing.T) { - client := framework.NewClient() - defer func() { - for _, cleanup := range []string{ - "delete from vitess_ints", - "delete from vitess_fracts", - "delete from vitess_strings", - } { - _, err := client.Execute(cleanup, nil) - if err != nil { - t.Error(err) - } - } - }() - - for _, query := range []string{ - "insert into vitess_ints(tiny, medium) values(1, -129)", - "insert into vitess_fracts(id, num) values(1, 1)", - "insert into vitess_strings(vb) values('a')", - } { - _, err := client.Execute(query, nil) - if err != nil { - t.Fatal(err) - } - } - - mismatchCases := []struct { - query string - bv map[string]*querypb.BindVariable - out string - }{{ - query: "insert into vitess_ints(tiny) values('str')", - bv: nil, - out: "strconv.ParseInt", - }, { - query: "insert into vitess_ints(tiny) values(:str)", - bv: map[string]*querypb.BindVariable{"str": sqltypes.StringBindVariable("str")}, - out: "strconv.ParseInt", - }, { - query: "insert into vitess_ints(tiny) values(1.2)", - bv: nil, - out: "Duplicate entry '1' for key 'PRIMARY'", - }, { - query: "insert into vitess_ints(tiny) values(:fl)", - bv: map[string]*querypb.BindVariable{"fl": sqltypes.Float64BindVariable(1.2)}, - out: "invalid syntax", - }, { - query: "insert into vitess_strings(vb) select tiny from vitess_ints", - bv: nil, - out: "type mismatch", - }, { - query: "insert into vitess_ints(tiny) select num from vitess_fracts", - bv: nil, - out: "type mismatch", - }, { - query: "insert into vitess_ints(tiny) select vb from vitess_strings", - bv: nil, - out: "type mismatch", - }} - for _, tcase := range mismatchCases { - _, err := client.Execute(tcase.query, tcase.bv) - if err == nil || !strings.Contains(err.Error(), tcase.out) { - t.Errorf("Error(%s): %v, want %s", tcase.query, err, tcase.out) - } - } - - want := "Out of range" - for _, query := range []string{ - "insert into vitess_ints(tiny) values(-129)", - "insert into vitess_ints(tiny) select medium from vitess_ints", - } { - _, err := client.Execute(query, nil) - if err == nil || !strings.HasPrefix(err.Error(), want) { - t.Errorf("Error(%s): %v, want %s", query, err, want) - } - } - - want = "Data too long" - _, err := client.Execute("insert into vitess_strings(vb) values('12345678901234567')", nil) - if err == nil || !strings.HasPrefix(err.Error(), want) { - t.Errorf("Error: %v, want %s", err, want) - } -} - func TestJSONType(t *testing.T) { // JSON is supported only after mysql57. client := framework.NewClient() diff --git a/go/vt/vttablet/endtoend/config_test.go b/go/vt/vttablet/endtoend/config_test.go index 507ae7ea2b6..b83000074e2 100644 --- a/go/vt/vttablet/endtoend/config_test.go +++ b/go/vt/vttablet/endtoend/config_test.go @@ -360,106 +360,6 @@ func TestWarnResultSize(t *testing.T) { } } -func TestMaxDMLRows(t *testing.T) { - client := framework.NewClient() - _, err := client.Execute( - "insert into vitess_a(eid, id, name, foo) values "+ - "(3, 1, '', ''), (3, 2, '', ''), (3, 3, '', '')", - nil, - ) - if err != nil { - t.Error(err) - return - } - catcher := framework.NewQueryCatcher() - defer catcher.Close() - - // Verify all three rows are updated in a single DML. - _, err = client.Execute("update vitess_a set foo='fghi' where eid = 3", nil) - if err != nil { - t.Error(err) - return - } - queryInfo, err := catcher.Next() - if err != nil { - t.Error(err) - return - } - want := "begin; " + - "select eid, id from vitess_a where eid = 3 limit 10001 for update; " + - "update vitess_a set foo = 'fghi' where " + - "(eid = 3 and id = 1) or (eid = 3 and id = 2) or (eid = 3 and id = 3); " + - "commit" - if queryInfo.RewrittenSQL() != want { - t.Errorf("Query info: \n%s, want \n%s", queryInfo.RewrittenSQL(), want) - } - - // Verify that rows get split, and if pk changes, those values are also - // split correctly. - defer framework.Server.SetMaxDMLRows(framework.Server.MaxDMLRows()) - framework.Server.SetMaxDMLRows(2) - _, err = client.Execute("update vitess_a set eid=2 where eid = 3", nil) - if err != nil { - t.Error(err) - return - } - queryInfo, err = catcher.Next() - if err != nil { - t.Error(err) - return - } - want = "begin; " + - "select eid, id from vitess_a where eid = 3 limit 10001 for update; " + - "update vitess_a set eid = 2 where " + - "(eid = 3 and id = 1) or (eid = 3 and id = 2); " + - "update vitess_a set eid = 2 where (eid = 3 and id = 3); " + - "commit" - if queryInfo.RewrittenSQL() != want { - t.Errorf("Query info: \n%s, want \n%s", queryInfo.RewrittenSQL(), want) - } - - // Verify that a normal update is split correctly. - _, err = client.Execute("update vitess_a set foo='fghi' where eid = 2", nil) - if err != nil { - t.Error(err) - return - } - queryInfo, err = catcher.Next() - if err != nil { - t.Error(err) - return - } - want = "begin; " + - "select eid, id from vitess_a where eid = 2 limit 10001 for update; " + - "update vitess_a set foo = 'fghi' where (eid = 2 and id = 1) or " + - "(eid = 2 and id = 2); " + - "update vitess_a set foo = 'fghi' where (eid = 2 and id = 3); " + - "commit" - if queryInfo.RewrittenSQL() != want { - t.Errorf("Query info: \n%s, want \n%s", queryInfo.RewrittenSQL(), want) - } - - // Verufy that a delete is split correctly. - _, err = client.Execute("delete from vitess_a where eid = 2", nil) - if err != nil { - t.Error(err) - return - } - queryInfo, err = catcher.Next() - if err != nil { - t.Error(err) - return - } - want = "begin; " + - "select eid, id from vitess_a where eid = 2 limit 10001 for update; " + - "delete from vitess_a where (eid = 2 and id = 1) or (eid = 2 and id = 2); " + - "delete from vitess_a where (eid = 2 and id = 3); " + - "commit" - if queryInfo.RewrittenSQL() != want { - t.Errorf("Query info: \n%s, want \n%s", queryInfo.RewrittenSQL(), want) - } -} - func TestQueryTimeout(t *testing.T) { vstart := framework.DebugVars() defer framework.Server.QueryTimeout.Set(framework.Server.QueryTimeout.Get()) diff --git a/go/vt/vttablet/endtoend/misc_test.go b/go/vt/vttablet/endtoend/misc_test.go index eac102015d5..39634869519 100644 --- a/go/vt/vttablet/endtoend/misc_test.go +++ b/go/vt/vttablet/endtoend/misc_test.go @@ -52,7 +52,7 @@ func TestSimpleRead(t *testing.T) { if err := compareIntDiff(vend, "Queries/TotalCount", vstart, 1); err != nil { t.Error(err) } - if err := compareIntDiff(vend, "Queries/Histograms/PASS_SELECT/Count", vstart, 1); err != nil { + if err := compareIntDiff(vend, "Queries/Histograms/Select/Count", vstart, 1); err != nil { t.Error(err) } } @@ -479,7 +479,7 @@ func TestQueryStats(t *testing.T) { want := framework.QueryStat{ Query: query, Table: "vitess_a", - Plan: "PASS_SELECT", + Plan: "Select", QueryCount: 1, RowCount: 2, ErrorCount: 0, @@ -497,7 +497,7 @@ func TestQueryStats(t *testing.T) { want = framework.QueryStat{ Query: query, Table: "vitess_a", - Plan: "PASS_SELECT", + Plan: "Select", QueryCount: 1, RowCount: 0, ErrorCount: 1, @@ -506,13 +506,13 @@ func TestQueryStats(t *testing.T) { t.Errorf("stat: %+v, want %+v", stat, want) } vend := framework.DebugVars() - if err := compareIntDiff(vend, "QueryCounts/vitess_a.PASS_SELECT", vstart, 2); err != nil { + if err := compareIntDiff(vend, "QueryCounts/vitess_a.Select", vstart, 2); err != nil { t.Error(err) } - if err := compareIntDiff(vend, "QueryRowCounts/vitess_a.PASS_SELECT", vstart, 2); err != nil { + if err := compareIntDiff(vend, "QueryRowCounts/vitess_a.Select", vstart, 2); err != nil { t.Error(err) } - if err := compareIntDiff(vend, "QueryErrorCounts/vitess_a.PASS_SELECT", vstart, 1); err != nil { + if err := compareIntDiff(vend, "QueryErrorCounts/vitess_a.Select", vstart, 1); err != nil { t.Error(err) } @@ -607,7 +607,7 @@ func TestLogTruncation(t *testing.T) { "insert into vitess_test values(123, null, :data, null)", map[string]*querypb.BindVariable{"data": sqltypes.StringBindVariable("THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED")}, ) - wantLog := `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null)", BindVars: {#maxLimit: "type:INT64 value:\"10001\" "data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\" "}` + wantLog := `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null)", BindVars: {data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\" "}` wantErr := wantLog if err == nil { t.Errorf("query unexpectedly succeeded") @@ -626,8 +626,8 @@ func TestLogTruncation(t *testing.T) { "insert into vitess_test values(123, null, :data, null)", map[string]*querypb.BindVariable{"data": sqltypes.StringBindVariable("THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED")}, ) - wantLog = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess [TRUNCATED]", BindVars: {#maxLim [TRUNCATED]` - wantErr = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null)", BindVars: {#maxLimit: "type:INT64 value:\"10001\" "data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\" "}` + wantLog = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess [TRUNCATED]", BindVars: {data: " [TRUNCATED]` + wantErr = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null)", BindVars: {data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\" "}` if err == nil { t.Errorf("query unexpectedly succeeded") } @@ -644,8 +644,8 @@ func TestLogTruncation(t *testing.T) { "insert into vitess_test values(123, null, :data, null) /* KEEP ME */", map[string]*querypb.BindVariable{"data": sqltypes.StringBindVariable("THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED")}, ) - wantLog = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess [TRUNCATED] /* KEEP ME */", BindVars: {#maxLim [TRUNCATED]` - wantErr = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null) /* KEEP ME */", BindVars: {#maxLimit: "type:INT64 value:\"10001\" "data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\" "}` + wantLog = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess [TRUNCATED] /* KEEP ME */", BindVars: {data: " [TRUNCATED]` + wantErr = `Data too long for column 'charval' at row 1 (errno 1406) (sqlstate 22001) (CallerID: dev): Sql: "insert into vitess_test values(123, null, :data, null) /* KEEP ME */", BindVars: {data: "type:VARCHAR value:\"THIS IS A LONG LONG LONG LONG QUERY STRING THAT SHOULD BE SHORTENED\" "}` if err == nil { t.Errorf("query unexpectedly succeeded") } diff --git a/go/vt/vttablet/endtoend/queries_test.go b/go/vt/vttablet/endtoend/queries_test.go index d9287359925..1cd00a795dc 100644 --- a/go/vt/vttablet/endtoend/queries_test.go +++ b/go/vt/vttablet/endtoend/queries_test.go @@ -33,7 +33,7 @@ RowsAffected mismatch: 2, want 1 Rewritten mismatch: '["select eid, id from vitess_a where 1 != 1 union select eid, id from vitess_b where 1 != 1" "select /* fail */ eid, id from vitess_a union select eid, id from vitess_b limit 10001"]' does not match '["select eid id from vitess_a where 1 != 1 union select eid, id from vitess_b where 1 != 1" "select /* fail */ eid, id from vitess_a union select eid, id from vitess_b"]' -Plan mismatch: PASS_SELECT, want aa` +Plan mismatch: Select, want aa` func TestTheFramework(t *testing.T) { client := framework.NewClient() @@ -59,7 +59,8 @@ func TestTheFramework(t *testing.T) { } } -// TODO(sougou): break this up into smaller parts. +// Most of these tests are not really needed because the queries are mostly pass-through. +// They're left as is because they still demonstrate the variety of constructs being supported. func TestQueries(t *testing.T) { client := framework.NewClient() @@ -478,7 +479,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert /* simple */ into vitess_a values (2, 1, 'aaaa', 'bbbb')", Rewritten: []string{ - "insert /* simple */ into vitess_a(eid, id, name, foo) values (2, 1, 'aaaa', 'bbbb')", + "insert /* simple */ into vitess_a values (2, 1, 'aaaa', 'bbbb')", }, RowsAffected: 1, }, @@ -501,7 +502,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert /* simple */ ignore into vitess_a values (2, 1, 'aaaa', 'bbbb')", Rewritten: []string{ - "insert /* simple */ ignore into vitess_a(eid, id, name, foo) values (2, 1, 'aaaa', 'bbbb')", + "insert /* simple */ ignore into vitess_a values (2, 1, 'aaaa', 'bbbb')", }, RowsAffected: 1, }, @@ -516,7 +517,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert /* simple */ ignore into vitess_a values (2, 1, 'cccc', 'cccc')", Rewritten: []string{ - "insert /* simple */ ignore into vitess_a(eid, id, name, foo) values (2, 1, 'cccc', 'cccc')", + "insert /* simple */ ignore into vitess_a values (2, 1, 'cccc', 'cccc')", }, }, framework.TestQuery("commit"), @@ -759,8 +760,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert /* subquery */ into vitess_a(eid, name, foo) select eid, name, foo from vitess_c", Rewritten: []string{ - "select eid, name, foo from vitess_c limit 10001", - "insert /* subquery */ into vitess_a(eid, name, foo) values (10, 'abcd', '20'), (11, 'bcde', '30')", + "insert /* subquery */ into vitess_a(eid, name, foo) select eid, name, foo from vitess_c", }, RowsAffected: 2, }, @@ -777,8 +777,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert into vitess_e(id, name, foo) select eid, name, foo from vitess_c", Rewritten: []string{ - "select eid, name, foo from vitess_c limit 10001", - "insert into vitess_e(id, name, foo) values (10, 'abcd', '20'), (11, 'bcde', '30')", + "insert into vitess_e(id, name, foo) select eid, name, foo from vitess_c", }, RowsAffected: 2, }, @@ -796,22 +795,6 @@ func TestQueries(t *testing.T) { framework.TestQuery("commit"), }, }, - &framework.MultiCase{ - Name: "reorganize partition with bindvar", - Cases: []framework.Testable{ - framework.TestQuery("begin"), - &framework.TestCase{ - Query: "alter table vitess_part reorganize partition p1 into (partition p2 values less than (:bv), partition p3 values less than (maxvalue))", - BindVars: map[string]*querypb.BindVariable{ - "bv": sqltypes.Int64BindVariable(1000), - }, - Rewritten: []string{ - "alter table vitess_part reorganize partition p1 into (partition p2 values less than (1000), partition p3 values less than (maxvalue))", - }, - }, - framework.TestQuery("commit"), - }, - }, &framework.MultiCase{ Name: "multi-value", Cases: []framework.Testable{ @@ -995,7 +978,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update /* pk */ vitess_a set foo='bar' where eid = 1 and id = 1", Rewritten: []string{ - "update /* pk */ vitess_a set foo = 'bar' where (eid = 1 and id = 1)", + "update /* pk */ vitess_a set foo = 'bar' where eid = 1 and id = 1 limit 10001", }, RowsAffected: 1, }, @@ -1018,7 +1001,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update /* pk */ vitess_a set foo='bar' where eid = 1 and id in (1, 2)", Rewritten: []string{ - "update /* pk */ vitess_a set foo = 'bar' where (eid = 1 and id = 1) or (eid = 1 and id = 2)", + "update /* pk */ vitess_a set foo = 'bar' where eid = 1 and id in (1, 2) limit 10001", }, RowsAffected: 2, }, @@ -1042,8 +1025,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update /* pk */ vitess_a set foo='bar' where eid in (1) and id in (1, 2)", Rewritten: []string{ - "select eid, id from vitess_a where eid in (1) and id in (1, 2) limit 10001 for update", - "update /* pk */ vitess_a set foo = 'bar' where (eid = 1 and id = 1) or (eid = 1 and id = 2)", + "update /* pk */ vitess_a set foo = 'bar' where eid in (1) and id in (1, 2) limit 10001", }, RowsAffected: 2, }, @@ -1067,8 +1049,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update /* pk */ vitess_a set foo='bar' where eid in (1, 2) and id in (1, 2)", Rewritten: []string{ - "select eid, id from vitess_a where eid in (1, 2) and id in (1, 2) limit 10001 for update", - "update /* pk */ vitess_a set foo = 'bar' where (eid = 1 and id = 1) or (eid = 1 and id = 2)", + "update /* pk */ vitess_a set foo = 'bar' where eid in (1, 2) and id in (1, 2) limit 10001", }, RowsAffected: 2, }, @@ -1092,7 +1073,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update vitess_a set eid = 2 where eid = 1 and id = 1", Rewritten: []string{ - "update vitess_a set eid = 2 where (eid = 1 and id = 1)", + "update vitess_a set eid = 2 where eid = 1 and id = 1 limit 10001", }, RowsAffected: 1, }, @@ -1115,8 +1096,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update /* pk */ vitess_a set foo='bar' where id = 1", Rewritten: []string{ - "select eid, id from vitess_a where id = 1 limit 10001 for update", - "update /* pk */ vitess_a set foo = 'bar' where (eid = 1 and id = 1)", + "update /* pk */ vitess_a set foo = 'bar' where id = 1 limit 10001", }, RowsAffected: 1, }, @@ -1139,8 +1119,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update /* pk */ vitess_a set foo='bar' where eid = 1 limit 1", Rewritten: []string{ - "select eid, id from vitess_a where eid = 1 limit 1 for update", - "update /* pk */ vitess_a set foo = 'bar' where (eid = 1 and id = 1)", + "update /* pk */ vitess_a set foo = 'bar' where eid = 1 limit 1", }, RowsAffected: 1, }, @@ -1163,8 +1142,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update /* pk */ vitess_a set foo='bar' where eid = 1 order by id desc limit 1", Rewritten: []string{ - "select eid, id from vitess_a where eid = 1 order by id desc limit 1 for update", - "update /* pk */ vitess_a set foo = 'bar' where (eid = 1 and id = 2) order by id desc", + "update /* pk */ vitess_a set foo = 'bar' where eid = 1 order by id desc limit 1", }, RowsAffected: 1, }, @@ -1187,8 +1165,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update vitess_a set foo='bar'", Rewritten: []string{ - "select eid, id from vitess_a limit 10001 for update", - "update vitess_a set foo = 'bar' where (eid = 1 and id = 1) or (eid = 1 and id = 2)", + "update vitess_a set foo = 'bar' limit 10001", }, RowsAffected: 2, }, @@ -1216,7 +1193,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update vitess_f set id=2 where vb='a'", Rewritten: []string{ - "update vitess_f set id = 2 where vb in ('a')", + "update vitess_f set id = 2 where vb = 'a' limit 10001", }, RowsAffected: 1, }, @@ -1243,7 +1220,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update vitess_f set id=3 where vb in ('a', 'b')", Rewritten: []string{ - "update vitess_f set id = 3 where vb in ('a', 'b')", + "update vitess_f set id = 3 where vb in ('a', 'b') limit 10001", }, RowsAffected: 2, }, @@ -1270,8 +1247,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update vitess_f set id=4 where id >= 0", Rewritten: []string{ - "select vb from vitess_f where id >= 0 limit 10001 for update", - "update vitess_f set id = 4 where vb in ('a', 'b')", + "update vitess_f set id = 4 where id >= 0 limit 10001", }, RowsAffected: 2, }, @@ -1298,7 +1274,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update vitess_f set id=4 where id < 0", Rewritten: []string{ - "select vb from vitess_f where id < 0 limit 10001 for update", + "update vitess_f set id = 4 where id < 0 limit 10001", }, }, framework.TestQuery("commit"), @@ -1322,7 +1298,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete /* pk */ from vitess_a where eid = 2 and id = 1", Rewritten: []string{ - "delete /* pk */ from vitess_a where (eid = 2 and id = 1)", + "delete /* pk */ from vitess_a where eid = 2 and id = 1 limit 10001", }, RowsAffected: 1, }, @@ -1340,7 +1316,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete /* pk */ from vitess_a where eid = 2 and id in (1, 2)", Rewritten: []string{ - "delete /* pk */ from vitess_a where (eid = 2 and id = 1) or (eid = 2 and id = 2)", + "delete /* pk */ from vitess_a where eid = 2 and id in (1, 2) limit 10001", }, RowsAffected: 1, }, @@ -1358,8 +1334,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete /* pk */ from vitess_a where eid in (2) and id in (1, 2)", Rewritten: []string{ - "select eid, id from vitess_a where eid in (2) and id in (1, 2) limit 10001 for update", - "delete /* pk */ from vitess_a where (eid = 2 and id = 1)", + "delete /* pk */ from vitess_a where eid in (2) and id in (1, 2) limit 10001", }, RowsAffected: 1, }, @@ -1377,8 +1352,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete /* pk */ from vitess_a where eid in (2, 3) and id in (1, 2)", Rewritten: []string{ - "select eid, id from vitess_a where eid in (2, 3) and id in (1, 2) limit 10001 for update", - "delete /* pk */ from vitess_a where (eid = 2 and id = 1)", + "delete /* pk */ from vitess_a where eid in (2, 3) and id in (1, 2) limit 10001", }, RowsAffected: 1, }, @@ -1396,8 +1370,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete from vitess_a where eid = 1+1 and id = 1", Rewritten: []string{ - "select eid, id from vitess_a where eid = 1 + 1 and id = 1 limit 10001 for update", - "delete from vitess_a where (eid = 2 and id = 1)", + "delete from vitess_a where eid = 1 + 1 and id = 1 limit 10001", }, RowsAffected: 1, }, @@ -1415,8 +1388,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete from vitess_a where eid = 2", Rewritten: []string{ - "select eid, id from vitess_a where eid = 2 limit 10001 for update", - "delete from vitess_a where (eid = 2 and id = 1)", + "delete from vitess_a where eid = 2 limit 10001", }, RowsAffected: 1, }, @@ -1434,8 +1406,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete from vitess_a where eid = 2 limit 1", Rewritten: []string{ - "select eid, id from vitess_a where eid = 2 limit 1 for update", - "delete from vitess_a where (eid = 2 and id = 1)", + "delete from vitess_a where eid = 2 limit 1", }, RowsAffected: 1, }, @@ -1454,8 +1425,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "delete from vitess_a where eid = 2 order by id desc", Rewritten: []string{ - "select eid, id from vitess_a where eid = 2 order by id desc limit 10001 for update", - "delete from vitess_a where (eid = 2 and id = 2) or (eid = 2 and id = 1) order by id desc", + "delete from vitess_a where eid = 2 order by id desc limit 10001", }, RowsAffected: 2, }, @@ -1485,7 +1455,7 @@ func TestQueries(t *testing.T) { "mediumu": sqltypes.Int64BindVariable(16777215), }, Rewritten: []string{ - "insert into vitess_ints(tiny, tinyu, small, smallu, medium, mediumu, normal, normalu, big, bigu, y) values (-128, 255, -32768, 65535, -8388608, 16777215, -2147483648, 4294967295, -9223372036854775808, 18446744073709551615, 2012)", + "insert into vitess_ints values (-128, 255, -32768, 65535, -8388608, 16777215, -2147483648, 4294967295, -9223372036854775808, 18446744073709551615, 2012)", }, }, framework.TestQuery("commit"), @@ -1512,8 +1482,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert into vitess_ints select 2, tinyu, small, smallu, medium, mediumu, normal, normalu, big, bigu, y from vitess_ints", Rewritten: []string{ - "select 2, tinyu, small, smallu, medium, mediumu, normal, normalu, big, bigu, y from vitess_ints limit 10001", - "insert into vitess_ints(tiny, tinyu, small, smallu, medium, mediumu, normal, normalu, big, bigu, y) values (2, 255, -32768, 65535, -8388608, 16777215, -2147483648, 4294967295, -9223372036854775808, 18446744073709551615, 2012)", + "insert into vitess_ints select 2, tinyu, small, smallu, medium, mediumu, normal, normalu, big, bigu, y from vitess_ints", }, }, framework.TestQuery("commit"), @@ -1536,7 +1505,7 @@ func TestQueries(t *testing.T) { "deci": sqltypes.StringBindVariable("1.99"), }, Rewritten: []string{ - "insert into vitess_fracts(id, deci, num, f, d) values (1, '1.99', '2.99', 3.99, 4.99)", + "insert into vitess_fracts values (1, '1.99', '2.99', 3.99, 4.99)", }, }, framework.TestQuery("commit"), @@ -1563,8 +1532,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert into vitess_fracts select 2, deci, num, f, d from vitess_fracts", Rewritten: []string{ - "select 2, deci, num, f, d from vitess_fracts limit 10001", - "insert into vitess_fracts(id, deci, num, f, d) values (2, 1.99, 2.99, 3.99, 4.99)", + "insert into vitess_fracts select 2, deci, num, f, d from vitess_fracts", }, }, framework.TestQuery("commit"), @@ -1592,7 +1560,7 @@ func TestQueries(t *testing.T) { "c": sqltypes.StringBindVariable("b"), }, Rewritten: []string{ - "insert into vitess_strings(vb, c, vc, b, tb, bl, ttx, tx, en, s) values ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'a,b')", + "insert into vitess_strings values ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'a,b')", }, }, framework.TestQuery("commit"), @@ -1619,8 +1587,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "insert into vitess_strings select 'b', c, vc, b, tb, bl, ttx, tx, en, s from vitess_strings", Rewritten: []string{ - "select 'b', c, vc, b, tb, bl, ttx, tx, en, s from vitess_strings limit 10001", - "insert into vitess_strings(vb, c, vc, b, tb, bl, ttx, tx, en, s) values ('b', 'b', 'c', 'd\\0\\0\\0', 'e', 'f', 'g', 'h', 'a', 'a,b')", + "insert into vitess_strings select 'b', c, vc, b, tb, bl, ttx, tx, en, s from vitess_strings", }, }, framework.TestQuery("commit"), @@ -1643,7 +1610,7 @@ func TestQueries(t *testing.T) { "d": sqltypes.StringBindVariable("2012-01-01"), }, Rewritten: []string{ - "insert into vitess_misc(id, b, d, dt, t, g) values (1, '\x01', '2012-01-01', '2012-01-01 15:45:45', '15:45:45', point(1, 2))", + "insert into vitess_misc values (1, '\x01', '2012-01-01', '2012-01-01 15:45:45', '15:45:45', point(1, 2))", }, }, framework.TestQuery("commit"), @@ -1671,8 +1638,7 @@ func TestQueries(t *testing.T) { // Skip geometry test. The binary representation is non-trivial to represent as go string. Query: "insert into vitess_misc(id, b, d, dt, t) select 2, b, d, dt, t from vitess_misc", Rewritten: []string{ - "select 2, b, d, dt, t from vitess_misc limit 10001", - "insert into vitess_misc(id, b, d, dt, t) values (2, b'00000001', '2012-01-01', '2012-01-01 15:45:45', '15:45:45')", + "insert into vitess_misc(id, b, d, dt, t) select 2, b, d, dt, t from vitess_misc", }, }, framework.TestQuery("commit"), @@ -1752,8 +1718,7 @@ func TestQueries(t *testing.T) { &framework.TestCase{ Query: "update vitess_bool set sval = 'test' where bval is false or ival = 23", Rewritten: []string{ - "select auto from vitess_bool where bval is false or ival = 23 limit 10001 for update", - "update vitess_bool set sval = 'test' where auto in (1, 2, 6, 7, 8, 9)", + "update vitess_bool set sval = 'test' where bval is false or ival = 23 limit 10001", }, RowsAffected: 6, }, @@ -1821,40 +1786,3 @@ func TestQueries(t *testing.T) { } } } - -func TestBitDefault(t *testing.T) { - // Default values for bit fields that are PKs are not supported - // Does not make sense to use a bit field as PK - client := framework.NewClient() - - expectedError := "bit default value: Execute failed: could not create default row for insert without row values: cannot convert value BIT(\"\\x05\") to AST (CallerID: dev)" - testCases := []framework.Testable{ - &framework.MultiCase{ - Name: "bit default value", - Cases: []framework.Testable{ - framework.TestQuery("begin"), - &framework.TestCase{ - Query: "insert into vitess_bit_default values()", - Rewritten: []string{ - "insert into vitess_bit_default(id) values ('\x05')", - }, - RowsAffected: 1, - }, - framework.TestQuery("commit"), - &framework.TestCase{ - Query: "select hex(id) from vitess_bit_default", - Result: [][]string{ - {"5"}, - }, - RowsAffected: 1, - }, - }, - }, - } - for _, tcase := range testCases { - err := tcase.Test("", client) - if err == nil || err.Error() != expectedError { - t.Errorf("TestBitDefault result: \n%q\nexpecting\n%q", err.Error(), expectedError) - } - } -} diff --git a/go/vt/vttablet/endtoend/transaction_test.go b/go/vt/vttablet/endtoend/transaction_test.go index eecb7d74526..78e811bb574 100644 --- a/go/vt/vttablet/endtoend/transaction_test.go +++ b/go/vt/vttablet/endtoend/transaction_test.go @@ -119,13 +119,13 @@ func TestCommit(t *testing.T) { tag: "Queries/Histograms/COMMIT/Count", diff: 1, }, { - tag: "Queries/Histograms/INSERT_PK/Count", + tag: "Queries/Histograms/Insert/Count", diff: 1, }, { - tag: "Queries/Histograms/DML_PK/Count", + tag: "Queries/Histograms/DeleteLimit/Count", diff: 1, }, { - tag: "Queries/Histograms/PASS_SELECT/Count", + tag: "Queries/Histograms/Select/Count", diff: 2, }} vend := framework.DebugVars() @@ -164,7 +164,7 @@ func TestRollback(t *testing.T) { t.Error(err) return } - want := []string{"insert into vitess_test(intval, floatval, charval, binval) values (4, null, null, null)"} + want := []string{"insert into vitess_test values (4, null, null, null)"} if !reflect.DeepEqual(tx.Queries, want) { t.Errorf("queries: %v, want %v", tx.Queries, want) } @@ -197,7 +197,7 @@ func TestRollback(t *testing.T) { tag: "Queries/Histograms/ROLLBACK/Count", diff: 1, }, { - tag: "Queries/Histograms/INSERT_PK/Count", + tag: "Queries/Histograms/Insert/Count", diff: 1, }} vend := framework.DebugVars() @@ -282,13 +282,13 @@ func TestAutoCommit(t *testing.T) { tag: "Queries/Histograms/COMMIT/Count", diff: 0, }, { - tag: "Queries/Histograms/INSERT_PK/Count", + tag: "Queries/Histograms/Insert/Count", diff: 1, }, { - tag: "Queries/Histograms/DML_PK/Count", + tag: "Queries/Histograms/DeleteLimit/Count", diff: 1, }, { - tag: "Queries/Histograms/PASS_SELECT/Count", + tag: "Queries/Histograms/Select/Count", diff: 2, }} vend := framework.DebugVars() @@ -303,17 +303,6 @@ func TestAutoCommit(t *testing.T) { } } -func TestAutoCommitOff(t *testing.T) { - framework.Server.SetAutoCommit(false) - defer framework.Server.SetAutoCommit(true) - - _, err := framework.NewClient().Execute("insert into vitess_test values(4, null, null, null)", nil) - want := "INSERT_PK disallowed outside transaction" - if err == nil || !strings.HasPrefix(err.Error(), want) { - t.Errorf("%v, must start with %s", err, want) - } -} - func TestTxPoolSize(t *testing.T) { vstart := framework.DebugVars() @@ -398,7 +387,7 @@ func TestForUpdate(t *testing.T) { client := framework.NewClient() query := fmt.Sprintf("select * from vitess_test where intval=2 %s", mode) _, err := client.Execute(query, nil) - want := "SELECT_LOCK disallowed outside transaction" + want := "SelectLock disallowed outside transaction" if err == nil || !strings.HasPrefix(err.Error(), want) { t.Errorf("%v, must have prefix %s", err, want) } From f8ef3e64e8322972f0cc85eda9a406b584676971 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 19 Mar 2020 08:08:10 +0100 Subject: [PATCH 328/825] Support for user defined variables (#5921) * Support set statement with user defined variables Signed-off-by: Harshit Gangal * Add needed bind variables Signed-off-by: Andres Taylor * Changes the session UDV holder to use BindVariable instead of query.Value Signed-off-by: Andres Taylor * Make sure to allow float values for UDVs Signed-off-by: Andres Taylor * added udv set end2end Signed-off-by: Harshit Gangal * Allow @ in sql identifiers Signed-off-by: Andres Taylor * Make sure to allow variable names that contain characters that are not letters or digits Signed-off-by: Andres Taylor * Improved user defined variable testing Signed-off-by: Andres Taylor * Minor clean ups Signed-off-by: Andres Taylor * Make sure to go over all variables in the SET statement Signed-off-by: Andres Taylor Co-authored-by: Harshit Gangal --- .../endtoend/vtgate/setstatement/udv_test.go | 163 + go/vt/proto/vtgate/vtgate.pb.go | 248 +- go/vt/sqlparser/analyzer.go | 49 +- go/vt/sqlparser/analyzer_test.go | 50 +- go/vt/sqlparser/ast.go | 12 +- go/vt/sqlparser/ast_funcs.go | 46 +- go/vt/sqlparser/ast_test.go | 2 +- go/vt/sqlparser/constants.go | 1 + go/vt/sqlparser/expression_rewriting.go | 24 +- go/vt/sqlparser/expression_rewriting_test.go | 53 +- go/vt/sqlparser/parse_test.go | 17 +- go/vt/sqlparser/sql.go | 7618 ++++++++--------- go/vt/sqlparser/sql.y | 110 +- go/vt/sqlparser/token.go | 35 +- go/vt/sqlparser/token_test.go | 16 + go/vt/vtgate/executor.go | 412 +- go/vt/vtgate/executor_select_test.go | 18 + go/vt/vtgate/executor_set_test.go | 340 + go/vt/vtgate/executor_test.go | 285 +- go/vt/vtgate/safe_session.go | 10 + proto/vtgate.proto | 3 + 21 files changed, 4971 insertions(+), 4541 deletions(-) create mode 100644 go/test/endtoend/vtgate/setstatement/udv_test.go create mode 100644 go/vt/vtgate/executor_set_test.go diff --git a/go/test/endtoend/vtgate/setstatement/udv_test.go b/go/test/endtoend/vtgate/setstatement/udv_test.go new file mode 100644 index 00000000000..86baee4adce --- /dev/null +++ b/go/test/endtoend/vtgate/setstatement/udv_test.go @@ -0,0 +1,163 @@ +package setstatement + +import ( + "context" + "flag" + "fmt" + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + keyspaceName = "ks" + cell = "zone1" + hostname = "localhost" + sqlSchema = ` + create table test( + id bigint, + val1 varchar(16), + val2 int, + val3 float, + primary key(id) + )Engine=InnoDB;` + + vSchema = ` + { + "sharded":true, + "vindexes": { + "hash_index": { + "type": "hash" + } + }, + "tables": { + "test":{ + "column_vindexes": [ + { + "column": "id", + "name": "hash_index" + } + ] + } + } + } + ` +) + +func TestMain(m *testing.M) { + defer cluster.PanicHandler(nil) + flag.Parse() + + exitCode := func() int { + clusterInstance = cluster.NewCluster(cell, hostname) + defer clusterInstance.Teardown() + + // Start topo server + if err := clusterInstance.StartTopo(); err != nil { + return 1 + } + + // Start keyspace + keyspace := &cluster.Keyspace{ + Name: keyspaceName, + SchemaSQL: sqlSchema, + VSchema: vSchema, + } + if err := clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 1, false); err != nil { + return 1 + } + + // Start vtgate + if err := clusterInstance.StartVtgate(); err != nil { + return 1 + } + + return m.Run() + }() + os.Exit(exitCode) +} + +func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { + t.Helper() + qr, err := conn.ExecuteFetch(query, 1000, true) + require.Nil(t, err) + return qr +} + +func TestSet(t *testing.T) { + defer cluster.PanicHandler(t) + ctx := context.Background() + vtParams := mysql.ConnParams{ + Host: "localhost", + Port: clusterInstance.VtgateMySQLPort, + } + type queriesWithExpectations struct { + query string + expectedRows string + rowsAffected int + } + + queries := []queriesWithExpectations{{ + query: "set @foo = 'abc', @bar = 42, @baz = 30.5", + expectedRows: "", rowsAffected: 0, + }, { + query: "select @foo, @bar, @baz", + expectedRows: `[[VARCHAR("abc") INT64(42) DECIMAL(30.5)]]`, rowsAffected: 1, + }, { + query: "insert into test(id, val1, val2, val3) values(1, @foo, null, null), (2, null, @bar, null), (3, null, null, @baz)", + expectedRows: ``, rowsAffected: 3, + }, { + query: "select id, val1, val2, val3 from test order by id", + expectedRows: `[[INT64(1) VARCHAR("abc") NULL NULL] [INT64(2) NULL INT32(42) NULL] [INT64(3) NULL NULL FLOAT32(30.5)]]`, rowsAffected: 3, + }, { + query: "select id, val1 from test where val1=@foo", + expectedRows: `[[INT64(1) VARCHAR("abc")]]`, rowsAffected: 1, + }, { + query: "select id, val2 from test where val2=@bar", + expectedRows: `[[INT64(2) INT32(42)]]`, rowsAffected: 1, + }, { + query: "select id, val3 from test where val3=@baz", + expectedRows: `[[INT64(3) FLOAT32(30.5)]]`, rowsAffected: 1, + }, { + query: "delete from test where val2 = @bar", + expectedRows: ``, rowsAffected: 1, + }, { + query: "select id, val2 from test where val2=@bar", + expectedRows: ``, rowsAffected: 0, + }, { + query: "update test set val2 = @bar where val1 = @foo", + expectedRows: ``, rowsAffected: 1, + }, { + query: "select id, val1, val2 from test where val1=@foo", + expectedRows: `[[INT64(1) VARCHAR("abc") INT32(42)]]`, rowsAffected: 1, + }, { + query: "delete from test", + expectedRows: ``, rowsAffected: 2, + }} + + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + for i, q := range queries { + t.Run(fmt.Sprintf("%d-%s", i, q.query), func(t *testing.T) { + qr := exec(t, conn, q.query) + assert.Equal(t, uint64(q.rowsAffected), qr.RowsAffected, "rows affected wrong for query: %s", q.query) + if q.expectedRows != "" { + result := fmt.Sprintf("%v", qr.Rows) + if diff := cmp.Diff(q.expectedRows, result); diff != "" { + t.Errorf("%s\nfor query: %s", diff, q.query) + } + } + }) + } +} diff --git a/go/vt/proto/vtgate/vtgate.pb.go b/go/vt/proto/vtgate/vtgate.pb.go index 4f2b378f521..43f45d52c17 100644 --- a/go/vt/proto/vtgate/vtgate.pb.go +++ b/go/vt/proto/vtgate/vtgate.pb.go @@ -5,9 +5,8 @@ package vtgate import ( fmt "fmt" - math "math" - proto "github.com/golang/protobuf/proto" + math "math" binlogdata "vitess.io/vitess/go/vt/proto/binlogdata" query "vitess.io/vitess/go/vt/proto/query" topodata "vitess.io/vitess/go/vt/proto/topodata" @@ -139,10 +138,12 @@ type Session struct { // last_insert_id keeps track of the last seen insert_id for this session LastInsertId uint64 `protobuf:"varint,11,opt,name=last_insert_id,json=lastInsertId,proto3" json:"last_insert_id,omitempty"` // found_rows keeps track of how many rows the last query returned - FoundRows uint64 `protobuf:"varint,12,opt,name=found_rows,json=foundRows,proto3" json:"found_rows,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + FoundRows uint64 `protobuf:"varint,12,opt,name=found_rows,json=foundRows,proto3" json:"found_rows,omitempty"` + // user_defined_variables contains all the @variables defined for this session + UserDefinedVariables map[string]*query.BindVariable `protobuf:"bytes,13,rep,name=user_defined_variables,json=userDefinedVariables,proto3" json:"user_defined_variables,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Session) Reset() { *m = Session{} } @@ -254,6 +255,13 @@ func (m *Session) GetFoundRows() uint64 { return 0 } +func (m *Session) GetUserDefinedVariables() map[string]*query.BindVariable { + if m != nil { + return m.UserDefinedVariables + } + return nil +} + type Session_ShardSession struct { Target *query.Target `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` TransactionId int64 `protobuf:"varint,2,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` @@ -3111,6 +3119,7 @@ func init() { proto.RegisterEnum("vtgate.TransactionMode", TransactionMode_name, TransactionMode_value) proto.RegisterEnum("vtgate.CommitOrder", CommitOrder_name, CommitOrder_value) proto.RegisterType((*Session)(nil), "vtgate.Session") + proto.RegisterMapType((map[string]*query.BindVariable)(nil), "vtgate.Session.UserDefinedVariablesEntry") proto.RegisterType((*Session_ShardSession)(nil), "vtgate.Session.ShardSession") proto.RegisterType((*ExecuteRequest)(nil), "vtgate.ExecuteRequest") proto.RegisterType((*ExecuteResponse)(nil), "vtgate.ExecuteResponse") @@ -3160,115 +3169,120 @@ func init() { func init() { proto.RegisterFile("vtgate.proto", fileDescriptor_aab96496ceaf1ebb) } var fileDescriptor_aab96496ceaf1ebb = []byte{ - // 1756 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0x5b, 0x8f, 0x1b, 0x49, - 0x15, 0xde, 0x6e, 0xdf, 0x4f, 0xfb, 0x96, 0x8a, 0x93, 0xf5, 0x7a, 0x67, 0x13, 0xd3, 0xcb, 0x68, - 0x4d, 0x88, 0x6c, 0xd6, 0x2b, 0x56, 0x08, 0x2d, 0x42, 0x19, 0x67, 0x36, 0xb2, 0x36, 0x73, 0xa1, - 0xec, 0x24, 0x80, 0x40, 0x56, 0x8f, 0xbb, 0xd6, 0x69, 0xc6, 0xee, 0xf6, 0x76, 0x95, 0x1d, 0xe6, - 0x05, 0xed, 0x3f, 0x58, 0xf1, 0x80, 0x84, 0x56, 0x48, 0x08, 0x09, 0x89, 0x27, 0x5e, 0x91, 0x80, - 0x17, 0x9e, 0x79, 0x41, 0xfc, 0x05, 0x7e, 0x02, 0xbf, 0x60, 0xd5, 0x55, 0xd5, 0x17, 0x77, 0xc6, - 0x19, 0x8f, 0x9d, 0x19, 0x39, 0x2f, 0xa3, 0xae, 0x73, 0xea, 0x72, 0xce, 0x77, 0xbe, 0x3a, 0x75, - 0x5c, 0x35, 0x90, 0x9f, 0xb3, 0x91, 0xc1, 0x48, 0x73, 0xea, 0x3a, 0xcc, 0x41, 0x69, 0xd1, 0xaa, - 0x95, 0x4f, 0x2c, 0x7b, 0xec, 0x8c, 0x4c, 0x83, 0x19, 0x42, 0x53, 0xd3, 0xbe, 0x98, 0x11, 0xf7, - 0x4c, 0x36, 0x8a, 0xcc, 0x99, 0x3a, 0x51, 0xe5, 0x9c, 0xb9, 0xd3, 0xa1, 0x68, 0xe8, 0x7f, 0x4e, - 0x41, 0xa6, 0x47, 0x28, 0xb5, 0x1c, 0x1b, 0xed, 0x42, 0xd1, 0xb2, 0x07, 0xcc, 0x35, 0x6c, 0x6a, - 0x0c, 0x99, 0xe5, 0xd8, 0x55, 0xa5, 0xae, 0x34, 0xb2, 0xb8, 0x60, 0xd9, 0xfd, 0x50, 0x88, 0x3a, - 0x50, 0xa4, 0xcf, 0x0d, 0xd7, 0x1c, 0x50, 0x31, 0x8e, 0x56, 0xd5, 0x7a, 0xa2, 0xa1, 0xb5, 0x77, - 0x9a, 0xd2, 0x3a, 0x39, 0x5f, 0xb3, 0xe7, 0xf5, 0x92, 0x0d, 0x5c, 0xa0, 0x91, 0x16, 0x45, 0xef, - 0x42, 0x8e, 0x5a, 0xf6, 0x68, 0x4c, 0x06, 0xe6, 0x49, 0x35, 0xc1, 0x97, 0xc9, 0x0a, 0xc1, 0xc3, - 0x13, 0x74, 0x07, 0xc0, 0x98, 0x31, 0x67, 0xe8, 0x4c, 0x26, 0x16, 0xab, 0x26, 0xb9, 0x36, 0x22, - 0x41, 0xef, 0x43, 0x81, 0x19, 0xee, 0x88, 0xb0, 0x01, 0x65, 0xae, 0x65, 0x8f, 0xaa, 0xa9, 0xba, - 0xd2, 0xc8, 0xe1, 0xbc, 0x10, 0xf6, 0xb8, 0x0c, 0xb5, 0x20, 0xe3, 0x4c, 0x19, 0xb7, 0x2f, 0x5d, - 0x57, 0x1a, 0x5a, 0xfb, 0x56, 0x53, 0xa0, 0xb2, 0xff, 0x6b, 0x32, 0x9c, 0x31, 0x72, 0x24, 0x94, - 0xd8, 0xef, 0x85, 0xf6, 0xa0, 0x1c, 0xf1, 0x7d, 0x30, 0x71, 0x4c, 0x52, 0xcd, 0xd4, 0x95, 0x46, - 0xb1, 0xfd, 0xb6, 0xef, 0x59, 0x04, 0x86, 0x03, 0xc7, 0x24, 0xb8, 0xc4, 0x16, 0x05, 0xa8, 0x05, - 0xd9, 0x17, 0x86, 0x6b, 0x5b, 0xf6, 0x88, 0x56, 0xb3, 0x1c, 0x95, 0x9b, 0x72, 0xd5, 0x9f, 0x78, - 0x7f, 0x9f, 0x09, 0x1d, 0x0e, 0x3a, 0xa1, 0x1f, 0x43, 0x7e, 0xea, 0x92, 0x10, 0xca, 0xdc, 0x0a, - 0x50, 0x6a, 0x53, 0x97, 0x04, 0x40, 0x3e, 0x80, 0xc2, 0xd4, 0xa1, 0x2c, 0x9c, 0x01, 0x56, 0x98, - 0x21, 0xef, 0x0d, 0x09, 0xa6, 0xf8, 0x36, 0x14, 0xc7, 0x06, 0x65, 0x03, 0xcb, 0xa6, 0xc4, 0x65, - 0x03, 0xcb, 0xac, 0x6a, 0x75, 0xa5, 0x91, 0xc4, 0x79, 0x4f, 0xda, 0xe5, 0xc2, 0xae, 0x89, 0xde, - 0x03, 0xf8, 0xdc, 0x99, 0xd9, 0xe6, 0xc0, 0x75, 0x5e, 0xd0, 0x6a, 0x9e, 0xf7, 0xc8, 0x71, 0x09, - 0x76, 0x5e, 0xd0, 0xda, 0x2f, 0x20, 0x1f, 0x5d, 0x02, 0xed, 0x42, 0x5a, 0x84, 0x83, 0x93, 0x48, - 0x6b, 0x17, 0x24, 0x0e, 0x7d, 0x2e, 0xc4, 0x52, 0xe9, 0x71, 0x2e, 0x0a, 0xba, 0x65, 0x56, 0xd5, - 0xba, 0xd2, 0x48, 0xe0, 0x42, 0x44, 0xda, 0x35, 0xf5, 0xff, 0xa8, 0x50, 0x94, 0x71, 0xc3, 0xe4, - 0x8b, 0x19, 0xa1, 0x0c, 0xdd, 0x87, 0xdc, 0xd0, 0x18, 0x8f, 0x89, 0xeb, 0x0d, 0x12, 0x6b, 0x94, - 0x9a, 0x82, 0xda, 0x1d, 0x2e, 0xef, 0x3e, 0xc4, 0x59, 0xd1, 0xa3, 0x6b, 0xa2, 0xef, 0x40, 0x46, - 0x22, 0xc4, 0x17, 0x10, 0x7d, 0xa3, 0x00, 0x61, 0x5f, 0x8f, 0x3e, 0x80, 0x14, 0x37, 0x95, 0xd3, - 0x52, 0x6b, 0xdf, 0x90, 0x86, 0xef, 0x79, 0xae, 0xf2, 0x28, 0x62, 0xa1, 0x47, 0xdf, 0x07, 0x8d, - 0x19, 0x27, 0x63, 0xc2, 0x06, 0xec, 0x6c, 0x4a, 0x38, 0x4f, 0x8b, 0xed, 0x4a, 0x33, 0xd8, 0x6e, - 0x7d, 0xae, 0xec, 0x9f, 0x4d, 0x09, 0x06, 0x16, 0x7c, 0xa3, 0xfb, 0x80, 0x6c, 0xc7, 0x43, 0x7b, - 0x61, 0xab, 0xa5, 0x38, 0xcb, 0xcb, 0xb6, 0xc3, 0xba, 0x0b, 0xbb, 0x6d, 0x17, 0x8a, 0xa7, 0xe4, - 0x8c, 0x4e, 0x8d, 0x21, 0x19, 0xf0, 0x2d, 0xc4, 0xd9, 0x9c, 0xc3, 0x05, 0x5f, 0xca, 0x51, 0x8f, - 0xb2, 0x3d, 0xb3, 0x0a, 0xdb, 0xf5, 0xaf, 0x14, 0x28, 0x05, 0x88, 0xd2, 0xa9, 0x63, 0x53, 0x82, - 0x76, 0x21, 0x45, 0x5c, 0xd7, 0x71, 0x63, 0x70, 0xe2, 0xe3, 0xce, 0xbe, 0x27, 0xc6, 0x42, 0x7b, - 0x19, 0x2c, 0xef, 0x41, 0xda, 0x25, 0x74, 0x36, 0x66, 0x12, 0x4c, 0x14, 0xdd, 0x0d, 0x98, 0x6b, - 0xb0, 0xec, 0xa1, 0xff, 0x4f, 0x85, 0x8a, 0xb4, 0x88, 0xfb, 0x44, 0xb7, 0x27, 0xd2, 0x35, 0xc8, - 0xfa, 0x70, 0xf3, 0x30, 0xe7, 0x70, 0xd0, 0x46, 0xb7, 0x21, 0xcd, 0xe3, 0x42, 0xab, 0xa9, 0x7a, - 0xa2, 0x91, 0xc3, 0xb2, 0x15, 0x67, 0x47, 0x7a, 0x23, 0x76, 0x64, 0x96, 0xb0, 0x23, 0x12, 0xf6, - 0xec, 0x4a, 0x61, 0xff, 0x9d, 0x02, 0xb7, 0x62, 0x20, 0x6f, 0x45, 0xf0, 0xff, 0xaf, 0xc2, 0x3b, - 0xd2, 0xae, 0xcf, 0x24, 0xb2, 0xdd, 0x37, 0x85, 0x01, 0xdf, 0x82, 0x7c, 0xb0, 0x45, 0x2d, 0xc9, - 0x83, 0x3c, 0xd6, 0x4e, 0x43, 0x3f, 0xb6, 0x94, 0x0c, 0x5f, 0x2b, 0x50, 0x3b, 0x0f, 0xf4, 0xad, - 0x60, 0xc4, 0x97, 0x09, 0x78, 0x3b, 0x34, 0x0e, 0x1b, 0xf6, 0x88, 0xbc, 0x21, 0x7c, 0xf8, 0x10, - 0xe0, 0x94, 0x9c, 0x0d, 0x5c, 0x6e, 0x32, 0x67, 0x83, 0xe7, 0x69, 0x10, 0x6b, 0xdf, 0x1b, 0x9c, - 0x3b, 0xf5, 0xfd, 0xda, 0x52, 0x7e, 0xfc, 0x5e, 0x81, 0xea, 0xcb, 0x21, 0xd8, 0x0a, 0x76, 0xfc, - 0x3d, 0x19, 0xb0, 0x63, 0xdf, 0x66, 0x16, 0x3b, 0x7b, 0x63, 0xb2, 0xc5, 0x7d, 0x40, 0x84, 0x5b, - 0x3c, 0x18, 0x3a, 0xe3, 0xd9, 0xc4, 0x1e, 0xd8, 0xc6, 0x84, 0xc8, 0x0a, 0xb6, 0x2c, 0x34, 0x1d, - 0xae, 0x38, 0x34, 0x26, 0x04, 0xfd, 0x14, 0x6e, 0xca, 0xde, 0x0b, 0x29, 0x26, 0xcd, 0x49, 0xd5, - 0xf0, 0x2d, 0x5d, 0x82, 0x44, 0xd3, 0x17, 0xe0, 0x1b, 0x62, 0x92, 0xcf, 0x96, 0xa7, 0xa4, 0xcc, - 0x46, 0x94, 0xcb, 0x5e, 0x4c, 0xb9, 0xdc, 0x2a, 0x94, 0xab, 0x9d, 0x40, 0xd6, 0x37, 0x1a, 0xdd, - 0x85, 0x24, 0x37, 0x4d, 0xe1, 0xa6, 0x69, 0x7e, 0x01, 0xe9, 0x59, 0xc4, 0x15, 0xa8, 0x02, 0xa9, - 0xb9, 0x31, 0x9e, 0x11, 0x1e, 0xb8, 0x3c, 0x16, 0x0d, 0x74, 0x17, 0xb4, 0x08, 0x56, 0x3c, 0x56, - 0x79, 0x0c, 0x61, 0x36, 0x8e, 0xd2, 0x3a, 0x82, 0xd8, 0x56, 0xd0, 0xfa, 0xbf, 0x2a, 0xdc, 0x94, - 0xa6, 0xed, 0x19, 0x6c, 0xf8, 0xfc, 0xca, 0x29, 0xfd, 0x5d, 0xc8, 0x78, 0xd6, 0x58, 0x84, 0x56, - 0x13, 0x9c, 0x53, 0xe7, 0x90, 0xda, 0xef, 0xb1, 0x6e, 0xc1, 0xbb, 0x0b, 0x45, 0x83, 0x9e, 0x53, - 0xec, 0x16, 0x0c, 0x7a, 0x1d, 0x95, 0xee, 0xd7, 0x4a, 0x50, 0x57, 0x4a, 0x4c, 0xaf, 0x2c, 0xd4, - 0xdf, 0x83, 0x8c, 0x08, 0xa4, 0x8f, 0xe6, 0x6d, 0x69, 0x9b, 0x08, 0xf3, 0x33, 0x8b, 0x3d, 0x17, - 0x53, 0xfb, 0xdd, 0x74, 0x1b, 0x4a, 0x1c, 0x69, 0xee, 0x1b, 0x87, 0x3b, 0xcc, 0x32, 0xca, 0x25, - 0xb2, 0x8c, 0xba, 0xb4, 0x2a, 0x4d, 0x44, 0xab, 0x52, 0xfd, 0x6f, 0x61, 0x9d, 0xc5, 0xc1, 0xb8, - 0xa6, 0x4a, 0xfb, 0xc3, 0x38, 0xcd, 0x82, 0x9f, 0xd4, 0x31, 0xef, 0xaf, 0x8b, 0x6c, 0x97, 0xbd, - 0x1d, 0xd0, 0xff, 0x10, 0xd6, 0x4a, 0x0b, 0xc0, 0x5d, 0x19, 0x97, 0xee, 0xc7, 0xb9, 0x74, 0x5e, - 0xde, 0x08, 0x78, 0xf4, 0x1b, 0xa8, 0x70, 0x24, 0xc3, 0x0c, 0xff, 0x1a, 0xc9, 0x14, 0x2f, 0x70, - 0x13, 0x2f, 0x15, 0xb8, 0xfa, 0xbf, 0x54, 0xb8, 0x13, 0x85, 0xe7, 0x3a, 0x8b, 0xf8, 0x8f, 0xe3, - 0xe4, 0xda, 0x59, 0x20, 0x57, 0x0c, 0x92, 0xad, 0x65, 0xd8, 0x9f, 0x14, 0xb8, 0xbb, 0x14, 0xc2, - 0x2d, 0xa1, 0xd9, 0x5f, 0x54, 0xa8, 0xf4, 0x98, 0x4b, 0x8c, 0xc9, 0x46, 0xb7, 0x31, 0x01, 0x2b, - 0xd5, 0xcb, 0x5d, 0xb1, 0x24, 0x56, 0x0f, 0x51, 0xec, 0x28, 0x49, 0x5e, 0x70, 0x94, 0xa4, 0x56, - 0xba, 0x22, 0x8c, 0xe0, 0x9a, 0x7e, 0x35, 0xae, 0x7a, 0x07, 0x6e, 0xc5, 0x80, 0x92, 0x21, 0x0c, - 0xcb, 0x01, 0xe5, 0xc2, 0x72, 0xe0, 0x2b, 0x15, 0x6a, 0x0b, 0xb3, 0x6c, 0x92, 0xae, 0x57, 0x06, - 0x3d, 0x9a, 0x0a, 0x12, 0x4b, 0xcf, 0x95, 0xe4, 0xab, 0x6e, 0x3b, 0x52, 0x2b, 0x06, 0xea, 0xd2, - 0x9b, 0xa4, 0x0b, 0xef, 0x9e, 0x0b, 0xc8, 0x1a, 0xe0, 0xfe, 0x51, 0x85, 0xbb, 0x0b, 0x73, 0x6d, - 0x9c, 0xb3, 0x5e, 0x0b, 0xc2, 0xf1, 0x64, 0x9b, 0xbc, 0xf0, 0x36, 0xe1, 0xca, 0xc0, 0x3e, 0x84, - 0xfa, 0x72, 0x80, 0xd6, 0x40, 0xfc, 0xaf, 0x2a, 0xbc, 0x17, 0x9f, 0x70, 0x93, 0x1f, 0xf6, 0xaf, - 0x05, 0xef, 0xc5, 0x5f, 0xeb, 0xc9, 0x35, 0x7e, 0xad, 0x5f, 0x19, 0xfe, 0x8f, 0xe1, 0xce, 0x32, - 0xb8, 0xd6, 0x40, 0xff, 0x67, 0x90, 0xdf, 0x23, 0x23, 0xcb, 0x5e, 0x0f, 0xeb, 0x85, 0x07, 0x1b, - 0x75, 0xf1, 0xc1, 0x46, 0xff, 0x21, 0x14, 0xe4, 0xd4, 0xd2, 0xae, 0x48, 0xa2, 0x54, 0x2e, 0x48, - 0x94, 0x5f, 0x2a, 0x50, 0xe8, 0xf0, 0x77, 0x9d, 0x2b, 0x2f, 0x14, 0x6e, 0x43, 0xda, 0x60, 0xce, - 0xc4, 0x1a, 0xca, 0x17, 0x27, 0xd9, 0xd2, 0xcb, 0x50, 0xf4, 0x2d, 0x10, 0xf6, 0xeb, 0xbf, 0x82, - 0x12, 0x76, 0xc6, 0xe3, 0x13, 0x63, 0x78, 0x7a, 0xd5, 0x56, 0xe9, 0x08, 0xca, 0xe1, 0x5a, 0x72, - 0xfd, 0x5f, 0xc2, 0x3b, 0x98, 0x50, 0x67, 0x3c, 0x27, 0x91, 0x92, 0x62, 0x3d, 0x4b, 0x10, 0x24, - 0x4d, 0x26, 0xdf, 0x55, 0x72, 0x98, 0x7f, 0xeb, 0xff, 0x54, 0xa0, 0x72, 0x40, 0x28, 0x35, 0x46, - 0x44, 0x10, 0x6c, 0xbd, 0xa9, 0x5f, 0x55, 0x33, 0x56, 0x20, 0x25, 0x4e, 0x5e, 0xb1, 0xdf, 0x44, - 0x03, 0xb5, 0x20, 0x17, 0x6c, 0x36, 0x7e, 0x26, 0x9f, 0xbf, 0xd7, 0xb2, 0xfe, 0x5e, 0xf3, 0xac, - 0x8f, 0xdc, 0x8f, 0xf0, 0x6f, 0xfd, 0xb7, 0x0a, 0xdc, 0x90, 0xd6, 0x3f, 0x58, 0x37, 0x3e, 0xaf, - 0x32, 0xdd, 0x5f, 0x33, 0x11, 0xae, 0x89, 0xee, 0x40, 0xc2, 0x4f, 0xc6, 0x5a, 0x3b, 0x2f, 0x77, - 0xd9, 0x53, 0x63, 0x3c, 0x23, 0xd8, 0x53, 0xe8, 0x07, 0x90, 0xef, 0x46, 0x2a, 0x4d, 0xb4, 0x03, - 0x6a, 0x60, 0xc6, 0x62, 0x77, 0xd5, 0x32, 0xe3, 0x57, 0x14, 0xea, 0x4b, 0x57, 0x14, 0xff, 0x50, - 0x60, 0x27, 0x74, 0x71, 0xe3, 0x83, 0xe9, 0xb2, 0xde, 0x7e, 0x02, 0x25, 0xcb, 0x1c, 0xbc, 0x74, - 0x0c, 0x69, 0xed, 0x8a, 0xcf, 0xe2, 0xa8, 0xb3, 0xb8, 0x60, 0x45, 0x5a, 0x54, 0xdf, 0x81, 0xda, - 0x79, 0xe4, 0x95, 0xd4, 0xfe, 0x08, 0x6e, 0x3d, 0x22, 0xac, 0xe7, 0xce, 0xfd, 0x21, 0xbe, 0x4b, - 0x51, 0x23, 0x95, 0x45, 0x23, 0x75, 0x0c, 0xb7, 0xe3, 0x83, 0x64, 0xa6, 0xf9, 0x01, 0xe4, 0xa9, - 0x3b, 0x1f, 0x2c, 0x8c, 0xf4, 0x32, 0x6b, 0x40, 0xaa, 0xe8, 0x20, 0x8d, 0x86, 0x0d, 0xfd, 0xdf, - 0x0a, 0x14, 0x9f, 0x6e, 0x42, 0xff, 0xd8, 0x31, 0xa0, 0xae, 0x78, 0x0c, 0x7c, 0x00, 0xa9, 0xf9, - 0x88, 0xc9, 0x9b, 0x29, 0xef, 0xd4, 0x8a, 0x3c, 0xdf, 0x3f, 0x7d, 0xc4, 0x2c, 0x13, 0x0b, 0xbd, - 0x97, 0xdc, 0x3f, 0xb7, 0xc6, 0x8c, 0xb8, 0xc1, 0x4e, 0x89, 0xf4, 0xfc, 0x94, 0x6b, 0xb0, 0xec, - 0xa1, 0xff, 0x08, 0x4a, 0x81, 0x2f, 0xe1, 0xd9, 0x40, 0xe6, 0xc4, 0x66, 0xb4, 0xaa, 0xc8, 0x43, - 0x2d, 0xba, 0xd0, 0xbe, 0xa7, 0xc2, 0xb2, 0xc7, 0xbd, 0x87, 0x50, 0x8a, 0xbd, 0x6d, 0xa3, 0x12, - 0x68, 0x4f, 0x0e, 0x7b, 0xc7, 0xfb, 0x9d, 0xee, 0xa7, 0xdd, 0xfd, 0x87, 0xe5, 0xb7, 0x10, 0x40, - 0xba, 0xd7, 0x3d, 0x7c, 0xf4, 0x78, 0xbf, 0xac, 0xa0, 0x1c, 0xa4, 0x0e, 0x9e, 0x3c, 0xee, 0x77, - 0xcb, 0xaa, 0xf7, 0xd9, 0x7f, 0x76, 0x74, 0xdc, 0x29, 0x27, 0xee, 0x7d, 0x02, 0x9a, 0xc8, 0xa3, - 0x47, 0xae, 0x49, 0x5c, 0x6f, 0xc0, 0xe1, 0x11, 0x3e, 0x78, 0xf0, 0xb8, 0xfc, 0x16, 0xca, 0x40, - 0xe2, 0x18, 0x7b, 0x23, 0xb3, 0x90, 0x3c, 0x3e, 0xea, 0xf5, 0xcb, 0x2a, 0x2a, 0x02, 0x3c, 0x78, - 0xd2, 0x3f, 0xea, 0x1c, 0x1d, 0x1c, 0x74, 0xfb, 0xe5, 0xc4, 0xde, 0xc7, 0x50, 0xb2, 0x9c, 0xe6, - 0xdc, 0x62, 0x84, 0x52, 0xf1, 0xdf, 0x09, 0x3f, 0x7f, 0x5f, 0xb6, 0x2c, 0xa7, 0x25, 0xbe, 0x5a, - 0x23, 0xa7, 0x35, 0x67, 0x2d, 0xae, 0x6d, 0x09, 0x2a, 0x9e, 0xa4, 0x79, 0xeb, 0xa3, 0x6f, 0x02, - 0x00, 0x00, 0xff, 0xff, 0xe2, 0x00, 0xc6, 0xcc, 0x1d, 0x21, 0x00, 0x00, + // 1835 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xdd, 0x6e, 0x1b, 0xc7, + 0x15, 0xce, 0xee, 0xf2, 0xf7, 0x2c, 0xff, 0x3c, 0xa6, 0x15, 0x9a, 0x51, 0x6c, 0x76, 0x53, 0x21, + 0xb4, 0x6b, 0x48, 0x8d, 0x82, 0x06, 0x41, 0x90, 0xa2, 0xb0, 0x28, 0xc5, 0x20, 0x62, 0xfd, 0x74, + 0x44, 0xdb, 0x6d, 0x91, 0x62, 0xb1, 0xe2, 0x8e, 0xe9, 0xad, 0xa8, 0x5d, 0x66, 0x67, 0x48, 0x57, + 0x37, 0x45, 0xde, 0x20, 0xe8, 0x45, 0x81, 0xc2, 0x28, 0x50, 0xf4, 0xaa, 0x57, 0xbd, 0x2d, 0xd0, + 0xf6, 0xa6, 0xd7, 0xbd, 0x29, 0xfa, 0x0a, 0xed, 0x1b, 0xf4, 0x09, 0x82, 0x9d, 0x99, 0xfd, 0xe1, + 0x5a, 0xb4, 0x28, 0xca, 0x32, 0xe8, 0x1b, 0x61, 0x67, 0xce, 0xec, 0xcc, 0x37, 0xdf, 0xf9, 0xce, + 0x99, 0xc3, 0x59, 0x41, 0x69, 0xc2, 0x06, 0x16, 0x23, 0xeb, 0x23, 0xdf, 0x63, 0x1e, 0xca, 0x89, + 0x56, 0xb3, 0x76, 0xe4, 0xb8, 0x43, 0x6f, 0x60, 0x5b, 0xcc, 0x12, 0x96, 0xa6, 0xfe, 0xf5, 0x98, + 0xf8, 0xa7, 0xb2, 0x51, 0x61, 0xde, 0xc8, 0x4b, 0x1a, 0x27, 0xcc, 0x1f, 0xf5, 0x45, 0xc3, 0xf8, + 0x5f, 0x0e, 0xf2, 0x87, 0x84, 0x52, 0xc7, 0x73, 0xd1, 0x1a, 0x54, 0x1c, 0xd7, 0x64, 0xbe, 0xe5, + 0x52, 0xab, 0xcf, 0x1c, 0xcf, 0x6d, 0x28, 0x2d, 0xa5, 0x5d, 0xc0, 0x65, 0xc7, 0xed, 0xc5, 0x9d, + 0xa8, 0x03, 0x15, 0xfa, 0xcc, 0xf2, 0x6d, 0x93, 0x8a, 0xf7, 0x68, 0x43, 0x6d, 0x69, 0x6d, 0x7d, + 0x73, 0x75, 0x5d, 0xa2, 0x93, 0xf3, 0xad, 0x1f, 0x06, 0xa3, 0x64, 0x03, 0x97, 0x69, 0xa2, 0x45, + 0xd1, 0x7b, 0x50, 0xa4, 0x8e, 0x3b, 0x18, 0x12, 0xd3, 0x3e, 0x6a, 0x68, 0x7c, 0x99, 0x82, 0xe8, + 0xd8, 0x3e, 0x42, 0xb7, 0x00, 0xac, 0x31, 0xf3, 0xfa, 0xde, 0xc9, 0x89, 0xc3, 0x1a, 0x19, 0x6e, + 0x4d, 0xf4, 0xa0, 0x0f, 0xa0, 0xcc, 0x2c, 0x7f, 0x40, 0x98, 0x49, 0x99, 0xef, 0xb8, 0x83, 0x46, + 0xb6, 0xa5, 0xb4, 0x8b, 0xb8, 0x24, 0x3a, 0x0f, 0x79, 0x1f, 0xda, 0x80, 0xbc, 0x37, 0x62, 0x1c, + 0x5f, 0xae, 0xa5, 0xb4, 0xf5, 0xcd, 0x1b, 0xeb, 0x82, 0x95, 0x9d, 0x5f, 0x93, 0xfe, 0x98, 0x91, + 0x7d, 0x61, 0xc4, 0xe1, 0x28, 0xb4, 0x05, 0xb5, 0xc4, 0xde, 0xcd, 0x13, 0xcf, 0x26, 0x8d, 0x7c, + 0x4b, 0x69, 0x57, 0x36, 0xdf, 0x0d, 0x77, 0x96, 0xa0, 0x61, 0xd7, 0xb3, 0x09, 0xae, 0xb2, 0xe9, + 0x0e, 0xb4, 0x01, 0x85, 0xe7, 0x96, 0xef, 0x3a, 0xee, 0x80, 0x36, 0x0a, 0x9c, 0x95, 0xeb, 0x72, + 0xd5, 0x9f, 0x06, 0x7f, 0x9f, 0x08, 0x1b, 0x8e, 0x06, 0xa1, 0x9f, 0x40, 0x69, 0xe4, 0x93, 0x98, + 0xca, 0xe2, 0x1c, 0x54, 0xea, 0x23, 0x9f, 0x44, 0x44, 0xde, 0x87, 0xf2, 0xc8, 0xa3, 0x2c, 0x9e, + 0x01, 0xe6, 0x98, 0xa1, 0x14, 0xbc, 0x12, 0x4d, 0xf1, 0x7d, 0xa8, 0x0c, 0x2d, 0xca, 0x4c, 0xc7, + 0xa5, 0xc4, 0x67, 0xa6, 0x63, 0x37, 0xf4, 0x96, 0xd2, 0xce, 0xe0, 0x52, 0xd0, 0xdb, 0xe5, 0x9d, + 0x5d, 0x1b, 0xbd, 0x0f, 0xf0, 0xd4, 0x1b, 0xbb, 0xb6, 0xe9, 0x7b, 0xcf, 0x69, 0xa3, 0xc4, 0x47, + 0x14, 0x79, 0x0f, 0xf6, 0x9e, 0x53, 0x64, 0xc2, 0xca, 0x98, 0x12, 0xdf, 0xb4, 0xc9, 0x53, 0xc7, + 0x25, 0xb6, 0x39, 0xb1, 0x7c, 0xc7, 0x3a, 0x1a, 0x12, 0xda, 0x28, 0x73, 0x40, 0x77, 0xd2, 0x80, + 0x1e, 0x51, 0xe2, 0x6f, 0x8b, 0xc1, 0x8f, 0xc3, 0xb1, 0x3b, 0x2e, 0xf3, 0x4f, 0x71, 0x7d, 0x7c, + 0x86, 0xa9, 0xf9, 0x15, 0x94, 0x92, 0x7b, 0x40, 0x6b, 0x90, 0x13, 0xfe, 0xe6, 0x2a, 0xd5, 0x37, + 0xcb, 0x92, 0xe8, 0x1e, 0xef, 0xc4, 0xd2, 0x18, 0x88, 0x3a, 0xe9, 0x55, 0xc7, 0x6e, 0xa8, 0x2d, + 0xa5, 0xad, 0xe1, 0x72, 0xa2, 0xb7, 0x6b, 0x37, 0xbf, 0x82, 0x9b, 0x33, 0x01, 0xa1, 0x1a, 0x68, + 0xc7, 0xe4, 0x94, 0xaf, 0x53, 0xc4, 0xc1, 0x23, 0xba, 0x03, 0xd9, 0x89, 0x35, 0x1c, 0x13, 0x3e, + 0x59, 0xec, 0xe4, 0x2d, 0xc7, 0x8d, 0xde, 0xc5, 0x62, 0xc4, 0x67, 0xea, 0xa7, 0x8a, 0xf1, 0x6f, + 0x15, 0x2a, 0x52, 0x76, 0x98, 0x7c, 0x3d, 0x26, 0x94, 0xa1, 0x7b, 0x50, 0xec, 0x5b, 0xc3, 0x21, + 0xf1, 0x03, 0x48, 0x62, 0x07, 0xd5, 0x75, 0x11, 0x99, 0x1d, 0xde, 0xdf, 0xdd, 0xc6, 0x05, 0x31, + 0xa2, 0x6b, 0xa3, 0x3b, 0x90, 0x97, 0x0e, 0x96, 0x2b, 0x56, 0x53, 0x74, 0xe2, 0xd0, 0x8e, 0x3e, + 0x84, 0x2c, 0x07, 0xc3, 0xa3, 0x4a, 0xdf, 0xbc, 0x16, 0x42, 0x0b, 0x3c, 0xc5, 0x45, 0x88, 0x85, + 0x1d, 0xfd, 0x08, 0x74, 0x16, 0x00, 0x65, 0x26, 0x3b, 0x1d, 0x11, 0x1e, 0x66, 0x95, 0xcd, 0xfa, + 0x7a, 0x94, 0x2d, 0x7a, 0xdc, 0xd8, 0x3b, 0x1d, 0x11, 0x0c, 0x2c, 0x7a, 0x46, 0xf7, 0x00, 0xb9, + 0x5e, 0x20, 0x96, 0xa9, 0x4c, 0x91, 0xe5, 0x41, 0x5a, 0x73, 0x3d, 0xd6, 0x9d, 0x4a, 0x16, 0x6b, + 0x50, 0x39, 0x26, 0xa7, 0x74, 0x64, 0xf5, 0x89, 0xc9, 0x33, 0x00, 0x0f, 0xc6, 0x22, 0x2e, 0x87, + 0xbd, 0xdc, 0xa7, 0xc9, 0x60, 0xcd, 0xcf, 0x13, 0xac, 0xc6, 0xb7, 0x0a, 0x54, 0x23, 0x46, 0xe9, + 0xc8, 0x73, 0x29, 0x41, 0x6b, 0x90, 0x25, 0xbe, 0xef, 0xf9, 0x29, 0x3a, 0xf1, 0x41, 0x67, 0x27, + 0xe8, 0xc6, 0xc2, 0x7a, 0x11, 0x2e, 0xef, 0x42, 0xce, 0x27, 0x74, 0x3c, 0x64, 0x92, 0x4c, 0x94, + 0x0c, 0x66, 0xcc, 0x2d, 0x58, 0x8e, 0x30, 0xfe, 0xab, 0x42, 0x5d, 0x22, 0xe2, 0x7b, 0xa2, 0xcb, + 0xe3, 0xe9, 0x26, 0x14, 0x42, 0xba, 0xb9, 0x9b, 0x8b, 0x38, 0x6a, 0xa3, 0x15, 0xc8, 0x71, 0xbf, + 0xd0, 0x46, 0xb6, 0xa5, 0xb5, 0x8b, 0x58, 0xb6, 0xd2, 0xea, 0xc8, 0x5d, 0x4a, 0x1d, 0xf9, 0x19, + 0xea, 0x48, 0xb8, 0xbd, 0x30, 0x97, 0xdb, 0x7f, 0xa7, 0xc0, 0x8d, 0x14, 0xc9, 0x4b, 0xe1, 0xfc, + 0xff, 0xab, 0x70, 0x53, 0xe2, 0xfa, 0x52, 0x32, 0xdb, 0x7d, 0x5b, 0x14, 0xf0, 0x3d, 0x28, 0x45, + 0x21, 0xea, 0x48, 0x1d, 0x94, 0xb0, 0x7e, 0x1c, 0xef, 0x63, 0x49, 0xc5, 0xf0, 0x42, 0x81, 0xe6, + 0x59, 0xa4, 0x2f, 0x85, 0x22, 0xbe, 0xd1, 0xe0, 0xdd, 0x18, 0x1c, 0xb6, 0xdc, 0x01, 0x79, 0x4b, + 0xf4, 0xf0, 0x11, 0xc0, 0x31, 0x39, 0x35, 0x7d, 0x0e, 0x99, 0xab, 0x21, 0xd8, 0x69, 0xe4, 0xeb, + 0x70, 0x37, 0xb8, 0x78, 0x1c, 0xee, 0x6b, 0x49, 0xf5, 0xf1, 0x7b, 0x05, 0x1a, 0x2f, 0xbb, 0x60, + 0x29, 0xd4, 0xf1, 0xb7, 0x4c, 0xa4, 0x8e, 0x1d, 0x97, 0x39, 0xec, 0xf4, 0xad, 0xc9, 0x16, 0xf7, + 0x00, 0x11, 0x8e, 0xd8, 0xec, 0x7b, 0xc3, 0xf1, 0x89, 0x6b, 0xba, 0xd6, 0x09, 0x91, 0x05, 0x78, + 0x4d, 0x58, 0x3a, 0xdc, 0xb0, 0x67, 0x9d, 0x10, 0xf4, 0x33, 0xb8, 0x2e, 0x47, 0x4f, 0xa5, 0x98, + 0x1c, 0x17, 0x55, 0x3b, 0x44, 0x3a, 0x83, 0x89, 0xf5, 0xb0, 0x03, 0x5f, 0x13, 0x93, 0x7c, 0x39, + 0x3b, 0x25, 0xe5, 0x2f, 0x25, 0xb9, 0xc2, 0xf9, 0x92, 0x2b, 0xce, 0x23, 0xb9, 0xe6, 0x11, 0x14, + 0x42, 0xd0, 0xe8, 0x36, 0x64, 0x38, 0x34, 0x85, 0x43, 0xd3, 0xc3, 0xf2, 0x34, 0x40, 0xc4, 0x0d, + 0xa8, 0x9e, 0x2c, 0x22, 0x4b, 0xb2, 0x5e, 0x44, 0xb7, 0x41, 0x4f, 0x70, 0xc5, 0x7d, 0x55, 0xc2, + 0x10, 0x67, 0xe3, 0xa4, 0xac, 0x13, 0x8c, 0x2d, 0x85, 0xac, 0xff, 0xa3, 0xc2, 0x75, 0x09, 0x6d, + 0xcb, 0x62, 0xfd, 0x67, 0x57, 0x2e, 0xe9, 0x1f, 0x40, 0x3e, 0x40, 0xe3, 0x10, 0xda, 0xd0, 0xb8, + 0xa6, 0xce, 0x10, 0x75, 0x38, 0x62, 0xd1, 0x82, 0x77, 0x0d, 0x2a, 0x16, 0x3d, 0xa3, 0xd8, 0x2d, + 0x5b, 0xf4, 0x4d, 0x54, 0xba, 0x2f, 0x94, 0xa8, 0xae, 0x94, 0x9c, 0x5e, 0x99, 0xab, 0x7f, 0x08, + 0x79, 0xe1, 0xc8, 0x90, 0xcd, 0x15, 0x89, 0x4d, 0xb8, 0xf9, 0x89, 0xc3, 0x9e, 0x89, 0xa9, 0xc3, + 0x61, 0x86, 0x0b, 0x55, 0xce, 0x34, 0xdf, 0x1b, 0xa7, 0x3b, 0xce, 0x32, 0xca, 0x05, 0xb2, 0x8c, + 0x3a, 0xb3, 0x2a, 0xd5, 0x92, 0x55, 0xa9, 0xf1, 0xd7, 0xb8, 0xce, 0xe2, 0x64, 0xbc, 0xa1, 0x4a, + 0xfb, 0xa3, 0xb4, 0xcc, 0xa2, 0x1b, 0x81, 0xd4, 0xee, 0xdf, 0x94, 0xd8, 0x2e, 0x7a, 0xb9, 0x61, + 0xfc, 0x21, 0xae, 0x95, 0xa6, 0x88, 0xbb, 0x32, 0x2d, 0xdd, 0x4b, 0x6b, 0xe9, 0xac, 0xbc, 0x11, + 0xe9, 0xe8, 0x37, 0x50, 0xe7, 0x4c, 0xc6, 0x19, 0xfe, 0x35, 0x8a, 0x29, 0x5d, 0xe0, 0x6a, 0x2f, + 0x15, 0xb8, 0xc6, 0x3f, 0x55, 0xb8, 0x95, 0xa4, 0xe7, 0x4d, 0x16, 0xf1, 0x9f, 0xa4, 0xc5, 0xb5, + 0x3a, 0x25, 0xae, 0x14, 0x25, 0x4b, 0xab, 0xb0, 0x3f, 0x29, 0x70, 0x7b, 0x26, 0x85, 0x4b, 0x22, + 0xb3, 0x3f, 0xab, 0x50, 0x3f, 0x64, 0x3e, 0xb1, 0x4e, 0x2e, 0x75, 0x1b, 0x13, 0xa9, 0x52, 0xbd, + 0xd8, 0x15, 0x8b, 0x36, 0xbf, 0x8b, 0x52, 0x47, 0x49, 0xe6, 0x9c, 0xa3, 0x24, 0x3b, 0xd7, 0x0d, + 0x67, 0x82, 0xd7, 0xdc, 0xab, 0x79, 0x35, 0x3a, 0x70, 0x23, 0x45, 0x94, 0x74, 0x61, 0x5c, 0x0e, + 0x28, 0xe7, 0x96, 0x03, 0xdf, 0xaa, 0xd0, 0x9c, 0x9a, 0xe5, 0x32, 0xe9, 0x7a, 0x6e, 0xd2, 0x93, + 0xa9, 0x40, 0x9b, 0x79, 0xae, 0x64, 0x5e, 0x75, 0xdb, 0x91, 0x9d, 0xd3, 0x51, 0x17, 0x0e, 0x92, + 0x2e, 0xbc, 0x77, 0x26, 0x21, 0x0b, 0x90, 0xfb, 0x47, 0x15, 0x6e, 0x4f, 0xcd, 0x75, 0xe9, 0x9c, + 0xf5, 0x5a, 0x18, 0x4e, 0x27, 0xdb, 0xcc, 0xb9, 0xb7, 0x09, 0x57, 0x46, 0xf6, 0x1e, 0xb4, 0x66, + 0x13, 0xb4, 0x00, 0xe3, 0x7f, 0x51, 0xe1, 0xfd, 0xf4, 0x84, 0x97, 0xf9, 0x61, 0xff, 0x5a, 0xf8, + 0x9e, 0xfe, 0xb5, 0x9e, 0x59, 0xe0, 0xd7, 0xfa, 0x95, 0xf1, 0xff, 0x10, 0x6e, 0xcd, 0xa2, 0x6b, + 0x01, 0xf6, 0x7f, 0x0e, 0xa5, 0x2d, 0x32, 0x70, 0xdc, 0xc5, 0xb8, 0x9e, 0xfa, 0xde, 0xa4, 0x4e, + 0x7f, 0x6f, 0x32, 0x3e, 0x83, 0xb2, 0x9c, 0x5a, 0xe2, 0x4a, 0x24, 0x4a, 0xe5, 0x9c, 0x44, 0xf9, + 0x8d, 0x02, 0xe5, 0x0e, 0xff, 0x2c, 0x75, 0xe5, 0x85, 0xc2, 0x0a, 0xe4, 0x2c, 0xe6, 0x9d, 0x38, + 0x7d, 0xf9, 0xc1, 0x4c, 0xb6, 0x8c, 0x1a, 0x54, 0x42, 0x04, 0x02, 0xbf, 0xf1, 0x2b, 0xa8, 0x62, + 0x6f, 0x38, 0x3c, 0xb2, 0xfa, 0xc7, 0x57, 0x8d, 0xca, 0x40, 0x50, 0x8b, 0xd7, 0x92, 0xeb, 0xff, + 0x12, 0x6e, 0x62, 0x42, 0xbd, 0xe1, 0x84, 0x24, 0x4a, 0x8a, 0xc5, 0x90, 0x20, 0xc8, 0xd8, 0x4c, + 0x7e, 0xb5, 0x29, 0x62, 0xfe, 0x6c, 0xfc, 0x43, 0x81, 0xfa, 0x2e, 0xa1, 0xd4, 0x1a, 0x10, 0x21, + 0xb0, 0xc5, 0xa6, 0x7e, 0x55, 0xcd, 0x58, 0x87, 0xac, 0x38, 0x79, 0x45, 0xbc, 0x89, 0x06, 0xda, + 0x80, 0x62, 0x14, 0x6c, 0xfc, 0x4c, 0x3e, 0x3b, 0xd6, 0x0a, 0x61, 0xac, 0x05, 0xe8, 0x13, 0xf7, + 0x23, 0xfc, 0xd9, 0xf8, 0xad, 0x02, 0xd7, 0x24, 0xfa, 0xfb, 0x8b, 0xfa, 0xe7, 0x55, 0xd0, 0xc3, + 0x35, 0xb5, 0x78, 0x4d, 0x74, 0x0b, 0xb4, 0x30, 0x19, 0xeb, 0x9b, 0x25, 0x19, 0x65, 0x8f, 0xad, + 0xe1, 0x98, 0xe0, 0xc0, 0x60, 0xec, 0x42, 0xa9, 0x9b, 0xa8, 0x34, 0xd1, 0x2a, 0xa8, 0x11, 0x8c, + 0xe9, 0xe1, 0xaa, 0x63, 0xa7, 0xaf, 0x28, 0xd4, 0x97, 0xae, 0x28, 0xfe, 0xae, 0xc0, 0x6a, 0xbc, + 0xc5, 0x4b, 0x1f, 0x4c, 0x17, 0xdd, 0xed, 0xe7, 0x50, 0x75, 0x6c, 0xf3, 0xa5, 0x63, 0x48, 0xdf, + 0xac, 0x87, 0x2a, 0x4e, 0x6e, 0x16, 0x97, 0x9d, 0x44, 0x8b, 0x1a, 0xab, 0xd0, 0x3c, 0x4b, 0xbc, + 0x52, 0xda, 0x1f, 0xc3, 0x8d, 0x07, 0x84, 0x1d, 0xfa, 0x93, 0xf0, 0x95, 0x70, 0x4b, 0x49, 0x90, + 0xca, 0x34, 0x48, 0x03, 0xc3, 0x4a, 0xfa, 0x25, 0x99, 0x69, 0x3e, 0x85, 0x12, 0xf5, 0x27, 0xe6, + 0xd4, 0x9b, 0x41, 0x66, 0x8d, 0x44, 0x95, 0x7c, 0x49, 0xa7, 0x71, 0xc3, 0xf8, 0x97, 0x02, 0x95, + 0xc7, 0x97, 0x91, 0x7f, 0xea, 0x18, 0x50, 0xe7, 0x3c, 0x06, 0x3e, 0x84, 0xec, 0x64, 0xc0, 0xe4, + 0xcd, 0x54, 0x70, 0x6a, 0x25, 0xfe, 0xfb, 0xe0, 0xf1, 0x03, 0xe6, 0xd8, 0x58, 0xd8, 0x83, 0xe4, + 0xfe, 0xd4, 0x19, 0x32, 0xe2, 0x47, 0x91, 0x92, 0x18, 0xf9, 0x05, 0xb7, 0x60, 0x39, 0xc2, 0xf8, + 0x31, 0x54, 0xa3, 0xbd, 0xc4, 0x67, 0x03, 0x99, 0x10, 0x97, 0xd1, 0x86, 0x22, 0x0f, 0xb5, 0xe4, + 0x42, 0x3b, 0x81, 0x09, 0xcb, 0x11, 0x77, 0xb7, 0xa1, 0x9a, 0xfa, 0x34, 0x8f, 0xaa, 0xa0, 0x3f, + 0xda, 0x3b, 0x3c, 0xd8, 0xe9, 0x74, 0xbf, 0xe8, 0xee, 0x6c, 0xd7, 0xde, 0x41, 0x00, 0xb9, 0xc3, + 0xee, 0xde, 0x83, 0x87, 0x3b, 0x35, 0x05, 0x15, 0x21, 0xbb, 0xfb, 0xe8, 0x61, 0xaf, 0x5b, 0x53, + 0x83, 0xc7, 0xde, 0x93, 0xfd, 0x83, 0x4e, 0x4d, 0xbb, 0xfb, 0x39, 0xe8, 0x22, 0x8f, 0xee, 0xfb, + 0x36, 0xf1, 0x83, 0x17, 0xf6, 0xf6, 0xf1, 0xee, 0xfd, 0x87, 0xb5, 0x77, 0x50, 0x1e, 0xb4, 0x03, + 0x1c, 0xbc, 0x59, 0x80, 0xcc, 0xc1, 0xfe, 0x61, 0xaf, 0xa6, 0xa2, 0x0a, 0xc0, 0xfd, 0x47, 0xbd, + 0xfd, 0xce, 0xfe, 0xee, 0x6e, 0xb7, 0x57, 0xd3, 0xb6, 0x3e, 0x81, 0xaa, 0xe3, 0xad, 0x4f, 0x1c, + 0x46, 0x28, 0x15, 0xff, 0x5c, 0xf1, 0x8b, 0x0f, 0x64, 0xcb, 0xf1, 0x36, 0xc4, 0xd3, 0xc6, 0xc0, + 0xdb, 0x98, 0xb0, 0x0d, 0x6e, 0xdd, 0x10, 0x52, 0x3c, 0xca, 0xf1, 0xd6, 0xc7, 0xdf, 0x05, 0x00, + 0x00, 0xff, 0xff, 0xb3, 0x65, 0x97, 0x24, 0xdc, 0x21, 0x00, 0x00, } diff --git a/go/vt/sqlparser/analyzer.go b/go/vt/sqlparser/analyzer.go index 3d78e3e993d..e9d573707d9 100644 --- a/go/vt/sqlparser/analyzer.go +++ b/go/vt/sqlparser/analyzer.go @@ -327,30 +327,39 @@ func ExtractSetValues(sql string) (keyValues map[SetKey]interface{}, scope strin } result := make(map[SetKey]interface{}) for _, expr := range setStmt.Exprs { - scope := ImplicitStr + var scope string key := expr.Name.Lowered() - switch { - case strings.HasPrefix(key, "@@global."): - scope = GlobalStr - key = strings.TrimPrefix(key, "@@global.") - case strings.HasPrefix(key, "@@session."): - scope = SessionStr - key = strings.TrimPrefix(key, "@@session.") - case strings.HasPrefix(key, "@@vitess_metadata."): - scope = VitessMetadataStr - key = strings.TrimPrefix(key, "@@vitess_metadata.") - case strings.HasPrefix(key, "@@"): - key = strings.TrimPrefix(key, "@@") - } - if strings.HasPrefix(expr.Name.Lowered(), "@@") { - if setStmt.Scope != "" && scope != "" { - return nil, "", fmt.Errorf("unsupported in set: mixed using of variable scope") + switch expr.Name.at { + case NoAt: + scope = ImplicitStr + case SingleAt: + scope = VariableStr + case DoubleAt: + switch { + case strings.HasPrefix(key, "global."): + scope = GlobalStr + key = strings.TrimPrefix(key, "global.") + case strings.HasPrefix(key, "session."): + scope = SessionStr + key = strings.TrimPrefix(key, "session.") + case strings.HasPrefix(key, "vitess_metadata."): + scope = VitessMetadataStr + key = strings.TrimPrefix(key, "vitess_metadata.") + default: + scope = SessionStr } + + // This is what correctly allows us to handle queries such as "set @@session.`autocommit`=1" + // it will remove backticks and double quotes that might surround the part after the first period _, out := NewStringTokenizer(key).Scan() key = string(out) } + if setStmt.Scope != "" && scope != "" { + return nil, "", fmt.Errorf("unsupported in set: mixed using of variable scope") + } + setKey := SetKey{ Key: key, Scope: scope, @@ -367,6 +376,12 @@ func ExtractSetValues(sql string) (keyValues map[SetKey]interface{}, scope strin return nil, "", err } result[setKey] = num + case FloatVal: + num, err := strconv.ParseFloat(string(expr.Val), 64) + if err != nil { + return nil, "", err + } + result[setKey] = num default: return nil, "", fmt.Errorf("invalid value type: %v", String(expr)) } diff --git a/go/vt/sqlparser/analyzer_test.go b/go/vt/sqlparser/analyzer_test.go index 062a3e3b66b..42dd56989be 100644 --- a/go/vt/sqlparser/analyzer_test.go +++ b/go/vt/sqlparser/analyzer_test.go @@ -21,6 +21,9 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" "vitess.io/vitess/go/sqltypes" ) @@ -579,19 +582,46 @@ func TestExtractSetValues(t *testing.T) { sql: "set session sql_safe_updates = 1", out: map[SetKey]interface{}{{Key: "sql_safe_updates", Scope: ImplicitStr}: int64(1)}, scope: SessionStr, + }, { + sql: "set @foo = 42", + out: map[SetKey]interface{}{ + {Key: "foo", Scope: VariableStr}: int64(42), + }, + scope: ImplicitStr, + }, { + sql: "set @foo.bar.baz = 42", + out: map[SetKey]interface{}{ + {Key: "foo.bar.baz", Scope: VariableStr}: int64(42), + }, + scope: ImplicitStr, + }, { + sql: "set @`string` = 'abc', @`float` = 4.2, @`int` = 42", + out: map[SetKey]interface{}{ + {Key: "string", Scope: VariableStr}: "abc", + {Key: "float", Scope: VariableStr}: 4.2, + {Key: "int", Scope: VariableStr}: int64(42), + }, + scope: ImplicitStr, + }, { + sql: "set session @foo = 42", + err: "unsupported in set: scope and user defined variables", + }, { + sql: "set global @foo = 42", + err: "unsupported in set: scope and user defined variables", }} for _, tcase := range testcases { - out, _, err := ExtractSetValues(tcase.sql) - if tcase.err != "" { - if err == nil || err.Error() != tcase.err { - t.Errorf("ExtractSetValues(%s): %v, want '%s'", tcase.sql, err, tcase.err) + t.Run(tcase.sql, func(t *testing.T) { + out, _, err := ExtractSetValues(tcase.sql) + if tcase.err != "" { + require.Error(t, err, tcase.err) + } else if err != nil { + require.NoError(t, err) } - } else if err != nil { - t.Errorf("ExtractSetValues(%s): %v, want no error", tcase.sql, err) - } - if !reflect.DeepEqual(out, tcase.out) { - t.Errorf("ExtractSetValues(%s): %v, want '%v'", tcase.sql, out, tcase.out) - } + + if diff := cmp.Diff(tcase.out, out); diff != "" { + t.Error(diff) + } + }) } } diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index f99edf0495d..4966222bd82 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -802,6 +802,7 @@ type ColIdent struct { // last field in the struct. _ [0]struct{ _ []byte } val, lowered string + at atCount } // TableIdent is a case sensitive SQL identifier. It will be escaped with @@ -1547,7 +1548,7 @@ func (node *FuncExpr) Format(buf *TrackedBuffer) { // if they match a reserved word, only if they contain illegal characters funcName := node.Name.String() - if containEscapableChars(funcName) { + if containEscapableChars(funcName, NoAt) { writeEscapedString(buf, funcName) } else { buf.WriteString(funcName) @@ -1726,7 +1727,7 @@ func (node *SetExpr) Format(buf *TrackedBuffer) { sqlVal := node.Expr.(*SQLVal) buf.Myprintf("%s %s", node.Name.String(), strings.ToLower(string(sqlVal.Val))) } else { - buf.Myprintf("%s = %v", node.Name.String(), node.Expr) + buf.Myprintf("%v = %v", node.Name, node.Expr) } } @@ -1740,10 +1741,13 @@ func (node OnDup) Format(buf *TrackedBuffer) { // Format formats the node. func (node ColIdent) Format(buf *TrackedBuffer) { - formatID(buf, node.val, node.Lowered()) + for i := NoAt; i < node.at; i++ { + buf.WriteByte('@') + } + formatID(buf, node.val, node.Lowered(), node.at) } // Format formats the node. func (node TableIdent) Format(buf *TrackedBuffer) { - formatID(buf, node.v, strings.ToLower(node.v)) + formatID(buf, node.v, strings.ToLower(node.v), NoAt) } diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go index e1b6c07bec0..2403dc7c07f 100644 --- a/go/vt/sqlparser/ast_funcs.go +++ b/go/vt/sqlparser/ast_funcs.go @@ -53,11 +53,7 @@ func Walk(visit Visit, nodes ...SQLNode) error { return kontinue } post := func(cursor *Cursor) bool { - if err != nil { - return false // now we can abort the traversal if an error was found - } - - return true + return err == nil // now we can abort the traversal if an error was found } Rewrite(node, pre, post) @@ -533,6 +529,14 @@ func NewColIdent(str string) ColIdent { } } +// NewColIdentWithAt makes a new ColIdent. +func NewColIdentWithAt(str string, at atCount) ColIdent { + return ColIdent{ + val: str, + at: at, + } +} + // IsEmpty returns true if the name is empty. func (node ColIdent) IsEmpty() bool { return node.val == "" @@ -543,7 +547,11 @@ func (node ColIdent) IsEmpty() bool { // instead. The Stringer conformance is for usage // in templates. func (node ColIdent) String() string { - return node.val + atStr := "" + for i := NoAt; i < node.at; i++ { + atStr += "@" + } + return atStr + node.val } // CompliantName returns a compliant id name @@ -631,14 +639,13 @@ func (node *TableIdent) UnmarshalJSON(b []byte) error { return nil } -func containEscapableChars(s string) bool { - isDbSystemVariable := false - if len(s) > 1 && s[:2] == "@@" { - isDbSystemVariable = true - } +func containEscapableChars(s string, at atCount) bool { + isDbSystemVariable := at != NoAt for i, c := range s { - if !isLetter(uint16(c)) && (!isDbSystemVariable || !isCarat(uint16(c))) { + letter := isLetter(uint16(c)) + systemVarChar := isDbSystemVariable && isCarat(uint16(c)) + if !(letter || systemVarChar) { if i == 0 || !isDigit(uint16(c)) { return true } @@ -653,8 +660,8 @@ func isKeyword(s string) bool { return isKeyword } -func formatID(buf *TrackedBuffer, original, lowered string) { - if containEscapableChars(original) || isKeyword(lowered) { +func formatID(buf *TrackedBuffer, original, lowered string, at atCount) { + if containEscapableChars(original, at) || isKeyword(lowered) { writeEscapedString(buf, original) } else { buf.Myprintf("%s", original) @@ -759,3 +766,14 @@ func (node *Union) AddOrder(order *Order) { func (node *Union) SetLimit(limit *Limit) { node.Limit = limit } + +type atCount int + +const ( + // NoAt represents no @ + NoAt atCount = iota + // SingleAt represents @ + SingleAt + // DoubleAt represnts @@ + DoubleAt +) diff --git a/go/vt/sqlparser/ast_test.go b/go/vt/sqlparser/ast_test.go index 579efc45dfd..ae333445058 100644 --- a/go/vt/sqlparser/ast_test.go +++ b/go/vt/sqlparser/ast_test.go @@ -669,7 +669,7 @@ func TestColIdentMarshal(t *testing.T) { func TestColIdentSize(t *testing.T) { size := unsafe.Sizeof(NewColIdent("")) - want := 2 * unsafe.Sizeof("") + want := 2*unsafe.Sizeof("") + 8 if size != want { t.Errorf("Size of ColIdent: %d, want 32", want) } diff --git a/go/vt/sqlparser/constants.go b/go/vt/sqlparser/constants.go index b194967fa3b..13c22dc8be5 100644 --- a/go/vt/sqlparser/constants.go +++ b/go/vt/sqlparser/constants.go @@ -42,6 +42,7 @@ const ( SessionStr = "session" GlobalStr = "global" VitessMetadataStr = "vitess_metadata" + VariableStr = "variable" ImplicitStr = "" // DDL strings. diff --git a/go/vt/sqlparser/expression_rewriting.go b/go/vt/sqlparser/expression_rewriting.go index 667c3eb6667..be933738aa1 100644 --- a/go/vt/sqlparser/expression_rewriting.go +++ b/go/vt/sqlparser/expression_rewriting.go @@ -31,9 +31,10 @@ func PrepareAST(in Statement, bindVars map[string]*querypb.BindVariable, prefix // BindVarNeeds represents the bind vars that need to be provided as the result of expression rewriting. type BindVarNeeds struct { - NeedLastInsertID bool - NeedDatabase bool - NeedFoundRows bool + NeedLastInsertID bool + NeedDatabase bool + NeedFoundRows bool + NeedUserDefinedVariables bool } // RewriteAST rewrites the whole AST, replacing function calls and adding column aliases to queries @@ -54,6 +55,9 @@ func RewriteAST(in Statement) (*RewriteASTResult, error) { if _, ok := er.bindVars[FoundRowsName]; ok { r.NeedFoundRows = true } + if _, ok := er.bindVars[UserDefinedVariableName]; ok { + r.NeedUserDefinedVariables = true + } return r, nil } @@ -102,10 +106,14 @@ const ( //FoundRowsName is a reserved bind var name for found_rows() FoundRowsName = "__vtfrows" + + //UserDefinedVariableName is what we prepend bind var names for user defined variables + UserDefinedVariableName = "__vtudv" ) func (er *expressionRewriter) goingDown(cursor *Cursor) bool { switch node := cursor.Node().(type) { + // select last_insert_id() -> select :__lastInsertId as `last_insert_id()` case *AliasedExpr: if node.As.IsEmpty() { buf := NewTrackedBuffer(nil) @@ -127,11 +135,11 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { } return false } - case *FuncExpr: switch { + // last_insert_id() -> :__lastInsertId case node.Name.EqualString("last_insert_id"): - if len(node.Exprs) > 0 { + if len(node.Exprs) > 0 { //last_insert_id(x) er.err = vterrors.New(vtrpc.Code_UNIMPLEMENTED, "Argument to LAST_INSERT_ID() not supported") } else { cursor.Replace(bindVarExpression(LastInsertIDName)) @@ -154,7 +162,11 @@ func (er *expressionRewriter) goingDown(cursor *Cursor) bool { er.needBindVarFor(FoundRowsName) } } - + case *ColName: + if node.Name.at == SingleAt { + cursor.Replace(bindVarExpression(UserDefinedVariableName + node.Name.CompliantName())) + er.needBindVarFor(UserDefinedVariableName) + } } return true } diff --git a/go/vt/sqlparser/expression_rewriting_test.go b/go/vt/sqlparser/expression_rewriting_test.go index b7dd524cbbb..4989675f926 100644 --- a/go/vt/sqlparser/expression_rewriting_test.go +++ b/go/vt/sqlparser/expression_rewriting_test.go @@ -23,8 +23,8 @@ import ( ) type myTestCase struct { - in, expected string - liid, db, foundRows bool + in, expected string + liid, db, foundRows, udv bool } func TestRewrites(in *testing.T) { @@ -32,67 +32,82 @@ func TestRewrites(in *testing.T) { { in: "SELECT 42", expected: "SELECT 42", - db: false, liid: false, foundRows: false, + // no bindvar needs }, { in: "SELECT last_insert_id()", expected: "SELECT :__lastInsertId as `last_insert_id()`", - db: false, liid: true, + liid: true, }, { in: "SELECT database()", expected: "SELECT :__vtdbname as `database()`", - db: true, liid: false, foundRows: false, + db: true, }, { in: "SELECT database() from test", expected: "SELECT database() from test", - db: false, liid: false, + // no bindvar needs }, { in: "SELECT last_insert_id() as test", expected: "SELECT :__lastInsertId as test", - db: false, liid: true, foundRows: false, + liid: true, }, { in: "SELECT last_insert_id() + database()", expected: "SELECT :__lastInsertId + :__vtdbname as `last_insert_id() + database()`", - db: true, liid: true, foundRows: false, + db: true, liid: true, }, { in: "select (select database()) from test", expected: "select (select database() from dual) from test", - db: false, liid: false, + // no bindvar needs }, { in: "select (select database() from dual) from test", expected: "select (select database() from dual) from test", - db: false, liid: false, + // no bindvar needs }, { in: "select (select database() from dual) from dual", expected: "select (select :__vtdbname as `database()` from dual) as `(select database() from dual)` from dual", - db: true, liid: false, foundRows: false, + db: true, }, { in: "select id from user where database()", expected: "select id from user where database()", - db: false, liid: false, + // no bindvar needs }, { in: "select table_name from information_schema.tables where table_schema = database()", expected: "select table_name from information_schema.tables where table_schema = database()", - db: false, liid: false, foundRows: false, + // no bindvar needs }, { in: "select schema()", - expected: "select :__vtdbname as 'schema()'", - db: true, liid: false, foundRows: false, + expected: "select :__vtdbname as `schema()`", + db: true, }, { - in: "select found_rows()", - expected: "select :__vtfrows as 'found_rows()'", - db: false, liid: false, foundRows: true, + in: "select found_rows()", + expected: "select :__vtfrows as `found_rows()`", + foundRows: true, + }, + { + in: "select @`x y`", + expected: "select :__vtudvx_y as `@``x y``` from dual", + udv: true, + }, + { + in: "select id from t where id = @x", + expected: "select id from t where id = :__vtudvx", + db: false, udv: true, + }, + { + in: "insert into t(id) values(@xyx)", + expected: "insert into t(id) values(:__vtudvxyx)", + db: false, udv: true, }, } @@ -105,7 +120,7 @@ func TestRewrites(in *testing.T) { require.NoError(t, err) expected, err := Parse(tc.expected) - require.NoError(t, err) + require.NoError(t, err, "test expectation does not parse [%s]", tc.expected) s := String(expected) require.Equal(t, s, String(result.AST)) diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index 2ba6a02cf05..acd55be20fc 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -25,6 +25,8 @@ import ( "strings" "sync" "testing" + + "github.com/stretchr/testify/require" ) var ( @@ -829,6 +831,10 @@ var ( input: "set sql_safe_updates = 0", }, { input: "set sql_safe_updates = 1", + }, { + input: "set @variable = 42", + }, { + input: "set @period.variable = 42", }, { input: "alter ignore table a add foo", output: "alter table a", @@ -1357,6 +1363,12 @@ var ( }, { input: "use `ks:-80@master`", output: "use `ks:-80@master`", + }, { + input: "use @replica", + output: "use `@replica`", + }, { + input: "use ks@replica", + output: "use `ks@replica`", }, { input: "describe foobar", output: "otherread", @@ -1539,10 +1551,7 @@ func TestValid(t *testing.T) { tcase.output = tcase.input } tree, err := Parse(tcase.input) - if err != nil { - t.Errorf("Parse(%q) err: %v, want nil", tcase.input, err) - return - } + require.NoError(t, err) out := String(tree) if out != tcase.output { t.Errorf("Parse(%q) = %q, want: %q", tcase.input, out, tcase.output) diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index 3415bbfbbbe..40c3a374383 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -159,281 +159,283 @@ const FORCE = 57394 const ON = 57395 const USING = 57396 const ID = 57397 -const HEX = 57398 -const STRING = 57399 -const INTEGRAL = 57400 -const FLOAT = 57401 -const HEXNUM = 57402 -const VALUE_ARG = 57403 -const LIST_ARG = 57404 -const COMMENT = 57405 -const COMMENT_KEYWORD = 57406 -const BIT_LITERAL = 57407 -const NULL = 57408 -const TRUE = 57409 -const FALSE = 57410 -const OFF = 57411 -const OR = 57412 -const AND = 57413 -const NOT = 57414 -const BETWEEN = 57415 -const CASE = 57416 -const WHEN = 57417 -const THEN = 57418 -const ELSE = 57419 -const END = 57420 -const LE = 57421 -const GE = 57422 -const NE = 57423 -const NULL_SAFE_EQUAL = 57424 -const IS = 57425 -const LIKE = 57426 -const REGEXP = 57427 -const IN = 57428 -const SHIFT_LEFT = 57429 -const SHIFT_RIGHT = 57430 -const DIV = 57431 -const MOD = 57432 -const UNARY = 57433 -const COLLATE = 57434 -const BINARY = 57435 -const UNDERSCORE_BINARY = 57436 -const UNDERSCORE_UTF8MB4 = 57437 -const INTERVAL = 57438 -const JSON_EXTRACT_OP = 57439 -const JSON_UNQUOTE_EXTRACT_OP = 57440 -const CREATE = 57441 -const ALTER = 57442 -const DROP = 57443 -const RENAME = 57444 -const ANALYZE = 57445 -const ADD = 57446 -const FLUSH = 57447 -const SCHEMA = 57448 -const TABLE = 57449 -const INDEX = 57450 -const VIEW = 57451 -const TO = 57452 -const IGNORE = 57453 -const IF = 57454 -const UNIQUE = 57455 -const PRIMARY = 57456 -const COLUMN = 57457 -const SPATIAL = 57458 -const FULLTEXT = 57459 -const KEY_BLOCK_SIZE = 57460 -const CHECK = 57461 -const ACTION = 57462 -const CASCADE = 57463 -const CONSTRAINT = 57464 -const FOREIGN = 57465 -const NO = 57466 -const REFERENCES = 57467 -const RESTRICT = 57468 -const SHOW = 57469 -const DESCRIBE = 57470 -const EXPLAIN = 57471 -const DATE = 57472 -const ESCAPE = 57473 -const REPAIR = 57474 -const OPTIMIZE = 57475 -const TRUNCATE = 57476 -const MAXVALUE = 57477 -const PARTITION = 57478 -const REORGANIZE = 57479 -const LESS = 57480 -const THAN = 57481 -const PROCEDURE = 57482 -const TRIGGER = 57483 -const VINDEX = 57484 -const VINDEXES = 57485 -const STATUS = 57486 -const VARIABLES = 57487 -const WARNINGS = 57488 -const SEQUENCE = 57489 -const BEGIN = 57490 -const START = 57491 -const TRANSACTION = 57492 -const COMMIT = 57493 -const ROLLBACK = 57494 -const BIT = 57495 -const TINYINT = 57496 -const SMALLINT = 57497 -const MEDIUMINT = 57498 -const INT = 57499 -const INTEGER = 57500 -const BIGINT = 57501 -const INTNUM = 57502 -const REAL = 57503 -const DOUBLE = 57504 -const FLOAT_TYPE = 57505 -const DECIMAL = 57506 -const NUMERIC = 57507 -const TIME = 57508 -const TIMESTAMP = 57509 -const DATETIME = 57510 -const YEAR = 57511 -const CHAR = 57512 -const VARCHAR = 57513 -const BOOL = 57514 -const CHARACTER = 57515 -const VARBINARY = 57516 -const NCHAR = 57517 -const TEXT = 57518 -const TINYTEXT = 57519 -const MEDIUMTEXT = 57520 -const LONGTEXT = 57521 -const BLOB = 57522 -const TINYBLOB = 57523 -const MEDIUMBLOB = 57524 -const LONGBLOB = 57525 -const JSON = 57526 -const ENUM = 57527 -const GEOMETRY = 57528 -const POINT = 57529 -const LINESTRING = 57530 -const POLYGON = 57531 -const GEOMETRYCOLLECTION = 57532 -const MULTIPOINT = 57533 -const MULTILINESTRING = 57534 -const MULTIPOLYGON = 57535 -const NULLX = 57536 -const AUTO_INCREMENT = 57537 -const APPROXNUM = 57538 -const SIGNED = 57539 -const UNSIGNED = 57540 -const ZEROFILL = 57541 -const COLLATION = 57542 -const DATABASES = 57543 -const TABLES = 57544 -const VITESS_METADATA = 57545 -const VSCHEMA = 57546 -const FULL = 57547 -const PROCESSLIST = 57548 -const COLUMNS = 57549 -const FIELDS = 57550 -const ENGINES = 57551 -const PLUGINS = 57552 -const NAMES = 57553 -const CHARSET = 57554 -const GLOBAL = 57555 -const SESSION = 57556 -const ISOLATION = 57557 -const LEVEL = 57558 -const READ = 57559 -const WRITE = 57560 -const ONLY = 57561 -const REPEATABLE = 57562 -const COMMITTED = 57563 -const UNCOMMITTED = 57564 -const SERIALIZABLE = 57565 -const CURRENT_TIMESTAMP = 57566 -const DATABASE = 57567 -const CURRENT_DATE = 57568 -const CURRENT_TIME = 57569 -const LOCALTIME = 57570 -const LOCALTIMESTAMP = 57571 -const UTC_DATE = 57572 -const UTC_TIME = 57573 -const UTC_TIMESTAMP = 57574 -const REPLACE = 57575 -const CONVERT = 57576 -const CAST = 57577 -const SUBSTR = 57578 -const SUBSTRING = 57579 -const GROUP_CONCAT = 57580 -const SEPARATOR = 57581 -const TIMESTAMPADD = 57582 -const TIMESTAMPDIFF = 57583 -const MATCH = 57584 -const AGAINST = 57585 -const BOOLEAN = 57586 -const LANGUAGE = 57587 -const WITH = 57588 -const QUERY = 57589 -const EXPANSION = 57590 -const UNUSED = 57591 -const ARRAY = 57592 -const CUME_DIST = 57593 -const DESCRIPTION = 57594 -const DENSE_RANK = 57595 -const EMPTY = 57596 -const EXCEPT = 57597 -const FIRST_VALUE = 57598 -const GROUPING = 57599 -const GROUPS = 57600 -const JSON_TABLE = 57601 -const LAG = 57602 -const LAST_VALUE = 57603 -const LATERAL = 57604 -const LEAD = 57605 -const MEMBER = 57606 -const NTH_VALUE = 57607 -const NTILE = 57608 -const OF = 57609 -const OVER = 57610 -const PERCENT_RANK = 57611 -const RANK = 57612 -const RECURSIVE = 57613 -const ROW_NUMBER = 57614 -const SYSTEM = 57615 -const WINDOW = 57616 -const ACTIVE = 57617 -const ADMIN = 57618 -const BUCKETS = 57619 -const CLONE = 57620 -const COMPONENT = 57621 -const DEFINITION = 57622 -const ENFORCED = 57623 -const EXCLUDE = 57624 -const FOLLOWING = 57625 -const GEOMCOLLECTION = 57626 -const GET_MASTER_PUBLIC_KEY = 57627 -const HISTOGRAM = 57628 -const HISTORY = 57629 -const INACTIVE = 57630 -const INVISIBLE = 57631 -const LOCKED = 57632 -const MASTER_COMPRESSION_ALGORITHMS = 57633 -const MASTER_PUBLIC_KEY_PATH = 57634 -const MASTER_TLS_CIPHERSUITES = 57635 -const MASTER_ZSTD_COMPRESSION_LEVEL = 57636 -const NESTED = 57637 -const NETWORK_NAMESPACE = 57638 -const NOWAIT = 57639 -const NULLS = 57640 -const OJ = 57641 -const OLD = 57642 -const OPTIONAL = 57643 -const ORDINALITY = 57644 -const ORGANIZATION = 57645 -const OTHERS = 57646 -const PATH = 57647 -const PERSIST = 57648 -const PERSIST_ONLY = 57649 -const PRECEDING = 57650 -const PRIVILEGE_CHECKS_USER = 57651 -const PROCESS = 57652 -const RANDOM = 57653 -const REFERENCE = 57654 -const REQUIRE_ROW_FORMAT = 57655 -const RESOURCE = 57656 -const RESPECT = 57657 -const RESTART = 57658 -const RETAIN = 57659 -const REUSE = 57660 -const ROLE = 57661 -const SECONDARY = 57662 -const SECONDARY_ENGINE = 57663 -const SECONDARY_LOAD = 57664 -const SECONDARY_UNLOAD = 57665 -const SKIP = 57666 -const SRID = 57667 -const THREAD_PRIORITY = 57668 -const TIES = 57669 -const UNBOUNDED = 57670 -const VCPU = 57671 -const VISIBLE = 57672 +const AT_ID = 57398 +const AT_AT_ID = 57399 +const HEX = 57400 +const STRING = 57401 +const INTEGRAL = 57402 +const FLOAT = 57403 +const HEXNUM = 57404 +const VALUE_ARG = 57405 +const LIST_ARG = 57406 +const COMMENT = 57407 +const COMMENT_KEYWORD = 57408 +const BIT_LITERAL = 57409 +const NULL = 57410 +const TRUE = 57411 +const FALSE = 57412 +const OFF = 57413 +const OR = 57414 +const AND = 57415 +const NOT = 57416 +const BETWEEN = 57417 +const CASE = 57418 +const WHEN = 57419 +const THEN = 57420 +const ELSE = 57421 +const END = 57422 +const LE = 57423 +const GE = 57424 +const NE = 57425 +const NULL_SAFE_EQUAL = 57426 +const IS = 57427 +const LIKE = 57428 +const REGEXP = 57429 +const IN = 57430 +const SHIFT_LEFT = 57431 +const SHIFT_RIGHT = 57432 +const DIV = 57433 +const MOD = 57434 +const UNARY = 57435 +const COLLATE = 57436 +const BINARY = 57437 +const UNDERSCORE_BINARY = 57438 +const UNDERSCORE_UTF8MB4 = 57439 +const INTERVAL = 57440 +const JSON_EXTRACT_OP = 57441 +const JSON_UNQUOTE_EXTRACT_OP = 57442 +const CREATE = 57443 +const ALTER = 57444 +const DROP = 57445 +const RENAME = 57446 +const ANALYZE = 57447 +const ADD = 57448 +const FLUSH = 57449 +const SCHEMA = 57450 +const TABLE = 57451 +const INDEX = 57452 +const VIEW = 57453 +const TO = 57454 +const IGNORE = 57455 +const IF = 57456 +const UNIQUE = 57457 +const PRIMARY = 57458 +const COLUMN = 57459 +const SPATIAL = 57460 +const FULLTEXT = 57461 +const KEY_BLOCK_SIZE = 57462 +const CHECK = 57463 +const ACTION = 57464 +const CASCADE = 57465 +const CONSTRAINT = 57466 +const FOREIGN = 57467 +const NO = 57468 +const REFERENCES = 57469 +const RESTRICT = 57470 +const SHOW = 57471 +const DESCRIBE = 57472 +const EXPLAIN = 57473 +const DATE = 57474 +const ESCAPE = 57475 +const REPAIR = 57476 +const OPTIMIZE = 57477 +const TRUNCATE = 57478 +const MAXVALUE = 57479 +const PARTITION = 57480 +const REORGANIZE = 57481 +const LESS = 57482 +const THAN = 57483 +const PROCEDURE = 57484 +const TRIGGER = 57485 +const VINDEX = 57486 +const VINDEXES = 57487 +const STATUS = 57488 +const VARIABLES = 57489 +const WARNINGS = 57490 +const SEQUENCE = 57491 +const BEGIN = 57492 +const START = 57493 +const TRANSACTION = 57494 +const COMMIT = 57495 +const ROLLBACK = 57496 +const BIT = 57497 +const TINYINT = 57498 +const SMALLINT = 57499 +const MEDIUMINT = 57500 +const INT = 57501 +const INTEGER = 57502 +const BIGINT = 57503 +const INTNUM = 57504 +const REAL = 57505 +const DOUBLE = 57506 +const FLOAT_TYPE = 57507 +const DECIMAL = 57508 +const NUMERIC = 57509 +const TIME = 57510 +const TIMESTAMP = 57511 +const DATETIME = 57512 +const YEAR = 57513 +const CHAR = 57514 +const VARCHAR = 57515 +const BOOL = 57516 +const CHARACTER = 57517 +const VARBINARY = 57518 +const NCHAR = 57519 +const TEXT = 57520 +const TINYTEXT = 57521 +const MEDIUMTEXT = 57522 +const LONGTEXT = 57523 +const BLOB = 57524 +const TINYBLOB = 57525 +const MEDIUMBLOB = 57526 +const LONGBLOB = 57527 +const JSON = 57528 +const ENUM = 57529 +const GEOMETRY = 57530 +const POINT = 57531 +const LINESTRING = 57532 +const POLYGON = 57533 +const GEOMETRYCOLLECTION = 57534 +const MULTIPOINT = 57535 +const MULTILINESTRING = 57536 +const MULTIPOLYGON = 57537 +const NULLX = 57538 +const AUTO_INCREMENT = 57539 +const APPROXNUM = 57540 +const SIGNED = 57541 +const UNSIGNED = 57542 +const ZEROFILL = 57543 +const COLLATION = 57544 +const DATABASES = 57545 +const TABLES = 57546 +const VITESS_METADATA = 57547 +const VSCHEMA = 57548 +const FULL = 57549 +const PROCESSLIST = 57550 +const COLUMNS = 57551 +const FIELDS = 57552 +const ENGINES = 57553 +const PLUGINS = 57554 +const NAMES = 57555 +const CHARSET = 57556 +const GLOBAL = 57557 +const SESSION = 57558 +const ISOLATION = 57559 +const LEVEL = 57560 +const READ = 57561 +const WRITE = 57562 +const ONLY = 57563 +const REPEATABLE = 57564 +const COMMITTED = 57565 +const UNCOMMITTED = 57566 +const SERIALIZABLE = 57567 +const CURRENT_TIMESTAMP = 57568 +const DATABASE = 57569 +const CURRENT_DATE = 57570 +const CURRENT_TIME = 57571 +const LOCALTIME = 57572 +const LOCALTIMESTAMP = 57573 +const UTC_DATE = 57574 +const UTC_TIME = 57575 +const UTC_TIMESTAMP = 57576 +const REPLACE = 57577 +const CONVERT = 57578 +const CAST = 57579 +const SUBSTR = 57580 +const SUBSTRING = 57581 +const GROUP_CONCAT = 57582 +const SEPARATOR = 57583 +const TIMESTAMPADD = 57584 +const TIMESTAMPDIFF = 57585 +const MATCH = 57586 +const AGAINST = 57587 +const BOOLEAN = 57588 +const LANGUAGE = 57589 +const WITH = 57590 +const QUERY = 57591 +const EXPANSION = 57592 +const UNUSED = 57593 +const ARRAY = 57594 +const CUME_DIST = 57595 +const DESCRIPTION = 57596 +const DENSE_RANK = 57597 +const EMPTY = 57598 +const EXCEPT = 57599 +const FIRST_VALUE = 57600 +const GROUPING = 57601 +const GROUPS = 57602 +const JSON_TABLE = 57603 +const LAG = 57604 +const LAST_VALUE = 57605 +const LATERAL = 57606 +const LEAD = 57607 +const MEMBER = 57608 +const NTH_VALUE = 57609 +const NTILE = 57610 +const OF = 57611 +const OVER = 57612 +const PERCENT_RANK = 57613 +const RANK = 57614 +const RECURSIVE = 57615 +const ROW_NUMBER = 57616 +const SYSTEM = 57617 +const WINDOW = 57618 +const ACTIVE = 57619 +const ADMIN = 57620 +const BUCKETS = 57621 +const CLONE = 57622 +const COMPONENT = 57623 +const DEFINITION = 57624 +const ENFORCED = 57625 +const EXCLUDE = 57626 +const FOLLOWING = 57627 +const GEOMCOLLECTION = 57628 +const GET_MASTER_PUBLIC_KEY = 57629 +const HISTOGRAM = 57630 +const HISTORY = 57631 +const INACTIVE = 57632 +const INVISIBLE = 57633 +const LOCKED = 57634 +const MASTER_COMPRESSION_ALGORITHMS = 57635 +const MASTER_PUBLIC_KEY_PATH = 57636 +const MASTER_TLS_CIPHERSUITES = 57637 +const MASTER_ZSTD_COMPRESSION_LEVEL = 57638 +const NESTED = 57639 +const NETWORK_NAMESPACE = 57640 +const NOWAIT = 57641 +const NULLS = 57642 +const OJ = 57643 +const OLD = 57644 +const OPTIONAL = 57645 +const ORDINALITY = 57646 +const ORGANIZATION = 57647 +const OTHERS = 57648 +const PATH = 57649 +const PERSIST = 57650 +const PERSIST_ONLY = 57651 +const PRECEDING = 57652 +const PRIVILEGE_CHECKS_USER = 57653 +const PROCESS = 57654 +const RANDOM = 57655 +const REFERENCE = 57656 +const REQUIRE_ROW_FORMAT = 57657 +const RESOURCE = 57658 +const RESPECT = 57659 +const RESTART = 57660 +const RETAIN = 57661 +const REUSE = 57662 +const ROLE = 57663 +const SECONDARY = 57664 +const SECONDARY_ENGINE = 57665 +const SECONDARY_LOAD = 57666 +const SECONDARY_UNLOAD = 57667 +const SKIP = 57668 +const SRID = 57669 +const THREAD_PRIORITY = 57670 +const TIES = 57671 +const UNBOUNDED = 57672 +const VCPU = 57673 +const VISIBLE = 57674 var yyToknames = [...]string{ "$end", @@ -494,6 +496,8 @@ var yyToknames = [...]string{ "','", "')'", "ID", + "AT_ID", + "AT_AT_ID", "HEX", "STRING", "INTEGRAL", @@ -797,1796 +801,1743 @@ var yyExca = [...]int{ 1, -1, -2, 0, -1, 3, - 5, 29, + 5, 32, -2, 4, -1, 37, - 161, 302, - 162, 302, - -2, 290, - -1, 322, - 113, 646, - -2, 642, - -1, 323, - 113, 647, - -2, 643, - -1, 392, - 83, 895, - -2, 63, - -1, 393, - 83, 813, - -2, 64, - -1, 398, - 83, 782, - -2, 608, - -1, 400, - 83, 843, - -2, 610, - -1, 699, - 1, 355, - 5, 355, - 12, 355, - 13, 355, - 14, 355, - 15, 355, - 17, 355, - 19, 355, - 30, 355, - 31, 355, - 43, 355, - 44, 355, - 45, 355, - 46, 355, - 47, 355, - 49, 355, - 50, 355, - 53, 355, - 54, 355, - 56, 355, - 57, 355, - 348, 355, - -2, 373, + 163, 307, + 164, 307, + -2, 295, + -1, 325, + 115, 651, + -2, 647, + -1, 326, + 115, 652, + -2, 648, + -1, 395, + 85, 900, + -2, 66, + -1, 396, + 85, 818, + -2, 67, + -1, 401, + 85, 787, + -2, 613, + -1, 403, + 85, 848, + -2, 615, -1, 702, - 54, 44, - 56, 44, - -2, 48, - -1, 854, - 113, 649, - -2, 645, - -1, 1084, - 5, 30, - -2, 441, - -1, 1115, - 5, 29, - -2, 582, - -1, 1359, - 5, 30, - -2, 583, - -1, 1412, - 5, 29, - -2, 585, - -1, 1492, - 5, 30, - -2, 586, + 1, 360, + 5, 360, + 12, 360, + 13, 360, + 14, 360, + 15, 360, + 17, 360, + 19, 360, + 30, 360, + 31, 360, + 43, 360, + 44, 360, + 45, 360, + 46, 360, + 47, 360, + 49, 360, + 50, 360, + 53, 360, + 54, 360, + 56, 360, + 57, 360, + 350, 360, + -2, 378, + -1, 705, + 54, 47, + 56, 47, + -2, 51, + -1, 857, + 115, 654, + -2, 650, + -1, 1087, + 5, 33, + -2, 446, + -1, 1118, + 5, 32, + -2, 587, + -1, 1364, + 5, 33, + -2, 588, + -1, 1417, + 5, 32, + -2, 590, + -1, 1497, + 5, 33, + -2, 591, } const yyPrivate = 57344 -const yyLast = 16995 +const yyLast = 16474 var yyAct = [...]int{ - 323, 1526, 1516, 1321, 1480, 654, 1392, 1379, 1210, 1118, - 327, 353, 1261, 340, 969, 1295, 942, 1425, 1262, 998, - 965, 1136, 1119, 940, 552, 1258, 968, 1012, 1163, 978, - 1268, 57, 81, 397, 301, 1274, 266, 1233, 801, 266, - 817, 879, 890, 1189, 1180, 886, 1075, 982, 715, 992, - 908, 292, 856, 585, 944, 591, 714, 521, 386, 701, - 391, 922, 1008, 310, 929, 597, 606, 266, 81, 383, - 325, 388, 266, 1142, 266, 696, 668, 704, 56, 1519, - 1503, 61, 695, 669, 1514, 653, 3, 1031, 1490, 1511, - 1322, 541, 1502, 563, 1489, 263, 293, 294, 295, 296, - 1250, 1030, 299, 329, 1351, 526, 314, 63, 64, 65, - 66, 67, 1454, 619, 618, 628, 629, 621, 622, 623, - 624, 625, 626, 627, 620, 1289, 385, 630, 556, 1151, - 1035, 523, 1150, 525, 716, 1152, 717, 300, 365, 1029, - 371, 372, 369, 370, 368, 367, 366, 1290, 1291, 960, - 961, 959, 579, 298, 373, 374, 261, 257, 258, 259, - 574, 394, 297, 1171, 575, 572, 573, 1234, 991, 1212, - 1382, 1399, 253, 999, 1342, 251, 1340, 255, 291, 790, - 577, 567, 568, 789, 1214, 787, 1513, 1510, 1481, 1026, - 1023, 1024, 1209, 1022, 558, 923, 560, 983, 1473, 1534, - 542, 1215, 528, 255, 1426, 1236, 794, 1530, 778, 1137, - 1139, 578, 1284, 985, 1283, 1282, 524, 1428, 788, 791, - 531, 268, 1213, 1434, 1206, 1033, 1036, 557, 559, 538, - 1208, 256, 1043, 642, 643, 1042, 1093, 1462, 1090, 1238, - 1362, 1242, 1219, 1237, 1147, 1235, 1103, 1069, 985, 828, - 1240, 710, 266, 610, 548, 1307, 966, 266, 620, 1239, - 630, 630, 1028, 266, 24, 25, 52, 27, 28, 266, - 254, 955, 1241, 1243, 81, 260, 81, 81, 1164, 81, - 825, 81, 1455, 43, 1027, 1427, 1138, 81, 29, 48, - 49, 252, 535, 605, 536, 822, 1471, 537, 522, 1443, - 554, 1272, 718, 999, 818, 1488, 1308, 70, 276, 38, - 984, 532, 555, 54, 1252, 1528, 540, 81, 1529, 780, - 1527, 909, 547, 1032, 1476, 1435, 1433, 1207, 549, 1205, - 1169, 520, 863, 286, 54, 1088, 593, 1087, 1034, 642, - 643, 642, 643, 71, 859, 984, 861, 862, 860, 581, - 582, 544, 545, 546, 604, 603, 619, 618, 628, 629, - 621, 622, 623, 624, 625, 626, 627, 620, 1089, 603, - 630, 605, 600, 1494, 31, 32, 34, 33, 36, 553, - 50, 266, 266, 266, 269, 605, 819, 1388, 988, 595, - 81, 272, 594, 909, 989, 1100, 81, 1535, 1387, 280, - 275, 527, 37, 44, 45, 694, 1184, 46, 47, 35, - 1076, 621, 622, 623, 624, 625, 626, 627, 620, 604, - 603, 630, 1183, 39, 40, 985, 41, 42, 640, 584, - 1172, 880, 278, 881, 1348, 1496, 605, 1536, 285, 1153, - 693, 1154, 702, 671, 673, 675, 677, 679, 681, 682, - 672, 674, 703, 678, 680, 522, 683, 846, 848, 849, - 1472, 712, 708, 847, 1406, 270, 619, 618, 628, 629, - 621, 622, 623, 624, 625, 626, 627, 620, 529, 530, - 630, 1385, 1181, 394, 1052, 699, 623, 624, 625, 626, - 627, 620, 282, 273, 630, 283, 284, 289, 831, 832, - 806, 274, 277, 22, 271, 288, 287, 1469, 53, 619, - 618, 628, 629, 621, 622, 623, 624, 625, 626, 627, - 620, 266, 984, 630, 604, 603, 81, 981, 979, 1324, - 980, 266, 266, 81, 81, 81, 977, 983, 1164, 266, - 827, 605, 266, 1431, 1512, 266, 1159, 604, 603, 266, - 250, 81, 882, 352, 1498, 584, 81, 81, 81, 266, - 81, 81, 800, 305, 605, 799, 604, 603, 81, 81, - 913, 1431, 1484, 1254, 1431, 584, 805, 781, 826, 779, - 726, 776, 803, 605, 550, 79, 1066, 1067, 1068, 543, - 782, 783, 534, 1354, 533, 604, 603, 81, 792, 1431, - 1463, 385, 266, 584, 798, 1431, 1430, 1440, 81, 889, - 1439, 795, 605, 1377, 1376, 380, 381, 1304, 811, 354, - 51, 396, 1364, 584, 986, 857, 1361, 584, 833, 1314, - 1313, 619, 618, 628, 629, 621, 622, 623, 624, 625, - 626, 627, 620, 1271, 854, 630, 1310, 1311, 1259, 852, - 1211, 1271, 81, 343, 342, 345, 346, 347, 348, 835, - 892, 842, 344, 349, 1310, 1309, 1082, 584, 926, 584, - 1222, 51, 899, 902, 850, 892, 584, 706, 910, 306, - 725, 724, 58, 1357, 706, 81, 81, 24, 931, 934, - 935, 936, 932, 266, 933, 937, 1143, 24, 1275, 1276, - 24, 266, 266, 1442, 853, 266, 266, 1143, 926, 266, - 266, 266, 81, 883, 884, 1082, 1411, 858, 1312, 925, - 707, 1113, 709, 1082, 1155, 81, 1114, 707, 950, 705, - 906, 958, 952, 918, 919, 1106, 54, 949, 1105, 705, - 926, 1082, 894, 705, 803, 926, 54, 307, 711, 54, - 1395, 1271, 924, 829, 793, 54, 1504, 1394, 993, 1000, - 1001, 1002, 1369, 1013, 948, 951, 1300, 1158, 953, 1009, - 957, 1004, 956, 1275, 1276, 1521, 1003, 1016, 1517, 266, - 81, 1302, 81, 973, 994, 995, 996, 997, 266, 266, - 266, 266, 266, 1278, 266, 266, 54, 1259, 266, 81, - 1005, 1006, 1007, 1014, 699, 394, 1185, 823, 699, 797, - 1130, 1128, 699, 1197, 841, 1131, 1129, 266, 970, 266, - 266, 1281, 1280, 1132, 266, 935, 936, 396, 1054, 396, - 396, 1127, 396, 1126, 396, 1010, 1011, 1508, 1017, 1501, - 396, 1218, 1049, 1195, 311, 312, 1506, 1037, 1038, 1039, - 1040, 1041, 598, 1044, 1045, 1064, 1063, 1046, 1176, 723, - 598, 1168, 854, 586, 551, 599, 1478, 1057, 596, 1477, - 608, 583, 857, 599, 1409, 587, 1048, 1166, 1160, 1355, - 1390, 1448, 1019, 1053, 796, 1058, 939, 1059, 308, 309, - 302, 1062, 303, 562, 58, 562, 562, 1447, 562, 1061, - 562, 931, 934, 935, 936, 932, 562, 933, 937, 1397, - 1196, 1143, 576, 1071, 1094, 1201, 1198, 1191, 1199, 1194, - 1091, 1190, 853, 816, 1192, 1193, 51, 266, 266, 266, - 266, 266, 1523, 1522, 1523, 601, 1120, 1459, 1200, 266, - 1383, 639, 266, 396, 641, 824, 266, 60, 62, 720, - 266, 55, 1, 1515, 1323, 1391, 1025, 1479, 1099, 1424, - 1294, 976, 967, 69, 858, 519, 68, 1470, 975, 81, - 974, 1432, 652, 1144, 656, 657, 658, 659, 660, 661, - 662, 663, 664, 1156, 667, 670, 670, 670, 676, 670, - 670, 676, 670, 684, 685, 686, 687, 688, 689, 690, - 1141, 700, 1133, 1122, 1123, 1381, 1125, 1148, 1115, 1121, - 987, 1165, 1124, 1170, 1173, 1174, 1347, 81, 81, 1175, - 990, 1177, 1178, 1179, 1145, 1301, 1146, 894, 1161, 1162, - 699, 699, 699, 699, 699, 1167, 1475, 731, 729, 730, - 728, 733, 732, 727, 279, 699, 389, 81, 938, 1182, - 719, 1015, 602, 699, 72, 1204, 1203, 1021, 821, 570, - 571, 266, 970, 281, 638, 1060, 1149, 1202, 395, 1266, - 81, 830, 590, 1188, 1446, 1396, 1098, 665, 907, 396, - 328, 845, 341, 338, 339, 836, 396, 396, 396, 1217, - 1112, 619, 618, 628, 629, 621, 622, 623, 624, 625, - 626, 627, 620, 320, 396, 630, 612, 326, 318, 396, - 396, 396, 698, 396, 396, 691, 1225, 81, 81, 1226, - 1220, 396, 396, 1260, 1120, 1232, 1263, 1245, 930, 1251, - 1244, 928, 927, 384, 1277, 1273, 697, 854, 1221, 1350, - 1453, 81, 1057, 840, 26, 562, 59, 313, 19, 18, - 837, 1346, 562, 562, 562, 17, 81, 20, 81, 81, - 1279, 608, 16, 1224, 396, 834, 15, 14, 539, 30, - 562, 1285, 1293, 21, 1286, 562, 562, 562, 13, 562, - 562, 12, 1292, 11, 10, 9, 266, 562, 562, 1297, - 1298, 1299, 1270, 8, 1305, 1306, 7, 1255, 6, 5, - 4, 304, 1265, 23, 266, 885, 2, 0, 0, 0, - 81, 0, 0, 81, 81, 81, 266, 0, 1288, 0, - 81, 911, 0, 266, 891, 893, 619, 618, 628, 629, - 621, 622, 623, 624, 625, 626, 627, 620, 915, 916, - 630, 1316, 1329, 0, 0, 1315, 1331, 0, 0, 970, - 0, 970, 0, 0, 1317, 0, 1319, 0, 0, 0, - 0, 51, 0, 1318, 0, 396, 1338, 895, 896, 0, - 0, 901, 904, 905, 0, 1328, 656, 0, 396, 0, - 0, 0, 1120, 0, 1335, 1336, 1356, 1337, 0, 316, - 1339, 0, 1341, 81, 1366, 1330, 917, 0, 0, 920, - 921, 81, 1365, 0, 0, 0, 0, 1156, 0, 0, - 0, 0, 0, 1224, 0, 0, 81, 0, 0, 0, - 941, 1375, 0, 81, 700, 0, 699, 0, 700, 1384, - 0, 1386, 0, 396, 0, 396, 628, 629, 621, 622, - 623, 624, 625, 626, 627, 620, 1378, 0, 630, 0, - 0, 0, 396, 0, 0, 0, 1398, 0, 0, 0, - 0, 0, 81, 81, 0, 81, 0, 0, 0, 1263, - 81, 0, 81, 81, 81, 266, 1410, 1418, 81, 1419, - 1421, 1422, 396, 0, 0, 1405, 970, 0, 0, 0, - 0, 1423, 0, 1429, 0, 81, 266, 1436, 0, 562, - 1417, 562, 1444, 0, 0, 1437, 0, 1438, 0, 0, - 0, 0, 0, 0, 0, 0, 1393, 0, 562, 0, - 0, 0, 0, 1263, 1460, 0, 0, 0, 0, 0, - 0, 0, 81, 0, 644, 645, 646, 647, 648, 649, - 650, 651, 1468, 81, 81, 1467, 1412, 0, 0, 1482, - 1065, 0, 0, 1078, 1486, 1445, 0, 1079, 1483, 0, - 0, 0, 0, 81, 0, 1084, 1085, 1086, 0, 1491, - 1120, 0, 1092, 1070, 266, 1095, 1096, 0, 0, 911, - 0, 1102, 81, 0, 0, 1104, 0, 0, 1107, 1108, - 1109, 1110, 1111, 1500, 0, 0, 0, 1080, 1081, 1461, - 0, 0, 0, 1505, 1507, 81, 0, 0, 0, 0, - 561, 1135, 1509, 0, 0, 0, 1097, 0, 1520, 0, - 0, 0, 396, 0, 0, 1531, 0, 0, 0, 0, - 0, 0, 0, 1495, 1353, 0, 1393, 970, 0, 0, - 0, 0, 1116, 1117, 0, 0, 700, 700, 700, 700, - 700, 0, 0, 0, 0, 589, 0, 0, 0, 0, - 0, 941, 0, 1140, 0, 0, 0, 0, 0, 700, - 1186, 396, 619, 618, 628, 629, 621, 622, 623, 624, - 625, 626, 627, 620, 0, 0, 630, 0, 0, 0, - 0, 264, 588, 592, 290, 0, 0, 1345, 0, 0, - 396, 0, 0, 0, 0, 0, 0, 0, 0, 611, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 317, - 0, 0, 387, 396, 0, 0, 0, 264, 0, 264, - 0, 0, 0, 0, 0, 0, 0, 562, 0, 0, - 0, 0, 0, 0, 655, 0, 0, 0, 0, 0, - 0, 1230, 1231, 666, 0, 0, 0, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 562, 911, 0, 0, - 1267, 1269, 619, 618, 628, 629, 621, 622, 623, 624, - 625, 626, 627, 620, 0, 0, 630, 0, 0, 0, - 0, 0, 0, 0, 1269, 0, 619, 618, 628, 629, - 621, 622, 623, 624, 625, 626, 627, 620, 0, 396, - 630, 396, 1296, 0, 0, 0, 855, 1227, 0, 864, - 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, - 875, 876, 877, 878, 1264, 0, 51, 619, 618, 628, - 629, 621, 622, 623, 624, 625, 626, 627, 620, 0, - 0, 630, 618, 628, 629, 621, 622, 623, 624, 625, - 626, 627, 620, 1320, 0, 630, 1325, 1326, 1327, 0, - 0, 0, 0, 396, 914, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 564, 565, 0, 566, - 0, 569, 0, 0, 0, 0, 1332, 580, 0, 0, - 0, 0, 0, 0, 1334, 0, 0, 264, 0, 0, - 0, 0, 264, 0, 0, 1343, 1344, 0, 264, 0, - 0, 0, 0, 0, 264, 911, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1358, 1359, 1360, 0, 1363, - 0, 0, 700, 0, 807, 0, 396, 0, 0, 0, - 0, 0, 0, 0, 1380, 0, 1374, 1077, 0, 0, - 0, 0, 0, 0, 0, 0, 820, 0, 0, 396, - 1349, 0, 0, 0, 0, 0, 396, 619, 618, 628, - 629, 621, 622, 623, 624, 625, 626, 627, 620, 0, - 0, 630, 0, 843, 844, 0, 0, 0, 0, 0, - 0, 0, 1371, 1372, 1373, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1414, 1415, 0, 1416, 0, - 0, 0, 0, 1380, 0, 1380, 1380, 1380, 0, 0, - 0, 1296, 0, 0, 0, 562, 264, 264, 264, 0, - 0, 0, 1420, 0, 0, 0, 655, 0, 1380, 897, - 898, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1072, 1073, 1074, 0, 0, - 0, 1449, 1450, 1451, 1452, 0, 1456, 1264, 1457, 1458, - 1413, 0, 0, 0, 0, 1474, 0, 0, 0, 0, - 1464, 0, 1465, 1466, 0, 0, 396, 396, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 964, 0, - 1441, 0, 0, 911, 0, 0, 1493, 0, 0, 0, - 0, 0, 0, 0, 1487, 0, 0, 0, 0, 0, - 0, 1264, 1492, 51, 0, 1499, 777, 0, 0, 0, - 0, 0, 0, 784, 785, 786, 0, 0, 0, 0, - 1497, 0, 0, 0, 0, 0, 0, 0, 1380, 0, - 0, 804, 0, 0, 0, 0, 808, 809, 810, 0, - 812, 813, 0, 0, 0, 0, 264, 0, 814, 815, - 0, 0, 0, 0, 0, 0, 264, 264, 0, 0, - 0, 0, 0, 0, 264, 1532, 1533, 264, 0, 0, - 264, 0, 0, 0, 802, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 264, 0, 1055, 1056, 614, 592, - 617, 0, 0, 0, 0, 0, 631, 632, 633, 634, - 635, 636, 637, 1518, 615, 616, 613, 619, 618, 628, - 629, 621, 622, 623, 624, 625, 626, 627, 620, 0, - 0, 630, 0, 0, 0, 0, 0, 264, 0, 0, - 0, 0, 0, 0, 0, 0, 802, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1228, 1229, 1083, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1246, 1247, 0, 1248, 1249, 1101, - 0, 0, 0, 0, 0, 0, 0, 0, 317, 1256, - 1257, 0, 0, 317, 317, 0, 0, 317, 317, 317, - 0, 0, 0, 912, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 317, 317, 317, 317, 317, 0, 264, 0, - 0, 0, 0, 0, 0, 0, 264, 946, 0, 0, - 264, 264, 0, 0, 264, 954, 802, 0, 0, 0, - 0, 0, 1303, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1018, 0, 1020, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1047, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1333, 0, 0, 264, 0, 0, 0, 0, 0, - 0, 0, 0, 264, 264, 264, 264, 264, 0, 264, - 264, 0, 0, 264, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 264, 0, 1050, 1051, 0, 0, 0, 264, - 0, 0, 0, 0, 802, 0, 0, 0, 0, 0, - 1253, 0, 0, 0, 0, 0, 317, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 748, - 0, 0, 0, 1287, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 317, 317, 0, 0, 0, 0, 1400, - 1401, 1402, 1403, 1404, 0, 0, 0, 1407, 1408, 0, - 0, 0, 317, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 912, 264, 264, 264, 264, 264, 0, 0, 0, - 0, 0, 0, 0, 1134, 0, 0, 264, 0, 0, - 0, 946, 0, 0, 0, 264, 0, 0, 736, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1187, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1352, 0, 749, 0, 0, 0, - 0, 0, 0, 0, 655, 0, 0, 1216, 0, 0, - 0, 0, 1367, 0, 0, 1368, 0, 0, 1370, 762, - 765, 766, 767, 768, 769, 770, 0, 771, 772, 773, - 774, 775, 750, 751, 752, 753, 734, 735, 763, 0, - 737, 0, 738, 739, 740, 741, 742, 743, 744, 745, - 746, 747, 754, 755, 756, 757, 758, 759, 760, 761, - 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, - 0, 0, 1524, 0, 0, 0, 317, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 317, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 802, - 764, 0, 0, 0, 0, 0, 0, 0, 0, 912, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1485, 655, 0, - 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 264, 0, 0, 0, 0, 0, 0, 264, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1389, 912, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 946, 0, 0, 0, 0, 0, 506, 494, 0, 451, - 509, 424, 441, 517, 442, 445, 482, 409, 464, 165, - 439, 264, 428, 404, 435, 405, 426, 453, 111, 457, - 423, 496, 467, 508, 137, 429, 515, 139, 473, 0, - 211, 153, 0, 0, 455, 498, 462, 491, 450, 483, - 414, 472, 510, 440, 480, 511, 0, 0, 0, 80, - 0, 971, 972, 0, 0, 0, 0, 0, 101, 0, - 477, 505, 437, 479, 481, 403, 474, 0, 407, 410, - 516, 501, 432, 433, 1157, 912, 0, 0, 0, 0, - 0, 454, 463, 488, 448, 0, 0, 0, 0, 264, - 0, 0, 0, 430, 0, 471, 0, 0, 0, 411, - 408, 0, 0, 452, 0, 0, 0, 413, 0, 431, - 489, 0, 401, 119, 493, 500, 449, 267, 504, 447, - 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 497, 427, 436, 105, 434, - 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 229, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 95, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 406, 0, 212, 234, 249, 99, 422, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 157, - 96, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 418, 421, 416, 417, 465, 466, 512, 513, - 514, 490, 412, 0, 419, 420, 0, 495, 502, 503, - 469, 82, 91, 138, 246, 186, 116, 235, 402, 415, - 109, 425, 0, 0, 438, 443, 444, 456, 458, 459, - 460, 461, 468, 475, 476, 478, 484, 485, 486, 487, - 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 506, - 494, 0, 451, 509, 424, 441, 517, 442, 445, 482, - 409, 464, 165, 439, 0, 428, 404, 435, 405, 426, - 453, 111, 457, 423, 496, 467, 508, 137, 429, 515, - 139, 473, 0, 211, 153, 0, 0, 455, 498, 462, - 491, 450, 483, 414, 472, 510, 440, 480, 511, 0, - 0, 0, 80, 0, 971, 972, 0, 0, 0, 0, - 0, 101, 0, 477, 505, 437, 479, 481, 403, 474, - 0, 407, 410, 516, 501, 432, 433, 0, 0, 0, - 0, 0, 0, 0, 454, 463, 488, 448, 0, 0, - 0, 0, 0, 0, 0, 0, 430, 0, 471, 0, - 0, 0, 411, 408, 0, 0, 452, 0, 0, 0, - 413, 0, 431, 489, 0, 401, 119, 493, 500, 449, - 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 497, 427, - 436, 105, 434, 193, 172, 231, 470, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 406, 0, 212, 234, - 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 418, 421, 416, 417, 465, - 466, 512, 513, 514, 490, 412, 0, 419, 420, 0, - 495, 502, 503, 469, 82, 91, 138, 246, 186, 116, - 235, 402, 415, 109, 425, 0, 0, 438, 443, 444, - 456, 458, 459, 460, 461, 468, 475, 476, 478, 484, - 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 506, 494, 0, 451, 509, 424, 441, 517, - 442, 445, 482, 409, 464, 165, 439, 0, 428, 404, - 435, 405, 426, 453, 111, 457, 423, 496, 467, 508, - 137, 429, 515, 139, 473, 0, 211, 153, 0, 0, - 455, 498, 462, 491, 450, 483, 414, 472, 510, 440, - 480, 511, 54, 0, 0, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 477, 505, 437, 479, - 481, 403, 474, 0, 407, 410, 516, 501, 432, 433, - 0, 0, 0, 0, 0, 0, 0, 454, 463, 488, - 448, 0, 0, 0, 0, 0, 0, 0, 0, 430, - 0, 471, 0, 0, 0, 411, 408, 0, 0, 452, - 0, 0, 0, 413, 0, 431, 489, 0, 401, 119, - 493, 500, 449, 267, 504, 447, 446, 507, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 497, 427, 436, 105, 434, 193, 172, 231, 470, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 406, - 0, 212, 234, 249, 99, 422, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 418, 421, - 416, 417, 465, 466, 512, 513, 514, 490, 412, 0, - 419, 420, 0, 495, 502, 503, 469, 82, 91, 138, - 246, 186, 116, 235, 402, 415, 109, 425, 0, 0, - 438, 443, 444, 456, 458, 459, 460, 461, 468, 475, - 476, 478, 484, 485, 486, 487, 492, 499, 518, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 506, 494, 0, 451, 509, - 424, 441, 517, 442, 445, 482, 409, 464, 165, 439, - 0, 428, 404, 435, 405, 426, 453, 111, 457, 423, - 496, 467, 508, 137, 429, 515, 139, 473, 0, 211, - 153, 0, 0, 455, 498, 462, 491, 450, 483, 414, - 472, 510, 440, 480, 511, 0, 0, 0, 80, 0, - 0, 0, 0, 0, 0, 0, 0, 101, 0, 477, - 505, 437, 479, 481, 403, 474, 0, 407, 410, 516, - 501, 432, 433, 0, 0, 0, 0, 0, 0, 0, - 454, 463, 488, 448, 0, 0, 0, 0, 0, 0, - 1223, 0, 430, 0, 471, 0, 0, 0, 411, 408, - 0, 0, 452, 0, 0, 0, 413, 0, 431, 489, - 0, 401, 119, 493, 500, 449, 267, 504, 447, 446, - 507, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 497, 427, 436, 105, 434, 193, - 172, 231, 470, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 406, 0, 212, 234, 249, 99, 422, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 418, 421, 416, 417, 465, 466, 512, 513, 514, - 490, 412, 0, 419, 420, 0, 495, 502, 503, 469, - 82, 91, 138, 246, 186, 116, 235, 402, 415, 109, - 425, 0, 0, 438, 443, 444, 456, 458, 459, 460, - 461, 468, 475, 476, 478, 484, 485, 486, 487, 492, - 499, 518, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 506, 494, - 0, 451, 509, 424, 441, 517, 442, 445, 482, 409, - 464, 165, 439, 0, 428, 404, 435, 405, 426, 453, - 111, 457, 423, 496, 467, 508, 137, 429, 515, 139, - 473, 0, 211, 153, 0, 0, 455, 498, 462, 491, - 450, 483, 414, 472, 510, 440, 480, 511, 0, 0, - 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, - 101, 0, 477, 505, 437, 479, 481, 403, 474, 0, - 407, 410, 516, 501, 432, 433, 0, 0, 0, 0, - 0, 0, 0, 454, 463, 488, 448, 0, 0, 0, - 0, 0, 0, 955, 0, 430, 0, 471, 0, 0, - 0, 411, 408, 0, 0, 452, 0, 0, 0, 413, - 0, 431, 489, 0, 401, 119, 493, 500, 449, 267, - 504, 447, 446, 507, 184, 0, 215, 122, 136, 97, - 83, 93, 0, 121, 162, 191, 195, 497, 427, 436, - 105, 434, 193, 172, 231, 470, 174, 192, 140, 221, - 185, 230, 240, 241, 218, 238, 245, 208, 86, 217, - 229, 102, 203, 88, 227, 214, 151, 131, 132, 87, - 0, 189, 110, 117, 107, 164, 224, 225, 106, 248, - 94, 237, 90, 95, 236, 158, 220, 228, 152, 145, - 89, 226, 150, 144, 135, 114, 124, 182, 142, 183, - 125, 155, 154, 156, 0, 406, 0, 212, 234, 249, - 99, 422, 219, 243, 244, 0, 0, 100, 118, 113, - 181, 157, 96, 127, 209, 134, 141, 188, 247, 171, - 194, 103, 233, 210, 418, 421, 416, 417, 465, 466, - 512, 513, 514, 490, 412, 0, 419, 420, 0, 495, - 502, 503, 469, 82, 91, 138, 246, 186, 116, 235, - 402, 415, 109, 425, 0, 0, 438, 443, 444, 456, - 458, 459, 460, 461, 468, 475, 476, 478, 484, 485, - 486, 487, 492, 499, 518, 84, 85, 92, 98, 104, - 108, 112, 115, 120, 123, 126, 128, 129, 130, 133, - 143, 146, 147, 148, 149, 159, 160, 161, 163, 166, - 167, 168, 169, 170, 173, 175, 176, 177, 178, 179, - 180, 187, 190, 196, 197, 198, 199, 200, 201, 202, - 204, 205, 206, 207, 213, 216, 222, 223, 232, 239, - 242, 506, 494, 0, 451, 509, 424, 441, 517, 442, - 445, 482, 409, 464, 165, 439, 0, 428, 404, 435, - 405, 426, 453, 111, 457, 423, 496, 467, 508, 137, - 429, 515, 139, 473, 0, 211, 153, 0, 0, 455, - 498, 462, 491, 450, 483, 414, 472, 510, 440, 480, - 511, 0, 0, 0, 322, 0, 0, 0, 0, 0, - 0, 0, 0, 101, 0, 477, 505, 437, 479, 481, - 403, 474, 0, 407, 410, 516, 501, 432, 433, 0, - 0, 0, 0, 0, 0, 0, 454, 463, 488, 448, - 0, 0, 0, 0, 0, 0, 851, 0, 430, 0, - 471, 0, 0, 0, 411, 408, 0, 0, 452, 0, - 0, 0, 413, 0, 431, 489, 0, 401, 119, 493, - 500, 449, 267, 504, 447, 446, 507, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 497, 427, 436, 105, 434, 193, 172, 231, 470, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 406, 0, - 212, 234, 249, 99, 422, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 418, 421, 416, - 417, 465, 466, 512, 513, 514, 490, 412, 0, 419, - 420, 0, 495, 502, 503, 469, 82, 91, 138, 246, - 186, 116, 235, 402, 415, 109, 425, 0, 0, 438, - 443, 444, 456, 458, 459, 460, 461, 468, 475, 476, - 478, 484, 485, 486, 487, 492, 499, 518, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 506, 494, 0, 451, 509, 424, - 441, 517, 442, 445, 482, 409, 464, 165, 439, 0, - 428, 404, 435, 405, 426, 453, 111, 457, 423, 496, - 467, 508, 137, 429, 515, 139, 473, 0, 211, 153, - 0, 0, 455, 498, 462, 491, 450, 483, 414, 472, - 510, 440, 480, 511, 0, 0, 0, 80, 0, 0, - 0, 0, 0, 0, 0, 0, 101, 0, 477, 505, - 437, 479, 481, 403, 474, 0, 407, 410, 516, 501, - 432, 433, 0, 0, 0, 0, 0, 0, 0, 454, - 463, 488, 448, 0, 0, 0, 0, 0, 0, 0, - 0, 430, 0, 471, 0, 0, 0, 411, 408, 0, - 0, 452, 0, 0, 0, 413, 0, 431, 489, 0, - 401, 119, 493, 500, 449, 267, 504, 447, 446, 507, - 184, 0, 215, 122, 136, 97, 83, 93, 0, 121, - 162, 191, 195, 497, 427, 436, 105, 434, 193, 172, - 231, 470, 174, 192, 140, 221, 185, 230, 240, 241, - 218, 238, 245, 208, 86, 217, 229, 102, 203, 88, - 227, 214, 151, 131, 132, 87, 0, 189, 110, 117, - 107, 164, 224, 225, 106, 248, 94, 237, 90, 95, - 236, 158, 220, 228, 152, 145, 89, 226, 150, 144, - 135, 114, 124, 182, 142, 183, 125, 155, 154, 156, - 0, 406, 0, 212, 234, 249, 99, 422, 219, 243, - 244, 0, 0, 100, 118, 113, 181, 157, 96, 127, - 209, 134, 141, 188, 247, 171, 194, 103, 233, 210, - 418, 421, 416, 417, 465, 466, 512, 513, 514, 490, - 412, 0, 419, 420, 0, 495, 502, 503, 469, 82, - 91, 138, 246, 186, 116, 235, 402, 415, 109, 425, - 0, 0, 438, 443, 444, 456, 458, 459, 460, 461, - 468, 475, 476, 478, 484, 485, 486, 487, 492, 499, - 518, 84, 85, 92, 98, 104, 108, 112, 115, 120, - 123, 126, 128, 129, 130, 133, 143, 146, 147, 148, - 149, 159, 160, 161, 163, 166, 167, 168, 169, 170, - 173, 175, 176, 177, 178, 179, 180, 187, 190, 196, - 197, 198, 199, 200, 201, 202, 204, 205, 206, 207, - 213, 216, 222, 223, 232, 239, 242, 506, 494, 0, - 451, 509, 424, 441, 517, 442, 445, 482, 409, 464, - 165, 439, 0, 428, 404, 435, 405, 426, 453, 111, - 457, 423, 496, 467, 508, 137, 429, 515, 139, 473, - 0, 211, 153, 0, 0, 455, 498, 462, 491, 450, - 483, 414, 472, 510, 440, 480, 511, 0, 0, 0, - 322, 0, 0, 0, 0, 0, 0, 0, 0, 101, - 0, 477, 505, 437, 479, 481, 403, 474, 0, 407, - 410, 516, 501, 432, 433, 0, 0, 0, 0, 0, - 0, 0, 454, 463, 488, 448, 0, 0, 0, 0, - 0, 0, 0, 0, 430, 0, 471, 0, 0, 0, - 411, 408, 0, 0, 452, 0, 0, 0, 413, 0, - 431, 489, 0, 401, 119, 493, 500, 449, 267, 504, - 447, 446, 507, 184, 0, 215, 122, 136, 97, 83, - 93, 0, 121, 162, 191, 195, 497, 427, 436, 105, - 434, 193, 172, 231, 470, 174, 192, 140, 221, 185, - 230, 240, 241, 218, 238, 245, 208, 86, 217, 229, - 102, 203, 88, 227, 214, 151, 131, 132, 87, 0, - 189, 110, 117, 107, 164, 224, 225, 106, 248, 94, - 237, 90, 95, 236, 158, 220, 228, 152, 145, 89, - 226, 150, 144, 135, 114, 124, 182, 142, 183, 125, - 155, 154, 156, 0, 406, 0, 212, 234, 249, 99, - 422, 219, 243, 244, 0, 0, 100, 118, 113, 181, - 157, 96, 127, 209, 134, 141, 188, 247, 171, 194, - 103, 233, 210, 418, 421, 416, 417, 465, 466, 512, - 513, 514, 490, 412, 0, 419, 420, 0, 495, 502, - 503, 469, 82, 91, 138, 246, 186, 116, 235, 402, - 415, 109, 425, 0, 0, 438, 443, 444, 456, 458, - 459, 460, 461, 468, 475, 476, 478, 484, 485, 486, - 487, 492, 499, 518, 84, 85, 92, 98, 104, 108, - 112, 115, 120, 123, 126, 128, 129, 130, 133, 143, - 146, 147, 148, 149, 159, 160, 161, 163, 166, 167, - 168, 169, 170, 173, 175, 176, 177, 178, 179, 180, - 187, 190, 196, 197, 198, 199, 200, 201, 202, 204, - 205, 206, 207, 213, 216, 222, 223, 232, 239, 242, - 506, 494, 0, 451, 509, 424, 441, 517, 442, 445, - 482, 409, 464, 165, 439, 0, 428, 404, 435, 405, - 426, 453, 111, 457, 423, 496, 467, 508, 137, 429, - 515, 139, 473, 0, 211, 153, 0, 0, 455, 498, - 462, 491, 450, 483, 414, 472, 510, 440, 480, 511, - 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, - 0, 0, 101, 0, 477, 505, 437, 479, 481, 403, - 474, 0, 407, 410, 516, 501, 432, 433, 0, 0, - 0, 0, 0, 0, 0, 454, 463, 488, 448, 0, - 0, 0, 0, 0, 0, 0, 0, 430, 0, 471, - 0, 0, 0, 411, 408, 0, 0, 452, 0, 0, - 0, 413, 0, 431, 489, 0, 401, 119, 493, 500, - 449, 267, 504, 447, 446, 507, 184, 0, 215, 122, - 136, 97, 83, 93, 0, 121, 162, 191, 195, 497, - 427, 436, 105, 434, 193, 172, 231, 470, 174, 192, - 140, 221, 185, 230, 240, 241, 218, 238, 245, 208, - 86, 217, 229, 102, 203, 88, 227, 214, 151, 131, - 132, 87, 0, 189, 110, 117, 107, 164, 224, 225, - 106, 248, 94, 237, 90, 399, 236, 158, 220, 228, - 152, 145, 89, 226, 150, 144, 135, 114, 124, 182, - 142, 183, 125, 155, 154, 156, 0, 406, 0, 212, - 234, 249, 99, 422, 219, 243, 244, 0, 0, 100, - 118, 113, 181, 400, 398, 127, 209, 134, 141, 188, - 247, 171, 194, 103, 233, 210, 418, 421, 416, 417, - 465, 466, 512, 513, 514, 490, 412, 0, 419, 420, - 0, 495, 502, 503, 469, 82, 91, 138, 246, 186, - 116, 235, 402, 415, 109, 425, 0, 0, 438, 443, - 444, 456, 458, 459, 460, 461, 468, 475, 476, 478, - 484, 485, 486, 487, 492, 499, 518, 84, 85, 92, - 98, 104, 108, 112, 115, 120, 123, 126, 128, 129, - 130, 133, 143, 146, 147, 148, 149, 159, 160, 161, - 163, 166, 167, 168, 169, 170, 173, 175, 176, 177, - 178, 179, 180, 187, 190, 196, 197, 198, 199, 200, - 201, 202, 204, 205, 206, 207, 213, 216, 222, 223, - 232, 239, 242, 506, 494, 0, 451, 509, 424, 441, - 517, 442, 445, 482, 409, 464, 165, 439, 0, 428, - 404, 435, 405, 426, 453, 111, 457, 423, 496, 467, - 508, 137, 429, 515, 139, 473, 0, 211, 153, 0, - 0, 455, 498, 462, 491, 450, 483, 414, 472, 510, - 440, 480, 511, 0, 0, 0, 265, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 477, 505, 437, - 479, 481, 403, 474, 0, 407, 410, 516, 501, 432, - 433, 0, 0, 0, 0, 0, 0, 0, 454, 463, - 488, 448, 0, 0, 0, 0, 0, 0, 0, 0, - 430, 0, 471, 0, 0, 0, 411, 408, 0, 0, - 452, 0, 0, 0, 413, 0, 431, 489, 0, 401, - 119, 493, 500, 449, 267, 504, 447, 446, 507, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 497, 427, 436, 105, 434, 193, 172, 231, - 470, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 406, 0, 212, 234, 249, 99, 422, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 418, - 421, 416, 417, 465, 466, 512, 513, 514, 490, 412, - 0, 419, 420, 0, 495, 502, 503, 469, 82, 91, - 138, 246, 186, 116, 235, 402, 415, 109, 425, 0, - 0, 438, 443, 444, 456, 458, 459, 460, 461, 468, - 475, 476, 478, 484, 485, 486, 487, 492, 499, 518, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 506, 494, 0, 451, - 509, 424, 441, 517, 442, 445, 482, 409, 464, 165, - 439, 0, 428, 404, 435, 405, 426, 453, 111, 457, - 423, 496, 467, 508, 137, 429, 515, 139, 473, 0, - 211, 153, 0, 0, 455, 498, 462, 491, 450, 483, - 414, 472, 510, 440, 480, 511, 0, 0, 0, 80, - 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, - 477, 505, 437, 479, 481, 403, 474, 0, 407, 410, - 516, 501, 432, 433, 0, 0, 0, 0, 0, 0, - 0, 454, 463, 488, 448, 0, 0, 0, 0, 0, - 0, 0, 0, 430, 0, 471, 0, 0, 0, 411, - 408, 0, 0, 452, 0, 0, 0, 413, 0, 431, - 489, 0, 401, 119, 493, 500, 449, 267, 504, 447, - 446, 507, 184, 0, 215, 122, 136, 97, 83, 93, - 0, 121, 162, 191, 195, 497, 427, 436, 105, 434, - 193, 172, 231, 470, 174, 192, 140, 221, 185, 230, - 240, 241, 218, 238, 245, 208, 86, 217, 713, 102, - 203, 88, 227, 214, 151, 131, 132, 87, 0, 189, - 110, 117, 107, 164, 224, 225, 106, 248, 94, 237, - 90, 399, 236, 158, 220, 228, 152, 145, 89, 226, - 150, 144, 135, 114, 124, 182, 142, 183, 125, 155, - 154, 156, 0, 406, 0, 212, 234, 249, 99, 422, - 219, 243, 244, 0, 0, 100, 118, 113, 181, 400, - 398, 127, 209, 134, 141, 188, 247, 171, 194, 103, - 233, 210, 418, 421, 416, 417, 465, 466, 512, 513, - 514, 490, 412, 0, 419, 420, 0, 495, 502, 503, - 469, 82, 91, 138, 246, 186, 116, 235, 402, 415, - 109, 425, 0, 0, 438, 443, 444, 456, 458, 459, - 460, 461, 468, 475, 476, 478, 484, 485, 486, 487, - 492, 499, 518, 84, 85, 92, 98, 104, 108, 112, - 115, 120, 123, 126, 128, 129, 130, 133, 143, 146, - 147, 148, 149, 159, 160, 161, 163, 166, 167, 168, - 169, 170, 173, 175, 176, 177, 178, 179, 180, 187, - 190, 196, 197, 198, 199, 200, 201, 202, 204, 205, - 206, 207, 213, 216, 222, 223, 232, 239, 242, 506, - 494, 0, 451, 509, 424, 441, 517, 442, 445, 482, - 409, 464, 165, 439, 0, 428, 404, 435, 405, 426, - 453, 111, 457, 423, 496, 467, 508, 137, 429, 515, - 139, 473, 0, 211, 153, 0, 0, 455, 498, 462, - 491, 450, 483, 414, 472, 510, 440, 480, 511, 0, - 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, - 0, 101, 0, 477, 505, 437, 479, 481, 403, 474, - 0, 407, 410, 516, 501, 432, 433, 0, 0, 0, - 0, 0, 0, 0, 454, 463, 488, 448, 0, 0, - 0, 0, 0, 0, 0, 0, 430, 0, 471, 0, - 0, 0, 411, 408, 0, 0, 452, 0, 0, 0, - 413, 0, 431, 489, 0, 401, 119, 493, 500, 449, - 267, 504, 447, 446, 507, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 497, 427, - 436, 105, 434, 193, 172, 231, 470, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 390, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 399, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 406, 0, 212, 234, - 249, 99, 422, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 400, 398, 393, 392, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 418, 421, 416, 417, 465, - 466, 512, 513, 514, 490, 412, 0, 419, 420, 0, - 495, 502, 503, 469, 82, 91, 138, 246, 186, 116, - 235, 402, 415, 109, 425, 0, 0, 438, 443, 444, - 456, 458, 459, 460, 461, 468, 475, 476, 478, 484, - 485, 486, 487, 492, 499, 518, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 887, 0, 324, 0, 0, - 0, 111, 0, 321, 0, 0, 0, 137, 888, 364, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, - 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, - 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, - 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, - 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, - 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, - 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, - 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, - 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, - 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, - 356, 0, 0, 0, 0, 0, 0, 962, 0, 54, - 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, - 0, 101, 344, 349, 350, 351, 963, 0, 0, 319, - 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 333, 334, 0, 0, 0, 0, 378, 0, - 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, - 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, - 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, - 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, - 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, - 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, - 0, 584, 322, 343, 342, 345, 346, 347, 348, 0, - 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, - 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 333, 334, 0, 0, 0, 0, 378, 0, - 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, - 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, - 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, - 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, - 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, - 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, - 0, 0, 322, 343, 342, 345, 346, 347, 348, 0, - 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, - 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, - 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, - 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, - 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, - 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, - 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, - 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, - 0, 0, 322, 343, 903, 345, 346, 347, 348, 0, - 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, - 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, - 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, - 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, - 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, - 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 165, 0, 0, 0, 0, 324, 0, 0, - 0, 111, 0, 321, 0, 0, 0, 137, 0, 364, - 139, 0, 0, 211, 153, 0, 0, 0, 0, 355, - 356, 0, 0, 0, 0, 0, 0, 0, 0, 54, - 0, 0, 322, 343, 900, 345, 346, 347, 348, 0, - 0, 101, 344, 349, 350, 351, 0, 0, 0, 319, - 336, 0, 363, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 333, 334, 315, 0, 0, 0, 378, 0, - 335, 0, 0, 330, 331, 332, 337, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 377, 0, 0, - 267, 0, 0, 375, 0, 184, 0, 215, 122, 136, - 97, 83, 93, 0, 121, 162, 191, 195, 0, 0, - 0, 105, 0, 193, 172, 231, 0, 174, 192, 140, - 221, 185, 230, 240, 241, 218, 238, 245, 208, 86, - 217, 229, 102, 203, 88, 227, 214, 151, 131, 132, - 87, 0, 189, 110, 117, 107, 164, 224, 225, 106, - 248, 94, 237, 90, 95, 236, 158, 220, 228, 152, - 145, 89, 226, 150, 144, 135, 114, 124, 182, 142, - 183, 125, 155, 154, 156, 0, 0, 0, 212, 234, - 249, 99, 0, 219, 243, 244, 0, 0, 100, 118, - 113, 181, 157, 96, 127, 209, 134, 141, 188, 247, - 171, 194, 103, 233, 210, 365, 376, 371, 372, 369, - 370, 368, 367, 366, 379, 357, 358, 359, 360, 362, - 0, 373, 374, 361, 82, 91, 138, 246, 186, 116, - 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 84, 85, 92, 98, - 104, 108, 112, 115, 120, 123, 126, 128, 129, 130, - 133, 143, 146, 147, 148, 149, 159, 160, 161, 163, - 166, 167, 168, 169, 170, 173, 175, 176, 177, 178, - 179, 180, 187, 190, 196, 197, 198, 199, 200, 201, - 202, 204, 205, 206, 207, 213, 216, 222, 223, 232, - 239, 242, 24, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 165, 0, 0, 0, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, - 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 324, - 0, 0, 0, 111, 0, 321, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 319, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, - 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, - 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 0, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, - 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 1525, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, - 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 584, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 0, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, - 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, - 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, - 0, 364, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 355, 356, 0, 0, 0, 0, 0, 0, 0, - 0, 54, 0, 0, 322, 343, 342, 345, 346, 347, - 348, 0, 0, 101, 344, 349, 350, 351, 0, 0, - 0, 0, 336, 0, 363, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 333, 334, 0, 0, 0, 0, - 378, 0, 335, 0, 0, 330, 331, 332, 337, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 377, - 0, 0, 267, 0, 0, 375, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 365, 376, 371, - 372, 369, 370, 368, 367, 366, 379, 357, 358, 359, - 360, 362, 0, 373, 374, 361, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, - 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, - 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, - 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 619, 618, 628, 629, 621, 622, 623, 624, 625, 626, - 627, 620, 0, 0, 630, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 607, 0, - 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, - 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 80, 0, 609, 0, 0, 0, - 0, 0, 0, 101, 0, 0, 0, 0, 0, 604, - 603, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 605, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 0, 0, - 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, - 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, - 0, 0, 0, 101, 0, 0, 0, 0, 0, 74, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 76, - 77, 0, 73, 0, 0, 0, 78, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 0, 75, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 165, 0, 0, 0, 945, 0, - 0, 0, 0, 111, 0, 0, 0, 0, 0, 137, - 0, 0, 139, 0, 0, 211, 153, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 265, 0, 947, 0, 0, 0, - 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, - 0, 0, 267, 0, 0, 0, 0, 184, 0, 215, - 122, 136, 97, 83, 93, 0, 121, 162, 191, 195, - 0, 0, 0, 105, 0, 193, 172, 231, 0, 174, - 192, 140, 221, 185, 230, 240, 241, 218, 238, 245, - 208, 86, 217, 229, 102, 203, 88, 227, 214, 151, - 131, 132, 87, 0, 189, 110, 117, 107, 164, 224, - 225, 106, 248, 94, 237, 90, 95, 236, 158, 220, - 228, 152, 145, 89, 226, 150, 144, 135, 114, 124, - 182, 142, 183, 125, 155, 154, 156, 0, 0, 0, - 212, 234, 249, 99, 0, 219, 243, 244, 0, 0, - 100, 118, 113, 181, 157, 96, 127, 209, 134, 141, - 188, 247, 171, 194, 103, 233, 210, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 82, 91, 138, 246, - 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, - 92, 98, 104, 108, 112, 115, 120, 123, 126, 128, - 129, 130, 133, 143, 146, 147, 148, 149, 159, 160, - 161, 163, 166, 167, 168, 169, 170, 173, 175, 176, - 177, 178, 179, 180, 187, 190, 196, 197, 198, 199, - 200, 201, 202, 204, 205, 206, 207, 213, 216, 222, - 223, 232, 239, 242, 24, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 165, 0, 0, 0, - 0, 0, 0, 0, 0, 111, 0, 0, 0, 0, - 0, 137, 0, 0, 139, 0, 0, 211, 153, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 54, 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 119, 0, 0, 0, 267, 0, 0, 0, 0, 184, - 0, 215, 122, 136, 97, 83, 93, 0, 121, 162, - 191, 195, 0, 0, 0, 105, 0, 193, 172, 231, - 0, 174, 192, 140, 221, 185, 230, 240, 241, 218, - 238, 245, 208, 86, 217, 229, 102, 203, 88, 227, - 214, 151, 131, 132, 87, 0, 189, 110, 117, 107, - 164, 224, 225, 106, 248, 94, 237, 90, 95, 236, - 158, 220, 228, 152, 145, 89, 226, 150, 144, 135, - 114, 124, 182, 142, 183, 125, 155, 154, 156, 0, - 0, 0, 212, 234, 249, 99, 0, 219, 243, 244, - 0, 0, 100, 118, 113, 181, 157, 96, 127, 209, - 134, 141, 188, 247, 171, 194, 103, 233, 210, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 91, - 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 84, 85, 92, 98, 104, 108, 112, 115, 120, 123, - 126, 128, 129, 130, 133, 143, 146, 147, 148, 149, - 159, 160, 161, 163, 166, 167, 168, 169, 170, 173, - 175, 176, 177, 178, 179, 180, 187, 190, 196, 197, - 198, 199, 200, 201, 202, 204, 205, 206, 207, 213, - 216, 222, 223, 232, 239, 242, 24, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 54, 0, 0, 265, 0, - 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 945, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, - 947, 0, 0, 0, 0, 0, 0, 101, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 943, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, - 0, 838, 0, 0, 839, 0, 0, 101, 0, 0, + 325, 1521, 1326, 1531, 1215, 1121, 657, 1384, 1397, 1485, + 972, 319, 1430, 968, 1139, 343, 1266, 945, 304, 330, + 1122, 656, 3, 1300, 1263, 1267, 943, 356, 57, 1001, + 1015, 332, 80, 566, 1145, 1273, 268, 971, 289, 268, + 1166, 981, 1279, 555, 804, 400, 1238, 893, 820, 889, + 882, 1078, 1183, 985, 1192, 947, 932, 911, 859, 295, + 718, 704, 699, 588, 594, 698, 394, 268, 80, 717, + 524, 525, 268, 303, 268, 389, 600, 328, 925, 313, + 609, 386, 391, 707, 1011, 671, 56, 61, 1524, 1508, + 544, 1519, 995, 1495, 1034, 1516, 1327, 266, 1507, 1494, + 1255, 397, 1356, 672, 296, 297, 298, 299, 1033, 529, + 302, 317, 1294, 63, 64, 65, 66, 67, 82, 83, + 84, 1295, 1296, 264, 260, 261, 262, 256, 388, 719, + 254, 720, 258, 526, 962, 528, 582, 82, 83, 84, + 1154, 301, 300, 1153, 963, 964, 1155, 1174, 1032, 1459, + 622, 621, 631, 632, 624, 625, 626, 627, 628, 629, + 630, 623, 994, 1387, 633, 368, 1347, 374, 375, 372, + 373, 371, 370, 369, 1217, 82, 83, 84, 1002, 577, + 916, 376, 377, 578, 575, 576, 561, 1345, 563, 294, + 793, 1239, 570, 571, 580, 581, 792, 1219, 1029, 1026, + 1027, 790, 1025, 1518, 1515, 1486, 1214, 926, 986, 1478, + 1539, 1431, 1140, 1142, 279, 1535, 545, 1211, 892, 560, + 562, 531, 258, 1213, 1220, 257, 1433, 1218, 797, 1241, + 794, 791, 781, 1439, 1036, 1039, 988, 1289, 1288, 82, + 83, 84, 263, 1287, 1096, 527, 255, 534, 271, 259, + 1046, 645, 646, 1045, 1467, 268, 536, 537, 1093, 1367, + 268, 1224, 546, 1243, 1150, 1247, 268, 1242, 1106, 1240, + 1072, 1031, 268, 553, 1245, 831, 559, 80, 541, 80, + 80, 713, 80, 1244, 80, 613, 551, 969, 633, 958, + 80, 1141, 272, 1030, 1432, 623, 1246, 1248, 633, 275, + 834, 835, 828, 70, 558, 825, 608, 283, 278, 1002, + 821, 1476, 1493, 82, 83, 84, 535, 591, 595, 1460, + 80, 543, 1212, 1448, 1210, 1533, 1202, 550, 1534, 1277, + 1532, 597, 1035, 552, 614, 987, 596, 1440, 1438, 721, + 281, 71, 1257, 538, 988, 539, 288, 1037, 540, 645, + 646, 607, 606, 547, 548, 549, 1198, 1199, 1200, 643, + 584, 585, 912, 645, 646, 866, 1313, 557, 608, 658, + 912, 991, 1103, 273, 82, 83, 84, 992, 669, 864, + 865, 863, 783, 1172, 268, 268, 268, 626, 627, 628, + 629, 630, 623, 80, 822, 633, 1481, 603, 598, 80, + 285, 276, 1499, 286, 287, 292, 1393, 1392, 830, 277, + 280, 697, 274, 291, 290, 1187, 702, 624, 625, 626, + 627, 628, 629, 630, 623, 1201, 397, 633, 1186, 1175, + 1206, 1203, 1194, 1204, 1197, 606, 1193, 607, 606, 1195, + 1196, 1540, 1091, 987, 1090, 696, 829, 705, 556, 530, + 587, 608, 1445, 1205, 608, 674, 676, 678, 680, 682, + 684, 685, 587, 607, 606, 607, 606, 706, 1069, 1070, + 1071, 711, 1501, 675, 677, 715, 681, 683, 1477, 686, + 608, 1411, 608, 1541, 586, 622, 621, 631, 632, 624, + 625, 626, 627, 628, 629, 630, 623, 1390, 1184, 633, + 253, 622, 621, 631, 632, 624, 625, 626, 627, 628, + 629, 630, 623, 607, 606, 633, 82, 83, 84, 1055, + 1259, 809, 1092, 22, 268, 1444, 532, 533, 779, 80, + 608, 782, 54, 784, 268, 268, 80, 80, 80, 1079, + 1436, 1517, 268, 1264, 862, 268, 1276, 988, 268, 802, + 803, 523, 268, 1309, 80, 82, 83, 84, 58, 80, + 80, 80, 268, 80, 80, 383, 384, 1503, 587, 810, + 989, 80, 80, 1436, 1489, 607, 606, 82, 83, 84, + 849, 851, 852, 308, 1276, 729, 850, 1436, 587, 1436, + 1468, 823, 608, 1436, 1435, 785, 786, 806, 808, 1085, + 80, 1382, 1381, 795, 895, 268, 388, 1369, 587, 801, + 54, 80, 82, 83, 84, 836, 884, 1362, 846, 847, + 82, 83, 84, 814, 1157, 1447, 798, 346, 345, 348, + 349, 350, 351, 929, 883, 1146, 347, 352, 1227, 564, + 1366, 587, 1146, 885, 860, 1317, 987, 856, 861, 1319, + 1318, 984, 982, 24, 983, 80, 1315, 1316, 855, 857, + 980, 986, 1315, 1314, 1085, 587, 845, 929, 587, 895, + 587, 658, 728, 727, 900, 901, 709, 1116, 838, 929, + 928, 897, 1117, 1085, 902, 905, 1276, 709, 80, 80, + 913, 853, 952, 24, 708, 1158, 268, 24, 961, 1109, + 1108, 1085, 54, 1509, 268, 268, 929, 708, 268, 268, + 714, 832, 268, 268, 268, 80, 796, 310, 1399, 710, + 1216, 712, 1416, 886, 887, 996, 1374, 1016, 80, 525, + 710, 1400, 708, 967, 953, 702, 1305, 1161, 955, 702, + 921, 922, 54, 702, 909, 1012, 54, 1007, 397, 934, + 937, 938, 939, 935, 1006, 936, 940, 927, 1019, 806, + 1526, 973, 1280, 1281, 844, 1522, 54, 1307, 1283, 1264, + 954, 1188, 1003, 1004, 1005, 826, 800, 1135, 837, 938, + 939, 959, 268, 80, 951, 80, 960, 1038, 956, 1286, + 1285, 268, 268, 268, 268, 268, 1130, 268, 268, 976, + 1129, 268, 80, 1513, 934, 937, 938, 939, 935, 1017, + 936, 940, 1133, 1131, 1280, 1281, 1506, 1134, 1132, 1223, + 268, 1057, 268, 268, 314, 315, 1511, 268, 1067, 1066, + 997, 998, 999, 1000, 1179, 726, 601, 894, 896, 589, + 554, 1058, 1059, 1020, 595, 1171, 1008, 1009, 1010, 602, + 1483, 590, 1040, 1041, 1042, 1043, 1044, 1482, 1047, 1048, + 1013, 1014, 1049, 1414, 1052, 856, 631, 632, 624, 625, + 626, 627, 628, 629, 630, 623, 1060, 857, 633, 898, + 899, 1051, 1169, 904, 907, 908, 601, 1163, 1056, 1360, + 1395, 860, 1022, 799, 942, 861, 305, 1061, 1065, 602, + 1062, 1453, 599, 311, 312, 306, 1064, 1086, 920, 58, + 1452, 923, 924, 1402, 1146, 579, 1528, 1527, 567, 568, + 1097, 569, 1074, 572, 1104, 1094, 819, 604, 1528, 583, + 268, 268, 268, 268, 268, 1464, 1388, 827, 60, 62, + 55, 1, 268, 1520, 1328, 268, 1396, 1118, 1123, 268, + 1028, 1484, 1429, 268, 1299, 979, 970, 69, 522, 68, + 1475, 702, 702, 702, 702, 702, 897, 326, 1102, 978, + 1156, 977, 80, 1437, 1386, 990, 702, 1173, 993, 1147, + 1306, 1162, 1159, 1170, 702, 1167, 1167, 1480, 1148, 734, + 1149, 732, 733, 1125, 1126, 1124, 1128, 1136, 1127, 81, + 731, 736, 735, 269, 1144, 973, 269, 730, 282, 392, + 941, 722, 1018, 605, 72, 1178, 1209, 1180, 1181, 1182, + 80, 80, 1151, 1208, 1024, 824, 1168, 1176, 1177, 573, + 574, 284, 641, 1063, 269, 81, 1152, 1164, 1165, 269, + 398, 269, 1271, 833, 593, 1451, 1401, 1101, 668, 910, + 80, 331, 848, 344, 341, 342, 839, 1115, 615, 329, + 1185, 321, 1068, 701, 268, 694, 1081, 933, 931, 930, + 1082, 387, 1282, 80, 1278, 700, 1226, 1355, 1087, 1088, + 1089, 1207, 1458, 843, 1191, 1095, 26, 59, 1098, 1099, + 316, 19, 883, 18, 1105, 17, 20, 16, 1107, 15, + 1222, 1110, 1111, 1112, 1113, 1114, 1229, 14, 542, 1083, + 1084, 30, 21, 13, 12, 1258, 11, 10, 9, 8, + 80, 80, 1265, 1230, 1138, 1225, 7, 1231, 1100, 1256, + 6, 5, 4, 1237, 307, 23, 1123, 2, 0, 1250, + 1260, 1270, 1249, 0, 80, 1268, 0, 0, 0, 0, + 0, 1060, 857, 0, 0, 0, 1275, 0, 1292, 80, + 0, 80, 80, 0, 0, 1167, 1167, 0, 780, 0, + 1284, 1298, 0, 0, 1291, 787, 788, 789, 0, 1290, + 1312, 0, 1293, 0, 0, 0, 0, 0, 0, 268, + 1310, 1311, 973, 807, 973, 0, 1297, 0, 811, 812, + 813, 0, 815, 816, 1302, 1303, 1304, 0, 0, 268, + 817, 818, 0, 0, 0, 80, 0, 1329, 80, 80, + 80, 268, 269, 0, 0, 80, 0, 269, 268, 0, + 0, 0, 0, 269, 0, 0, 0, 0, 0, 269, + 0, 0, 0, 0, 81, 0, 81, 81, 0, 81, + 1320, 81, 1321, 0, 1336, 0, 0, 81, 1229, 702, + 0, 1335, 0, 0, 1235, 1236, 1334, 1322, 0, 1324, + 1323, 1357, 0, 0, 1343, 0, 0, 0, 0, 0, + 0, 658, 1333, 0, 0, 0, 0, 81, 0, 1372, + 1361, 0, 1373, 0, 0, 1375, 1123, 1371, 80, 0, + 0, 0, 0, 0, 1370, 0, 80, 0, 1159, 0, + 1380, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 80, 0, + 0, 973, 1340, 1341, 0, 1342, 0, 0, 1344, 0, + 1346, 0, 1404, 0, 1389, 0, 1391, 0, 0, 0, + 0, 269, 269, 269, 0, 0, 0, 0, 0, 0, + 81, 1398, 0, 0, 0, 0, 81, 80, 80, 0, + 80, 1403, 0, 0, 1410, 80, 0, 80, 80, 80, + 268, 0, 1423, 80, 1424, 1426, 1427, 1417, 1415, 1422, + 1268, 0, 0, 0, 1383, 0, 0, 1428, 0, 0, + 80, 268, 0, 1442, 1441, 1443, 1434, 1449, 0, 0, + 0, 1337, 0, 0, 0, 0, 0, 0, 0, 1339, + 0, 0, 1021, 0, 1023, 0, 0, 0, 0, 0, + 1348, 1349, 1474, 1465, 0, 0, 0, 80, 0, 0, + 1466, 1050, 1473, 1472, 1268, 0, 0, 0, 80, 80, + 1363, 1364, 1365, 0, 1368, 0, 1487, 0, 0, 1488, + 1491, 0, 1450, 0, 1490, 658, 0, 0, 80, 0, + 1496, 1379, 0, 0, 0, 0, 0, 0, 0, 268, + 0, 1398, 973, 0, 1123, 0, 0, 80, 0, 0, + 0, 269, 0, 0, 1505, 0, 81, 0, 357, 51, + 0, 269, 269, 81, 81, 81, 0, 0, 1512, 269, + 80, 0, 269, 1510, 0, 269, 0, 1514, 0, 269, + 0, 81, 1525, 0, 0, 0, 81, 81, 81, 269, + 81, 81, 1536, 0, 0, 0, 0, 0, 81, 81, + 1500, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 0, 0, 0, 0, 0, 0, 1425, 309, 0, + 0, 0, 0, 0, 0, 0, 0, 81, 0, 1359, + 0, 0, 269, 0, 0, 0, 0, 0, 81, 0, + 0, 0, 0, 0, 1358, 0, 1454, 1455, 1456, 1457, + 0, 1461, 0, 1462, 1463, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1469, 0, 1470, 1471, 622, + 621, 631, 632, 624, 625, 626, 627, 628, 629, 630, + 623, 0, 81, 633, 622, 621, 631, 632, 624, 625, + 626, 627, 628, 629, 630, 623, 0, 0, 633, 1492, + 0, 0, 0, 0, 0, 0, 0, 1497, 0, 0, + 0, 0, 0, 0, 0, 81, 81, 0, 0, 0, + 1190, 0, 0, 269, 0, 1502, 0, 0, 0, 0, + 0, 269, 269, 0, 0, 269, 269, 0, 0, 269, + 269, 269, 81, 0, 0, 355, 0, 0, 0, 1221, + 0, 0, 0, 0, 0, 81, 621, 631, 632, 624, + 625, 626, 627, 628, 629, 630, 623, 0, 0, 633, + 1537, 1538, 617, 0, 620, 0, 0, 79, 1353, 0, + 634, 635, 636, 637, 638, 639, 640, 0, 618, 619, + 616, 622, 621, 631, 632, 624, 625, 626, 627, 628, + 629, 630, 623, 0, 0, 633, 0, 0, 0, 269, + 81, 1352, 81, 399, 0, 0, 0, 0, 269, 269, + 269, 269, 269, 0, 269, 269, 0, 0, 269, 81, + 0, 0, 0, 0, 0, 565, 0, 565, 565, 0, + 565, 0, 565, 0, 0, 0, 0, 269, 565, 269, + 269, 1232, 0, 0, 269, 622, 621, 631, 632, 624, + 625, 626, 627, 628, 629, 630, 623, 0, 51, 633, + 0, 622, 621, 631, 632, 624, 625, 626, 627, 628, + 629, 630, 623, 642, 0, 633, 644, 0, 622, 621, + 631, 632, 624, 625, 626, 627, 628, 629, 630, 623, + 0, 0, 633, 0, 1351, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 655, 0, 659, 660, 661, 662, + 663, 664, 665, 666, 667, 1350, 670, 673, 673, 673, + 679, 673, 673, 679, 673, 687, 688, 689, 690, 691, + 692, 693, 0, 703, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 269, 269, 269, + 269, 269, 0, 0, 0, 0, 0, 0, 0, 269, + 0, 0, 269, 0, 0, 0, 269, 0, 0, 0, + 269, 622, 621, 631, 632, 624, 625, 626, 627, 628, + 629, 630, 623, 0, 0, 633, 0, 0, 0, 81, + 0, 0, 622, 621, 631, 632, 624, 625, 626, 627, + 628, 629, 630, 623, 0, 0, 633, 0, 0, 0, + 1394, 0, 399, 0, 399, 399, 0, 399, 0, 399, + 1080, 0, 0, 0, 0, 399, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 81, 81, 0, + 622, 621, 631, 632, 624, 625, 626, 627, 628, 629, + 630, 623, 0, 0, 633, 611, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 565, 0, 0, + 0, 269, 0, 0, 565, 565, 565, 0, 0, 0, + 81, 0, 0, 0, 0, 0, 323, 0, 0, 0, + 0, 0, 565, 0, 0, 0, 0, 565, 565, 565, + 0, 565, 565, 0, 0, 0, 0, 0, 0, 565, + 565, 0, 0, 0, 0, 0, 0, 0, 399, 0, + 0, 0, 0, 0, 723, 0, 0, 81, 81, 622, + 621, 631, 632, 624, 625, 626, 627, 628, 629, 630, + 623, 0, 0, 633, 0, 0, 0, 0, 0, 0, + 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 81, 0, 81, 81, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 269, 0, 659, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 269, 0, 0, 0, + 0, 0, 81, 0, 0, 81, 81, 81, 269, 0, + 0, 0, 81, 0, 0, 269, 0, 0, 0, 0, + 0, 0, 944, 0, 0, 0, 703, 0, 0, 0, + 703, 0, 0, 0, 399, 0, 0, 0, 0, 0, + 0, 399, 399, 399, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 399, + 0, 0, 0, 0, 399, 399, 399, 0, 399, 399, + 0, 0, 0, 0, 0, 0, 399, 399, 0, 0, + 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, + 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, + 0, 565, 0, 565, 0, 840, 0, 0, 81, 0, + 0, 0, 0, 0, 0, 81, 611, 0, 0, 399, + 565, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 81, 0, 81, 0, 0, + 888, 0, 81, 0, 81, 81, 81, 269, 0, 0, + 81, 0, 0, 0, 0, 1073, 914, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 81, 269, 0, + 0, 0, 0, 918, 919, 0, 0, 0, 0, 0, + 647, 648, 649, 650, 651, 652, 653, 654, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 751, + 399, 0, 0, 0, 81, 0, 0, 0, 0, 0, + 0, 0, 0, 399, 0, 81, 81, 0, 0, 0, + 0, 0, 0, 0, 1119, 1120, 0, 0, 703, 703, + 703, 703, 703, 0, 0, 81, 0, 0, 0, 0, + 0, 0, 0, 944, 0, 1143, 269, 0, 0, 0, + 0, 703, 0, 0, 81, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 399, 0, + 399, 0, 0, 0, 0, 0, 0, 81, 0, 0, + 739, 24, 25, 52, 27, 28, 0, 399, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 43, 0, 0, 0, 0, 29, 48, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 399, 752, 565, + 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 765, 768, 769, 770, 771, 772, 773, 565, 774, + 775, 776, 777, 778, 753, 754, 755, 756, 737, 738, + 766, 0, 740, 0, 741, 742, 743, 744, 745, 746, + 747, 748, 749, 750, 757, 758, 759, 760, 761, 762, + 763, 764, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 31, 32, 34, 33, 36, 0, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 914, 0, 1269, 0, 51, 0, + 0, 37, 44, 45, 0, 0, 46, 47, 35, 0, + 0, 0, 767, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 39, 40, 0, 41, 42, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 399, 0, 0, + 0, 0, 858, 0, 592, 867, 868, 869, 870, 871, + 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 267, 0, 0, 293, 0, 1189, 399, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 917, 0, 0, 0, 0, 0, 703, 53, 320, 0, + 0, 390, 0, 0, 0, 399, 267, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1354, 0, 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 722, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, - 721, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1376, 1377, 1378, 0, + 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 914, 0, 0, 1272, 1274, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 565, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 399, 0, 399, 1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1269, 0, 0, 1418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, + 0, 0, 0, 0, 1446, 0, 0, 0, 0, 0, + 1325, 0, 0, 1330, 1331, 1332, 0, 0, 0, 0, + 399, 1075, 1076, 1077, 0, 1269, 0, 51, 0, 267, + 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, + 267, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 584, 80, 0, - 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 914, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 399, 0, 0, 0, 0, 0, 0, + 0, 1385, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 399, 0, 0, 0, + 0, 0, 0, 399, 0, 0, 0, 1523, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 267, 267, + 267, 0, 1419, 1420, 0, 1421, 0, 0, 0, 0, + 1385, 0, 1385, 1385, 1385, 0, 0, 0, 1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 54, 0, 0, 265, 0, - 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, + 0, 0, 1479, 0, 0, 0, 1233, 1234, 0, 0, + 0, 0, 0, 399, 399, 0, 0, 0, 0, 0, + 1251, 1252, 0, 1253, 1254, 0, 0, 0, 0, 0, + 914, 0, 0, 1498, 0, 1261, 1262, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1504, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 267, 0, + 0, 0, 0, 0, 0, 1385, 0, 0, 267, 267, + 0, 0, 0, 0, 0, 0, 267, 0, 0, 267, + 0, 0, 267, 0, 0, 0, 805, 0, 1308, 0, + 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 267, + 0, 0, 0, 0, 0, 0, 0, 0, 805, 1338, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, - 947, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 320, 0, 0, 0, 0, 320, 320, 0, 0, 320, + 320, 320, 0, 0, 0, 915, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 320, 320, 320, 320, 320, 0, + 267, 0, 0, 0, 0, 0, 0, 0, 267, 949, + 0, 0, 267, 267, 0, 0, 267, 957, 805, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1405, 1406, 1407, + 1408, 1409, 0, 0, 0, 1412, 1413, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, - 609, 0, 0, 0, 0, 0, 0, 101, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, + 0, 0, 0, 0, 0, 267, 267, 267, 267, 267, + 0, 267, 267, 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 267, 0, 1053, 1054, 0, 0, + 0, 267, 0, 0, 0, 0, 805, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 165, 0, - 0, 0, 0, 0, 0, 0, 692, 111, 0, 0, - 0, 0, 0, 137, 0, 0, 139, 0, 0, 211, - 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 265, 0, - 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 119, 0, 0, 0, 267, 0, 0, 0, - 0, 184, 0, 215, 122, 136, 97, 83, 93, 0, - 121, 162, 191, 195, 0, 0, 0, 105, 0, 193, - 172, 231, 0, 174, 192, 140, 221, 185, 230, 240, - 241, 218, 238, 245, 208, 86, 217, 229, 102, 203, - 88, 227, 214, 151, 131, 132, 87, 0, 189, 110, - 117, 107, 164, 224, 225, 106, 248, 94, 237, 90, - 95, 236, 158, 220, 228, 152, 145, 89, 226, 150, - 144, 135, 114, 124, 182, 142, 183, 125, 155, 154, - 156, 0, 0, 0, 212, 234, 249, 99, 0, 219, - 243, 244, 0, 0, 100, 118, 113, 181, 157, 96, - 127, 209, 134, 141, 188, 247, 171, 194, 103, 233, - 210, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 91, 138, 246, 186, 116, 235, 0, 0, 109, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 84, 85, 92, 98, 104, 108, 112, 115, - 120, 123, 126, 128, 129, 130, 133, 143, 146, 147, - 148, 149, 159, 160, 161, 163, 166, 167, 168, 169, - 170, 173, 175, 176, 177, 178, 179, 180, 187, 190, - 196, 197, 198, 199, 200, 201, 202, 204, 205, 206, - 207, 213, 216, 222, 223, 232, 239, 242, 382, 0, - 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 262, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, 165, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 0, 0, 0, 0, 0, - 137, 0, 0, 139, 0, 0, 211, 153, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 322, 0, 0, 0, 0, - 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, - 0, 0, 0, 267, 0, 0, 0, 0, 184, 0, - 215, 122, 136, 97, 83, 93, 0, 121, 162, 191, - 195, 0, 0, 0, 105, 0, 193, 172, 231, 0, - 174, 192, 140, 221, 185, 230, 240, 241, 218, 238, - 245, 208, 86, 217, 229, 102, 203, 88, 227, 214, - 151, 131, 132, 87, 0, 189, 110, 117, 107, 164, - 224, 225, 106, 248, 94, 237, 90, 95, 236, 158, - 220, 228, 152, 145, 89, 226, 150, 144, 135, 114, - 124, 182, 142, 183, 125, 155, 154, 156, 0, 0, - 0, 212, 234, 249, 99, 0, 219, 243, 244, 0, - 0, 100, 118, 113, 181, 157, 96, 127, 209, 134, - 141, 188, 247, 171, 194, 103, 233, 210, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 91, 138, - 246, 186, 116, 235, 0, 0, 109, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, - 85, 92, 98, 104, 108, 112, 115, 120, 123, 126, - 128, 129, 130, 133, 143, 146, 147, 148, 149, 159, - 160, 161, 163, 166, 167, 168, 169, 170, 173, 175, - 176, 177, 178, 179, 180, 187, 190, 196, 197, 198, - 199, 200, 201, 202, 204, 205, 206, 207, 213, 216, - 222, 223, 232, 239, 242, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 320, 320, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1529, 0, 0, 0, 320, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 915, 267, 267, 267, 267, 267, 0, + 0, 0, 0, 0, 0, 0, 1137, 0, 0, 267, + 0, 0, 0, 949, 0, 0, 0, 267, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 267, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 320, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 805, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 915, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, + 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 915, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 509, + 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, + 412, 467, 168, 442, 949, 431, 407, 438, 408, 429, + 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, + 142, 476, 0, 214, 156, 267, 0, 458, 501, 465, + 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, + 0, 0, 82, 83, 84, 0, 974, 975, 0, 0, + 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, + 406, 477, 0, 410, 413, 519, 504, 435, 436, 1160, + 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, + 0, 0, 0, 0, 0, 0, 0, 0, 433, 915, + 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, + 0, 0, 416, 267, 434, 492, 0, 404, 122, 496, + 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, + 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, + 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, + 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, + 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, + 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, + 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, + 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, + 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, + 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, + 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, + 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, + 0, 974, 975, 0, 0, 0, 0, 0, 104, 0, + 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, + 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, + 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, + 0, 0, 0, 433, 0, 474, 0, 0, 0, 414, + 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, + 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, + 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, + 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, + 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, + 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, + 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, + 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, + 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, + 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, + 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, + 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, + 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, + 494, 453, 486, 417, 475, 513, 443, 483, 514, 54, + 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, + 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, + 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, + 0, 0, 0, 0, 0, 0, 0, 0, 433, 0, + 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, + 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, + 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, + 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, + 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, + 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, + 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, + 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, + 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, + 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, + 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, + 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, + 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, + 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, + 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, + 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, + 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, + 0, 1228, 0, 433, 0, 474, 0, 0, 0, 414, + 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, + 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, + 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, + 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, + 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, + 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, + 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, + 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, + 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, + 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, + 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, + 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, + 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, + 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, + 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, + 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, + 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, + 0, 0, 0, 0, 0, 0, 958, 0, 433, 0, + 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, + 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, + 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, + 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, + 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, + 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, + 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, + 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, + 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, + 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, + 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, + 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, + 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, + 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, + 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, + 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, + 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, + 0, 854, 0, 433, 0, 474, 0, 0, 0, 414, + 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, + 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, + 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, + 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, + 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, + 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, + 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, + 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, + 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, + 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, + 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, + 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, + 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, + 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, + 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, + 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, + 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, + 0, 0, 0, 0, 0, 0, 0, 0, 433, 0, + 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, + 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, + 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, + 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, + 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, + 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, + 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, + 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, + 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, + 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, + 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, + 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, + 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, + 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, + 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, + 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, + 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, + 0, 0, 0, 433, 0, 474, 0, 0, 0, 414, + 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, + 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, + 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, + 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 402, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 403, + 401, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, + 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, + 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, + 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, + 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, + 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, + 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, + 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, + 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, + 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, + 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, + 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, + 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, + 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, + 0, 0, 0, 0, 0, 0, 0, 0, 433, 0, + 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, + 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, + 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 716, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 402, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, + 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 403, 401, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, + 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, + 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, + 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, + 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, + 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, + 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, + 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, + 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, + 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, + 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, + 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, + 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, + 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, + 0, 0, 0, 433, 0, 474, 0, 0, 0, 414, + 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, + 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, + 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, + 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 393, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 402, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 403, + 401, 396, 395, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, + 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, + 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, + 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, + 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, + 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, + 0, 0, 890, 0, 327, 0, 0, 0, 114, 0, + 324, 0, 0, 0, 140, 891, 367, 142, 0, 0, + 214, 156, 0, 0, 0, 0, 358, 359, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 82, + 83, 84, 346, 345, 348, 349, 350, 351, 0, 0, + 104, 347, 352, 353, 354, 0, 0, 0, 322, 339, + 0, 366, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 336, 337, 318, 0, 0, 0, 381, 0, 338, + 0, 0, 333, 334, 335, 340, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 122, 380, 0, 0, 270, + 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, + 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, + 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, + 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, + 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, + 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, + 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, + 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, + 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, + 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, + 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, + 197, 106, 236, 213, 368, 379, 374, 375, 372, 373, + 371, 370, 369, 382, 360, 361, 362, 363, 365, 0, + 376, 377, 364, 85, 94, 141, 249, 189, 119, 238, + 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, + 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, + 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, + 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, + 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, + 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, + 245, 168, 0, 0, 0, 0, 327, 0, 0, 0, + 114, 0, 324, 0, 0, 0, 140, 0, 367, 142, + 0, 0, 214, 156, 0, 0, 0, 0, 358, 359, + 0, 0, 0, 0, 0, 0, 965, 0, 54, 0, + 0, 82, 83, 84, 346, 345, 348, 349, 350, 351, + 0, 0, 104, 347, 352, 353, 354, 966, 0, 0, + 322, 339, 0, 366, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 336, 337, 0, 0, 0, 0, 381, + 0, 338, 0, 0, 333, 334, 335, 340, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 122, 380, 0, + 0, 270, 0, 0, 378, 0, 187, 0, 218, 125, + 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, + 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, + 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, + 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, + 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, + 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, + 155, 148, 92, 229, 153, 147, 138, 117, 127, 185, + 145, 186, 128, 158, 157, 159, 0, 0, 0, 215, + 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, + 121, 116, 184, 160, 99, 130, 212, 137, 144, 191, + 250, 174, 197, 106, 236, 213, 368, 379, 374, 375, + 372, 373, 371, 370, 369, 382, 360, 361, 362, 363, + 365, 0, 376, 377, 364, 85, 94, 141, 249, 189, + 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, + 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, + 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, + 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, + 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, + 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, + 235, 242, 245, 168, 0, 0, 0, 0, 327, 0, + 0, 0, 114, 0, 324, 0, 0, 0, 140, 0, + 367, 142, 0, 0, 214, 156, 0, 0, 0, 0, + 358, 359, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 587, 82, 83, 84, 346, 345, 348, 349, + 350, 351, 0, 0, 104, 347, 352, 353, 354, 0, + 0, 0, 322, 339, 0, 366, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 336, 337, 0, 0, 0, + 0, 381, 0, 338, 0, 0, 333, 334, 335, 340, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 380, 0, 0, 270, 0, 0, 378, 0, 187, 0, + 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, + 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, + 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, + 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, + 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, + 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, + 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, + 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, + 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, + 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, + 144, 191, 250, 174, 197, 106, 236, 213, 368, 379, + 374, 375, 372, 373, 371, 370, 369, 382, 360, 361, + 362, 363, 365, 0, 376, 377, 364, 85, 94, 141, + 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, + 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, + 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, + 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, + 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, + 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, + 327, 0, 0, 0, 114, 0, 324, 0, 0, 0, + 140, 0, 367, 142, 0, 0, 214, 156, 0, 0, + 0, 0, 358, 359, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 82, 83, 84, 346, 345, + 348, 349, 350, 351, 0, 0, 104, 347, 352, 353, + 354, 0, 0, 0, 322, 339, 0, 366, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 336, 337, 318, + 0, 0, 0, 381, 0, 338, 0, 0, 333, 334, + 335, 340, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 122, 380, 0, 0, 270, 0, 0, 378, 0, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, + 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 368, 379, 374, 375, 372, 373, 371, 370, 369, 382, + 360, 361, 362, 363, 365, 0, 376, 377, 364, 85, + 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, + 0, 0, 327, 0, 0, 0, 114, 0, 324, 0, + 0, 0, 140, 0, 367, 142, 0, 0, 214, 156, + 0, 0, 0, 0, 358, 359, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 82, 83, 84, + 346, 906, 348, 349, 350, 351, 0, 0, 104, 347, + 352, 353, 354, 0, 0, 0, 322, 339, 0, 366, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 336, + 337, 318, 0, 0, 0, 381, 0, 338, 0, 0, + 333, 334, 335, 340, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 122, 380, 0, 0, 270, 0, 0, + 378, 0, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, + 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 368, 379, 374, 375, 372, 373, 371, 370, + 369, 382, 360, 361, 362, 363, 365, 0, 376, 377, + 364, 85, 94, 141, 249, 189, 119, 238, 0, 0, + 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, + 0, 0, 0, 0, 327, 0, 0, 0, 114, 0, + 324, 0, 0, 0, 140, 0, 367, 142, 0, 0, + 214, 156, 0, 0, 0, 0, 358, 359, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 0, 82, + 83, 84, 346, 903, 348, 349, 350, 351, 0, 0, + 104, 347, 352, 353, 354, 0, 0, 0, 322, 339, + 0, 366, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 336, 337, 318, 0, 0, 0, 381, 0, 338, + 0, 0, 333, 334, 335, 340, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 122, 380, 0, 0, 270, + 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, + 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, + 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, + 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, + 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, + 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, + 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, + 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, + 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, + 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, + 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, + 197, 106, 236, 213, 368, 379, 374, 375, 372, 373, + 371, 370, 369, 382, 360, 361, 362, 363, 365, 0, + 376, 377, 364, 85, 94, 141, 249, 189, 119, 238, + 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, + 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, + 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, + 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, + 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, + 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, + 245, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 168, 0, 0, 0, 0, 327, 0, + 0, 0, 114, 0, 324, 0, 0, 0, 140, 0, + 367, 142, 0, 0, 214, 156, 0, 0, 0, 0, + 358, 359, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 82, 83, 84, 346, 345, 348, 349, + 350, 351, 0, 0, 104, 347, 352, 353, 354, 0, + 0, 0, 322, 339, 0, 366, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 336, 337, 0, 0, 0, + 0, 381, 0, 338, 0, 0, 333, 334, 335, 340, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 380, 0, 0, 270, 0, 0, 378, 0, 187, 0, + 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, + 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, + 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, + 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, + 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, + 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, + 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, + 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, + 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, + 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, + 144, 191, 250, 174, 197, 106, 236, 213, 368, 379, + 374, 375, 372, 373, 371, 370, 369, 382, 360, 361, + 362, 363, 365, 0, 376, 377, 364, 85, 94, 141, + 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, + 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, + 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, + 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, + 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, + 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, + 327, 0, 0, 0, 114, 0, 324, 0, 0, 0, + 140, 0, 367, 142, 0, 0, 214, 156, 0, 0, + 0, 0, 358, 359, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 0, 0, 82, 83, 84, 346, 345, + 348, 349, 350, 351, 0, 0, 104, 347, 352, 353, + 354, 0, 0, 0, 322, 339, 0, 366, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 336, 337, 0, + 0, 0, 0, 381, 0, 338, 0, 0, 333, 334, + 335, 340, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 122, 380, 0, 0, 270, 0, 0, 378, 0, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, + 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 368, 379, 374, 375, 372, 373, 371, 370, 369, 382, + 360, 361, 362, 363, 365, 0, 376, 377, 364, 85, + 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, + 0, 0, 140, 0, 367, 142, 0, 0, 214, 156, + 0, 0, 0, 0, 358, 359, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 82, 83, 84, + 346, 345, 348, 349, 350, 351, 0, 0, 104, 347, + 352, 353, 354, 0, 0, 0, 0, 339, 0, 366, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 336, + 337, 0, 0, 0, 0, 381, 0, 338, 0, 0, + 333, 334, 335, 340, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 122, 380, 0, 0, 270, 0, 0, + 378, 0, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, + 196, 175, 234, 1530, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 368, 379, 374, 375, 372, 373, 371, 370, + 369, 382, 360, 361, 362, 363, 365, 0, 376, 377, + 364, 85, 94, 141, 249, 189, 119, 238, 0, 0, + 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, + 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, + 0, 0, 0, 0, 140, 0, 367, 142, 0, 0, + 214, 156, 0, 0, 0, 0, 358, 359, 0, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 587, 82, + 83, 84, 346, 345, 348, 349, 350, 351, 0, 0, + 104, 347, 352, 353, 354, 0, 0, 0, 0, 339, + 0, 366, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 336, 337, 0, 0, 0, 0, 381, 0, 338, + 0, 0, 333, 334, 335, 340, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 122, 380, 0, 0, 270, + 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, + 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, + 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, + 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, + 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, + 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, + 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, + 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, + 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, + 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, + 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, + 197, 106, 236, 213, 368, 379, 374, 375, 372, 373, + 371, 370, 369, 382, 360, 361, 362, 363, 365, 0, + 376, 377, 364, 85, 94, 141, 249, 189, 119, 238, + 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, + 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, + 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, + 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, + 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, + 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, + 245, 168, 0, 0, 0, 0, 0, 0, 0, 0, + 114, 0, 0, 0, 0, 0, 140, 0, 367, 142, + 0, 0, 214, 156, 0, 0, 0, 0, 358, 359, + 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 0, 82, 83, 84, 346, 345, 348, 349, 350, 351, + 0, 0, 104, 347, 352, 353, 354, 0, 0, 0, + 0, 339, 0, 366, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 336, 337, 0, 0, 0, 0, 381, + 0, 338, 0, 0, 333, 334, 335, 340, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 122, 380, 0, + 0, 270, 0, 0, 378, 0, 187, 0, 218, 125, + 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, + 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, + 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, + 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, + 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, + 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, + 155, 148, 92, 229, 153, 147, 138, 117, 127, 185, + 145, 186, 128, 158, 157, 159, 0, 0, 0, 215, + 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, + 121, 116, 184, 160, 99, 130, 212, 137, 144, 191, + 250, 174, 197, 106, 236, 213, 368, 379, 374, 375, + 372, 373, 371, 370, 369, 382, 360, 361, 362, 363, + 365, 0, 376, 377, 364, 85, 94, 141, 249, 189, + 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, + 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, + 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, + 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, + 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, + 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, + 235, 242, 245, 168, 0, 0, 0, 0, 0, 0, + 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, + 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 83, 84, 0, 0, 0, 0, + 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 622, 621, 631, 632, 624, 625, 626, 627, 628, + 629, 630, 623, 0, 0, 633, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 0, 0, 0, 270, 0, 0, 0, 0, 187, 0, + 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, + 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, + 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, + 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, + 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, + 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, + 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, + 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, + 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, + 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, + 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 94, 141, + 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, + 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, + 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, + 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, + 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, + 225, 226, 235, 242, 245, 168, 0, 0, 0, 610, + 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, + 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 83, 84, 0, 612, + 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, + 0, 0, 607, 606, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 608, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, + 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, + 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, + 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, + 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 122, 76, 77, 0, 73, 0, 0, + 0, 78, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, + 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 0, 75, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 85, 94, 141, 249, 189, 119, 238, 0, 0, + 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, + 0, 0, 0, 948, 0, 0, 0, 0, 114, 0, + 0, 0, 0, 0, 140, 0, 0, 142, 0, 0, + 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, + 83, 84, 0, 950, 0, 0, 0, 0, 0, 0, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 122, 0, 0, 0, 270, + 0, 0, 0, 0, 187, 0, 218, 125, 139, 100, + 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, + 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, + 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, + 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, + 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, + 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, + 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, + 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, + 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, + 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, + 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 94, 141, 249, 189, 119, 238, + 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, + 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, + 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, + 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, + 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, + 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, + 245, 24, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, + 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, + 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 82, 83, 84, 0, 0, 0, 0, + 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 0, 0, 0, 270, 0, 0, 0, 0, 187, 0, + 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, + 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, + 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, + 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, + 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, + 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, + 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, + 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, + 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, + 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, + 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 94, 141, + 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, + 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, + 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, + 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, + 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, + 225, 226, 235, 242, 245, 168, 0, 0, 0, 948, + 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, + 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 83, 84, 0, 950, + 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, + 234, 0, 946, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, + 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, + 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, + 0, 0, 841, 0, 0, 842, 0, 0, 104, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 122, 0, 0, 0, 270, 0, 0, + 0, 0, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, + 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 85, 94, 141, 249, 189, 119, 238, 0, 0, + 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, + 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, + 725, 0, 0, 0, 140, 0, 0, 142, 0, 0, + 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, + 83, 84, 0, 724, 0, 0, 0, 0, 0, 0, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 122, 0, 0, 0, 270, + 0, 0, 0, 0, 187, 0, 218, 125, 139, 100, + 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, + 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, + 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, + 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, + 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, + 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, + 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, + 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, + 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, + 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, + 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 94, 141, 249, 189, 119, 238, + 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, + 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, + 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, + 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, + 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, + 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, + 245, 168, 0, 0, 0, 0, 0, 0, 0, 0, + 114, 0, 0, 0, 0, 0, 140, 0, 0, 142, + 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 587, 82, 83, 84, 0, 0, 0, 0, 0, 0, + 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, + 0, 270, 0, 0, 0, 0, 187, 0, 218, 125, + 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, + 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, + 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, + 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, + 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, + 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, + 155, 148, 92, 229, 153, 147, 138, 117, 127, 185, + 145, 186, 128, 158, 157, 159, 0, 0, 0, 215, + 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, + 121, 116, 184, 160, 99, 130, 212, 137, 144, 191, + 250, 174, 197, 106, 236, 213, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 85, 94, 141, 249, 189, + 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, + 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, + 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, + 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, + 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, + 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, + 235, 242, 245, 168, 0, 0, 0, 0, 0, 0, + 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, + 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 82, 83, 84, 0, 0, 0, 0, + 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 0, 0, 0, 270, 0, 0, 0, 0, 187, 0, + 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, + 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, + 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, + 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, + 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, + 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, + 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, + 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, + 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, + 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, + 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 94, 141, + 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, + 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, + 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, + 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, + 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, + 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, + 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, + 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 83, 84, 0, 950, + 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, + 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, + 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, + 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, + 0, 612, 0, 0, 0, 0, 0, 0, 104, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 122, 0, 0, 0, 270, 0, 0, + 0, 0, 187, 0, 218, 125, 139, 100, 86, 96, + 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, + 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, + 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, + 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, + 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, + 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, + 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, + 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, + 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, + 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, + 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 85, 94, 141, 249, 189, 119, 238, 0, 0, + 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, + 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, + 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, + 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, + 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, + 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, + 0, 0, 0, 0, 0, 0, 0, 695, 114, 0, + 0, 0, 0, 0, 140, 0, 0, 142, 0, 0, + 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, + 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 122, 0, 0, 0, 270, + 0, 0, 0, 0, 187, 0, 218, 125, 139, 100, + 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, + 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, + 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, + 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, + 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, + 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, + 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, + 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, + 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, + 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, + 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 94, 141, 249, 189, 119, 238, + 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, + 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, + 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, + 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, + 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, + 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, + 245, 385, 0, 0, 0, 0, 0, 0, 168, 0, + 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, + 0, 0, 0, 140, 0, 0, 142, 0, 0, 214, + 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, + 84, 0, 0, 0, 0, 0, 0, 0, 0, 104, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 122, 0, 0, 0, 270, 0, + 0, 0, 0, 187, 0, 218, 125, 139, 100, 86, + 96, 0, 124, 165, 194, 198, 0, 0, 0, 108, + 0, 196, 175, 234, 0, 177, 195, 143, 224, 188, + 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, + 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, + 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, + 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, + 229, 153, 147, 138, 117, 127, 185, 145, 186, 128, + 158, 157, 159, 0, 0, 0, 215, 237, 252, 102, + 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, + 160, 99, 130, 212, 137, 144, 191, 250, 174, 197, + 106, 236, 213, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 85, 94, 141, 249, 189, 119, 238, 0, + 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, + 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, + 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, + 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, + 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, + 208, 209, 210, 216, 219, 225, 226, 235, 242, 245, + 168, 0, 0, 0, 0, 0, 0, 0, 0, 114, + 0, 0, 0, 0, 0, 140, 0, 0, 142, 0, + 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 122, 0, 265, 0, + 270, 0, 0, 0, 0, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 0, 0, + 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 0, 0, 215, 237, + 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 94, 141, 249, 189, 119, + 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 168, 0, 0, 0, 0, 0, 0, 0, + 0, 114, 0, 0, 0, 0, 0, 140, 0, 0, + 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, + 0, 0, 270, 0, 0, 0, 0, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 0, 0, 0, 108, 0, 196, 175, 234, 0, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 0, 0, + 215, 237, 252, 102, 0, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 85, 94, 141, 249, + 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, } var yyPact = [...]int{ - 258, -1000, -270, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 879, 942, -1000, -1000, -1000, -1000, -1000, -1000, - 252, 11336, 49, 107, 33, 15657, 97, 275, 16317, -1000, - 11, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -57, -66, - -1000, 694, -1000, -1000, -1000, -1000, -1000, 873, 876, 741, - 868, 803, -1000, 8024, 75, 75, 15327, 6704, -1000, -1000, - 240, 16317, 91, 16317, -145, 73, 73, 73, -1000, -1000, + 2475, -1000, -264, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 894, 933, -1000, -1000, -1000, -1000, -1000, -1000, + 248, 11789, 2, 123, -2, 15792, 122, 181, 16124, -1000, + 20, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -79, -80, + -1000, 691, -1000, -1000, -1000, -1000, -1000, 879, 889, 711, + 883, 783, -1000, 8457, 92, 92, 15460, 7129, -1000, -1000, + 458, 16124, 118, 16124, -143, 90, 90, 90, -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, @@ -2603,21 +2554,23 @@ 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, - 96, 16317, 536, 534, 176, -1000, 16317, 71, 531, 71, - 71, 71, 16317, -1000, 141, -1000, -1000, -1000, 16317, 526, - 834, 288, 70, 3617, -1000, 3617, 3617, -1000, 3617, 20, - 3617, -59, 900, 18, -8, -1000, 3617, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 546, 844, 9356, 9356, 879, -1000, 694, -1000, -1000, -1000, - 831, -1000, -1000, 306, 924, -1000, 11006, 140, -1000, 9356, - 2043, 700, -1000, -1000, 700, -1000, -1000, 119, -1000, -1000, - 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, -1000, -1000, + -1000, -1000, -1000, 121, 16124, 497, 497, 225, -1000, 16124, + 85, 497, 85, 85, 85, 16124, -1000, 171, -1000, -1000, + -1000, 16124, 497, 810, 355, 60, 4714, -1000, 4714, 4714, + -1000, 4714, 29, 4714, -42, 903, 30, -26, -1000, 4714, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 700, -1000, 9026, 700, 700, 700, 700, 700, - 700, 700, 700, 9356, 700, 700, 700, 700, 700, 700, - 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, - 14990, 14000, 16317, 673, 666, -1000, -1000, 138, 692, 6361, - -98, -1000, -1000, -1000, 219, 13340, -1000, -1000, -1000, 829, + -1000, -1000, -1000, 393, 820, 9797, 9797, 894, -1000, 691, + -1000, -1000, -1000, 865, -1000, -1000, 329, 916, -1000, 11457, + 170, -1000, 9797, 1635, 555, -1000, -1000, 555, -1000, -1000, + 135, -1000, -1000, 10793, 10793, 10793, 10793, 10793, 10793, 10793, + 10793, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 555, -1000, 9465, 555, 555, + 555, 555, 555, 555, 555, 555, 9797, 555, 555, 555, + 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, + 555, 555, 555, 15121, 14125, 16124, 676, 665, -1000, -1000, + 166, 654, 6784, -105, -1000, -1000, -1000, 254, 13461, -1000, + -1000, -1000, 805, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -2629,200 +2582,201 @@ 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, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 624, - 16317, -1000, 2399, -1000, 523, 3617, 82, 521, 244, 519, - 16317, 16317, 3617, 3617, 3617, 26, 59, 55, 16317, 698, - 79, 16317, 861, 756, 16317, 507, 504, -1000, 6018, -1000, - 3617, 288, -1000, 440, 9356, 3617, 3617, 3617, 16317, 3617, - 3617, -1000, -1000, -1000, -1000, -1000, -1000, 3617, 3617, -1000, - 912, 293, -1000, -1000, -1000, -1000, 9356, 204, -1000, 754, - -1000, -1000, -1000, -1000, -1000, -1000, 936, 187, 522, 136, - 697, -1000, 474, 873, 546, 803, 13010, 770, -1000, -1000, - -1000, 16317, -1000, 9356, 9356, 388, -1000, 14660, -1000, -1000, - 4646, 203, 10346, 279, 255, 10346, 10346, 10346, 10346, 10346, - 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, 10346, - 373, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 494, - -1000, 694, 594, 594, 152, 152, 152, 152, 152, 152, - 152, 10676, 7034, 546, 619, 451, 9026, 8024, 8024, 9356, - 9356, 8684, 8354, 8024, 839, 242, 451, 16647, -1000, -1000, - 10016, -1000, -1000, -1000, -1000, -1000, 546, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 15987, 15987, 8024, 8024, 8024, 8024, - 8024, 42, 16317, -1000, 689, 858, -1000, -1000, -1000, 864, - 12350, 12680, 42, 683, 14000, 16317, -1000, -1000, 14000, 16317, - 4303, 5675, 692, -98, 675, -1000, -82, -86, 7364, 148, - -1000, -1000, -1000, -1000, 3274, 397, 567, 319, -47, -1000, - -1000, -1000, 703, -1000, 703, 703, 703, 703, -17, -17, - -17, -17, -1000, -1000, -1000, -1000, -1000, 721, 716, -1000, - 703, 703, 703, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 616, 16124, -1000, 2369, -1000, 497, 4714, 104, + 497, 305, 497, 16124, 16124, 4714, 4714, 4714, 40, 70, + 64, 16124, 660, 99, 16124, 870, 723, 16124, 497, 497, + -1000, 6094, -1000, 4714, 355, -1000, 459, 9797, 4714, 4714, + 4714, 16124, 4714, 4714, -1000, -1000, -1000, -1000, -1000, -1000, + 4714, 4714, -1000, 915, 299, -1000, -1000, -1000, -1000, 9797, + 212, -1000, 722, -1000, -1000, -1000, -1000, -1000, -1000, 928, + 207, 390, 160, 655, -1000, 276, 879, 393, 783, 13129, + 720, -1000, -1000, -1000, 16124, -1000, 9797, 9797, 509, -1000, + 14789, -1000, -1000, 5749, 214, 10793, 477, 286, 10793, 10793, + 10793, 10793, 10793, 10793, 10793, 10793, 10793, 10793, 10793, 10793, + 10793, 10793, 10793, 554, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 497, -1000, 691, 566, 566, 178, 178, 178, + 178, 178, 178, 178, 11125, 7461, 393, 613, 362, 9465, + 8457, 8457, 9797, 9797, 9121, 8789, 8457, 815, 281, 362, + 16124, -1000, -1000, 10461, -1000, -1000, -1000, -1000, -1000, 393, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16124, 16124, 8457, + 8457, 8457, 8457, 8457, 52, 16124, -1000, 650, 706, -1000, + -1000, -1000, 872, 12465, 12797, 52, 638, 14125, 16124, -1000, + -1000, 14125, 16124, 5404, 6439, 654, -105, 642, -1000, -101, + -93, 7793, 177, -1000, -1000, -1000, -1000, 4369, 519, 513, + 300, -55, -1000, -1000, -1000, 670, -1000, 670, 670, 670, + 670, -14, -14, -14, -14, -1000, -1000, -1000, -1000, -1000, + 699, 692, -1000, 670, 670, 670, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 714, 714, 714, 708, 708, 723, -1000, 16317, 3617, - 859, 3617, -1000, 72, -1000, -1000, -1000, 16317, 16317, 16317, - 16317, 16317, 114, 16317, 16317, 687, -1000, 16317, 3617, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 451, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 16317, 288, 16317, 16317, - 451, -1000, 424, 16317, -1000, 789, 9356, 9356, 5332, 9356, - -1000, -1000, -1000, 844, -1000, 839, 880, -1000, 821, 820, - 8024, -1000, -1000, 203, 295, -1000, -1000, 517, -1000, -1000, - -1000, -1000, 134, 700, -1000, 1602, -1000, -1000, -1000, -1000, - 279, 10346, 10346, 10346, 262, 1602, 1783, 1240, 1657, 152, - 386, 386, 153, 153, 153, 153, 153, 313, 313, -1000, - -1000, -1000, 546, -1000, -1000, -1000, 546, 8024, 8024, 685, - -1000, -1000, 9356, -1000, 546, 610, 610, 281, 346, 227, - 909, 610, 225, 903, 610, 610, 8024, 314, -1000, 9356, - 546, -1000, 133, -1000, 372, 682, 679, 610, 546, 546, - 610, 610, 691, 700, -1000, 16647, 14000, 14000, 14000, 14000, - 14000, -1000, 790, 788, -1000, 768, 767, 780, 16317, -1000, - 612, 12350, 158, 700, -1000, 14330, -1000, -1000, 899, 14000, - 684, -1000, 684, -1000, 131, -1000, -1000, 675, -98, -105, - -1000, -1000, -1000, -1000, 451, -1000, 381, 668, 2931, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 712, 488, -1000, 850, - 185, 220, 480, 849, -1000, -1000, -1000, 832, -1000, 261, - -53, -1000, -1000, 369, -17, -17, -1000, -1000, 148, 828, - 148, 148, 148, 422, 422, -1000, -1000, -1000, -1000, 361, - -1000, -1000, -1000, 345, -1000, 753, 15987, 3617, -1000, -1000, - -1000, -1000, 785, 785, 202, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 39, 596, -1000, -1000, - -1000, -1000, 10, 25, 74, -1000, 3617, -1000, 293, -1000, - -1000, -1000, -1000, -1000, 801, 451, 451, 129, -1000, -1000, - 16317, -1000, -1000, -1000, -1000, 659, -1000, -1000, -1000, 3960, - 8024, -1000, 262, 1602, 1643, -1000, 10346, 10346, -1000, -1000, - 610, 610, 8024, 451, -1000, -1000, -1000, 58, 373, 58, - 10346, 10346, -1000, 10346, 10346, -1000, -160, 667, 232, -1000, - 9356, 493, -1000, 5332, -1000, 10346, 10346, -1000, -1000, -1000, - -1000, -1000, 744, 16647, 700, -1000, 12008, 15987, 695, -1000, - 218, 858, 720, 740, 645, -1000, -1000, -1000, -1000, 779, - -1000, 778, -1000, -1000, -1000, -1000, -1000, 90, 89, 87, - 15987, -1000, 879, 9356, 684, -1000, -1000, 171, -1000, -1000, - -109, -91, -1000, -1000, -1000, 3274, -1000, 3274, 15987, 57, - -1000, 480, 480, -1000, -1000, -1000, 711, 728, 10346, -1000, - -1000, -1000, 560, 148, 148, -1000, 197, -1000, -1000, -1000, - 608, -1000, 590, 662, 573, 16317, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 690, 690, 690, 672, 672, 704, + -1000, 16124, 4714, 869, 4714, -1000, 79, -1000, -1000, -1000, + 16124, 16124, 16124, 16124, 16124, 130, 16124, 16124, 651, -1000, + 16124, 4714, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 362, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16124, + 355, 16124, 16124, 362, -1000, 457, 16124, -1000, 782, 9797, + 9797, 6094, 9797, -1000, -1000, -1000, 820, -1000, 815, 887, + -1000, 794, 793, 8457, -1000, -1000, 214, 359, -1000, -1000, + 397, -1000, -1000, -1000, -1000, 155, 555, -1000, 1993, -1000, + -1000, -1000, -1000, 477, 10793, 10793, 10793, 389, 1993, 1894, + 768, 1599, 178, 285, 285, 188, 188, 188, 188, 188, + 317, 317, -1000, -1000, -1000, 393, -1000, -1000, -1000, 393, + 8457, 8457, 645, -1000, -1000, 9797, -1000, 393, 608, 608, + 388, 500, 247, 914, 608, 233, 909, 608, 608, 8457, + 289, -1000, 9797, 393, -1000, 153, -1000, 405, 644, 643, + 608, 393, 393, 608, 608, 647, 555, -1000, 16124, 14125, + 14125, 14125, 14125, 14125, -1000, 757, 753, -1000, 770, 769, + 734, 16124, -1000, 611, 12465, 161, 555, -1000, 14457, -1000, + -1000, 902, 14125, 623, -1000, 623, -1000, 149, -1000, -1000, + 642, -105, -96, -1000, -1000, -1000, -1000, 362, -1000, 562, + 639, 4024, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 682, + 497, -1000, 859, 208, 316, 497, 854, -1000, -1000, -1000, + 816, -1000, 312, -71, -1000, -1000, 366, -14, -14, -1000, + -1000, 177, 804, 177, 177, 177, 436, 436, -1000, -1000, + -1000, -1000, 365, -1000, -1000, -1000, 352, -1000, 718, 16124, + 4714, -1000, -1000, -1000, -1000, 298, 298, 195, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 51, + 666, -1000, -1000, -1000, -1000, 13, 36, 95, -1000, 4714, + -1000, 299, -1000, -1000, -1000, -1000, -1000, 779, 362, 362, + 146, -1000, -1000, 16124, -1000, -1000, -1000, -1000, 627, -1000, + -1000, -1000, 5059, 8457, -1000, 389, 1993, 1715, -1000, 10793, + 10793, -1000, -1000, 608, 608, 8457, 362, -1000, -1000, -1000, + 80, 554, 80, 10793, 10793, -1000, 10793, 10793, -1000, -162, + 543, 258, -1000, 9797, 438, -1000, 6094, -1000, 10793, 10793, + -1000, -1000, -1000, -1000, -1000, 716, 16124, 555, -1000, 12465, + 16124, 630, -1000, 244, 706, 709, 715, 761, -1000, -1000, + -1000, -1000, 747, -1000, 746, -1000, -1000, -1000, -1000, -1000, + 116, 111, 110, 16124, -1000, 894, 9797, 623, -1000, -1000, + 187, -1000, -1000, -124, -119, -1000, -1000, -1000, 4369, -1000, + 4369, 16124, 66, -1000, 497, 497, -1000, -1000, -1000, 681, + 714, 10793, -1000, -1000, -1000, 496, 177, 177, -1000, 255, + -1000, -1000, -1000, 606, -1000, 600, 589, 593, 16124, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 16317, -1000, -1000, -1000, -1000, -1000, 15987, - -173, 471, 15987, 15987, 15987, 16317, -1000, 288, -1000, 4989, - -1000, 899, 14000, -1000, -1000, 546, -1000, 10346, 1602, 1602, - -1000, -1000, -1000, 546, 703, 703, -1000, 703, 708, -1000, - 703, 1, 703, -1, 546, 546, 1578, 1132, 997, 415, - 700, -152, -1000, 451, 9356, -1000, 1478, 537, -1000, 852, - 595, 627, -1000, -1000, 7694, 546, 570, 127, 566, -1000, - 879, 16647, 9356, -1000, -1000, 9356, 707, -1000, 9356, -1000, - -1000, -1000, 700, 700, 700, 566, 873, 451, -1000, -1000, - -1000, -1000, 2931, -1000, 557, -1000, 703, -1000, -1000, -1000, - 15987, -42, 931, 1602, -1000, -1000, -1000, -1000, -1000, -17, - 421, -17, 337, -1000, 326, 3617, -1000, -1000, -1000, -1000, - 854, -1000, 4989, -1000, -1000, 702, 696, -1000, -1000, -1000, - 896, 652, -1000, 1602, -1000, -1000, 113, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 10346, 10346, 10346, 10346, 10346, - 873, 404, 451, 10346, 10346, 846, -1000, 700, -1000, -1000, - 681, 15987, 15987, -1000, 15987, 873, -1000, 451, 451, 15987, - 451, 13670, 15987, 15987, 11666, -1000, 150, 15987, -1000, 549, - -1000, 195, -1000, -103, 148, -1000, 148, 553, 550, -1000, - 700, 647, -1000, 216, 15987, 16317, 883, 865, -1000, -1000, - 372, 372, 372, 372, 19, 546, -1000, 372, 372, 928, - -1000, 700, -1000, 694, 124, -1000, -1000, -1000, 543, 518, - -1000, 518, 518, 158, 150, -1000, 449, 213, 400, -1000, - 56, 15987, 257, 841, -1000, 838, -1000, -1000, -1000, -1000, - -1000, 35, 4989, 3274, 515, -1000, -1000, 9356, 9356, -1000, - -1000, -1000, -1000, 546, 44, -176, -1000, -1000, -1000, 16647, - 627, 546, 15987, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 312, -1000, -1000, 16317, -1000, -1000, 375, -1000, -1000, 498, - -1000, 15987, -1000, -1000, 596, 451, 604, -1000, 799, -170, - -185, 587, -1000, -1000, -1000, 701, -1000, -1000, 35, 811, - -173, -1000, 797, -1000, 15987, -1000, 32, -1000, -174, 487, - 30, -180, 725, 700, -186, 722, -1000, 923, 9686, -1000, - -1000, 925, 177, 177, 372, 546, -1000, -1000, -1000, 62, - 368, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16124, -1000, + -1000, -1000, -1000, -1000, 16124, -169, 497, 16124, 16124, 16124, + 16124, -1000, 355, -1000, 6094, -1000, 902, 14125, -1000, -1000, + 393, -1000, 10793, 1993, 1993, -1000, -1000, -1000, 393, 670, + 670, -1000, 670, 672, -1000, 670, 10, 670, -11, 393, + 393, 1846, 1825, 1732, 1699, 555, -156, -1000, 362, 9797, + -1000, 1528, 1513, -1000, 862, 490, 561, -1000, -1000, 8125, + 393, 584, 144, 551, -1000, 894, 16124, 9797, -1000, -1000, + 9797, 671, -1000, 9797, -1000, -1000, -1000, 555, 555, 555, + 551, 879, 362, -1000, -1000, -1000, -1000, 4024, -1000, 545, + -1000, 670, -1000, -1000, -1000, 16124, -51, 927, 1993, -1000, + -1000, -1000, -1000, -1000, -14, 435, -14, 344, -1000, 343, + 4714, -1000, -1000, -1000, -1000, 864, -1000, 6094, -1000, -1000, + 663, 677, -1000, -1000, -1000, 900, 577, -1000, 1993, -1000, + -1000, 117, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 10793, 10793, 10793, 10793, 10793, 879, 419, 362, 10793, 10793, + 835, -1000, 555, -1000, -1000, 687, 16124, 16124, -1000, 16124, + 879, -1000, 362, 362, 16124, 362, 13793, 16124, 16124, 12121, + -1000, 157, 16124, -1000, 537, -1000, 205, -1000, -78, 177, + -1000, 177, 468, 395, -1000, 555, 569, -1000, 238, 16124, + 16124, 896, 885, -1000, -1000, 405, 405, 405, 405, 54, + 393, -1000, 405, 405, 926, -1000, 555, -1000, 691, 139, + -1000, -1000, -1000, 533, 531, -1000, 531, 531, 161, 157, + -1000, 497, 226, 416, -1000, 65, 16124, 327, 829, -1000, + 822, -1000, -1000, -1000, -1000, -1000, 50, 6094, 4369, 517, + -1000, -1000, 9797, 9797, -1000, -1000, -1000, -1000, 393, 49, + -173, -1000, -1000, -1000, 16124, 561, 393, 16124, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 339, -1000, -1000, 16124, -1000, + -1000, 410, -1000, -1000, 511, -1000, 16124, -1000, -1000, 666, + 362, 548, -1000, 776, -166, -178, 528, -1000, -1000, -1000, + 648, -1000, -1000, 50, 791, -169, -1000, 763, -1000, 16124, + -1000, 47, -1000, -170, 484, 45, -175, 712, 555, -179, + 707, -1000, 907, 10129, -1000, -1000, 919, 185, 185, 405, + 393, -1000, -1000, -1000, 71, 412, -1000, -1000, -1000, -1000, + -1000, -1000, } var yyPgo = [...]int{ - 0, 1206, 85, 503, 1203, 1201, 1200, 1199, 1198, 1196, - 1193, 1185, 1184, 1183, 1181, 1178, 1173, 1169, 1168, 1167, - 1166, 1162, 1157, 1155, 1149, 1148, 81, 1147, 1146, 1144, - 65, 1143, 63, 1140, 1139, 46, 609, 45, 42, 1289, - 1138, 23, 82, 75, 1136, 35, 1135, 1134, 69, 1133, - 1132, 64, 1131, 1128, 59, 1115, 58, 1112, 21, 73, - 1108, 1107, 1106, 1090, 70, 1103, 1085, 1084, 13, 1083, - 1082, 83, 1081, 52, 5, 12, 11, 18, 1080, 103, - 10, 1078, 50, 1077, 1076, 1075, 1074, 31, 1072, 55, - 1071, 34, 53, 1069, 7, 61, 30, 25, 9, 71, - 56, 1068, 22, 60, 48, 1066, 1065, 550, 1064, 1063, - 40, 1060, 1059, 24, 1058, 91, 401, 1057, 1056, 1055, - 1054, 33, 0, 553, 93, 66, 1052, 1051, 1050, 1555, - 38, 54, 16, 1048, 51, 1510, 41, 1046, 1044, 37, - 1043, 1042, 1041, 1040, 1039, 1038, 1037, 49, 1036, 1035, - 1025, 19, 20, 1020, 1013, 62, 27, 1010, 1005, 971, - 44, 57, 970, 968, 47, 28, 967, 966, 965, 963, - 962, 26, 14, 961, 15, 960, 17, 959, 29, 957, - 4, 956, 6, 955, 3, 954, 8, 43, 1, 953, - 2, 952, 951, 619, 570, 77, 948, 76, + 0, 1137, 21, 523, 1135, 1134, 1132, 1131, 1130, 1126, + 1119, 1118, 1117, 1116, 1114, 1113, 1112, 1111, 1108, 1107, + 1099, 1097, 1096, 1095, 1093, 1091, 87, 1090, 1087, 1086, + 76, 1083, 79, 1082, 1077, 51, 218, 49, 47, 11, + 1076, 26, 65, 62, 1075, 42, 1074, 1072, 81, 1071, + 1069, 56, 1068, 1067, 61, 1065, 75, 1063, 14, 34, + 1061, 1059, 1058, 1057, 77, 2046, 1056, 1055, 15, 1054, + 1053, 103, 1052, 58, 6, 16, 27, 25, 1051, 31, + 19, 1049, 57, 1048, 1047, 1046, 1045, 28, 1044, 64, + 1043, 18, 63, 1042, 7, 78, 35, 24, 5, 82, + 69, 1040, 20, 66, 60, 1036, 1033, 500, 1032, 1031, + 48, 1030, 1029, 43, 1025, 90, 449, 1024, 1023, 1016, + 1014, 45, 967, 1685, 33, 80, 1013, 1012, 1011, 2664, + 44, 55, 17, 1010, 59, 639, 50, 1009, 1008, 46, + 1007, 1002, 1001, 1000, 992, 991, 989, 92, 987, 983, + 980, 29, 13, 978, 977, 84, 30, 975, 974, 973, + 52, 70, 971, 969, 53, 40, 960, 959, 958, 957, + 956, 37, 10, 955, 23, 954, 12, 952, 41, 951, + 9, 950, 8, 946, 2, 0, 944, 4, 54, 3, + 943, 1, 941, 940, 1498, 180, 83, 939, 85, } var yyR1 = [...]int{ - 0, 191, 192, 192, 1, 1, 1, 1, 1, 1, + 0, 192, 193, 193, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 2, 6, 3, 4, - 4, 5, 5, 7, 7, 29, 29, 8, 9, 9, - 9, 9, 195, 195, 48, 48, 49, 49, 95, 95, - 10, 10, 10, 10, 100, 100, 104, 104, 104, 105, - 105, 105, 105, 137, 137, 11, 11, 11, 11, 11, - 11, 11, 186, 186, 185, 184, 184, 183, 183, 182, - 17, 167, 169, 169, 168, 168, 168, 168, 161, 140, - 140, 140, 140, 143, 143, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 142, 142, 142, 142, 142, 144, - 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, - 145, 145, 145, 145, 145, 145, 145, 145, 145, 146, - 146, 146, 146, 146, 146, 146, 146, 160, 160, 147, - 147, 155, 155, 156, 156, 156, 153, 153, 154, 154, - 157, 157, 157, 149, 149, 150, 150, 158, 158, 151, - 151, 151, 152, 152, 152, 159, 159, 159, 159, 159, - 148, 148, 162, 162, 177, 177, 176, 176, 176, 166, - 166, 173, 173, 173, 173, 173, 164, 164, 165, 165, - 175, 175, 174, 163, 163, 178, 178, 178, 178, 189, - 190, 188, 188, 188, 188, 188, 170, 170, 170, 171, - 171, 171, 172, 172, 172, 12, 12, 12, 12, 12, + 1, 1, 1, 1, 185, 185, 185, 2, 2, 2, + 6, 3, 4, 4, 5, 5, 7, 7, 29, 29, + 8, 9, 9, 9, 9, 196, 196, 48, 48, 49, + 49, 95, 95, 10, 10, 10, 10, 100, 100, 104, + 104, 104, 105, 105, 105, 105, 137, 137, 11, 11, + 11, 11, 11, 11, 11, 187, 187, 186, 184, 184, + 183, 183, 182, 17, 167, 169, 169, 168, 168, 168, + 168, 161, 140, 140, 140, 140, 143, 143, 141, 141, + 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, + 142, 142, 144, 144, 144, 144, 144, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 146, 146, 146, 146, 146, 146, 146, 146, + 160, 160, 147, 147, 155, 155, 156, 156, 156, 153, + 153, 154, 154, 157, 157, 157, 149, 149, 150, 150, + 158, 158, 151, 151, 151, 152, 152, 152, 159, 159, + 159, 159, 159, 148, 148, 162, 162, 177, 177, 176, + 176, 176, 166, 166, 173, 173, 173, 173, 173, 164, + 164, 165, 165, 175, 175, 174, 163, 163, 178, 178, + 178, 178, 190, 191, 189, 189, 189, 189, 189, 170, + 170, 170, 171, 171, 171, 172, 172, 172, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 187, 187, 187, 187, 187, 187, 187, 187, - 187, 187, 187, 187, 181, 179, 179, 180, 180, 13, - 18, 18, 14, 14, 14, 14, 14, 15, 15, 19, + 12, 12, 12, 12, 12, 188, 188, 188, 188, 188, + 188, 188, 188, 188, 188, 188, 188, 188, 188, 181, + 179, 179, 180, 180, 13, 18, 18, 14, 14, 14, + 14, 14, 15, 15, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 111, 111, - 109, 109, 112, 112, 110, 110, 110, 113, 113, 113, - 114, 114, 138, 138, 138, 21, 21, 23, 23, 24, - 25, 22, 22, 22, 22, 22, 22, 22, 16, 196, - 26, 27, 27, 28, 28, 28, 32, 32, 32, 30, - 30, 30, 31, 31, 37, 37, 36, 36, 38, 38, - 38, 38, 126, 126, 126, 125, 125, 40, 40, 41, - 41, 42, 42, 43, 43, 43, 43, 57, 57, 94, - 94, 96, 96, 44, 44, 44, 44, 45, 45, 46, - 46, 47, 47, 133, 133, 132, 132, 132, 131, 131, - 50, 50, 50, 52, 51, 51, 51, 51, 53, 53, - 55, 55, 54, 54, 56, 58, 58, 58, 58, 58, - 59, 59, 39, 39, 39, 39, 39, 39, 39, 108, - 108, 61, 61, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 72, 72, 72, 72, 72, 72, 62, - 62, 62, 62, 62, 62, 62, 35, 35, 73, 73, - 73, 79, 74, 74, 65, 65, 65, 65, 65, 65, + 20, 20, 20, 111, 111, 109, 109, 112, 112, 110, + 110, 110, 113, 113, 113, 114, 114, 138, 138, 138, + 21, 21, 23, 23, 24, 25, 22, 22, 22, 22, + 22, 22, 22, 16, 197, 26, 27, 27, 28, 28, + 28, 32, 32, 32, 30, 30, 30, 31, 31, 37, + 37, 36, 36, 38, 38, 38, 38, 126, 126, 126, + 125, 125, 40, 40, 41, 41, 42, 42, 43, 43, + 43, 43, 57, 57, 94, 94, 96, 96, 44, 44, + 44, 44, 45, 45, 46, 46, 47, 47, 133, 133, + 132, 132, 132, 131, 131, 50, 50, 50, 52, 51, + 51, 51, 51, 53, 53, 55, 55, 54, 54, 56, + 58, 58, 58, 58, 58, 59, 59, 39, 39, 39, + 39, 39, 39, 39, 108, 108, 61, 61, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 72, 72, + 72, 72, 72, 72, 62, 62, 62, 62, 62, 62, + 62, 35, 35, 73, 73, 73, 79, 74, 74, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 69, 69, 69, 69, - 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 197, - 197, 71, 70, 70, 70, 70, 70, 70, 70, 33, - 33, 33, 33, 33, 136, 136, 139, 139, 139, 139, - 139, 139, 139, 139, 139, 139, 139, 139, 139, 83, - 83, 34, 34, 81, 81, 82, 84, 84, 80, 80, - 80, 64, 64, 64, 64, 64, 64, 64, 64, 66, - 66, 66, 85, 85, 86, 86, 87, 87, 88, 88, - 89, 90, 90, 90, 91, 91, 91, 91, 92, 92, - 92, 63, 63, 63, 63, 63, 63, 93, 93, 93, - 93, 97, 97, 75, 75, 77, 77, 76, 78, 98, - 98, 102, 99, 99, 103, 103, 103, 103, 101, 101, - 101, 128, 128, 128, 106, 106, 115, 115, 116, 116, - 107, 107, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 118, 118, 118, 119, 119, 120, 120, 120, - 127, 127, 123, 123, 124, 124, 129, 129, 130, 130, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 69, 69, 69, 69, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 198, 198, 71, 70, 70, 70, + 70, 70, 70, 70, 33, 33, 33, 33, 33, 136, + 136, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 83, 83, 34, 34, 81, 81, + 82, 84, 84, 80, 80, 80, 64, 64, 64, 64, + 64, 64, 64, 64, 66, 66, 66, 85, 85, 86, + 86, 87, 87, 88, 88, 89, 90, 90, 90, 91, + 91, 91, 91, 92, 92, 92, 63, 63, 63, 63, + 63, 63, 93, 93, 93, 93, 97, 97, 75, 75, + 77, 77, 76, 78, 98, 98, 102, 99, 99, 103, + 103, 103, 103, 101, 101, 101, 128, 128, 128, 106, + 106, 115, 115, 116, 116, 107, 107, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 118, 118, 118, + 119, 119, 120, 120, 120, 127, 127, 123, 123, 124, + 124, 129, 129, 130, 130, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, @@ -2834,7 +2788,7 @@ var yyR1 = [...]int{ 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, - 121, 121, 121, 121, 121, 121, 121, 121, 122, 122, + 121, 121, 121, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, @@ -2851,76 +2805,75 @@ var yyR1 = [...]int{ 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, - 122, 122, 122, 122, 122, 122, 122, 193, 194, 134, - 135, 135, 135, + 122, 122, 194, 195, 134, 135, 135, 135, } var yyR2 = [...]int{ 0, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 0, 4, 6, 7, 5, 10, 1, - 3, 1, 3, 7, 8, 1, 1, 9, 8, 7, - 6, 6, 1, 1, 1, 3, 1, 3, 0, 4, - 3, 4, 5, 4, 1, 3, 3, 2, 2, 2, - 2, 2, 1, 1, 1, 2, 2, 8, 4, 6, - 5, 5, 0, 2, 1, 0, 2, 1, 3, 3, - 4, 4, 2, 4, 1, 3, 3, 3, 8, 3, - 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, - 2, 2, 2, 1, 4, 4, 2, 2, 3, 3, - 3, 3, 1, 1, 1, 1, 1, 6, 6, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 0, - 3, 0, 5, 0, 3, 5, 0, 1, 0, 1, - 0, 1, 2, 0, 2, 0, 3, 0, 1, 0, - 3, 3, 0, 2, 2, 0, 2, 1, 2, 1, - 0, 2, 5, 4, 1, 2, 2, 3, 2, 0, - 1, 2, 3, 3, 2, 2, 1, 1, 0, 1, - 1, 3, 2, 3, 1, 10, 11, 11, 12, 3, - 3, 1, 1, 2, 2, 2, 0, 1, 3, 1, - 2, 3, 1, 1, 1, 6, 7, 7, 7, 7, - 4, 5, 4, 4, 7, 5, 5, 5, 12, 7, - 5, 9, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 7, 1, 3, 8, 8, 3, - 3, 5, 4, 6, 5, 4, 4, 3, 2, 3, - 4, 4, 3, 4, 4, 4, 4, 4, 4, 3, - 2, 3, 3, 2, 3, 4, 3, 7, 5, 4, - 2, 4, 4, 3, 3, 5, 2, 3, 1, 1, - 0, 1, 1, 1, 0, 2, 2, 0, 2, 2, - 0, 2, 0, 1, 1, 2, 1, 1, 2, 1, - 1, 2, 2, 2, 2, 2, 3, 3, 2, 0, - 2, 0, 2, 1, 2, 2, 0, 1, 1, 0, - 1, 1, 0, 1, 0, 1, 1, 3, 1, 2, - 3, 5, 0, 1, 2, 1, 1, 0, 2, 1, - 3, 1, 1, 1, 3, 1, 3, 3, 7, 1, - 3, 1, 3, 4, 4, 4, 3, 2, 4, 0, - 1, 0, 2, 0, 1, 0, 1, 2, 1, 1, - 1, 2, 2, 1, 2, 3, 2, 3, 2, 2, - 2, 1, 1, 3, 3, 0, 5, 4, 5, 5, - 0, 2, 1, 3, 3, 2, 3, 1, 2, 0, - 3, 1, 1, 3, 3, 4, 4, 5, 3, 4, - 5, 6, 2, 1, 2, 1, 2, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, - 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, - 2, 3, 1, 1, 1, 1, 4, 5, 5, 6, - 4, 4, 6, 6, 6, 8, 8, 8, 8, 9, - 8, 5, 4, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 8, 8, 0, - 2, 3, 4, 4, 4, 4, 4, 4, 4, 0, - 3, 4, 7, 3, 1, 1, 2, 3, 3, 1, - 2, 2, 1, 2, 1, 2, 2, 1, 2, 0, - 1, 0, 2, 1, 2, 4, 0, 2, 1, 3, - 5, 1, 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, 2, 1, 3, 5, 4, 6, 1, 3, 3, - 5, 0, 5, 1, 3, 1, 2, 3, 1, 1, - 3, 3, 1, 3, 3, 3, 3, 3, 1, 2, - 1, 1, 1, 1, 1, 1, 0, 2, 0, 3, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, - 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 4, 6, 7, + 5, 10, 1, 3, 1, 3, 7, 8, 1, 1, + 9, 8, 7, 6, 6, 1, 1, 1, 3, 1, + 3, 0, 4, 3, 4, 5, 4, 1, 3, 3, + 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, + 8, 4, 6, 5, 5, 0, 2, 1, 0, 2, + 1, 3, 3, 4, 4, 2, 4, 1, 3, 3, + 3, 8, 3, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 1, 2, 2, 2, 1, 4, 4, 2, + 2, 3, 3, 3, 3, 1, 1, 1, 1, 1, + 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 0, 3, 0, 5, 0, 3, 5, 0, + 1, 0, 1, 0, 1, 2, 0, 2, 0, 3, + 0, 1, 0, 3, 3, 0, 2, 2, 0, 2, + 1, 2, 1, 0, 2, 5, 4, 1, 2, 2, + 3, 2, 0, 1, 2, 3, 3, 2, 2, 1, + 1, 0, 1, 1, 3, 2, 3, 1, 10, 11, + 11, 12, 3, 3, 1, 1, 2, 2, 2, 0, + 1, 3, 1, 2, 3, 1, 1, 1, 6, 7, + 7, 7, 7, 4, 5, 4, 4, 7, 5, 5, + 5, 12, 7, 5, 9, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, + 1, 3, 8, 8, 3, 3, 5, 4, 6, 5, + 4, 4, 3, 2, 3, 4, 4, 3, 4, 4, + 4, 4, 4, 4, 3, 2, 3, 3, 2, 3, + 4, 3, 7, 5, 4, 2, 4, 4, 3, 3, + 5, 2, 3, 1, 1, 0, 1, 1, 1, 0, + 2, 2, 0, 2, 2, 0, 2, 0, 1, 1, + 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 2, 0, 2, 0, 2, 1, 2, + 2, 0, 1, 1, 0, 1, 1, 0, 1, 0, + 1, 1, 3, 1, 2, 3, 5, 0, 1, 2, + 1, 1, 0, 2, 1, 3, 1, 1, 1, 3, + 1, 3, 3, 7, 1, 3, 1, 3, 4, 4, + 4, 3, 2, 4, 0, 1, 0, 2, 0, 1, + 0, 1, 2, 1, 1, 1, 2, 2, 1, 2, + 3, 2, 3, 2, 2, 2, 1, 1, 3, 3, + 0, 5, 4, 5, 5, 0, 2, 1, 3, 3, + 2, 3, 1, 2, 0, 3, 1, 1, 3, 3, + 4, 4, 5, 3, 4, 5, 6, 2, 1, 2, + 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, + 1, 0, 2, 1, 1, 1, 3, 1, 3, 1, + 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 1, 1, 1, + 1, 4, 5, 5, 6, 4, 4, 6, 6, 6, + 8, 8, 8, 8, 9, 8, 5, 4, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 8, 8, 0, 2, 3, 4, 4, 4, + 4, 4, 4, 4, 0, 3, 4, 7, 3, 1, + 1, 2, 3, 3, 1, 2, 2, 1, 2, 1, + 2, 2, 1, 2, 0, 1, 0, 2, 1, 2, + 4, 0, 2, 1, 3, 5, 1, 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, 2, 1, 3, 5, + 4, 6, 1, 3, 3, 5, 0, 5, 1, 3, + 1, 2, 3, 1, 1, 3, 3, 1, 3, 3, + 3, 3, 3, 1, 2, 1, 1, 1, 1, 1, + 1, 0, 2, 0, 3, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 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, @@ -2949,338 +2902,340 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 1, 1, } var yyChk = [...]int{ - -1000, -191, -1, -2, -6, -7, -8, -9, -10, -11, + -1000, -192, -1, -2, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -19, -20, -21, -23, -24, -25, -22, -16, -3, -4, 6, 7, -29, 9, 10, 30, - -17, 116, 117, 119, 118, 151, 120, 144, 51, 165, - 166, 168, 169, 25, 145, 146, 149, 150, 31, 32, - 122, -193, 8, 250, 55, -192, 348, -87, 15, -28, - 5, -26, -196, -26, -26, -26, -26, -26, -167, -169, - 55, 91, -120, 126, 73, 242, 123, 124, 130, -123, - 58, -122, 260, 137, 292, 293, 165, 176, 170, 197, - 189, 261, 294, 138, 187, 190, 229, 136, 295, 217, - 224, 67, 168, 238, 296, 147, 185, 181, 297, 269, - 179, 27, 298, 226, 202, 299, 265, 180, 225, 122, - 300, 140, 134, 301, 203, 207, 302, 230, 303, 304, - 305, 174, 175, 306, 232, 201, 135, 33, 262, 36, - 155, 233, 205, 307, 200, 196, 308, 309, 310, 311, - 199, 173, 195, 40, 209, 208, 210, 228, 192, 312, - 313, 314, 141, 315, 182, 18, 316, 317, 318, 319, - 320, 236, 150, 321, 153, 322, 323, 324, 325, 326, - 327, 227, 204, 206, 131, 157, 264, 328, 234, 178, - 329, 142, 154, 149, 237, 143, 330, 331, 332, 333, - 334, 335, 336, 169, 337, 338, 339, 340, 164, 231, - 240, 39, 214, 341, 172, 133, 342, 166, 161, 219, - 193, 156, 343, 344, 183, 184, 198, 171, 194, 167, - 158, 151, 345, 239, 215, 266, 191, 188, 162, 346, - 159, 160, 347, 220, 221, 163, 263, 235, 186, 216, - -107, 126, 242, 123, 221, 128, 124, 124, 125, 126, - 242, 123, 124, -54, -129, 58, -122, 126, 124, 109, - 190, 229, 116, 218, 226, 125, 33, 227, 157, -138, - 124, -109, 217, 220, 221, 163, 58, 231, 230, 222, - -129, 167, -134, -134, -134, -134, -134, 219, 219, -134, - -2, -91, 17, 16, -5, -3, -193, 6, 20, 21, - -32, 41, 42, -27, -38, 100, -39, -129, -60, 75, - -65, 29, 58, -122, 23, -64, -61, -80, -78, -79, - 109, 110, 111, 98, 99, 106, 76, 112, -69, -67, - -68, -70, 60, 59, 68, 61, 62, 63, 64, 69, - 70, 71, -123, -76, -193, 45, 46, 251, 252, 253, - 254, 259, 255, 78, 35, 241, 249, 248, 247, 245, - 246, 243, 244, 257, 258, 129, 242, 123, 104, 250, - -107, -107, 11, -48, -49, -54, -56, -129, -99, -137, - 167, -103, 231, 230, -124, -101, -123, -121, 229, 190, - 228, 121, 267, 74, 22, 24, 212, 77, 109, 16, - 78, 108, 251, 116, 49, 268, 243, 244, 241, 253, - 254, 242, 218, 29, 10, 270, 25, 145, 21, 34, - 102, 118, 81, 82, 148, 23, 146, 71, 273, 19, - 52, 11, 13, 274, 275, 14, 129, 128, 93, 125, - 47, 8, 112, 26, 90, 43, 276, 28, 277, 278, - 279, 280, 45, 91, 17, 245, 246, 31, 281, 259, - 152, 104, 50, 37, 75, 282, 283, 69, 284, 72, - 53, 73, 15, 48, 285, 286, 287, 288, 92, 119, - 250, 46, 289, 123, 6, 256, 30, 144, 44, 290, - 124, 80, 257, 258, 127, 70, 5, 130, 32, 9, - 51, 54, 247, 248, 249, 35, 79, 12, 291, -168, - 91, -161, 58, -54, 125, -54, 250, -116, 129, -116, - -116, 124, -54, 58, 58, 116, 118, 121, 53, -18, - -54, -115, 129, 58, -115, -115, -115, -54, 113, -54, - 58, 30, -113, 91, 12, 242, 58, 157, 124, 158, - 126, -135, -193, -124, -135, -135, -135, 161, 162, -135, - -112, -111, 224, 225, 219, 223, 12, 162, 219, 160, - -135, -134, -134, -194, 57, -92, 19, 31, -39, -129, - -88, -89, -39, -87, -2, -26, 37, -30, 21, 34, - 66, 11, -126, 74, 73, 90, -125, 22, -123, 60, - 113, -39, -62, 93, 75, 91, 92, 77, 95, 94, - 105, 98, 99, 100, 101, 102, 103, 104, 96, 97, - 108, 83, 84, 85, 86, 87, 88, 89, -108, -193, - -79, -193, 114, 115, -65, -65, -65, -65, -65, -65, - -65, -65, -193, -2, -74, -39, -193, -193, -193, -193, - -193, -193, -193, -193, -193, -83, -39, -193, -197, -71, - -193, -197, -71, -197, -71, -197, -193, -197, -71, -197, - -71, -197, -197, -71, -193, -193, -193, -193, -193, -193, - -193, -55, 26, -54, -41, -42, -43, -44, -57, -79, - -193, -54, -54, -48, -195, 56, 11, 54, -195, 56, - 113, 56, -99, 167, -100, -104, 232, 234, 83, -128, - -123, 60, 29, 30, 57, 56, -54, -140, -143, -145, - -144, -146, -141, -142, 187, 188, 109, 191, 193, 194, - 195, 196, 197, 198, 199, 200, 201, 202, 30, 147, - 183, 184, 185, 186, 203, 204, 205, 206, 207, 208, - 209, 210, 170, 189, 261, 171, 172, 173, 174, 175, - 176, 178, 179, 180, 181, 182, 58, -135, 126, 58, - 75, 58, -54, -54, -135, -135, -135, 159, 159, 124, - 124, 164, -54, 56, 127, -48, 23, 53, -54, 58, - 58, -130, -129, -121, -135, -113, 60, -39, -135, -135, - -135, -54, -135, -135, -135, -135, 11, -110, 11, 93, - -39, -114, 91, 53, 9, 93, 56, 18, 113, 56, - -90, 24, 25, -91, -194, -32, -66, -123, 61, 64, - -31, 44, -54, -39, -39, -72, 69, 75, 70, 71, - -125, 100, -130, -124, -121, -65, -73, -76, -79, 65, - 93, 91, 92, 77, -65, -65, -65, -65, -65, -65, - -65, -65, -65, -65, -65, -65, -65, -65, -65, -136, - 58, 60, 58, -64, -64, -123, -37, 21, 34, -36, - -38, -194, 56, -194, -2, -36, -36, -39, -39, -80, - 60, -36, -80, 60, -36, -36, -30, -81, -82, 79, - -80, -123, -129, -194, -65, -123, -123, -36, -37, -37, - -36, -36, -95, 153, -54, 30, 56, -50, -52, -51, - -53, 43, 47, 49, 44, 45, 46, 50, -133, 22, - -41, -193, -132, 153, -131, 22, -129, 60, -95, 54, - -41, -54, -41, -56, -129, 100, -103, -100, 56, 233, - 235, 236, 53, 72, -39, -152, 108, -170, -171, -172, - -124, 60, 61, -161, -162, -163, -173, 139, -178, 131, - 133, 130, -164, 140, 125, 28, 57, -157, 69, 75, - -153, 215, -147, 55, -147, -147, -147, -147, -151, 190, - -151, -151, -151, 55, 55, -147, -147, -147, -155, 55, - -155, -155, -156, 55, -156, -127, 54, -54, -135, 23, - -135, -117, 121, 118, 119, -181, 117, 212, 190, 67, - 29, 15, 251, 153, 266, 58, 154, -54, -54, -54, - -54, -54, 121, 118, -54, -54, -54, -135, -54, -113, - -129, -129, 60, -54, 39, -39, -39, -130, -89, -92, - -106, 19, 11, 35, 35, -36, 69, 70, 71, 113, - -193, -73, -65, -65, -65, -35, 148, 74, -194, -194, - -36, -36, 56, -39, -194, -194, -194, 56, 54, 22, - 11, 11, -194, 11, 11, -194, -194, -36, -84, -82, - 81, -39, -194, 113, -194, 56, 56, -194, -194, -194, - -194, -194, -63, 30, 35, -2, -193, -193, -98, -102, - -80, -42, -43, -43, -42, -43, 43, 43, 43, 48, - 43, 48, 43, -51, -129, -194, -58, 51, 128, 52, - -193, -131, -59, 12, -41, -59, -59, 113, -104, -105, - 237, 234, 240, 58, 60, 56, -172, 83, 55, 58, - 28, -164, -164, -165, 58, -165, 28, -149, 29, 69, - -154, 216, 61, -151, -151, -152, 30, -152, -152, -152, - -160, 60, -160, 61, 61, 53, -123, -135, -134, -187, - 136, 132, 139, 140, 134, 58, 125, 28, 131, 133, - 153, 130, -187, -118, -119, 127, 22, 125, 28, 153, - -186, 54, 159, 212, 159, 127, -135, -110, 40, 113, - -54, -40, 11, 100, -124, -37, -35, 74, -65, -65, - -194, -194, -38, -139, 109, 187, 147, 185, 181, 201, - 192, 214, 183, 215, -136, -139, -65, -65, -65, -65, - 260, -87, 82, -39, 80, -124, -65, -65, -97, 53, - -98, -75, -77, -76, -193, -2, -93, -123, -96, -123, - -59, 56, 83, -46, -45, 53, 54, -47, 53, -45, - 43, 43, 125, 125, 125, -96, -87, -39, -59, 234, - 238, 239, -171, -172, -175, -174, -123, -178, -165, -165, - 55, -150, 53, -65, 57, -152, -152, 58, 109, 57, - 56, 57, 56, 57, 56, -54, -134, -134, -54, -134, - -123, -184, 263, -185, 58, -123, -123, -123, -54, -113, - -59, -41, -194, -65, -194, -147, -147, -147, -156, -147, - 175, -147, 175, -194, -194, 19, 19, 19, 19, -193, - -34, 256, -39, 56, 56, 27, -97, 56, -194, -194, - -194, 56, 113, -194, 56, -87, -102, -39, -39, 55, - -39, -193, -193, -193, -194, -91, 57, 56, -147, -94, - -123, -158, 212, 9, -151, 60, -151, 61, 61, -135, - 26, -183, -182, -124, 55, 54, -85, 13, -151, 58, - -65, -65, -65, -65, -65, -91, 60, -65, -65, 28, - -77, 35, -2, -193, -123, -123, -123, -91, -94, -94, - -194, -94, -94, -132, -177, -176, 54, 135, 67, -174, - 57, 56, -159, 131, 28, 130, -68, -152, -152, 57, - 57, -193, 56, 83, -94, -54, -86, 14, 16, -194, - -194, -194, -194, -33, 93, 263, -194, -194, -194, 9, - -75, -2, 113, 57, -194, -194, -194, -58, -176, 58, - -166, 83, 60, 142, -123, -148, 67, 28, 28, -179, - -180, 153, -182, -172, 57, -39, -74, -194, 261, 50, - 264, -98, -194, -123, 61, -54, 60, -194, 56, -123, - -186, 40, 262, 265, 55, -180, 35, -184, 40, -94, - 155, 263, 57, 156, 264, -189, -190, 53, -193, 265, - -190, 53, 10, 9, -65, 152, -188, 143, 138, 141, - 30, -188, -194, -194, 137, 29, 69, + -17, 118, 119, 121, 120, 153, 122, 146, 51, 167, + 168, 170, 171, 25, 147, 148, 151, 152, 31, 32, + 124, -194, 8, 252, 55, -193, 350, -87, 15, -28, + 5, -26, -197, -26, -26, -26, -26, -26, -167, -169, + 55, 93, -120, 128, 75, 244, 125, 126, 132, -123, + -185, -122, 58, 59, 60, 262, 139, 294, 295, 167, + 178, 172, 199, 191, 263, 296, 140, 189, 192, 231, + 138, 297, 219, 226, 69, 170, 240, 298, 149, 187, + 183, 299, 271, 181, 27, 300, 228, 204, 301, 267, + 182, 227, 124, 302, 142, 136, 303, 205, 209, 304, + 232, 305, 306, 307, 176, 177, 308, 234, 203, 137, + 33, 264, 36, 157, 235, 207, 309, 202, 198, 310, + 311, 312, 313, 201, 175, 197, 40, 211, 210, 212, + 230, 194, 314, 315, 316, 143, 317, 184, 18, 318, + 319, 320, 321, 322, 238, 152, 323, 155, 324, 325, + 326, 327, 328, 329, 229, 206, 208, 133, 159, 266, + 330, 236, 180, 331, 144, 156, 151, 239, 145, 332, + 333, 334, 335, 336, 337, 338, 171, 339, 340, 341, + 342, 166, 233, 242, 39, 216, 343, 174, 135, 344, + 168, 163, 221, 195, 158, 345, 346, 185, 186, 200, + 173, 196, 169, 160, 153, 347, 241, 217, 268, 193, + 190, 164, 348, 161, 162, 349, 222, 223, 165, 265, + 237, 188, 218, -107, 128, 244, 125, 223, 130, 126, + 126, 127, 128, 244, 125, 126, -54, -129, -185, -122, + 128, 126, 111, 192, 231, 118, 220, 228, 127, 33, + 229, 159, -138, 126, -109, 219, 222, 223, 165, -185, + 233, 232, 224, -129, 169, -134, -134, -134, -134, -134, + 221, 221, -134, -2, -91, 17, 16, -5, -3, -194, + 6, 20, 21, -32, 41, 42, -27, -38, 102, -39, + -129, -60, 77, -65, 29, -185, -122, 23, -64, -61, + -80, -78, -79, 111, 112, 113, 100, 101, 108, 78, + 114, -69, -67, -68, -70, 62, 61, 70, 63, 64, + 65, 66, 71, 72, 73, -123, -76, -194, 45, 46, + 253, 254, 255, 256, 261, 257, 80, 35, 243, 251, + 250, 249, 247, 248, 245, 246, 259, 260, 131, 244, + 125, 106, 252, -107, -107, 11, -48, -49, -54, -56, + -129, -99, -137, 169, -103, 233, 232, -124, -101, -123, + -121, 231, 192, 230, 123, 269, 76, 22, 24, 214, + 79, 111, 16, 80, 110, 253, 118, 49, 270, 245, + 246, 243, 255, 256, 244, 220, 29, 10, 272, 25, + 147, 21, 34, 104, 120, 83, 84, 150, 23, 148, + 73, 275, 19, 52, 11, 13, 276, 277, 14, 131, + 130, 95, 127, 47, 8, 114, 26, 92, 43, 278, + 28, 279, 280, 281, 282, 45, 93, 17, 247, 248, + 31, 283, 261, 154, 106, 50, 37, 77, 284, 285, + 71, 286, 74, 53, 75, 15, 48, 287, 288, 289, + 290, 94, 121, 252, 46, 291, 125, 6, 258, 30, + 146, 44, 292, 126, 82, 259, 260, 129, 72, 5, + 132, 32, 9, 51, 54, 249, 250, 251, 35, 81, + 12, 293, -168, 93, -161, -185, -54, 127, -54, 252, + -116, 131, -116, -116, 126, -54, -185, -185, 118, 120, + 123, 53, -18, -54, -115, 131, -185, -115, -115, -115, + -54, 115, -54, -185, 30, -113, 93, 12, 244, -185, + 159, 126, 160, 128, -135, -194, -124, -135, -135, -135, + 163, 164, -135, -112, -111, 226, 227, 221, 225, 12, + 164, 221, 162, -135, -134, -134, -195, 57, -92, 19, + 31, -39, -129, -88, -89, -39, -87, -2, -26, 37, + -30, 21, 34, 68, 11, -126, 76, 75, 92, -125, + 22, -123, 62, 115, -39, -62, 95, 77, 93, 94, + 79, 97, 96, 107, 100, 101, 102, 103, 104, 105, + 106, 98, 99, 110, 85, 86, 87, 88, 89, 90, + 91, -108, -194, -79, -194, 116, 117, -65, -65, -65, + -65, -65, -65, -65, -65, -194, -2, -74, -39, -194, + -194, -194, -194, -194, -194, -194, -194, -194, -83, -39, + -194, -198, -71, -194, -198, -71, -198, -71, -198, -194, + -198, -71, -198, -71, -198, -198, -71, -194, -194, -194, + -194, -194, -194, -194, -55, 26, -54, -41, -42, -43, + -44, -57, -79, -194, -54, -54, -48, -196, 56, 11, + 54, -196, 56, 115, 56, -99, 169, -100, -104, 234, + 236, 85, -128, -123, 62, 29, 30, 57, 56, -54, + -140, -143, -145, -144, -146, -141, -142, 189, 190, 111, + 193, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 30, 149, 185, 186, 187, 188, 205, 206, 207, + 208, 209, 210, 211, 212, 172, 191, 263, 173, 174, + 175, 176, 177, 178, 180, 181, 182, 183, 184, -185, + -135, 128, -185, 77, -185, -54, -54, -135, -135, -135, + 161, 161, 126, 126, 166, -54, 56, 129, -48, 23, + 53, -54, -185, -185, -130, -129, -121, -135, -113, 62, + -39, -135, -135, -135, -54, -135, -135, -135, -135, 11, + -110, 11, 95, -39, -114, 93, 53, 9, 95, 56, + 18, 115, 56, -90, 24, 25, -91, -195, -32, -66, + -123, 63, 66, -31, 44, -54, -39, -39, -72, 71, + 77, 72, 73, -125, 102, -130, -124, -121, -65, -73, + -76, -79, 67, 95, 93, 94, 79, -65, -65, -65, + -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, + -65, -65, -136, -185, 62, -185, -64, -64, -123, -37, + 21, 34, -36, -38, -195, 56, -195, -2, -36, -36, + -39, -39, -80, 62, -36, -80, 62, -36, -36, -30, + -81, -82, 81, -80, -123, -129, -195, -65, -123, -123, + -36, -37, -37, -36, -36, -95, 155, -54, 30, 56, + -50, -52, -51, -53, 43, 47, 49, 44, 45, 46, + 50, -133, 22, -41, -194, -132, 155, -131, 22, -129, + 62, -95, 54, -41, -54, -41, -56, -129, 102, -103, + -100, 56, 235, 237, 238, 53, 74, -39, -152, 110, + -170, -171, -172, -124, 62, 63, -161, -162, -163, -173, + 141, -178, 133, 135, 132, -164, 142, 127, 28, 57, + -157, 71, 77, -153, 217, -147, 55, -147, -147, -147, + -147, -151, 192, -151, -151, -151, 55, 55, -147, -147, + -147, -155, 55, -155, -155, -156, 55, -156, -127, 54, + -54, -135, 23, -135, -117, 123, 120, 121, -181, 119, + 214, 192, 69, 29, 15, 253, 155, 268, -185, 156, + -54, -54, -54, -54, -54, 123, 120, -54, -54, -54, + -135, -54, -113, -129, -129, 62, -54, 39, -39, -39, + -130, -89, -92, -106, 19, 11, 35, 35, -36, 71, + 72, 73, 115, -194, -73, -65, -65, -65, -35, 150, + 76, -195, -195, -36, -36, 56, -39, -195, -195, -195, + 56, 54, 22, 11, 11, -195, 11, 11, -195, -195, + -36, -84, -82, 83, -39, -195, 115, -195, 56, 56, + -195, -195, -195, -195, -195, -63, 30, 35, -2, -194, + -194, -98, -102, -80, -42, -43, -43, -42, -43, 43, + 43, 43, 48, 43, 48, 43, -51, -129, -195, -58, + 51, 130, 52, -194, -131, -59, 12, -41, -59, -59, + 115, -104, -105, 239, 236, 242, -185, 62, 56, -172, + 85, 55, -185, 28, -164, -164, -165, -185, -165, 28, + -149, 29, 71, -154, 218, 63, -151, -151, -152, 30, + -152, -152, -152, -160, 62, -160, 63, 63, 53, -123, + -135, -134, -188, 138, 134, 141, 142, 136, 58, 59, + 60, 127, 28, 133, 135, 155, 132, -188, -118, -119, + 129, 22, 127, 28, 155, -187, 54, 161, 214, 161, + 129, -135, -110, 40, 115, -54, -40, 11, 102, -124, + -37, -35, 76, -65, -65, -195, -195, -38, -139, 111, + 189, 149, 187, 183, 203, 194, 216, 185, 217, -136, + -139, -65, -65, -65, -65, 262, -87, 84, -39, 82, + -124, -65, -65, -97, 53, -98, -75, -77, -76, -194, + -2, -93, -123, -96, -123, -59, 56, 85, -46, -45, + 53, 54, -47, 53, -45, 43, 43, 127, 127, 127, + -96, -87, -39, -59, 236, 240, 241, -171, -172, -175, + -174, -123, -178, -165, -165, 55, -150, 53, -65, 57, + -152, -152, -185, 111, 57, 56, 57, 56, 57, 56, + -54, -134, -134, -54, -134, -123, -184, 265, -186, -185, + -123, -123, -123, -54, -113, -59, -41, -195, -65, -195, + -147, -147, -147, -156, -147, 177, -147, 177, -195, -195, + 19, 19, 19, 19, -194, -34, 258, -39, 56, 56, + 27, -97, 56, -195, -195, -195, 56, 115, -195, 56, + -87, -102, -39, -39, 55, -39, -194, -194, -194, -195, + -91, 57, 56, -147, -94, -123, -158, 214, 9, -151, + 62, -151, 63, 63, -135, 26, -183, -182, -124, 55, + 54, -85, 13, -151, -185, -65, -65, -65, -65, -65, + -91, 62, -65, -65, 28, -77, 35, -2, -194, -123, + -123, -123, -91, -94, -94, -195, -94, -94, -132, -177, + -176, 54, 137, 69, -174, 57, 56, -159, 133, 28, + 132, -68, -152, -152, 57, 57, -194, 56, 85, -94, + -54, -86, 14, 16, -195, -195, -195, -195, -33, 95, + 265, -195, -195, -195, 9, -75, -2, 115, 57, -195, + -195, -195, -58, -176, -185, -166, 85, 62, 144, -123, + -148, 69, 28, 28, -179, -180, 155, -182, -172, 57, + -39, -74, -195, 263, 50, 266, -98, -195, -123, 63, + -54, 62, -195, 56, -123, -187, 40, 264, 267, 55, + -180, 35, -184, 40, -94, 157, 265, 57, 158, 266, + -190, -191, 53, -194, 267, -191, 53, 10, 9, -65, + 154, -189, 145, 140, 143, 30, -189, -195, -195, 139, + 29, 71, } var yyDef = [...]int{ 23, -2, 2, -2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 566, 0, 319, 319, 319, 319, 319, 319, - 0, 637, 620, 0, 0, 0, 0, -2, 306, 307, - 0, 309, 310, 939, 939, 939, 939, 939, 0, 0, - 939, 0, 35, 36, 937, 1, 3, 574, 0, 0, - 323, 326, 321, 0, 620, 620, 0, 0, 65, 66, - 0, 0, 0, 926, 0, 618, 618, 618, 638, 639, - 642, 643, 768, 769, 770, 771, 772, 773, 774, 775, - 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, - 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, - 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, - 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, - 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, - 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, - 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, - 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, - 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, - 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, - 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, - 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, - 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, - 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, - 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, - 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, - 0, 0, 0, 0, 0, 621, 0, 616, 0, 616, - 616, 616, 0, 258, 392, 646, 647, 926, 0, 0, - 0, 297, 0, 940, 270, 940, 940, 273, 940, 0, - 940, 0, 280, 0, 0, 286, 940, 303, 304, 291, - 305, 308, 311, 312, 313, 314, 315, 939, 939, 318, - 29, 578, 0, 0, 566, 31, 0, 319, 324, 325, - 329, 327, 328, 320, 0, 338, 342, 0, 402, 0, - 407, 409, -2, -2, 0, 444, 445, 446, 447, 448, - 0, 0, 0, 0, 0, 0, 0, 0, 472, 473, - 474, 475, 551, 552, 553, 554, 555, 556, 557, 558, - 411, 412, 548, 598, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 539, 0, 509, 509, 509, 509, 509, - 509, 509, 509, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 44, 46, 392, 50, 0, - 915, 602, -2, -2, 0, 0, 644, 645, -2, 781, - -2, 650, 651, 652, 653, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, - 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, - 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, - 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, - 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, - 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, - 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, - 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, - 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, - 759, 760, 761, 762, 763, 764, 765, 766, 767, 0, - 0, 84, 0, 82, 0, 940, 0, 0, 0, 0, - 0, 0, 940, 940, 940, 0, 0, 0, 0, 249, - 0, 0, 0, 0, 0, 0, 0, 257, 0, 259, - 940, 297, 262, 0, 0, 940, 940, 940, 0, 940, - 940, 269, 941, 942, 271, 272, 274, 940, 940, 276, - 0, 294, 292, 293, 288, 289, 0, 300, 283, 284, - 287, 316, 317, 30, 938, 24, 0, 0, 575, 0, - 567, 568, 571, 574, 29, 326, 0, 332, 330, 331, - 322, 0, 339, 0, 0, 0, 343, 0, 345, 346, - 0, 405, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 429, 430, 431, 432, 433, 434, 435, 408, 0, - 422, 0, 0, 0, 464, 465, 466, 467, 468, 469, - 470, 0, 334, 29, 0, 442, 0, 0, 0, 0, - 0, 0, 0, 0, 329, 0, 540, 0, 493, 501, - 0, 494, 502, 495, 503, 496, 0, 497, 504, 498, - 505, 499, 500, 506, 0, 0, 0, 334, 334, 0, - 0, 48, 0, 391, 0, 349, 351, 352, 353, -2, - 0, 375, -2, 0, 0, 0, 42, 43, 0, 0, - 0, 0, 51, 915, 53, 54, 0, 0, 0, 162, - 611, 612, 613, 609, 206, 0, 0, 150, 146, 90, - 91, 92, 139, 94, 139, 139, 139, 139, 159, 159, - 159, 159, 122, 123, 124, 125, 126, 0, 0, 109, - 139, 139, 139, 113, 129, 130, 131, 132, 133, 134, - 135, 136, 95, 96, 97, 98, 99, 100, 101, 102, - 103, 141, 141, 141, 143, 143, 640, 68, 0, 940, - 0, 940, 80, 0, 220, 222, 223, 0, 0, 0, - 0, 0, 0, 0, 0, 252, 617, 0, 940, 255, - 256, 393, 648, 649, 260, 261, 298, 299, 263, 264, - 265, 266, 267, 268, 275, 279, 0, 297, 0, 0, - 281, 282, 0, 0, 579, 0, 0, 0, 0, 0, - 570, 572, 573, 578, 32, 329, 0, 559, 0, 0, - 0, 333, 27, 403, 404, 406, 423, 0, 425, 427, - 344, 340, 0, 549, -2, 413, 414, 438, 439, 440, - 0, 0, 0, 0, 436, 418, 0, 449, 450, 451, - 452, 453, 454, 455, 456, 457, 458, 459, 460, 463, - 524, 525, 0, 461, 462, 471, 0, 0, 0, 335, - 336, 441, 0, 597, 29, 0, 0, 0, 0, 446, - 551, 0, 446, 551, 0, 0, 0, 546, 543, 0, - 0, 548, 0, 510, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 390, 0, 0, 0, 0, 0, - 0, 380, 0, 0, 383, 0, 0, 0, 0, 374, - 0, 0, 395, 860, 376, 0, 378, 379, 400, 0, - 400, 45, 400, 47, 0, 394, 603, 52, 0, 0, - 57, 58, 604, 605, 606, 607, 0, 81, 207, 209, - 212, 213, 214, 85, 86, 87, 0, 0, 194, 0, - 0, 188, 188, 0, 186, 187, 83, 153, 151, 0, - 148, 147, 93, 0, 159, 159, 116, 117, 162, 0, - 162, 162, 162, 0, 0, 110, 111, 112, 104, 0, - 105, 106, 107, 0, 108, 0, 0, 940, 70, 619, - 71, 939, 0, 0, 632, 221, 622, 623, 624, 625, - 626, 627, 628, 629, 630, 631, 0, 72, 225, 227, - 226, 230, 0, 0, 0, 250, 940, 254, 294, 278, - 295, 296, 301, 285, 0, 576, 577, 0, 569, 25, - 0, 614, 615, 560, 561, 347, 424, 426, 428, 0, - 334, 415, 436, 419, 0, 416, 0, 0, 410, 476, - 0, 0, 0, 443, -2, 480, 481, 0, 0, 0, - 0, 0, 517, 0, 0, 518, 0, 566, 0, 544, - 0, 0, 492, 0, 511, 0, 0, 512, 513, 514, - 515, 516, 591, 0, 0, -2, 0, 0, 400, 599, - 0, 350, 369, 371, 0, 366, 381, 382, 384, 0, - 386, 0, 388, 389, 354, 356, 357, 0, 0, 0, - 0, 377, 566, 0, 400, 40, 41, 0, 55, 56, - 0, 0, 62, 163, 164, 0, 210, 0, 0, 0, - 181, 188, 188, 184, 189, 185, 0, 155, 0, 152, - 89, 149, 0, 162, 162, 118, 0, 119, 120, 121, - 0, 137, 0, 0, 0, 0, 641, 69, 215, 939, - 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, - 242, 243, 939, 0, 939, 633, 634, 635, 636, 0, - 75, 0, 0, 0, 0, 0, 253, 297, 580, 0, - 26, 400, 0, 341, 550, 0, 417, 0, 437, 420, - 477, 478, 337, 0, 139, 139, 529, 139, 143, 532, - 139, 534, 139, 537, 0, 0, 0, 0, 0, 0, - 0, 541, 491, 547, 0, 549, 0, 0, 33, 0, - 591, 581, 593, 595, 0, 29, 0, 587, 0, 361, - 566, 0, 0, 363, 370, 0, 0, 364, 0, 365, - 385, 387, 0, 0, 0, 0, 574, 401, 39, 59, - 60, 61, 208, 211, 0, 190, 139, 193, 182, 183, - 0, 157, 0, 154, 140, 114, 115, 160, 161, 159, - 0, 159, 0, 144, 0, 940, 216, 217, 218, 219, - 0, 224, 0, 73, 74, 0, 0, 229, 251, 277, - 562, 348, 479, 421, 482, 526, 159, 530, 531, 533, - 535, 536, 538, 484, 483, 0, 0, 0, 0, 0, - 574, 0, 545, 0, 0, 0, 34, 0, 596, -2, - 0, 0, 0, 49, 0, 574, 600, 601, 367, 0, - 372, 0, 0, 0, 375, 38, 173, 0, 192, 0, - 359, 165, 158, 0, 162, 138, 162, 0, 0, 67, - 0, 76, 77, 0, 0, 0, 564, 0, 527, 528, - 0, 0, 0, 0, 519, 0, 542, 0, 0, 0, - 594, 0, -2, 0, 589, 588, 362, 37, 0, 0, - 397, 0, 0, 395, 172, 174, 0, 179, 0, 191, - 0, 0, 170, 0, 167, 169, 156, 127, 128, 142, - 145, 0, 0, 0, 0, 231, 28, 0, 0, 485, - 487, 486, 488, 0, 0, 0, 490, 507, 508, 0, - 584, 29, 0, 368, 396, 398, 399, 358, 175, 176, - 0, 180, 178, 0, 360, 88, 0, 166, 168, 0, - 245, 0, 78, 79, 72, 565, 563, 489, 0, 0, - 0, 592, -2, 590, 177, 0, 171, 244, 0, 0, - 75, 520, 0, 523, 0, 246, 0, 228, 521, 0, - 0, 0, 195, 0, 0, 196, 197, 0, 0, 522, - 198, 0, 0, 0, 0, 0, 199, 201, 202, 0, - 0, 200, 247, 248, 203, 204, 205, + 21, 22, 571, 0, 324, 324, 324, 324, 324, 324, + 0, 642, 625, 0, 0, 0, 0, -2, 311, 312, + 0, 314, 315, 944, 944, 944, 944, 944, 0, 0, + 944, 0, 38, 39, 942, 1, 3, 579, 0, 0, + 328, 331, 326, 0, 625, 625, 0, 0, 68, 69, + 0, 0, 0, 931, 0, 623, 623, 623, 643, 644, + 647, 648, 24, 25, 26, 773, 774, 775, 776, 777, + 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, + 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, + 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, + 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, + 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, + 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, + 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, + 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, + 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, + 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, + 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, + 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, + 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, + 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, + 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, + 928, 929, 930, 932, 933, 934, 935, 936, 937, 938, + 939, 940, 941, 0, 0, 0, 0, 0, 626, 0, + 621, 0, 621, 621, 621, 0, 263, 397, 651, 652, + 931, 0, 0, 0, 302, 0, 945, 275, 945, 945, + 278, 945, 0, 945, 0, 285, 0, 0, 291, 945, + 308, 309, 296, 310, 313, 316, 317, 318, 319, 320, + 944, 944, 323, 32, 583, 0, 0, 571, 34, 0, + 324, 329, 330, 334, 332, 333, 325, 0, 343, 347, + 0, 407, 0, 412, 414, -2, -2, 0, 449, 450, + 451, 452, 453, 0, 0, 0, 0, 0, 0, 0, + 0, 477, 478, 479, 480, 556, 557, 558, 559, 560, + 561, 562, 563, 416, 417, 553, 603, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 544, 0, 514, 514, + 514, 514, 514, 514, 514, 514, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 47, 49, + 397, 53, 0, 920, 607, -2, -2, 0, 0, 649, + 650, -2, 786, -2, 655, 656, 657, 658, 659, 660, + 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, + 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, + 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, + 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, + 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, + 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, + 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, + 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, + 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, + 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, + 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, + 771, 772, 0, 0, 87, 0, 85, 0, 945, 0, + 0, 0, 0, 0, 0, 945, 945, 945, 0, 0, + 0, 0, 254, 0, 0, 0, 0, 0, 0, 0, + 262, 0, 264, 945, 302, 267, 0, 0, 945, 945, + 945, 0, 945, 945, 274, 946, 947, 276, 277, 279, + 945, 945, 281, 0, 299, 297, 298, 293, 294, 0, + 305, 288, 289, 292, 321, 322, 33, 943, 27, 0, + 0, 580, 0, 572, 573, 576, 579, 32, 331, 0, + 337, 335, 336, 327, 0, 344, 0, 0, 0, 348, + 0, 350, 351, 0, 410, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 434, 435, 436, 437, 438, 439, + 440, 413, 0, 427, 0, 0, 0, 469, 470, 471, + 472, 473, 474, 475, 0, 339, 32, 0, 447, 0, + 0, 0, 0, 0, 0, 0, 0, 334, 0, 545, + 0, 498, 506, 0, 499, 507, 500, 508, 501, 0, + 502, 509, 503, 510, 504, 505, 511, 0, 0, 0, + 339, 339, 0, 0, 51, 0, 396, 0, 354, 356, + 357, 358, -2, 0, 380, -2, 0, 0, 0, 45, + 46, 0, 0, 0, 0, 54, 920, 56, 57, 0, + 0, 0, 165, 616, 617, 618, 614, 209, 0, 0, + 153, 149, 93, 94, 95, 142, 97, 142, 142, 142, + 142, 162, 162, 162, 162, 125, 126, 127, 128, 129, + 0, 0, 112, 142, 142, 142, 116, 132, 133, 134, + 135, 136, 137, 138, 139, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 144, 144, 144, 146, 146, 645, + 71, 0, 945, 0, 945, 83, 0, 223, 225, 226, + 0, 0, 0, 0, 0, 0, 0, 0, 257, 622, + 0, 945, 260, 261, 398, 653, 654, 265, 266, 303, + 304, 268, 269, 270, 271, 272, 273, 280, 284, 0, + 302, 0, 0, 286, 287, 0, 0, 584, 0, 0, + 0, 0, 0, 575, 577, 578, 583, 35, 334, 0, + 564, 0, 0, 0, 338, 30, 408, 409, 411, 428, + 0, 430, 432, 349, 345, 0, 554, -2, 418, 419, + 443, 444, 445, 0, 0, 0, 0, 441, 423, 0, + 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, + 464, 465, 468, 529, 530, 0, 466, 467, 476, 0, + 0, 0, 340, 341, 446, 0, 602, 32, 0, 0, + 0, 0, 451, 556, 0, 451, 556, 0, 0, 0, + 551, 548, 0, 0, 553, 0, 515, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, + 0, 0, 0, 0, 385, 0, 0, 388, 0, 0, + 0, 0, 379, 0, 0, 400, 865, 381, 0, 383, + 384, 405, 0, 405, 48, 405, 50, 0, 399, 608, + 55, 0, 0, 60, 61, 609, 610, 611, 612, 0, + 84, 210, 212, 215, 216, 217, 88, 89, 90, 0, + 0, 197, 0, 0, 191, 191, 0, 189, 190, 86, + 156, 154, 0, 151, 150, 96, 0, 162, 162, 119, + 120, 165, 0, 165, 165, 165, 0, 0, 113, 114, + 115, 107, 0, 108, 109, 110, 0, 111, 0, 0, + 945, 73, 624, 74, 944, 0, 0, 637, 224, 627, + 628, 629, 630, 631, 632, 633, 634, 635, 636, 0, + 75, 228, 230, 229, 233, 0, 0, 0, 255, 945, + 259, 299, 283, 300, 301, 306, 290, 0, 581, 582, + 0, 574, 28, 0, 619, 620, 565, 566, 352, 429, + 431, 433, 0, 339, 420, 441, 424, 0, 421, 0, + 0, 415, 481, 0, 0, 0, 448, -2, 485, 486, + 0, 0, 0, 0, 0, 522, 0, 0, 523, 0, + 571, 0, 549, 0, 0, 497, 0, 516, 0, 0, + 517, 518, 519, 520, 521, 596, 0, 0, -2, 0, + 0, 405, 604, 0, 355, 374, 376, 0, 371, 386, + 387, 389, 0, 391, 0, 393, 394, 359, 361, 362, + 0, 0, 0, 0, 382, 571, 0, 405, 43, 44, + 0, 58, 59, 0, 0, 65, 166, 167, 0, 213, + 0, 0, 0, 184, 191, 191, 187, 192, 188, 0, + 158, 0, 155, 92, 152, 0, 165, 165, 121, 0, + 122, 123, 124, 0, 140, 0, 0, 0, 0, 646, + 72, 218, 944, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 944, 0, 944, + 638, 639, 640, 641, 0, 78, 0, 0, 0, 0, + 0, 258, 302, 585, 0, 29, 405, 0, 346, 555, + 0, 422, 0, 442, 425, 482, 483, 342, 0, 142, + 142, 534, 142, 146, 537, 142, 539, 142, 542, 0, + 0, 0, 0, 0, 0, 0, 546, 496, 552, 0, + 554, 0, 0, 36, 0, 596, 586, 598, 600, 0, + 32, 0, 592, 0, 366, 571, 0, 0, 368, 375, + 0, 0, 369, 0, 370, 390, 392, 0, 0, 0, + 0, 579, 406, 42, 62, 63, 64, 211, 214, 0, + 193, 142, 196, 185, 186, 0, 160, 0, 157, 143, + 117, 118, 163, 164, 162, 0, 162, 0, 147, 0, + 945, 219, 220, 221, 222, 0, 227, 0, 76, 77, + 0, 0, 232, 256, 282, 567, 353, 484, 426, 487, + 531, 162, 535, 536, 538, 540, 541, 543, 489, 488, + 0, 0, 0, 0, 0, 579, 0, 550, 0, 0, + 0, 37, 0, 601, -2, 0, 0, 0, 52, 0, + 579, 605, 606, 372, 0, 377, 0, 0, 0, 380, + 41, 176, 0, 195, 0, 364, 168, 161, 0, 165, + 141, 165, 0, 0, 70, 0, 79, 80, 0, 0, + 0, 569, 0, 532, 533, 0, 0, 0, 0, 524, + 0, 547, 0, 0, 0, 599, 0, -2, 0, 594, + 593, 367, 40, 0, 0, 402, 0, 0, 400, 175, + 177, 0, 182, 0, 194, 0, 0, 173, 0, 170, + 172, 159, 130, 131, 145, 148, 0, 0, 0, 0, + 234, 31, 0, 0, 490, 492, 491, 493, 0, 0, + 0, 495, 512, 513, 0, 589, 32, 0, 373, 401, + 403, 404, 363, 178, 179, 0, 183, 181, 0, 365, + 91, 0, 169, 171, 0, 250, 0, 81, 82, 75, + 570, 568, 494, 0, 0, 0, 597, -2, 595, 180, + 0, 174, 249, 0, 0, 78, 525, 0, 528, 0, + 251, 0, 231, 526, 0, 0, 0, 198, 0, 0, + 199, 200, 0, 0, 527, 201, 0, 0, 0, 0, + 0, 202, 204, 205, 0, 0, 203, 252, 253, 206, + 207, 208, } var yyTok1 = [...]int{ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 76, 3, 3, 3, 103, 95, 3, - 55, 57, 100, 98, 56, 99, 113, 101, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 348, - 84, 83, 85, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 78, 3, 3, 3, 105, 97, 3, + 55, 57, 102, 100, 56, 101, 115, 103, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 350, + 86, 85, 87, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 105, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 107, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 94, 3, 106, + 3, 3, 3, 3, 96, 3, 108, } var yyTok2 = [...]int{ @@ -3291,9 +3246,9 @@ var yyTok2 = [...]int{ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 77, 78, 79, 80, 81, 82, 86, 87, 88, - 89, 90, 91, 92, 93, 96, 97, 102, 104, 107, - 108, 109, 110, 111, 112, 114, 115, 116, 117, 118, + 75, 76, 77, 79, 80, 81, 82, 83, 84, 88, + 89, 90, 91, 92, 93, 94, 95, 98, 99, 104, + 106, 109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, @@ -3326,7 +3281,8 @@ var yyTok3 = [...]int{ 57655, 330, 57656, 331, 57657, 332, 57658, 333, 57659, 334, 57660, 335, 57661, 336, 57662, 337, 57663, 338, 57664, 339, 57665, 340, 57666, 341, 57667, 342, 57668, 343, 57669, 344, - 57670, 345, 57671, 346, 57672, 347, 0, + 57670, 345, 57671, 346, 57672, 347, 57673, 348, 57674, 349, + 0, } var yyErrorMessages = [...]struct { @@ -3695,8 +3651,26 @@ yydefault: setParseTree(yylex, nil) } case 24: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:362 + { + yyVAL.colIdent = NewColIdentWithAt(string(yyDollar[1].bytes), NoAt) + } + case 25: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:366 + { + yyVAL.colIdent = NewColIdentWithAt(string(yyDollar[1].bytes), SingleAt) + } + case 26: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:370 + { + yyVAL.colIdent = NewColIdentWithAt(string(yyDollar[1].bytes), DoubleAt) + } + case 27: + yyDollar = yyS[yypt-4 : yypt+1] +//line sql.y:376 { sel := yyDollar[1].selStmt.(*Select) sel.OrderBy = yyDollar[2].orderBy @@ -3704,57 +3678,57 @@ yydefault: sel.Lock = yyDollar[4].str yyVAL.selStmt = sel } - case 25: + case 28: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:370 +//line sql.y:384 { yyVAL.selStmt = &Union{Type: yyDollar[2].str, Left: yyDollar[1].selStmt, Right: yyDollar[3].selStmt, OrderBy: yyDollar[4].orderBy, Limit: yyDollar[5].limit, Lock: yyDollar[6].str} } - case 26: + case 29: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:374 +//line sql.y:388 { 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 27: + case 30: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:380 +//line sql.y:394 { yyVAL.statement = &Stream{Comments: Comments(yyDollar[2].bytes2), SelectExpr: yyDollar[3].selectExpr, Table: yyDollar[5].tableName} } - case 28: + case 31: yyDollar = yyS[yypt-10 : yypt+1] -//line sql.y:387 +//line sql.y:401 { 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)} } - case 29: + case 32: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:393 +//line sql.y:407 { yyVAL.selStmt = yyDollar[1].selStmt } - case 30: + case 33: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:397 +//line sql.y:411 { yyVAL.selStmt = &ParenSelect{Select: yyDollar[2].selStmt} } - case 31: + case 34: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:403 +//line sql.y:417 { yyVAL.selStmt = yyDollar[1].selStmt } - case 32: + case 35: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:407 +//line sql.y:421 { yyVAL.selStmt = &ParenSelect{Select: yyDollar[2].selStmt} } - case 33: + case 36: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:414 +//line sql.y:428 { // insert_data returns a *Insert pre-filled with Columns & Values ins := yyDollar[6].ins @@ -3766,9 +3740,9 @@ yydefault: ins.OnDup = OnDup(yyDollar[7].updateExprs) yyVAL.statement = ins } - case 34: + case 37: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:426 +//line sql.y:440 { cols := make(Columns, 0, len(yyDollar[7].updateExprs)) vals := make(ValTuple, 0, len(yyDollar[8].updateExprs)) @@ -3778,334 +3752,334 @@ yydefault: } yyVAL.statement = &Insert{Action: yyDollar[1].str, Comments: Comments(yyDollar[2].bytes2), Ignore: yyDollar[3].str, Table: yyDollar[4].tableName, Partitions: yyDollar[5].partitions, Columns: cols, Rows: Values{vals}, OnDup: OnDup(yyDollar[8].updateExprs)} } - case 35: + case 38: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:438 +//line sql.y:452 { yyVAL.str = InsertStr } - case 36: + case 39: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:442 +//line sql.y:456 { yyVAL.str = ReplaceStr } - case 37: + case 40: yyDollar = yyS[yypt-9 : yypt+1] -//line sql.y:448 +//line sql.y:462 { yyVAL.statement = &Update{Comments: Comments(yyDollar[2].bytes2), Ignore: yyDollar[3].str, TableExprs: yyDollar[4].tableExprs, Exprs: yyDollar[6].updateExprs, Where: NewWhere(WhereStr, yyDollar[7].expr), OrderBy: yyDollar[8].orderBy, Limit: yyDollar[9].limit} } - case 38: + case 41: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:454 +//line sql.y:468 { yyVAL.statement = &Delete{Comments: Comments(yyDollar[2].bytes2), TableExprs: TableExprs{&AliasedTableExpr{Expr: yyDollar[4].tableName}}, Partitions: yyDollar[5].partitions, Where: NewWhere(WhereStr, yyDollar[6].expr), OrderBy: yyDollar[7].orderBy, Limit: yyDollar[8].limit} } - case 39: + case 42: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:458 +//line sql.y:472 { yyVAL.statement = &Delete{Comments: Comments(yyDollar[2].bytes2), Targets: yyDollar[4].tableNames, TableExprs: yyDollar[6].tableExprs, Where: NewWhere(WhereStr, yyDollar[7].expr)} } - case 40: + case 43: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:462 +//line sql.y:476 { yyVAL.statement = &Delete{Comments: Comments(yyDollar[2].bytes2), Targets: yyDollar[3].tableNames, TableExprs: yyDollar[5].tableExprs, Where: NewWhere(WhereStr, yyDollar[6].expr)} } - case 41: + case 44: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:466 +//line sql.y:480 { yyVAL.statement = &Delete{Comments: Comments(yyDollar[2].bytes2), Targets: yyDollar[3].tableNames, TableExprs: yyDollar[5].tableExprs, Where: NewWhere(WhereStr, yyDollar[6].expr)} } - case 42: + case 45: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:471 +//line sql.y:485 { } - case 43: + case 46: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:472 +//line sql.y:486 { } - case 44: + case 47: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:476 +//line sql.y:490 { yyVAL.tableNames = TableNames{yyDollar[1].tableName} } - case 45: + case 48: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:480 +//line sql.y:494 { yyVAL.tableNames = append(yyVAL.tableNames, yyDollar[3].tableName) } - case 46: + case 49: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:486 +//line sql.y:500 { yyVAL.tableNames = TableNames{yyDollar[1].tableName} } - case 47: + case 50: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:490 +//line sql.y:504 { yyVAL.tableNames = append(yyVAL.tableNames, yyDollar[3].tableName) } - case 48: + case 51: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:495 +//line sql.y:509 { yyVAL.partitions = nil } - case 49: + case 52: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:499 +//line sql.y:513 { yyVAL.partitions = yyDollar[3].partitions } - case 50: + case 53: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:505 +//line sql.y:519 { yyVAL.statement = &Set{Comments: Comments(yyDollar[2].bytes2), Exprs: yyDollar[3].setExprs} } - case 51: + case 54: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:509 +//line sql.y:523 { yyVAL.statement = &Set{Comments: Comments(yyDollar[2].bytes2), Scope: yyDollar[3].str, Exprs: yyDollar[4].setExprs} } - case 52: + case 55: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:513 +//line sql.y:527 { yyVAL.statement = &Set{Comments: Comments(yyDollar[2].bytes2), Scope: yyDollar[3].str, Exprs: yyDollar[5].setExprs} } - case 53: + case 56: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:517 +//line sql.y:531 { yyVAL.statement = &Set{Comments: Comments(yyDollar[2].bytes2), Exprs: yyDollar[4].setExprs} } - case 54: + case 57: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:523 +//line sql.y:537 { yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} } - case 55: + case 58: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:527 +//line sql.y:541 { yyVAL.setExprs = append(yyVAL.setExprs, yyDollar[3].setExpr) } - case 56: + case 59: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:533 +//line sql.y:547 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(TransactionStr), Expr: NewStrVal([]byte(yyDollar[3].str))} } - case 57: + case 60: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:537 +//line sql.y:551 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(TransactionStr), Expr: NewStrVal([]byte(TxReadWrite))} } - case 58: + case 61: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:541 +//line sql.y:555 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(TransactionStr), Expr: NewStrVal([]byte(TxReadOnly))} } - case 59: + case 62: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:547 +//line sql.y:561 { yyVAL.str = IsolationLevelRepeatableRead } - case 60: + case 63: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:551 +//line sql.y:565 { yyVAL.str = IsolationLevelReadCommitted } - case 61: + case 64: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:555 +//line sql.y:569 { yyVAL.str = IsolationLevelReadUncommitted } - case 62: + case 65: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:559 +//line sql.y:573 { yyVAL.str = IsolationLevelSerializable } - case 63: + case 66: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:565 +//line sql.y:579 { yyVAL.str = SessionStr } - case 64: + case 67: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:569 +//line sql.y:583 { yyVAL.str = GlobalStr } - case 65: + case 68: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:575 +//line sql.y:589 { yyDollar[1].ddl.TableSpec = yyDollar[2].TableSpec yyVAL.statement = yyDollar[1].ddl } - case 66: + case 69: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:580 +//line sql.y:594 { // Create table [name] like [name] yyDollar[1].ddl.OptLike = yyDollar[2].optLike yyVAL.statement = yyDollar[1].ddl } - case 67: + case 70: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:586 +//line sql.y:600 { // Change this to an alter statement yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[7].tableName} } - case 68: + case 71: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:591 +//line sql.y:605 { yyVAL.statement = &DDL{Action: CreateStr, Table: yyDollar[3].tableName.ToViewName()} } - case 69: + case 72: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:595 +//line sql.y:609 { yyVAL.statement = &DDL{Action: CreateStr, Table: yyDollar[5].tableName.ToViewName()} } - case 70: + case 73: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:599 +//line sql.y:613 { - yyVAL.statement = &DBDDL{Action: CreateStr, DBName: string(yyDollar[4].bytes)} + yyVAL.statement = &DBDDL{Action: CreateStr, DBName: string(yyDollar[4].colIdent.String())} } - case 71: + case 74: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:603 +//line sql.y:617 { - yyVAL.statement = &DBDDL{Action: CreateStr, DBName: string(yyDollar[4].bytes)} + yyVAL.statement = &DBDDL{Action: CreateStr, DBName: string(yyDollar[4].colIdent.String())} } - case 72: + case 75: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:608 +//line sql.y:622 { yyVAL.colIdent = NewColIdent("") } - case 73: + case 76: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:612 +//line sql.y:626 { yyVAL.colIdent = yyDollar[2].colIdent } - case 74: + case 77: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:618 +//line sql.y:632 { - yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) + yyVAL.colIdent = yyDollar[1].colIdent } - case 75: + case 78: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:623 +//line sql.y:637 { var v []VindexParam yyVAL.vindexParams = v } - case 76: + case 79: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:628 +//line sql.y:642 { yyVAL.vindexParams = yyDollar[2].vindexParams } - case 77: + case 80: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:634 +//line sql.y:648 { yyVAL.vindexParams = make([]VindexParam, 0, 4) yyVAL.vindexParams = append(yyVAL.vindexParams, yyDollar[1].vindexParam) } - case 78: + case 81: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:639 +//line sql.y:653 { yyVAL.vindexParams = append(yyVAL.vindexParams, yyDollar[3].vindexParam) } - case 79: + case 82: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:645 +//line sql.y:659 { yyVAL.vindexParam = VindexParam{Key: yyDollar[1].colIdent, Val: yyDollar[3].str} } - case 80: + case 83: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:651 +//line sql.y:665 { yyVAL.ddl = &DDL{Action: CreateStr, Table: yyDollar[4].tableName} setDDL(yylex, yyVAL.ddl) } - case 81: + case 84: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:658 +//line sql.y:672 { yyVAL.TableSpec = yyDollar[2].TableSpec yyVAL.TableSpec.Options = yyDollar[4].str } - case 82: + case 85: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:665 +//line sql.y:679 { yyVAL.optLike = &OptLike{LikeTable: yyDollar[2].tableName} } - case 83: + case 86: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:669 +//line sql.y:683 { yyVAL.optLike = &OptLike{LikeTable: yyDollar[3].tableName} } - case 84: + case 87: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:675 +//line sql.y:689 { yyVAL.TableSpec = &TableSpec{} yyVAL.TableSpec.AddColumn(yyDollar[1].columnDefinition) } - case 85: + case 88: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:680 +//line sql.y:694 { yyVAL.TableSpec.AddColumn(yyDollar[3].columnDefinition) } - case 86: + case 89: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:684 +//line sql.y:698 { yyVAL.TableSpec.AddIndex(yyDollar[3].indexDefinition) } - case 87: + case 90: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:688 +//line sql.y:702 { yyVAL.TableSpec.AddConstraint(yyDollar[3].constraintDefinition) } - case 88: + case 91: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:694 +//line sql.y:708 { yyDollar[2].columnType.NotNull = yyDollar[3].boolVal yyDollar[2].columnType.Default = yyDollar[4].optVal @@ -4113,110 +4087,86 @@ yydefault: yyDollar[2].columnType.Autoincrement = yyDollar[6].boolVal yyDollar[2].columnType.KeyOpt = yyDollar[7].colKeyOpt yyDollar[2].columnType.Comment = yyDollar[8].sqlVal - yyVAL.columnDefinition = &ColumnDefinition{Name: NewColIdent(string(yyDollar[1].bytes)), Type: yyDollar[2].columnType} + yyVAL.columnDefinition = &ColumnDefinition{Name: yyDollar[1].colIdent, Type: yyDollar[2].columnType} } - case 89: + case 92: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:705 +//line sql.y:719 { yyVAL.columnType = yyDollar[1].columnType yyVAL.columnType.Unsigned = yyDollar[2].boolVal yyVAL.columnType.Zerofill = yyDollar[3].boolVal } - case 93: + case 96: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:716 +//line sql.y:730 { yyVAL.columnType = yyDollar[1].columnType yyVAL.columnType.Length = yyDollar[2].sqlVal } - case 94: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:721 - { - yyVAL.columnType = yyDollar[1].columnType - } - case 95: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:727 - { - yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} - } - case 96: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:731 - { - yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} - } case 97: yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:735 { - yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} + yyVAL.columnType = yyDollar[1].columnType } case 98: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:739 +//line sql.y:741 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 99: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:743 +//line sql.y:745 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 100: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:747 +//line sql.y:749 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 101: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:751 +//line sql.y:753 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 102: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:755 +//line sql.y:757 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 103: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:759 +//line sql.y:761 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } case 104: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-1 : yypt+1] //line sql.y:765 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} - yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 105: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:771 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:769 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} - yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 106: - yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:777 + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:773 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} - yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length - yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 107: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:783 +//line sql.y:779 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length @@ -4224,717 +4174,741 @@ yydefault: } case 108: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:789 +//line sql.y:785 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 109: - yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:797 + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:791 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} - } + yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale + } case 110: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:801 +//line sql.y:797 { - yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} + yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 111: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:805 +//line sql.y:803 { - yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} + yyVAL.columnType.Length = yyDollar[2].LengthScaleOption.Length + yyVAL.columnType.Scale = yyDollar[2].LengthScaleOption.Scale } case 112: + yyDollar = yyS[yypt-1 : yypt+1] +//line sql.y:811 + { + yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} + } + case 113: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:809 +//line sql.y:815 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } - case 113: + case 114: + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:819 + { + yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + } + case 115: + yyDollar = yyS[yypt-2 : yypt+1] +//line sql.y:823 + { + yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} + } + case 116: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:813 +//line sql.y:827 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 114: + case 117: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:819 +//line sql.y:833 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Collate: yyDollar[4].str} } - case 115: + case 118: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:823 +//line sql.y:837 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Collate: yyDollar[4].str} } - case 116: + case 119: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:827 +//line sql.y:841 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } - case 117: + case 120: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:831 +//line sql.y:845 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } - case 118: + case 121: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:835 +//line sql.y:849 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Charset: yyDollar[2].str, Collate: yyDollar[3].str} } - case 119: + case 122: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:839 +//line sql.y:853 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Charset: yyDollar[2].str, Collate: yyDollar[3].str} } - case 120: + case 123: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:843 +//line sql.y:857 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Charset: yyDollar[2].str, Collate: yyDollar[3].str} } - case 121: + case 124: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:847 +//line sql.y:861 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), Charset: yyDollar[2].str, Collate: yyDollar[3].str} } - case 122: + case 125: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:851 +//line sql.y:865 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 123: + case 126: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:855 +//line sql.y:869 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 124: + case 127: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:859 +//line sql.y:873 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 125: + case 128: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:863 +//line sql.y:877 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 126: + case 129: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:867 +//line sql.y:881 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 127: + case 130: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:871 +//line sql.y:885 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), EnumValues: yyDollar[3].strs, Charset: yyDollar[5].str, Collate: yyDollar[6].str} } - case 128: + case 131: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:876 +//line sql.y:890 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes), EnumValues: yyDollar[3].strs, Charset: yyDollar[5].str, Collate: yyDollar[6].str} } - case 129: + case 132: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:882 +//line sql.y:896 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 130: + case 133: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:886 +//line sql.y:900 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 131: + case 134: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:890 +//line sql.y:904 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 132: + case 135: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:894 +//line sql.y:908 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 133: + case 136: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:898 +//line sql.y:912 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 134: + case 137: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:902 +//line sql.y:916 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 135: + case 138: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:906 +//line sql.y:920 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 136: + case 139: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:910 +//line sql.y:924 { yyVAL.columnType = ColumnType{Type: string(yyDollar[1].bytes)} } - case 137: + case 140: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:916 +//line sql.y:930 { yyVAL.strs = make([]string, 0, 4) yyVAL.strs = append(yyVAL.strs, "'"+string(yyDollar[1].bytes)+"'") } - case 138: + case 141: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:921 +//line sql.y:935 { yyVAL.strs = append(yyDollar[1].strs, "'"+string(yyDollar[3].bytes)+"'") } - case 139: + case 142: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:926 +//line sql.y:940 { yyVAL.sqlVal = nil } - case 140: + case 143: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:930 +//line sql.y:944 { yyVAL.sqlVal = NewIntVal(yyDollar[2].bytes) } - case 141: + case 144: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:935 +//line sql.y:949 { yyVAL.LengthScaleOption = LengthScaleOption{} } - case 142: + case 145: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:939 +//line sql.y:953 { yyVAL.LengthScaleOption = LengthScaleOption{ Length: NewIntVal(yyDollar[2].bytes), Scale: NewIntVal(yyDollar[4].bytes), } } - case 143: + case 146: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:947 +//line sql.y:961 { yyVAL.LengthScaleOption = LengthScaleOption{} } - case 144: + case 147: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:951 +//line sql.y:965 { yyVAL.LengthScaleOption = LengthScaleOption{ Length: NewIntVal(yyDollar[2].bytes), } } - case 145: + case 148: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:957 +//line sql.y:971 { yyVAL.LengthScaleOption = LengthScaleOption{ Length: NewIntVal(yyDollar[2].bytes), Scale: NewIntVal(yyDollar[4].bytes), } } - case 146: + case 149: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:965 +//line sql.y:979 { yyVAL.boolVal = BoolVal(false) } - case 147: + case 150: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:969 +//line sql.y:983 { yyVAL.boolVal = BoolVal(true) } - case 148: + case 151: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:974 +//line sql.y:988 { yyVAL.boolVal = BoolVal(false) } - case 149: + case 152: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:978 +//line sql.y:992 { yyVAL.boolVal = BoolVal(true) } - case 150: + case 153: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:984 +//line sql.y:998 { yyVAL.boolVal = BoolVal(false) } - case 151: + case 154: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:988 +//line sql.y:1002 { yyVAL.boolVal = BoolVal(false) } - case 152: + case 155: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:992 +//line sql.y:1006 { yyVAL.boolVal = BoolVal(true) } - case 153: + case 156: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:997 +//line sql.y:1011 { yyVAL.optVal = nil } - case 154: + case 157: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1001 +//line sql.y:1015 { yyVAL.optVal = yyDollar[2].expr } - case 155: + case 158: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1006 +//line sql.y:1020 { yyVAL.optVal = nil } - case 156: + case 159: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1010 +//line sql.y:1024 { yyVAL.optVal = yyDollar[3].expr } - case 157: + case 160: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1015 +//line sql.y:1029 { yyVAL.boolVal = BoolVal(false) } - case 158: + case 161: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1019 +//line sql.y:1033 { yyVAL.boolVal = BoolVal(true) } - case 159: + case 162: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1024 +//line sql.y:1038 { yyVAL.str = "" } - case 160: + case 163: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1028 +//line sql.y:1042 { - yyVAL.str = string(yyDollar[3].bytes) + yyVAL.str = string(yyDollar[3].colIdent.String()) } - case 161: + case 164: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1032 +//line sql.y:1046 { yyVAL.str = string(yyDollar[3].bytes) } - case 162: + case 165: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1037 +//line sql.y:1051 { yyVAL.str = "" } - case 163: + case 166: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1041 +//line sql.y:1055 { - yyVAL.str = string(yyDollar[2].bytes) + yyVAL.str = string(yyDollar[2].colIdent.String()) } - case 164: + case 167: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1045 +//line sql.y:1059 { yyVAL.str = string(yyDollar[2].bytes) } - case 165: + case 168: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1050 +//line sql.y:1064 { yyVAL.colKeyOpt = colKeyNone } - case 166: + case 169: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1054 +//line sql.y:1068 { yyVAL.colKeyOpt = colKeyPrimary } - case 167: + case 170: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1058 +//line sql.y:1072 { yyVAL.colKeyOpt = colKey } - case 168: + case 171: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1062 +//line sql.y:1076 { yyVAL.colKeyOpt = colKeyUniqueKey } - case 169: + case 172: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1066 +//line sql.y:1080 { yyVAL.colKeyOpt = colKeyUnique } - case 170: + case 173: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1071 +//line sql.y:1085 { yyVAL.sqlVal = nil } - case 171: + case 174: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1075 +//line sql.y:1089 { yyVAL.sqlVal = NewStrVal(yyDollar[2].bytes) } - case 172: + case 175: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1081 +//line sql.y:1095 { yyVAL.indexDefinition = &IndexDefinition{Info: yyDollar[1].indexInfo, Columns: yyDollar[3].indexColumns, Options: yyDollar[5].indexOptions} } - case 173: + case 176: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1085 +//line sql.y:1099 { yyVAL.indexDefinition = &IndexDefinition{Info: yyDollar[1].indexInfo, Columns: yyDollar[3].indexColumns} } - case 174: + case 177: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1091 +//line sql.y:1105 { yyVAL.indexOptions = []*IndexOption{yyDollar[1].indexOption} } - case 175: + case 178: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1095 +//line sql.y:1109 { yyVAL.indexOptions = append(yyVAL.indexOptions, yyDollar[2].indexOption) } - case 176: + case 179: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1101 +//line sql.y:1115 { - yyVAL.indexOption = &IndexOption{Name: string(yyDollar[1].bytes), Using: string(yyDollar[2].bytes)} + yyVAL.indexOption = &IndexOption{Name: string(yyDollar[1].bytes), Using: string(yyDollar[2].colIdent.String())} } - case 177: + case 180: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1105 +//line sql.y:1119 { // should not be string yyVAL.indexOption = &IndexOption{Name: string(yyDollar[1].bytes), Value: NewIntVal(yyDollar[3].bytes)} } - case 178: + case 181: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1110 +//line sql.y:1124 { yyVAL.indexOption = &IndexOption{Name: string(yyDollar[1].bytes), Value: NewStrVal(yyDollar[2].bytes)} } - case 179: + case 182: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1116 +//line sql.y:1130 { yyVAL.str = "" } - case 180: + case 183: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1120 +//line sql.y:1134 { yyVAL.str = string(yyDollar[1].bytes) } - case 181: + case 184: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1126 +//line sql.y:1140 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].bytes) + " " + string(yyDollar[2].bytes), Name: NewColIdent("PRIMARY"), Primary: true, Unique: true} } - case 182: + case 185: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1130 +//line sql.y:1144 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].bytes) + " " + string(yyDollar[2].str), Name: NewColIdent(yyDollar[3].str), Spatial: true, Unique: false} } - case 183: + case 186: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1134 +//line sql.y:1148 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].bytes) + " " + string(yyDollar[2].str), Name: NewColIdent(yyDollar[3].str), Unique: true} } - case 184: + case 187: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1138 +//line sql.y:1152 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].bytes), Name: NewColIdent(yyDollar[2].str), Unique: true} } - case 185: + case 188: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1142 +//line sql.y:1156 { yyVAL.indexInfo = &IndexInfo{Type: string(yyDollar[1].str), Name: NewColIdent(yyDollar[2].str), Unique: false} } - case 186: + case 189: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1148 +//line sql.y:1162 { yyVAL.str = string(yyDollar[1].bytes) } - case 187: + case 190: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1152 +//line sql.y:1166 { yyVAL.str = string(yyDollar[1].bytes) } - case 188: + case 191: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1157 +//line sql.y:1171 { yyVAL.str = "" } - case 189: + case 192: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1161 +//line sql.y:1175 { - yyVAL.str = string(yyDollar[1].bytes) + yyVAL.str = string(yyDollar[1].colIdent.String()) } - case 190: + case 193: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1167 +//line sql.y:1181 { yyVAL.indexColumns = []*IndexColumn{yyDollar[1].indexColumn} } - case 191: + case 194: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1171 +//line sql.y:1185 { yyVAL.indexColumns = append(yyVAL.indexColumns, yyDollar[3].indexColumn) } - case 192: + case 195: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1177 +//line sql.y:1191 { yyVAL.indexColumn = &IndexColumn{Column: yyDollar[1].colIdent, Length: yyDollar[2].sqlVal} } - case 193: + case 196: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1183 +//line sql.y:1197 { - yyVAL.constraintDefinition = &ConstraintDefinition{Name: string(yyDollar[2].bytes), Details: yyDollar[3].constraintInfo} + yyVAL.constraintDefinition = &ConstraintDefinition{Name: string(yyDollar[2].colIdent.String()), Details: yyDollar[3].constraintInfo} } - case 194: + case 197: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1187 +//line sql.y:1201 { yyVAL.constraintDefinition = &ConstraintDefinition{Details: yyDollar[1].constraintInfo} } - case 195: + case 198: yyDollar = yyS[yypt-10 : yypt+1] -//line sql.y:1194 +//line sql.y:1208 { yyVAL.constraintInfo = &ForeignKeyDefinition{Source: yyDollar[4].columns, ReferencedTable: yyDollar[7].tableName, ReferencedColumns: yyDollar[9].columns} } - case 196: + case 199: yyDollar = yyS[yypt-11 : yypt+1] -//line sql.y:1198 +//line sql.y:1212 { yyVAL.constraintInfo = &ForeignKeyDefinition{Source: yyDollar[4].columns, ReferencedTable: yyDollar[7].tableName, ReferencedColumns: yyDollar[9].columns, OnDelete: yyDollar[11].ReferenceAction} } - case 197: + case 200: yyDollar = yyS[yypt-11 : yypt+1] -//line sql.y:1202 +//line sql.y:1216 { yyVAL.constraintInfo = &ForeignKeyDefinition{Source: yyDollar[4].columns, ReferencedTable: yyDollar[7].tableName, ReferencedColumns: yyDollar[9].columns, OnUpdate: yyDollar[11].ReferenceAction} } - case 198: + case 201: yyDollar = yyS[yypt-12 : yypt+1] -//line sql.y:1206 +//line sql.y:1220 { yyVAL.constraintInfo = &ForeignKeyDefinition{Source: yyDollar[4].columns, ReferencedTable: yyDollar[7].tableName, ReferencedColumns: yyDollar[9].columns, OnDelete: yyDollar[11].ReferenceAction, OnUpdate: yyDollar[12].ReferenceAction} } - case 199: + case 202: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1212 +//line sql.y:1226 { yyVAL.ReferenceAction = yyDollar[3].ReferenceAction } - case 200: + case 203: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1218 +//line sql.y:1232 { yyVAL.ReferenceAction = yyDollar[3].ReferenceAction } - case 201: + case 204: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1224 +//line sql.y:1238 { yyVAL.ReferenceAction = Restrict } - case 202: + case 205: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1228 +//line sql.y:1242 { yyVAL.ReferenceAction = Cascade } - case 203: + case 206: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1232 +//line sql.y:1246 { yyVAL.ReferenceAction = NoAction } - case 204: + case 207: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1236 +//line sql.y:1250 { yyVAL.ReferenceAction = SetDefault } - case 205: + case 208: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1240 +//line sql.y:1254 { yyVAL.ReferenceAction = SetNull } - case 206: + case 209: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1245 +//line sql.y:1259 { yyVAL.str = "" } - case 207: + case 210: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1249 +//line sql.y:1263 { yyVAL.str = " " + string(yyDollar[1].str) } - case 208: + case 211: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1253 +//line sql.y:1267 { yyVAL.str = string(yyDollar[1].str) + ", " + string(yyDollar[3].str) } - case 209: + case 212: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1261 +//line sql.y:1275 { yyVAL.str = yyDollar[1].str } - case 210: + case 213: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1265 +//line sql.y:1279 { yyVAL.str = yyDollar[1].str + " " + yyDollar[2].str } - case 211: + case 214: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1269 +//line sql.y:1283 { yyVAL.str = yyDollar[1].str + "=" + yyDollar[3].str } - case 212: + case 215: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1275 +//line sql.y:1289 { yyVAL.str = yyDollar[1].colIdent.String() } - case 213: + case 216: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1279 +//line sql.y:1293 { yyVAL.str = "'" + string(yyDollar[1].bytes) + "'" } - case 214: + case 217: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1283 +//line sql.y:1297 { yyVAL.str = string(yyDollar[1].bytes) } - case 215: + case 218: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:1289 +//line sql.y:1303 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableName} } - case 216: + case 219: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1293 +//line sql.y:1307 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableName} } - case 217: + case 220: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1297 +//line sql.y:1311 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableName} } - case 218: + case 221: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1301 +//line sql.y:1315 { // Change this to a rename statement yyVAL.statement = &DDL{Action: RenameStr, FromTables: TableNames{yyDollar[4].tableName}, ToTables: TableNames{yyDollar[7].tableName}} } - case 219: + case 222: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1306 +//line sql.y:1320 { // Rename an index can just be an alter yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableName} } - case 220: + case 223: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1311 +//line sql.y:1325 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName.ToViewName()} } - case 221: + case 224: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1315 +//line sql.y:1329 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[4].tableName, PartitionSpec: yyDollar[5].partSpec} } - case 222: + case 225: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1319 +//line sql.y:1333 { - yyVAL.statement = &DBDDL{Action: AlterStr, DBName: string(yyDollar[3].bytes)} + yyVAL.statement = &DBDDL{Action: AlterStr, DBName: string(yyDollar[3].colIdent.String())} } - case 223: + case 226: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1323 +//line sql.y:1337 { - yyVAL.statement = &DBDDL{Action: AlterStr, DBName: string(yyDollar[3].bytes)} + yyVAL.statement = &DBDDL{Action: AlterStr, DBName: string(yyDollar[3].colIdent.String())} } - case 224: + case 227: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1327 +//line sql.y:1341 { yyVAL.statement = &DDL{ Action: CreateVindexStr, @@ -4946,9 +4920,9 @@ yydefault: }, } } - case 225: + case 228: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1339 +//line sql.y:1353 { yyVAL.statement = &DDL{ Action: DropVindexStr, @@ -4958,21 +4932,21 @@ yydefault: }, } } - case 226: + case 229: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1349 +//line sql.y:1363 { yyVAL.statement = &DDL{Action: AddVschemaTableStr, Table: yyDollar[5].tableName} } - case 227: + case 230: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1353 +//line sql.y:1367 { yyVAL.statement = &DDL{Action: DropVschemaTableStr, Table: yyDollar[5].tableName} } - case 228: + case 231: yyDollar = yyS[yypt-12 : yypt+1] -//line sql.y:1357 +//line sql.y:1371 { yyVAL.statement = &DDL{ Action: AddColVindexStr, @@ -4985,9 +4959,9 @@ yydefault: VindexCols: yyDollar[9].columns, } } - case 229: + case 232: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1370 +//line sql.y:1384 { yyVAL.statement = &DDL{ Action: DropColVindexStr, @@ -4997,15 +4971,15 @@ yydefault: }, } } - case 230: + case 233: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1380 +//line sql.y:1394 { yyVAL.statement = &DDL{Action: AddSequenceStr, Table: yyDollar[5].tableName} } - case 231: + case 234: yyDollar = yyS[yypt-9 : yypt+1] -//line sql.y:1384 +//line sql.y:1398 { yyVAL.statement = &DDL{ Action: AddAutoIncStr, @@ -5016,59 +4990,59 @@ yydefault: }, } } - case 244: + case 249: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1411 +//line sql.y:1427 { yyVAL.partSpec = &PartitionSpec{Action: ReorganizeStr, Name: yyDollar[3].colIdent, Definitions: yyDollar[6].partDefs} } - case 245: + case 250: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1417 +//line sql.y:1433 { yyVAL.partDefs = []*PartitionDefinition{yyDollar[1].partDef} } - case 246: + case 251: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1421 +//line sql.y:1437 { yyVAL.partDefs = append(yyDollar[1].partDefs, yyDollar[3].partDef) } - case 247: + case 252: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:1427 +//line sql.y:1443 { yyVAL.partDef = &PartitionDefinition{Name: yyDollar[2].colIdent, Limit: yyDollar[7].expr} } - case 248: + case 253: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:1431 +//line sql.y:1447 { yyVAL.partDef = &PartitionDefinition{Name: yyDollar[2].colIdent, Maxvalue: true} } - case 249: + case 254: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1437 +//line sql.y:1453 { yyVAL.statement = yyDollar[3].ddl } - case 250: + case 255: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1443 +//line sql.y:1459 { yyVAL.ddl = &DDL{Action: RenameStr, FromTables: TableNames{yyDollar[1].tableName}, ToTables: TableNames{yyDollar[3].tableName}} } - case 251: + case 256: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1447 +//line sql.y:1463 { yyVAL.ddl = yyDollar[1].ddl yyVAL.ddl.FromTables = append(yyVAL.ddl.FromTables, yyDollar[3].tableName) yyVAL.ddl.ToTables = append(yyVAL.ddl.ToTables, yyDollar[5].tableName) } - case 252: + case 257: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1455 +//line sql.y:1471 { var exists bool if yyDollar[3].byt != 0 { @@ -5076,16 +5050,16 @@ yydefault: } yyVAL.statement = &DDL{Action: DropStr, FromTables: yyDollar[4].tableNames, IfExists: exists} } - case 253: + case 258: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:1463 +//line sql.y:1479 { // Change this to an alter statement yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[5].tableName} } - case 254: + case 259: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1468 +//line sql.y:1484 { var exists bool if yyDollar[3].byt != 0 { @@ -5093,150 +5067,150 @@ yydefault: } yyVAL.statement = &DDL{Action: DropStr, FromTables: TableNames{yyDollar[4].tableName.ToViewName()}, IfExists: exists} } - case 255: + case 260: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1476 +//line sql.y:1492 { - yyVAL.statement = &DBDDL{Action: DropStr, DBName: string(yyDollar[4].bytes)} + yyVAL.statement = &DBDDL{Action: DropStr, DBName: string(yyDollar[4].colIdent.String())} } - case 256: + case 261: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1480 +//line sql.y:1496 { - yyVAL.statement = &DBDDL{Action: DropStr, DBName: string(yyDollar[4].bytes)} + yyVAL.statement = &DBDDL{Action: DropStr, DBName: string(yyDollar[4].colIdent.String())} } - case 257: + case 262: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1486 +//line sql.y:1502 { yyVAL.statement = &DDL{Action: TruncateStr, Table: yyDollar[3].tableName} } - case 258: + case 263: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1490 +//line sql.y:1506 { yyVAL.statement = &DDL{Action: TruncateStr, Table: yyDollar[2].tableName} } - case 259: + case 264: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1495 +//line sql.y:1511 { yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName} } - case 260: + case 265: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1501 +//line sql.y:1517 { - yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} + yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].colIdent.String())} } - case 261: + case 266: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1506 +//line sql.y:1522 { showTablesOpt := &ShowTablesOpt{Filter: yyDollar[4].showFilter} yyVAL.statement = &Show{Type: CharsetStr, ShowTablesOpt: showTablesOpt} } - case 262: + case 267: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1511 +//line sql.y:1527 { showTablesOpt := &ShowTablesOpt{Filter: yyDollar[3].showFilter} yyVAL.statement = &Show{Type: string(yyDollar[2].bytes), ShowTablesOpt: showTablesOpt} } - case 263: + case 268: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1516 +//line sql.y:1532 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } - case 264: + case 269: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1521 +//line sql.y:1537 { - yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} + yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].colIdent.String())} } - case 265: + case 270: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1525 +//line sql.y:1541 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } - case 266: + case 271: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1529 +//line sql.y:1545 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes), Table: yyDollar[4].tableName} } - case 267: + case 272: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1533 +//line sql.y:1549 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } - case 268: + case 273: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1537 +//line sql.y:1553 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } - case 269: + case 274: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1541 +//line sql.y:1557 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 270: + case 275: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1545 +//line sql.y:1561 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 271: + case 276: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1549 +//line sql.y:1565 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 272: + case 277: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1553 +//line sql.y:1569 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 273: + case 278: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1557 +//line sql.y:1573 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 274: + case 279: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1561 +//line sql.y:1577 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 275: + case 280: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1565 +//line sql.y:1581 { yyVAL.statement = &Show{Scope: yyDollar[2].str, Type: string(yyDollar[3].bytes)} } - case 276: + case 281: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1569 +//line sql.y:1585 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 277: + case 282: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1573 +//line sql.y:1589 { showTablesOpt := &ShowTablesOpt{Full: yyDollar[2].str, DbName: yyDollar[6].str, Filter: yyDollar[7].showFilter} yyVAL.statement = &Show{Type: string(yyDollar[3].str), ShowTablesOpt: showTablesOpt, OnTable: yyDollar[5].tableName} } - case 278: + case 283: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1578 +//line sql.y:1594 { // this is ugly, but I couldn't find a better way for now if yyDollar[3].str == "processlist" { @@ -5246,651 +5220,651 @@ yydefault: yyVAL.statement = &Show{Type: yyDollar[3].str, ShowTablesOpt: showTablesOpt} } } - case 279: + case 284: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1588 +//line sql.y:1604 { yyVAL.statement = &Show{Scope: yyDollar[2].str, Type: string(yyDollar[3].bytes)} } - case 280: + case 285: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1592 +//line sql.y:1608 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 281: + case 286: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1596 +//line sql.y:1612 { // Cannot dereference $4 directly, or else the parser stackcannot be pooled. See yyParsePooled showCollationFilterOpt := yyDollar[4].expr yyVAL.statement = &Show{Type: string(yyDollar[2].bytes), ShowCollationFilterOpt: &showCollationFilterOpt} } - case 282: + case 287: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1602 +//line sql.y:1618 { showTablesOpt := &ShowTablesOpt{Filter: yyDollar[4].showFilter} yyVAL.statement = &Show{Scope: string(yyDollar[2].bytes), Type: string(yyDollar[3].bytes), ShowTablesOpt: showTablesOpt} } - case 283: + case 288: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1607 +//line sql.y:1623 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } - case 284: + case 289: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1611 +//line sql.y:1627 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } - case 285: + case 290: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1615 +//line sql.y:1631 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes), OnTable: yyDollar[5].tableName} } - case 286: + case 291: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1619 +//line sql.y:1635 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } - case 287: + case 292: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1633 +//line sql.y:1649 { - yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} + yyVAL.statement = &Show{Type: string(yyDollar[2].colIdent.String())} } - case 288: + case 293: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1639 +//line sql.y:1655 { yyVAL.str = string(yyDollar[1].bytes) } - case 289: + case 294: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1643 +//line sql.y:1659 { yyVAL.str = string(yyDollar[1].bytes) } - case 290: + case 295: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1649 +//line sql.y:1665 { yyVAL.str = "" } - case 291: + case 296: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1653 +//line sql.y:1669 { yyVAL.str = "full " } - case 292: + case 297: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1659 +//line sql.y:1675 { yyVAL.str = string(yyDollar[1].bytes) } - case 293: + case 298: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1663 +//line sql.y:1679 { yyVAL.str = string(yyDollar[1].bytes) } - case 294: + case 299: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1669 +//line sql.y:1685 { yyVAL.str = "" } - case 295: + case 300: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1673 +//line sql.y:1689 { yyVAL.str = yyDollar[2].tableIdent.v } - case 296: + case 301: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1677 +//line sql.y:1693 { yyVAL.str = yyDollar[2].tableIdent.v } - case 297: + case 302: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1683 +//line sql.y:1699 { yyVAL.showFilter = nil } - case 298: + case 303: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1687 +//line sql.y:1703 { yyVAL.showFilter = &ShowFilter{Like: string(yyDollar[2].bytes)} } - case 299: + case 304: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1691 +//line sql.y:1707 { yyVAL.showFilter = &ShowFilter{Filter: yyDollar[2].expr} } - case 300: + case 305: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1697 +//line sql.y:1713 { yyVAL.showFilter = nil } - case 301: + case 306: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1701 +//line sql.y:1717 { yyVAL.showFilter = &ShowFilter{Like: string(yyDollar[2].bytes)} } - case 302: + case 307: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1707 +//line sql.y:1723 { yyVAL.str = "" } - case 303: + case 308: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1711 +//line sql.y:1727 { yyVAL.str = SessionStr } - case 304: + case 309: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1715 +//line sql.y:1731 { yyVAL.str = GlobalStr } - case 305: + case 310: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1721 +//line sql.y:1737 { yyVAL.statement = &Use{DBName: yyDollar[2].tableIdent} } - case 306: + case 311: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1725 +//line sql.y:1741 { yyVAL.statement = &Use{DBName: TableIdent{v: ""}} } - case 307: + case 312: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1731 +//line sql.y:1747 { yyVAL.statement = &Begin{} } - case 308: + case 313: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1735 +//line sql.y:1751 { yyVAL.statement = &Begin{} } - case 309: + case 314: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1741 +//line sql.y:1757 { yyVAL.statement = &Commit{} } - case 310: + case 315: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1747 +//line sql.y:1763 { yyVAL.statement = &Rollback{} } - case 311: + case 316: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1753 +//line sql.y:1769 { yyVAL.statement = &OtherRead{} } - case 312: + case 317: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1757 +//line sql.y:1773 { yyVAL.statement = &OtherRead{} } - case 313: + case 318: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1761 +//line sql.y:1777 { yyVAL.statement = &OtherRead{} } - case 314: + case 319: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1765 +//line sql.y:1781 { yyVAL.statement = &OtherAdmin{} } - case 315: + case 320: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1769 +//line sql.y:1785 { yyVAL.statement = &OtherAdmin{} } - case 316: + case 321: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1773 +//line sql.y:1789 { yyVAL.statement = &OtherAdmin{} } - case 317: + case 322: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1777 +//line sql.y:1793 { yyVAL.statement = &OtherAdmin{} } - case 318: + case 323: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1783 +//line sql.y:1799 { yyVAL.statement = &DDL{Action: FlushStr} } - case 319: + case 324: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1787 +//line sql.y:1803 { setAllowComments(yylex, true) } - case 320: + case 325: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1791 +//line sql.y:1807 { yyVAL.bytes2 = yyDollar[2].bytes2 setAllowComments(yylex, false) } - case 321: + case 326: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1797 +//line sql.y:1813 { yyVAL.bytes2 = nil } - case 322: + case 327: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1801 +//line sql.y:1817 { yyVAL.bytes2 = append(yyDollar[1].bytes2, yyDollar[2].bytes) } - case 323: + case 328: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1807 +//line sql.y:1823 { yyVAL.str = UnionStr } - case 324: + case 329: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1811 +//line sql.y:1827 { yyVAL.str = UnionAllStr } - case 325: + case 330: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1815 +//line sql.y:1831 { yyVAL.str = UnionDistinctStr } - case 326: + case 331: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1820 +//line sql.y:1836 { yyVAL.str = "" } - case 327: + case 332: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1824 +//line sql.y:1840 { yyVAL.str = SQLNoCacheStr } - case 328: + case 333: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1828 +//line sql.y:1844 { yyVAL.str = SQLCacheStr } - case 329: + case 334: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1833 +//line sql.y:1849 { yyVAL.str = "" } - case 330: + case 335: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1837 +//line sql.y:1853 { yyVAL.str = DistinctStr } - case 331: + case 336: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1841 +//line sql.y:1857 { yyVAL.str = DistinctStr } - case 332: + case 337: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1846 +//line sql.y:1862 { yyVAL.str = "" } - case 333: + case 338: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1850 +//line sql.y:1866 { yyVAL.str = StraightJoinHint } - case 334: + case 339: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1855 +//line sql.y:1871 { yyVAL.selectExprs = nil } - case 335: + case 340: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1859 +//line sql.y:1875 { yyVAL.selectExprs = yyDollar[1].selectExprs } - case 336: + case 341: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1865 +//line sql.y:1881 { yyVAL.selectExprs = SelectExprs{yyDollar[1].selectExpr} } - case 337: + case 342: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1869 +//line sql.y:1885 { yyVAL.selectExprs = append(yyVAL.selectExprs, yyDollar[3].selectExpr) } - case 338: + case 343: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1875 +//line sql.y:1891 { yyVAL.selectExpr = &StarExpr{} } - case 339: + case 344: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1879 +//line sql.y:1895 { yyVAL.selectExpr = &AliasedExpr{Expr: yyDollar[1].expr, As: yyDollar[2].colIdent} } - case 340: + case 345: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1883 +//line sql.y:1899 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Name: yyDollar[1].tableIdent}} } - case 341: + case 346: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1887 +//line sql.y:1903 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}} } - case 342: + case 347: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1892 +//line sql.y:1908 { yyVAL.colIdent = ColIdent{} } - case 343: + case 348: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1896 +//line sql.y:1912 { yyVAL.colIdent = yyDollar[1].colIdent } - case 344: + case 349: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1900 +//line sql.y:1916 { yyVAL.colIdent = yyDollar[2].colIdent } - case 346: + case 351: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1907 +//line sql.y:1923 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 347: + case 352: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1912 +//line sql.y:1928 { yyVAL.tableExprs = TableExprs{&AliasedTableExpr{Expr: TableName{Name: NewTableIdent("dual")}}} } - case 348: + case 353: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1916 +//line sql.y:1932 { yyVAL.tableExprs = yyDollar[2].tableExprs } - case 349: + case 354: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1922 +//line sql.y:1938 { yyVAL.tableExprs = TableExprs{yyDollar[1].tableExpr} } - case 350: + case 355: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1926 +//line sql.y:1942 { yyVAL.tableExprs = append(yyVAL.tableExprs, yyDollar[3].tableExpr) } - case 353: + case 358: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1936 +//line sql.y:1952 { yyVAL.tableExpr = yyDollar[1].aliasedTableName } - case 354: + case 359: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1940 +//line sql.y:1956 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery, As: yyDollar[3].tableIdent} } - case 355: + case 360: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1944 +//line sql.y:1960 { // missed alias for subquery yylex.Error("Every derived table must have its own alias") return 1 } - case 356: + case 361: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1950 +//line sql.y:1966 { yyVAL.tableExpr = &ParenTableExpr{Exprs: yyDollar[2].tableExprs} } - case 357: + case 362: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1956 +//line sql.y:1972 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].tableIdent, Hints: yyDollar[3].indexHints} } - case 358: + case 363: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1960 +//line sql.y:1976 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, Partitions: yyDollar[4].partitions, As: yyDollar[6].tableIdent, Hints: yyDollar[7].indexHints} } - case 359: + case 364: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1966 +//line sql.y:1982 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 360: + case 365: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1970 +//line sql.y:1986 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 361: + case 366: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1976 +//line sql.y:1992 { yyVAL.partitions = Partitions{yyDollar[1].colIdent} } - case 362: + case 367: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1980 +//line sql.y:1996 { yyVAL.partitions = append(yyVAL.partitions, yyDollar[3].colIdent) } - case 363: + case 368: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1993 +//line sql.y:2009 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } - case 364: + case 369: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1997 +//line sql.y:2013 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } - case 365: + case 370: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2001 +//line sql.y:2017 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } - case 366: + case 371: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2005 +//line sql.y:2021 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr} } - case 367: + case 372: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2011 +//line sql.y:2027 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } - case 368: + case 373: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2013 +//line sql.y:2029 { yyVAL.joinCondition = JoinCondition{Using: yyDollar[3].columns} } - case 369: + case 374: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2017 +//line sql.y:2033 { yyVAL.joinCondition = JoinCondition{} } - case 370: + case 375: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2019 +//line sql.y:2035 { yyVAL.joinCondition = yyDollar[1].joinCondition } - case 371: + case 376: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2023 +//line sql.y:2039 { yyVAL.joinCondition = JoinCondition{} } - case 372: + case 377: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2025 +//line sql.y:2041 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } - case 373: + case 378: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2028 +//line sql.y:2044 { yyVAL.empty = struct{}{} } - case 374: + case 379: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2030 +//line sql.y:2046 { yyVAL.empty = struct{}{} } - case 375: + case 380: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2033 +//line sql.y:2049 { yyVAL.tableIdent = NewTableIdent("") } - case 376: + case 381: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2037 +//line sql.y:2053 { yyVAL.tableIdent = yyDollar[1].tableIdent } - case 377: + case 382: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2041 +//line sql.y:2057 { yyVAL.tableIdent = yyDollar[2].tableIdent } - case 379: + case 384: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2048 +//line sql.y:2064 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 380: + case 385: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2054 +//line sql.y:2070 { yyVAL.str = JoinStr } - case 381: + case 386: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2058 +//line sql.y:2074 { yyVAL.str = JoinStr } - case 382: + case 387: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2062 +//line sql.y:2078 { yyVAL.str = JoinStr } - case 383: + case 388: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2068 +//line sql.y:2084 { yyVAL.str = StraightJoinStr } - case 384: + case 389: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2074 +//line sql.y:2090 { yyVAL.str = LeftJoinStr } - case 385: + case 390: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2078 +//line sql.y:2094 { yyVAL.str = LeftJoinStr } - case 386: + case 391: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2082 +//line sql.y:2098 { yyVAL.str = RightJoinStr } - case 387: + case 392: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2086 +//line sql.y:2102 { yyVAL.str = RightJoinStr } - case 388: + case 393: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2092 +//line sql.y:2108 { yyVAL.str = NaturalJoinStr } - case 389: + case 394: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2096 +//line sql.y:2112 { if yyDollar[2].str == LeftJoinStr { yyVAL.str = NaturalLeftJoinStr @@ -5898,471 +5872,471 @@ yydefault: yyVAL.str = NaturalRightJoinStr } } - case 390: + case 395: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2106 +//line sql.y:2122 { yyVAL.tableName = yyDollar[2].tableName } - case 391: + case 396: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2110 +//line sql.y:2126 { yyVAL.tableName = yyDollar[1].tableName } - case 392: + case 397: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2116 +//line sql.y:2132 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } - case 393: + case 398: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2120 +//line sql.y:2136 { yyVAL.tableName = TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent} } - case 394: + case 399: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2126 +//line sql.y:2142 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } - case 395: + case 400: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2131 +//line sql.y:2147 { yyVAL.indexHints = nil } - case 396: + case 401: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2135 +//line sql.y:2151 { yyVAL.indexHints = &IndexHints{Type: UseStr, Indexes: yyDollar[4].columns} } - case 397: + case 402: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2139 +//line sql.y:2155 { yyVAL.indexHints = &IndexHints{Type: UseStr} } - case 398: + case 403: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2143 +//line sql.y:2159 { yyVAL.indexHints = &IndexHints{Type: IgnoreStr, Indexes: yyDollar[4].columns} } - case 399: + case 404: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2147 +//line sql.y:2163 { yyVAL.indexHints = &IndexHints{Type: ForceStr, Indexes: yyDollar[4].columns} } - case 400: + case 405: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2152 +//line sql.y:2168 { yyVAL.expr = nil } - case 401: + case 406: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2156 +//line sql.y:2172 { yyVAL.expr = yyDollar[2].expr } - case 402: + case 407: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2162 +//line sql.y:2178 { yyVAL.expr = yyDollar[1].expr } - case 403: + case 408: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2166 +//line sql.y:2182 { yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } - case 404: + case 409: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2170 +//line sql.y:2186 { yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } - case 405: + case 410: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2174 +//line sql.y:2190 { yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} } - case 406: + case 411: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2178 +//line sql.y:2194 { yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} } - case 407: + case 412: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2182 +//line sql.y:2198 { yyVAL.expr = yyDollar[1].expr } - case 408: + case 413: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2186 +//line sql.y:2202 { yyVAL.expr = &Default{ColName: yyDollar[2].str} } - case 409: + case 414: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2192 +//line sql.y:2208 { yyVAL.str = "" } - case 410: + case 415: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2196 +//line sql.y:2212 { - yyVAL.str = string(yyDollar[2].bytes) + yyVAL.str = string(yyDollar[2].colIdent.String()) } - case 411: + case 416: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2202 +//line sql.y:2218 { yyVAL.boolVal = BoolVal(true) } - case 412: + case 417: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2206 +//line sql.y:2222 { yyVAL.boolVal = BoolVal(false) } - case 413: + case 418: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2212 +//line sql.y:2228 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } - case 414: + case 419: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2216 +//line sql.y:2232 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } - case 415: + case 420: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2220 +//line sql.y:2236 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } - case 416: + case 421: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2224 +//line sql.y:2240 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } - case 417: + case 422: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2228 +//line sql.y:2244 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } - case 418: + case 423: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2232 +//line sql.y:2248 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } - case 419: + case 424: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2236 +//line sql.y:2252 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } - case 420: + case 425: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2240 +//line sql.y:2256 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } - case 421: + case 426: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2244 +//line sql.y:2260 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } - case 422: + case 427: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2248 +//line sql.y:2264 { yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } - case 423: + case 428: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2254 +//line sql.y:2270 { yyVAL.str = IsNullStr } - case 424: + case 429: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2258 +//line sql.y:2274 { yyVAL.str = IsNotNullStr } - case 425: + case 430: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2262 +//line sql.y:2278 { yyVAL.str = IsTrueStr } - case 426: + case 431: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2266 +//line sql.y:2282 { yyVAL.str = IsNotTrueStr } - case 427: + case 432: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2270 +//line sql.y:2286 { yyVAL.str = IsFalseStr } - case 428: + case 433: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2274 +//line sql.y:2290 { yyVAL.str = IsNotFalseStr } - case 429: + case 434: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2280 +//line sql.y:2296 { yyVAL.str = EqualStr } - case 430: + case 435: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2284 +//line sql.y:2300 { yyVAL.str = LessThanStr } - case 431: + case 436: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2288 +//line sql.y:2304 { yyVAL.str = GreaterThanStr } - case 432: + case 437: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2292 +//line sql.y:2308 { yyVAL.str = LessEqualStr } - case 433: + case 438: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2296 +//line sql.y:2312 { yyVAL.str = GreaterEqualStr } - case 434: + case 439: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2300 +//line sql.y:2316 { yyVAL.str = NotEqualStr } - case 435: + case 440: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2304 +//line sql.y:2320 { yyVAL.str = NullSafeEqualStr } - case 436: + case 441: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2309 +//line sql.y:2325 { yyVAL.expr = nil } - case 437: + case 442: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2313 +//line sql.y:2329 { yyVAL.expr = yyDollar[2].expr } - case 438: + case 443: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2319 +//line sql.y:2335 { yyVAL.colTuple = yyDollar[1].valTuple } - case 439: + case 444: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2323 +//line sql.y:2339 { yyVAL.colTuple = yyDollar[1].subquery } - case 440: + case 445: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2327 +//line sql.y:2343 { yyVAL.colTuple = ListArg(yyDollar[1].bytes) } - case 441: + case 446: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2333 +//line sql.y:2349 { yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } - case 442: + case 447: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2339 +//line sql.y:2355 { yyVAL.exprs = Exprs{yyDollar[1].expr} } - case 443: + case 448: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2343 +//line sql.y:2359 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } - case 444: + case 449: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2349 +//line sql.y:2365 { yyVAL.expr = yyDollar[1].expr } - case 445: + case 450: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2353 +//line sql.y:2369 { yyVAL.expr = yyDollar[1].boolVal } - case 446: + case 451: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2357 +//line sql.y:2373 { yyVAL.expr = yyDollar[1].colName } - case 447: + case 452: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2361 +//line sql.y:2377 { yyVAL.expr = yyDollar[1].expr } - case 448: + case 453: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2365 +//line sql.y:2381 { yyVAL.expr = yyDollar[1].subquery } - case 449: + case 454: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2369 +//line sql.y:2385 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } - case 450: + case 455: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2373 +//line sql.y:2389 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } - case 451: + case 456: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2377 +//line sql.y:2393 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } - case 452: + case 457: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2381 +//line sql.y:2397 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } - case 453: + case 458: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2385 +//line sql.y:2401 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } - case 454: + case 459: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2389 +//line sql.y:2405 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } - case 455: + case 460: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2393 +//line sql.y:2409 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } - case 456: + case 461: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2397 +//line sql.y:2413 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } - case 457: + case 462: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2401 +//line sql.y:2417 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } - case 458: + case 463: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2405 +//line sql.y:2421 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } - case 459: + case 464: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2409 +//line sql.y:2425 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } - case 460: + case 465: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2413 +//line sql.y:2429 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} } - case 461: + case 466: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2417 +//line sql.y:2433 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONExtractOp, Right: yyDollar[3].expr} } - case 462: + case 467: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2421 +//line sql.y:2437 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONUnquoteExtractOp, Right: yyDollar[3].expr} } - case 463: + case 468: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2425 +//line sql.y:2441 { yyVAL.expr = &CollateExpr{Expr: yyDollar[1].expr, Charset: yyDollar[3].str} } - case 464: + case 469: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2429 +//line sql.y:2445 { yyVAL.expr = &UnaryExpr{Operator: BinaryStr, Expr: yyDollar[2].expr} } - case 465: + case 470: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2433 +//line sql.y:2449 { yyVAL.expr = &UnaryExpr{Operator: UBinaryStr, Expr: yyDollar[2].expr} } - case 466: + case 471: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2437 +//line sql.y:2453 { yyVAL.expr = &UnaryExpr{Operator: Utf8mb4Str, Expr: yyDollar[2].expr} } - case 467: + case 472: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2441 +//line sql.y:2457 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -6370,9 +6344,9 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UPlusStr, Expr: yyDollar[2].expr} } } - case 468: + case 473: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2449 +//line sql.y:2465 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -6386,21 +6360,21 @@ yydefault: yyVAL.expr = &UnaryExpr{Operator: UMinusStr, Expr: yyDollar[2].expr} } } - case 469: + case 474: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2463 +//line sql.y:2479 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } - case 470: + case 475: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2467 +//line sql.y:2483 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } - case 471: + case 476: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2471 +//line sql.y:2487 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -6408,497 +6382,497 @@ yydefault: // will be non-trivial because of grammar conflicts. yyVAL.expr = &IntervalExpr{Expr: yyDollar[2].expr, Unit: yyDollar[3].colIdent.String()} } - case 476: + case 481: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2489 +//line sql.y:2505 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } - case 477: + case 482: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2493 +//line sql.y:2509 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } - case 478: + case 483: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2497 +//line sql.y:2513 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } - case 479: + case 484: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2501 +//line sql.y:2517 { yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } - case 480: + case 485: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2511 +//line sql.y:2527 { yyVAL.expr = &FuncExpr{Name: NewColIdent("left"), Exprs: yyDollar[3].selectExprs} } - case 481: + case 486: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2515 +//line sql.y:2531 { yyVAL.expr = &FuncExpr{Name: NewColIdent("right"), Exprs: yyDollar[3].selectExprs} } - case 482: + case 487: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2519 +//line sql.y:2535 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } - case 483: + case 488: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2523 +//line sql.y:2539 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } - case 484: + case 489: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2527 +//line sql.y:2543 { yyVAL.expr = &ConvertUsingExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].str} } - case 485: + case 490: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2531 +//line sql.y:2547 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } - case 486: + case 491: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2535 +//line sql.y:2551 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } - case 487: + case 492: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2539 +//line sql.y:2555 { yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } - case 488: + case 493: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2543 +//line sql.y:2559 { yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } - case 489: + case 494: yyDollar = yyS[yypt-9 : yypt+1] -//line sql.y:2547 +//line sql.y:2563 { yyVAL.expr = &MatchExpr{Columns: yyDollar[3].selectExprs, Expr: yyDollar[7].expr, Option: yyDollar[8].str} } - case 490: + case 495: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2551 +//line sql.y:2567 { yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str, Limit: yyDollar[7].limit} } - case 491: + case 496: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2555 +//line sql.y:2571 { yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } - case 492: + case 497: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2559 +//line sql.y:2575 { yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} } - case 493: + case 498: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2569 +//line sql.y:2585 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_timestamp")} } - case 494: + case 499: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2573 +//line sql.y:2589 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_timestamp")} } - case 495: + case 500: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2577 +//line sql.y:2593 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_time")} } - case 496: + case 501: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2582 +//line sql.y:2598 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_date")} } - case 497: + case 502: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2587 +//line sql.y:2603 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtime")} } - case 498: + case 503: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2592 +//line sql.y:2608 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtimestamp")} } - case 499: + case 504: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2598 +//line sql.y:2614 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_date")} } - case 500: + case 505: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2603 +//line sql.y:2619 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_time")} } - case 501: + case 506: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2608 +//line sql.y:2624 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_timestamp"), Fsp: yyDollar[2].expr} } - case 502: + case 507: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2612 +//line sql.y:2628 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_timestamp"), Fsp: yyDollar[2].expr} } - case 503: + case 508: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2616 +//line sql.y:2632 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_time"), Fsp: yyDollar[2].expr} } - case 504: + case 509: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2621 +//line sql.y:2637 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtime"), Fsp: yyDollar[2].expr} } - case 505: + case 510: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2626 +//line sql.y:2642 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtimestamp"), Fsp: yyDollar[2].expr} } - case 506: + case 511: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2631 +//line sql.y:2647 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_time"), Fsp: yyDollar[2].expr} } - case 507: + case 512: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2635 +//line sql.y:2651 { yyVAL.expr = &TimestampFuncExpr{Name: string("timestampadd"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } - case 508: + case 513: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2639 +//line sql.y:2655 { yyVAL.expr = &TimestampFuncExpr{Name: string("timestampdiff"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } - case 511: + case 516: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2649 +//line sql.y:2665 { yyVAL.expr = yyDollar[2].expr } - case 512: + case 517: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2659 +//line sql.y:2675 { yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } - case 513: + case 518: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2663 +//line sql.y:2679 { yyVAL.expr = &FuncExpr{Name: NewColIdent("database"), Exprs: yyDollar[3].selectExprs} } - case 514: + case 519: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2667 +//line sql.y:2683 { yyVAL.expr = &FuncExpr{Name: NewColIdent("schema"), Exprs: yyDollar[3].selectExprs} } - case 515: + case 520: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2671 +//line sql.y:2687 { yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } - case 516: + case 521: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2675 +//line sql.y:2691 { yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } - case 517: + case 522: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2679 +//line sql.y:2695 { yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } - case 518: + case 523: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2683 +//line sql.y:2699 { yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } - case 519: + case 524: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2689 +//line sql.y:2705 { yyVAL.str = "" } - case 520: + case 525: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2693 +//line sql.y:2709 { yyVAL.str = BooleanModeStr } - case 521: + case 526: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2697 +//line sql.y:2713 { yyVAL.str = NaturalLanguageModeStr } - case 522: + case 527: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:2701 +//line sql.y:2717 { yyVAL.str = NaturalLanguageModeWithQueryExpansionStr } - case 523: + case 528: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2705 +//line sql.y:2721 { yyVAL.str = QueryExpansionStr } - case 524: + case 529: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2711 +//line sql.y:2727 { - yyVAL.str = string(yyDollar[1].bytes) + yyVAL.str = string(yyDollar[1].colIdent.String()) } - case 525: + case 530: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2715 +//line sql.y:2731 { yyVAL.str = string(yyDollar[1].bytes) } - case 526: + case 531: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2721 +//line sql.y:2737 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } - case 527: + case 532: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2725 +//line sql.y:2741 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Operator: CharacterSetStr} } - case 528: + case 533: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2729 +//line sql.y:2745 { - yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].bytes)} + yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].colIdent.String())} } - case 529: + case 534: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2733 +//line sql.y:2749 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 530: + case 535: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2737 +//line sql.y:2753 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } - case 531: + case 536: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2741 +//line sql.y:2757 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length yyVAL.convertType.Scale = yyDollar[2].LengthScaleOption.Scale } - case 532: + case 537: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2747 +//line sql.y:2763 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 533: + case 538: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2751 +//line sql.y:2767 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } - case 534: + case 539: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2755 +//line sql.y:2771 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 535: + case 540: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2759 +//line sql.y:2775 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 536: + case 541: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2763 +//line sql.y:2779 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } - case 537: + case 542: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2767 +//line sql.y:2783 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 538: + case 543: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2771 +//line sql.y:2787 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } - case 539: + case 544: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2776 +//line sql.y:2792 { yyVAL.expr = nil } - case 540: + case 545: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2780 +//line sql.y:2796 { yyVAL.expr = yyDollar[1].expr } - case 541: + case 546: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2785 +//line sql.y:2801 { yyVAL.str = string("") } - case 542: + case 547: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2789 +//line sql.y:2805 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } - case 543: + case 548: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2795 +//line sql.y:2811 { yyVAL.whens = []*When{yyDollar[1].when} } - case 544: + case 549: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2799 +//line sql.y:2815 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } - case 545: + case 550: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2805 +//line sql.y:2821 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } - case 546: + case 551: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2810 +//line sql.y:2826 { yyVAL.expr = nil } - case 547: + case 552: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2814 +//line sql.y:2830 { yyVAL.expr = yyDollar[2].expr } - case 548: + case 553: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2820 +//line sql.y:2836 { yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } - case 549: + case 554: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2824 +//line sql.y:2840 { yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } - case 550: + case 555: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2828 +//line sql.y:2844 { yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } - case 551: + case 556: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2834 +//line sql.y:2850 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } - case 552: + case 557: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2838 +//line sql.y:2854 { yyVAL.expr = NewHexVal(yyDollar[1].bytes) } - case 553: + case 558: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2842 +//line sql.y:2858 { yyVAL.expr = NewBitVal(yyDollar[1].bytes) } - case 554: + case 559: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2846 +//line sql.y:2862 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 555: + case 560: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2850 +//line sql.y:2866 { yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } - case 556: + case 561: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2854 +//line sql.y:2870 { yyVAL.expr = NewHexNum(yyDollar[1].bytes) } - case 557: + case 562: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2858 +//line sql.y:2874 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 558: + case 563: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2862 +//line sql.y:2878 { yyVAL.expr = &NullVal{} } - case 559: + case 564: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2868 +//line sql.y:2884 { // TODO(sougou): Deprecate this construct. if yyDollar[1].colIdent.Lowered() != "value" { @@ -6907,239 +6881,239 @@ yydefault: } yyVAL.expr = NewIntVal([]byte("1")) } - case 560: + case 565: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2877 +//line sql.y:2893 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } - case 561: + case 566: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2881 +//line sql.y:2897 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } - case 562: + case 567: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2886 +//line sql.y:2902 { yyVAL.exprs = nil } - case 563: + case 568: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2890 +//line sql.y:2906 { yyVAL.exprs = yyDollar[3].exprs } - case 564: + case 569: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2895 +//line sql.y:2911 { yyVAL.expr = nil } - case 565: + case 570: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2899 +//line sql.y:2915 { yyVAL.expr = yyDollar[2].expr } - case 566: + case 571: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2904 +//line sql.y:2920 { yyVAL.orderBy = nil } - case 567: + case 572: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2908 +//line sql.y:2924 { yyVAL.orderBy = yyDollar[3].orderBy } - case 568: + case 573: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2914 +//line sql.y:2930 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } - case 569: + case 574: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2918 +//line sql.y:2934 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } - case 570: + case 575: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2924 +//line sql.y:2940 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } - case 571: + case 576: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2929 +//line sql.y:2945 { yyVAL.str = AscScr } - case 572: + case 577: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2933 +//line sql.y:2949 { yyVAL.str = AscScr } - case 573: + case 578: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2937 +//line sql.y:2953 { yyVAL.str = DescScr } - case 574: + case 579: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2942 +//line sql.y:2958 { yyVAL.limit = nil } - case 575: + case 580: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2946 +//line sql.y:2962 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } - case 576: + case 581: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2950 +//line sql.y:2966 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } - case 577: + case 582: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2954 +//line sql.y:2970 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } - case 578: + case 583: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2959 +//line sql.y:2975 { yyVAL.str = "" } - case 579: + case 584: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2963 +//line sql.y:2979 { yyVAL.str = ForUpdateStr } - case 580: + case 585: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2967 +//line sql.y:2983 { yyVAL.str = ShareModeStr } - case 581: + case 586: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2980 +//line sql.y:2996 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } - case 582: + case 587: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2984 +//line sql.y:3000 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } - case 583: + case 588: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2988 +//line sql.y:3004 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } - case 584: + case 589: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2993 +//line sql.y:3009 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } - case 585: + case 590: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2997 +//line sql.y:3013 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } - case 586: + case 591: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:3001 +//line sql.y:3017 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } - case 587: + case 592: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3008 +//line sql.y:3024 { yyVAL.columns = Columns{yyDollar[1].colIdent} } - case 588: + case 593: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3012 +//line sql.y:3028 { yyVAL.columns = Columns{yyDollar[3].colIdent} } - case 589: + case 594: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3016 +//line sql.y:3032 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } - case 590: + case 595: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3020 +//line sql.y:3036 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } - case 591: + case 596: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3025 +//line sql.y:3041 { yyVAL.updateExprs = nil } - case 592: + case 597: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3029 +//line sql.y:3045 { yyVAL.updateExprs = yyDollar[5].updateExprs } - case 593: + case 598: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3035 +//line sql.y:3051 { yyVAL.values = Values{yyDollar[1].valTuple} } - case 594: + case 599: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3039 +//line sql.y:3055 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } - case 595: + case 600: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3045 +//line sql.y:3061 { yyVAL.valTuple = yyDollar[1].valTuple } - case 596: + case 601: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3049 +//line sql.y:3065 { yyVAL.valTuple = ValTuple{} } - case 597: + case 602: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3055 +//line sql.y:3071 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } - case 598: + case 603: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3061 +//line sql.y:3077 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -7147,312 +7121,312 @@ yydefault: yyVAL.expr = yyDollar[1].valTuple } } - case 599: + case 604: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3071 +//line sql.y:3087 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } - case 600: + case 605: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3075 +//line sql.y:3091 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } - case 601: + case 606: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3081 +//line sql.y:3097 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } - case 602: + case 607: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3087 +//line sql.y:3103 { yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} } - case 603: + case 608: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3091 +//line sql.y:3107 { yyVAL.setExprs = append(yyDollar[1].setExprs, yyDollar[3].setExpr) } - case 604: + case 609: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3097 +//line sql.y:3113 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("on"))} } - case 605: + case 610: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3101 +//line sql.y:3117 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("off"))} } - case 606: + case 611: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3105 +//line sql.y:3121 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: yyDollar[3].expr} } - case 607: + case 612: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3109 +//line sql.y:3125 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(string(yyDollar[1].bytes)), Expr: yyDollar[2].expr} } - case 609: + case 614: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3116 +//line sql.y:3132 { yyVAL.bytes = []byte("charset") } - case 611: + case 616: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3123 +//line sql.y:3139 { yyVAL.expr = NewStrVal([]byte(yyDollar[1].colIdent.String())) } - case 612: + case 617: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3127 +//line sql.y:3143 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } - case 613: + case 618: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3131 +//line sql.y:3147 { yyVAL.expr = &Default{} } - case 616: + case 621: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3140 +//line sql.y:3156 { yyVAL.byt = 0 } - case 617: + case 622: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3142 +//line sql.y:3158 { yyVAL.byt = 1 } - case 618: + case 623: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3145 +//line sql.y:3161 { yyVAL.empty = struct{}{} } - case 619: + case 624: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3147 +//line sql.y:3163 { yyVAL.empty = struct{}{} } - case 620: + case 625: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3150 +//line sql.y:3166 { yyVAL.str = "" } - case 621: + case 626: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3152 +//line sql.y:3168 { yyVAL.str = IgnoreStr } - case 622: + case 627: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3156 +//line sql.y:3172 { yyVAL.empty = struct{}{} } - case 623: + case 628: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3158 +//line sql.y:3174 { yyVAL.empty = struct{}{} } - case 624: + case 629: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3160 +//line sql.y:3176 { yyVAL.empty = struct{}{} } - case 625: + case 630: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3162 +//line sql.y:3178 { yyVAL.empty = struct{}{} } - case 626: + case 631: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3164 +//line sql.y:3180 { yyVAL.empty = struct{}{} } - case 627: + case 632: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3166 +//line sql.y:3182 { yyVAL.empty = struct{}{} } - case 628: + case 633: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3168 +//line sql.y:3184 { yyVAL.empty = struct{}{} } - case 629: + case 634: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3170 +//line sql.y:3186 { yyVAL.empty = struct{}{} } - case 630: + case 635: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3172 +//line sql.y:3188 { yyVAL.empty = struct{}{} } - case 631: + case 636: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3174 +//line sql.y:3190 { yyVAL.empty = struct{}{} } - case 632: + case 637: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3177 +//line sql.y:3193 { yyVAL.empty = struct{}{} } - case 633: + case 638: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3179 +//line sql.y:3195 { yyVAL.empty = struct{}{} } - case 634: + case 639: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3181 +//line sql.y:3197 { yyVAL.empty = struct{}{} } - case 635: + case 640: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3185 +//line sql.y:3201 { yyVAL.empty = struct{}{} } - case 636: + case 641: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3187 +//line sql.y:3203 { yyVAL.empty = struct{}{} } - case 637: + case 642: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3190 +//line sql.y:3206 { yyVAL.empty = struct{}{} } - case 638: + case 643: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3192 +//line sql.y:3208 { yyVAL.empty = struct{}{} } - case 639: + case 644: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3194 +//line sql.y:3210 { yyVAL.empty = struct{}{} } - case 640: + case 645: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3197 +//line sql.y:3213 { yyVAL.colIdent = ColIdent{} } - case 641: + case 646: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3199 +//line sql.y:3215 { yyVAL.colIdent = yyDollar[2].colIdent } - case 642: + case 647: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3203 +//line sql.y:3219 { - yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) + yyVAL.colIdent = yyDollar[1].colIdent } - case 643: + case 648: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3207 +//line sql.y:3223 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 645: + case 650: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3214 +//line sql.y:3230 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } - case 646: + case 651: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3220 +//line sql.y:3236 { - yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) + yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].colIdent.String())) } - case 647: + case 652: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3224 +//line sql.y:3240 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 649: + case 654: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3231 +//line sql.y:3247 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } - case 937: + case 942: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3544 +//line sql.y:3560 { if incNesting(yylex) { yylex.Error("max nesting level reached") return 1 } } - case 938: + case 943: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3553 +//line sql.y:3569 { decNesting(yylex) } - case 939: + case 944: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3558 +//line sql.y:3574 { skipToEnd(yylex) } - case 940: + case 945: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3563 +//line sql.y:3579 { skipToEnd(yylex) } - case 941: + case 946: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3567 +//line sql.y:3583 { skipToEnd(yylex) } - case 942: + case 947: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3571 +//line sql.y:3587 { skipToEnd(yylex) } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index f26b31d6cad..55140b62936 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -128,7 +128,7 @@ func skipToEnd(yylex interface{}) { %left JOIN STRAIGHT_JOIN LEFT RIGHT INNER OUTER CROSS NATURAL USE FORCE %left ON USING %token '(' ',' ')' -%token ID HEX STRING INTEGRAL FLOAT HEXNUM VALUE_ARG LIST_ARG COMMENT COMMENT_KEYWORD BIT_LITERAL +%token ID AT_ID AT_AT_ID HEX STRING INTEGRAL FLOAT HEXNUM VALUE_ARG LIST_ARG COMMENT COMMENT_KEYWORD BIT_LITERAL %token NULL TRUE FALSE OFF // Precedence dictated by mysql. But the vitess grammar is simplified. @@ -311,7 +311,7 @@ func skipToEnd(yylex interface{}) { %type partition_operation %type vindex_param %type vindex_param_list vindex_params_opt -%type vindex_type vindex_type_opt +%type id_or_var vindex_type vindex_type_opt %type alter_object_type %type fk_reference_action fk_on_delete fk_on_update @@ -357,6 +357,20 @@ command: setParseTree(yylex, nil) } +id_or_var: + ID + { + $$ = NewColIdentWithAt(string($1), NoAt) + } +| AT_ID + { + $$ = NewColIdentWithAt(string($1), SingleAt) + } +| AT_AT_ID + { + $$ = NewColIdentWithAt(string($1), DoubleAt) + } + select_statement: base_select order_by_opt limit_opt lock_opt { @@ -582,7 +596,7 @@ create_statement: $1.OptLike = $2 $$ = $1 } -| CREATE constraint_opt INDEX ID using_opt ON table_name ddl_skip_to_end +| CREATE constraint_opt INDEX id_or_var using_opt ON table_name ddl_skip_to_end { // Change this to an alter statement $$ = &DDL{Action: AlterStr, Table: $7} @@ -595,13 +609,13 @@ create_statement: { $$ = &DDL{Action: CreateStr, Table: $5.ToViewName()} } -| CREATE DATABASE not_exists_opt ID ddl_skip_to_end +| CREATE DATABASE not_exists_opt id_or_var ddl_skip_to_end { - $$ = &DBDDL{Action: CreateStr, DBName: string($4)} + $$ = &DBDDL{Action: CreateStr, DBName: string($4.String())} } -| CREATE SCHEMA not_exists_opt ID ddl_skip_to_end +| CREATE SCHEMA not_exists_opt id_or_var ddl_skip_to_end { - $$ = &DBDDL{Action: CreateStr, DBName: string($4)} + $$ = &DBDDL{Action: CreateStr, DBName: string($4.String())} } vindex_type_opt: @@ -614,9 +628,9 @@ vindex_type_opt: } vindex_type: - ID + id_or_var { - $$ = NewColIdent(string($1)) + $$ = $1 } vindex_params_opt: @@ -690,7 +704,7 @@ table_column_list: } column_definition: - ID column_type null_opt column_default_opt on_update_opt auto_increment_opt column_key_opt column_comment_opt + id_or_var column_type null_opt column_default_opt on_update_opt auto_increment_opt column_key_opt column_comment_opt { $2.NotNull = $3 $2.Default = $4 @@ -698,7 +712,7 @@ column_definition: $2.Autoincrement = $6 $2.KeyOpt = $7 $2.Comment = $8 - $$ = &ColumnDefinition{Name: NewColIdent(string($1)), Type: $2} + $$ = &ColumnDefinition{Name: $1, Type: $2} } column_type: numeric_type unsigned_opt zero_fill_opt @@ -1024,9 +1038,9 @@ charset_opt: { $$ = "" } -| CHARACTER SET ID +| CHARACTER SET id_or_var { - $$ = string($3) + $$ = string($3.String()) } | CHARACTER SET BINARY { @@ -1037,9 +1051,9 @@ collate_opt: { $$ = "" } -| COLLATE ID +| COLLATE id_or_var { - $$ = string($2) + $$ = string($2.String()) } | COLLATE STRING { @@ -1097,9 +1111,9 @@ index_option_list: } index_option: - USING ID + USING id_or_var { - $$ = &IndexOption{Name: string($1), Using: string($2)} + $$ = &IndexOption{Name: string($1), Using: string($2.String())} } | KEY_BLOCK_SIZE equal_opt INTEGRAL { @@ -1157,9 +1171,9 @@ name_opt: { $$ = "" } -| ID +| id_or_var { - $$ = string($1) + $$ = string($1.String()) } index_column_list: @@ -1179,9 +1193,9 @@ index_column: } constraint_definition: - CONSTRAINT ID constraint_info + CONSTRAINT id_or_var constraint_info { - $$ = &ConstraintDefinition{Name: string($2), Details: $3} + $$ = &ConstraintDefinition{Name: string($2.String()), Details: $3} } | constraint_info { @@ -1315,13 +1329,13 @@ alter_statement: { $$ = &DDL{Action: AlterStr, Table: $4, PartitionSpec: $5} } -| ALTER DATABASE ID ddl_skip_to_end +| ALTER DATABASE id_or_var ddl_skip_to_end { - $$ = &DBDDL{Action: AlterStr, DBName: string($3)} + $$ = &DBDDL{Action: AlterStr, DBName: string($3.String())} } -| ALTER SCHEMA ID ddl_skip_to_end +| ALTER SCHEMA id_or_var ddl_skip_to_end { - $$ = &DBDDL{Action: AlterStr, DBName: string($3)} + $$ = &DBDDL{Action: AlterStr, DBName: string($3.String())} } | ALTER VSCHEMA CREATE VINDEX table_name vindex_type_opt vindex_params_opt { @@ -1399,6 +1413,8 @@ alter_object_type: | FOREIGN | FULLTEXT | ID +| AT_ID +| AT_AT_ID | INDEX | KEY | PRIMARY @@ -1459,7 +1475,7 @@ drop_statement: } $$ = &DDL{Action: DropStr, FromTables: $4, IfExists: exists} } -| DROP INDEX ID ON table_name ddl_skip_to_end +| DROP INDEX id_or_var ON table_name ddl_skip_to_end { // Change this to an alter statement $$ = &DDL{Action: AlterStr, Table: $5} @@ -1472,13 +1488,13 @@ drop_statement: } $$ = &DDL{Action: DropStr, FromTables: TableNames{$4.ToViewName()}, IfExists: exists} } -| DROP DATABASE exists_opt ID +| DROP DATABASE exists_opt id_or_var { - $$ = &DBDDL{Action: DropStr, DBName: string($4)} + $$ = &DBDDL{Action: DropStr, DBName: string($4.String())} } -| DROP SCHEMA exists_opt ID +| DROP SCHEMA exists_opt id_or_var { - $$ = &DBDDL{Action: DropStr, DBName: string($4)} + $$ = &DBDDL{Action: DropStr, DBName: string($4.String())} } truncate_statement: @@ -1497,9 +1513,9 @@ analyze_statement: } show_statement: - SHOW BINARY ID ddl_skip_to_end /* SHOW BINARY LOGS */ + SHOW BINARY id_or_var ddl_skip_to_end /* SHOW BINARY LOGS */ { - $$ = &Show{Type: string($2) + " " + string($3)} + $$ = &Show{Type: string($2) + " " + string($3.String())} } /* SHOW CHARACTER SET and SHOW CHARSET are equivalent */ | SHOW CHARACTER SET like_or_where_opt @@ -1517,9 +1533,9 @@ show_statement: $$ = &Show{Type: string($2) + " " + string($3)} } /* Rule to handle SHOW CREATE EVENT, SHOW CREATE FUNCTION, etc. */ -| SHOW CREATE ID ddl_skip_to_end +| SHOW CREATE id_or_var ddl_skip_to_end { - $$ = &Show{Type: string($2) + " " + string($3)} + $$ = &Show{Type: string($2) + " " + string($3.String())} } | SHOW CREATE PROCEDURE ddl_skip_to_end { @@ -1629,9 +1645,9 @@ show_statement: * SHOW VITESS_SHARDS * SHOW VITESS_TARGET */ -| SHOW ID ddl_skip_to_end +| SHOW id_or_var ddl_skip_to_end { - $$ = &Show{Type: string($2)} + $$ = &Show{Type: string($2.String())} } tables_or_processlist: @@ -2192,9 +2208,9 @@ default_opt: { $$ = "" } -| openb ID closeb +| openb id_or_var closeb { - $$ = string($2) + $$ = string($2.String()) } boolean_value: @@ -2707,9 +2723,9 @@ match_option: } charset: - ID + id_or_var { - $$ = string($1) + $$ = string($1.String()) } | STRING { @@ -2725,9 +2741,9 @@ convert_type: { $$ = &ConvertType{Type: string($1), Length: $2, Charset: $3, Operator: CharacterSetStr} } -| CHAR length_opt ID +| CHAR length_opt id_or_var { - $$ = &ConvertType{Type: string($1), Length: $2, Charset: string($3)} + $$ = &ConvertType{Type: string($1), Length: $2, Charset: string($3.String())} } | DATE { @@ -3170,7 +3186,7 @@ non_add_drop_or_rename_operation: { $$ = struct{}{} } | UNUSED { $$ = struct{}{} } -| ID +| id_or_var { $$ = struct{}{} } to_opt: @@ -3199,9 +3215,9 @@ using_opt: { $$ = $2 } sql_id: - ID + id_or_var { - $$ = NewColIdent(string($1)) + $$ = $1 } | non_reserved_keyword { @@ -3216,9 +3232,9 @@ reserved_sql_id: } table_id: - ID + id_or_var { - $$ = NewTableIdent(string($1)) + $$ = NewTableIdent(string($1.String())) } | non_reserved_keyword { diff --git a/go/vt/sqlparser/token.go b/go/vt/sqlparser/token.go index e6d59753920..61c453f0d7f 100644 --- a/go/vt/sqlparser/token.go +++ b/go/vt/sqlparser/token.go @@ -494,6 +494,26 @@ func (tkn *Tokenizer) Scan() (int, []byte) { tkn.skipBlank() switch ch := tkn.lastChar; { + case ch == '@': + tokenID := AT_ID + tkn.next() + if tkn.lastChar == '@' { + tokenID = AT_AT_ID + tkn.next() + } + var tID int + var tBytes []byte + ch = tkn.lastChar + tkn.next() + if ch == '`' { + tID, tBytes = tkn.scanLiteralIdentifier() + } else { + tID, tBytes = tkn.scanIdentifier(byte(ch), true) + } + if tID == LEX_ERROR { + return tID, nil + } + return tokenID, tBytes case isLetter(ch): tkn.next() if ch == 'X' || ch == 'x' { @@ -508,11 +528,7 @@ func (tkn *Tokenizer) Scan() (int, []byte) { return tkn.scanBitLiteral() } } - isDbSystemVariable := false - if ch == '@' && tkn.lastChar == '@' { - isDbSystemVariable = true - } - return tkn.scanIdentifier(byte(ch), isDbSystemVariable) + return tkn.scanIdentifier(byte(ch), false) case isDigit(ch): return tkn.scanNumber(false) case ch == ':': @@ -651,10 +667,13 @@ func (tkn *Tokenizer) skipBlank() { } } -func (tkn *Tokenizer) scanIdentifier(firstByte byte, isDbSystemVariable bool) (int, []byte) { +func (tkn *Tokenizer) scanIdentifier(firstByte byte, isVariable bool) (int, []byte) { buffer := &bytes2.Buffer{} buffer.WriteByte(firstByte) - for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || (isDbSystemVariable && isCarat(tkn.lastChar)) { + for isLetter(tkn.lastChar) || + isDigit(tkn.lastChar) || + tkn.lastChar == '@' || + (isVariable && isCarat(tkn.lastChar)) { buffer.WriteByte(byte(tkn.lastChar)) tkn.next() } @@ -955,7 +974,7 @@ func (tkn *Tokenizer) reset() { } func isLetter(ch uint16) bool { - return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch == '@' + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' } func isCarat(ch uint16) bool { diff --git a/go/vt/sqlparser/token_test.go b/go/vt/sqlparser/token_test.go index 531696d37e9..cac25328e00 100644 --- a/go/vt/sqlparser/token_test.go +++ b/go/vt/sqlparser/token_test.go @@ -54,6 +54,22 @@ func TestLiteralID(t *testing.T) { in: "``", id: LEX_ERROR, out: "", + }, { + in: "@x", + id: AT_ID, + out: "x", + }, { + in: "@@x", + id: AT_AT_ID, + out: "x", + }, { + in: "@@`x y`", + id: AT_AT_ID, + out: "x y", + }, { + in: "@@`@x @y`", + id: AT_AT_ID, + out: "@x @y", }} for _, tcase := range testcases { diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 175fe4a04fe..ce2fc4196cf 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -287,16 +287,12 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql } normalized := sqlparser.String(rewriteResult.AST) sql = comments.Leading + normalized + comments.Trailing - if rewriteResult.NeedDatabase { - keyspace, _, _, _ := e.ParseDestinationTarget(safeSession.TargetString) - if keyspace == "" { - bindVars[sqlparser.DBVarName] = sqltypes.NullBindVariable - } else { - bindVars[sqlparser.DBVarName] = sqltypes.StringBindVariable(keyspace) - } + neededBindVariables, err := e.createNeededBindVariables(rewriteResult.BindVarNeeds, safeSession) + if err != nil { + return nil, err } - if rewriteResult.NeedLastInsertID { - bindVars[sqlparser.LastInsertIDName] = sqltypes.Uint64BindVariable(safeSession.GetLastInsertId()) + for k, v := range neededBindVariables { + bindVars[k] = v } } logStats.PlanTime = execStart.Sub(logStats.StartTime) @@ -327,20 +323,13 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql return nil, err } - bindVarNeeds := plan.BindVarNeeds - if bindVarNeeds.NeedLastInsertID { - bindVars[sqlparser.LastInsertIDName] = sqltypes.Uint64BindVariable(safeSession.GetLastInsertId()) - } - if bindVarNeeds.NeedDatabase { - keyspace, _, _, _ := e.ParseDestinationTarget(safeSession.TargetString) - if keyspace == "" { - bindVars[sqlparser.DBVarName] = sqltypes.NullBindVariable - } else { - bindVars[sqlparser.DBVarName] = sqltypes.StringBindVariable(keyspace) - } + neededBindVariables, err := e.createNeededBindVariables(plan.BindVarNeeds, safeSession) + if err != nil { + logStats.Error = err + return nil, err } - if bindVarNeeds.NeedFoundRows { - bindVars[sqlparser.FoundRowsName] = sqltypes.Uint64BindVariable(safeSession.FoundRows) + for k, v := range neededBindVariables { + bindVars[k] = v } qr, err := plan.Instructions.Execute(vcursor, bindVars, true) @@ -370,6 +359,37 @@ func (e *Executor) handleExec(ctx context.Context, safeSession *SafeSession, sql return qr, err } +// createNeededBindVariables creates a map of bind vars that are needed by the bindvar-needs sent in +func (e *Executor) createNeededBindVariables(bindVarNeeds sqlparser.BindVarNeeds, session *SafeSession) (map[string]*querypb.BindVariable, error) { + bindVars := make(map[string]*querypb.BindVariable) + + if bindVarNeeds.NeedDatabase { + keyspace, _, _, _ := e.ParseDestinationTarget(session.TargetString) + if keyspace == "" { + bindVars[sqlparser.DBVarName] = sqltypes.NullBindVariable + } else { + bindVars[sqlparser.DBVarName] = sqltypes.StringBindVariable(keyspace) + } + } + + if bindVarNeeds.NeedLastInsertID { + bindVars[sqlparser.LastInsertIDName] = sqltypes.Uint64BindVariable(session.GetLastInsertId()) + } + + // todo: do we need to check this map for nil? + if bindVarNeeds.NeedUserDefinedVariables && session.UserDefinedVariables != nil { + for k, v := range session.UserDefinedVariables { + bindVars[sqlparser.UserDefinedVariableName+k] = v + } + } + + if bindVarNeeds.NeedFoundRows { + bindVars[sqlparser.FoundRowsName] = sqltypes.Uint64BindVariable(session.FoundRows) + } + + return bindVars, nil +} + func (e *Executor) destinationExec(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, dest key.Destination, destKeyspace string, destTabletType topodatapb.TabletType, logStats *LogStats) (*sqltypes.Result, error) { return e.resolver.Execute(ctx, sql, bindVars, destKeyspace, destTabletType, dest, safeSession, false /* notInTransaction */, safeSession.Options, logStats, false /* canAutocommit */) } @@ -511,188 +531,204 @@ func (e *Executor) handleSet(ctx context.Context, safeSession *SafeSession, sql return &sqltypes.Result{}, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported in set: global") case sqlparser.VitessMetadataStr: return e.handleSetVitessMetadata(ctx, safeSession, k, v) - } - - switch k.Key { - case "autocommit": - val, err := validateSetOnOff(v, k.Key) + case sqlparser.VariableStr: + err := handleSetUserDefinedVariables(safeSession, k, v) if err != nil { return nil, err } + case sqlparser.ImplicitStr: + switch k.Key { + case "autocommit": + val, err := validateSetOnOff(v, k.Key) + if err != nil { + return nil, err + } - switch val { - case 0: - safeSession.Autocommit = false - case 1: - if safeSession.InTransaction() { - if err := e.txConn.Commit(ctx, safeSession); err != nil { - return nil, err + switch val { + case 0: + safeSession.Autocommit = false + case 1: + if safeSession.InTransaction() { + if err := e.txConn.Commit(ctx, safeSession); err != nil { + return nil, err + } } + safeSession.Autocommit = true + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for autocommit: %d", val) + } + case "client_found_rows": + val, ok := v.(int64) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for client_found_rows: %T", v) + } + if safeSession.Options == nil { + safeSession.Options = &querypb.ExecuteOptions{} + } + switch val { + case 0: + safeSession.Options.ClientFoundRows = false + case 1: + safeSession.Options.ClientFoundRows = true + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for client_found_rows: %d", val) + } + case "skip_query_plan_cache": + val, ok := v.(int64) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for skip_query_plan_cache: %T", v) + } + if safeSession.Options == nil { + safeSession.Options = &querypb.ExecuteOptions{} + } + switch val { + case 0: + safeSession.Options.SkipQueryPlanCache = false + case 1: + safeSession.Options.SkipQueryPlanCache = true + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for skip_query_plan_cache: %d", val) + } + case "sql_safe_updates": + val, err := validateSetOnOff(v, k.Key) + if err != nil { + return nil, err } - safeSession.Autocommit = true - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for autocommit: %d", val) - } - case "client_found_rows": - val, ok := v.(int64) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for client_found_rows: %T", v) - } - if safeSession.Options == nil { - safeSession.Options = &querypb.ExecuteOptions{} - } - switch val { - case 0: - safeSession.Options.ClientFoundRows = false - case 1: - safeSession.Options.ClientFoundRows = true - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for client_found_rows: %d", val) - } - case "skip_query_plan_cache": - val, ok := v.(int64) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for skip_query_plan_cache: %T", v) - } - if safeSession.Options == nil { - safeSession.Options = &querypb.ExecuteOptions{} - } - switch val { - case 0: - safeSession.Options.SkipQueryPlanCache = false - case 1: - safeSession.Options.SkipQueryPlanCache = true - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for skip_query_plan_cache: %d", val) - } - case "sql_safe_updates": - val, err := validateSetOnOff(v, k.Key) - if err != nil { - return nil, err - } - switch val { - case 0, 1: - // no op - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for sql_safe_updates: %d", val) - } - case "transaction_mode": - val, ok := v.(string) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for transaction_mode: %T", v) - } - out, ok := vtgatepb.TransactionMode_value[strings.ToUpper(val)] - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid transaction_mode: %s", val) - } - safeSession.TransactionMode = vtgatepb.TransactionMode(out) - case sqlparser.TransactionStr: - // Parser ensures it's well-formed. - - // TODO: This is a NOP, modeled off of tx_isolation and tx_read_only. It's incredibly - // dangerous that it's a NOP, but fixing that is left to. Note that vtqueryservice needs - // to be updated as well: - // https://github.com/vitessio/vitess/issues/4127 - case "tx_isolation": - val, ok := v.(string) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for tx_isolation: %T", v) - } - switch val { - case "repeatable read", "read committed", "read uncommitted", "serializable": - // TODO (4127): This is a dangerous NOP. - default: - return nil, fmt.Errorf("unexpected value for tx_isolation: %v", val) - } - case "tx_read_only", "transaction_read_only": - val, err := validateSetOnOff(v, k.Key) - if err != nil { - return nil, err - } - switch val { - case 0, 1: - // TODO (4127): This is a dangerous NOP. - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for %v: %d", k.Key, val) - } - case "workload": - val, ok := v.(string) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for workload: %T", v) - } - out, ok := querypb.ExecuteOptions_Workload_value[strings.ToUpper(val)] - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid workload: %s", val) - } - if safeSession.Options == nil { - safeSession.Options = &querypb.ExecuteOptions{} - } - safeSession.Options.Workload = querypb.ExecuteOptions_Workload(out) - case "sql_select_limit": - var val int64 - - switch cast := v.(type) { - case int64: - val = cast - case string: - if !strings.EqualFold(cast, "default") { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected string value for sql_select_limit: %v", v) + switch val { + case 0, 1: + // no op + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for sql_safe_updates: %d", val) + } + case "transaction_mode": + val, ok := v.(string) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for transaction_mode: %T", v) + } + out, ok := vtgatepb.TransactionMode_value[strings.ToUpper(val)] + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid transaction_mode: %s", val) + } + safeSession.TransactionMode = vtgatepb.TransactionMode(out) + case sqlparser.TransactionStr: + // Parser ensures it's well-formed. + + // TODO: This is a NOP, modeled off of tx_isolation and tx_read_only. It's incredibly + // dangerous that it's a NOP, but fixing that is left to. Note that vtqueryservice needs + // to be updated as well: + // https://github.com/vitessio/vitess/issues/4127 + case "tx_isolation": + val, ok := v.(string) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for tx_isolation: %T", v) + } + switch val { + case "repeatable read", "read committed", "read uncommitted", "serializable": + // TODO (4127): This is a dangerous NOP. + default: + return nil, fmt.Errorf("unexpected value for tx_isolation: %v", val) + } + case "tx_read_only", "transaction_read_only": + val, err := validateSetOnOff(v, k.Key) + if err != nil { + return nil, err + } + switch val { + case 0, 1: + // TODO (4127): This is a dangerous NOP. + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for %v: %d", k.Key, val) + } + case "workload": + val, ok := v.(string) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for workload: %T", v) + } + out, ok := querypb.ExecuteOptions_Workload_value[strings.ToUpper(val)] + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid workload: %s", val) + } + if safeSession.Options == nil { + safeSession.Options = &querypb.ExecuteOptions{} + } + safeSession.Options.Workload = querypb.ExecuteOptions_Workload(out) + case "sql_select_limit": + var val int64 + + switch cast := v.(type) { + case int64: + val = cast + case string: + if !strings.EqualFold(cast, "default") { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected string value for sql_select_limit: %v", v) + } + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for sql_select_limit: %T", v) } - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for sql_select_limit: %T", v) - } - if safeSession.Options == nil { - safeSession.Options = &querypb.ExecuteOptions{} - } - safeSession.Options.SqlSelectLimit = val - case "sql_auto_is_null": - val, ok := v.(int64) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for sql_auto_is_null: %T", v) - } - switch val { - case 0: - // This is the default setting for MySQL. Do nothing. - case 1: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "sql_auto_is_null is not currently supported") - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for sql_auto_is_null: %d", val) - } - case "character_set_results": - // This is a statement that mysql-connector-j sends at the beginning. We return a canned response for it. - switch v { - case nil, "utf8", "utf8mb4", "latin1": - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "disallowed value for character_set_results: %v", v) - } - case "wait_timeout": - _, ok := v.(int64) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for wait_timeout: %T", v) - } - case "sql_mode", "net_write_timeout", "net_read_timeout", "lc_messages", "collation_connection", "foreign_key_checks", "sql_quote_show_create", "unique_checks": - log.Warningf("Ignored inapplicable SET %v = %v", k, v) - warnings.Add("IgnoredSet", 1) - case "charset", "names": - val, ok := v.(string) - if !ok { - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for charset/names: %T", v) - } - switch val { - case "", "utf8", "utf8mb4", "latin1", "default": - break + if safeSession.Options == nil { + safeSession.Options = &querypb.ExecuteOptions{} + } + safeSession.Options.SqlSelectLimit = val + case "sql_auto_is_null": + val, ok := v.(int64) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for sql_auto_is_null: %T", v) + } + switch val { + case 0: + // This is the default setting for MySQL. Do nothing. + case 1: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "sql_auto_is_null is not currently supported") + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for sql_auto_is_null: %d", val) + } + case "character_set_results": + // This is a statement that mysql-connector-j sends at the beginning. We return a canned response for it. + switch v { + case nil, "utf8", "utf8mb4", "latin1": + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "disallowed value for character_set_results: %v", v) + } + case "wait_timeout": + _, ok := v.(int64) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for wait_timeout: %T", v) + } + case "sql_mode", "net_write_timeout", "net_read_timeout", "lc_messages", "collation_connection", "foreign_key_checks", "sql_quote_show_create", "unique_checks": + log.Warningf("Ignored inapplicable SET %v = %v", k, v) + warnings.Add("IgnoredSet", 1) + case "charset", "names": + val, ok := v.(string) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for charset/names: %T", v) + } + switch val { + case "", "utf8", "utf8mb4", "latin1", "default": + break + default: + return nil, fmt.Errorf("unexpected value for charset/names: %v", val) + } default: - return nil, fmt.Errorf("unexpected value for charset/names: %v", val) + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported construct: %s", sql) } - default: - return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported construct: %s", sql) + } + } return &sqltypes.Result{}, nil } +func handleSetUserDefinedVariables(session *SafeSession, k sqlparser.SetKey, v interface{}) error { + variable, err := sqltypes.BuildBindVariable(v) + if err != nil { + return err + } + session.SetUserDefinedVariable(k.Key, variable) + return nil +} + func (e *Executor) handleSetVitessMetadata(ctx context.Context, session *SafeSession, k sqlparser.SetKey, v interface{}) (*sqltypes.Result, error) { //TODO(kalfonso): move to its own acl check and consolidate into an acl component that can handle multiple operations (vschema, metadata) allowed := vschemaacl.Authorized(callerid.ImmediateCallerIDFromContext(ctx)) diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index 3cc40311a57..eec59509766 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -235,6 +235,24 @@ func TestSelectLastInsertId(t *testing.T) { assert.Equal(t, wantQueries, sbc1.Queries) } +func TestSelectUserDefindVariable(t *testing.T) { + executor, sbc1, _, _ := createExecutorEnv() + executor.normalize = true + logChan := QueryLogger.Subscribe("Test") + defer QueryLogger.Unsubscribe(logChan) + + sql := "select @foo" + masterSession = &vtgatepb.Session{UserDefinedVariables: createMap([]string{"foo"}, []interface{}{"bar"})} + _, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) + require.NoError(t, err) + wantQueries := []*querypb.BoundQuery{{ + Sql: "select :__vtudvfoo as `@foo` from dual", + BindVariables: map[string]*querypb.BindVariable{"__vtudvfoo": sqltypes.StringBindVariable("bar")}, + }} + + assert.Equal(t, wantQueries, sbc1.Queries) +} + func TestFoundRows(t *testing.T) { executor, sbc1, _, _ := createExecutorEnv() executor.normalize = true diff --git a/go/vt/vtgate/executor_set_test.go b/go/vt/vtgate/executor_set_test.go new file mode 100644 index 00000000000..1fa5f8605a4 --- /dev/null +++ b/go/vt/vtgate/executor_set_test.go @@ -0,0 +1,340 @@ +/* +Copyright 2019 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package vtgate + +import ( + "testing" + + "vitess.io/vitess/go/vt/vterrors" + + "context" + + "github.com/golang/protobuf/proto" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/vtgate/vschemaacl" + + querypb "vitess.io/vitess/go/vt/proto/query" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func createMap(keys []string, values []interface{}) map[string]*querypb.BindVariable { + result := make(map[string]*querypb.BindVariable) + for i, key := range keys { + variable, err := sqltypes.BuildBindVariable(values[i]) + if err != nil { + panic(err) + } + result[key] = variable + } + return result +} + +func TestExecutorSet(t *testing.T) { + executor, _, _, _ := createExecutorEnv() + + testcases := []struct { + in string + out *vtgatepb.Session + err string + }{{ + in: "set autocommit = 1", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set @@autocommit = true", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set @@session.autocommit = true", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set @@session.`autocommit` = true", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set @@session.'autocommit' = true", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set @@session.\"autocommit\" = true", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set autocommit = true", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set autocommit = on", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set autocommit = ON", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set autocommit = 'on'", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set autocommit = `on`", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set autocommit = \"on\"", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set autocommit = false", + out: &vtgatepb.Session{}, + }, { + in: "set autocommit = off", + out: &vtgatepb.Session{}, + }, { + in: "set autocommit = OFF", + out: &vtgatepb.Session{}, + }, { + in: "set AUTOCOMMIT = 0", + out: &vtgatepb.Session{}, + }, { + in: "set AUTOCOMMIT = 'aa'", + err: "unexpected value for autocommit: aa", + }, { + in: "set autocommit = 2", + err: "unexpected value for autocommit: 2", + }, { + in: "set client_found_rows = 1", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{ClientFoundRows: true}}, + }, { + in: "set client_found_rows = true", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{ClientFoundRows: true}}, + }, { + in: "set client_found_rows = 0", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{}}, + }, { + in: "set client_found_rows = false", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{}}, + }, { + in: "set @@global.client_found_rows = 1", + err: "unsupported in set: global", + }, { + in: "set global client_found_rows = 1", + err: "unsupported in set: global", + }, { + in: "set global @@session.client_found_rows = 1", + err: "unsupported in set: mixed using of variable scope", + }, { + in: "set client_found_rows = 'aa'", + err: "unexpected value type for client_found_rows: string", + }, { + in: "set client_found_rows = 2", + err: "unexpected value for client_found_rows: 2", + }, { + in: "set transaction_mode = 'unspecified'", + out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_UNSPECIFIED}, + }, { + in: "set transaction_mode = 'single'", + out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}, + }, { + in: "set transaction_mode = 'multi'", + out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_MULTI}, + }, { + in: "set transaction_mode = 'twopc'", + out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_TWOPC}, + }, { + in: "set transaction_mode = twopc", + out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_TWOPC}, + }, { + in: "set transaction_mode = 'aa'", + err: "invalid transaction_mode: aa", + }, { + in: "set transaction_mode = 1", + err: "unexpected value type for transaction_mode: int64", + }, { + in: "set workload = 'unspecified'", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_UNSPECIFIED}}, + }, { + in: "set workload = 'oltp'", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLTP}}, + }, { + in: "set workload = 'olap'", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLAP}}, + }, { + in: "set workload = 'dba'", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_DBA}}, + }, { + in: "set workload = 'aa'", + err: "invalid workload: aa", + }, { + in: "set workload = 1", + err: "unexpected value type for workload: int64", + }, { + in: "set transaction_mode = 'twopc', autocommit=1", + out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_TWOPC}, + }, { + in: "set sql_select_limit = 5", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{SqlSelectLimit: 5}}, + }, { + in: "set sql_select_limit = DEFAULT", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{SqlSelectLimit: 0}}, + }, { + in: "set sql_select_limit = 'asdfasfd'", + err: "unexpected string value for sql_select_limit: asdfasfd", + }, { + in: "set autocommit = 1+1", + err: "invalid syntax: 1 + 1", + }, { + in: "set character_set_results=null", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set character_set_results='abcd'", + err: "disallowed value for character_set_results: abcd", + }, { + in: "set foo = 1", + err: "unsupported construct: set foo = 1", + }, { + in: "set names utf8", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set names ascii", + err: "unexpected value for charset/names: ascii", + }, { + in: "set charset utf8", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set character set default", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set character set ascii", + err: "unexpected value for charset/names: ascii", + }, { + in: "set net_write_timeout = 600", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set sql_mode = 'STRICT_ALL_TABLES'", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set net_read_timeout = 600", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set sql_quote_show_create = 1", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set foreign_key_checks = 0", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set unique_checks = 0", + out: &vtgatepb.Session{Autocommit: true}, + }, { + in: "set skip_query_plan_cache = 1", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{SkipQueryPlanCache: true}}, + }, { + in: "set skip_query_plan_cache = 0", + out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{}}, + }, { + in: "set sql_auto_is_null = 0", + out: &vtgatepb.Session{Autocommit: true}, // no effect + }, { + in: "set sql_auto_is_null = 1", + err: "sql_auto_is_null is not currently supported", + }, { + in: "set tx_read_only = 2", + err: "unexpected value for tx_read_only: 2", + }, { + in: "set transaction_read_only = 2", + err: "unexpected value for transaction_read_only: 2", + }, { + in: "set tx_isolation = 'invalid'", + err: "unexpected value for tx_isolation: invalid", + }, { + in: "set sql_safe_updates = 2", + err: "unexpected value for sql_safe_updates: 2", + }, { + in: "set @foo = 'bar'", + out: &vtgatepb.Session{UserDefinedVariables: createMap([]string{"foo"}, []interface{}{"bar"}), Autocommit: true}, + }, { + in: "set @foo = 2", + out: &vtgatepb.Session{UserDefinedVariables: createMap([]string{"foo"}, []interface{}{2}), Autocommit: true}, + }, { + in: "set @foo = 2.0, @bar = 'baz'", + out: &vtgatepb.Session{UserDefinedVariables: createMap([]string{"foo", "bar"}, []interface{}{2.0, "baz"}), Autocommit: true}, + }} + for _, tcase := range testcases { + t.Run(tcase.in, func(t *testing.T) { + session := NewSafeSession(&vtgatepb.Session{Autocommit: true}) + _, err := executor.Execute(context.Background(), "TestExecute", session, tcase.in, nil) + if err != nil { + if err.Error() != tcase.err { + t.Errorf("%s error: %v, want %s", tcase.in, err, tcase.err) + } + return + } + if !proto.Equal(session.Session, tcase.out) { + t.Errorf("%s: %v, want %s", tcase.in, session.Session, tcase.out) + } + }) + } +} + +func TestExecutorSetMetadata(t *testing.T) { + executor, _, _, _ := createExecutorEnv() + session := NewSafeSession(&vtgatepb.Session{TargetString: "@master", Autocommit: true}) + + set := "set @@vitess_metadata.app_keyspace_v1= '1'" + _, err := executor.Execute(context.Background(), "TestExecute", session, set, nil) + assert.Equalf(t, vtrpcpb.Code_PERMISSION_DENIED, vterrors.Code(err), "expected error %v, got error: %v", vtrpcpb.Code_PERMISSION_DENIED, err) + + *vschemaacl.AuthorizedDDLUsers = "%" + defer func() { + *vschemaacl.AuthorizedDDLUsers = "" + }() + + executor, _, _, _ = createExecutorEnv() + session = NewSafeSession(&vtgatepb.Session{TargetString: "@master", Autocommit: true}) + + set = "set @@vitess_metadata.app_keyspace_v1= '1'" + _, err = executor.Execute(context.Background(), "TestExecute", session, set, nil) + assert.NoError(t, err, "%s error: %v", set, err) + + show := `show vitess_metadata variables like 'app\\_keyspace\\_v_'` + result, err := executor.Execute(context.Background(), "TestExecute", session, show, nil) + assert.NoError(t, err) + + want := "1" + got := string(result.Rows[0][1].ToString()) + assert.Equalf(t, want, got, "want migrations %s, result %s", want, got) + + // Update metadata + set = "set @@vitess_metadata.app_keyspace_v2='2'" + _, err = executor.Execute(context.Background(), "TestExecute", session, set, nil) + assert.NoError(t, err, "%s error: %v", set, err) + + show = `show vitess_metadata variables like 'app\\_keyspace\\_v%'` + gotqr, err := executor.Execute(context.Background(), "TestExecute", session, show, nil) + assert.NoError(t, err) + + wantqr := &sqltypes.Result{ + Fields: buildVarCharFields("Key", "Value"), + Rows: [][]sqltypes.Value{ + buildVarCharRow("app_keyspace_v1", "1"), + buildVarCharRow("app_keyspace_v2", "2"), + }, + RowsAffected: 2, + } + + assert.Equal(t, wantqr.Fields, gotqr.Fields) + assert.ElementsMatch(t, wantqr.Rows, gotqr.Rows) + + show = "show vitess_metadata variables" + gotqr, err = executor.Execute(context.Background(), "TestExecute", session, show, nil) + require.NoError(t, err) + + assert.Equal(t, wantqr.Fields, gotqr.Fields) + assert.ElementsMatch(t, wantqr.Rows, gotqr.Rows) +} diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index d23b00131b2..0f382bde580 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -29,10 +29,8 @@ import ( "testing" "time" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/vterrors" - "context" + "vitess.io/vitess/go/vt/topo" "github.com/golang/protobuf/proto" "vitess.io/vitess/go/mysql" @@ -283,287 +281,6 @@ func TestExecutorTransactionsAutoCommit(t *testing.T) { } } -func TestExecutorSet(t *testing.T) { - executor, _, _, _ := createExecutorEnv() - - testcases := []struct { - in string - out *vtgatepb.Session - err string - }{{ - in: "set autocommit = 1", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set @@autocommit = true", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set @@session.autocommit = true", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set @@session.`autocommit` = true", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set @@session.'autocommit' = true", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set @@session.\"autocommit\" = true", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set autocommit = true", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set autocommit = on", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set autocommit = ON", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set autocommit = 'on'", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set autocommit = `on`", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set autocommit = \"on\"", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set autocommit = false", - out: &vtgatepb.Session{}, - }, { - in: "set autocommit = off", - out: &vtgatepb.Session{}, - }, { - in: "set autocommit = OFF", - out: &vtgatepb.Session{}, - }, { - in: "set AUTOCOMMIT = 0", - out: &vtgatepb.Session{}, - }, { - in: "set AUTOCOMMIT = 'aa'", - err: "unexpected value for autocommit: aa", - }, { - in: "set autocommit = 2", - err: "unexpected value for autocommit: 2", - }, { - in: "set client_found_rows = 1", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{ClientFoundRows: true}}, - }, { - in: "set client_found_rows = true", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{ClientFoundRows: true}}, - }, { - in: "set client_found_rows = 0", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{}}, - }, { - in: "set client_found_rows = false", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{}}, - }, { - in: "set @@global.client_found_rows = 1", - err: "unsupported in set: global", - }, { - in: "set global client_found_rows = 1", - err: "unsupported in set: global", - }, { - in: "set global @@session.client_found_rows = 1", - err: "unsupported in set: mixed using of variable scope", - }, { - in: "set client_found_rows = 'aa'", - err: "unexpected value type for client_found_rows: string", - }, { - in: "set client_found_rows = 2", - err: "unexpected value for client_found_rows: 2", - }, { - in: "set transaction_mode = 'unspecified'", - out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_UNSPECIFIED}, - }, { - in: "set transaction_mode = 'single'", - out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_SINGLE}, - }, { - in: "set transaction_mode = 'multi'", - out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_MULTI}, - }, { - in: "set transaction_mode = 'twopc'", - out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_TWOPC}, - }, { - in: "set transaction_mode = twopc", - out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_TWOPC}, - }, { - in: "set transaction_mode = 'aa'", - err: "invalid transaction_mode: aa", - }, { - in: "set transaction_mode = 1", - err: "unexpected value type for transaction_mode: int64", - }, { - in: "set workload = 'unspecified'", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_UNSPECIFIED}}, - }, { - in: "set workload = 'oltp'", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLTP}}, - }, { - in: "set workload = 'olap'", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_OLAP}}, - }, { - in: "set workload = 'dba'", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{Workload: querypb.ExecuteOptions_DBA}}, - }, { - in: "set workload = 'aa'", - err: "invalid workload: aa", - }, { - in: "set workload = 1", - err: "unexpected value type for workload: int64", - }, { - in: "set transaction_mode = 'twopc', autocommit=1", - out: &vtgatepb.Session{Autocommit: true, TransactionMode: vtgatepb.TransactionMode_TWOPC}, - }, { - in: "set sql_select_limit = 5", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{SqlSelectLimit: 5}}, - }, { - in: "set sql_select_limit = DEFAULT", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{SqlSelectLimit: 0}}, - }, { - in: "set sql_select_limit = 'asdfasfd'", - err: "unexpected string value for sql_select_limit: asdfasfd", - }, { - in: "set autocommit = 1+1", - err: "invalid syntax: 1 + 1", - }, { - in: "set character_set_results=null", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set character_set_results='abcd'", - err: "disallowed value for character_set_results: abcd", - }, { - in: "set foo = 1", - err: "unsupported construct: set foo = 1", - }, { - in: "set names utf8", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set names ascii", - err: "unexpected value for charset/names: ascii", - }, { - in: "set charset utf8", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set character set default", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set character set ascii", - err: "unexpected value for charset/names: ascii", - }, { - in: "set net_write_timeout = 600", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set sql_mode = 'STRICT_ALL_TABLES'", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set net_read_timeout = 600", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set sql_quote_show_create = 1", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set foreign_key_checks = 0", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set unique_checks = 0", - out: &vtgatepb.Session{Autocommit: true}, - }, { - in: "set skip_query_plan_cache = 1", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{SkipQueryPlanCache: true}}, - }, { - in: "set skip_query_plan_cache = 0", - out: &vtgatepb.Session{Autocommit: true, Options: &querypb.ExecuteOptions{}}, - }, { - in: "set sql_auto_is_null = 0", - out: &vtgatepb.Session{Autocommit: true}, // no effect - }, { - in: "set sql_auto_is_null = 1", - err: "sql_auto_is_null is not currently supported", - }, { - in: "set tx_read_only = 2", - err: "unexpected value for tx_read_only: 2", - }, { - in: "set transaction_read_only = 2", - err: "unexpected value for transaction_read_only: 2", - }, { - in: "set tx_isolation = 'invalid'", - err: "unexpected value for tx_isolation: invalid", - }, { - in: "set sql_safe_updates = 2", - err: "unexpected value for sql_safe_updates: 2", - }} - for _, tcase := range testcases { - session := NewSafeSession(&vtgatepb.Session{Autocommit: true}) - _, err := executor.Execute(context.Background(), "TestExecute", session, tcase.in, nil) - if err != nil { - if err.Error() != tcase.err { - t.Errorf("%s error: %v, want %s", tcase.in, err, tcase.err) - } - continue - } - if !proto.Equal(session.Session, tcase.out) { - t.Errorf("%s: %v, want %s", tcase.in, session.Session, tcase.out) - } - } -} - -func TestExecutorSetMetadata(t *testing.T) { - executor, _, _, _ := createExecutorEnv() - session := NewSafeSession(&vtgatepb.Session{TargetString: "@master", Autocommit: true}) - - set := "set @@vitess_metadata.app_keyspace_v1= '1'" - _, err := executor.Execute(context.Background(), "TestExecute", session, set, nil) - assert.Equalf(t, vtrpcpb.Code_PERMISSION_DENIED, vterrors.Code(err), "expected error %v, got error: %v", vtrpcpb.Code_PERMISSION_DENIED, err) - - *vschemaacl.AuthorizedDDLUsers = "%" - defer func() { - *vschemaacl.AuthorizedDDLUsers = "" - }() - - executor, _, _, _ = createExecutorEnv() - session = NewSafeSession(&vtgatepb.Session{TargetString: "@master", Autocommit: true}) - - set = "set @@vitess_metadata.app_keyspace_v1= '1'" - _, err = executor.Execute(context.Background(), "TestExecute", session, set, nil) - assert.NoError(t, err, "%s error: %v", set, err) - - show := `show vitess_metadata variables like 'app\\_keyspace\\_v_'` - result, err := executor.Execute(context.Background(), "TestExecute", session, show, nil) - assert.NoError(t, err) - - want := "1" - got := string(result.Rows[0][1].ToString()) - assert.Equalf(t, want, got, "want migrations %s, result %s", want, got) - - // Update metadata - set = "set @@vitess_metadata.app_keyspace_v2='2'" - _, err = executor.Execute(context.Background(), "TestExecute", session, set, nil) - assert.NoError(t, err, "%s error: %v", set, err) - - show = `show vitess_metadata variables like 'app\\_keyspace\\_v%'` - gotqr, err := executor.Execute(context.Background(), "TestExecute", session, show, nil) - assert.NoError(t, err) - - wantqr := &sqltypes.Result{ - Fields: buildVarCharFields("Key", "Value"), - Rows: [][]sqltypes.Value{ - buildVarCharRow("app_keyspace_v1", "1"), - buildVarCharRow("app_keyspace_v2", "2"), - }, - RowsAffected: 2, - } - - assert.Equal(t, wantqr.Fields, gotqr.Fields) - assert.ElementsMatch(t, wantqr.Rows, gotqr.Rows) - - show = "show vitess_metadata variables" - gotqr, err = executor.Execute(context.Background(), "TestExecute", session, show, nil) - require.NoError(t, err) - - assert.Equal(t, wantqr.Fields, gotqr.Fields) - assert.ElementsMatch(t, wantqr.Rows, gotqr.Rows) -} - func TestExecutorDeleteMetadata(t *testing.T) { *vschemaacl.AuthorizedDDLUsers = "%" defer func() { diff --git a/go/vt/vtgate/safe_session.go b/go/vt/vtgate/safe_session.go index 7e0e7b87aac..ef5a0987f53 100644 --- a/go/vt/vtgate/safe_session.go +++ b/go/vt/vtgate/safe_session.go @@ -237,3 +237,13 @@ func (session *SafeSession) ClearWarnings() { defer session.mu.Unlock() session.Session.Warnings = nil } + +// SetUserDefinedVariable sets the user defined variable in the session. +func (session *SafeSession) SetUserDefinedVariable(key string, value *querypb.BindVariable) { + session.mu.Lock() + defer session.mu.Unlock() + if session.UserDefinedVariables == nil { + session.UserDefinedVariables = make(map[string]*querypb.BindVariable) + } + session.UserDefinedVariables[key] = value +} diff --git a/proto/vtgate.proto b/proto/vtgate.proto index 870ae25835f..6b187530502 100644 --- a/proto/vtgate.proto +++ b/proto/vtgate.proto @@ -111,6 +111,9 @@ message Session { // found_rows keeps track of how many rows the last query returned uint64 found_rows = 12; + + // user_defined_variables contains all the @variables defined for this session + map user_defined_variables = 13; } // ExecuteRequest is the payload to Execute. From fcf30f4a703f9de5e2befa86c66de0e12c1780ea Mon Sep 17 00:00:00 2001 From: Toliver Jue Date: Thu, 19 Mar 2020 20:19:12 +0900 Subject: [PATCH 329/825] Clean up TestExecutorDDL Signed-off-by: Toliver Jue --- go/vt/vtgate/executor_test.go | 118 ++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index ec148f45594..967230897ea 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -35,6 +35,7 @@ import ( "context" "github.com/golang/protobuf/proto" + "github.com/google/go-cmp/cmp" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" @@ -1314,6 +1315,52 @@ func TestExecutorDDL(t *testing.T) { executor, sbc1, sbc2, sbclookup := createExecutorEnv() + type cnts struct { + Sbc1Cnt int64 + Sbc2Cnt int64 + SbcLookupCnt int64 + } + + tcs := []struct { + targetStr string + + hasNoKeyspaceErr bool + shardQueryCnt int + wantCnts cnts + }{ + { + targetStr: "", + hasNoKeyspaceErr: true, + }, + { + targetStr: KsTestUnsharded, + shardQueryCnt: 1, + wantCnts: cnts{ + Sbc1Cnt: 0, + Sbc2Cnt: 0, + SbcLookupCnt: 1, + }, + }, + { + targetStr: "TestExecutor", + shardQueryCnt: 8, + wantCnts: cnts{ + Sbc1Cnt: 1, + Sbc2Cnt: 1, + SbcLookupCnt: 0, + }, + }, + { + targetStr: "TestExecutor/-20", + shardQueryCnt: 1, + wantCnts: cnts{ + Sbc1Cnt: 1, + Sbc2Cnt: 0, + SbcLookupCnt: 0, + }, + }, + } + stmts := []string{ "create", "alter", @@ -1321,61 +1368,32 @@ func TestExecutorDDL(t *testing.T) { "drop", "truncate", } - wantCount := []int64{0, 0, 0} + for _, stmt := range stmts { - _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount := []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[2]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) - } - testQueryLog(t, logChan, "TestExecute", "DDL", stmt, 1) + for _, tc := range tcs { + sbc1.ExecCount.Set(0) + sbc2.ExecCount.Set(0) + sbclookup.ExecCount.Set(0) + + _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + if tc.hasNoKeyspaceErr { + assert.Error(t, err, errNoKeyspace) + } else { + assert.NoError(t, err) + } - _, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount = []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[0]++ - wantCount[1]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) - } - testQueryLog(t, logChan, "TestExecute", "DDL", stmt, 8) + diff := cmp.Diff(tc.wantCnts, cnts{ + Sbc1Cnt: sbc1.ExecCount.Get(), + Sbc2Cnt: sbc2.ExecCount.Get(), + SbcLookupCnt: sbclookup.ExecCount.Get(), + }) + if diff != "" { + t.Errorf("stmt: %s\ntc: %+v\n-want,+got:\n%s", stmt, tc, diff) + } - _, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/-20"}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount = []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[0]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) + testQueryLog(t, logChan, "TestExecute", "DDL", stmt, tc.shardQueryCnt) } - testQueryLog(t, logChan, "TestExecute", "DDL", stmt, 1) - } - - _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{}), "create", nil) - want := errNoKeyspace.Error() - if err == nil || err.Error() != want { - t.Errorf("ddl with no keyspace: %v, want %v", err, want) } - testQueryLog(t, logChan, "TestExecute", "DDL", "create", 0) } func waitForVindex(t *testing.T, ks, name string, watch chan *vschemapb.SrvVSchema, executor *Executor) (*vschemapb.SrvVSchema, *vschemapb.Vindex) { From 073d3333380ac35b994dca3294bf0f5e02b89142 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Thu, 19 Mar 2020 18:30:54 +0100 Subject: [PATCH 330/825] Test case for int filter Signed-off-by: Rohit Nayak --- .../tabletserver/vstreamer/planbuilder.go | 19 +++---- .../vstreamer/planbuilder_test.go | 18 +++---- .../tabletserver/vstreamer/vstreamer_test.go | 53 +++++++++++++++++++ 3 files changed, 68 insertions(+), 22 deletions(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index c6474d1b8ec..918c2d421b5 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -44,13 +44,15 @@ type Plan struct { // Filters is the list of filters to be applied to the columns // of the table. Filters []Filter - } +// Opcode enumerates the operators supported in a where clause type Opcode int const ( + // Equal is used to filter an integer column on a specific value Equal = Opcode(iota) + // VindexMatch is used for an in_keyrange() construct VindexMatch ) @@ -382,9 +384,9 @@ func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) er return err } plan.Filters = append(plan.Filters, Filter{ - Opcode: Equal, - ColNum: colnum, - Value: resolved, + Opcode: Equal, + ColNum: colnum, + Value: resolved, }) case *sqlparser.FuncExpr: if !expr.Name.EqualString("in_keyrange") { @@ -421,15 +423,6 @@ func splitAndExpression(filters []sqlparser.Expr, node sqlparser.Expr) []sqlpars return append(filters, node) } -// skipParenthesis skips the parenthesis (if any) of an expression and -// returns the innermost unparenthesized expression. -func skipParenthesis(node sqlparser.Expr) sqlparser.Expr { - if node, ok := node.(*sqlparser.ParenExpr); ok { - return skipParenthesis(node.Expr) - } - return node -} - func (plan *Plan) analyzeExprs(vschema *localVSchema, selExprs sqlparser.SelectExprs) error { if _, ok := selExprs[0].(*sqlparser.StarExpr); !ok { for _, expr := range selExprs { diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go index e9ac90e98fa..fa0a44ec1b0 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder_test.go @@ -245,7 +245,7 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("val"), Type: sqltypes.VarBinary, }}, - Filters: []Filter {{ + Filters: []Filter{{ Opcode: VindexMatch, ColNum: 0, Value: sqltypes.NULL, @@ -309,7 +309,7 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("id"), Type: sqltypes.Int64, }}, - Filters: []Filter {{ + Filters: []Filter{{ Opcode: VindexMatch, ColNum: 0, Value: sqltypes.NULL, @@ -331,7 +331,7 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("id"), Type: sqltypes.Int64, }}, - Filters: []Filter {{ + Filters: []Filter{{ Opcode: VindexMatch, ColNum: 0, Value: sqltypes.NULL, @@ -340,7 +340,7 @@ func TestPlanbuilder(t *testing.T) { KeyRange: nil, }}, }, - },{ + }, { inTable: t1, inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select val, id from t1 where id = 1"}, outPlan: &Plan{ @@ -353,7 +353,7 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("id"), Type: sqltypes.Int64, }}, - Filters: []Filter {{ + Filters: []Filter{{ Opcode: Equal, ColNum: 0, Value: sqltypes.NewInt64(1), @@ -378,12 +378,12 @@ func TestPlanbuilder(t *testing.T) { Alias: sqlparser.NewColIdent("id"), Type: sqltypes.Int64, }}, - Filters: []Filter {{ + Filters: []Filter{{ Opcode: VindexMatch, ColNum: 0, Value: sqltypes.NULL, Vindex: nil, - VindexColumns: []int{0,1}, + VindexColumns: []int{0, 1}, KeyRange: nil, }}, }, @@ -450,7 +450,7 @@ func TestPlanbuilder(t *testing.T) { inTable: t1, inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select *, id from t1"}, outErr: `unsupported: *, id`, - },{ + }, { inTable: t1, inRule: &binlogdatapb.Rule{Match: "t1", Filter: "select id, val from t1 where max(id)"}, outErr: `unsupported constraint: max(id)`, @@ -515,7 +515,7 @@ func TestPlanbuilder(t *testing.T) { }) if plan != nil { plan.Table = nil - for ind, _ := range plan.Filters { + for ind := range plan.Filters { plan.Filters[ind].KeyRange = nil if plan.Filters[ind]. Opcode == VindexMatch { diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 5b9edf50860..572c07da983 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -37,6 +37,59 @@ type testcase struct { output [][]string } +func TestFilteredInt(t *testing.T) { + if testing.Short() { + t.Skip() + } + + execStatements(t, []string{ + "create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))", + }) + defer execStatements(t, []string{ + "drop table t1", + }) + engine.se.Reload(context.Background()) + + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "t1", + Filter: "select id1, val from t1 where id2 = 200", + }}, + } + + testcases := []testcase{{ + input: []string{ + "begin", + "insert into t1 values (1, 100, 'aaa')", + "insert into t1 values (2, 200, 'bbb')", + "insert into t1 values (3, 100, 'ccc')", + "insert into t1 values (4, 200, 'ddd')", + "insert into t1 values (5, 200, 'eee')", + "update t1 set val = 'newddd' where id1 = 4", + "update t1 set id2 = 200 where id1 = 1", + "update t1 set id2 = 100 where id1 = 2", + "update t1 set id2 = 100 where id1 = 1", + "update t1 set id2 = 200 where id1 = 2", + "commit", + }, + output: [][]string{{ + `begin`, + `type:FIELD field_event: fields: > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: after: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `gtid`, + `commit`, + }}, + }} + runCases(t, filter, testcases, "") +} + func TestStatements(t *testing.T) { if testing.Short() { t.Skip() From e69b85c53f7902992808251a193747a8b744e994 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 18 Mar 2020 22:24:04 -0700 Subject: [PATCH 331/825] deprecation: fix other e2e tests Signed-off-by: Sugu Sougoumarane --- .../encrypted_transport_test.go | 6 +-- go/test/endtoend/messaging/message_test.go | 3 +- .../endtoend/mysqlserver/mysql_server_test.go | 2 +- .../endtoend/preparestmt/stmt_methods_test.go | 2 +- .../multi-output/deletesharded-output.txt | 38 +++++++----------- .../testdata/multi-output/options-output.txt | 4 +- .../multi-output/unsharded-output.txt | 8 ++-- .../multi-output/updatesharded-output.txt | 39 ++++++++----------- .../twopc-output/deletesharded-output.txt | 9 ++--- go/vt/vtexplain/vtexplain_flaky_test.go | 2 +- .../tabletserver/planbuilder/builder.go | 10 ++++- .../planbuilder/testdata/exec_cases.txt | 36 +++++++++++++---- 12 files changed, 83 insertions(+), 76 deletions(-) diff --git a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go index 3bca70c58eb..9e38feb1689 100644 --- a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go +++ b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go @@ -194,7 +194,7 @@ func TestSecureTransport(t *testing.T) { err = vterrors.FromVTRPC(qr.Error) require.Error(t, err) assert.Contains(t, err.Error(), "table acl error") - assert.Contains(t, err.Error(), "cannot run PASS_SELECT on table") + assert.Contains(t, err.Error(), "cannot run Select on table") // now restart vtgate in the mode where we don't use SSL // for client connections, but we copy effective caller id @@ -218,7 +218,7 @@ func TestSecureTransport(t *testing.T) { err = vterrors.FromVTRPC(qr.Error) require.Error(t, err) assert.Contains(t, err.Error(), "table acl error") - assert.Contains(t, err.Error(), "cannot run PASS_SELECT on table") + assert.Contains(t, err.Error(), "cannot run Select on table") // 'vtgate client 1' is authorized to access vt_insert_test callerID := &vtrpc.CallerID{ @@ -238,7 +238,7 @@ func TestSecureTransport(t *testing.T) { err = vterrors.FromVTRPC(qr.Error) require.Error(t, err) assert.Contains(t, err.Error(), "table acl error") - assert.Contains(t, err.Error(), "cannot run PASS_SELECT on table") + assert.Contains(t, err.Error(), "cannot run Select on table") clusterInstance.Teardown() } diff --git a/go/test/endtoend/messaging/message_test.go b/go/test/endtoend/messaging/message_test.go index 7bcdf056a19..667b77709a2 100644 --- a/go/test/endtoend/messaging/message_test.go +++ b/go/test/endtoend/messaging/message_test.go @@ -387,7 +387,8 @@ func TestMessageTopic(t *testing.T) { // this should fail because the topic doesn't exist. Any other outcome fails the test _, err = conn.ExecuteFetch("insert into test_topic(id, message) values(4, 'msg4'), (5, 'msg5'), (6, 'msg6')", 1, false) - want := "table test_topic not found in schema" + // 1146: table doesn't exist. + want := "errno 1146" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("non-topic insert err: %v, must contain %v", err, want) } diff --git a/go/test/endtoend/mysqlserver/mysql_server_test.go b/go/test/endtoend/mysqlserver/mysql_server_test.go index 656e0b509f5..192f6912fb1 100644 --- a/go/test/endtoend/mysqlserver/mysql_server_test.go +++ b/go/test/endtoend/mysqlserver/mysql_server_test.go @@ -194,7 +194,7 @@ func TestSelectWithUnauthorizedUser(t *testing.T) { _, err = conn.ExecuteFetch("SELECT * from vt_insert_test limit 1", 1, false) require.NotNilf(t, err, "error expected, got nil") assert.Contains(t, err.Error(), "table acl error") - assert.Contains(t, err.Error(), "cannot run PASS_SELECT on table") + assert.Contains(t, err.Error(), "cannot run Select on table") } func connectDB(t *testing.T, vtParams mysql.ConnParams, params ...string) *sql.DB { diff --git a/go/test/endtoend/preparestmt/stmt_methods_test.go b/go/test/endtoend/preparestmt/stmt_methods_test.go index d631b904068..7d4b84e9922 100644 --- a/go/test/endtoend/preparestmt/stmt_methods_test.go +++ b/go/test/endtoend/preparestmt/stmt_methods_test.go @@ -180,5 +180,5 @@ func TestWrongTableName(t *testing.T) { defer cluster.PanicHandler(t) dbo := Connect(t) defer dbo.Close() - execWithError(t, dbo, []uint16{1105}, "select * from teseting_table;") + execWithError(t, dbo, []uint16{1146}, "select * from teseting_table;") } diff --git a/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt b/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt index 554453ec54a..ed6ca3710c8 100644 --- a/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/deletesharded-output.txt @@ -2,15 +2,14 @@ delete from music_extra where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: delete from music_extra where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: delete from music_extra where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 1 ks_sharded/-40: commit ---------------------------------------------------------------------- delete from music_extra where id=1 and extra='abc' 1 ks_sharded/-40: begin -1 ks_sharded/-40: select id from music_extra where id = 1 and extra = 'abc' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ -1 ks_sharded/-40: delete from music_extra where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: delete from music_extra where id = 1 and extra = 'abc' limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 1 ks_sharded/-40: commit ---------------------------------------------------------------------- @@ -19,8 +18,8 @@ delete from user where id=1 1 ks_sharded/-40: begin 1 ks_sharded/-40: select id, name from user where id = 1 limit 10001 for update 2 ks_sharded/40-80: begin -2 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ -3 ks_sharded/-40: delete from user where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +2 ks_sharded/40-80: delete from name_user_map where name = 'name_val_2' and user_id = 1 limit 10001 /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ +3 ks_sharded/-40: delete from user where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 4 ks_sharded/-40: commit 5 ks_sharded/40-80: commit @@ -32,9 +31,8 @@ delete from user where name='billy' 2 ks_sharded/-40: begin 2 ks_sharded/-40: select id, name from user where name = 'billy' limit 10001 for update 3 ks_sharded/40-80: begin -3 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ -4 ks_sharded/-40: select id from user where name = 'billy' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ -4 ks_sharded/-40: delete from user where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +3 ks_sharded/40-80: delete from name_user_map where name = 'name_val_2' and user_id = 1 limit 10001 /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ +4 ks_sharded/-40: delete from user where name = 'billy' limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 5 ks_sharded/c0-: commit 6 ks_sharded/-40: commit 7 ks_sharded/40-80: commit @@ -43,40 +41,32 @@ delete from user where name='billy' delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra='abc' 1 ks_sharded/-40: begin -1 ks_sharded/-40: select id from music_extra where extra = 'abc' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/-40: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/-40: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/-40: commit 1 ks_sharded/40-80: begin -1 ks_sharded/40-80: select id from music_extra where extra = 'abc' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/40-80: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/40-80: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/40-80: commit 1 ks_sharded/80-c0: begin -1 ks_sharded/80-c0: select id from music_extra where extra = 'abc' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/80-c0: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/80-c0: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/80-c0: commit 1 ks_sharded/c0-: begin -1 ks_sharded/c0-: select id from music_extra where extra = 'abc' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/c0-: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/c0-: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/c0-: commit ---------------------------------------------------------------------- delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from `ks_sharded[-]`.music_extra where extra='abc' LIMIT 10 1 ks_sharded/-40: begin -1 ks_sharded/-40: select id from music_extra where extra = 'abc' limit 10 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/-40: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/-40: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/-40: commit 1 ks_sharded/40-80: begin -1 ks_sharded/40-80: select id from music_extra where extra = 'abc' limit 10 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/40-80: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/40-80: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/40-80: commit 1 ks_sharded/80-c0: begin -1 ks_sharded/80-c0: select id from music_extra where extra = 'abc' limit 10 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/80-c0: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/80-c0: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/80-c0: commit 1 ks_sharded/c0-: begin -1 ks_sharded/c0-: select id from music_extra where extra = 'abc' limit 10 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/c0-: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where id in (1)/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/c0-: delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from music_extra where extra = 'abc' limit 10/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/c0-: commit ---------------------------------------------------------------------- diff --git a/go/vt/vtexplain/testdata/multi-output/options-output.txt b/go/vt/vtexplain/testdata/multi-output/options-output.txt index 5e763a2e749..fa6f2610656 100644 --- a/go/vt/vtexplain/testdata/multi-output/options-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/options-output.txt @@ -17,9 +17,9 @@ select * from user where id in (1,2,3,4,5,6,7,8) insert into user (id, name) values (2, 'bob') 1 ks_sharded/c0-: begin -1 ks_sharded/c0-: insert into name_user_map(name, user_id) values ('bob', 2) /* _stream name_user_map (name user_id ) ('Ym9i' 2 ); */ /* vtgate:: keyspace_id:da8a82595aa28154c17717955ffeed8b */ +1 ks_sharded/c0-: insert into name_user_map(name, user_id) values ('bob', 2) /* vtgate:: keyspace_id:da8a82595aa28154c17717955ffeed8b */ 2 ks_sharded/-40: begin -2 ks_sharded/-40: insert into user(id, name) values (2, 'bob') /* _stream user (id ) (2 ); */ /* vtgate:: keyspace_id:06e7ea22ce92708f */ +2 ks_sharded/-40: insert into user(id, name) values (2, 'bob') /* vtgate:: keyspace_id:06e7ea22ce92708f */ 3 ks_sharded/c0-: commit 4 ks_sharded/-40: commit diff --git a/go/vt/vtexplain/testdata/multi-output/unsharded-output.txt b/go/vt/vtexplain/testdata/multi-output/unsharded-output.txt index b3b3560a58e..98bdad579b9 100644 --- a/go/vt/vtexplain/testdata/multi-output/unsharded-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/unsharded-output.txt @@ -14,23 +14,21 @@ insert into t1 (id,intval,floatval) values (1,2,3.14) update t1 set intval = 10 1 ks_unsharded/-: begin -1 ks_unsharded/-: select id from t1 limit 10001 for update -1 ks_unsharded/-: update t1 set intval = 10 where id in (1) +1 ks_unsharded/-: update t1 set intval = 10 limit 10001 1 ks_unsharded/-: commit ---------------------------------------------------------------------- update t1 set floatval = 9.99 1 ks_unsharded/-: begin -1 ks_unsharded/-: select id from t1 limit 10001 for update -1 ks_unsharded/-: update t1 set floatval = 9.99 where id in (1) +1 ks_unsharded/-: update t1 set floatval = 9.99 limit 10001 1 ks_unsharded/-: commit ---------------------------------------------------------------------- delete from t1 where id = 100 1 ks_unsharded/-: begin -1 ks_unsharded/-: delete from t1 where id in (100) +1 ks_unsharded/-: delete from t1 where id = 100 limit 10001 1 ks_unsharded/-: commit ---------------------------------------------------------------------- diff --git a/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt b/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt index 7f143656dbd..95b03cc4327 100644 --- a/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt +++ b/go/vt/vtexplain/testdata/multi-output/updatesharded-output.txt @@ -2,7 +2,7 @@ update user set nickname='alice' where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: update user set nickname = 'alice' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: update user set nickname = 'alice' where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 1 ks_sharded/-40: commit ---------------------------------------------------------------------- @@ -11,8 +11,7 @@ update user set nickname='alice' where name='alice' 1 ks_sharded/40-80: begin 1 ks_sharded/40-80: select user_id from name_user_map where name = 'alice' limit 10001 2 ks_sharded/-40: begin -2 ks_sharded/-40: select id from user where name = 'alice' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ -2 ks_sharded/-40: update user set nickname = 'alice' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +2 ks_sharded/-40: update user set nickname = 'alice' where name = 'alice' limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 3 ks_sharded/40-80: commit 4 ks_sharded/-40: commit @@ -20,7 +19,7 @@ update user set nickname='alice' where name='alice' update user set pet='fido' where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: update user set pet = 'fido' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: update user set pet = 'fido' where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 1 ks_sharded/-40: commit ---------------------------------------------------------------------- @@ -29,10 +28,10 @@ update user set name='alicia' where id=1 1 ks_sharded/-40: begin 1 ks_sharded/-40: select id, name from user where id = 1 limit 10001 for update 2 ks_sharded/40-80: begin -2 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ +2 ks_sharded/40-80: delete from name_user_map where name = 'name_val_2' and user_id = 1 limit 10001 /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ 3 ks_sharded/c0-: begin 3 ks_sharded/c0-: insert into name_user_map(name, user_id) values ('alicia', 1) /* vtgate:: keyspace_id:e2821261367fbee90bb5cf72955146c6 */ -4 ks_sharded/-40: update user set name = 'alicia' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +4 ks_sharded/-40: update user set name = 'alicia' where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 5 ks_sharded/-40: commit 6 ks_sharded/40-80: commit 7 ks_sharded/c0-: commit @@ -44,11 +43,10 @@ update user set name='alicia' where name='alice' 1 ks_sharded/40-80: select user_id from name_user_map where name = 'alice' limit 10001 2 ks_sharded/-40: begin 2 ks_sharded/-40: select id, name from user where name = 'alice' limit 10001 for update -3 ks_sharded/40-80: delete from name_user_map where (name = 'name_val_2' and user_id = 1) /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ +3 ks_sharded/40-80: delete from name_user_map where name = 'name_val_2' and user_id = 1 limit 10001 /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ 4 ks_sharded/c0-: begin 4 ks_sharded/c0-: insert into name_user_map(name, user_id) values ('alicia', 1) /* vtgate:: keyspace_id:e2821261367fbee90bb5cf72955146c6 */ -5 ks_sharded/-40: select id from user where name = 'alice' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ -5 ks_sharded/-40: update user set name = 'alicia' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +5 ks_sharded/-40: update user set name = 'alicia' where name = 'alice' limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 6 ks_sharded/40-80: commit 7 ks_sharded/-40: commit 8 ks_sharded/c0-: commit @@ -57,20 +55,16 @@ update user set name='alicia' where name='alice' update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname=1 where nickname != '' 1 ks_sharded/-40: begin -1 ks_sharded/-40: select name from name_info where nickname != '' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/-40: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where name in ('name_val_1')/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/-40: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where nickname != '' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/-40: commit 1 ks_sharded/40-80: begin -1 ks_sharded/40-80: select name from name_info where nickname != '' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/40-80: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where name in ('name_val_1')/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/40-80: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where nickname != '' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/40-80: commit 1 ks_sharded/80-c0: begin -1 ks_sharded/80-c0: select name from name_info where nickname != '' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/80-c0: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where name in ('name_val_1')/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/80-c0: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where nickname != '' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/80-c0: commit 1 ks_sharded/c0-: begin -1 ks_sharded/c0-: select name from name_info where nickname != '' limit 10001 for update/* vtgate:: filtered_replication_unfriendly */ -1 ks_sharded/c0-: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where name in ('name_val_1')/* vtgate:: filtered_replication_unfriendly */ +1 ks_sharded/c0-: update /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ name_info set has_nickname = 1 where nickname != '' limit 10001/* vtgate:: filtered_replication_unfriendly */ 1 ks_sharded/c0-: commit ---------------------------------------------------------------------- @@ -79,8 +73,7 @@ update user set pet='rover' where name='alice' 1 ks_sharded/40-80: begin 1 ks_sharded/40-80: select user_id from name_user_map where name = 'alice' limit 10001 2 ks_sharded/-40: begin -2 ks_sharded/-40: select id from user where name = 'alice' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ -2 ks_sharded/-40: update user set pet = 'rover' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +2 ks_sharded/-40: update user set pet = 'rover' where name = 'alice' limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 3 ks_sharded/40-80: commit 4 ks_sharded/-40: commit @@ -92,12 +85,12 @@ begin update user set nickname='alice' where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: update user set nickname = 'alice' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: update user set nickname = 'alice' where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ ---------------------------------------------------------------------- update user set nickname='bob' where id=1 -2 ks_sharded/-40: update user set nickname = 'bob' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +2 ks_sharded/-40: update user set nickname = 'bob' where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ ---------------------------------------------------------------------- commit @@ -112,13 +105,13 @@ begin update user set nickname='alice' where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: update user set nickname = 'alice' where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: update user set nickname = 'alice' where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ ---------------------------------------------------------------------- update user set nickname='bob' where id=3 2 ks_sharded/40-80: begin -2 ks_sharded/40-80: update user set nickname = 'bob' where id in (3) /* vtgate:: keyspace_id:4eb190c9a2fa169c */ +2 ks_sharded/40-80: update user set nickname = 'bob' where id = 3 limit 10001 /* vtgate:: keyspace_id:4eb190c9a2fa169c */ ---------------------------------------------------------------------- commit diff --git a/go/vt/vtexplain/testdata/twopc-output/deletesharded-output.txt b/go/vt/vtexplain/testdata/twopc-output/deletesharded-output.txt index 9270d59f3ac..f811b5dfd18 100644 --- a/go/vt/vtexplain/testdata/twopc-output/deletesharded-output.txt +++ b/go/vt/vtexplain/testdata/twopc-output/deletesharded-output.txt @@ -2,15 +2,14 @@ delete from music_extra where id=1 1 ks_sharded/-40: begin -1 ks_sharded/-40: delete from music_extra where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: delete from music_extra where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 2 ks_sharded/-40: commit ---------------------------------------------------------------------- delete from music_extra where id=1 and extra='abc' 1 ks_sharded/-40: begin -1 ks_sharded/-40: select id from music_extra where id = 1 and extra = 'abc' limit 10001 for update /* vtgate:: keyspace_id:166b40b44aba4bd6 */ -1 ks_sharded/-40: delete from music_extra where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +1 ks_sharded/-40: delete from music_extra where id = 1 and extra = 'abc' limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 2 ks_sharded/-40: commit ---------------------------------------------------------------------- @@ -19,8 +18,8 @@ delete from user where id=1 1 ks_sharded/-40: begin 1 ks_sharded/-40: select name from user where id = 1 limit 10001 for update 2 ks_sharded/80-c0: begin -2 ks_sharded/80-c0: delete from name_user_map where (name = 'name_val_1' and user_id = 1) /* vtgate:: keyspace_id:a6e89b54b129c33051b76db219595660 */ -3 ks_sharded/-40: delete from user where id in (1) /* vtgate:: keyspace_id:166b40b44aba4bd6 */ +2 ks_sharded/80-c0: delete from name_user_map where name = 'name_val_2' and user_id = 1 limit 10001 /* vtgate:: keyspace_id:73004f940e97faf0a1b54ec5586a090e */ +3 ks_sharded/-40: delete from user where id = 1 limit 10001 /* vtgate:: keyspace_id:166b40b44aba4bd6 */ 4 ks_sharded/-40: begin 4 ks_sharded/-40: insert into `_vt`.dt_state(dtid, state, time_created) values ('ks_sharded:-40:1515392388787015722', 1, 1515392388898391433) 4 ks_sharded/-40: insert into `_vt`.dt_participant(dtid, id, keyspace, shard) values ('ks_sharded:-40:1515392388787015722', 1, 'ks_sharded', '80-c0') diff --git a/go/vt/vtexplain/vtexplain_flaky_test.go b/go/vt/vtexplain/vtexplain_flaky_test.go index ef35da06890..8cd492c52d6 100644 --- a/go/vt/vtexplain/vtexplain_flaky_test.go +++ b/go/vt/vtexplain/vtexplain_flaky_test.go @@ -153,7 +153,7 @@ func TestErrors(t *testing.T) { { SQL: "SELECT * FROM table_not_in_schema", - Err: "target: ks_unsharded.-.master, used tablet: explainCell-0 (ks_unsharded/-): table table_not_in_schema not found in schema", + Err: "target: ks_unsharded.-.master, used tablet: explainCell-0 (ks_unsharded/-): unknown error: unable to resolve table name table_not_in_schema", }, } diff --git a/go/vt/vttablet/tabletserver/planbuilder/builder.go b/go/vt/vttablet/tabletserver/planbuilder/builder.go index 74fb0e4e42d..d2c5f9479a6 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/builder.go +++ b/go/vt/vttablet/tabletserver/planbuilder/builder.go @@ -73,7 +73,10 @@ func analyzeUpdate(upd *sqlparser.Update, tables map[string]*schema.Table) (plan plan.WhereClause = buf.ParsedQuery() } - if PassthroughDMLs || upd.Limit != nil { + // If plan.Table==nil, it's likely a multi-table statement. + // MySQL doesn't allow limit clauses for multi-table dmls. + // If there's an explicity Limit, honor it. + if PassthroughDMLs || plan.Table == nil || upd.Limit != nil { plan.FullQuery = GenerateFullQuery(upd) return plan, nil } @@ -98,7 +101,10 @@ func analyzeDelete(del *sqlparser.Delete, tables map[string]*schema.Table) (plan plan.WhereClause = buf.ParsedQuery() } - if PassthroughDMLs || del.Limit != nil { + // If plan.Table==nil, it's likely a multi-table statement. + // MySQL doesn't allow limit clauses for multi-table dmls. + // If there's an explicity Limit, honor it. + if PassthroughDMLs || plan.Table == nil || del.Limit != nil { plan.FullQuery = GenerateFullQuery(del) return plan, nil } diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index 32a47b833f9..045715451a9 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -492,7 +492,7 @@ options:PassthroughDMLs # cross-db update "update a.b set foo='foo' where name in ('a', 'b')" { - "PlanID": "UpdateLimit", + "PlanID": "Update", "TableName": "", "Permissions": [ { @@ -500,14 +500,14 @@ options:PassthroughDMLs "Role": 1 } ], - "FullQuery": "update a.b set foo = 'foo' where name in ('a', 'b') limit :#maxLimit", + "FullQuery": "update a.b set foo = 'foo' where name in ('a', 'b')", "WhereClause": "where name in ('a', 'b')" } # update unknown table "update bogus set name='foo' where id=1" { - "PlanID": "UpdateLimit", + "PlanID": "Update", "TableName": "", "Permissions": [ { @@ -515,7 +515,7 @@ options:PassthroughDMLs "Role": 1 } ], - "FullQuery": "update bogus set name = 'foo' where id = 1 limit :#maxLimit", + "FullQuery": "update bogus set name = 'foo' where id = 1", "WhereClause": "where id = 1" } @@ -538,7 +538,7 @@ options:PassthroughDMLs # multi-table update "update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test'" { - "PlanID": "UpdateLimit", + "PlanID": "Update", "TableName": "", "Permissions": [ { @@ -550,7 +550,7 @@ options:PassthroughDMLs "Role": 1 } ], - "FullQuery": "update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test' limit :#maxLimit", + "FullQuery": "update a, b set a.name = 'foo' where a.id = b.id and b.var = 'test'", "WhereClause": "where a.id = b.id and b.var = 'test'" } @@ -652,7 +652,7 @@ options:PassthroughDMLs # delete unknown table "delete from bogus" { - "PlanID": "DeleteLimit", + "PlanID": "Delete", "TableName": "", "Permissions": [ { @@ -660,7 +660,7 @@ options:PassthroughDMLs "Role": 1 } ], - "FullQuery": "delete from bogus limit :#maxLimit" + "FullQuery": "delete from bogus" } # delete unknown table @@ -678,6 +678,26 @@ options:PassthroughDMLs "FullQuery": "delete from bogus" } +# multi-table delete +"delete a, b from a, b where id = 1" +{ + "PlanID": "Delete", + "TableName": "", + "Permissions": [ + { + "TableName": "a", + "Role": 1 + }, + { + "TableName": "b", + "Role": 1 + } + ], + "FullQuery": "delete a, b from a, b where id = 1", + "WhereClause": "where id = 1" +} + + # delete with limit "delete from a limit 10" { From c141cc0fe57247c3a7254fc3c377483881865205 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Thu, 19 Mar 2020 23:09:20 +0100 Subject: [PATCH 332/825] Added rowstreamer test for filtered vreplication Signed-off-by: Rohit Nayak --- .../vstreamer/rowstreamer_test.go | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index ab294562dde..63fb44a3168 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -210,6 +210,36 @@ func TestStreamRowsKeyRange(t *testing.T) { checkStream(t, "select * from t1 where in_keyrange('-80')", nil, wantQuery, wantStream) } +func TestStreamRowsFilterInt(t *testing.T) { + if testing.Short() { + t.Skip() + } + + if err := env.SetVSchema(shardedVSchema); err != nil { + t.Fatal(err) + } + defer env.SetVSchema("{}") + + execStatements(t, []string{ + "create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))", + "insert into t1 values (1, 100, 'aaa'), (2, 200, 'bbb'), (3, 200, 'ccc'), (4, 100, 'ddd'), (5, 200, 'eee')", + }) + defer execStatements(t, []string{ + "drop table t1", + }) + engine.se.Reload(context.Background()) + + time.Sleep(1 * time.Second) + + // Only the first row should be returned, but lastpk should be 6. + wantStream := []string{ + `fields: fields: pkfields: `, + `rows: rows: lastpk: `, + } + wantQuery := "select id1, id2, val from t1 order by id1" + checkStream(t, "select id1, val from t1 where id2 = 100", nil, wantQuery, wantStream) +} + func TestStreamRowsMultiPacket(t *testing.T) { if testing.Short() { t.Skip() From ebd6524e8b6f949cb81165da7b66694fece1b2e5 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Thu, 19 Mar 2020 22:14:02 -0700 Subject: [PATCH 333/825] deprecation: rollback on limit failure Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/query_executor.go | 54 +++++++----- .../tabletserver/query_executor_test.go | 87 +++++++++++++++++++ go/vt/vttablet/tabletserver/tx_pool.go | 45 +++++++--- 3 files changed, 151 insertions(+), 35 deletions(-) diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 4a1818509f7..8dc5deece83 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -134,45 +134,51 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { case planbuilder.PlanSet, planbuilder.PlanOtherRead, planbuilder.PlanOtherAdmin: return qre.execOther() case planbuilder.PlanInsert, planbuilder.PlanUpdate, planbuilder.PlanDelete, planbuilder.PlanInsertMessage, planbuilder.PlanDDL: - return qre.execAsTransaction(true /* autocommit */, qre.txConnExec) + return qre.execAutocommit(qre.txConnExec) case planbuilder.PlanUpdateLimit, planbuilder.PlanDeleteLimit: - return qre.execAsTransaction(false /* autocommit */, qre.txConnExec) + return qre.execAsTransaction(qre.txConnExec) } return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s unexpected plan type", qre.plan.PlanID.String()) } -func (qre *QueryExecutor) execAsTransaction(autocommit bool, f func(conn *TxConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { - if autocommit { - if qre.options == nil { - qre.options = &querypb.ExecuteOptions{} - } - qre.options.TransactionIsolation = querypb.ExecuteOptions_AUTOCOMMIT +func (qre *QueryExecutor) execAutocommit(f func(conn *TxConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { + if qre.options == nil { + qre.options = &querypb.ExecuteOptions{} } - conn, beginSQL, err := qre.tsv.te.txPool.LocalBegin(qre.ctx, qre.options) + qre.options.TransactionIsolation = querypb.ExecuteOptions_AUTOCOMMIT + conn, _, err := qre.tsv.te.txPool.LocalBegin(qre.ctx, qre.options) if err != nil { return nil, err } defer qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) - if beginSQL != "" { - qre.logStats.AddRewrittenSQL(beginSQL, time.Now()) - } - reply, err = f(conn) + return f(conn) +} - start := time.Now() +func (qre *QueryExecutor) execAsTransaction(f func(conn *TxConnection) (*sqltypes.Result, error)) (reply *sqltypes.Result, err error) { + conn, beginSQL, err := qre.tsv.te.txPool.LocalBegin(qre.ctx, qre.options) if err != nil { - qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) - qre.logStats.AddRewrittenSQL("rollback", start) return nil, err } - commitSQL, err := qre.tsv.te.txPool.LocalCommit(qre.ctx, conn) + defer qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) + qre.logStats.AddRewrittenSQL(beginSQL, time.Now()) - // As above LocalCommit is a no-op for autocommmit so don't log anything. - if commitSQL != "" { - qre.logStats.AddRewrittenSQL(commitSQL, start) + reply, err = f(conn) + if err != nil { + // dbConn is nil, it means the transaction was aborted. + // If so, we should not relog the rollback. + // TODO(sougou): these txPool functions should take the logstats + // and log any statements they issue. This needs to be done as + // a separate refactor because it impacts lot of code. + if conn.dbConn != nil { + defer qre.logStats.AddRewrittenSQL("rollback", time.Now()) + qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) + } + return nil, err } - if err != nil { + defer qre.logStats.AddRewrittenSQL("commit", time.Now()) + if _, err := qre.tsv.te.txPool.LocalCommit(qre.ctx, conn); err != nil { return nil, err } return reply, nil @@ -228,7 +234,7 @@ func (qre *QueryExecutor) Stream(callback func(*sqltypes.Result) error) error { return err } defer txConn.Recycle() - conn = txConn.DBConn + conn = txConn.dbConn } else { dbConn, err := qre.getStreamConn() if err != nil { @@ -385,7 +391,7 @@ func (qre *QueryExecutor) execNextval() (*sqltypes.Result, error) { t.SequenceInfo.Lock() defer t.SequenceInfo.Unlock() if t.SequenceInfo.NextVal == 0 || t.SequenceInfo.NextVal+inc > t.SequenceInfo.LastVal { - _, err := qre.execAsTransaction(false /* autocommit */, func(conn *TxConnection) (*sqltypes.Result, error) { + _, err := qre.execAsTransaction(func(conn *TxConnection) (*sqltypes.Result, error) { query := fmt.Sprintf("select next_id, cache from %s where id = 0 for update", sqlparser.String(tableName)) qr, err := qre.execSQL(conn, query, false) if err != nil { @@ -473,6 +479,8 @@ func (qre *QueryExecutor) execDMLLimit(conn *TxConnection) (*sqltypes.Result, er return nil, err } if err := qre.verifyRowCount(int64(result.RowsAffected), maxrows); err != nil { + defer qre.logStats.AddRewrittenSQL("rollback", time.Now()) + qre.tsv.te.txPool.LocalConclude(qre.ctx, conn) return nil, err } return result, nil diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index aa2e38ceffc..9fce5aa53f3 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -307,6 +307,89 @@ func TestQueryExecutorSelectImpossible(t *testing.T) { } } +func TestQueryExecutorDMLLimitFailure(t *testing.T) { + type dbResponse struct { + query string + result *sqltypes.Result + } + + dmlResult := &sqltypes.Result{ + RowsAffected: 3, + } + + // The queries are run both in and outside a transaction. + testcases := []struct { + input string + passThrough bool + dbResponses []dbResponse + planWant string + logWant string + inTxWant string + }{{ + input: "update test_table set a=1", + dbResponses: []dbResponse{{ + query: "update test_table set a = 1 limit 3", + result: dmlResult, + }}, + logWant: "begin; update test_table set a = 1 limit 3; rollback", + inTxWant: "update test_table set a = 1 limit 3; rollback", + }, { + input: "delete from test_table", + dbResponses: []dbResponse{{ + query: "delete from test_table limit 3", + result: dmlResult, + }}, + logWant: "begin; delete from test_table limit 3; rollback", + inTxWant: "delete from test_table limit 3; rollback", + }} + for _, tcase := range testcases { + func() { + db := setUpQueryExecutorTest(t) + defer db.Close() + for _, dbr := range tcase.dbResponses { + db.AddQuery(dbr.query, dbr.result) + } + ctx := callerid.NewContext(context.Background(), callerid.NewEffectiveCallerID("a", "b", "c"), callerid.NewImmediateCallerID("d")) + tsv := newTestTabletServer(ctx, smallResultSize, db) + defer tsv.StopService() + + tsv.SetPassthroughDMLs(tcase.passThrough) + + // Test outside a transaction. + qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) + _, err := qre.Execute() + wantErr := "caller id: d: row count exceeded 2 (errno 10001) (sqlstate HY000)" + if err == nil || err.Error() != wantErr { + t.Errorf("Execute(%v): %v, want %v", tcase.input, err, wantErr) + } + assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) + + // Test inside a transaction. + txid, err := tsv.Begin(ctx, &tsv.target, nil) + require.NoError(t, err) + defer tsv.Commit(ctx, &tsv.target, txid) + + qre = newTestQueryExecutor(ctx, tsv, tcase.input, txid) + _, err = qre.Execute() + if err == nil || err.Error() != wantErr { + t.Errorf("Execute(%v): %v, want %v", tcase.input, err, wantErr) + } + want := tcase.logWant + if tcase.inTxWant != "" { + want = tcase.inTxWant + } + assert.Equal(t, want, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) + + qre = newTestQueryExecutor(ctx, tsv, "update test_table set a=1", txid) + _, err = qre.Execute() + notxError := "ended at" + if err == nil || !strings.Contains(err.Error(), notxError) { + t.Errorf("Execute(%v): %v, must contain %v", tcase.input, err, notxError) + } + }() + } +} + func TestQueryExecutorPlanInsertMessage(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() @@ -938,6 +1021,7 @@ const ( smallTxPool noTwopc shortTwopcAge + smallResultSize ) // newTestQueryExecutor uses a package level variable testTabletServer defined in tabletserver_test.go @@ -967,6 +1051,9 @@ func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb } else { config.TwoPCAbandonAge = 10 } + if flags&smallResultSize > 0 { + config.MaxResultSize = 2 + } tsv := NewTabletServer(config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) testUtils := newTestUtils() dbconfigs := testUtils.newDBConfigs(db) diff --git a/go/vt/vttablet/tabletserver/tx_pool.go b/go/vt/vttablet/tabletserver/tx_pool.go index 4d597e0108c..f5312e76b35 100644 --- a/go/vt/vttablet/tabletserver/tx_pool.go +++ b/go/vt/vttablet/tabletserver/tx_pool.go @@ -357,14 +357,19 @@ func (axp *TxPool) LocalCommit(ctx context.Context, conn *TxConnection) (string, // LocalConclude concludes a transaction started by LocalBegin. // If the transaction was not previously concluded, it's rolled back. func (axp *TxPool) LocalConclude(ctx context.Context, conn *TxConnection) { + if conn.dbConn == nil { + return + } span, ctx := trace.NewSpan(ctx, "TxPool.LocalConclude") defer span.Finish() - if conn.DBConn != nil { - _ = axp.localRollback(ctx, conn) - } + _ = axp.localRollback(ctx, conn) } func (axp *TxPool) localRollback(ctx context.Context, conn *TxConnection) error { + if conn.Autocommit { + conn.conclude(TxCommit, "returned to pool") + return nil + } defer conn.conclude(TxRollback, "transaction rolled back") if _, err := conn.Exec(ctx, "rollback", 1, false); err != nil { conn.Close() @@ -413,7 +418,7 @@ func (axp *TxPool) SetPoolTimeout(timeout time.Duration) { // the tx pool correctly. It also does not retry statements if there // are failures. type TxConnection struct { - *connpool.DBConn + dbConn *connpool.DBConn TransactionID int64 pool *TxPool StartTime time.Time @@ -428,7 +433,7 @@ type TxConnection struct { func newTxConnection(conn *connpool.DBConn, transactionID int64, pool *TxPool, immediate *querypb.VTGateCallerID, effective *vtrpcpb.CallerID, autocommit bool) *TxConnection { return &TxConnection{ - DBConn: conn, + dbConn: conn, TransactionID: transactionID, pool: pool, StartTime: time.Now(), @@ -438,9 +443,19 @@ func newTxConnection(conn *connpool.DBConn, transactionID int64, pool *TxPool, i } } +// Close closes the connection. +func (txc *TxConnection) Close() { + if txc.dbConn != nil { + txc.dbConn.Close() + } +} + // Exec executes the statement for the current transaction. func (txc *TxConnection) Exec(ctx context.Context, query string, maxrows int, wantfields bool) (*sqltypes.Result, error) { - r, err := txc.DBConn.ExecOnce(ctx, query, maxrows, wantfields) + if txc.dbConn == nil { + return nil, vterrors.Errorf(vtrpcpb.Code_ABORTED, "transaction was aborted: %v", txc.Conclusion) + } + r, err := txc.dbConn.ExecOnce(ctx, query, maxrows, wantfields) if err != nil { if mysql.IsConnErr(err) { select { @@ -458,13 +473,13 @@ func (txc *TxConnection) Exec(ctx context.Context, query string, maxrows int, wa // BeginAgain commits the existing transaction and begins a new one func (txc *TxConnection) BeginAgain(ctx context.Context) error { - if txc.Autocommit { + if txc.dbConn == nil || txc.Autocommit { return nil } - if _, err := txc.DBConn.Exec(ctx, "commit", 1, false); err != nil { + if _, err := txc.dbConn.Exec(ctx, "commit", 1, false); err != nil { return err } - if _, err := txc.DBConn.Exec(ctx, "begin", 1, false); err != nil { + if _, err := txc.dbConn.Exec(ctx, "begin", 1, false); err != nil { return err } return nil @@ -473,7 +488,10 @@ func (txc *TxConnection) BeginAgain(ctx context.Context) error { // Recycle returns the connection to the pool. The transaction remains // active. func (txc *TxConnection) Recycle() { - if txc.IsClosed() { + if txc.dbConn == nil { + return + } + if txc.dbConn.IsClosed() { txc.conclude(TxClose, "closed") } else { txc.pool.activePool.Put(txc.TransactionID) @@ -486,9 +504,12 @@ func (txc *TxConnection) RecordQuery(query string) { } func (txc *TxConnection) conclude(conclusion, reason string) { + if txc.dbConn == nil { + return + } txc.pool.activePool.Unregister(txc.TransactionID, reason) - txc.DBConn.Recycle() - txc.DBConn = nil + txc.dbConn.Recycle() + txc.dbConn = nil txc.pool.limiter.Release(txc.ImmediateCallerID, txc.EffectiveCallerID) txc.log(conclusion) } From ac924c3e90fb6e7637f9ab1893033e839ce0570a Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Wed, 18 Mar 2020 13:38:56 -0700 Subject: [PATCH 334/825] Support SHOW statements properly Signed-off-by: Saif Alharthi --- go/vt/sqlparser/ast.go | 15 +- go/vt/sqlparser/parse_test.go | 27 +- go/vt/sqlparser/sql.go | 4121 ++++++++++++++++----------------- go/vt/sqlparser/sql.y | 10 +- go/vt/vtgate/executor.go | 11 + go/vt/vtgate/executor_test.go | 42 + jira_show.txt | 0 7 files changed, 2141 insertions(+), 2085 deletions(-) create mode 100644 jira_show.txt diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 4966222bd82..60de165ef1d 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -1170,10 +1170,11 @@ func (f *ForeignKeyDefinition) Format(buf *TrackedBuffer) { // Format formats the node. func (node *Show) Format(buf *TrackedBuffer) { - if (node.Type == "tables" || node.Type == "columns" || node.Type == "fields") && node.ShowTablesOpt != nil { + nodeType := strings.ToLower(node.Type) + if (nodeType == "tables" || nodeType == "columns" || nodeType == "fields" || nodeType == "index" || nodeType == "keys") && node.ShowTablesOpt != nil { opt := node.ShowTablesOpt - buf.Myprintf("show %s%s", opt.Full, node.Type) - if (node.Type == "columns" || node.Type == "fields") && node.HasOnTable() { + buf.Myprintf("show %s%s", opt.Full, nodeType) + if (nodeType == "columns" || nodeType == "fields" || nodeType == "index" || nodeType == "keys") && node.HasOnTable() { buf.Myprintf(" from %v", node.OnTable) } if opt.DbName != "" { @@ -1183,17 +1184,17 @@ func (node *Show) Format(buf *TrackedBuffer) { return } if node.Scope == "" { - buf.Myprintf("show %s", node.Type) + buf.Myprintf("show %s", nodeType) } else { - buf.Myprintf("show %s %s", node.Scope, node.Type) + buf.Myprintf("show %s %s", node.Scope, nodeType) } if node.HasOnTable() { buf.Myprintf(" on %v", node.OnTable) } - if node.Type == "collation" && node.ShowCollationFilterOpt != nil { + if nodeType == "collation" && node.ShowCollationFilterOpt != nil { buf.Myprintf(" where %v", *node.ShowCollationFilterOpt) } - if node.Type == "charset" && node.ShowTablesOpt != nil { + if nodeType == "charset" && node.ShowTablesOpt != nil { buf.Myprintf("%v", node.ShowTablesOpt.Filter) } if node.HasTable() { diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index acd55be20fc..96aee9f4dda 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1224,14 +1224,12 @@ var ( input: "show grants for 'root@localhost'", output: "show grants", }, { - input: "show index from table", - output: "show index", + input: "show index from t", }, { input: "show indexes from table", output: "show indexes", }, { - input: "show keys from table", - output: "show keys", + input: "show keys from t", }, { input: "show master status", output: "show master", @@ -1541,6 +1539,27 @@ var ( input: "select status() from t", // should not escape function names that are keywords }, { input: "select * from `weird table name`", + }, { + input: "SHOW FULL TABLES FROM `jiradb` LIKE 'AO_E8B6CC_ISSUE_MAPPING'", + output: "show full tables from jiradb like 'AO_E8B6CC_ISSUE_MAPPING'", + }, { + input: "SHOW FULL COLUMNS FROM AO_E8B6CC_ISSUE_MAPPING FROM jiradb LIKE '%'", + output: "show full columns from AO_E8B6CC_ISSUE_MAPPING from jiradb like '%'", + }, { + input: "SHOW KEYS FROM `AO_E8B6CC_ISSUE_MAPPING` FROM `jiradb`", + output: "show keys from AO_E8B6CC_ISSUE_MAPPING from jiradb", + }, { + input: "SHOW CREATE TABLE `jiradb`.`AO_E8B6CC_ISSUE_MAPPING`", + output: "show create table jiradb.AO_E8B6CC_ISSUE_MAPPING", + }, { + input: "SHOW INDEX FROM `AO_E8B6CC_ISSUE_MAPPING` FROM `jiradb`", + output: "show index from AO_E8B6CC_ISSUE_MAPPING from jiradb", + }, { + input: "SHOW FULL TABLES FROM `jiradb` LIKE '%'", + output: "show full tables from jiradb like '%'", + }, { + input: "SHOW INDEX FROM `AO_E8B6CC_PROJECT_MAPPING` FROM `jiradb`", + output: "show index from AO_E8B6CC_PROJECT_MAPPING from jiradb", }} ) diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index 40c3a374383..ea715ff187e 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -853,394 +853,373 @@ var yyExca = [...]int{ 54, 47, 56, 47, -2, 51, - -1, 857, + -1, 859, 115, 654, -2, 650, - -1, 1087, + -1, 1091, 5, 33, -2, 446, - -1, 1118, + -1, 1122, 5, 32, -2, 587, - -1, 1364, + -1, 1370, 5, 33, -2, 588, - -1, 1417, + -1, 1423, 5, 32, -2, 590, - -1, 1497, + -1, 1503, 5, 33, -2, 591, } const yyPrivate = 57344 -const yyLast = 16474 +const yyLast = 16267 var yyAct = [...]int{ - 325, 1521, 1326, 1531, 1215, 1121, 657, 1384, 1397, 1485, - 972, 319, 1430, 968, 1139, 343, 1266, 945, 304, 330, - 1122, 656, 3, 1300, 1263, 1267, 943, 356, 57, 1001, - 1015, 332, 80, 566, 1145, 1273, 268, 971, 289, 268, - 1166, 981, 1279, 555, 804, 400, 1238, 893, 820, 889, - 882, 1078, 1183, 985, 1192, 947, 932, 911, 859, 295, - 718, 704, 699, 588, 594, 698, 394, 268, 80, 717, - 524, 525, 268, 303, 268, 389, 600, 328, 925, 313, - 609, 386, 391, 707, 1011, 671, 56, 61, 1524, 1508, - 544, 1519, 995, 1495, 1034, 1516, 1327, 266, 1507, 1494, - 1255, 397, 1356, 672, 296, 297, 298, 299, 1033, 529, - 302, 317, 1294, 63, 64, 65, 66, 67, 82, 83, - 84, 1295, 1296, 264, 260, 261, 262, 256, 388, 719, - 254, 720, 258, 526, 962, 528, 582, 82, 83, 84, - 1154, 301, 300, 1153, 963, 964, 1155, 1174, 1032, 1459, - 622, 621, 631, 632, 624, 625, 626, 627, 628, 629, - 630, 623, 994, 1387, 633, 368, 1347, 374, 375, 372, - 373, 371, 370, 369, 1217, 82, 83, 84, 1002, 577, - 916, 376, 377, 578, 575, 576, 561, 1345, 563, 294, - 793, 1239, 570, 571, 580, 581, 792, 1219, 1029, 1026, - 1027, 790, 1025, 1518, 1515, 1486, 1214, 926, 986, 1478, - 1539, 1431, 1140, 1142, 279, 1535, 545, 1211, 892, 560, - 562, 531, 258, 1213, 1220, 257, 1433, 1218, 797, 1241, - 794, 791, 781, 1439, 1036, 1039, 988, 1289, 1288, 82, - 83, 84, 263, 1287, 1096, 527, 255, 534, 271, 259, - 1046, 645, 646, 1045, 1467, 268, 536, 537, 1093, 1367, - 268, 1224, 546, 1243, 1150, 1247, 268, 1242, 1106, 1240, - 1072, 1031, 268, 553, 1245, 831, 559, 80, 541, 80, - 80, 713, 80, 1244, 80, 613, 551, 969, 633, 958, - 80, 1141, 272, 1030, 1432, 623, 1246, 1248, 633, 275, - 834, 835, 828, 70, 558, 825, 608, 283, 278, 1002, - 821, 1476, 1493, 82, 83, 84, 535, 591, 595, 1460, - 80, 543, 1212, 1448, 1210, 1533, 1202, 550, 1534, 1277, - 1532, 597, 1035, 552, 614, 987, 596, 1440, 1438, 721, - 281, 71, 1257, 538, 988, 539, 288, 1037, 540, 645, - 646, 607, 606, 547, 548, 549, 1198, 1199, 1200, 643, - 584, 585, 912, 645, 646, 866, 1313, 557, 608, 658, - 912, 991, 1103, 273, 82, 83, 84, 992, 669, 864, - 865, 863, 783, 1172, 268, 268, 268, 626, 627, 628, - 629, 630, 623, 80, 822, 633, 1481, 603, 598, 80, - 285, 276, 1499, 286, 287, 292, 1393, 1392, 830, 277, - 280, 697, 274, 291, 290, 1187, 702, 624, 625, 626, - 627, 628, 629, 630, 623, 1201, 397, 633, 1186, 1175, - 1206, 1203, 1194, 1204, 1197, 606, 1193, 607, 606, 1195, - 1196, 1540, 1091, 987, 1090, 696, 829, 705, 556, 530, - 587, 608, 1445, 1205, 608, 674, 676, 678, 680, 682, - 684, 685, 587, 607, 606, 607, 606, 706, 1069, 1070, - 1071, 711, 1501, 675, 677, 715, 681, 683, 1477, 686, - 608, 1411, 608, 1541, 586, 622, 621, 631, 632, 624, - 625, 626, 627, 628, 629, 630, 623, 1390, 1184, 633, - 253, 622, 621, 631, 632, 624, 625, 626, 627, 628, - 629, 630, 623, 607, 606, 633, 82, 83, 84, 1055, - 1259, 809, 1092, 22, 268, 1444, 532, 533, 779, 80, - 608, 782, 54, 784, 268, 268, 80, 80, 80, 1079, - 1436, 1517, 268, 1264, 862, 268, 1276, 988, 268, 802, - 803, 523, 268, 1309, 80, 82, 83, 84, 58, 80, - 80, 80, 268, 80, 80, 383, 384, 1503, 587, 810, - 989, 80, 80, 1436, 1489, 607, 606, 82, 83, 84, - 849, 851, 852, 308, 1276, 729, 850, 1436, 587, 1436, - 1468, 823, 608, 1436, 1435, 785, 786, 806, 808, 1085, - 80, 1382, 1381, 795, 895, 268, 388, 1369, 587, 801, - 54, 80, 82, 83, 84, 836, 884, 1362, 846, 847, - 82, 83, 84, 814, 1157, 1447, 798, 346, 345, 348, - 349, 350, 351, 929, 883, 1146, 347, 352, 1227, 564, - 1366, 587, 1146, 885, 860, 1317, 987, 856, 861, 1319, - 1318, 984, 982, 24, 983, 80, 1315, 1316, 855, 857, - 980, 986, 1315, 1314, 1085, 587, 845, 929, 587, 895, - 587, 658, 728, 727, 900, 901, 709, 1116, 838, 929, - 928, 897, 1117, 1085, 902, 905, 1276, 709, 80, 80, - 913, 853, 952, 24, 708, 1158, 268, 24, 961, 1109, - 1108, 1085, 54, 1509, 268, 268, 929, 708, 268, 268, - 714, 832, 268, 268, 268, 80, 796, 310, 1399, 710, - 1216, 712, 1416, 886, 887, 996, 1374, 1016, 80, 525, - 710, 1400, 708, 967, 953, 702, 1305, 1161, 955, 702, - 921, 922, 54, 702, 909, 1012, 54, 1007, 397, 934, - 937, 938, 939, 935, 1006, 936, 940, 927, 1019, 806, - 1526, 973, 1280, 1281, 844, 1522, 54, 1307, 1283, 1264, - 954, 1188, 1003, 1004, 1005, 826, 800, 1135, 837, 938, - 939, 959, 268, 80, 951, 80, 960, 1038, 956, 1286, - 1285, 268, 268, 268, 268, 268, 1130, 268, 268, 976, - 1129, 268, 80, 1513, 934, 937, 938, 939, 935, 1017, - 936, 940, 1133, 1131, 1280, 1281, 1506, 1134, 1132, 1223, - 268, 1057, 268, 268, 314, 315, 1511, 268, 1067, 1066, - 997, 998, 999, 1000, 1179, 726, 601, 894, 896, 589, - 554, 1058, 1059, 1020, 595, 1171, 1008, 1009, 1010, 602, - 1483, 590, 1040, 1041, 1042, 1043, 1044, 1482, 1047, 1048, - 1013, 1014, 1049, 1414, 1052, 856, 631, 632, 624, 625, - 626, 627, 628, 629, 630, 623, 1060, 857, 633, 898, - 899, 1051, 1169, 904, 907, 908, 601, 1163, 1056, 1360, - 1395, 860, 1022, 799, 942, 861, 305, 1061, 1065, 602, - 1062, 1453, 599, 311, 312, 306, 1064, 1086, 920, 58, - 1452, 923, 924, 1402, 1146, 579, 1528, 1527, 567, 568, - 1097, 569, 1074, 572, 1104, 1094, 819, 604, 1528, 583, - 268, 268, 268, 268, 268, 1464, 1388, 827, 60, 62, - 55, 1, 268, 1520, 1328, 268, 1396, 1118, 1123, 268, - 1028, 1484, 1429, 268, 1299, 979, 970, 69, 522, 68, - 1475, 702, 702, 702, 702, 702, 897, 326, 1102, 978, - 1156, 977, 80, 1437, 1386, 990, 702, 1173, 993, 1147, - 1306, 1162, 1159, 1170, 702, 1167, 1167, 1480, 1148, 734, - 1149, 732, 733, 1125, 1126, 1124, 1128, 1136, 1127, 81, - 731, 736, 735, 269, 1144, 973, 269, 730, 282, 392, - 941, 722, 1018, 605, 72, 1178, 1209, 1180, 1181, 1182, - 80, 80, 1151, 1208, 1024, 824, 1168, 1176, 1177, 573, - 574, 284, 641, 1063, 269, 81, 1152, 1164, 1165, 269, - 398, 269, 1271, 833, 593, 1451, 1401, 1101, 668, 910, - 80, 331, 848, 344, 341, 342, 839, 1115, 615, 329, - 1185, 321, 1068, 701, 268, 694, 1081, 933, 931, 930, - 1082, 387, 1282, 80, 1278, 700, 1226, 1355, 1087, 1088, - 1089, 1207, 1458, 843, 1191, 1095, 26, 59, 1098, 1099, - 316, 19, 883, 18, 1105, 17, 20, 16, 1107, 15, - 1222, 1110, 1111, 1112, 1113, 1114, 1229, 14, 542, 1083, - 1084, 30, 21, 13, 12, 1258, 11, 10, 9, 8, - 80, 80, 1265, 1230, 1138, 1225, 7, 1231, 1100, 1256, - 6, 5, 4, 1237, 307, 23, 1123, 2, 0, 1250, - 1260, 1270, 1249, 0, 80, 1268, 0, 0, 0, 0, - 0, 1060, 857, 0, 0, 0, 1275, 0, 1292, 80, - 0, 80, 80, 0, 0, 1167, 1167, 0, 780, 0, - 1284, 1298, 0, 0, 1291, 787, 788, 789, 0, 1290, - 1312, 0, 1293, 0, 0, 0, 0, 0, 0, 268, - 1310, 1311, 973, 807, 973, 0, 1297, 0, 811, 812, - 813, 0, 815, 816, 1302, 1303, 1304, 0, 0, 268, - 817, 818, 0, 0, 0, 80, 0, 1329, 80, 80, - 80, 268, 269, 0, 0, 80, 0, 269, 268, 0, - 0, 0, 0, 269, 0, 0, 0, 0, 0, 269, - 0, 0, 0, 0, 81, 0, 81, 81, 0, 81, - 1320, 81, 1321, 0, 1336, 0, 0, 81, 1229, 702, - 0, 1335, 0, 0, 1235, 1236, 1334, 1322, 0, 1324, - 1323, 1357, 0, 0, 1343, 0, 0, 0, 0, 0, - 0, 658, 1333, 0, 0, 0, 0, 81, 0, 1372, - 1361, 0, 1373, 0, 0, 1375, 1123, 1371, 80, 0, - 0, 0, 0, 0, 1370, 0, 80, 0, 1159, 0, - 1380, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 80, 0, 0, 0, 0, 0, 0, 80, 0, - 0, 973, 1340, 1341, 0, 1342, 0, 0, 1344, 0, - 1346, 0, 1404, 0, 1389, 0, 1391, 0, 0, 0, - 0, 269, 269, 269, 0, 0, 0, 0, 0, 0, - 81, 1398, 0, 0, 0, 0, 81, 80, 80, 0, - 80, 1403, 0, 0, 1410, 80, 0, 80, 80, 80, - 268, 0, 1423, 80, 1424, 1426, 1427, 1417, 1415, 1422, - 1268, 0, 0, 0, 1383, 0, 0, 1428, 0, 0, - 80, 268, 0, 1442, 1441, 1443, 1434, 1449, 0, 0, - 0, 1337, 0, 0, 0, 0, 0, 0, 0, 1339, - 0, 0, 1021, 0, 1023, 0, 0, 0, 0, 0, - 1348, 1349, 1474, 1465, 0, 0, 0, 80, 0, 0, - 1466, 1050, 1473, 1472, 1268, 0, 0, 0, 80, 80, - 1363, 1364, 1365, 0, 1368, 0, 1487, 0, 0, 1488, - 1491, 0, 1450, 0, 1490, 658, 0, 0, 80, 0, - 1496, 1379, 0, 0, 0, 0, 0, 0, 0, 268, - 0, 1398, 973, 0, 1123, 0, 0, 80, 0, 0, - 0, 269, 0, 0, 1505, 0, 81, 0, 357, 51, - 0, 269, 269, 81, 81, 81, 0, 0, 1512, 269, - 80, 0, 269, 1510, 0, 269, 0, 1514, 0, 269, - 0, 81, 1525, 0, 0, 0, 81, 81, 81, 269, - 81, 81, 1536, 0, 0, 0, 0, 0, 81, 81, - 1500, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 51, 0, 0, 0, 0, 0, 0, 1425, 309, 0, - 0, 0, 0, 0, 0, 0, 0, 81, 0, 1359, - 0, 0, 269, 0, 0, 0, 0, 0, 81, 0, - 0, 0, 0, 0, 1358, 0, 1454, 1455, 1456, 1457, - 0, 1461, 0, 1462, 1463, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1469, 0, 1470, 1471, 622, - 621, 631, 632, 624, 625, 626, 627, 628, 629, 630, - 623, 0, 81, 633, 622, 621, 631, 632, 624, 625, - 626, 627, 628, 629, 630, 623, 0, 0, 633, 1492, - 0, 0, 0, 0, 0, 0, 0, 1497, 0, 0, - 0, 0, 0, 0, 0, 81, 81, 0, 0, 0, - 1190, 0, 0, 269, 0, 1502, 0, 0, 0, 0, - 0, 269, 269, 0, 0, 269, 269, 0, 0, 269, - 269, 269, 81, 0, 0, 355, 0, 0, 0, 1221, - 0, 0, 0, 0, 0, 81, 621, 631, 632, 624, - 625, 626, 627, 628, 629, 630, 623, 0, 0, 633, - 1537, 1538, 617, 0, 620, 0, 0, 79, 1353, 0, - 634, 635, 636, 637, 638, 639, 640, 0, 618, 619, - 616, 622, 621, 631, 632, 624, 625, 626, 627, 628, - 629, 630, 623, 0, 0, 633, 0, 0, 0, 269, - 81, 1352, 81, 399, 0, 0, 0, 0, 269, 269, - 269, 269, 269, 0, 269, 269, 0, 0, 269, 81, - 0, 0, 0, 0, 0, 565, 0, 565, 565, 0, - 565, 0, 565, 0, 0, 0, 0, 269, 565, 269, - 269, 1232, 0, 0, 269, 622, 621, 631, 632, 624, - 625, 626, 627, 628, 629, 630, 623, 0, 51, 633, - 0, 622, 621, 631, 632, 624, 625, 626, 627, 628, - 629, 630, 623, 642, 0, 633, 644, 0, 622, 621, + 325, 1537, 1125, 1527, 1219, 1332, 330, 974, 1491, 1390, + 1403, 1272, 1436, 356, 1143, 657, 343, 566, 947, 1306, + 1003, 997, 1273, 1269, 57, 304, 656, 3, 1170, 945, + 1126, 1017, 80, 555, 983, 970, 268, 973, 289, 268, + 1279, 1285, 1244, 332, 295, 884, 1082, 804, 895, 1187, + 1196, 822, 891, 564, 718, 1149, 400, 934, 987, 949, + 861, 699, 588, 594, 1013, 394, 389, 268, 80, 698, + 524, 525, 268, 913, 268, 927, 328, 313, 303, 717, + 600, 609, 386, 391, 707, 397, 671, 56, 1036, 296, + 297, 298, 299, 672, 704, 302, 61, 1530, 1514, 544, + 1525, 368, 1035, 374, 375, 372, 373, 371, 370, 369, + 1501, 1522, 317, 1333, 1513, 1500, 1261, 376, 377, 1362, + 529, 1300, 63, 64, 65, 66, 67, 1301, 1302, 964, + 266, 82, 83, 84, 301, 82, 83, 84, 965, 966, + 1245, 582, 1034, 1465, 622, 621, 631, 632, 624, 625, + 626, 627, 628, 629, 630, 623, 300, 719, 633, 720, + 1178, 388, 264, 260, 261, 262, 526, 1158, 528, 256, + 1157, 577, 254, 1159, 258, 578, 575, 576, 1247, 996, + 1221, 1393, 82, 83, 84, 1004, 1353, 1351, 580, 294, + 918, 1223, 1031, 1028, 1029, 1206, 1027, 790, 793, 1524, + 581, 570, 571, 561, 792, 563, 1521, 1492, 1218, 928, + 1484, 988, 1249, 1545, 1253, 1437, 1248, 545, 1246, 1445, + 531, 258, 1224, 1251, 797, 1202, 1203, 1204, 1038, 1041, + 1439, 990, 1250, 1222, 781, 1215, 560, 562, 794, 791, + 1541, 1217, 1295, 1294, 1293, 1252, 1254, 527, 1144, 1146, + 1473, 534, 894, 271, 259, 268, 536, 537, 1048, 1373, + 268, 1047, 546, 1230, 541, 1033, 268, 257, 1100, 1154, + 1097, 1110, 268, 553, 645, 646, 559, 80, 1076, 833, + 990, 263, 80, 713, 80, 613, 551, 1032, 255, 971, + 80, 82, 83, 84, 1205, 633, 960, 830, 1438, 1210, + 1207, 1198, 1208, 1201, 608, 1197, 1482, 827, 1199, 1200, + 82, 83, 84, 1466, 623, 557, 1004, 633, 823, 70, + 80, 558, 1209, 1446, 1444, 1454, 1037, 1145, 1499, 538, + 989, 539, 596, 1283, 540, 569, 597, 572, 990, 721, + 1216, 1039, 1214, 583, 1319, 584, 585, 1263, 914, 535, + 1539, 783, 1176, 1540, 543, 1538, 1095, 71, 1094, 1096, + 550, 1487, 547, 548, 549, 603, 552, 868, 82, 83, + 84, 643, 1505, 645, 646, 645, 646, 607, 606, 989, + 1399, 866, 867, 865, 268, 268, 268, 626, 627, 628, + 629, 630, 623, 80, 608, 633, 556, 836, 837, 80, + 587, 1398, 824, 82, 83, 84, 914, 598, 1107, 253, + 397, 1546, 607, 606, 697, 606, 622, 621, 631, 632, + 624, 625, 626, 627, 628, 629, 630, 623, 702, 608, + 633, 608, 54, 607, 606, 993, 1507, 989, 523, 1191, + 1265, 994, 986, 984, 864, 985, 1190, 1179, 607, 606, + 608, 982, 988, 1547, 607, 606, 674, 676, 678, 680, + 682, 684, 685, 675, 677, 608, 681, 683, 706, 686, + 1083, 608, 711, 1282, 383, 384, 715, 1483, 696, 617, + 705, 620, 1073, 1074, 1075, 1417, 1396, 634, 635, 636, + 637, 638, 639, 640, 586, 618, 619, 616, 622, 621, 631, 632, 624, 625, 626, 627, 628, 629, 630, 623, - 0, 0, 633, 0, 1351, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 655, 0, 659, 660, 661, 662, - 663, 664, 665, 666, 667, 1350, 670, 673, 673, 673, - 679, 673, 673, 679, 673, 687, 688, 689, 690, 691, - 692, 693, 0, 703, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 269, 269, 269, - 269, 269, 0, 0, 0, 0, 0, 0, 0, 269, - 0, 0, 269, 0, 0, 0, 269, 0, 0, 0, - 269, 622, 621, 631, 632, 624, 625, 626, 627, 628, - 629, 630, 623, 0, 0, 633, 0, 0, 0, 81, - 0, 0, 622, 621, 631, 632, 624, 625, 626, 627, - 628, 629, 630, 623, 0, 0, 633, 0, 0, 0, - 1394, 0, 399, 0, 399, 399, 0, 399, 0, 399, - 1080, 0, 0, 0, 0, 399, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 81, 81, 0, + 1188, 1059, 633, 621, 631, 632, 624, 625, 626, 627, + 628, 629, 630, 623, 268, 530, 633, 832, 779, 80, + 809, 782, 1451, 784, 268, 268, 80, 80, 80, 82, + 83, 84, 268, 886, 22, 268, 1442, 1523, 268, 802, + 803, 1450, 268, 1315, 80, 82, 83, 84, 991, 80, + 80, 80, 268, 80, 80, 831, 1509, 587, 268, 268, + 1233, 80, 80, 624, 625, 626, 627, 628, 629, 630, + 623, 897, 780, 633, 607, 606, 1442, 1495, 808, 787, + 788, 789, 851, 853, 854, 82, 83, 84, 852, 1161, + 80, 608, 532, 533, 308, 268, 1368, 807, 806, 1442, + 587, 80, 811, 812, 813, 1089, 815, 816, 729, 1442, + 1474, 1453, 838, 1270, 819, 820, 1282, 798, 785, 786, + 862, 858, 1442, 1441, 885, 931, 795, 1388, 1387, 388, + 1375, 587, 801, 887, 1323, 631, 632, 624, 625, 626, + 627, 628, 629, 630, 623, 80, 814, 633, 1372, 587, + 863, 857, 817, 818, 346, 345, 348, 349, 350, 351, + 859, 904, 907, 347, 352, 709, 840, 915, 1325, 1324, + 1321, 1322, 1321, 1320, 1089, 587, 899, 24, 80, 80, + 931, 587, 855, 897, 587, 709, 268, 728, 727, 847, + 930, 24, 319, 24, 268, 268, 1162, 963, 268, 268, + 1150, 1120, 268, 268, 268, 80, 1121, 58, 710, 954, + 712, 708, 888, 889, 1150, 1113, 931, 1112, 80, 525, + 1422, 1089, 397, 708, 714, 834, 54, 955, 710, 796, + 708, 957, 54, 923, 924, 975, 1515, 702, 911, 1405, + 54, 702, 54, 1220, 931, 702, 998, 1406, 1089, 999, + 1000, 1001, 1002, 1005, 1006, 1007, 1380, 1021, 1282, 1018, + 806, 310, 1311, 1286, 1287, 1010, 1011, 1012, 1165, 958, + 961, 953, 268, 80, 1014, 80, 1009, 1040, 839, 1008, + 929, 268, 268, 268, 268, 268, 962, 268, 268, 978, + 1532, 268, 80, 956, 1528, 936, 939, 940, 941, 937, + 1019, 938, 942, 1313, 1292, 1286, 1287, 1289, 587, 1270, + 54, 1192, 268, 828, 268, 268, 800, 1137, 1135, 268, + 846, 1291, 1138, 1136, 1134, 1133, 1023, 1519, 1025, 1061, + 1015, 1016, 1139, 1512, 940, 941, 1229, 896, 898, 314, + 315, 858, 1517, 1071, 1070, 1052, 1056, 622, 621, 631, + 632, 624, 625, 626, 627, 628, 629, 630, 623, 1053, + 1054, 633, 1183, 726, 554, 1175, 1022, 1489, 1488, 862, + 601, 1064, 1420, 1173, 601, 1042, 1043, 1044, 1045, 1046, + 859, 1049, 1050, 602, 1167, 1051, 599, 602, 1065, 1366, + 589, 1066, 1401, 1024, 936, 939, 940, 941, 937, 863, + 938, 942, 590, 900, 901, 799, 1055, 906, 909, 910, + 944, 311, 312, 1060, 305, 1459, 1078, 306, 58, 1458, + 1069, 1408, 268, 268, 268, 268, 268, 1127, 1068, 1150, + 579, 1101, 922, 1098, 268, 925, 926, 268, 1534, 1533, + 62, 268, 821, 604, 1122, 268, 568, 567, 1534, 1470, + 326, 1394, 829, 60, 55, 1, 1526, 1334, 1402, 1030, + 1490, 1435, 1160, 899, 80, 702, 702, 702, 702, 702, + 1305, 1163, 981, 1166, 1151, 972, 1106, 1171, 1171, 69, + 702, 975, 81, 522, 1129, 1130, 269, 1132, 702, 269, + 1140, 1128, 68, 1481, 1131, 980, 979, 1443, 591, 595, + 1148, 1152, 1392, 1153, 992, 1177, 1172, 995, 1155, 1312, + 1180, 1181, 80, 80, 1174, 614, 1486, 269, 81, 734, + 732, 733, 269, 731, 269, 736, 735, 730, 282, 1182, + 392, 1184, 1185, 1186, 1168, 1169, 943, 722, 1020, 605, + 72, 1213, 80, 1212, 1026, 826, 573, 574, 284, 1189, + 658, 641, 1067, 1156, 398, 1277, 835, 593, 268, 669, + 1457, 1195, 1407, 1105, 668, 912, 1194, 80, 1085, 1211, + 331, 850, 1086, 344, 341, 342, 841, 1226, 1227, 1119, + 1091, 1092, 1093, 615, 1235, 329, 885, 1099, 1072, 321, + 1102, 1103, 701, 694, 935, 1225, 1109, 1228, 933, 932, + 1111, 387, 1288, 1114, 1115, 1116, 1117, 1118, 1284, 700, + 1232, 1361, 1464, 1271, 80, 80, 1237, 1127, 1266, 1262, + 1236, 845, 26, 59, 316, 1274, 1142, 19, 1243, 1256, + 18, 1255, 17, 20, 16, 1087, 1088, 15, 80, 14, + 1276, 542, 30, 21, 13, 12, 11, 10, 1064, 9, + 8, 7, 1231, 80, 1104, 80, 80, 859, 6, 1171, + 1171, 5, 1304, 1290, 1297, 4, 307, 23, 2, 0, + 975, 1281, 975, 0, 1318, 0, 0, 0, 1296, 0, + 0, 0, 0, 268, 0, 0, 0, 1309, 1310, 0, + 1303, 1308, 0, 0, 0, 0, 0, 1299, 323, 0, + 0, 0, 0, 268, 0, 269, 1316, 1317, 0, 80, + 269, 1335, 80, 80, 80, 268, 269, 0, 0, 0, + 0, 80, 269, 0, 268, 0, 0, 81, 0, 0, + 0, 1327, 81, 0, 81, 0, 0, 0, 1235, 0, + 81, 0, 0, 0, 0, 0, 1328, 0, 1330, 0, + 810, 0, 1340, 1342, 0, 0, 0, 1346, 1347, 0, + 1348, 0, 0, 1350, 0, 1352, 0, 702, 1241, 1242, + 81, 1349, 825, 0, 0, 0, 0, 1326, 1341, 1127, + 0, 0, 0, 0, 0, 1367, 0, 0, 0, 0, + 0, 0, 0, 0, 80, 0, 1376, 1329, 0, 848, + 849, 1163, 80, 1377, 0, 0, 0, 0, 0, 1339, + 0, 975, 0, 1386, 0, 0, 0, 80, 0, 1389, + 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, + 0, 1395, 0, 1397, 269, 269, 269, 0, 1410, 0, + 0, 1404, 0, 81, 0, 0, 0, 0, 0, 81, + 0, 0, 658, 0, 0, 902, 903, 0, 1409, 0, + 0, 0, 0, 80, 80, 0, 80, 0, 0, 0, + 1400, 80, 1274, 80, 80, 80, 268, 1416, 0, 80, + 1429, 1421, 1430, 1432, 1433, 0, 0, 0, 1423, 0, + 0, 0, 1428, 0, 1434, 0, 80, 268, 1440, 0, + 0, 1447, 0, 0, 0, 1455, 0, 0, 0, 0, + 0, 0, 0, 0, 969, 0, 0, 1343, 0, 0, + 0, 1448, 0, 1449, 1471, 1345, 1274, 0, 1480, 0, + 0, 0, 0, 80, 0, 0, 1354, 1355, 1479, 1478, + 0, 1472, 0, 0, 80, 80, 0, 0, 0, 0, + 0, 0, 1494, 0, 1493, 0, 1369, 1370, 1371, 0, + 1374, 1404, 975, 1502, 80, 1497, 0, 1127, 0, 0, + 0, 0, 0, 0, 269, 268, 0, 1385, 0, 81, + 0, 0, 0, 80, 269, 269, 81, 81, 81, 0, + 1511, 1456, 269, 0, 0, 269, 0, 0, 269, 0, + 0, 0, 269, 0, 81, 0, 80, 1518, 1516, 81, + 81, 81, 269, 81, 81, 1520, 0, 0, 269, 269, + 1531, 81, 81, 0, 1062, 1063, 1542, 595, 0, 0, + 1365, 0, 647, 648, 649, 650, 651, 652, 653, 654, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 81, 0, 0, 0, 0, 269, 0, 0, 0, 0, + 0, 81, 0, 1431, 0, 0, 0, 357, 51, 1506, 622, 621, 631, 632, 624, 625, 626, 627, 628, 629, - 630, 623, 0, 0, 633, 611, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 565, 0, 0, - 0, 269, 0, 0, 565, 565, 565, 0, 0, 0, - 81, 0, 0, 0, 0, 0, 323, 0, 0, 0, - 0, 0, 565, 0, 0, 0, 0, 565, 565, 565, - 0, 565, 565, 0, 0, 0, 0, 0, 0, 565, - 565, 0, 0, 0, 0, 0, 0, 0, 399, 0, - 0, 0, 0, 0, 723, 0, 0, 81, 81, 622, + 630, 623, 0, 0, 633, 0, 0, 0, 0, 0, + 1090, 0, 1460, 1461, 1462, 1463, 0, 1467, 279, 1468, + 1469, 0, 0, 0, 0, 81, 0, 1108, 0, 0, + 0, 1475, 0, 1476, 1477, 0, 0, 0, 0, 51, + 0, 0, 0, 82, 83, 84, 0, 309, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 81, + 0, 0, 0, 0, 0, 1498, 269, 0, 0, 0, + 0, 0, 0, 1503, 269, 269, 1364, 0, 269, 269, + 0, 0, 269, 269, 269, 81, 0, 0, 0, 0, + 0, 1508, 0, 0, 0, 0, 272, 0, 81, 0, + 0, 0, 1359, 275, 0, 0, 0, 355, 0, 0, + 0, 283, 278, 0, 0, 0, 622, 621, 631, 632, + 624, 625, 626, 627, 628, 629, 630, 623, 0, 0, + 633, 0, 0, 0, 0, 0, 1543, 1544, 0, 79, + 0, 0, 0, 0, 281, 1358, 0, 0, 0, 0, + 288, 0, 269, 81, 0, 81, 0, 0, 0, 0, + 0, 269, 269, 269, 269, 269, 0, 269, 269, 0, + 0, 269, 81, 0, 0, 399, 0, 273, 0, 622, 621, 631, 632, 624, 625, 626, 627, 628, 629, 630, - 623, 0, 0, 633, 0, 0, 0, 0, 0, 0, - 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 81, 0, 81, 81, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 269, 0, 659, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 269, 0, 0, 0, - 0, 0, 81, 0, 0, 81, 81, 81, 269, 0, - 0, 0, 81, 0, 0, 269, 0, 0, 0, 0, - 0, 0, 944, 0, 0, 0, 703, 0, 0, 0, - 703, 0, 0, 0, 399, 0, 0, 0, 0, 0, - 0, 399, 399, 399, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 399, - 0, 0, 0, 0, 399, 399, 399, 0, 399, 399, - 0, 0, 0, 0, 0, 0, 399, 399, 0, 0, - 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, - 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, - 0, 565, 0, 565, 0, 840, 0, 0, 81, 0, - 0, 0, 0, 0, 0, 81, 611, 0, 0, 399, - 565, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 81, 81, 0, 81, 0, 0, - 888, 0, 81, 0, 81, 81, 81, 269, 0, 0, - 81, 0, 0, 0, 0, 1073, 914, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 81, 269, 0, - 0, 0, 0, 918, 919, 0, 0, 0, 0, 0, - 647, 648, 649, 650, 651, 652, 653, 654, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 751, - 399, 0, 0, 0, 81, 0, 0, 0, 0, 0, - 0, 0, 0, 399, 0, 81, 81, 0, 0, 0, - 0, 0, 0, 0, 1119, 1120, 0, 0, 703, 703, - 703, 703, 703, 0, 0, 81, 0, 0, 0, 0, - 0, 0, 0, 944, 0, 1143, 269, 0, 0, 0, - 0, 703, 0, 0, 81, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 399, 0, - 399, 0, 0, 0, 0, 0, 0, 81, 0, 0, - 739, 24, 25, 52, 27, 28, 0, 399, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 43, 0, 0, 0, 0, 29, 48, 49, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 399, 752, 565, - 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, - 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 765, 768, 769, 770, 771, 772, 773, 565, 774, - 775, 776, 777, 778, 753, 754, 755, 756, 737, 738, - 766, 0, 740, 0, 741, 742, 743, 744, 745, 746, - 747, 748, 749, 750, 757, 758, 759, 760, 761, 762, - 763, 764, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 31, 32, 34, 33, 36, 0, 50, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 914, 0, 1269, 0, 51, 0, - 0, 37, 44, 45, 0, 0, 46, 47, 35, 0, - 0, 0, 767, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 39, 40, 0, 41, 42, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 399, 0, 0, - 0, 0, 858, 0, 592, 867, 868, 869, 870, 871, + 623, 0, 269, 633, 269, 269, 0, 0, 0, 269, + 0, 0, 1357, 0, 285, 276, 0, 286, 287, 292, + 0, 0, 0, 277, 280, 0, 274, 291, 290, 0, + 1264, 0, 622, 621, 631, 632, 624, 625, 626, 627, + 628, 629, 630, 623, 860, 0, 633, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 267, 0, 0, 293, 0, 1189, 399, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 917, 0, 0, 0, 0, 0, 703, 53, 320, 0, - 0, 390, 0, 0, 0, 399, 267, 0, 267, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1354, 0, 0, 0, 399, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1376, 1377, 1378, 0, - 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 914, 0, 0, 1272, 1274, 0, 0, 0, + 882, 883, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1298, 565, 0, 0, 0, 0, 565, + 0, 565, 0, 0, 0, 0, 0, 565, 0, 622, + 621, 631, 632, 624, 625, 626, 627, 628, 629, 630, + 623, 0, 919, 633, 0, 0, 0, 51, 751, 0, + 0, 0, 269, 269, 269, 269, 269, 0, 1238, 0, + 0, 0, 642, 0, 269, 644, 0, 269, 0, 0, + 0, 269, 0, 0, 0, 269, 0, 1356, 622, 621, + 631, 632, 624, 625, 626, 627, 628, 629, 630, 623, + 0, 0, 633, 655, 81, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 0, 670, 673, 673, 673, 679, + 673, 673, 679, 673, 687, 688, 689, 690, 691, 692, + 693, 0, 703, 0, 0, 0, 0, 0, 1363, 739, + 0, 0, 0, 0, 399, 0, 0, 0, 658, 399, + 0, 399, 81, 81, 0, 0, 1378, 399, 0, 1379, + 0, 0, 1381, 0, 622, 621, 631, 632, 624, 625, + 626, 627, 628, 629, 630, 623, 0, 752, 633, 0, + 0, 0, 81, 0, 0, 0, 0, 611, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 269, 0, + 765, 768, 769, 770, 771, 772, 773, 81, 774, 775, + 776, 777, 778, 753, 754, 755, 756, 737, 738, 766, + 0, 740, 0, 741, 742, 743, 744, 745, 746, 747, + 748, 749, 750, 757, 758, 759, 760, 761, 762, 763, + 764, 0, 0, 0, 0, 1079, 1080, 1081, 0, 0, + 0, 0, 0, 0, 81, 81, 0, 0, 0, 0, + 399, 0, 0, 0, 0, 0, 723, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 565, 0, 81, 0, + 0, 0, 0, 565, 565, 565, 0, 0, 0, 0, + 0, 767, 0, 81, 0, 81, 81, 0, 0, 0, + 0, 565, 0, 0, 0, 0, 565, 565, 565, 0, + 565, 565, 0, 0, 0, 0, 0, 0, 565, 565, + 0, 0, 0, 269, 0, 0, 0, 0, 0, 0, + 0, 1496, 658, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 269, 0, 1084, 0, 0, 0, 81, + 0, 0, 81, 81, 81, 269, 0, 0, 0, 0, + 0, 81, 0, 0, 269, 622, 621, 631, 632, 624, + 625, 626, 627, 628, 629, 630, 623, 0, 0, 633, + 622, 621, 631, 632, 624, 625, 626, 627, 628, 629, + 630, 623, 51, 0, 633, 0, 399, 0, 0, 0, + 0, 0, 0, 399, 399, 399, 0, 659, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 399, 0, 0, 0, 0, 399, 399, 399, 0, + 399, 399, 0, 0, 81, 0, 0, 0, 399, 399, + 0, 0, 81, 0, 0, 0, 0, 0, 0, 0, + 0, 946, 0, 0, 0, 703, 0, 81, 0, 703, + 0, 0, 1239, 1240, 81, 0, 0, 842, 0, 0, + 0, 0, 0, 0, 0, 0, 1257, 1258, 611, 1259, + 1260, 399, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1267, 1268, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 81, 81, 0, 81, 0, 0, 0, + 0, 81, 0, 81, 81, 81, 269, 0, 0, 81, + 0, 0, 890, 0, 0, 0, 0, 0, 0, 0, + 565, 0, 565, 0, 0, 0, 81, 269, 916, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 565, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1274, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 399, 0, 399, 1301, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1269, 0, 0, 1418, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1314, 920, 921, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, + 0, 0, 399, 0, 81, 81, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 399, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 0, 1077, 0, 0, 0, + 24, 25, 52, 27, 28, 269, 0, 1344, 0, 0, + 0, 592, 0, 81, 0, 0, 0, 0, 0, 43, + 0, 0, 0, 0, 29, 48, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, + 399, 0, 399, 0, 0, 38, 0, 267, 0, 54, + 293, 0, 0, 0, 0, 0, 0, 0, 0, 399, + 0, 0, 0, 0, 0, 1123, 1124, 0, 0, 703, + 703, 703, 703, 703, 0, 320, 0, 0, 390, 0, + 0, 0, 0, 267, 946, 267, 1147, 0, 0, 0, + 0, 399, 703, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 31, 32, 34, 33, 36, 0, 50, 0, + 0, 0, 0, 0, 0, 1411, 1412, 1413, 1414, 1415, + 0, 0, 0, 1418, 1419, 0, 0, 0, 0, 0, + 37, 44, 45, 0, 0, 46, 47, 35, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 565, 39, 40, 0, 41, 42, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 916, 565, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 399, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1275, + 0, 51, 0, 0, 0, 0, 267, 0, 0, 0, + 0, 267, 0, 0, 0, 0, 0, 267, 0, 1193, + 399, 0, 0, 267, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1535, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1446, 0, 0, 0, 0, 0, - 1325, 0, 0, 1330, 1331, 1332, 0, 0, 0, 0, - 399, 1075, 1076, 1077, 0, 1269, 0, 51, 0, 267, - 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, - 267, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 914, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 399, 0, 0, 0, 0, 0, 0, - 0, 1385, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 399, 0, 0, 0, - 0, 0, 0, 399, 0, 0, 0, 1523, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 399, 0, + 0, 703, 0, 0, 0, 0, 0, 0, 916, 0, + 0, 1278, 1280, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 267, 267, 267, 0, 1360, + 0, 0, 0, 0, 0, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 399, 0, 399, 1307, 0, 0, 0, 0, 0, 0, + 0, 1382, 1383, 1384, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 267, 267, - 267, 0, 1419, 1420, 0, 1421, 0, 0, 0, 0, - 1385, 0, 1385, 1385, 1385, 0, 0, 0, 1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1385, 0, 0, 0, 0, + 0, 0, 0, 0, 565, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1331, 0, 0, 1336, + 1337, 1338, 0, 0, 0, 0, 0, 0, 399, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1275, 0, 0, 1424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1479, 0, 0, 0, 1233, 1234, 0, 0, - 0, 0, 0, 399, 399, 0, 0, 0, 0, 0, - 1251, 1252, 0, 1253, 1254, 0, 0, 0, 0, 0, - 914, 0, 0, 1498, 0, 1261, 1262, 0, 0, 0, + 0, 0, 0, 0, 0, 267, 0, 0, 0, 1452, + 916, 0, 0, 0, 0, 267, 267, 0, 0, 0, + 0, 0, 0, 267, 0, 0, 267, 0, 0, 267, + 1275, 399, 51, 805, 0, 0, 0, 0, 0, 1391, + 0, 0, 0, 267, 0, 0, 0, 0, 0, 267, + 267, 0, 0, 0, 399, 0, 0, 0, 0, 0, + 0, 399, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1504, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 267, 0, - 0, 0, 0, 0, 0, 1385, 0, 0, 267, 267, - 0, 0, 0, 0, 0, 0, 267, 0, 0, 267, - 0, 0, 267, 0, 0, 0, 805, 0, 1308, 0, 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, + 0, 0, 0, 0, 0, 805, 0, 0, 0, 0, + 1425, 1426, 0, 1427, 0, 0, 0, 0, 1391, 0, + 1391, 1391, 1391, 0, 0, 0, 1307, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1529, 1391, 0, 0, 0, 320, 0, 0, + 0, 0, 320, 320, 0, 0, 320, 320, 320, 0, + 0, 0, 917, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1485, 320, 320, 320, 320, 320, 0, 267, 0, 0, + 0, 399, 399, 0, 0, 267, 951, 0, 0, 267, + 267, 0, 0, 267, 959, 805, 0, 0, 916, 0, + 0, 1504, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 267, - 0, 0, 0, 0, 0, 0, 0, 0, 805, 1338, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 320, 0, 0, 0, 0, 320, 320, 0, 0, 320, - 320, 320, 0, 0, 0, 915, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 320, 320, 320, 320, 320, 0, - 267, 0, 0, 0, 0, 0, 0, 0, 267, 949, - 0, 0, 267, 267, 0, 0, 267, 957, 805, 0, + 1510, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1405, 1406, 1407, - 1408, 1409, 0, 0, 0, 1412, 1413, 0, 0, 0, + 0, 0, 0, 1391, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, + 0, 0, 267, 267, 267, 267, 267, 0, 267, 267, + 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, - 0, 0, 0, 0, 0, 267, 267, 267, 267, 267, - 0, 267, 267, 0, 0, 267, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 267, 0, 1053, 1054, 0, 0, - 0, 267, 0, 0, 0, 0, 805, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 320, 0, + 0, 0, 0, 267, 0, 1057, 1058, 0, 0, 0, + 267, 0, 0, 0, 0, 805, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 320, 320, 0, 0, 0, + 0, 0, 0, 0, 320, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1529, 0, 0, 0, 320, 0, 0, 0, 0, 0, + 0, 0, 0, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 915, 267, 267, 267, 267, 267, 0, - 0, 0, 0, 0, 0, 0, 1137, 0, 0, 267, - 0, 0, 0, 949, 0, 0, 0, 267, 0, 0, + 0, 0, 917, 267, 267, 267, 267, 267, 0, 0, + 0, 0, 0, 0, 0, 1141, 0, 0, 267, 0, + 0, 0, 951, 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1251,29 +1230,29 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 267, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 320, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 267, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 805, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 915, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 805, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 917, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, - 0, 0, 267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 915, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 917, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1281,1157 +1260,1057 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 509, - 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, - 412, 467, 168, 442, 949, 431, 407, 438, 408, 429, - 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, - 142, 476, 0, 214, 156, 267, 0, 458, 501, 465, - 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, - 0, 0, 82, 83, 84, 0, 974, 975, 0, 0, - 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, - 406, 477, 0, 410, 413, 519, 504, 435, 436, 1160, - 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, - 0, 0, 0, 0, 0, 0, 0, 0, 433, 915, - 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, - 0, 0, 416, 267, 434, 492, 0, 404, 122, 496, - 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 509, 497, 0, 454, 512, 427, 444, 520, + 445, 448, 485, 412, 467, 168, 442, 951, 431, 407, + 438, 408, 429, 456, 114, 460, 426, 499, 470, 511, + 140, 432, 518, 142, 476, 0, 214, 156, 267, 0, + 458, 501, 465, 494, 453, 486, 417, 475, 513, 443, + 483, 514, 0, 0, 0, 82, 83, 84, 0, 976, + 977, 0, 0, 0, 0, 0, 104, 0, 480, 508, + 440, 482, 484, 406, 477, 0, 410, 413, 519, 504, + 435, 436, 1164, 0, 0, 0, 0, 0, 0, 457, + 466, 491, 451, 0, 0, 0, 0, 0, 0, 0, + 0, 433, 917, 474, 0, 0, 0, 414, 411, 0, + 0, 455, 0, 0, 0, 416, 267, 434, 492, 0, + 404, 122, 496, 503, 452, 270, 507, 450, 449, 510, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 500, 430, 439, 108, 437, 196, 175, + 234, 473, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 409, 0, 215, 237, 252, 102, 425, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 421, 424, 419, 420, 468, 469, 515, 516, 517, 493, + 415, 0, 422, 423, 0, 498, 505, 506, 472, 85, + 94, 141, 249, 189, 119, 238, 405, 418, 112, 428, + 0, 0, 441, 446, 447, 459, 461, 462, 463, 464, + 471, 478, 479, 481, 487, 488, 489, 490, 495, 502, + 521, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 509, 497, 0, + 454, 512, 427, 444, 520, 445, 448, 485, 412, 467, + 168, 442, 0, 431, 407, 438, 408, 429, 456, 114, + 460, 426, 499, 470, 511, 140, 432, 518, 142, 476, + 0, 214, 156, 0, 0, 458, 501, 465, 494, 453, + 486, 417, 475, 513, 443, 483, 514, 0, 0, 0, + 82, 83, 84, 0, 976, 977, 0, 0, 0, 0, + 0, 104, 0, 480, 508, 440, 482, 484, 406, 477, + 0, 410, 413, 519, 504, 435, 436, 0, 0, 0, + 0, 0, 0, 0, 457, 466, 491, 451, 0, 0, + 0, 0, 0, 0, 0, 0, 433, 0, 474, 0, + 0, 0, 414, 411, 0, 0, 455, 0, 0, 0, + 416, 0, 434, 492, 0, 404, 122, 496, 503, 452, + 270, 507, 450, 449, 510, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 500, 430, + 439, 108, 437, 196, 175, 234, 473, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 409, 0, 215, 237, + 252, 102, 425, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 421, 424, 419, 420, 468, + 469, 515, 516, 517, 493, 415, 0, 422, 423, 0, + 498, 505, 506, 472, 85, 94, 141, 249, 189, 119, + 238, 405, 418, 112, 428, 0, 0, 441, 446, 447, + 459, 461, 462, 463, 464, 471, 478, 479, 481, 487, + 488, 489, 490, 495, 502, 521, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 509, 497, 0, 454, 512, 427, 444, 520, + 445, 448, 485, 412, 467, 168, 442, 0, 431, 407, + 438, 408, 429, 456, 114, 460, 426, 499, 470, 511, + 140, 432, 518, 142, 476, 0, 214, 156, 0, 0, + 458, 501, 465, 494, 453, 486, 417, 475, 513, 443, + 483, 514, 54, 0, 0, 82, 83, 84, 0, 0, + 0, 0, 0, 0, 0, 0, 104, 0, 480, 508, + 440, 482, 484, 406, 477, 0, 410, 413, 519, 504, + 435, 436, 0, 0, 0, 0, 0, 0, 0, 457, + 466, 491, 451, 0, 0, 0, 0, 0, 0, 0, + 0, 433, 0, 474, 0, 0, 0, 414, 411, 0, + 0, 455, 0, 0, 0, 416, 0, 434, 492, 0, + 404, 122, 496, 503, 452, 270, 507, 450, 449, 510, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 500, 430, 439, 108, 437, 196, 175, + 234, 473, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 409, 0, 215, 237, 252, 102, 425, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 421, 424, 419, 420, 468, 469, 515, 516, 517, 493, + 415, 0, 422, 423, 0, 498, 505, 506, 472, 85, + 94, 141, 249, 189, 119, 238, 405, 418, 112, 428, + 0, 0, 441, 446, 447, 459, 461, 462, 463, 464, + 471, 478, 479, 481, 487, 488, 489, 490, 495, 502, + 521, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 509, 497, 0, + 454, 512, 427, 444, 520, 445, 448, 485, 412, 467, + 168, 442, 0, 431, 407, 438, 408, 429, 456, 114, + 460, 426, 499, 470, 511, 140, 432, 518, 142, 476, + 0, 214, 156, 0, 0, 458, 501, 465, 494, 453, + 486, 417, 475, 513, 443, 483, 514, 0, 0, 0, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 0, 480, 508, 440, 482, 484, 406, 477, + 0, 410, 413, 519, 504, 435, 436, 0, 0, 0, + 0, 0, 0, 0, 457, 466, 491, 451, 0, 0, + 0, 0, 0, 0, 1234, 0, 433, 0, 474, 0, + 0, 0, 414, 411, 0, 0, 455, 0, 0, 0, + 416, 0, 434, 492, 0, 404, 122, 496, 503, 452, + 270, 507, 450, 449, 510, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 500, 430, + 439, 108, 437, 196, 175, 234, 473, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 409, 0, 215, 237, + 252, 102, 425, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 421, 424, 419, 420, 468, + 469, 515, 516, 517, 493, 415, 0, 422, 423, 0, + 498, 505, 506, 472, 85, 94, 141, 249, 189, 119, + 238, 405, 418, 112, 428, 0, 0, 441, 446, 447, + 459, 461, 462, 463, 464, 471, 478, 479, 481, 487, + 488, 489, 490, 495, 502, 521, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 509, 497, 0, 454, 512, 427, 444, 520, + 445, 448, 485, 412, 467, 168, 442, 0, 431, 407, + 438, 408, 429, 456, 114, 460, 426, 499, 470, 511, + 140, 432, 518, 142, 476, 0, 214, 156, 0, 0, + 458, 501, 465, 494, 453, 486, 417, 475, 513, 443, + 483, 514, 0, 0, 0, 82, 83, 84, 0, 0, + 0, 0, 0, 0, 0, 0, 104, 0, 480, 508, + 440, 482, 484, 406, 477, 0, 410, 413, 519, 504, + 435, 436, 0, 0, 0, 0, 0, 0, 0, 457, + 466, 491, 451, 0, 0, 0, 0, 0, 0, 960, + 0, 433, 0, 474, 0, 0, 0, 414, 411, 0, + 0, 455, 0, 0, 0, 416, 0, 434, 492, 0, + 404, 122, 496, 503, 452, 270, 507, 450, 449, 510, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 500, 430, 439, 108, 437, 196, 175, + 234, 473, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 409, 0, 215, 237, 252, 102, 425, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 421, 424, 419, 420, 468, 469, 515, 516, 517, 493, + 415, 0, 422, 423, 0, 498, 505, 506, 472, 85, + 94, 141, 249, 189, 119, 238, 405, 418, 112, 428, + 0, 0, 441, 446, 447, 459, 461, 462, 463, 464, + 471, 478, 479, 481, 487, 488, 489, 490, 495, 502, + 521, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 509, 497, 0, + 454, 512, 427, 444, 520, 445, 448, 485, 412, 467, + 168, 442, 0, 431, 407, 438, 408, 429, 456, 114, + 460, 426, 499, 470, 511, 140, 432, 518, 142, 476, + 0, 214, 156, 0, 0, 458, 501, 465, 494, 453, + 486, 417, 475, 513, 443, 483, 514, 0, 0, 0, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 0, 480, 508, 440, 482, 484, 406, 477, + 0, 410, 413, 519, 504, 435, 436, 0, 0, 0, + 0, 0, 0, 0, 457, 466, 491, 451, 0, 0, + 0, 0, 0, 0, 856, 0, 433, 0, 474, 0, + 0, 0, 414, 411, 0, 0, 455, 0, 0, 0, + 416, 0, 434, 492, 0, 404, 122, 496, 503, 452, + 270, 507, 450, 449, 510, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 500, 430, + 439, 108, 437, 196, 175, 234, 473, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 409, 0, 215, 237, + 252, 102, 425, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 421, 424, 419, 420, 468, + 469, 515, 516, 517, 493, 415, 0, 422, 423, 0, + 498, 505, 506, 472, 85, 94, 141, 249, 189, 119, + 238, 405, 418, 112, 428, 0, 0, 441, 446, 447, + 459, 461, 462, 463, 464, 471, 478, 479, 481, 487, + 488, 489, 490, 495, 502, 521, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 509, 497, 0, 454, 512, 427, 444, 520, + 445, 448, 485, 412, 467, 168, 442, 0, 431, 407, + 438, 408, 429, 456, 114, 460, 426, 499, 470, 511, + 140, 432, 518, 142, 476, 0, 214, 156, 0, 0, + 458, 501, 465, 494, 453, 486, 417, 475, 513, 443, + 483, 514, 0, 0, 0, 82, 83, 84, 0, 0, + 0, 0, 0, 0, 0, 0, 104, 0, 480, 508, + 440, 482, 484, 406, 477, 0, 410, 413, 519, 504, + 435, 436, 0, 0, 0, 0, 0, 0, 0, 457, + 466, 491, 451, 0, 0, 0, 0, 0, 0, 0, + 0, 433, 0, 474, 0, 0, 0, 414, 411, 0, + 0, 455, 0, 0, 0, 416, 0, 434, 492, 0, + 404, 122, 496, 503, 452, 270, 507, 450, 449, 510, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 500, 430, 439, 108, 437, 196, 175, + 234, 473, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 409, 0, 215, 237, 252, 102, 425, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 421, 424, 419, 420, 468, 469, 515, 516, 517, 493, + 415, 0, 422, 423, 0, 498, 505, 506, 472, 85, + 94, 141, 249, 189, 119, 238, 405, 418, 112, 428, + 0, 0, 441, 446, 447, 459, 461, 462, 463, 464, + 471, 478, 479, 481, 487, 488, 489, 490, 495, 502, + 521, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 509, 497, 0, + 454, 512, 427, 444, 520, 445, 448, 485, 412, 467, + 168, 442, 0, 431, 407, 438, 408, 429, 456, 114, + 460, 426, 499, 470, 511, 140, 432, 518, 142, 476, + 0, 214, 156, 0, 0, 458, 501, 465, 494, 453, + 486, 417, 475, 513, 443, 483, 514, 0, 0, 0, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 0, 480, 508, 440, 482, 484, 406, 477, + 0, 410, 413, 519, 504, 435, 436, 0, 0, 0, + 0, 0, 0, 0, 457, 466, 491, 451, 0, 0, + 0, 0, 0, 0, 0, 0, 433, 0, 474, 0, + 0, 0, 414, 411, 0, 0, 455, 0, 0, 0, + 416, 0, 434, 492, 0, 404, 122, 496, 503, 452, + 270, 507, 450, 449, 510, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 500, 430, + 439, 108, 437, 196, 175, 234, 473, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 402, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 409, 0, 215, 237, + 252, 102, 425, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 403, 401, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 421, 424, 419, 420, 468, + 469, 515, 516, 517, 493, 415, 0, 422, 423, 0, + 498, 505, 506, 472, 85, 94, 141, 249, 189, 119, + 238, 405, 418, 112, 428, 0, 0, 441, 446, 447, + 459, 461, 462, 463, 464, 471, 478, 479, 481, 487, + 488, 489, 490, 495, 502, 521, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 509, 497, 0, 454, 512, 427, 444, 520, + 445, 448, 485, 412, 467, 168, 442, 0, 431, 407, + 438, 408, 429, 456, 114, 460, 426, 499, 470, 511, + 140, 432, 518, 142, 476, 0, 214, 156, 0, 0, + 458, 501, 465, 494, 453, 486, 417, 475, 513, 443, + 483, 514, 0, 0, 0, 82, 83, 84, 0, 0, + 0, 0, 0, 0, 0, 0, 104, 0, 480, 508, + 440, 482, 484, 406, 477, 0, 410, 413, 519, 504, + 435, 436, 0, 0, 0, 0, 0, 0, 0, 457, + 466, 491, 451, 0, 0, 0, 0, 0, 0, 0, + 0, 433, 0, 474, 0, 0, 0, 414, 411, 0, + 0, 455, 0, 0, 0, 416, 0, 434, 492, 0, + 404, 122, 496, 503, 452, 270, 507, 450, 449, 510, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 500, 430, 439, 108, 437, 196, 175, + 234, 473, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 716, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 402, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 409, 0, 215, 237, 252, 102, 425, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 403, 401, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 421, 424, 419, 420, 468, 469, 515, 516, 517, 493, + 415, 0, 422, 423, 0, 498, 505, 506, 472, 85, + 94, 141, 249, 189, 119, 238, 405, 418, 112, 428, + 0, 0, 441, 446, 447, 459, 461, 462, 463, 464, + 471, 478, 479, 481, 487, 488, 489, 490, 495, 502, + 521, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, 509, 497, 0, + 454, 512, 427, 444, 520, 445, 448, 485, 412, 467, + 168, 442, 0, 431, 407, 438, 408, 429, 456, 114, + 460, 426, 499, 470, 511, 140, 432, 518, 142, 476, + 0, 214, 156, 0, 0, 458, 501, 465, 494, 453, + 486, 417, 475, 513, 443, 483, 514, 0, 0, 0, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 0, 480, 508, 440, 482, 484, 406, 477, + 0, 410, 413, 519, 504, 435, 436, 0, 0, 0, + 0, 0, 0, 0, 457, 466, 491, 451, 0, 0, + 0, 0, 0, 0, 0, 0, 433, 0, 474, 0, + 0, 0, 414, 411, 0, 0, 455, 0, 0, 0, + 416, 0, 434, 492, 0, 404, 122, 496, 503, 452, + 270, 507, 450, 449, 510, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 500, 430, + 439, 108, 437, 196, 175, 234, 473, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 393, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 402, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 409, 0, 215, 237, + 252, 102, 425, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 403, 401, 396, 395, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 421, 424, 419, 420, 468, + 469, 515, 516, 517, 493, 415, 0, 422, 423, 0, + 498, 505, 506, 472, 85, 94, 141, 249, 189, 119, + 238, 405, 418, 112, 428, 0, 0, 441, 446, 447, + 459, 461, 462, 463, 464, 471, 478, 479, 481, 487, + 488, 489, 490, 495, 502, 521, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 168, 0, 0, 892, 0, 327, 0, 0, + 0, 114, 0, 324, 0, 0, 0, 140, 893, 367, + 142, 0, 0, 214, 156, 0, 0, 0, 0, 358, + 359, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 82, 83, 84, 346, 345, 348, 349, 350, + 351, 0, 0, 104, 347, 352, 353, 354, 0, 0, + 0, 322, 339, 0, 366, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 336, 337, 318, 0, 0, 0, + 381, 0, 338, 0, 0, 333, 334, 335, 340, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 122, 380, + 0, 0, 270, 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, - 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 0, 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, - 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, - 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 185, 145, 186, 128, 158, 157, 159, 0, 0, 0, + 215, 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, - 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, - 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, - 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, - 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, - 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, - 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 191, 250, 174, 197, 106, 236, 213, 368, 379, 374, + 375, 372, 373, 371, 370, 369, 382, 360, 361, 362, + 363, 365, 0, 376, 377, 364, 85, 94, 141, 249, + 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, - 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, - 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, - 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, - 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, - 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, - 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, - 0, 974, 975, 0, 0, 0, 0, 0, 104, 0, - 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, - 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, - 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, - 0, 0, 0, 433, 0, 474, 0, 0, 0, 414, - 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, - 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, - 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, - 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, - 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, - 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, - 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, - 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, - 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, - 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, - 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, - 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, - 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, - 494, 453, 486, 417, 475, 513, 443, 483, 514, 54, - 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, - 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, - 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, - 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, - 0, 0, 0, 0, 0, 0, 0, 0, 433, 0, - 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, - 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, - 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 226, 235, 242, 245, 168, 0, 0, 0, 0, 327, + 0, 0, 0, 114, 0, 324, 0, 0, 0, 140, + 0, 367, 142, 0, 0, 214, 156, 0, 0, 0, + 0, 358, 359, 0, 0, 0, 0, 0, 0, 967, + 0, 54, 0, 0, 82, 83, 84, 346, 345, 348, + 349, 350, 351, 0, 0, 104, 347, 352, 353, 354, + 968, 0, 0, 322, 339, 0, 366, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 336, 337, 0, 0, + 0, 0, 381, 0, 338, 0, 0, 333, 334, 335, + 340, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 122, 380, 0, 0, 270, 0, 0, 378, 0, 187, + 0, 218, 125, 139, 100, 86, 96, 0, 124, 165, + 194, 198, 0, 0, 0, 108, 0, 196, 175, 234, + 0, 177, 195, 143, 224, 188, 233, 243, 244, 221, + 241, 248, 211, 89, 220, 232, 105, 206, 91, 230, + 217, 154, 134, 135, 90, 0, 192, 113, 120, 110, + 167, 227, 228, 109, 251, 97, 240, 93, 98, 239, + 161, 223, 231, 155, 148, 92, 229, 153, 147, 138, + 117, 127, 185, 145, 186, 128, 158, 157, 159, 0, + 0, 0, 215, 237, 252, 102, 0, 222, 246, 247, + 0, 0, 103, 121, 116, 184, 160, 99, 130, 212, + 137, 144, 191, 250, 174, 197, 106, 236, 213, 368, + 379, 374, 375, 372, 373, 371, 370, 369, 382, 360, + 361, 362, 363, 365, 0, 376, 377, 364, 85, 94, + 141, 249, 189, 119, 238, 0, 0, 112, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 87, 88, 95, 101, 107, 111, 115, 118, 123, 126, + 129, 131, 132, 133, 136, 146, 149, 150, 151, 152, + 162, 163, 164, 166, 169, 170, 171, 172, 173, 176, + 178, 179, 180, 181, 182, 183, 190, 193, 199, 200, + 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, + 219, 225, 226, 235, 242, 245, 168, 0, 0, 0, + 0, 327, 0, 0, 0, 114, 0, 324, 0, 0, + 0, 140, 0, 367, 142, 0, 0, 214, 156, 0, + 0, 0, 0, 358, 359, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 587, 82, 83, 84, 346, + 345, 348, 349, 350, 351, 0, 0, 104, 347, 352, + 353, 354, 0, 0, 0, 322, 339, 0, 366, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 336, 337, + 0, 0, 0, 0, 381, 0, 338, 0, 0, 333, + 334, 335, 340, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 122, 380, 0, 0, 270, 0, 0, 378, + 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, + 124, 165, 194, 198, 0, 0, 0, 108, 0, 196, + 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, + 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, + 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, + 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, + 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, + 147, 138, 117, 127, 185, 145, 186, 128, 158, 157, + 159, 0, 0, 0, 215, 237, 252, 102, 0, 222, + 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, + 130, 212, 137, 144, 191, 250, 174, 197, 106, 236, + 213, 368, 379, 374, 375, 372, 373, 371, 370, 369, + 382, 360, 361, 362, 363, 365, 0, 376, 377, 364, + 85, 94, 141, 249, 189, 119, 238, 0, 0, 112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, + 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, + 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, + 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, + 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, + 210, 216, 219, 225, 226, 235, 242, 245, 168, 0, + 0, 0, 0, 327, 0, 0, 0, 114, 0, 324, + 0, 0, 0, 140, 0, 367, 142, 0, 0, 214, + 156, 0, 0, 0, 0, 358, 359, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 82, 83, + 84, 346, 345, 348, 349, 350, 351, 0, 0, 104, + 347, 352, 353, 354, 0, 0, 0, 322, 339, 0, + 366, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 336, 337, 318, 0, 0, 0, 381, 0, 338, 0, + 0, 333, 334, 335, 340, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 122, 380, 0, 0, 270, 0, + 0, 378, 0, 187, 0, 218, 125, 139, 100, 86, + 96, 0, 124, 165, 194, 198, 0, 0, 0, 108, + 0, 196, 175, 234, 0, 177, 195, 143, 224, 188, + 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, + 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, + 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, + 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, + 229, 153, 147, 138, 117, 127, 185, 145, 186, 128, + 158, 157, 159, 0, 0, 0, 215, 237, 252, 102, + 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, + 160, 99, 130, 212, 137, 144, 191, 250, 174, 197, + 106, 236, 213, 368, 379, 374, 375, 372, 373, 371, + 370, 369, 382, 360, 361, 362, 363, 365, 0, 376, + 377, 364, 85, 94, 141, 249, 189, 119, 238, 0, + 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, + 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, + 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, + 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, + 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, + 208, 209, 210, 216, 219, 225, 226, 235, 242, 245, + 168, 0, 0, 0, 0, 327, 0, 0, 0, 114, + 0, 324, 0, 0, 0, 140, 0, 367, 142, 0, + 0, 214, 156, 0, 0, 0, 0, 358, 359, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, + 82, 83, 84, 346, 908, 348, 349, 350, 351, 0, + 0, 104, 347, 352, 353, 354, 0, 0, 0, 322, + 339, 0, 366, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 336, 337, 318, 0, 0, 0, 381, 0, + 338, 0, 0, 333, 334, 335, 340, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 122, 380, 0, 0, + 270, 0, 0, 378, 0, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 0, 0, + 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 0, 0, 215, 237, + 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 368, 379, 374, 375, 372, + 373, 371, 370, 369, 382, 360, 361, 362, 363, 365, + 0, 376, 377, 364, 85, 94, 141, 249, 189, 119, + 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 168, 0, 0, 0, 0, 327, 0, 0, + 0, 114, 0, 324, 0, 0, 0, 140, 0, 367, + 142, 0, 0, 214, 156, 0, 0, 0, 0, 358, + 359, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 82, 83, 84, 346, 905, 348, 349, 350, + 351, 0, 0, 104, 347, 352, 353, 354, 0, 0, + 0, 322, 339, 0, 366, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 336, 337, 318, 0, 0, 0, + 381, 0, 338, 0, 0, 333, 334, 335, 340, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 122, 380, + 0, 0, 270, 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, - 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 0, 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, - 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, - 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 185, 145, 186, 128, 158, 157, 159, 0, 0, 0, + 215, 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, - 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, - 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, - 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, - 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, - 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, - 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 191, 250, 174, 197, 106, 236, 213, 368, 379, 374, + 375, 372, 373, 371, 370, 369, 382, 360, 361, 362, + 363, 365, 0, 376, 377, 364, 85, 94, 141, 249, + 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, - 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, - 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, - 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, - 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, - 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, - 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, - 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, - 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, - 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, - 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, - 0, 1228, 0, 433, 0, 474, 0, 0, 0, 414, - 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, - 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, - 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, - 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, - 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, - 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, - 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, - 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, - 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, - 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, - 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, - 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, - 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, - 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, - 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, - 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, - 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, - 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, - 0, 0, 0, 0, 0, 0, 958, 0, 433, 0, - 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, - 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, - 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, + 226, 235, 242, 245, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, + 0, 327, 0, 0, 0, 114, 0, 324, 0, 0, + 0, 140, 0, 367, 142, 0, 0, 214, 156, 0, + 0, 0, 0, 358, 359, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 82, 83, 84, 346, + 345, 348, 349, 350, 351, 0, 0, 104, 347, 352, + 353, 354, 0, 0, 0, 322, 339, 0, 366, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 336, 337, + 0, 0, 0, 0, 381, 0, 338, 0, 0, 333, + 334, 335, 340, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 122, 380, 0, 0, 270, 0, 0, 378, + 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, + 124, 165, 194, 198, 0, 0, 0, 108, 0, 196, + 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, + 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, + 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, + 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, + 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, + 147, 138, 117, 127, 185, 145, 186, 128, 158, 157, + 159, 0, 0, 0, 215, 237, 252, 102, 0, 222, + 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, + 130, 212, 137, 144, 191, 250, 174, 197, 106, 236, + 213, 368, 379, 374, 375, 372, 373, 371, 370, 369, + 382, 360, 361, 362, 363, 365, 0, 376, 377, 364, + 85, 94, 141, 249, 189, 119, 238, 0, 0, 112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, + 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, + 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, + 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, + 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, + 210, 216, 219, 225, 226, 235, 242, 245, 168, 0, + 0, 0, 0, 327, 0, 0, 0, 114, 0, 324, + 0, 0, 0, 140, 0, 367, 142, 0, 0, 214, + 156, 0, 0, 0, 0, 358, 359, 0, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 82, 83, + 84, 346, 345, 348, 349, 350, 351, 0, 0, 104, + 347, 352, 353, 354, 0, 0, 0, 322, 339, 0, + 366, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 336, 337, 0, 0, 0, 0, 381, 0, 338, 0, + 0, 333, 334, 335, 340, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 122, 380, 0, 0, 270, 0, + 0, 378, 0, 187, 0, 218, 125, 139, 100, 86, + 96, 0, 124, 165, 194, 198, 0, 0, 0, 108, + 0, 196, 175, 234, 0, 177, 195, 143, 224, 188, + 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, + 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, + 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, + 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, + 229, 153, 147, 138, 117, 127, 185, 145, 186, 128, + 158, 157, 159, 0, 0, 0, 215, 237, 252, 102, + 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, + 160, 99, 130, 212, 137, 144, 191, 250, 174, 197, + 106, 236, 213, 368, 379, 374, 375, 372, 373, 371, + 370, 369, 382, 360, 361, 362, 363, 365, 0, 376, + 377, 364, 85, 94, 141, 249, 189, 119, 238, 0, + 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, + 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, + 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, + 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, + 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, + 208, 209, 210, 216, 219, 225, 226, 235, 242, 245, + 168, 0, 0, 0, 0, 0, 0, 0, 0, 114, + 0, 0, 0, 0, 0, 140, 0, 367, 142, 0, + 0, 214, 156, 0, 0, 0, 0, 358, 359, 0, + 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, + 82, 83, 84, 346, 345, 348, 349, 350, 351, 0, + 0, 104, 347, 352, 353, 354, 0, 0, 0, 0, + 339, 0, 366, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 336, 337, 0, 0, 0, 0, 381, 0, + 338, 0, 0, 333, 334, 335, 340, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 122, 380, 0, 0, + 270, 0, 0, 378, 0, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 0, 0, + 0, 108, 0, 196, 175, 234, 1536, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 0, 0, 215, 237, + 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 368, 379, 374, 375, 372, + 373, 371, 370, 369, 382, 360, 361, 362, 363, 365, + 0, 376, 377, 364, 85, 94, 141, 249, 189, 119, + 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 168, 0, 0, 0, 0, 0, 0, 0, + 0, 114, 0, 0, 0, 0, 0, 140, 0, 367, + 142, 0, 0, 214, 156, 0, 0, 0, 0, 358, + 359, 0, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 587, 82, 83, 84, 346, 345, 348, 349, 350, + 351, 0, 0, 104, 347, 352, 353, 354, 0, 0, + 0, 0, 339, 0, 366, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 336, 337, 0, 0, 0, 0, + 381, 0, 338, 0, 0, 333, 334, 335, 340, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 122, 380, + 0, 0, 270, 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, - 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, + 0, 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, - 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, - 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, + 185, 145, 186, 128, 158, 157, 159, 0, 0, 0, + 215, 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, - 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, - 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, - 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, - 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, - 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, - 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, + 191, 250, 174, 197, 106, 236, 213, 368, 379, 374, + 375, 372, 373, 371, 370, 369, 382, 360, 361, 362, + 363, 365, 0, 376, 377, 364, 85, 94, 141, 249, + 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, - 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, - 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, - 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, - 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, - 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, - 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, - 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, - 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, - 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, - 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, - 0, 854, 0, 433, 0, 474, 0, 0, 0, 414, - 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, - 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, - 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, - 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, - 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, - 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, - 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, - 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, - 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, - 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, - 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, - 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, - 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, - 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, - 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, - 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, - 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, - 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, - 0, 0, 0, 0, 0, 0, 0, 0, 433, 0, - 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, - 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, - 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, - 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, - 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, - 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, - 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, - 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, - 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, - 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, - 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, - 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, - 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, - 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, - 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, - 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, - 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, - 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, - 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, - 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, - 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, - 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, - 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, - 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, - 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, - 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, - 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, - 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, - 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, - 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, - 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, - 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, - 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, - 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, - 0, 0, 0, 433, 0, 474, 0, 0, 0, 414, - 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, - 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, - 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, - 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 402, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 403, - 401, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, - 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, - 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, - 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, - 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, - 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 509, - 497, 0, 454, 512, 427, 444, 520, 445, 448, 485, - 412, 467, 168, 442, 0, 431, 407, 438, 408, 429, - 456, 114, 460, 426, 499, 470, 511, 140, 432, 518, - 142, 476, 0, 214, 156, 0, 0, 458, 501, 465, - 494, 453, 486, 417, 475, 513, 443, 483, 514, 0, - 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, - 0, 0, 0, 104, 0, 480, 508, 440, 482, 484, - 406, 477, 0, 410, 413, 519, 504, 435, 436, 0, - 0, 0, 0, 0, 0, 0, 457, 466, 491, 451, - 0, 0, 0, 0, 0, 0, 0, 0, 433, 0, - 474, 0, 0, 0, 414, 411, 0, 0, 455, 0, - 0, 0, 416, 0, 434, 492, 0, 404, 122, 496, - 503, 452, 270, 507, 450, 449, 510, 187, 0, 218, - 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, - 500, 430, 439, 108, 437, 196, 175, 234, 473, 177, - 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, - 211, 89, 220, 716, 105, 206, 91, 230, 217, 154, - 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, - 228, 109, 251, 97, 240, 93, 402, 239, 161, 223, - 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, - 185, 145, 186, 128, 158, 157, 159, 0, 409, 0, - 215, 237, 252, 102, 425, 222, 246, 247, 0, 0, - 103, 121, 116, 184, 403, 401, 130, 212, 137, 144, - 191, 250, 174, 197, 106, 236, 213, 421, 424, 419, - 420, 468, 469, 515, 516, 517, 493, 415, 0, 422, - 423, 0, 498, 505, 506, 472, 85, 94, 141, 249, - 189, 119, 238, 405, 418, 112, 428, 0, 0, 441, - 446, 447, 459, 461, 462, 463, 464, 471, 478, 479, - 481, 487, 488, 489, 490, 495, 502, 521, 87, 88, - 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, - 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, - 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, - 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, - 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, - 226, 235, 242, 245, 509, 497, 0, 454, 512, 427, - 444, 520, 445, 448, 485, 412, 467, 168, 442, 0, - 431, 407, 438, 408, 429, 456, 114, 460, 426, 499, - 470, 511, 140, 432, 518, 142, 476, 0, 214, 156, - 0, 0, 458, 501, 465, 494, 453, 486, 417, 475, - 513, 443, 483, 514, 0, 0, 0, 82, 83, 84, - 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, - 480, 508, 440, 482, 484, 406, 477, 0, 410, 413, - 519, 504, 435, 436, 0, 0, 0, 0, 0, 0, - 0, 457, 466, 491, 451, 0, 0, 0, 0, 0, - 0, 0, 0, 433, 0, 474, 0, 0, 0, 414, - 411, 0, 0, 455, 0, 0, 0, 416, 0, 434, - 492, 0, 404, 122, 496, 503, 452, 270, 507, 450, - 449, 510, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 500, 430, 439, 108, 437, - 196, 175, 234, 473, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 393, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 402, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 409, 0, 215, 237, 252, 102, 425, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 403, - 401, 396, 395, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 421, 424, 419, 420, 468, 469, 515, 516, - 517, 493, 415, 0, 422, 423, 0, 498, 505, 506, - 472, 85, 94, 141, 249, 189, 119, 238, 405, 418, - 112, 428, 0, 0, 441, 446, 447, 459, 461, 462, - 463, 464, 471, 478, 479, 481, 487, 488, 489, 490, - 495, 502, 521, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, - 0, 0, 890, 0, 327, 0, 0, 0, 114, 0, - 324, 0, 0, 0, 140, 891, 367, 142, 0, 0, - 214, 156, 0, 0, 0, 0, 358, 359, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 82, - 83, 84, 346, 345, 348, 349, 350, 351, 0, 0, - 104, 347, 352, 353, 354, 0, 0, 0, 322, 339, - 0, 366, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 336, 337, 318, 0, 0, 0, 381, 0, 338, - 0, 0, 333, 334, 335, 340, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 122, 380, 0, 0, 270, - 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, - 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, - 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, - 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, - 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, - 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, - 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, - 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, - 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, - 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, - 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, - 197, 106, 236, 213, 368, 379, 374, 375, 372, 373, - 371, 370, 369, 382, 360, 361, 362, 363, 365, 0, - 376, 377, 364, 85, 94, 141, 249, 189, 119, 238, - 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, - 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, - 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, - 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, - 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, - 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, - 245, 168, 0, 0, 0, 0, 327, 0, 0, 0, - 114, 0, 324, 0, 0, 0, 140, 0, 367, 142, - 0, 0, 214, 156, 0, 0, 0, 0, 358, 359, - 0, 0, 0, 0, 0, 0, 965, 0, 54, 0, - 0, 82, 83, 84, 346, 345, 348, 349, 350, 351, - 0, 0, 104, 347, 352, 353, 354, 966, 0, 0, - 322, 339, 0, 366, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 336, 337, 0, 0, 0, 0, 381, - 0, 338, 0, 0, 333, 334, 335, 340, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 122, 380, 0, - 0, 270, 0, 0, 378, 0, 187, 0, 218, 125, - 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, - 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, - 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, - 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, - 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, - 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, - 155, 148, 92, 229, 153, 147, 138, 117, 127, 185, - 145, 186, 128, 158, 157, 159, 0, 0, 0, 215, - 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, - 121, 116, 184, 160, 99, 130, 212, 137, 144, 191, - 250, 174, 197, 106, 236, 213, 368, 379, 374, 375, - 372, 373, 371, 370, 369, 382, 360, 361, 362, 363, - 365, 0, 376, 377, 364, 85, 94, 141, 249, 189, - 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, + 226, 235, 242, 245, 168, 0, 0, 0, 0, 0, + 0, 0, 0, 114, 0, 0, 0, 0, 0, 140, + 0, 367, 142, 0, 0, 214, 156, 0, 0, 0, + 0, 358, 359, 0, 0, 0, 0, 0, 0, 0, + 0, 54, 0, 0, 82, 83, 84, 346, 345, 348, + 349, 350, 351, 0, 0, 104, 347, 352, 353, 354, + 0, 0, 0, 0, 339, 0, 366, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 336, 337, 0, 0, + 0, 0, 381, 0, 338, 0, 0, 333, 334, 335, + 340, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 122, 380, 0, 0, 270, 0, 0, 378, 0, 187, + 0, 218, 125, 139, 100, 86, 96, 0, 124, 165, + 194, 198, 0, 0, 0, 108, 0, 196, 175, 234, + 0, 177, 195, 143, 224, 188, 233, 243, 244, 221, + 241, 248, 211, 89, 220, 232, 105, 206, 91, 230, + 217, 154, 134, 135, 90, 0, 192, 113, 120, 110, + 167, 227, 228, 109, 251, 97, 240, 93, 98, 239, + 161, 223, 231, 155, 148, 92, 229, 153, 147, 138, + 117, 127, 185, 145, 186, 128, 158, 157, 159, 0, + 0, 0, 215, 237, 252, 102, 0, 222, 246, 247, + 0, 0, 103, 121, 116, 184, 160, 99, 130, 212, + 137, 144, 191, 250, 174, 197, 106, 236, 213, 368, + 379, 374, 375, 372, 373, 371, 370, 369, 382, 360, + 361, 362, 363, 365, 0, 376, 377, 364, 85, 94, + 141, 249, 189, 119, 238, 0, 0, 112, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 87, 88, 95, 101, 107, 111, 115, 118, 123, 126, + 129, 131, 132, 133, 136, 146, 149, 150, 151, 152, + 162, 163, 164, 166, 169, 170, 171, 172, 173, 176, + 178, 179, 180, 181, 182, 183, 190, 193, 199, 200, + 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, + 219, 225, 226, 235, 242, 245, 168, 0, 0, 0, + 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, + 0, 140, 0, 0, 142, 0, 0, 214, 156, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 82, 83, 84, 0, + 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 622, 621, 631, 632, 624, 625, + 626, 627, 628, 629, 630, 623, 0, 0, 633, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 122, 0, 0, 0, 270, 0, 0, 0, + 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, + 124, 165, 194, 198, 0, 0, 0, 108, 0, 196, + 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, + 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, + 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, + 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, + 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, + 147, 138, 117, 127, 185, 145, 186, 128, 158, 157, + 159, 0, 0, 0, 215, 237, 252, 102, 0, 222, + 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, + 130, 212, 137, 144, 191, 250, 174, 197, 106, 236, + 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 85, 94, 141, 249, 189, 119, 238, 0, 0, 112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, + 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, + 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, + 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, + 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, + 210, 216, 219, 225, 226, 235, 242, 245, 168, 0, + 0, 0, 610, 0, 0, 0, 0, 114, 0, 0, + 0, 0, 0, 140, 0, 0, 142, 0, 0, 214, + 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, + 84, 0, 612, 0, 0, 0, 0, 0, 0, 104, + 0, 0, 0, 0, 0, 607, 606, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, - 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, - 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, - 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, - 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, - 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, - 235, 242, 245, 168, 0, 0, 0, 0, 327, 0, - 0, 0, 114, 0, 324, 0, 0, 0, 140, 0, - 367, 142, 0, 0, 214, 156, 0, 0, 0, 0, - 358, 359, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 587, 82, 83, 84, 346, 345, 348, 349, - 350, 351, 0, 0, 104, 347, 352, 353, 354, 0, - 0, 0, 322, 339, 0, 366, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 336, 337, 0, 0, 0, - 0, 381, 0, 338, 0, 0, 333, 334, 335, 340, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, - 380, 0, 0, 270, 0, 0, 378, 0, 187, 0, - 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, - 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, - 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, - 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, - 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, - 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, - 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, - 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, - 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, - 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, - 144, 191, 250, 174, 197, 106, 236, 213, 368, 379, - 374, 375, 372, 373, 371, 370, 369, 382, 360, 361, - 362, 363, 365, 0, 376, 377, 364, 85, 94, 141, - 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 608, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, - 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, - 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, - 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, - 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, - 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, - 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, - 327, 0, 0, 0, 114, 0, 324, 0, 0, 0, - 140, 0, 367, 142, 0, 0, 214, 156, 0, 0, - 0, 0, 358, 359, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 82, 83, 84, 346, 345, - 348, 349, 350, 351, 0, 0, 104, 347, 352, 353, - 354, 0, 0, 0, 322, 339, 0, 366, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 336, 337, 318, - 0, 0, 0, 381, 0, 338, 0, 0, 333, 334, - 335, 340, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 122, 380, 0, 0, 270, 0, 0, 378, 0, - 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, - 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, - 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, - 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, - 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, - 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, - 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, - 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, - 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, - 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, - 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, - 368, 379, 374, 375, 372, 373, 371, 370, 369, 382, - 360, 361, 362, 363, 365, 0, 376, 377, 364, 85, - 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 122, 0, 0, 0, 270, 0, + 0, 0, 0, 187, 0, 218, 125, 139, 100, 86, + 96, 0, 124, 165, 194, 198, 0, 0, 0, 108, + 0, 196, 175, 234, 0, 177, 195, 143, 224, 188, + 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, + 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, + 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, + 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, + 229, 153, 147, 138, 117, 127, 185, 145, 186, 128, + 158, 157, 159, 0, 0, 0, 215, 237, 252, 102, + 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, + 160, 99, 130, 212, 137, 144, 191, 250, 174, 197, + 106, 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, - 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, - 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, - 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, - 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, - 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, - 0, 0, 327, 0, 0, 0, 114, 0, 324, 0, - 0, 0, 140, 0, 367, 142, 0, 0, 214, 156, - 0, 0, 0, 0, 358, 359, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, 0, 82, 83, 84, - 346, 906, 348, 349, 350, 351, 0, 0, 104, 347, - 352, 353, 354, 0, 0, 0, 322, 339, 0, 366, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 336, - 337, 318, 0, 0, 0, 381, 0, 338, 0, 0, - 333, 334, 335, 340, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 380, 0, 0, 270, 0, 0, - 378, 0, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, - 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 368, 379, 374, 375, 372, 373, 371, 370, - 369, 382, 360, 361, 362, 363, 365, 0, 376, 377, - 364, 85, 94, 141, 249, 189, 119, 238, 0, 0, - 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, - 0, 0, 0, 0, 327, 0, 0, 0, 114, 0, - 324, 0, 0, 0, 140, 0, 367, 142, 0, 0, - 214, 156, 0, 0, 0, 0, 358, 359, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 82, - 83, 84, 346, 903, 348, 349, 350, 351, 0, 0, - 104, 347, 352, 353, 354, 0, 0, 0, 322, 339, - 0, 366, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 336, 337, 318, 0, 0, 0, 381, 0, 338, - 0, 0, 333, 334, 335, 340, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 122, 380, 0, 0, 270, - 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, - 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, - 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, - 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, - 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, - 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, - 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, - 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, - 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, - 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, - 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, - 197, 106, 236, 213, 368, 379, 374, 375, 372, 373, - 371, 370, 369, 382, 360, 361, 362, 363, 365, 0, - 376, 377, 364, 85, 94, 141, 249, 189, 119, 238, - 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, - 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, - 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, - 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, - 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, - 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, - 245, 24, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 168, 0, 0, 0, 0, 327, 0, - 0, 0, 114, 0, 324, 0, 0, 0, 140, 0, - 367, 142, 0, 0, 214, 156, 0, 0, 0, 0, - 358, 359, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 82, 83, 84, 346, 345, 348, 349, - 350, 351, 0, 0, 104, 347, 352, 353, 354, 0, - 0, 0, 322, 339, 0, 366, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 336, 337, 0, 0, 0, - 0, 381, 0, 338, 0, 0, 333, 334, 335, 340, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, - 380, 0, 0, 270, 0, 0, 378, 0, 187, 0, - 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, - 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, - 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, - 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, - 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, - 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, - 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, - 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, - 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, - 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, - 144, 191, 250, 174, 197, 106, 236, 213, 368, 379, - 374, 375, 372, 373, 371, 370, 369, 382, 360, 361, - 362, 363, 365, 0, 376, 377, 364, 85, 94, 141, - 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 85, 94, 141, 249, 189, 119, 238, 0, + 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, - 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, - 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, - 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, - 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, - 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, - 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, - 327, 0, 0, 0, 114, 0, 324, 0, 0, 0, - 140, 0, 367, 142, 0, 0, 214, 156, 0, 0, - 0, 0, 358, 359, 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 82, 83, 84, 346, 345, - 348, 349, 350, 351, 0, 0, 104, 347, 352, 353, - 354, 0, 0, 0, 322, 339, 0, 366, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 336, 337, 0, - 0, 0, 0, 381, 0, 338, 0, 0, 333, 334, - 335, 340, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 122, 380, 0, 0, 270, 0, 0, 378, 0, - 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, - 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, - 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, - 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, - 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, - 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, - 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, - 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, - 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, - 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, - 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, - 368, 379, 374, 375, 372, 373, 371, 370, 369, 382, - 360, 361, 362, 363, 365, 0, 376, 377, 364, 85, - 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, + 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, + 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, + 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, + 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, + 208, 209, 210, 216, 219, 225, 226, 235, 242, 245, + 168, 0, 0, 0, 0, 0, 0, 0, 0, 114, + 0, 0, 0, 0, 0, 140, 0, 0, 142, 0, + 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, - 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, - 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, - 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, - 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, - 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, - 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, - 0, 0, 140, 0, 367, 142, 0, 0, 214, 156, - 0, 0, 0, 0, 358, 359, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, 0, 82, 83, 84, - 346, 345, 348, 349, 350, 351, 0, 0, 104, 347, - 352, 353, 354, 0, 0, 0, 0, 339, 0, 366, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 336, - 337, 0, 0, 0, 0, 381, 0, 338, 0, 0, - 333, 334, 335, 340, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 380, 0, 0, 270, 0, 0, - 378, 0, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, - 196, 175, 234, 1530, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 368, 379, 374, 375, 372, 373, 371, 370, - 369, 382, 360, 361, 362, 363, 365, 0, 376, 377, - 364, 85, 94, 141, 249, 189, 119, 238, 0, 0, - 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, - 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, - 0, 0, 0, 0, 140, 0, 367, 142, 0, 0, - 214, 156, 0, 0, 0, 0, 358, 359, 0, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 587, 82, - 83, 84, 346, 345, 348, 349, 350, 351, 0, 0, - 104, 347, 352, 353, 354, 0, 0, 0, 0, 339, - 0, 366, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 336, 337, 0, 0, 0, 0, 381, 0, 338, - 0, 0, 333, 334, 335, 340, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 122, 380, 0, 0, 270, - 0, 0, 378, 0, 187, 0, 218, 125, 139, 100, - 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, - 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, - 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, - 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, - 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, - 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, - 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, - 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, - 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, - 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, - 197, 106, 236, 213, 368, 379, 374, 375, 372, 373, - 371, 370, 369, 382, 360, 361, 362, 363, 365, 0, - 376, 377, 364, 85, 94, 141, 249, 189, 119, 238, - 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, - 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, - 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, - 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, - 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, - 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, - 245, 168, 0, 0, 0, 0, 0, 0, 0, 0, - 114, 0, 0, 0, 0, 0, 140, 0, 367, 142, - 0, 0, 214, 156, 0, 0, 0, 0, 358, 359, - 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, - 0, 82, 83, 84, 346, 345, 348, 349, 350, 351, - 0, 0, 104, 347, 352, 353, 354, 0, 0, 0, - 0, 339, 0, 366, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 336, 337, 0, 0, 0, 0, 381, - 0, 338, 0, 0, 333, 334, 335, 340, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 122, 380, 0, - 0, 270, 0, 0, 378, 0, 187, 0, 218, 125, - 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, - 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, - 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, - 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, - 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, - 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, - 155, 148, 92, 229, 153, 147, 138, 117, 127, 185, - 145, 186, 128, 158, 157, 159, 0, 0, 0, 215, - 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, - 121, 116, 184, 160, 99, 130, 212, 137, 144, 191, - 250, 174, 197, 106, 236, 213, 368, 379, 374, 375, - 372, 373, 371, 370, 369, 382, 360, 361, 362, 363, - 365, 0, 376, 377, 364, 85, 94, 141, 249, 189, - 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, - 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, - 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, - 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, - 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, - 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, - 235, 242, 245, 168, 0, 0, 0, 0, 0, 0, - 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, - 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 82, 83, 84, 0, 0, 0, 0, - 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 122, 76, 77, 0, + 73, 0, 0, 0, 78, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 0, 0, + 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 0, 0, 215, 237, + 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 0, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 622, 621, 631, 632, 624, 625, 626, 627, 628, - 629, 630, 623, 0, 0, 633, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, - 0, 0, 0, 270, 0, 0, 0, 0, 187, 0, - 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, - 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, - 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, - 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, - 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, - 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, - 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, - 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, - 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, - 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, - 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, + 0, 0, 0, 0, 85, 94, 141, 249, 189, 119, + 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 85, 94, 141, - 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 168, 0, 0, 0, 950, 0, 0, 0, + 0, 114, 0, 0, 0, 0, 0, 140, 0, 0, + 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, - 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, - 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, - 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, - 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, - 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, - 225, 226, 235, 242, 245, 168, 0, 0, 0, 610, - 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, - 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, + 0, 0, 82, 83, 84, 0, 952, 0, 0, 0, + 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 82, 83, 84, 0, 612, - 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, - 0, 0, 607, 606, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 608, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, - 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, - 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, - 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, - 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, - 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, - 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, - 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, - 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, - 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, - 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, - 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, + 0, 0, 270, 0, 0, 0, 0, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 0, 0, 0, 108, 0, 196, 175, 234, 0, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 0, 0, + 215, 237, 252, 102, 0, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, - 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 85, 94, 141, 249, + 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 168, 0, 0, 0, + 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, + 0, 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, - 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, - 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, - 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, - 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, - 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, - 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, - 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, - 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, - 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 76, 77, 0, 73, 0, 0, - 0, 78, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, - 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 0, 75, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 85, 94, 141, 249, 189, 119, 238, 0, 0, - 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, - 0, 0, 0, 948, 0, 0, 0, 0, 114, 0, - 0, 0, 0, 0, 140, 0, 0, 142, 0, 0, - 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 83, 84, 0, 950, 0, 0, 0, 0, 0, 0, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 122, 0, 0, 0, 270, - 0, 0, 0, 0, 187, 0, 218, 125, 139, 100, - 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, - 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, - 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, - 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, - 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, - 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, - 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, - 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, - 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, - 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, - 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 85, 94, 141, 249, 189, 119, 238, - 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, - 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, - 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, - 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, - 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, - 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, - 245, 24, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, - 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, - 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 82, 83, 84, 0, + 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 82, 83, 84, 0, 0, 0, 0, - 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, - 0, 0, 0, 270, 0, 0, 0, 0, 187, 0, - 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, - 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, - 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, - 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, - 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, - 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, - 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, - 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, - 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, - 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, - 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, + 0, 0, 122, 0, 0, 0, 270, 0, 0, 0, + 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, + 124, 165, 194, 198, 0, 0, 0, 108, 0, 196, + 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, + 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, + 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, + 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, + 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, + 147, 138, 117, 127, 185, 145, 186, 128, 158, 157, + 159, 0, 0, 0, 215, 237, 252, 102, 0, 222, + 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, + 130, 212, 137, 144, 191, 250, 174, 197, 106, 236, + 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 85, 94, 141, - 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 85, 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, - 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, - 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, - 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, - 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, - 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, - 225, 226, 235, 242, 245, 168, 0, 0, 0, 948, - 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, - 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 82, 83, 84, 0, 950, - 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, + 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, + 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, + 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, + 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, + 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, + 210, 216, 219, 225, 226, 235, 242, 245, 168, 0, + 0, 0, 950, 0, 0, 0, 0, 114, 0, 0, + 0, 0, 0, 140, 0, 0, 142, 0, 0, 214, + 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, + 84, 0, 952, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, - 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, - 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, - 234, 0, 946, 195, 143, 224, 188, 233, 243, 244, - 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, - 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, - 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, - 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, - 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, - 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, - 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, - 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 0, 0, 0, 0, 122, 0, 0, 0, 270, 0, + 0, 0, 0, 187, 0, 218, 125, 139, 100, 86, + 96, 0, 124, 165, 194, 198, 0, 0, 0, 108, + 0, 196, 175, 234, 0, 948, 195, 143, 224, 188, + 233, 243, 244, 221, 241, 248, 211, 89, 220, 232, + 105, 206, 91, 230, 217, 154, 134, 135, 90, 0, + 192, 113, 120, 110, 167, 227, 228, 109, 251, 97, + 240, 93, 98, 239, 161, 223, 231, 155, 148, 92, + 229, 153, 147, 138, 117, 127, 185, 145, 186, 128, + 158, 157, 159, 0, 0, 0, 215, 237, 252, 102, + 0, 222, 246, 247, 0, 0, 103, 121, 116, 184, + 160, 99, 130, 212, 137, 144, 191, 250, 174, 197, + 106, 236, 213, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 85, 94, 141, 249, 189, 119, 238, 0, + 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 88, 95, 101, 107, 111, + 115, 118, 123, 126, 129, 131, 132, 133, 136, 146, + 149, 150, 151, 152, 162, 163, 164, 166, 169, 170, + 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, + 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, + 208, 209, 210, 216, 219, 225, 226, 235, 242, 245, + 168, 0, 0, 0, 0, 0, 0, 0, 0, 114, + 0, 0, 0, 0, 0, 140, 0, 0, 142, 0, + 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 83, 84, 0, 0, 843, 0, 0, 844, 0, + 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 122, 0, 0, 0, + 270, 0, 0, 0, 0, 187, 0, 218, 125, 139, + 100, 86, 96, 0, 124, 165, 194, 198, 0, 0, + 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, + 224, 188, 233, 243, 244, 221, 241, 248, 211, 89, + 220, 232, 105, 206, 91, 230, 217, 154, 134, 135, + 90, 0, 192, 113, 120, 110, 167, 227, 228, 109, + 251, 97, 240, 93, 98, 239, 161, 223, 231, 155, + 148, 92, 229, 153, 147, 138, 117, 127, 185, 145, + 186, 128, 158, 157, 159, 0, 0, 0, 215, 237, + 252, 102, 0, 222, 246, 247, 0, 0, 103, 121, + 116, 184, 160, 99, 130, 212, 137, 144, 191, 250, + 174, 197, 106, 236, 213, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 94, 141, 249, 189, 119, + 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, - 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 87, 88, 95, 101, + 107, 111, 115, 118, 123, 126, 129, 131, 132, 133, + 136, 146, 149, 150, 151, 152, 162, 163, 164, 166, + 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, + 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, + 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, + 242, 245, 168, 0, 0, 0, 0, 0, 0, 0, + 0, 114, 0, 725, 0, 0, 0, 140, 0, 0, + 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 83, 84, 0, 724, 0, 0, 0, + 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, - 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, - 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, - 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, - 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, - 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, - 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, - 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, - 0, 0, 841, 0, 0, 842, 0, 0, 104, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 0, 0, 0, 270, 0, 0, - 0, 0, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, - 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 85, 94, 141, 249, 189, 119, 238, 0, 0, - 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, - 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, - 725, 0, 0, 0, 140, 0, 0, 142, 0, 0, - 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 83, 84, 0, 724, 0, 0, 0, 0, 0, 0, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 122, 0, 0, 0, 270, - 0, 0, 0, 0, 187, 0, 218, 125, 139, 100, - 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, - 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, - 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, - 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, - 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, - 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, - 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, - 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, - 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, - 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, - 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 85, 94, 141, 249, 189, 119, 238, - 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, - 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, - 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, - 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, - 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, - 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, - 245, 168, 0, 0, 0, 0, 0, 0, 0, 0, - 114, 0, 0, 0, 0, 0, 140, 0, 0, 142, - 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 587, 82, 83, 84, 0, 0, 0, 0, 0, 0, - 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 122, 0, + 0, 0, 270, 0, 0, 0, 0, 187, 0, 218, + 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, + 0, 0, 0, 108, 0, 196, 175, 234, 0, 177, + 195, 143, 224, 188, 233, 243, 244, 221, 241, 248, + 211, 89, 220, 232, 105, 206, 91, 230, 217, 154, + 134, 135, 90, 0, 192, 113, 120, 110, 167, 227, + 228, 109, 251, 97, 240, 93, 98, 239, 161, 223, + 231, 155, 148, 92, 229, 153, 147, 138, 117, 127, + 185, 145, 186, 128, 158, 157, 159, 0, 0, 0, + 215, 237, 252, 102, 0, 222, 246, 247, 0, 0, + 103, 121, 116, 184, 160, 99, 130, 212, 137, 144, + 191, 250, 174, 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 85, 94, 141, 249, + 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, - 0, 270, 0, 0, 0, 0, 187, 0, 218, 125, - 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, - 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, - 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, - 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, - 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, - 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, - 155, 148, 92, 229, 153, 147, 138, 117, 127, 185, - 145, 186, 128, 158, 157, 159, 0, 0, 0, 215, - 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, - 121, 116, 184, 160, 99, 130, 212, 137, 144, 191, - 250, 174, 197, 106, 236, 213, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, + 95, 101, 107, 111, 115, 118, 123, 126, 129, 131, + 132, 133, 136, 146, 149, 150, 151, 152, 162, 163, + 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, + 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, + 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, + 226, 235, 242, 245, 168, 0, 0, 0, 0, 0, + 0, 0, 0, 114, 0, 0, 0, 0, 0, 140, + 0, 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 85, 94, 141, 249, 189, - 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 587, 82, 83, 84, 0, 0, 0, + 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, - 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, - 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, - 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, - 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, - 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, - 235, 242, 245, 168, 0, 0, 0, 0, 0, 0, - 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, - 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 82, 83, 84, 0, 0, 0, 0, - 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 122, 0, 0, 0, 270, 0, 0, 0, 0, 187, + 0, 218, 125, 139, 100, 86, 96, 0, 124, 165, + 194, 198, 0, 0, 0, 108, 0, 196, 175, 234, + 0, 177, 195, 143, 224, 188, 233, 243, 244, 221, + 241, 248, 211, 89, 220, 232, 105, 206, 91, 230, + 217, 154, 134, 135, 90, 0, 192, 113, 120, 110, + 167, 227, 228, 109, 251, 97, 240, 93, 98, 239, + 161, 223, 231, 155, 148, 92, 229, 153, 147, 138, + 117, 127, 185, 145, 186, 128, 158, 157, 159, 0, + 0, 0, 215, 237, 252, 102, 0, 222, 246, 247, + 0, 0, 103, 121, 116, 184, 160, 99, 130, 212, + 137, 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, - 0, 0, 0, 270, 0, 0, 0, 0, 187, 0, - 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, - 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, - 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, - 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, - 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, - 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, - 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, - 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, - 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, - 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, - 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 85, 94, + 141, 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 85, 94, 141, - 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, - 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, - 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, - 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, - 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, - 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, - 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, - 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, - 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, + 87, 88, 95, 101, 107, 111, 115, 118, 123, 126, + 129, 131, 132, 133, 136, 146, 149, 150, 151, 152, + 162, 163, 164, 166, 169, 170, 171, 172, 173, 176, + 178, 179, 180, 181, 182, 183, 190, 193, 199, 200, + 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, + 219, 225, 226, 235, 242, 245, 168, 0, 0, 0, + 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, + 0, 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 82, 83, 84, 0, 950, - 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, + 0, 0, 0, 54, 0, 0, 82, 83, 84, 0, + 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, - 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, - 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, - 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, - 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, - 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, - 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, - 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, - 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, - 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, - 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, - 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 0, 0, 122, 0, 0, 0, 270, 0, 0, 0, + 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, + 124, 165, 194, 198, 0, 0, 0, 108, 0, 196, + 175, 234, 0, 177, 195, 143, 224, 188, 233, 243, + 244, 221, 241, 248, 211, 89, 220, 232, 105, 206, + 91, 230, 217, 154, 134, 135, 90, 0, 192, 113, + 120, 110, 167, 227, 228, 109, 251, 97, 240, 93, + 98, 239, 161, 223, 231, 155, 148, 92, 229, 153, + 147, 138, 117, 127, 185, 145, 186, 128, 158, 157, + 159, 0, 0, 0, 215, 237, 252, 102, 0, 222, + 246, 247, 0, 0, 103, 121, 116, 184, 160, 99, + 130, 212, 137, 144, 191, 250, 174, 197, 106, 236, + 213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, - 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 85, 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, - 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, - 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, - 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, - 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, - 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, - 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, - 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, - 0, 612, 0, 0, 0, 0, 0, 0, 104, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 122, 0, 0, 0, 270, 0, 0, - 0, 0, 187, 0, 218, 125, 139, 100, 86, 96, - 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, - 196, 175, 234, 0, 177, 195, 143, 224, 188, 233, - 243, 244, 221, 241, 248, 211, 89, 220, 232, 105, - 206, 91, 230, 217, 154, 134, 135, 90, 0, 192, - 113, 120, 110, 167, 227, 228, 109, 251, 97, 240, - 93, 98, 239, 161, 223, 231, 155, 148, 92, 229, - 153, 147, 138, 117, 127, 185, 145, 186, 128, 158, - 157, 159, 0, 0, 0, 215, 237, 252, 102, 0, - 222, 246, 247, 0, 0, 103, 121, 116, 184, 160, - 99, 130, 212, 137, 144, 191, 250, 174, 197, 106, - 236, 213, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 85, 94, 141, 249, 189, 119, 238, 0, 0, - 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 87, 88, 95, 101, 107, 111, 115, - 118, 123, 126, 129, 131, 132, 133, 136, 146, 149, - 150, 151, 152, 162, 163, 164, 166, 169, 170, 171, - 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, - 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, - 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, - 0, 0, 0, 0, 0, 0, 0, 695, 114, 0, - 0, 0, 0, 0, 140, 0, 0, 142, 0, 0, - 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, - 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 122, 0, 0, 0, 270, - 0, 0, 0, 0, 187, 0, 218, 125, 139, 100, - 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, - 108, 0, 196, 175, 234, 0, 177, 195, 143, 224, - 188, 233, 243, 244, 221, 241, 248, 211, 89, 220, - 232, 105, 206, 91, 230, 217, 154, 134, 135, 90, - 0, 192, 113, 120, 110, 167, 227, 228, 109, 251, - 97, 240, 93, 98, 239, 161, 223, 231, 155, 148, - 92, 229, 153, 147, 138, 117, 127, 185, 145, 186, - 128, 158, 157, 159, 0, 0, 0, 215, 237, 252, - 102, 0, 222, 246, 247, 0, 0, 103, 121, 116, - 184, 160, 99, 130, 212, 137, 144, 191, 250, 174, - 197, 106, 236, 213, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 85, 94, 141, 249, 189, 119, 238, - 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 87, 88, 95, 101, 107, - 111, 115, 118, 123, 126, 129, 131, 132, 133, 136, - 146, 149, 150, 151, 152, 162, 163, 164, 166, 169, - 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, - 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, - 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, - 245, 385, 0, 0, 0, 0, 0, 0, 168, 0, + 0, 0, 87, 88, 95, 101, 107, 111, 115, 118, + 123, 126, 129, 131, 132, 133, 136, 146, 149, 150, + 151, 152, 162, 163, 164, 166, 169, 170, 171, 172, + 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, + 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, + 210, 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, - 84, 0, 0, 0, 0, 0, 0, 0, 0, 104, + 84, 0, 952, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -2464,13 +2343,13 @@ var yyAct = [...]int{ 0, 0, 0, 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, + 82, 83, 84, 0, 612, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 122, 0, 265, 0, + 0, 0, 0, 0, 0, 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, 143, @@ -2494,7 +2373,7 @@ var yyAct = [...]int{ 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, 0, 0, 0, - 0, 114, 0, 0, 0, 0, 0, 140, 0, 0, + 695, 114, 0, 0, 0, 0, 0, 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, @@ -2526,18 +2405,119 @@ var yyAct = [...]int{ 164, 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, 225, - 226, 235, 242, 245, + 226, 235, 242, 245, 385, 0, 0, 0, 0, 0, + 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, + 114, 0, 0, 0, 0, 0, 140, 0, 0, 142, + 0, 0, 214, 156, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 82, 83, 84, 0, 0, 0, 0, 0, 0, + 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 122, 0, 0, + 0, 270, 0, 0, 0, 0, 187, 0, 218, 125, + 139, 100, 86, 96, 0, 124, 165, 194, 198, 0, + 0, 0, 108, 0, 196, 175, 234, 0, 177, 195, + 143, 224, 188, 233, 243, 244, 221, 241, 248, 211, + 89, 220, 232, 105, 206, 91, 230, 217, 154, 134, + 135, 90, 0, 192, 113, 120, 110, 167, 227, 228, + 109, 251, 97, 240, 93, 98, 239, 161, 223, 231, + 155, 148, 92, 229, 153, 147, 138, 117, 127, 185, + 145, 186, 128, 158, 157, 159, 0, 0, 0, 215, + 237, 252, 102, 0, 222, 246, 247, 0, 0, 103, + 121, 116, 184, 160, 99, 130, 212, 137, 144, 191, + 250, 174, 197, 106, 236, 213, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 85, 94, 141, 249, 189, + 119, 238, 0, 0, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 87, 88, 95, + 101, 107, 111, 115, 118, 123, 126, 129, 131, 132, + 133, 136, 146, 149, 150, 151, 152, 162, 163, 164, + 166, 169, 170, 171, 172, 173, 176, 178, 179, 180, + 181, 182, 183, 190, 193, 199, 200, 201, 202, 203, + 204, 205, 207, 208, 209, 210, 216, 219, 225, 226, + 235, 242, 245, 168, 0, 0, 0, 0, 0, 0, + 0, 0, 114, 0, 0, 0, 0, 0, 140, 0, + 0, 142, 0, 0, 214, 156, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 82, 83, 84, 0, 0, 0, 0, + 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 0, 265, 0, 270, 0, 0, 0, 0, 187, 0, + 218, 125, 139, 100, 86, 96, 0, 124, 165, 194, + 198, 0, 0, 0, 108, 0, 196, 175, 234, 0, + 177, 195, 143, 224, 188, 233, 243, 244, 221, 241, + 248, 211, 89, 220, 232, 105, 206, 91, 230, 217, + 154, 134, 135, 90, 0, 192, 113, 120, 110, 167, + 227, 228, 109, 251, 97, 240, 93, 98, 239, 161, + 223, 231, 155, 148, 92, 229, 153, 147, 138, 117, + 127, 185, 145, 186, 128, 158, 157, 159, 0, 0, + 0, 215, 237, 252, 102, 0, 222, 246, 247, 0, + 0, 103, 121, 116, 184, 160, 99, 130, 212, 137, + 144, 191, 250, 174, 197, 106, 236, 213, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 85, 94, 141, + 249, 189, 119, 238, 0, 0, 112, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, + 88, 95, 101, 107, 111, 115, 118, 123, 126, 129, + 131, 132, 133, 136, 146, 149, 150, 151, 152, 162, + 163, 164, 166, 169, 170, 171, 172, 173, 176, 178, + 179, 180, 181, 182, 183, 190, 193, 199, 200, 201, + 202, 203, 204, 205, 207, 208, 209, 210, 216, 219, + 225, 226, 235, 242, 245, 168, 0, 0, 0, 0, + 0, 0, 0, 0, 114, 0, 0, 0, 0, 0, + 140, 0, 0, 142, 0, 0, 214, 156, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, 83, 84, 0, 0, + 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 122, 0, 0, 0, 270, 0, 0, 0, 0, + 187, 0, 218, 125, 139, 100, 86, 96, 0, 124, + 165, 194, 198, 0, 0, 0, 108, 0, 196, 175, + 234, 0, 177, 195, 143, 224, 188, 233, 243, 244, + 221, 241, 248, 211, 89, 220, 232, 105, 206, 91, + 230, 217, 154, 134, 135, 90, 0, 192, 113, 120, + 110, 167, 227, 228, 109, 251, 97, 240, 93, 98, + 239, 161, 223, 231, 155, 148, 92, 229, 153, 147, + 138, 117, 127, 185, 145, 186, 128, 158, 157, 159, + 0, 0, 0, 215, 237, 252, 102, 0, 222, 246, + 247, 0, 0, 103, 121, 116, 184, 160, 99, 130, + 212, 137, 144, 191, 250, 174, 197, 106, 236, 213, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, + 94, 141, 249, 189, 119, 238, 0, 0, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 95, 101, 107, 111, 115, 118, 123, + 126, 129, 131, 132, 133, 136, 146, 149, 150, 151, + 152, 162, 163, 164, 166, 169, 170, 171, 172, 173, + 176, 178, 179, 180, 181, 182, 183, 190, 193, 199, + 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, + 216, 219, 225, 226, 235, 242, 245, } var yyPact = [...]int{ - 2475, -1000, -264, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 2434, -1000, -263, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 913, 958, -1000, -1000, -1000, -1000, -1000, -1000, + 264, 11582, 44, 128, 37, 15585, 127, 1575, 15917, -1000, + 20, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -65, -87, + -1000, 697, -1000, -1000, -1000, -1000, -1000, 907, 911, 765, + 901, 808, -1000, 8250, 91, 91, 15253, 6922, -1000, -1000, + 345, 15917, 120, 15917, -132, 89, 89, 89, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 894, 933, -1000, -1000, -1000, -1000, -1000, -1000, - 248, 11789, 2, 123, -2, 15792, 122, 181, 16124, -1000, - 20, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -79, -80, - -1000, 691, -1000, -1000, -1000, -1000, -1000, 879, 889, 711, - 883, 783, -1000, 8457, 92, 92, 15460, 7129, -1000, -1000, - 458, 16124, 118, 16124, -143, 90, 90, 90, -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, @@ -2554,23 +2534,23 @@ 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, -1000, -1000, 125, 15917, 497, 497, 211, -1000, 15917, + 86, 497, 86, 86, 86, 15917, -1000, 171, -1000, -1000, + -1000, 15917, 497, 844, 303, 77, 4507, -1000, 946, 945, + -1000, 4507, 38, 4507, -50, 928, 24, -21, -1000, 4507, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 121, 16124, 497, 497, 225, -1000, 16124, - 85, 497, 85, 85, 85, 16124, -1000, 171, -1000, -1000, - -1000, 16124, 497, 810, 355, 60, 4714, -1000, 4714, 4714, - -1000, 4714, 29, 4714, -42, 903, 30, -26, -1000, 4714, + -1000, -1000, -1000, 343, 881, 9590, 9590, 913, -1000, 697, + -1000, -1000, -1000, 859, -1000, -1000, 297, 942, -1000, 11250, + 170, -1000, 9590, 402, 687, -1000, -1000, 687, -1000, -1000, + 158, -1000, -1000, 10586, 10586, 10586, 10586, 10586, 10586, 10586, + 10586, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 687, -1000, 9258, 687, 687, + 687, 687, 687, 687, 687, 687, 9590, 687, 687, 687, + 687, 687, 687, 687, 687, 687, 687, 687, 687, 687, + 687, 687, 687, 14914, 13918, 15917, 684, 664, -1000, -1000, + 168, 678, 6577, -77, -1000, -1000, -1000, 254, 13254, -1000, + -1000, -1000, 843, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 393, 820, 9797, 9797, 894, -1000, 691, - -1000, -1000, -1000, 865, -1000, -1000, 329, 916, -1000, 11457, - 170, -1000, 9797, 1635, 555, -1000, -1000, 555, -1000, -1000, - 135, -1000, -1000, 10793, 10793, 10793, 10793, 10793, 10793, 10793, - 10793, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 555, -1000, 9465, 555, 555, - 555, 555, 555, 555, 555, 555, 9797, 555, 555, 555, - 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, - 555, 555, 555, 15121, 14125, 16124, 676, 665, -1000, -1000, - 166, 654, 6784, -105, -1000, -1000, -1000, 254, 13461, -1000, - -1000, -1000, 805, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -2581,133 +2561,132 @@ 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, -1000, 641, 15917, -1000, 1858, -1000, 497, 4507, 106, + 497, 274, 497, 15917, 15917, 4507, 4507, 4507, 36, 78, + 72, 15917, 683, 95, 15917, 892, 773, 15917, 497, 497, + -1000, 5887, -1000, 4507, 303, -1000, 468, 9590, 4507, 4507, + 4507, 15917, 4507, 4507, -1000, -1000, -1000, 15917, 15917, -1000, + 4507, 4507, -1000, 941, 307, -1000, -1000, -1000, -1000, 9590, + 214, -1000, 770, -1000, -1000, -1000, -1000, -1000, -1000, 953, + 202, 509, 164, 679, -1000, 373, 907, 343, 808, 12922, + 786, -1000, -1000, -1000, 15917, -1000, 9590, 9590, 521, -1000, + 14582, -1000, -1000, 5542, 212, 10586, 377, 288, 10586, 10586, + 10586, 10586, 10586, 10586, 10586, 10586, 10586, 10586, 10586, 10586, + 10586, 10586, 10586, 481, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 497, -1000, 697, 603, 603, 185, 185, 185, + 185, 185, 185, 185, 10918, 7254, 343, 637, 379, 9258, + 8250, 8250, 9590, 9590, 8914, 8582, 8250, 863, 267, 379, + 15917, -1000, -1000, 10254, -1000, -1000, -1000, -1000, -1000, 343, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 15917, 15917, 8250, + 8250, 8250, 8250, 8250, 54, 15917, -1000, 670, 861, -1000, + -1000, -1000, 898, 12258, 12590, 54, 665, 13918, 15917, -1000, + -1000, 13918, 15917, 5197, 6232, 678, -77, 651, -1000, -106, + -99, 7586, 179, -1000, -1000, -1000, -1000, 4162, 310, 501, + 364, -38, -1000, -1000, -1000, 701, -1000, 701, 701, 701, + 701, -7, -7, -7, -7, -1000, -1000, -1000, -1000, -1000, + 734, 731, -1000, 701, 701, 701, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 616, 16124, -1000, 2369, -1000, 497, 4714, 104, - 497, 305, 497, 16124, 16124, 4714, 4714, 4714, 40, 70, - 64, 16124, 660, 99, 16124, 870, 723, 16124, 497, 497, - -1000, 6094, -1000, 4714, 355, -1000, 459, 9797, 4714, 4714, - 4714, 16124, 4714, 4714, -1000, -1000, -1000, -1000, -1000, -1000, - 4714, 4714, -1000, 915, 299, -1000, -1000, -1000, -1000, 9797, - 212, -1000, 722, -1000, -1000, -1000, -1000, -1000, -1000, 928, - 207, 390, 160, 655, -1000, 276, 879, 393, 783, 13129, - 720, -1000, -1000, -1000, 16124, -1000, 9797, 9797, 509, -1000, - 14789, -1000, -1000, 5749, 214, 10793, 477, 286, 10793, 10793, - 10793, 10793, 10793, 10793, 10793, 10793, 10793, 10793, 10793, 10793, - 10793, 10793, 10793, 554, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 497, -1000, 691, 566, 566, 178, 178, 178, - 178, 178, 178, 178, 11125, 7461, 393, 613, 362, 9465, - 8457, 8457, 9797, 9797, 9121, 8789, 8457, 815, 281, 362, - 16124, -1000, -1000, 10461, -1000, -1000, -1000, -1000, -1000, 393, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16124, 16124, 8457, - 8457, 8457, 8457, 8457, 52, 16124, -1000, 650, 706, -1000, - -1000, -1000, 872, 12465, 12797, 52, 638, 14125, 16124, -1000, - -1000, 14125, 16124, 5404, 6439, 654, -105, 642, -1000, -101, - -93, 7793, 177, -1000, -1000, -1000, -1000, 4369, 519, 513, - 300, -55, -1000, -1000, -1000, 670, -1000, 670, 670, 670, - 670, -14, -14, -14, -14, -1000, -1000, -1000, -1000, -1000, - 699, 692, -1000, 670, 670, 670, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 729, 729, 729, 714, 714, 713, + -1000, 15917, 4507, 880, 4507, -1000, 73, -1000, -1000, -1000, + 15917, 15917, 15917, 15917, 15917, 138, 15917, 15917, 677, -1000, + 15917, 4507, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 379, -1000, -1000, -1000, -1000, -1000, -1000, 307, 307, -1000, + -1000, 15917, 303, 15917, 15917, 379, -1000, 449, 15917, -1000, + 800, 9590, 9590, 5887, 9590, -1000, -1000, -1000, 881, -1000, + 863, 919, -1000, 819, 818, 8250, -1000, -1000, 212, 339, + -1000, -1000, 411, -1000, -1000, -1000, -1000, 163, 687, -1000, + 2114, -1000, -1000, -1000, -1000, 377, 10586, 10586, 10586, 320, + 2114, 2099, 547, 416, 185, 285, 285, 207, 207, 207, + 207, 207, 473, 473, -1000, -1000, -1000, 343, -1000, -1000, + -1000, 343, 8250, 8250, 675, -1000, -1000, 9590, -1000, 343, + 628, 628, 302, 337, 259, 932, 628, 257, 930, 628, + 628, 8250, 325, -1000, 9590, 343, -1000, 156, -1000, 761, + 671, 669, 628, 343, 343, 628, 628, 681, 687, -1000, + 15917, 13918, 13918, 13918, 13918, 13918, -1000, 792, 791, -1000, + 785, 784, 799, 15917, -1000, 634, 12258, 197, 687, -1000, + 14250, -1000, -1000, 927, 13918, 698, -1000, 698, -1000, 154, + -1000, -1000, 651, -77, -69, -1000, -1000, -1000, -1000, 379, + -1000, 537, 650, 3817, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 723, 497, -1000, 866, 203, 252, 497, 855, -1000, + -1000, -1000, 846, -1000, 281, -58, -1000, -1000, 384, -7, + -7, -1000, -1000, 179, 842, 179, 179, 179, 448, 448, + -1000, -1000, -1000, -1000, 383, -1000, -1000, -1000, 376, -1000, + 768, 15917, 4507, -1000, -1000, -1000, -1000, 167, 167, 213, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 690, 690, 690, 672, 672, 704, - -1000, 16124, 4714, 869, 4714, -1000, 79, -1000, -1000, -1000, - 16124, 16124, 16124, 16124, 16124, 130, 16124, 16124, 651, -1000, - 16124, 4714, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 362, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 16124, - 355, 16124, 16124, 362, -1000, 457, 16124, -1000, 782, 9797, - 9797, 6094, 9797, -1000, -1000, -1000, 820, -1000, 815, 887, - -1000, 794, 793, 8457, -1000, -1000, 214, 359, -1000, -1000, - 397, -1000, -1000, -1000, -1000, 155, 555, -1000, 1993, -1000, - -1000, -1000, -1000, 477, 10793, 10793, 10793, 389, 1993, 1894, - 768, 1599, 178, 285, 285, 188, 188, 188, 188, 188, - 317, 317, -1000, -1000, -1000, 393, -1000, -1000, -1000, 393, - 8457, 8457, 645, -1000, -1000, 9797, -1000, 393, 608, 608, - 388, 500, 247, 914, 608, 233, 909, 608, 608, 8457, - 289, -1000, 9797, 393, -1000, 153, -1000, 405, 644, 643, - 608, 393, 393, 608, 608, 647, 555, -1000, 16124, 14125, - 14125, 14125, 14125, 14125, -1000, 757, 753, -1000, 770, 769, - 734, 16124, -1000, 611, 12465, 161, 555, -1000, 14457, -1000, - -1000, 902, 14125, 623, -1000, 623, -1000, 149, -1000, -1000, - 642, -105, -96, -1000, -1000, -1000, -1000, 362, -1000, 562, - 639, 4024, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 682, - 497, -1000, 859, 208, 316, 497, 854, -1000, -1000, -1000, - 816, -1000, 312, -71, -1000, -1000, 366, -14, -14, -1000, - -1000, 177, 804, 177, 177, 177, 436, 436, -1000, -1000, - -1000, -1000, 365, -1000, -1000, -1000, 352, -1000, 718, 16124, - 4714, -1000, -1000, -1000, -1000, 298, 298, 195, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 51, - 666, -1000, -1000, -1000, -1000, 13, 36, 95, -1000, 4714, - -1000, 299, -1000, -1000, -1000, -1000, -1000, 779, 362, 362, - 146, -1000, -1000, 16124, -1000, -1000, -1000, -1000, 627, -1000, - -1000, -1000, 5059, 8457, -1000, 389, 1993, 1715, -1000, 10793, - 10793, -1000, -1000, 608, 608, 8457, 362, -1000, -1000, -1000, - 80, 554, 80, 10793, 10793, -1000, 10793, 10793, -1000, -162, - 543, 258, -1000, 9797, 438, -1000, 6094, -1000, 10793, 10793, - -1000, -1000, -1000, -1000, -1000, 716, 16124, 555, -1000, 12465, - 16124, 630, -1000, 244, 706, 709, 715, 761, -1000, -1000, - -1000, -1000, 747, -1000, 746, -1000, -1000, -1000, -1000, -1000, - 116, 111, 110, 16124, -1000, 894, 9797, 623, -1000, -1000, - 187, -1000, -1000, -124, -119, -1000, -1000, -1000, 4369, -1000, - 4369, 16124, 66, -1000, 497, 497, -1000, -1000, -1000, 681, - 714, 10793, -1000, -1000, -1000, 496, 177, 177, -1000, 255, - -1000, -1000, -1000, 606, -1000, 600, 589, 593, 16124, -1000, + -1000, 53, 699, -1000, -1000, -1000, -1000, 19, 30, 93, + -1000, 4507, -1000, 303, 303, 307, -1000, -1000, -1000, -1000, + -1000, 806, 379, 379, 148, -1000, -1000, 15917, -1000, -1000, + -1000, -1000, 559, -1000, -1000, -1000, 4852, 8250, -1000, 320, + 2114, 1822, -1000, 10586, 10586, -1000, -1000, 628, 628, 8250, + 379, -1000, -1000, -1000, 29, 481, 29, 10586, 10586, -1000, + 10586, 10586, -1000, -146, 702, 263, -1000, 9590, 358, -1000, + 5887, -1000, 10586, 10586, -1000, -1000, -1000, -1000, -1000, 766, + 15917, 687, -1000, 12258, 15917, 712, -1000, 248, 861, 720, + 764, 762, -1000, -1000, -1000, -1000, 788, -1000, 771, -1000, + -1000, -1000, -1000, -1000, 117, 116, 115, 15917, -1000, 913, + 9590, 698, -1000, -1000, 194, -1000, -1000, -115, -113, -1000, + -1000, -1000, 4162, -1000, 4162, 15917, 69, -1000, 497, 497, + -1000, -1000, -1000, 717, 760, 10586, -1000, -1000, -1000, 496, + 179, 179, -1000, 233, -1000, -1000, -1000, 626, -1000, 624, + 588, 622, 15917, -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, 16124, -1000, - -1000, -1000, -1000, -1000, 16124, -169, 497, 16124, 16124, 16124, - 16124, -1000, 355, -1000, 6094, -1000, 902, 14125, -1000, -1000, - 393, -1000, 10793, 1993, 1993, -1000, -1000, -1000, 393, 670, - 670, -1000, 670, 672, -1000, 670, 10, 670, -11, 393, - 393, 1846, 1825, 1732, 1699, 555, -156, -1000, 362, 9797, - -1000, 1528, 1513, -1000, 862, 490, 561, -1000, -1000, 8125, - 393, 584, 144, 551, -1000, 894, 16124, 9797, -1000, -1000, - 9797, 671, -1000, 9797, -1000, -1000, -1000, 555, 555, 555, - 551, 879, 362, -1000, -1000, -1000, -1000, 4024, -1000, 545, - -1000, 670, -1000, -1000, -1000, 16124, -51, 927, 1993, -1000, - -1000, -1000, -1000, -1000, -14, 435, -14, 344, -1000, 343, - 4714, -1000, -1000, -1000, -1000, 864, -1000, 6094, -1000, -1000, - 663, 677, -1000, -1000, -1000, 900, 577, -1000, 1993, -1000, - -1000, 117, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 10793, 10793, 10793, 10793, 10793, 879, 419, 362, 10793, 10793, - 835, -1000, 555, -1000, -1000, 687, 16124, 16124, -1000, 16124, - 879, -1000, 362, 362, 16124, 362, 13793, 16124, 16124, 12121, - -1000, 157, 16124, -1000, 537, -1000, 205, -1000, -78, 177, - -1000, 177, 468, 395, -1000, 555, 569, -1000, 238, 16124, - 16124, 896, 885, -1000, -1000, 405, 405, 405, 405, 54, - 393, -1000, 405, 405, 926, -1000, 555, -1000, 691, 139, - -1000, -1000, -1000, 533, 531, -1000, 531, 531, 161, 157, - -1000, 497, 226, 416, -1000, 65, 16124, 327, 829, -1000, - 822, -1000, -1000, -1000, -1000, -1000, 50, 6094, 4369, 517, - -1000, -1000, 9797, 9797, -1000, -1000, -1000, -1000, 393, 49, - -173, -1000, -1000, -1000, 16124, 561, 393, 16124, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 339, -1000, -1000, 16124, -1000, - -1000, 410, -1000, -1000, 511, -1000, 16124, -1000, -1000, 666, - 362, 548, -1000, 776, -166, -178, 528, -1000, -1000, -1000, - 648, -1000, -1000, 50, 791, -169, -1000, 763, -1000, 16124, - -1000, 47, -1000, -170, 484, 45, -175, 712, 555, -179, - 707, -1000, 907, 10129, -1000, -1000, 919, 185, 185, 405, - 393, -1000, -1000, -1000, 71, 412, -1000, -1000, -1000, -1000, - -1000, -1000, + -1000, -1000, 15917, -1000, -1000, -1000, -1000, -1000, 15917, -152, + 497, 15917, 15917, 15917, 15917, -1000, -1000, -1000, 303, -1000, + 5887, -1000, 927, 13918, -1000, -1000, 343, -1000, 10586, 2114, + 2114, -1000, -1000, -1000, 343, 701, 701, -1000, 701, 714, + -1000, 701, 10, 701, 9, 343, 343, 1898, 1773, 1716, + 1673, 687, -139, -1000, 379, 9590, -1000, 1610, 1484, -1000, + 872, 570, 550, -1000, -1000, 7918, 343, 602, 144, 584, + -1000, 913, 15917, 9590, -1000, -1000, 9590, 711, -1000, 9590, + -1000, -1000, -1000, 687, 687, 687, 584, 907, 379, -1000, + -1000, -1000, -1000, 3817, -1000, 581, -1000, 701, -1000, -1000, + -1000, 15917, -33, 952, 2114, -1000, -1000, -1000, -1000, -1000, + -7, 424, -7, 338, -1000, 317, 4507, -1000, -1000, -1000, + -1000, 876, -1000, 5887, -1000, -1000, 694, 703, -1000, -1000, + -1000, 918, 579, -1000, 2114, -1000, -1000, 124, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 10586, 10586, 10586, 10586, + 10586, 907, 423, 379, 10586, 10586, 854, -1000, 687, -1000, + -1000, 695, 15917, 15917, -1000, 15917, 907, -1000, 379, 379, + 15917, 379, 13586, 15917, 15917, 11914, -1000, 161, 15917, -1000, + 576, -1000, 191, -1000, -142, 179, -1000, 179, 494, 475, + -1000, 687, 565, -1000, 240, 15917, 15917, 915, 909, -1000, + -1000, 761, 761, 761, 761, 48, 343, -1000, 761, 761, + 950, -1000, 687, -1000, 697, 135, -1000, -1000, -1000, 563, + 553, -1000, 553, 553, 197, 161, -1000, 497, 221, 415, + -1000, 66, 15917, 292, 850, -1000, 849, -1000, -1000, -1000, + -1000, -1000, 52, 5887, 4162, 530, -1000, -1000, 9590, 9590, + -1000, -1000, -1000, -1000, 343, 65, -156, -1000, -1000, -1000, + 15917, 550, 343, 15917, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 309, -1000, -1000, 15917, -1000, -1000, 374, -1000, -1000, + 510, -1000, 15917, -1000, -1000, 699, 379, 525, -1000, 803, + -150, -169, 417, -1000, -1000, -1000, 691, -1000, -1000, 52, + 817, -152, -1000, 797, -1000, 15917, -1000, 49, -1000, -154, + 490, 41, -166, 751, 687, -170, 747, -1000, 939, 9922, + -1000, -1000, 949, 210, 210, 761, 343, -1000, -1000, -1000, + 74, 382, -1000, -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int{ - 0, 1137, 21, 523, 1135, 1134, 1132, 1131, 1130, 1126, - 1119, 1118, 1117, 1116, 1114, 1113, 1112, 1111, 1108, 1107, - 1099, 1097, 1096, 1095, 1093, 1091, 87, 1090, 1087, 1086, - 76, 1083, 79, 1082, 1077, 51, 218, 49, 47, 11, - 1076, 26, 65, 62, 1075, 42, 1074, 1072, 81, 1071, - 1069, 56, 1068, 1067, 61, 1065, 75, 1063, 14, 34, - 1061, 1059, 1058, 1057, 77, 2046, 1056, 1055, 15, 1054, - 1053, 103, 1052, 58, 6, 16, 27, 25, 1051, 31, - 19, 1049, 57, 1048, 1047, 1046, 1045, 28, 1044, 64, - 1043, 18, 63, 1042, 7, 78, 35, 24, 5, 82, - 69, 1040, 20, 66, 60, 1036, 1033, 500, 1032, 1031, - 48, 1030, 1029, 43, 1025, 90, 449, 1024, 1023, 1016, - 1014, 45, 967, 1685, 33, 80, 1013, 1012, 1011, 2664, - 44, 55, 17, 1010, 59, 639, 50, 1009, 1008, 46, - 1007, 1002, 1001, 1000, 992, 991, 989, 92, 987, 983, - 980, 29, 13, 978, 977, 84, 30, 975, 974, 973, - 52, 70, 971, 969, 53, 40, 960, 959, 958, 957, - 956, 37, 10, 955, 23, 954, 12, 952, 41, 951, - 9, 950, 8, 946, 2, 0, 944, 4, 54, 3, - 943, 1, 941, 940, 1498, 180, 83, 939, 85, + 0, 1178, 26, 544, 1177, 1176, 1175, 1171, 1168, 1161, + 1160, 1159, 1157, 1156, 1155, 1154, 1153, 1152, 1151, 1149, + 1147, 1144, 1143, 1142, 1140, 1137, 96, 1134, 1133, 1132, + 80, 1131, 77, 1122, 1121, 46, 252, 52, 48, 702, + 1120, 29, 69, 61, 1119, 41, 1118, 1112, 82, 1111, + 1109, 57, 1108, 1104, 94, 1103, 66, 1102, 14, 55, + 1099, 1095, 1093, 1089, 76, 1208, 1086, 1085, 16, 1084, + 1083, 93, 1081, 60, 15, 11, 13, 22, 1080, 43, + 6, 1075, 73, 1074, 1073, 1072, 1070, 24, 1067, 63, + 1066, 25, 62, 1065, 9, 75, 40, 23, 2, 83, + 79, 1064, 30, 65, 54, 1063, 1062, 409, 1061, 1058, + 51, 1057, 1056, 33, 1055, 99, 525, 1054, 1053, 1051, + 1050, 56, 960, 1697, 17, 81, 1049, 1048, 1047, 2451, + 47, 59, 18, 1046, 44, 53, 45, 1040, 1038, 42, + 1037, 1036, 1035, 1033, 1031, 1030, 1029, 21, 1026, 1024, + 1019, 20, 35, 1017, 1015, 64, 31, 1014, 1012, 1007, + 49, 70, 1006, 1005, 58, 28, 1003, 1002, 993, 989, + 985, 37, 7, 982, 19, 980, 12, 971, 34, 970, + 8, 969, 10, 968, 5, 0, 967, 4, 50, 1, + 966, 3, 965, 964, 1577, 190, 84, 950, 86, } var yyR1 = [...]int{ @@ -2836,7 +2815,7 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 1, 3, 8, 8, 3, 3, 5, 4, 6, 5, 4, 4, 3, 2, 3, 4, 4, 3, 4, 4, - 4, 4, 4, 4, 3, 2, 3, 3, 2, 3, + 4, 4, 4, 4, 3, 2, 6, 6, 2, 3, 4, 3, 7, 5, 4, 2, 4, 4, 3, 3, 5, 2, 3, 1, 1, 0, 1, 1, 1, 0, 2, 2, 0, 2, 2, 0, 2, 0, 1, 1, @@ -2963,7 +2942,7 @@ var yyChk = [...]int{ -116, 131, -116, -116, 126, -54, -185, -185, 118, 120, 123, 53, -18, -54, -115, 131, -185, -115, -115, -115, -54, 115, -54, -185, 30, -113, 93, 12, 244, -185, - 159, 126, 160, 128, -135, -194, -124, -135, -135, -135, + 159, 126, 160, 128, -135, -194, -124, 11, 11, -135, 163, 164, -135, -112, -111, 226, 227, 221, 225, 12, 164, 221, 162, -135, -134, -134, -195, 57, -92, 19, 31, -39, -129, -88, -89, -39, -87, -2, -26, 37, @@ -2988,80 +2967,80 @@ var yyChk = [...]int{ -135, 128, -185, 77, -185, -54, -54, -135, -135, -135, 161, 161, 126, 126, 166, -54, 56, 129, -48, 23, 53, -54, -185, -185, -130, -129, -121, -135, -113, 62, - -39, -135, -135, -135, -54, -135, -135, -135, -135, 11, - -110, 11, 95, -39, -114, 93, 53, 9, 95, 56, - 18, 115, 56, -90, 24, 25, -91, -195, -32, -66, - -123, 63, 66, -31, 44, -54, -39, -39, -72, 71, - 77, 72, 73, -125, 102, -130, -124, -121, -65, -73, - -76, -79, 67, 95, 93, 94, 79, -65, -65, -65, + -39, -135, -135, -135, -54, -135, -135, -54, -54, -135, + -135, 11, -110, 11, 95, -39, -114, 93, 53, 9, + 95, 56, 18, 115, 56, -90, 24, 25, -91, -195, + -32, -66, -123, 63, 66, -31, 44, -54, -39, -39, + -72, 71, 77, 72, 73, -125, 102, -130, -124, -121, + -65, -73, -76, -79, 67, 95, 93, 94, 79, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, - -65, -65, -136, -185, 62, -185, -64, -64, -123, -37, - 21, 34, -36, -38, -195, 56, -195, -2, -36, -36, - -39, -39, -80, 62, -36, -80, 62, -36, -36, -30, - -81, -82, 81, -80, -123, -129, -195, -65, -123, -123, - -36, -37, -37, -36, -36, -95, 155, -54, 30, 56, - -50, -52, -51, -53, 43, 47, 49, 44, 45, 46, - 50, -133, 22, -41, -194, -132, 155, -131, 22, -129, - 62, -95, 54, -41, -54, -41, -56, -129, 102, -103, - -100, 56, 235, 237, 238, 53, 74, -39, -152, 110, - -170, -171, -172, -124, 62, 63, -161, -162, -163, -173, - 141, -178, 133, 135, 132, -164, 142, 127, 28, 57, - -157, 71, 77, -153, 217, -147, 55, -147, -147, -147, - -147, -151, 192, -151, -151, -151, 55, 55, -147, -147, - -147, -155, 55, -155, -155, -156, 55, -156, -127, 54, - -54, -135, 23, -135, -117, 123, 120, 121, -181, 119, - 214, 192, 69, 29, 15, 253, 155, 268, -185, 156, - -54, -54, -54, -54, -54, 123, 120, -54, -54, -54, - -135, -54, -113, -129, -129, 62, -54, 39, -39, -39, - -130, -89, -92, -106, 19, 11, 35, 35, -36, 71, - 72, 73, 115, -194, -73, -65, -65, -65, -35, 150, - 76, -195, -195, -36, -36, 56, -39, -195, -195, -195, - 56, 54, 22, 11, 11, -195, 11, 11, -195, -195, - -36, -84, -82, 83, -39, -195, 115, -195, 56, 56, - -195, -195, -195, -195, -195, -63, 30, 35, -2, -194, - -194, -98, -102, -80, -42, -43, -43, -42, -43, 43, - 43, 43, 48, 43, 48, 43, -51, -129, -195, -58, - 51, 130, 52, -194, -131, -59, 12, -41, -59, -59, - 115, -104, -105, 239, 236, 242, -185, 62, 56, -172, - 85, 55, -185, 28, -164, -164, -165, -185, -165, 28, - -149, 29, 71, -154, 218, 63, -151, -151, -152, 30, - -152, -152, -152, -160, 62, -160, 63, 63, 53, -123, - -135, -134, -188, 138, 134, 141, 142, 136, 58, 59, - 60, 127, 28, 133, 135, 155, 132, -188, -118, -119, - 129, 22, 127, 28, 155, -187, 54, 161, 214, 161, - 129, -135, -110, 40, 115, -54, -40, 11, 102, -124, - -37, -35, 76, -65, -65, -195, -195, -38, -139, 111, - 189, 149, 187, 183, 203, 194, 216, 185, 217, -136, - -139, -65, -65, -65, -65, 262, -87, 84, -39, 82, - -124, -65, -65, -97, 53, -98, -75, -77, -76, -194, - -2, -93, -123, -96, -123, -59, 56, 85, -46, -45, - 53, 54, -47, 53, -45, 43, 43, 127, 127, 127, - -96, -87, -39, -59, 236, 240, 241, -171, -172, -175, - -174, -123, -178, -165, -165, 55, -150, 53, -65, 57, - -152, -152, -185, 111, 57, 56, 57, 56, 57, 56, - -54, -134, -134, -54, -134, -123, -184, 265, -186, -185, - -123, -123, -123, -54, -113, -59, -41, -195, -65, -195, - -147, -147, -147, -156, -147, 177, -147, 177, -195, -195, - 19, 19, 19, 19, -194, -34, 258, -39, 56, 56, - 27, -97, 56, -195, -195, -195, 56, 115, -195, 56, - -87, -102, -39, -39, 55, -39, -194, -194, -194, -195, - -91, 57, 56, -147, -94, -123, -158, 214, 9, -151, - 62, -151, 63, 63, -135, 26, -183, -182, -124, 55, - 54, -85, 13, -151, -185, -65, -65, -65, -65, -65, - -91, 62, -65, -65, 28, -77, 35, -2, -194, -123, - -123, -123, -91, -94, -94, -195, -94, -94, -132, -177, - -176, 54, 137, 69, -174, 57, 56, -159, 133, 28, - 132, -68, -152, -152, 57, 57, -194, 56, 85, -94, - -54, -86, 14, 16, -195, -195, -195, -195, -33, 95, - 265, -195, -195, -195, 9, -75, -2, 115, 57, -195, - -195, -195, -58, -176, -185, -166, 85, 62, 144, -123, - -148, 69, 28, 28, -179, -180, 155, -182, -172, 57, - -39, -74, -195, 263, 50, 266, -98, -195, -123, 63, - -54, 62, -195, 56, -123, -187, 40, 264, 267, 55, - -180, 35, -184, 40, -94, 157, 265, 57, 158, 266, - -190, -191, 53, -194, 267, -191, 53, 10, 9, -65, - 154, -189, 145, 140, 143, 30, -189, -195, -195, 139, - 29, 71, + -65, -65, -65, -65, -136, -185, 62, -185, -64, -64, + -123, -37, 21, 34, -36, -38, -195, 56, -195, -2, + -36, -36, -39, -39, -80, 62, -36, -80, 62, -36, + -36, -30, -81, -82, 81, -80, -123, -129, -195, -65, + -123, -123, -36, -37, -37, -36, -36, -95, 155, -54, + 30, 56, -50, -52, -51, -53, 43, 47, 49, 44, + 45, 46, 50, -133, 22, -41, -194, -132, 155, -131, + 22, -129, 62, -95, 54, -41, -54, -41, -56, -129, + 102, -103, -100, 56, 235, 237, 238, 53, 74, -39, + -152, 110, -170, -171, -172, -124, 62, 63, -161, -162, + -163, -173, 141, -178, 133, 135, 132, -164, 142, 127, + 28, 57, -157, 71, 77, -153, 217, -147, 55, -147, + -147, -147, -147, -151, 192, -151, -151, -151, 55, 55, + -147, -147, -147, -155, 55, -155, -155, -156, 55, -156, + -127, 54, -54, -135, 23, -135, -117, 123, 120, 121, + -181, 119, 214, 192, 69, 29, 15, 253, 155, 268, + -185, 156, -54, -54, -54, -54, -54, 123, 120, -54, + -54, -54, -135, -110, -110, -54, -113, -129, -129, 62, + -54, 39, -39, -39, -130, -89, -92, -106, 19, 11, + 35, 35, -36, 71, 72, 73, 115, -194, -73, -65, + -65, -65, -35, 150, 76, -195, -195, -36, -36, 56, + -39, -195, -195, -195, 56, 54, 22, 11, 11, -195, + 11, 11, -195, -195, -36, -84, -82, 83, -39, -195, + 115, -195, 56, 56, -195, -195, -195, -195, -195, -63, + 30, 35, -2, -194, -194, -98, -102, -80, -42, -43, + -43, -42, -43, 43, 43, 43, 48, 43, 48, 43, + -51, -129, -195, -58, 51, 130, 52, -194, -131, -59, + 12, -41, -59, -59, 115, -104, -105, 239, 236, 242, + -185, 62, 56, -172, 85, 55, -185, 28, -164, -164, + -165, -185, -165, 28, -149, 29, 71, -154, 218, 63, + -151, -151, -152, 30, -152, -152, -152, -160, 62, -160, + 63, 63, 53, -123, -135, -134, -188, 138, 134, 141, + 142, 136, 58, 59, 60, 127, 28, 133, 135, 155, + 132, -188, -118, -119, 129, 22, 127, 28, 155, -187, + 54, 161, 214, 161, 129, -135, -113, -113, -110, 40, + 115, -54, -40, 11, 102, -124, -37, -35, 76, -65, + -65, -195, -195, -38, -139, 111, 189, 149, 187, 183, + 203, 194, 216, 185, 217, -136, -139, -65, -65, -65, + -65, 262, -87, 84, -39, 82, -124, -65, -65, -97, + 53, -98, -75, -77, -76, -194, -2, -93, -123, -96, + -123, -59, 56, 85, -46, -45, 53, 54, -47, 53, + -45, 43, 43, 127, 127, 127, -96, -87, -39, -59, + 236, 240, 241, -171, -172, -175, -174, -123, -178, -165, + -165, 55, -150, 53, -65, 57, -152, -152, -185, 111, + 57, 56, 57, 56, 57, 56, -54, -134, -134, -54, + -134, -123, -184, 265, -186, -185, -123, -123, -123, -54, + -113, -59, -41, -195, -65, -195, -147, -147, -147, -156, + -147, 177, -147, 177, -195, -195, 19, 19, 19, 19, + -194, -34, 258, -39, 56, 56, 27, -97, 56, -195, + -195, -195, 56, 115, -195, 56, -87, -102, -39, -39, + 55, -39, -194, -194, -194, -195, -91, 57, 56, -147, + -94, -123, -158, 214, 9, -151, 62, -151, 63, 63, + -135, 26, -183, -182, -124, 55, 54, -85, 13, -151, + -185, -65, -65, -65, -65, -65, -91, 62, -65, -65, + 28, -77, 35, -2, -194, -123, -123, -123, -91, -94, + -94, -195, -94, -94, -132, -177, -176, 54, 137, 69, + -174, 57, 56, -159, 133, 28, 132, -68, -152, -152, + 57, 57, -194, 56, 85, -94, -54, -86, 14, 16, + -195, -195, -195, -195, -33, 95, 265, -195, -195, -195, + 9, -75, -2, 115, 57, -195, -195, -195, -58, -176, + -185, -166, 85, 62, 144, -123, -148, 69, 28, 28, + -179, -180, 155, -182, -172, 57, -39, -74, -195, 263, + 50, 266, -98, -195, -123, 63, -54, 62, -195, 56, + -123, -187, 40, 264, 267, 55, -180, 35, -184, 40, + -94, 157, 265, 57, 158, 266, -190, -191, 53, -194, + 267, -191, 53, 10, 9, -65, 154, -189, 145, 140, + 143, 30, -189, -195, -195, 139, 29, 71, } var yyDef = [...]int{ @@ -3092,7 +3071,7 @@ var yyDef = [...]int{ 928, 929, 930, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 0, 0, 0, 0, 0, 626, 0, 621, 0, 621, 621, 621, 0, 263, 397, 651, 652, - 931, 0, 0, 0, 302, 0, 945, 275, 945, 945, + 931, 0, 0, 0, 302, 0, 945, 275, 0, 0, 278, 945, 0, 945, 0, 285, 0, 0, 291, 945, 308, 309, 296, 310, 313, 316, 317, 318, 319, 320, 944, 944, 323, 32, 583, 0, 0, 571, 34, 0, @@ -3121,7 +3100,7 @@ var yyDef = [...]int{ 0, 0, 0, 0, 0, 945, 945, 945, 0, 0, 0, 0, 254, 0, 0, 0, 0, 0, 0, 0, 262, 0, 264, 945, 302, 267, 0, 0, 945, 945, - 945, 0, 945, 945, 274, 946, 947, 276, 277, 279, + 945, 0, 945, 945, 274, 946, 947, 0, 0, 279, 945, 945, 281, 0, 299, 297, 298, 293, 294, 0, 305, 288, 289, 292, 321, 322, 33, 943, 27, 0, 0, 580, 0, 572, 573, 576, 579, 32, 331, 0, @@ -3146,80 +3125,80 @@ var yyDef = [...]int{ 71, 0, 945, 0, 945, 83, 0, 223, 225, 226, 0, 0, 0, 0, 0, 0, 0, 0, 257, 622, 0, 945, 260, 261, 398, 653, 654, 265, 266, 303, - 304, 268, 269, 270, 271, 272, 273, 280, 284, 0, - 302, 0, 0, 286, 287, 0, 0, 584, 0, 0, - 0, 0, 0, 575, 577, 578, 583, 35, 334, 0, - 564, 0, 0, 0, 338, 30, 408, 409, 411, 428, - 0, 430, 432, 349, 345, 0, 554, -2, 418, 419, - 443, 444, 445, 0, 0, 0, 0, 441, 423, 0, - 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, - 464, 465, 468, 529, 530, 0, 466, 467, 476, 0, - 0, 0, 340, 341, 446, 0, 602, 32, 0, 0, - 0, 0, 451, 556, 0, 451, 556, 0, 0, 0, - 551, 548, 0, 0, 553, 0, 515, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 395, 0, 0, - 0, 0, 0, 0, 385, 0, 0, 388, 0, 0, - 0, 0, 379, 0, 0, 400, 865, 381, 0, 383, - 384, 405, 0, 405, 48, 405, 50, 0, 399, 608, - 55, 0, 0, 60, 61, 609, 610, 611, 612, 0, - 84, 210, 212, 215, 216, 217, 88, 89, 90, 0, - 0, 197, 0, 0, 191, 191, 0, 189, 190, 86, - 156, 154, 0, 151, 150, 96, 0, 162, 162, 119, - 120, 165, 0, 165, 165, 165, 0, 0, 113, 114, - 115, 107, 0, 108, 109, 110, 0, 111, 0, 0, - 945, 73, 624, 74, 944, 0, 0, 637, 224, 627, - 628, 629, 630, 631, 632, 633, 634, 635, 636, 0, - 75, 228, 230, 229, 233, 0, 0, 0, 255, 945, - 259, 299, 283, 300, 301, 306, 290, 0, 581, 582, - 0, 574, 28, 0, 619, 620, 565, 566, 352, 429, - 431, 433, 0, 339, 420, 441, 424, 0, 421, 0, - 0, 415, 481, 0, 0, 0, 448, -2, 485, 486, - 0, 0, 0, 0, 0, 522, 0, 0, 523, 0, - 571, 0, 549, 0, 0, 497, 0, 516, 0, 0, - 517, 518, 519, 520, 521, 596, 0, 0, -2, 0, - 0, 405, 604, 0, 355, 374, 376, 0, 371, 386, - 387, 389, 0, 391, 0, 393, 394, 359, 361, 362, - 0, 0, 0, 0, 382, 571, 0, 405, 43, 44, - 0, 58, 59, 0, 0, 65, 166, 167, 0, 213, - 0, 0, 0, 184, 191, 191, 187, 192, 188, 0, - 158, 0, 155, 92, 152, 0, 165, 165, 121, 0, - 122, 123, 124, 0, 140, 0, 0, 0, 0, 646, - 72, 218, 944, 235, 236, 237, 238, 239, 240, 241, - 242, 243, 244, 245, 246, 247, 248, 944, 0, 944, - 638, 639, 640, 641, 0, 78, 0, 0, 0, 0, - 0, 258, 302, 585, 0, 29, 405, 0, 346, 555, - 0, 422, 0, 442, 425, 482, 483, 342, 0, 142, - 142, 534, 142, 146, 537, 142, 539, 142, 542, 0, - 0, 0, 0, 0, 0, 0, 546, 496, 552, 0, - 554, 0, 0, 36, 0, 596, 586, 598, 600, 0, - 32, 0, 592, 0, 366, 571, 0, 0, 368, 375, - 0, 0, 369, 0, 370, 390, 392, 0, 0, 0, - 0, 579, 406, 42, 62, 63, 64, 211, 214, 0, - 193, 142, 196, 185, 186, 0, 160, 0, 157, 143, - 117, 118, 163, 164, 162, 0, 162, 0, 147, 0, - 945, 219, 220, 221, 222, 0, 227, 0, 76, 77, - 0, 0, 232, 256, 282, 567, 353, 484, 426, 487, - 531, 162, 535, 536, 538, 540, 541, 543, 489, 488, - 0, 0, 0, 0, 0, 579, 0, 550, 0, 0, - 0, 37, 0, 601, -2, 0, 0, 0, 52, 0, - 579, 605, 606, 372, 0, 377, 0, 0, 0, 380, - 41, 176, 0, 195, 0, 364, 168, 161, 0, 165, - 141, 165, 0, 0, 70, 0, 79, 80, 0, 0, - 0, 569, 0, 532, 533, 0, 0, 0, 0, 524, - 0, 547, 0, 0, 0, 599, 0, -2, 0, 594, - 593, 367, 40, 0, 0, 402, 0, 0, 400, 175, - 177, 0, 182, 0, 194, 0, 0, 173, 0, 170, - 172, 159, 130, 131, 145, 148, 0, 0, 0, 0, - 234, 31, 0, 0, 490, 492, 491, 493, 0, 0, - 0, 495, 512, 513, 0, 589, 32, 0, 373, 401, - 403, 404, 363, 178, 179, 0, 183, 181, 0, 365, - 91, 0, 169, 171, 0, 250, 0, 81, 82, 75, - 570, 568, 494, 0, 0, 0, 597, -2, 595, 180, - 0, 174, 249, 0, 0, 78, 525, 0, 528, 0, - 251, 0, 231, 526, 0, 0, 0, 198, 0, 0, - 199, 200, 0, 0, 527, 201, 0, 0, 0, 0, - 0, 202, 204, 205, 0, 0, 203, 252, 253, 206, - 207, 208, + 304, 268, 269, 270, 271, 272, 273, 299, 299, 280, + 284, 0, 302, 0, 0, 286, 287, 0, 0, 584, + 0, 0, 0, 0, 0, 575, 577, 578, 583, 35, + 334, 0, 564, 0, 0, 0, 338, 30, 408, 409, + 411, 428, 0, 430, 432, 349, 345, 0, 554, -2, + 418, 419, 443, 444, 445, 0, 0, 0, 0, 441, + 423, 0, 454, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 468, 529, 530, 0, 466, 467, + 476, 0, 0, 0, 340, 341, 446, 0, 602, 32, + 0, 0, 0, 0, 451, 556, 0, 451, 556, 0, + 0, 0, 551, 548, 0, 0, 553, 0, 515, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 395, + 0, 0, 0, 0, 0, 0, 385, 0, 0, 388, + 0, 0, 0, 0, 379, 0, 0, 400, 865, 381, + 0, 383, 384, 405, 0, 405, 48, 405, 50, 0, + 399, 608, 55, 0, 0, 60, 61, 609, 610, 611, + 612, 0, 84, 210, 212, 215, 216, 217, 88, 89, + 90, 0, 0, 197, 0, 0, 191, 191, 0, 189, + 190, 86, 156, 154, 0, 151, 150, 96, 0, 162, + 162, 119, 120, 165, 0, 165, 165, 165, 0, 0, + 113, 114, 115, 107, 0, 108, 109, 110, 0, 111, + 0, 0, 945, 73, 624, 74, 944, 0, 0, 637, + 224, 627, 628, 629, 630, 631, 632, 633, 634, 635, + 636, 0, 75, 228, 230, 229, 233, 0, 0, 0, + 255, 945, 259, 302, 302, 299, 283, 300, 301, 306, + 290, 0, 581, 582, 0, 574, 28, 0, 619, 620, + 565, 566, 352, 429, 431, 433, 0, 339, 420, 441, + 424, 0, 421, 0, 0, 415, 481, 0, 0, 0, + 448, -2, 485, 486, 0, 0, 0, 0, 0, 522, + 0, 0, 523, 0, 571, 0, 549, 0, 0, 497, + 0, 516, 0, 0, 517, 518, 519, 520, 521, 596, + 0, 0, -2, 0, 0, 405, 604, 0, 355, 374, + 376, 0, 371, 386, 387, 389, 0, 391, 0, 393, + 394, 359, 361, 362, 0, 0, 0, 0, 382, 571, + 0, 405, 43, 44, 0, 58, 59, 0, 0, 65, + 166, 167, 0, 213, 0, 0, 0, 184, 191, 191, + 187, 192, 188, 0, 158, 0, 155, 92, 152, 0, + 165, 165, 121, 0, 122, 123, 124, 0, 140, 0, + 0, 0, 0, 646, 72, 218, 944, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 944, 0, 944, 638, 639, 640, 641, 0, 78, + 0, 0, 0, 0, 0, 258, 276, 277, 302, 585, + 0, 29, 405, 0, 346, 555, 0, 422, 0, 442, + 425, 482, 483, 342, 0, 142, 142, 534, 142, 146, + 537, 142, 539, 142, 542, 0, 0, 0, 0, 0, + 0, 0, 546, 496, 552, 0, 554, 0, 0, 36, + 0, 596, 586, 598, 600, 0, 32, 0, 592, 0, + 366, 571, 0, 0, 368, 375, 0, 0, 369, 0, + 370, 390, 392, 0, 0, 0, 0, 579, 406, 42, + 62, 63, 64, 211, 214, 0, 193, 142, 196, 185, + 186, 0, 160, 0, 157, 143, 117, 118, 163, 164, + 162, 0, 162, 0, 147, 0, 945, 219, 220, 221, + 222, 0, 227, 0, 76, 77, 0, 0, 232, 256, + 282, 567, 353, 484, 426, 487, 531, 162, 535, 536, + 538, 540, 541, 543, 489, 488, 0, 0, 0, 0, + 0, 579, 0, 550, 0, 0, 0, 37, 0, 601, + -2, 0, 0, 0, 52, 0, 579, 605, 606, 372, + 0, 377, 0, 0, 0, 380, 41, 176, 0, 195, + 0, 364, 168, 161, 0, 165, 141, 165, 0, 0, + 70, 0, 79, 80, 0, 0, 0, 569, 0, 532, + 533, 0, 0, 0, 0, 524, 0, 547, 0, 0, + 0, 599, 0, -2, 0, 594, 593, 367, 40, 0, + 0, 402, 0, 0, 400, 175, 177, 0, 182, 0, + 194, 0, 0, 173, 0, 170, 172, 159, 130, 131, + 145, 148, 0, 0, 0, 0, 234, 31, 0, 0, + 490, 492, 491, 493, 0, 0, 0, 495, 512, 513, + 0, 589, 32, 0, 373, 401, 403, 404, 363, 178, + 179, 0, 183, 181, 0, 365, 91, 0, 169, 171, + 0, 250, 0, 81, 82, 75, 570, 568, 494, 0, + 0, 0, 597, -2, 595, 180, 0, 174, 249, 0, + 0, 78, 525, 0, 528, 0, 251, 0, 231, 526, + 0, 0, 0, 198, 0, 0, 199, 200, 0, 0, + 527, 201, 0, 0, 0, 0, 0, 202, 204, 205, + 0, 0, 203, 252, 253, 206, 207, 208, } var yyTok1 = [...]int{ @@ -5166,51 +5145,53 @@ yydefault: yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 276: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-6 : yypt+1] //line sql.y:1565 { - yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} + showTablesOpt := &ShowTablesOpt{DbName: yyDollar[5].str, Filter: yyDollar[6].showFilter} + yyVAL.statement = &Show{Type: string(yyDollar[2].bytes), ShowTablesOpt: showTablesOpt, OnTable: yyDollar[4].tableName} } case 277: - yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1569 + yyDollar = yyS[yypt-6 : yypt+1] +//line sql.y:1570 { - yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} + showTablesOpt := &ShowTablesOpt{DbName: yyDollar[5].str, Filter: yyDollar[6].showFilter} + yyVAL.statement = &Show{Type: string(yyDollar[2].bytes), ShowTablesOpt: showTablesOpt, OnTable: yyDollar[4].tableName} } case 278: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1573 +//line sql.y:1575 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 279: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1577 +//line sql.y:1579 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 280: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1581 +//line sql.y:1583 { yyVAL.statement = &Show{Scope: yyDollar[2].str, Type: string(yyDollar[3].bytes)} } case 281: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1585 +//line sql.y:1587 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 282: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1589 +//line sql.y:1591 { showTablesOpt := &ShowTablesOpt{Full: yyDollar[2].str, DbName: yyDollar[6].str, Filter: yyDollar[7].showFilter} yyVAL.statement = &Show{Type: string(yyDollar[3].str), ShowTablesOpt: showTablesOpt, OnTable: yyDollar[5].tableName} } case 283: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1594 +//line sql.y:1596 { // this is ugly, but I couldn't find a better way for now if yyDollar[3].str == "processlist" { @@ -5222,19 +5203,19 @@ yydefault: } case 284: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1604 +//line sql.y:1606 { yyVAL.statement = &Show{Scope: yyDollar[2].str, Type: string(yyDollar[3].bytes)} } case 285: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1608 +//line sql.y:1610 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 286: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1612 +//line sql.y:1614 { // Cannot dereference $4 directly, or else the parser stackcannot be pooled. See yyParsePooled showCollationFilterOpt := yyDollar[4].expr @@ -5242,429 +5223,429 @@ yydefault: } case 287: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:1618 +//line sql.y:1620 { showTablesOpt := &ShowTablesOpt{Filter: yyDollar[4].showFilter} yyVAL.statement = &Show{Scope: string(yyDollar[2].bytes), Type: string(yyDollar[3].bytes), ShowTablesOpt: showTablesOpt} } case 288: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1623 +//line sql.y:1625 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 289: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1627 +//line sql.y:1629 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes)} } case 290: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1631 +//line sql.y:1633 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes) + " " + string(yyDollar[3].bytes), OnTable: yyDollar[5].tableName} } case 291: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1635 +//line sql.y:1637 { yyVAL.statement = &Show{Type: string(yyDollar[2].bytes)} } case 292: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1649 +//line sql.y:1651 { yyVAL.statement = &Show{Type: string(yyDollar[2].colIdent.String())} } case 293: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1655 +//line sql.y:1657 { yyVAL.str = string(yyDollar[1].bytes) } case 294: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1659 +//line sql.y:1661 { yyVAL.str = string(yyDollar[1].bytes) } case 295: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1665 +//line sql.y:1667 { yyVAL.str = "" } case 296: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1669 +//line sql.y:1671 { yyVAL.str = "full " } case 297: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1675 +//line sql.y:1677 { yyVAL.str = string(yyDollar[1].bytes) } case 298: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1679 +//line sql.y:1681 { yyVAL.str = string(yyDollar[1].bytes) } case 299: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1685 +//line sql.y:1687 { yyVAL.str = "" } case 300: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1689 +//line sql.y:1691 { yyVAL.str = yyDollar[2].tableIdent.v } case 301: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1693 +//line sql.y:1695 { yyVAL.str = yyDollar[2].tableIdent.v } case 302: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1699 +//line sql.y:1701 { yyVAL.showFilter = nil } case 303: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1703 +//line sql.y:1705 { yyVAL.showFilter = &ShowFilter{Like: string(yyDollar[2].bytes)} } case 304: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1707 +//line sql.y:1709 { yyVAL.showFilter = &ShowFilter{Filter: yyDollar[2].expr} } case 305: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1713 +//line sql.y:1715 { yyVAL.showFilter = nil } case 306: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1717 +//line sql.y:1719 { yyVAL.showFilter = &ShowFilter{Like: string(yyDollar[2].bytes)} } case 307: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1723 +//line sql.y:1725 { yyVAL.str = "" } case 308: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1727 +//line sql.y:1729 { yyVAL.str = SessionStr } case 309: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1731 +//line sql.y:1733 { yyVAL.str = GlobalStr } case 310: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1737 +//line sql.y:1739 { yyVAL.statement = &Use{DBName: yyDollar[2].tableIdent} } case 311: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1741 +//line sql.y:1743 { yyVAL.statement = &Use{DBName: TableIdent{v: ""}} } case 312: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1747 +//line sql.y:1749 { yyVAL.statement = &Begin{} } case 313: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1751 +//line sql.y:1753 { yyVAL.statement = &Begin{} } case 314: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1757 +//line sql.y:1759 { yyVAL.statement = &Commit{} } case 315: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1763 +//line sql.y:1765 { yyVAL.statement = &Rollback{} } case 316: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1769 +//line sql.y:1771 { yyVAL.statement = &OtherRead{} } case 317: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1773 +//line sql.y:1775 { yyVAL.statement = &OtherRead{} } case 318: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1777 +//line sql.y:1779 { yyVAL.statement = &OtherRead{} } case 319: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1781 +//line sql.y:1783 { yyVAL.statement = &OtherAdmin{} } case 320: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1785 +//line sql.y:1787 { yyVAL.statement = &OtherAdmin{} } case 321: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1789 +//line sql.y:1791 { yyVAL.statement = &OtherAdmin{} } case 322: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1793 +//line sql.y:1795 { yyVAL.statement = &OtherAdmin{} } case 323: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1799 +//line sql.y:1801 { yyVAL.statement = &DDL{Action: FlushStr} } case 324: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1803 +//line sql.y:1805 { setAllowComments(yylex, true) } case 325: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1807 +//line sql.y:1809 { yyVAL.bytes2 = yyDollar[2].bytes2 setAllowComments(yylex, false) } case 326: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1813 +//line sql.y:1815 { yyVAL.bytes2 = nil } case 327: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1817 +//line sql.y:1819 { yyVAL.bytes2 = append(yyDollar[1].bytes2, yyDollar[2].bytes) } case 328: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1823 +//line sql.y:1825 { yyVAL.str = UnionStr } case 329: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1827 +//line sql.y:1829 { yyVAL.str = UnionAllStr } case 330: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1831 +//line sql.y:1833 { yyVAL.str = UnionDistinctStr } case 331: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1836 +//line sql.y:1838 { yyVAL.str = "" } case 332: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1840 +//line sql.y:1842 { yyVAL.str = SQLNoCacheStr } case 333: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1844 +//line sql.y:1846 { yyVAL.str = SQLCacheStr } case 334: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1849 +//line sql.y:1851 { yyVAL.str = "" } case 335: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1853 +//line sql.y:1855 { yyVAL.str = DistinctStr } case 336: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1857 +//line sql.y:1859 { yyVAL.str = DistinctStr } case 337: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1862 +//line sql.y:1864 { yyVAL.str = "" } case 338: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1866 +//line sql.y:1868 { yyVAL.str = StraightJoinHint } case 339: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1871 +//line sql.y:1873 { yyVAL.selectExprs = nil } case 340: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1875 +//line sql.y:1877 { yyVAL.selectExprs = yyDollar[1].selectExprs } case 341: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1881 +//line sql.y:1883 { yyVAL.selectExprs = SelectExprs{yyDollar[1].selectExpr} } case 342: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1885 +//line sql.y:1887 { yyVAL.selectExprs = append(yyVAL.selectExprs, yyDollar[3].selectExpr) } case 343: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1891 +//line sql.y:1893 { yyVAL.selectExpr = &StarExpr{} } case 344: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1895 +//line sql.y:1897 { yyVAL.selectExpr = &AliasedExpr{Expr: yyDollar[1].expr, As: yyDollar[2].colIdent} } case 345: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1899 +//line sql.y:1901 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Name: yyDollar[1].tableIdent}} } case 346: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:1903 +//line sql.y:1905 { yyVAL.selectExpr = &StarExpr{TableName: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}} } case 347: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1908 +//line sql.y:1910 { yyVAL.colIdent = ColIdent{} } case 348: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1912 +//line sql.y:1914 { yyVAL.colIdent = yyDollar[1].colIdent } case 349: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1916 +//line sql.y:1918 { yyVAL.colIdent = yyDollar[2].colIdent } case 351: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1923 +//line sql.y:1925 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 352: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:1928 +//line sql.y:1930 { yyVAL.tableExprs = TableExprs{&AliasedTableExpr{Expr: TableName{Name: NewTableIdent("dual")}}} } case 353: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:1932 +//line sql.y:1934 { yyVAL.tableExprs = yyDollar[2].tableExprs } case 354: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1938 +//line sql.y:1940 { yyVAL.tableExprs = TableExprs{yyDollar[1].tableExpr} } case 355: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1942 +//line sql.y:1944 { yyVAL.tableExprs = append(yyVAL.tableExprs, yyDollar[3].tableExpr) } case 358: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1952 +//line sql.y:1954 { yyVAL.tableExpr = yyDollar[1].aliasedTableName } case 359: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1956 +//line sql.y:1958 { yyVAL.tableExpr = &AliasedTableExpr{Expr: yyDollar[1].subquery, As: yyDollar[3].tableIdent} } case 360: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1960 +//line sql.y:1962 { // missed alias for subquery yylex.Error("Every derived table must have its own alias") @@ -5672,199 +5653,199 @@ yydefault: } case 361: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1966 +//line sql.y:1968 { yyVAL.tableExpr = &ParenTableExpr{Exprs: yyDollar[2].tableExprs} } case 362: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1972 +//line sql.y:1974 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, As: yyDollar[2].tableIdent, Hints: yyDollar[3].indexHints} } case 363: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:1976 +//line sql.y:1978 { yyVAL.aliasedTableName = &AliasedTableExpr{Expr: yyDollar[1].tableName, Partitions: yyDollar[4].partitions, As: yyDollar[6].tableIdent, Hints: yyDollar[7].indexHints} } case 364: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1982 +//line sql.y:1984 { yyVAL.columns = Columns{yyDollar[1].colIdent} } case 365: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1986 +//line sql.y:1988 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } case 366: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:1992 +//line sql.y:1994 { yyVAL.partitions = Partitions{yyDollar[1].colIdent} } case 367: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:1996 +//line sql.y:1998 { yyVAL.partitions = append(yyVAL.partitions, yyDollar[3].colIdent) } case 368: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2009 +//line sql.y:2011 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 369: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2013 +//line sql.y:2015 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 370: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2017 +//line sql.y:2019 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr, Condition: yyDollar[4].joinCondition} } case 371: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2021 +//line sql.y:2023 { yyVAL.tableExpr = &JoinTableExpr{LeftExpr: yyDollar[1].tableExpr, Join: yyDollar[2].str, RightExpr: yyDollar[3].tableExpr} } case 372: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2027 +//line sql.y:2029 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } case 373: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2029 +//line sql.y:2031 { yyVAL.joinCondition = JoinCondition{Using: yyDollar[3].columns} } case 374: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2033 +//line sql.y:2035 { yyVAL.joinCondition = JoinCondition{} } case 375: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2035 +//line sql.y:2037 { yyVAL.joinCondition = yyDollar[1].joinCondition } case 376: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2039 +//line sql.y:2041 { yyVAL.joinCondition = JoinCondition{} } case 377: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2041 +//line sql.y:2043 { yyVAL.joinCondition = JoinCondition{On: yyDollar[2].expr} } case 378: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2044 +//line sql.y:2046 { yyVAL.empty = struct{}{} } case 379: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2046 +//line sql.y:2048 { yyVAL.empty = struct{}{} } case 380: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2049 +//line sql.y:2051 { yyVAL.tableIdent = NewTableIdent("") } case 381: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2053 +//line sql.y:2055 { yyVAL.tableIdent = yyDollar[1].tableIdent } case 382: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2057 +//line sql.y:2059 { yyVAL.tableIdent = yyDollar[2].tableIdent } case 384: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2064 +//line sql.y:2066 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 385: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2070 +//line sql.y:2072 { yyVAL.str = JoinStr } case 386: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2074 +//line sql.y:2076 { yyVAL.str = JoinStr } case 387: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2078 +//line sql.y:2080 { yyVAL.str = JoinStr } case 388: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2084 +//line sql.y:2086 { yyVAL.str = StraightJoinStr } case 389: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2090 +//line sql.y:2092 { yyVAL.str = LeftJoinStr } case 390: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2094 +//line sql.y:2096 { yyVAL.str = LeftJoinStr } case 391: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2098 +//line sql.y:2100 { yyVAL.str = RightJoinStr } case 392: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2102 +//line sql.y:2104 { yyVAL.str = RightJoinStr } case 393: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2108 +//line sql.y:2110 { yyVAL.str = NaturalJoinStr } case 394: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2112 +//line sql.y:2114 { if yyDollar[2].str == LeftJoinStr { yyVAL.str = NaturalLeftJoinStr @@ -5874,469 +5855,469 @@ yydefault: } case 395: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2122 +//line sql.y:2124 { yyVAL.tableName = yyDollar[2].tableName } case 396: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2126 +//line sql.y:2128 { yyVAL.tableName = yyDollar[1].tableName } case 397: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2132 +//line sql.y:2134 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } case 398: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2136 +//line sql.y:2138 { yyVAL.tableName = TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent} } case 399: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2142 +//line sql.y:2144 { yyVAL.tableName = TableName{Name: yyDollar[1].tableIdent} } case 400: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2147 +//line sql.y:2149 { yyVAL.indexHints = nil } case 401: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2151 +//line sql.y:2153 { yyVAL.indexHints = &IndexHints{Type: UseStr, Indexes: yyDollar[4].columns} } case 402: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2155 +//line sql.y:2157 { yyVAL.indexHints = &IndexHints{Type: UseStr} } case 403: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2159 +//line sql.y:2161 { yyVAL.indexHints = &IndexHints{Type: IgnoreStr, Indexes: yyDollar[4].columns} } case 404: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2163 +//line sql.y:2165 { yyVAL.indexHints = &IndexHints{Type: ForceStr, Indexes: yyDollar[4].columns} } case 405: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2168 +//line sql.y:2170 { yyVAL.expr = nil } case 406: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2172 +//line sql.y:2174 { yyVAL.expr = yyDollar[2].expr } case 407: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2178 +//line sql.y:2180 { yyVAL.expr = yyDollar[1].expr } case 408: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2182 +//line sql.y:2184 { yyVAL.expr = &AndExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 409: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2186 +//line sql.y:2188 { yyVAL.expr = &OrExpr{Left: yyDollar[1].expr, Right: yyDollar[3].expr} } case 410: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2190 +//line sql.y:2192 { yyVAL.expr = &NotExpr{Expr: yyDollar[2].expr} } case 411: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2194 +//line sql.y:2196 { yyVAL.expr = &IsExpr{Operator: yyDollar[3].str, Expr: yyDollar[1].expr} } case 412: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2198 +//line sql.y:2200 { yyVAL.expr = yyDollar[1].expr } case 413: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2202 +//line sql.y:2204 { yyVAL.expr = &Default{ColName: yyDollar[2].str} } case 414: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2208 +//line sql.y:2210 { yyVAL.str = "" } case 415: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2212 +//line sql.y:2214 { yyVAL.str = string(yyDollar[2].colIdent.String()) } case 416: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2218 +//line sql.y:2220 { yyVAL.boolVal = BoolVal(true) } case 417: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2222 +//line sql.y:2224 { yyVAL.boolVal = BoolVal(false) } case 418: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2228 +//line sql.y:2230 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: yyDollar[2].str, Right: yyDollar[3].expr} } case 419: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2232 +//line sql.y:2234 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: InStr, Right: yyDollar[3].colTuple} } case 420: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2236 +//line sql.y:2238 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotInStr, Right: yyDollar[4].colTuple} } case 421: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2240 +//line sql.y:2242 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: LikeStr, Right: yyDollar[3].expr, Escape: yyDollar[4].expr} } case 422: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2244 +//line sql.y:2246 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotLikeStr, Right: yyDollar[4].expr, Escape: yyDollar[5].expr} } case 423: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2248 +//line sql.y:2250 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: RegexpStr, Right: yyDollar[3].expr} } case 424: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2252 +//line sql.y:2254 { yyVAL.expr = &ComparisonExpr{Left: yyDollar[1].expr, Operator: NotRegexpStr, Right: yyDollar[4].expr} } case 425: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2256 +//line sql.y:2258 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: BetweenStr, From: yyDollar[3].expr, To: yyDollar[5].expr} } case 426: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2260 +//line sql.y:2262 { yyVAL.expr = &RangeCond{Left: yyDollar[1].expr, Operator: NotBetweenStr, From: yyDollar[4].expr, To: yyDollar[6].expr} } case 427: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2264 +//line sql.y:2266 { yyVAL.expr = &ExistsExpr{Subquery: yyDollar[2].subquery} } case 428: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2270 +//line sql.y:2272 { yyVAL.str = IsNullStr } case 429: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2274 +//line sql.y:2276 { yyVAL.str = IsNotNullStr } case 430: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2278 +//line sql.y:2280 { yyVAL.str = IsTrueStr } case 431: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2282 +//line sql.y:2284 { yyVAL.str = IsNotTrueStr } case 432: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2286 +//line sql.y:2288 { yyVAL.str = IsFalseStr } case 433: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2290 +//line sql.y:2292 { yyVAL.str = IsNotFalseStr } case 434: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2296 +//line sql.y:2298 { yyVAL.str = EqualStr } case 435: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2300 +//line sql.y:2302 { yyVAL.str = LessThanStr } case 436: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2304 +//line sql.y:2306 { yyVAL.str = GreaterThanStr } case 437: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2308 +//line sql.y:2310 { yyVAL.str = LessEqualStr } case 438: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2312 +//line sql.y:2314 { yyVAL.str = GreaterEqualStr } case 439: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2316 +//line sql.y:2318 { yyVAL.str = NotEqualStr } case 440: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2320 +//line sql.y:2322 { yyVAL.str = NullSafeEqualStr } case 441: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2325 +//line sql.y:2327 { yyVAL.expr = nil } case 442: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2329 +//line sql.y:2331 { yyVAL.expr = yyDollar[2].expr } case 443: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2335 +//line sql.y:2337 { yyVAL.colTuple = yyDollar[1].valTuple } case 444: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2339 +//line sql.y:2341 { yyVAL.colTuple = yyDollar[1].subquery } case 445: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2343 +//line sql.y:2345 { yyVAL.colTuple = ListArg(yyDollar[1].bytes) } case 446: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2349 +//line sql.y:2351 { yyVAL.subquery = &Subquery{yyDollar[2].selStmt} } case 447: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2355 +//line sql.y:2357 { yyVAL.exprs = Exprs{yyDollar[1].expr} } case 448: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2359 +//line sql.y:2361 { yyVAL.exprs = append(yyDollar[1].exprs, yyDollar[3].expr) } case 449: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2365 +//line sql.y:2367 { yyVAL.expr = yyDollar[1].expr } case 450: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2369 +//line sql.y:2371 { yyVAL.expr = yyDollar[1].boolVal } case 451: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2373 +//line sql.y:2375 { yyVAL.expr = yyDollar[1].colName } case 452: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2377 +//line sql.y:2379 { yyVAL.expr = yyDollar[1].expr } case 453: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2381 +//line sql.y:2383 { yyVAL.expr = yyDollar[1].subquery } case 454: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2385 +//line sql.y:2387 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitAndStr, Right: yyDollar[3].expr} } case 455: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2389 +//line sql.y:2391 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitOrStr, Right: yyDollar[3].expr} } case 456: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2393 +//line sql.y:2395 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: BitXorStr, Right: yyDollar[3].expr} } case 457: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2397 +//line sql.y:2399 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: PlusStr, Right: yyDollar[3].expr} } case 458: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2401 +//line sql.y:2403 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MinusStr, Right: yyDollar[3].expr} } case 459: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2405 +//line sql.y:2407 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: MultStr, Right: yyDollar[3].expr} } case 460: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2409 +//line sql.y:2411 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: DivStr, Right: yyDollar[3].expr} } case 461: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2413 +//line sql.y:2415 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: IntDivStr, Right: yyDollar[3].expr} } case 462: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2417 +//line sql.y:2419 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 463: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2421 +//line sql.y:2423 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ModStr, Right: yyDollar[3].expr} } case 464: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2425 +//line sql.y:2427 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftLeftStr, Right: yyDollar[3].expr} } case 465: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2429 +//line sql.y:2431 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].expr, Operator: ShiftRightStr, Right: yyDollar[3].expr} } case 466: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2433 +//line sql.y:2435 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONExtractOp, Right: yyDollar[3].expr} } case 467: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2437 +//line sql.y:2439 { yyVAL.expr = &BinaryExpr{Left: yyDollar[1].colName, Operator: JSONUnquoteExtractOp, Right: yyDollar[3].expr} } case 468: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2441 +//line sql.y:2443 { yyVAL.expr = &CollateExpr{Expr: yyDollar[1].expr, Charset: yyDollar[3].str} } case 469: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2445 +//line sql.y:2447 { yyVAL.expr = &UnaryExpr{Operator: BinaryStr, Expr: yyDollar[2].expr} } case 470: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2449 +//line sql.y:2451 { yyVAL.expr = &UnaryExpr{Operator: UBinaryStr, Expr: yyDollar[2].expr} } case 471: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2453 +//line sql.y:2455 { yyVAL.expr = &UnaryExpr{Operator: Utf8mb4Str, Expr: yyDollar[2].expr} } case 472: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2457 +//line sql.y:2459 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { yyVAL.expr = num @@ -6346,7 +6327,7 @@ yydefault: } case 473: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2465 +//line sql.y:2467 { if num, ok := yyDollar[2].expr.(*SQLVal); ok && num.Type == IntVal { // Handle double negative @@ -6362,19 +6343,19 @@ yydefault: } case 474: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2479 +//line sql.y:2481 { yyVAL.expr = &UnaryExpr{Operator: TildaStr, Expr: yyDollar[2].expr} } case 475: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2483 +//line sql.y:2485 { yyVAL.expr = &UnaryExpr{Operator: BangStr, Expr: yyDollar[2].expr} } case 476: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2487 +//line sql.y:2489 { // This rule prevents the usage of INTERVAL // as a function. If support is needed for that, @@ -6384,325 +6365,325 @@ yydefault: } case 481: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2505 +//line sql.y:2507 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Exprs: yyDollar[3].selectExprs} } case 482: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2509 +//line sql.y:2511 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } case 483: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2513 +//line sql.y:2515 { yyVAL.expr = &FuncExpr{Name: yyDollar[1].colIdent, Distinct: true, Exprs: yyDollar[4].selectExprs} } case 484: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2517 +//line sql.y:2519 { yyVAL.expr = &FuncExpr{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].colIdent, Exprs: yyDollar[5].selectExprs} } case 485: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2527 +//line sql.y:2529 { yyVAL.expr = &FuncExpr{Name: NewColIdent("left"), Exprs: yyDollar[3].selectExprs} } case 486: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2531 +//line sql.y:2533 { yyVAL.expr = &FuncExpr{Name: NewColIdent("right"), Exprs: yyDollar[3].selectExprs} } case 487: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2535 +//line sql.y:2537 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } case 488: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2539 +//line sql.y:2541 { yyVAL.expr = &ConvertExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].convertType} } case 489: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:2543 +//line sql.y:2545 { yyVAL.expr = &ConvertUsingExpr{Expr: yyDollar[3].expr, Type: yyDollar[5].str} } case 490: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2547 +//line sql.y:2549 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } case 491: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2551 +//line sql.y:2553 { yyVAL.expr = &SubstrExpr{Name: yyDollar[3].colName, From: yyDollar[5].expr, To: yyDollar[7].expr} } case 492: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2555 +//line sql.y:2557 { yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } case 493: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2559 +//line sql.y:2561 { yyVAL.expr = &SubstrExpr{StrVal: NewStrVal(yyDollar[3].bytes), From: yyDollar[5].expr, To: yyDollar[7].expr} } case 494: yyDollar = yyS[yypt-9 : yypt+1] -//line sql.y:2563 +//line sql.y:2565 { yyVAL.expr = &MatchExpr{Columns: yyDollar[3].selectExprs, Expr: yyDollar[7].expr, Option: yyDollar[8].str} } case 495: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2567 +//line sql.y:2569 { yyVAL.expr = &GroupConcatExpr{Distinct: yyDollar[3].str, Exprs: yyDollar[4].selectExprs, OrderBy: yyDollar[5].orderBy, Separator: yyDollar[6].str, Limit: yyDollar[7].limit} } case 496: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2571 +//line sql.y:2573 { yyVAL.expr = &CaseExpr{Expr: yyDollar[2].expr, Whens: yyDollar[3].whens, Else: yyDollar[4].expr} } case 497: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2575 +//line sql.y:2577 { yyVAL.expr = &ValuesFuncExpr{Name: yyDollar[3].colName} } case 498: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2585 +//line sql.y:2587 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_timestamp")} } case 499: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2589 +//line sql.y:2591 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_timestamp")} } case 500: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2593 +//line sql.y:2595 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_time")} } case 501: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2598 +//line sql.y:2600 { yyVAL.expr = &FuncExpr{Name: NewColIdent("utc_date")} } case 502: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2603 +//line sql.y:2605 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtime")} } case 503: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2608 +//line sql.y:2610 { yyVAL.expr = &FuncExpr{Name: NewColIdent("localtimestamp")} } case 504: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2614 +//line sql.y:2616 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_date")} } case 505: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2619 +//line sql.y:2621 { yyVAL.expr = &FuncExpr{Name: NewColIdent("current_time")} } case 506: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2624 +//line sql.y:2626 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_timestamp"), Fsp: yyDollar[2].expr} } case 507: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2628 +//line sql.y:2630 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_timestamp"), Fsp: yyDollar[2].expr} } case 508: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2632 +//line sql.y:2634 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("utc_time"), Fsp: yyDollar[2].expr} } case 509: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2637 +//line sql.y:2639 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtime"), Fsp: yyDollar[2].expr} } case 510: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2642 +//line sql.y:2644 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("localtimestamp"), Fsp: yyDollar[2].expr} } case 511: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2647 +//line sql.y:2649 { yyVAL.expr = &CurTimeFuncExpr{Name: NewColIdent("current_time"), Fsp: yyDollar[2].expr} } case 512: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2651 +//line sql.y:2653 { yyVAL.expr = &TimestampFuncExpr{Name: string("timestampadd"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } case 513: yyDollar = yyS[yypt-8 : yypt+1] -//line sql.y:2655 +//line sql.y:2657 { yyVAL.expr = &TimestampFuncExpr{Name: string("timestampdiff"), Unit: yyDollar[3].colIdent.String(), Expr1: yyDollar[5].expr, Expr2: yyDollar[7].expr} } case 516: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2665 +//line sql.y:2667 { yyVAL.expr = yyDollar[2].expr } case 517: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2675 +//line sql.y:2677 { yyVAL.expr = &FuncExpr{Name: NewColIdent("if"), Exprs: yyDollar[3].selectExprs} } case 518: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2679 +//line sql.y:2681 { yyVAL.expr = &FuncExpr{Name: NewColIdent("database"), Exprs: yyDollar[3].selectExprs} } case 519: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2683 +//line sql.y:2685 { yyVAL.expr = &FuncExpr{Name: NewColIdent("schema"), Exprs: yyDollar[3].selectExprs} } case 520: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2687 +//line sql.y:2689 { yyVAL.expr = &FuncExpr{Name: NewColIdent("mod"), Exprs: yyDollar[3].selectExprs} } case 521: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2691 +//line sql.y:2693 { yyVAL.expr = &FuncExpr{Name: NewColIdent("replace"), Exprs: yyDollar[3].selectExprs} } case 522: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2695 +//line sql.y:2697 { yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 523: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2699 +//line sql.y:2701 { yyVAL.expr = &FuncExpr{Name: NewColIdent("substr"), Exprs: yyDollar[3].selectExprs} } case 524: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2705 +//line sql.y:2707 { yyVAL.str = "" } case 525: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2709 +//line sql.y:2711 { yyVAL.str = BooleanModeStr } case 526: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2713 +//line sql.y:2715 { yyVAL.str = NaturalLanguageModeStr } case 527: yyDollar = yyS[yypt-7 : yypt+1] -//line sql.y:2717 +//line sql.y:2719 { yyVAL.str = NaturalLanguageModeWithQueryExpansionStr } case 528: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2721 +//line sql.y:2723 { yyVAL.str = QueryExpansionStr } case 529: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2727 +//line sql.y:2729 { yyVAL.str = string(yyDollar[1].colIdent.String()) } case 530: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2731 +//line sql.y:2733 { yyVAL.str = string(yyDollar[1].bytes) } case 531: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2737 +//line sql.y:2739 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 532: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2741 +//line sql.y:2743 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: yyDollar[3].str, Operator: CharacterSetStr} } case 533: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2745 +//line sql.y:2747 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal, Charset: string(yyDollar[3].colIdent.String())} } case 534: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2749 +//line sql.y:2751 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 535: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2753 +//line sql.y:2755 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 536: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2757 +//line sql.y:2759 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} yyVAL.convertType.Length = yyDollar[2].LengthScaleOption.Length @@ -6710,169 +6691,169 @@ yydefault: } case 537: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2763 +//line sql.y:2765 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 538: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2767 +//line sql.y:2769 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 539: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2771 +//line sql.y:2773 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 540: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2775 +//line sql.y:2777 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 541: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2779 +//line sql.y:2781 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes), Length: yyDollar[2].sqlVal} } case 542: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2783 +//line sql.y:2785 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 543: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2787 +//line sql.y:2789 { yyVAL.convertType = &ConvertType{Type: string(yyDollar[1].bytes)} } case 544: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2792 +//line sql.y:2794 { yyVAL.expr = nil } case 545: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2796 +//line sql.y:2798 { yyVAL.expr = yyDollar[1].expr } case 546: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2801 +//line sql.y:2803 { yyVAL.str = string("") } case 547: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2805 +//line sql.y:2807 { yyVAL.str = " separator '" + string(yyDollar[2].bytes) + "'" } case 548: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2811 +//line sql.y:2813 { yyVAL.whens = []*When{yyDollar[1].when} } case 549: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2815 +//line sql.y:2817 { yyVAL.whens = append(yyDollar[1].whens, yyDollar[2].when) } case 550: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2821 +//line sql.y:2823 { yyVAL.when = &When{Cond: yyDollar[2].expr, Val: yyDollar[4].expr} } case 551: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2826 +//line sql.y:2828 { yyVAL.expr = nil } case 552: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2830 +//line sql.y:2832 { yyVAL.expr = yyDollar[2].expr } case 553: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2836 +//line sql.y:2838 { yyVAL.colName = &ColName{Name: yyDollar[1].colIdent} } case 554: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2840 +//line sql.y:2842 { yyVAL.colName = &ColName{Qualifier: TableName{Name: yyDollar[1].tableIdent}, Name: yyDollar[3].colIdent} } case 555: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:2844 +//line sql.y:2846 { yyVAL.colName = &ColName{Qualifier: TableName{Qualifier: yyDollar[1].tableIdent, Name: yyDollar[3].tableIdent}, Name: yyDollar[5].colIdent} } case 556: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2850 +//line sql.y:2852 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } case 557: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2854 +//line sql.y:2856 { yyVAL.expr = NewHexVal(yyDollar[1].bytes) } case 558: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2858 +//line sql.y:2860 { yyVAL.expr = NewBitVal(yyDollar[1].bytes) } case 559: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2862 +//line sql.y:2864 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } case 560: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2866 +//line sql.y:2868 { yyVAL.expr = NewFloatVal(yyDollar[1].bytes) } case 561: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2870 +//line sql.y:2872 { yyVAL.expr = NewHexNum(yyDollar[1].bytes) } case 562: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2874 +//line sql.y:2876 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } case 563: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2878 +//line sql.y:2880 { yyVAL.expr = &NullVal{} } case 564: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2884 +//line sql.y:2886 { // TODO(sougou): Deprecate this construct. if yyDollar[1].colIdent.Lowered() != "value" { @@ -6883,237 +6864,237 @@ yydefault: } case 565: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2893 +//line sql.y:2895 { yyVAL.expr = NewIntVal(yyDollar[1].bytes) } case 566: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2897 +//line sql.y:2899 { yyVAL.expr = NewValArg(yyDollar[1].bytes) } case 567: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2902 +//line sql.y:2904 { yyVAL.exprs = nil } case 568: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2906 +//line sql.y:2908 { yyVAL.exprs = yyDollar[3].exprs } case 569: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2911 +//line sql.y:2913 { yyVAL.expr = nil } case 570: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2915 +//line sql.y:2917 { yyVAL.expr = yyDollar[2].expr } case 571: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2920 +//line sql.y:2922 { yyVAL.orderBy = nil } case 572: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2924 +//line sql.y:2926 { yyVAL.orderBy = yyDollar[3].orderBy } case 573: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2930 +//line sql.y:2932 { yyVAL.orderBy = OrderBy{yyDollar[1].order} } case 574: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:2934 +//line sql.y:2936 { yyVAL.orderBy = append(yyDollar[1].orderBy, yyDollar[3].order) } case 575: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2940 +//line sql.y:2942 { yyVAL.order = &Order{Expr: yyDollar[1].expr, Direction: yyDollar[2].str} } case 576: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2945 +//line sql.y:2947 { yyVAL.str = AscScr } case 577: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2949 +//line sql.y:2951 { yyVAL.str = AscScr } case 578: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:2953 +//line sql.y:2955 { yyVAL.str = DescScr } case 579: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2958 +//line sql.y:2960 { yyVAL.limit = nil } case 580: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2962 +//line sql.y:2964 { yyVAL.limit = &Limit{Rowcount: yyDollar[2].expr} } case 581: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2966 +//line sql.y:2968 { yyVAL.limit = &Limit{Offset: yyDollar[2].expr, Rowcount: yyDollar[4].expr} } case 582: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2970 +//line sql.y:2972 { yyVAL.limit = &Limit{Offset: yyDollar[4].expr, Rowcount: yyDollar[2].expr} } case 583: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:2975 +//line sql.y:2977 { yyVAL.str = "" } case 584: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2979 +//line sql.y:2981 { yyVAL.str = ForUpdateStr } case 585: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:2983 +//line sql.y:2985 { yyVAL.str = ShareModeStr } case 586: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:2996 +//line sql.y:2998 { yyVAL.ins = &Insert{Rows: yyDollar[2].values} } case 587: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3000 +//line sql.y:3002 { yyVAL.ins = &Insert{Rows: yyDollar[1].selStmt} } case 588: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3004 +//line sql.y:3006 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Rows: yyDollar[2].selStmt} } case 589: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3009 +//line sql.y:3011 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].values} } case 590: yyDollar = yyS[yypt-4 : yypt+1] -//line sql.y:3013 +//line sql.y:3015 { yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[4].selStmt} } case 591: yyDollar = yyS[yypt-6 : yypt+1] -//line sql.y:3017 +//line sql.y:3019 { // Drop the redundant parenthesis. yyVAL.ins = &Insert{Columns: yyDollar[2].columns, Rows: yyDollar[5].selStmt} } case 592: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3024 +//line sql.y:3026 { yyVAL.columns = Columns{yyDollar[1].colIdent} } case 593: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3028 +//line sql.y:3030 { yyVAL.columns = Columns{yyDollar[3].colIdent} } case 594: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3032 +//line sql.y:3034 { yyVAL.columns = append(yyVAL.columns, yyDollar[3].colIdent) } case 595: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3036 +//line sql.y:3038 { yyVAL.columns = append(yyVAL.columns, yyDollar[5].colIdent) } case 596: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3041 +//line sql.y:3043 { yyVAL.updateExprs = nil } case 597: yyDollar = yyS[yypt-5 : yypt+1] -//line sql.y:3045 +//line sql.y:3047 { yyVAL.updateExprs = yyDollar[5].updateExprs } case 598: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3051 +//line sql.y:3053 { yyVAL.values = Values{yyDollar[1].valTuple} } case 599: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3055 +//line sql.y:3057 { yyVAL.values = append(yyDollar[1].values, yyDollar[3].valTuple) } case 600: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3061 +//line sql.y:3063 { yyVAL.valTuple = yyDollar[1].valTuple } case 601: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3065 +//line sql.y:3067 { yyVAL.valTuple = ValTuple{} } case 602: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3071 +//line sql.y:3073 { yyVAL.valTuple = ValTuple(yyDollar[2].exprs) } case 603: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3077 +//line sql.y:3079 { if len(yyDollar[1].valTuple) == 1 { yyVAL.expr = &ParenExpr{yyDollar[1].valTuple[0]} @@ -7123,277 +7104,277 @@ yydefault: } case 604: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3087 +//line sql.y:3089 { yyVAL.updateExprs = UpdateExprs{yyDollar[1].updateExpr} } case 605: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3091 +//line sql.y:3093 { yyVAL.updateExprs = append(yyDollar[1].updateExprs, yyDollar[3].updateExpr) } case 606: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3097 +//line sql.y:3099 { yyVAL.updateExpr = &UpdateExpr{Name: yyDollar[1].colName, Expr: yyDollar[3].expr} } case 607: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3103 +//line sql.y:3105 { yyVAL.setExprs = SetExprs{yyDollar[1].setExpr} } case 608: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3107 +//line sql.y:3109 { yyVAL.setExprs = append(yyDollar[1].setExprs, yyDollar[3].setExpr) } case 609: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3113 +//line sql.y:3115 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("on"))} } case 610: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3117 +//line sql.y:3119 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: NewStrVal([]byte("off"))} } case 611: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3121 +//line sql.y:3123 { yyVAL.setExpr = &SetExpr{Name: yyDollar[1].colIdent, Expr: yyDollar[3].expr} } case 612: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3125 +//line sql.y:3127 { yyVAL.setExpr = &SetExpr{Name: NewColIdent(string(yyDollar[1].bytes)), Expr: yyDollar[2].expr} } case 614: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3132 +//line sql.y:3134 { yyVAL.bytes = []byte("charset") } case 616: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3139 +//line sql.y:3141 { yyVAL.expr = NewStrVal([]byte(yyDollar[1].colIdent.String())) } case 617: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3143 +//line sql.y:3145 { yyVAL.expr = NewStrVal(yyDollar[1].bytes) } case 618: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3147 +//line sql.y:3149 { yyVAL.expr = &Default{} } case 621: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3156 +//line sql.y:3158 { yyVAL.byt = 0 } case 622: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3158 +//line sql.y:3160 { yyVAL.byt = 1 } case 623: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3161 +//line sql.y:3163 { yyVAL.empty = struct{}{} } case 624: yyDollar = yyS[yypt-3 : yypt+1] -//line sql.y:3163 +//line sql.y:3165 { yyVAL.empty = struct{}{} } case 625: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3166 +//line sql.y:3168 { yyVAL.str = "" } case 626: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3168 +//line sql.y:3170 { yyVAL.str = IgnoreStr } case 627: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3172 +//line sql.y:3174 { yyVAL.empty = struct{}{} } case 628: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3174 +//line sql.y:3176 { yyVAL.empty = struct{}{} } case 629: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3176 +//line sql.y:3178 { yyVAL.empty = struct{}{} } case 630: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3178 +//line sql.y:3180 { yyVAL.empty = struct{}{} } case 631: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3180 +//line sql.y:3182 { yyVAL.empty = struct{}{} } case 632: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3182 +//line sql.y:3184 { yyVAL.empty = struct{}{} } case 633: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3184 +//line sql.y:3186 { yyVAL.empty = struct{}{} } case 634: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3186 +//line sql.y:3188 { yyVAL.empty = struct{}{} } case 635: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3188 +//line sql.y:3190 { yyVAL.empty = struct{}{} } case 636: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3190 +//line sql.y:3192 { yyVAL.empty = struct{}{} } case 637: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3193 +//line sql.y:3195 { yyVAL.empty = struct{}{} } case 638: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3195 +//line sql.y:3197 { yyVAL.empty = struct{}{} } case 639: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3197 +//line sql.y:3199 { yyVAL.empty = struct{}{} } case 640: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3201 +//line sql.y:3203 { yyVAL.empty = struct{}{} } case 641: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3203 +//line sql.y:3205 { yyVAL.empty = struct{}{} } case 642: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3206 +//line sql.y:3208 { yyVAL.empty = struct{}{} } case 643: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3208 +//line sql.y:3210 { yyVAL.empty = struct{}{} } case 644: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3210 +//line sql.y:3212 { yyVAL.empty = struct{}{} } case 645: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3213 +//line sql.y:3215 { yyVAL.colIdent = ColIdent{} } case 646: yyDollar = yyS[yypt-2 : yypt+1] -//line sql.y:3215 +//line sql.y:3217 { yyVAL.colIdent = yyDollar[2].colIdent } case 647: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3219 +//line sql.y:3221 { yyVAL.colIdent = yyDollar[1].colIdent } case 648: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3223 +//line sql.y:3225 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 650: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3230 +//line sql.y:3232 { yyVAL.colIdent = NewColIdent(string(yyDollar[1].bytes)) } case 651: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3236 +//line sql.y:3238 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].colIdent.String())) } case 652: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3240 +//line sql.y:3242 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 654: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3247 +//line sql.y:3249 { yyVAL.tableIdent = NewTableIdent(string(yyDollar[1].bytes)) } case 942: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3560 +//line sql.y:3562 { if incNesting(yylex) { yylex.Error("max nesting level reached") @@ -7402,31 +7383,31 @@ yydefault: } case 943: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3569 +//line sql.y:3571 { decNesting(yylex) } case 944: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3574 +//line sql.y:3576 { skipToEnd(yylex) } case 945: yyDollar = yyS[yypt-0 : yypt+1] -//line sql.y:3579 +//line sql.y:3581 { skipToEnd(yylex) } case 946: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3583 +//line sql.y:3585 { skipToEnd(yylex) } case 947: yyDollar = yyS[yypt-1 : yypt+1] -//line sql.y:3587 +//line sql.y:3589 { skipToEnd(yylex) } diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 55140b62936..e1d5b3eced3 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -1561,13 +1561,15 @@ show_statement: { $$ = &Show{Type: string($2)} } -| SHOW INDEX ddl_skip_to_end +| SHOW INDEX FROM table_name from_database_opt like_or_where_opt { - $$ = &Show{Type: string($2)} + showTablesOpt := &ShowTablesOpt{DbName:$5, Filter:$6} + $$ = &Show{Type: string($2), ShowTablesOpt: showTablesOpt, OnTable: $4} } -| SHOW KEYS ddl_skip_to_end +| SHOW KEYS FROM table_name from_database_opt like_or_where_opt { - $$ = &Show{Type: string($2)} + showTablesOpt := &ShowTablesOpt{DbName:$5, Filter:$6} + $$ = &Show{Type: string($2), ShowTablesOpt: showTablesOpt, OnTable: $4} } | SHOW PLUGINS { diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index ce2fc4196cf..6e2e0a0a965 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -922,6 +922,17 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql break } sql = sqlparser.String(show) + case sqlparser.KeywordString(sqlparser.INDEX), sqlparser.KeywordString(sqlparser.KEYS): + if !show.OnTable.Qualifier.IsEmpty() { + destKeyspace = show.OnTable.Qualifier.String() + show.OnTable.Qualifier = sqlparser.NewTableIdent("") + } else if show.ShowTablesOpt != nil { + destKeyspace = show.ShowTablesOpt.DbName + show.ShowTablesOpt.DbName = "" + } else { + break + } + sql = sqlparser.String(show) case sqlparser.KeywordString(sqlparser.TABLES): if show.ShowTablesOpt != nil && show.ShowTablesOpt.DbName != "" { if destKeyspace == "" { diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 06838eb02f6..7905bdf786d 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -548,6 +548,48 @@ func TestExecutorShow(t *testing.T) { t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) } + // SHOW KEYS with two different syntax + _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show keys from %v.unknown", KsTestUnsharded), nil) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql + wantQuery = "show keys from unknown" + if lastQuery != wantQuery { + t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) + } + + _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show keys from unknown from %v", KsTestUnsharded), nil) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql + wantQuery = "show keys from unknown" + if lastQuery != wantQuery { + t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) + } + + // SHOW INDEX with two different syntax + _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show index from %v.unknown", KsTestUnsharded), nil) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql + wantQuery = "show index from unknown" + if lastQuery != wantQuery { + t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) + } + + _, err = executor.Execute(context.Background(), "TestExecute", session, fmt.Sprintf("show index from unknown from %v", KsTestUnsharded), nil) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + lastQuery = sbclookup.Queries[len(sbclookup.Queries)-1].Sql + wantQuery = "show index from unknown" + if lastQuery != wantQuery { + t.Errorf("Got: %v. Want: %v", lastQuery, wantQuery) + } + // Set desitation keyspace in session session.TargetString = KsTestUnsharded _, err = executor.Execute(context.Background(), "TestExecute", session, "show create table unknown", nil) diff --git a/jira_show.txt b/jira_show.txt new file mode 100644 index 00000000000..e69de29bb2d From 923531b6e54461c2b1b5c575dc8a2a5efbcbf6a1 Mon Sep 17 00:00:00 2001 From: Saif Alharthi Date: Wed, 18 Mar 2020 13:41:30 -0700 Subject: [PATCH 335/825] Delete unused file Signed-off-by: Saif Alharthi --- jira_show.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 jira_show.txt diff --git a/jira_show.txt b/jira_show.txt deleted file mode 100644 index e69de29bb2d..00000000000 From 67989f659c6ecf3c608bec077ef656a1e23a856d Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Thu, 19 Mar 2020 12:20:02 +0100 Subject: [PATCH 336/825] executor: use parser instead of preview and added support for special comments Signed-off-by: Harshit Gangal --- go/vt/sqlparser/parse_test.go | 2 +- go/vt/sqlparser/sql.go | 2 +- go/vt/sqlparser/sql.y | 2 +- go/vt/vtexplain/vtexplain_flaky_test.go | 2 +- go/vt/vtgate/executor.go | 55 ++-- go/vt/vtgate/executor_dml_test.go | 4 +- go/vt/vtgate/executor_test.go | 243 ++++++++++-------- .../planbuilder/testdata/exec_cases.txt | 10 +- 8 files changed, 174 insertions(+), 146 deletions(-) diff --git a/go/vt/sqlparser/parse_test.go b/go/vt/sqlparser/parse_test.go index acd55be20fc..b5f8fa630e3 100644 --- a/go/vt/sqlparser/parse_test.go +++ b/go/vt/sqlparser/parse_test.go @@ -1132,7 +1132,7 @@ var ( output: "alter table a", }, { input: "analyze table a", - output: "alter table a", + output: "otherread", }, { input: "flush tables", output: "flush", diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index 40c3a374383..ec49904aecb 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -5095,7 +5095,7 @@ yydefault: yyDollar = yyS[yypt-3 : yypt+1] //line sql.y:1511 { - yyVAL.statement = &DDL{Action: AlterStr, Table: yyDollar[3].tableName} + yyVAL.statement = &OtherRead{} } case 265: yyDollar = yyS[yypt-4 : yypt+1] diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 55140b62936..a3b33370a3c 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -1509,7 +1509,7 @@ truncate_statement: analyze_statement: ANALYZE TABLE table_name { - $$ = &DDL{Action: AlterStr, Table: $3} + $$ = &OtherRead{} } show_statement: diff --git a/go/vt/vtexplain/vtexplain_flaky_test.go b/go/vt/vtexplain/vtexplain_flaky_test.go index ef35da06890..a6f62fa29ff 100644 --- a/go/vt/vtexplain/vtexplain_flaky_test.go +++ b/go/vt/vtexplain/vtexplain_flaky_test.go @@ -138,7 +138,7 @@ func TestErrors(t *testing.T) { }{ { SQL: "INVALID SQL", - Err: "vtexplain execute error in 'INVALID SQL': unrecognized statement: INVALID SQL", + Err: "vtexplain execute error in 'INVALID SQL': syntax error at position 8 near 'INVALID'", }, { diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index ce2fc4196cf..ddca4b06eca 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -185,6 +185,16 @@ func (e *Executor) execute(ctx context.Context, safeSession *SafeSession, sql st stmtType := sqlparser.Preview(sql) logStats.StmtType = stmtType.String() + stmt, err := sqlparser.Parse(sql) + if err != nil { + // If the DDL statement failed to be properly parsed, fall through anyway + if stmtType == sqlparser.StmtDDL { + stmt = &sqlparser.DDL{} + } else { + return nil, err + } + } + // Mysql warnings are scoped to the current session, but are // cleared when a "non-diagnostic statement" is executed: // https://dev.mysql.com/doc/refman/8.0/en/show-warnings.html @@ -192,14 +202,14 @@ func (e *Executor) execute(ctx context.Context, safeSession *SafeSession, sql st // To emulate this behavior, clear warnings from the session // for all statements _except_ SHOW, so that SHOW WARNINGS // can actually return them. - if stmtType != sqlparser.StmtShow { + if _, isShow := stmt.(*sqlparser.Show); !isShow { safeSession.ClearWarnings() } - switch stmtType { - case sqlparser.StmtSelect: + switch specStmt := stmt.(type) { + case *sqlparser.Select, *sqlparser.Union: return e.handleExec(ctx, safeSession, sql, bindVars, destKeyspace, destTabletType, dest, logStats, stmtType) - case sqlparser.StmtInsert, sqlparser.StmtReplace, sqlparser.StmtUpdate, sqlparser.StmtDelete: + case *sqlparser.Insert, *sqlparser.Update, *sqlparser.Delete: safeSession := safeSession mustCommit := false @@ -236,24 +246,22 @@ func (e *Executor) execute(ctx context.Context, safeSession *SafeSession, sql st logStats.CommitTime = time.Since(commitStart) } return qr, nil - case sqlparser.StmtDDL: + case *sqlparser.DDL: return e.handleDDL(ctx, safeSession, sql, bindVars, dest, destKeyspace, destTabletType, logStats) - case sqlparser.StmtBegin: + case *sqlparser.Begin: return e.handleBegin(ctx, safeSession, sql, bindVars, destTabletType, logStats) - case sqlparser.StmtCommit: + case *sqlparser.Commit: return e.handleCommit(ctx, safeSession, sql, bindVars, logStats) - case sqlparser.StmtRollback: + case *sqlparser.Rollback: return e.handleRollback(ctx, safeSession, sql, bindVars, logStats) - case sqlparser.StmtSet: + case *sqlparser.Set: return e.handleSet(ctx, safeSession, sql, bindVars, logStats) - case sqlparser.StmtShow: + case *sqlparser.Show: return e.handleShow(ctx, safeSession, sql, bindVars, dest, destKeyspace, destTabletType, logStats) - case sqlparser.StmtUse: - return e.handleUse(ctx, safeSession, sql, bindVars) - case sqlparser.StmtOther: + case *sqlparser.Use: + return e.handleUse(ctx, safeSession, sql, bindVars, specStmt) + case *sqlparser.OtherAdmin, *sqlparser.OtherRead: return e.handleOther(ctx, safeSession, sql, bindVars, dest, destKeyspace, destTabletType, logStats) - case sqlparser.StmtComment: - return e.handleComment(sql) } return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unrecognized statement: %s", sql) } @@ -1143,16 +1151,7 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql return e.handleOther(ctx, safeSession, sql, bindVars, dest, destKeyspace, destTabletType, logStats) } -func (e *Executor) handleUse(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { - stmt, err := sqlparser.Parse(sql) - if err != nil { - return nil, err - } - use, ok := stmt.(*sqlparser.Use) - if !ok { - // This code is unreachable. - return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unrecognized USE statement: %v", sql) - } +func (e *Executor) handleUse(ctx context.Context, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, use *sqlparser.Use) (*sqltypes.Result, error) { destKeyspace, destTabletType, _, err := e.ParseDestinationTarget(use.DBName.String()) if err != nil { return nil, err @@ -1199,12 +1198,6 @@ func (e *Executor) handleOther(ctx context.Context, safeSession *SafeSession, sq return result, err } -func (e *Executor) handleComment(sql string) (*sqltypes.Result, error) { - _, _ = sqlparser.ExtractMysqlComment(sql) - // Not sure if this is a good idea. - return &sqltypes.Result{}, nil -} - // StreamExecute executes a streaming query. func (e *Executor) StreamExecute(ctx context.Context, method string, safeSession *SafeSession, sql string, bindVars map[string]*querypb.BindVariable, target querypb.Target, callback func(*sqltypes.Result) error) (err error) { logStats := NewLogStats(ctx, method, sql, bindVars) diff --git a/go/vt/vtgate/executor_dml_test.go b/go/vt/vtgate/executor_dml_test.go index 80ead36aac8..c87e1646c69 100644 --- a/go/vt/vtgate/executor_dml_test.go +++ b/go/vt/vtgate/executor_dml_test.go @@ -1601,7 +1601,7 @@ func TestKeyDestRangeQuery(t *testing.T) { sbc2.Queries = nil // it does not work for inserts - _, err = executorExec(executor, "INSERT INTO sharded_user_msgs(message) VALUE('test')", nil) + _, err = executorExec(executor, "INSERT INTO sharded_user_msgs(message) VALUES('test')", nil) want := "range queries not supported for inserts: TestExecutor[-]" if err == nil || err.Error() != want { @@ -1686,7 +1686,7 @@ func TestKeyShardDestQuery(t *testing.T) { // it works for inserts - sql = "INSERT INTO sharded_user_msgs(message) VALUE('test')" + sql = "INSERT INTO sharded_user_msgs(message) VALUES('test')" _, err = executorExec(executor, sql, nil) wantQueries = []*querypb.BoundQuery{{ diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go index 0f382bde580..0204e473763 100644 --- a/go/vt/vtgate/executor_test.go +++ b/go/vt/vtgate/executor_test.go @@ -30,9 +30,11 @@ import ( "time" "context" + "vitess.io/vitess/go/vt/topo" "github.com/golang/protobuf/proto" + "github.com/google/go-cmp/cmp" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/callerid" @@ -953,8 +955,8 @@ func TestExecutorComment(t *testing.T) { executor, _, _, _ := createExecutorEnv() stmts := []string{ - "/*! SET max_execution_time=5000*/", - "/*!50708 SET max_execution_time=5000*/", + "/*! SET autocommit=1*/", + "/*!50708 SET @x=5000*/", } wantResult := &sqltypes.Result{} @@ -972,56 +974,78 @@ func TestExecutorComment(t *testing.T) { func TestExecutorOther(t *testing.T) { executor, sbc1, sbc2, sbclookup := createExecutorEnv() - stmts := []string{ - "show other", - "analyze", - "describe", - "explain", - "repair", - "optimize", + type cnts struct { + Sbc1Cnt int64 + Sbc2Cnt int64 + SbcLookupCnt int64 } - wantCount := []int64{0, 0, 0} - for _, stmt := range stmts { - _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount := []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[2]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) - } - _, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount = []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[0]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) - } + tcs := []struct { + targetStr string + + hasNoKeyspaceErr bool + hasDestinationShardErr bool + wantCnts cnts + }{ + { + targetStr: "", + hasNoKeyspaceErr: true, + }, + { + targetStr: "TestExecutor[-]", + hasDestinationShardErr: true, + }, + { + targetStr: KsTestUnsharded, + wantCnts: cnts{ + Sbc1Cnt: 0, + Sbc2Cnt: 0, + SbcLookupCnt: 1, + }, + }, + { + targetStr: "TestExecutor", + wantCnts: cnts{ + Sbc1Cnt: 1, + Sbc2Cnt: 0, + SbcLookupCnt: 0, + }, + }, } - _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{}), "analyze", nil) - want := errNoKeyspace.Error() - if err == nil || err.Error() != want { - t.Errorf("show vschema tables: %v, want %v", err, want) + stmts := []string{ + "show tables", + "analyze table t1", + "describe t1", + "explain t1", + "repair table t1", + "optimize table t1", } - // Can't target a range with handle other - _, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor[-]"}), "analyze", nil) - want = "Destination can only be a single shard for statement: analyze, got: DestinationExactKeyRange(-)" - if err == nil || err.Error() != want { - t.Errorf("analyze: got %v, want %v", err, want) + for _, stmt := range stmts { + for _, tc := range tcs { + sbc1.ExecCount.Set(0) + sbc2.ExecCount.Set(0) + sbclookup.ExecCount.Set(0) + + _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + if tc.hasNoKeyspaceErr { + assert.Error(t, err, errNoKeyspace) + } else if tc.hasDestinationShardErr { + assert.Errorf(t, err, "Destination can only be a single shard for statement: %s, got: DestinationExactKeyRange(-)", stmt) + } else { + assert.NoError(t, err) + } + + diff := cmp.Diff(tc.wantCnts, cnts{ + Sbc1Cnt: sbc1.ExecCount.Get(), + Sbc2Cnt: sbc2.ExecCount.Get(), + SbcLookupCnt: sbclookup.ExecCount.Get(), + }) + if diff != "" { + t.Errorf("stmt: %s\ntc: %+v\n-want,+got:\n%s", stmt, tc, diff) + } + } } } @@ -1031,68 +1055,85 @@ func TestExecutorDDL(t *testing.T) { executor, sbc1, sbc2, sbclookup := createExecutorEnv() + type cnts struct { + Sbc1Cnt int64 + Sbc2Cnt int64 + SbcLookupCnt int64 + } + + tcs := []struct { + targetStr string + + hasNoKeyspaceErr bool + shardQueryCnt int + wantCnts cnts + }{ + { + targetStr: "", + hasNoKeyspaceErr: true, + }, + { + targetStr: KsTestUnsharded, + shardQueryCnt: 1, + wantCnts: cnts{ + Sbc1Cnt: 0, + Sbc2Cnt: 0, + SbcLookupCnt: 1, + }, + }, + { + targetStr: "TestExecutor", + shardQueryCnt: 8, + wantCnts: cnts{ + Sbc1Cnt: 1, + Sbc2Cnt: 1, + SbcLookupCnt: 0, + }, + }, + { + targetStr: "TestExecutor/-20", + shardQueryCnt: 1, + wantCnts: cnts{ + Sbc1Cnt: 1, + Sbc2Cnt: 0, + SbcLookupCnt: 0, + }, + }, + } + stmts := []string{ "create", - "alter", - "rename", - "drop", - "truncate", + "alter table t1 add primary key id", + "rename table t1 to t2", + "truncate table t2", + "drop table t2", } - wantCount := []int64{0, 0, 0} + for _, stmt := range stmts { - _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: KsTestUnsharded}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount := []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[2]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) - } - testQueryLog(t, logChan, "TestExecute", "DDL", stmt, 1) + for _, tc := range tcs { + sbc1.ExecCount.Set(0) + sbc2.ExecCount.Set(0) + sbclookup.ExecCount.Set(0) + + _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: tc.targetStr}), stmt, nil) + if tc.hasNoKeyspaceErr { + assert.Error(t, err, errNoKeyspace) + } else { + assert.NoError(t, err) + } - _, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount = []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[0]++ - wantCount[1]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) - } - testQueryLog(t, logChan, "TestExecute", "DDL", stmt, 8) + diff := cmp.Diff(tc.wantCnts, cnts{ + Sbc1Cnt: sbc1.ExecCount.Get(), + Sbc2Cnt: sbc2.ExecCount.Get(), + SbcLookupCnt: sbclookup.ExecCount.Get(), + }) + if diff != "" { + t.Errorf("stmt: %s\ntc: %+v\n-want,+got:\n%s", stmt, tc, diff) + } - _, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor/-20"}), stmt, nil) - if err != nil { - t.Error(err) - } - gotCount = []int64{ - sbc1.ExecCount.Get(), - sbc2.ExecCount.Get(), - sbclookup.ExecCount.Get(), - } - wantCount[0]++ - if !reflect.DeepEqual(gotCount, wantCount) { - t.Errorf("Exec %s: %v, want %v", stmt, gotCount, wantCount) + testQueryLog(t, logChan, "TestExecute", "DDL", stmt, tc.shardQueryCnt) } - testQueryLog(t, logChan, "TestExecute", "DDL", stmt, 1) - } - - _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{}), "create", nil) - want := errNoKeyspace.Error() - if err == nil || err.Error() != want { - t.Errorf("ddl with no keyspace: %v, want %v", err, want) } - testQueryLog(t, logChan, "TestExecute", "DDL", "create", 0) } func waitForVindex(t *testing.T, ks, name string, watch chan *vschemapb.SrvVSchema, executor *Executor) (*vschemapb.SrvVSchema, *vschemapb.Vindex) { @@ -1845,7 +1886,7 @@ func TestExecutorVindexDDLACL(t *testing.T) { func TestExecutorUnrecognized(t *testing.T) { executor, _, _, _ := createExecutorEnv() _, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(&vtgatepb.Session{}), "invalid statement", nil) - want := "unrecognized statement: invalid statement" + want := "syntax error at position 8 near 'invalid'" if err == nil || err.Error() != want { t.Errorf("show vschema tables: %v, want %v", err, want) } diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index d26044b6c73..dacde288127 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -2255,14 +2255,8 @@ options:PassthroughDMLs # analyze "analyze table a" { - "PlanID": "DDL", - "TableName": "", - "Permissions": [ - { - "TableName": "a", - "Role": 2 - } - ] + "PlanID": "OTHER_READ", + "TableName": "" } # reorganize partition with bind From b601056d2a7a93b724a2391810b3705834096bdc Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Fri, 20 Mar 2020 11:05:37 +0100 Subject: [PATCH 337/825] Added support for varbinary, tests for both int/varbinary Signed-off-by: Rohit Nayak --- .../tabletserver/vstreamer/planbuilder.go | 25 ++++++++- .../vstreamer/rowstreamer_test.go | 30 +++++++++++ .../tabletserver/vstreamer/vstreamer.go | 4 +- .../tabletserver/vstreamer/vstreamer_test.go | 53 +++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index 918c2d421b5..2d4d43819f7 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -110,13 +110,33 @@ func (plan *Plan) fields() []*querypb.Field { return fields } +func resolveValue(val sqltypes.Value) (sqltypes.Value, error) { + pv, err := sqlparser.NewPlanValue(sqlparser.NewStrVal(val.Raw())) + if err != nil { + return sqltypes.NULL, err + } + resolved, err := pv.ResolveValue(nil) + if err != nil { + return sqltypes.NULL, err + } + return resolved, nil +} + // filter filters the row against the plan. It returns false if the row did not match. // If the row matched, it returns the columns to be sent. func (plan *Plan) filter(values []sqltypes.Value) (bool, []sqltypes.Value, error) { for _, filter := range plan.Filters { switch filter.Opcode { case Equal: - result, err := sqltypes.NullsafeCompare(values[filter.ColNum], filter.Value) + filterValue, err := resolveValue(filter.Value) + if err != nil { + return false, nil, err + } + colValue, err := resolveValue(values[filter.ColNum]) + if err != nil { + return false, nil, err + } + result, err := sqltypes.NullsafeCompare(colValue, filterValue) if err != nil { return false, nil, err } @@ -372,7 +392,8 @@ func (plan *Plan) analyzeWhere(vschema *localVSchema, where *sqlparser.Where) er if !ok { return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) } - if val.Type != sqlparser.IntVal { + //StrVal is varbinary, we do not support varchar since we would have to implement all collation types + if val.Type != sqlparser.IntVal && val.Type != sqlparser.StrVal { return fmt.Errorf("unexpected: %v", sqlparser.String(expr)) } pv, err := sqlparser.NewPlanValue(val) diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index 63fb44a3168..8b2c4a39007 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -240,6 +240,36 @@ func TestStreamRowsFilterInt(t *testing.T) { checkStream(t, "select id1, val from t1 where id2 = 100", nil, wantQuery, wantStream) } +func TestStreamRowsFilterVarBinary(t *testing.T) { + if testing.Short() { + t.Skip() + } + + if err := env.SetVSchema(shardedVSchema); err != nil { + t.Fatal(err) + } + defer env.SetVSchema("{}") + + execStatements(t, []string{ + "create table t1(id1 int, val varbinary(128), primary key(id1))", + "insert into t1 values (1,'kepler'), (2, 'newton'), (3, 'newton'), (4, 'kepler'), (5, 'newton'), (6, 'kepler')", + }) + defer execStatements(t, []string{ + "drop table t1", + }) + engine.se.Reload(context.Background()) + + time.Sleep(1 * time.Second) + + // Only the first row should be returned, but lastpk should be 6. + wantStream := []string{ + `fields: fields: pkfields: `, + `rows: rows: rows: lastpk: `, + } + wantQuery := "select id1, val from t1 order by id1" + checkStream(t, "select id1, val from t1 where val = 'newton'", nil, wantQuery, wantStream) +} + func TestStreamRowsMultiPacket(t *testing.T) { if testing.Short() { t.Skip() diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 7f99791be36..08c5ea5aa64 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -545,7 +545,7 @@ func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { return err } if len(st.Columns) < len(tm.Types) { - return fmt.Errorf("cannot determine table columns for %s: event has %v, schema as %v", tm.Name, tm.Types, st.Columns) + return fmt.Errorf("cannot determine table columns for %s: event has %v, schema has %v", tm.Name, tm.Types, st.Columns) } table := &Table{ Name: "_vt.resharding_journal", @@ -620,7 +620,7 @@ func (vs *vstreamer) buildTableColumns(id uint64, tm *mysql.TableMap) ([]schema. if len(st.Columns) < len(tm.Types) { if vs.filter.FieldEventMode == binlogdatapb.Filter_ERR_ON_MISMATCH { - return nil, fmt.Errorf("cannot determine table columns for %s: event has %v, schema as %v", tm.Name, tm.Types, st.Columns) + return nil, fmt.Errorf("cannot determine table columns for %s: event has %v, schema has %v", tm.Name, tm.Types, st.Columns) } return cols, nil } diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 572c07da983..c693b9efc2e 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -37,6 +37,59 @@ type testcase struct { output [][]string } +func TestFilteredVarBinary(t *testing.T) { + if testing.Short() { + t.Skip() + } + + execStatements(t, []string{ + "create table t1(id1 int, val varbinary(128), primary key(id1))", + }) + defer execStatements(t, []string{ + "drop table t1", + }) + engine.se.Reload(context.Background()) + + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "t1", + Filter: "select id1, val from t1 where val = 'newton'", + }}, + } + + testcases := []testcase{{ + input: []string{ + "begin", + "insert into t1 values (1, 'kepler')", + "insert into t1 values (2, 'newton')", + "insert into t1 values (3, 'newton')", + "insert into t1 values (4, 'kepler')", + "insert into t1 values (5, 'newton')", + "update t1 set val = 'newton' where id1 = 1", + "update t1 set val = 'kepler' where id1 = 2", + "update t1 set val = 'newton' where id1 = 2", + "update t1 set val = 'kepler' where id1 = 1", + "delete from t1 where id1 in (2,3)", + "commit", + }, + output: [][]string{{ + `begin`, + `type:FIELD field_event: fields: > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > > `, + `type:ROW row_event: > row_changes: > > `, + `gtid`, + `commit`, + }}, + }} + runCases(t, filter, testcases, "") +} + func TestFilteredInt(t *testing.T) { if testing.Short() { t.Skip() From 18e13ea20dc803ba589dcc0ae3910651d3090ea0 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Fri, 20 Mar 2020 14:27:24 +0100 Subject: [PATCH 338/825] Modified logic to find serving shards. Earlier one seems broken resulting in the test depending on the sequence of shards returned by the topo Signed-off-by: Rohit Nayak --- go/vt/workflow/reshardingworkflowgen/workflow.go | 6 +++--- .../{workflow_flaky_test.go => workflow_test.go} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename go/vt/workflow/reshardingworkflowgen/{workflow_flaky_test.go => workflow_test.go} (98%) diff --git a/go/vt/workflow/reshardingworkflowgen/workflow.go b/go/vt/workflow/reshardingworkflowgen/workflow.go index cec2cc6842f..8485551526f 100644 --- a/go/vt/workflow/reshardingworkflowgen/workflow.go +++ b/go/vt/workflow/reshardingworkflowgen/workflow.go @@ -182,12 +182,12 @@ func findSourceAndDestinationShards(ts *topo.Server, keyspace string) ([][][]str var sourceShards, destinationShards []string var sourceShardInfo *topo.ShardInfo var destinationShardInfos []*topo.ShardInfo - // Judge which side is source shard by checking the number of servedTypes. - leftServingTypes, err := ts.GetShardServingTypes(context.Background(), os.Left[0]) + + isLeftServing := os.Left[0].IsMasterServing if err != nil { return nil, err } - if len(leftServingTypes) > 0 { + if isLeftServing { sourceShardInfo = os.Left[0] destinationShardInfos = os.Right } else { diff --git a/go/vt/workflow/reshardingworkflowgen/workflow_flaky_test.go b/go/vt/workflow/reshardingworkflowgen/workflow_test.go similarity index 98% rename from go/vt/workflow/reshardingworkflowgen/workflow_flaky_test.go rename to go/vt/workflow/reshardingworkflowgen/workflow_test.go index e65220d22aa..bc337f50aec 100644 --- a/go/vt/workflow/reshardingworkflowgen/workflow_flaky_test.go +++ b/go/vt/workflow/reshardingworkflowgen/workflow_test.go @@ -43,7 +43,7 @@ func init() { } // TestWorkflowGenerator runs the happy path of HorizontalReshardingWorkflow. -func TestWorfklowGenerator(t *testing.T) { +func TestWorkflowGenerator(t *testing.T) { ctx := context.Background() // Initialize the topology. From ac2567fc76196d5c91497e5bba5bbc4d7660ea4d Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 20 Mar 2020 09:45:02 -0700 Subject: [PATCH 339/825] deprecation: address review comments Signed-off-by: Sugu Sougoumarane --- .../vttablet/tabletserver/planbuilder/plan.go | 5 ++ go/vt/vttablet/tabletserver/query_executor.go | 2 + .../tabletserver/query_executor_test.go | 72 ++++++++++++++----- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/go/vt/vttablet/tabletserver/planbuilder/plan.go b/go/vt/vttablet/tabletserver/planbuilder/plan.go index a9b662a79c8..227523f8820 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/plan.go +++ b/go/vt/vttablet/tabletserver/planbuilder/plan.go @@ -55,9 +55,12 @@ const ( PlanDeleteLimit PlanDDL PlanSet + // PlanOtherRead is for statements like show, etc. PlanOtherRead + // PlanOtherAdmin is for statements like repair, lock table, etc. PlanOtherAdmin PlanSelectStream + // PlanMessageStream is for "stream" statements. PlanMessageStream NumPlans ) @@ -170,6 +173,8 @@ func Build(statement sqlparser.Statement, tables map[string]*schema.Table) (*Pla case *sqlparser.Set: plan, err = analyzeSet(stmt), nil case *sqlparser.DDL: + // DDLs and other statements below don't get fully parsed. + // We have to use the original query at the time of execution. plan = &Plan{PlanID: PlanDDL} case *sqlparser.Show: plan, err = &Plan{PlanID: PlanOtherRead}, nil diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index 8dc5deece83..88ce78e3c9b 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -100,6 +100,8 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) { case planbuilder.PlanNextval: return qre.execNextval() case planbuilder.PlanSelectImpossible: + // If the fields did not get cached, we have send the query + // to mysql, which you can see below. if qre.plan.Fields != nil { return &sqltypes.Result{ Fields: qre.plan.Fields, diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 9fce5aa53f3..8a7e0c758e2 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -307,7 +307,7 @@ func TestQueryExecutorSelectImpossible(t *testing.T) { } } -func TestQueryExecutorDMLLimitFailure(t *testing.T) { +func TestQueryExecutorLimitFailure(t *testing.T) { type dbResponse struct { query string result *sqltypes.Result @@ -316,31 +316,66 @@ func TestQueryExecutorDMLLimitFailure(t *testing.T) { dmlResult := &sqltypes.Result{ RowsAffected: 3, } + fields := sqltypes.MakeTestFields("a|b", "int64|varchar") + fieldResult := sqltypes.MakeTestResult(fields) + selectResult := sqltypes.MakeTestResult(fields, "1|aaa", "2|bbb", "3|ccc") // The queries are run both in and outside a transaction. testcases := []struct { - input string - passThrough bool - dbResponses []dbResponse - planWant string - logWant string - inTxWant string + input string + dbResponses []dbResponse + err string + logWant string + inTxWant string + testRollback bool }{{ + input: "select * from t", + dbResponses: []dbResponse{{ + query: "select * from t where 1 != 1", + result: fieldResult, + }, { + query: "select * from t limit 3", + result: selectResult, + }}, + err: "count exceeded", + logWant: "select * from t where 1 != 1; select * from t limit 3", + // Because the fields would have been cached before, the field query will + // not get re-executed. + inTxWant: "select * from t limit 3", + }, { input: "update test_table set a=1", dbResponses: []dbResponse{{ query: "update test_table set a = 1 limit 3", result: dmlResult, }}, - logWant: "begin; update test_table set a = 1 limit 3; rollback", - inTxWant: "update test_table set a = 1 limit 3; rollback", + err: "count exceeded", + logWant: "begin; update test_table set a = 1 limit 3; rollback", + inTxWant: "update test_table set a = 1 limit 3; rollback", + testRollback: true, }, { input: "delete from test_table", dbResponses: []dbResponse{{ query: "delete from test_table limit 3", result: dmlResult, }}, - logWant: "begin; delete from test_table limit 3; rollback", - inTxWant: "delete from test_table limit 3; rollback", + err: "count exceeded", + logWant: "begin; delete from test_table limit 3; rollback", + inTxWant: "delete from test_table limit 3; rollback", + testRollback: true, + }, { + // There should be no rollback on normal failures. + input: "update test_table set a=1", + dbResponses: nil, + err: "not supported", + logWant: "begin; update test_table set a = 1 limit 3; rollback", + inTxWant: "update test_table set a = 1 limit 3", + }, { + // There should be no rollback on normal failures. + input: "delete from test_table", + dbResponses: nil, + err: "not supported", + logWant: "begin; delete from test_table limit 3; rollback", + inTxWant: "delete from test_table limit 3", }} for _, tcase := range testcases { func() { @@ -353,14 +388,13 @@ func TestQueryExecutorDMLLimitFailure(t *testing.T) { tsv := newTestTabletServer(ctx, smallResultSize, db) defer tsv.StopService() - tsv.SetPassthroughDMLs(tcase.passThrough) + tsv.SetPassthroughDMLs(false) // Test outside a transaction. qre := newTestQueryExecutor(ctx, tsv, tcase.input, 0) _, err := qre.Execute() - wantErr := "caller id: d: row count exceeded 2 (errno 10001) (sqlstate HY000)" - if err == nil || err.Error() != wantErr { - t.Errorf("Execute(%v): %v, want %v", tcase.input, err, wantErr) + if err == nil || !strings.Contains(err.Error(), tcase.err) { + t.Errorf("Execute(%v): %v, must contain %v", tcase.input, err, tcase.err) } assert.Equal(t, tcase.logWant, qre.logStats.RewrittenSQL(), tcase.input) @@ -371,8 +405,8 @@ func TestQueryExecutorDMLLimitFailure(t *testing.T) { qre = newTestQueryExecutor(ctx, tsv, tcase.input, txid) _, err = qre.Execute() - if err == nil || err.Error() != wantErr { - t.Errorf("Execute(%v): %v, want %v", tcase.input, err, wantErr) + if err == nil || !strings.Contains(err.Error(), tcase.err) { + t.Errorf("Execute(%v): %v, must contain %v", tcase.input, err, tcase.err) } want := tcase.logWant if tcase.inTxWant != "" { @@ -380,6 +414,10 @@ func TestQueryExecutorDMLLimitFailure(t *testing.T) { } assert.Equal(t, want, qre.logStats.RewrittenSQL(), "in tx: %v", tcase.input) + if !tcase.testRollback { + return + } + // Ensure transaction was rolled back. qre = newTestQueryExecutor(ctx, tsv, "update test_table set a=1", txid) _, err = qre.Execute() notxError := "ended at" From 7a48f7213936840f6105bcea5f55bd1c6b3c6e1f Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Fri, 20 Mar 2020 11:30:34 -0600 Subject: [PATCH 340/825] Upgrade go minimum to 1.13 Signed-off-by: Morgan Tocker --- .github/workflows/create_release.yml | 2 +- build.env | 2 +- docker/bootstrap/Dockerfile.common | 2 +- go.mod | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 4759963a66c..9b59864e37f 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v1 with: - go-version: 1.12 + go-version: 1.13 - name: Check out code uses: actions/checkout@v2 diff --git a/build.env b/build.env index fa83ff30ea1..d39c3e996ae 100755 --- a/build.env +++ b/build.env @@ -17,7 +17,7 @@ source ./tools/shell_functions.inc go version >/dev/null 2>&1 || fail "Go is not installed or is not in \$PATH. See https://vitess.io/contributing/build-from-source for install instructions." -goversion_min 1.12 || fail "Go is not version 1.12+. See https://vitess.io/contributing/build-from-source for install instructions." +goversion_min 1.13 || fail "Go is not version 1.13+. See https://vitess.io/contributing/build-from-source for install instructions." mkdir -p dist mkdir -p bin diff --git a/docker/bootstrap/Dockerfile.common b/docker/bootstrap/Dockerfile.common index 4f7f22eb3a1..6a3f8d7b200 100644 --- a/docker/bootstrap/Dockerfile.common +++ b/docker/bootstrap/Dockerfile.common @@ -1,4 +1,4 @@ -FROM golang:1.12-stretch +FROM golang:1.13-stretch # Install Vitess build dependencies RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ diff --git a/go.mod b/go.mod index 4274af73af3..819ce95de57 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module vitess.io/vitess -go 1.12 +go 1.13 require ( cloud.google.com/go v0.45.1 From 5d09deda4e06e8d6cf181f87885a5dce96e60400 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Fri, 20 Mar 2020 11:52:51 -0600 Subject: [PATCH 341/825] Make it easier to understand current go version Signed-off-by: Morgan Tocker --- build.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.env b/build.env index d39c3e996ae..0cc095fe651 100755 --- a/build.env +++ b/build.env @@ -17,7 +17,7 @@ source ./tools/shell_functions.inc go version >/dev/null 2>&1 || fail "Go is not installed or is not in \$PATH. See https://vitess.io/contributing/build-from-source for install instructions." -goversion_min 1.13 || fail "Go is not version 1.13+. See https://vitess.io/contributing/build-from-source for install instructions." +goversion_min 1.13 || fail "Go version reported: ${go version}. Version 1.13+ required. See https://vitess.io/contributing/build-from-source for install instructions." mkdir -p dist mkdir -p bin From 25d6049a0377927cd723ac51f705bbf114e2a69b Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Fri, 20 Mar 2020 13:54:27 -0700 Subject: [PATCH 342/825] tabletserver: fix broken test Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt index 9658c4abfbd..fc7b3156f9f 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt +++ b/go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt @@ -877,7 +877,7 @@ options:PassthroughDMLs # analyze "analyze table a" { - "PlanID": "OTHER_READ", + "PlanID": "OtherRead", "TableName": "" } From 8f4277e232effeb8fa138cbfb84c9945c83b258e Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sat, 21 Mar 2020 00:07:55 +0100 Subject: [PATCH 343/825] Handle varbinary correctly Signed-off-by: Rohit Nayak --- go/mysql/binlog_event_rbr.go | 5 +++-- .../tabletserver/vstreamer/planbuilder.go | 22 +------------------ .../vstreamer/rowstreamer_test.go | 2 -- .../tabletserver/vstreamer/vstreamer.go | 1 + 4 files changed, 5 insertions(+), 25 deletions(-) diff --git a/go/mysql/binlog_event_rbr.go b/go/mysql/binlog_event_rbr.go index e29e3f0fabe..92866f63859 100644 --- a/go/mysql/binlog_event_rbr.go +++ b/go/mysql/binlog_event_rbr.go @@ -447,15 +447,16 @@ func CellValue(data []byte, pos int, typ byte, metadata uint16, styp querypb.Typ return sqltypes.MakeTrusted(querypb.Type_DATETIME, []byte(fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second))), 8, nil case TypeVarchar, TypeVarString: + // We trust that styp is compatible with the column type // Length is encoded in 1 or 2 bytes. if metadata > 255 { l := int(uint64(data[pos]) | uint64(data[pos+1])<<8) - return sqltypes.MakeTrusted(querypb.Type_VARCHAR, + return sqltypes.MakeTrusted(styp, data[pos+2:pos+2+l]), l + 2, nil } l := int(data[pos]) - return sqltypes.MakeTrusted(querypb.Type_VARCHAR, + return sqltypes.MakeTrusted(styp, data[pos+1:pos+1+l]), l + 1, nil case TypeBit: // The contents is just the bytes, quoted. diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go index 2d4d43819f7..48de92a03d3 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go +++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go @@ -110,33 +110,13 @@ func (plan *Plan) fields() []*querypb.Field { return fields } -func resolveValue(val sqltypes.Value) (sqltypes.Value, error) { - pv, err := sqlparser.NewPlanValue(sqlparser.NewStrVal(val.Raw())) - if err != nil { - return sqltypes.NULL, err - } - resolved, err := pv.ResolveValue(nil) - if err != nil { - return sqltypes.NULL, err - } - return resolved, nil -} - // filter filters the row against the plan. It returns false if the row did not match. // If the row matched, it returns the columns to be sent. func (plan *Plan) filter(values []sqltypes.Value) (bool, []sqltypes.Value, error) { for _, filter := range plan.Filters { switch filter.Opcode { case Equal: - filterValue, err := resolveValue(filter.Value) - if err != nil { - return false, nil, err - } - colValue, err := resolveValue(values[filter.ColNum]) - if err != nil { - return false, nil, err - } - result, err := sqltypes.NullsafeCompare(colValue, filterValue) + result, err := sqltypes.NullsafeCompare(values[filter.ColNum], filter.Value) if err != nil { return false, nil, err } diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index 8b2c4a39007..01056c893e3 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -231,7 +231,6 @@ func TestStreamRowsFilterInt(t *testing.T) { time.Sleep(1 * time.Second) - // Only the first row should be returned, but lastpk should be 6. wantStream := []string{ `fields: fields: pkfields: `, `rows: rows: lastpk: `, @@ -261,7 +260,6 @@ func TestStreamRowsFilterVarBinary(t *testing.T) { time.Sleep(1 * time.Second) - // Only the first row should be returned, but lastpk should be 6. wantStream := []string{ `fields: fields: pkfields: `, `rows: rows: rows: lastpk: `, diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 08c5ea5aa64..e7025f9b794 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -630,6 +630,7 @@ func (vs *vstreamer) buildTableColumns(id uint64, tm *mysql.TableMap) ([]schema. if !sqltypes.AreTypesEquivalent(cols[i].Type, st.Columns[i].Type) { return cols, nil } + cols[i].Type = st.Columns[i].Type } // Columns should be truncated to match those in tm. From 79baa663adeaa0ac146990e16868dd3393e084ec Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sun, 19 Jan 2020 19:45:59 +0100 Subject: [PATCH 344/825] Vreplication e2e: create initial cluster Signed-off-by: Rohit Nayak --- go/test/endtoend/vreplication/cluster.go | 334 ++++++++++++++++++ .../vreplication/vreplication_test.go | 75 ++++ 2 files changed, 409 insertions(+) create mode 100644 go/test/endtoend/vreplication/cluster.go create mode 100644 go/test/endtoend/vreplication/vreplication_test.go diff --git a/go/test/endtoend/vreplication/cluster.go b/go/test/endtoend/vreplication/cluster.go new file mode 100644 index 00000000000..0f932886ca3 --- /dev/null +++ b/go/test/endtoend/vreplication/cluster.go @@ -0,0 +1,334 @@ +package vreplication + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "os" + "os/exec" + "strings" + _ "strings" + "testing" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" + "vitess.io/vitess/go/vt/log" +) + +var ( + vtdataroot string + +) + +var globalConfig = struct { + hostname string + topoPort int + vtctldPort int + vtctldGrpcPort int + tmpDir string + vtgatePort int + vtgateGrpcPort int + vtgateMySQLPort int + tabletTypes string //TODO +} {"localhost", 2379, 15000, 15999, vtdataroot+"/tmp", + 15001, 15991, 15306, "MASTER,REPLICA"} + + +//TODO ports should be automatically (incrementally) allocated based on what is available? +var ( + tabletIndex = 1 + tabletSubIndex = 0 + tabletPortBase = 15000 + tabletGrpcPortBase = 16000 + tabletMysqlPortBase = 17000 +) + +type VitessCluster struct { + Name string + Cells map[string]*Cell + Topo *cluster.EtcdProcess + Vtctld *cluster.VtctldProcess + Vtctl *cluster.VtctlProcess + VtctlClient *cluster.VtctlClientProcess +} + +type Cell struct { + Name string + Keyspaces map[string]*Keyspace + Vtgates []*cluster.VtgateProcess +} + +type Keyspace struct { + Name string + Shards map[string]*Shard + VSchema string + Schema string +} + +type Shard struct { + Name string + IsSharded bool + Tablets map[string]*Tablet +} + +type Tablet struct { + Name string + Vttablet *cluster.VttabletProcess + DbServer *cluster.MysqlctlProcess +} + + +func initGlobals() { + vtdataroot = os.Getenv("VTDATAROOT") + globalConfig.tmpDir = vtdataroot + "/tmp" + //TODO init some vars (like ports, indexes) here rather than hardcoding +} + +func NewVitessCluster (name string) (cluster *VitessCluster, err error) { + return &VitessCluster{Name:name, Cells:make(map[string]*Cell)}, nil +} + +func InitCluster(t *testing.T, cellName string) *VitessCluster { + initGlobals() + vc, _ := NewVitessCluster("Vdemo") + assert.NotNil(t, vc) + topo := cluster.EtcdProcessInstance(globalConfig.topoPort, globalConfig.topoPort*10, globalConfig.hostname, "global") + assert.NotNil(t, topo) + assert.Nil(t, topo.Setup()) + topo.ManageTopoDir("mkdir", "/vitess/global") + vc.Topo = topo + topo.ManageTopoDir("mkdir", "/vitess/"+cellName) + + vtctld := cluster.VtctldProcessInstance(globalConfig.vtctldPort, globalConfig.vtctldGrpcPort, + globalConfig.topoPort, globalConfig.hostname, globalConfig.tmpDir) + vc.Vtctld =vtctld + assert.NotNil(t, vc.Vtctld) + vc.Vtctld.Setup(cellName) + + vc.Vtctl = cluster.VtctlProcessInstance(globalConfig.topoPort, globalConfig.hostname) + assert.NotNil(t, vc.Vtctl) + vc.Vtctl.AddCellInfo(cellName) + cell, err := vc.AddCell(t, cellName) + assert.Nil(t, err); assert.NotNil(t, cell) + + vc.VtctlClient = cluster.VtctlClientProcessInstance(globalConfig.hostname, vc.Vtctld.GrpcPort, globalConfig.tmpDir) + assert.NotNil(t, vc.VtctlClient) + + return vc +} + +func (vc *VitessCluster) AddKeyspace(t *testing.T, cell *Cell, ksName string, shards string, vschema string, schema string, numReplicas int, numRdonly int) (*Keyspace, error) { + keyspace := &Keyspace{ + Name: ksName, + Shards: make(map[string]*Shard), + } + + if err := vc.Vtctl.CreateKeyspace(keyspace.Name); err != nil { + t.Fatalf(err.Error()) + } + cell.Keyspaces[ksName] = keyspace + if err := vc.AddShards(t, cell, keyspace, shards, numReplicas, numRdonly); err != nil { + t.Fatalf(err.Error()) + } + if err := vc.VtctlClient.ApplySchema(ksName, schema); err != nil { + t.Fatalf(err.Error()) + } + keyspace.Schema = schema + if err := vc.VtctlClient.ApplyVSchema(ksName, vschema); err != nil { + t.Fatalf(err.Error()) + } + keyspace.VSchema = vschema + fmt.Println("Starting vtgate") + if len(cell.Vtgates) == 0 { + vc.StartVtgate(t, cell) + //cell.Vtgates[0].WaitForStatusOfTabletInShard() //TODO + } + return keyspace, nil +} + +func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, shard *Shard, tabletType string) (*Tablet, *exec.Cmd, error) { + tablet := &Tablet{} + var tabletId int + tabletId = tabletIndex*100 + tabletSubIndex + tabletSubIndex += 1 + + vttablet := cluster.VttabletProcessInstance( + tabletPortBase+tabletId, + tabletGrpcPortBase+tabletId, + tabletId, + cell.Name, + shard.Name, + keyspace.Name, + globalConfig.vtctldPort, + tabletType, + vc.Topo.Port, + globalConfig.hostname, + globalConfig.tmpDir, + nil, + true) + assert.NotNil(t, vttablet) + //vttablet.ServingStatus = "SERVING" + vttablet.SupportsBackup = false + + tablet.DbServer = cluster.MysqlCtlProcessInstance(tabletId, tabletMysqlPortBase+tabletId, globalConfig.tmpDir) + assert.NotNil(t, tablet.DbServer) + tablet.DbServer.InitMysql = true + proc, err := tablet.DbServer.StartProcess() + if err != nil { + t.Fatal(err.Error()) + } + assert.NotNil(t, proc) + tablet.Name = fmt.Sprintf("%s-%d", cell.Name, tabletId ) + tablet.Vttablet = vttablet + shard.Tablets[tablet.Name] = tablet + + return tablet, proc, nil +} + +func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, names string, numReplicas int, numRdonly int) error { + arrNames := strings.Split(names, ",") + isSharded := len(arrNames) > 1 + dbProcesses := make([]*exec.Cmd, 0) + tablets := make([]*Tablet, 0) + for _, name := range arrNames { + fmt.Printf("Adding Shard %s\n", name) + + shard := &Shard{Name: name, IsSharded: isSharded, Tablets: make(map[string]*Tablet, 1)} + fmt.Println("Adding Master tablet") + master, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica") + master.Vttablet.VreplicationTabletType = "MASTER" + if err != nil { + t.Fatalf(err.Error()) + } + assert.NotNil(t, master) + tablets = append(tablets, master) + dbProcesses = append(dbProcesses, proc) + if err != nil { + t.Fatalf(err.Error()) + } + for i := 0; i < numReplicas; i++ { + fmt.Println("Adding Replica tablet") + tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica") + if err != nil { + t.Fatalf(err.Error()) + } + assert.NotNil(t, tablet) + tablets = append(tablets, tablet) + dbProcesses = append(dbProcesses, proc) + } + for i := 0; i < numRdonly; i++ { + fmt.Println("Adding RdOnly tablet") + tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "rdonly") + if err != nil { + t.Fatalf(err.Error()) + } + assert.NotNil(t, tablet) + tablets = append(tablets, tablet) + dbProcesses = append(dbProcesses, proc) + } + + keyspace.Shards[name] = shard + for ind, proc := range dbProcesses { + fmt.Printf("Waiting for mysql process for tablet %s\n", tablets[ind].Name) + if err := proc.Wait(); err != nil { + t.Fatalf("%v :: Unable to start mysql server for %v", err, tablets[ind].Vttablet) + } + } + for ind, tablet := range tablets { + fmt.Printf("Creating vt_keyspace database for tablet %s\n", tablets[ind].Name) + if _, err := tablet.Vttablet.QueryTablet(fmt.Sprintf("create database vt_%s", keyspace.Name), + keyspace.Name, false); err != nil { + t.Fatalf("Unable to start create database vt_%s for tablet %v", keyspace.Name, tablet.Vttablet) + } + fmt.Printf("Running Setup() for vttablet %s\n", tablets[ind].Name) + if err := tablet.Vttablet.Setup(); err != nil { + t.Fatalf(err.Error()) + } + } + fmt.Printf("InitShardMaster for %d\n", master.Vttablet.TabletUID) + err = vc.VtctlClient.InitShardMaster(keyspace.Name, name, cell.Name, master.Vttablet.TabletUID) + if err != nil { + t.Fatal(err.Error()) + } + fmt.Printf("Finished creating shard %s\n", shard.Name) + } + return nil +} + +func (vc *VitessCluster) StartVtgate(t *testing.T, cell *Cell) { + vtgate := cluster.VtgateProcessInstance( + globalConfig.vtgatePort, + globalConfig.vtgateGrpcPort, + globalConfig.vtgateMySQLPort, + cell.Name, + cell.Name, + globalConfig.hostname, + globalConfig.tabletTypes, + globalConfig.topoPort, + globalConfig.tmpDir, + nil) + assert.NotNil(t, vtgate) + err := vtgate.Setup(); if err != nil { + t.Fatalf(err.Error()) + } + cell.Vtgates[0] = vtgate +} + +func (vc *VitessCluster) AddCell(t *testing.T, name string) (*Cell, error) { + cell := &Cell{Name: name, Keyspaces: make(map[string]*Keyspace), Vtgates:make([]*cluster.VtgateProcess,1)} + vc.Cells[name] = cell + return cell, nil +} + +func (vc *VitessCluster) TearDown() { + for _, cell := range vc.Cells { + for _, vtgate := range cell.Vtgates { + if err := vtgate.TearDown(); err != nil { + log.Errorf("Error in vtgate teardown - %s", err.Error()) + } + } + } + + var dbProcesses []*exec.Cmd + + for _, cell := range vc.Cells { + for _, keyspace := range cell.Keyspaces { + for _, shard := range keyspace.Shards { + for _, tablet := range shard.Tablets { + if tablet.DbServer != nil && tablet.DbServer.TabletUID > 0 { + if proc, err := tablet.DbServer.StopProcess(); err != nil { + log.Errorf("Error stopping mysql process: %s", err.Error()) + } else { + dbProcesses = append(dbProcesses, proc) + } + } + if err := tablet.Vttablet.TearDown(); err != nil { + log.Errorf("Error stopping vttablet %s", err.Error()) + } + } + } + } + } + + for _, proc := range dbProcesses { + if err := proc.Wait(); err != nil { + log.Errorf("Error waiting for mysql to stop: %s", err.Error()) + } + } + + if err := vc.Vtctld.TearDown(); err != nil { + log.Errorf("Error stopping Vtctld: %s", err.Error()) + } + + for _, cell := range vc.Cells { + if err := vc.Topo.TearDown(cell.Name, vtdataroot, vtdataroot, false); err != nil { + log.Errorf("Error in etcd teardown - %s", err.Error()) + } + } +} + +func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { + t.Helper() + qr, err := conn.ExecuteFetch(query, 1000, true) + assert.Nil(t, err) + return qr +} \ No newline at end of file diff --git a/go/test/endtoend/vreplication/vreplication_test.go b/go/test/endtoend/vreplication/vreplication_test.go new file mode 100644 index 00000000000..e39eae9a904 --- /dev/null +++ b/go/test/endtoend/vreplication/vreplication_test.go @@ -0,0 +1,75 @@ +package vreplication + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "os" + "testing" +) + +var ( + initialSchema = ` +create table product( + pid int, + description varbinary(128), + primary key(pid) +); +create table customer( + cid int, + name varbinary(128), + primary key(cid) +); +create table merchant( + mname varchar(128), + category varchar(128), + primary key(mname) +); +create table orders( + oid int, + cid int, + pid int, + mname varchar(128), + price int, + primary key(oid) +);` + initialVSchema = ` +{ + "tables": { + "product": {}, + "customer": {}, + "merchant": {}, + "orders": {} + } +} +` +) + + +func TestBasicVreplicationWorkflow (t *testing.T) { + // TODO remove Setenv block below + // This TEMPORARY HACK is required because GoLand is not using my env variables and defining them in Run/Edit Configurations did not work :( + + err := os.Setenv("PATH", "/home/rohit/vitess-ps/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess-ps/bin:/home/rohit/vitess-ps/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess-ps/bin:/usr/local/go/bin:/home/rohit/vitess/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess/bin:/home/rohit/vitess-ps/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess-ps/bin:/usr/local/go/bin:/home/rohit/vitess/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess/bin:/home/rohit/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin") + if err != nil { + fmt.Printf("err %v\n", err) + } + err = os.Setenv("VTDATAROOT", "/home/rohit/vtdataroot") + if err != nil { + fmt.Printf("err %v\n", err) + } + err = os.Setenv("VTROOT", "/home/rohit/vitess-ps/") + if err != nil { + fmt.Printf("err %v\n", err) + } + + vc := InitCluster(t, "zone1") + if false { //TODO use the keep-data flag + defer vc.TearDown() + } + + assert.NotNil(t, vc) + vc.AddKeyspace(t, vc.Cells["zone1"], "product", "0", initialVSchema, initialSchema, 1, 0) //TODO doesn't work with numReplicas = 0 + + //Initial cluster setup here, now testing various vreplication functionality + +} From 7fe56a56127b5299907f9cc48e36b6e6f2485eb3 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sun, 19 Jan 2020 20:18:12 +0100 Subject: [PATCH 345/825] gofmt-ed files Signed-off-by: Rohit Nayak --- go/test/endtoend/vreplication/cluster.go | 111 ++++++++++-------- .../vreplication/vreplication_test.go | 8 +- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/go/test/endtoend/vreplication/cluster.go b/go/test/endtoend/vreplication/cluster.go index 0f932886ca3..a25d9aa10bb 100644 --- a/go/test/endtoend/vreplication/cluster.go +++ b/go/test/endtoend/vreplication/cluster.go @@ -2,12 +2,13 @@ package vreplication import ( "fmt" - "github.com/stretchr/testify/assert" "os" "os/exec" "strings" _ "strings" "testing" + + "github.com/stretchr/testify/assert" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" @@ -16,77 +17,81 @@ import ( var ( vtdataroot string - ) var globalConfig = struct { - hostname string - topoPort int - vtctldPort int - vtctldGrpcPort int - tmpDir string - vtgatePort int - vtgateGrpcPort int + hostname string + topoPort int + vtctldPort int + vtctldGrpcPort int + tmpDir string + vtgatePort int + vtgateGrpcPort int vtgateMySQLPort int - tabletTypes string //TODO -} {"localhost", 2379, 15000, 15999, vtdataroot+"/tmp", + tabletTypes string //TODO +}{"localhost", 2379, 15000, 15999, vtdataroot + "/tmp", 15001, 15991, 15306, "MASTER,REPLICA"} - //TODO ports should be automatically (incrementally) allocated based on what is available? var ( - tabletIndex = 1 - tabletSubIndex = 0 - tabletPortBase = 15000 - tabletGrpcPortBase = 16000 + tabletIndex = 1 + tabletSubIndex = 0 + tabletPortBase = 15000 + tabletGrpcPortBase = 16000 tabletMysqlPortBase = 17000 ) +// TODO type VitessCluster struct { - Name string - Cells map[string]*Cell - Topo *cluster.EtcdProcess - Vtctld *cluster.VtctldProcess - Vtctl *cluster.VtctlProcess + Name string + Cells map[string]*Cell + Topo *cluster.EtcdProcess + Vtctld *cluster.VtctldProcess + Vtctl *cluster.VtctlProcess VtctlClient *cluster.VtctlClientProcess } +// TODO type Cell struct { - Name string + Name string Keyspaces map[string]*Keyspace - Vtgates []*cluster.VtgateProcess + Vtgates []*cluster.VtgateProcess } +// TODO type Keyspace struct { - Name string - Shards map[string]*Shard + Name string + Shards map[string]*Shard VSchema string - Schema string + Schema string } +// TODO type Shard struct { - Name string + Name string IsSharded bool - Tablets map[string]*Tablet + Tablets map[string]*Tablet } +// TODO type Tablet struct { - Name string + Name string Vttablet *cluster.VttabletProcess DbServer *cluster.MysqlctlProcess } - func initGlobals() { vtdataroot = os.Getenv("VTDATAROOT") globalConfig.tmpDir = vtdataroot + "/tmp" //TODO init some vars (like ports, indexes) here rather than hardcoding } -func NewVitessCluster (name string) (cluster *VitessCluster, err error) { - return &VitessCluster{Name:name, Cells:make(map[string]*Cell)}, nil +// TODO +func NewVitessCluster(name string) (cluster *VitessCluster, err error) { + return &VitessCluster{Name: name, Cells: make(map[string]*Cell)}, nil } +// TODO func InitCluster(t *testing.T, cellName string) *VitessCluster { initGlobals() vc, _ := NewVitessCluster("Vdemo") @@ -100,7 +105,7 @@ func InitCluster(t *testing.T, cellName string) *VitessCluster { vtctld := cluster.VtctldProcessInstance(globalConfig.vtctldPort, globalConfig.vtctldGrpcPort, globalConfig.topoPort, globalConfig.hostname, globalConfig.tmpDir) - vc.Vtctld =vtctld + vc.Vtctld = vtctld assert.NotNil(t, vc.Vtctld) vc.Vtctld.Setup(cellName) @@ -108,7 +113,8 @@ func InitCluster(t *testing.T, cellName string) *VitessCluster { assert.NotNil(t, vc.Vtctl) vc.Vtctl.AddCellInfo(cellName) cell, err := vc.AddCell(t, cellName) - assert.Nil(t, err); assert.NotNil(t, cell) + assert.Nil(t, err) + assert.NotNil(t, cell) vc.VtctlClient = cluster.VtctlClientProcessInstance(globalConfig.hostname, vc.Vtctld.GrpcPort, globalConfig.tmpDir) assert.NotNil(t, vc.VtctlClient) @@ -116,10 +122,11 @@ func InitCluster(t *testing.T, cellName string) *VitessCluster { return vc } +// TODO func (vc *VitessCluster) AddKeyspace(t *testing.T, cell *Cell, ksName string, shards string, vschema string, schema string, numReplicas int, numRdonly int) (*Keyspace, error) { keyspace := &Keyspace{ - Name: ksName, - Shards: make(map[string]*Shard), + Name: ksName, + Shards: make(map[string]*Shard), } if err := vc.Vtctl.CreateKeyspace(keyspace.Name); err != nil { @@ -145,16 +152,17 @@ func (vc *VitessCluster) AddKeyspace(t *testing.T, cell *Cell, ksName string, sh return keyspace, nil } +// TODO func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, shard *Shard, tabletType string) (*Tablet, *exec.Cmd, error) { tablet := &Tablet{} - var tabletId int - tabletId = tabletIndex*100 + tabletSubIndex - tabletSubIndex += 1 + var tabletID int + tabletID = tabletIndex*100 + tabletSubIndex + tabletSubIndex++ vttablet := cluster.VttabletProcessInstance( - tabletPortBase+tabletId, - tabletGrpcPortBase+tabletId, - tabletId, + tabletPortBase+tabletID, + tabletGrpcPortBase+tabletID, + tabletID, cell.Name, shard.Name, keyspace.Name, @@ -169,7 +177,7 @@ func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, //vttablet.ServingStatus = "SERVING" vttablet.SupportsBackup = false - tablet.DbServer = cluster.MysqlCtlProcessInstance(tabletId, tabletMysqlPortBase+tabletId, globalConfig.tmpDir) + tablet.DbServer = cluster.MysqlCtlProcessInstance(tabletID, tabletMysqlPortBase+tabletID, globalConfig.tmpDir) assert.NotNil(t, tablet.DbServer) tablet.DbServer.InitMysql = true proc, err := tablet.DbServer.StartProcess() @@ -177,13 +185,14 @@ func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, t.Fatal(err.Error()) } assert.NotNil(t, proc) - tablet.Name = fmt.Sprintf("%s-%d", cell.Name, tabletId ) + tablet.Name = fmt.Sprintf("%s-%d", cell.Name, tabletID) tablet.Vttablet = vttablet shard.Tablets[tablet.Name] = tablet return tablet, proc, nil } +// TODO func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, names string, numReplicas int, numRdonly int) error { arrNames := strings.Split(names, ",") isSharded := len(arrNames) > 1 @@ -207,7 +216,7 @@ func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, } for i := 0; i < numReplicas; i++ { fmt.Println("Adding Replica tablet") - tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica") + tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica") if err != nil { t.Fatalf(err.Error()) } @@ -217,7 +226,7 @@ func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, } for i := 0; i < numRdonly; i++ { fmt.Println("Adding RdOnly tablet") - tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "rdonly") + tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "rdonly") if err != nil { t.Fatalf(err.Error()) } @@ -254,6 +263,7 @@ func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, return nil } +// TODO func (vc *VitessCluster) StartVtgate(t *testing.T, cell *Cell) { vtgate := cluster.VtgateProcessInstance( globalConfig.vtgatePort, @@ -267,18 +277,21 @@ func (vc *VitessCluster) StartVtgate(t *testing.T, cell *Cell) { globalConfig.tmpDir, nil) assert.NotNil(t, vtgate) - err := vtgate.Setup(); if err != nil { + err := vtgate.Setup() + if err != nil { t.Fatalf(err.Error()) } cell.Vtgates[0] = vtgate } +// TODO func (vc *VitessCluster) AddCell(t *testing.T, name string) (*Cell, error) { - cell := &Cell{Name: name, Keyspaces: make(map[string]*Keyspace), Vtgates:make([]*cluster.VtgateProcess,1)} + cell := &Cell{Name: name, Keyspaces: make(map[string]*Keyspace), Vtgates: make([]*cluster.VtgateProcess, 1)} vc.Cells[name] = cell return cell, nil } +// TODO func (vc *VitessCluster) TearDown() { for _, cell := range vc.Cells { for _, vtgate := range cell.Vtgates { @@ -331,4 +344,4 @@ func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { qr, err := conn.ExecuteFetch(query, 1000, true) assert.Nil(t, err) return qr -} \ No newline at end of file +} diff --git a/go/test/endtoend/vreplication/vreplication_test.go b/go/test/endtoend/vreplication/vreplication_test.go index e39eae9a904..ae48bcbadb7 100644 --- a/go/test/endtoend/vreplication/vreplication_test.go +++ b/go/test/endtoend/vreplication/vreplication_test.go @@ -2,9 +2,10 @@ package vreplication import ( "fmt" - "github.com/stretchr/testify/assert" "os" "testing" + + "github.com/stretchr/testify/assert" ) var ( @@ -44,8 +45,7 @@ create table orders( ` ) - -func TestBasicVreplicationWorkflow (t *testing.T) { +func TestBasicVreplicationWorkflow(t *testing.T) { // TODO remove Setenv block below // This TEMPORARY HACK is required because GoLand is not using my env variables and defining them in Run/Edit Configurations did not work :( @@ -63,7 +63,7 @@ func TestBasicVreplicationWorkflow (t *testing.T) { } vc := InitCluster(t, "zone1") - if false { //TODO use the keep-data flag + if false { //TODO use the keep-data flag defer vc.TearDown() } From 2a7aac1b8cdf1f5b15e973b11486e5e25d656046 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sun, 19 Jan 2020 20:22:45 +0100 Subject: [PATCH 346/825] Added new flag vreplication_tablet_type Signed-off-by: Rohit Nayak --- go/test/endtoend/cluster/vttablet_process.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index b5477499bad..675aa7e93bd 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -66,6 +66,7 @@ type VttabletProcess struct { ServingStatus string DbPassword string DbPort int + VreplicationTabletType string //Extra Args to be set before starting the vttablet process ExtraArgs []string @@ -97,6 +98,8 @@ func (vttablet *VttabletProcess) Setup() (err error) { "-file_backup_storage_root", vttablet.FileBackupStorageRoot, "-service_map", vttablet.ServiceMap, "-vtctld_addr", vttablet.VtctldAddress, + "-vtctld_addr", vttablet.VtctldAddress, + "-vreplication_tablet_type", vttablet.VreplicationTabletType, ) if *isCoverage { vttablet.proc.Args = append(vttablet.proc.Args, "-test.coverprofile="+getCoveragePath("vttablet.out")) @@ -377,6 +380,7 @@ func VttabletProcessInstance(port int, grpcPort int, tabletUID int, cell string, ServingStatus: "NOT_SERVING", BackupStorageImplementation: "file", FileBackupStorageRoot: path.Join(os.Getenv("VTDATAROOT"), "/backups"), + VreplicationTabletType: "replica", } if tabletType == "rdonly" { From 49ba470bc5d43ad3428f5585f9f688cdd5729242 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Wed, 22 Jan 2020 22:30:45 +0100 Subject: [PATCH 347/825] partial commit Signed-off-by: Rohit Nayak --- go.mod | 2 + go/test/endtoend/cluster/vttablet_process.go | 2 + go/test/endtoend/vreplication/cluster.go | 68 +++---- go/test/endtoend/vreplication/config.go | 73 ++++++++ go/test/endtoend/vreplication/helper.go | 49 +++++ .../vreplication/unsharded_init_data.sql | 9 + .../vreplication/vreplication_test.go | 159 ++++++++++++---- go/vt/mysqlctl/rice-box.go | 177 ------------------ go/vt/vtctld/rice-box.go | 48 ++--- 9 files changed, 308 insertions(+), 279 deletions(-) create mode 100644 go/test/endtoend/vreplication/config.go create mode 100644 go/test/endtoend/vreplication/helper.go create mode 100644 go/test/endtoend/vreplication/unsharded_init_data.sql delete mode 100644 go/vt/mysqlctl/rice-box.go diff --git a/go.mod b/go.mod index 4274af73af3..a3cbaa5c8a1 100644 --- a/go.mod +++ b/go.mod @@ -100,6 +100,7 @@ require ( github.com/mattn/go-isatty v0.0.11 // indirect github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 github.com/mitchellh/copystructure v0.0.0-20160804032330-cdac8253d00f // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect @@ -156,6 +157,7 @@ require ( gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 // indirect gopkg.in/ory-am/dockertest.v3 v3.3.4 // indirect gopkg.in/square/go-jose.v2 v2.3.1 // indirect + gopkg.in/yaml.v2 v2.2.8 honnef.co/go/tools v0.0.1-2019.2.3 k8s.io/apiextensions-apiserver v0.17.3 k8s.io/apimachinery v0.17.3 diff --git a/go/test/endtoend/cluster/vttablet_process.go b/go/test/endtoend/cluster/vttablet_process.go index 675aa7e93bd..717e27353e8 100644 --- a/go/test/endtoend/cluster/vttablet_process.go +++ b/go/test/endtoend/cluster/vttablet_process.go @@ -381,6 +381,8 @@ func VttabletProcessInstance(port int, grpcPort int, tabletUID int, cell string, BackupStorageImplementation: "file", FileBackupStorageRoot: path.Join(os.Getenv("VTDATAROOT"), "/backups"), VreplicationTabletType: "replica", + VreplicationTabletType: "replica", + TabletUID: tabletUID, } if tabletType == "rdonly" { diff --git a/go/test/endtoend/vreplication/cluster.go b/go/test/endtoend/vreplication/cluster.go index a25d9aa10bb..44102083483 100644 --- a/go/test/endtoend/vreplication/cluster.go +++ b/go/test/endtoend/vreplication/cluster.go @@ -9,8 +9,6 @@ import ( "testing" "github.com/stretchr/testify/assert" - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" "vitess.io/vitess/go/vt/log" ) @@ -34,8 +32,6 @@ var globalConfig = struct { //TODO ports should be automatically (incrementally) allocated based on what is available? var ( - tabletIndex = 1 - tabletSubIndex = 0 tabletPortBase = 15000 tabletGrpcPortBase = 16000 tabletMysqlPortBase = 17000 @@ -123,7 +119,7 @@ func InitCluster(t *testing.T, cellName string) *VitessCluster { } // TODO -func (vc *VitessCluster) AddKeyspace(t *testing.T, cell *Cell, ksName string, shards string, vschema string, schema string, numReplicas int, numRdonly int) (*Keyspace, error) { +func (vc *VitessCluster) AddKeyspace(t *testing.T, cell *Cell, ksName string, shards string, vschema string, schema string, numReplicas int, numRdonly int, tabletIDBase int) (*Keyspace, error) { keyspace := &Keyspace{ Name: ksName, Shards: make(map[string]*Shard), @@ -133,31 +129,31 @@ func (vc *VitessCluster) AddKeyspace(t *testing.T, cell *Cell, ksName string, sh t.Fatalf(err.Error()) } cell.Keyspaces[ksName] = keyspace - if err := vc.AddShards(t, cell, keyspace, shards, numReplicas, numRdonly); err != nil { + if err := vc.AddShards(t, cell, keyspace, shards, numReplicas, numRdonly, tabletIDBase); err != nil { t.Fatalf(err.Error()) } - if err := vc.VtctlClient.ApplySchema(ksName, schema); err != nil { - t.Fatalf(err.Error()) + if schema != "" { + if err := vc.VtctlClient.ApplySchema(ksName, schema); err != nil { + t.Fatalf(err.Error()) + } } keyspace.Schema = schema - if err := vc.VtctlClient.ApplyVSchema(ksName, vschema); err != nil { - t.Fatalf(err.Error()) + if vschema != "" { + if err := vc.VtctlClient.ApplyVSchema(ksName, vschema); err != nil { + t.Fatalf(err.Error()) + } } keyspace.VSchema = vschema - fmt.Println("Starting vtgate") if len(cell.Vtgates) == 0 { + fmt.Println("Starting vtgate") vc.StartVtgate(t, cell) - //cell.Vtgates[0].WaitForStatusOfTabletInShard() //TODO } return keyspace, nil } // TODO -func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, shard *Shard, tabletType string) (*Tablet, *exec.Cmd, error) { +func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, shard *Shard, tabletType string, tabletID int) (*Tablet, *exec.Cmd, error) { tablet := &Tablet{} - var tabletID int - tabletID = tabletIndex*100 + tabletSubIndex - tabletSubIndex++ vttablet := cluster.VttabletProcessInstance( tabletPortBase+tabletID, @@ -172,7 +168,7 @@ func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, globalConfig.hostname, globalConfig.tmpDir, nil, - true) + false) assert.NotNil(t, vttablet) //vttablet.ServingStatus = "SERVING" vttablet.SupportsBackup = false @@ -193,17 +189,20 @@ func (vc *VitessCluster) AddTablet(t *testing.T, cell *Cell, keyspace *Keyspace, } // TODO -func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, names string, numReplicas int, numRdonly int) error { +func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, names string, numReplicas int, numRdonly int, tabletIdBase int) error { arrNames := strings.Split(names, ",") isSharded := len(arrNames) > 1 - dbProcesses := make([]*exec.Cmd, 0) - tablets := make([]*Tablet, 0) - for _, name := range arrNames { - fmt.Printf("Adding Shard %s\n", name) + tabletIndex := 0 + for _, shardName := range arrNames { + dbProcesses := make([]*exec.Cmd, 0) + tablets := make([]*Tablet, 0) + + fmt.Printf("Adding Shard %s\n", shardName) - shard := &Shard{Name: name, IsSharded: isSharded, Tablets: make(map[string]*Tablet, 1)} + shard := &Shard{Name: shardName, IsSharded: isSharded, Tablets: make(map[string]*Tablet, 1)} fmt.Println("Adding Master tablet") - master, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica") + master, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica", tabletIdBase+tabletIndex) + tabletIndex++ master.Vttablet.VreplicationTabletType = "MASTER" if err != nil { t.Fatalf(err.Error()) @@ -216,7 +215,8 @@ func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, } for i := 0; i < numReplicas; i++ { fmt.Println("Adding Replica tablet") - tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica") + tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "replica", tabletIdBase+tabletIndex) + tabletIndex++ if err != nil { t.Fatalf(err.Error()) } @@ -226,7 +226,8 @@ func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, } for i := 0; i < numRdonly; i++ { fmt.Println("Adding RdOnly tablet") - tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "rdonly") + tablet, proc, err := vc.AddTablet(t, cell, keyspace, shard, "rdonly", tabletIdBase+tabletIndex) + tabletIndex++ if err != nil { t.Fatalf(err.Error()) } @@ -235,7 +236,7 @@ func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, dbProcesses = append(dbProcesses, proc) } - keyspace.Shards[name] = shard + keyspace.Shards[shardName] = shard for ind, proc := range dbProcesses { fmt.Printf("Waiting for mysql process for tablet %s\n", tablets[ind].Name) if err := proc.Wait(); err != nil { @@ -254,7 +255,7 @@ func (vc *VitessCluster) AddShards(t *testing.T, cell *Cell, keyspace *Keyspace, } } fmt.Printf("InitShardMaster for %d\n", master.Vttablet.TabletUID) - err = vc.VtctlClient.InitShardMaster(keyspace.Name, name, cell.Name, master.Vttablet.TabletUID) + err = vc.VtctlClient.InitShardMaster(keyspace.Name, shardName, cell.Name, master.Vttablet.TabletUID) if err != nil { t.Fatal(err.Error()) } @@ -281,12 +282,12 @@ func (vc *VitessCluster) StartVtgate(t *testing.T, cell *Cell) { if err != nil { t.Fatalf(err.Error()) } - cell.Vtgates[0] = vtgate + cell.Vtgates = append(cell.Vtgates, vtgate) } // TODO func (vc *VitessCluster) AddCell(t *testing.T, name string) (*Cell, error) { - cell := &Cell{Name: name, Keyspaces: make(map[string]*Keyspace), Vtgates: make([]*cluster.VtgateProcess, 1)} + cell := &Cell{Name: name, Keyspaces: make(map[string]*Keyspace), Vtgates: make([]*cluster.VtgateProcess, 0)} vc.Cells[name] = cell return cell, nil } @@ -338,10 +339,3 @@ func (vc *VitessCluster) TearDown() { } } } - -func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { - t.Helper() - qr, err := conn.ExecuteFetch(query, 1000, true) - assert.Nil(t, err) - return qr -} diff --git a/go/test/endtoend/vreplication/config.go b/go/test/endtoend/vreplication/config.go new file mode 100644 index 00000000000..251ac80c83c --- /dev/null +++ b/go/test/endtoend/vreplication/config.go @@ -0,0 +1,73 @@ +package vreplication + +var ( + initialProductSchema = ` +create table product(pid int, description varbinary(128), primary key(pid)); +create table customer(cid int, name varbinary(128), primary key(cid)); +create table merchant(mname varchar(128), category varchar(128), primary key(mname)); +create table orders(oid int, cid int, pid int, mname varchar(128), price int, primary key(oid)); +create table customer_seq(id int, next_id bigint, cache bigint, primary key(id)) comment 'vitess_sequence'; +` + + initialProductVSchema = ` +{ + "tables": { + "product": {}, + "customer": {}, + "merchant": {}, + "orders": {} + } +} +` + customerSchema = "create table customer_seq(id int, next_id bigint, cache bigint, primary key(id)) comment 'vitess_sequence';" + customerVSchema = ` +{ + "sharded": false, + "tables": { + "customer":{} + } + } +` + /* + "vindexes": { + "reverse_bits": { + "type": "reverse_bits" + } + }, + "tables": { + "customer": { + "column_vindexes": [ + { + "column": "cid", + "name": "reverse_bits" + } + ], + "auto_increment": { + "column": "cid", + "sequence": "customer_seq" + } + } + } + + */ + merchantVSchema = ` +{ + "sharded": true, + "vindexes": { + "md5": { + "type": "unicode_loose_md5" + } + }, + "tables": { + "merchant": { + "column_vindexes": [ + { + "column": "mname", + "name": "md5" + } + ] + } + } +} +` +) diff --git a/go/test/endtoend/vreplication/helper.go b/go/test/endtoend/vreplication/helper.go new file mode 100644 index 00000000000..96ba7c738f2 --- /dev/null +++ b/go/test/endtoend/vreplication/helper.go @@ -0,0 +1,49 @@ +package vreplication + +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" +) + +func execMultipleQueries(t *testing.T, conn *mysql.Conn, database string, lines string) { + t.Helper() + queries := strings.Split(lines, "\n") + for _, query := range queries { + execVtgateQuery(t, conn, database, string(query)) + } + +} +func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { + t.Helper() + qr, err := conn.ExecuteFetch(query, 1000, false) + assert.Nil(t, err) + return qr +} + +func getConnection(t *testing.T) *mysql.Conn { + t.Helper() + vtParams := mysql.ConnParams{ + Host: globalConfig.hostname, + Port: globalConfig.vtgateMySQLPort, + } + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + assert.Nil(t, err) + return conn +} + +func execVtgateQuery(t *testing.T, conn *mysql.Conn, database string, query string) { + t.Helper() + if strings.TrimSpace(query) == "" { + return + } + execQuery(t, conn, "begin") + execQuery(t, conn, "use `"+database+"`;") + execQuery(t, conn, query) + execQuery(t, conn, "commit") +} diff --git a/go/test/endtoend/vreplication/unsharded_init_data.sql b/go/test/endtoend/vreplication/unsharded_init_data.sql new file mode 100644 index 00000000000..be0950b9d4a --- /dev/null +++ b/go/test/endtoend/vreplication/unsharded_init_data.sql @@ -0,0 +1,9 @@ +insert into customer(cid, name) values(1, 'sougou'); +insert into customer(cid, name) values(6, 'demmer'); +insert into merchant(mname, category) values('monoprice', 'electronics'); +insert into merchant(mname, category) values('newegg', 'electronics'); +insert into product(pid, description) values(1, 'keyboard'); +insert into product(pid, description) values(2, 'monitor'); +insert into orders(oid, cid, mname, pid, price) values(1, 1, 'monoprice', 1, 10); +insert into orders(oid, cid, mname, pid, price) values(2, 1, 'newegg', 2, 15); +insert into orders(oid, cid, mname, pid, price) values(3, 6, 'monoprice', 2, 20); \ No newline at end of file diff --git a/go/test/endtoend/vreplication/vreplication_test.go b/go/test/endtoend/vreplication/vreplication_test.go index ae48bcbadb7..c4227f8c1b1 100644 --- a/go/test/endtoend/vreplication/vreplication_test.go +++ b/go/test/endtoend/vreplication/vreplication_test.go @@ -2,51 +2,23 @@ package vreplication import ( "fmt" + "io/ioutil" "os" "testing" "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/test/endtoend/cluster" ) var ( - initialSchema = ` -create table product( - pid int, - description varbinary(128), - primary key(pid) -); -create table customer( - cid int, - name varbinary(128), - primary key(cid) -); -create table merchant( - mname varchar(128), - category varchar(128), - primary key(mname) -); -create table orders( - oid int, - cid int, - pid int, - mname varchar(128), - price int, - primary key(oid) -);` - initialVSchema = ` -{ - "tables": { - "product": {}, - "customer": {}, - "merchant": {}, - "orders": {} - } -} -` + vc *VitessCluster + vtgate *cluster.VtgateProcess + cell *Cell + conn *mysql.Conn ) -func TestBasicVreplicationWorkflow(t *testing.T) { - // TODO remove Setenv block below +func tmpSetupEnv() { // This TEMPORARY HACK is required because GoLand is not using my env variables and defining them in Run/Edit Configurations did not work :( err := os.Setenv("PATH", "/home/rohit/vitess-ps/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess-ps/bin:/home/rohit/vitess-ps/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess-ps/bin:/usr/local/go/bin:/home/rohit/vitess/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess/bin:/home/rohit/vitess-ps/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess-ps/bin:/usr/local/go/bin:/home/rohit/vitess/dist/etcd/etcd-v3.3.10-linux-amd64:/home/rohit/vitess/bin:/home/rohit/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin") @@ -61,15 +33,120 @@ func TestBasicVreplicationWorkflow(t *testing.T) { if err != nil { fmt.Printf("err %v\n", err) } +} + +func TestBasicVreplicationWorkflow(t *testing.T) { + // TODO remove this before final commit + tmpSetupEnv() + cellName := "zone1" - vc := InitCluster(t, "zone1") - if false { //TODO use the keep-data flag + vc = InitCluster(t, cellName) + assert.NotNil(t, vc) + if false { //TODO for testing: remove before commit defer vc.TearDown() } + cell = vc.Cells[cellName] - assert.NotNil(t, vc) - vc.AddKeyspace(t, vc.Cells["zone1"], "product", "0", initialVSchema, initialSchema, 1, 0) //TODO doesn't work with numReplicas = 0 + vc.AddKeyspace(t, cell, "product", "0", initialProductVSchema, initialProductSchema, 1, 1, 100) + + vtgate = cell.Vtgates[0] + assert.NotNil(t, vtgate) + vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", "product", "0"), 1) + + conn = getConnection(t) + defer conn.Close() + + insertInitialData(t) + shardCustomerTable(t) + + /* + split, merge, split/merge + Reshard 1->many, many->many (reshard into -40,40-), many->one + Migrate 1->many, many->1, many->many (orders to merchant new vindex) + + */ + + /* + Create Customer keyspace + Migrate Customer to unsharded + Reshard Customer + Migrate Orders to sharded customer + Create Merchant keyspace + + + */ + //shardCustomerTable(t) + //moveOrders + /* + shardMerchant(t) + materializeProduct(t) + materializeSales(t) + reshardOrders(t) + + create lookup vindex, no owner, by order id + order table: order id, + */ + //vdiff one for table, one for shard (introduce corruption) + //TODO test reverse replication for each + //TODO once dry run is implemented, test dry-run for each workflow and reverse replication + +} + +func insertInitialData(t *testing.T) { + fmt.Printf("Inserting initial data\n") + lines, _ := ioutil.ReadFile("unsharded_init_data.sql") + execMultipleQueries(t, conn, "product:0", string(lines)) + execVtgateQuery(t, conn, "product:0", "insert into customer_seq(id, next_id, cache) values(0, 100, 100);") + fmt.Printf("Done inserting initial data\n") + + qr := execQuery(t, conn, "select count(*) from product") + assert.NotNil(t, qr) + if got, want := fmt.Sprintf("%v", qr.Rows), "[[INT64(2)]]"; got != want { + t.Errorf("select:\n%v want\n%v", got, want) + } + qr = execQuery(t, conn, "select * from merchant") + assert.NotNil(t, qr) + if got, want := fmt.Sprintf("%v", qr.Rows), + `[[VARCHAR("monoprice") VARCHAR("electronics")] [VARCHAR("newegg") VARCHAR("electronics")]]`; got != want { + t.Errorf("select:\n%v want\n%v", got, want) + } +} + +func shardCustomerTable(t *testing.T) { + vc.AddKeyspace(t, cell, "customer", "0", customerVSchema, customerSchema, 1, 1, 200) + vtgate.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", "customer", "0"), 1) + //return + //execVtgateQuery(t, conn, "customer", "insert into customer_seq(id, next_id, cache) values(0, 100, 100);") + + vc.VtctlClient.ExecuteCommand("Migrate", "-cell="+cell.Name, "-workflow=p2c", + "-tablet_types="+"replica,rdonly", "product", "customer", "customer") + return + vc.VtctlClient.ExecuteCommand("MigrateReads", "-cells="+cell.Name, "-tablet_types=rdonly", "customer.p2c") + vc.VtctlClient.ExecuteCommand("MigrateReads", "-cells="+cell.Name, "-tablet_types=readonly", "customer.p2c") + vc.VtctlClient.ExecuteCommand("MigrateWrites", "customer.p2c") + //TODO Verify + +} + +func shardMerchant(t *testing.T) { + vc.AddKeyspace(t, cell, "merchant", "-80,80-", merchantVSchema, "", 0, 0, 300) + vc.VtctlClient.ExecuteCommand("Migrate", "-cell="+cell.Name, "-workflow=p2m", + "-tablet_types="+"replica,rdonly", "product", "merchant", "merchant") + vc.VtctlClient.ExecuteCommand("MigrateReads", "-cells="+cell.Name, "-tablet_types=rdonly", "merchant.p2m") + vc.VtctlClient.ExecuteCommand("MigrateReads", "-cells="+cell.Name, "-tablet_types=readonly", "merchant.p2m") + vc.VtctlClient.ExecuteCommand("MigrateWrites", "merchant.p2m") + //TODO Verify + +} + +func materializeProduct(t *testing.T) { + +} + +func materializeSales(t *testing.T) { + +} - //Initial cluster setup here, now testing various vreplication functionality +func reshardOrders(t *testing.T) { } diff --git a/go/vt/mysqlctl/rice-box.go b/go/vt/mysqlctl/rice-box.go deleted file mode 100644 index b2ee667d6e3..00000000000 --- a/go/vt/mysqlctl/rice-box.go +++ /dev/null @@ -1,177 +0,0 @@ -package mysqlctl - -import ( - "time" - - "github.com/GeertJohan/go.rice/embedded" -) - -func init() { - - // define files - file2 := &embedded.EmbeddedFile{ - Filename: "gomysql.pc.tmpl", - FileModTime: time.Unix(1562782645, 0), - - Content: string("Name: GoMysql\nDescription: Flags for using mysql C client in go\n"), - } - file3 := &embedded.EmbeddedFile{ - Filename: "init_db.sql", - FileModTime: time.Unix(1578077737, 0), - - Content: string("# This file is executed immediately after mysql_install_db,\n# to initialize a fresh data directory.\n\n###############################################################################\n# WARNING: This sql is *NOT* safe for production use,\n# as it contains default well-known users and passwords.\n# Care should be taken to change these users and passwords\n# for production.\n###############################################################################\n\n###############################################################################\n# Equivalent of mysql_secure_installation\n###############################################################################\n\n# Changes during the init db should not make it to the binlog.\n# They could potentially create errant transactions on replicas.\nSET sql_log_bin = 0;\n# Remove anonymous users.\nDELETE FROM mysql.user WHERE User = '';\n\n# Disable remote root access (only allow UNIX socket).\nDELETE FROM mysql.user WHERE User = 'root' AND Host != 'localhost';\n\n# Remove test database.\nDROP DATABASE IF EXISTS test;\n\n###############################################################################\n# Vitess defaults\n###############################################################################\n\n# Vitess-internal database.\nCREATE DATABASE IF NOT EXISTS _vt;\n# Note that definitions of local_metadata and shard_metadata should be the same\n# as in production which is defined in go/vt/mysqlctl/metadata_tables.go.\nCREATE TABLE IF NOT EXISTS _vt.local_metadata (\n name VARCHAR(255) NOT NULL,\n value VARCHAR(255) NOT NULL,\n db_name VARBINARY(255) NOT NULL,\n PRIMARY KEY (db_name, name)\n ) ENGINE=InnoDB;\nCREATE TABLE IF NOT EXISTS _vt.shard_metadata (\n name VARCHAR(255) NOT NULL,\n value MEDIUMBLOB NOT NULL,\n db_name VARBINARY(255) NOT NULL,\n PRIMARY KEY (db_name, name)\n ) ENGINE=InnoDB;\n\n# Admin user with all privileges.\nCREATE USER 'vt_dba'@'localhost';\nGRANT ALL ON *.* TO 'vt_dba'@'localhost';\nGRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost';\n\n# User for app traffic, with global read-write access.\nCREATE USER 'vt_app'@'localhost';\nGRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,\n REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,\n LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,\n SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER\n ON *.* TO 'vt_app'@'localhost';\n\n# User for app debug traffic, with global read access.\nCREATE USER 'vt_appdebug'@'localhost';\nGRANT SELECT, SHOW DATABASES, PROCESS ON *.* TO 'vt_appdebug'@'localhost';\n\n# User for administrative operations that need to be executed as non-SUPER.\n# Same permissions as vt_app here.\nCREATE USER 'vt_allprivs'@'localhost';\nGRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,\n REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,\n LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,\n SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER\n ON *.* TO 'vt_allprivs'@'localhost';\n\n# User for slave replication connections.\nCREATE USER 'vt_repl'@'%';\nGRANT REPLICATION SLAVE ON *.* TO 'vt_repl'@'%';\n\n# User for Vitess filtered replication (binlog player).\n# Same permissions as vt_app.\nCREATE USER 'vt_filtered'@'localhost';\nGRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,\n REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,\n LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,\n SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER\n ON *.* TO 'vt_filtered'@'localhost';\n\n# User for general MySQL monitoring.\nCREATE USER 'vt_monitoring'@'localhost';\nGRANT SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD\n ON *.* TO 'vt_monitoring'@'localhost';\nGRANT SELECT, UPDATE, DELETE, DROP\n ON performance_schema.* TO 'vt_monitoring'@'localhost';\n\n# User for Orchestrator (https://github.com/github/orchestrator).\nCREATE USER 'orc_client_user'@'%' IDENTIFIED BY 'orc_client_user_password';\nGRANT SUPER, PROCESS, REPLICATION SLAVE, RELOAD\n ON *.* TO 'orc_client_user'@'%';\nGRANT SELECT\n ON _vt.* TO 'orc_client_user'@'%';\n\nFLUSH PRIVILEGES;\n\nRESET SLAVE ALL;\nRESET MASTER;\n"), - } - file5 := &embedded.EmbeddedFile{ - Filename: "mycnf/default-fast.cnf", - FileModTime: time.Unix(1579019392, 0), - - Content: string("# This sets some unsafe settings specifically for \n# the test-suite which is currently MySQL 5.7 based\n# In future it should be renamed testsuite.cnf\n\ninnodb_buffer_pool_size = 32M\ninnodb_flush_log_at_trx_commit = 0\ninnodb_log_buffer_size = 1M\ninnodb_log_file_size = 5M\n\n# Native AIO tends to run into aio-max-nr limit during test startup.\ninnodb_use_native_aio = 0\n\nkey_buffer_size = 2M\nsync_binlog=0\ninnodb_doublewrite=0\n\n# These two settings are required for the testsuite to pass, \n# but enabling them does not spark joy. They should be removed\n# in the future. See:\n# https://github.com/vitessio/vitess/issues/5396\n\nsql_mode = STRICT_TRANS_TABLES\n"), - } - file6 := &embedded.EmbeddedFile{ - Filename: "mycnf/default.cnf", - FileModTime: time.Unix(1579632282, 0), - - Content: string("# Global configuration that is auto-included for all MySQL/MariaDB versions\n\ndatadir = {{.DataDir}}\ninnodb_data_home_dir = {{.InnodbDataHomeDir}}\ninnodb_log_group_home_dir = {{.InnodbLogGroupHomeDir}}\nlog-error = {{.ErrorLogPath}}\nlog-bin = {{.BinLogPath}}\nrelay-log = {{.RelayLogPath}}\nrelay-log-index = {{.RelayLogIndexPath}}\npid-file = {{.PidFile}}\nport = {{.MysqlPort}}\n\n# all db instances should start in read-only mode - once the db is started and\n# fully functional, we'll push it into read-write mode\nread-only\nserver-id = {{.ServerID}}\n\n# all db instances should skip the slave startup - that way we can do any\n# additional configuration (like enabling semi-sync) before we connect to\n# the master.\nskip_slave_start\nslave_load_tmpdir = {{.SlaveLoadTmpDir}}\nsocket = {{.SocketFile}}\ntmpdir = {{.TmpDir}}\n\nslow-query-log-file = {{.SlowLogPath}}\n\n# These are sensible defaults that apply to all MySQL/MariaDB versions\n\nlong_query_time = 2\nslow-query-log\nskip-name-resolve\nconnect_timeout = 30\ninnodb_lock_wait_timeout = 20\nmax_allowed_packet = 64M\nmax_connections = 500\n\n\n"), - } - file7 := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mariadb100.cnf", - FileModTime: time.Unix(1579019403, 0), - - Content: string("# This file is auto-included when MariaDB 10.0 is detected.\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the master goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when masters are\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\nslave_net_timeout = 60\n\n# MariaDB 10.0 is unstrict by default\nsql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no slaves. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a master that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n"), - } - file8 := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mariadb101.cnf", - FileModTime: time.Unix(1579019403, 0), - - Content: string("# This file is auto-included when MariaDB 10.1 is detected.\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the master goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when masters are\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\nslave_net_timeout = 60\n\n# MariaDB 10.1 default is only no-engine-substitution and no-auto-create-user\nsql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_AUTO_CREATE_USER\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no slaves. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a master that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n"), - } - file9 := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mariadb102.cnf", - FileModTime: time.Unix(1579019403, 0), - - Content: string("# This file is auto-included when MariaDB 10.2 is detected.\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the master goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when masters are\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no slaves. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a master that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n"), - } - filea := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mariadb103.cnf", - FileModTime: time.Unix(1579019403, 0), - - Content: string("# This file is auto-included when MariaDB 10.3 is detected.\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no slaves. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a master that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n\n"), - } - fileb := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mariadb104.cnf", - FileModTime: time.Unix(1579019403, 0), - - Content: string("# This file is auto-included when MariaDB 10.4 is detected.\n\n# enable strict mode so it's safe to compare sequence numbers across different server IDs.\ngtid_strict_mode = 1\ninnodb_stats_persistent = 0\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no slaves. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a master that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\nexpire_logs_days = 3\n\nsync_binlog = 1\nbinlog_format = ROW\nlog_slave_updates\nexpire_logs_days = 3\n\n# In MariaDB the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n\n"), - } - filec := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mysql56.cnf", - FileModTime: time.Unix(1579019403, 0), - - Content: string("# This file is auto-included when MySQL 5.6 is detected.\n\n# MySQL 5.6 does not enable the binary log by default, and \n# the default for sync_binlog is unsafe. The format is TABLE, and\n# info repositories also default to file.\n\nsync_binlog = 1\ngtid_mode = ON\nbinlog_format = ROW\nlog_slave_updates\nenforce_gtid_consistency\nexpire_logs_days = 3\nmaster_info_repository = TABLE\nrelay_log_info_repository = TABLE\nrelay_log_purge = 1\nrelay_log_recovery = 1\nslave_net_timeout = 60\n\n# In MySQL 5.6 the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n# MySQL 5.6 is unstrict by default\nsql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the master goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when masters are\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no slaves. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a master that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n"), - } - filed := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mysql57.cnf", - FileModTime: time.Unix(1579019403, 0), - - Content: string("# This file is auto-included when MySQL 5.7 is detected.\n\n# MySQL 5.7 does not enable the binary log by default, and \n# info repositories default to file\n\ngtid_mode = ON\nlog_slave_updates\nenforce_gtid_consistency\nexpire_logs_days = 3\nmaster_info_repository = TABLE\nrelay_log_info_repository = TABLE\nrelay_log_purge = 1\nrelay_log_recovery = 1\n\n# In MySQL 5.7 the default charset is latin1\n\ncharacter_set_server = utf8\ncollation_server = utf8_general_ci\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the master goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when masters are\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\n# When semi-sync is enabled, don't allow fallback to async\n# if you get no ack, or have no slaves. This is necessary to\n# prevent alternate futures when doing a failover in response to\n# a master that becomes unresponsive.\nrpl_semi_sync_master_timeout = 1000000000000000000\nrpl_semi_sync_master_wait_no_slave = 1\n\n"), - } - filee := &embedded.EmbeddedFile{ - Filename: "mycnf/master_mysql80.cnf", - FileModTime: time.Unix(1578945496, 0), - - Content: string("# This file is auto-included when MySQL 8.0 is detected.\n\n# MySQL 8.0 enables binlog by default with sync_binlog and TABLE info repositories\n# It does not enable GTIDs or enforced GTID consistency\n\ngtid_mode = ON\nenforce_gtid_consistency\nrelay_log_recovery = 1\nbinlog_expire_logs_seconds = 259200\n\n# disable mysqlx\nmysqlx = 0\n\n# 8.0 changes the default auth-plugin to caching_sha2_password\ndefault_authentication_plugin = mysql_native_password\n\n# Semi-sync replication is required for automated unplanned failover\n# (when the master goes away). Here we just load the plugin so it's\n# available if desired, but it's disabled at startup.\n#\n# If the -enable_semi_sync flag is used, VTTablet will enable semi-sync\n# at the proper time when replication is set up, or when masters are\n# promoted or demoted.\nplugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so\n\n# MySQL 8.0 will not load plugins during --initialize\n# which makes these options unknown. Prefixing with --loose\n# tells the server it's fine if they are not understood.\nloose_rpl_semi_sync_master_timeout = 1000000000000000000\nloose_rpl_semi_sync_master_wait_no_slave = 1\n\n"), - } - filef := &embedded.EmbeddedFile{ - Filename: "mycnf/sbr.cnf", - FileModTime: time.Unix(1579019392, 0), - - Content: string("# This file is used to allow legacy tests to pass\n# In theory it should not be required\nbinlog_format=statement\n"), - } - fileg := &embedded.EmbeddedFile{ - Filename: "zk-client-dev.json", - FileModTime: time.Unix(1562782645, 0), - - Content: string("{\n \"local\": \"localhost:3863\",\n \"global\": \"localhost:3963\"\n}\n"), - } - filei := &embedded.EmbeddedFile{ - Filename: "zkcfg/zoo.cfg", - FileModTime: time.Unix(1562782645, 0), - - Content: string("tickTime=2000\ndataDir={{.DataDir}}\nclientPort={{.ClientPort}}\ninitLimit=5\nsyncLimit=2\nmaxClientCnxns=0\n{{range .Servers}}\nserver.{{.ServerId}}={{.Hostname}}:{{.LeaderPort}}:{{.ElectionPort}}\n{{end}}\n"), - } - - // define dirs - dir1 := &embedded.EmbeddedDir{ - Filename: "", - DirModTime: time.Unix(1578267519, 0), - ChildFiles: []*embedded.EmbeddedFile{ - file2, // "gomysql.pc.tmpl" - file3, // "init_db.sql" - fileg, // "zk-client-dev.json" - - }, - } - dir4 := &embedded.EmbeddedDir{ - Filename: "mycnf", - DirModTime: time.Unix(1579632282, 0), - ChildFiles: []*embedded.EmbeddedFile{ - file5, // "mycnf/default-fast.cnf" - file6, // "mycnf/default.cnf" - file7, // "mycnf/master_mariadb100.cnf" - file8, // "mycnf/master_mariadb101.cnf" - file9, // "mycnf/master_mariadb102.cnf" - filea, // "mycnf/master_mariadb103.cnf" - fileb, // "mycnf/master_mariadb104.cnf" - filec, // "mycnf/master_mysql56.cnf" - filed, // "mycnf/master_mysql57.cnf" - filee, // "mycnf/master_mysql80.cnf" - filef, // "mycnf/sbr.cnf" - - }, - } - dirh := &embedded.EmbeddedDir{ - Filename: "zkcfg", - DirModTime: time.Unix(1578087479, 0), - ChildFiles: []*embedded.EmbeddedFile{ - filei, // "zkcfg/zoo.cfg" - - }, - } - - // link ChildDirs - dir1.ChildDirs = []*embedded.EmbeddedDir{ - dir4, // "mycnf" - dirh, // "zkcfg" - - } - dir4.ChildDirs = []*embedded.EmbeddedDir{} - dirh.ChildDirs = []*embedded.EmbeddedDir{} - - // register embeddedBox - embedded.RegisterEmbeddedBox(`../../../config`, &embedded.EmbeddedBox{ - Name: `../../../config`, - Time: time.Unix(1578267519, 0), - Dirs: map[string]*embedded.EmbeddedDir{ - "": dir1, - "mycnf": dir4, - "zkcfg": dirh, - }, - Files: map[string]*embedded.EmbeddedFile{ - "gomysql.pc.tmpl": file2, - "init_db.sql": file3, - "mycnf/default-fast.cnf": file5, - "mycnf/default.cnf": file6, - "mycnf/master_mariadb100.cnf": file7, - "mycnf/master_mariadb101.cnf": file8, - "mycnf/master_mariadb102.cnf": file9, - "mycnf/master_mariadb103.cnf": filea, - "mycnf/master_mariadb104.cnf": fileb, - "mycnf/master_mysql56.cnf": filec, - "mycnf/master_mysql57.cnf": filed, - "mycnf/master_mysql80.cnf": filee, - "mycnf/sbr.cnf": filef, - "zk-client-dev.json": fileg, - "zkcfg/zoo.cfg": filei, - }, - }) -} diff --git a/go/vt/vtctld/rice-box.go b/go/vt/vtctld/rice-box.go index 860d92bc0ff..a8c78067b22 100644 --- a/go/vt/vtctld/rice-box.go +++ b/go/vt/vtctld/rice-box.go @@ -11,127 +11,127 @@ func init() { // define files file2 := &embedded.EmbeddedFile{ Filename: "16e1d930cf13fb7a956372044b6d02d0.woff", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("wOFF\x00\x01\x00\x00\x00\x00HX\x00\x12\x00\x00\x00\x00\u007f\x8c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00GDEF\x00\x00\x01\x94\x00\x00\x00@\x00\x00\x00L\x050\x04\xf2GPOS\x00\x00\x01\xd4\x00\x00\x05\xcb\x00\x00\r\x0e\xce\xf6\xe4IGSUB\x00\x00\a\xa0\x00\x00\x00\\\x00\x00\x00\x88\x94&\x9eROS/2\x00\x00\a\xfc\x00\x00\x00V\x00\x00\x00`\xa0\xa7\xb1\xa6cmap\x00\x00\bT\x00\x00\x01\xa5\x00\x00\x038\xe2\x83!Zcvt \x00\x00\t\xfc\x00\x00\x00L\x00\x00\x00L$A\x06\xe5fpgm\x00\x00\nH\x00\x00\x01;\x00\x00\x01\xbcg\xf4\\\xabgasp\x00\x00\v\x84\x00\x00\x00\f\x00\x00\x00\f\x00\b\x00\x13glyf\x00\x00\v\x90\x00\x006r\x00\x00b&\b\x18.\xfahdmx\x00\x00B\x04\x00\x00\x00d\x00\x00\x00\xe8\x04\x02\xf8\xe2head\x00\x00Bh\x00\x00\x006\x00\x00\x006\xf8F\xab\x0ehhea\x00\x00B\xa0\x00\x00\x00\x1f\x00\x00\x00$\n\xba\x06}hmtx\x00\x00B\xc0\x00\x00\x02E\x00\x00\x03v\x81ZQ\x9floca\x00\x00E\b\x00\x00\x01\xbe\x00\x00\x01\xbe:\xfc!\xa8maxp\x00\x00F\xc8\x00\x00\x00 \x00\x00\x00 \x03\x0e\x02\xf9name\x00\x00F\xe8\x00\x00\x00\x99\x00\x00\x01\x10\x10o,\xa9post\x00\x00G\x84\x00\x00\x00\x13\x00\x00\x00 \xffm\x00dprep\x00\x00G\x98\x00\x00\x00\xbd\x00\x00\x00\xdbt\xa0\x8f\xecx\x01\r\xc1\xb1\x01\xc1P\x18\x06\xc0\xfb\x9e\nְ\x86\xce\x1a\xf6\xd0\x01@\xb2C\xd2e\xa9\xec\x92\xffN\xb0\x034\x1b{\aqt\x12\xe7\xbar\xadq\xaf\xf1\xaa\xf1\xad\xf1\u05cb\xc1\xa8\x99̲\x00\x0f\x01\n#x\x01\x94\xd2\x03p#L\x18\xc6\xf1\xff\xe6Ҟsi\xda϶m\xdb:۶m۶m\xdb6˳m\xb3\xef\xf7\xcc\\\xa6\x97\xe3\xf4v\xe7\xb7xVm\x12\x1c\x90\x86\xacT\xc5\xfb\xf3\xaf\u007fg\xe5\xc9\"\xb5*\x97\xe5\xd5\x12\x95\x8b\x95\xe1ò\x85\xaa\x96\xe7[\xbc\x00f8\b\x19\xbb\x90\xb1'd\x9c\x02o\x99b\x95\xcb\xf3\xe4\xdd-\xe0\x10\xbcx@\xad\xcf\xe3K\xd1ٛ\x19Gc\xcd\xe0k\xd7ҵ$ѵw}\xddT7\xday]\x1a\xe7\xd7\xe8Q\xf7\xbc\xfb\xd1eu\xb3]qwޕ\x95\x9b\xb5\xb6\xbb\xaa\xda2X\xa7\xea\\R\xd5\r\xa3\x83ujR\x9d\xad3wW݁\xe3k\xc2IK$\xaf\xf0\x1a\xaf\xf3\x06o\xf2\x1e\xef\xf3\x11\x1f\xf39_\xf2\x15\xdf\xf0-\u007f\xf0'\u007f\x93\x89\xccd!;\xb9\xc9G\x17\xbaҝ\x1e\x8cd\x14\xa3\x19\xc3X\xc61\x9e\tLd\x12\x93\x99\xc2,f3\x87\xb9\xccc1\xabY\xc3Zֱ\x99-D\x13C,qlg\a\x8eH{^mv;\xa86\x8f-B\xad\r\xc5CN|4\xb2\x01\xa4\xc2k\xf1Z\x9f\xa6|\x05\xe5\x12gR\xd7^\xd7\xca?4\xb7\xf6\xb4\xb2AxIO\x14?\x10\xa1]{)D$JȠ$\x9d\x92cJ|\xe8V;@\x8a\xe0\xce\x00ڥٟڕ\x93\x0fɣ\xb3\xdaAZe/\xd9\x12r\xda)\xed߅\xf6\xdbA~\xb0\xf3\x14R\xe2\xe1O;\xa9\xfc(\x8eFZ\xf5\x91F\xef\xe9F|\xb6\x01ݪ\xf5\xa3z1\x9e\x9c\xea\xf5\x02\xe5l\xb0\xf8;VC\xf7?8\r\xfe\xdd\xc9O\xc1\xba\xd9\x12B\xe7-\x83\xfd2\x82\xaf\xd8z\x9b\x18\xb2\x9e\x9d\xe4\x15\x0f\x91\x84\xabBZU\x87_\xd5C\x80\xc0\xffM\xd6\x03\xce]{\x00E\xf1ut\xed{\xcfy\xaf\xb6m\u007fQm\xbb\x9dL\xa3\xda\x18Am۶9\x99\xee\xec\xa8\xf9\xc5\xf8cE\x9b\x88A\f#f$\x13)2\x95.Z\xcca\x1e\xddX =Y,\xbdX\xc1jz\xb3V\xfa\xb3Q\x06\xb0\x99\xad\fd\x87\f\xe1 \xc7\x18\xcaI\xde1\x9a\xcf\xfc`\xb5\x97\xd0FB&\xf9\xd6\uefb5;\r\xe9AKz0X\x02\x86H\xccP\x89\x18&\t#\x19E\xc8h)1F\xf2\x8c\x95\x02\xe3\x18O\xc8\x04\x89\x98\x88\xdf*9\xa6I\x89\xe9Rf\x86Ԙ)\x15fI\x95.\xa93G\x1a̕\x16\xf3\xa4\u9ff5\xfd\xb7\x8e\x17`\x87U\x92\xb2ZZ\xfe\xe7\u007f\xfeg\xc6f\xf9\x9f-\x92\xb1U\xba\xf9\xcf-o\xc5\xee\x1c\x96\x1eތ\xdd9&=8)\x11\x17$\xe6\"WH\xb8\xcaMBnq\x8f\x88\xfb<\xa2\xc4c\xa9\xf2\x84\x974x%M^\xab]\x9b\xcf\xd2\xe1\x8b\n\xa6.\x98\x112\xd1\x05c\x17\x8c]\xb0FSZVc\x90T\xdc1d\x84\xb8\x1d\xff\xb9\x94\xfaK\xc8\x14\xe9\xb8W\u07bd\"\xf7*\xb9Wν\xaa\xeeU\xfe\xa7W\xca<5j\xb0H\x1an\xd4v\xa3čڬ\x91\x06\xeb$c\x83\x14\xdc+p\xaf\xa6{\x05\xeeUd\x9b\x84쒐\xdd\xd2`?\apAb\x17\xac\xb9`\xec\x825NH\x8bS\x9c\xa7\xe2\x8enG\xe8v\x91ە\xdd.U\xb9w4\\\xad\xedj\t?\xf9C\xc1킿H\xf9&\xa9\x00x\x01M\x89\xc5\x01BA\x10C_\x06w\xe7\xbc\x05l\x1f\xb8C\x01{\xc1\xe1DA\xd0(n\u007f4\xc9C@\x16O\x1b\xed\xc3\xe9H\x9a,\x9a/\xa6\x8e*\xdcn\b>_\xd8~\xb3\n\x14_\xb7\t\x18\xbc\xf8\x8b\x91\xc4\x11\aD\x1a\xe1??\xce\x19c\x10!\xd5\x1f\xb9`\x8c\x11\xa2{\a\x98Q\r\xa0x\x015\xc57\x01\xc2P\x00\x06\xe1/\xbd\x88H\xd1\xc0\xc6Ė1\xa8x6\x98\xd8Y\xe9\x16\xb0\x82\x03\x84\xd0\xdb_\xee$\xe92Z\xc9\xc86ل\xa8\xfd:9\v1b\xff\\ԏ\xb9~\x81\x1e\x86q\x1c\xccx\xdcrwB\xb1\x89u\xa2#\xa4\xd3\xec\xa4Ӊ\x9f\x9aF\x0e\x93\x00\x00x\x01}\xcb\x03\x8cnW\x00\x85\xd1\xf5\xa3\xb6ݞ\xba\rk\xc5\xc9Ĭ\xed\x18\xb5m۶;\xb6\xcd`l{&\xaa\xadۛ\x9c\"|;Y\xd9ч\\\xe4@9\x19\xf1\xb7's\x85|\xa6\x00\xa7\b\xf2\xf6\xc6N\xee\xf1\x9eBEZt\x1b\xb0\xeekI\xe6\xf8\xcc\xd9\xd9\xee\xectv6\xec\x19\xf6\x0f\a\x87\xc3\xc2\x19\xa1 \xdc\x14\n\x0f\xdb=I\xc0N\x82\xf7\xbc\xafH\xb1V=\x06m\xf8&\xedΊ]\xd8#\xec\x1b\x0eL\xbb\xd3\xff\xefd\x92\x9f\x92U['\xed\xc9oI\x1b\xc9fj9uQrƟ\xbd\u007fV\xfdY\xb9x\xcf❋w,\u07bax\xe5\xe2\x05\x8b\xc7.|7\xff\xb4\f\x00\xe0\x18\x9c\x80Kly\x9f\xfb\u0097\n\x15)v\xbf\xcfԪS\xafA\xa3&\x0fxP\xb3\x16\xadڴ{\xc8\xc3:t\xeaҭG\xafGE1Zp-Zх\xcf\xd1\x1d\xb9\x1f=\x18@\t\x06#\x9fa\b\xe3\xa8\xc5\x04&\xf1\x00\xa60\x87f\xccc\x01\x0fa\x11k\xe8\xc0z\xe4\x11l\xe0k\xf4\xe1\x1bѣ\xf8\x16?a\x18?\xe3\x17<\x86_\xf1'&\xf1\x97\xe8\t$\xa292\xc7G\x9e$sV\xeal1\xe2i\x8e\x01x@\x0e\x1e\x00\x18\xb6T\xfe\x89\xb2x\x0f\xbc!\xee9\xc4y\xd1\x13/F\x9c,y\xd6\xed\x13}3\xf4\x8f\xbe\n\x921%\xa0a#d\x15\x12\x84\xc4\xe4\xf8@\u007f9&\x16\xf5\xdf\xe9\xf3\xf1ٔэ\x1b\x8fNA\x1bĀ\xc7\xef\xd6o֬\xdfK/\x01\x86\x1cф\xad2\x80\bz\b\x06\xbb\x10\xa7`O\x81w!ή+\xb7\v\x06\xbbG9}\x9b`5'Hl\x83\x02\"ơ%ȗm\xa5id=\xfa%\x87m\x00\xc3T\x00\xf1\x1a\xa56\x14\"!_\xa5֛\x12\xe8]M\xad\x81\x9e\x18艳\x1f\x86\xaa\x93\x10z\x12\xc2\xfa\x11\xee͈\x9fa\x0f\x8f\vR@\xa8\xee\x93],\xb7\x87\xb3\xb1b[\xafr\xdam\xc5\xc3\xf9\xa3\x97\xc1\xee[ζ\xa6r\xbb\x87\xc1\xee\xcf\xef\n,\xb7\x83\xc1\x1eįD0\xfa\xa3)\xed֤hs4\xfdK\x10\xd8_B\x80\x95\xffY\x05z\x15;ȣ.\xf9]\x1e\"}[BPx\xb7\xfcnȣ\xdb\xf2n\x17\xbek{\xffI\x97\xe5Eȣ-y\x84֓4\xb4>\x17\r\xc9C[\xc8 \xf6\x97G\x8arI\x1a\xb6\xa2!\x00\b\xc6V6\x14\x8d\xf2F\xa8\vij\xef\xf5\xb4[\xfa\xea\x0e\xc7Г\x18\xd6G\x1c\xa3g\xec\xd5\x1b\xec\xde\xe5lk`[Ō\x9ev\b\x1b\x94`\xe7Y\x8c\xc1\x1eYζ\xb1l\xab\xd4~\xfa\v\xedTb\xac-66)19%)! 00\xc0\x9a\x18k\x8d\x91\x03\xfc\x03Ez\xe2/\xeb\x02\xacI\xf4\xe7\x04\xff\xc0\x84\xf8d\xa1\xcf\xdcu\x9f~rl\xe7\x9a]G\x0egO\x98:k!j\xb4\xa7\xeb\xf9\x0fV\x97^+Y\x99=\xbf\x00M}sVB\xab+[߹\xea\xff\xf9\x1d˃\x8bKwd\x8dM\xcb\x1c:\xb5h\xe4\xeeK\xe6cnjߝ\xca+\x98\x05\x12\xa4W\xfe$͗N\x82\x0f\x84@,$Bc\x98\xa6\xf6U\xa0\xdd\x13\xaa\xfbj\xa5'V\xdaW&=\x9e\xe5v\xcf8\xc5(V\x91ͮ)A\xce3\xab\xc1\x1e^NǛ\x1e(u\x9c\x97\xc3\rJ\xa3\xa7gJ\x92\x95KdR\x9c\x92\"X\xd9QJ\\\xc3F\xe6\xc4\xe4\x84\xf8\xc0\x00\xdaQkLl\x92\x85\xf72))1\x96r\x00%&G\xf1_\xa2\\\u007f`\xbc\x91lȒ\xbes\xfe\xc2\xed;\xe7\xce+^ܧS\xfb\u07bdV\xf7\xc2/\xedD\U000ae764\x82]\xe9Ыw\xc7\xf6}\x89.\x1d\x9fN\x17\xd7\xce/ޓ\xd3~\xc1Νy\xbaׇ\x0f\xe9\xd9\xf1\xf5\xb4\xb4n\x15W\xe6\xefޕ\xd7~\xfe\xee\x9dy\xf2\x1bÇ\xf6\xec\xf4ư\xa1=~o-Nh\r\x02\f\xaf| \xfe.\x9d\x80\b\xb0A\x02,|\x8e\x1c\x84ѓ\xb0j\xc1\x0f\xa2'AL(b\u0098P(u\xf4al'\xc4\xd4a;C\x9d\x18\xb63\xaa\x17\xcd\xea-\xc1\xea.\xd0\x18\xcc\x18\x11fP\"\xd1S>EG\x1aU\xd1R\x1a=\xbd\xc8\x19\x95\xc2Ą\xb1\x8b\x89\x85\x0e%S\xa1\xd0Y\xac6\x99\xb2\xae\x86\x14\x9f\x82tLj\x04\u007fKJr\xb2ʨᓷ\x0fn}bW\xc1\x89\xbe\xe3F\xa16mvf\x95\xdf\x1d\xdc\xe9\xf4[\x9f\x12\x82>[5\x9dl\x8a\u07b9.fʔ6\xf1\xc3^\xed6\b-J\xb7O\x9e\xb2\xbc\xfd\x8e\x0f\xf7-쳦\xdb\x1bd\xe6\xbc͕\xbb\xfe\x99غ\xedW\x9dǣ\xddAY\xf3\xa6,\x13\xbe\x1d\xb4\xbc{\xc3\xde\xcd_\xe9;\x0e\x10\xbc%F\x83\x83\xdb\x1c\x83jq\xa4*\x8bC\x89e6\xc6\x110\x17\xad\x91V\x90\xe5\xe8K@\xb0\x90\xd4\xc1k\xe4Q`\x04=\xd8}\xe3\xec\"\x1f|\x93)\xc5\"cAg0\aZt\xb16\xbc0\xf3\xbfscW\x1eѣ\xa5='\xc7.\xcc|\x80\xdf\xf8\nmA=\xdaM\x1fO\x12\xc9\xd7=\xc9,\xf2M\xf1\xa0\x8cN\xef\xa1\x1eLWc\xe9;S\xdcމ\xfc1\xd6ْM\xe6\xa4D\x8cm)\x81&\x13N\x99\xfc\xeb\x02\xdb\xfa#\xb8Ϧ!\xb6\x05\x0f\xa7\xe2\xb6_\x92\xb7Ȏ\x8e\x19SЯ(.\xea*JG\x11]2:\x92b2\f\x10\x84\xe1\xc1BO\xc9\x0e\xbe\x10\xa5\x8e\xbbHGW\xe4\xe3^\x1eL\x1dI\x90]O\xf7\xfa\xb8 \xdaT\xcd\x14\x89\x9a\xe7\x9a\x16ɬ\x13\x04\x1b\xeaD\xae6@\r\xf4\xeb\xf8\x10\b`\x04:r\nr5L\xb4a\xecC\x82\xd0\xf78\xb2\x10\x10,\xa7~l\n\\\x01=\x84\xaa\x9c\xd1Q\xce\xe8\xb81\x94\x9cO\xa50o\x15\xc0\xdc\xd5\xf2&\x13^ziB\x93\xc1\r[\xb6lؠys@`\xaa\x9c/\x18\xb8'4\x03%\xcd\xd5\x15\xd2'-V\x94\xb0\u007f3\x1eT4S\xae\x0f\x80a\"\xd5BO\xaa\x85>\xb4\xbd&j\x8b\x9e\xf4fOz\xb3\xd3X1\x1d\xf44(F\xa4\xb1L\xae\xbd0`k\f\x16\x92\f\xa6\x84x\x93\xd9\xc6\xf5Bg\xe4\xe6$E\xf4\xbc\xfb\xe0\x87{\xe2ݟ\u007f\xba+\x94,\xc8_6\x0f\xe7\xe6\xe5.\x14\xf0\x18r\x84\x9c@I(\xe1o\xd4\n5&\xd7\xc8)\x9f\x9f>\xbbq\x97\xdc|p\xff\xfa7\x80\xa0\x18\x00ߒ>\x00\xb9\x9a.\x89\x92\"=\x1bl\x80\xc4\x14B\x8cS\x04\xa7vc\x81]\xa41\x85\xd5(%\xd5L\xc0\xb7JI\x1e6\x85\x8bWrw\x9df#\x92\n &\xd0^[\xe05\xd0:\\ͻ-.\x9e\xa6\\\x11\f\x16֒\xc1\xa0\xe8\x9d\xd7\r\xcc7)\x01\x16\x83\xda^t\x12\xaa\xb6\v\xd4\xc7\xd4\xe0f\x04\xa1h1\xa1\xa2\x1dz4\xbe\xe7≋ו \xe1\xd6\xf9\a4\xac\x99\x8e?_\x88\x1b\xce\xde\xdcs\xc2\xcaMK\xce>\xfat\xffg\xe43\xd2\x17\x10\f\xa8| \xfcC\xa9\x8b\xad\xf6\x18>\x94 \x9fj\xea\x82\xe9I0\xa3\x0e|\xb8]\x838\xe52\xa0Te3\xa0\f%\x8e\x1d\r\x044\xc1\ue8d1t\x1f\x1e\xb9\x81A\x89u2\xc9\x1c\v\xec\xf9`\x83=\xac\x9cn\x95H\xd7\x01M\xa0\xdd0\xf0^<5\x86\xccS\x8aQ5T/\xc9:i\xb3\xa2\xec\xbc\xf9\x05\x95\xd3\xf2K\x1c\x9f\\\xfa1k\xe4\xb4\xf9\x95@\xd2Hei\xc1\xac\xece\x1bW\xe5\t\xf18{\x02\x82܌w\xbf\xbd\xfd\xf1@\xa5^\xac}\xf6\x89\xff\xdc=4q\xf1\x92\xf9\xb3s0`H\x02\x10\xbbPY\xf5\x04_\xe8\xa8\xf5\x00N\xb3\xc0\xfa\x8a\xf4\"\xa3\x15Q\x03\xe8\xec\x14\x8dr\x10\x8br\x14\x0fً\a\x94\xfc&F|\x80\x95\xab4J@\xd1F\x9d\x90}\xee\\\xa9#\x1d/9阋N\x06\xa2\x1f\n\xc9{\xa8\xdb\x18\xe1\u05ca&\xf8\\-@\xb0\x9e\xea@}JE8\fVi@\xb4YDip2\x9fi\x002(\xa2\x93{:\x1f\xc4(\xd2\xc5)!\u038b\x92.\x84]\xa4\xdc6\x96\xb3\x11\xf0w\x12\xabcޛ\xda\t3\xb57\x02e\xa9)\xc0\x1fSolK\b\xe4\xdcd\"\xa3\xd3]\xea\x84\u007ft\xbc_oT\xce\xe9\x1f\u007f\xbf}\xfc\x0f\xe3>\xe3\xf2)sWm^\x90ٶ\x11\xbe\x8do\xec!\x13[\x90\u007f\xee\xdd'\x8e\x1b\x1f͜m߰r\u007fR-\xc0\xb0\x80\xd2\x1f&\xed\a\u007f\x88\xa66\xcf)\xc2\xee=`\xe3\x8f\\{\x00U=\bp\x15\x14%\xccU\x0e\x02\xac\xb2\xa8{J.$%\x19\xa0\xa6@\x95;\xa0Z\b\x847bnV\x82\xff\xc4\xd3G\xbf\xfe\xf3\xd2\x15R\x81z\xa3\xeeW\an\x89ܖ93\u007f\x85\xb4\u007f\x93\xf8\xe8\xfe\x02\xf2\xc7\xf5\xfb\xe47\xd4\xca\xd1\x1e\xadD\xbb%\xc7\xf8\t\xbd\xda\x1c\xb8sxMA\t \xaa\x81 ֤ܗ!^\xab\xe7Ρ`\xb4K\x06W\x05\x87*\x05G\xc8\xca,\xa8Xӱ\xfdcܷ\xe2\x81pS\xea\xf0\xf8\xb0\x14\xb4\x16@\x80\xf1\x94/\xbe\\ˣ\xa1n\xf5\xd8\x06\xd0\x17\x06з;\x85\x8dɗM\x1f\xc0xa\xa3\xbadC\xa9\xaeC*\x84ؘl\xf9\xd9\xf8\xc8\xea]\xd5\xc4\x1e`Pb\\\x99\xa5*\x89\x1ae\xb9D\x12\x02\x8b\xb48\xbf\x10\x8f9\xe3\x93)\xdb\x1eL\x1c2aae\xf9U\xc7\xdc\t\x83\xc7?8^\xf6s\xe1\x86Dž\xab\xe6\xcf[M~\x1c\xb3h\xe1݅\x8b\xc5\xc41\xc5\r\x1b}8\xf5\xa3{\xf7?\x9cr\xb4Q\xc3\xe2чoެ\xd8:}\xfd\xdaG\xcb\xf2ŐE\x93\xc6\xe5\xe6\xde]\xc2l\xf8\xb0\xcaJ\xe1\x11\xefg\r\xe8\x03\xcet\xa7\xba\x97fzbf\xbd\x14\xccLOX\x18\x01\x02?2kDTc\xd4\xed^n\x16\x81E@\xbcGL\x1a\x8c\x01\x06\x90\xacI\xd4JԤ&>\xa9:\x9a\x16\x9a7\xdd:h֑\xb4q\x17rn\xfeC\xec\xe4\xdd\x1a\xb6o\xff\"\xbf\xa4n\xaaQ\x949}u>\x9eӶnj\xfb9+\u007f\x9cA>\"?$\x93\xde$S\xda(~\xf7xB\x8fN\x87\xbe9\xb2n\xcdq\xa8\xac\x84l\xea\x01Ljm \x16L\u007f\x01\xd2Q\u007f\xf6\xb70\x11\f.\x960\x8892zgm\xea\x95{\xd2;m\xf4\x9e/\x85\x89H\x06\x837\x80\xc1%\xca\xe6w\x02\x82\x0e\xf0\x890]\xec\x042\x04\xba\xc8\x18\xfd\x95\xdau\x1eK!)\x00%\xa1\x00\xe4\x81'T\\\xa2fk\x81\xe3\x04\xcaZ\x89\xa6\x9f`\xfc]\x8b\xf2\x85\xdb\xc2e\x10@\a5\x81*\f\r\x1c8\x13]\xb5[\x11ݼ:\v)\x84\xdb\x15G\x85\xd6\xecOh\xb0\xd5Q\xb6\x05\x00A6\x9c\x10\ue21d\x9d\xb4p\xe7\xcch\x91\xca\xed\x12\xa3\x85S\x92$\x84:F\t\xbd*\x8a\xf1b$\x1c%\xd9+\xc9\xc22\xc0й\xf2Oa\n\x1d\xeb \x88\x86\xae\xcf\xf1]Q\xf4$\x8a\x8du\xa83υP\x03\x13a\xd1\x00/\xf4_\xa0\xfa/\xea\xbcRR\x9e\xfa\xafd\xd5\xf2\xebh\xc8\xebL\x8f\x87\xe3\xf8\xd7\xe6\xf7\x18\x9b\x9e6\xa3\x04\u007f}\xe8\xa3k\xdb\xc6t<^\x95-\xaf\x9f\xba\xb8Ǣ\x81cҧ\x8e\xee\xbf\xed\xf4\xf9\xfd\xfb\x8aGw]K>\xa9J\x9f\x01\xc3H\xf2\x92|C*\x84$h\r#\xc0\x1e\xaf\xc6&\x1e\x94b\x0f\x9e\x01\x95+~\xf1\x1e\x8cD\xbf8E\xf2\xf0cG\x1e\x06\xa5\tv\xea[\xb9=ޠ\x84:/Д\xb5N\xb9\xbdN\x9cbu^\xf33(-\xb1\xab\xdcR\xbbe\xe3\u009b\x92lb=\xb2\b2\x8bKL\x10\x1d#b\x9dl\x12ٙ\x85\a\xf8\xd8l2Q\xa7-\x9a\x90\xccc\x97\x143\xe3\x84|\xce\xfb\xe6\xc1#I\xc9y\x9d\x96\xad4\xfbe\x1eK\xef:\xa7K\xa2yU\xc6\x12\xd9LJ\x88r\x96\x9c9\xe0\xe5\xbd\x1cٮ\xf4;\xf8r\xcd\xe6\x17\xd3\x1f\x93\xb5\xef{{\xdfE3\x1e\xfe\x83\x86\xbd\xf7\xe47߮}^\x1eW\x035h\xdcr\xf2:\xf4\xd7o\xe4\xfb]=\xbb\u007f\u007fq;\x12V\xd7m\xe1\xb8\xf1ݽ\x03h!Z{\x9a\xe4\xfc\xf5\x88\xac:^\xcf:\xd1\x16\u007f\x1f\xedD\xa1Ȍ>xx\x8f\xf4#˗\xadM\x1b\xa8G\u007f\x87\xff¸\x18\x06 \x85KvЁ'\xb4s\x8f\x9e\xdd\xe1\x02\x99\x9e\xc8L\x1c\xf4\"\xe6\xf6\xcbUj\x15O~QM\xf9Q\x023\xa5\x82p\xd1Q\x94{\n\xd7ۅ\xeb\x9ft\xbc\x86\x1e>B3\xc8\x02\n\xbat\xc1!x7\b\xb0\x1b@\xcc\xe1\x91{ DBOw\u007f\xa9\x89\x9e\x80{HK\x9c\xe2\xe7l\xd2Ï\x05Q\xcc\x13\x19\x9cB\x88\xb4\xa6&\xc1\x18\xed\x8c8d+\xa2\xa74\x18\x89\xae\x19\xad\xfa\xc9\xe8\xdd\xe8\xce\xcf\x0f'\r\x9b\x92K\xbe'\xa7Q\xb3\xec\r\xe4+R\x86bf\x15.\xc9'\xdfH\xf6\x13ei\x9b\xeaF\x97\xcc9q\x0f\xefv\xfc\x917\x1d\xe9\xd6\xcf\x1a=m\fӽ\xa9\xd4#ܤ\xda\x13\x06m\xb5vR\x13\xeb2\x84į\x9c\x19@\x93\xc68\x06\xbb\x9c\xb1\xa0\x89\t\x97h\x8d\x02cR\"\x93\x1a\xb0Xc\xa9\xb8`\x9aM\x9bx\xe0{3\x94\xdc\xff\x85\x10rv\x05\xf2\xdc\xf7=\n\xb2\x1c\x0f\xdeYp\xf8\xca)e˞0t\xe9\xbb'h\x02J^r\x1e%n'\x8eo\xdf\xdbH\xfe\xfbd\xe9\xcf\xe4\xfb\x15\a\x00s.\x9f\xa2\\\xf6\x82\x00h\xf2/Bf\f\x808%\xed8%\xaf\x83S\xa2\x9e\x89ϼD\x99\r\xab\x97\xc6(\x8a\x06M\xb4fP<4&\xd2\x18\x1dP\xf5'\xdcs\xe4\xe2/*\x86\n\xab\x1c\xf5\xf1T\xbc\xd5Q\xb1I\xb2\x17\x91zU\xed\xc7\xd1\xf6\xf5\xd0\xf2_d]\xd1c\x91\x8b\xb7{\xfb\xb2\xb6EkU{\x17\x1c\x9bʄlGS<\x14\xcfw\xccdm\xf9\x02\x82\xe9T:\xeeP鈄!\xff\a\xd2\xf1\xe2|H\x89P\xbdkD\xdc\x01\xbf\x88\xc8\b\x9cz\xa0E\xc4\x1bt\xa7\x84\xbbPSS\x16\x99\xc1\xb1р\x8a\x8e\x92Ŋ\xa3c\xb0P%9\xccu\x88w:\x91\xbf\xed\nyP\x88Q\x02\n\xfc\x06\x05F\x96%\x93\xbb\xc7O\xa3{\xc7FoK\"\xfb\xb0\xe1\xd8\xc8\xf4\x9d(\xf1\xfcl\xd4\x11\x8d\xfc\xe1\x06\x8a&\xbf\x92ʩ\u007f\x92/\x1b6A\xed7\xaa\xbc\x93<\xf8\xd8\xf5Ԏ\x9d&\xcd\xd3\b\x94fT\xed\xc0\xb2\x06\xc6\xd4\xcdz4A\x89ӣT\x97\x01\xe5\xd9\x15ci\x02\xdd&H\x1e\xc7\x1d1ee\xf8\xeeq:|\x03%\xbbc\x19\x9e\xc0\xe4\xe7\x03\xba\x99ʳ\xd1\xf0\u007f\x81\x8c\xf9\xbb\x12\xd0Բ2\xc9ΞJ\xa2\x19\xd0\x05z\xe8\a\r\xb4\xf1\xac\xd3y3\xfa(q\x12U\x00M\xdebTï\x045\xf9\x14\xbc\x8e?\xb9r뷲\xdcySW!\xc9\xfe\xe4\x9f+\x0f\ue799\xb9\xa4`\x11T\xf1G\xe2\xfc\x99\xa4\x8d<\x9d\x89\xb7V\xd0ܱd\xc0,\nmi\xec\x04\xfd`$L\x87Ű\x1e\x8a\xa1\x14\x90\xf4T\xeb\x8d\x10\tM\xb4\xf9\xb6\x96\x11>\x06\xd7DItK\x94\xfcq\x95>'\xaa\x11D\x80\x8dc\":\xaeҦ\x14I\xbf\x9c<\xb2\xef%\u007f\xad\xc2+\x91\xd7\xde}\xc8k\xf9\xb1\xf2#\x87\xae\b\xd7JJ/\bx\xf7\rr|\xd7n\xd4\xe4R\xfaU\xd4z\xcfnr\xecS\x8c\x04\x14@~\xfak\xd4\x13r\x1f\xf99\xa0\xca+\x94\xf1l\xd9\f\xaf)\x9e\xfeF\x8a\x8f\xa3\xaf\xfeB\xfaU\xf3Q\xafK\x8e\xb1Ȗ\xbf{\xebZr\x1b\xbf\xe6x_\xb2\u07fb\x91s.\xdeQ\xe0\x8d\u007f\\=c\xd1r\xc44\xa7\v\xf5[\x13\x18^D\xa9\x06[-ږ\xc6\xdck'\x8e\xec2\x83L\x987\xf6uR\x18\x1b\xc2\xddw\xac&\x1d\f\xe1\xd3)!n9.K\x01\x19\xae\xe5>\x87¤\xbf\xda\xec&\xc6\xe2^\u007f\xdfG\xe6;\x05?\xcd=\xbekݲͅh\xd4š\xe4\xc1w\x05\x84\x1a\xa3O\xde^\xbb\xad\x00綿\xbcv\xef\xfdI\x172\x17\x14\xce\x1c\xd77+-\xeb\x9dq\xfb?\x9dxf\xf6\x82u3nLf\xbdj\x04 \x16\xf28\xb7\x81\xb6;Z\xf6\xcb\x06\x8d\xcb/W\x19\xcb=h!\xa9w\x8c\xd4\x17\xd3$\xd3\xe3_$S\x11 XB9UL\xdfi\x82\x97\x15\xc9\xecO\xfb\xac5\x83\x1a\x04J\xe3k\x14_\x8d\xbaȠ\xd3ŀ\x8d\x9ao\x15\a1\x8aňT\xdeIxH\xbe\xc1G\xdf\xdb\xfa\xf6\xbb\x92\xbd\"\xe6\x12yl\xc0\b\u007f%ܫ\x88-\xda\xf7^\x91\xf09\x00b\xf1\xbb\xe8\xe0xGc\x05t,\xdf\xd1\x06xZ\xa3\xa71m\x80\x04n\xd3j\xf2`=\x01\xd3ٵ\xab\x8eo\x8a\x1d\xdf^\xa9\x14\xf7?\xee\xa2\xda\xd1f\x00\xf2ϼ\x8f}\xb5fAc@\x9d\xaa\xa9\x89\a\xaa\x05ڏ\x9e\xf8q\x1b/x2k*\xab;\x1f\xbe\xa3\f\xb0\xa4\xa0\x04\xba13:l:F\f\xfa:\xac6\x8aچ\x82kE~u\x9cl\xdeG\xae[,\xe4\xec>\xb2\xa5\f\x9d/{WxT\xe1a?!|\xf3\xb8\x8bXsܸ'\x9fS\xfa\x104\x01\x10/q\xff;J\xcb\x02'=\xeeFX\v\x89\x03\x12\x99\u007f\xd5gC\x01\xec\x80C R\x97[u)W^'\xef\x96KdzI\x00\x99Ѯ\x97A\xa5\x9d;\\\xfe/\x01/@\xa9_\x91D\xf4\xd97d=Y\xfb5\xbaI\x12\xee\v\xf3p\x03G\x03G\fn\xec8\x83\xef⫌\xa7~\x00\xe2\x11J\xa9\a4\xd2R\xea\xa4GC\xa9\x938\xb5=\xe6\b\x13\xf0p4\xe36\xf1,#\x9ew\xf0\xa7\xf8\xf3\x8aq\x8e\xfb8RX\xc1\xde\xdf\v@\x9c\xce}_\x1bxq\xf0\xe1jSD`\x86\x86\xe1\x0e\x92\xf3\xa2^\xd2ы\xaa\xefOb\x18D\x00j\"\xb4zrG\x88\xa8x(\xfc\xbdi\xd3rq^\xd1R\xd6\xdeJr\x06{\xc9s@\a1@)\xb6\xeb\xdc\xe3f\xc1Uک\xfbdQ3\xf6:v\x8c,B\x99\xd2\xf7\xffL[\xab;\xcc\xdeS\x8b\xce54\xae\x9ek\xc0q\xae\xbaD\x1fd\xd2Qˎ\a\xd9%\xfb?,\x8aѓ3h>o\xb5&\xbf\x1b\xc5ٱ+\xfdv\xa4\r\xd8\xcd\fC\xa1\xc1\x84\x1eM'\xd9Ǐ\xcbs\x1euX+\xe7\xb0v[\xe2[\x82?\xd7!\x9bS\x874\xa2^\xae \x1dS\x16\u0588ČBU\x86[|t/\xaa;\x1b\xd5\xd9#\x1e!\x11\xf83Gm@ V\xa6\vk\x01@\x80P\x97\u05f8\aT\x12\xc3s\xd6V\x8c\x16V\x17\x16\x02\x93^\xf1&\xba-\x11\x10\xc0\nvĒ\\\x94\xfat\xfa\n\x19Td\xe9\x80\x1f\x8eĘ\x05{\xac\xf9ۛ\t\xf9\x92>\x16\v\xdco>\x10~\x14_\x87\b\xa8\x05\x99\x8a\xa5v\x1d\x9e,\x1b\xecQU\xde\xc6B\x89\xb0\x04\xb9˿&\xc3\xc6\x16\x1eyy\xb1\x1d\xf3\xf0\xf4q\xd7\\\xdb\xe4e\xa9\xf2;\x91\x1aX\xcf\xea\x1a\xc3\xc7\xdaX\xbc\xcclz\xc2\xd3y\v\x0e\xfcX\x8c\xfe\x96\x00\x8e\xeeY\xa3\x18\xb4W\xcb\xe09\xf7\xfd-\x17\x11\xfa\xe1\xc0\xa4\x8c\xa1٥\x13ON9r]\x8c%^\xbd7ZW\x90\xbd\x93\xa2\xbae\x1f\xcc\xdb}\xa4\xe7\xe0\x89\xc3\xdaw-\xecs\xe4\x1d⻦\x8fai\xbf\x0ewO\xf7\x1e\x02\x98\xdb\xde(\x19\xc0\x17\x82a\x98b\n\te\xb4\x99\f\x14\xf6\x01j\x95\x9c\x91e\x90\x9b\xd5\xd3\xe2\x18\x92\x87\xc0\x83Mo>\xb8\xde\xee)\xbaE\x13>0\xe4G\xc7b\xec\xe4(\x93\xd1\xc6!v#3\xd7b\xd47e\x17\xc7\xeb\x8bKƣ\xc5\xf7KV\xe6~Х灅\xab\xb1\xf1\x11\xb9\xb6b\xa6\f\x8eO\xf2\xc8\r\xe2\x90>\xbaTL\xea\x15_\x02\x04oұz@\xc7*\x1c:*\x86\x88Hڴ\v\xf4\x16\xe4\x1eۀKD\xc3\xfa\xc7@6\u007f\xb6Ր\xc7X\x9b\xe0KId\xf8\x81\xcaqk,\x1f\x00\xcam\xdc|\xe8j\x8f}Ҕ\x93c\xbf O&\xddZ}\xe8W\x8f}\x1e\xf9\xe9K7\xac\x9f?\xad_\xea\xaeaȆ \xb2诜\xdb\xef\xa5/:_f=r\x8eI\xd4@J\xe5\u007fe\x00?\b\xa1\x1c\x0e\n\rc,\nR9,\xc7iPQM \xaf\xa5^\x12\xbc9\x87َs\xd8\xfc\xc2i?s\"\x8dm-\x94\xbfV&DX- H1\xb2N\f|p\xec踒b\xfd\xb8S\x1f\xfe\\\xb26\xdb\u07b5\xfbޜ\xb58\xf6\x1f\x147\x0f'>\x86I9(\xf1\x91\xeepy\x11\xfau\xddUF{*\xa5\xfdw\xca\xe1\x00\xca\xe3tŃ\xf3\xd8\xeeጽ8yA\xee\n\x10\xe2\x01쾐\xb8\x03\xc7B.\x87\xe0T70\xc4\xe02٥\xde額\xc1$6\xa3Q\x8dhG\a\x06\xaaH \xa2\xf3\x1bѴ\x1e\x04\xbfv\x87<\xc8\xfab\xee\xb5\x1f\x1dV\xf1\xfd\xc5Cr\x122rȭ\xf1kM8\xc2#\xc7\x1fE\xff\x16\xb3ՑO~$\x8e\u05f6\x9c\xecҺ\xcf\x15\xe1\xdc۫|\x97n\x00\x04/\x03\xe0sr\x00\xed\xcdP\xc5/\x90i\xa1K/\"\\\x05<\xc8\xd5\x15\xbb\xf7\x8f\xf1\x1bi\xc2c\x1fWci7\x97S\xb9b8\x0fE\xe2\xf9T\x83E\xa7\n\x10\x9b\x00\xfbnϞ\xd2\xe2\x96-<\xe3\x92\xfa\r\xf9\xee;aO\xfe\xb8\xf7\x8e\x1a\v\xf4\xe9C&\xe4W\xf4\x04\f\x83H/\xe17\xca\xf1`\xa8\x01\U000d561a\xb1\x8c?1,8\x04\ryN\xfd\vr\xcd\u007fM\xea\x89j\xa6T\xb9\xb1\xf0\x89\x12\vKݴ\xf0\x88\x12\xc0\u007fc\xe6)\\#L1\xcf\bSJU\x02oKQkQ\x98\\\xa58\xc5jЏ\xc7O\x8d\xd3\xef\xfe\xe7\xea\xe4{/\r\x9a\xbawQḲ\xa3?\x95\x16,\xda\u05edg\xf1\"*_\x0eTwɴ'\xf7\xae\xfe6\xac\xf7\xb8Uk\x17\xa7\xceA\xf1\xbf\u007fpe3\xfae\x03\xf7\xe3K\x00\x84/e\x00#\xd5cO\x93\x99\x8f\x8b!N\x83\xc2k\xa2>~\xe2KO|\x9d\xe9>\xf3Y\x9e\xaef&9!\x8ae\xba\\wyJ\x8f\xdaf\x9dD\xbd\x85\x12\x94>\xae_NlI\x89\xf0A!\x99\xe9H\xc2\x17&\x8f\x1f\xf4z\x05\xab\xe6\xc0\xc0\xdc\xf0\b\xe9\x04\xafWk\xae\xc8>\xbe\xec\xcd44G\xae\x95u\xcf\xc8\x03\xae.\x12\x92%\r\f\xe3R\xbeֳ\xa4䌊NjMQd\xfd\xe6\xcd\xfb\xbd\xfc2\xe0\xcac\xa43\xeaK\xdb\xf3\x86@f\xc3,A\xeaT\x00uX\xf0\x02\xd3K\a\xd8\x05\xb0\xa3\x86\x80\xc1\xef\x01q\x8aAr\r\x84\r\xd11\xb6$5\x04\x16\x98\x04\xaaDt&\xdf6K\x8cJl\xdeʜ\x92\x94LiI\x16\x9b>\xe9HΘV{\xbc\xd2]<\x82\"\xeb\xb5\xe0d\x01b|\x10\xbde\xa0tMvI\xb2\x98\x8ah\xb5\xfe\xc5\x18\x8c\a\xc7X\x02\x9a\x82;\ns\x0e\xe6\x01\xc8Ӹ\xf7\x98\xaa\x88ܲi5\xec\x05\x06N\xa3n\xd5\xfd\xf6\xa7'\xfe\xd5'\xa1\xf4$\x94G\xe9\x1e|\x02\\\xe2;\xbb\x87\x06\\\x0fbя\x85\x19\x87\xe4*IU\xcb3\u06015\x86\xedPSi깯\xbb%\xbf;\x05\r\x93KF\xce|+ǫ\xf4\xbbC\xaf\x94\x88M\xa7-y\xef\xf5Ad\x91\xa3.>7ib\xd6\bG<>\xf9`Cŏb\xd3j]\xa2\xbd2Bw\xc5\xcbd\xd6\xf6J\xdb\x117'\xee\x9ea\xb0\xf0\x9cY\x03/W\xad\xb2\xb8\xab\x15zI\x9eqƩV\x94\xba\x9c\x8d\xaejE\x89\x02\f\xfdi\x94\x91H\xa9\xf2\x83PxE\xf1\x0e\v\xaf\xaaa5\xbb\xe6\xf0\xee\x0eP1k\x82\xdf`\x8dUb\xde\x01\xa8Of\x13DP]#\xc7Y\xd8\u007f\xe6\x97\xf9_ c\xe6\xfd\x95w\xc8\xc3ҝK\x96\xeeؽ$w\x17\xb6m&\x8b\xc9%\xe2S\xf4d\t\x8a\xaf\xd0\x1f\xb8\xfd\xc5i\xe5\x8b\xdb,\x02\"\x83\xc4\bN[\b\x8cU̪\u007f63\xfcF\xab\x83\xce,T\xc39\x93{:(ykb\xa1\xe7zjM,\xc4D\x9dI\x826\x18\x8a\xf8\xea\xf8Ɍ\x92\xdd\xfa\x8c\xb3\x9f|]\xb21gW\x8f\xee{\x16n\xc2ƿ\xc9ՙ\x8e\xbf\xa5\xdbӖ\x90\xdb\xe4\xb1\xf8\xc1\xf55\x8e'\xab\xaf\xf18\x83\f\x12\x1eV\xf5c\x946\xce\xf8wϡ\n\xb9ɝ\xfb\x9a\x88\xe3\xf9\x9d\xd0z\b\xab\xf1\x99p\xe3Ǔe\xe3\x92\x01A\x0e\xe5\xfb\x1dj\"\f\xd4ZQ\fC\x8b\x91i\xc8rw7\xce@_-\xba\xc2ω\x01j2b8\x1aD)c\xf1\xb0Pk\xf4\x95w\x0f\xa2\x923\xd7ە\xd8G\xcd>{\x12\x979\xda\xfeU$\x98\x9f\x9c\x02@\x10\r \x94Rj\x9e\x8b\x01iD\xce=\x1bR@\xd2`@\xe8W\xe4\xfd\x11\x99\xb1\x94L\xfb\xe8\x89\xd0\xe2\xc9)f\x05\x11\xd4\x05\x90\xae\xd1Co\x18\xa4H>\xbe\xee\xef\xd7ʷV\xf2_\x9cz\x81\x97\xc0\x9d\x8c\xba\xf3\xf4\xaa\xa2\x83c@\x8c\x14\xb39A\xecT^BN\x16\x90\u007f*\xa1\x80\x9c:\xfciŚJ\xe1\xa5'\xa7\x84Ċ\vbӊ\x1bB\x1d@P\x1b@8Gi\xf3\x82\xeeZ\xa2\x9c\xa8Oп\xa3>\x9e<\xeb\x95՝;\xbacf\x8e\\\x10\x18\xba\x83\xfe\xfe\xefMR\x84\xc6\xdcx\xfc\xf834\x86\x14\xdd\xc0\xfbP\x9e\xe3\a\xc7\x17h\r\x19\x85\xad\xd8\x02\b\x02Ig\xc1N\xa9\xf1\xa3Q\x11\x18\x8c\xee\x9cr\x89\x005\xc3bbt\xf8J\xbce\x97\x94\x83\xe9\xa9J\x03\xb6\xdalI\x16VB@\xe9\xf8\xa5\xe2\x14ym\xf8\x1dk\xdb\xf8\x01\xe91\xb5ɬs\xc8O\xa8\xfb$\x92\xfc&\xf8\x14\x88\xaf\x0e\x1f+6\x00\x04}\x00\x84\xf7)%Z\f(\xe8\u007f\x00\x03j\x8a3+\xb6㞎\x03Bbaa\x8e\x10\xbcn>\xc7P\xc8J\\$7\x83 h\xa4\x98\x83Cx\xcf}\xe2\xa8.\xba\x14\x86E\xea\xd8,\x96\xe0̢d\xfer\x8e\x02'%Z\x93ⓒ\x92Y\xc6d\xa4U\"\xb4\x8a\xdcd\xc1\xd3\xf6\xbf\u007f\xfd:\xde\xf7~앝;\x85Kde\xfc\xb7\x1f\x9e\xfe}\xc5o\xe5\x1f\xdcO\x98\xf8\n\xf9Ϲ\xaf\xfb\xf6\xfb\xf6\x12\xf9\xb95\xa3`\x1f\xf9\r\xb5ya\xa5*b1T\x9b5k\xc8o\xf2Qv\xb7\x99\xd2;\xa1\x8a^I\xa5\x97A\x1eA\xb4bދG\xb3FJ\xaf\x91\xd2kr\xd2+\x99\x8c\x9c^K2\xadTN\xa14\x1bh^\x11Hg\xc4\x13\x02b\x98Kי\xcf\xeb\x0e\x1e\xbc\x12\xbb_A\u05ef\x1f\xdc\xf7\xa4\x15\xf2\xbc\xf3]\x8fn?\x9cG!\xafL\x8c\xbf_r\xf5\xe1\x8a\xdf\xcf}\xf8UZ)>\x14b\xc0\x02\xad\x15#\x9f\x1b\x00g \x13\xc81'\xbb\x1fO\xf4\xfd\xe3\\m\x99\x9f\xbb-\xf3b\"\xc2,p\x03\xcc,\xb2\x8e\xd9gJ\x14\x15\x96$\xf1\xe1\x91\xdc\x0esvw\xaa\xf3z\x8f\xad\x87\xf3^]bo\x19\xfe\xea@A_t+iu\x8dQ\x03\xf0\x96K-\xb7z\xe2I\x03\x01C\x1eY\x8b\xd2\xc4\xd7x\xfe\x10\xef\xcc\x1fx<\x8f\xe34*\xadY\U000826760h\xcel5\xb3P\x9d\xe5\rŻ\x9e\xaex\xc1{\x9f<\x90^\xad\xca\x1a\x10\xa4Wz\xd1J\xd4X\x88\x86n\n\xc4XY\x9f\x9dQh\x90;p\xac+w\x85><9\xf4\xe1\xcfl7\xdb\x06\xb1\xad\x12\xea\x0e\x80\xc8\xea\xda\vL\xc5\xc9\xc4\xd7fhP\x90N\xc3(\n\"\x9fZ^\xf2\xfe\x99s\xfbK\n\xcad\x15\x04\xd90?\xb3דּ!?\xfc\x18\x83\xc2\xeeX\xaf\xa3\xf0课\xb4\\/\xa8\x86B\x10\xf4\a\x10Yv\x15\r3\x940w\xba\x9f\x81\xf6#\xfdy^\x17\x19\xa7\f\x8cD\x13\x94\xfcH\x94\xa1\x1c\x8bD\xa9\xce\xdeh\xcb\x0fYO\xe9R\x1fU\xdd푬\x1c\xe0\xf9=D\x16\u007f]\xb4.\x9a\x95ɥ$٨\xab\x124\xbe\xd8Lu\xf3\xb8Gs\xfc\x05z\xf2\xe8\xb5Z\xf1\xd8c\xfb\x16\xfd\x9fg\x8e\xde=6{\\\xfa<=j\x81\xc7\xdeY\xdf\u007f\xd3&\xfd\xfcA\x1e_lB\xe2\xe9_\xae\xef\x1f5c\xfdLR\xb1\t\x00ӱ\xf9V\xea/\xfe\n\xa1P\aZ(\xb8n=\xd5/\xd9m\xe5\xa0\xf5_\x86r\x96\xe3b\r\nH\xb1(\x9b\x9b\xff\xb2\xe9l)\xb1TKl)<\x86I\xb1\xe8X\xed\x97E\x87\xfc\x03\x03\xe3yQ3#Z\x16\xdf\xd8|\xe5\xf2\xe6\xecyy#Ư\x9a\xb7\xaa\xe8\x93\x13\x9b\xd7,(\x1c\x9b\x96\xbf\xa0b\xd0\xc4\x13_\x9d\xc8\xc889>\xe3\xc4\xc4\xf1\v\xe6\xe5,Z\xba\xf1ܥ\xb7\vs\n\xa6e\xae\xcd]\xff\xf6\xf9\x13\x9b\x97\xe7ବ\xebӳ\xaeeM\xbf\x969\xed:ӪH\x00\x91N\x96@`\xf5\x1c@ %;P3\x89\x19\xe6\x1e/\xfa\x99\x85\xba\nx\a\xd2\x0e\x1f\xf03G\x9aq\xaa\xdd̄\x8dn\x15\x13v\x16\x9cp}Ա\xad\xe2\xeb\xbc\xecY\xaeb\xf2\xd4>\x06<\xad\xfdg\xff\xa3\x04\x9c\x86F\u007fN\x06\xa0\xc6d\x0e\x9aC\xe6\x1c'\xd9\fyFMȀ;\x82/\xde\xe6h8s\xfb\fr\x14\xb5\x9e\xb1}&.gc\xb0\x8aZ\xb0\u05f8\x05\xd3A\x10\xb3a\x9a\x89*EBB]\xaak4 `\xa1\xe1\xaa\xd2\xd2Rj\xf2-\x15?\bg\xf0_\x80\xa1\x1f1\x8a\x99TJ\x13\xa9ş\xa3X۶\xa3\x1db\xab|\x928\"\xea\\\x1e\xc4^\x15\xe8ae\xbf\x06\x1a\x94\x16.e\xee\x81-\xaa`2_*\x90\x1aĥ\xa1\x95W\xd254()\u038b\xe1)\ry\x13LH\xf8*\"M\x86\xa0\x16x\xb3@&6)\xe5i\x8dw\x03\xcc,\xa7:\xe9':\xa3\xc8@1\x02פرzl\xe1\xf2!f\x96\xb6\xed@*\xbe\x19\u007f\xb6]^遍9;\x96]9T\x96z\xa8u{\xe4u\xf7{$\x96\xec\xca[\xb6\xb5\xf1\"\x14\xb9;\xa3\xa9\xe3~\xbf\xce]:'/Da\xf5^\uf447\xf6}ԳQΰ]\xa7\x1b7ɸ\x88\xe7\x15N\x1e\xd47\xbdy\xc3q\x1bƕ\xf6\xa1\x97w\x9c\xf9\xea\xe2\xcc\r\x13\x87\xb4\xedҪK\x87\xc1s\xf7\x04\x85\x9a\x87\xb7mץu7\xa3\xff\xf0\xb6}ưqH\x13\xff\x8b\x1fJ'\xc1\x03\x8c\x10\xce*4]VK\xb0\xd9N\x9dA\xd5\x02:\x18\xd5@I\xcdꃴ\xaa\xea\xc5\xf7Խԟ\x16,R\xebW\xb7\xca\b\x82\x00\xfd\xe9J\xae\xffH'\xb8\xa65\x815\x8a\xad\xe9K\xbcX\x98\xc9\x1c\xdb&Wi\\\x03:X\r\xaa\xa55\x99\x9e$\xf3\x18>\xb9\x81Zt{\xc0O\x88\x14(F\xe9\xd9 \x99O\xdc\xc7\x1d\x00O\x83'\xbd\xe0\xeb\xc9#*#\xd6$>OϔP>\xebfO\xa6\xb3\xe3\xcee_\r\xa8\x06\x8b\xae\xc3\xc7\xe3am\xb0_U\xa4]\xd3\xdfD\xeb\xf6M\xb4\xf8\xab\x86\x95\x95<ҙ\xf4(:\x91^U\xb1 \x0e|\xb7h\xd3G\xc76lٻr`\xff\x01\xa3G\x0fx3Ձ\xb6 3j\x89L[\x8a\xc8\xcf[\xb7\x90\a\x9b'\x1eA\x9d\xd1\x1c\xd4\xe9\xc8\x01r\xf0\xd4Ir\xf0\x10\xee\xb9n\xeb\xb5Cc\xed\xd7\xdeY3\xb4\xe7⬌i\x8b{\f_\xb0\x97|\xbec\a\xaa\xb9\xb7\x18\xc5\xec\xdcN\xee\x14\x9fC\xfdN\x9f&;Ν$\xbb\xae\\F}\xb8\xc6\xe0\xf7\x05?\xce˚0Z\U0004dd71\x9e\xf9\xb2\xdaxmy\xb6\"\xc6\x06\xa8\xeb:\xd8\\\x0e\x16ى\xe2\xc5w\xcc\xeb\aP\x95v\xbay\xa3\x17\xbf\xceJ2\x04\u05c9\x8e(W\x06UOt$\xb3\x89\x0e\xc6\x1d\x9b\xca+:͡\xcer0Y\xc6#\xbd\xe5W\xe7L\x9d\xb7s\xef\xe8\x19m\xdfغ7w\xe1\xfa\x10\xf2N\xdd>a\xe3\xbb\xf7\xc5\xc7cb{\xcdxkܴ\xc4E\xc9\t\xdeo\xcd\xcb\xcf&'\x06wͪ\x15\xba\f\xb5H\x18PY\tiP(\f\x13އXX\xff\x98\x80\x0e֣6\x95\x04\x10\xccFS\x85\xa3B\x14H\x10\x02\xacC\xa8\x9c\x1a6Mu\x1c\xa3\x8f\xdb!\xe1h\xa9#Kh\x81\xa6\"\x8f\xad B?*y\xdfRn\xf9B\x04$B3ح$7\xe7*\x9f\xcc\xc2\b\xb65\xf3\xadUS\x01\xe2\x0e\x91\x995+\nC\x9f\x9e)Q<\x86\xa75\xc3J\x8cZ\xb1\x16Ce2\xc6\x10Ce2)*\xa6j\x1d\xe1\xb1$\x94z\x00\x92\fI\xf4j\xa4S(kE&\xf1g\r\xf6\xb8r{\\\x1c[p\xd9\f\xbb\xe6\xea\x1a!K\xf1ge\xb6N\x19\x94\x98\xd9\xe5\xc5\xc5,\x1a\xb3\x04Xc\x13m\xb16[\x82\xbax\xc6\xda\xcf]\xdc&R\x81ܲ\x15\xf9o\xdaD\x1ep\x81DeK\x91\xff\xba\xdds\xa6#\xe3\n_\xc10Ѿ\xb1C\x9fׇ-Ʒ\xdcd\xee4\x93\xc9\xdd(f\xef\x1e\x14\xcdd\xf2O\xf2\x9e\xd0u\xfe̬V\xf1\xeb\x9b\xf5\x8a5\xd4(1\x0e\x12\xe7\xe0\xd6\xc9\x1d\x9b\x01 \x98&E\v\xad\xb9=7\xf29=,hk\xba\x85֎d|N\x8a\xce\a\f\xf3\x842<\x83\x8e\x8d\x17\x04B\x9c\x1b\xfb\xbd˩\xb7R\xf4\x1a\x856i\xb8\xc3E\x8f묥\x1aN\xf2\x0f\x9c\xb7f\xe4\x88U\xabF\xa4\x17T&wꔜҹ\xb3X6b\xe3\x86\xf4\x91\x05\x05\x81\xed\x9b4\xed\xfc\xc6\xe8N\x80a0\x80\xf0\xa7\xf8;m\xd7\x0fڱ\xd5hڌZox\xde:7\xbb\x8f\xa6\xdeC\xf2\x95\x99\xf3\xf4\x92B%\x9c\xaa]\x03Ǻ\x89s\xd1h\xb2z'\x99\x8d\xe6\xeeD\xad*>\x12^\x11z\x15\x92!\xa8\xa8\x10mzⳖq\xaa5ހ\xf7HGh\xdf{(\x1e\x1cM\x06g\xaa\xa4)\xd0\xf2g\xde\vk\x16R\xf9\x06\xaaZ\xafx8\xaf\xfb\xf2\x95<\xfe\x81,\x90\xe5\xccF\xceuTl\x89\xaa\xbf\x1f\x8e\xc6{\x1c\xbbQ\xbd1-_\xee\xdc~\xd3N\xe4\xb1~T\x0f;\xca\xc7\x1bF\xa3\x90\xb4\xb6\x8d\x9a\xbd\xdad\xc2\xea\xd9cG\x8czc\x05 h\x8e\xf3\xf1z\xa9\x14b!W\x89\xb2\xd5\xd2ҧYJ\xc5\xe8\x83`\xd6,\xa5\xf7\xd08\x98\r\xf9 \xa4*-\x00e\x1c\xf2\x83H\x88\x03A]R\xc5\b\xd69\x15\xc0\xd37\xb8\xaa\x929֩Q\xfe|M\x95\x12\xae\xbe/\x98\x9a \xd7RtեjWUUI\xc1\xd3UU6+\xd2\xf7\xe8ԡE\xbb&\x9d7\xbd=#wc\xbb\xd6k\x8as\x16\xec(z\xe3\x95v\x1d7\xf6\x11\x877kX\xbfiB\xed!Y\x99#R\xfa\a\xd7\xce\x1f9s֨\xfa/\xbf\x9c\x90\xc1\xabu\xb3ě8\xaczN\x1a\xd39i\x8cRU\x80\xe5\x00\xeb\n\r\x940sZ\xc1v\x81/\xadd\x99\xfb\xcb\xf7\xc8\x1fk$Bn\xb3\xe7\xd7Sd\xef\x17\x9a\x83\x9a\xa0\x8b\xe2㬏\xd1¸\x9a\xbc\x94\x03#&\xf72(ѥ\xf8I磎\xa81\x90\xa3u|\xb2P\xc5\"Qw\xf4֤\xd3\xcdKw\xeaS\x8bWu\xa5\x80\xaec\xd1\xdb\x1b\xd7\nQONM\\ԑԕn\x03\x82W\x00\x84\x96\xbc歮\x82\xbd\xbc\x19E/.\x04T\x10x\xa8E\u007fFjtX\xc8\x12m\xc4\xd9=\xfeC\xfeF\x9e\xffA>\u0603\xdc\xf8\xb4\xf2q\x17\xd6\xd3Ux\x14\xca\x14:\x82\x1e\x02\x15\xa1\xaa\xda\x10\xc7\xed\xf7\xa0t\xdf\f\xbd\xc3T\xf4\xe9\x12\xcbU\xea\x12K|\x8b\xad\xb1\x8ckA\x05\x03&\x91\xd7P1\xb0\xb9\xecV\x8a\xe4g\xd0\xf2\xc9\xe0\x06\xda\x1e0\xcbV\x99\x1aO\xc4!\x8a\xfd\xba\xaa&\xecHE\v\xa2U\x11\xb0\xa9\xe1V\n\n\xf7^\xb7%_\xf7F\xef\x96C\xa2\x12CW\x0e\xce\x18\x95\x18W\xaf\x8e>\x9b\xd5'\xe3m\xe8!_\x11\xd9RA\f5z\xe1\xc2H\u007f\xed\xc2H\xec\xbe0\x92y\x1b\x1d]\x18\x89\x1en\xba\x89\x86\x9a\xf06ܷɜ\xc9\x00\x98\xb6\xa1\b6>O\x14B[\x11TlW`\xe1\x93\xd6\xc4)\x9e\xa1\x82\x1a\xc5(\x06A3\x03/\xb8\x9b\xb9\xe7\xad.\xd7M\xffp\xfd\x86\x0f\x8f\x90\vG\xdf\x1eܣ\xfb\xc0\xc1ݻ\x0e\xc2\xe2ТO\x8e\xef\xe8\xb4\xf9㏷\xcb\xc3&L\x1c\xfe\xfa\xd0\tc\a\x03\xf7\xabk\x85i\xc2A\x88\x85\r>\x00:\u0600F\x02\xbb\xde\x13@Z!\xbd\v6\xd8V\xf99^K\xf7\x1bQ\x10x \x1dl\xc77\x00\xc0\xa0e\x0f_3D\a_:˟\xd9\b\x0f\xc0\x83?{\x03\xaf\xa5ϼ-D\xf0g45`\xfc\x99q\x00\xd2\xcf\xd2\x11\xfe\xfeB\xfe\xccv\x81A\x02:x\a\x1a\xe1\r\xd5\xcfD\xd3g\xa2\xf93\x80\xa1\r\x99-L\xa3\xf9y\x10\xc4@oE\xafVE\xea٪\x016\xe3輟\xe5\xe7`\x88\xe6H\xd6\xffn}\x8e=\x9azo\xd7\xd97_A\xa7S\xb9\x9a \xab\xc1\r[\xbd\xc3\xf3\xfa$&\xbe\xf8UT{\xd0a/\xebHKo\xbc/{\xd3\xc8¢\xad\xb7\xf1f?+!\xea\xcdM\xf9\xceޔ\xde\xdc\xf4rS\xea\v\xa35\x9a_\x139\xd3\n\xf5κ\xe5l>3Nc\a\x93\xa9.JQ6\xd9%\x8d\x8bu\xe6q\x16^\xc5\x13\xa0V\xf1\xa4PLڙ\xd4\x05\xf2\x82\x1e\x06P\xfcB\x9e\xe4\xb4\xfaj\xe9\xce\x0f\xbf\xfe\xe2˱#җ\x1c\xfe\xed\xc8D{|\x8b\xf7ү~逸{o\xcd\xea̸\x96E\x15\xfa\x0eE\xf1\xdfL^2R\xe82q\x8d\x11\x87d\xfb\x1f\xe85\xe0\xdd\xc2M\xef\xf5\x1c\x955\xb6\xab9\xff`\xf7\xeeo\xf4&\x95?L\xb4\x1f{52'\xb3\xa0S\xf2\xcfxl\xf7\xd7R\x85\xc4=y\x91\vV1/:\x8b\xad(\x97\xebA8ԥ\xf1\xb1\xae^}\x15\xa7\xb7\xd7*\ag\xe0\x19\xe1>\xb1\xe1\x17\xac\xabZ\xfdh\xe4GJ\x8c\v\xe2\xa8\x15$d\xa2\x90\xbb\xeb\x8a\\K,\xc3\xef\x98,Q\xe4N\xb0\xa9\x10\xaa\xea\x1b\x84\xc4\u007f&\xdf\xcc^t's\xfc\xe5\xb9og6\\z\xbbcق\x0f\x9a<\xdc\xf7Q\xc7\x118:\xef\xcd\x15\xdbvΙ\xb9N\n \x8f\xc8[\xa9E\x8e\xe5s\xefe\xaf\xfaaބO\x96\xae\x1e:\xbf\u007f\xa3\xcd\xc9ys\x87U\xfc\u07b8Y\xc7c;\x97\x9c\xf8\xea0\xb3k\xeda\xafX_,\xa5G>`\x81\xd7\x15]PpU\xff\xa0\x9cm\x8d\xe5n\x9f\xb9`\xdfx\xf2\xae\n{\xf6\xcb\xce\xf8g\xbf\xa1\xfab`Ձ\x9af\xa1\xa7\xb1\x97\xf1\xe9\x91X\xbf\"D\xf8\x8fc\x8b\x1a\x8c\xa9[\xdc\xe70jʣ\xb2\xe6\xcd+\b;h@\xc33\x8e\xd8L\x17\x13\xc5ü\x9a,\x16\x86*\"\xcf\x00\xec\"3\xa2lku\xaf\x06\xd0T$X\x9d\xe2\x18m\x15\xb9\xe3\xe5\xe8\xae\xe8\x8a\xee*\xc1\xa1~<\xc6g\x15\x9b\xd5u\x02\xdc\xd4\x1a\xf9\xc0p\vlV\x8d\xae\xccm.\x1b\x8b\xfeY\xf7\xf3\xc7\x0e\xe88#\xed\x93\x19\xf7\x96\x8f\xe8\xd5iư\x8b%\x83Q\xef\xe6\x1d\x16\xef\xc2i\xbd\xc9ޔ\xd6y;i\x01\x81#\xaf\xce\xea\x8b\xebIY\x11ɵ\xae9\xbf\x06\xa5|<\x0e\x1f\x8a\xba}\xf0\xd08G\x97\xe0\xcfK\x00\xc3\x1a2H\xb4=\xa7\xd6\xcf۽\x9a\xf2\u007f\xbe\x82\xc0\xf8L\x05\x81\x8dW\x10\x94d|\xfc\x9c\x02\x02\xdd\x16ǹ\x05\xda\n\x02\x04\x1b覧\xd8ԭ\x8e&\xe8\x05u4\xa5\xa5jmLjʟtz\xbe\xce\xdd\n+\x14T\xa3&\xf7\x18\x06{H\xb9\xf6\x13Z\x9aD\xef\xc5+\x85\xec\xbeZ\x8b\x05\xbeܚ\xc7)\xfe\x1a\xe3\x14\xf8\x02\x960v\x85\xbb\xda\xf6h\xa6\x8e\xd8fL41Ix\xba\xb8Q\xa2IO\xf5\xf7Pt\xfa\n\xbb2a\x1bY\x82\xf2ȶ\x19\xc5B;\xb6ޑ\xad}|\xb2$#m\xcc\xe8w?Ɵ\x04\x14 \xbf=(i/2\x16\xfa\xd3%\x90\x9b\x1c\x15\xa1\xbe\xe22\xbfo/\x93\xd3\xd7~b\xb2=\xb8\xf2\x81\x0e\xa8]\x8f\x82z\x90\b\x05\x8a\x9c\xc4\xe1D\x99\xc5\x13l\xdbP+\xdb\xdaiUMɞ\xd3\xe23Y\xd0yr\x8b݈\xef\xec\x8d\xe2\\\x1c.\xb3\xed&\xe4Lr\xd5;C\xf8\xae*\x04\xa9㼹\xbe\xaa\x0eO\x8dr`\xb5Q\xb6po\xf7l\x1dM\xcd*\xf3<8\xf3\xf3e\xb9g\x9b\xb5:1\xfe\xf2\x8f\x8ex\xdd;\xd9\x1fN\xeb\x94\xfb碋-\x9a\x9fξC\x1e\x95l\xc9\xcbݲu\xf1\xa2mb\xfd\xa1\x05\x81\xd87\x17۶\x90\xdc\xc9\xc3\xd2&\x92\x9f&\x15\x9f\x1c=k\xc1\xf8aC3PC\xa2\xff\xe0\xe6g\x97\x0eݺq\xe3\xed\xc5!3\v\xf8\xda2\xf1\x1bA\x96\x01< Q\xadw\xa7v\x8a\xa6ĺH\x1d\xf5E\x92Ȍm\xb0]bu\xdb47Ft/\x1a8P\xc1\u007f\xe1_\xc1\xa0\xea\x9cdN\x10\xe4u\x05k\xd6\xfd5^\xfcų\xb8\xd8\x1b\x19\x98Tf\x8a\a\xb0M\xee\x02z\xf0\x87:\x8a\x10\xc0\xb1\rA\xfdF\x9ePՔ\xd2B\x87R\xf9En M\xd53\x80|\xfaM\xe2\xac\xe0\x9c\xc0\xb6\x19\xfd\xdf\xcc\xca:\x9cE\x0e\xbeҨa\x9bW\x1aŷ\x96\xe6\xf4\x9e\xc0O\x93\x99\xf9;\xcf輈E;\vɝh\x04\xe6\xfe\xa2y֛\xb5_\x1e\\7\xacv\xd3\x19\x99\xa9\xd6ƃ\x12\"\xeb6\x91\xee\x8c\xce\xf6o\xeeӢ\x91n\xd4\x12\xff\xa6\xa66\xf5i^\xb4\r\xe7\x89K\x85\x18h\a\x9bo\xc1\xab\x17[\x02\xbd\xb6\x1c\xe7Ig\xd5k\v -\xad%\xa3w\x90\xd8\bM\x93͔\xfbъ\x87\xdeS\x9b-G03\xa4\xe7N\x9f\u007f\xfc\xc0\xc2'\x04\x13\x93\x91\xef\xc8T\xe1\xf0`\xd9\xdca\xf6\xaa\x9cIig\x00AC1\x10\xb5\x97\xc1\xf5=\x9a\x92A\x05\xf8\a`\\\xbf\xc7$\xcb\xeb\xd2S\x85\x0f\xd0`1\xb0ü\xe5\xcb\xe7\r;\r\bj\x90o\xd1Kp\x90\xbe'\xe2\xe9{\xfe\xfd\xa3NK\xd3\xfb\v%Cɷ\x1dg\xaf\x9c2l\xf89\xda\xcf\x11b#|\x84\xf6)\x16Vy\xb0\x9ct\x15z\x05X\xff_\x16\x03q\xb6\f\xf4\xfajov}5j\x01\x00\x98\xb6y\r\x0f\x83\xbf\xf8\fEê\xdaS6E*Q)1=۶\xba\xee\xd6.2\x89q\xa1â%\xe8\v\xba-\x1dL\xaeu\xc8]W\xf2\xce\xe8k-\xf9~\xd4\x15\xc6\xf3\xc5\u0602c\x84\xf3,\nW<\xab*\x9c\x84\xb8\xfd\x9eN|C\xf3M\xb8\xc5\xe3\xde\xec?a\xec\x80\xd4q\x82\xd8{\xfc\x98Ԛ\xbdǎ\xeb\x03\bF\xc3:\x1c%\x1c\x04\x19\f@\x87G\xc1:\xa9\xfa\xebc\xac\xe4\x83}`'\x02\xe1\xa5\xe4\x16\xb2-űd\"Z`F\vyֵ\x16\xfbV?\a\x14\x81\xd0\x01\xd7\n\xc6S\x14@\x9fD\xdf!\x1b\xb9\xb5\faB\x84\x83$\xcbL\xb2\xd02\x94\x0f\b^\x82\xb1\xc2H)\x06\x04г'yS)6dy{\x04>=b,\xfbt\x1c\xe3r2^\x8e\x8fH\xdb\x05\x9a\xe9\x03\xe0\xe5`\xd6f\xed\x9ci\xa0V[\x89a\xd4cXa\x91\x12\xc0\xfd\x85v\x92Q\xfb\x11\x18\xab\x9e\xeb\xae5N\x01'.\xa9w\xfd\xb2\x87\x1d\x98\x87eɘ\x1f\xdfF\x96\xd3Y\xe3C~\x91\x91\x91q\x91B\xaa\x13\xec㓮\x91\x1c\x9d\x0ft\x9b\x12v\x9aB\x10X\xd96\xfd3Au嶎OI\n]\xc9\x1c\xf1\xc0\xf6Ii\xfeӦ=\"_\xcb\n\xfd\xcf\x13\x85\xff>r\x91\xff\x98\xb1\xef\xec\x97\xd1l6?\x89\xc3?\xb9\x1a\xb3%\x02\xd5D\xde3rg\"\x19EFn\xb3\x9e\xffpf. \u0605\xf3\x84\x9f\x04\xc6\xc5P\x17\xff\x1a\xc8\xf2,\xe4\x86h\xff\xc4\xe0]\x9cW\b\x98\xcd\xc5\n\x17y\xd5z0\xccW<\xd4\x15\x1a\x1e\xccT\xfc\x9fV\xaf;\xbf\xd4\xe7\xee˝l\xff\xff\xadkg1\xc1\xb9={.\u007f4~O\xe4̌\xa9\xe9_|\x81;\x96\x96\xd2\xfa\xf67\x0f\x9ek\xb61~Ԩ\x01\xf9\x15,\xb6\xe0U\xf8\xc2\x0f\xb4?\x81\xf0\xa6\xda\x01\x13m\xdf\x14\xa1-L\xd4ʀ\x96L\x93\xfb\a\ft\xc8uVQf\xf3\xaej\xed\x9fJ\x9a\x95\x13\xcc|\x1e\x9d\xdf\x10\x1a\xcex\xf5\xccwߕ\xecك\xca\xf6\x0fGuK\xa4\xa1\xfeWGQ\xda(\xa9S\xde=\xdc\xecq-`\x98\x1e\xdd<\x14\x02@\x86$\x97\xf43\xd0}\x11\xaf(\xa1g\xc0<;f\xe8\xa1\x16\xc8\xe30\x1e\x82\xd6\x00x\x8f\x10\xf5|t>\xd0=\x9d\rР\xf3\xfe\x01\xbe\x1c\x8d\xf2\xf5\u007f>H\xcf\x1a\xfd?G矋\xcdW\xfe\x89\xd7S\xeab!CK\x96\xf6\xfbf*(?\x83\xad\xa7:4\a\x96\xc3\x16\x10R\xdd1x\xd0Uc\xf0X\x83\xc1\xbb\x83\xefJxl\x14\xbd\xf8?\x82\xc1\xa3\xf3\xff\x82\xc1cH\xa6\x9b#B\xc03\xdf4\v\xfc\x97o\x9a\xe1\xe7|\xd3\xccKv\xff\xa6\x19\xf5\x8b\xd16\x94\x80\x12\x8c:\xdceԨM$WF\x99[ɂ/\x8d\xa8\xee\xbcݻӰL\x02QtXee\xf5\x17\a\xb1\fF\xee}j\x02\x88\xdfp,/\x1c\xd2\xfeϾ\xa6wYD\xa9J\x1c\xdbl\x16Q\x86\x8b\xba\xb2z\x1eЬ\xbb\x92\\M\x1c\xf3YaU\xca\xeb\xfa\x01\x12\xba\x98\x8f\x81N\xec\v$T\x87Oo\xdb\xe6\xf2\x19\x92\xf6\xe4?g\xef\xfe\x8d~\xb8q\x0f\x85\xe0\xf5\x85x\xb6\xf3{$x=\x11\x90\xe9\x9fT\xf6=\x12\x12\xc9l?\xff\xfe\x8d.\x11Dz^!\x1d\xb4A\rQ2\xf8k\x97h\xef\xf7\xf6\xa0\x9f\x14~\xceݓ\xd11v\xb7\x96\a\xfb}4w'?\xbd{\x19\x9c}λ\xfdd\u05fbc\x9e\u07bd\x1cN\xa2\xa6ϼ\xdbWt\xb9[~\xf2\xf4\xee\x91\xf0\x0f}\xb7\xc5\xed\xddF\xf1\x0f{(e\x9f\xb3\xa7+\x9e>\x91\x8f\xba\xa0V\xcf<\xe1+\xffa\x0f\xaazb*iþoC\x9f\xb0\x01\x8b>2\xd1\xd5\xff\xd5\xc6]CK\rDa\x00\xbe\xf3\xde\xee\xe0\xee\xee\xee\xee\xee\x1d\xee\xee\xee\xe9\x0f\x15\x1e\x16\xa7\xa2\xa5})\x91\xeeYI\x0554x\x87\xeb0\x99{ϟ\xc9f\xda=_\xee\xf9\xf7\x8e\xacN\xccwc\xf8..-\x16\xda\xc7Gq\xcf\xe8\xb7Z\xccIe#D\x17\x02:RW\x02\xba}N/\x86\x8e\xe9N@wԾn\x01}\x94\x1a\xac\xeeQ\xa5;g]0\xaf\xac\xbb\xe0Ҍ\xe3\xec\xe6\x11\xea\xe3\xef\x82IY\xd2\xd4\xd9\xe9\xbe\xc6\xd3\x11M\x83Ə\x17\x89fm\xde[\xcd\xd9Y\xc7fa\xa0vK\xcd\xfa\xa5ջ\\v\xd6G\xcd\xe9,;tk\x9b\xbd+g\xe7{Z\xb8\x192Yf\xc8o5\xbb\xd8w\x9e!|'\n7\xfb\xa6\xc8\xcc^\xabf\x8a\xc6=)\x92A-\xc3:R\xaf\x03zpNτ\x8e\xe9cHk_\x0f\x85\xae\xd0[5\xb7\xa0\x87\xf8\xb9\xf5_裪\x8f\xadݣJ\x8f\xb0}\x99\xc8}\xe1s\xf8nUΒg:\x06\xeb\f\a\xbf\x93\xae\x9c\xbd\xa0#\xf5\x04\x9a_\xd3S\xdd-\xa7\xa7C\xc7\xd4\x10\xa8\xddC{Z\xff\x86>J_\xb0*3\xdd\xcbf\x1f\xc2\xd9\xf9,\xb8K3O\xd2\\B}\xfca-i\xc3i\xf8\x94o\xf9\x9f\xd5\xcbd\xe5\xbd \x12\x8d\xaf^\x93ѭ\xc2:R\xfb\x03z\x8c\xaf5\x89\xd6\x14\x9f\fX\xe5W\xfe\x04[9Z*ر9ۈ\x14G\xe9!\x11\xf5\xa8\xd2\x13\u0557\xbaY\xd2\x13N\xb2\rWܠ\xafԦp\xc5X\xdb\xc5\xe9r\xc5\xf6\u007fK\xd3\x13\xb5\xf6\x8a\x15\xb2S-Hw*>\a\xea:\xb0J\xfaՈ\x0e\xe0\x1f\xeb\xc9\x00\xee@AGj}@\x0f\xf4\xb5&њ\xe2\x9d\x01\xab\xfcʍ\xa8|\x94\xe2\xac\a\xd0\xc3l\x0f\xc6\xcb>\xd5dݡr\xba\n\xae\xba\xeaK\xaf\x9b\xdcN\x92\xfa\xb2$\xb9GT3óQ\x1f\x93ۣR\xabٚ\xe7֎-\x1bgy\x8f\x1an\x8a\x95[*\xd6Ϭ^Vn\x80>j6ZݣJ\xb7Qأ\xf8\x1c\x97\x9b\x19kyf\xec)\x15\xb2t\xe3\xea|\xba\xca\xf5{\x9d\x8c\x8eM\x97{}Lu\xbfVa\x1d\xa9\x9d\x01\xdd\xdfךDk\x8a\x0f\al.\xc7'\xd8\xca\xfeR\xc1\x0e\xc8\xd9F\xa48J\x0f\xb2\x91\x84\x1eb;2\x96;\xc2gE\\\xee-\xf2,\x9b\x91\x1b\xff~H:s\ue08e\xd4Fh\x9c(I\xba\xf8Z\x93hM\xf1\xeeBe\xee6*7\xa2\xf2Q\xaadɡ{\xda\xe4\x8389\x9ffpYvH\x96>D\xb9\xbd\tɡ\x1bEk:\xba\x01\xd5\xddw\xe1\xa9\xeda\xab\x0fL\xab\xff\a\"\x880\x93\x00\x00x\x01MLJ\x81\x031\x10BQ\xdd\aF\xda\xfe\x1b\xbcR\x8c\xb3\xdfD\xd6Z\u007f\x9d\xff\xeb\xf2Z\x80&{\x90\x1c\xc0\xf9\x80.\x9f\xa9d\x86L\x1a^\xe9tcl%\xd5\r\xe1\xa4\xd4\xf2\xb6-4$\x13\xc6\xcd\xd9V$\xc5 {\xef\xed\x93IN\x8a\xebX\x9e\r\xe8\xc1J\xa9`^R\xd4|\xe4\a\x95\x0f\xfb\x06\x85\xcc\x04\xdf\x00\x01\x00\x00\x00\x02\x00\x000\x1bQ\xae_\x0f<\xf5\x00\x1b\b\x00\x00\x00\x00\x00\xc4\xf0\x11.\x00\x00\x00\x00\xd0\xdbN\x9a\xfa\x1b\xfd\xd5\t0\bs\x00\x00\x00\t\x00\x02\x00\x00\x00\x00\x00\x00x\x01c`d``\xcf\xf9\xc7\xc3\xc0\xc0\xe9\xf9K\xfa\x9f\x17\xa7\x01P\x04\x15\xdc\x05\x00p[\x05E\x00x\x01m\x92\x03\f&M\x10D\xdf\xce\xf4\xee\xf7۶m\xf3l۶m۶m۶m\x87g\xdbWg&y\x99v:\xd3\xe5[Q\b\x00 \xb8,\x84{\x91~\xae-\xcd\xec(U\xad\x0ee\xc2]\x14\xb7\x8b\x14\x0e\nS\xd4͢\xb1[\xc6\x17\xbe\x02\xefYs\xd2\x04\x1dx\xdf\xfd\xce\xe7\xee]\xda\xf9T\xbc\xa2\xfa\x8ab\x84\xc8-\xf2\x88\xdfE\x0f\xd1H\xa4\x16\xe5D\xa1\xe0\x04M\x82\xf1|m/\x93\xcc\n\xd1\xcd~\xa0\x89_J\xca؏\x94\b\xff\xd2\xec\xcb\f\x0f\u007f\xa7j\x18\x97\xe1\xd6J\x94\x92_\x91\x1a\xe1$\x86\xbb\xef\x99j%\xf8=4\xc5\xd30<\xba\xa2\xdc$Q\x8f*\xe1s\xb7\xde\xd2\xda}\x98\xcd%\x9d-\xe1\xe7\xf0'Z\x85o\xf2^\xecm\xfe\x0f\x8d\xbfm\r/\xda\n\xb2\xb8\x0f\xe9\xe0\x93\xf1\x95ާ}6\xe2\xf9^\x98\xab\xae|\x0e\xf5צ\x95\xfdD.kH^\xfb\x9f\xdcn)\xff)\x96ϊ\xd1*8I\xeb\xe0\xe0\xf5\xb9\xf6\x82ޓ\xf4\x8ayZZ\tշ#签V\xe4u\x93\xf5~\xa4\xb77O[q\x9a\xf9}|\x1cy\xbe\xf5\xe7\xf9\xda/\xe7\r\xbd\xd9T\x13/8\xcdX\xbd\xaf\x86\xe5i(\xbb\xa5壸\r!g8I\xef6>\f\x0e\xd3\xd1\x0e\x90C\xfb\x15\x89\x92\x93\xd3w\xa4\xa3\x9fO\x11+C\xdd(\x85\xe2#\xa8\xe6\xae\xd2\xc0\x92\x90\xdf\x1d'\x81\x88\xe3\x1aPӚ\xd1\xc3\x1f$\xa1{\x83\x8e\xbai%\xc5k\xf8\x81b\x1eEt\xcf\xcc\xd1喇\xfe\xa6\xacvI\x14\xabq\xfd\x94%\"\xfb\xcd\xff\xb3\x19\f\xb1\x05\xb4\x8c\xe6\x93\xc6\xc6Q\xc7ړT\xb3rZ/:\a\x97\xe8\x19\xebO\xb1X\"\xf2\xfbE\fw\xa3\xa9\xee\x97P3\x8cǀ(.\xed\x82\xee\xe4\x13?\x05]\xf9\xccm\xa2\x98\xdb\xc9\u007fn\xbe\xec\t\xb4pE(%\xb2k\x87\u007f\xb5\xcb\x1f\xd2@^\xeb\xa0\x1b\x95\xe1Ck\xa9:\xedwo\u007f\xe5o\xe9P\xba\ns\v\xe9\xe1\x89H\x1f&m<\x88\xfb\xfe\xfa>\xe9c\x9cރb\xcb=]<\x8a4\xf1 \xb74\xa1\xbb?\x11\xe9\xc0\xa4\x81\a\t.]_\x12\\\xa2\x8b\u07b5bֽ\xfb?\x8an\xff 7o\xcf\x1b7\x00\x1c\x98\xd7\xf8\x00\x00\x00\x00\x00\x00a\x00a\x00a\x00a\x00a\x00\x93\x00\xb8\x018\x01\xaa\x02:\x02\xcd\x02\xe4\x03\x0e\x038\x03k\x03\x90\x03\xaf\x03\xc5\x03\xe6\x03\xfd\x04J\x04x\x04\xc7\x05<\x05\u007f\x05\xdf\x06>\x06k\x06\xdf\aF\a[\ap\a\x8f\a\xb6\a\xd5\b3\b\xd6\t\x15\tt\t\xc8\n\r\nM\n\x83\n\xeb\v-\vH\v{\v\xd0\v\xf4\fB\f~\f\xd3\r\x1e\r\x83\r\xdf\x0eJ\x0et\x0e\xb6\x0e\xe6\x0f;\x0f\x90\x0f\xc0\x0f\xf8\x10\x1c\x103\x10X\x10\u007f\x10\x9a\x10\xba\x112\x11\x90\x11\xe3\x12A\x12\xa8\x12\xfa\x13t\x13\xb9\x13\xf1\x14=\x14\x94\x14\xaf\x15\x1a\x15e\x15\xb3\x16\x17\x16x\x16\xb5\x17\x1f\x17q\x17\xb8\x17\xe8\x186\x18}\x18\xc2\x18\xfa\x19;\x19R\x19\x92\x19\xd9\x1a\f\x1ah\x1a\xda\x1b=\x1b\x9c\x1b\xbb\x1c`\x1c\x8f\x1d5\x1d\xa3\x1d\xaf\x1d\xcc\x1e\x84\x1e\x9a\x1e\xd6\x1f\x19\x1fi\x1f\xe4 \x04 M y \x98 \xd3!\x05!O![!u!\x8f!\xa9\"\n\"m\"\xab#&#z#\xea$\xa8%\x17%h%\xd9&8&S&\xd6'p'\x9e'\xd7(\x1b(%(/(S(w(\x99(\xa5(\xb1(\xe9)\f)()E)X)l)\xe8*\x03*k*\xbd*\xe8+7+\xa6+\xe8+\xe8+\xf0,V,m,\x84,\x9b,\xb2,\xcb,\xe4,\xf0-\a-\x1e-5-N-e-|-\x93-\xac-\xc3-\xda-\xf1.\b.\x1f.8.O.f.}.\x96.\xad.\xc4.\xdb.\xf1/\a/ /9/E/\\/s/\x89/\xa2/\xb8/\xce/\xe5/\xfe0\x140+0B0X0n0\x870\x9e0\xb50\xcb0\xe40\xfb1\x13\x00\x00\x00\x01\x00\x00\x00\xde\x00\x8f\x00\x16\x00T\x00\x05\x00\x01\x00\x00\x00\x00\x00\x0e\x00\x00\x02\x00\x02\x14\x00\x06\x00\x01x\x01]\x8e3{\x03\x00\x18\x84\xdf\xda\xdd\xeb\xeee0%[l{\x8am\xfe\xfd\\\x8c\xe73\xef\x80+r\x9cqr~\xc3\t\xf7\xb0\xceOyT\xb5\xca\xcf\xf6\xfa\xe7{\xf9\x05\xdf<\xaf\xf3K^q\xad\xf3G\x12\x14\x89ѓ\xef1\x96ŨPcB\x9b\x02CRT\xe4G44\xe9\xf2\x89\x91_\xfe%\x06\x89Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x04\x02\x00\x11\x129\xb2\x05\x02\x00\x11\x129\xb2\a\x02\x00\x11\x129\xb2\b\x02\x00\x11\x129\xb1\n\f\xf4\xb2\f\x02\x00\x11\x129\xb2\r\x02\x00\x11\x129\xb0\x02\x10\xb1\x0e\f\xf401!!\x11!\x03\x11\x01\x01\x11\x01\x03!\x015\x01!\x03(\xfd<\x02\xc46\xfe\xee\xfe\xba\x01\f\xe4\x02\x03\xfe\xfe\x01\x02\xfd\xfd\x05\xb0\xfa\xa4\x05\a\xfd}\x02w\xfb\x11\x02x\xfd^\x02^\x88\x02^\x00\x02\x00\xa0\xff\xf5\x01{\x05\xb0\x00\x03\x00\f\x00/\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x10>Y\xb2\x06\x05\n+X!\xd8\x1b\xf4Y\xb2\x01\x06\x02\x11\x12901\x01#\x033\x03462\x16\x14\x06\"&\x01[\xa7\r\xc2\xc97l88l7\x01\x9b\x04\x15\xfa\xad-==Z;;\x00\x02\x00\x88\x04\x12\x02#\x06\x00\x00\x04\x00\t\x00\x19\x00\xb0\x03/\xb2\x02\n\x03\x11\x129\xb0\x02/\xb0\aа\x03\x10\xb0\b\xd001\x01\x03#\x133\x05\x03#\x133\x01\x15\x1eo\x01\x8c\x01\x0e\x1eo\x01\x8c\x05x\xfe\x9a\x01\xee\x88\xfe\x9a\x01\xee\x00\x02\x00w\x00\x00\x04\xd3\x05\xb0\x00\x1b\x00\x1f\x00\x8f\x00\xb0\x00EX\xb0\f/\x1b\xb1\f\x1c>Y\xb0\x00EX\xb0\x10/\x1b\xb1\x10\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x00EX\xb0\x1a/\x1b\xb1\x1a\x10>Y\xb2\x1d\f\x02\x11\x129|\xb0\x1d/\x18\xb2\x00\x03\n+X!\xd8\x1b\xf4Y\xb0\x04а\x1d\x10\xb0\x06а\x1d\x10\xb0\vа\v/\xb2\b\x03\n+X!\xd8\x1b\xf4Y\xb0\v\x10\xb0\x0eа\v\x10\xb0\x12а\b\x10\xb0\x14а\x1d\x10\xb0\x16а\x00\x10\xb0\x18а\b\x10\xb0\x1e\xd001\x01!\x03#\x13#5!\x13!5!\x133\x03!\x133\x033\x15#\x033\x15#\x03#\x03!\x13!\x02\xfd\xfe\xf8P\x8fP\xef\x01\tE\xfe\xfe\x01\x1dR\x8fR\x01\bR\x90R\xcc\xe7E\xe1\xfbP\x90\x9e\x01\bE\xfe\xf8\x01\x9a\xfef\x01\x9a\x89\x01b\x8b\x01\xa0\xfe`\x01\xa0\xfe`\x8b\xfe\x9e\x89\xfef\x02#\x01b\x00\x00\x01\x00n\xff0\x04\x11\x06\x9c\x00+\x00f\x00\xb0\x00EX\xb0\t/\x1b\xb1\t\x1c>Y\xb0\x00EX\xb0\"/\x1b\xb1\"\x10>Y\xb2\x02\"\t\x11\x129\xb0\t\x10\xb0\fа\t\x10\xb0\x10а\t\x10\xb2\x13\x01\n+X!\xd8\x1b\xf4Y\xb0\x02\x10\xb2\x19\x01\n+X!\xd8\x1b\xf4Y\xb0\"\x10\xb0\x1fа\"\x10\xb0&а\"\x10\xb2)\x01\n+X!\xd8\x1b\xf4Y01\x014&'&&546753\x15\x16\x16\x15#4&#\"\x06\x15\x14\x16\x04\x16\x16\x15\x14\x06\a\x15#5&&53\x14\x16326\x03X\x81\x99\xd5ÿ\xa7\x95\xa8\xbb\xb8\x86rw~\x85\x011\xabQ˷\x94\xbaӹ\x92\x86\x83\x96\x01w\\~3Aѡ\xa4\xd2\x14\xdb\xdc\x17\xec͍\xa6{nfycw\x9ej\xa9\xce\x13\xbf\xbf\x11\xe7Ƌ\x96~\x00\x05\x00i\xff\xeb\x05\x83\x05\xc5\x00\r\x00\x1a\x00&\x004\x008\x00x\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb0\x00EX\xb0#/\x1b\xb1#\x10>Y\xb0\x03\x10\xb0\nа\n/\xb2\x11\x04\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x04\n+X!\xd8\x1b\xf4Y\xb0#\x10\xb0\x1dа\x1d/\xb0#\x10\xb2*\x04\n+X!\xd8\x1b\xf4Y\xb0\x1d\x10\xb21\x04\n+X!\xd8\x1b\xf4Y\xb25#\x03\x11\x129\xb05/\xb27\x03#\x11\x129\xb07/01\x134632\x16\x15\x15\x14\x06#\"&5\x17\x14\x16326554&\"\x06\x15\x0146 \x16\x15\x15\x14\x06 &5\x17\x14\x16326554&#\"\x06\x15\x05'\x01\x17i\xa7\x83\x85\xa5\xa7\x81\x82\xaa\x8aXJGWV\x94V\x02;\xa7\x01\x06\xa8\xa7\xfe\xfc\xaa\x8aXJHVWIGY\xfe\ai\x02\xc7i\x04\x98\x83\xaa\xab\x88G\x84\xa7\xa7\x8b\aNebUINffR\xfcу\xa9\xa8\x8bG\x83\xa9\xa7\x8b\x06OecUJOdcT\xf3B\x04rB\x00\x03\x00e\xff\xec\x04\xf3\x05\xc4\x00\x1e\x00'\x003\x00\x85\x00\xb0\x00EX\xb0\t/\x1b\xb1\t\x1c>Y\xb0\x00EX\xb0\x1c/\x1b\xb1\x1c\x10>Y\xb0\x00EX\xb0\x18/\x1b\xb1\x18\x10>Y\xb2\"\x1c\t\x11\x129\xb2*\t\x1c\x11\x129\xb2\x03\"*\x11\x129\xb2\x10*\"\x11\x129\xb2\x11\t\x1c\x11\x129\xb2\x13\x1c\t\x11\x129\xb2\x19\x1c\t\x11\x129\xb2\x16\x11\x19\x11\x129\xb0\x1c\x10\xb2\x1f\x01\n+X!\xd8\x1b\xf4Y\xb2!\x1f\x11\x11\x129\xb0\t\x10\xb21\x01\n+X!\xd8\x1b\xf4Y01\x13467&&54632\x16\x15\x14\x06\a\a\x01653\x14\a\x17#'\x06\x06#\"$\x0527\x01\a\x06\x15\x14\x16\x03\x14\x1776654&#\"\x06eu\xa5aBĨ\x96\xc4Yok\x01DD\xa7{\xd0\xdeaJ\xc7g\xd5\xfe\xfe\x01דz\xfe\x9d!\xa7\x99\"vvD2dLR`\x01\x87i\xb0uv\x90G\xa6\xbc\xaf\x85X\x95RO\xfe}\x82\x9f\xff\xa8\xf9sBE\xe2Kp\x01\xa9\x18{\x82v\x8e\x03\xe5`\x90S0W>CYo\x00\x01\x00g\x04!\x00\xfd\x06\x00\x00\x04\x00\x10\x00\xb0\x03/\xb2\x02\x05\x03\x11\x129\xb0\x02/01\x13\x03#\x133\xfd\x15\x81\x01\x95\x05\x91\xfe\x90\x01\xdf\x00\x01\x00\x85\xfe*\x02\x95\x06k\x00\x11\x00\t\x00\xb0\x0e/\xb0\x04/01\x134\x12\x127\x17\x06\x02\x03\a\x10\x13\x16\x17\a&'\x02\x85y\xf0\x81&\x92\xbb\t\x01\x8dUu&\x85y\xec\x02O\xe2\x01\xa0\x01TFzp\xfe4\xfe\xe3U\xfe~\xfe\xe4\xaa`qJ\xae\x01T\x00\x00\x01\x00&\xfe*\x027\x06k\x00\x11\x00\t\x00\xb0\x0e/\xb0\x04/01\x01\x14\x02\x02\a'6\x12\x1354\x02\x02'7\x16\x12\x12\x027u\xf1\x84'\x9a\xbb\x02X\x9db'\x84\xefw\x02E\xdf\xfeg\xfe\xa6Iqv\x01\xf1\x01/ \xd2\x01i\x01\x1ePqI\xfe\xaa\xfed\x00\x01\x00\x1c\x02a\x03U\x05\xb0\x00\x0e\x00 \x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb0\x00\xd0\x19\xb0\x00/\x18\xb0\t\xd0\x19\xb0\t/\x1801\x01%7\x05\x033\x03%\x17\x05\x13\a\x03\x03'\x01J\xfe\xd2.\x01.\t\x99\n\x01).\xfe\xcd\xc6|\xba\xb4}\x03\xd7Z\x97p\x01X\xfe\xa3n\x98[\xfe\xf1^\x01 \xfe\xe7[\x00\x00\x01\x00N\x00\x92\x044\x04\xb6\x00\v\x00\x1a\x00\xb0\t/\xb0\x00а\t\x10\xb2\x06\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\xd001\x01!\x15!\x11#\x11!5!\x113\x02\x9e\x01\x96\xfej\xba\xfej\x01\x96\xba\x03\r\xaf\xfe4\x01̯\x01\xa9\x00\x01\x00\x1d\xfe\xde\x014\x00\xdb\x00\b\x00\x17\x00\xb0\t/\xb2\x04\x05\n+X!\xd8\x1b\xf4Y\xb0\x00а\x00/01\x13'6753\x15\x14\x06\x86i^\x04\xb5c\xfe\xdeH\x83\x8b\xa7\x91e\xca\x00\x00\x01\x00%\x02\x1f\x02\r\x02\xb6\x00\x03\x00\x11\x00\xb0\x02/\xb2\x01\x01\n+X!\xd8\x1b\xf4Y01\x01!5!\x02\r\xfe\x18\x01\xe8\x02\x1f\x97\x00\x01\x00\x90\xff\xf5\x01v\x00\xd1\x00\t\x00\x1b\x00\xb0\x00EX\xb0\a/\x1b\xb1\a\x10>Y\xb2\x02\x05\n+X!\xd8\x1b\xf4Y017462\x16\x15\x14\x06\"&\x909r;;r9a0@@0.>>\x00\x01\x00\x12\xff\x83\x03\x10\x05\xb0\x00\x03\x00\x13\x00\xb0\x00/\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y01\x17#\x013\xb1\x9f\x02`\x9e}\x06-\x00\x00\x02\x00s\xff\xec\x04\n\x05\xc4\x00\r\x00\x1b\x009\x00\xb0\x00EX\xb0\n/\x1b\xb1\n\x1c>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb0\n\x10\xb2\x11\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y01\x01\x10\x02#\"\x02\x035\x10\x1232\x12\x13'4&#\"\x06\a\x11\x14\x163267\x04\n\xde\xec\xe9\xe0\x04\xde\xed\xeb\xde\x03\xb9\x84\x8f\x8e\x82\x02\x89\x8b\x89\x85\x03\x02m\xfe\xbb\xfe\xc4\x015\x013\xf7\x01A\x018\xfe\xd3\xfe\xc6\r\xeb\xd7\xd6\xde\xfe\xd8\xec\xe1\xd4\xe4\x00\x01\x00\xaa\x00\x00\x02\xd9\x05\xb7\x00\x06\x009\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x04\x00\x05\x11\x129\xb0\x04/\xb2\x03\x01\n+X!\xd8\x1b\xf4Y\xb2\x02\x03\x05\x11\x12901!#\x11\x055%3\x02ٺ\xfe\x8b\x02\x12\x1d\x04щ\xa8\xc7\x00\x00\x01\x00]\x00\x00\x043\x05\xc4\x00\x17\x00M\x00\xb0\x00EX\xb0\x10/\x1b\xb1\x10\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x17\x01\n+X!\xd8\x1b\xf4Y\xb0\x02в\x03\x10\x17\x11\x129\xb0\x10\x10\xb2\t\x01\n+X!\xd8\x1b\xf4Y\xb0\x10\x10\xb0\fв\x15\x17\x10\x11\x12901!!5\x016654&#\"\x06\x15#4$32\x16\x15\x14\x01\x01!\x043\xfcF\x01\xf8pU\x8as\x8a\x99\xb9\x01\x03\xd9\xcb\xec\xfe\xee\xfez\x02ۅ\x020\u007f\x9fUr\x92\x9d\x8c\xc9\xf8ձ\xd7\xfe\xd7\xfeY\x00\x01\x00^\xff\xec\x03\xf9\x05\xc4\x00&\x00x\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x1c>Y\xb0\x00EX\xb0\x19/\x1b\xb1\x19\x10>Y\xb2\x00\r\x19\x11\x129\xb0\x00/\xb2\xcf\x00\x01]\xb2\x9f\x00\x01q\xb2/\x00\x01]\xb2_\x00\x01r\xb0\r\x10\xb2\x06\x01\n+X!\xd8\x1b\xf4Y\xb0\r\x10\xb0\tа\x00\x10\xb2&\x01\n+X!\xd8\x1b\xf4Y\xb2\x13&\x00\x11\x129\xb0\x19\x10\xb0\x1cа\x19\x10\xb2\x1f\x01\n+X!\xd8\x1b\xf4Y01\x013665\x10#\"\x06\x15#4632\x16\x15\x14\x06\a\x16\x16\x15\x14\x04 $53\x14\x1632654&'#\x01\x86\x8b\x83\x96\xffx\x8f\xb9\xfd\xc3\xce\xea{jx\x83\xff\x00\xfef\xfe\xff\xba\x96~\x86\x8e\x9c\x93\x8b\x032\x02\x86r\x01\x00\x89q\xad\xe5\xda\xc2_\xb2,&\xb0\u007f\xc4\xe6\u07b6s\x8a\x8c\x83\u007f\x88\x02\x00\x02\x005\x00\x00\x04P\x05\xb0\x00\n\x00\x0e\x00I\x00\xb0\x00EX\xb0\t/\x1b\xb1\t\x1c>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb2\x01\t\x04\x11\x129\xb0\x01/\xb2\x02\x01\n+X!\xd8\x1b\xf4Y\xb0\x06а\x01\x10\xb0\vв\b\x06\v\x11\x129\xb2\r\t\x04\x11\x12901\x013\x15#\x11#\x11!5\x013\x01!\x11\a\x03\x86\xcaʺ\xfdi\x02\x8c\xc5\xfd\x81\x01\xc5\x16\x01\xe9\x97\xfe\xae\x01Rm\x03\xf1\xfc9\x02\xca(\x00\x01\x00\x9a\xff\xec\x04-\x05\xb0\x00\x1d\x00a\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb0\x00EX\xb0\r/\x1b\xb1\r\x10>Y\xb0\x01\x10\xb2\x04\x01\n+X!\xd8\x1b\xf4Y\xb2\a\r\x01\x11\x129\xb0\a/\xb2\x1a\x01\n+X!\xd8\x1b\xf4Y\xb2\x05\a\x1a\x11\x129\xb0\r\x10\xb0\x11а\r\x10\xb2\x14\x01\n+X!\xd8\x1b\xf4Y\xb0\a\x10\xb0\x1d\xd001\x13\x13!\x15!\x03632\x12\x15\x14\x02#\"&'3\x16\x1632654&#\"\a\a\xceJ\x02\xea\xfd\xb3,k\x88\xc7\xea\xf3\xda\xc1\xf4\x11\xaf\x11\x90v\x81\x93\x9f\x84yE1\x02\xda\x02֫\xfes?\xfe\xf9\xe0\xe1\xfe\xfdֽ}\u007f\xb0\x9b\x92\xb15(\x00\x02\x00\x84\xff\xec\x04\x1c\x05\xb1\x00\x14\x00!\x00N\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x1c>Y\xb0\x00EX\xb0\r/\x1b\xb1\r\x10>Y\xb0\x00\x10\xb2\x01\x01\n+X!\xd8\x1b\xf4Y\xb2\a\r\x00\x11\x129\xb0\a/\xb2\x15\x01\n+X!\xd8\x1b\xf4Y\xb0\r\x10\xb2\x1c\x01\n+X!\xd8\x1b\xf4Y01\x01\x15#\x06\x04\a632\x12\x15\x14\x02#\"\x0055\x10\x00%\x03\"\x06\a\x15\x14\x1632654&\x03O\"\xd8\xff\x00\x14sǾ\xe3\xf5\xce\xd1\xfe\xfc\x01W\x01S\xd2_\xa0\x1f\xa2y}\x8f\x91\x05\xb1\x9d\x04\xf8\xe1\x84\xfe\xf4\xd4\xe1\xfe\xf2\x01A\xfdG\x01\x92\x01\xa9\x05\xfdprVD\xb4ܸ\x95\x96\xb9\x00\x01\x00M\x00\x00\x04%\x05\xb0\x00\x06\x002\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x10>Y\xb0\x05\x10\xb2\x03\x01\n+X!\xd8\x1b\xf4Y\xb2\x00\x03\x05\x11\x12901\x01\x01#\x01!5!\x04%\xfd\xa5\xc2\x02Y\xfc\xec\x03\xd8\x05H\xfa\xb8\x05\x18\x98\x00\x00\x03\x00p\xff\xec\x04\x0e\x05\xc4\x00\x17\x00!\x00+\x00a\x00\xb0\x00EX\xb0\x15/\x1b\xb1\x15\x1c>Y\xb0\x00EX\xb0\t/\x1b\xb1\t\x10>Y\xb2'\t\x15\x11\x129\xb0'/\xb2\xcf'\x01]\xb2\x1a\x01\n+X!\xd8\x1b\xf4Y\xb2\x03\x1a'\x11\x129\xb2\x0f'\x1a\x11\x129\xb0\t\x10\xb2\x1f\x01\n+X!\xd8\x1b\xf4Y\xb0\x15\x10\xb2\"\x01\n+X!\xd8\x1b\xf4Y01\x01\x14\x06\a\x16\x16\x15\x14\x06#\"&5467&&54632\x16\x034&\"\x06\x14\x16326\x01\"\x06\x15\x14\x16264&\x03\xecsbr\x85\xff\xd0\xd2\xfd\x81rap\xec\xc1\xc0헛\xfa\x97\x93\x83\x82\x94\xfe\xeam\x87\x85ޅ\x8a\x044m\xaa01\xbcw\xbd\xe0\xe1\xbcv\xbe10\xaal\xb8\xd8\xd8\xfc\xa1z\x9a\x98\xf8\x8e\x8f\x04\x1a\x87to\x89\x89ތ\x00\x00\x02\x00d\xff\xff\x03\xf8\x05\xc4\x00\x17\x00$\x00X\x00\xb0\x00EX\xb0\v/\x1b\xb1\v\x1c>Y\xb0\x00EX\xb0\x13/\x1b\xb1\x13\x10>Y\xb2\x03\x13\v\x11\x129\xb0\x03/\xb2\x00\x03\v\x11\x129\xb0\x13\x10\xb2\x14\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y\xb0\v\x10\xb2\x1f\x01\n+X!\xd8\x1b\xf4Y01\x01\x06\x06#\"&&546632\x12\x11\x15\x10\x00\x05#5366%26754&#\"\x06\x15\x14\x16\x03>:\xa1`~\xbbfö\xd8\xf9\xfe\xb0\xfe\xad$'\xe5\xf6\xfe\xee]\x9d$\x9eyz\x94\x8f\x02\x80ET|ሒ\xea|\xfe\xbd\xfe\xe96\xfeW\xfey\x05\x9c\x04\xe7\xfarTJ\xb6仙\x95\xc1\x00\xff\xff\x00\x86\xff\xf5\x01m\x04D\x00&\x00\x12\xf6\x00\x01\a\x00\x12\xff\xf7\x03s\x00\x10\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x18>Y01\xff\xff\x00)\xfe\xde\x01U\x04D\x00'\x00\x12\xff\xdf\x03s\x01\x06\x00\x10\f\x00\x00\x10\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y01\x00\x01\x00H\x00\xc3\x03z\x04J\x00\x06\x00\x16\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x18>Y\xb0\x02а\x02/01\x01\x05\x15\x015\x01\x15\x01\b\x02r\xfc\xce\x032\x02\x84\xfd\xc4\x01{\x92\x01z\xc4\x00\x00\x02\x00\x98\x01\x8f\x03\xda\x03\xcf\x00\x03\x00\a\x00%\x00\xb0\a/\xb0\x03а\x03/\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\a\x10\xb2\x04\x01\n+X!\xd8\x1b\xf4Y01\x01!5!\x11!5!\x03\xda\xfc\xbe\x03B\xfc\xbe\x03B\x03.\xa1\xfd\xc0\xa0\x00\x00\x01\x00\x86\x00\xc4\x03\xdc\x04K\x00\x06\x00\x16\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x18>Y\xb0\x05а\x05/01\x01\x015\x01\x15\x015\x03\x1b\xfdk\x03V\xfc\xaa\x02\x8a\x01\x03\xbe\xfe\x86\x92\xfe\x85\xc0\x00\x02\x00K\xff\xf5\x03v\x05\xc4\x00\x18\x00!\x00Q\x00\xb0\x00EX\xb0\x10/\x1b\xb1\x10\x1c>Y\xb0\x00EX\xb0 /\x1b\xb1 \x10>Y\xb2\x1b\x05\n+X!\xd8\x1b\xf4Y\xb2\x00\x1b\x10\x11\x129\xb2\x04\x10\x00\x11\x129\xb0\x10\x10\xb2\t\x01\n+X!\xd8\x1b\xf4Y\xb0\x10\x10\xb0\fв\x15\x00\x10\x11\x12901\x016677654&#\"\x06\x15#6632\x16\x15\x14\a\a\x06\x15\x03462\x16\x14\x06\"&\x01e\x022M\x83Tnif|\xb9\x02㶽ӢmI\xc17l88l7\x01\x9aw\x8aT\x87_miwl[\xa2\xc7˱\xaf\xaalQ\x98\xfe\xc3-==Z;;\x00\x00\x02\x00j\xfe;\x06\xd6\x05\x97\x005\x00B\x00h\x00\xb02/\xb0\x00EX\xb0\b/\x1b\xb1\b\x10>Y\xb0\x03в\x0f2\b\x11\x129\xb0\x0f/\xb2\x05\b\x0f\x11\x129\xb0\b\x10\xb29\x02\n+X!\xd8\x1b\xf4Y\xb0\x15а2\x10\xb2\x1b\x02\n+X!\xd8\x1b\xf4Y\xb0\b\x10\xb0*а*/\xb2#\x02\n+X!\xd8\x1b\xf4Y\xb0\x0f\x10\xb2@\x02\n+X!\xd8\x1b\xf4Y01\x01\x06\x02#\"'\x06\x06#\"&76\x12632\x16\x17\x03\x063267\x12\x00!\"\x04\x02\a\x06\x12\x043267\x17\x06\x06#\"$\x02\x13\x12\x12$32\x04\x12\x01\x06\x1632677\x13&#\"\x06\x06\xca\fص\xbb56\x8bJ\x8e\x92\x13\x0fy\xbfiQ\x80P4\x13\x93q\x8c\x06\x13\xfe\xb9\xfe\xb2\xc9\xfeȴ\v\f\x90\x01'\xd1Z\xb5<%>\xcdi\xfa\xfe\x98\xb3\f\f\xde\x01|\xef\xf9\x01d\xae\xfb\xf2\x0eQXY\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x10>Y\xb2\t\x04\x02\x11\x129\xb0\t/\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb2\n\x04\x02\x11\x12901\x01!\x03#\x013\x01#\x01!\x03\x03\xcd\xfd\x9e\x89\xc6\x02,\xa8\x02-\xc5\xfdM\x01\xef\xf8\x01|\xfe\x84\x05\xb0\xfaP\x02\x1a\x02\xa9\x00\x03\x00\xa9\x00\x00\x04\x88\x05\xb0\x00\x0e\x00\x16\x00\x1f\x00U\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x17\x00\x01\x11\x129\xb0\x17/\xb2\x0f\x01\n+X!\xd8\x1b\xf4Y\xb2\b\x0f\x17\x11\x129\xb0\x00\x10\xb2\x10\x01\n+X!\xd8\x1b\xf4Y\xb0\x01\x10\xb2\x1f\x01\n+X!\xd8\x1b\xf4Y013\x11!2\x16\x15\x14\x06\a\x16\x16\x15\x14\x06#\x01\x11!265\x10!%!2654&#!\xa9\x01\xdc\xed\xeftdv\x89\xfe\xe8\xfe\xc7\x01=\x86\x9b\xfe\xe2\xfe\xc0\x01\"~\x97\x8c\x8f\xfe\xe4\x05\xb0\xc4\xc0f\x9d+!\xb9\x80\xc4\xe0\x02\xa9\xfd\xf4\x8bz\x01\a\x9a~lxm\x00\x00\x01\x00w\xff\xec\x04\xd8\x05\xc4\x00\x1c\x00E\x00\xb0\x00EX\xb0\v/\x1b\xb1\v\x1c>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb0\v\x10\xb0\x0fа\v\x10\xb2\x12\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x19\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb0\x1c\xd001\x01\x06\x04# \x00\x1154\x12$32\x00\x17#&&#\"\x02\x15\x15\x14\x123267\x04\xd8\x1b\xfe\xe1\xee\xfe\xfe\xfeɑ\x01\n\xaf\xe8\x01\x18\x17\xc1\x19\xa7\x96\xb8\xd1Ʋ\xa0\xab\x1c\x01\xce\xe7\xfb\x01r\x016\x8c\xcb\x014\xa5\xfe\xfd宜\xfe\xf0\xfb\x8d\xed\xfe葴\x00\x02\x00\xa9\x00\x00\x04\xc6\x05\xb0\x00\v\x00\x15\x009\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x01\x10\xb2\f\x01\n+X!\xd8\x1b\xf4Y\xb0\x00\x10\xb2\r\x01\n+X!\xd8\x1b\xf4Y013\x11!2\x04\x12\x17\x15\x14\x02\x04\a\x03\x1132\x12554\x02'\xa9\x01\x9b\xbe\x01$\x9f\x01\x9f\xfe\xd9\xc4\xd3\xca\xde\xf7\xe9\xd6\x05\xb0\xa8\xfe\xca\xc9]\xce\xfeʦ\x02\x05\x12\xfb\x8b\x01\x14\xffU\xf8\x01\x13\x02\x00\x00\x01\x00\xa9\x00\x00\x04F\x05\xb0\x00\v\x00N\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb2\v\x04\x06\x11\x129\xb0\v/\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\x04\x10\xb2\x02\x01\n+X!\xd8\x1b\xf4Y\xb0\x06\x10\xb2\b\x01\n+X!\xd8\x1b\xf4Y01\x01!\x11!\x15!\x11!\x15!\x11!\x03\xe0\xfd\x89\x02\xdd\xfcc\x03\x93\xfd-\x02w\x02\xa1\xfd\xfc\x9d\x05\xb0\x9e\xfe,\x00\x01\x00\xa9\x00\x00\x04/\x05\xb0\x00\t\x00@\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb2\t\x02\x04\x11\x129\xb0\t/\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\x04\x10\xb2\x06\x01\n+X!\xd8\x1b\xf4Y01\x01!\x11#\x11!\x15!\x11!\x03\xcc\xfd\x9d\xc0\x03\x86\xfd:\x02c\x02\x83\xfd}\x05\xb0\x9e\xfe\x0e\x00\x01\x00z\xff\xec\x04\xdc\x05\xc4\x00\x1f\x00b\x00\xb0\x00EX\xb0\v/\x1b\xb1\v\x1c>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb0\v\x10\xb0\x0fа\v\x10\xb2\x11\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y\xb2\x1e\x03\v\x11\x129\xb0\x1e/\xb4\x0f\x1e\x1f\x1e\x02]\xb4?\x1eO\x1e\x02]\xb2\x1d\x01\n+X!\xd8\x1b\xf4Y01%\x06\x04#\"$\x02'5\x10\x00!2\x04\x17#\x02!\"\x02\x03\x15\x14\x123267\x11!5!\x04\xdcJ\xfe\xf7\xb0\xb2\xfe\xec\x97\x02\x013\x01\x16\xe4\x01\x16\x1f\xc06\xfe\xde\xc1\xc7\x01\xe0\xbfl\xa25\xfe\xaf\x02\x10\xbfji\xa7\x014\xcb\u007f\x01I\x01j\xe9\xd6\x01!\xfe\xf1\xfe\xffw\xf5\xfe\xdf09\x01G\x9c\x00\x01\x00\xa9\x00\x00\x05\b\x05\xb0\x00\v\x00U\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb0\x00EX\xb0\n/\x1b\xb1\n\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb0\x00\x10\xb0\tа\t/\xb2\x9f\t\x01r\xb2/\t\x01]\xb2\x02\x01\n+X!\xd8\x1b\xf4Y01!#\x11!\x11#\x113\x11!\x113\x05\b\xc1\xfd\"\xc0\xc0\x02\xde\xc1\x02\xa1\xfd_\x05\xb0\xfd\x8e\x02r\x00\x00\x01\x00\xb7\x00\x00\x01w\x05\xb0\x00\x03\x00\x1d\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y01!#\x113\x01w\xc0\xc0\x05\xb0\x00\x00\x01\x005\xff\xec\x03\xcc\x05\xb0\x00\x0f\x00.\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x1c>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x10>Y\xb0\tа\x05\x10\xb2\f\x01\n+X!\xd8\x1b\xf4Y01\x013\x11\x14\x06#\"&53\x14\x163267\x03\v\xc1\xfb\xd1\xd9\xf2\xc0\x89\x82w\x93\x01\x05\xb0\xfb\xf9\xd1\xec\xde\xc8}\x8c\x96\x87\x00\x00\x01\x00\xa9\x00\x00\x05\x05\x05\xb0\x00\v\x00t\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb0\x00EX\xb0\a/\x1b\xb1\a\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x10>Y\xb2\x00\x02\x05\x11\x129@\x11J\x00Z\x00j\x00z\x00\x8a\x00\x9a\x00\xaa\x00\xba\x00\b]\xb29\x00\x01]\xb2\x06\x05\x02\x11\x129@\x136\x06F\x06V\x06f\x06v\x06\x86\x06\x96\x06\xa6\x06\xb6\x06\t]01\x01\a\x11#\x113\x11\x013\x01\x01#\x02\x1b\xb2\xc0\xc0\x02\x87\xe8\xfd\xc3\x02j\xe6\x02\xa5\xb9\xfe\x14\x05\xb0\xfd0\x02\xd0\xfd}\xfc\xd3\x00\x01\x00\xa9\x00\x00\x04\x1c\x05\xb0\x00\x05\x00(\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb2\x00\x01\n+X!\xd8\x1b\xf4Y01%!\x15!\x113\x01j\x02\xb2\xfc\x8d\xc1\x9d\x9d\x05\xb0\x00\x00\x01\x00\xa9\x00\x00\x06R\x05\xb0\x00\x0e\x00Y\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x10>Y\xb0\x00EX\xb0\f/\x1b\xb1\f\x10>Y\xb2\x01\x00\x04\x11\x129\xb2\a\x00\x04\x11\x129\xb2\n\x00\x04\x11\x12901\t\x023\x11#\x11\x13\x01#\x01\x13\x11#\x11\x01\xa1\x01\xdc\x01\xdc\xf9\xc0\x12\xfe\"\x93\xfe#\x13\xc0\x05\xb0\xfb\\\x04\xa4\xfaP\x027\x02d\xfbe\x04\x98\xfd\x9f\xfd\xc9\x05\xb0\x00\x00\x01\x00\xa9\x00\x00\x05\b\x05\xb0\x00\t\x00L\xb2\x01\n\v\x11\x129\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb2\x02\x05\x00\x11\x129\xb2\a\x05\x00\x11\x12901!#\x01\x11#\x113\x01\x113\x05\b\xc1\xfd#\xc1\xc1\x02\u07ff\x04b\xfb\x9e\x05\xb0\xfb\x99\x04g\x00\x02\x00v\xff\xec\x05\t\x05\xc4\x00\x11\x00\x1f\x009\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x1c>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb0\r\x10\xb2\x15\x01\n+X!\xd8\x1b\xf4Y\xb0\x04\x10\xb2\x1c\x01\n+X!\xd8\x1b\xf4Y01\x01\x14\x02\x04#\"$\x02'54\x12$32\x04\x12\x15'\x10\x02#\"\x02\a\x15\x14\x1232\x127\x05\t\x90\xfe\xf8\xb0\xac\xfe\xf6\x93\x02\x92\x01\v\xac\xaf\x01\v\x90\xbfл\xb6\xd1\x03ӹ\xba\xcc\x03\x02\xa9\xd6\xfe\xc1\xa8\xa9\x019\xcei\xd2\x01B\xab\xa9\xfe\xbf\xd5\x02\x01\x03\x01\x15\xfe\xeb\xf6k\xfb\xfe\xe1\x01\x0f\xfd\x00\x00\x02\x00\xa9\x00\x00\x04\xc0\x05\xb0\x00\n\x00\x13\x00M\xb2\n\x14\x15\x11\x129\xb0\n\x10\xb0\f\xd0\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x10>Y\xb2\v\x03\x01\x11\x129\xb0\v/\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x12\x01\n+X!\xd8\x1b\xf4Y01\x01\x11#\x11!2\x04\x15\x14\x04#%!2654&'!\x01i\xc0\x02\x19\xef\x01\x0f\xfe\xf7\xf7\xfe\xa9\x01Y\x9a\xa4\xa4\x8f\xfe\x9c\x02:\xfd\xc6\x05\xb0\xf4\xc9\xd4坑\x89\x82\x9c\x03\x00\x02\x00m\xff\n\x05\x06\x05\xc4\x00\x15\x00\"\x00M\xb2\b#$\x11\x129\xb0\b\x10\xb0\x19\xd0\x00\xb0\x00EX\xb0\x11/\x1b\xb1\x11\x1c>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x10>Y\xb2\x03\b\x11\x11\x129\xb0\x11\x10\xb2\x19\x01\n+X!\xd8\x1b\xf4Y\xb0\b\x10\xb2 \x01\n+X!\xd8\x1b\xf4Y01\x01\x14\x02\a\x05\a%\x06#\"$\x02'54\x12$32\x04\x12\x15'\x10\x02#\"\x02\a\x15\x14\x12 \x127\x05\x01\x86y\x01\x04\x83\xfe\xcdHP\xac\xfe\xf6\x93\x02\x92\x01\v\xac\xb0\x01\v\x90\xc0;\xb5\xd1\x03\xd1\x01t\xcc\x03\x02\xa9\xd3\xfe\xcfV\xccy\xf4\x12\xa9\x019\xcei\xd2\x01B\xab\xaa\xfe\xc1\xd5\x01\x01\x01\x01\x17\xfe\xeb\xf6k\xfa\xfe\xe0\x01\x0f\xfd\x00\x00\x02\x00\xa8\x00\x00\x04\xc9\x05\xb0\x00\x0e\x00\x17\x00a\xb2\x05\x18\x19\x11\x129\xb0\x05\x10\xb0\x16\xd0\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x00EX\xb0\r/\x1b\xb1\r\x10>Y\xb2\x10\x04\x02\x11\x129\xb0\x10/\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb2\v\x00\x04\x11\x129\xb0\x04\x10\xb2\x16\x01\n+X!\xd8\x1b\xf4Y01\x01!\x11#\x11!2\x04\x15\x14\x06\a\x01\x15#\x01!2654&'!\x02\xbf\xfe\xaa\xc1\x01\xe2\xf6\x01\t\x93\x83\x01V\xce\xfdn\x01'\x8f\xa9\xa1\x98\xfe\xda\x02M\xfd\xb3\x05\xb0\xe0\u0588\xca2\xfd\x96\f\x02\xea\x94|\x87\x90\x01\x00\x00\x01\x00P\xff\xec\x04r\x05\xc4\x00&\x00a\xb2\x00'(\x11\x129\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb0\x00EX\xb0\x1a/\x1b\xb1\x1a\x10>Y\xb0\x06\x10\xb0\vа\x06\x10\xb2\x0e\x01\n+X!\xd8\x1b\xf4Y\xb2&\x1a\x06\x11\x129\xb0&\x10\xb2\x14\x01\n+X!\xd8\x1b\xf4Y\xb0\x1a\x10\xb0\x1fа\x1a\x10\xb2\"\x01\n+X!\xd8\x1b\xf4Y01\x01&&54$32\x16\x16\x15#4&#\"\x06\x15\x14\x16\x04\x16\x16\x15\x14\x04#\"$&53\x14\x163264&\x02V\xf7\xe1\x01\x13ܖ\xeb\x81\xc1\xa8\x99\x8e\x9f\x97\x01k\xcdc\xfe\xec\xe7\x96\xfe\xfc\x8d\xc1ã\x98\xa2\x96\x02\x89GϘ\xac\xe1t\xccy\x84\x97}oY{f{\xa4o\xb1\xd5s\xc8\u007f\x84\x99|\xd6u\x00\x00\x01\x001\x00\x00\x04\x97\x05\xb0\x00\a\x00.\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x06\x10\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\x04\xd001\x01!\x11#\x11!5!\x04\x97\xfe,\xbf\xfe-\x04f\x05\x12\xfa\xee\x05\x12\x9e\x00\x01\x00\x8c\xff\xec\x04\xaa\x05\xb0\x00\x12\x00<\xb2\x05\x13\x14\x11\x129\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x1c>Y\xb0\x00EX\xb0\t/\x1b\xb1\t\x1c>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x10>Y\xb2\x0e\x01\n+X!\xd8\x1b\xf4Y01\x01\x11\x06\x00\a\a\"\x00'\x113\x11\x14\x163265\x11\x04\xaa\x01\xfe\xff\xdc3\xef\xfe\xe4\x02\xbe\xae\xa1\xa3\xad\x05\xb0\xfc\"\xce\xfe\xfa\x10\x02\x01\x02\xe2\x03\xe0\xfc&\x9e\xaf\xae\x9e\x03\xdb\x00\x00\x01\x00\x1c\x00\x00\x04\xfd\x05\xb0\x00\x06\x008\xb2\x00\a\b\x11\x129\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb2\x00\x01\x03\x11\x12901%\x013\x01#\x013\x02\x8b\x01\xa0\xd2\xfd\xe4\xaa\xfd\xe5\xd1\xff\x04\xb1\xfaP\x05\xb0\x00\x00\x01\x00=\x00\x00\x06\xed\x05\xb0\x00\x12\x00Y\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x1c>Y\xb0\x00EX\xb0\x11/\x1b\xb1\x11\x1c>Y\xb0\x00EX\xb0\n/\x1b\xb1\n\x10>Y\xb0\x00EX\xb0\x0f/\x1b\xb1\x0f\x10>Y\xb2\x01\x03\n\x11\x129\xb2\x06\x03\n\x11\x129\xb2\r\x03\n\x11\x12901\x01\x177\x013\x01\x177\x133\x01#\x01'\a\x01#\x013\x01\xe3\x1c)\x01 \xa2\x01\x19(\x1f\xe2\xc1\xfe\x9f\xaf\xfe\xd4\x17\x17\xfeɯ\xfe\xa0\xc0\x01\xcb\xc0\xad\x03\xf8\xfc\b\xb0\xc4\x03\xe4\xfaP\x04%oo\xfb\xdb\x05\xb0\x00\x01\x009\x00\x00\x04\xce\x05\xb0\x00\v\x00k\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb0\x00EX\xb0\n/\x1b\xb1\n\x1c>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb0\x00EX\xb0\a/\x1b\xb1\a\x10>Y\xb2\x00\x01\x04\x11\x129@\t\x86\x00\x96\x00\xa6\x00\xb6\x00\x04]\xb2\x06\x01\x04\x11\x129@\t\x89\x06\x99\x06\xa9\x06\xb9\x06\x04]\xb2\x03\x00\x06\x11\x129\xb2\t\x06\x00\x11\x12901\x01\x013\x01\x01#\x01\x01#\x01\x013\x02\x84\x01]\xe2\xfe4\x01\xd7\xe4\xfe\x9a\xfe\x98\xe3\x01\xd8\xfe3\xe1\x03\x82\x02.\xfd.\xfd\"\x028\xfd\xc8\x02\xde\x02\xd2\x00\x00\x01\x00\x0f\x00\x00\x04\xbb\x05\xb0\x00\b\x001\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb0\x00EX\xb0\a/\x1b\xb1\a\x1c>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb2\x00\x01\x04\x11\x12901\x01\x013\x01\x11#\x11\x013\x02e\x01|\xda\xfe\n\xc0\xfe\n\xdc\x02\xd5\x02\xdb\xfco\xfd\xe1\x02\x1f\x03\x91\x00\x00\x01\x00V\x00\x00\x04z\x05\xb0\x00\t\x00D\x00\xb0\x00EX\xb0\a/\x1b\xb1\a\x1c>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb2\x04\x00\x02\x11\x129\xb0\a\x10\xb2\x05\x01\n+X!\xd8\x1b\xf4Y\xb2\t\x05\a\x11\x12901%!\x15!5\x01!5!\x15\x019\x03A\xfb\xdc\x03\x1e\xfc\xef\x03\xf7\x9d\x9d\x90\x04\x82\x9e\x8d\x00\x00\x01\x00\x92\xfe\xc8\x02\v\x06\x80\x00\a\x00\"\x00\xb0\x04/\xb0\a/\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\x04\x10\xb2\x03\x01\n+X!\xd8\x1b\xf4Y01\x01#\x113\x15!\x11!\x02\v\xbf\xbf\xfe\x87\x01y\x05\xe8\xf9x\x98\a\xb8\x00\x00\x01\x00(\xff\x83\x038\x05\xb0\x00\x03\x00\x13\x00\xb0\x02/\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x1c>Y01\x133\x01#(\xb0\x02`\xb0\x05\xb0\xf9\xd3\x00\x01\x00\t\xfe\xc8\x01\x83\x06\x80\x00\a\x00%\x00\xb0\x02/\xb0\x01/\xb0\x02\x10\xb2\x05\x01\n+X!\xd8\x1b\xf4Y\xb0\x01\x10\xb2\x06\x01\n+X!\xd8\x1b\xf4Y01\x13!\x11!53\x11#\t\x01z\xfe\x86\xc1\xc1\x06\x80\xf8H\x98\x06\x88\x00\x00\x01\x00@\x02\xd9\x03\x14\x05\xb0\x00\x06\x00'\xb2\x00\a\b\x11\x129\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb0\x00в\x01\a\x03\x11\x129\xb0\x01/\xb0\x05\xd001\x01\x03#\x013\x01#\x01\xaa\xbe\xac\x01+\u007f\x01*\xab\x04\xbb\xfe\x1e\x02\xd7\xfd)\x00\x01\x00\x04\xffi\x03\x98\x00\x00\x00\x03\x00\x1b\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb2\x00\x01\n+X!\xd8\x1b\xf4Y01\x05!5!\x03\x98\xfcl\x03\x94\x97\x97\x00\x00\x01\x009\x04\xd8\x01\xda\x05\xfe\x00\x03\x00#\x00\xb0\x01/\xb2\x0f\x01\x01]\xb0\x00\xd0\x19\xb0\x00/\x18\xb0\x01\x10\xb0\x02а\x02/\xb4\x0f\x02\x1f\x02\x02]01\x01#\x013\x01ڟ\xfe\xfe\xdf\x04\xd8\x01&\x00\x00\x02\x00m\xff\xec\x03\xea\x04N\x00\x1e\x00(\x00y\xb2\x17)*\x11\x129\xb0\x17\x10\xb0 \xd0\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x02\x17\x04\x11\x129\xb2\v\x17\x04\x11\x129\xb0\v/\xb0\x17\x10\xb2\x0f\x01\n+X!\xd8\x1b\xf4Y\xb2\x12\v\x17\x11\x129\xb0\x04\x10\xb2\x1f\x01\n+X!\xd8\x1b\xf4Y\xb0\v\x10\xb2#\x01\n+X!\xd8\x1b\xf4Y01!&'\x06#\"&54$3354&#\"\x06\x15#46632\x16\x17\x11\x14\x17\x15%2675# \x15\x14\x16\x03(\x10\n\x81\xb3\xa0\xcd\x01\x01\xe9\xb4tqc\x86\xbas\xc5v\xbb\xd4\x04&\xfe\vW\x9c#\x91\xfe\xact R\x86\xb5\x8b\xa9\xbbUasdGQ\x97X\xbb\xa4\xfe\x0e\x95X\x10\x8dZH\xde\xc7Wb\x00\x02\x00\x8c\xff\xec\x04 \x06\x00\x00\x0e\x00\x19\x00d\xb2\x12\x1a\x1b\x11\x129\xb0\x12\x10\xb0\x03\xd0\x00\xb0\b/\xb0\x00EX\xb0\f/\x1b\xb1\f\x18>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x10>Y\xb2\x05\b\x03\x11\x129\xb2\n\f\x03\x11\x129\xb0\f\x10\xb2\x12\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x17\x01\n+X!\xd8\x1b\xf4Y01\x01\x14\x02#\"'\a#\x113\x116 \x12\x11'4&#\"\a\x11\x16326\x04 \xe4\xc0\xcdp\t\xaa\xb9p\x01\x8aṒ\x89\xb7PU\xb4\x85\x94\x02\x11\xf8\xfeӑ}\x06\x00\xfdË\xfe\xd6\xfe\xfd\x05\xbdΪ\xfe,\xaa\xce\x00\x01\x00\\\xff\xec\x03\xec\x04N\x00\x1d\x00I\xb2\x10\x1e\x1f\x11\x129\x00\xb0\x00EX\xb0\x10/\x1b\xb1\x10\x18>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x10>Y\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\b\x10\xb0\x03а\x10\x10\xb0\x14а\x10\x10\xb2\x17\x01\n+X!\xd8\x1b\xf4Y01%2673\x0e\x02#\"\x00\x11546632\x16\x17#&&#\"\x06\x15\x15\x14\x16\x02>c\x94\b\xaf\x05v\xc5n\xdd\xfe\xfbtٔ\xb6\xf1\b\xaf\b\x8fi\x8d\x9b\x9a\x83xZ]\xa8d\x01'\x01\x00\x1f\x9e\xf6\x88ڮi\x87\xcb\xc0#\xbb\xca\x00\x00\x02\x00_\xff\xec\x03\xf0\x06\x00\x00\x0f\x00\x1a\x00d\xb2\x18\x1b\x1c\x11\x129\xb0\x18\x10\xb0\x03\xd0\x00\xb0\x06/\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb0\x00EX\xb0\f/\x1b\xb1\f\x10>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x10>Y\xb2\x05\x03\f\x11\x129\xb2\n\x03\f\x11\x129\xb0\f\x10\xb2\x13\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y01\x134\x1232\x17\x113\x11#'\x06#\"\x025\x17\x14\x16327\x11&#\"\x06_쿾o\xb9\xaa\toƼ\xed\xb9\x98\x86\xb0QS\xac\x88\x98\x02&\xf9\x01/\x82\x024\xfa\x00t\x88\x014\xf8\a\xb8О\x01\xf1\x99\xd2\x00\x00\x02\x00]\xff\xec\x03\xf3\x04N\x00\x15\x00\x1d\x00i\xb2\b\x1e\x1f\x11\x129\xb0\b\x10\xb0\x16\xd0\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x1a\b\x00\x11\x129\xb0\x1a/\xb4\xbf\x1a\xcf\x1a\x02]\xb2\f\x01\n+X!\xd8\x1b\xf4Y\xb0\x00\x10\xb2\x10\x01\n+X!\xd8\x1b\xf4Y\xb2\x13\b\x00\x11\x129\xb0\b\x10\xb2\x16\x01\n+X!\xd8\x1b\xf4Y01\x05\"\x005546632\x12\x11\x15!\x16\x163267\x17\x06\x01\"\x06\a!5&&\x02M\xdc\xfe\xec{݁\xd3\xea\xfd#\x04\xb3\x8ab\x883q\x88\xfe\xd9p\x98\x12\x02\x1e\b\x88\x14\x01!\xf2\"\xa1\xfd\x8f\xfe\xea\xfe\xfdM\xa0\xc5PBX\xd1\x03ʣ\x93\x0e\x8d\x9b\x00\x01\x00<\x00\x00\x02\xca\x06\x15\x00\x15\x00c\xb2\x0f\x16\x17\x11\x129\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x1e>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb0\x00EX\xb0\x11/\x1b\xb1\x11\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x03\x10\xb2\x01\x01\n+X!\xd8\x1b\xf4Y\xb0\b\x10\xb2\r\x01\n+X!\xd8\x1b\xf4Y\xb0\x01\x10\xb0\x13а\x14\xd0013\x11#5354632\x17\a&#\"\x06\x15\x153\x15#\x11竫\xba\xaa@?\n/5Zb\xe7\xe7\x03\xab\x8fo\xae\xbe\x11\x96\tibr\x8f\xfcU\x00\x02\x00`\xfeV\x03\xf2\x04N\x00\x19\x00$\x00\x83\xb2\"%&\x11\x129\xb0\"\x10\xb0\v\xd0\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x18>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x12>Y\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x10>Y\xb2\x05\x03\x17\x11\x129\xb2\x0f\x17\v\x11\x129\xb0\v\x10\xb2\x11\x01\n+X!\xd8\x1b\xf4Y\xb2\x15\x03\x17\x11\x129\xb0\x17\x10\xb2\x1d\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\"\x01\n+X!\xd8\x1b\xf4Y01\x134\x1232\x1773\x11\x14\x06#\"&'7\x1632655\x06#\"\x027\x14\x16327\x11&#\"\x06`\xea\xc1\xc6o\t\xa9\xf9\xd2u\xe0;`w\xac\x87\x97o\xc0\xbe뺖\x87\xafRU\xaa\x87\x98\x02&\xfd\x01+\x8cx\xfb\xe0\xd2\xf2dWo\x93\x98\x8a]\x80\x012\xf3\xb7џ\x01\xee\x9b\xd2\x00\x00\x01\x00\x8c\x00\x00\x03\xdf\x06\x00\x00\x11\x00I\xb2\n\x12\x13\x11\x129\x00\xb0\x10/\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x18>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x10>Y\xb0\x00EX\xb0\x0e/\x1b\xb1\x0e\x10>Y\xb2\x00\x02\x05\x11\x129\xb0\x02\x10\xb2\n\x01\n+X!\xd8\x1b\xf4Y01\x0163 \x13\x11#\x11&&#\"\x06\a\x11#\x113\x01E{\xc5\x01W\x03\xb9\x01ioZ\x88&\xb9\xb9\x03\xb7\x97\xfe}\xfd5\x02\xccup`N\xfc\xfd\x06\x00\x00\x02\x00\x8d\x00\x00\x01h\x05\xc4\x00\x03\x00\f\x00>\xb2\x06\r\x0e\x11\x129\xb0\x06\x10\xb0\x01\xd0\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x02\x10\xb0\nа\n/\xb2\x06\x05\n+X!\xd8\x1b\xf4Y01!#\x113\x03462\x16\x14\x06\"&\x01U\xb9\xb9\xc87l88l7\x04:\x01\x1f->>Z<<\x00\x02\xff\xbf\xfeK\x01Y\x05\xc4\x00\f\x00\x16\x00I\xb2\x10\x17\x18\x11\x129\xb0\x10\x10\xb0\x00\xd0\x00\xb0\x00EX\xb0\f/\x1b\xb1\f\x18>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x12>Y\xb2\b\x01\n+X!\xd8\x1b\xf4Y\xb0\f\x10\xb0\x15а\x15/\xb2\x10\x05\n+X!\xd8\x1b\xf4Y01\x01\x11\x10!\"'5\x163265\x11\x034632\x16\x14\x06\"&\x01K\xfe\xe5=4 4>A\x1375688l6\x04:\xfbI\xfe\xc8\x12\x94\bCS\x04\xbb\x01\x1f,?>Z<<\x00\x00\x01\x00\x8d\x00\x00\x04\f\x06\x00\x00\f\x00u\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1e>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x10>Y\xb2\x00\b\x02\x11\x129@\x15:\x00J\x00Z\x00j\x00z\x00\x8a\x00\x9a\x00\xaa\x00\xba\x00\xca\x00\n]\xb2\x06\b\x02\x11\x129@\x156\x06F\x06V\x06f\x06v\x06\x86\x06\x96\x06\xa6\x06\xb6\x06\xc6\x06\n]01\x01\a\x11#\x113\x117\x013\x01\x01#\x01\xbat\xb9\xb9c\x01Q\xe1\xfe[\x01\xd6\xd9\x01\xf5y\xfe\x84\x06\x00\xfc_w\x01d\xfe<\xfd\x8a\x00\x01\x00\x9c\x00\x00\x01U\x06\x00\x00\x03\x00\x1d\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1e>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y01!#\x113\x01U\xb9\xb9\x06\x00\x00\x00\x01\x00\x8b\x00\x00\x06x\x04N\x00\x1d\x00w\xb2\x04\x1e\x1f\x11\x129\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x18>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x10>Y\xb0\x00EX\xb0\x14/\x1b\xb1\x14\x10>Y\xb0\x00EX\xb0\x1b/\x1b\xb1\x1b\x10>Y\xb2\x01\b\v\x11\x129\xb2\x05\b\v\x11\x129\xb0\b\x10\xb2\x10\x01\n+X!\xd8\x1b\xf4Y\xb0\x18\xd001\x01\x17632\x17663 \x13\x11#\x114&#\"\x06\a\x11#\x114#\"\a\x11#\x11\x01:\x05w\xca\xe3R6\xadv\x01d\x06\xb9j}g\x88\v\xba\xe7\xb6C\xb9\x04:x\x8c\xaeN`\xfe\x87\xfd+\x02\xcats{h\xfd2\x02\xc5\xec\x9b\xfc\xea\x04:\x00\x01\x00\x8c\x00\x00\x03\xdf\x04N\x00\x11\x00S\xb2\v\x12\x13\x11\x129\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x18>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x10>Y\xb0\x00EX\xb0\x0f/\x1b\xb1\x0f\x10>Y\xb2\x01\x03\x06\x11\x129\xb0\x03\x10\xb2\v\x01\n+X!\xd8\x1b\xf4Y01\x01\x1763 \x13\x11#\x11&&#\"\x06\a\x11#\x11\x01;\x06|\xc8\x01W\x03\xb9\x01ioZ\x88&\xb9\x04:\x88\x9c\xfe}\xfd5\x02\xccup`N\xfc\xfd\x04:\x00\x00\x02\x00[\xff\xec\x044\x04N\x00\x0f\x00\x1b\x00C\xb2\f\x1c\x1d\x11\x129\xb0\f\x10\xb0\x13\xd0\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb0\x00EX\xb0\f/\x1b\xb1\f\x10>Y\xb2\x13\x01\n+X!\xd8\x1b\xf4Y\xb0\x04\x10\xb2\x19\x01\n+X!\xd8\x1b\xf4Y01\x1346632\x00\x15\x15\x14\x06\x06#\"\x005\x17\x14\x1632654&#\"\x06[}ߏ\xdd\x01\x11y\xe1\x92\xdc\xfeﺧ\x8c\x8d\xa6\xa9\x8c\x89\xa8\x02'\x9f\xfe\x8a\xfe\xce\xfe\r\x9e\xfb\x8c\x012\xfc\t\xb4\xda\xddDz\xdd\xda\x00\x02\x00\x8c\xfe`\x04\x1e\x04N\x00\x0f\x00\x1a\x00n\xb2\x13\x1b\x1c\x11\x129\xb0\x13\x10\xb0\f\xd0\x00\xb0\x00EX\xb0\f/\x1b\xb1\f\x18>Y\xb0\x00EX\xb0\t/\x1b\xb1\t\x18>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x12>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb2\x05\f\x03\x11\x129\xb2\n\f\x03\x11\x129\xb0\f\x10\xb2\x13\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y01\x01\x14\x02#\"'\x11#\x113\x17632\x12\x11'4&#\"\a\x11\x16326\x04\x1e\xe2\xc1\xc5q\xb9\xa9\tq\xc9\xc3㹜\x88\xa8TS\xab\x85\x9d\x02\x11\xf7\xfe\xd2}\xfd\xf7\x05\xdax\x8c\xfe\xda\xfe\xfa\x04\xb7ԕ\xfd\xfb\x94\xd3\x00\x00\x02\x00_\xfe`\x03\xef\x04N\x00\x0f\x00\x1a\x00k\xb2\x18\x1b\x1c\x11\x129\xb0\x18\x10\xb0\x03\xd0\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x18>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x12>Y\xb0\x00EX\xb0\f/\x1b\xb1\f\x10>Y\xb2\x05\x03\f\x11\x129\xb2\n\x03\f\x11\x129\xb2\x13\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y01\x134\x1232\x1773\x11#\x11\x06#\"\x025\x17\x14\x16327\x11&#\"\x06_\xea\xc5\xc0o\b\xaa\xb9p\xba\xc4鹝\x85\xa5WX\xa2\x86\x9e\x02&\xff\x01)\x81m\xfa&\x02\x04x\x011\xfc\b\xbaԒ\x02\x12\x8f\xd5\x00\x01\x00\x8c\x00\x00\x02\x97\x04N\x00\r\x00F\xb2\x04\x0e\x0f\x11\x129\x00\xb0\x00EX\xb0\v/\x1b\xb1\v\x18>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x10>Y\xb0\v\x10\xb2\x02\x01\n+X!\xd8\x1b\xf4Y\xb2\t\v\x05\x11\x12901\x01&#\"\a\x11#\x113\x17632\x17\x02\x97*1\xb6A\xb9\xb4\x03[\xa76\x1c\x03\x94\a\x9b\xfd\x00\x04:}\x91\x0e\x00\x01\x00_\xff\xec\x03\xbb\x04N\x00&\x00a\xb2\t'(\x11\x129\x00\xb0\x00EX\xb0\t/\x1b\xb1\t\x18>Y\xb0\x00EX\xb0\x1c/\x1b\xb1\x1c\x10>Y\xb2\x03\x1c\t\x11\x129\xb0\t\x10\xb0\rа\t\x10\xb2\x10\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x15\x01\n+X!\xd8\x1b\xf4Y\xb0\x1c\x10\xb0!а\x1c\x10\xb2$\x01\n+X!\xd8\x1b\xf4Y01\x014&$&&54632\x16\x15#4&#\"\x06\x15\x14\x16\x04\x16\x16\x15\x14\x06#\"&&53\x16\x16326\x03\x02q\xfe\xe7\xa5O\u1bf8庁berj\x01\x15\xacS蹂\xc8q\xb9\x05\x8bri\u007f\x01\x1fKSVyW\x91\xaf\\\xa5`]mU\x00\x01\x00\t\xff\xec\x02V\x05@\x00\x15\x00_\xb2\x0e\x16\x17\x11\x129\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x18>Y\xb0\x00EX\xb0\x13/\x1b\xb1\x13\x18>Y\xb0\x00EX\xb0\r/\x1b\xb1\r\x10>Y\xb0\x01\x10\xb0\x00а\x00/\xb0\x01\x10\xb2\x03\x01\n+X!\xd8\x1b\xf4Y\xb0\r\x10\xb2\b\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb0\x11а\x12\xd001\x01\x113\x15#\x11\x14\x16327\x15\x06#\"&5\x11#53\x11\x01\x87\xca\xca6A 8IE|~\xc5\xc5\x05@\xfe\xfa\x8f\xfdaAA\f\x96\x14\x96\x8a\x02\x9f\x8f\x01\x06\x00\x01\x00\x88\xff\xec\x03\xdc\x04:\x00\x10\x00S\xb2\n\x11\x12\x11\x129\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x18>Y\xb0\x00EX\xb0\r/\x1b\xb1\r\x18>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x00EX\xb0\x10/\x1b\xb1\x10\x10>Y\xb2\x00\r\x02\x11\x129\xb0\x02\x10\xb2\n\x01\n+X!\xd8\x1b\xf4Y01%\x06#\"&'\x113\x11\x14327\x113\x11#\x03(lѭ\xb5\x01\xb9\xc8\xd4F\xb9\xb0k\u007f\xc9\xc5\x02\xc0\xfdE\xf6\x9e\x03\x13\xfb\xc6\x00\x00\x01\x00!\x00\x00\x03\xba\x04:\x00\x06\x008\xb2\x00\a\b\x11\x129\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x18>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x18>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb2\x00\x05\x03\x11\x12901%\x013\x01#\x013\x01\xf1\x01\f\xbd\xfe|\x8d\xfex\xbd\xfb\x03?\xfb\xc6\x04:\x00\x00\x01\x00+\x00\x00\x05\xd3\x04:\x00\f\x00`\xb2\x05\r\x0e\x11\x129\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x18>Y\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x18>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x10>Y\xb2\x00\v\x03\x11\x129\xb2\x05\v\x03\x11\x129\xb2\n\v\x03\x11\x12901%\x133\x01#\x01\x01#\x013\x13\x133\x04Jй\xfeŖ\xfe\xf9\xff\x00\x96\xfeƸ\xd5\xfc\x95\xff\x03;\xfb\xc6\x034\xfc\xcc\x04:\xfc\xd6\x03*\x00\x01\x00)\x00\x00\x03\xca\x04:\x00\v\x00S\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x18>Y\xb0\x00EX\xb0\n/\x1b\xb1\n\x18>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb0\x00EX\xb0\a/\x1b\xb1\a\x10>Y\xb2\x00\n\x04\x11\x129\xb2\x06\n\x04\x11\x129\xb2\x03\x00\x06\x11\x129\xb2\t\x06\x00\x11\x12901\x01\x133\x01\x01#\x03\x03#\x01\x013\x01\xf7\xf0\xd8\xfe\x9e\x01m\xd6\xfa\xfa\xd7\x01m\xfe\x9e\xd6\x02\xaf\x01\x8b\xfd\xe9\xfd\xdd\x01\x95\xfek\x02#\x02\x17\x00\x01\x00\x16\xfeK\x03\xb0\x04:\x00\x0f\x00I\xb2\x00\x10\x11\x11\x129\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x18>Y\xb0\x00EX\xb0\x0e/\x1b\xb1\x0e\x18>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x12>Y\xb2\x00\x0e\x05\x11\x129\xb2\t\x01\n+X!\xd8\x1b\xf4Y\xb0\x00\x10\xb0\r\xd001\x01\x133\x01\x02#''5\x172677\x013\x01\xee\xfc\xc6\xfeMe\xdc#E2^i\")\xfe~\xca\x01\x0f\x03+\xfb\x1f\xfe\xf2\x03\r\x96\x04Len\x04.\x00\x01\x00X\x00\x00\x03\xb3\x04:\x00\t\x00D\x00\xb0\x00EX\xb0\a/\x1b\xb1\a\x18>Y\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb2\x04\x00\x02\x11\x129\xb0\a\x10\xb2\x05\x01\n+X!\xd8\x1b\xf4Y\xb2\t\x05\a\x11\x12901%!\x15!5\x01!5!\x15\x01:\x02y\xfc\xa5\x02U\xfd\xb4\x034\x97\x97\x88\x03\x19\x99\x83\x00\x00\x01\x00@\xfe\x92\x02\x9e\x06=\x00\x18\x001\xb2\x13\x19\x1a\x11\x129\x00\xb0\r/\xb0\x00/\xb2\a\r\x00\x11\x129\xb0\a/\xb2\x1f\a\x01]\xb2\x06\x03\n+X!\xd8\x1b\xf4Y\xb2\x13\x06\a\x11\x12901\x01&&554#5255667\x17\x06\x11\x15\x14\a\x16\x15\x15\x12\x17\x02x\xb1\xb3\xd4\xd4\x02\xaf\xb3&ѧ\xa7\x03\xce\xfe\x922\xe5\xbc\xc7\xf3\x91\xf2з\xe13sC\xfe\xe6\xca\xe3YZ\xe5\xce\xfe\xedB\x00\x00\x01\x00\xaf\xfe\xf2\x01D\x05\xb0\x00\x03\x00\x13\x00\xb0\x00/\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y01\x01#\x113\x01D\x95\x95\xfe\xf2\x06\xbe\x00\x00\x01\x00\x13\xfe\x92\x02r\x06=\x00\x18\x001\xb2\x05\x19\x1a\x11\x129\x00\xb0\v/\xb0\x18/\xb2\x11\v\x18\x11\x129\xb0\x11/\xb2\x1f\x11\x01]\xb2\x12\x03\n+X!\xd8\x1b\xf4Y\xb2\x05\x12\x11\x11\x12901\x176\x13547&55\x10'7\x16\x16\x17\x15\x143\x15\"\x15\x15\x14\x06\a\x13\xcb\a\xb5\xb5\xd1&\xb1\xb2\x01\xd4Ե\xaf\xfbA\x01\n\xdc\xe7TR\xe9\xcb\x01\x1aCs2\xe1\xb9\xd2\xef\x91\xf3ʼ\xe22\x00\x00\x01\x00\x83\x01\x92\x04\xef\x03\"\x00\x17\x00B\xb2\x11\x18\x19\x11\x129\x00\xb0\x00EX\xb0\x0f/\x1b\xb1\x0f\x16>Y\xb0\x00а\x0f\x10\xb0\x14а\x14/\xb2\x03\x01\n+X!\xd8\x1b\xf4Y\xb0\x0f\x10\xb2\b\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb0\v\xd001\x01\x14\x06#\".\x02#\"\x06\x15\a4632\x16\x16\x17\x17265\x04ﻉH\x80\xa9J*NT\xa1\xb8\x8bL\x8c\xb0@\x1dL_\x03\t\x9e\xd95\x94$k^\x02\xa0\xce@\xa1\n\x02t_\x00\x02\x00\x8b\xfe\x98\x01f\x04M\x00\x03\x00\f\x002\xb2\x06\r\x0e\x11\x129\xb0\x06\x10\xb0\x00\xd0\x00\xb0\x02/\xb0\x00EX\xb0\v/\x1b\xb1\v\x18>Y\xb2\x06\x05\n+X!\xd8\x1b\xf4Y\xb2\x01\x02\x06\x11\x12901\x133\x13#\x13\x14\x06\"&462\x16\xaa\xa8\r\xc2\xc97l88l7\x02\xac\xfb\xec\x05L->>Z<<\x00\x01\x00i\xff\v\x03\xf9\x05&\x00!\x00R\xb2\x00\"#\x11\x129\x00\xb0\x00EX\xb0\x14/\x1b\xb1\x14\x18>Y\xb0\x00EX\xb0\n/\x1b\xb1\n\x10>Y\xb0\aв\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\n\x10\xb0\x03а\x14\x10\xb0\x11а\x14\x10\xb0\x18а\x14\x10\xb2\x1b\x01\n+X!\xd8\x1b\xf4Y01%2673\x06\x06\a\x15#5&\x02554\x12753\x15\x16\x16\x17#&&#\"\x06\x15\x15\x14\x16\x02Jd\x94\b\xaf\x06Ɛ\xb9\xb3\xc8ʱ\xb9\x96\xc0\x06\xaf\b\x8fi\x8d\x9b\x9b\x83yY~\xc9\x1a\xe9\xea\"\x01\x1c\xdc#\xd4\x01\x1d!\xe2\xdf\x17Ԗi\x87\xcb\xc0#\xbb\xca\x00\x01\x00[\x00\x00\x04h\x05\xc4\x00!\x00|\xb2\x1c\"#\x11\x129\x00\xb0\x00EX\xb0\x14/\x1b\xb1\x14\x1c>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x10>Y\xb2\x1f\x14\x05\x11\x129\xb0\x1f/\xb2_\x1f\x01r\xb2\x8f\x1f\x01q\xb2\xbf\x1f\x01]\xb2\x00\x01\n+X!\xd8\x1b\xf4Y\xb0\x05\x10\xb2\x03\x01\n+X!\xd8\x1b\xf4Y\xb0\aа\bа\x00\x10\xb0\rа\x1f\x10\xb0\x0fа\x14\x10\xb0\x18а\x14\x10\xb2\x1b\x01\n+X!\xd8\x1b\xf4Y01\x01\x17\x14\a!\a!536675'#53\x034632\x16\x15#4&#\"\x06\x15\x13!\x15\x01\xc1\b>\x02\xdd\x01\xfb\xf8M(2\x02\b\xa5\xa0\t\xf5Ⱦ\u07bf\u007foi\x82\t\x01?\x02nܚ[\x9d\x9d\t\x83`\bݝ\x01\x04\xc7\xeeԱk|\x9a}\xfe\xfc\x9d\x00\x00\x02\x00i\xff\xe5\x05[\x04\xf1\x00\x1b\x00*\x00?\xb2\x02+,\x11\x129\xb0\x02\x10\xb0'\xd0\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x10>Y\xb0\x10а\x10/\xb0\x02\x10\xb2\x1f\x01\n+X!\xd8\x1b\xf4Y\xb0\x10\x10\xb2'\x01\n+X!\xd8\x1b\xf4Y01%\x06#\"'\a'7&547'7\x17632\x177\x17\a\x16\x15\x14\a\x17\a\x01\x14\x16\x1626654&&#\"\x06\x06\x04O\x9f\xd1ϟ\x86\x82\x8bhp\x93\x82\x93\x9e\xc3ğ\x95\x84\x97nf\x8f\x84\xfc`s\xc4\xe2\xc4qq\xc5pq\xc4sp\x84\x82\x88\x87\x8d\x9c\xcaΣ\x97\x88\x96xy\x98\x89\x9a\xa3\xcbğ\x90\x88\x02{{\xd4z{\xd3{z\xd3yx\xd4\x00\x00\x01\x00\x1f\x00\x00\x04\xad\x05\xb0\x00\x16\x00k\x00\xb0\x00EX\xb0\x16/\x1b\xb1\x16\x1c>Y\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb0\x00EX\xb0\f/\x1b\xb1\f\x10>Y\xb2\x0f\x13\x03+\xb2\x00\f\x16\x11\x129\xb4\x0f\x13\x1f\x13\x02]\xb0\x13\x10\xb0\x03а\x13\x10\xb2\x12\x02\n+X!\xd8\x1b\xf4Y\xb0\x06а\x0f\x10\xb0\aа\x0f\x10\xb2\x0e\x02\n+X!\xd8\x1b\xf4Y\xb0\n\xd001\x01\x013\x01!\x15!\x15!\x15!\x11#\x11!5!5!5!\x013\x02f\x01l\xdb\xfe^\x018\xfe\x80\x01\x80\xfe\x80\xc1\xfe\x86\x01z\xfe\x86\x019\xfe^\xdc\x03\x0e\x02\xa2\xfd0}\xa5|\xfe\xbe\x01B|\xa5}\x02\xd0\x00\x00\x02\x00\x93\xfe\xf2\x01M\x05\xb0\x00\x03\x00\a\x00\x18\x00\xb0\x00/\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb2\x05\x01\x03+01\x13\x113\x11\x11#\x113\x93\xba\xba\xba\xfe\xf2\x03\x17\xfc\xe9\x03\xc8\x02\xf6\x00\x02\x00Z\xfe\x11\x04y\x05\xc4\x004\x00D\x00\x80\xb2#EF\x11\x129\xb0#\x10\xb05\xd0\x00\xb0\b/\xb0\x00EX\xb0#/\x1b\xb1#\x1c>Y\xb2\x16\b#\x11\x129\xb0\x16\x10\xb2?\x01\n+X!\xd8\x1b\xf4Y\xb2\x02\x16?\x11\x129\xb0\b\x10\xb0\x0eа\b\x10\xb2\x11\x01\n+X!\xd8\x1b\xf4Y\xb20#\b\x11\x129\xb00\x10\xb27\x01\n+X!\xd8\x1b\xf4Y\xb2\x1d70\x11\x129\xb0#\x10\xb0'а#\x10\xb2*\x01\n+X!\xd8\x1b\xf4Y01\x01\x14\a\x16\x16\x15\x14\x04#\"&'&57\x14\x1632654&'.\x02547&&54$32\x04\x15#4&#\"\x06\x15\x14\x16\x16\x04\x1e\x02%&'\x06\x06\x15\x14\x16\x16\x04\x176654&\x04y\xbaEH\xfe\xfc\xe4p\xc9F\x8b\xba\xb4\x9c\x88\xa6\x8eѶ\xc0]\xb6BG\x01\v\xde\xe8\x01\x04\xb9\xa8\x8b\x8e\xa18\x87\x01\x1f\xa9q:\xfd\xe1ZKPK6\x85\x01\x1c,NT\x8b\x01\xaf\xbdU1\x88d\xa8\xc789q\xcd\x02\x82\x97u`Yi>0o\x9bo\xbaX1\x88d\xa6\xc8\xe2\xcd}\x9bsbEPAPHa\x81\xab\x18\x1b\x13eEFPBR\x11\x14eEXm\x00\x00\x02\x00f\x04\xf0\x02\xef\x05\xc5\x00\b\x00\x11\x00\x1d\x00\xb0\a/\xb2\x02\x05\n+X!\xd8\x1b\xf4Y\xb0\vа\a\x10\xb0\x10а\x10/01\x13462\x16\x14\x06\"&%462\x16\x14\x06\"&f7l88l7\x01\xae7l88l7\x05[-==Z<<+->>Z<<\x00\x00\x03\x00[\xff\xeb\x05\xe6\x05\xc4\x00\x1b\x00*\x009\x00\x95\xb2':;\x11\x129\xb0'\x10\xb0\x03а'\x10\xb06\xd0\x00\xb0\x00EX\xb0./\x1b\xb1.\x1c>Y\xb0\x00EX\xb06/\x1b\xb16\x10>Y\xb2\x036.\x11\x129\xb0\x03/\xb4\x0f\x03\x1f\x03\x02]\xb2\n.6\x11\x129\xb0\n/\xb4\x00\n\x10\n\x02]\xb2\x0e\n\x03\x11\x129\xb2\x11\x02\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x02\n+X!\xd8\x1b\xf4Y\xb2\x1b\x03\n\x11\x129\xb06\x10\xb2 \x04\n+X!\xd8\x1b\xf4Y\xb0.\x10\xb2'\x04\n+X!\xd8\x1b\xf4Y01\x01\x14\x06#\"&554632\x16\x15#4&#\"\x06\x15\x15\x14\x163265%\x14\x12\x04 $\x1254\x02$#\"\x04\x02\a4\x12$ \x04\x12\x15\x14\x02\x04#\"$\x02\x04_\xad\x9e\x9d\xbd\xbf\x9b\xa0\xac\x92_[^ll^\\]\xfd\x01\xa0\x01\x13\x01@\x01\x12\xa0\x9e\xfe\xed\xa1\xa0\xfe\xec\x9fs\xbb\x01K\x01\x80\x01J\xbb\xb4\xfe\xb5\xc6\xc5\xfe\xb5\xb6\x02U\x99\xa1Ӷn\xb0Ӥ\x95cU\x8a{qx\x8aTe\x84\xac\xfeۦ\xa6\x01%\xac\xaa\x01\"\xa7\xa5\xfeܪ\xca\x01Z\xc7\xc7\xfe\xa6\xca\xc5\xfe\xa8\xd1\xcf\x01X\x00\x00\x02\x00\x93\x02\xb3\x03\x0f\x05\xc4\x00\x1b\x00%\x00l\xb2\x0e&'\x11\x129\xb0\x0e\x10\xb0\x1d\xd0\x00\xb0\x00EX\xb0\x15/\x1b\xb1\x15\x1c>Y\xb2\x04&\x15\x11\x129\xb0\x04/\xb0\x00в\x02\x04\x15\x11\x129\xb2\v\x04\x15\x11\x129\xb0\v/\xb0\x15\x10\xb2\x0e\x03\n+X!\xd8\x1b\xf4Y\xb2\x11\v\x15\x11\x129\xb0\x04\x10\xb2\x1c\x03\n+X!\xd8\x1b\xf4Y\xb0\v\x10\xb2 \x04\n+X!\xd8\x1b\xf4Y01\x01&'\x06#\"&5463354#\"\x06\x15'4632\x16\x15\x11\x14\x17%2675#\x06\x06\x15\x14\x02j\f\x06L\x80w\x82\xa7\xacl|EO\xa1\xac\x89\x85\x9a\x1a\xfe\xa4+X\x1cpSY\x02\xc1\"&V|gox4\x8763\fg\x82\x8f\x86\xfe\xc4aQ{(\x1b\x8e\x01?3^\xff\xff\x00f\x00\x97\x03d\x03\xb3\x00&\x00\x9a\xfa\xfe\x00\a\x00\x9a\x01D\xff\xfe\x00\x01\x00\u007f\x01w\x03\xbe\x03 \x00\x05\x00\x1a\x00\xb0\x04/\xb0\x01а\x01/\xb0\x04\x10\xb2\x02\x01\n+X!\xd8\x1b\xf4Y01\x01#\x11!5!\x03\xbe\xba\xfd{\x03?\x01w\x01\b\xa1\x00\x04\x00Z\xff\xeb\x05\xe5\x05\xc4\x00\x0e\x00\x1e\x004\x00=\x00\xa9\xb26>?\x11\x129\xb06\x10\xb0\vа6\x10\xb0\x13а6\x10\xb0#\xd0\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x10>Y\xb2\x13\x04\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x1b\x04\n+X!\xd8\x1b\xf4Y\xb2 \v\x03\x11\x129\xb0 /\xb2\"\x03\v\x11\x129\xb0\"/\xb4\x00\"\x10\"\x02]\xb25 \"\x11\x129\xb05/\xb2\xbf5\x01]\xb4\x005\x105\x02]\xb2\x1f\x02\n+X!\xd8\x1b\xf4Y\xb2(\x1f5\x11\x129\xb0 \x10\xb0/а//\xb0\"\x10\xb2=\x02\n+X!\xd8\x1b\xf4Y01\x134\x12$ \x04\x12\x15\x14\x02\x04#\"$\x027\x14\x12\x0432$\x1254\x02$#\"\x04\x02\x05\x11#\x11!2\x16\x15\x14\a\x16\x17\x15\x14\x17\x15#&4'&''36654&##Z\xbb\x01K\x01\x80\x01J\xbb\xb4\xfe\xb5\xc6\xc5\xfe\xb5\xb6s\xa0\x01\x13\xa0\xa1\x01\x14\x9d\x9d\xfe졠\xfe\xec\x9f\x01\xc0\x8d\x01\x14\x99\xa9\x80z\x01\x11\x91\x0e\x03\x10s\xb0\x9cHXNd\x8a\x02\xd9\xca\x01Z\xc7\xc7\xfe\xa6\xca\xc5\xfe\xa8\xd1\xcf\x01XǬ\xfeۦ\xa9\x01\"\xac\xab\x01!\xa7\xa5\xfe\xdc\xf5\xfe\xae\x03Q\x83}{A2\x9a=V&\x10$\xb9\x11`\x04\x80\x02B6I=\x00\x00\x01\x00x\x05!\x03B\x05\xb0\x00\x03\x00\x11\x00\xb0\x01/\xb2\x02\x03\n+X!\xd8\x1b\xf4Y01\x01!5!\x03B\xfd6\x02\xca\x05!\x8f\x00\x02\x00\x82\x03\xc0\x02|\x05\xc4\x00\v\x00\x16\x00/\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb0\fа\f/\xb2\t\x02\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x12\x02\n+X!\xd8\x1b\xf4Y01\x134632\x16\x15\x14\x06#\"&\x172654&#\"\x06\x14\x16\x82\x95jh\x93\x93hi\x96\xff6JJ67KK\x04\xc0h\x9c\x9bij\x96\x96\x16G9:KOlJ\x00\x02\x00a\x00\x00\x03\xf5\x04\xf3\x00\v\x00\x0f\x00F\x00\xb0\t/\xb0\x00EX\xb0\r/\x1b\xb1\r\x10>Y\xb0\t\x10\xb0\x00а\t\x10\xb2\x06\x01\n+X!\xd8\x1b\xf4Y\xb0\x03а\r\x10\xb2\x0e\x01\n+X!\xd8\x1b\xf4Y\xb2\x05\x0e\x06\x11\x129\xb4\v\x05\x1b\x05\x02]01\x01!\x15!\x11#\x11!5!\x113\x01!5!\x02\x89\x01l\xfe\x94\xa7\xfe\u007f\x01\x81\xa7\x01A\xfc\xbd\x03C\x03V\x97\xfeb\x01\x9e\x97\x01\x9d\xfb\r\x98\x00\x00\x01\x00B\x02\x9b\x02\xab\x05\xbb\x00\x16\x00T\xb2\b\x17\x18\x11\x129\x00\xb0\x00EX\xb0\x0e/\x1b\xb1\x0e\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x14>Y\xb2\x16\x02\n+X!\xd8\x1b\xf4Y\xb0\x02в\x03\x0e\x16\x11\x129\xb0\x0e\x10\xb2\b\x02\n+X!\xd8\x1b\xf4Y\xb0\x0e\x10\xb0\vв\x14\x16\x0e\x11\x12901\x01!5\x01654&#\"\x06\x15#46 \x16\x15\x14\x0f\x02!\x02\xab\xfd\xa9\x01,m@\x02\x8f\x02\x9a\x05\xba\x00&\x00\x89\xb2 '(\x11\x129\x00\xb0\x00EX\xb0\x0e/\x1b\xb1\x0e\x1c>Y\xb0\x00EX\xb0\x19/\x1b\xb1\x19\x14>Y\xb2\x00\x19\x0e\x11\x129\xb0\x00/\xb6o\x00\u007f\x00\x8f\x00\x03]\xb2?\x00\x01q\xb6\x0f\x00\x1f\x00/\x00\x03]\xb2_\x00\x01r\xb0\x0e\x10\xb2\a\x02\n+X!\xd8\x1b\xf4Y\xb2\n\x0e\x19\x11\x129\xb0\x00\x10\xb2&\x04\n+X!\xd8\x1b\xf4Y\xb2\x14&\x00\x11\x129\xb2\x1d\x19\x0e\x11\x129\xb0\x19\x10\xb2 \x02\n+X!\xd8\x1b\xf4Y01\x0132654&#\"\x06\x15#4632\x16\x15\x14\x06\a\x16\x15\x14\x06#\"&53\x14\x1632654'#\x01\tTJH?F9K\x9d\xa3|\x89\x9cFB\x95\xaa\x88\x84\xa6\x9eOCFI\x9cX\x04e=0-:3)b{yh7[\x19)\x8fj}~k-<<3q\x02\x00\x00\x01\x00{\x04\xd8\x02\x1c\x05\xfe\x00\x03\x00#\x00\xb0\x02/\xb2\x0f\x02\x01]\xb0\x00а\x00/\xb4\x0f\x00\x1f\x00\x02]\xb0\x02\x10\xb0\x03\xd0\x19\xb0\x03/\x1801\x013\x01#\x01<\xe0\xfe\xf4\x95\x05\xfe\xfe\xda\x00\x00\x01\x00\x9a\xfe`\x03\xee\x04:\x00\x12\x00P\xb2\r\x13\x14\x11\x129\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x18>Y\xb0\x00EX\xb0\a/\x1b\xb1\a\x18>Y\xb0\x00EX\xb0\x10/\x1b\xb1\x10\x12>Y\xb0\x00EX\xb0\r/\x1b\xb1\r\x10>Y\xb2\x04\x01\n+X!\xd8\x1b\xf4Y\xb2\v\a\r\x11\x12901\x01\x11\x16\x16327\x113\x11#'\x06#\"'\x11#\x11\x01S\x01gt\xc7>\xba\xa7\t]\xaa\x93Q\xb9\x04:\xfd\x87\xa3\x9c\x98\x03 \xfb\xc6s\x87I\xfe+\x05\xda\x00\x01\x00C\x00\x00\x03@\x05\xb0\x00\n\x00+\xb2\x02\v\f\x11\x129\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x01\x00\b\x11\x12901!\x11#\"$54$3!\x11\x02\x86T\xe6\xfe\xf7\x01\n\xe6\x01\r\x02\b\xfe\xd6\xd5\xff\xfaP\x00\x00\x01\x00\x93\x02k\x01y\x03I\x00\t\x00\x16\xb2\x03\n\v\x11\x129\x00\xb0\x02/\xb1\b\n+X\xd8\x1b\xdcY01\x13462\x16\x15\x14\x06\"&\x939r;;r9\x02\xd90@@0/??\x00\x01\x00t\xfeM\x01\xaa\x00\x00\x00\x0e\x00A\xb2\x05\x0f\x10\x11\x129\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x12>Y\xb4\x13\x06#\x06\x02]\xb2\x01\x06\x00\x11\x129\xb1\a\n+X\xd8\x1b\xdcY\xb0\x01\x10\xb0\r\xd001!\a\x16\x15\x14\x06#'2654&'7\x01\x1d\f\x99\xa0\x8f\aOW@b 4\x1b\x92aqk4/,*\t\x86\x00\x01\x00z\x02\xa2\x01\xef\x05\xb7\x00\x06\x00@\xb2\x01\a\b\x11\x129\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x14>Y\xb2\x04\x00\x05\x11\x129\xb0\x04/\xb2\x03\x02\n+X!\xd8\x1b\xf4Y\xb2\x02\x03\x05\x11\x12901\x01#\x11\a5%3\x01\xef\x9d\xd8\x01c\x12\x02\xa2\x02Y9\x80u\x00\x00\x02\x00z\x02\xb2\x03'\x05\xc4\x00\f\x00\x1a\x00@\xb2\x03\x1b\x1c\x11\x129\xb0\x03\x10\xb0\x10\xd0\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb2\n\x1b\x03\x11\x129\xb0\n/\xb2\x10\x03\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x17\x03\n+X!\xd8\x1b\xf4Y01\x134632\x16\x15\x15\x14\x06 &5\x17\x14\x16326554&#\"\x06\az\xbc\x9a\x9b\xbc\xbb\xfe̾\xa3aTS_aSQ`\x02\x04c\x9e\xc3\xc1\xa6J\x9f\xc2¥\x06drseNcrna\x00\xff\xff\x00f\x00\x98\x03x\x03\xb5\x00&\x00\x9b\r\x00\x00\a\x00\x9b\x01j\x00\x00\xff\xff\x00U\x00\x00\x05\x91\x05\xad\x00'\x00\xa2\xff\xdb\x02\x98\x00'\x00\x9c\x01\x18\x00\b\x01\a\x00\xa5\x02\xd6\x00\x00\x00\x10\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y01\xff\xff\x00P\x00\x00\x05\xc9\x05\xad\x00'\x00\x9c\x00\xec\x00\b\x00'\x00\xa2\xff\xd6\x02\x98\x01\a\x00\xa3\x03\x1e\x00\x00\x00\x10\x00\xb0\x00EX\xb0\t/\x1b\xb1\t\x1c>Y01\xff\xff\x00o\x00\x00\x05\xed\x05\xbb\x00'\x00\x9c\x01\x97\x00\b\x00'\x00\xa5\x032\x00\x00\x01\a\x00\xa4\x001\x02\x9b\x00\x10\x00\xb0\x00EX\xb0!/\x1b\xb1!\x1c>Y01\x00\x02\x00D\xfe\u007f\x03x\x04M\x00\x18\x00\"\x00W\xb2\t#$\x11\x129\xb0\t\x10\xb0\x1c\xd0\x00\xb0\x10/\xb0\x00EX\xb0!/\x1b\xb1!\x18>Y\xb2\x00\x10!\x11\x129\xb2\x03\x10\x00\x11\x129\xb0\x10\x10\xb2\t\x01\n+X!\xd8\x1b\xf4Y\xb0\x10\x10\xb0\fв\x15\x00\x10\x11\x129\xb0!\x10\xb2\x1b\x05\n+X!\xd8\x1b\xf4Y01\x01\x0e\x03\a\a\x14\x1632653\x06\x06#\"&547765\x13\x14\x06\"&5462\x16\x02L\x01)`\xb8\v\x02tmd}\xb9\x02\xe1\xb7\xc4֠mB\xc17l88l7\x02\xa8j\u007fv\xc1c%msq[\xa1\xccɳ\xad\xafqN\x92\x01=->>-,<<\x00\x02\xff\xf2\x00\x00\aW\x05\xb0\x00\x0f\x00\x12\x00w\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb2\x11\x06\x00\x11\x129\xb0\x11/\xb2\x02\x01\n+X!\xd8\x1b\xf4Y\xb0\x06\x10\xb2\b\x01\n+X!\xd8\x1b\xf4Y\xb2\v\x00\x06\x11\x129\xb0\v/\xb2\f\x01\n+X!\xd8\x1b\xf4Y\xb0\x00\x10\xb2\x0e\x01\n+X!\xd8\x1b\xf4Y\xb2\x12\x06\x00\x11\x12901!!\x03!\x03#\x01!\x15!\x13!\x15!\x13!\x01!\x03\aW\xfc\x8d\x0f\xfd\xcc\xcd\xe2\x03p\x03\xb7\xfdM\x14\x02N\xfd\xb8\x16\x02\xc1\xfa\xaf\x01\xc8\x1f\x01a\xfe\x9f\x05\xb0\x98\xfe)\x97\xfd\xed\x01x\x02\xdd\x00\x01\x00Y\x00\xce\x03\xdd\x04c\x00\v\x008\x00\xb0\x03/\xb2\t\f\x03\x11\x129\xb0\t/\xb2\n\t\x03\x11\x129\xb2\x04\x03\t\x11\x129\xb2\x01\n\x04\x11\x129\xb0\x03\x10\xb0\x05в\a\x04\n\x11\x129\xb0\t\x10\xb0\v\xd001\x13\x01\x017\x01\x01\x17\x01\x01\a\x01\x01Y\x01J\xfe\xb8w\x01I\x01Iw\xfe\xb8\x01Jw\xfe\xb5\xfe\xb5\x01I\x01P\x01O{\xfe\xb1\x01O{\xfe\xb1\xfe\xb0{\x01Q\xfe\xaf\x00\x00\x03\x00v\xff\xa3\x05\x1d\x05\xec\x00\x17\x00 \x00)\x00f\xb2\x04*+\x11\x129\xb0\x04\x10\xb0\x1dа\x04\x10\xb0&\xd0\x00\xb0\x00EX\xb0\x10/\x1b\xb1\x10\x1c>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb2\x1a\x10\x04\x11\x129\xb2#\x10\x04\x11\x129\xb0#\x10\xb0\x1bа\x10\x10\xb2\x1d\x01\n+X!\xd8\x1b\xf4Y\xb0\x1a\x10\xb0$а\x04\x10\xb2&\x01\n+X!\xd8\x1b\xf4Y01\x01\x14\x02\x04#\"'\a#7&\x1154\x12$32\x1773\a\x16\x13\x05\x14\x17\x01&#\"\x02\a\x054'\x01\x1632\x127\x05\t\x90\xfe\xf8\xb0\xab\x83a\x8e\x90\xbe\x92\x01\v\xac֔g\x8d\x9f\x89\x02\xfc,b\x024f\xa6\xb6\xd1\x03\x03\x158\xfd\xdb[y\xba\xcc\x03\x02\xa9\xd6\xfe\xc1\xa8R\x9b\xe7\xc0\x01hS\xd2\x01B\xab}\xa5\xff\xbb\xfe\xdac\xf4\x8d\x03\x88o\xfe\xeb\xf6\r\xb6\x83\xfc\x8f@\x01\x0f\xfd\x00\x02\x00\xa6\x00\x00\x04]\x05\xb0\x00\r\x00\x16\x00W\xb2\t\x17\x18\x11\x129\xb0\t\x10\xb0\x10\xd0\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x1c>Y\xb0\x00EX\xb0\v/\x1b\xb1\v\x10>Y\xb2\x01\x00\v\x11\x129\xb0\x01/\xb2\x10\x00\v\x11\x129\xb0\x10/\xb2\t\x01\n+X!\xd8\x1b\xf4Y\xb0\x01\x10\xb2\x0e\x01\n+X!\xd8\x1b\xf4Y01\x01\x11!2\x16\x16\x15\x14\x04#!\x11#\x11\x13\x11!2654&'\x01`\x01\x17\x93\xdcw\xfe\xf8\xe3\xfe\ueeba\x01\x15\x8e\xa0\xa0\x88\x05\xb0\xfe\xdbi\xc2~\xc2\xe7\xfe\xc7\x05\xb0\xfeC\xfdޗx{\x97\x01\x00\x01\x00\x8b\xff\xec\x04j\x06\x12\x00*\x00i\xb2!+,\x11\x129\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1e>Y\xb0\x00EX\xb0\x13/\x1b\xb1\x13\x10>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\n\x13\x05\x11\x129\xb2\x0e\x05\x13\x11\x129\xb0\x13\x10\xb2\x1a\x01\n+X!\xd8\x1b\xf4Y\xb2 \x13\x05\x11\x129\xb2#\x05\x13\x11\x129\xb0\x05\x10\xb2(\x01\n+X!\xd8\x1b\xf4Y01!#\x114632\x16\x15\x14\x06\x15\x14\x1e\x02\x15\x14\x06#\"&'7\x16\x1632654.\x0254654&#\"\x11\x01D\xb9Ϻ\xb4ŀK\xbcV˶Q\xb5&+1\x875kqJ\xbdW\x8bhX\xda\x04W\xd0볟}\xcbE3_\x90\x88L\x9f\xb2,\x1c\x9b ,^R4`\x93\x8aQY\xcfT^k\xfe\xdb\x00\x03\x00N\xff\xec\x06|\x04N\x00*\x005\x00=\x00Ʋ\x02>?\x11\x129\xb0\x02\x10\xb0.а\x02\x10\xb09\xd0\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb0\x00EX\xb0\x1d/\x1b\xb1\x1d\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x10>Y\xb2\x02\x1d\x00\x11\x129\xb2\f\x05\x17\x11\x129\xb0\f/\xb4\xbf\f\xcf\f\x02]\xb0\x17\x10\xb2\x10\x01\n+X!\xd8\x1b\xf4Y\xb2\x13\f\x17\x11\x129\xb2\x1a\x1d\x00\x11\x129\xb2:\x1d\x00\x11\x129\xb0:/\xb4\xbf:\xcf:\x02]\xb2!\x01\n+X!\xd8\x1b\xf4Y\xb0\x00\x10\xb2%\x01\n+X!\xd8\x1b\xf4Y\xb2(\x1d\x00\x11\x129\xb0+а\f\x10\xb2/\x01\n+X!\xd8\x1b\xf4Y\xb0\x10\x10\xb06\xd001\x05 '\x06\x06#\"&5463354&#\"\x06\x15'4632\x16\x176632\x12\x15\x15!\x16\x163277\x17\x06%2675#\x06\x06\x15\x14\x16\x01\"\x06\a!54&\x04\xee\xfe\xfb\x88A⍧\xbc\xe3\xdd\xdfnhi\x8c\xb8\xf2\xbbs\xb02?\xaei\xd2\xe8\xfd(\a\xae\x95\x94y/@\x9e\xfc\tH\x9e2\xe4u\x8cj\x03Ps\x95\x11\x02\x1a\x86\x14\xb4V^\xad\x97\x9d\xaeUk{nQ\x13\x8f\xb5SSOW\xfe\xff\xe9s\xb0\xbfL\x1f\x88y\x96J6\xed\x02nSM]\x034\xab\x8b\x1f\x84\x93\x00\x00\x02\x00~\xff\xec\x04-\x06,\x00\x1d\x00+\x00T\xb2\a,-\x11\x129\xb0\a\x10\xb0(\xd0\x00\xb0\x00EX\xb0\x19/\x1b\xb1\x19\x1e>Y\xb0\x00EX\xb0\a/\x1b\xb1\a\x10>Y\xb2\x0f\x19\a\x11\x129\xb0\x0f/\xb2\x11\x19\a\x11\x129\xb2\"\x01\n+X!\xd8\x1b\xf4Y\xb0\a\x10\xb2(\x01\n+X!\xd8\x1b\xf4Y01\x01\x12\x11\x15\x14\x06\x06#\"&&546632\x17&'\a'7&'7\x16\x177\x17\x03'&&#\"\x06\x15\x14\x163265\x034\xf9u؆\x87\xdcypρ\xa3y0\x8d\xdaI\xc0\x84\xb79ﯽIh\x02!\x8b\\\x91\xa2\xa7\x80}\x99\x05\x15\xfe\xf8\xfeg]\x9e\xfd\x90\x81\xe0\x86\x93\xe9\x82rÍ\x94c\x83[1\x9f6\x8b\x81d\xfc\xf38=I\xbf\xa7\x8c\xc4\xe2\xb8\x00\x00\x03\x00G\x00\xac\x04-\x04\xba\x00\x03\x00\r\x00\x17\x00N\xb2\a\x18\x19\x11\x129\xb0\a\x10\xb0\x00а\a\x10\xb0\x11\xd0\x00\xb0\x02/\xb2\x01\x01\n+X!\xd8\x1b\xf4Y\xb0\x02\x10\xb1\f\n+X\xd8\x1b\xdcY\xb1\x06\n+X\xd8\x1b\xdcY\xb0\x01\x10\xb1\x10\n+X\xd8\x1b\xdcY\xb1\x16\n+X\xd8\x1b\xdcY01\x01!5!\x01462\x16\x15\x14\x06\"&\x11462\x16\x15\x14\x06\"&\x04-\xfc\x1a\x03\xe6\xfd\xa09r;;r99r;;r9\x02X\xb8\x01:0@@0/>>\xfc\xfe0@@0.??\x00\x00\x03\x00[\xffz\x044\x04\xb8\x00\x15\x00\x1d\x00&\x00c\xb2\x04'(\x11\x129\xb0\x04\x10\xb0\x1bа\x04\x10\xb0#\xd0\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb0\x00EX\xb0\x0f/\x1b\xb1\x0f\x10>Y\xb2#\x01\n+X!\xd8\x1b\xf4Y\xb2!#\x04\x11\x129\xb0!\x10\xb0\x18а\x04\x10\xb2\x1b\x01\n+X!\xd8\x1b\xf4Y\xb2\x19\x1b\x0f\x11\x129\xb0\x19\x10\xb0 \xd001\x1346632\x1773\a\x16\x11\x14\x06\x06#\"'\a#7&\x13\x14\x17\x01&#\"\x06\x054'\x01\x163265[{\xe1\x8fn^I|f\xc3|\xe0\x90hVJ|d\u0379a\x01W>H\x8a\xa8\x02fW\xfe\xac7B\x8b\xa7\x02'\x9f\xfd\x8b*\x94͚\xfe\xc0\x9e\xfe\x89#\x95˕\x017\xc2o\x02\xb6 ڵ\xb6o\xfdP\x19۹\x00\x02\x00\x95\xfe`\x04'\x06\x00\x00\x0f\x00\x1a\x00d\xb2\x18\x1b\x1c\x11\x129\xb0\x18\x10\xb0\f\xd0\x00\xb0\b/\xb0\x00EX\xb0\f/\x1b\xb1\f\x18>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x12>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb2\x05\f\x03\x11\x129\xb2\n\f\x03\x11\x129\xb0\f\x10\xb2\x13\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y01\x01\x14\x02#\"'\x11#\x113\x11632\x12\x11'4&#\"\a\x11\x16326\x04'\xe2\xc1\xc5q\xb9\xb9q\xc2\xc3㹜\x88\xa8TS\xab\x85\x9d\x02\x11\xf7\xfe\xd2}\xfd\xf7\a\xa0\xfdʄ\xfe\xda\xfe\xfa\x04\xb7ԕ\xfd\xfb\x94\xd3\x00\x00\x01\x00\x9b\x00\x00\x01U\x04:\x00\x03\x00\x1d\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y01!#\x113\x01U\xba\xba\x04:\x00\x00\x02\x00h\xff\xeb\a\t\x05\xc4\x00\x17\x00#\x00\x91\xb2\x01$%\x11\x129\xb0\x01\x10\xb0\x1a\xd0\x00\xb0\x00EX\xb0\f/\x1b\xb1\f\x1c>Y\xb0\x00EX\xb0\x0e/\x1b\xb1\x0e\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x10>Y\xb0\x0e\x10\xb2\x10\x01\n+X!\xd8\x1b\xf4Y\xb2\x13\x00\x0e\x11\x129\xb0\x13/\xb2\x14\x01\n+X!\xd8\x1b\xf4Y\xb0\x00\x10\xb2\x16\x01\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x18\x01\n+X!\xd8\x1b\xf4Y\xb0\f\x10\xb2\x1d\x01\n+X!\xd8\x1b\xf4Y01!!\x06#\"&\x02'\x114\x12632\x17!\x15!\x11!\x15!\x11!\x0527\x11&#\"\x06\a\x11\x14\x16\a\t\xfc\xb0\xb2r\xa2\xfe\x8c\x01\x8b\xfe\xa2|\xaa\x03F\xfd-\x02w\xfd\x89\x02\xdd\xfb\x8cqfml\xad\xc2\x02\xc3\x15\x96\x01\x0f\xab\x015\xac\x01\x11\x97\x14\x9e\xfe,\x9d\xfd\xfc\x1b\x0e\x04\x8e\x0f\xe5\xcf\xfe\xc7\xd3\xeb\x00\x00\x03\x00a\xff\xec\a\x00\x04N\x00 \x00,\x004\x00\x96\xb2\x0656\x11\x129\xb0\x06\x10\xb0&а\x06\x10\xb00\xd0\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb0\x00EX\xb0\n/\x1b\xb1\n\x18>Y\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x10>Y\xb0\x00EX\xb0\x1d/\x1b\xb1\x1d\x10>Y\xb2\a\n\x17\x11\x129\xb21\n\x17\x11\x129\xb01/\xb2\x0e\x01\n+X!\xd8\x1b\xf4Y\xb0\x17\x10\xb2\x12\x01\n+X!\xd8\x1b\xf4Y\xb2\x14\n\x17\x11\x129\xb2\x1a\n\x17\x11\x129\xb0$а\x04\x10\xb2*\x01\n+X!\xd8\x1b\xf4Y\xb0-\xd001\x1346632\x16\x176632\x16\x15\x15!\x16\x16327\x17\x06#\"&'\x06\x06#\"\x005\x17\x14\x1632654&#\"\x06%\"\x06\a!54&ayێ\x89\xc9=A\xc4p\xcf\xea\xfd2\a\xa4\x86\xbcxJ\x89\xf5\x87\xcd?>dž\xdc\xfe\xf8\xb9\xa0\x8b\x89\xa0\xa1\x8a\x87\xa2\x04-c\x96\x16\x02\x0e\x89\x02'\xa0\xfe\x89udfs\xfe\xebt\xaa\xc5l~\x84pdcq\x010\xfe\t\xb7\xd8\xd7ζ\xd9\xd6֣\x8a\x1a}\x96\x00\x00\x01\x00\xa9\x04\xe4\x03\x06\x06\x00\x00\b\x004\x00\xb0\x04/\xb0\aа\a/\xb4\x0f\a\x1f\a\x02]\xb2\x05\x04\a\x11\x129\x19\xb0\x05/\x18\xb0\x01\xd0\x19\xb0\x01/\x18\xb0\x04\x10\xb0\x02в\x03\x04\a\x11\x12901\x01\x15#'\a#5\x133\x03\x06\x99\x96\x95\x99\xf6p\x04\xee\n\xaa\xaa\f\x01\x10\x00\x00\x02\x00y\x04\xb4\x02'\x06P\x00\t\x00\x14\x00*\xb2\x03\x15\x16\x11\x129\xb0\x03\x10\xb0\r\xd0\x00\xb0\x03/\xb0\aа\a/\xb2?\a\x01]\xb0\x03\x10\xb0\rа\a\x10\xb0\x12\xd001\x01\x14\x06#\"&462\x16\x05\x14\x163264&#\"\x06\x02'|[\\{{\xb8{\xfe\xb5C10DC12B\x05\x80Wuv\xaczzV/DBbEF\x00\x00\x01\x00{\x04\xd9\x03>\x05\xe8\x00\x17\x00>\x00\xb0\x03/\xb0\bа\b/\xb4\x0f\b\x1f\b\x02]\xb0\x03\x10\xb0\vа\v/\xb0\b\x10\xb2\x0f\x03\n+X!\xd8\x1b\xf4Y\xb0\x03\x10\xb2\x14\x03\n+X!\xd8\x1b\xf4Y\xb0\x0f\x10\xb0\x17\xd001\x01\x14\x06#\".\x02#\"\x06\x15'4632\x1e\x023265\x03>{\\)\r?1\ak\x8c\x14:\x12D-\xff\xff\x00\xa2\x02\x8b\x04\x8d\x03\"\x00F\x00\x9f\xd9\x00L\xcd@\x00\xff\xff\x00\x90\x02\x8b\x05\xc9\x03\"\x00F\x00\x9f\x84\x00ff@\x00\x00\x01\x00`\x041\x01x\x06\x13\x00\b\x00!\xb2\b\t\n\x11\x129\x00\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x1e>Y\xb2\x05\t\x00\x11\x129\xb0\x05/01\x01\x17\x06\a\x15#546\x01\x0ej]\x03\xb8a\x06\x13H\u007f\x93\x88tf\xc8\x00\x01\x000\x04\x16\x01G\x06\x00\x00\b\x00!\xb2\b\t\n\x11\x129\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1e>Y\xb2\x00\t\x04\x11\x129\xb0\x00/01\x13'6753\x15\x06\x06\x99i]\x03\xb7\x01a\x04\x16H\x82\x90\x90\x82d\xc7\x00\x01\x00$\xfe\xe5\x01;\x00\xb5\x00\b\x00\x1e\xb2\b\t\n\x11\x129\x00\xb0\t/\xb2\x04\x05\n+X!\xd8\x1b\xf4Y\xb0\x00а\x00/01\x13'6753\x15\x14\x06\x8di[\x03\xb9c\xfe\xe5I\u007f\x92vde\xca\xff\xff\x00h\x041\x02\xbb\x06\x13\x00&\x00\x93\b\x00\x00\a\x00\x93\x01C\x00\x00\xff\xff\x00<\x04\x16\x02\x86\x06\x00\x00&\x00\x94\f\x00\x00\a\x00\x94\x01?\x00\x00\x00\x02\x00$\xfe\xd3\x02d\x00\xf6\x00\b\x00\x11\x000\xb2\n\x12\x13\x11\x129\xb0\n\x10\xb0\x05\xd0\x00\xb0\x12/\xb2\x04\x05\n+X!\xd8\x1b\xf4Y\xb0\x00а\x00/\xb0\tа\t/\xb0\x04\x10\xb0\r\xd001\x13'6753\x15\x14\x06\x17'6753\x15\x14\x06\x8di[\x03\xb9c\xddi[\x03\xbaa\xfe\xd3H\x89\x99\xb9\xa4l\xd3@H\x89\x99\xb9\xa4k\xd1\x00\x00\x01\x00\x8a\x02\x17\x02\"\x03\xcb\x00\r\x00\x16\xb2\n\x0e\x0f\x11\x129\x00\xb0\x03/\xb1\n\n+X\xd8\x1b\xdcY01\x134632\x16\x15\x15\x14\x06#\"&5\x8ao\\[rn^]o\x03\x04Wpm]%WnoX\x00\x01\x00l\x00\x99\x02 \x03\xb5\x00\x06\x00\x10\x00\xb0\x05/\xb2\x02\a\x05\x11\x129\xb0\x02/01\x01\x01#\x015\x013\x01\x1e\x01\x02\x8d\xfe\xd9\x01'\x8d\x02&\xfes\x01\x84\x13\x01\x85\x00\x01\x00Y\x00\x98\x02\x0e\x03\xb5\x00\x06\x00\x10\x00\xb0\x00/\xb2\x03\a\x00\x11\x129\xb0\x03/01\x13\x01\x15\x01#\x01\x01\xe7\x01'\xfeَ\x01\x02\xfe\xfe\x03\xb5\xfe{\x13\xfe{\x01\x8e\x01\x8f\x00\x01\x00;\x00n\x03j\x05\"\x00\x03\x00\t\x00\xb0\x00/\xb0\x02/017'\x01\x17\xa3h\x02\xc7hnB\x04rB\x00\xff\xff\x006\x02\x90\x02\xbb\x05\xa5\x03\a\x00\xa5\x00\x00\x02\x90\x00\x13\x00\xb0\x00EX\xb0\t/\x1b\xb1\t\x1c>Y\xb0\r\xd001\x00\x00\x01\x00_\xff\xec\x04\x1c\x05\xc4\x00#\x00\x87\xb2\x15$%\x11\x129\x00\xb0\x00EX\xb0\x16/\x1b\xb1\x16\x1c>Y\xb0\x00EX\xb0\t/\x1b\xb1\t\x10>Y\xb2#\t\x16\x11\x129\xb0#/\xb2\x00\x02\n+X!\xd8\x1b\xf4Y\xb0\t\x10\xb2\x04\x01\n+X!\xd8\x1b\xf4Y\xb0\x00\x10\xb0\fа#\x10\xb0\x0fа#\x10\xb0\x1fа\x1f/\xb6\x0f\x1f\x1f\x1f/\x1f\x03]\xb2 \x02\n+X!\xd8\x1b\xf4Y\xb0\x10а\x1f\x10\xb0\x13а\x16\x10\xb2\x1b\x01\n+X!\xd8\x1b\xf4Y01\x01!\x16\x16327\x17\x06#\"\x00\x03#535#53\x12\x0032\x17\a&#\"\x06\a!\x15!\x15!\x03Q\xfe\x80\x04\xb4\xa5tf\x14xx\xf8\xfe\xe3\x06\xb2\xb2\xb2\xb2\n\x01\x1d\xf3j\x87\x14mn\xa4\xb1\x06\x01\u007f\xfe\x80\x01\x80\x02\x1d\xc3\xd2\"\xa0\x1e\x01%\x01\f|\x89}\x01\x06\x01\x1f\x1f\xa2#˼}\x89\x00\x01\x00\xa8\x02\x8b\x03\xeb\x03\"\x00\x03\x00\x1b\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x16>Y\xb2\x01\x01\n+X!\xd8\x1b\xf4Y01\x01!5!\x03\xeb\xfc\xbd\x03C\x02\x8b\x97\x00\x02\x00\x1f\x00\x00\x03\xcd\x06\x15\x00\x15\x00\x19\x00\x83\xb2\b\x1a\x1b\x11\x129\xb0\b\x10\xb0\x17\xd0\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x1e>Y\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb0\x00EX\xb0\x11/\x1b\xb1\x11\x18>Y\xb0\x00EX\xb0\x18/\x1b\xb1\x18\x18>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb0\x00EX\xb0\x16/\x1b\xb1\x16\x10>Y\xb0\x03\x10\xb2\x01\x01\n+X!\xd8\x1b\xf4Y\xb0\b\x10\xb2\r\x01\n+X!\xd8\x1b\xf4Y\xb0\x01\x10\xb0\x13а\x14\xd0013\x11#5354632\x17\a&#\"\x06\x15\x153\x15#\x11!#\x113ʫ\xabϽp\xab\x1f}qwi\xdd\xdd\x02I\xba\xba\x03\xab\x8f\\\xb5\xca=\x9c2kk^\x8f\xfcU\x04:\x00\x01\x00<\x00\x00\x03\xe9\x06\x15\x00\x16\x00\\\x00\xb0\x00EX\xb0\x12/\x1b\xb1\x12\x1e>Y\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x18>Y\xb0\x00EX\xb0\t/\x1b\xb1\t\x10>Y\xb0\x00EX\xb0\x16/\x1b\xb1\x16\x10>Y\xb0\x12\x10\xb2\x02\x01\n+X!\xd8\x1b\xf4Y\xb0\x06\x10\xb2\a\x01\n+X!\xd8\x1b\xf4Y\xb0\vа\x06\x10\xb0\x0e\xd001\x01&#\"\x15\x153\x15#\x11#\x11#5356632\x05\x11#\x030|L\xc8\xe7繫\xab\x01\xc0\xb1e\x01+\xb9\x05c\x14\xd2k\x8f\xfcU\x03\xab\x8fv\xad\xb8=\xfa(\x00\x00\x01\x00z\x00\x00\x01\xef\x03\x15\x00\x06\x005\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x16>Y\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x10>Y\xb2\x04\x05\x01\x11\x129\xb0\x04/\xb2\x03\x02\n+X!\xd8\x1b\xf4Y\xb0\x02\xd001!#\x11\a5%3\x01\xef\x9d\xd8\x01c\x12\x02Y9\x80u\x00\x01\x00B\x00\x00\x02\xab\x03 \x00\x16\x00T\xb2\b\x17\x18\x11\x129\x00\xb0\x00EX\xb0\x0e/\x1b\xb1\x0e\x16>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x15\x02\n+X!\xd8\x1b\xf4Y\xb0\x02в\x14\x15\x0e\x11\x129\xb2\x03\x0e\x14\x11\x129\xb0\x0e\x10\xb2\b\x02\n+X!\xd8\x1b\xf4Y\xb0\x0e\x10\xb0\v\xd001!!5\x01654&#\"\x06\x15#46 \x16\x15\x14\x0f\x02!\x02\xab\xfd\xa9\x01,m@\xff\xf5\x02\x9a\x03 \x00&\x00q\x00\xb0\x00EX\xb0\x0e/\x1b\xb1\x0e\x16>Y\xb0\x00EX\xb0\x19/\x1b\xb1\x19\x10>Y\xb2\x00\x19\x0e\x11\x129|\xb0\x00/\x18\xb6\x80\x00\x90\x00\xa0\x00\x03]\xb0\x0e\x10\xb2\a\x02\n+X!\xd8\x1b\xf4Y\xb2\n\x00\a\x11\x129\xb0\x00\x10\xb2&\x02\n+X!\xd8\x1b\xf4Y\xb2\x14&\x00\x11\x129\xb0\x19\x10\xb2 \x02\n+X!\xd8\x1b\xf4Y\xb2\x1d& \x11\x12901\x0132654&#\"\x06\x15#4632\x16\x15\x14\x06\a\x16\x15\x14\x06#\"&53\x14\x1632654'#\x01\tTJH?F9K\x9d\xa3|\x89\x9cFB\x95\xaa\x88\x84\xa6\x9eOCFI\x9cX\x01\xcb=0-:3)b{yh7[\x19)\x8fj}~k-<<3q\x02\x00\x00\x02\x006\x00\x00\x02\xbb\x03\x15\x00\n\x00\x0e\x00I\x00\xb0\x00EX\xb0\t/\x1b\xb1\t\x16>Y\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x10>Y\xb2\x01\t\x04\x11\x129\xb0\x01/\xb2\x02\x02\n+X!\xd8\x1b\xf4Y\xb0\x06а\x01\x10\xb0\vв\b\v\x06\x11\x129\xb2\r\t\x04\x11\x12901\x013\x15#\x15#5!'\x013\x013\x11\a\x02Pkk\x9d\xfe\x89\x06\x01y\xa1\xfe\x84\xdf\x11\x01+\x82\xa9\xa9f\x02\x06\xfe\x16\x01!\x1c\xff\xff\x00%\x02\x1f\x02\r\x02\xb6\x02\x06\x00\x11\x00\x00\x00\x02\x00%\x00\x00\x04\xe4\x05\xb0\x00\x0f\x00\x1d\x00f\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb0\x00EX\xb0\x00/\x1b\xb1\x00\x10>Y\xb2\x04\x00\x05\x11\x129\xb0\x04/\xb2\xcf\x04\x01]\xb2/\x04\x01]\xb2\x9f\x04\x01q\xb2\x01\x01\n+X!\xd8\x1b\xf4Y\xb0\x11а\x00\x10\xb2\x12\x01\n+X!\xd8\x1b\xf4Y\xb0\x05\x10\xb2\x1b\x01\n+X!\xd8\x1b\xf4Y\xb0\x04\x10\xb0\x1c\xd0013\x11#53\x11!2\x04\x12\x17\x15\x14\x02\x04\a\x13!\x1132\x12754\x02'#\x11!Ǣ\xa2\x01\x9b\xbe\x01$\x9f\x01\x9f\xfe\xd9\xc4G\xfe\xe6\xc9\xde\xf7\x01\xe9\xd6\xe0\x01\x1a\x02\x9a\x97\x02\u007f\xa8\xfe\xca\xc9]\xce\xfeʦ\x02\x02\x9a\xfe\x03\x01\x12\xf9]\xf8\x01\x13\x02\xfe\x1f\x00\xff\xff\x00\x1c\x00\x00\x05\x1d\a4\x02&\x00%\x00\x00\x01\a\x00D\x010\x016\x00\x14\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb1\f\b\xf401\xff\xff\x00\x1c\x00\x00\x05\x1d\a4\x02&\x00%\x00\x00\x01\a\x00u\x01\xbf\x016\x00\x14\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb1\r\b\xf401\xff\xff\x00\x1c\x00\x00\x05\x1d\a6\x02&\x00%\x00\x00\x01\a\x00\x8e\x00\xc9\x016\x00\x14\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb1\x0f\x06\xf401\xff\xff\x00\x1c\x00\x00\x05\x1d\a\"\x02&\x00%\x00\x00\x01\a\x00\x90\x00\xc5\x01:\x00\x14\x00\xb0\x00EX\xb0\x05/\x1b\xb1\x05\x1c>Y\xb1\x0e\x04\xf401\xff\xff\x00\x1c\x00\x00\x05\x1d\x06\xfb\x02&\x00%\x00\x00\x01\a\x00j\x00\xf9\x016\x00\x17\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb1\x11\x04\xf4\xb0\x1b\xd001\x00\xff\xff\x00\x1c\x00\x00\x05\x1d\a\x91\x02&\x00%\x00\x00\x01\a\x00\x8f\x01P\x01A\x00\x17\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x1c>Y\xb1\x0e\x06\xf4\xb0\x18\xd001\x00\xff\xff\x00w\xfeD\x04\xd8\x05\xc4\x02&\x00'\x00\x00\x00\a\x00y\x01\xd2\xff\xf7\xff\xff\x00\xa9\x00\x00\x04F\a@\x02&\x00)\x00\x00\x01\a\x00D\x00\xfb\x01B\x00\x14\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb1\r\b\xf401\xff\xff\x00\xa9\x00\x00\x04F\a@\x02&\x00)\x00\x00\x01\a\x00u\x01\x8a\x01B\x00\x14\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb1\x0e\b\xf401\xff\xff\x00\xa9\x00\x00\x04F\aB\x02&\x00)\x00\x00\x01\a\x00\x8e\x00\x94\x01B\x00\x14\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb1\x10\x06\xf401\xff\xff\x00\xa9\x00\x00\x04F\a\a\x02&\x00)\x00\x00\x01\a\x00j\x00\xc4\x01B\x00\x17\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb1\x12\x04\xf4\xb0\x1b\xd001\x00\xff\xff\xff\xe0\x00\x00\x01\x81\a@\x02&\x00-\x00\x00\x01\a\x00D\xff\xa7\x01B\x00\x14\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y\xb1\x05\b\xf401\xff\xff\x00\xb0\x00\x00\x02Q\a@\x02&\x00-\x00\x00\x01\a\x00u\x005\x01B\x00\x14\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x1c>Y\xb1\x06\b\xf401\xff\xff\xff\xe9\x00\x00\x02F\aB\x02&\x00-\x00\x00\x01\a\x00\x8e\xff@\x01B\x00\x14\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y\xb1\b\x06\xf401\xff\xff\xff\xd6\x00\x00\x02_\a\a\x02&\x00-\x00\x00\x01\a\x00j\xffp\x01B\x00\x17\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x1c>Y\xb1\n\x04\xf4\xb0\x14\xd001\x00\xff\xff\x00\xa9\x00\x00\x05\b\a\"\x02&\x002\x00\x00\x01\a\x00\x90\x00\xfb\x01:\x00\x14\x00\xb0\x00EX\xb0\x06/\x1b\xb1\x06\x1c>Y\xb1\r\x04\xf401\xff\xff\x00v\xff\xec\x05\t\a6\x02&\x003\x00\x00\x01\a\x00D\x01R\x018\x00\x14\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x1c>Y\xb1!\b\xf401\xff\xff\x00v\xff\xec\x05\t\a6\x02&\x003\x00\x00\x01\a\x00u\x01\xe1\x018\x00\x14\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x1c>Y\xb1\"\b\xf401\xff\xff\x00v\xff\xec\x05\t\a8\x02&\x003\x00\x00\x01\a\x00\x8e\x00\xeb\x018\x00\x14\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x1c>Y\xb1\"\x06\xf401\xff\xff\x00v\xff\xec\x05\t\a$\x02&\x003\x00\x00\x01\a\x00\x90\x00\xe7\x01<\x00\x14\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x1c>Y\xb1#\x04\xf401\xff\xff\x00v\xff\xec\x05\t\x06\xfd\x02&\x003\x00\x00\x01\a\x00j\x01\x1b\x018\x00\x17\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x1c>Y\xb1'\x04\xf4\xb00\xd001\x00\xff\xff\x00\x8c\xff\xec\x04\xaa\a4\x02&\x009\x00\x00\x01\a\x00D\x01+\x016\x00\x14\x00\xb0\x00EX\xb0\n/\x1b\xb1\n\x1c>Y\xb1\x14\b\xf401\xff\xff\x00\x8c\xff\xec\x04\xaa\a4\x02&\x009\x00\x00\x01\a\x00u\x01\xba\x016\x00\x14\x00\xb0\x00EX\xb0\x12/\x1b\xb1\x12\x1c>Y\xb1\x15\b\xf401\xff\xff\x00\x8c\xff\xec\x04\xaa\a6\x02&\x009\x00\x00\x01\a\x00\x8e\x00\xc4\x016\x00\x14\x00\xb0\x00EX\xb0\n/\x1b\xb1\n\x1c>Y\xb1\x17\x06\xf401\xff\xff\x00\x8c\xff\xec\x04\xaa\x06\xfb\x02&\x009\x00\x00\x01\a\x00j\x00\xf4\x016\x00\x17\x00\xb0\x00EX\xb0\n/\x1b\xb1\n\x1c>Y\xb1\x19\x04\xf4\xb0#\xd001\x00\xff\xff\x00\x0f\x00\x00\x04\xbb\a4\x02&\x00=\x00\x00\x01\a\x00u\x01\x88\x016\x00\x14\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x1c>Y\xb1\v\b\xf401\xff\xff\x00m\xff\xec\x03\xea\x05\xfe\x02&\x00E\x00\x00\x01\a\x00D\x00\xd5\x00\x00\x00\x14\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb1*\t\xf401\xff\xff\x00m\xff\xec\x03\xea\x05\xfe\x02&\x00E\x00\x00\x01\a\x00u\x01d\x00\x00\x00\x14\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb1+\t\xf401\xff\xff\x00m\xff\xec\x03\xea\x06\x00\x02&\x00E\x00\x00\x01\x06\x00\x8en\x00\x00\x14\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb1+\x01\xf401\xff\xff\x00m\xff\xec\x03\xea\x05\xec\x02&\x00E\x00\x00\x01\x06\x00\x90j\x04\x00\x14\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb1,\x01\xf401\xff\xff\x00m\xff\xec\x03\xea\x05\xc5\x02&\x00E\x00\x00\x01\a\x00j\x00\x9e\x00\x00\x00\x17\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb10\x01\xf4\xb09\xd001\x00\xff\xff\x00m\xff\xec\x03\xea\x06[\x02&\x00E\x00\x00\x01\a\x00\x8f\x00\xf5\x00\v\x00\x17\x00\xb0\x00EX\xb0\x17/\x1b\xb1\x17\x18>Y\xb1,\x04\xf4\xb06\xd001\x00\xff\xff\x00\\\xfeD\x03\xec\x04N\x02&\x00G\x00\x00\x00\a\x00y\x01?\xff\xf7\xff\xff\x00]\xff\xec\x03\xf3\x05\xfe\x02&\x00I\x00\x00\x01\a\x00D\x00\xc5\x00\x00\x00\x14\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb1\x1f\t\xf401\xff\xff\x00]\xff\xec\x03\xf3\x05\xfe\x02&\x00I\x00\x00\x01\a\x00u\x01T\x00\x00\x00\x14\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb1 \t\xf401\xff\xff\x00]\xff\xec\x03\xf3\x06\x00\x02&\x00I\x00\x00\x01\x06\x00\x8e^\x00\x00\x14\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb1 \x01\xf401\xff\xff\x00]\xff\xec\x03\xf3\x05\xc5\x02&\x00I\x00\x00\x01\a\x00j\x00\x8e\x00\x00\x00\x17\x00\xb0\x00EX\xb0\b/\x1b\xb1\b\x18>Y\xb1%\x01\xf4\xb0.\xd001\x00\xff\xff\xff\xc6\x00\x00\x01g\x05\xfd\x02&\x00\x8b\x00\x00\x01\x06\x00D\x8d\xff\x00\x14\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x18>Y\xb1\x05\t\xf401\xff\xff\x00\x96\x00\x00\x027\x05\xfd\x02&\x00\x8b\x00\x00\x01\x06\x00u\x1b\xff\x00\x14\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb1\x06\t\xf401\xff\xff\xff\xcf\x00\x00\x02,\x05\xff\x02&\x00\x8b\x00\x00\x01\a\x00\x8e\xff&\xff\xff\x00\x14\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x18>Y\xb1\b\x01\xf401\xff\xff\xff\xbc\x00\x00\x02E\x05\xc4\x02&\x00\x8b\x00\x00\x01\a\x00j\xffV\xff\xff\x00\x17\x00\xb0\x00EX\xb0\x02/\x1b\xb1\x02\x18>Y\xb1\v\x01\xf4\xb0\x14\xd001\x00\xff\xff\x00\x8c\x00\x00\x03\xdf\x05\xec\x02&\x00R\x00\x00\x01\x06\x00\x90a\x04\x00\x14\x00\xb0\x00EX\xb0\x03/\x1b\xb1\x03\x18>Y\xb1\x15\x01\xf401\xff\xff\x00[\xff\xec\x044\x05\xfe\x02&\x00S\x00\x00\x01\a\x00D\x00\xcf\x00\x00\x00\x14\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb1\x1d\t\xf401\xff\xff\x00[\xff\xec\x044\x05\xfe\x02&\x00S\x00\x00\x01\a\x00u\x01^\x00\x00\x00\x14\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb1\x1e\t\xf401\xff\xff\x00[\xff\xec\x044\x06\x00\x02&\x00S\x00\x00\x01\x06\x00\x8eh\x00\x00\x14\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb1\x1e\x01\xf401\xff\xff\x00[\xff\xec\x044\x05\xec\x02&\x00S\x00\x00\x01\x06\x00\x90d\x04\x00\x14\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb1\x1f\x01\xf401\xff\xff\x00[\xff\xec\x044\x05\xc5\x02&\x00S\x00\x00\x01\a\x00j\x00\x98\x00\x00\x00\x17\x00\xb0\x00EX\xb0\x04/\x1b\xb1\x04\x18>Y\xb1#\x01\xf4\xb0,\xd001\x00\xff\xff\x00\x88\xff\xec\x03\xdc\x05\xfe\x02&\x00Y\x00\x00\x01\a\x00D\x00\xc7\x00\x00\x00\x14\x00\xb0\x00EX\xb0\a/\x1b\xb1\a\x18>Y\xb1\x12\t\xf401\xff\xff\x00\x88\xff\xec\x03\xdc\x05\xfe\x02&\x00Y\x00\x00\x01\a\x00u\x01V\x00\x00\x00\x14\x00\xb0\x00EX\xb0\r/\x1b\xb1\r\x18>Y\xb1\x13\t\xf401\xff\xff\x00\x88\xff\xec\x03\xdc\x06\x00\x02&\x00Y\x00\x00\x01\x06\x00\x8e`\x00\x00\x14\x00\xb0\x00EX\xb0\a/\x1b\xb1\a\x18>Y\xb1\x15\x01\xf401\xff\xff\x00\x88\xff\xec\x03\xdc\x05\xc5\x02&\x00Y\x00\x00\x01\a\x00j\x00\x90\x00\x00\x00\x17\x00\xb0\x00EX\xb0\a/\x1b\xb1\a\x18>Y\xb1\x18\x01\xf4\xb0!\xd001\x00\xff\xff\x00\x16\xfeK\x03\xb0\x05\xfe\x02&\x00]\x00\x00\x01\a\x00u\x01\x1b\x00\x00\x00\x14\x00\xb0\x00EX\xb0\x01/\x1b\xb1\x01\x18>Y\xb1\x12\t\xf401\xff\xff\x00\x16\xfeK\x03\xb0\x05\xc5\x02&\x00]\x00\x00\x01\x06\x00jU\x00\x00\x17\x00\xb0\x00EX\xb0\x0f/\x1b\xb1\x0f\x18>Y\xb1\x17\x01\xf4\xb0 \xd001\x00\x00\x00\x00\x01\x00\x00\x00\xde\x00\x8f\x00\x16\x00T\x00\x05\x00\x01\x00\x00\x00\x00\x00\x0e\x00\x00\x02\x00\x02\x14\x00\x06\x00\x01\x00\x00\x00a\x00a\x00a\x00a\x00a\x00\x93\x00\xb8\x018\x01\xaa\x02:\x02\xcd\x02\xe4\x03\x0e\x038\x03k\x03\x90\x03\xaf\x03\xc5\x03\xe6\x03\xfd\x04J\x04x\x04\xc7\x05<\x05\u007f\x05\xdf\x06>\x06k\x06\xdf\aF\a[\ap\a\x8f\a\xb6\a\xd5\b3\b\xd6\t\x15\tt\t\xc8\n\r\nM\n\x83\n\xeb\v-\vH\v{\v\xd0\v\xf4\fB\f~\f\xd3\r\x1e\r\x83\r\xdf\x0eJ\x0et\x0e\xb6\x0e\xe6\x0f;\x0f\x90\x0f\xc0\x0f\xf8\x10\x1c\x103\x10X\x10\u007f\x10\x9a\x10\xba\x112\x11\x90\x11\xe3\x12A\x12\xa8\x12\xfa\x13t\x13\xb9\x13\xf1\x14=\x14\x94\x14\xaf\x15\x1a\x15e\x15\xb3\x16\x17\x16x\x16\xb5\x17\x1f\x17q\x17\xb8\x17\xe8\x186\x18}\x18\xc2\x18\xfa\x19;\x19R\x19\x92\x19\xd9\x1a\f\x1ah\x1a\xda\x1b=\x1b\x9c\x1b\xbb\x1c`\x1c\x8f\x1d5\x1d\xa3\x1d\xaf\x1d\xcc\x1e\x84\x1e\x9a\x1e\xd6\x1f\x19\x1fi\x1f\xe4 \x04 M y \x98 \xd3!\x05!O![!u!\x8f!\xa9\"\n\"m\"\xab#&#z#\xea$\xa8%\x17%h%\xd9&8&S&\xd6'p'\x9e'\xd7(\x1b(%(/(S(w(\x99(\xa5(\xb1(\xe9)\f)()E)X)l)\xe8*\x03*k*\xbd*\xe8+7+\xa6+\xe8+\xe8+\xf0,V,m,\x84,\x9b,\xb2,\xcb,\xe4,\xf0-\a-\x1e-5-N-e-|-\x93-\xac-\xc3-\xda-\xf1.\b.\x1f.8.O.f.}.\x96.\xad.\xc4.\xdb.\xf1/\a/ /9/E/\\/s/\x89/\xa2/\xb8/\xce/\xe5/\xfe0\x140+0B0X0n0\x870\x9e0\xb50\xcb0\xe40\xfb1\x13\x00\x00\x00\x01\x00\x00\x00\x02\x00\x000\x1bQ\xae_\x0f<\xf5\x00\x1b\b\x00\x00\x00\x00\x00\xc4\xf0\x11.\x00\x00\x00\x00\xd0\xdbN\x9a\xfa\x1b\xfd\xd5\t0\bs\x00\x00\x00\t\x00\x02\x00\x00\x00\x00\x00\x00\x03\x8c\x00d\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfb\x00\x00\x01\xfb\x00\x00\x02\x0f\x00\xa0\x02\x8f\x00\x88\x04\xed\x00w\x04~\x00n\x05\xdc\x00i\x04\xf9\x00e\x01e\x00g\x02\xbc\x00\x85\x02\xc8\x00&\x03r\x00\x1c\x04\x89\x00N\x01\x92\x00\x1d\x025\x00%\x02\x1b\x00\x90\x03L\x00\x12\x04~\x00s\x04~\x00\xaa\x04~\x00]\x04~\x00^\x04~\x005\x04~\x00\x9a\x04~\x00\x84\x04~\x00M\x04~\x00p\x04~\x00d\x01\xf0\x00\x86\x01\xb1\x00)\x04\x11\x00H\x04d\x00\x98\x04.\x00\x86\x03\xc7\x00K\a/\x00j\x058\x00\x1c\x04\xfb\x00\xa9\x055\x00w\x05?\x00\xa9\x04\x8c\x00\xa9\x04l\x00\xa9\x05s\x00z\x05\xb4\x00\xa9\x02-\x00\xb7\x04j\x005\x05\x04\x00\xa9\x04N\x00\xa9\x06\xfc\x00\xa9\x05\xb4\x00\xa9\x05\x80\x00v\x05\f\x00\xa9\x05\x80\x00m\x04\xed\x00\xa8\x04\xbf\x00P\x04\xc6\x001\x050\x00\x8c\x05\x17\x00\x1c\a\x19\x00=\x05\x04\x009\x04\xce\x00\x0f\x04\xca\x00V\x02\x1f\x00\x92\x03H\x00(\x02\x1f\x00\t\x03X\x00@\x03\x9c\x00\x04\x02y\x009\x04Z\x00m\x04}\x00\x8c\x040\x00\\\x04\x83\x00_\x04=\x00]\x02\xc7\x00<\x04}\x00`\x04h\x00\x8c\x01\xf1\x00\x8d\x01\xe9\xff\xbf\x04\x0e\x00\x8d\x01\xf1\x00\x9c\a\x03\x00\x8b\x04j\x00\x8c\x04\x90\x00[\x04}\x00\x8c\x04\x8c\x00_\x02\xb5\x00\x8c\x04 \x00_\x02\x9d\x00\t\x04i\x00\x88\x03\xe0\x00!\x06\x03\x00+\x03\xf7\x00)\x03\xc9\x00\x16\x03\xf7\x00X\x02\xb5\x00@\x01\xf3\x00\xaf\x02\xb5\x00\x13\x05q\x00\x83\x01\xf3\x00\x8b\x04`\x00i\x04\xa6\x00[\x05\xb4\x00i\x04\xd8\x00\x1f\x01\xeb\x00\x93\x04\xe8\x00Z\x03X\x00f\x06I\x00[\x03\x93\x00\x93\x03\xc1\x00f\x04n\x00\u007f\x06J\x00Z\x03\xaa\x00x\x02\xfd\x00\x82\x04F\x00a\x02\xef\x00B\x02\xef\x00>\x02\x82\x00{\x04\x88\x00\x9a\x03\xe9\x00C\x02\x16\x00\x93\x01\xfb\x00t\x02\xef\x00z\x03\xa3\x00z\x03\xc0\x00f\x05\xdc\x00U\x065\x00P\x069\x00o\x03\xc9\x00D\az\xff\xf2\x04D\x00Y\x05\x80\x00v\x04\xba\x00\xa6\x04\xc2\x00\x8b\x06\xc1\x00N\x04\xb0\x00~\x04\x91\x00G\x04\x88\x00[\x04\x9c\x00\x95\x01\xfa\x00\x9b\a\xa1\x00h\aD\x00a\x03\xc4\x00\xa9\x02\xad\x00y\x03\xc6\x00{\x05@\x00\xa2\x06?\x00\x90\x01\x99\x00`\x01\x99\x000\x01\x97\x00$\x02\xd4\x00h\x02\xdb\x00<\x02\xc1\x00$\x02\xb2\x00\x8a\x02f\x00l\x02f\x00Y\x03\xa3\x00;\x02\xef\x006\x04~\x00_\x04\x92\x00\xa8\x04n\x00\x1f\x04\x8b\x00<\x02\xef\x00z\x02\xef\x00B\x02\xef\x00>\x02\xef\x006\x01\xfb\x00\x00\x025\x00%\x05]\x00%\x058\x00\x1c\x058\x00\x1c\x058\x00\x1c\x058\x00\x1c\x058\x00\x1c\x058\x00\x1c\x055\x00w\x04\x8c\x00\xa9\x04\x8c\x00\xa9\x04\x8c\x00\xa9\x04\x8c\x00\xa9\x02-\xff\xe0\x02-\x00\xb0\x02-\xff\xe9\x02-\xff\xd6\x05\xb4\x00\xa9\x05\x80\x00v\x05\x80\x00v\x05\x80\x00v\x05\x80\x00v\x05\x80\x00v\x050\x00\x8c\x050\x00\x8c\x050\x00\x8c\x050\x00\x8c\x04\xce\x00\x0f\x04Z\x00m\x04Z\x00m\x04Z\x00m\x04Z\x00m\x04Z\x00m\x04Z\x00m\x040\x00\\\x04=\x00]\x04=\x00]\x04=\x00]\x04=\x00]\x01\xfa\xff\xc6\x01\xfa\x00\x96\x01\xfa\xff\xcf\x01\xfa\xff\xbc\x04j\x00\x8c\x04\x90\x00[\x04\x90\x00[\x04\x90\x00[\x04\x90\x00[\x04\x90\x00[\x04i\x00\x88\x04i\x00\x88\x04i\x00\x88\x04i\x00\x88\x03\xc9\x00\x16\x00\x16\x00\x00\x00\x01\x00\x00\al\xfe\f\x00\x00\tI\xfa\x1b\xfeJ\t0\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdd\x00\x03\x04\x85\x01\x90\x00\x05\x00\x00\x05\x9a\x053\x00\x00\x01\x1f\x05\x9a\x053\x00\x00\x03\xd1\x00f\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\n\xffP\x00!\u007f\x00\x00\x00!\x00\x00\x00\x00GOOG\x00@\x00\x00\xff\xfd\x06\x00\xfe\x00\x00f\a\x9a\x02\x00 \x00\x01\x9f\x00\x00\x00\x00\x04:\x05\xb0\x00 \x00 \x00\x02\x00\x00\x00\x01\x00\x00\x00\xe0\t\t\x04\x00\x00\x02\x02\x02\x03\x06\x05\a\x06\x02\x03\x03\x04\x05\x02\x02\x02\x04\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x02\x02\x05\x05\x05\x04\b\x06\x06\x06\x06\x05\x05\x06\x06\x02\x05\x06\x05\b\x06\x06\x06\x06\x06\x05\x05\x06\x06\b\x06\x05\x05\x02\x04\x02\x04\x04\x03\x05\x05\x05\x05\x05\x03\x05\x05\x02\x02\x05\x02\b\x05\x05\x05\x05\x03\x05\x03\x05\x04\a\x04\x04\x04\x03\x02\x03\x06\x02\x05\x05\x06\x05\x02\x06\x04\a\x04\x04\x05\a\x04\x03\x05\x03\x03\x03\x05\x04\x02\x02\x03\x04\x04\a\a\a\x04\b\x05\x06\x05\x05\b\x05\x05\x05\x05\x02\t\b\x04\x03\x04\x06\a\x02\x02\x02\x03\x03\x03\x03\x03\x03\x04\x03\x05\x05\x05\x05\x03\x03\x03\x03\x02\x02\x06\x06\x06\x06\x06\x06\x06\x06\x05\x05\x05\x05\x02\x02\x02\x02\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x02\x02\x02\x02\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x04\x04\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\x1c\x00\x03\x00\x01\x00\x00\x00\x1c\x00\x03\x00\n\x00\x00\x01`\x00\x04\x01D\x00\x00\x006\x00 \x00\x04\x00\x16\x00\x00\x00\r\x00~\x00\xa0\x00\xac\x00\xad\x00\xbf\x00\xc6\x00\xcf\x00\xe6\x00\xef\x00\xff\x011\x01S\x02\xc6\x02\xda\x02\xdc \x14 \x1a \x1e \" : D t \xac\"\x12\xff\xff\x00\x00\x00\x00\x00\r\x00 \x00\xa0\x00\xa1\x00\xad\x00\xae\x00\xc0\x00\xc7\x00\xd0\x00\xe7\x00\xf0\x011\x01R\x02\xc6\x02\xda\x02\xdc \x13 \x18 \x1c \" 9 D t \xac\"\x12\xff\xff\x00\x01\xff\xf6\xff\xe4\x00\x06\xff\xc2\xff\xfa\xff\xc1\x00\x00\xff\xe8\x00\x00\xff\xe2\x00\x00\xffZ\xff:\xfd\xc8\xfd\xb5\xfd\xb4\xe0~\xe0{\xe0z\xe0w\xe0a\xe0X\xe0)\xdf\xf2ލ\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x002\x00\x00\x00\\\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\x81\x00\xa8\x00\xb8\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\x82\x00\x83\x00\xbe\x00\xbf\x00\xc0\x00\xc1\x00\xc2\x00\x84\x00\x85\x00\xc3\x00\xc4\x00\xc5\x00\xc6\x00\xc7\x00\xc8\x00\x86\x00\x87\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\x88\x00\x89\x00\xd8\x00\xd9\x00\xda\x00\xdb\x00\xdc\x00\x8a\x00\xdd\x00\f\x00\x00\x00\x00\x01\xd8\x00\x00\x00\x00\x00\x00\x00&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\r\x00\x00\x00\r\x00\x00\x00\x03\x00\x00\x00 \x00\x00\x00~\x00\x00\x00\x04\x00\x00\x00\xa0\x00\x00\x00\xa0\x00\x00\x00\xa6\x00\x00\x00\xa1\x00\x00\x00\xac\x00\x00\x00c\x00\x00\x00\xad\x00\x00\x00\xad\x00\x00\x00\xa7\x00\x00\x00\xae\x00\x00\x00\xbf\x00\x00\x00o\x00\x00\x00\xc0\x00\x00\x00\xc5\x00\x00\x00\xa9\x00\x00\x00\xc6\x00\x00\x00\xc6\x00\x00\x00\x81\x00\x00\x00\xc7\x00\x00\x00\xcf\x00\x00\x00\xaf\x00\x00\x00\xd0\x00\x00\x00\xd0\x00\x00\x00\xa8\x00\x00\x00\xd1\x00\x00\x00\xd6\x00\x00\x00\xb8\x00\x00\x00\xd7\x00\x00\x00\xd8\x00\x00\x00\x82\x00\x00\x00\xd9\x00\x00\x00\xdd\x00\x00\x00\xbe\x00\x00\x00\xde\x00\x00\x00\xdf\x00\x00\x00\x84\x00\x00\x00\xe0\x00\x00\x00\xe5\x00\x00\x00\xc3\x00\x00\x00\xe6\x00\x00\x00\xe6\x00\x00\x00\x86\x00\x00\x00\xe7\x00\x00\x00\xef\x00\x00\x00\xc9\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\x87\x00\x00\x00\xf1\x00\x00\x00\xf6\x00\x00\x00\xd2\x00\x00\x00\xf7\x00\x00\x00\xf8\x00\x00\x00\x88\x00\x00\x00\xf9\x00\x00\x00\xfd\x00\x00\x00\xd8\x00\x00\x00\xfe\x00\x00\x00\xfe\x00\x00\x00\x8a\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xdd\x00\x00\x011\x00\x00\x011\x00\x00\x00\x8b\x00\x00\x01R\x00\x00\x01S\x00\x00\x00\x8c\x00\x00\x02\xc6\x00\x00\x02\xc6\x00\x00\x00\x8e\x00\x00\x02\xda\x00\x00\x02\xda\x00\x00\x00\x8f\x00\x00\x02\xdc\x00\x00\x02\xdc\x00\x00\x00\x90\x00\x00 \x13\x00\x00 \x14\x00\x00\x00\x91\x00\x00 \x18\x00\x00 \x1a\x00\x00\x00\x93\x00\x00 \x1c\x00\x00 \x1e\x00\x00\x00\x96\x00\x00 \"\x00\x00 \"\x00\x00\x00\x99\x00\x00 9\x00\x00 :\x00\x00\x00\x9a\x00\x00 D\x00\x00 D\x00\x00\x00\x9c\x00\x00 t\x00\x00 t\x00\x00\x00\x9d\x00\x00 \xac\x00\x00 \xac\x00\x00\x00\x9e\x00\x00\"\x12\x00\x00\"\x12\x00\x00\x00\x9f\xb0\x00,K\xb0\tPX\xb1\x01\x01\x8eY\xb8\x01\xff\x85\xb0\x84\x1d\xb1\t\x03_^-\xb0\x01, EiD\xb0\x01`-\xb0\x02,\xb0\x01*!-\xb0\x03, F\xb0\x03%FRX#Y \x8a \x8aId\x8a F had\xb0\x04%F hadRX#e\x8aY/ \xb0\x00SXi \xb0\x00TX!\xb0@Y\x1bi \xb0\x00TX!\xb0@eYY:-\xb0\x04, F\xb0\x04%FRX#\x8aY F jad\xb0\x04%F jadRX#\x8aY/\xfd-\xb0\x05,K \xb0\x03&PXQX\xb0\x80D\x1b\xb0@DY\x1b!! E\xb0\xc0PX\xb0\xc0D\x1b!YY-\xb0\x06, EiD\xb0\x01` E}i\x18D\xb0\x01`-\xb0\a,\xb0\x06*-\xb0\b,K \xb0\x03&SX\xb0@\x1b\xb0\x00Y\x8a\x8a \xb0\x03&SX#!\xb0\x80\x8a\x8a\x1b\x8a#Y \xb0\x03&SX#!\xb0\xc0\x8a\x8a\x1b\x8a#Y \xb0\x03&SX#!\xb8\x01\x00\x8a\x8a\x1b\x8a#Y \xb0\x03&SX#!\xb8\x01@\x8a\x8a\x1b\x8a#Y \xb0\x03&SX\xb0\x03%E\xb8\x01\x80PX#!\xb8\x01\x80#!\x1b\xb0\x03%E#!#!Y\x1b!YD-\xb0\t,KSXED\x1b!!Y-\xb0\n,\xb0$E-\xb0\v,\xb0%E-\xb0\f,\xb1'\x01\x88 \x8aSX\xb9@\x00\x04\x00c\xb8\b\x00\x88TX\xb9\x00$\x03\xe8pY\x1b\xb0#SX\xb0 \x88\xb8\x10\x00TX\xb9\x00$\x03\xe8pYYY-\xb0\r,\xb0@\x88\xb8 \x00ZX\xb1%\x00D\x1b\xb9\x00%\x03\xe8DY-\xb0\f+\xb0\x00+\x00\xb2\x01\x0e\x02+\x01\xb2\x0f\x01\x02+\x01\xb7\x0f:0%\x1b\x10\x00\b+\x00\xb7\x01H;.!\x14\x00\b+\xb7\x02XH8(\x14\x00\b+\xb7\x03RC4%\x16\x00\b+\xb7\x04^M<+\x19\x00\b+\xb7\x056,\"\x19\x0f\x00\b+\xb7\x06q]F2\x1b\x00\b+\xb7\a\x91w\\:#\x00\b+\xb7\b~gP9\x1a\x00\b+\xb7\tTE6&\x17\x00\b+\xb7\nv`K6\x1d\x00\b+\xb7\v\x83dN:#\x00\b+\xb7\fٲ\x8ac<\x00\b+\xb7\r\x14\x11\r\t\x06\x00\b+\xb7\x0e<2'\x1c\x11\x00\b+\x00\xb2\x10\n\a+\xb0\x00 E}i\x18D\xb20\x12\x01s\xb2\xb0\x14\x01s\xb2P\x14\x01t\xb2\x80\x14\x01t\xb2p\x14\x01u\xb2\x0f\x1c\x01s\xb2o\x1c\x01u\x00\x00*\x00\x9d\x00\x80\x00\x8a\x00x\x00\xd4\x00d\x00N\x00Z\x00\x87\x00`\x00V\x004\x02<\x00\xbc\x00\xc4\x00\x00\x00\x14\xfe`\x00\x14\x02\x9b\x00 \x03!\x00\v\x04:\x00\x14\x04\x8d\x00\x10\x05\xb0\x00\x14\x06\x18\x00\x15\x01\xa6\x00\x11\x06\xc0\x00\x0e\x00\x00\x00\x00\x00\x00\x00\a\x00Z\x00\x03\x00\x01\x04\t\x00\x01\x00\f\x00\x00\x00\x03\x00\x01\x04\t\x00\x02\x00\x0e\x00\f\x00\x03\x00\x01\x04\t\x00\x03\x00\f\x00\x00\x00\x03\x00\x01\x04\t\x00\x04\x00\f\x00\x00\x00\x03\x00\x01\x04\t\x00\x05\x00,\x00\x1a\x00\x03\x00\x01\x04\t\x00\x06\x00\x1c\x00F\x00\x03\x00\x01\x04\t\x00\x0e\x00T\x00b\x00R\x00o\x00b\x00o\x00t\x00o\x00R\x00e\x00g\x00u\x00l\x00a\x00r\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00 \x002\x00.\x000\x000\x001\x001\x000\x001\x00;\x00 \x002\x000\x001\x004\x00R\x00o\x00b\x00o\x00t\x00o\x00-\x00R\x00e\x00g\x00u\x00l\x00a\x00r\x00h\x00t\x00t\x00p\x00:\x00/\x00/\x00w\x00w\x00w\x00.\x00a\x00p\x00a\x00c\x00h\x00e\x00.\x00o\x00r\x00g\x00/\x00l\x00i\x00c\x00e\x00n\x00s\x00e\x00s\x00/\x00L\x00I\x00C\x00E\x00N\x00S\x00E\x00-\x002\x00.\x000\x00\x03\x00\x00\x00\x00\x00\x00\xffj\x00d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\b\x00\x02\xff\xff\x00\x0f\x00\x01\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00\x02\x00\n\x00%\x00>\x00\x01\x00E\x00^\x00\x01\x00y\x00y\x00\x03\x00\x81\x00\x81\x00\x01\x00\x83\x00\x83\x00\x01\x00\x86\x00\x86\x00\x01\x00\x89\x00\x89\x00\x01\x00\x8b\x00\x8d\x00\x01\x00\xa0\x00\xa1\x00\x02\x00\xa8\x00\xdd\x00\x01\x00\x01\x00\x00\x00\n\x00T\x00t\x00\x04DFLT\x00\x1acyrl\x00&grek\x002latn\x00>\x00\x04\x00\x00\x00\x00\xff\xff\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\xff\xff\x00\x01\x00\x01\x00\x04\x00\x00\x00\x00\xff\xff\x00\x01\x00\x02\x00\x04\x00\x00\x00\x00\xff\xff\x00\x01\x00\x03\x00\x04kern\x00\x1akern\x00\x1akern\x00\x1akern\x00\x1a\x00\x00\x00\x01\x00\x00\x00\x01\x00\x04\x00\x02\x00\x00\x00\x04\x00\x0e\x02\x0e\x03\x92\x04R\x00\x01\x00\x82\x00\x04\x00\x00\x00<\x01\x88\x01\x88\x00\xfe\x01\x8e\x01\x9c\x01\xb4\x01\xaa\x01\x04\x01\n\x01\x10\x01\xb4\x01\x16\x01 \x01B\x01T\x01\xba\x01f\x01\xf4\x01l\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01z\x01\xfa\x01\xfa\x01\x88\x01\x88\x01\x88\x01\x88\x01\xb4\x01\x8e\x01\x8e\x01\x8e\x01\x8e\x01\x8e\x01\x8e\x01\x9c\x01\xaa\x01\xaa\x01\xaa\x01\xaa\x01\xb4\x01\xb4\x01\xb4\x01\xb4\x01\xb4\x01\xba\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01\xf4\x01\xfa\x01\xfa\x00\x01\x00<\x00\x06\x00\v\x00\x13\x00%\x00'\x00(\x00)\x00*\x00/\x000\x003\x004\x008\x00:\x00;\x00=\x00>\x00I\x00J\x00L\x00Q\x00R\x00S\x00V\x00Z\x00]\x00\x93\x00\x94\x00\x96\x00\x97\x00\xa8\x00\xa9\x00\xaa\x00\xab\x00\xac\x00\xad\x00\xae\x00\xaf\x00\xb0\x00\xb1\x00\xb2\x00\xb3\x00\xb9\x00\xba\x00\xbb\x00\xbc\x00\xbd\x00\xc2\x00\xca\x00\xcb\x00\xcc\x00\xcd\x00\xd2\x00\xd3\x00\xd4\x00\xd5\x00\xd6\x00\xd7\x00\xdc\x00\xdd\x00\x01\x00\x13\xff \x00\x01\x00V\xff\xe6\x00\x01\x00[\xff\xc1\x00\x01\x00[\xff\xa4\x00\x02\x00X\x00\x0e\x00\x81\xff\x9f\x00\b\x00\x04\xff\xd8\x00V\xff\xb5\x00[\xff\xc7\x00m\xfe\xb8\x00|\xff(\x00\x81\xffM\x00\x86\xff\x8e\x00\x89\xff\xa1\x00\x04\x00\r\x00\x14\x00A\x00\x11\x00V\xff\xe2\x00a\x00\x13\x00\x04\x00\r\x00\x0f\x00A\x00\f\x00V\xff\xeb\x00a\x00\x0e\x00\x01\x00[\xff\xe5\x00\x03\x00\r\x00\x14\x00A\x00\x12\x00a\x00\x13\x00\x03\x00J\x00\x0f\x00X\x002\x00[\x00\x11\x00\x01\x00[\x00\v\x00\x03\x00#\xff\xc3\x00X\xff\xef\x00[\xff\xdf\x00\x03\x00\r\xff\xe6\x00A\xff\xf4\x00a\xff\xef\x00\x02\x00J\xff\xee\x00[\xff\xea\x00\x01\x00\x81\xff\xdf\x00\x0e\x00\n\xff\xe2\x00\r\x00\x14\x00\x0e\xff\xcf\x00A\x00\x12\x00J\xff\xea\x00V\xff\xd8\x00X\xff\xea\x00a\x00\x13\x00m\xff\xae\x00|\xff\xcd\x00\x81\xff\xa0\x00\x86\xff\xc1\x00\x89\xff\xc0\x00\x99\xff\xd3\x00\x01\x00\x94\xff\xb0\x00\x01\x00J\x00\r\x00\x01\x00\x18\x00\x04\x00\x00\x00\a\x00*\x000\x00B\x00\xfc\x01\x12\x01$\x01>\x00\x01\x00\a\x00\x04\x00\f\x00*\x005\x006\x00?\x00J\x00\x01\x008\xff\xd8\x00\x04\x00:\x00\x14\x00;\x00\x12\x00=\x00\x16\x00\xc2\x00\x16\x00.\x00\x10\xff\x16\x00\x12\xff\x16\x00%\xffV\x00.\xfe\xf8\x008\x00\x14\x00E\xff\xde\x00G\xff\xeb\x00H\xff\xeb\x00I\xff\xeb\x00K\xff\xeb\x00S\xff\xeb\x00U\xff\xeb\x00Y\xff\xea\x00Z\xff\xe8\x00]\xff\xe8\x00\x8d\xff\xeb\x00\x95\xff\x16\x00\x98\xff\x16\x00\xa9\xffV\x00\xaa\xffV\x00\xab\xffV\x00\xac\xffV\x00\xad\xffV\x00\xae\xffV\x00\xc3\xff\xde\x00\xc4\xff\xde\x00\xc5\xff\xde\x00\xc6\xff\xde\x00\xc7\xff\xde\x00\xc8\xff\xde\x00\xc9\xff\xeb\x00\xca\xff\xeb\x00\xcb\xff\xeb\x00\xcc\xff\xeb\x00\xcd\xff\xeb\x00\xd3\xff\xeb\x00\xd4\xff\xeb\x00\xd5\xff\xeb\x00\xd6\xff\xeb\x00\xd7\xff\xeb\x00\xd8\xff\xea\x00\xd9\xff\xea\x00\xda\xff\xea\x00\xdb\xff\xea\x00\xdc\xff\xe8\x00\xdd\xff\xe8\x00\x05\x008\xff\xd5\x00:\xff\xe4\x00;\xff\xec\x00=\xff\xdd\x00\xc2\xff\xdd\x00\x04\x008\xff\xb0\x00:\xff\xed\x00=\xff\xd0\x00\xc2\xff\xd0\x00\x06\x00.\xff\xee\x009\xff\xee\x00\xbe\xff\xee\x00\xbf\xff\xee\x00\xc0\xff\xee\x00\xc1\xff\xee\x00\x11\x00\x06\x00\x10\x00\v\x00\x10\x00G\xff\xe8\x00H\xff\xe8\x00I\xff\xe8\x00K\xff\xe8\x00U\xff\xe8\x00\x8d\xff\xe8\x00\x93\x00\x10\x00\x94\x00\x10\x00\x96\x00\x10\x00\x97\x00\x10\x00\xc9\xff\xe8\x00\xca\xff\xe8\x00\xcb\xff\xe8\x00\xcc\xff\xe8\x00\xcd\xff\xe8\x00\x01\x00\x14\x00\x04\x00\x00\x00\x05\x00\"\x00P\x00j\x00|\x00\x96\x00\x01\x00\x05\x00O\x00X\x00[\x00_\x00\x94\x00\v\x00G\xff\xec\x00H\xff\xec\x00I\xff\xec\x00K\xff\xec\x00U\xff\xec\x00\x8d\xff\xec\x00\xc9\xff\xec\x00\xca\xff\xec\x00\xcb\xff\xec\x00\xcc\xff\xec\x00\xcd\xff\xec\x00\x06\x00S\xff\xec\x00\xd3\xff\xec\x00\xd4\xff\xec\x00\xd5\xff\xec\x00\xd6\xff\xec\x00\xd7\xff\xec\x00\x04\x00\x10\xff\x84\x00\x12\xff\x84\x00\x95\xff\x84\x00\x98\xff\x84\x00\x06\x00.\xff\xec\x009\xff\xec\x00\xbe\xff\xec\x00\xbf\xff\xec\x00\xc0\xff\xec\x00\xc1\xff\xec\x00\n\x00L\x00 \x00O\x00 \x00P\x00 \x00S\xff\x80\x00W\xff\x90\x00\xd3\xff\x80\x00\xd4\xff\x80\x00\xd5\xff\x80\x00\xd6\xff\x80\x00\xd7\xff\x80\x00\x02\x05P\x00\x04\x00\x00\x05\xc6\a\b\x00\x1c\x00\x18\x00\x00\xff\xce\xff\xf5\xff\xef\xff\x88\xff\xf4\xff\xbb\xff\u007f\xff\xf5\x00\f\xff\xa9\xff\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe5\x00\x00\x00\x00\xff\xe8\xff\xc9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe3\x00\x00\x00\x00\x00\x00\xff\xe4\x00\x12\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe5\x00\x00\x00\x00\xff\xea\xff\xd5\xff\xeb\xff\xea\xff\x9a\xff\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe6\x00\x00\x00\x00\x00\x00\xff\xed\x00\x00\x00\x14\xff\xef\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xed\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xb8\xff\xe4\x00\x00\x00\x00\xff\x9d\x00\x0f\x00\x10\xff\xa1\xff\xc4\x00\x10\x00\x10\xff\xb1\x00\x00\xff&\x00\x00\xff\x9d\xff\xb3\xff\x18\xff\x93\xff\xf0\xff\x8f\xff\x8c\xff\x10\x00\x00\xff\xd8\xff\xe1\x00\x00\x00\x00\xff\xe5\x00\x00\x00\x00\xff\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe6\x00\x00\xff\xc0\xff\xe9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff{\xff\xbf\xff\xca\xfe\xb0\x00\x00\xffq\xfe\xed\xff\xd4\x00\x00\xffQ\xff\x11\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\xff\xf3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xffv\xff\xe1\xfe\xbc\xff\xe6\xff\xf3\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf5\x00\x00\xff8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xea\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf5\xff\xf3\x00\x00\x00\x00\xff\xd2\x00\x00\x00\x00\xff\xe4\x00\x00\x00\x00\x00\x00\xff\xb5\x00\x00\xff\x1f\x00\x00\xff\xd4\x00\x00\xff\xdb\x00\x00\x00\x00\xff\xd2\x00\x00\x00\x00\x00\x00\xff\xe1\xff\xe7\x00\x00\x00\x00\xff\xeb\x00\x00\x00\x00\xff\xeb\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe6\x00\x00\xff\xd2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xec\xff\xe3\xff\xa0\x00\x00\xff\xbf\x00\x11\x00\x11\xff\xd9\xff\xe2\x00\x12\x00\x12\xff\xa2\x00\r\xff-\x00\x00\xff\xbf\xff\xe9\xff\xcc\xff\xd8\xff\xf0\xff\xb7\xff\xc6\xff\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe1\x00\x00\x00\x0e\xff\xed\x00\x00\x00\x00\x00\x00\xff\xd5\x00\x00\xff\x85\x00\x00\xff\xe1\x00\x00\xff\xc4\x00\x00\x00\x00\xff\xdf\x00\x00\x00\x00\x00\x00\xff\xe5\xff\xe6\x00\x00\x00\x00\xff\xeb\x00\x00\x00\x00\xff\xed\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00\x00\x00\x00\xff\xeb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf1\x00\x00\x00\x00\xff\xbd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf5\x00\x00\x00\x00\xff\xe3\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf3\x00\x00\x00\x00\xff\xf2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x98\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf1\x00\x00\x00\x00\xffx\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xeb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xff\xf1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x95\x00\x00\xff\xf3\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf1\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x00\x10\xff\xec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x85\x00\x00\xff\xed\x00\x00\x00\x00\x00\x00\x00\x00\xff\xd8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x95\xff\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x88\x00\x00\x00\x00\x00\x00\xff\xc5\x00\x00\x00\x00\xff\xec\x00\x00\xff\xce\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xffV\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x13\x00\x06\x00\x06\x00\x00\x00\v\x00\v\x00\x01\x00\x10\x00\x10\x00\x02\x00\x12\x00\x12\x00\x03\x00%\x00)\x00\x04\x00,\x004\x00\t\x008\x00>\x00\x12\x00E\x00G\x00\x19\x00I\x00I\x00\x1c\x00L\x00L\x00\x1d\x00Q\x00T\x00\x1e\x00V\x00V\x00\"\x00Z\x00Z\x00#\x00\\\x00^\x00$\x00\x8a\x00\x8a\x00'\x00\x93\x00\x98\x00(\x00\xa8\x00\xcd\x00.\x00\xd2\x00\xd7\x00T\x00\xdc\x00\xdd\x00Z\x00\x02\x005\x00\x06\x00\x06\x00\x1a\x00\v\x00\v\x00\x1a\x00\x10\x00\x10\x00\x1b\x00\x12\x00\x12\x00\x1b\x00&\x00&\x00\x01\x00'\x00'\x00\x04\x00(\x00(\x00\x03\x00)\x00)\x00\x05\x00,\x00-\x00\x02\x00.\x00.\x00\n\x00/\x00/\x00\a\x000\x000\x00\b\x001\x002\x00\x02\x003\x003\x00\x03\x004\x004\x00\t\x008\x008\x00\x06\x009\x009\x00\n\x00:\x00:\x00\v\x00;\x00;\x00\x0e\x00<\x00<\x00\f\x00=\x00=\x00\r\x00>\x00>\x00\x0f\x00E\x00E\x00\x10\x00F\x00F\x00\x12\x00G\x00G\x00\x11\x00I\x00I\x00\x13\x00L\x00L\x00\x14\x00Q\x00R\x00\x14\x00S\x00S\x00\x15\x00T\x00T\x00\x12\x00V\x00V\x00\x17\x00Z\x00Z\x00\x16\x00\\\x00\\\x00\x18\x00]\x00]\x00\x16\x00^\x00^\x00\x19\x00\x8a\x00\x8a\x00\x12\x00\x93\x00\x94\x00\x1a\x00\x95\x00\x95\x00\x1b\x00\x96\x00\x97\x00\x1a\x00\x98\x00\x98\x00\x1b\x00\xa8\x00\xa8\x00\x03\x00\xaf\x00\xaf\x00\x04\x00\xb0\x00\xb3\x00\x05\x00\xb4\x00\xb8\x00\x02\x00\xb9\x00\xbd\x00\x03\x00\xbe\x00\xc1\x00\n\x00\xc2\x00\xc2\x00\r\x00\xc3\x00\xc8\x00\x10\x00\xc9\x00\xc9\x00\x11\x00\xca\x00\xcd\x00\x13\x00\xd2\x00\xd2\x00\x14\x00\xd3\x00\xd7\x00\x15\x00\xdc\x00\xdd\x00\x16\x00\x02\x004\x00\x06\x00\x06\x00\x04\x00\v\x00\v\x00\x04\x00\x10\x00\x10\x00\x0e\x00\x11\x00\x11\x00\x12\x00\x12\x00\x12\x00\x0e\x00%\x00%\x00\f\x00'\x00'\x00\x02\x00+\x00+\x00\x02\x00.\x00.\x00\x17\x003\x003\x00\x02\x005\x005\x00\x02\x007\x007\x00\x14\x008\x008\x00\a\x009\x009\x00\x03\x00:\x00:\x00\n\x00;\x00;\x00\x06\x00<\x00<\x00\r\x00=\x00=\x00\v\x00>\x00>\x00\x0f\x00E\x00E\x00\x15\x00G\x00I\x00\x10\x00K\x00K\x00\x10\x00Q\x00R\x00\x13\x00S\x00S\x00\x05\x00T\x00T\x00\x13\x00U\x00U\x00\x10\x00W\x00W\x00\x16\x00Y\x00Y\x00\b\x00Z\x00Z\x00\x01\x00\\\x00\\\x00\x11\x00]\x00]\x00\x01\x00^\x00^\x00\t\x00\x83\x00\x83\x00\x02\x00\x8c\x00\x8c\x00\x02\x00\x8d\x00\x8d\x00\x10\x00\x91\x00\x92\x00\x12\x00\x93\x00\x94\x00\x04\x00\x95\x00\x95\x00\x0e\x00\x96\x00\x97\x00\x04\x00\x98\x00\x98\x00\x0e\x00\xa7\x00\xa7\x00\x12\x00\xa9\x00\xae\x00\f\x00\xaf\x00\xaf\x00\x02\x00\xb9\x00\xbd\x00\x02\x00\xbe\x00\xc1\x00\x03\x00\xc2\x00\xc2\x00\v\x00\xc3\x00\xc8\x00\x15\x00\xc9\x00\xcd\x00\x10\x00\xd2\x00\xd2\x00\x13\x00\xd3\x00\xd7\x00\x05\x00\xd8\x00\xdb\x00\b\x00\xdc\x00\xdd\x00\x01\x00\x00\x00\x01\x00\x00\x00\n\x00,\x00H\x00\x01latn\x00\b\x00\n\x00\x01TUR \x00\x12\x00\x00\xff\xff\x00\x01\x00\x00\x00\x00\xff\xff\x00\x01\x00\x01\x00\x02liga\x00\x0eliga\x00\x16\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\x00\x01\x00\x02\x00\x06\x00 \x00\x04\x00\x00\x00\x01\x00\b\x00\x01\x00,\x00\x01\x00\b\x00\x01\x00\x04\x00\xa0\x00\x02\x00M\x00\x04\x00\x00\x00\x01\x00\b\x00\x01\x00\x12\x00\x01\x00\b\x00\x01\x00\x04\x00\xa1\x00\x02\x00P\x00\x01\x00\x01\x00J"), } file4 := &embedded.EmbeddedFile{ Filename: "3d3a53586bd78d1069ae4b89a3b9aa98.svg", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"), } file5 := &embedded.EmbeddedFile{ Filename: "674f50d287a8c48dc19ba404d20fe713.eot", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("n\x87\x02\x00\xac\x86\x02\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x90\x01\x00\x00\x00\x00LP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00Yxϐ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00\x00\x0e\x00R\x00e\x00g\x00u\x00l\x00a\x00r\x00\x00\x00$\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00 \x004\x00.\x007\x00.\x000\x00 \x002\x000\x001\x006\x00\x00\x00\x16\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\r\x00\x80\x00\x03\x00PFFTMk\xbeG\xb9\x00\x02\x86\x90\x00\x00\x00\x1cGDEF\x02\xf0\x00\x04\x00\x02\x86p\x00\x00\x00 OS/2\x882z@\x00\x00\x01X\x00\x00\x00`cmap\n\xbf:\u007f\x00\x00\f\xa8\x00\x00\x02\xf2gasp\xff\xff\x00\x03\x00\x02\x86h\x00\x00\x00\bglyf\x8f\xf7\xaeM\x00\x00\x1a\xac\x00\x02L\xbchead\x10\x89\xe5-\x00\x00\x00\xdc\x00\x00\x006hhea\x0f\x03\n\xb5\x00\x00\x01\x14\x00\x00\x00$hmtxEy\x18\x85\x00\x00\x01\xb8\x00\x00\n\xf0loca\x02\xf5\xa2\\\x00\x00\x0f\x9c\x00\x00\v\x10maxp\x03,\x02\x1c\x00\x00\x018\x00\x00\x00 name㗋\xac\x00\x02gh\x00\x00\x04\x86post\xaf\x8f\x9b\xa1\x00\x02k\xf0\x00\x00\x1au\x00\x01\x00\x00\x00\x04\x01ː\xcfxY_\x0f<\xf5\x00\v\a\x00\x00\x00\x00\x00\xd43\xcd2\x00\x00\x00\x00\xd43\xcd2\xff\xff\xff\x00\t\x01\x06\x00\x00\x00\x00\b\x00\x02\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x06\x00\xff\x00\x00\x00\t\x00\xff\xff\xff\xff\t\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xb5\x00\x01\x00\x00\x02\xc3\x02\x19\x00'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x01\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x03\x06i\x01\x90\x00\x05\x00\x00\x04\x8c\x043\x00\x00\x00\x86\x04\x8c\x043\x00\x00\x02s\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00pyrs\x00@\x00 \xf5\x00\x06\x00\xff\x00\x00\x00\x06\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x01\x03\x80\x00p\x00\x00\x00\x00\x02U\x00\x00\x01\xc0\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00]\x06\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\x05\x00\x00\x00\a\x80\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00y\x05\x80\x00n\x06\x80\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x06\x80\x00\x1a\x06\x00\x00\x00\x06\x00\x00\x00\a\x80\x002\x06\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x04\x80\x00\x00\a\x00\x00@\x06\x80\x00\x00\x03\x00\x00\x00\x04\x80\x00\x00\x06\x80\x00\x00\x05\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\x06\x80\x00\n\x05\x00\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\x06\x80\x00\x00\x05\x80\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x80\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x80\x00z\x05\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\x06\x02\x00\x01\x05\x00\x00\x9a\x05\x00\x00Z\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00@\x06\x00\x00\x00\x06\x80\x005\x06\x80\x005\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\r\x05\x80\x00\x00\x05\x80\x00\x00\x06\x80\x00z\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x05\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x10\x05\x80\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00Z\a\x00\x00Z\a\x80\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\x03\x00\x00@\a\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x03\x80\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x04\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x05\x80\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00,\x04\x00\x00_\x06\x00\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\x05\x80\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00@\x06\x00\x00\x02\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x15\a\x00\x00\x00\x05\x80\x00\x05\a\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\x06\x80\x00\x10\a\x80\x00\x00\x06\x80\x00s\a\x00\x00\x01\a\x00\x00\x00\x05\x80\x00\x04\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x0f\a\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x1b\a\x00\x00@\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\t\x00\x00\x00\a\x80\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x02\x80\x00@\x02\x80\x00\x00\x06\x80\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00(\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x03\x80\x00\x01\a\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\a\x80\x00\x00\a\x80\x00\x00\x05\x80\x00\x00\x05\x80\x00\x00\a\x00\x00\x00\a\x00\x00@\a\x80\x00\x00\x05\x80\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x05\x80\x00\x00\a\x80\x00@\a\x00\x00\x00\a\x80\x00\x00\x06\x80\x00@\x06\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00-\x04\x00\x00\r\x04\x80\x00M\x04\x80\x00M\x02\x80\x00-\x02\x80\x00\r\x04\x80\x00M\x04\x80\x00M\a\x80\x00\x00\a\x80\x00\x00\x04\x80\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x00\x00@\x06\x00\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\a\x80\x00\x00\a\x00\x00@\a\x00\x00@\x06\x80\x00\r\a\x80\x00-\a\x00\x00\x00\x06\x80\x00\x02\x05\x80\x00\x02\x06\x80\x00\x00\x04\x00\x00\x00\x06\x80\x00\x00\x04\x00\x00`\x02\x80\x00\x00\x02\x80\x00b\x06\x00\x00\x05\x06\x00\x00\x05\a\x80\x00\x01\x06\x80\x00\x00\x04\x80\x00\x00\x05\x80\x00\r\x05\x00\x00\x00\x06\x80\x00\x00\x05\x80\x00\x03\x06\x80\x00$\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\a\x00\x00\f\a\x00\x00\x00\x04\x80\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x01\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x006\x06\x00\x00\x00\x05\x80\x00\x00\x04\x00\x00\x03\x04\x00\x00\x03\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x004\x03\x82\x00\x00\x04\x03\x00\x04\x05\x00\x00\x00\a\x00\x00\x00\x05\x00\x008\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\"\x06\x80\x00\"\a\x00\x00\"\a\x00\x00\"\x06\x00\x00\"\x06\x00\x00\"\x06\x80\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x1b\x05\x80\x00\x05\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00@\x06\x00\x00\v\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x06\x00\x00\x00\x04\x00\x00D\x06\x00\x00\x00\x03\x00\x00\x03\x03\x00\x00\x03\a\x00\x00@\a\x00\x00\x00\x05\x80\x00\x00\x06\x80\x00\x00\x05\x80\x00\x00\x06\x00\x00\v\x06\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00,\x06\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\a\x00\x00,\x06\x00\x00\x00\a\x00\x00@\x06\x80\x00 \a\x80\xff\xff\a\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x05\x00\x00\x15\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x04\x80\x00\x00\x05\x80\x00\x00\b\x80\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\t\x00\x00\x00\x06\x00\x00m\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\a\xf6\x00)\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00@\x06\x80\x00\x00\x03\x00\x00@\a\x00\x00\x00\t\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x10\b\x00\x00\x00\b\x00\x00\x00\x06\x00\x00 \x06\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00'\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00 \a\x00\x00\x13\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00D\x06\x00\x00\x00\x05\x00\x009\a\x00\x00\x12\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00>\x05\x00\x00\x18\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x19\a\x00\x00d\x06\x00\x00Y\b\x00\x00\x00\b\x00\x00*\a\x00\x00\x00\x06\x00\x00\t\a\x00\x00'\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\b\x00\x00\x0e\b\x00\x00\x0e\x05\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\x05\x00\x00\v\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\b\x00\x00\x00\b\x00\x00\x13\x06\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x02\x06\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x02\a\x00\x00\x00\a\x00\x00\x02\a\x80\x00\x01\b\x00\x00\x06\x06\x00\x00\x00\x05\x00\x00\x02\b\x00\x00\x04\x05\x00\x00\x00\x05\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\b\xf8\x00T\t\x00\x00\x00\a\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\b\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\a\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\xb5\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00@\a\x00\x00\x00\t\x00\x00\x00\x05\x00\x00f\x06\x00\x00\x00\x06\xb8\x00\x00\t\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x02\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x16\x06\x00\x00\x0e\a\x00\x00\x1d\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00%\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00R\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00E\t\x00\x00\x00\a\x00\x00\x00\a\x00\x00 \a\x00\x00\x00\t\x00\x00\x00\a\x00\x00\x00\t\x00\x00\x00\x06\x00\x00$\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00!\x06\x00\x00k\x04\x00\x00(\x06\x00\x00\x00\a\x00\x00\x03\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00D\x06\x00\x00\x00\x05\x80\x00'\t\x00\x00\x03\x05\x80\x00\x00\b\x80\x00\x00\a\x00\x00\x00\t\x00\x00\x03\a\x00\x00\x00\x06\x00\x00\x00\x05\xff\x00%\x06\x80\x00\x01\a\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x0f\x06\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00%\t\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x15\x06\x80\x00\x00\x06\x80\x00\x00\b\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x1d\t\x00\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\a\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x01\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x02\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\b\x80\x000\a\x00\x00%\x06\x00\x00\x00\x06\x80\x00/\a\x00\x00\x00\a\x00\x00\x00\a\x80\x00&\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\x1c\x00\x01\x00\x00\x00\x00\x01\xec\x00\x03\x00\x01\x00\x00\x00\x1c\x00\x04\x01\xd0\x00\x00\x00p\x00@\x00\x05\x000\x00 \x00\xa9\x00\xae\x00\xb4\x00\xc6\x00\xd8!\"\"\x1e\"`\xf0\x0e\xf0\x1e\xf0>\xf0N\xf0^\xf0n\xf0~\xf0\x8e\xf0\x9e\xf0\xae\xf0\xb2\xf0\xce\xf0\xde\xf0\xee\xf0\xfe\xf1\x0e\xf1\x1e\xf1.\xf1>\xf1N\xf1^\xf1n\xf1~\xf1\x8e\xf1\x9e\xf1\xae\xf1\xbe\xf1\xce\xf1\xde\xf1\xee\xf1\xfe\xf2\x0e\xf2\x1e\xf2>\xf2N\xf2^\xf2n\xf2~\xf2\x8e\xf2\x9e\xf2\xae\xf2\xbe\xf2\xce\xf2\xde\xf2\xee\xf5\x00\xff\xff\x00\x00\x00 \x00\xa8\x00\xae\x00\xb4\x00\xc6\x00\xd8!\"\"\x1e\"`\xf0\x00\xf0\x10\xf0!\xf0@\xf0P\xf0`\xf0p\xf0\x80\xf0\x90\xf0\xa0\xf0\xb0\xf0\xc0\xf0\xd0\xf0\xe0\xf0\xf0\xf1\x00\xf1\x10\xf1 \xf10\xf1@\xf1P\xf1`\xf1p\xf1\x80\xf1\x90\xf1\xa0\xf1\xb0\xf1\xc0\xf1\xd0\xf1\xe0\xf1\xf0\xf2\x00\xf2\x10\xf2!\xf2@\xf2P\xf2`\xf2p\xf2\x80\xf2\x90\xf2\xa0\xf2\xb0\xf2\xc0\xf2\xd0\xf2\xe0\xf5\x00\xff\xff\xff\xe3\xff\\\xffX\xffS\xffB\xff1\xde\xe8\xdd\xedݬ\x10\r\x10\f\x10\n\x10\t\x10\b\x10\a\x10\x06\x10\x05\x10\x04\x10\x03\x10\x02\x0f\xf5\x0f\xf4\x0f\xf3\x0f\xf2\x0f\xf1\x0f\xf0\x0f\xef\x0f\xee\x0f\xed\x0f\xec\x0f\xeb\x0f\xea\x0f\xe9\x0f\xe8\x0f\xe7\x0f\xe6\x0f\xe5\x0f\xe4\x0f\xe3\x0f\xe2\x0f\xe1\x0f\xe0\x0f\xde\x0f\xdd\x0f\xdc\x0f\xdb\x0f\xda\x0f\xd9\x0f\xd8\x0f\xd7\x0f\xd6\x0f\xd5\x0f\xd4\x0f\xd3\r\xc2\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x05\n\a\x04\f\b\t\v\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00\x90\x00\x00\x01\x14\x00\x00\x01\x98\x00\x00\x02t\x00\x00\x02\xd0\x00\x00\x03L\x00\x00\x03\xf0\x00\x00\x04T\x00\x00\x06$\x00\x00\x06\xe0\x00\x00\bl\x00\x00\tx\x00\x00\t\xd0\x00\x00\nT\x00\x00\v(\x00\x00\v\xd4\x00\x00\f\x84\x00\x00\rd\x00\x00\x0e\xa8\x00\x00\x0f\xd4\x00\x00\x10\x84\x00\x00\x11\x00\x00\x00\x11\x9c\x00\x00\x12l\x00\x00\x13,\x00\x00\x13\xd8\x00\x00\x14\x80\x00\x00\x14\xfc\x00\x00\x15\x90\x00\x00\x164\x00\x00\x17\x10\x00\x00\x18d\x00\x00\x18\xcc\x00\x00\x19p\x00\x00\x1aH\x00\x00\x1a\x94\x00\x00\x1b$\x00\x00\x1cd\x00\x00\x1d,\x00\x00\x1e\b\x00\x00\x1et\x00\x00\x1f(\x00\x00 \x8c\x00\x00 \xf0\x00\x00!\xa0\x00\x00\"0\x00\x00# \x00\x00$,\x00\x00$\xe0\x00\x00&D\x00\x00'\xe4\x00\x00(\x9c\x00\x00)T\x00\x00*\b\x00\x00*\xbc\x00\x00,\x10\x00\x00,\xf4\x00\x00-\xd8\x00\x00.@\x00\x00.\xd8\x00\x00/`\x00\x00/\xbc\x00\x000\x14\x00\x000\xa4\x00\x001\x94\x00\x002\x90\x00\x003d\x00\x0044\x00\x004\x94\x00\x005 \x00\x005\x80\x00\x005\xb8\x00\x006 \x00\x006\\\x00\x006\xbc\x00\x007H\x00\x007\xa8\x00\x008\f\x00\x008`\x00\x008\xb4\x00\x009L\x00\x009\xb4\x00\x00:h\x00\x00:\xec\x00\x00;\xc0\x00\x00<\x00\x00>\xe4\x00\x00?h\x00\x00?\xd8\x00\x00@H\x00\x00@\xbc\x00\x00A0\x00\x00A\xb8\x00\x00BX\x00\x00B\xf8\x00\x00Cd\x00\x00C\x9c\x00\x00DL\x00\x00D\xe4\x00\x00E\xb8\x00\x00F\x9c\x00\x00G0\x00\x00G\xdc\x00\x00H\xec\x00\x00I\x8c\x00\x00J8\x00\x00K\xac\x00\x00L\xe4\x00\x00Md\x00\x00N,\x00\x00N\x80\x00\x00N\xd4\x00\x00O\xb0\x00\x00P`\x00\x00P\xa8\x00\x00Q4\x00\x00Q\xa0\x00\x00R\f\x00\x00Rl\x00\x00S,\x00\x00S\x98\x00\x00T`\x00\x00U0\x00\x00W\xf0\x00\x00X\xdc\x00\x00Z\b\x00\x00[@\x00\x00[\x8c\x00\x00\\<\x00\x00\\\xf8\x00\x00]\x98\x00\x00^(\x00\x00^\xe4\x00\x00_\xa0\x00\x00`p\x00\x00b,\x00\x00b\xf4\x00\x00d\x04\x00\x00d\xec\x00\x00eP\x00\x00e\xd0\x00\x00f\xc4\x00\x00g`\x00\x00g\xa8\x00\x00iL\x00\x00i\xc0\x00\x00jD\x00\x00k\f\x00\x00k\xd4\x00\x00l\x80\x00\x00m@\x00\x00n,\x00\x00oL\x00\x00p\x84\x00\x00q\xa4\x00\x00r\xdc\x00\x00sx\x00\x00t\x10\x00\x00t\xa8\x00\x00uD\x00\x00{`\x00\x00|\x00\x00\x00|\xbc\x00\x00}\x10\x00\x00}\xa4\x00\x00~\x88\x00\x00\u007f\x94\x00\x00\x80\xbc\x00\x00\x81\x18\x00\x00\x81\x8c\x00\x00\x83H\x00\x00\x84\x14\x00\x00\x84\xd4\x00\x00\x85\xa8\x00\x00\x85\xe4\x00\x00\x86l\x00\x00\x87@\x00\x00\x88\x98\x00\x00\x89\xc0\x00\x00\x8b\x10\x00\x00\x8c\xc8\x00\x00\x8d\x8c\x00\x00\x8el\x00\x00\x8fH\x00\x00\x90 \x00\x00\x90\xc0\x00\x00\x91T\x00\x00\x92\f\x00\x00\x92H\x00\x00\x92\x84\x00\x00\x92\xc0\x00\x00\x92\xfc\x00\x00\x93`\x00\x00\x93\xc8\x00\x00\x94\x04\x00\x00\x94@\x00\x00\x94\xf0\x00\x00\x95\x80\x00\x00\x96$\x00\x00\x97\\\x00\x00\x98X\x00\x00\x99\x1c\x00\x00\x9aD\x00\x00\x9a\xb8\x00\x00\x9b\x98\x00\x00\x9c\xa0\x00\x00\x9dT\x00\x00\x9eX\x00\x00\x9e\xf8\x00\x00\x9f\x9c\x00\x00\xa0D\x00\x00\xa1P\x00\x00\xa2,\x00\x00\xa2\xa4\x00\x00\xa38\x00\x00\xa3\xa8\x00\x00\xa4d\x00\x00\xa5\\\x00\x00\xa8\x90\x00\x00\xab\b\x00\x00\xac\x1c\x00\x00\xac\xec\x00\x00\xad\x90\x00\x00\xad\xe8\x00\x00\xae\x80\x00\x00\xaf\x18\x00\x00\xaf\xb0\x00\x00\xb0H\x00\x00\xb0\xe0\x00\x00\xb1x\x00\x00\xb1\xcc\x00\x00\xb2 \x00\x00\xb2t\x00\x00\xb2\xc8\x00\x00\xb3X\x00\x00\xb3\xf4\x00\x00\xb4p\x00\x00\xb5\x00\x00\x00\xb5d\x00\x00\xb6\x1c\x00\x00\xb6\xd4\x00\x00\xb7\xb4\x00\x00\xb7\xf0\x00\x00\xb8x\x00\x00\xb9t\x00\x00\xb9\xf8\x00\x00\xba\xcc\x00\x00\xba\xcc\x00\x00\xba\xcc\x00\x00\xbb\xa8\x00\x00\xbc\x84\x00\x00\xbd@\x00\x00\xbe\x04\x00\x00\xbf\xc8\x00\x00\xc0\xc4\x00\x00\xc2\f\x00\x00\u008c\x00\x00\xc3\\\x00\x00\xc4 \x00\x00ļ\x00\x00\xc5\x10\x00\x00Ÿ\x00\x00Ɣ\x00\x00\xc80\x00\x00\xc8\xe0\x00\x00\xc9d\x00\x00\xc9\xcc\x00\x00ʨ\x00\x00ˀ\x00\x00\xcb\xe0\x00\x00\xcc\xf4\x00\x00͔\x00\x00\xcex\x00\x00\xce\xe8\x00\x00ϰ\x00\x00Ќ\x00\x00\xd1,\x00\x00ш\x00\x00\xd2\b\x00\x00҈\x00\x00\xd3\f\x00\x00ӌ\x00\x00\xd3\xec\x00\x00\xd48\x00\x00\xd5,\x00\x00՜\x00\x00\xd6`\x00\x00\xd6\xe8\x00\x00\xd7l\x00\x00\xd8H\x00\x00ش\x00\x00\xd9`\x00\x00\xd9\xc4\x00\x00\xdaT\x00\x00ڸ\x00\x00\xdb\x18\x00\x00۔\x00\x00\xdc@\x00\x00\xdc\xc8\x00\x00\xddl\x00\x00\xdd\xf0\x00\x00ބ\x00\x00\xdf\x18\x00\x00߬\x00\x00\xe0\xbc\x00\x00\xe1l\x00\x00\xe2p\x00\x00\xe3 \x00\x00\xe3\xe4\x00\x00\xe4\x80\x00\x00\xe5\xc8\x00\x00\xe6\xc0\x00\x00\xe7\x18\x00\x00\xe7\xec\x00\x00\xe8\xe4\x00\x00\xe9\xd8\x00\x00\xea\xd8\x00\x00\xeb\xd8\x00\x00\xec\xd4\x00\x00\xed\xd0\x00\x00\xee\xdc\x00\x00\xef\xe4\x00\x00\xf2\x04\x00\x00\xf3\xf4\x00\x00\xf4\x80\x00\x00\xf54\x00\x00\xf6\x10\x00\x00\xf6\x9c\x00\x00\xf7\x18\x00\x00\xf8X\x00\x00\xf8\xc0\x00\x00\xf9$\x00\x00\xfal\x00\x00\xfb\xbc\x00\x00\xfc(\x00\x00\xfc\xb8\x00\x00\xfd\f\x00\x00\xfd`\x00\x00\xfd\xb4\x00\x00\xfe\b\x00\x00\xfe\xb8\x00\x00\xff\b\x00\x01\x00\x14\x00\x01\x05\xb4\x00\x01\x06\xf4\x00\x01\a\xf8\x00\x01\b\xd0\x00\x01\td\x00\x01\n\x10\x00\x01\n\x98\x00\x01\v\x18\x00\x01\f\x04\x00\x01\f\xa4\x00\x01\r,\x00\x01\x0e\x00\x00\x01\x0f\x88\x00\x01\x11,\x00\x01\x11\xa0\x00\x01\x12\xcc\x00\x01\x138\x00\x01\x13\xe4\x00\x01\x14\x90\x00\x01\x15(\x00\x01\x15\xa4\x00\x01\x16X\x00\x01\x16\xfc\x00\x01\x17\xc0\x00\x01\x18\x84\x00\x01\x19x\x00\x01\x1a|\x00\x01\x1bT\x00\x01\x1c\xd4\x00\x01\x1d@\x00\x01\x1d\xd4\x00\x01\x1e\x90\x00\x01\x1f\x04\x00\x01\x1f|\x00\x01 \xa4\x00\x01!\xc0\x00\x01\"x\x00\x01#\b\x00\x01#l\x00\x01$\x04\x00\x01$\xcc\x00\x01'h\x00\x01(\xe8\x00\x01*L\x00\x01,T\x00\x01.L\x00\x011t\x00\x011\xf4\x00\x012\xe0\x00\x0130\x00\x013\xb0\x00\x014\xa8\x00\x015t\x00\x016T\x00\x017$\x00\x018\f\x00\x019H\x00\x01:\x10\x00\x01:\xf0\x00\x01;\x90\x00\x01<\x84\x00\x01<\xd8\x00\x01?X\x00\x01@\x1c\x00\x01A\xc0\x00\x01B\xc8\x00\x01C\xc8\x00\x01D\x9c\x00\x01EH\x00\x01FH\x00\x01Gp\x00\x01HH\x00\x01Ix\x00\x01J \x00\x01J\xe4\x00\x01K\xd4\x00\x01L\xa0\x00\x01M\x18\x00\x01N@\x00\x01P@\x00\x01Q\xa0\x00\x01R\xe0\x00\x01SD\x00\x01T \x00\x01UL\x00\x01V`\x00\x01V\xd4\x00\x01WX\x00\x01X4\x00\x01X\xa0\x00\x01Z\x04\x00\x01Z\x88\x00\x01[d\x00\x01[\xe0\x00\x01\\|\x00\x01]\xd8\x00\x01^\xa0\x00\x01`\x94\x00\x01aH\x00\x01a\xbc\x00\x01b\xf0\x00\x01cX\x00\x01d\xac\x00\x01et\x00\x01fh\x00\x01g\xdc\x00\x01h\xb4\x00\x01i\\\x00\x01jx\x00\x01n\x84\x00\x01p@\x00\x01s\xe0\x00\x01v\x10\x00\x01w\xc8\x00\x01x\x90\x00\x01y\x88\x00\x01z\x8c\x00\x01{h\x00\x01|\x8c\x00\x01}\x1c\x00\x01}\xa4\x00\x01\u007f\\\x00\x01\u007f\x98\x00\x01\u007f\xf8\x00\x01\x80l\x00\x01\x81t\x00\x01\x82\x90\x00\x01\x834\x00\x01\x83\xa4\x00\x01\x84\xc8\x00\x01\x85\xb0\x00\x01\x86\xa4\x00\x01\x88t\x00\x01\x89\x8c\x00\x01\x8a8\x00\x01\x8b8\x00\x01\x8b\xa0\x00\x01\x8eL\x00\x01\x8e\xa8\x00\x01\x8fT\x00\x01\x90\x10\x00\x01\x91\x14\x00\x01\x93\x90\x00\x01\x94\x14\x00\x01\x95\x04\x00\x01\x95\xfc\x00\x01\x96\xf8\x00\x01\x97\xa0\x00\x01\x99|\x00\x01\x9a\xc8\x00\x01\x9c\x10\x00\x01\x9d\b\x00\x01\x9d\xd8\x00\x01\x9e|\x00\x01\x9f\x18\x00\x01\x9f\xe8\x00\x01\xa0\xc4\x00\x01\xa2\f\x00\x01\xa34\x00\x01\xa4x\x00\x01\xa5\xb0\x00\x01\xa6\x80\x00\x01\xa7L\x00\x01\xa8\x1c\x00\x01\xa8\x90\x00\x01\xa8\xec\x00\x01\xa8\xec\x00\x01\xa8\xec\x00\x01\xa9X\x00\x01\xaa(\x00\x01\xab \x00\x01\xab\xcc\x00\x01\xac\xac\x00\x01\xad\xa8\x00\x01\xae \x00\x01\xae\x88\x00\x01\xaf\x04\x00\x01\xaf\xa8\x00\x01\xb0@\x00\x01\xb0\x88\x00\x01\xb6\xbc\x00\x01\xb7l\x00\x01\xb8\xe0\x00\x01\xb9t\x00\x01\xba\x04\x00\x01\xba\x94\x00\x01\xbb$\x00\x01\xbb\xa4\x00\x01\xbc\b\x00\x01\xbcx\x00\x01\xbdL\x00\x01\xbeL\x00\x01\xbe\xa4\x00\x01\xbf \x00\x01\xc0H\x00\x01\xc1\x18\x00\x01\xc1\xc4\x00\x01\xc3\x04\x00\x01\xc3\xe4\x00\x01Ġ\x00\x01\xc5T\x00\x01\xc6(\x00\x01\xc6\xec\x00\x01\xc8\f\x00\x01\xc9\f\x00\x01ʈ\x00\x01ˠ\x00\x01\xcc\xf8\x00\x01\xce\x1c\x00\x01ϔ\x00\x01\xd0l\x00\x01\xd1d\x00\x01\xd2\xdc\x00\x01\xd3P\x00\x01\xd3\xf8\x00\x01Մ\x00\x01\xd6x\x00\x01\xd7p\x00\x01\xd7\xfc\x00\x01\xd8\xf4\x00\x01ڬ\x00\x01\xdbT\x00\x01\xdcT\x00\x01\xdd\f\x00\x01\xdd\xf0\x00\x01ވ\x00\x01\xdfL\x00\x01\xe1\x80\x00\x01\xe2\xf8\x00\x01\xe4\x18\x00\x01\xe5\f\x00\x01\xe6<\x00\x01\xe7H\x00\x01\xe7\xa8\x00\x01\xe8$\x00\x01\xe8\xd4\x00\x01\xe9l\x00\x01\xea\x1c\x00\x01\xea\xd4\x00\x01\xeb\xe4\x00\x01\xec4\x00\x01\xec\xb8\x00\x01\xec\xf4\x00\x01\xed\xf0\x00\x01\xef\b\x00\x01\xef\xa4\x00\x01\xf0\x04\x00\x01\xf0\xcc\x00\x01\xf1 \x00\x01\xf2P\x00\x01\xf3l\x00\x01\xf3\xe8\x00\x01\xf5\f\x00\x01\xf6,\x00\x01\xf6\xc0\x00\x01\xf7x\x00\x01\xf7\xe0\x00\x01\xf8p\x00\x01\xf9,\x00\x01\xfax\x00\x01\xfbt\x00\x01\xfc\f\x00\x01\xfcd\x00\x01\xfd\f\x00\x01\xfd\x8c\x00\x01\xfe4\x00\x01\xff\b\x00\x01\xff\xd0\x00\x02\x014\x00\x02\x02\x1c\x00\x02\x03,\x00\x02\x04h\x00\x02\x05\xd4\x00\x02\aP\x00\x02\t4\x00\x02\n\xd4\x00\x02\f\xe0\x00\x02\r\xf0\x00\x02\x0f\x18\x00\x02\x104\x00\x02\x11\xe4\x00\x02\x13<\x00\x02\x14,\x00\x02\x15,\x00\x02\x164\x00\x02\x170\x00\x02\x188\x00\x02\x19$\x00\x02\x1a\x88\x00\x02\x1b8\x00\x02\x1d\xb4\x00\x02\x1eT\x00\x02\x1e\xcc\x00\x02 |\x00\x02!h\x00\x02\"\xac\x00\x02$L\x00\x02%0\x00\x02&H\x00\x02'\x88\x00\x02(\xf4\x00\x02)\x8c\x00\x02*0\x00\x02*\xdc\x00\x02+\x94\x00\x02,\xdc\x00\x02.$\x00\x02.\xec\x00\x020\xec\x00\x021\x84\x00\x022@\x00\x022\xfc\x00\x023\xb8\x00\x024t\x00\x025$\x00\x026\xf4\x00\x029 \x00\x02:\x8c\x00\x02:\xd4\x00\x02;\f\x00\x02;\x88\x00\x02<(\x00\x02<\xd8\x00\x02=4\x00\x02?\xb8\x00\x02@\x98\x00\x02A\xe0\x00\x02C\xa0\x00\x02D\xfc\x00\x02F\x98\x00\x02H`\x00\x02H\xf4\x00\x02I\xcc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02\x00p\x00\x00\x03\x10\x06\x00\x00\x03\x00\a\x00\x007!\x11!\x03\x11!\x11\xe0\x01\xc0\xfe@p\x02\xa0p\x05 \xfap\x06\x00\xfa\x00\x00\x00\x00\x00\x01\x00]\xff\x00\x06\xa3\x05\x80\x00\x1d\x00\x00\x01\x14\a\x01\x11!2\x16\x14\x06#!\"&463!\x11\x01&54>\x013!2\x1e\x01\x06\xa3+\xfd\x88\x01@\x1a&&\x1a\xfc\x80\x1a&&\x1a\x01@\xfd\x88+$(\x17\x05\x80\x17($\x05F#+\xfd\x88\xfd\x00&4&&4&\x03\x00\x02x+#\x17\x1b\b\b\x1b\x00\x00\x01\x00\x00\xff\x00\x06\x00\x05\x80\x00+\x00\x00\x01\x11\x14\x0e\x02\".\x024>\x0232\x17\x11\x05\x11\x14\x0e\x02\".\x024>\x0232\x17\x11467\x01632\x16\x06\x00DhgZghDDhg-iW\xfd\x00DhgZghDDhg-iW&\x1e\x03@\f\x10(8\x05 \xfb\xa02N+\x15\x15+NdN+\x15'\x02\x19\xed\xfd;2N+\x15\x15+NdN+\x15'\x03\xc7\x1f3\n\x01\x00\x048\x00\x02\x00\x00\xff\x00\x06\x80\x05\x80\x00\a\x00!\x00\x00\x00\x10\x00 \x00\x10\x00 \x01\x14\x06#\"'\x01\x06#\"$&\x02\x10\x126$ \x04\x16\x12\x15\x14\a\x01\x16\x04\x80\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x03\aL46$\xfe\xa9\xb3\u070f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdo|\x01W%\x02\a\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\xfe\x804L&\x01V|o\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\xfe\xfb\x8fܳ\xfe\xa9%\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x00\x00\x1a\x00=\x00M\x00\x00%\x11\x06\a\x04\a\x0e\x02+\x02\".\x01'&%&'\x11\x14\x163!26\x11<\x02.\x03#!\"\x06\x15\x14\x17\x16\x17\x1e\x04;\x022>\x03767>\x017\x11\x14\x06#!\"&5\x11463!2\x16\x06\x80 %\xfe\xf4\x9e3@m0\x01\x010m@3\x9e\xfe\xf4% \x13\r\x05\xc0\r\x13\x01\x05\x06\f\b\xfa@\r\x13\x93\xc1\xd0\x06:\"7.\x14\x01\x01\x14.7\":\x06\xd0\xc16]\x80^B\xfa@B^^B\x05\xc0B^ \x03\x00$\x1e΄+0110+\x84\xce\x1e$\xfd\x00\r\x13\x13\x04(\x02\x12\t\x11\b\n\x05\x13\r\xa8t\x98\xa5\x051\x1a%\x12\x12%\x1a1\x05\xa5\x98+\x91`\xfb\xc0B^^B\x04@B^^\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00\x1c\x00\x00\x04\"'\x01.\x0454632\x1e\x02\x17>\x0332\x16\x15\x14\a\x01\x03\x9a4\x12\xfd\x90\n#L\x81oP$$Po\x81>\xe0\xfe\xe5\xfd\x91\x80\x12\x02Z\b$_d\x8eC\xdc\xf8+I@$$@I+\xf8\xdc\xdd\xe5\xfd\xa8\x00\x00\x01\x00\x00\xff\xad\x06\x80\x05\xe0\x00\"\x00\x00\x01\x14\a\x01\x13\x16\x15\x14\x06#\"'%\x05\x06#\"&547\x13\x01&547%\x1362\x17\x13\x05\x16\x06\x80\x1a\xfe\x95V\x01\x15\x14\x13\x15\xfe?\xfe?\x16\x12\x15\x15\x02V\xfe\x94\x198\x01\xf6\xe1\x13<\x13\xe1\x01\xf68\x03y\x16\x1a\xfe\x9e\xfe\f\a\r\x15\x1d\f\xec\xec\f\x1d\x15\x06\x0e\x01\xf4\x01b\x1b\x15%\tI\x01\xc7))\xfe9I\t\x00\x00\x00\x00\x02\x00\x00\xff\xad\x06\x80\x05\xe0\x00\t\x00+\x00\x00\t\x01%\v\x01\x05\x01\x03%\x05\x01\x14\a\x01\x13\x16\x15\x14#\"'%\x05\x06#\"&547\x13\x01&547%\x1362\x17\x13\x05\x16\x04q\x012\xfeZ\xbd\xbd\xfeZ\x012I\x01z\x01y\x01\xc7\x1a\xfe\x95V\x01)\x13\x15\xfe?\xfe?\x16\x12\x15\x15\x02V\xfe\x94\x198\x01\xf6\xe1\x13<\x13\xe1\x01\xf68\x02\x14\x01)>\x01~\xfe\x82>\xfe\xd7\xfe[\xc7\xc7\x03\n\x16\x1a\xfe\x9e\xfe\f\a\r2\f\xec\xec\f\x1d\x15\x06\x0e\x01\xf4\x01b\x1b\x15%\tI\x01\xc7))\xfe9I\t\x00\x00\x02\x00\x00\xff\x80\x05\x00\x05\x80\x00\x15\x00\x1d\x00\x00%\x14\x06#!\"&54>\x033\x16 72\x1e\x03\x00\x10\x06 &\x106 \x05\x00}X\xfc\xaaX}\x11.GuL\x83\x01l\x83LuG.\x11\xff\x00\xe1\xfe\xc2\xe1\xe1\x01>\x89m\x9c\x9cmU\x97\x99mE\x80\x80Em\x99\x97\x03\xc1\xfe\xc2\xe1\xe1\x01>\xe1\x00\x00\x00\v\x00\x00\xff\x00\a\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\x00\x0554&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x01267\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\x00&\x1a\xfd\x00\x1a&&\x1a\x03\x00\x1a&\xfc\x00&\x1a\x80\x1a&&\x1a\x80\x1a&\x05\x80&\x1a\x80\x1a&&\x1a\x80\x1a&\xfe\x80&\x1a\xfd\x00\x1a&&\x1a\x03\x00\x1a&\x01\x80&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&\x80^B\xf9\xc0B^^B\x06@B^@\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\xfd\x1a\x02\x00\x1a&&\x1a\xfe\x00\x1a&&\x04\x9a\x80\x1a&&\x1a\x80\x1a&&\xfb\x9a\x80\x1a&&\x1a\x80\x1a&&\x03\x1a\x02\x00\x1a&&\x1a\xfe\x00\x1a&&\xfe\x9a\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\xba\xfa\xc0B^^B\x05@B^^\x00\x04\x00\x00\x00\x00\x06\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x01\x11\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x03\x00L4\xfe\x004LL4\x02\x004LL4\xfe\x004LL4\x02\x004L\x03\x80L4\xfe\x004LL4\x02\x004LL4\xfe\x004LL4\x02\x004L\x02\x00\xfe\x804LL4\x01\x804LL\x02\xcc\xfe\x804LL4\x01\x804LL\xfc\xcc\xfe\x804LL4\x01\x804LL\x02\xcc\xfe\x804LL4\x01\x804LL\x00\t\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x02\x008(\xfe\xc0(88(\x01@(88(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(8\xfd\x808(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(8\xfd\x808(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(88(\xfe\xc0(88(\x01@(8\x01 \xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x03\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x03\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x00\x00\x06\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x02\x008(\xfe\xc0(88(\x01@(88(\xfe\xc0(88(\x01@(8\x05\x008(\xfc@(88(\x03\xc0(8\xfb\x008(\xfe\xc0(88(\x01@(8\x05\x008(\xfc@(88(\x03\xc0(88(\xfc@(88(\x03\xc0(8\x01 \xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x03\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x00\x00\x00\x01\x00y\x00\x0e\x06\x87\x04\xb2\x00\x16\x00\x00\x00\x14\a\x01\a\x06\"/\x01\x01&4?\x0162\x17\t\x0162\x1f\x01\x06\x87\x1c\xfd,\x88\x1cP\x1c\x88\xfe\x96\x1c\x1c\x88\x1cP\x1c\x01&\x02\x90\x1cP\x1c\x88\x03\xf2P\x1c\xfd,\x88\x1c\x1c\x88\x01j\x1cP\x1c\x88\x1c\x1c\xfe\xd9\x02\x91\x1c\x1c\x88\x00\x01\x00n\xff\xee\x05\x12\x04\x92\x00#\x00\x00$\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\t\x0162\x1f\x01\x16\x14\a\t\x01\x05\x12\x1c\x88\x1cP\x1c\xfe\xda\xfe\xda\x1cP\x1c\x88\x1c\x1c\x01&\xfe\xda\x1c\x1c\x88\x1cP\x1c\x01&\x01&\x1cP\x1c\x88\x1c\x1c\xfe\xda\x01&\xfeP\x1c\x88\x1c\x1c\x01&\xfe\xda\x1c\x1c\x88\x1cP\x1c\x01&\x01&\x1cP\x1c\x88\x1c\x1c\xfe\xda\x01&\x1c\x1c\x88\x1cP\x1c\xfe\xda\xfe\xda\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00#\x00+\x00D\x00\x00\x01\x15\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x01546;\x012\x16\x1d\x0132\x1e\x01\x10\x00 \x00\x10\x00 \x00\x14\x06#\"'\x01\x06#\"$&\x02\x10\x126$ \x04\x16\x12\x15\x14\a\x01\x04\x00\x13\r\xe0\x13\r@\r\x13\xe0\r\x13\x13\r\xe0\x13\r@\r\x13\xe0\r\x13\x80\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x03\aK56$\xfe\xa9\xb3\u070f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdo|\x01W\x02\xe0@\r\x13\xe0\r\x13\x13\r\xe0\x13\r@\r\x13\xe0\r\x13\x13\r\xe0\x13\xe6\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\xfe\xb5jK&\x01V|o\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\xfe\xfb\x8fܳ\xfe\xa9\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00\x0f\x00\x17\x000\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x1e\x01\x10\x00 \x00\x10\x00 \x00\x14\x06#\"'\x01\x06#\"$&\x02\x10\x126$ \x04\x16\x12\x15\x14\a\x01\x04\x00\x13\r\xfd\xc0\r\x13\x13\r\x02@\r\x13\x80\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x03\aK56$\xfe\xa9\xb3\u070f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdo|\x01W\x02\xe0@\r\x13\x13\r@\r\x13\x13\xe6\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\xfe\xb5jK&\x01V|o\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\xfe\xfb\x8fܳ\xfe\xa9\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x06\x00\x00)\x005\x00\x00\x01\x14\x02\x06\x04 $&\x0254\x1276\x16\x17\x16\x06\a\x0e\x01\x15\x14\x1e\x022>\x0254&'.\x017>\x01\x17\x16\x12\x01\x11\x14\x06\"&5\x11462\x16\x06\x00z\xce\xfe\xe4\xfe\xc8\xfe\xe4\xcez\xa1\x92+i\x1f \x0f*bkQ\x8a\xbdн\x8aQkb*\x0f \x1fj*\x92\xa1\xfd\x80LhLLhL\x02\x80\x9c\xfe\xe4\xcezz\xce\x01\x1c\x9c\xb6\x01Bm \x0e+*i J\xd6yh\xbd\x8aQQ\x8a\xbdhy\xd6J i*+\x0e m\xfe\xbe\x02J\xfd\x804LL4\x02\x804LL\x00\x00\x00\x00\x05\x00\x00\xff\x80\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x11\x14\x06+\x01\"&5\x1146;\x012\x16%\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x01\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x01\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x01\x00\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12`\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12r\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\xf2\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x01r\xfc@\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x12\x01\xf2\xfa@\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00n\x00\x00\x004&\"\x06\x14\x162\x01\x15\x14\x06\x0f\x01\x06\a\x16\x17\x16\x14\a\x0e\x01#\"/\x01\x06\a\x06\a\x06+\x01\"&/\x01&'\a\x06#\"'&'&547>\x017&/\x01.\x01=\x0146?\x0167&'&547>\x0132\x1f\x0167676;\x012\x16\x1f\x01\x16\x177632\x17\x16\x17\x16\x15\x14\a\x0e\x01\a\x16\x1f\x01\x1e\x01\x04\x00\x96Ԗ\x96\xd4\x02\x96\x10\f\xb9\x13\x14#H\n\t\x1b\x90\x16\f\x0e\x8a,/\x10\r\a\x1d\xde\x0e\x15\x01\x1c1)\x8d\n\x0f\x0e\v~'\a\b\x0fH\x12\x1b\x0e\xb7\r\x10\x10\v\xba\x0e\x19(C\n\t\x1a\x91\x16\r\r\x8a,/\x10\r\a\x1d\xde\x0e\x15\x01\x1c1)\x8e\t\x0f\r\f\x81$\a\b\x0fH\x12\x1a\x0f\xb7\r\x10\x02\x16Ԗ\x96Ԗ\x01m\xde\f\x16\x02\x1c6%2X\f\x1a\n%\x8e\tl\x17\x0f\x882\x1c\x11\r\xb8\x10\x15k\t\vr6\n\r\f\v\x15[\x1921\x1b\x02\x15\r\xde\f\x16\x02\x1c..9Q\f\f\n\r$\x8f\nk\x17\x0f\x882\x1c\x11\r\xb8\x10\x15k\t\nw3\b\x0e\f\v\x15[\x1920\x1c\x02\x15\x00\x00\x06\x00\x00\xff\x80\x05\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00;\x00C\x00g\x00\x00\x01\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x05\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x05\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x13\x11!\x11\x14\x1e\x013!2>\x01\x01!'&'!\x06\a\x05\x15\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&=\x01463!7>\x013!2\x16\x1f\x01!2\x16\x02\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x80\xfc\x80\x0e\x0f\x03\x03@\x03\x0f\x0e\xfd`\x01\xc00\a\n\xfe\xc3\n\a\x03o\x12\x0e`^B\xfc\xc0B^`\x0e\x12\x12\x0e\x015F\x0fN(\x01@(N\x0fF\x015\x0e\x12\x03 \xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\xfd\x1e\x03\xb4\xfcL\x16%\x11\x11%\x04Ju\t\x02\x02\t\x95@\x0e\x12\xfcLSyuS\x03\xb8\x12\x0e@\x0e\x12\xa7%44%\xa7\x12\x00\x00\x00\x00\x02\x00\x1a\x00\x00\x06f\x05\x03\x00\x13\x005\x00\x00\x01\x11\x14\x06#!\x11!\x11!\"&5\x11465\t\x01\x167\a\x06\a#\"'\t\x01\x06'&/\x01&67\x0162\x1f\x01546;\x012\x16\x15\x11\x17\x1e\x01\x05\x80&\x1a\xfe\x80\xff\x00\xfe\x80\x1a&\x01\x02?\x02?\x01\xdf>\b\r\x03\r\b\xfdL\xfdL\f\f\r\b>\b\x02\n\x02\xcf X \xf4\x12\x0e\xc0\x0e\x12\xdb\n\x02\x02 \xfe \x1a&\x01\x80\xfe\x80&\x1a\x01\xe0\x01\x04\x01\x01\xda\xfe&\x02AJ\t\x02\a\x02A\xfd\xbf\b\x01\x02\tJ\n\x1b\b\x02W\x1a\x1a\xcc\xc3\x0e\x12\x12\x0e\xfeh\xb6\b\x1b\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00,\x00\x00\x01\x11\x14\x06#!\"&=\x0146;\x01\x1146;\x012\x16\x00\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x80\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x01\xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03\xe0\xfe@\x0e\x12\x12\x0e@\x0e\x12\x01`\x0e\x12\x12\xfd\xfe\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x002\x00\x00\aN\x05\x00\x00\x11\x00C\x00\x00\x015\x03.\x01+\x01\"\x06\a\x03\x15\x06\x16;\x0126\x01\x14#!26'\x03.\x01#!\"\x06\a\x03\x06\x163!\"547\x01>\x013!\"\x06\x0f\x01\x06\x16;\x0126/\x01.\x01#!2\x16\x17\x01\x16\x04W\x18\x01\x14\r\xba\r\x14\x01\x18\x01\x12\f\xf4\f\x12\x02\xf6.\xfd@\r\x12\x01\x14\x01\x14\r\xfe\xf0\r\x14\x01\x14\x01\x12\r\xfd@.\x1a\x01\xa1\b$\x14\x01S\r\x14\x01\x0f\x01\x12\r\xa6\r\x12\x01\x0f\x01\x14\r\x01S\x14$\b\x01\xa1\x1a\x02\x1c\x04\x01@\r\x13\x13\r\xfe\xc0\x04\f\x10\x10\xfe9I\x13\r\x01\x00\r\x13\x13\r\xff\x00\r\x13I6>\x04\x14\x13\x1c\x13\r\xc0\x0e\x12\x12\x0e\xc0\r\x13\x1c\x13\xfb\xec>\x00\x04\x00\x00\x00\x00\x06\x80\x06\x00\x00\a\x00\x0f\x00%\x00=\x00\x00$4&\"\x06\x14\x162$4&\"\x06\x14\x162\x13\x11\x14\x06#!\"&5\x11463!\x17\x162?\x01!2\x16\x01\x16\a\x01\x06\"'\x01&763!\x11463!2\x16\x15\x11!2\x05\x00&4&&4\x01&&4&&4\xa68(\xfa@(88(\x01ч:\x9c:\x88\x01\xd0(8\xfe\xbb\x11\x1f\xfe@\x126\x12\xfe@\x1f\x11\x11*\x01\x00&\x1a\x01\x00\x1a&\x01\x00*\xa64&&4&&4&&4&\x01 \xfe\xc0(88(\x01@(8\x8888\x888\x02\x11)\x1d\xfe@\x13\x13\x01\xc0\x1d)'\x01\xc0\x1a&&\x1a\xfe@\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x18\x00$\x000\x00\x00\x01\x14\a\x01\x06\"'\x01&76;\x01\x1146;\x012\x16\x15\x1132\x16\x02 \x0e\x01\x10\x1e\x01 >\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04`\n\xfe\xc1\v\x18\v\xfe\xc0\x0f\b\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\xcc\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02`\f\f\xfe\xc1\t\t\x01@\x10\x13\x14\x01`\x0e\x12\x12\x0e\xfe\xa0\x12\x022\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x18\x00$\x000\x00\x00\x01\x06+\x01\x11\x14\x06+\x01\"&5\x11#\"&547\x0162\x17\x01\x16\x02 \x0e\x01\x10\x1e\x01 >\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04^\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\n\x01?\v\x18\v\x01@\x0f\xd2\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\x94\x14\xfe\xa0\x0e\x12\x12\x0e\x01`\x12\x0e\f\f\x01?\t\t\xfe\xc0\x10\x01\xf9\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\x00\x00\x06\x00\x05\x00\x00\r\x00#\x00\x00\x01!.\x01'\x03!\x03\x0e\x01\a!\x17!%\x11\x14\x06#!\"&5\x1147\x13>\x013!2\x16\x17\x13\x16\x03\xff\x01<\x01\x03\x01\xd4\xfd<\xd4\x01\x03\x01\x01<_\x01@\x02`&\x1a\xfa\x80\x1a&\x19\xee\n5\x1a\x03@\x1a5\n\xee\x19\x02@\x03\v\x02\x01\xf0\xfe\x10\x03\v\x02\xc0\xa2\xfe\x1e\x1a&&\x1a\x01\xe2>=\x02(\x19\"\"\x19\xfd\xd8=\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00'\x00\x00\x00\x14\a\x01\x06#\"'&5\x11476\x17\x01\x16\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xa0 \xfd\xe0\x0f\x11\x10\x10 !\x1f\x02 \xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xa5J\x12\xfe\xc0\t\b\x13%\x02\x80%\x13\x12\x13\xfe\xc0\xcb\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x003\x00\x00\x01\x11\x14\x06#!\"'&?\x01&#\"\x0e\x02\x14\x1e\x023267672\x1f\x01\x1e\x01\a\x06\x04#\"$&\x02\x10\x126$32\x04\x1776\x17\x16\x06\x00&\x1a\xfe@*\x11\x11\x1f\x8a\x94\xc9h\xbd\x8aQQ\x8a\xbdhw\xd4I\a\x10\x0f\n\x89\t\x01\bm\xfeʬ\x9c\xfe\xe4\xcezz\xce\x01\x1c\x9c\x93\x01\x13k\x82\x1d)'\x05\x00\xfe@\x1a&('\x1e\x8a\x89Q\x8a\xbdн\x8aQh_\n\x02\t\x8a\b\x19\n\x84\x91z\xce\x01\x1c\x018\x01\x1c\xcezoe\x81\x1f\x11\x11\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00$\x00G\x00\x00\x01\x14\a\x02\x00!\"$'\a\x06\"&5\x11463!2\x16\x14\x0f\x01\x1e\x013267676;\x012\x16\x13\x11\x14\x06#!\"&4?\x01&#\"\x06\a\x06\a\x06+\x01\"&=\x01\x12\x00!2\x04\x17762\x16\x05\xe7\x01@\xfeh\xfe\xee\x92\xfe\xefk\x81\x134&&\x1a\x01\xc0\x1a&\x13\x89G\xb4a\x86\xe8F\v*\b\x16\xc0\r\x13\x19&\x1a\xfe@\x1a&\x13\x8a\x94Ɇ\xe8F\v*\b\x16\xc7\r\x13A\x01\x9a\x01\x13\x92\x01\x14k\x82\x134&\x01\xe0\x05\x02\xfe\xf4\xfe\xb3nf\x81\x13&\x1a\x01\xc0\x1a&&4\x13\x89BH\x82r\x11d\x17\x13\x03\x13\xfe@\x1a&&4\x13\x8a\x89\x82r\x11d\x17\x13\r\a\x01\f\x01Moe\x81\x13&\x00\x00\x00\x00\b\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x04\x80\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x80\x13\r\xfa@\r\x13\x13\r\x05\xc0\r\x13\x80^B\xfa@B^^B\x05\xc0B^\x01`@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd3\x03@\r\x13\x13\r\xfc\xc0\r\x13\x13\x04M\xfb\xc0B^^B\x04@B^^\x00\x02\x00\x00\x00\x00\x04\x80\x05\x80\x00\a\x00\x1f\x00\x00\x01!54&\"\x06\x15\x01\x11\x14\x06#!\"&5\x1146;\x0154\x00 \x00\x1d\x0132\x16\x01@\x02\x00\x96Ԗ\x03@8(\xfc@(88( \x01\b\x01p\x01\b (8\x03\x00\xc0j\x96\x96j\xfe\xe0\xfd\xc0(88(\x02@(8\xc0\xb8\x01\b\xfe\xf8\xb8\xc08\x00\x00\x02\x00@\xff\x80\a\x00\x05\x80\x00\x11\x007\x00\x00\x01\x14\a\x11\x14\x06+\x01\"&5\x11&5462\x16\x05\x11\x14\x06\a\x06#\".\x02#\"\x05\x06#\"&5\x114767632\x16\x17\x1632>\x0232\x16\x01@@\x13\r@\r\x13@KjK\x05\xc0\x19\x1bך=}\\\x8bI\xc0\xfe\xf0\x11\x10\x1a&\x1f\x15:\xec\xb9k\xba~&26\u007f]S\r\x1a&\x05\x00H&\xfb\x0e\r\x13\x13\r\x04\xf2&H5KKu\xfd\x05\x19\x1b\x0et,4,\x92\t&\x1a\x02\xe6 \x17\x0e\x1dx:;\x13*4*&\x00\x00\x00\x01\x00\x00\x00\x00\x06\x80\x05\x80\x00K\x00\x00\x01\x14\x0f\x02\x0e\x01#\x15\x14\x06+\x01\"&5\x1146;\x012\x16\x1d\x012\x16\x177654\x02$ \x04\x02\x15\x14\x1f\x01>\x013546;\x012\x16\x15\x11\x14\x06+\x01\"&=\x01\"&/\x02&54\x126$ \x04\x16\x12\x06\x80<\x14\xb9\x16\x89X\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12Gv\"D\x1d\xb0\xfe\xd7\xfe\xb2\xfeװ\x1dD\"vG\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12X\x89\x16\xb9\x14<\x86\xe0\x014\x01L\x014\xe0\x86\x02\x8a\xa6\x941!Sk \x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e G<\f_b\x94\x01\x06\x9c\x9c\xfe\xfa\x94b_\f\x034.\x0354632\x17\x16\x03\x00&4\x13\xfe\xb3\xfe\xfa\x1a&&\x1a\x01\x06\x01M\x134&\x01\x80UF\n\x0f\x1a&\x18\"\"\x18\x18\"\"\x18&\x1a\x0f\nF\x04\xa0\xfb\xc0\x1a&\x13\x01M&\x1a\x01\x80\x1a&\x01M\x13&\xfe\x12\x98\x83\x1c\x05%\x1b\x15\x1d\x15\x19/B/\x19\x15\x1d\x15\x1b%\x05\x1b\x00\x00\x00\x00\x04\x00\x00\xff\xb9\x06\x80\x05G\x00\x13\x00-\x00I\x00k\x00\x00\x01\x11\x14\x06\"'\x01!\"&5\x11463!\x0162\x16\x00\x14\x06\a\x06#\"&54>\x034.\x0354632\x17\x16\x04\x10\x02\a\x06#\"&54767>\x014&'&'&54632\x17\x16\x04\x10\x02\a\x06#\"&547>\x017676\x12\x10\x02'&'.\x01'&54632\x17\x16\x03\x00&4\x13\xfe\xb3\xfe\xfa\x1a&&\x1a\x01\x06\x01M\x134&\x01\x80UF\n\x0f\x1a&\x18\"\"\x18\x18\"\"\x18&\x1a\x0f\nF\x01U\xaa\x8c\r\f\x1b&'8\x14JSSJ\x148'&\x1a\r\r\x8c\x01\xaa\xfe\xd3\r\r\x1a&'\a\x1f\a.${\x8a\x8a{$.\a\x1f\a'&\x1a\r\r\xd3\x04\xa0\xfb\xc0\x1a&\x13\x01M&\x1a\x01\x80\x1a&\x01M\x13&\xfe\x12\x98\x83\x1c\x05%\x1b\x15\x1d\x15\x19/B/\x19\x15\x1d\x15\x1b%\x05\x1b7\xfe\xce\xfe\xfd;\x05&\x1a'\x14\x1d\x0f6\xa3\xb8\xa36\x0f\x1d\x14'\x1a&\x05;\xb6\xfe4\xfe\u007f[\x05&\x1a$\x17\x04\r\x04\x19\x1a[\x01\x10\x012\x01\x10[\x1a\x19\x04\r\x04\x17$\x1a&\x05[\x00\f\x00\x00\x00\x00\x05\x80\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x1f\x00#\x00/\x003\x007\x00\x00\x01\x15#5\x13\x15#5!\x15#5\x01!\x11!\x11!\x11!\x01!\x11!\x01\x11!\x11\x01\x15#5!\x15#5\x13\x11!5#\x11#\x11!\x1535\x01\x11!\x11!\x11!\x11\x01\x80\x80\x80\x80\x03\x80\x80\xfc\x80\x01\x80\xfe\x80\x01\x80\xfe\x80\x03\x00\x01\x80\xfe\x80\xff\x00\xfd\x80\x04\x80\x80\x01\x80\x80\x80\xfe\x80\x80\x80\x01\x80\x80\xfd\x80\xfd\x80\x05\x80\xfd\x80\x01\x80\x80\x80\x03\x00\x80\x80\x80\x80\xfc\x01\x01\u007f\x01\x80\x01\x80\xfe\x80\x01\x80\xfd\x80\xfd\x80\x02\x80\xfe\x00\x80\x80\x80\x80\x02\x00\xfe\x80\x80\xfe\x80\x02\x80\x80\x80\x03\x00\xfd\x80\x02\x80\xfd\x80\x02\x80\x00\x00\x00\x00\x10\x00\x00\x00\x00\a\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x1f\x00#\x00'\x00+\x00/\x003\x007\x00;\x00?\x00\x003#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113???? ^\x1f\x1f\x9d\x1f\x1f\x9d>>~\x1f\x1f?\x1f\x1f?\x1f\x1f\x9d??\x9d??~??~??^??\xbd^^? ^??\x05\x80\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x80\x05\x80\x00\x00\x00\x02\x00\x00\xff\x95\x05\xeb\x05\x80\x00\a\x00\x1d\x00\x00\x004&\"\x06\x14\x162\x01\x14\a\x01\x06#\"'\x01.\x015\x11463!2\x16\x17\x01\x16\x01\xc0KjKKj\x04v%\xfe\x15'45%\xfd5&5L4\x01\xa05\x80&\x02\xcb%\x04\vjKKjK\xfe@5%\xfe\x14%%\x02\xcc%\x805\x01\xa04L5&\xfd6'\x00\x00\x00\x00\x03\x00\x00\xff\x95\ak\x05\x80\x00\a\x00\x1d\x005\x00\x00\x004&\"\x06\x14\x162\x01\x14\a\x01\x06#\"'\x01.\x015\x11463!2\x16\x17\x01\x16\x05\x14\a\x01\x06#\"&'\x01654'\x01.\x01#32\x16\x17\x01\x16\x01\xc0KjKKj\x04v%\xfe\x15'45%\xfd5&5L4\x01\xa05\x80&\x02\xcb%\x01\x80%\xfe\x15'4$.\x1e\x01\xd6%%\xfd5&\x805\xe05\x80&\x02\xcb%\x04\vjKKjK\xfe@5%\xfe\x14%%\x02\xcc%\x805\x01\xa04L5&\xfd6'45%\xfe\x14%\x1c\x1f\x01\xd6%54'\x02\xca&55&\xfd6'\x00\x03\x00\n\xff\x80\x06y\x05\x80\x00T\x00d\x00t\x00\x00\x01\x16\a\x01\x0e\x01#!\"&'&74676&7>\x027>\x0176&7>\x017>\x0176&7>\x017>\x0176&7>\x027>\x06\x17\a63!2\x16\a\x01\x0e\x01#!\"\a\x06\x17\x163!267\x016'\x16\x05\x06\x163!26?\x016&#!\"\x06\a\x03\x06\x163!26?\x016&#!\"\x06\a\x06g(\x16\xfe\xed\x13sA\xfceM\x8f\x1c\x18\x16\x06\x01\x01\b\x01\x02\f\x15\x06\x17,\b\x03\x05\x02\x03\x1c\x03\x15*\x04\x01\a\x04\x04$\x04\x13/\x04\x01\b\x02\x02\x0e\x16\x06\b\x11\r\x13\x14!'\x1c\x01&\r\x02\xf9JP\x16\xfe\xee$G]\xfc\x9b\x1b\v\v\n\x18x\x03\x9b\x1d6\b\x01,\a\x02&\xfb\xed\x04\f\x0e\x02`\r\x19\x04\x15\x04\f\x0e\xfd\xa0\r\x19\x04h\x04\f\x0e\x02`\r\x19\x04\x15\x04\f\x0e\xfd\xa0\r\x19\x04\x04\"9H\xfcv@WkNC<\x04.\x0e\b\x1b\x06\v\x14\x1b\n&k&\n(\b\v\"\x06$p\"\t.\x05\r#\x05\x1au&\b#\t\b\x14\x1a\b\f%!'\x19\x16\x01\x06\x03\tpJ\xfcvwE\x0f\x10\x1bF\x1f\x1a\x03\xdb\x16#\x0f\x1e\r\x13\x13\r@\r\x13\x13\r\xfe\xc0\r\x13\x13\r@\r\x13\x13\r\x00\x00\x01\x00\x00\xff\x97\x05\x00\x05\x80\x00\x1c\x00\x00\x012\x17\x1e\x01\x15\x11\x14\x06\a\x06#\"'\t\x01\x06#\"'.\x015\x1146763\x04\x8c\x17\x15!''!\x13\x190#\xfeG\xfeG$/\x17\x15!''!\x15\x17\x05\x80\t\r8\"\xfa\xf7\"8\r\b \x01\xa8\xfeX!\t\r8\"\x05\t\"8\r\t\x00\x00\x00\x00\x04\x00\x00\xff\x80\x06\x80\x05\x80\x00\x03\x00\f\x00\x14\x00<\x00\x00)\x01\x11!\x11!\x11#\"&=\x01!\x004&\"\x06\x14\x1627\x11\x14\x06+\x01\x15\x14\x06#!\"&=\x01#\"&5\x1146;\x01\x11463!2\x16\x1f\x01\x1e\x01\x15\x1132\x16\x01\x80\x03\x80\xfc\x80\x03\x80\xa0(8\xfd\x80\x04\x80&4&&4\xa6\x13\r\xe08(\xfc@(8\xe0\r\x13qO@8(\x02\xa0(`\x1c\x98\x1c(@Oq\x01\x00\x01\x80\x01\x808(\xa0\xfd&4&&4&@\xfe`\r\x13\xa0(88(\xa0\x13\r\x01\xa0Oq\x02 (8(\x1c\x98\x1c`(\xff\x00q\x00\x03\x00\x00\xff\x80\a\x80\x06\x00\x00\a\x00!\x00)\x00\x00\x002\x16\x14\x06\"&4\x012\x16\x15\x11\x14\x06#!\"&5\x1146;\x017>\x013!2\x16\x1f\x01\x00 \x00\x10\x00 \x00\x10\x03I\uea69\xee\xa9\x03\xe0j\x96\x96j\xfa\x80j\x96\x96j\xe03\x13e5\x02\x005e\x133\xfdg\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x03`\xa9\uea69\xee\x02I\x96j\xfc\x80j\x96\x96j\x03\x80j\x96\x881GG1\x88\xfb\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x80\x05\x80\x00\a\x00P\x00\x00\x01\x032\x16327&\x017>\x047\x13\x01;\x01\x16\x17\x13\x16\x12\x17\x1e\x01\x17\x16\x17\x1e\x01\x17\x16\x15\x14\x06\x15\"&#\"\x04\a4?\x012>\x0554.\x01'%\x06\x02\x15\x14\x1e\x033\x16\x15\x14\a\"&#\"\x06#\x06\x02ժ!\xcf9\x13&W\xfc\xca\x02\x17B03&\f\xed\x01\x18K5\b\x03\xcd!\x92)\x0fV\x1d\x14\x0f\x13\x8a\x0f\x06\x01?\xfe@L\xfe\xea'\x04\x83\x01\x17\b\x15\t\r\x05>R\x01\xfe>\x1ae\x1c;&L\x03\x01\x02:\xe9:\b%\x03P\x03\xd1\xfe>\x04\x02\xfd\xfcvO\a\v\n\x13'\x1f\x02h\x02\xd4\x0e\a\xfe N\xfe\x99_\"\xdd:-\f\x0f\x1d\x06&\x13\x05\x11\x04\x10\x0e\x01+#\x1c\x05\x02\a\x06\n\f\b\x10\xa1\xc2\x03\x02:\xfe\xed\x19\x16\x1f\x12\t\b\x13'\t\x12\x14\b\x0e\x00\x00\x03\x00\x00\xff\x80\x05\x80\x05\x80\x00\x15\x00+\x00a\x00\x00%\x163 \x114'.\x04#\"\a\x14\x06\x15\x14\x06\x1e\x01\x03\x1632>\x0254.\x02#\"\a\x14\x16\x15\x14\x06\x15\x14\x017>\x017>\x04<\x015\x10'.\x04/\x016$32\x1632\x1e\x03\x15\x14\x0e\x03\a\x1e\x01\x15\x14\x0e\x03#\"&#\"\x04\x02+JB\x01x)\x1bEB_I:I\x1c\x01\x02\x01\b\x06*CRzb3:dtB2P\b\x01\xfd\xe4\x02\x0f\x8c$\a\v\x06\x05\x01\x16\x04$5.3\x05\x04b\x01\xe4\x83\x17Z\x17F\x85|\\8!-T>5\x9a\xcdFu\x9f\xa8\\,\xb0,j\xfen\x0f \x01OrB,\x027676\x1a\x01'5.\x02'7\x1e\x0232>\x017\x06\a\x0e\x01\a\x0e\x03\a\x06\x02\a\x0e\x03\x1f\x01\x16\x17\x06\a\"\x06#\"&#&#\"\x06\x11\x16OA\x1b\x1c\r\x01zj\x01\x18=N\x13\x13!\xae}:0e\x8d\x1c\x05\x0e\x1e\x8f%\b\f\x06\t\x02\x1by\x11\x02\x16\x12\x0e\x01\x01\x11\xa8\x03\r\v+\v\x1dt\x1c\x8aD3\xb8~U\a\x13\x13\x0e#B\a\x024\x02\v#\x19\r\v\x05\x03g\x02\t\x05\x05\t\x02'2\n%\x0f\x13/!:\r\x94\xfd\xe1T\tbRU\x0f\x12\x04\x1b,7\x03\x14\x02\x12\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\xfa\x05\x80\x00\x1b\x00}\x00\x00%2\x16\x0f\x01\x06\"/\x01&6;\x01\x11#\"&?\x0162\x1f\x01\x16\x06+\x01\x11\x01\x17\x1632632\x163!2\x16>\x02?\x012\x163\x16\x15\x14\a\x06\a&'.\x02'.\x03\x06#\"&\"\x06\a\x06\x17\x14\x12\x15\x14\x06\x16\x17\x1e\x01\x17\x16\x15\x14\x0f\x01\x06$#\"\x06#&=\x01>\x0276\x114\x02=\x01464.\x01'&#\"\x06\a\x0e\x02\a&'\x11\x06\xd0!\x12\x14~\x14:\x14~\x14\x12!PP!\x12\x14~\x14:\x14~\x14\x12!P\xf9\xd16\f\xc7,\xb0,$\x8f$\x01%\x06\x1e\v\x15\x0e\b*\x04\x14\x04\x02\x05'\x1d\x19\x1d\x03\x10\r\x01\x06\f\x13\a\x1d\x02\x11c2N \t\x01\x04\x05\x05\n(\xa8$\x05\x03\"L\xfe\xe4A2\xca3\x03\x11Yl\x18\x13\x06\x01\x02\x04\x03\v\x97!x\x14\x13\x1e!\x1a*\x0e\x80%\x1a\xa2\x1a\x1a\xa2\x1a%\x04\x00%\x1a\xa2\x1a\x1a\xa2\x1a%\xfc\x00\x04\xff\x1b\x05\x04\x01\x01\x01\x05\r\v\x01\x01p\xe0P\x1d\x0e\x04,T\tNE\x01\b\t\x03\x02\x01\x01\x04\x04Q7^\xfd\xb4\xa1\x10oH!\x15+\x10(\n\x0e\x0f\x01\x02\x14\x123\x01\t\x1b \x1a\x0e*\x01Ue\x01\x94eu\x02\x1b\x17\x1c\x14\x04\f\x18\x0e\rwg\x02\x1a\x12\x01\u007f\x00\x00\x02\x00\x00\xff\x03\x06\x00\x05\x80\x00a\x00\x95\x00\x00\x13\x17\x1632632$\x04\x17\x16?\x012\x163\x16\x15\x14\a\x06\a&'.\x025&'&#\"&\"\x06\a\x06\x1f\x015\x14\x1e\x01\x15\x14\x06\x16\x17\x1e\x01\x17\x16\x15\x14\x0f\x01\x06$#\"\x06#&=\x01>\x027>\x024&54&54>\x01.\x01'&#\"\x06\a\x0e\x02\a&'\x11\x012\x1e\x02\x17\x16\x14\a\x0e\x03#\".\x01465!\x14\x16\x14\x0e\x01#\".\x02'&47>\x0332\x1e\x01\x14\x06\x15!4&4>\x01Q6\f\xc7,\xb0,F\x01a\x01\x00w!\x17*\x04\x14\x04\x02\x05'\x1d\x19\x1d\x03\x10\x0e\n\x11\x05=\x1e~Pl*\t\x01\x01\x02\x01\x05\x05\n(\xa8$\x05\x03\"L\xfe\xe4A2\xca3\x03\x11Yl\x18\a\t\x03\x01\x05\x01\x01\x01\x05\x04\v\x97)\xf4\x10\x13\x1e!\x1a*\x0e\x05\x1e\f<7@\x04\x1a\x1a\x04@7<\f\r\x0f\x05\x03\xfc\x00\x03\x05\x0f\r\f<7@\x04\x1a\x1a\x04@7<\f\r\x0f\x05\x03\x04\x00\x03\x05\x0f\x05\u007f\x1b\x05\x04\x02\x01\x04\x01 \x01\x01p\xe0P\x1d\x0e\x04,T\tMF\x01\r\x06\x02\x02\x04\x05Q7\x9847ƢH\x10oH!\x15+\x10(\n\x0e\x0f\x01\x02\x14\x123\x01\t\x1b \x1a\x0e\x10t\xaf\x87\xac\x03\a\x1d\b\aJHQ6\x05\f\x1b\v\fwh\x02\x1a\x12\x01\u007f\xfa\xff',6\x03\x158\x15\x036,'\x15$\x1f#\x02\x02#\x1f$\x15',6\x03\x158\x15\x036,'\x15$\x1f#\x02\x02#\x1f$\x15\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xfe\x80&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&\x01\x00&\x1a\xfa\x00\x1a&&\x1a\x06\x00\x1a&\xfe\x80&\x1a\xfb\x80\x1a&&\x1a\x04\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xfe\x80&\x1a\xfc\x80\x1a&&\x1a\x03\x80\x1a&\x01\x00&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\xfe\x80&\x1a\xfd\x80\x1a&&\x1a\x02\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&&\x1a\xfa\x00\x1a&&\x1a\x06\x00\x1a&&\x1a\xfb\x80\x1a&&\x1a\x04\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x00\x00\b\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x00\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x06\x00\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\xfa\x00\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x06\x00\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\xe0\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\xfc\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x04s\xc0\r\x13\x13\r\xc0\r\x13\x13\xfc\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x05\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00\x00\x01\x11\x14\x06#\"'\x01&47\x01632\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x80\x13\r\x0e\t\xfe\xe0\t\t\x01 \t\x0e\r\x13\x05\x80\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x03\xe0\xfd\xc0\r\x13\t\x01 \t\x1c\t\x01 \t\x13\xfc\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x05\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00\x00\x00\x14\a\x01\x06#\"&5\x114632\x17\t\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01`\t\xfe\xe0\t\x0e\r\x13\x13\r\x0e\t\x01 \x05\xa9\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x02\xce\x1c\t\xfe\xe0\t\x13\r\x02@\r\x13\t\xfe\xe0\xfe\t\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x01\x00\x00\x00\x00\a\x00\x05\x00\x00\x1f\x00\x00\x01\x11\x14\a\x06#\"'\x01\x15\x14\x06#!\"&5\x11463!2\x16\x1d\x01\x01632\x17\x16\a\x00'\r\f\x1b\x12\xfem\xa9w\xfd@w\xa9\xa9w\x02\xc0w\xa9\x01\x93\x12\x1b\f\r'\x04\xa0\xfb\xc0*\x11\x05\x13\x01\x93\xa6w\xa9\xa9w\x02\xc0w\xa9\xa9w\xa5\x01\x92\x13\x05\x11\x00\x00\x00\x00\x04\x00\x00\xff\x80\a\x80\x05\x80\x00\a\x00\x0e\x00\x1e\x00.\x00\x00\x00\x14\x06\"&462\x01\x11!5\x01\x17\t\x01!\"\x06\x15\x11\x14\x163!265\x114&\x17\x11\x14\x06#!\"&5\x11463!2\x16\x02\x80p\xa0pp\xa0\x04p\xfa\x80\x01@\xa0\x02\x00\x02\x00\xf9\xc0\r\x13\x13\r\x06@\r\x13\x13\x93^B\xf9\xc0B^^B\x06@B^\x04\x10\xa0pp\xa0p\xfd\xc0\xfe@\xc0\x01@\xa0\x02\x00\x01 \x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13 \xfb@B^^B\x04\xc0B^^\x00\x04\x00\x00\xff\x80\x05\xeb\x05k\x00\x06\x00\x14\x00\x19\x00%\x00\x00!7'\a\x153\x15\x014#\"\a\x01\x06\x15\x14327\x016'\t\x01!\x11\x01\x14\x0f\x01\x017632\x1f\x01\x16\x01k[\xeb[\x80\x02v\x16\n\a\xfd\xe2\a\x16\n\a\x02\x1e\a6\x01\xa0\xfc\xc0\xfe`\x05\xeb%\xa6\xfe`\xa6$65&\xeb%[\xeb[k\x80\x03\xa0\x16\a\xfd\xe2\a\n\x16\a\x02\x1e\a\xca\xfe`\xfc\xc0\x01\xa0\x02\xe05%\xa6\x01\xa0\xa5&&\xea'\x00\x00\x02\x00\x00\xff\x80\x04\x00\x05\x80\x00\a\x00\x17\x00\x00\x004&\"\x06\x14\x162\x01\x14\a\x01\x0e\x01\"&'\x01&54\x00 \x00\x03\x00\x96Ԗ\x96\xd4\x01\x96!\xfe\x94\x10?H?\x0f\xfe\x93!\x01,\x01\xa8\x01,\x03\x16Ԗ\x96Ԗ\x01\x00mF\xfc\xfa!&&!\x03\x06Fm\xd4\x01,\xfe\xd4\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x13\x00\x00%\x11\"\x0e\x01\x10\x1e\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x00\x94\xfa\x92\x92\xfa\x03\x94\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a`\x04@\x92\xfa\xfe\xd8\xfa\x92\x02\xf1\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x05\xc0\x00\x15\x00-\x00\x00\x014'.\x03'&\"\a\x0e\x03\a\x06\x15\x14\x1626%\x14\x00 \x00547>\x037>\x012\x16\x17\x1e\x03\x17\x16\x02\x00\x14\x01\x1d\x16\x1c\a\x04\"\x04\a\x1c\x16\x1d\x01\x14KjK\x02\x00\xfe\xd4\xfeX\xfe\xd4Q\x06qYn\x1c\t243\b\x1cnYq\x06Q\x01\x80$!\x01+!7\x17\x10\x10\x177!+\x01!$5KK\xb5\xd4\xfe\xd4\x01,ԑ\x82\t\xa3\x8b\xd9]\x1e\"\"\x1e]ً\xa3\t\u007f\x00\x05\x00\x00\x00\x00\x06\xf8\x05\x80\x00\x06\x00\x0e\x009\x00>\x00H\x00\x00\x017'\a\x153\x15\x00&\a\x01\x06\x167\x01\x13\x15\x14\x06#!\"&5\x11463!2\x17\x16\x17\x16\x0f\x01\x06'&#!\"\x06\x15\x11\x14\x163!26=\x014?\x016\x16\x03\t\x01!\x11\x01\a\x01762\x1f\x01\x16\x14\x03xt\x98t`\x02\x00 \x11\xfe\xa2\x11 \x11\x01^Q\xa9w\xfc\xc0w\xa9\xa9w\x03@?6\x0f\x03\x03\f1\x0e\x12\x17\x16\xfc\xc0B^^B\x03@B^\t@\x0f(`\x01 \xfd`\xfe\xe0\x04\\\\\xfe\xe0\\\x1cP\x1c\x98\x1c\x01`t\x98t8`\x02\xc0 \x11\xfe\xa2\x11 \x11\x01^\xfdϾw\xa9\xa9w\x03@w\xa9\x19\a\x10\x11\f1\x0e\x06\x06^B\xfc\xc0B^^B~\r\t@\x0f\x10\x02\xcd\xfe\xe0\xfd`\x01 \x02\x1c\\\x01 \\\x1c\x1c\x98\x1cP\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x06\x00\x00+\x00Z\x00\x00\x01\x11\x14\x06#!\"&5\x11463!12\x16\x15\x14\a\x06\a\x06+\x01\"\x06\x15\x11\x14\x163!26=\x0147676\x17\x16\x13\x01\x06#\"'&=\x01# \a\x06\x13\x16\a\x06#\"'.\x0454>\a;\x01547632\x17\x01\x16\x14\x05\x80\xa9w\xfc\xc0w\xa9\xa9w\x00\xff\r\x13\x1aM8\n\x06pB^^B\x03@B^\x12\x1c\x1a\x10\x13\x15\xed\xfe\x80\x12\x1b\f\r'\xa0\xfe\xbdsw-\x03\x17\b\x04\x10\n\n\x169*#\a\x15#;No\x8a\xb5j\xa0'\r\f\x1a\x13\x01\x80\x13\x02#\xfe\xfdw\xa9\xa9w\x03@w\xa9\x13\r\x1b\x05\x1a\"\x04^B\xfc\xc0B^^B\xd6\x13\n\r\x18\x10\b\t\x01\xdc\xfe\x80\x13\x05\x11*\xc0\x83\x89\xfe\xb0\x17\v\x02\r\x0e\"g`\x8481T`PSA:'\x16\xc0*\x11\x05\x13\xfe\x80\x134\x00\x00\x02\x00\x00\x00\x00\x06\u007f\x05\x80\x00/\x00D\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x17\x16\x17\x16\x0f\x01\x06#\"'&#!\"\x06\x15\x11\x14\x163!26=\x014?\x01632\x17\x16\x13\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x16\x14\x05\x80\xa9w\xfc\xc0w\xa9\xa9w\x03@?6\x0f\x03\x03\f1\n\r\x03\x06\x17\x16\xfc\xc0B^^B\x03@B^\t@\n\r\x06\x06\x14\xe7\xfc\xd2\x18B\x18\xfeR\x18\x18n\x18B\x18\x01\a\x02\x87\x18B\x18n\x18\x02^\xfe\xc2w\xa9\xa9w\x03@w\xa9\x19\a\x10\x11\f1\n\x02\x06^B\xfc\xc0B^^B\xfe\r\t@\n\x03\b\x01\xd4\xfc\xd2\x18\x18\x01\xae\x18B\x18n\x18\x18\xfe\xf9\x02\x87\x18\x18n\x18B\x00\x00\x00\x00\x01\x00\x00\xff\x00\a\x00\x06\x00\x00C\x00\x00\x00\x14\a\x01\x06\"&=\x01!\x1132\x16\x14\a\x01\x06\"'\x01&46;\x01\x11!\x15\x14\x06\"'\x01&47\x0162\x16\x1d\x01!\x11#\"&47\x0162\x17\x01\x16\x14\x06+\x01\x11!5462\x17\x01\a\x00\x13\xff\x00\x134&\xfe\x80\x80\x1a&\x13\xff\x00\x134\x13\xff\x00\x13&\x1a\x80\xfe\x80&4\x13\xff\x00\x13\x13\x01\x00\x134&\x01\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x13&\x1a\x80\x01\x80&4\x13\x01\x00\x02\x9a4\x13\xff\x00\x13&\x1a\x80\xfe\x80&4\x13\xff\x00\x13\x13\x01\x00\x134&\x01\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x13&\x1a\x80\x01\x80&4\x13\x01\x00\x13\x13\xff\x00\x134&\xfe\x80\x80\x1a&\x13\xff\x00\x00\x01\x00\x00\xff\x80\x04\x00\x05\x80\x00\x1d\x00\x00\x016\x16\x15\x11\x14\x06'\x01&'\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x15\x1167\x03\xd3\x13\x1a\x1a\x13\xfd:\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\t\x05s\x13\f\x1a\xfa@\x1a\f\x13\x02\xc6\t\n\xfdZ\x1a&&\x1a\x05\x80\x1a&&\x1a\xfdZ\n\t\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00+\x00\x00\x016\x16\x15\x11\x14\x06'\x01&'\x11\x14\x06'\x01&'\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x15\x1167\x016\x16\x15\x1167\x06\xd3\x13\x1a\x1a\x13\xfd:\t\x04\x1a\x13\xfd:\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\t\x02\xc6\x13\x1a\x04\t\x05s\x13\f\x1a\xfa@\x1a\f\x13\x02\xc6\t\n\xfd:\x1a\f\x13\x02\xc6\t\n\xfdZ\x1a&&\x1a\x05\x80\x1a&&\x1a\xfdZ\n\t\x02\xc6\x13\f\x1a\xfd:\n\t\x00\x01\x00z\xff\x80\x06\x80\x05\x80\x00\x19\x00\x00\x016\x16\x15\x11\x14\x06'\x01&'\x11\x14\x06'\x01&47\x016\x16\x15\x1167\x06S\x13\x1a\x1a\x13\xfd:\t\x04\x1a\x13\xfd:\x13\x13\x02\xc6\x13\x1a\x04\t\x05s\x13\f\x1a\xfa@\x1a\f\x13\x02\xc6\t\n\xfd:\x1a\f\x13\x02\xc6\x134\x13\x02\xc6\x13\f\x1a\xfd:\n\t\x00\x00\x01\x00\x00\xff|\x05\u007f\x05\x84\x00\v\x00\x00\t\x01\x06&5\x1146\x17\x01\x16\x14\x05h\xfa\xd0\x17!!\x17\x050\x17\x02a\xfd\x1e\r\x14\x1a\x05\xc0\x1a\x14\r\xfd\x1e\r$\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00&\x1a\xfe\x00\x1a&&\x1a\x02\x00\x1a&\xfc\x80&\x1a\xfe\x00\x1a&&\x1a\x02\x00\x1a&\x05@\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\x05@\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x06\x05\x80\x00\x19\x00\x00\x17\x06&5\x1146\x17\x01\x16\x17\x1146\x17\x01\x16\x14\a\x01\x06&5\x11\x06\a-\x13\x1a\x1a\x13\x02\xc6\t\x04\x1a\x13\x02\xc6\x13\x13\xfd:\x13\x1a\x04\ts\x13\f\x1a\x05\xc0\x1a\f\x13\xfd:\t\n\x02\xc6\x1a\f\x13\xfd:\x134\x13\xfd:\x13\f\x1a\x02\xc6\n\t\x00\x00\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00+\x00\x00\x17\x06&5\x1146\x17\x01\x16\x17\x1146\x17\x01\x16\x17\x1146;\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\x06\a\x01\x06&5\x11\x06\a-\x13\x1a\x1a\x13\x02\xc6\t\x04\x1a\x13\x02\xc6\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\t\xfd:\x13\x1a\x04\ts\x13\f\x1a\x05\xc0\x1a\f\x13\xfd:\t\n\x02\xc6\x1a\f\x13\xfd:\t\n\x02\xa6\x1a&&\x1a\xfa\x80\x1a&&\x1a\x02\xa6\n\t\xfd:\x13\f\x1a\x02\xc6\n\t\x00\x00\x00\x01\x00\x00\xff\x80\x04\x00\x05\x80\x00\x1d\x00\x00\x17\x06&5\x1146\x17\x01\x16\x17\x1146;\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\x06\a-\x13\x1a\x1a\x13\x02\xc6\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\ts\x13\f\x1a\x05\xc0\x1a\f\x13\xfd:\t\n\x02\xa6\x1a&&\x1a\xfa\x80\x1a&&\x1a\x02\xa6\n\t\x00\x00\x00\x02\x00\x01\x00\x00\x06\x01\x05\x06\x00\v\x00\x1b\x00\x00\x13\x0162\x17\x01\x16\x06#!\"&\x01!\"&5\x11463!2\x16\x15\x11\x14\x06\x0e\x02\xc6\x134\x13\x02\xc6\x13\f\x1a\xfa@\x1a\f\x05\xc6\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x02-\x02\xc6\x13\x13\xfd:\x13\x1a\x1a\xfd\xe6&\x1a\x01\x00\x1a&&\x1a\xff\x00\x1a&\x00\x00\x00\x00\x01\x00\x9a\xff\x9a\x04\xa6\x05\xe6\x00\x14\x00\x00\t\x02\x16\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x16\x14\x04\x93\xfd\xed\x02\x13\x13\x13\xa6\x134\x13\xfd\x1a\x13\x13\x02\xe6\x134\x13\xa6\x13\x04\xd3\xfd\xed\xfd\xed\x134\x13\xa6\x13\x13\x02\xe6\x134\x13\x02\xe6\x13\x13\xa6\x134\x00\x00\x00\x00\x01\x00Z\xff\x9a\x04f\x05\xe6\x00\x14\x00\x00\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x16\x14\x04S\xfd\x1a\x134\x13\xa6\x13\x13\x02\x13\xfd\xed\x13\x13\xa6\x134\x13\x02\xe6\x13\x02\x93\xfd\x1a\x13\x13\xa6\x134\x13\x02\x13\x02\x13\x134\x13\xa6\x13\x13\xfd\x1a\x134\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x00/\x00\x00\x0154&#!\x114&+\x01\"\x06\x15\x11!\"\x06\x1d\x01\x14\x163!\x11\x14\x16;\x01265\x11!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xc0&\x1a\xff\x00&\x1a\x80\x1a&\xff\x00\x1a&&\x1a\x01\x00&\x1a\x80\x1a&\x01\x00\x1a&\x01@\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02@\x80\x1a&\x01\x00\x1a&&\x1a\xff\x00&\x1a\x80\x1a&\xff\x00\x1a&&\x1a\x01\x00&\x01+\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00\x00\x0154&#!\"\x06\x1d\x01\x14\x163!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xc0&\x1a\xfd\x00\x1a&&\x1a\x03\x00\x1a&\x01@\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02@\x80\x1a&&\x1a\x80\x1a&&\x01+\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00+\x007\x00\x00\x014/\x017654/\x01&#\"\x0f\x01'&#\"\x0f\x01\x06\x15\x14\x1f\x01\a\x06\x15\x14\x1f\x01\x1632?\x01\x17\x1632?\x016\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04}\x13\xb5\xb5\x13\x13Z\x13\x1b\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x13\xb5\xb5\x13\x13Z\x13\x1b\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x01\x83\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\x9e\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x13\xb5\xb5\x13\x13Z\x13\x1b\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x13\xb5\xb5\x13\x13Z\x13\x01\xce\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x17\x00#\x00\x00\x014/\x01&\"\a\x01'&\"\x0f\x01\x06\x15\x14\x17\x01\x16327\x01>\x01\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x04\x12[\x134\x13\xfeh\xe2\x134\x13[\x12\x12\x01j\x13\x1a\x1b\x13\x02\x1f\x12\xfc\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03\"\x1c\x12Z\x13\x13\xfei\xe2\x13\x13Z\x12\x1c\x1b\x12\xfe\x96\x13\x13\x02\x1f\x12J\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00:\x00F\x00\x00%54&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x014.\x01#\"\a\x06\x1f\x01\x1632767632\x16\x15\x14\x06\a\x0e\x01\x1d\x01\x14\x16;\x01265467>\x04$\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x00o\xa6W\xf3\x80\x0f\x17\x84\a\f\x10\t5!\"40K(0?i\x12\x0e\xc0\x0e\x12+! \":\x1f\x19\x01\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xa0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x02\xaeX\x96R\xd5\x18\x12d\x06\fD\x18\x184!&.\x16\x1cuC$\x0e\x12\x12\x0e\x13=\x13\x12\x151/J=\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1e\x00.\x00:\x00\x00%54&+\x01\x114&#!\"\x06\x1d\x01\x14\x16;\x01\x11#\"\x06\x1d\x01\x14\x163!26\x0354&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00\x12\x0e`\x12\x0e\xfe\xc0\x0e\x12\x12\x0e``\x0e\x12\x12\x0e\x01\xc0\x0e\x12\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x02\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xa0\xa0\x0e\x12\x02\x00\x0e\x12\x12\x0e\xa0\x0e\x12\xfe\xc0\x12\x0e\xa0\x0e\x12\x12\x03\x8e\xa0\x0e\x12\x12\x0e\xa0\x0e\x12\x12\xc1\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00/\x00_\x00\x00\x01#\"&=\x0146;\x01.\x01'\x15\x14\x06+\x01\"&=\x01\x0e\x01\a32\x16\x1d\x01\x14\x06+\x01\x1e\x01\x17546;\x012\x16\x1d\x01>\x01\x01\x15\x14\x06+\x01\x0e\x01\a\x15\x14\x06+\x01\"&=\x01.\x01'#\"&=\x0146;\x01>\x017546;\x012\x16\x1d\x01\x1e\x01\x1732\x16\x04\xadm\x1a&&\x1am \xa1l&\x1a\x80\x1a&l\xa1 m\x1a&&\x1am \xa1l&\x1a\x80\x1a&l\xa1\x01s&\x1a\x8f%\xeb\xa1&\x1a\x80\x1a&\xa1\xeb%\x8f\x1a&&\x1a\x8f%\xeb\xa1&\x1a\x80\x1a&\xa1\xeb%\x8f\x1a&\x02\x00&\x1a\x80\x1a&l\xa1 m\x1a&&\x1am \xa1l&\x1a\x80\x1a&l\xa1 m\x1a&&\x1am \xa1\x01,\x80\x1a&\xa1\xeb%\x8f\x1a&&\x1a\x8f%\xeb\xa1&\x1a\x80\x1a&\xa1\xeb%\x8f\x1a&&\x1a\x8f%\xeb\xa1&\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x00/\x00;\x00\x00\x01\a\x06\"/\x01\a\x06\"/\x01&4?\x01'&4?\x0162\x1f\x01762\x1f\x01\x16\x14\x0f\x01\x17\x16\x146\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04I\x92\n\x1a\n\x89\x89\n\x1a\n\x92\n\n\x89\x89\n\n\x92\n\x1a\n\x89\x89\n\x1a\n\x92\n\n\x89\x89\n͒\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01ɒ\n\n\x89\x89\n\n\x92\n\x1a\n\x89\x89\n\x1a\n\x92\n\n\x89\x89\n\n\x92\n\x1a\n\x89\x89\n\x1a\x19\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00,\x00\x00\t\x01\x06\"'\x01&4?\x0162\x1f\x01\x0162\x1f\x01\x16\x14\x16\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x93\xfeZ\x134\x13\xfe\xda\x13\x13f\x134\x13\x93\x01\x13\x134\x13f\x13z\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xd3\xfeZ\x13\x13\x01&\x134\x13f\x13\x13\x93\x01\x13\x13\x13f\x134\xfa\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x85\x00\t\x00\x12\x00\"\x00\x00\x014'\x01\x1632>\x02\x05\x01&#\"\x0e\x01\x15\x14\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x05 W\xfd\x0e\x89\xa0oɒV\xfc\x19\x02\U000c7954\xfa\x92\x05 z\xcd\xfe\xe3\xfe\xc8\xfe\xe3\xcdzz\xcd\x01\x1d\x018\x01\x1d\xcd\x02\x83\xa1\x86\xfd\x0fYW\x92˼\x02\xf2[\x92\xfc\x94\xa2\x01?\xfe\xc6\xfe\xe2\xcezz\xce\x01\x1e\x01:\x01\x1d\xcezz\xce\x00\x00\x01\x00@\xff5\x06\x00\x05K\x00 \x00\x00\x01\x15\x14\x06#!\x01\x16\x14\x0f\x01\x06#\"'\x01&547\x01632\x1f\x01\x16\x14\a\x01!2\x16\x06\x00A4\xfd@\x01%&&K%54'\xfdu%%\x02\x8b&54&K&&\xfe\xdb\x02\xc04A\x02\x80\x805K\xfe\xda$l$L%%\x02\x8c%54'\x02\x8a&&J&j&\xfe\xdbK\x00\x00\x01\x00\x00\xff5\x05\xc0\x05K\x00 \x00\x00\x01\x14\a\x01\x06#\"/\x01&47\x01!\"&=\x01463!\x01&4?\x01632\x17\x01\x16\x05\xc0%\xfdu'43'K&&\x01%\xfd@4AA4\x02\xc0\xfe\xdb&&K&45&\x02\x8b%\x02@6%\xfdu%%K&j&\x01%K5\x805K\x01&$l$K&&\xfdu#\x00\x00\x01\x005\xff\x80\x06K\x05@\x00!\x00\x00\x01\x14\x0f\x01\x06#\"'\x01\x11\x14\x06+\x01\"&5\x11\x01\x06\"/\x01&547\x01632\x17\x01\x16\x06K%K&56$\xfe\xdaK5\x805K\xfe\xda$l$K&&\x02\x8b#76%\x02\x8b%\x0253'K&&\x01%\xfd@4AA4\x02\xc0\xfe\xdb&&K&45&\x02\x8b%%\xfdu'\x00\x00\x00\x00\x01\x005\xff\xb5\x06K\x05\x80\x00\"\x00\x00\x01\x14\a\x01\x06#\"'\x01&54?\x01632\x17\x01\x1146;\x012\x16\x15\x11\x01632\x1f\x01\x16\x06K%\xfdu'45%\xfdu&&J'45%\x01&L4\x804L\x01&%54'K%\x02\xc05%\xfdt%%\x02\x8c$65&K%%\xfe\xda\x02\xc04LL4\xfd@\x01&%%K'\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\xc0\x00,\x00\x00\x00\x14\a\x01\x06\"&5\x11#\"\x0e\x05\x15\x14\x17\x14\x16\x15\x14\x06#\"'.\x02'\x02547\x12!3\x11462\x17\x01\a\x00\x13\xfe\x00\x134&\xe0b\x9b\x99qb>#\x05\x05\x11\x0f\x10\f\a\f\x0f\x03\u007f5\xa2\x02\xc9\xe0&4\x13\x02\x00\x03\x9a4\x13\xfe\x00\x13&\x1a\x01\x00\f\x1f6Uu\xa0e7D\x06#\t\x0f\x14\x11\t\x1a\"\a\x01\x1d\xa6dž\x01\x93\x01\x00\x1a&\x13\xfe\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x17\x00/\x00\x00\x00\x14\a\x01\x17\x16\x14\x06#!\"&5\x11462\x1f\x01\x0162\x1f\x01\x01\x11\x14\x06\"/\x01\x01\x06\"/\x01&47\x01'&463!2\x16\x02\xf3\n\xfe\xb4\x90\x13&\x1a\xfe@\x1a&&4\x13\x90\x01L\n\x1a\nr\x03\x17&4\x13\x90\xfe\xb4\n\x1a\nr\n\n\x01L\x90\x13&\x1a\x01\xc0\x1a&\x01\xed\x1a\n\xfe\xb4\x90\x134&&\x1a\x01\xc0\x1a&\x13\x90\x01L\n\nr\x03I\xfe@\x1a&\x13\x90\xfe\xb4\n\nr\n\x1a\n\x01L\x90\x134&&\x00\x00\x00\x00\x02\x00\r\xff\x8d\x05\xf3\x05s\x00\x17\x00/\x00\x00\x01\x11\x14\x06\"/\x01\x01\x06\"/\x01&47\x01'&463!2\x16\x00\x14\a\x01\x17\x16\x14\x06#!\"&5\x11462\x1f\x01\x0162\x1f\x01\x03\x00&4\x13\x90\xfe\xb4\n\x1a\nr\n\n\x01L\x90\x13&\x1a\x01\xc0\x1a&\x02\xf3\n\xfe\xb4\x90\x13&\x1a\xfe@\x1a&&4\x13\x90\x01L\n\x1a\nr\x02@\xfe@\x1a&\x13\x90\xfe\xb4\n\nr\n\x1a\n\x01L\x90\x134&&\x02\x93\x1a\n\xfe\xb4\x90\x134&&\x1a\x01\xc0\x1a&\x13\x90\x01L\n\nr\x00\x00\x00\x00\x01\x00\x00\x00\x00\x05\x80\x05\x80\x00#\x00\x00\x01\x15\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!\x1146;\x012\x16\x15\x11!2\x16\x05\x808(\xfe`8(\xc0(8\xfe`(88(\x01\xa08(\xc0(8\x01\xa0(8\x03 \xc0(8\xfe`(88(\x01\xa08(\xc0(8\x01\xa0(88(\xfe`8\x00\x00\x00\x00\x01\x00\x00\x02\x00\x05\x80\x03\x80\x00\x0f\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x05\x808(\xfb@(88(\x04\xc0(8\x03 \xc0(88(\xc0(88\x00\x00\x01\x00z\xff\x80\x06\x06\x05\x80\x005\x00\x00\x01\x1e\x01\x0f\x01\x0e\x01'%\x11\x14\x06+\x01\"&5\x11\x05\x06&/\x01&67-\x01.\x01?\x01>\x01\x17\x05\x1146;\x012\x16\x15\x11%6\x16\x1f\x01\x16\x06\a\x05\x05\xca.\x1b\x1a@\x1ag.\xfe\xf6L4\x804L\xfe\xf6.g\x1a@\x1a\x1b.\x01\n\xfe\xf6.\x1b\x1a@\x1ag.\x01\nL4\x804L\x01\n.g\x1a@\x1a\x1b.\xfe\xf6\x01\xe6\x1ag.n.\x1b\x1a\x99\xfe\xcd4LL4\x013\x99\x1a\x1b.n.g\x1a\x9a\x9a\x1ag.n.\x1b\x1a\x99\x0134LL4\xfe͙\x1a\x1b.n.g\x1a\x9a\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x1b\x00-\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x03\x134'&+\x01\"\a\x06\x15\x13\x14\x16;\x0126\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x02\xb2\x12\r\xc0\r\x14\x14\r\xc0\r\x12\x02\x12\n\n\x0e\xdc\x0e\n\n\x11\x14\x0e\xb9\x0e\x13\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfb\xef\xbe\x0e\x13\x14\r\xbe\r\x14\x13\x01f\x02m\f\x06\b\b\x06\f\xfd\x93\n\x0f\x0f\x00\x00\x00\x04\x00\x00\x00\x00\x06\x00\x05@\x00\r\x00\x16\x00\x1f\x00J\x00\x00%5\x115!\x15\x11\x15\x14\x16;\x0126\x013'&#\"\x06\x14\x16$4&#\"\x0f\x0132\x05\x11\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&5\x11463!\"&4632\x1f\x017632\x16\x14\x06#!2\x16\x03\xa0\xfe\xc0$\x1c\xc0\x1c$\xfe8\xc3~\x1a+(88\x02\xd88(+\x1a}\xc2(\x01\xb0\x12\x0e`8(\xfb\xc0(8`\x0e\x12\x12\x0e\x01\xb8]\x83\x83]k=\x80\x80=k]\x83\x83]\x01\xb8\x0e\x12\xb48\x01\xd4\xc0\xc0\xfe,8\x19\x1b\x1b\x03e\xa1\x1f8P88P8\x1f\xa1\xa0\xfe\xc0\x0e\x12\xfe`(88(\x01\xa0\x12\x0e\x01@\x0e\x12\x83\xba\x83M\xa5\xa5M\x83\xba\x83\x12\x00\x02\x00\x00\x00\x00\a\x00\x05\x80\x00\x15\x00N\x00\x00\x004&#\"\x04\x06\a\x06\x15\x14\x16327>\x0176$32\x01\x14\a\x06\x00\a\x06#\"'.\x01#\"\x0e\x02#\"&'.\x0354>\x0254&'&54>\x027>\x047>\x0432\x1e\x02\x05\x00&\x1a\xac\xfe\xdc\xe3z\x13&\x1a\x18\x15\x1b^\x14\x89\x01\a\xb6\x1a\x02&\x14.\xfe\xeb\xdb\xd6\xe0\x94\x8a\x0f\x92\x17\x10/+>\x1d+)\x19\x02\b\x03\x03>J>\x1c\x02\tW\x97\xbem7\xb4\xb3\xb2\x95'\n'\x14\"'\x18'? \x10\x03&4&c\xa9\x87\x15\x18\x1a&\x13\x18^\x13|h\x01\x06_b\xe0\xfe\xc2ml/\x05J@L@#*\x04\x0e\x06\r\a#M6:\x13\x04D\n35sҟw$\x12\x0f\x03\t'%\n'\x11\x17\t\\\x84t\x00\x00\x00\x00\x02\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x003\x00\x00\x05\x15\x14\x06#!\"&=\x01463!2\x16\x01\x14\x0e\x05\x15\x14\x17'\x17.\x0454>\x0554'\x17'\x1e\x04\x05\x80\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\xff\x001O``O1C\x04\x01Z\x8c\x89Z71O``O1B\x03\x01Z\x8c\x89Z7\xa0@\r\x13\x13\r@\r\x13\x13\x04\x13N\x84]SHH[3`\x80\x01\x01)Tt\x81\xacbN\x84]SHH[3^\x82\x01\x01)Tt\x81\xac\x00\x00\x00\x00\x03\x00\x00\x00\x00\a\x00\x04\x80\x00\x11\x00!\x001\x00\x00\x01&'\x16\x15\x14\x00 \x00547\x06\a\x16\x04 $\x004&#\"\x06\x15\x14\x162654632\x00\x14\a\x06\x00 \x00'&476\x00 \x00\x17\x06\x80\x98\xe5=\xfe\xf9\xfe\x8e\xfe\xf9=嘅\x01\x91\x01\xd4\x01\x91\xfd\xb5\x1c\x14}\xb3\x1c(\x1czV\x14\x03l\x14\x8c\xfe'\xfd\xf2\xfe'\x8c\x14\x14\x8c\x01\xd9\x02\x0e\x01ٌ\x02@\xecuhy\xb9\xfe\xf9\x01\a\xb9yhu\xec\xcd\xf3\xf3\x029(\x1c\xb3}\x14\x1c\x1c\x14Vz\xfe\xd2D#\xe6\xfe\xeb\x01\x16\xe5#D#\xe5\x01\x16\xfe\xea\xe5\x00\x05\x00\x00\xff\xa0\a\x00\x04\xe0\x00\t\x00\x19\x00=\x00C\x00U\x00\x00%7.\x01547\x06\a\x12\x004&#\"\x06\x15\x14\x162654632%\x14\a\x06\x00\x0f\x01\x06#\"'&547.\x01'&476\x00!2\x177632\x1e\x03\x17\x16\x13\x14\x06\a\x01\x16\x04\x14\a\x06\a\x06\x04#76$7&'7\x1e\x01\x17\x02+NWb=嘧\x02\x89\x1c\x14}\xb3\x1c(\x1czV\x14\x01\x87\x01j\xfe\\i1\n\x12\fz\x10,\x8f\xf1X\x14\x14\x99\x01\xc6\x01\rY[6\n\x12\x05\x1a$\x1e!\x03\x10%\x9e\x82\x01\x18\b\x01\xc0\x14'F\x96\xfeu\xdeJ\xd4\x01iys\xa7?_\xaf9ɍ?\xc0kyhu\xec\xfe\xfe\x02n(\x1c\xb3}\x14\x1c\x1c\x14Vz\xef\a\x02\xbd\xfd\f\xbcY\x10F\n\x12\fKA؉\x1fL\x1f\xeb\x01\x10\x11a\x10\f\x13\x12\x13\x02\n\xfe0\x8b\xe52\x01\xf6-\x84F\"@Q\xac\xbe\x84\x12\uef33sp@\xb2_\x00\x00\x00\x00\x03\x00\x10\xff\x80\x06\xf0\x06\x00\x00\x0f\x00!\x003\x00\x00%54&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x03\x134'&+\x01\"\a\x06\x15\x13\x14\x16;\x0126\x03\x01\x16\a\x0e\x01#!\"&'&7\x01>\x012\x16\x04\x00\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x02\x12\n\r\v\xdc\v\r\n\x11\x14\x0e\xb9\x0e\x13\r\x03\x00#%\x11;\"\xfa\x00\";\x11%#\x03\x00\x11\x01\x05`,@L\xa1\xa0\x05\x11\x80\a\f\x04\x03\x0f\x06\xfe\xe9\xfe\xfd5\x05\r`\t\x0e\x02\x0f\t\xbd\xfc\v\x02\x01\n`\t\x0e\x06\x02\xc2\x01\x03\xfe\x04\x0e\x03\x02\v\x80\x0e\x10\x02\x99\xa0L\xc0\x05`4\xc0L\xa1\xfdH\x13\x0e`\x06\x01\x03\r\x01\xfc\xfe\xfd\xc2\x11\x0e`\t\x02\v\xfc\xbd\a\x10\r\fa\t\x015\x01\x03\x01\x17\b\x10\x10\v\x80\r\x05\x9f\xa0L@\x00\x0f\x00\x00\xff\x00\x06\x80\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x1f\x00#\x003\x007\x00;\x00?\x00O\x00s\x00\x00\x17!\x11!\x01!\x11!%!\x11!\x01!\x11!%!\x11!\x01!\x11!\x01!\x11!\x01!\x11!%!\x11!\x01\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126\x01!\x11!%!\x11!\x01!\x11!7\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x11\x14\x06#!\"&5\x1146;\x01546;\x012\x16\x1d\x01!546;\x012\x16\x1d\x0132\x16\x80\x01 \xfe\xe0\x01`\x01@\xfe\xc0\xfe\xa0\x01 \xfe\xe0\x01`\x01@\xfe\xc0\xfe\xa0\x01 \xfe\xe0\x02\xe0\x01@\xfe\xc0\xfe\x80\x01@\xfe\xc0\x03\x00\x01 \xfe\xe0\xfe\x80\x01@\xfe\xc0\xfe\xa0\x13\r@\r\x13\x13\r@\r\x13\x02\xe0\x01 \xfe\xe0\xfe\x80\x01@\xfe\xc0\x01\x80\x01 \xfe\xe0 \x13\r@\r\x13\x13\r@\r\x13\x01\x80L4\xfa\x804LL4\x80^B@B^\x01\x80^B@B^\x804L\x80\x01 \xfe\xe0\x01 @\x01@\xfe\xc0\x01@@\x01 \xfc\x00\x01 \x01\xc0\x01 \xfc\x00\x01 @\x01@\x02 \x01 \r\x13\x13\r\xfe\xe0\r\x13\x13\xfc\xad\x01@@\x01 \xfe\xe0\x01 \xc0\x01 \r\x13\x13\r\xfe\xe0\r\x13\x13M\xfb\x004LL4\x05\x004L`B^^B``B^^B`L\x00\x00\x00\x03\x00\x00\xff\xa0\a\x00\x05\xe0\x00\x12\x007\x00q\x00\x00\x01\x06\a.\x04+\x01\"&=\x0146;\x012\x00\x14\a\x01\x06#\"&=\x01\"\x0e\x01.\x06'67\x1e\x043!54632\x17\x01\x12\x14\a\x01\x06#\"&=\x01!\"\x0e\x02\a\x06\a\x0e\x06+\x01\"&=\x0146;\x012>\x02767>\x063!54632\x17\x01\x02\x9amBZxPV3!\x12\x0e\xc0\x0e\x12\x1emBZxPV3!\xc0\x0e\x12\n\xfe\xc1\x00\x00\x00\x01\x00\x00\xff\x00\a\x00\x05\x00\x00&\x00\x00\x00\x10\x02\x04#\"'\x06\x05\x06\a\x06&'5&6&>\x027>\x057&\x0254>\x01$32\x04\a\x00\xf0\xfed\xf4FK\xc6\xfe\xfa1A\x11\x1b\x04\x03\x05\x01\n\x02\f\x02\a0\x15)\x18\x1e\v\x9d\xb5\x8e\xf0\x01L\xb6\xf4\x01\x9c\x03.\xfe\xa4\xfe٫\b\xafC\x0e\b\x02\x16\x12\x01\x04\x10\x04\x0f\x03\x0e\x02\b5\x178.H(Y\x01\x06\x96\x82\xed\xace\xab\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00C\x00\x00\x01\x15\x14\x02\x04 $\x02=\x01463!2\x16\x1d\x01\x14\x1e\x032>\x03=\x01463!2\x16\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00\xc5\xfe\xa1\xfeH\xfe\xa1\xc5&\x1a\x01\x80\x1a&/\x027\x03#\"&463!2\x1e\x04\x17!2\x16\x02\x80LhLLh\x03\xccLhLLh\xcc!\x18\xfb\xec\r\x18\x03\x98\x1a&&\x1a\xfc\x00\x1a&\x10\x10\x1b\x02\xb1\xcc\x1a&&\x1a\x01\x00\x10\x19\x0e\f\x04\a\x01\x04\xb1\x1a&4hLLhLLhLLhL\x03\xc0\xfe\x00\x18%\x03z<\n\x100&4&&\x1a\v)\x1f1\x05\x037&4&\r\x12\x1f\x15&\a&\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x80\x05\x80\x00\x14\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x1d\x01!2\x16\x06\x80\x84\\\xfb@\\\x84\x84\\\x01@\\\x84\x02\xa0\\\x84\x03\xa0\xfd@\\\x84\x84\\\x03\xc0\\\x84\x84\\ \x84\x00\x00\x00\x00\x02\x00\x00\x00\x00\aW\x05\x80\x00\x13\x00*\x00\x00\x01\x14\a\x01\x0e\x01#!\"&547\x01>\x013!2\x16\x01\x15!\"\x06\a\x01\a4&5\x11463!2\x16\x1d\x01!2\x16\aW\x1f\xfe\xb0+\x9bB\xfb\xc0\"5\x1f\x01P+\x9bB\x04@\"5\xfe\xa9\xfc\xc0^\xce=\xfe\xaf\x05\x01\x84\\\x01@\\\x84\x02 \\\x84\x02H\x1f#\xfet3G\x1a\x1e\x1f#\x01\x8c3G\x1a\x01:\xa0_H\xfet\x06\x04\x11\x04\x03\xc0\\\x84\x84\\ \x84\x00\x00\x00\x01\x00@\xff\x00\x02\xc0\x06\x00\x00\x1f\x00\x00\x00\x14\x06+\x01\x1132\x16\x14\a\x01\x06\"'\x01&46;\x01\x11#\"&47\x0162\x17\x01\x02\xc0&\x1a\x80\x80\x1a&\x13\xff\x00\x134\x13\xff\x00\x13&\x1a\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x04\xda4&\xfc\x00&4\x13\xff\x00\x13\x13\x01\x00\x134&\x04\x00&4\x13\x01\x00\x13\x13\xff\x00\x00\x00\x00\x01\x00\x00\x01@\a\x00\x03\xc0\x00\x1f\x00\x00\x00\x14\a\x01\x06\"&=\x01!\x15\x14\x06\"'\x01&47\x0162\x16\x1d\x01!5462\x17\x01\a\x00\x13\xff\x00\x134&\xfc\x00&4\x13\xff\x00\x13\x13\x01\x00\x134&\x04\x00&4\x13\x01\x00\x02\x9a4\x13\xff\x00\x13&\x1a\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x13&\x1a\x80\x80\x1a&\x13\xff\x00\x00\x00\x00\x05\x00\x00\xff\x80\b\x00\x05\x80\x00\x03\x00\a\x00\r\x00\x11\x00\x15\x00\x00\x01\x11!\x11\x01\x11!\x11\x01\x15!\x113\x11\x01\x11!\x11\x01\x11!\x11\x02\x80\xff\x00\x02\x80\xff\x00\x05\x00\xf8\x00\x80\x05\x00\xff\x00\x02\x80\xff\x00\x02\x80\xfe\x00\x02\x00\x02\x00\xfc\x00\x04\x00\xfb\x80\x80\x06\x00\xfa\x80\x03\x80\xfd\x00\x03\x00\x01\x80\xfb\x80\x04\x80\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x000\x00@\x00\x00\x01\x06\a67\x06\a&#\"\x06\x15\x14\x17.\x01'\x06\x15\x14\x17&'\x15\x14\x16\x17\x06#\"'\x1e\x01\x17\x06#\"'\x1632>\x0354'6\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x008AD\x19AE=\\W{\x05\x81\xe2O\x1d[/5dI\x1d\x16\r\x1a\x15kDt\x91\x1a\x18\x94\xaepČe1\x01?\x01*\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\x9e\x19\t(M&\rB{W\x1d\x13\ata28r=\x01\x19\x02Ku\x0e\b\x04?R\x01Z\x03^Gw\x9b\xa9T\x12\t-\x01\x02\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00$\x00\x00\x012\x16\x15\x11\x14\x06+\x01\x1137#546375&#\"\x06\x1d\x01#\x153\x11!\"&5\x11463\x04\xe0w\xa9\xa9w\xbc\xc7\x1e\xe5/Dz?s\x88\xa3\xc8\xc8\xfd\xecw\xa9\xa9w\x05\x80\xa9w\xfc@w\xa9\x02S\xe8\x9488\x01\xcf\t\xa0\x92\xab\xe8\xfd\xad\xa9w\x03\xc0w\xa9\x00\x00\x00\x00\a\x00\x00\xff\x80\a\x00\x05\x80\x00\x0f\x00\x17\x00\x1b\x00#\x00'\x00.\x00>\x00\x00\x004&#\"\x06\x15\x14\x1626546326\x14\x06\"&462\x01!5!\x00\x10& \x06\x10\x16 \x01!5!\x03!=\x01!\a!%\x11\x14\x06#!\"&5\x11463!2\x16\x03\xa0\x12\x0eB^\x12\x1c\x128(\x0e\xf2\x96Ԗ\x96\xd4\xfc\x96\x06\x00\xfa\x00\x04\x80\xe1\xfe\xc2\xe1\xe1\x01>\xfc\xe1\x01\x80\xfe\x80\x80\x06\x00\xfc\xc4@\xfd|\x06\x80K5\xfa\x005KK5\x06\x005K\x02\xb2\x1c\x12^B\x0e\x12\x12\x0e(8\bԖ\x96Ԗ\xfc\u0080\x01\x1f\x01>\xe1\xe1\xfe\xc2\xe1\x04\x02\x80\xfe\xc0v\x8a\x80\x80\xfb\x005KK5\x05\x005KK\x00\x02\x00\x00\xffH\x06\x93\x05\x80\x00\x15\x00G\x00\x00\x004&\"\x06\x15\x14\x17&#\"\x06\x14\x162654'\x1632\x01\x14\x06#\".\x02'\a\x17\x16\x15\x14\x06#\"'\x01\x06#\"&54\x12$32\x16\x15\x14\a\x017.\x0354632\x17\x1e\x04\x03@p\xa0p\x13)*Ppp\xa0p\x13)*P\x03\xc3b\x11\t'\"+\x03`\xdc\x1cN*(\x1c\xfda\xb0\xbd\xa3;\x012\xa0\xa3̓\x01c`\x03.\" b\x11\r\n\x06PTY9\x03\xb0\xa0ppP*)\x13p\xa0ppP*)\x13\xfe\x00\x11b \".\x03`\xdc\x1c(*N\x1c\x02\x9f\x83ͣ\xa0\x012\xbeͣ\xbd\xb0\xfe\x9d`\x03+\"'\t\x11b\n\x06MRZB\x00\x00\x00\x00\x06\x00\x00\xff\x0f\a\x80\x05\xf0\x00\a\x00\x11\x00\x1b\x00\u007f\x00\xbd\x00\xfb\x00\x00\x004&\"\x06\x14\x162\x014&\"\x06\x15\x14\x1626\x114&\"\x06\x15\x14\x1626\x01\x15\x14\x06\x0f\x01\x06\a\x16\x17\x16\x15\x14\a\x0e\x01#\"/\x01\x06\a\x06\a\x06+\x01\"&/\x01&'\a\x06#\"'&547>\x017&/\x01.\x01=\x0146?\x0167&'&547>\x0132\x1f\x0167676;\x012\x16\x1f\x01\x16\x177632\x17\x16\x15\x14\a\x0e\x01\a\x16\x1f\x01\x1e\x01\x01\x15\x14\a\x06\a\x16\x15\x14\a\x06#\"&'\x06\"'\x0e\x01#\"'&547&'&=\x014767&547>\x0232\x16\x1762\x176?\x012\x17\x16\x15\x14\a\x16\x17\x16\x11\x15\x14\a\x06\a\x16\x15\x14\a\x06#\"&'\x06\"'\x0e\x01#\"'&547&'&=\x014767&547>\x0232\x16\x1762\x176?\x012\x17\x16\x15\x14\a\x16\x17\x16\x03\x80\x96Ԗ\x96\xd4\x03\x96LhLKjKLhLKjK\xfe\x80\x0e\t\x9b\v\x15\"8\a\a\x17w\x13\v\ns%(\v\f\a\x17\xba\v\x12\x01\x17\")v\a\r\v\n\x90\a\n>\x10\x17\f\x98\n\x0e\x0e\t\x9b\v\x15\"8\a\a\x16x\x13\v\ns\"+\v\f\a\x17\xba\v\x12\x01\x17\")v\b\f\v\n\x90\a\f<\x0f\x17\v\x98\n\x0e\x02\x80\x95\f\x123\x04z\x02\bL\x0e\x14\x14\x14\x0eL\b\x02z\x043\x12\f\x95\x95\r\x113\x04\x04>8\x02\bL\x0e\x14\x14\x143)\x06\x04x\x043\x11\r\x95\x95\f\x123\x04z\x02\bL\x0e\x14\x14\x14\x0eL\b\x02z\x043\x12\f\x95\x95\r\x113\x04\x04>8\x02\bL\x0e\x14\x14\x143)\x06\x04x\x043\x11\r\x95\x02\x16Ԗ\x96Ԗ\xff\x004LL45KK\x0454LL45KK\xfe\x90\xb9\n\x13\x01\x18#)0C\v\t\f\a\x1ew\aZ\x13\fl/\x18\x0f\n\x99\n\x15Y\a\b\x85\x1b\t\n\x0eN\x16,&\x18\x01\x11\v\xb9\n\x13\x01\x18#)0C\v\t\f\b\x1ev\aZ\x12\x0el.\x18\x0f\n\x99\n\x15Y\a\b\x85\x1b\b\v\x10L\x160\"\x17\x02\x11\xfd\xe0\x8c\x10\x0f\x1b\x19q\x19\x04\x03G^\x15\x02\x02\x15^G\x03\x04\x19q\x19\x1b\x0f\x10\x8c\x10\x0f\x1d\x17q\x19\x04\x03\x02$ ]\x15\x02\x02G)\x02F\x03\x04\x19q\x17\x1d\x0f\x03\xf0\x8c\x10\x0f\x1b\x19q\x19\x04\x03G^\x15\x02\x02\x15^G\x03\x04\x19q\x19\x1b\x0f\x10\x8c\x10\x0f\x1d\x17q\x19\x04\x03\x02$ ]\x15\x02\x02G)\x02F\x03\x04\x19q\x17\x1d\x0f\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x00\x00%\x00O\x00\x00\x00\x10\x06\x04#\"'\x06\a\x06\a#\"&'&4>\x057>\x047.\x01546$ \x04\x01\x14\x06\a\x1e\x04\x17\x1e\x06\x14\a\x0e\x01'&'&'\x06# '\x1632$7>\x0154'\x1e\x01\x05\x80\xbc\xfe\xbb\xbfVZ|\x9a$2\x03\v\x13\x02\x01\x01\x03\x02\x05\x03\x06\x01\x05$\x10\x1d\x15\n|\x8e\xbc\x01E\x01~\x01E\x02<\x8e|\n\x15\x1d\x10$\x05\x01\x06\x03\x05\x02\x03\x01\x01\x03\x14\f2$\x9a|ZV\xfe\xf1\xc9:\x1e\xa1\x01(t}\x86\x17\x81\x96\x03\x8b\xfe\xea\xec\x89\x10X(\t\a\x10\r\x03\a\x06\x06\x04\a\x03\a\x01\x06&\x15%(\x18H\xd2w\x8b쉉\xfd\x89x\xd1H\x18(%\x15&\x06\x01\a\x03\a\x04\x06\x06\a\x03\x0e\x10\x01\a\t(X\x10\x84\x04ZT\\\xf0\x86MKG\xd6\x00\x00\x03\x00\x00\xff\x80\x06\x00\x06\x00\x00\a\x00<\x00m\x00\x00$4&\"\x06\x14\x162\x014&#!4654&#\x0e\x02\a\x06\a\x0e\x06+\x01\x1132\x1e\x04\x17\x16;\x01254'>\x014'654&'>\x017\x14\a\x16\x15\x14\a\x16\x15\x14\a\x16\x06+\x02\"&'&#!\"&5\x11463!6767>\x027632\x1e\x01\x15\x14\a32\x16\x01\x00&4&&4\x04\xa6N2\xfe\xa0`@`\x1a\x18%)\x167\x04&\x19,$)'\x10 \r%\x1d/\x170\x05Ӄy\xc0\x05\x1e#\x125\x14\x0f +\x801\t&\x03<\x01\xac\x8d$]`\xbb{t\x16\xfe\xe05KK5\x01\x12$e:1\x18\x17&+'3T\x86F0\xb0h\x98\xa64&&4&\x02\x803M:\xcb;b^\x1av\x85+\x17D\x052 5#$\x12\xfd\x80\x06\a\x0f\b\x11\x02I\xa7\x1a\x1e\x10IJ 2E\x19=\x11\x01\\$YJ!$MC\x15\x16eM\x8b\xa1-+(K5\x02\x805K\x18\x83K5\x19y\x84*%A\x8au]c\x98\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x05\x80\x00\a\x00>\x00q\x00\x00\x004&\"\x06\x14\x162\x014&'>\x0154'654&'654&+\x01\"\a\x0e\x05+\x01\x1132\x1e\x05\x17\x16\x17\x1e\x02\x172654&5!267\x14\x06+\x01\x16\x15\x14\a\x0e\x01#\"'.\x03'&'&'!\"&5\x11463!27>\x01;\x012\x16\a\x15\x16\x15\x14\a\x16\x15\x14\a\x16\x01\x00&4&&4\x04\xa6+ \x0f\x145\x12#\x1e\x05bW\x80\x83\xd3\x050\x17/\x1d%\r \x10')$,\x19&\x047\x16)%\x18\x1a`@`\x01`2N\x80\x98h\xb00##\x86T3'\"(\v\x18\x130;e$\xfe\xee5KK5\x01 \x16t\x80\xbeip\x8c\xad\x01<\x03&\t1\x04&4&&4&\xfe\x00#\\\x01\x11=\x19E2\x1f&%I\x10\x1e\x1aURI\x02\x11\b\x0f\a\x06\xfd\x80\x12$#5 2\x05D\x17+\x85v\x1a^b;\xcb:M2g\x98c]vDEA%!bSV\x152M\x83\x18K5\x02\x805K(,,\x9e\x89\x05Me\x16\x15CM$!I\x00\x00\x00\x01\x00\x00\xff\xad\x03@\x05\xe0\x00\x12\x00\x00\x01\x11\x05\x06#\"&547\x13\x01&547%\x136\x03@\xfe?\x16\x12\x15\x15\x02V\xfe\x94\x198\x01\xf6\xe1\x13\x05\xe0\xfa\xc5\xec\f\x1d\x15\x06\x0e\x01\xf4\x01b\x1b\x15%\tI\x01\xc7)\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00\x1c\x009\x00\x00\x014.\x03\"\x0e\x02\a\x06\"'.\x03\"\x0e\x03\x15\x14\x17\t\x0167\x14\a\x01\x06\"'\x01.\x0454632\x1e\x02\x17>\x0332\x16\x06\x80+C`\\hxeH\x18\x12>\x12\x18Hexh\\`C+\xbb\x02E\x02D\xbc\x80\xe5\xfd\x91\x124\x12\xfd\x90\n#L\x81oP$$Po\x81>\xe0\xfe\x03\xacQ|I.\x103MC\x1c\x16\x16\x1cCM3\x10.I|Q\xa8\xbb\xfd\xd0\x02/\xbc\xa8\xdd\xe5\xfd\xa8\x12\x12\x02Z\b$_d\x8eC\xdc\xf8+I@$$@I+\xf8\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06 \x05\x00\x00(\x00@\x00\x00%\x14\x16\x0e\x02#!\"&5\x11463!2\x16\x15\x14\x16\x0e\x02#!\"\x06\x15\x11\x14\x163!:\x02\x1e\x03\x00\x14\a\x01\x06\"&5\x11!\"&5\x11463!\x11462\x17\x01\x02\x80\x02\x01\x05\x0f\r\xfe\xc0w\xa9\xa9w\x01@\r\x13\x02\x01\x05\x0f\r\xfe\xc0B^^B\x01 \x01\x14\x06\x11\x06\n\x04\x03\xa0\x13\xfd\xe0\x134&\xfe@\x1a&&\x1a\x01\xc0&4\x13\x02 `\x04 \x15\x1a\r\xa9w\x02\xc0w\xa9\x13\r\x04 \x15\x1a\r^B\xfd@B^\x02\x04\a\v\x0224\x13\xfd\xe0\x13&\x1a\x01 &\x1a\x01\x80\x1a&\x01 \x1a&\x13\xfd\xe0\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\x0f\x00%\x005\x00\x0073\x11#7.\x01\"\x06\x15\x14\x16;\x0126\x013\x114&#\"\a35#\x16\x033\x1147>\x0132\x15\x01\x11\x14\x06#!\"&5\x11463!2\x16\xed\xe7\xe7\xf6\x01FtIG9\x01;H\x02I\xe7\x92x\x88I\x02\xe7\x03\x03\xe7\a\x0f<,t\x01ԩw\xfc@w\xa9\xa9w\x03\xc0w\xa9z\x02\xb6\xd64DD43EE\xfc\xa7\x01\x8e\x9a\x9eueB\xfd\x8c\x01\x84&\x12#1\x9d\x02s\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x02\x00\x00\xff\x00\x04\x80\x05\x80\x00\v\x00.\x00\x00\x01\x114&\"\x06\x15\x11\x14\x1626\x01\x14\x06#!\x03\x0e\x01+\x01\"'\x03!\"&5463\x11\"&463!2\x16\x14\x06#\x112\x16\x01\xe0\x12\x1c\x12\x12\x1c\x12\x02\xa0&\x1a\xfeS3\x02\x11\f\x01\x1b\x05L\xfel\x1a&\x9dc4LL4\x02\x804LL4c\x9d\x02\xa0\x01\xc0\x0e\x12\x12\x0e\xfe@\x0e\x12\x12\xfe\xae\x1a&\xfe\x1d\f\x11\x1b\x01\xe5&\x1a{\xc5\x02\x00LhLLhL\xfe\x00\xc5\x00\x00\x00\x02\x00\x00\x00\x00\a\x00\x06\x00\x00'\x00?\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x1d\x01\x14\x06#!\"\x06\x15\x11\x14\x163!265\x1146;\x012\x16\x01\x11\x14\x06\"/\x01\x01\x06\"/\x01&47\x01'&463!2\x16\x05\x80\xa9w\xfc\xc0w\xa9\xa9w\x02\xc0\x0e\x12\x12\x0e\xfd@B^^B\x03@B^\x12\x0e@\x0e\x12\x01\x80&4\x13\xb0\xfdt\n\x1a\nr\n\n\x02\x8c\xb0\x13&\x1a\x02\x00\x1a&\x02`\xfe\xc0w\xa9\xa9w\x03@w\xa9\x12\x0e@\x0e\x12^B\xfc\xc0B^^B\x01@\x0e\x12\x12\x03R\xfe\x00\x1a&\x13\xb0\xfdt\n\nr\n\x1a\n\x02\x8c\xb0\x134&&\x00\x02\x00\x00\x00\x00\x06\x00\x05\x00\x00\x17\x00@\x00\x00\x00\x14\a\x01\x06\"&5\x11!\"&5\x11463!\x11462\x17\t\x01\x11\x14\x06#!\"&54&>\x023!265\x114&#!*\x02.\x0354&>\x023!2\x16\x04\xa0\x13\xfd\xe0\x134&\xfe@\x1a&&\x1a\x01\xc0&4\x13\x02 \x01s\xa9w\xfe\xc0\r\x13\x02\x01\x05\x0f\r\x01@B^^B\xfe\xe0\x01\x14\x06\x11\x06\n\x04\x02\x01\x05\x0f\r\x01@w\xa9\x02\x9a4\x13\xfd\xe0\x13&\x1a\x01 &\x1a\x01\x80\x1a&\x01 \x1a&\x13\xfd\xe0\x013\xfd@w\xa9\x13\r\x04 \x15\x1a\r^B\x02\xc0B^\x02\x04\a\v\b\x04 \x15\x1a\r\xa9\x00\x03\x00\x00\xff\x80\x06\x80\x05\x80\x00\x06\x00\r\x00I\x00\x00\x01&5!\x15\x14\x16%5!\x14\a>\x017\x15\x14\x0e\x02\a\x06\a\x0e\x01\x15\x14\x1632\x16\x1d\x01\x14\x06#!\"&=\x014632654&'&'.\x03=\x01463!5463!2\x16\x1d\x01!2\x16\x01\xcaJ\xff\x00\xbd\x04\xc3\xff\x00J\x8d\xbd\x80S\x8d\xcdq*5&\x1d=CKu\x12\x0e\xfc\xc0\x0e\x12uKC=\x1d&5*q͍S8(\x01 ^B\x02@B^\x01 (8\x02\x8d\xa2\xd1`N\xa8\xf6`Ѣ\x1d\xa8\u0380G\x90tO\x056)\"M36J[E@\x0e\x12\x12\x0e@E[J63M\")6\x05Ot\x90G\x80(8`B^^B`8\x00\x00\x00\t\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00\x1f\x00'\x00,\x002\x00\x81\x00\x91\x00\x00\x016'&\a\x06\x17\x16'&\a\x06\x17\x1676'6'&\a\x06\x17\x16\x176&'&\x06\x17\x16\x176'&\a\x06\x17\x1e\x014#\"\x147&\x06\x17\x166\x014\x00 \x00\x15\x14\x12\x17\x16654'\x0e\x02.\x01'&'.\x03632\x1e\x01\x17\x1e\x0126767.\x03547&76\x16\x1f\x0162\x17>\x02\x17\x16\a\x16\x15\x14\x0e\x03\a\x16\x15\x14\x06\x15\x14\x1676\x12\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\a\x04\a\t\x05\x04\a\t\x17\x05\a\x06\x06\a\x05\x06/\x02\a\a\x01\x03\a\b\x16\x02\x01\x03\x06\b\x05\x06[\x02\v\t\x04\x02\v\t.\f\n=\x02\x16\x02\x02\x14\x02\x82\xfe\xd4\xfeX\xfe\xd4Ě\x12\x11\x01\x06\x134,+\b\x17\"\x02\x05\v\x03\v\x0e\x06\x12*\f\x10+, \x0e\a\x1a1JH'5\x18\x1d\x13G\x19\x1a:\x8c:\v#L\x13\x1d\x185\x1c+@=&#\x01\x11\x12\x9a\xc4\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01P\x06\a\a\x05\x06\a\a.\a\x03\x04\b\b\x03\x041\x04\x04\x02\x04\x05\x03\x02\x13\x01\a\x02\a\b\a\x06G\a\x04\x03\a\a\x04\x03\x04\x10\x10\x0f\a\x04\a\b\x04\x01E\xd4\x01,\xfe\xd4ԧ\xfe\xf54\x03\x10\f4+\x01\x03\x01\t\x1f\x1a;\x0f\x01\x05\v\b\a\x04\x1b\x16\x1c\x1c\a\x06/\x16\x06\x195cFO:>J\x06\x1b\x10\x10\x11\x11\a\x16\x1e\x06J>:O9W5$\x10\x04\x1f@(b\x02\f\x10\x034\x01\v\x02\x87\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x04\x00\x00\xff\x80\x06\x80\x05\xc0\x00\a\x00\x0f\x00'\x00?\x00\x00$4&\"\x06\x14\x162$4&\"\x06\x14\x162\x13\x11\x14\x06#!\"&5\x11463!\x1e\x013!267!2\x16\x01\x06#!\x11\x14\x06#!\"&5\x11!\"'&7\x0162\x17\x01\x16\x05\x00&4&&4\x01&&4&&4\xa68(\xfa@(88(\x01\xab\x15c=\x01\x00=c\x15\x01\xab(8\xfe\xbb\x11*\xff\x00&\x1a\xff\x00\x1a&\xff\x00*\x11\x11\x1f\x01\xc0\x126\x12\x01\xc0\x1f&4&&4&&4&&4&\x01 \xfe\xc0(88(\x01@(88HH88\x02`(\xfe@\x1a&&\x1a\x01\xc0('\x1e\x01\xc0\x13\x13\xfe@\x1e\x00\x00\x00\x00\x02\x00\x00\xff\x80\x05\xff\x05\x80\x001\x00c\x00\x00\x014&'.\x0254654'&#\"\x06#\"&#\"\x0e\x01\a\x06\a\x0e\x02\x15\x14\x16\x15\x14\x06\x14\x1632632\x16327>\x01\x127\x14\x02\x06\a\x06#\"&#\"\x06#\"&54654&54>\x02767632\x1632632\x16\x15\x14\x06\x15\x14\x1e\x02\x17\x1e\x01\x05\u007f\x0e\v\f\n\b\n\n\x04\t\x13N\x14<\xe8;+gC8\x89A`\u007f1\x19\x16\x18\x16\x18a\x199\xe19\xb5g\x81\xd5w\x80\x8c\xfc\x9b|\xca9\xe28\x18a\x19Ie\x16\x19$I\x80VN\x9a\xc2z<\xe7:\x13L\x14QJ\n\x04\x03\f\x02\x10\x12\x02\xc6,\x8b\x1b\x1e\x1c-\x1a\x17[\x16%\x12\x01\t0\x17\x18\x1661I\xe9\xef\x81(\xa0)\x17W,\x1d\x16\x1f$-\xd7\x01\x14\x8b\xa5\xfe\xbb\xfb7,\x1d\x1doI\x18X\x17(\xa1)o\xd5ζA;=N0\neT\x17Z\x17\r\x18\t \x04(\x9d\x00\x00\x01\x00\x00\x00\x00\x05\x80\x05\x80\x00O\x00\x00\x01\x14\x06\a\x06\a\x06#\".\x03'&'&\x00'&'.\x0454767>\x0132\x17\x16\x17\x1e\x02\x17\x1e\x02\x15\x14\x0e\x02\x15\x14\x1e\x02\x17\x1e\x01\x17\x1e\x0332>\x0232\x1e\x01\x17\x1e\x02\x17\x16\x17\x16\x05\x80\x14\v\x15e^\\\x1b4?\x1fP\tbM\u007f\xfe\xeeO0#\x03\x1e\v\x12\a382\x19W\x1b\x0e\a\x12#\v& \x0f\x03\x1d\x0e9C9\n\a\x15\x01Lĉ\x02\"\x0e\x1b\t\x1282<\x14\x0e\x1d*\x04\x199F\x13F\x06\x03\x01(\x1bW\x19283\a\x12\v\x1e\x03#0O\x01\x12\u007fMb\tP\x1f?4\x1b\\^e\x15\v\x14\x03\x06F\x13F9\x19\x04*\x1d\x0e\x14<28\x12\t\x1b\x0e\"\x02\x89\xc4L\x01\x15\a\n9C9\x0e\x1d\x03\x0f &\v#\x12\a\x00\x00\x00\x02\x00\x00\x00\x00\x05\x80\x05\x80\x00\x0f\x00\x1f\x00\x00\x01!\"\x06\x15\x11\x14\x163!265\x114&\x17\x11\x14\x06#!\"&5\x11463!2\x16\x04`\xfc\xc0B^^B\x03@B^^ީw\xfc\xc0w\xa9\xa9w\x03@w\xa9\x05\x00^B\xfc\xc0B^^B\x03@B^\xa0\xfc\xc0w\xa9\xa9w\x03@w\xa9\xa9\x00\x02\x00\x00\xff\x97\x05\x00\x05\x80\x00\x06\x00#\x00\x00\x01!\x11\x017\x17\x01\x132\x17\x1e\x01\x15\x11\x14\x06\a\x06#\"'\t\x01\x06#\"'.\x015\x1146763\x04\x80\xfc\x00\x01\xa7YY\x01\xa7\f\x17\x15!''!\x13\x190#\xfeG\xfeG$/\x17\x15!''!\x15\x17\x05\x00\xfb&\x01\x96UU\xfej\x05Z\t\r8\"\xfa\xf7\"8\r\b \x01\xa8\xfeX!\t\r8\"\x05\t\"8\r\t\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00G\x00W\x00\x00\x014.\x04'.\x02#\"\x0e\x02#\".\x02'.\x01'.\x0354>\x0254.\x01'.\x05#\"\a\x0e\x01\x15\x14\x1e\x04\x17\x16\x00\x17\x1e\x0532676\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00\x04 1.-\x06\x05\x1c\x16\n\x0f+$)\r\a\x13\f\x16\x03c\x8e8\x02\r\x06\a)1)\n\x14\x03\x03\x18\x1a\x1b\x17\n\v05.D\x05\x05\r\a\x12\x02<\x019\xa4\x060\x12)\x19$\x109\x93\x15\x16\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01W\v\n\x17\x1b\x1a\x18\x03\x03\x14\n)1)\a\x06\r\x027\x8fc\x03\x16\f\x13\a\r)$+\x0f\n\x16\x1c\x05\x06-.1 \x04\x16\x15\x939\x10$\x19)\x120\x06\xa4\xfe\xc7<\x02\x12\a\r\x05\x05D.5\x039\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x01\x00,\x00\x00\x06T\x05\x00\x001\x00\x00\x01\x06\a\x16\x15\x14\x02\x0e\x01\x04# '\x16327.\x01'\x16327.\x01=\x01\x16\x17.\x01547\x16\x04\x17&54632\x1767\x06\a6\x06TC_\x01L\x9b\xd6\xfeҬ\xfe\xf1\xe1#+\xe1\xb0i\xa6\x1f!\x1c+*p\x93DNBN,y\x01[\xc6\b\xbd\x86\x8c`m`%i]\x04hbE\x0e\x1c\x82\xfe\xfd\xee\xb7m\x91\x04\x8a\x02}a\x05\v\x17\xb1u\x04&\x03,\x8eSXK\x95\xb3\n&$\x86\xbdf\x159s?\n\x00\x00\x00\x01\x00_\xff\x80\x03\xbf\x06\x00\x00\x14\x00\x00\x01\x11#\"\x06\x1d\x01!\x03#\x11!\x11#\x11!54632\x03\xbf\x9dV<\x01%'\xfe\xfe\xce\xff\x00\xffЭ\x93\x05\xf4\xfe\xf8HH\xbd\xfe\xd8\xfd\t\x02\xf7\x01(ں\xcd\x00\x00\x00\b\x00\x00\xff\xa7\x06\x00\x05\x80\x00T\x00\\\x00d\x00k\x00s\x00z\x00\x82\x00\x88\x00\x00\x00 \x04\x12\x15\x14\x00\a\x06&54654'>\x0454'6'&\x06\x0f\x01&\"\a.\x02\a\x06\x17\x06\x15\x14\x1e\x03\x17\x06\a\x0e\x01\"&'.\x01/\x01\"\x06\x1e\x01\x1f\x01\x1e\x01\x1f\x01\x1e\x03?\x01\x14\x16\x15\x14\x06'&\x0054\x12\x136'&\a\x06\x17\x16\x176'&\a\x06\x17\x16\x176'&\a\x06\x16\x176'&\a\x06\x17\x16\x176'&\x06\x17\x1674\a\"\x15\x14727&\a\x06\x166\x02/\x01\xa2\x01a\xce\xfe\xdb\xe8\x1b\x1a\x0149[aA)O%-\x1cj'&]\xc6]\x105r\x1c-%O)@a[9'\n\x150BA\x17\x13;\x14\x14\x15\x10\x06\f\a\a\x16+\n\n\r>HC\x16\x17\x01\x1a\x1b\xe8\xfe\xdb\xceU\x03\n\n\x03\x03\n\t#\a\t\n\x06\a\t\n$\t\t\b\t\t\x122\b\f\f\b\t\r\fA\x03\x10\x0f\b\x11\x0fC\x11\x10\x11\x10:\x02\x10\x10\x04 \x05\x80\xce\xfe\x9f\xd1\xfb\xfeoM\x05\x18\x12\x03\x93=a-\x06\x186O\x83UwW[q\t(\x18\x18\x1a\x1a\v -\tq[WwU\x82P6\x18\x06$C\n\n+) (\x04\x03\t\x0e\x0e\x05\x05\n8\x17\x17&/\r\x01\x04\x04&e\x04\x12\x18\x05M\x01\x91\xfb\xd1\x01a\xfc\u007f\a\x05\x03\x05\a\x05\x06\x1a\x05\v\t\x06\x05\v\n&\a\f\r\a\x05\x1a$\b\v\f\t\b\v\f\x10\v\x05\x04\x16\x04\x06\a\r\x02\v\r\x02\x15\v\x02\x03\x18\b\x00\x00\x00\x01\x00\x00\x00\x00\x06\x80\x05\x80\x00%\x00\x00\x01\x11\x14\x06+\x01\"&5\x114&\"\x06\x1d\x0132\x16\x15\x11\x14\x06#!\"&5\x11463!54\x00 \x00\x06\x80&\x1a@\x1a&\x96Ԗ`(88(\xfc@(88(\x02\xa0\x01\a\x01r\x01\a\x03\xc0\xff\x00\x1a&&\x1a\x01\x00j\x96\x96j\xc08(\xfd\xc0(88(\x02@(8\xc0\xb9\x01\a\xfe\xf9\x00\x00\x00\x05\x00\x00\xff\x80\a\x80\x05\x80\x00\x0f\x00\x19\x00#\x00'\x00+\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x15\"\x06\x1d\x01!54&#\x11265\x11!\x11\x14\x16375!\x1535!\x15\x06\xe0B^^B\xf9\xc0B^^B\r\x13\x06\x80\x13\r\r\x13\xf9\x80\x13\r`\x01\x00\x80\x01\x80\x05\x80^B\xfb@B^^B\x04\xc0B^\x80\x13\r\xe0\xe0\r\x13\xfb\x00\x13\r\x02`\xfd\xa0\r\x13\x80\x80\x80\x80\x80\x00\x03\x00\x00\x00\x00\x05\x80\x05\x80\x00\a\x00!\x00=\x00\x00\x00\x14\x06\"&462\x01\x16\a\x06+\x01\"&'&\x00'.\x01=\x01476;\x01\x16\x04\x17\x16\x12\x05\x16\a\x06+\x01\"&'&\x02\x00$'.\x01=\x01476;\x01\f\x01\x17\x16\x12\x01\x80p\xa0pp\xa0\x02p\x02\x13\x12\x1d\x87\x19$\x02\x16\xfe\xbb\xe5\x19!\x15\x11\x1a\x05\xa0\x01$qr\x87\x02\r\x02\x14\x12\x1c\x8f\x1a%\x01\f\xb2\xfe\xe3\xfe}\xd7\x19#\x14\x12\x1a\x03\x01\x06\x01ߺ\xbb\xd6\x01\x10\xa0pp\xa0p\xfe\xc5\x1c\x14\x15!\x19\xe5\x01E\x16\x02$\x19\x87\x1d\x12\x11\r\x87rq\xfeܢ\x1b\x14\x14#\x19\xd7\x01\x83\x01\x1d\xb2\r\x01%\x19\x8f\x1c\x12\x12\rֻ\xba\xfe!\x00\x05\x00\x00\x00\x00\x06\x00\x05\x00\x00\a\x00\x0f\x00\x1f\x00)\x00?\x00\x00\x00\x14\x06\"&462\x04\x14\x06\"&462\x17\x114&#!\"\x06\x15\x11\x14\x163!26\x01!\x03.\x01#!\"\x06\a\x01\x11\x14\x06#!\"&5\x1147\x13>\x013!2\x16\x17\x13\x16\x04\x10/B//B\x01//B//B\x9f\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\xfb2\x04\x9c\x9d\x04\x18\x0e\xfc\xf2\x0e\x18\x04\x04\xb1^B\xfb@B^\x10\xc5\x11\\7\x03\x0e7\\\x11\xc5\x10\x01aB//B//B//B/\xf0\x01@\r\x13\x13\r\xfe\xc0\r\x13\x13\x01\xed\x01\xe2\r\x11\x11\r\xfd~\xfe\xc0B^^B\x01@\x192\x02^5BB5\xfd\xa22\x00\x02\x00\x00\xff\x83\a\x00\x05\x80\x00.\x004\x00\x00\x012\x16\x14\x06#\x11\x14\x06#\x00%\x0e\x01\x16\x17\x0e\x01\x1e\x02\x17\x0e\x01&'.\x0467#\"&=\x01463! \x012\x16\x15\x03\x11\x00\x05\x11\x04\x06\x805KK5L4\xfe_\xfeu:B\x04&\x14\x06\x121/&\x1d\xa5\xac.\a-\x13\x1b\x03\n\x11zB^^B\x01\xe0\x01\xb3\x01\xcd4L\x80\xfev\xfe\x8a\x01y\x03\x80KjK\xfe\x804L\x01[!\x13^k'!A3;)\x1e:2\x1b*\x17\x81\x0454\x127&5462\x16\x15\x14\a\x16\x12\x15\x14\x1e\x03\x03\x90\x10;U gI\xfdv\x05\x14\xfe\xf60Z\x99\xba\x99Z0\x04\xc0L4\xfe@\x96Ԗ\xfe@4L2RX='\xea\xbe\b8P8\b\xbe\xea'=XR\xb0 U;\x10\x10Ig\x010\x01,\x02\x143lb??bl3\xfd\xec\xfe\xd44Lj\x96\x96jL4*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x1c\xfe\xfb\x98\x8b\xf2\xaa\x93\\\x00\x00\x00\x01\x00\x02\xff\x80\x05\xfe\x05}\x00I\x00\x00\x01\x17\x16\a\x06\x0f\x01\x17\x16\a\x06/\x01\a\x06\a\x06#\"/\x01\a\x06'&/\x01\a\x06'&?\x01'&'&?\x01'&76?\x01'&76\x1f\x017676\x1f\x0176\x17\x16\x1f\x0176\x17\x16\x0f\x01\x17\x16\x17\x16\a\x05`\x8a\x1e\n\f(\xbc5\f\x1f\x1d)\xba0\n)\f\a\x1f\x14\x87\x87\x1c*)\n0\xba)\x1d\x1f\f5\xbc(\f\n\x1e\x8a\x8a\x1e\n\f(\xbc5\f\x1f\x1d)\xba0\n))\x1d\x87\x87\x1d))\n0\xba)\x1d\x1f\f5\xbc(\f\n\x1e\x02\x80\x87\x1c*)\n0\xba)\x1d\x1f\f5\xbc(\f\x02\x16\x8a\x8a\x1e\n\v)\xbc5\f\x1f\x1d)\xba0\n)*\x1c\x87\x87\x1c*)\n0\xba)\x1d\x1f\f5\xbc)\n\f\x1f\x8b\x8b\x1e\v\n)\xbc5\f\x1f\x1d)\xba0\n)*\x1c\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x00\a\x005\x00h\x00\x00$4&\"\x06\x14\x162\x014&#!4>\x0254&#\"\a\x06\a\x06\a\x06\a\x06+\x01\x1132\x1e\x013254'>\x014'654&'!267\x14\x06+\x01\x06\a\x16\x15\x14\a\x16\x06#\"'&#!\"&5\x11463!2>\x05767>\x0432\x16\x15\x14\a!2\x16\x01\x00&4&&4\x05\xa6N2\xfd\xc0\x1e$\x1eYG\x18B\x18\r(HG\x1eEG H\xbe\xc5Q\xbd\x05\x1e#\x125\x14\x0f\x01K4L\x80\x97i\xa9\x04!\x03<\x01\xac\x8d\x85\xbd\xa4;\xfe\xe05KK5\x01 \n\x17\x18\x15\x1b\x0e\x18\x02A#\r(\"/?&}\xa3\x16\x01vh\x98\xa64&&4&\x02\x803M\x1495S+C=\x8b,\x15@QQ\x199\xfd\x80@@\xa7\x1a\x1e\x10IJ 2E\x19=\x11L5i\x98>9\x15\x16eM\x8b\xa1E;K5\x02\x805K\t\x13\x11\x1c\x0f\x1c\x03J7\x15R>@#\x86zD<\x98\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x005\x00=\x00q\x00\x00%3\x11#\".\x02'&'&'&'.\x04#\"\x06\x15\x14\x1e\x02\x15!\"\x06\x15\x14\x163!\x0e\x01\x15\x14\x17\x06\x14\x16\x17\x06\x15\x14\x1632>\x01$4&\"\x06\x14\x162\x13\x11\x14\x06#!\"\a\x06#\"&?\x01&547&'#\"&5463!&54632\x1e\x03\x17\x16\x17\x1e\x063!2\x16\x05` #A<(\x1d\b\x04H(\x0e\x18\x01\x13\x12\x16\x15\bGY\x1e$\x1e\xfd\xc02NL4\x01K\x0f\x145\x12#\x1e\x04aWTƾ\x01h&4&&4\xa6K5\xfe\xe0;\xa4\xbe\u007f\x8e\xb0\x01\x01=\x03!\x04\xa9i\x97\x98h\x01v\x16\xa3}&?/\"(\r#A\x02\x18\x0e\x1b\x15\x18\x17\n\x01 5K\x80\x02\x80\x182*!\t\x05Q@\x16.\x03'!&\x17=C+S59\x14M34L\x11=\x19E2 JI\x10\x18 UR@@&4&&4&\x02\x80\xfd\x805K;E\x9b\x8c\x05Lf\x16\x159>\x98ig\x98R\x157J\x03\x1c\x0f\x1c\x11\x13\tK\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\a\x005\x00h\x00\x00\x044&\"\x06\x14\x162\x134#\"\a.\x01\"\a&#\"\x06\a\x114&#\"\x06\x15\x11\".\x02#\"\x06\x15\x14\x17\x16\x17\x16\x17\x16\x17\x16\x1d\x01!54>\x017\x14\a\x06\x15\x11\x14\x06#!\"&5\x114.\x05'&'.\x0454632\x17\x114632\x16\x1d\x01\x16\x17632\x176\x16\x05\x00&4&&4\xa6\xa7\x1a\x1e\x10IJ 2E\x19=\x11L43M\x1495S+C=\x8b,\x15@QQ\x199\x02\x80@@\x80E;K5\xfd\x805K\t\x13\x11\x1c\x0f\x1c\x03J7\x15R>@#\x86zD<\x98gi\x98>9\x15\x16eM\x8b\xa1Z4&&4&\x03<\xbd\x05\x1e#\x125\x14\x0f\x01K4LN2\xfd\xc0\x1e$\x1eYG\x18B\x18\r(HG\x1eEG H\xbe\xc5V\x85\xbd\xa4;\xfe\xe05KK5\x01 \n\x17\x18\x15\x1b\x0e\x18\x02A#\r(\"/?&}\xa3\x16\x01vh\x98\x97i\xa9\x04!\x03<\x01\xac\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x004\x00<\x00p\x00\x00\x014.\x01=\x01!\x15\x14\x0e\x02\a\x06\a\x06\a\x06\a\x0e\x04\x15\x14\x1632>\x023\x11\x14\x163265\x11\x16327\x16267\x16326\x024&\"\x06\x14\x162\x01\x14\x06/\x01\x06#\"'\x06\a\x15\x14\x06#\"&5\x11\x06#\"&54>\x03767>\x065\x11463!2\x16\x15\x11\x14\x17\x16\x05\x80@@\xfd\x80\x182*!\t\x05Q@\x16.\x03'!&\x17=C+S59\x14M34L.9E2 JI\x10\x18 UR\x80&4&&4\x01&\x9b\x8c\x05Lf\x16\x156A\x98ig\x986Jy\x87#@>R\x157J\x03\x1c\x0f\x1c\x11\x13\tK5\x02\x805K;E\x02@TƾH #A<(\x1d\b\x04H(\x0e\x18\x01\x13\x12\x16\x15\bGY\x1e$\x1e\xfd\xc02NL4\x01K#5\x12#\x1e\x04a\x03=4&&4&\xfdD\x8e\xb0\x01\x01=\x03\x1e\a\xa9i\x97\x98h\x01v\x16\xa3}&?/\"(\r#A\x02\x18\x0e\x1b\x15\x18\x17\n\x01 5KK5\xfe\xe0;\xa4\xbe\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x0154&#!764/\x01&\"\a\x01\a\x06\x14\x1f\x01\x01\x162?\x0164/\x01!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x00&\x1a\xfe\n\xbd\x13\x13[\x126\x12\xfe\x96[\x12\x12[\x01j\x126\x12[\x12\x12\xbd\x01\xf6\x1a&\x01\x00\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02@\x80\x1a&\xbd\x134\x13[\x12\x12\xfe\x96[\x126\x12[\xfe\x96\x12\x12[\x126\x12\xbd&\x01+\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x004/\x01\x01&\"\x0f\x01\x06\x14\x1f\x01!\"\x06\x1d\x01\x14\x163!\a\x06\x14\x1f\x01\x1627\x017$\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x05\x12[\xfe\x96\x126\x12[\x12\x12\xbd\xfe\n\x1a&&\x1a\x01\xf6\xbd\x13\x13[\x126\x12\x01j[\x01\r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02e6\x12[\x01j\x12\x12[\x126\x12\xbd&\x1a\x80\x1a&\xbd\x134\x13[\x12\x12\x01j[\xfe\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x004'\x01'&\"\x0f\x01\x01\x06\x14\x1f\x01\x162?\x01\x11\x14\x16;\x01265\x11\x17\x162?\x01$\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x04\x12\xfe\x96[\x126\x12[\xfe\x96\x12\x12[\x126\x12\xbd&\x1a\x80\x1a&\xbd\x134\x13[\x01\x0e\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02f6\x12\x01j[\x12\x12[\xfe\x96\x126\x12[\x12\x12\xbd\xfe\n\x1a&&\x1a\x01\xf6\xbd\x13\x13[\xfd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x004/\x01&\"\x0f\x01\x114&+\x01\"\x06\x15\x11'&\"\x0f\x01\x06\x14\x17\x01\x17\x162?\x01\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x04\x12[\x126\x12\xbd&\x1a\x80\x1a&\xbd\x134\x13[\x12\x12\x01j[\x126\x12[\x01j\x01\x0e\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02d6\x12[\x12\x12\xbd\x01\xf6\x1a&&\x1a\xfe\n\xbd\x13\x13[\x126\x12\xfe\x96[\x12\x12[\x01j\x00\xff\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x01\xd8\x02\x18\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x01\x0e\x01\a2>\x01767676\x17&67>\x01?\x01\x06&'\x14\a4&\x06'.\x02'.\x01'.\x03\"\x0e\x01#&\x0e\x02\a\x0e\x01\a6'&\a6&'3.\x02'.\x01\a\x06\x1e\x01\x15\x16\x06\x15\x14\x16\a\x0e\x01\a\x06\x16\x17\x16\x0e\x02\x0f\x01\x06&'&'&\a&'&\a6'&\a>\x01567>\x02#\x167>\x0176\x1e\x013\x166'\x16'&'&\a\x06\x17&\x0e\x01'.\x01'\"\a6&'6'.\x01\a\x0e\x01\x1e\x02\x17\x16\a\x0e\x02\a\x06\x16\a.\x01'\x16/\x01\"\x06&'&76\x17.\x01'\x06\a\x167>\x0176\x177\x16\x17&\a\x06\a\x16\a.\x02'\"\a\x06\a\x16\x17\x1e\x027\x16\a6\x17\x16\x17\x16\a.\x01\a\x06\x167\"\x06\x14\a\x17\x06\x167\x06\x17\x16\x17\x1e\x02\x17\x1e\x01\x17\x06\x16\a\"\x06#\x1e\x01\x17\x1e\x0276'&'.\x01'2\x1e\x02\a\x06\x1e\x02\x17\x1e\x01#2\x16\x17\x1e\x01\x17\x1e\x03\x17\x1e\x01\x17\x162676\x16\x17\x167\x06\x1e\x02\x17\x1e\x01\x1767\x06\x16765\x06'4.\x026326&'.\x01'\x06&'\x14\x06\x15\"'>\x017>\x03&\a\x06\a\x0e\x02\a\x06&'.\x0154>\x01'>\x017>\x01\x1667&'&#\x166\x17\x1674&7\x167\x1e\x01\x17\x1e\x0267\x16\x17\x16\x17\x16>\x01&/\x0145'.\x0167>\x0276'27\".\x01#6'>\x017\x1676'>\x017\x16647>\x01?\x016#\x1676'6&'6\x1676'&\x0367.\x01'&'6.\x02'.\x03\x06#\a\x0e\x03\x17&'.\x02\x06\a\x0e\x01\a&6'&\x0e\x04\a\x0e\x01\a.\x015\x1e\x01\x17\x16\a\x06\a\x06\x17\x14\x06\x17\x14\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x03D\x02\x0f\x06\x02\x05\x05\x01\x06\x10\x0e&\"\x11\x02\x17\x03\x03\x18\x03\x02\f\v\x01\x06\t\x0e\x02\n\n\x06\x01\x02\x0f\x02\x01\x03\x03\x05\x06\b\a\x01\x03\x06\x03\x06\x02\x03\v\x03\x0f\x10\n\x06\t\x03\a\x05\x01\x0f\x14\x03\b4\a\x05\x01\a\x01\r\x1c\x04\x03\x1a\x03\x05\a\a\x02\x01\x06\x05\x04\x03\v\x13\x04\a\t\x17\x06\x05$\x19!\x06\x06\a\f\x03\x02\x03\t\x01\f\a\x03#\x0f\x05\r\x04\t\n\x13\x05\x0e\x03\t\f\t\x04\x04\f\x0f\b\n\x01\x11\x10\b\x01\t\x05\b\b\x03\x1c\n\x13\x1b\a\x1b\x06\x05\x01\v\n\r\x02\x0e\x06\x02\r\n\x01\x03\x06\x05\x05\b\x03\a \n\x04\x18\x11\x05\x04\x04\x01\x03\x04\x0e\x03.0\x06\x06\x05\x10\x02\"\b\x05\x0e\x06\a\x17\x14\x02\a\x02\x04\x0f\x0e\b\x10\x06\x92Y\a\x05\x04\x02\x03\n\t\x06\x01+\x13\x02\x03\r\x01\x10\x01\x03\a\a\a\x05\x01\x02\x03\x11\r\r!\x06\x02\x03\x12\f\x04\x04\f\b\x02\x17\x01\x01\x03\x01\x03\x19\x03\x01\x02\x04\x06\x02\x1a\x0f\x02\x03\x05\x02\x02\b\t\x06\x01\x03\n\x0e\x14\x02\x06\x10\b\t\x16\x06\x05\x06\x02\x02\r\f\x14\x03\x05\x1b\b\n\f\x11\x05\x0f\x1c\a$\x13\x02\x05\v\a\x02\x05\x1a\x05\x06\x01\x03\x14\b\x0e\x1f\x12\x05\x03\x02\x02\x04\t\x02\x06\x01\x01\x14\x02\x05\x16\x05\x03\r\x02\x01\x03\x02\x01\t\x06\x02\v\f\x13\a\x01\x04\x06\x06\a\"\a\r\x13\x05\x01\x06\x03\f\x04\x02\x05\x04\x04\x01\x01\x03\x03\x01\a+\x06\x0f\a\x05\x02\x05\x18\x03\x19\x05\x03\b\x03\a\x05\n\x02\v\b\a\b\x01\x01\x01\x01\x01\x0f\a\n\n\x01\x0e\x11\x04\x15\x06\a\x04\x01\b\a\x01\t\a\x05\x05\x05\t\f\b\a\x05\x1f\x03\a\x02\x03\x04\x16\x02\x11\x03\x03\x12\r\n\x10\x03\f\t\x03\x11\x02\x0f\x16\x11\xbdΑ\x03\x13\x03\x12\x06\x01\a\t\x10\x03\x02\n\x04\v\x06\a\x03\x03\x05\x06\x02\x01\x15\x0f\x05\f\t\v\x06\x05\x02\x01\a\x0e\x05\x03\x0f\t\x0e\x04\r\x02\x03\x06\x02\x02\x13\x02\x04\x03\a\x13\x1b\x02\x04\x10\x10\x01\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfe\xc5\x01\x11\x01\n\f\x01\a\b\x06\x06\b\x13\x02\x16\x01\x02\x05\x05\x16\x01\x10\r\x02\x06\a\x02\x04\x01\x03\t\x18\x03\x05\f\x04\x02\a\x06\x05\n\n\x02\x01\x01\x05\x01\x02\x02\x01\x05\x06\x04\x01\x04\x10\x06\x04\t\b\x02\x05\t\x04\x06\t\x13\x03\x06\x0e\x05\a\x11\r\b\x10\x04\b\x15\x06\x02\x04\x05\x03\x02\x02\x05\x16\x0f\x19\x05\b\t\r\r\t\x05\x01\x0e\x0f\x03\x06\x17\x02\r\n\x01\x0f\f\x04\x0f\x05\x18\x05\x06\x01\n\x01\x18\b\x01\x12\a\x02\x04\t\x04\x04\x01\x17\f\v\x01\x19\x01\x0f\b\x0e\x01\f\x0f\x04\x02\x05\a\t\a\x04\x04\x01\n\x04\x01\x05\x04\x02\x04\x14\x04\x05\x19\x04\t\x03\x01\x04\x02\a\b\f\x04\x02\x03\r\x02\x0f\x1a\x01\x02\x02\t\x01\x0e\a\x05\x10\t\x04\x03\x06\x06\f\x06\x03\x0e\b\x01\x01P\x8e\a\x01\x01\x10\x06\x06\b\v\x01\x1c\x11\x04\v\a\x02\x0e\x03\x05\x1b\x01 '\x04\x01\f-\x03\x03(\b\x01\x02\v\t\x06\x05#\x06\x06\x1c\t\x02\a\x0e\x06\x03\x0e\b\x02\x14*\x19\x04\x05\x15\x04\x03\x04\x04\x01\a\x15\x10\x16\x02\x06\x1b\x15\t\b$\x06\a\r\x06\n\x02\x02\x11\x03\x04\x05\x01\x02\"\x04\x13\b\x01\r\x12\v\x03\x06\x12\x06\x04\x05\b\x18\x02\x03\x1d\x0f!\x01\t\b\t\x06\a\x12\x04\b\x18\x03\t\x02\b\x01\t\x02\x01\x03\x1d\b\x04\x10\r\f\a\x01\x01\x13\x03\x0f\b\x03\x03\x02\x04\b*\x10\n!\x11\x10\x02\x0f\x03\x01\x01\x01\x04\x04\x01\x02\x03\x03\t\x06\v\r\x01\x11\x05\x1b\x12\x03\x04\x03\x02\a\x02\x03\x05\x0e\n(\x04\x03\x02\x11\v\a\b\t\t\b\x03\x12\x13\t\x01\x05\b\x04\x13\x10\t\x06\x04\x05\v\x03\x10\x02\f\n\b\b\a\a\x06\x02\b\x10\x04\x05\b\x01\v\x04\x02\r\v\t\x06\a\x02\x01\x01\x02\n\x06\x05\xfc\x82$\x99\x03\x03\x02\a\x01\a\f\x06\n\x02\x02\b\x03\x06\x02\x01\x01\x03\x03\x03\x01\x11\x05\x01\t\x05\x02\x06\x05\x14\x03\x05\x19\x06\x06\x03\x06\v\x02\t\x03\x04\x10\x03\x04\x05\x03\n2\r\x1f\x11\x19\x0f\x16\x04\a\x1b\b\x06\x00\x00\x03\x00\x15\xff\x15\x06~\x05\x80\x00\a\x00\x15\x00/\x00\x00$4&\"\x06\x14\x162\t\x01\x06#\"/\x01&547\x01\x1e\x01\x01\x14\a\x0e\x01#\"\x00\x10\x0032\x16\x17\x16\x14\a\x05\x15\x17>\x0232\x16\x01\x80&4&&4\x02\xaa\xfdV%54'j&&\x02\xa9'\x97\x02\xdc\x17/덹\xfe\xf9\x01\a\xb9:\u007f,\x10\x10\xfe\xdb\xc1\x05\x94{\t\x0f\x11&4&&4&\x01\xe4\xfdV%%l$65&\x02\xa9b\x97\x01\x8c'C\x86\xa7\x01\a\x01r\x01\a!\x1e\v\"\v\xa9\xe0k\x03[G\x14\x00\x00\x00\x06\x00\x00\x00\x00\a\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x1b\x00+\x00;\x00\x00%!5!\x01!5!\x01!5!\x01\x11\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x04\x00\x02\x80\xfd\x80\xfe\x80\x04\x00\xfc\x00\x02\x80\x01\x80\xfe\x80\x02\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\x80\x80\x01\x80\x80\x01\x80\x80\xfc@\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x01\xe6\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x01\xe6\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x00\x00\x01\x00\x05\xff\x80\x05{\x05\x00\x00\x15\x00\x00\x01\x16\a\x01\x11\x14\a\x06#\"'\x01&5\x11\x01&763!2\x05{\x11\x1f\xfe\x13'\r\f\x1b\x12\xff\x00\x13\xfe\x13\x1f\x11\x11*\x05\x00*\x04\xd9)\x1d\xfe\x13\xfd\x1a*\x11\x05\x13\x01\x00\x13\x1a\x01\xe6\x01\xed\x1d)'\x00\x00\x00\x04\x00\x00\x00\x00\a\x00\x06\x00\x00\x03\x00\x17\x00\x1b\x00/\x00\x00\x01!5!\x01\x11\x14\x06#!\"&5\x11!\x15\x14\x163!26=\x01#\x15!5\x01\x11!\x11463!5463!2\x16\x1d\x01!2\x16\x02\x80\x02\x00\xfe\x00\x04\x80^B\xfa@B^\x02\xa0&\x1a\x01@\x1a&`\xff\x00\x04\x00\xf9\x00^B\x01`8(\x02@(8\x01`B^\x05\x00\x80\xfd\x00\xfe B^^B\x01\xe0\xa0\x1a&&\x1a\xa0\x80\x80\x01\xe0\xfe\x80\x01\x80B^\xa0(88(\xa0^\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00G\x00\x00\t\x0276\x17\x16\x15\x11\x14\x06#!\"'&?\x01\t\x01\x17\x16\a\x06#!\"&5\x11476\x1f\x01\t\x01\a\x06#\"'&5\x11463!2\x17\x16\x0f\x01\t\x01'&763!2\x16\x15\x11\x14\a\x06#\"'\x05\x03\xfe\x9d\x01c\x90\x1d)'&\x1a\xfe@*\x11\x11\x1f\x90\xfe\x9d\xfe\x9d\x90\x1f\x11\x11*\xfe@\x1a&('\x1e\x90\x01c\xfe\x9d\x90\x13\x1a\f\f(&\x1a\x01\xc0*\x11\x11\x1f\x90\x01c\x01c\x90\x1f\x11\x11*\x01\xc0\x1a&'\r\f\x1a\x13\x03\xe3\xfe\x9d\xfe\x9d\x90\x1f\x11\x11*\xfe@\x1a&('\x1e\x90\x01c\xfe\x9d\x90\x1e'(&\x1a\x01\xc0*\x11\x11\x1f\x90\x01c\x01c\x90\x13\x05\x11*\x01\xc0\x1a&('\x1e\x90\xfe\x9d\x01c\x90\x1e'(&\x1a\xfe@*\x11\x05\x13\x00\x00\x06\x00\x00\xff\x00\a\x80\x06\x00\x00\x11\x001\x009\x00A\x00S\x00[\x00\x00\x01\x06\a#\"&5\x1032\x1e\x01327\x06\x15\x14\x01\x14\x06#!\"&54>\x0532\x1e\x022>\x0232\x1e\x05\x00\x14\x06\"&462\x00\x10\x06 &\x106 \x01\x14\x06+\x01&'654'\x1632>\x0132\x02\x14\x06\"&462\x02Q\xa2g\x86Rp|\x06Kx;CB\x05\x04\x80\x92y\xfc\x96y\x92\a\x15 6Fe=\nBP\x86\x88\x86PB\n=eF6 \x15\a\xfc\x00\x96Ԗ\x96\xd4\x03V\xe1\xfe\xc2\xe1\xe1\x01>\x03!pR\x86g\xa2Q\x05BC;xK\x06|\x80\x96Ԗ\x96\xd4\x02\x80\x05{QN\x01a*+\x17%\x1d\x8b\xfd\x0ex\x8b\x8bx5eud_C(+5++5+(C_due\x052Ԗ\x96Ԗ\xfe\x1f\xfe\xc2\xe1\xe1\x01>\xe1\xfd\x9fNQ{\x05u\x8b\x1d%\x17+*\x01jԖ\x96Ԗ\x00\x00\x00\x00\x03\x00\x10\xff\x90\x06p\x05\xf0\x00!\x00C\x00i\x00\x00\x014/\x01&#\"\a\x1e\x04\x15\x14\x06#\".\x03'\x06\x15\x14\x1f\x01\x1632?\x016\x014/\x01&#\"\x0f\x01\x06\x15\x14\x1f\x01\x16327.\x0454632\x1e\x03\x176\x00\x14\x0f\x01\x06#\"/\x01&547'\x06#\"/\x01&4?\x01632\x1f\x01\x16\x15\x14\a\x17632\x1f\x01\x05\xb0\x1c\xd0\x1c(*\x1e\x03 \v\x13\a8(\x0f\x19\x1a\f\x1f\x03!\x1c\xce\x1b)(\x1c\x93\x1c\xfdA\x1c\xce\x1c('\x1d\x93\x1c\x1c\xd0\x1b)*\x1e\x03 \v\x13\a8(\x0f\x19\x1a\f\x1f\x03!\x03\u007fU\x93SxyS\xceSXXVzxT\xd0TU\x93SxyS\xceSXXVzxT\xd0\x01@(\x1c\xd0\x1c \x03\x1f\f\x1a\x19\x0f(8\a\x13\v \x03\x1f*(\x1c\xcf\x1b\x1a\x92\x1c\x02\xe8(\x1c\xcf\x1c\x1b\x92\x1c'(\x1c\xd0\x1b\x1f\x03\x1f\f\x1a\x19\x0f(8\a\x13\v \x03\x1f\xfd\xe1\xf0S\x92SU\xcfSx{VXXT\xd0T\xf0S\x92SU\xcfSx{VXXT\xd0\x00\x01\x00\x00\x00\x00\a\x80\x05\x80\x00\x1b\x00\x00\x01\x14\x06#!\"\x005467&54\x0032\x04\x17632\x16\x15\x14\a\x1e\x01\a\x80\xe1\x9f\xfb\xc0\xb9\xfe\xf9\x8et\x02\x01,Ԟ\x01\x01;F`j\x96)\x81\xa8\x01\x80\x9f\xe1\x01\a\xb9\x84\xdb6\x1c\x0f\xd4\x01,\xb0\x8e>\x96jK?\x1e\xd1\x00\x02\x00s\xff\x80\x06\r\x05\x80\x00\x17\x00!\x00\x00%\x16\x06#!\"&7\x01\x11#\"&463!2\x16\x14\x06+\x01\x11\x05\x01!\x01'5\x11#\x11\x15\x05\xf78Ej\xfb\x80jE8\x01\xf7@\x1a&&\x1a\x02\x00\x1a&&\x1a@\xfe\xec\xfe\xf0\x02\xc8\xfe\xf0\x14\x80XY\u007f\u007fY\x03\x19\x01\x8f&4&&4&\xfeqD\xfeS\x01\xad\x1f%\x01\x8f\xfeq%\x00\x00\x00\x00\a\x00\x01\xff\x80\a\x00\x05\x00\x00\a\x00N\x00\\\x00j\x00x\x00\x86\x00\x8c\x00\x00\x002\x16\x14\x06\"&4\x05\x01\x16\a\x06\x0f\x01\x06#\"'\x01\a\x06\a\x16\a\x0e\x01\a\x06#\"'&7>\x017632\x176?\x01'&'\x06#\"'.\x01'&67632\x17\x1e\x01\x17\x16\a\x16\x1f\x01\x01632\x1f\x01\x16\x17\x16\a\x056&'&#\"\a\x06\x16\x17\x1632\x03>\x01'&#\"\a\x0e\x01\x17\x1632\x01\x1754?\x01'\a\x0e\x01\a\x0e\x01\a\x1f\x01\x01'\x01\x15\a\x17\x16\x17\x1e\x01\x1f\x01\x017\x01\a\x06\a\x03\xa64&&4&\x01l\x01\xfb\x1c\x03\x05\x1e\x80\r\x10\x11\x0e\xfdNn\b\x04\x0e\x04\abS\x84\x91\x88VZ\v\abR\x84\x92SD\t\rzz\r\tDS\x92\x84Rb\a\x05)+U\x89\x91\x84Sb\a\x04\x0e\x04\bn\x02\xb2\x0e\x11\x10\r\x80\x1e\x05\x03\x1c\xfb\\.2Q\\dJ'.2Q\\dJ.Q2.'Jd\\Q2.'Jd\x01\x0e`!\x0eO\x1a\x03\x0e\x05\x02\x04\x01\xd7`\x02\xe0\x80\xfd\x00\xa0\t\x02\x05\x04\x0e\x04\x1a\x03`\x80\xfd\xf8\xb1\x02\v\x02\x80&4&&4\x1a\xfer\x14$#\x10@\a\b\x01\x83B\x04\x0110M\x8d5TNT{L\x8e5T\x1f\r\tII\t\r\x1fT5\x8eL;l'OT4\x8eM01\x01\x04B\x01\x83\b\a@\x10#$\x14\x8a*\x843;$*\x843;\xfd;3\x84*$;3\x84*$\x02\xa0:\v$\x14\b/\x1a\x03\x10\x04\x02\x03\x01\xe9 \x02@@\xfeQq`\b\x02\x04\x04\x10\x04\x1a\xfe\xc0@\x01\x98\x8a\x03\x04\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\x1f\x00\"\x00%\x003\x00<\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11!\"&5\x11467\x01>\x013!2\x16\x15\x1163\a\x01!\t\x01!\x13\x01\x11!\x11\x14\x06#!\x11!\x1146\x01\x11!\x11\x14\x06#!\x11\x06\xa0(88(\xfc@(8\xfd\xe0(8(\x1c\x01\x98\x1c`(\x01\xa0(8D<\x80\xfe\xd5\x01+\xfd\x80\xfe\xd5\x01+\xc4\x01<\xfe\x808(\xfe`\x02\x00(\x03\xd8\xfe\x808(\xfe`\x04\x808(\xfb@(88(\x01 8(\x02\xa0(`\x1c\x01\x98\x1c(8(\xfe\xb8(\xd5\xfe\xd5\x02\xab\xfe\xd5\xfe\xa4\x01<\x01\xa0\xfe`(8\xfd\x80\x01\x00(`\xfc\xf8\x04\x80\xfe`(8\xfd\x80\x00\x00\x00\x01\x00\x04\xff\x84\x05|\x05|\x00?\x00\x00%\x14\x06#\"'\x01&54632\x17\x01\x16\x15\x14\x06#\"'\x01&#\"\x06\x15\x14\x17\x01\x1632654'\x01&#\"\x06\x15\x14\x17\x01\x16\x15\x14\x06#\"'\x01&54632\x17\x01\x16\x05|\x9eu\x87d\xfc\xf7qܟ\x9es\x02]\n=\x10\r\n\xfd\xa2Ofj\x92L\x03\b?R@T?\xfd\xbb\x1a\"\x1d&\x19\x01\x9a\n>\x10\f\n\xfef?rRX=\x02Ed\x97u\x9ed\x03\bs\x9c\x9f\xdeq\xfd\xa2\n\f\x10=\n\x02_M\x96jiL\xfc\xf7?T@R?\x02E\x18&\x1d \x1b\xfef\n\f\x10>\n\x01\x9a=XRr?\xfd\xbbb\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00!\x001\x00E\x00\x00)\x01\x11!\x013\x114&'\x01.\x01#\x11\x14\x06#!\"&5\x11#\x113\x11463!2\x16\x15\x01\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126\x05\x11\x14\x06#!\"&5\x11463!2\x16\x17\x01\x1e\x01\x01\x80\x03\x00\xfd\x00\x03\x80\x80\x14\n\xfe\xe7\n0\x0f8(\xfd\xc0(8\x80\x808(\x03@(8\xfe\x80\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x02\x808(\xfa\xc0(88(\x03\xa0(`\x1c\x01\x18\x1c(\x01\x80\xfe\x80\x03\x80\x0e1\n\x01\x19\n\x14\xfe`(88(\x01\xa0\xfb\x00\x01\xa0(88(\x02\x00\x01@\r\x13\x13\r\xfe\xc0\r\x13\x13\x13\xfc`(88(\x05@(8(\x1c\xfe\xe8\x1c`\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x04`\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x05\x00\x00\x0f\x00\x1f\x00/\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x06\x00&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01\xe6\x80\x1a&&\x1a\x80\x1a&&\x01\xe6\x80\x1a&&\x1a\x80\x1a&&\x00\x06\x00\x00\xff\xc0\a\x00\x05@\x00\a\x00\x0f\x00\x1f\x00'\x007\x00G\x00\x00$\x14\x06\"&462\x12\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x16\x00\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x80p\xa0pp\xa0pp\xa0pp\xa0\x05\xf0\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\xfa\x80p\xa0pp\xa0\x05\xf0\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13Рpp\xa0p\x01\x90\xa0pp\xa0p\xfd\xa0\xc0\r\x13\x13\r\xc0\r\x13\x13\x03\xe3\xa0pp\xa0p\xfd\xa0\xc0\r\x13\x13\r\xc0\r\x13\x13\x01\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x00\x00\x06\x00\x0f\xff\x00\a\x00\x05\xf7\x00\x1e\x00<\x00L\x00\\\x00l\x00|\x00\x00\x05\x14\x06#\"'7\x1632654\a'>\x0275\"\x06#\x15#5!\x15\a\x1e\x01\x13\x15!&54>\x0354&#\"\a'>\x0132\x16\x15\x14\x0e\x02\a35\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15!5346=\x01#\x06\a'73\x11\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01}mQjB919\x1d+i\x1a\b1$\x13\x10A\x10j\x01M_3<\x02\xfe\x96\x06/BB/\x1d\x19.#U\x18_:IdDRE\x01\u007f\x05\xea\x13\r\xfb@\r\x13\x12\x0e\x04\xc0\r\x13\xfa\x80\xfe\xb1k\x01\x02\b*G\x88j\x05\xec\x13\r\xfb@\r\x13\x12\x0e\x04\xc0\r\x13\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13TP\\BX-\x1d\x1c@\b8\nC)\x12\x01\x025\x98Xs\fJ\x02@\x9f$\x123T4+,\x17\x19\x1b:;39SG2S.7\x19<\xfe\xc1\xc0\r\x13\x13\r\xc0\x0e\x12\x13\x03vcc)\xa1)\f\x11%L\u007f\xfel\xfe}\xc0\r\x13\x13\r\xc0\x0e\x12\x13\x01\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x00\x0f\x005\x00e\x00\x00\x012\x16\x1d\x01\x14\x06#!\"&=\x01463%&'&5476!2\x17\x16\x17\x16\x17\x16\x15\x14\x0f\x01/\x01&'&#\"\a\x06\x15\x14\x17\x16\x17\x16\x17\x16\x17\x03!\x16\x15\x14\a\x06\a\x06\a\x06\a\x06#\"/\x01&'&=\x014'&?\x0157\x1e\x02\x17\x16\x17\x16\x17\x1632767654'&\x06\xe0\x0e\x12\x12\x0e\xf9@\x0e\x12\x12\x0e\x01\xc3\x1c\x170\x86\x85\x01\x042uBo\n\v\x0e\x05\fT\x0e25XzrDCBB\xd5Eh:%\xec\x01\x9b\a)\x170%HPIP{rQ\x8c9\x0f\b\x02\x01\x01\x02f\x0f\x1e\x0f\x05#-+>;I@KM-/Q\"\x02\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12@#-bZ\xb5\x80\u007f\x13\f$&P{<\x12\x1b\x03\x06\x02\x958[;:XICC>\x14.\x1c\x18\xff\x00'5oe80#.0\x12\x15\x17(\x10\f\b\x0e\rl0\x1e&%,\x02\"J&\b9%$\x15\x16\x1b\x1a<=DTI\x1d\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00c\x00s\x00\x00\x13&/\x01632\x17\x163276727\a\x17\x15\x06#\"\a\x06\x15\x14\x16\x15\x17\x13\x16\x17\x16\x17\x16327676767654.\x01/\x01&'&\x0f\x01'73\x17\x167\x17\x16\x15\x14\a\x06\a\x06\a\x06\x15\x14\x16\x15\x16\x13\x16\a\x06\a\x06\a\x06\a\x06#\"'&'&'&5\x114'&\x0154&#!\"\x06\x1d\x01\x14\x163!260%\b\x03\r\x1b<4\x84\"VRt\x1e8\x1e\x01\x02<@<\x13\r\x01\x01\x0e\x06-#=XYhW8+0\x11$\x11\x15\a\x0f\x06\x04\x05\x13\"+d\x0e\x02T\xcdLx\x12\x06\x04-'I\x06\x0f\x03\b\x0e\x06\x15\x0f\x1a&JKkm\x92\xa7uw<=\x16\x10\x11\x19\x05V\x12\x0e\xfa@\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x05!\x02\x02X\x01\x04\a\x03\x04\x01\x02\x0e@\t\t\x19\x0ev\r'\x06\xe5\xfe\xe8|N;!/\x1c\x12!$\x1c8:I\x9cOb\x93V;C\x15#\x01\x02\x03V\n\x03\r\x02&\r\a\x18\f\x01\v\x06\x0f\x1a\a(\v\x13\xfe\x87\xc3mL.A:9 !./KLwP\x9d\x01M\xbc\x19$\xfa\x82@\x0e\x12\x12\x0e@\x0e\x12\x12\x00\x00\n\x00\x00\x00\x00\x06\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\x00%54&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\xfe\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\xfe\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x80^B\xfa\xc0B^^B\x05@B^\xa0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x03\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x03\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01N\xfb\xc0B^^B\x04@B^^\x00\x00\x00\x06\x00\x1b\xff\x9b\x06\x80\x06\x00\x00\x03\x00\x13\x00\x1b\x00#\x00+\x003\x00\x00\t\x01'\x01$\x14\a\x01\x06\"/\x01&47\x0162\x1f\x01%\x17\x0f\x01/\x01?\x01\x01\x17\x0f\x01/\x01?\x01\x01\x17\x0f\x01/\x01?\x01\x01\x17\x0f\x01/\x01?\x01\x04\xa6\x01%k\xfe\xdb\x02*\x12\xfa\xfa\x126\x12\xc6\x12\x12\x05\x06\x126\x12\xc6\xfa\xcbbb\x1e\x1ebb\x1e\x01|\xc4\xc4<<\xc4\xc4<\x03\xdebb\x1e\x1ebb\x1e\xfd\x9ebb\x1e\x1ebb\x1e\x03\xbb\x01%k\xfe\xdb\xd56\x12\xfa\xfa\x12\x12\xc6\x126\x12\x05\x06\x12\x12Ƒ\x1e\x1ebb\x1e\x1eb\xfe\xfc<<\xc4\xc4<<\xc4\xfd^\x1e\x1ebb\x1e\x1eb\x02\x1e\x1e\x1ebb\x1e\x1eb\x00\x00\x00\x04\x00@\xff\x80\a\x00\x05\x00\x00\a\x00\x10\x00\x18\x00M\x00\x00$4&\"\x06\x14\x162\x01!\x11#\"\x0f\x01\x06\x15\x004&\"\x06\x14\x162\x01\x11\x14\x0e\x04&#\x14\x06\"&5!\x14\x06\"&5#\"\x06.\x045463\x114&>\x03?\x01>\x01;\x015463!2\x16\x02\x80LhLLh\xfe\xcc\x01\x80\x9e\r\t\xc3\t\x05\x00LhLLh\x01L\b\x13\x0e!\f'\x03\x96Ԗ\xfe\x80\x96Ԗ@\x03'\f!\x0e\x13\b&\x1a\x01\x01\x04\t\x13\r\xc6\x13?\x1b\xa0&\x1a\x04\x00\x1a&LhLLhL\x02\x80\x01\x00\t\xc3\t\r\xfd\xaehLLhL\x04\xc0\xfc\x00\x0f\x17\x0e\t\x03\x01\x01j\x96\x96jj\x96\x96j\x01\x01\x03\t\x0e\x17\x0f\x1a&\x01@\b6\x16/\x1b\"\r\xc6\x13\x1a\xc0\x1a&&\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00J\x00\x00\x00\x10\x02\x04#\"'6767\x1e\x0132>\x0154.\x01#\"\x0e\x03\x15\x14\x16\x17\x167>\x0176'&54632\x16\x15\x14\x06#\"&7>\x0254&#\"\x06\x15\x14\x17\x03\x06\x17&\x0254\x12$ \x04\x06\x00\xce\xfe\x9f\xd1ok;\x13\t-\x14j=y\xbehw\xe2\x8ei\xb6\u007f[+PM\x1e\b\x02\f\x02\x06\x113ѩ\x97\xa9\x89k=J\x0e\b%\x1762>V\x19c\x11\x04\xce\xfe\xce\x01a\x01\xa2\x01a\x03Q\xfe^\xfe\x9f\xce ]G\"\xb1'9\x89\xf0\x96r\xc8~:`}\x86Ch\x9e \f \a0\x06\x17\x14=Z\x97٤\x83\xaa\xeeW=#uY\x1f2BrUI1\xfe^Fk[\x01|\xe9\xd1\x01a\xce\xce\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00L\x00\x00\x012\x16\x15\x11\x14\x06#!6767\x1e\x0132\x1254.\x02#\"\x0e\x03\x15\x14\x16\x17\x1667676'&54632\x16\x15\x14\x06#\"&7>\x0254&#\"\x06\x15\x14\x17\x03\x06\x17#\"&5\x11463\x04\xe0w\xa9\xa9w\xfd+U\x17\t,\x15i<\xb5\xe5F{\xb6jh\xb5}Z+OM\r\x15\x04\n\x05\x06\x112ϧ\x95\xa7\x87jX\x96բ\x81\xa8\xecW<\"uW\x1f1AqSH1\xfebd\x9a\xa9w\x03\xc0w\xa9\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1b\x00'\x007\x00\x00\x014'!\x153\x0e\x03#\"&4632\x177&#\"\x06\x10\x16326%35#5#\x15#\x153\x153\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x95\x06\xfe\x96\xd9\x03\x1b0U6c\x8c\x8cc\\=hl\x95\xa0\xe0ࠥ\xcb\x01Ymmnnnn\x01\x12\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02w\x1a&\x84\x1846#\x8eȎ;ed\xe1\xfe\xc2\xe1\xd2wnnnnn\x02\x85\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x02\x00\x00\xff\xa3\t\x00\x05]\x00#\x00/\x00\x00\x01\x14\x02\x04#\"$&\x02\x10\x126$3 \x17\a&#\"\x0e\x01\x14\x1e\x0132>\x037!5!\x16%\x15#\x15#5#5353\x15\x05\x9d\xae\xfe\xbeЕ\xfe\xf0\xc4tt\xc4\x01\x10\x95\x01\x1e\xcd\xc7u\xaf{\xd1zz\xd1{S\x8bZC\x1f\x06\xfe`\x02\xb4\f\x03c\xd1\xd2\xd1\xd1\xd2\x02o\xd0\xfe\xbb\xb7t\xc4\x01\x10\x01*\x01\x10\xc4t\xc0\xbfq|\xd5\xfc\xd5|.EXN#\xfc??\xd2\xd1\xd1\xd2\xd1\xd1\x00\x00\x00\x04\x00\x00\x00\x00\a\x80\x05\x00\x00\f\x00\x1c\x00,\x00<\x00\x00\x01!5#\x11#\a\x17673\x11#$\x14\x0e\x02\".\x024>\x022\x1e\x01\x01\x11\"&5!\x14\x06#\x112\x16\x15!46\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03\x00\x01\x80\x80r\x94M*\r\x02\x80\x02\x00*M~\x96~M**M~\x96~M\x02*j\x96\xfb\x80\x96jj\x96\x04\x80\x96\xea&\x1a\xf9\x00\x1a&&\x1a\a\x00\x1a&\x01\x80`\x01\xc0\x89P%\x14\xfe\xe0挐|NN|\x90\x8c\x90|NN|\xfe*\x02\x00\x96jj\x96\xfe\x00\x96jj\x96\x03@\xfb\x80\x1a&&\x1a\x04\x80\x1a&&\x00\x00\x01\x00\x00\x01@\x04\x00\x03\x80\x00\r\x00\x00\x00\x14\a\x01\x06\"'\x01&463!2\x04\x00\x13\xfe@\x134\x13\xfe@\x13&\x1a\x03\x80\x1a\x03Z4\x13\xfe@\x13\x13\x01\xc0\x134&\x00\x00\x00\x00\x01\x00\x00\x01\x00\x04\x00\x03@\x00\r\x00\x00\x00\x14\x06#!\"&47\x0162\x17\x01\x04\x00&\x1a\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x01Z4&&4\x13\x01\xc0\x13\x13\xfe@\x00\x00\x00\x00\x01\x00@\x00\x80\x02\x80\x04\x80\x00\r\x00\x00\x01\x11\x14\x06\"'\x01&47\x0162\x16\x02\x80&4\x13\xfe@\x13\x13\x01\xc0\x134&\x04@\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x13&\x00\x00\x00\x01\x00\x00\x00\x80\x02@\x04\x80\x00\r\x00\x00\x00\x14\a\x01\x06\"&5\x11462\x17\x01\x02@\x13\xfe@\x134&&4\x13\x01\xc0\x02\x9a4\x13\xfe@\x13&\x1a\x03\x80\x1a&\x13\xfe@\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x80\x05\x80\x00\x06\x00\r\x00\x1d\x00\x003!\x11!\x11\x14\x16%\x11!\x11!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\xa0\x02`\xfd\x80\x13\x05m\xfd\x80\x02`\r\x13\x80^B\xfa\xc0B^^B\x05@B^\x04\x80\xfb\xa0\r\x13 \x04`\xfb\x80\x13\x04\xcd\xfb@B^^B\x04\xc0B^^\x00\x02\x00\x00\xff\xc0\x04\x00\x05@\x00\r\x00\x1b\x00\x00\x00\x14\a\x01\x06\"'\x01&463!2\x12\x14\x06#!\"&47\x0162\x17\x01\x04\x00\x13\xfe@\x134\x13\xfe@\x13&\x1a\x03\x80\x1a&&\x1a\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x01\xda4\x13\xfe@\x13\x13\x01\xc0\x134&\x01Z4&&4\x13\x01\xc0\x13\x13\xfe@\x00\x00\x00\x00\x01\x00\x00\xff\xc0\x04\x00\x02\x00\x00\r\x00\x00\x00\x14\a\x01\x06\"'\x01&463!2\x04\x00\x13\xfe@\x134\x13\xfe@\x13&\x1a\x03\x80\x1a\x01\xda4\x13\xfe@\x13\x13\x01\xc0\x134&\x00\x00\x00\x00\x01\x00\x00\x03\x00\x04\x00\x05@\x00\r\x00\x00\x00\x14\x06#!\"&47\x0162\x17\x01\x04\x00&\x1a\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x03Z4&&4\x13\x01\xc0\x13\x13\xfe@\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x00\x00\x1a\x00:\x00\x00\x01\x11\x14\x06#!\"&5\x11\x16\x17\x04\x17\x1e\x02;\x022>\x0176%6\x13\x14\x06\a\x00\a\x0e\x04+\x02\".\x03'&$'.\x015463!2\x16\a\x00^B\xfa@B^,9\x01j\x879Gv3\x01\x013vG9\xaa\x01H9+bI\xfe\x88\\\nA+=6\x17\x01\x01\x176=+A\n[\xfe\xaa\">nSM\x05\xc0A_\x03:\xfc\xe6B^^B\x03\x1a1&\xf6c*/11/*{\xde'\x01VO\x903\xfe\xfb@\a/\x1d$\x12\x12$\x1d/\a@\xed\x18*\x93?Nh^\x00\x03\x00\x00\xff\xb0\x06\x00\x05l\x00\x03\x00\x0f\x00+\x00\x00\x01\x11!\x11\x01\x16\x06+\x01\"&5462\x16\x01\x11!\x114&#\"\x06\a\x06\x15\x11!\x12\x10/\x01!\x15#>\x0332\x16\x01]\xfe\xb6\x01_\x01gT\x02Rdg\xa6d\x04\x8f\xfe\xb7QV?U\x15\v\xfe\xb7\x02\x01\x01\x01I\x02\x14*Gg?\xab\xd0\x03\x8f\xfc!\x03\xdf\x012IbbIJaa\xfc\xdd\xfd\xc8\x02\x12iwE3\x1e3\xfd\xd7\x01\x8f\x01\xf000\x90 08\x1f\xe3\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x004\x00\x00\x00\x10\x02\x06\x04#\"$'&6?\x0163\x16\x17\x1e\x0132>\x024.\x02#\"\x06\a\x17\x16\a\x06#!\"&5\x11476\x1f\x016$32\x04\x16\x06\x00z\xce\xfe䜬\xfe\xcam\a\x01\b\x89\n\x0f\x10\aI\xd4wh\xbd\x8aQQ\x8a\xbdhb\xb4F\x89\x1f\x11\x11*\xfe@\x1a&('\x1e\x82k\x01\x13\x93\x9c\x01\x1c\xce\x03\x1c\xfe\xc8\xfe\xe4\xcez\x91\x84\n\x19\b\x8a\t\x02\n_hQ\x8a\xbdн\x8aQGB\x8a\x1e'(&\x1a\x01\xc0*\x11\x11\x1f\x81eoz\xce\x00\x01\x00(\xff\x15\x06\xeb\x05\xd8\x00q\x00\x00!\x14\x0f\x01\x06#\"'\x01&547\x01\a\x06\"'\x1e\x06\x15\x14\a\x0e\x05#\"'\x01&54>\x047632\x1e\x05\x17&47\x0162\x17.\x06547>\x0532\x17\x01\x16\x15\x14\x0e\x04\a\x06#\".\x05'\x16\x14\x0f\x01\x01632\x17\x01\x16\x06\xeb%k'45%\xfe\x95&+\xff\x00~\x0e(\x0e\x02\x15\x04\x10\x04\b\x03\x1c\x03\x1b\v\x1a\x12\x1a\r(\x1c\xfeh\x1c\t\t\x16\v\x1e\x03\x1e&\n\x10\x11\n\x11\x06\x14\x02\x0e\x0e\x01\\\x0e(\x0e\x02\x15\x04\x10\x04\b\x03\x1c\x03\x1b\v\x1a\x12\x1a\r(\x1c\x01\x98\x1c\t\t\x16\v\x1e\x03\x1e&\n\x10\x11\n\x11\x06\x14\x02\x0e\x0e~\x01\x00+54'\x01k%5%l%%\x01l$65+\x01\x00~\x0e\x0e\x02\x14\x06\x11\n\x11\x10\n&\x1e\x03\x1e\v\x16\t\t\x1c\x01\x98\x1c(\r\x1a\x12\x1a\v\x1b\x03\x1c\x03\b\x04\x10\x04\x15\x02\x0e(\x0e\x01\\\x0e\x0e\x02\x14\x06\x11\n\x11\x10\n&\x1e\x03\x1e\v\x16\t\t\x1c\xfeh\x1c(\r\x1a\x12\x1a\v\x1b\x03\x1c\x03\b\x04\x10\x04\x15\x02\x0e(\x0e~\xff\x00+%\xfe\x95'\x00\x00\a\x00\x00\xff\x80\a\x00\x05\x00\x00\a\x00\x0f\x00!\x00)\x001\x009\x00K\x00\x00\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x01\x136.\x01\x06\a\x03\x0e\x01\a\x06\x1e\x01676&$4&\"\x06\x14\x162\x004&\"\x06\x14\x162\x044&\"\x06\x14\x162\x01\x10\a\x06#!\"'&\x114\x126$ \x04\x16\x12\x01\x80KjKKj\x01\vKjKKj\x01\xf7e\x06\x1b2.\ae<^\x10\x14P\x9a\x8a\x14\x10,\x02bKjKKj\xfd\xcbKjKKj\x02\vKjKKj\x01\x8b\x8d\x13#\xfa\x86#\x13\x8d\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x01KjKKjK\x02\vjKKjK\xfe\x9f\x01~\x1a-\x0e\x1b\x1a\xfe\x82\x05M\x027>\x057&\x0254\x12$ \x04\x04L\xfeh\xfe\x9dя\x82W\x1b\x18.\x98{+9E=\xcc\x01c\xd1\xd1\x01Q\xf0\xfed\xf4FK\xc6\xfe\xfa1A\x05\x0f\x18\x04\x03\x05\x01\n\x02\f\x02\a0\x15)\x18\x1e\v\x9d\xb5\xf0\x01\x9c\x01\xe8\x01\x9c\x04\x80\x8b\xec\x89p\xcbJ2`[Q?l&\x06\b\x8b\xec\x01\x12\xec\xc7\xfe\xa4\xfe٫\b\xafC\x0e\b\x15\x11\x01\x04\x10\x04\x0f\x03\x0e\x02\b5\x178.H(Y\x01\x06\x96\xae\x01'\xab\xab\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x00\x00\x14\x00:\x00d\x00\x00\x00 \x04\x06\x15\x14\x16\x1f\x01\a6?\x01\x17\x1632$64&$ \x04\x16\x10\x06\x04#\"'\x06\a\x06\a#\"&'&4>\x057>\x047.\x01546\x01\x1e\x04\x17\x1e\x06\x14\a\x0e\x01'&'&'\x06# '\x1632$7>\x0154'\x1e\x01\x15\x14\x06\x03Y\xfe\xce\xfe\xf6\x9dj`a#\"\x1c,5NK\x99\x01\n\x9d\x9d\xfd\x9e\x01~\x01E\xbc\xbc\xfe\xbb\xbfVZ|\x9a$2\x03\v\x13\x02\x01\x01\x03\x02\x05\x03\x06\x01\x05$\x10\x1d\x15\n|\x8e\xbc\x05:\n\x15\x1d\x10$\x05\x01\x06\x03\x05\x02\x03\x01\x01\x03\x14\f2$\x9a|ZV\xfe\xf1\xc9:\x1e\xa1\x01(t}\x86\x17\x81\x96\x8e\x04\x80h\xb2fR\x9888T\x14\x13\x1f\n\x0eh\xb2̲\xe8\x89\xec\xfe\xea\xec\x89\x10X(\t\a\x10\r\x03\a\x06\x06\x04\a\x03\a\x01\x06&\x15%(\x18H\xd2w\x8b\xec\xfb\xf8\x18(%\x15&\x06\x01\a\x03\a\x04\x06\x06\a\x03\x0e\x10\x01\a\t(X\x10\x84\x04ZT\\\xf0\x86MKG\xd6{x\xd1\x00\x01\x00\x01\xff\x00\x03|\x05\x80\x00!\x00\x00\x01\x16\a\x01\x06#\"'.\x017\x13\x05\x06#\"'&7\x13>\x013!2\x16\x15\x14\a\x03%632\x03u\x12\v\xfd\xe4\r\x1d\x04\n\x11\x11\x04\xc5\xfej\x04\b\x12\r\x12\x05\xc9\x04\x18\x10\x01H\x13\x1a\x05\xab\x01\x8c\b\x04\x13\x03\xca\x14\x18\xfb{\x19\x02\x05\x1c\x10\x03(e\x01\v\x0f\x18\x039\x0e\x12\x19\x11\b\n\xfe1b\x02\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00U\x00\x00\x01\x11\x14\x06#!\"&5\x1146;\x015!\x1532\x16\x15\x11\x14\x06#!\"&5\x1146;\x015!\x1532\x16\x15\x11\x14\x06#!\"&5\x1146;\x015463!5#\"&5\x11463!2\x16\x15\x11\x14\x06+\x01\x15!2\x16\x1d\x0132\x16\a\x008(\xfe\xc0(88(`\xfe\x00`(88(\xfe\xc0(88(`\xfe\x00`(88(\xfe\xc0(88(`L4\x02\x00`(88(\x01@(88(`\x02\x004L`(8\x01 \xfe\xc0(88(\x01@(8\xc0\xc08(\xfe\xc0(88(\x01@(8\xc0\xc08(\xfe\xc0(88(\x01@(8\xc04L\xc08(\x01@(88(\xfe\xc0(8\xc0L4\xc08\x00\x00\x03\x00\x00\xff\x80\x06\x80\x05\xc0\x00\x13\x00O\x00Y\x00\x00\x01\x11\x14\x06\"&5462\x16\x15\x14\x16265\x1162\x05\x14\x06#\"'.\x01#\"\x06\a\x0e\x01\a\x06#\"'.\x01'.\x01\"\x06\a\x0e\x01\a\x06#\"'.\x01'.\x01#\"\x06\a\x06#\"&5476\x00$32\x04\x1e\x01\x17\x16\x01\x15&\"\a5462\x16\x03\x80\x98И&4&NdN!>\x03!\x13\r\v\f1X:Dx+\a\x15\x04\v\x11\x12\v\x04\x15\a+w\x88w+\a\x15\x04\v\x12\x11\v\x04\x15\a+xD:X1\f\v\r\x13\x01-\x00\xff\x01U\xbe\x8c\x01\r\xe0\xa5!\x01\xfd\x00*,*&4&\x02\xc4\xfd\xbch\x98\x98h\x1a&&\x1a2NN2\x02D\v&\r\x13\n..J<\n$\x06\x11\x11\x06$\n\x01767\x14\a\x0e\x02\a\x16\x15\x14\a\x16\x15\x14\a\x16\x15\x14\x06#\x0e\x01\"&'\"&547&547&547.\x02'&54>\x022\x1e\x02\x02\xe0\x13\x1a\x13l4\r\x13\x13\r2cK\xa0Eo\x87\x8a\x87oED\n)\n\x80\r\xe4\r\x80\n)\nD\x80g-;<\x04/\x19\x19-\r?.\x14P^P\x14.?\r-\x19\x19/\x04<;-gY\x91\xb7\xbe\xb7\x91Y\x03\xc0\r\x13\x13\r.2\x13\x1a\x13 L4H|O--O|HeO\v,\v\x99\x91\x91\x99\v,\vOe\x9bq1Ls2\x1c6%\x1b\x1b%4\x1d\x17\x18.2,44,2.\x18\x17\x1d4%\x1b\x1b%6\x1c2sL1q\x9bc\xabqAAq\xab\x00\x02\x00\x00\xff\xa0\a\x00\x04\xe0\x00\x1a\x004\x00\x00\x01\x15\x14\x06#!\x15\x14\x06#\"'\x01&547\x01632\x16\x1d\x01!2\x16\x10\x14\a\x01\x06#\"&=\x01!\"&=\x01463!54632\x17\x01\a\x00\x13\r\xfa\xa0\x13\r\f\f\xfe\xc1\t\t\x01@\t\x0e\r\x13\x05`\r\x13\t\xfe\xc0\t\x0e\r\x13\xfa\xa0\r\x13\x13\r\x05`\x12\x0e\f\f\x01?\x01`\xc0\r\x13\xc0\r\x13\n\x01@\t\r\x0e\t\x01@\t\x13\r\xc0\x13\x02!\x1c\t\xfe\xc0\t\x13\r\xc0\x13\r\xc0\r\x13\xc0\x0e\x12\n\xfe\xc1\x00\x00\x00\x00\x02\x00\x00\x00\x00\a\x80\x05\x80\x00\x19\x005\x00\x00\x014&+\x01\x114&+\x01\"\x06\x15\x11#\"\x06\x15\x14\x17\x01\x1627\x016\x05\x14\x06#!\"\x005467&54\x0032\x04\x17632\x16\x15\x14\a\x1e\x01\x05\x00\x12\x0e\xe0\x13\r\xc0\r\x13\xe0\r\x13\t\x01`\t\x1c\t\x01_\n\x02\x80\xe1\x9f\xfb\xc0\xb9\xfe\xf9\x8cv\x02\x01,Ԝ\x01\x03;G_j\x96)\x82\xa7\x02`\x0e\x12\x01`\r\x13\x13\r\xfe\xa0\x13\r\x0e\t\xfe\xa0\t\t\x01_\fԟ\xe1\x01\a\xb9\x82\xdc7\x1e\r\xd4\x01,\xae\x90>\x96jL>\x1f\xd1\x00\x02\x00\x00\x00\x00\a\x80\x05\x80\x00\x19\x005\x00\x00\x014'\x01&\"\a\x01\x06\x15\x14\x16;\x01\x11\x14\x16;\x01265\x11326\x01\x14\x06#!\"\x005467&54\x0032\x04\x17632\x16\x15\x14\a\x1e\x01\x05\x00\t\xfe\xa0\t\x1c\t\xfe\xa1\n\x12\x0e\xe0\x13\r\xc0\r\x13\xe0\r\x13\x02\x80\xe1\x9f\xfb\xc0\xb9\xfe\xf9\x8cv\x02\x01,Ԝ\x01\x03;G_j\x96)\x82\xa7\x02\xa0\x0e\t\x01`\t\t\xfe\xa1\f\f\x0e\x12\xfe\xa0\r\x13\x13\r\x01`\x13\xfe\xed\x9f\xe1\x01\a\xb9\x82\xdc7\x1e\r\xd4\x01,\xae\x90>\x96jL>\x1f\xd1\x00\x00\x00\x00\x03\x00\x00\xff\x80\x05\x80\x05\x80\x00\a\x00X\x00`\x00\x00$\x14\x06\"&462\x05\x14\x06#!\"&54>\x037\x06\x1d\x01\x0e\x01\x15\x14\x162654&'547\x16 7\x16\x1d\x01\"\x06\x1d\x01\x06\x15\x14\x162654'5462\x16\x1d\x01\x06\x15\x14\x162654'54&'46.\x02'\x1e\x04\x00\x10\x06 &\x106 \x01\x80&4&&4\x04&\x92y\xfc\x96y\x92\v%:hD\x16:Fp\xa0pG9\x19\x84\x01F\x84\x19j\x96 8P8 LhL 8P8 E;\x01\x01\x04\n\bDh:%\v\xfe\xc0\xe1\xfe\xc2\xe1\xe1\x01>\xda4&&4&}y\x8a\x8ayD~\x96s[\x0f4D\xcb\x14d=PppP=d\x14\xcb>\x1fhh\x1f>@\x96jY\x1d*(88(*\x1dY4LL4Y\x1d*(88(*\x1dYDw\"\nA\x1f4*\x13\x0f[s\x96~\x03\xd8\xfe\xc2\xe1\xe1\x01>\xe1\x00\x00\x00\x02\x00\x00\xff\x80\x05\x80\x05\x80\x00\a\x00M\x00\x00\x004&\"\x06\x14\x1627\x14\x06\a\x11\x14\x04 $=\x01.\x015\x114632\x17>\x0132\x16\x14\x06#\"'\x11\x14\x16 65\x11\x06#\"&4632\x16\x17632\x16\x15\x11\x14\x06\a\x15\x14\x16 65\x11.\x015462\x16\x05\x00&4&&4\xa6G9\xfe\xf9\xfe\x8e\xfe\xf9\xa4\xdc&\x1a\x06\n\x11<#5KK5!\x1f\xbc\x01\b\xbc\x1f!5KK5#<\x11\n\x06\x1a&ܤ\xbc\x01\b\xbc9Gp\xa0p\x03&4&&4&@>b\x15\xfeu\x9f\xe1ោ\x14ؐ\x02\x00\x1a&\x02\x1e$KjK\x12\xfenj\x96\x96j\x01\x92\x12KjK$\x1e\x02&\x1a\xfe\x00\x90\xd8\x14\x84j\x96\x96j\x01\x8b\x15b>Ppp\x00\x04\x00\x00\xff\x80\a\x00\x05\x80\x00\x03\x00\r\x00\x1b\x00%\x00\x00\x01!5!\x05\x11#\"&5\x11463!\x11!\x1135463!2\x16\x1d\x01\x05\x11\x14\x06+\x01\x1132\x16\x02\x80\x02\x00\xfe\x00\xfe\xa0@\\\x84\x84\\\x04\xa0\xfc\x00\x808(\x02@(8\x02\x00\x84\\@@\\\x84\x04\x80\x80\x80\xfb\x00\x84\\\x03@\\\x84\xfb\x00\x05\x00\xa0(88(\xa0\xe0\xfc\xc0\\\x84\x05\x00\x84\x00\x02\x00@\xff\x00\x06\xc0\x06\x00\x00\v\x003\x00\x00\x044#\"&54\"\x15\x14\x163\x01\x14\x06#!\x14\x06\"&5!\"&5>\x0454\x127&5462\x16\x15\x14\a\x16\x12\x15\x14\x1e\x03\x03\x90\x10;U gI\x03@L4\xfe@\x96Ԗ\xfe@4L2RX='\xea\xbe\b8P8\b\xbe\xea'=XR\xb0 U;\x10\x10Ig\x0104Lj\x96\x96jL4*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x1c\xfe\xfb\x98\x8b\xf2\xaa\x93\\\x00\x00\x03\x00\x00\xff\x80\a@\x05\x00\x00\a\x00\x0f\x00\"\x00\x00\x004&+\x01\x1132\x01!\x14\x06#!\"&\x00\x10\x06+\x01\x15\x14\x06#!\"&5\x11463!2\x06\x80pP@@P\xf9\xf0\a\x00\x96j\xfb\x00j\x96\a@\xe1\x9f@\x84\\\xfd@\\\x84&\x1a\x04\x80\x9f\x030\xa0p\xfe\x80\xfd\xc0j\x96\x96\x04\t\xfe\xc2\xe1 \\\x84\x84\\\x02\xe0\x1a&\x00\x00\x02\x00\x00\xff\x00\x05\x80\x06\x00\x00-\x00B\x00\x00\x01\x11\x14\x06\a\x11\x14\x06+\x01\"&5\x11.\x015\x11462\x16\x15\x11\x14\x16265\x11462\x16\x15\x11\x14\x16265\x11462\x16\x05\x11\x14\x06+\x01\"&5\x11#\"&5\x11463!2\x16\x02\x80G9L4\x804L9G&4&&4&&4&&4&&4&\x03\x00L4\x804L\xe0\r\x13\xbc\x84\x01\x00\x1a&\x05\xc0\xfd\x80=d\x14\xfc\xf54LL4\x03\v\x14d=\x02\x80\x1a&&\x1a\xfe`\x1a&&\x1a\x01\xa0\x1a&&\x1a\xfe`\x1a&&\x1a\x01\xa0\x1a&&\x1a\xf9\xc04LL4\x02\x00\x13\r\x03 \x84\xbc&\x00\x06\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x003\x00C\x00S\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01463!2\x16\x1d\x01\x14\x06#!\"&5\x052\x16\x1d\x01\x14\x06#!\"&=\x01463\x012\x16\x1d\x01\x14\x06#!\"&=\x01463\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01\x00\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x02\xe0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03`\x0e\x12\x12\x0e@\x0e\x12\x12\x0e\xa0\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xff\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x00\x14\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\xbf\x00\xcf\x00\xdf\x00\xef\x00\xff\x01\x0f\x01\x1f\x01-\x01=\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01!\x11!\x11!5463!2\x16\x15\x01\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x02\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x01\x80\xfb\x80\x01\x80\x13\r\x01@\r\x13\x02\x00&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&\xe0@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfe\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xfa\x93\x06\x00\xfa\x00\xe0\r\x13\x13\r\x05`\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x00\r\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xb7\x00\xdb\x00\xf5\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01!\x11!\x15\x14\x06#!\"&=\x01!\x11!5463!2\x16\x15\x19\x014&+\x01\"\x06\x1d\x01#54&+\x01\"\x06\x15\x11\x14\x16;\x0126=\x013\x15\x14\x16;\x0126%\x11\x14\x06#!\"&5\x11463!\x11463!2\x16\x15\x11!2\x16\x01\x80\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x02\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x01\x80\xff\x008(\xfe@(8\xff\x00\x01\x80\x13\r\x01@\r\x13\x13\r@\r\x13\x80\x13\r@\r\x13\x13\r@\r\x13\x80\x13\r@\r\x13\x02\x00&\x1a\xfb\x00\x1a&&\x1a\x01@8(\x01\xc0(8\x01@\x1a&\xe0@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfe\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xfc\x93\x04\x80 (88( \xfb\x80\xe0\r\x13\x13\r\x03\xc0\x01@\r\x13\x13\r``\r\x13\x13\r\xfe\xc0\r\x13\x13\r``\r\x13\x13-\xfb\x00\x1a&&\x1a\x05\x00\x1a&\x01 (88(\xfe\xe0&\x00\x05\x00@\xff\x80\a\x80\x05\x80\x00\a\x00\x10\x00\x18\x00<\x00c\x00\x00$4&\"\x06\x14\x162\x01!\x11#\x06\x0f\x01\x06\a\x004&\"\x06\x14\x162\x1354&+\x0154&+\x01\"\x06\x1d\x01#\"\x06\x1d\x01\x14\x16;\x01\x15\x14\x16;\x0126=\x01326\x01\x11\x14\x06+\x01\x14\x06\"&5!\x14\x06\"&5#\"&463\x1146?\x01>\x01;\x01\x11463!2\x16\x02\x80KjKKj\xfe\xcb\x01\x80\x9e\x0e\b\xc3\a\x02\x05\x00KjKKj\xcb\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x01\x00&\x1a\xc0\x96Ԗ\xfe\x80\x96Ԗ\x80\x1a&&\x1a\x1a\x13\xc6\x13@\x1a\xa0&\x1a\x04\x80\x1a&KjKKjK\x02\x80\x01\x00\x02\a\xc3\f\n\xfd\xadjKKjK\x03 \xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x02.\xfb\x80\x1a&j\x96\x96jj\x96\x96j&4&\x01\xa0\x1a@\x13\xc6\x13\x1a\x01@\x1a&&\x00\x00\x05\x00\x00\xff\x80\a\x00\x05\x80\x00#\x00'\x001\x00?\x00I\x00\x00\x0154&+\x0154&+\x01\"\x06\x1d\x01#\"\x06\x1d\x01\x14\x16;\x01\x15\x14\x16;\x0126=\x01326\x01!5!\x05\x11#\"&5\x11463!\x11!\x1135463!2\x16\x1d\x01\x05\x11\x14\x06+\x01\x1132\x16\x05\x00\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\xfd\x80\x02\x00\xfe\x00\xfe\x80 \\\x84\x84\\\x04\xc0\xfb\xc0\xa08(\x02@(8\x02\x00\x84\\ \\\x84\x01\xa0\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x02\ue000\xfb\x00\x84\\\x03@\\\x84\xfb\x00\x05\x00\xa0(88(\xa0\xe0\xfc\xc0\\\x84\x05\x00\x84\x00\x00\x00\x00\x01\x00\x00\x00\x00\a\x80\x04\x80\x00:\x00\x00\x01\x06\r\x01\a#\x0132\x16\x14\x06+\x0353\x11#\a#'53535'575#5#573\x173\x11#5;\x022\x16\x14\x06+\x01\x013\x17\x05\x1e\x01\x17\a\x80\x01\xfe\xe1\xfe\xa0\xe0@\xfe\xdbE\x1a&&\x1a`\xa0@@\xa0\xc0` \x80\xc0\xc0\x80 `\xc0\xa0@@\xa0`\x1a&&\x1aE\x01%@\xe0\x01`\x80\x90\b\x02@ @ @\xfe\xa0\t\x0e\t \x01\xa0\xe0 \xc0 \b\x18\x80\x18\b \xc0 \xe0\x01\xa0 \t\x0e\t\xfe\xa0@ \x1c0\n\x00\x00\x00\x02\x00@\x00\x00\x06\x80\x05\x80\x00\x06\x00\x18\x00\x00\x01\x11!\x11\x14\x163\x01\x15!57#\"&5\x11'7!7!\x17\a\x11\x02\x80\xff\x00K5\x04\x80\xfb\x80\x80\x80\x9f\xe1@ \x01\xe0 \x03\xc0 @\x02\x80\x01\x80\xff\x005K\xfe@\xc0\xc0\xc0\xe1\x9f\x01@@\x80\x80\xc0 \xfc\xe0\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00\x00%\x114&+\x01\"\x06\x15\x11!\x114&+\x01\"\x06\x15\x11\x14\x16;\x01265\x11!\x11\x14\x16;\x0126\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\x80\x1a&\xfe\x00&\x1a\x80\x1a&&\x1a\x80\x1a&\x02\x00&\x1a\x80\x1a&\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xc0\x03\x80\x1a&&\x1a\xfe\xc0\x01@\x1a&&\x1a\xfc\x80\x1a&&\x1a\x01@\xfe\xc0\x1a&&\x03\xba\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00\x00\x0154&#!\x114&+\x01\"\x06\x15\x11!\"\x06\x1d\x01\x14\x163!\x11\x14\x16;\x01265\x11!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\xfe\xc0&\x1a\x80\x1a&\xfe\xc0\x1a&&\x1a\x01@&\x1a\x80\x1a&\x01@\x1a&\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02@\x80\x1a&\x01@\x1a&&\x1a\xfe\xc0&\x1a\x80\x1a&\xfe\xc0\x1a&&\x1a\x01@&\x02:\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00-\x00M\x03\xf3\x043\x00\x14\x00)\x00\x00$\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x16\x14\a\t\x01\x04\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x16\x14\a\t\x01\x02s\n2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\x01\x8a\n2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\xad\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\x00\x00\x00\x02\x00\r\x00M\x03\xd3\x043\x00\x14\x00)\x00\x00\x00\x14\a\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x04\x14\a\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x02S\n\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\x01\x8a\n\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\x02M\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\x00\x00\x02\x00M\x00\x8d\x043\x04S\x00\x14\x00)\x00\x00$\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\x0162\x17\x01\x12\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\x0162\x17\x01\x043\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\xed\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\n\xfe.\x01v\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\n\xfe.\x00\x00\x00\x02\x00M\x00\xad\x043\x04s\x00\x14\x00)\x00\x00\x00\x14\a\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x12\x14\a\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x043\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\x02\xad\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\n2\x01v\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\n2\x00\x00\x01\x00-\x00M\x02s\x043\x00\x14\x00\x00\x00\x14\a\t\x01\x16\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x02s\n\xfew\x01\x89\n\n2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\x03\xed\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\x00\x00\x00\x01\x00\r\x00M\x02S\x043\x00\x14\x00\x00\x00\x14\a\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x02S\n\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\x02M\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\x00\x00\x00\x01\x00M\x01\r\x043\x03S\x00\x14\x00\x00\x00\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\x0162\x17\x01\x043\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\x01m\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\n\xfe.\x00\x00\x00\x01\x00M\x01-\x043\x03s\x00\x14\x00\x00\x00\x14\a\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x043\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\x03-\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\n2\x00\x00\x00\x02\x00\x00\xff\x80\a\x80\x06\x00\x00\x0f\x00/\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\x14\x1e\x01\x15\x14\x06#!\"&54>\x015!\"&5\x11463!2\x16\a\x00\x13\r\xf9\xc0\r\x13\x13\r\x06@\r\x13\x80^B\xfd\xe0 &\x1a\xfe\x00\x1a& \xfd\xe0B^^B\x06@B^\x02 \x03@\r\x13\x13\r\xfc\xc0\r\x13\x13\x03M\xfb\xc0B^%Q=\r\x1a&&\x1a\x0e\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x94\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x04\xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x05\x80\x00!\x00C\x00\x00\x01\x11\x14\x06#!\"&5\x114>\x02;\x012\x16\x1d\x01\x14\x06+\x01\"\x06\x1d\x01\x14\x16;\x012\x16\x05\x11\x14\x06#!\"&5\x114>\x02;\x012\x16\x1d\x01\x14\x06+\x01\"\x06\x1d\x01\x14\x16;\x012\x16\x03\x00pP\xfe\x80PpQ\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0Pp\x03\x80pP\xfe\x80PpQ\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0Pp\x02@\xfe\x80PppP\x02\xc0h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8pP\xfe\x80PppP\x02\xc0h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8p\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x05\x80\x00!\x00C\x00\x00\x01\x11\x14\x0e\x02+\x01\"&=\x0146;\x0126=\x014&+\x01\"&5\x11463!2\x16\x05\x11\x14\x0e\x02+\x01\"&=\x0146;\x0126=\x014&+\x01\"&5\x11463!2\x16\x03\x00Q\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0PppP\x01\x80Pp\x03\x80Q\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0PppP\x01\x80Pp\x04\xc0\xfd@h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8pP\x01\x80PppP\xfd@h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8pP\x01\x80Ppp\x00\x00\x00\x00\b\x00@\xff@\x06\xc0\x06\x00\x00\t\x00\x11\x00\x19\x00#\x00+\x003\x00;\x00G\x00\x00$\x14\x06#\"&5462\x00\x14\x06\"&462\x00\x14\x06\"&462\x01\x14\x06#\"&462\x16\x00\x14\x06\"&462\x00\x14\x06\"&462\x00\x14\x06\"&462\x01\x14\x06#\"&54632\x16\x02\x0eK54LKj\x02=KjKKj\xfd\x8bKjKKj\x04\xfdL45KKjK\xfc<^\x84^^\x84\x04\xf0KjKKj\xfd\xcbp\xa0pp\xa0\x02\x82\x84\\]\x83\x83]\\\x84\xc3jKL45K\xfe\xe7jKKjK\x02ujKKjK\xfd\x8e4LKjKK\x03\xf1\x84^^\x84^\xfd\xa3jKKjK\x02\x90\xa0pp\xa0p\xfer]\x83\x83]\\\x84\x84\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x00\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x06\x00\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03Q\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\xc0\x00,\x00\x00\x01\x14\x03\x0e\x02\a\x06#\"&5465654.\x05+\x01\x11\x14\x06\"'\x01&47\x0162\x16\x15\x113 \x13\x16\a\x00\u007f\x03\x0f\f\a\f\x10\x0f\x11\x05\x05#>bq\x99\x9bb\xe0&4\x13\xfe\x00\x13\x13\x02\x00\x134&\xe0\x02ɢ5\x01\xa0\xa6\xfe\xe3\a\"\x1a\t\x11\x14\x0f\t#\x06D7e\xa0uU6\x1f\f\xff\x00\x1a&\x13\x02\x00\x134\x13\x02\x00\x13&\x1a\xff\x00\xfem\x86\x00\x04\x00\x00\xff\x80\x06\x80\x05\x00\x00\v\x00\x17\x001\x00X\x00\x00\x00\x14\x0e\x01\".\x014>\x012\x16\x04\x14\x0e\x01\".\x014>\x012\x16\x174&#\"\a\x06\"'&#\"\x06\x15\x14\x1e\x03;\x012>\x03\x13\x14\a\x0e\x04#\".\x04'&547&5472\x16\x17632\x17>\x013\x16\x15\x14\a\x16\x02\x80\x19=T=\x19\x19=T=\x02\x99\x19=T=\x19\x19=T=\xb9\x8av)\x9aG\xacG\x98+v\x8a@b\x92\x86R\xa8R\x86\x92b@\xe0=&\x87\x93\xc1\x96\\N\x80\xa7\x8a\x88j!>\x88\x1b3l\xa4k\x93\xa2\x94\x84i\xa4k3\x1b\x88\x01hPTDDTPTDDTPTDDTPTDD|x\xa8\x15\v\v\x15\xa8xX\x83K-\x0e\x0e-K\x83\x01\b\xcf|Mp<#\t\x06\x13)>dA{\xd0\xed\x9fRXtfOT# RNftWQ\xa0\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x05\x80\x00\x17\x00,\x00\x00%\x114&#!\"&=\x014&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x1d\x01!2\x16\x06\x008(\xfd@(88(\xfe\xc0(88(\x04\xc0(8\x80\x84\\\xfb@\\\x84\x84\\\x01@\\\x84\x02\xa0\\\x84\xe0\x02\xc0(88(@(88(\xfc@(88\x02\xe8\xfd@\\\x84\x84\\\x03\xc0\\\x84\x84\\ \x84\x00\x00\x03\x00\x00\x00\x00\au\x05\x80\x00\x11\x00'\x00E\x00\x00\x014#!\"\x06\a\x01\x06\x15\x143!267\x016%!54&#!\"&=\x014&#!\"\x06\x15\x11\x01>\x01\x05\x14\a\x01\x0e\x01#!\"&5\x11463!2\x16\x1d\x01!2\x16\x1d\x0132\x16\x17\x16\x06\xf55\xfb\xc0([\x1a\xfe\xda\x125\x04@(\\\x19\x01&\x12\xfb\x8b\x03\x008(\xfd\xc0(88(\xfe\xc0(8\x01\x00,\x90\x059.\xfe\xd9+\x92C\xfb\xc0\\\x84\x84\\\x01@\\\x84\x02 \\\x84\xc06Z\x16\x0f\x02]#+\x1f\xfe\x95\x18\x10#,\x1f\x01k\x16\xb4\xa0(88(@(88(\xfc\xab\x01;5E\xa3>:\xfe\x955E\x84\\\x03\xc0\\\x84\x84\\ \x84\\\xa01. \x00\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00\x1c\x00$\x004\x00@\x00\x00\x01\x0e\x01\"&'&676\x16\x17\x1e\x01267>\x01\x1e\x01\x00\x14\x06\"&462\x04\x14\x06\"&462\x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04n%\xca\xfe\xca%\b\x18\x1a\x19/\b\x19\x87\xa8\x87\x19\b02\x18\xfe\nKjKKj\x02KKjKKj\x01Kf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xcdy\x94\x94y\x19/\b\b\x18\x1aPccP\x1a\x18\x10/\x01\xcfjKKjKKjKKjK\xfd\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00\x1c\x00$\x004\x00@\x00\x00\x01\x16\x0e\x01&'.\x01\"\x06\a\x0e\x01'.\x017>\x012\x16\x00\x14\x06\"&462\x04\x14\x06\"&462\x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04n\b\x1820\b\x19\x87\xa8\x87\x19\b/\x19\x1a\x18\b%\xca\xfe\xca\xfe7KjKKj\x02KKjKKj\x01Kf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x013\x19/\x10\x18\x1aPccP\x1a\x18\b\b/\x19y\x94\x94\x02\tjKKjKKjKKjK\xfd\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x13\x00\x1b\x00+\x007\x00\x00\x00\x14\x06#!\"&463!2\x00\x14\x06\"&462\x04\x14\x06\"&462\x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x80&\x1a\xfd\x80\x1a&&\x1a\x02\x80\x1a\xfe&KjKKj\x02KKjKKj\x01Kf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xda4&&4&\x01\xb5jKKjKKjKKjK\xfd\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x04\x00\x00\x00\x00\a\x80\x04\x00\x00#\x00+\x003\x00C\x00\x00\x0154&+\x0154&+\x01\"\x06\x1d\x01#\"\x06\x1d\x01\x14\x16;\x01\x15\x14\x16;\x0126=\x01326\x044&\"\x06\x14\x162\x004&\"\x06\x14\x162$\x10\x00#\"'#\x06#\"\x00\x10\x003!2\x03@\x12\x0e\xc0\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x02@KjKKj\x01KKjKKj\x01K\xfe\xd4\xd4\xc0\x92ܒ\xc0\xd4\xfe\xd4\x01,\xd4\x03\x80\xd4\x01\xc0\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12gjKKjK\x01KjKKjK\xd4\xfeX\xfeԀ\x80\x01,\x01\xa8\x01,\x00\x00\x00\x0f\x00\x00\x00\x00\a\x80\x04\x80\x00\v\x00\x17\x00#\x00/\x00;\x00G\x00S\x00_\x00k\x00w\x00\x83\x00\x8f\x00\x9f\x00\xa3\x00\xb3\x00\x00\x01\x15\x14+\x01\"=\x014;\x0127\x15\x14+\x01\"=\x014;\x012'\x15\x14+\x01\"=\x014;\x012\x01\x15\x14#!\"=\x0143!2%\x15\x14+\x01\"=\x014;\x012'\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012'\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012\x05\x15\x14+\x01\"=\x014;\x012\x05\x11\x14+\x01\"=\x014;\x0154;\x012\x13\x11!\x11\x01\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80\x10`\x10\x10`\x10\x80\x10\xe0\x10\x10\xe0\x10\x80\x10`\x10\x10`\x10\x04\x00\x10\xfc\xa0\x10\x10\x03`\x10\xfd\x80\x10`\x10\x10`\x10\x80\x10`\x10\x10`\x10\x01\x80\x10`\x10\x10`\x10\x80\x10`\x10\x10`\x10\x01\x80\x10`\x10\x10`\x10\x01\x80\x10`\x10\x10`\x10\xfe\x00\x10`\x10\x10`\x10\x01\x00\x10`\x10\x10`\x10\x01\x00\x10\xe0\x10\x10p\x10`\x10\x80\xf9\x80\a\x00K5\xf9\x805KK5\x06\x805K\x01p`\x10\x10`\x10\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xfd\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xfe\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xfe\xf0`\x10\x10`\x10\xfe\xf0`\x10\x10`\x10\x01\xf0`\x10\x10`\x10\x10`\x10\x10`\x10\x10\xfe\xa0\x10\x10`\x10\xf0\x10\xfd\x00\x03\x80\xfc\x80\x03\x80\xfc\x805KK5\x03\x805KK\x00\x00\x00\x00\x03\x00@\xff\x80\a\x00\x05\x80\x00\x16\x00*\x00V\x00\x00\x01\x11\x06#\"'.\x01#\"\a\x11632\x1e\x02\x1f\x01\x1632\x01\x14\x06\a\x11\x14\x06+\x01\"&5\x11.\x015462\x16\x05\x11\x14\a\x06\a\x06#\"/\x01.\x02#\"\x04\a\x06#\"'&5\x1147>\x0332\x16\x17\x16327676\x17\x16\x06\x80\xa9\x89R?d\xa8^\xad\xe6\xf5\xbc7ac77\x1c,9x\xfbm#\x1d\x12\x0e@\x0e\x12\x1d#KjK\x05\xc0#\n\aڗXF\x1c@Fp:f\xfe\xf5_\x0f\x12\x10\x10 \x1f#W\x8d\xa4Ip\xc2p&3z\xbc\x16\t\x1f\x1f\x1f\x01\xeb\x02h[ 17\u007f\xfd\xa9q\x0f%\x19\x1b\x0e\x16\x03q#:\x11\xfb\x0e\x0e\x12\x12\x0e\x04\xf2\x11:#5KKu\xfd\x05'\x12\x05\x04t#\x0e!\x1e\x1cX:\t\b\x13%\x02\xe6#\x14\x15+=&>7\x13p\f\x05\x10\x12\x14\x00\x00\x06\x00@\xff\x80\a\x00\x05\x80\x00\x05\x00\v\x00*\x002\x00F\x00r\x00\x00\x015\x06\a\x156\x135\x06\a\x156\x015\x06'5&'.\t#\"\a\x1532\x16\x17\x16\x17\x15\x1632\x135\x06#\"'\x15\x16\x01\x14\x06\a\x11\x14\x06+\x01\"&5\x11.\x015462\x16\x05\x11\x14\a\x06\a\x06#\"/\x01.\x02#\"\x04\a\x06#\"'&5\x1147>\x0332\x16\x17\x16327676\x17\x16\x03@\xb5\xcbͳ\xac\xd4\xd7\x03\xe9\xeb\x95\x14\x13\x058\r2\x13.\x1a,#,\x16\x17\x1a\x13f\xb5k\x13\x14*1x\xad\xa9\x89-!\x94\xfb\xac#\x1d\x12\x0e@\x0e\x12\x1d#KjK\x05\xc0#\n\aڗXF\x1c@Fp:f\xfe\xf5_\x0f\x12\x10\x10 \x1f#W\x8d\xa4Ip\xc2p&3z\xbc\x16\t\x1f\x1f\x1f\x02\x18\xc0\x10e\xb9`\x01\xb0\xc5\bv\xbdo\xfe8\xb8t-\xe0\x06\t\x03\x1c\x06\x18\a\x13\x06\v\x04\x04\x03\xde:5\t\x06\xbc\x11\x02\a\xbd[\b\xc4*\x01\xee#:\x11\xfb\x0e\x0e\x12\x12\x0e\x04\xf2\x11:#5KKu\xfd\x05'\x12\x05\x04t#\x0e!\x1e\x1cX:\t\b\x13%\x02\xe6#\x14\x15+=&>7\x13p\f\x05\x10\x12\x14\x00\x02\x00\r\x00\x00\x06\x80\x043\x00\x14\x00$\x00\x00\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x16\x14\x01\x15\x14\x06#!\"&=\x01463!2\x16\x02I\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\x04-\x12\x0e\xfc@\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x02)\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\xfe-@\x0e\x12\x12\x0e@\x0e\x12\x12\x00\x00\x00\x00\x03\x00-\xff\x93\aS\x04\xed\x00\x14\x00$\x009\x00\x00%\a\x06\"'\x01&47\x0162\x1f\x01\x16\x14\a\t\x01\x16\x14\t\x01\x0e\x01/\x01.\x017\x01>\x01\x1f\x01\x1e\x01\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x16\x14\x02i2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\x02E\xfe\x8b\x04\x17\f>\r\r\x04\x01u\x04\x17\f>\r\r\x02\x8d\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\x892\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\n\x1a\x04!\xfa\xf5\r\r\x04\x11\x04\x17\r\x05\v\r\r\x04\x11\x04\x17\xfdh\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\xbb\x00\x15\x00;\x00\x00\x01\x15\x14\a\x06#\"'\x01&47\x016\x17\x16\x1d\x01\x01\x06\x14\x17\x01\x14\x0e\x03\a\x06#\"'&7\x12'.\x01'\x15\x14\a\x06#\"'\x01&47\x016\x17\x16\x15\x11\x04\x17\x16\x02\x80'\r\f\x1b\x12\xfe\x00\x13\x13\x02\x00\x1d)'\xfes\x13\x13\x06\r\"+5\x1c\x06\b\x14\x06\x03\x19\x02+\x95@ա'\r\f\x1b\x12\xfe\x00\x13\x13\x02\x00\x1d)'\x01\x9b\xbc\xa9\x01\xc6F*\x11\x05\x13\x02\x00\x134\x13\x02\x00\x1f\x11\x11*E\xfer\x134\x13\xfeM:\x97}}8\f\x11\x01\b\x1a\x01\x90\xa5GO\r\xfb*\x11\x05\x13\x02\x00\x134\x13\x02\x00\x1f\x11\x11*\xfe\xfa\x1c\xc1\xad\x00\x00\x00\x00\x02\x00\x02\xff\xad\x06~\x05\xe0\x00\n\x00(\x00\x00\x01-\x01/\x01\x03\x11\x17\x05\x03'\t\x01\x13\x16\x06#\"'%\x05\x06#\"&7\x13\x01&67%\x13632\x17\x13\x05\x1e\x01\x04\xa2\x01\x01\xfe\x9cB\x1e\x9f;\x01><\f\x01\xf5\xfe\x95V\x05\x16\x17\x11\x17\xfe?\xfe?\x17\x11\x17\x16\x05V\xfe\x94 \x12-\x01\xf6\xe1\x14\x1d\x1c\x15\xe1\x01\xf6-\x12\x02C\xfa4\n<\x01B\xfc=\x1f\xa8\x01cB\x015\xfe\x9e\xfe\f!%\f\xec\xec\f%!\x01\xf4\x01b 7\aI\x01\xc7))\xfe9I\a7\x00\x00\x00\x01\x00\x02\xff\x80\x05\x80\x05\x00\x00\x16\x00\x00\t\x01\x06#\"'.\x015\x11!\".\x0167\x01632\x17\x1e\x01\x05y\xfd\x80\x11(\x05\n\x16\x1b\xfd\xc0\x16#\n\x12\x14\x05\x00\r\x10\x1b\x12\x0f\a\x04\xa3\xfb\x00#\x02\x05#\x16\x02@\x1b,(\n\x02\x80\a\x13\x0e)\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00\x02\x00\x05\x008\x00\x00\x01!\x11\t\x01!\x01\x15\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01!\"&5\x11#\"&=\x0146;\x01546;\x012\x16\x1d\x01!762\x17\x16\x14\x0f\x01\x1132\x16\x02-\x02S\xfd\x80\x02S\xfd\xad\x04\x80\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xfc\xa0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\x03S\xf6\n\x1a\n\t\t\xf7\xe0\x0e\x12\x01\x00\x02S\xfd\xda\x02S\xfd`\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\x03`\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\xf7\t\t\n\x1a\n\xf6\xfc\xad\x12\x00\x00\x00\x04\x00\x00\xff\x80\x04\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00K\x00\x00$4&\"\x06\x14\x162\x124&\"\x06\x14\x162\x044&\"\x06\x14\x1627\x14\x06\a\x02\a\x06\a\x0e\x01\x1d\x01\x1e\x01\x15\x14\x06\"&5467\x11.\x015462\x16\x15\x14\x06\a\x1167>\x055.\x015462\x16\x01 8P88P88P88P\x02\xb88P88P\x984,\x02\xe0C\x88\x80S,4p\xa0p4,,4p\xa0p4,6d7AL*'\x11,4p\xa0p\x18P88P8\x04\xb8P88P8HP88P8`4Y\x19\xfe\xe1\u007f&+(>E\x1a\x19Y4PppP4Y\x19\x034\x19Y4PppP4Y\x19\xfe\x0f\x1a\x1f\x11\x19%*\x0154&#\"\a\x06\a\x06#\"/\x01.\x017\x12!2\x1e\x02\x02\xc0\x18\x10\xf0\x10\x18\x18\x10\xf0\x10\x18\x01<\x1f'G,')7\x18\x10\xf0\x0f\x15\x82N;2]=A+#H\r\x12\f\r\xa4\r\x05\b\xa0\x010P\xa2\x82R\x01\x18\xf0\x10\x18\x18\x10\xf0\x10\x18\x18\x02H6^;<\x1b\x16\x17T\x19\x11\x1f%\x13-S\x93#\x1b:/*@\x1d\x19Z\x10\b}\n\x1e\r\x01\n>h\x97\x00\x00\x00\x02\x00\x00\x00\x00\x02\x80\x05\x80\x00\x1e\x00.\x00\x00%\x15\x14\x06#!\"&=\x0146;\x01\x11#\"&=\x01463!2\x16\x15\x1132\x16\x03\x15\x14\x06#!\"&=\x01463!2\x16\x02\x80&\x1a\xfe\x00\x1a&&\x1a@@\x1a&&\x1a\x01\x80\x1a&@\x1a&\x80&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&\x01\x80&\x1a\x80\x1a&&\x1a\xfd\xc0&\x04f\xc0\x1a&&\x1a\xc0\x1a&&\x00\x00\x02\x00b\x00\x00\x02\x1e\x05\x80\x00\x0f\x00\x1f\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x13\x03\x0e\x01#!\"&'\x03&63!2\x16\x02\x00&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\x1e\x1c\x01'\x1a\xff\x00\x1a'\x01\x1c\x01%\x1a\x01@\x1a%\x01 \xe0\x1a&&\x1a\xe0\x1a&&\x04\x06\xfd\x00\x1a&&\x1a\x03\x00\x1a&&\x00\x02\x00\x05\x00\x00\x05\xfe\x05k\x00%\x00J\x00\x00%\x15#/\x01&'#\x0e\x02\a\x06\x0f\x01!53\x13\x03#5!\x17\x16\x17\x16\x1736?\x02!\x15#\x03\x13\x01\x15!'&54>\x0454&#\"\a\x06\a'67632\x16\x15\x14\x0e\x04\a35\x03\x81\xf8\x9f\x18\b\x03\x03\x01\x03\x04\x01\n\x0f\x9b\xfe\xfe\x80Ź\x89\x01\x14\x8b\x02\x15\b\x03\x03\x03\b\x19\x8c\x01\x01}\xb8\xcc\x02\xea\xfd\xfe\x03\x044NZN4;)3.\x0e\x16i\x1a%Sin\x881KXL7\x03觧\xfc*\t\f\x03\a\t\x02\x14\x18\xfa\xa7\x01#\x01\x10\xa8\xe4\x04&\t\f\t\f*\xe4\xa8\xfe\xf5\xfe\xd8\x02\xa7\xce\x1b\x1c\x12@jC?.>!&1'\v\x1b\\%\x1dAwc8^;:+\x0454&#\"\a\x06\a'67632\x16\x15\x14\x0e\x03\a35\x03\x81\xf8\x9f\x18\b\x03\x03\x01\x03\x04\x01\n\x0f\x9b\xfe\xfe\x80Ź\x89\x01\x14\x8b\x02\x15\b\x03\x03\x03\b\x19\x8c\x01\x01}\xb8\xcc\x02\xec\xfd\xfe\x04\x034NZN4;)3.\x0e\x16i\x1a%Pln\x88EcdJ\x04觧\xfc*\t\f\x03\a\t\x02\x14\x18\xfa\xa7\x01#\x01\x10\xa8\xe4\x04&\t\f\t\f*\xe4\xa8\xfe\xf5\xfe\xd8\xd9\xce\x1b-\x01@jC?.>!&1'\v\x1b\\%\x1dAwcBiC:D'P\x00\x00\x00\x02\x00\x01\x00\x00\a\u007f\x05\x00\x00\x03\x00\x17\x00\x00%\x01!\t\x01\x16\x06\a\x01\x06#!\"&'&67\x0163!2\x16\x03\x80\x01P\xfd\x00\xfe\xb0\x06\xf5\x0f\v\x19\xfc\x80&:\xfd\x00&?\x10\x0f\v\x19\x03\x80&:\x03\x00&?\x80\x01\x80\xfe\x80\x045\"K\x1c\xfc\x00,)\"\"K\x1c\x04\x00,)\x00\x00\x01\x00\x00\xff\xdc\x06\x80\x06\x00\x00h\x00\x00\x01\x14\x06#\".\x02#\"\x15\x14\x16\a\x15\"\a\x0e\x02#\"&54>\x0254&#\"\x06\x15\x14\x1e\x02\x15\x14\a\x06#\"'.\x01/\x01\"'\"5\x11\x1e\x02\x17\x16327654.\x0254632\x16\x15\x14\x0e\x02\x15\x14\x163267\x15\x0e\x02\a\x06\x15\x14\x17\x1632>\x0232\x16\x06\x80YO)I-D%n \x01\x16\v\"\u007fh.=T#)#lQTv\x1e%\x1e.%P_\x96\t%\t\r\x01\x02\x02\x02\x1f%\x03\x96_P%.\x1e%\x1evUPl#)#T=@\xe8/\x01\x05\x05\x01\x18#,-\x1691P+R[\x01\xb6Ql#)#|'\x98'\x05\x01\x03\x11\n59%D-I)OY[R+P19\x16-,#\x18\x02\x04\x02\x02\x01\x01\x04\x00\x01\x05\x05\x01\x18#,-\x1691P+R[YO)I-D%95\x1e\x02\x02\x02\x1f%\x03\x96_P%.\x1e%\x1ev\x00\x00\x02\x00\x00\xff\x80\x04\x80\x06\x00\x00'\x003\x00\x00\x01\x15\x14\x00\a\x15!2\x16\x14\x06#!\"&463!5&\x00=\x01462\x16\x1d\x01\x14\x00 \x00=\x01462\x16\x01\x11\x14\x06 &5\x1146 \x16\x04\x80\xfe\xd9\xd9\x01\x00\x1a&&\x1a\xfd\x80\x1a&&\x1a\x01\x00\xd9\xfe\xd9&4&\x01\a\x01r\x01\a&4&\xff\x00\xbc\xfe\xf8\xbc\xbc\x01\b\xbc\x03@\x80\xdd\xfe\xb9\x18\x84&4&&4&\x84\x18\x01G݀\x1a&&\x1a\x80\xb9\xfe\xf9\x01\a\xb9\x80\x1a&&\x01f\xfe\x00\x84\xbc\xbc\x84\x02\x00\x84\xbc\xbc\x00\x03\x00\r\xff\x80\x05s\x06\x00\x00\v\x00C\x00K\x00\x00\x01\a&=\x01462\x16\x1d\x01\x14\t\x01\x15\x14\x06#\"'\a\x1632\x00=\x01462\x16\x1d\x01\x14\x00\a\x15!2\x16\x14\x06#!\"&463!5&'\a\x06\"/\x01&47\x0162\x1f\x01\x16\x14%\x01\x114632\x16\x01\x0fe*&4&\x04i\xfe\x97\xbc\x8476`al\xb9\x01\a&4&\xfe\xd9\xd9\x01\x00\x1a&&\x1a\xfd\x80\x1a&&\x1a\x01\x00}n\xfe\n\x1a\nR\n\n\x04\xd2\n\x1a\nR\n\xfez\xfd\x93\xbc\x84f\xa5\x02Oego\x80\x1a&&\x1a\x805\x02\x1e\xfe\x97\x80\x84\xbc\x13`3\x01\a\xb9\x80\x1a&&\x1a\x80\xdd\xfe\xb9\x18\x84&4&&4&\x84\rD\xfe\n\nR\n\x1a\n\x04\xd2\n\nR\n\x1az\xfd\x93\x02\x00\x84\xbcv\x00\x00\x00\x02\x00\x00\xff\x80\x05\x00\x05\x80\x00\x06\x00\"\x00\x00\x01\x11!\x11676\x13\x11\x14\x0e\x05\a\x06\"'.\x065\x11463!2\x16\x04@\xfe@w^\xeb\xc0Cc\x89t~5\x10\f\x1c\f\x105~t\x89cC&\x1a\x04\x80\x1a&\x02@\x02\x80\xfb\x8f?J\xb8\x03\xb0\xfd\x00V\xa9\x83|RI\x1a\a\x06\x06\a\x1aIR|\x83\xa9V\x03\x00\x1a&&\x00\x00\x00\x00\x04\x00\x00\xff\x00\x06\x80\x06\x00\x00\x03\x00\x13\x00#\x00G\x00\x00\x17!\x11!%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x11\x14\x06#!\"&5\x1146;\x01546;\x012\x16\x1d\x01!546;\x012\x16\x1d\x0132\x16\x80\x05\x80\xfa\x80\x01\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x03\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x80L4\xfa\x804LL4\x80^B@B^\x01\x80^B@B^\x804L\x80\x04\x00\xc0\x01 \x0e\x12\x12\x0e\xfe\xe0\x0e\x12\x12\x0e\x01 \x0e\x12\x12\x0e\xfe\xe0\x0e\x12\x12N\xfb\x004LL4\x05\x004L`B^^B``B^^B`L\x00\x00\x00\x02\x00\x03\xff\x80\x05\x80\x05\xe0\x00\a\x00L\x00\x00\x004&\"\x06\x14\x162%\x11\x14\a\x06#\"'%.\x015!\x15\x1e\x01\x15\x11\x14\x06#!\"&5\x114675#\"\x0e\x03\a\x06#\"'.\x017>\x047&5462\x16\x15\x14\a!467%632\x17\x16\x02\x00&4&&4\x03\xa6\f\b\f\x04\x03\xfe@\v\x0e\xff\x00o\x91&\x1a\xfe\x00\x1a&}c ;pG=\x14\x04\x11(\x10\r\x17\x11\f\x05\x138Ai8\x19^\x84^\x0e\x01.\x0e\v\x01\xc0\x03\x04\f\b\f\x05&4&&4&`\xfe\xc0\x10\t\a\x01`\x02\x12\vf\x17\xb0s\xfc\xe0\x1a&&\x1a\x03 j\xa9\x1eo/;J!\b#\a\f2\x18\n KAE\x12*,B^^B!\x1f\v\x12\x02`\x01\a\t\x00\x00\x02\x00$\xff \x06\x80\x05\x80\x00\a\x00-\x00\x00\x004&\"\x06\x14\x162\x01\x14\x02\a\x06\a\x03\x06\a\x05\x06#\"/\x01&7\x13\x01\x05\x06#\"/\x01&7\x1367%676$!2\x16\x05\xa08P88P\x01\x18\x97\xb2Qr\x14\x02\x0e\xfe\x80\a\t\f\v@\r\x05U\xfe\xe7\xfe\xec\x03\x06\x0e\t@\x11\f\xe0\n\x10\x01{`P\xbc\x01T\x01\x05\x0e\x14\x04\x18P88P8\x01\x80\xf9\xfe\x95\xb3P`\xfe\x85\x10\n\xe0\x04\t@\x0e\x12\x01\x14\x01\x19U\x01\t@\x13\x14\x01\x80\x0e\x02\x14rQ\xbb\x8e\x13\x00\x00\x00\x01\x00\x00\x00\x00\x06\xd1\x05\x00\x00\x16\x00\x00\x01\x03!\x136'&+\x01\x03!\x13!\x03!\x13\x03!2\x16\x17\x1e\x01\x06Ѥ\xfe\xb2\xb2\r\x1c\x1b8\xa9\xcc\xfe\xb2\xcc\xfe\xe2\xcc\xfe\xb2̙\x04\xfce\xb1;<*\x02\xfb\xfd\x05\x03@8 !\xfcG\x03\xb9\xfcG\x03\xb9\x01GQII\xbf\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00%764'\t\x0164/\x01&\"\a\x01\x06\x14\x17\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x8df\x13\x13\xfe\xcd\x013\x13\x13f\x134\x13\xfe:\x13\x13\x01\xc6\x134\x02\x86\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x8df\x134\x13\x013\x013\x134\x13f\x13\x13\xfe:\x134\x13\xfe:\x13\x02\xd7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00%\x0164'\x01&\"\x0f\x01\x06\x14\x17\t\x01\x06\x14\x1f\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x02\xcd\x01\xc6\x13\x13\xfe:\x134\x13f\x13\x13\x013\xfe\xcd\x13\x13f\x134\x03F\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x8d\x01\xc6\x134\x13\x01\xc6\x13\x13f\x134\x13\xfe\xcd\xfe\xcd\x134\x13f\x13\x02\xd7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00\x01764'\x01&\"\a\x01\x06\x14\x1f\x01\x1627\t\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x8df\x13\x13\xfe:\x134\x13\xfe:\x13\x13f\x134\x13\x013\x013\x134\x01\x86\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\x8df\x134\x13\x01\xc6\x13\x13\xfe:\x134\x13f\x13\x13\x013\xfe\xcd\x13\x01\xd7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00%\x0164/\x01&\"\a\t\x01&\"\x0f\x01\x06\x14\x17\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03-\x01\xc6\x13\x13f\x134\x13\xfe\xcd\xfe\xcd\x134\x13f\x13\x13\x01\xc6\x134\x02\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xed\x01\xc6\x134\x13f\x13\x13\xfe\xcd\x013\x13\x13f\x134\x13\xfe:\x13\x02w\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff@\x05\x80\x05\x80\x00\x11\x00\x16\x00\x00\x017!\x13!\x0f\x01/\x01#\x13\x0535%\x13!'\x01!\x03\x05%\x04j\x10\xfc\x8c/\x02d\x16\xc5\xc4\r\xaf\x16\x01j\x04\x01g2\xfd|\x0f\xfe8\x05\x80\x80\xfd\xbe\xfd\xc2\x03\xab\xaf\xfd\xea\xe455\x8c\xfe\xead\x01c\x02 \xb5\x01\xd5\xfab\xa2\xa2\x00\x00\x00\x01\x00\f\xff@\x06\xf4\x05\x80\x00\x0f\x00\x00\x01!\t\x02\x13!\a\x05%\x13!\x13!7!\x01\x13\x05\xe1\xfe\xf6\xfc\xdc\xfdFG\x01)\x1d\x01\xa6\x01\xe6D\xfbH:\x04\xb9&\xfbH\x05\x80\xfa\xcb\xfe\xf5\x01\v\x01d\x93\xa1\xa1\x01S\x01)\xbf\x00\x00\x00\x02\x00\x00\xff\x10\a\x00\x06\x00\x00\a\x00U\x00\x00\x004&\"\x06\x14\x162\x01\x11\x14\a\x06#\"/\x01\x06\x04 $'\a\x06#\"'&5\x11463!2\x17\x16\x0f\x01\x1e\x01\x17\x11#\"&=\x0146;\x015.\x015462\x16\x15\x14\x06\a\x1532\x16\x1d\x01\x14\x06+\x01\x11>\x017'&763!2\x16\x03\xc0&4&&4\x03f\x14\b\x04\f\v]w\xfeq\xfe4\xfeqw]\t\x0e\x04\b\x14\x12\x0e\x01`\x16\b\b\x0fdC\xf5\x95\xc0\x1a&&\x1a\xc0:F\x96ԖF:\xc0\x1a&&\x1a\xc0\x95\xf5Cd\x0f\b\b\x16\x01`\x0e\x12\x04\xe64&&4&\xfc\xa0\xfe\xa0\x16\b\x02\t]\x8f\xa7\xa7\x8f]\t\x02\b\x16\x01`\x0e\x12\x14\x13\x10d[}\x14\x02\x87&\x1a\x80\x1a&\xa3\"uFj\x96\x96jFu\"\xa3&\x1a\x80\x1a&\xfdy\x14}[d\x10\x13\x14\x12\x00\x01\x00\x00\x00\x00\x04\x80\x06\x00\x00#\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x1146;\x01\x114\x00 \x00\x15\x14\x06+\x01\"&54&\"\x06\x15\x11\x04 (88(\xfc@(88( \x01\a\x01r\x01\a&\x1a@\x1a&\x96Ԗ\x03\x008(\xfd\xc0(88(\x02@(8\x01@\xb9\x01\a\xfe\xf9\xb9\x1a&&\x1aj\x96\x96j\xfe\xc0\x00\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00'\x003\x00\x00\x00\x14\x06\"&462\x00\x10& \x06\x10\x16 \x00\x10\x00 \x00\x10\x00 \x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00\x96Ԗ\x96\xd4\x01\x16\xe1\xfe\xc2\xe1\xe1\x01>\x01a\xfe\xd4\xfeX\xfe\xd4\x01,\x01\xa8\x01\xacf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xeaԖ\x96Ԗ\xfea\x01>\xe1\xe1\xfe\xc2\xe1\x02T\xfeX\xfe\xd4\x01,\x01\xa8\x01,\xfd~\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\x02\x00\x05\x80\x03\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x808(\xc0(88(\xc0(8\x02\x008(\xc0(88(\xc0(8\x02\x008(\xc0(88(\xc0(8\x03 \xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(88\x00\x00\x00\x00\x03\x00\x00\x00\x00\x01\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x808(\xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(8\x01 \xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x1b\x005\x00E\x00\x00$4&\"\x06\x14\x162%&\x00'&\x06\x1d\x01\x14\x16\x17\x1e\x01\x17\x1e\x01;\x0126%&\x02.\x01$'&\a\x06\x1d\x01\x14\x16\x17\x16\x04\x12\x17\x1e\x01;\x01276\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\x00KjKKj\x01\xaa\r\xfe\xb9\xe9\x0e\x14\x11\r\x9a\xdc\v\x01\x12\r\x80\r\x14\x01\u007f\x05f\xb1\xe9\xfe\xe1\x9a\x0e\t\n\x12\r\xcc\x01\\\xd1\a\x01\x12\r\x80\r\n\v\x01\x1f\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xcbjKKjK\"\xe9\x01G\r\x01\x14\r\x80\r\x12\x01\vܚ\r\x11\x14\r\x9a\x01\x1f\xe9\xb1f\x05\x01\n\n\r\x80\r\x12\x01\a\xd1\xfe\xa4\xcc\r\x12\n\t\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x1b\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x0164'\x01&\a\x06\x15\x11\x14\x17\x16327\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x03\xb2 \xfd\xe0\x1f! \x10\x10\x11\x0f\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfd\x97\x12J\x12\x01@\x13\x12\x13%\xfd\x80%\x13\b\t\x00\x03\x006\xff5\x06\xcb\x05\xca\x00\x03\x00\x13\x00/\x00\x00\t\x0564'\x01&\"\a\x01\x06\x14\x17\x01\x162\t\x01\x06\"/\x0164&\"\a'&47\x0162\x1f\x01\x06\x14\x1627\x17\x16\x14\x04\x00\x01<\xfd\xc4\xfe\xc4\x01i\x02j\x13\x13\xfe\x96\x126\x12\xfd\x96\x13\x13\x01j\x126\x03\x8b\xfcu%k%~8p\xa08}%%\x03\x8b%k%}8p\xa08~%\x04<\xfe\xc4\xfd\xc4\x01<\xfei\x02j\x134\x13\x01j\x12\x12\xfd\x96\x134\x13\xfe\x96\x12\x02\x8f\xfct%%~8\xa0p8~%k%\x03\x8a%%}8\xa0p8}%k\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00\x00\x0154&#!\"\x06\x1d\x01\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\xfc\x80\x1a&&\x1a\x03\x80\x1a&\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02@\x80\x1a&&\x1a\x80\x1a&&\x02:\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\x00\x00\x05\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04\x80\x12\x0e\xfc\xc0\x0e\x12\x12\x0e\x03@\x0e\x12\x80^B\xfc\xc0B^^B\x03@B^\x80\xa9w\xfc\xc0w\xa9\xa9w\x03@w\xa9\x02\xe0@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe2\x03@B^^B\xfc\xc0B^^\x03\x82\xfc\xc0w\xa9\xa9w\x03@w\xa9\xa9\x00\x00\x01\x00\x03\x00\x00\x03\xfa\x05\u007f\x00\x1c\x00\x00\x01\x06+\x01\x11\x14\x06#!\"'&?\x0163!\x11#\"'&7\x0162\x17\x01\x16\x03\xfa\x12(\xc0\x12\x0e\xfd@\x15\b\b\f\xa0\t\x10\x01@\xc0(\x12\x11\x1a\x01@\x12>\x12\x01@\x1b\x03\xa5%\xfc\xa0\x0e\x12\x12\x14\x0f\xc0\v\x02\x80%%\x1f\x01\x80\x16\x16\xfe\x80 \x00\x00\x00\x01\x00\x03\xff\x80\x03\xfa\x05\x00\x00\x1b\x00\x00\x13!2\x16\x15\x1132\x16\a\x01\x06\"'\x01&76;\x01\x11!\"/\x01&76 \x02\xc0\r\x13\xc0($\x1b\xfe\xc0\x12>\x12\xfe\xc0\x1a\x11\x12(\xc0\xfe\xc0\x0e\v\xa0\r\t\t\x05\x00\x13\x0e\xfc\xa1J \xfe\x80\x16\x16\x01\x80\x1f&%\x02\x80\v\xc0\x0e\x14\x13\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00$\x00\x00%\x0164/\x01&\"\a\x01'&\"\x0f\x01\x06\x14\x17\x01\x162\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\xad\x02f\x13\x13f\x134\x13\xfe-\xd3\x134\x13f\x13\x13\x01f\x134\x03f\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xed\x02f\x134\x13f\x13\x13\xfe-\xd3\x13\x13f\x134\x13\xfe\x9a\x13\x03\x86\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x06\x00\x10\x00\x15\x00\x1f\x00/\x00\x00\x01\x17\a#5#5\x01\x16\a\x01\x06'&7\x016\t\x03\x11\x01764/\x01&\"\x0f\x01%\x11\x14\x06#!\"&5\x11463!2\x16\x01\x94\x9848`\x01\xd2\x0e\x11\xfe\xdd\x11\r\x0e\x11\x01#\x11\xfe\xfb\x02 \xfe\xe0\xfd\xe0\x03\x80\\\x1c\x1c\x98\x1cP\x1c\\\x02\xa0\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xac\x984`8\x01\xba\r\x11\xfe\xdd\x11\x0e\r\x11\x01#\x11\xfd@\x02 \x01 \xfd\xe0\xfe\xe0\x02`\\\x1cP\x1c\x98\x1c\x1c\\`\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00)\x00\x00\x01\x114&#!\"\a\x06\x1f\x01\x01\x06\x14\x1f\x01\x1627\x01\x17\x163276\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\xfe *\x11\x11\x1f\x90\xfd\xea\x13\x13f\x134\x13\x02\x16\x90\x12\x1b\f\r'\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02`\x01\xe0\x1a&')\x1d\x90\xfd\xea\x134\x13f\x13\x13\x02\x16\x90\x13\x05\x11\x02*\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00%\x005\x00\x00\t\x0164'\x01&\a\x06\x1d\x01\"\x0e\x05\x15\x14\x17\x163276'\x027>\x013\x15\x14\x17\x1632\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\xed\x01`\x13\x13\xfe\xa0\x1e'(w\u0083a8!\n\xa7\v\x0e\a\x06\x16\x03,j.\xa8\x8c(\f\f\x1a\x02&\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xb3\x01`\x134\x13\x01`\x1f\x11\x11*\xa0'?_`ze<\xb5\xdf\f\x03\t\x18\x01bw4/\xa0*\x11\x05\x02\xc0\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x02\x00\x06\x00\x12\x00\x1e\x00\x00\x01-\x01\x01\x11\x01\x11\x00\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x02\x80\x01\x00\xff\x00\x01\x80\xfe\x00\x03 \x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xc0\x80\x80\x01O\xfd\xe2\xff\x00\x02\x1e\xfe\xdd\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\r\x00\x1d\x00-\x00\x00\x01\x16\a\x01\x06\"'\x01&763!2\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04y\x12\x17\xfe\xc0\x13B\x13\xfe\xc0\x17\x12\x11(\x02\x80(\x98\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03]#\x1f\xfe@\x1b\x1b\x01\xc0\x1f##\xfd \x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\r\x00\x1d\x00-\x00\x00\x01\x06#!\"'&7\x0162\x17\x01\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04y\x11(\xfd\x80(\x11\x12\x17\x01@\x13B\x13\x01@\x17u\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xa3###\x1f\x01\xc0\x1b\x1b\xfe@\x1f\xfe\xda\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\r\x00\x1d\x00-\x00\x00\x00\x14\a\x01\x06'&5\x11476\x17\x01\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04@\x1b\xfe@\x1f####\x1f\x01\xc0\xdb\x12\x0e\xfc@\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02\xa1B\x13\xfe\xc0\x17\x12\x11(\x02\x80(\x11\x12\x17\xfe\xc0\xfd\xec\x03\xc0\x0e\x12\x12\x0e\xfc@\x0e\x12\x12\x03\xce\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x01\x00\x00\x00\x00\x03\xf3\x05\x80\x00`\x00\x00%\x17\x16\x06\x0f\x01\x0e\a#\"\x00'#\"&=\x0146;\x01&7#\"&=\x0146;\x016\x0032\x17\x16\x17\x16\x0f\x01\x0e\x01/\x01.\x05#\"\x06\a!2\x17\x16\x0f\x01\x06#!\x06\x17!2\x17\x16\x0f\x01\x0e\x01#!\x1e\x0132>\x04?\x016\x17\x16\x03\xd0#\x03\f\v\x05\x04\r\x13\x18\x1b!\"'\x13\xea\xfe\xa2?_\r\x13\x13\rB\x02\x03C\x0e\x12\x12\x0ebC\x01a\xe0f\\\v\t\x06\x03+\x03\x16\r\x04\x04\x0f\x14\x19\x1b\x1f\x0e~\xc82\x01\xd4\x10\t\n\x03\x18\x05\x1b\xfe\x18\x03\x03\x01\xcb\x0f\n\t\x03\x18\x02\x12\v\xfe}0\xcb\u007f\x12$\x1f\x1c\x15\x10\x04\x05\r\r\f\xe5\x9f\f\x15\x04\x01\x02\x03\x06\x05\x05\x05\x04\x02\x01\x05\xdd\x13\rq\r\x1390\x12\x0er\x0e\x12\xd2\x01\x00\x17\x03\f\v\r\x9f\r\r\x04\x01\x01\x03\x04\x03\x03\x02\x80p\f\f\x0er\x1a%D\f\f\x0fp\v\x0fu\x89\x03\x04\x05\x05\x04\x01\x02\x05\a\a\x00\x00\x01\x00\x00\x00\x00\x03\xfc\x05\x80\x00?\x00\x00\x01\x11\x14\x06#!\"&=\x0146;\x01\x11#\"&=\x0146;\x0154632\x17\x1e\x01\x0f\x01\x06\a\x06'.\x02#\"\x06\x1d\x01!2\x16\x1d\x01\x14\x06#!\x11!546;\x012\x16\x03\xfc\x12\x0e\xfcD\x0e\x12\x13\ra_\x0e\x12\x12\x0e_\xf7\xbf\xb9\x96\t\x02\bg\t\r\r\n\x05*`-Uh\x011\r\x13\x13\r\xfe\xcf\x01\x9e\x12\x0e\xa2\x0e\x12\x01\x8f\xfe\x91\x0e\x12\x12\x0e\x96\r\x13\x01\u007f\x13\r\x83\x0e\x12߫\xde}\b\x19\n\u007f\v\x01\x02\t\x05\x1c$^L\xd7\x12\x0e\x83\r\x13\xfe\x85\xb5\r\x13\x13\x00\x00\x00\x01\x004\xff\x00\x03\xd2\x06\x00\x00b\x00\x00\x01\x14\x06\a\x15\x14\x06+\x01\"&=\x01.\x04'&?\x01676\x170\x17\x16\x17\x1632654.\x03'.\b5467546;\x012\x16\x1d\x01\x1e\x04\x17\x16\x0f\x01\x06\a\x06'.\x04#\"\x06\x15\x14\x1e\x04\x17\x1e\x06\x03\xd2ǟ\x12\x0e\x87\r\x13B{PD\x19\x05\x11\x0fg\a\x10\x0f\t\x02q\x82%%Q{\x1e%P46'-N/B).\x19\x11ĝ\x13\r\x87\x0e\x129kC<\x12\x06\x11\fQ\b\x0f\x0e\r\x03\x177>W*_x\x11*%K./58`7E%\x1a\x01_\x99\xdd\x1a\xaf\x0e\x12\x13\r\xaf\t,-3\x18\x06\x15\x14\x87\n\x02\x02\v\x02c\x1a\bVO\x1c2\")\x17\x15\x10\x12#\x1b,)9;J)\x8a\xd0\x1e\xb4\r\x13\x12\x0e\xb0\x06\"!*\x10\x06\x12\x14\x92\x0f\x01\x03\n\x03\x12#\x1d\x17VD\x1a,'\x1b#\x13\x12\x14\x17/&>AX\x00\x01\x00\x00\x00\x00\x03\x82\x05\x80\x00>\x00\x00\x01\x15\x14\x06+\x01\x0e\x01\a\x16\x01\x16\a\x06+\x01\"'\x00'&=\x0146;\x01267!\"&=\x01463!&+\x01\"&=\x01463!2\x16\x1d\x01\x14\x06+\x01\x16\x1732\x16\x03\x82\x12\x0e\xa8\x17Ԫ\xa7\x01$\x0e\n\b\x15\xc3\x10\t\xfe\xce\xc0\t\x13\rp\x84\xa1\x16\xfeU\x0e\x12\x12\x0e\x01\x9d9ӑ\r\x13\x12\x0e\x03@\x0e\x12\x12\x0e\xe9/\x11\xab\x0e\x12\x04*f\x0e\x12\x90\xb4\x14\xb2\xfe\x9a\x10\x12\x12\f\x01o\xcc\t\r\u007f\r\x13VR\x12\x0ef\x0e\x12q\x13\r\x85\x0e\x12\x12\x0ef\x0e\x12=S\x12\x00\x01\x00\x04\x00\x00\x03\xff\x05\x80\x00E\x00\x00!#\"&5\x11!\"&=\x01463!5!\"&=\x0146;\x01\x01&76;\x012\x17\x13\x16\x17>\x017\x136;\x012\x17\x16\a\x0132\x16\x1d\x01\x14\x06#!\x15!2\x16\x1d\x01\x14\x06#!\x11\x14\x06\x02[\xac\r\x13\xfe\xe0\r\x13\x13\r\x01 \xfe\xe0\r\x13\x13\r\xd6\xfe\xbf\b\b\n\x12\xc2\x13\n\xd7\x13%\n)\a\xbf\b\x15\xbf\x11\n\t\b\xfe\xc7\xd7\r\x13\x13\r\xfe\xde\x01\"\r\x13\x13\r\xfe\xde\x13\x12\x0e\x01J\x12\x0eg\r\x13U\x12\x0eh\r\x13\x02B\x10\x10\x10\x12\xfeW&W\x18X\x11\x01\xa4\x13\x10\x0e\x11\xfd\xbd\x13\rh\x0e\x12U\x13\rg\x0e\x12\xfe\xb6\r\x13\x00\x02\x00\x00\x00\x00\x05\x00\x05\x80\x00\a\x008\x00\x00\x004&#!\x11!2\x00\x10\x06#!\x15!2\x16\x1d\x01\x14\x06#!\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015#\"&=\x0146;\x01\x11463!2\x04\x13\x82j\xfe\xc0\x01@j\x01o\xfd\xc8\xfe\xac\x01\xf9\x0e\x12\x12\x0e\xfe\a\x13\r\xa7\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\x02\x1b\xc8\x03g\xc8|\xfe@\x01\xa1\xfe~\xf4v\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12\x0e\x80\x0e\x12v\x12\x0e\x95\r\x13\x02u\x0e\x12\x00\x06\x00\x00\x00\x00\a\x00\x05\x80\x00\b\x00\f\x00\x10\x00\x19\x00\x1d\x00n\x00\x00\x01\x13#\x13\x16\x14\x1746\x137!\x17!3'#\x01\x13#\x13\x14\x16\x1746\x137!\x17\x05\x15\x14\x06+\x01\x03\x06+\x01\"'\x03#\x03\x06+\x01\"&'\x03#\"&=\x0146;\x01'#\"&=\x0146;\x01\x03&76;\x012\x17\x13!\x136;\x012\x17\x13!\x136;\x012\x17\x16\a\x0332\x16\x1d\x01\x14\x06+\x01\a32\x16\x02\x02Q\x9fK\x01\x01\x01t#\xfe\xdc \x01\xa1\x8b#F\x01\x9fN\xa2Q\x01\x01\x01o!\xfe\xd7\"\x02\x80\x12\x0eդ\a\x18\x9f\x18\a\xa6ѧ\a\x18\x9f\v\x11\x02\xa0\xd0\x0e\x12\x12\x0e\xaf!\x8e\x0e\x12\x12\x0emY\x05\n\n\x10\x89\x1a\x05Z\x01ga\a\x18~\x18\ab\x01m]\x05\x1a\x89\x10\n\n\x05[o\x0e\x12\x12\x0e\x91\"\xb3\x0e\x12\x01U\x01+\xfe\xd4\x01\x04\x01\x01\x05\x01\xac\x80\x80\x80\xfd\xd4\x01,\xfe\xd5\x01\x05\x01\x01\x04\x01\xad\x80\x80 @\x0e\x12\xfd\x98\x18\x18\x02h\xfd\x98\x18\x0e\n\x02h\x12\x0e@\x0e\x12\x80\x12\x0e@\x0e\x12\x01X\x0f\r\f\x18\xfe\x98\x01h\x18\x18\xfe\x98\x01h\x18\f\r\x0f\xfe\xa8\x12\x0e@\x0e\x12\x80\x12\x00\x00\x03\x008\xff\x00\x04\xe8\x05\x80\x003\x00H\x00\\\x00\x00\x01\x16\a\x1e\x01\a\x0e\x04\a\x15#5\"'\x15#\x11\"&+\x017327\x113&#\x11&+\x015\x172753\x156353\x15\x1e\x03\x034.\x04\"\x06#\x112\x162>\x06\x034.\x04\x0e\x01#\x112\x16>\x06\x04\x8f\x12\x95ut\r\a3Nt\u007fR\x9aP*\x9a\x12H\x13\xc8\x1fo2\b\x10\x06\n\rLo\xd4@!\x9aR(\x9aOzh=\xd1\x1e,GID2F\xfd\x9e\n\xfe\xc1\n\r\f\v\xfe\xc0\x0f\b\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\x02\xee\x1a8PuE>.\x18\x12'\x0f\x10%&Te\x10\x02\x15Q,j\x86\x90m{\xa4\x1e\xfe+\xa7\x01\x02\a\b\x12>R\xc0{\xdf?jJrL6V\f\f\xfe\xc1\t\t\x01@\x10\x13\x14\x05`\x0e\x12\x12\x0e\xfa\xa0\x127>wmR1\x10\b\aq\a\x04\ruW\x17\x1c\x8fei\x92\xbd\x02/rr\x01\xb0\a\x18\x05\x10\f\r\x12:V\xb9\xfdr\x00\x00\x00\x00\x04\x00\"\xff\x00\x05\xce\x06\x00\x00\n\x00$\x007\x00V\x00\x00\x014&#\"\x06\x14\x16326\x01\x14\a\x01\x06#\"'\x01&76;\x01\x1146;\x012\x16\x15\x1132\x16\x05\x15!53\x1146=\x01#\a\x06\x0f\x01'73\x11\x13\x14\x0e\x03#\"'&'7\x16\x17\x163267#\x0e\x01#\"&54632\x16\x05BX;4>ID2F\xfd\x9e\n\xfe\xc1\n\r\f\v\xfe\xc0\x0f\b\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\x02\xd0\xfe+\xa7\x01\x02\a\b\x12>R\xc0{\xc3\x1a8PuE>.\x18\x12'\x0f\x10%&Te\x10\x02\x15Q,j\x86\x90m{\xa4\x04\xdf?jJrL6\xfb\xaa\f\f\xfe\xc1\t\t\x01@\x10\x13\x14\x05`\x0e\x12\x12\x0e\xfa\xa0\x12\xfcrr\x01\xb0\a\x18\x05\x10\f\r\x12:V\xb9\xfdr\x053>wmR1\x10\b\aq\a\x04\ruW\x17\x1c\x8fei\x92\xbd\x00\x00\x03\x00\x00\xff\x80\x06@\x05\x80\x00\v\x00\x1b\x00\\\x00\x00%4&#\"\x06\x15\x14\x16326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\x14\a\x16\x15\x16\a\x16\a\x06\a\x16\a\x06\a+\x02\".\x01'&'.\x015\x11467>\x01767>\x027>\x027632\x1e\x05\x15\x14\x0e\x01\a\x0e\x02\a!2\x16\x01\x00&\x1a\x1b%%\x1b\x1a&\xa0&\x1a\xfe\xe0\x1a&&\x1a\x01 \x1a&\x04\xa07\x0f\x03.\x11\x11\x0f'\t:@\x85$L\x11B\x9cWM{#\x1a&$\x19\x18h1D!\x12\x1a\t\t\a\v\x1c\x14\x13\x1a.I/!\x0f\t\x01\x13\x13\x12\x03\x0e\b\x04\x01\x15Nr\xc0\x1a&&\x1a\x1b%%\x02\x1b\xfd\x80\x1a&&\x1a\x02\x80\x1a&&\x1aV?, L=8=9%pEL\x02\x1f\x1b\x1a+\x01\x01%\x1a\x02\x81\x19%\x02\x02r@W!\x12<%*',<\x14\x13\x15\x1f2(<\x1e\x18&L,\"\x06\x18\x14\x0er\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06@\x05\x00\x00\v\x00\x1b\x00\\\x00\x00\x01\x14\x06#\"&54632\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26%\x16\x15\x0e\x01#!\x1e\x02\x17\x1e\x02\x15\x14\x0e\x05#\"'.\x02'.\x02'&'.\x01'.\x015\x1146767>\x02;\x03\x16\x17\x16\a\x16\x17\x16\a\x16\a\x14\x01\x00&\x1a\x1b%%\x1b\x1a&\xa0&\x1a\xfe\xe0\x1a&&\x1a\x01 \x1a&\x04i7\x01qN\xfe\xeb\x04\b\x0e\x03\x12\x12\x14\x01\t\x0f!/I.\x1a\x13\x14\x1c\v\a\t\t\x1a\x12!D1h\x18\x19$&\x1a#{MW\x9cB\x11L$\x85@:\t'\x0f\x11\x11.\x03\x03\xc0\x1a&&\x1a\x1b%%\xfd\xe5\x02\x80\x1a&&\x1a\xfd\x80\x1a&&\xaf=XNr\x0e\x14\x18\x06%(M&\x18\x1e<(2\x1f\x15\x13\x14<,'*%<\x12!W@r\x02\x02%\x19\x02\x81\x1a%\x01\x01+\x1a\x1b\x1f\x02LEp%9=8=L \x00\x00\f\x00\x00\xff\x80\x06\x00\x05\x80\x00\t\x00\x0f\x00\x17\x00+\x00=\x00\\\x00d\x00\u007f\x00\x8c\x00\x9e\x00\xb2\x00\xc2\x00\x00%54#\"\a\x15\x16327354\"\x15%\x15#\x11#\x11#5\x05\x11#5\x06#\"'&5\x113\x11\x14\x17\x16327\x11\x05\x15\x14\a\x06#\"'\x15#\x113\x15632\x17\x16\x17\x15\x14\a\x06\a\x06#\"'&=\x014762\x17\x16\x1d\x01#\x15\x143274645\x01\x15\x14\"=\x0142\x014'.\x01'&! \a\x0e\x01\a\x06\x15\x14\x17\x1e\x01\x17\x16 7>\x0176\x01\x13#\a'#\x1e\x01\x17\x16\x17\x153%54'&#\"\a\x06\x1d\x01\x14\x17\x163276\x173\x11#\x11\x06#\"'&5\x11#\x11\x14\x17\x16327\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x97\x1d\x11\x10\x10\x11\x1d\xb8BB\xfd\xc5PJN\x01\xb1C'%!\t\x06B\x01\x01\x0e\x14\x16\x01?\a\f)#!CC $)\f\a\xfb\x02\x03\f\x1b54\x1d\x15\x14\x1df\x1b\x15\x85\"\x18\x06\x01\xfe\x81@@\x02\x15\x13\nB+\x88\xfe\xec\xfe\xed\x88,A\n\x14\x14\nA+\x89\x02&\x89+A\n\x14\xfd\rZK35N\a \b#\vJ\x01!\x15\x1d13\x1b\x15\x15\x1b31\x1d\x15\xb5CC\x16\x14\x0f\x01\x01C\x06\v $)\x01\xf7\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xe9\x9d2\x10\xe0\x10\xab\"33\xe8F\xfeY\x01\xa7F~\xfe\x91(-\x1c\x11%\x01\"\xfe\xf2\x18\x02\x0f\x1f\x01\x18o\x924\x15*)$\x01\xed\xa1(*\x15\xb6\t\x1d\x0e\x16\x12(&\x1b;\x81;\x1b&&\x1d9LA3\x1a\x01\f\x15\v\x038\x9c33\x9c4\xfd\x03\xb1S,;\x05\x0f\x0f\x05;,W\xad\xb0T+<\x05\x0f\x0f\x05<+T\x03;\x01(\xc3\xc3\x17\\\x17g7\xc9x\x82:\x1d&&\x1d:\x82:\x1d&&\x1b<\x01r\xfe\xe5\x1f\x10\x02\x18\x01\x10\xfe\xdb%\x12\x1b-\x01\b\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\v\x00\x1b\xff\x00\x05\xe5\x06\x00\x00\t\x00\x0f\x00\x17\x00+\x00=\x00[\x00c\x00}\x00\x89\x00\x9b\x00\xaf\x00\x00\x01\x15\x14#\"'\x11632\x05\x15#542%35!\x153\x113!3\x11#\x11\x06#\"'&5\x11#\x11\x14\x17\x16327%54'&#\"\a5#\x1135\x163276%5#\x14\a\x06#\"=\x01354'&#\"\a\x06\x1d\x01\x14\x17\x16327676\x0154\"\x1d\x01\x142\x01\x14\a\x0e\x01\a\x06 '.\x01'&547>\x0176 \x17\x1e\x01\x17\x16\x013\x03\x11#\x11&'&'3\x13\x05\x15\x14\a\x06#\"'&=\x0147632\x17\x16%\x11#5\x06#\"'&5\x113\x11\x14\x17\x16327\x11\x03\xcb'\x17\x16\x16\x17'\x01RZZ\xfc:k\xfe\xc8id\x01 YY\x1e\x1b\x12\x03\x01Y\b\f.06\x01\xad\t\x1162+YY-06\x11\t\x01R[\x02\a!.\xb3\x1b'CD'\x1c\x1d'EH$\x12\x03\x02\xfd\xa0VV\x02\xcf\x1a\x0eX:\xb8\xfd\x1a\xb8:Y\r\x1a\x1a\x0eX;\xb7\x02\xe6\xb8:Y\r\x1a\xfc\x1afyd\x0e/%\x1cjG\x01\xb6\x1c&DC&\x1c\x1c&CD&\x1c\x01O[52.\r\b[\x01\x03\x12\x1b\x1e\x01$\xd3C\x16\x01-\x16D..D\x96^^\xfd\xc7\x01\xee\xfe\x86*\x15\x03 \x01l\xfey1\x18%=^\xc5I\x1a86\xd9\xfdi077\x1bS\r3\n$EWgO%33%O\xadO%35\x1b\x1b\t\x03\xc2\xd2EE\xd2F\xfdW\xeat;P\x06\x15\x15\x06P;p\xee\xeat;P\a\x14\x14\aP;p\x04\x0e\xfeq\xfe\xf1\x01\x0fJ\x8agT\xfe\xf9F\xafQ%33&P\xafP%33%R\xfe\r7>%\x183\x01\x8a\xfe\x91!\x02\x16+\x01}\x00\x00\x02\x00\x05\xff\x80\x05{\x05\xf6\x00\x13\x00'\x00\x00\x01\x06\x03\x06+\x01\"&7\x132'\x03&76;\x012\x17\x01\x16\a\x01\x15\x01\x16\a\x06+\x01\"'\x016\x016;\x012\x02U\n\xf7\x1b&\xef\x15\x14\n\xfd\x01\x01\xa1\f\v\t\x17\xef(\x1a\x03\xca\v\v\xfd\xf0\x01P\v\n\n\x16\xef*\x18\xfe\xad\x12\x02\x01\x19'\xf1\x16\x03e\x12\xfeJ.\"\x13\x01\xc0\x01\x01\x17\x16\x0f\x0f-\x01d\x10\x15\xfcZ\x01\xfd\x99\x14\x11\x0f-\x02n \x03\x8e-\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x13\x00'\x007\x00\x00\x014'&+\x01\"\a\x06\x1f\x01\x15\x03\x06\x17\x16;\x0127\x01&+\x01\"\a\x01\x16\x01\x16;\x01276'\x015\x016\x17\x11\x14\x06#!\"&5\x11463!2\x16\x02\xad~\x15\x1f\xb8\x12\b\a\b}\xc4\t\t\b\x10\xb9\x1f\x13\x037\a\x11\xbb\x1e\x13\xfee\x01\x01\x05\x14 \xb8\x12\a\b\t\xfe\xfc\x01\x99\b۩w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\x03\x01\xdd\"\v\f\x11\xd8\x01\xfe\xa6\x0e\x0e\r$\x03Q\f#\xfd'\x02\xfe!#\f\r\x0f\x01\xdc\x01\x02\xd3\x10\x88\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x02\x00\x00\x00\n\a\x00\x04\xf6\x00\x02\x00I\x00\x00\x01-\x01\x132\x04\x1f\x012\x1e\x05\x17\x1e\x02\x17\x1e\x01\x17\x1d\x01\x16\a\x0e\x01\x0f\x01\x0e\x06#\x06!&$/\x02.\x02'.\x02'.\x01'=\x01&7>\x01?\x01>\x0636\x02\xc7\x01\xe4\xfe\x1c\xb9\xa8\x019II\x01 \x0e!\x18 \x1e\x0e\x06\x13'\a\b\t\x01\x01\x13\a$\x0e\x0e\x0e\x1e \x18!\x0f\x1f\x01\xfb\xfe\x88\xcf\xfe\xcf01$$%A\x18\x06\x13'\a\b\t\x01\x01\x13\a$\x0e\x0e\x0e\x1e \x18!\x0e \x01\xfb\x01\x98\xfa\xfd\x01g\t\x05\x04\x03\x03\x06\n\x10\x17\x0f\x06\x19\\7@\x91)(\x88\x91\x917Y\x11\x11\x0f\x17\x0f\n\x06\x03\x03\x13\x02\t\x03\x04\x04\x05\n \x19\x06\x19\\7@\x91)(\x88\x91\x917Y\x11\x11\x0f\x17\x10\n\x06\x03\x03\x12\x00\x00\x05\x00@\xff\x80\x06\xc0\x05\x8a\x00\x03\x00\x13\x00\x17\x00\x1b\x00\x1f\x00\x00\t\x04\x15\x01\x15'\a5\x015\x17\x015\x177\x15\t\f\x01\x92\x01\xee\xfe\xaa\xfe\x16\x05,\xfe\x16\x01\x01\xfe\x17\x93\x01V\x01\x01\x01W\xfdQ\x01V\xfe\x12\xfe\xae\x05.\x01R\xfe\x17\xfe\xa9\x01W\x01\xe9\xfe\xae\xfe\x12\x03=\xfe\xcf\xfe\xe3\x01?\xfe\xe4l\xfe\xdb\x01\x01\x01\x01\x01%l`\x01\x1c\x02\x01\x01\x02\xfe\xe4\x04\xd8\xfe\xe3\xfe\xd0\x01\x0e\xfe\xf2\xfe\xf1\xfe\xc1\x01\x1d\x03~\xfe\xc1\xfe\xf2\x010\x00\x06\x00\v\xff\x00\x05\xf5\x06\x00\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x00\x05!\x11#\x11!\x11#%7\x05\a\x017\x01\a\x017\x01\a\x03\x01\a\t\x015!\x15\x05\t\xfb\xa2\xa0\x05\x9e\xa0\xfcR!\x03\x0f!\xfdXC\x02\xd5C\xfd\xf4f\x02ff\xd9\x01݀\xfe#\xfd\xb2\x03 `\x01\xe0\xfd\x80\x02\x80,\x9d\xa5\x9c\x02\x1a\x92\xfe\xad\x91\x02\xb6{\xfd\xff{\x03{\xfd\u007f`\x02\x81\xfa\xa1\x9f\x9f\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00O\x00g\x00\x00\x004&\"\x06\x14\x162\x00\x10\x06 &\x106 $\x14\x06\"&462$\"&\x0e\x02\a\x0e\x01\a\x0e\x03\x16\x14\x06\x1e\x02\x17\x1e\x01\x17\x1e\x0362\x16>\x027>\x017>\x03&46.\x02'.\x01'.\x03\x00\x10\a\x0e\x01\a\x06 '.\x01'&\x107>\x0176 \x17\x1e\x01\x17\x04\x00\x96Ԗ\x96\xd4\x01 \xe6\xfe\xb8\xe6\xe6\x01H\x01R6L66L\xfeG\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\x01\x01\x01\x05\x0f\v\x14L2\x1dUyH\x8b\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\x01\x01\x01\x05\x0f\v\x14L2\x1dUyH\x02n\x05\n\xe4\xd0X\xfe6X\xd0\xe4\n\x05\x05\n\xe4\xd0X\x01\xcaX\xd0\xe4\n\x02\x16Ԗ\x96Ԗ\x01\xa4\xfe\xb8\xe6\xe6\x01H\xe66L66L6\x80\x01\x01\x05\x0f\v\x14L2\x1dUyH\x8b\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\x01\x01\x01\x05\x0f\v\x14L2\x1dUyH\x8b\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\xfen\xfe6X\xd0\xe4\n\x05\x05\n\xe4\xd0X\x01\xcaX\xd0\xe4\n\x05\x05\n\xe4\xd0\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x17\x00\x1f\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x004&\"\x06\x14\x162$4&\"\x06\x14\x162\x04\xe0w\xa9\xa9w\xfc@w\xa9\xa9w\x01\x9a|\xb0||\xb0\x02\xb0|\xb0||\xb0\x05\x80\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfc\xa8\xb0||\xb0||\xb0||\xb0|\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x02\x00\t\x00\x15\x00\x00\x01\x13!\x053\t\x0137!\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x00\xc9\xfen\x026^\xfe5\xfe5^h\x02\n\x01\xfb\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03\x92\xfe\xce\xe0\x02\xb3\xfdM\xa0\x011\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x05\x00\x00\xffP\x05\x81\x05\xa3\x00\n\x00\x16\x00*\x00C\x00g\x00\x00\x01\x16\x06'.\x01676\x1e\x01\x17.\x01\a\x0e\x01\x17\x1e\x017>\x01\x13.\x02'$\x05\x0e\x02\a\x1e\x02\x17\x167>\x02\x13\x0e\x03\a\x0e\x01&'.\x03'&'?\x01\x16 7\x1e\x01\x06\x13\x06\x03\x0e\x02\a\x06%&'.\x04'.\x03'>\x04767$\x05\x16\x17\x1e\x01\x03/\bu5'\x1d\x1c&$I7o\x0e\xc6b?K\x03\x04\x93\\[z\xe4\x14H,1\xfe\xdd\xfe\xed+.@\x12\x1e\\7<\xe4\xdc?5\\V\b\x0f\r,$V\xcf\xc5g.GR@\x14\x19 \x06\x12\xdf\x027\xe0\x15\x06\x10\xb5\x1aU\x05,+!\xfc\xfe\x9a\xf8\x92\x0f\x15\r\x05\a\x02\t#\x15\x1a\t\x03\x1d\"8$\x1e}\xbc\x01{\x01)\x9b<\x10\x01\x02\xa5?L \x11RR\x11\x12\f;\x11kr,\x1cyE[\x80\b\b\x98\x02z\x1b#\t\b/1\a\n\"\x1a\x1c#\t\a\x1d\x1c\b\b#\xfc\x12\x1aeCI\x140/\x03\x11\b\x14\"5#`\xc4\x10\t\x94\x94\x06\"8\x03\xb8\xa7\xfe\x18\x1e4\x1c\x11~&\x1bp\f\x1d)\x1b4\t2\xc8{\xacH\x1a-\x1e\x1e\x0f\v.\x12%W.L\x14>\x00\x06\x00\x00\xff\x80\x06\x00\x05\x80\x00\b\x00\x13\x00'\x00:\x00Y\x00i\x00\x00\x014&\a\x06\x16\x17\x1667\x16\x0e\x01&'&676\x16\x13\x0e\x02\a\x06'.\x02'>\x0276\x17\x1e\x02\x1346&'\x06 '\x0f\x01\x16\x17\x16\x17\x167>\x02\x136'&'&\x05\x06\a\x0e\x02\a\x1e\x02\x17\x1e\x03\x17\x16\x17\x047>\x027\x12\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03PR$+\x01+'TJ\bX\x84j\x03\x027-F\x8f\xb6\x14C',\x9b\xa9,&C\x15\r.\"\x1e\xc6\xd2!$28\v\x05\x0f\xa1\xfeh\xa2\f\x05\x1a\x0f/\x9d\xf9\xb3\"\x1e\x0f\x87\t\x11+p\xd8\xfe\xf1\x84^&+3\x04\b\x16$\x06\x01\b\x06\x12\ri\xb3\x01\x03\xb5\x18\x1f\x1f\x040\x01(\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02\x9a+.\x16\x14i\x12\x176=Bn\f\\C1X\x14\x1fR\x01:\x15\x1a\x06\x05\x14\x14\x06\a\x19\x14\x13\x18\a\x05#\"\x05\a\x19\xfd\x03\a'\x19\x04jj\x06\f\x9a8Q\x1b.c\x13Aj\x02\xc75\x167!?\x1b\f\"\x0f\x140\x1eD\x8c\xca$\x054\x14\"\vP\x14\x1c[\r\x14&\x15\x01\v\x012\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x01\x00D\xff\x80\x04\x00\x06\x00\x00\"\x00\x00%\x17\x0e\x01\a\x06.\x035\x11#5>\x047>\x01;\x01\x11!\x15!\x11\x14\x1e\x0276\x03\xb0P\x17\xb0Yh\xadpN!\xa8HrD0\x14\x05\x01\a\x04\xf4\x01M\xfe\xb2\r C0N\xcf\xed#>\x01\x028\\xx:\x02 \xd7\x1aW]oW-\x05\a\xfeX\xfc\xfd\xfa\x1e45\x1e\x01\x02\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00/\x00\x00%'\x06#\x06.\x025\x11!5!\x11#\"\a\x0e\x03\a\x153\x11\x14\x1e\x027>\x01\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04p>,;$4\x19\n\x01\x01\xff\x00\xbc\b\x01\x05\x195eD\x82+W\x9bcE\x87\x01\xa2\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9K\xb7\x16\x01\x17()\x17\x01\x8e\xc2\x01F\n,VhV\x19\xa5\xfe^9tjA\x02\x010\x04/\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x01\x00\x03\xff@\x02\xfd\x06\x00\x00\x17\x00\x00\x00\x16\a\x01\x06#\"'\x01&76;\x01\x1146;\x012\x16\x15\x113\x02\xf5\x10\r\xfe\xa2\n\r\x0e\n\xfe\x9d\r\b\t\x14\xe0\x12\x0e\xc0\x0e\x12\xe0\x01\x00&\x10\xfe\x80\n\n\x01\x80\x10\x13\x13\x04\xe0\x0e\x12\x12\x0e\xfb \x00\x00\x00\x01\x00\x03\xff\x00\x02\xfd\x05\xc0\x00\x17\x00\x00\x01\x06+\x01\x11\x14\x06+\x01\"&5\x11#\"&7\x01632\x17\x01\x16\x02\xfd\t\x14\xe0\x12\x0e\xc0\x0e\x12\xe0\x15\x10\r\x01^\n\r\x0e\n\x01c\r\x04\x13\x13\xfb \x0e\x12\x12\x0e\x04\xe0&\x10\x01\x80\n\n\xfe\x80\x10\x00\x00\x00\x00\x01\x00@\x01\x03\a\x00\x03\xfd\x00\x17\x00\x00\x01\x15\x14\x06#!\x15\x14\x06'\x01&547\x016\x17\x16\x1d\x01!2\x16\a\x00\x12\x0e\xfb &\x10\xfe\x80\n\n\x01\x80\x10\x13\x13\x04\xe0\x0e\x12\x02\xe0\xc0\x0e\x12\xe0\x15\x10\r\x01^\n\r\x0e\n\x01b\x0e\b\t\x14\xe0\x12\x00\x00\x00\x01\x00\x00\x01\x03\x06\xc0\x03\xfd\x00\x17\x00\x00\x01\x14\a\x01\x06'&=\x01!\"&=\x01463!546\x17\x01\x16\x06\xc0\n\xfe\x80\x10\x13\x13\xfb \x0e\x12\x12\x0e\x04\xe0&\x10\x01\x80\n\x02\x83\x0e\n\xfe\x9e\x0e\b\t\x14\xe0\x12\x0e\xc0\x0e\x12\xe0\x15\x10\r\xfe\xa2\n\x00\x00\x00\x02\x00\x00\xff\x80\x05q\x06\x00\x00&\x008\x00\x00\x01\x06\a\x06#\"'&#\"\a\x06#\"\x03\x02547632\x17\x16327632\x17\x16\x17\x06\a\x06\x15\x14\x16\x01\x14\a\x06\a\x06\a\x06\a6767\x1e\x01\x17\x14\x16\x05q'T\x81\x801[VA=QQ3\x98\x95\x93qq\xabHih\"-bfGw^44O#A\x8a\xfe\xe1\x1d\x1e?66%C\x03KJ\xb0\x01\x03\x01\x01\x01A}}\xc4 !\"\x01\x03\x01\x05\xf2䒐\x1e\x1e\"\"A$@C3^q|\xc6\x04z=KK?6\x12\v\x06\x95lk)\x03\x10\x03\x04\f\x00\x00\x04\x00\x00\xff\x00\x06\x80\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x00\x01\x11%\x11\x01\x11!\x11\x01\x11%\x11\x01\x11!\x11\x02\xaa\xfdV\x02\xaa\xfdV\x06\x80\xfcu\x03\x8b\xfcu\x02\x12\xfdu^\x02-\x02\xe7\xfdm\x025\xfdw\xfc\xee}\x02\x95\x03n\xfc\xe6\x02\x9d\x00\x00\x00\x06\x00\x00\xff\x00\x05\x80\x05~\x00\a\x00\x0f\x00\x1c\x007\x00M\x00[\x00\x00\x00264&\"\x06\x14\x04264&\"\x06\x14\x052\x16\x15\x11\x14\x06\"&5\x1146\x05\x11\x14\x06+\x01\x15\x14\x06\"&=\x01#\x15\x14\x06#\"&5'#\"&5\x11\x01\x1e\x01\x15!467'&76\x1f\x0162\x1776\x17\x16\a\x01\x11\x14\x06#\"&5\x114632\x16\x01\xdd \x17\x17 \x16\x01\xbc \x16\x16 \x17\xfc\xfb*<;V<<\x04O@-K\x03<\x01&\x014'>\x03&4.\x02'.\x01'\x16\x17\x16\a\x06\a\x06.\x01'.\x04'.\x03'&6&'.\x01'.\x01676\x16\a\x06\x167645.\x03'\x06\x17\x14#.\x01\x06'6&'&\x06\a\x06\x1e\x017676\a\"&'&6\x172\x16\x06\a\x06\a\x0e\x01\a\x0e\x01\x17\x1e\x03\x17\x167>\x0376\x17\x1e\x01\x06\a\x0e\x01\a\x06\a\x06'&\x17\x16\x17\x167>\x05\x16\x17\x14\x0e\x05\a\x0e\x02'&'&\a\x06\x15\x14\x0e\x02\x17\x0e\x01\a\x06\x16\a\x06'&'&76\a\x06\a\x06\x17\x1e\x01\x17\x1e\x01\x17\x1e\x01\x06\a\x1e\x02\x156'.\x027>\x01\x17\x167676\x17\x16\a\x06\a\x06\x16\x17>\x0176&6763>\x01\x16\x016&'&\x15\x16\x172\a\x0632\x05.\x02'.\x04\a\x06\x16\x17\x166'4.\x01\a\"\x06\x16\x17\x16\x17\x147674.\x01'&#\x0e\x01\x16\a\x0e\x02\x17\x16>\x017626\x01\x1e\x02\x0e\x05\a\x0e\x01\a\x0e\x01'.\x03'&#\"\x06\a\x0e\x03'.\x01'.\x04'&676.\x0167>\x017>\x015\x16\a\x06'&\a\x06\x17\x1e\x03\a\x14\x06\x17\x16\x17\x1e\x01\x17\x1e\x027>\x02.\x01'&'&\a\x06'&7>\x027>\x03767&'&67636\x16\x17\x1e\x01\a\x06\x17\x16\x17\x1e\x01\x17\x16\x0e\x01\a\x0e\x03'.\x04'&\x0e\x01\x17\x16\a\x06\x1667>\x017>\x01.\x01'.\x0167\x1e\x05\x02\x97\v\t\x04\x05\x13\x05\\\x04\x0f\n\x18\b\x03\xfe\x9b\x04\x04\x05\x03\x03\a\n\t\x04\x11\x04\x01\x02\x02\x01\x02\x03U7\x04\a\x03\x03\x02\a\x01\t\x01\nJ#\x18!W!\v'\x1f\x0f\x01\v\t\x15\x12\r\r\x01\x0e\"\x19\x16\x04\x04\x14\v'\x0f;\x06\b\x06\x16\x19%\x1c\n\v\x12\x15\r\x05\x11\x19\x16\x10k\x12\x01\t)\x19\x03\x01\"\x1c\x1b\x1d\x02\x01\t\x11\a\n\x06\x04\v\a\x11\x01\x01\x14\x18\x11\x14\x01\x01\x16\t\b'\x01\r\x05\n\x0e\x16\n\x1b\x16/7\x02*\x1b \x05\t\v\x05\x03\t\f\x14I\t,\x1a\x196\n\x01\x01\x10\x19*\x11&\"!\x1b\x16\r\x02\x02\x06\x06\v\a\r\x03\x1cO6\x16\x15*\x16\x03\x01\x1e\x1d\r\x12\x17O\b\x02\x01\x06\b\x15 \x04\x02\x06\x04\x05\x02\x02$.\x05(\x04\x14\xa8\t\x10\x03\x1f\x1e\b*\x0e.'\x04\r\x06\x01\x03\x14\n.x\x85,\x17\v\f\x02\x01\x16\t\x06\x15\x03\x17\x02\x02\x11\x02\x16\x0f$\x01CN\xfd\xa1\x03\v\x06\t\x02\x03\n\x03\x03\v\x03\x01\xa3\x02\t\x11\x06\x05\t\x05\x06\x02\x03\x0e*\x12\t\v\xb4\n\f\x03\x06\x04\x04\x03\x0e\x04\b\x026\x05\r\x03\x0f\t\t\x05\x03\x02\x01\n\x02\x04\x04\b\x0e\b\x01\x10\x0e\x027\x14\x16\x02\a\x18\x17%\x1a&\b&_\x1c\x11f&\x12\x17\n\"\x1e,V\x13L\x14,G$3\x1c\x1d\xa4@\x13@$+\x18\x05\n\"\x01\x01\n\n\x01\n\x0eV\x11\x1e\x18\x155 3\"\t\r\x12\x02\f\x05\x04\x01\"\x03\x03\"\x14\x81#\x18dA\x17++\x03\x12\x14\ny0D-\v\x04\x03\x01\x01\x12\x1e\a\b%\x16&\x14n\x0e\f\x04\x024P'A5j$9E\x05\x05#\"c7Y\x0f\b\x06\x12\v\n\x1b\x1b6\"\x12\x1b\x12\t\x0e\x02\x16&\x12\x10\x14\x13\n8Z(;=I50\v' !!\x03\x0e\x01\x0e\x0f\x1a\x10\x1b\x04e\x01\x13\x01\x06\f\x03\x0e\x01\x0f\x03\v\r\x06\xfeR\x01\b\x11\x05\x05\b\v\x01\x01\x10\n\x03\b\x04\x05\x03\x03\x02\xfe\x9a\x12\x18\x0f\x19\x1b\x10\x1d\n\"\a+\x050n\x14\x14?\xa2t(\x02\x04-z.'<\x1f\x12\f\x01>R\x1e$\x16\x15A\"\b\x03\x1e\x01\x0124\x01\x03B\x19\x13\x0f\a\x04@\x05\x1e(\x15\t\x03\b~\x0f\t\x03\x04\a9B\x01\x019\x1f\x0f,\x1f\x02\x03\v\t\x01\x1d\x13\x16\x1e\x01*$\x04\x0f\x0e\f\x17\x01\x0e\x1a\x05\b\x17\x0f\v\x01\x02\x11\x01\f\t\x11\t\x0e\x06\x03\v\r\x03\x06\x1f\x04\x13\x04\x05\a\x02\x04\x04\x0f\x17\x01\x01\f\x10\x13\x0f\t\x04\t\x02\x05\x05\x04\x06\x03\a\x01\x0e<\x1a\f\v>\x1f\t\x03\a\x19?0D\x1d\x06\xa89\x12f\b\x18\x15\x1f?\x1c\x1c\x13\x01\x01\x04Ae\f \x04\x17\x87\t\x0f.(\x03\x0f;1.\x18D\b\x10\b\x02\x05\t\a4\x10\x0fH&\b\x06.\x19C\x17\x1d\x01\x13t \x15iY\x1a\x12% \v\x03*\x11\x1a\x02\x02\t\x05\x01\x0f\x14\xc2\b\a\x03\x04\x03\n\x06\a\x01\x02\x107\x04\x01\x12\xe0\v\x11\b\x01\x04\x04\x01\x04\x1b\x03\x05\x02\xea\x02\x06\b\x02\x0f\x01\r\r\x06\x04\r\x05\x06\x03\x06\f\x03\x01\x04\xfa\xc8\f\x19\x17\x16\x16\x11\x14\r\x12\x04\x13J\x1b\x10\a\x12\t\x1d\x16\x11\x01\x01\x03\x01\x01\x1c \x19\x01\x01<\r\x04\v\a\f\x11\v\x17W\v\x100%$\t\f\x04\n\x12\"\"I!\x14\x05\x03\r\x0f*\x06\x18\f\x16\v\x0fD\x0e\x11\t\x06\x19\b\x06 \x0e\x03\x06,4A'\x11\xbe4J\"\t\x18\x10\x16\x1d.0\x12\x15f6D\x14\x8f4p\xc6Z{+\x15\x01\x1d\x1b*\x9fD_wqi;\xd0W1G(\x02\x02\"%\x1e\x01\x01\b\x13\f\x1d\x05%\x0eT7F}AG\x05!1#\x19\x12% \x19\v\vJG\f\x1f3\x1e\x1b\v\x0f\x00\b\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0e\x00 \x00'\x00.\x002\x00>\x00V\x00b\x00\x00%&\x03#\a\x0e\x04\a'\x1632\x03&'\x04!\x06\x15\x14\x16\x17>\x03?\x01>\x01'&'\x0e\x01\a \x05&\a\x16\x17>\x01\x01\"\a6\x05&#\"\a\x16\x17>\x04\x13&'\a\x0e\x04\a\x16\x17\x1e\x01\x17>\x012\x1e\x04\x176\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00*b\x02\x02\x106\x94~\x88#\x0f\xb8\xea\x84=\x15 \xfe\xc9\xfe\x96\x01XP2\x93\x8a{&%\x04\x12gx|\x8a\xc0 \x01.\x03\xdc\xd2\xc7W)o\x94\xfc\xf1\x01\x01\x01\x02O\xb9\xf8LO\x83sEzG<\x0f\xe4\x03\x92\x01\t\x14CK}E\x19\x13\x02\t\x03$MFD<5+\x1e\nz\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a$\xf1\x01\x01\x01\x06\x15MW\x8eM\v\x96\x02\x931>]\a\x0e|\xe1YY\x9b^D\x0e\r\x01\x05\xd6եA\xf2\x97\xef<\x1f\xef\xe6K\xe5\x03m\x01\x01\x91\xa4\x13\xaa\xd4\x1aE6<\x15\xfe\"\xe8\xb2\x01\f\x19@9I\x1c5*\x05\x18\x05\x05\x04\x03\x05\x06\a\x05\x02\xc8\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00>\x00^\x00\x00\x014.\x03/\x01.\x045432\x1e\x0332654.\x01#\"\x0e\x02\x15\x14\x1e\x02\x1f\x01\x16\x17\x16\x15\x14\x06#\".\x03#\"\x06\x15\x14\x1632>\x02\x05\x14\x06#\"'\x06#\"$&\x02547&54632\x17632\x04\x16\x12\x15\x14\a\x16\x04\x95':XM1h\x1e\x1c*\x12\x0f\x90+D($,\x1a/9p\xac`D\x80oC&JV<\x92Z\x16 PA3Q1*2\x1d23\xf4\xa9I\x86oB\x01kែhMI\x8f\xfe\xfb\xbdo\x10PែhMI\x8f\x01\x05\xbdo\x10P\x01\xd92S6,\x18\v\x18\a\a\x10\x10\x1a\x11M\x18!\"\x18@-7Y.\x1f?oI=[<%\x0e$\x16\x0e\x14('3 -- <-\\\x83%Fu\x90\x9f\xe1P\x10o\xbd\x01\x05\x8fIMh\x82\x9f\xe1P\x10o\xbd\xfe\xfb\x8fIMh\x00\x00\x00\x03\x00,\xff\x80\x04\xcb\x06\x00\x00#\x00?\x00D\x00\x00\x0176&#!\"\x06\x15\x11\x147\x01>\x01;\x01267676&#!\"&=\x01463!267\x06\n\x01\a\x0e\x04#!\"\a\x06\x01\x0e\x01'&5\x11463!2\x16\a\x036\x1a\x01\x03\xe8%\x05\x1c\x15\xfd8\x17\x1f\x06\x01#\x17\x1e!\xef\x16\x1e\x03\x18\r\x04\x1f\x15\xfe\xda\x1d&&\x1d\x01Z\x12\"\xe6\x0fM>\x04\x06\x06\x16\x1b2!\xfe\xf1\r\t\b\xfe^\x16I\f7LR\x03x_@\x16\x9e\x04>M\x04N\xc2\x17\"\"\x14\xfb\xb3\a\x06\x01`\x1a\x0f\x1d\x0f\x82=\x15&&\x1d*\x1d%\x1b\xeeI\xfe}\xfe\xc7\x11\x16\x15,\x16\x14\n\t\xfe\x1b\x19\a\t\x16L\x05\x827_jj\xfc\xea\x11\x019\x01\x83\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00%\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x02\xc0\x12\x0e\xfe \x0e\x12\x12\x0e\x01\xe0\x0e\x12\x02\xa0\x12\x0e\xfe \x0e\x12\x12\x0e\x01\xe0\x0e\x12\xa0&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\xc0\x04\x00\x0e\x12\x12\x0e\xfc\x00\x0e\x12\x12\x01\x8e\x02\x80\x0e\x12\x12\x0e\xfd\x80\x0e\x12\x12\x03\x0e\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x00\x00\x00\x00\x02\x00\x00\xff\x00\x05\x00\x05\xe0\x001\x009\x00\x00\x01\x14\x06#\"'\x03#\x15\x13\x16\x15\x14\x06+\x01\x11\x14\x06+\x01\"&5\x11#\"&547\x135#\x03\x06#\"&547\x0163!2\x17\x01\x16\x00\x14\x06\"&462\x05\x008(3\x1d\xe3-\xf7\t&\x1a\xc0B.\xa0.B\xc0\x1a&\t\xf7-\xe3\x1d3(8\x10\x01\x00Ig\x01\x80gI\x01\x00\x10\xfe`\x83\xba\x83\x83\xba\x01\xe0(8+\x01U\x84\xfee\x0f\x12\x1a&\xfe\xf0.BB.\x01\x10&\x1a\x12\x0f\x01\x9b\x84\xfe\xab+8(\x1d\x18\x01\x80kk\xfe\x80\x18\x03`\xba\x83\x83\xba\x83\x00\x02\x00\x00\xff\x00\x04\x00\x05\xe0\x00%\x00-\x00\x00\x01\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11463!2\x16\x00\x14\x06\"&462\x04\x008P8@B\\B@B\\B@8P8pP\x02\x80Pp\xfe\xe0\x83\xba\x83\x83\xba\x03@\xfe`(88(\x01`\xfcp.BB.\x01\xd0\xfe0.BB.\x03\x90\xfe\xa0(88(\x01\xa0Ppp\x01ͺ\x83\x83\xba\x83\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x15\x00!\x00\x00%\x01>\x01&'&\x0e\x01\a\x06#\"'.\x02\a\x0e\x01\x16\x17$\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x05\x01^\x10\x11\x1d/(V=\x18$<;$\x18=V).\x1d\x11\x10\x04X\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xea\x01\xd9\x16J`\x1f\x1a\x01\"\x1c((\x1c\"\x01\x1a\x1f`J\x16\x8e\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00,\xff\x00\x06\xd4\x05\xff\x00\x0f\x00I\x00\x00\x004.\x02\"\x0e\x02\x14\x1e\x022>\x01%\x06\a\x05\x11\x14\a\x06'%\a\x06\"/\x01\x05\x06'&5\x11%&'&?\x01'&767%\x11476\x17\x05762\x1f\x01%6\x17\x16\x15\x11\x05\x16\x17\x16\x0f\x01\x17\x16\x05\xc0[\x9b\xd5\xea՛[[\x9b\xd5\xea՛\x01o\x04\x10\xfe\xdc\r\x0f\x0e\xfeܴ\n \n\xb4\xfe\xdc\x0e\x0f\r\xfe\xdc\x10\x04\x05\t\xb4\xb4\t\x05\x04\x10\x01$\r\x0f\x0e\x01$\xb4\t\"\t\xb4\x01$\x0e\x0f\r\x01$\x10\x04\x05\t\xb4\xb4\t\x02\v\xea՛[[\x9b\xd5\xea՛[[\x9b5\x0f\x05`\xfe\xce\x10\n\n\x06^\xf8\r\r\xf8^\x06\n\n\x10\x012`\x05\x0f\x11\f\xf8\xf8\r\x10\x0f\x05`\x012\x10\n\n\x06^\xf8\f\f\xf8^\x06\n\n\x10\xfe\xce`\x05\x0f\x10\r\xf8\xf8\f\x00\x02\x00\x00\xff\x80\x05\xbe\x05\u007f\x00\x12\x001\x00\x00%\x06#\"$\x02547\x06\x02\x15\x14\x1e\x0232$%\x06\x04#\"$&\x0254\x126$76\x17\x16\a\x0e\x01\x15\x14\x1e\x013276\x17\x1e\x01\x04\xee68\xb6\xfeʴh\xc9\xfff\xab킐\x01\x03\x01&^\xfe\x85\xe0\x9c\xfe\xe4\xcezs\xc5\x01\x12\x99,\x11\x12!V[\x92\xfa\x94vn)\x1f\x0e\a\xe9\t\xb4\x016\xb6\xc0\xa5<\xfe\xaeׂ\xed\xabf{\xc3\xcb\xf3z\xce\x01\x1c\x9c\x99\x01\x17\xcc}\x06\x02))\x1fN\xcfs\x94\xfa\x923\x12\x1f\x0e(\x00\x03\x00@\xff\x80\x06\xc0\x05\x80\x00\v\x00\x1b\x00+\x00\x00\x004&#!\"\x06\x14\x163!2\x01\x11\x14\x06#!\"&5\x11463!2\x16\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04@&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a\x02f&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&@&\x1a\xfa\x00\x1a&&\x1a\x06\x00\x1a&\x02\xa64&&4&\x01\x00\xfc@\x1a&&\x1a\x03\xc0\x1a&&\x01\xa6\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x00\x00\x02\x00 \xff\xa0\x06`\x05\xc0\x00B\x00H\x00\x00\x00\x14\x06+\x01\x14\a\x17\x16\x14\a\x06\"/\x01\x0e\x04#\x11#\x11\".\x02/\x01\a\x06#\"'.\x01?\x01&5#\"&46;\x01\x11'&462\x1f\x01!762\x16\x14\x0f\x01\x1132\x01!46 \x16\x06`&\x1a\xe0C\xd0\x13\x13\x126\x12\xc6\x05\x14@Bb0\x803eI;\x0e\x0f\xb7\x14\x1c\x18\x13\x13\x03\x11\xca:\xe0\x1a&&\x1a\xe0\xad\x13&4\x13\xad\x03L\xad\x134&\x13\xad\xe0\x1a\xfeF\xfd\x80\xbb\x01\n\xbb\x02Z4&\xabw\xd1\x134\x13\x13\x13\xc5\x05\x10) \x1a\x03\x80\xfc\x80\x1b''\r\x0e\xcf\x15\x10\x125\x14\xe3r\xa0&4&\x01&\xad\x134&\x13\xad\xad\x13&4\x13\xad\xfe\xda\x02\x00\x85\xbb\xbb\x00\x00\x01\xff\xff\x00\x01\a}\x04G\x00\x85\x00\x00\x01\x16\a\x06\a\x0e\x02\x1e\x02\x17\x16\x17\x16\x17\x1e\x02\x0e\x01#\x05\x06&/\x01.\x03\a\x0e\x04\x17\x14\x06\x0f\x01\x06\a#\x06.\x02/\x01.\x03\x02'&4?\x0163%\x1e\x01\x1f\x01\x16\x17\x1e\x01\x1f\x01\x1e\x0327>\x04'.\x01/\x01&'&7676\x17\x16\x17\x1e\x03\x14\x0e\x01\x15\x14\x06\x1e\x02\x17\x1e\x01>\x02767>\x01?\x01>\x02\x17%6\x16\x17\a}\x17\xad\x18)(\x1e\x1f\a\x13.\"\x04\x01\x8d2\x03\a\a\b*&\xff\x00\x18@\x14\x14\x1eP9A\x18\x03\n\x18\x13\x0f\x01\a\x04\x04\x12#sG\x96q]\x18\x19\n#lh\x8d<\x06\x03\x04\x0f*\x01\x12\f\x16\x05\x05\x10\b\x144\x0f\x10\x1d6+(\x1c\r\x02\x06\x12\t\n\x05\x02\x0e\a\x06\x19<\r\x12\x10\x165\xbaR5\x14\x1b\x0e\a\x02\x03\x02\x01\x06\x11\x0e\b\x12\"*>%\"/\x1f\t\x02\x04\x1a+[>hy\n\x0f\x03\x03\x01\x03\x03\x01\x02\x05\x0f\t\x00\a\x00\x00\xff\xaa\x06\xf7\x05K\x00\n\x00\x15\x00!\x00/\x00U\x00i\x00\u007f\x00\x00%6&'&\x06\a\x06\x1e\x01676&'&\x06\a\x06\x17\x166\x17\x0e\x01'.\x017>\x01\x17\x1e\x01%.\x01$\a\x06\x04\x17\x1e\x01\x0476$%\x14\x0e\x02\x04 $.\x0154\x1276$\x17\x16\a\x06\x1e\x016?\x0162\x17\x16\a\x0e\x01\x1e\x01\x17\x1e\x02\x02\x1e\x01\a\x0e\x01'.\x0176&\a\x06&'&676%\x1e\x01\a\x0e\x01.\x0176&'.\x01\a\x06.\x01676\x16\x02\xa3\x15\x14#\"N\x15\x16\x12DQt\b\t\r\x0e\x1d\a\x11\x1e\x0e\x1e\xb5-\xe2okQ//\xd1jo_\x01\v\t\xa0\xfe\xff\x92\xdf\xfe\xdb\x0e\t\xa0\x01\x01\x92\xdf\x01%\x01&J\x90\xc1\xfe\xfd\xfe\xe6\xfe\xf4Ղ\x8b\x80\xa9\x01YJA-\x04\x06\x0e\x0f\x06\x06\x8b\xd6.--\x02\x05\x0e\n\f9\\DtT\x19\x13\b+\x17\x17\x16\a\x14X?\x18*\x04\x05\x1a\x18<\x01UW3'\t26\x1a\b\x1c$>>\xacW\x1c0\f\x1f\x1c{\xf2\xfc\"F\x0f\x0e\x1a!\"E \x1b\x9b\r\x1b\x05\x05\v\r\x1f\x0e\x05\v^f`$\"\xb9_]\\\x1b\x1d\xb5<`\x94F\x0e\x17\xed\x92`\x94F\x0e\x17\xed\x8eD\x8f\x83h>Cw\xb7ls\x01\x04\x80\xa9\x86J@\x91\x0e\f\x02\x03\x02\x02;=?s\r\x0e\v\x04\x04\x12:i\x02_^{8\x17\x16\a\b+\x17?`\r\x05\x1a\x18\x18)\x05\rO`\xfds\x1b\x1a\x122\x1bR\xb4DE5\x12\x06\x1f8/\x06\x1aK\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05r\x00\t\x00\x13\x00\x1d\x00\x00\x05\x06#\"'>\x017\x1e\x01\x01\x11\x14\x02\a&\x114\x12$\x01\x10\a&\x025\x11\x16\x04\x12\x04m\xab\xc5ī\x8a\xc3\"#\xc3\xfe\x9b\xfd̵\xa7\x01$\x045\xb5\xcc\xfd\xb3\x01$\xa7\"^^W\xf8\x90\x90\xf8\x05=\xfe\x1b\xfc\xfeac\xd7\x01\x18\xbb\x01E\xd6\xfd*\xfe\xe8\xd7c\x01\x9f\xfc\x01\xe5\x1e\xd6\xfe\xbb\x00\x00\x00\x01\x00\x00\xff\x00\x05z\x06\x00\x00k\x00\x00\x01\x0e\x03.\x03/\x01\x06\x00\a\"&4636$7\x0e\x02.\x03'>\x01\x1e\x02\x1767\x0e\x02.\x05'>\x01\x1e\x05\x1f\x0165.\x0567\x1e\x04\x0e\x02\x0f\x01\x16\x14\a>\x05\x16\x17\x0e\x06&/\x01\x06\a>\x05\x16\x05z X^hc^O<\x10\x11q\xfe\x9f\xd0\x13\x1a\x1a\x13\xad\x01+f$H^XbVS!rȇr?\x195\x1a\a\x16GD_RV@-\x06F\u007fbV=3!\x16\x05\x04\f\b\x1bG84\x0e&3Im<$\x05\x06\x14\x12\b\a\x01\x01\x03\x0e/6X_\x81D\x02'=NUTL;\x11\x11\x172\x06\x18KPwt\x8e\x01\xb1Pt= \x03\x0e\x1e\x19\n\n\xe4\xfe\xf9\x01\x1a&\x19\x01ռ\x0e\x12\b\r,J~S/\x14#NL,\x83\xa0\x01\x03\x02\x03\x11\x1d8JsF\x1c\x11\x13);??1\x0f\x10zI\x06\x14EJpq\x8dD\x19IPZXSF6\x0f\x0f\x04\\\x1a\a\x17?5:\x1f\x02\x17N\u007fR=\x1e\x12\x01\x03\x03\x03\x93\x88\a\x17;.&\x021\x00\x04\x00\x15\xff\x00\x04\xeb\x05\x00\x00\f\x00\x10\x00\x14\x00\x1e\x00\x00\x01\x15\x14\x06+\x01\x01\x11!\"&=\x01\x01\x15!\x11\x01\x15!\x11%\x15!5463!2\x16\x04\xebsQ9\xfe\xfc\xfd\xefQs\x04\xd6\xfb*\x04\xd6\xfb*\x04\xd6\xfb*sQ\x03NQs\x01\x1bBUw\xfe\xf3\x01\rwUB\x01F\xff\x00\xff\x01H\xff\x00\xff\x8cCCTww\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00%\x001\x00\x00\x00\x14\a\x01\x06#\"&=\x01!\"&=\x01463!54632\x17\x01\x16\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x80\t\xfe\xc0\t\x0e\r\x13\xfe\xa0\r\x13\x13\r\x01`\x12\x0e\f\f\x01?\xa9\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\x8e\x1c\t\xfe\xc0\t\x13\r\xc0\x13\r\xc0\r\x13\xc0\x0e\x12\n\xfe\xc1\xab\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00%\x001\x00\x00\x01\x15\x14\x06#!\x15\x14\x06#\"'\x01&47\x01632\x16\x1d\x01!2\x16\x12\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x80\x13\r\xfe\xa0\x12\x0e\f\f\xfe\xc1\t\t\x01@\t\x0e\r\x13\x01`\r\x13\xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xe0\xc0\r\x13\xc0\x0e\x12\n\x01?\t\x1c\t\x01@\t\x13\r\xc0\x13\xfe\xff\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x11\x14\x06#\"'\x01&47\x01632\x16\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04\x00&\x1a\x14\x11\xfe@\x1b\x1b\x01\xc0\x11\x14\x1a&\x01\x00\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\xc0\xfd\x80\x1a&\f\x01@\x13B\x13\x01@\f&\xfc\xc6\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x13\x00\x1f\x00\x00\x00\x14\x06\"&462\x12 \x0e\x01\x10\x1e\x01 >\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00\x96Ԗ\x96\xd4*\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xeaԖ\x96Ԗ\x01 \x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06]\x05\xe0\x00\x15\x006\x00\x00\x01\x17\x06\x04#\"$\x0254\x127\x17\x0e\x01\x15\x14\x0032>\x01%\x17\x05\x06#\"'\x03!\"&'\x03&7>\x0132\x16\x15\x14\x06'\x13!\x15!\x17!2\x17\x13\x03\xfff:\xfeл\x9c\xfe\xf7\x9bѪ\x11z\x92\x01\a\xb9~\xd5u\x02\x1b:\xff\x00\r\x10(\x11\xef\xfe(\x18%\x03`\x02\b\x0eV6B^hD%\x01\xa7\xfei\x10\x01\xc7(\x11\xe4\x01]̳ޛ\x01\t\x9c\xb5\x01*>\x836߅\xb9\xfe\xf9\x82\xdd\x1ar\x80\a#\x01\xdd!\x18\x03\v\x11\x193?^BEa\a\xfe߀\x80#\xfe9\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00\x00\x016'&\x03632\a\x0e\x01#\"'&'&\a\x06\a\x0e\x01\a\x17632\x17\x1e\x01\x17\x1632\x13\x12\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\f\n\xab\xe7Q,&U\v\x04\x8c#+'\r \x1e\x82;i\x1bl\x1b4L\v92\x0f<\x0fD`\x9d\xe2\xdc\xfa\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\x82\xd8\x06\b\xfe\xf3\x13`9ܩ6ɽ\f\a]\x18`\x18C4\xb37\xdb7\xb3\x01&\x01\x1b\x01\u007f\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x01\x00\x00\x00\x00\x04\x80\x05\x80\x00D\x00\x00\x01\x14\x02\x04+\x01\"&5\x11\a\x06#\"'&=\x014?\x015\a\x06#\"'&=\x014?\x01546;\x012\x16\x1d\x01%6\x16\x1d\x01\x14\a\x05\x15%6\x16\x1d\x01\x14\a\x05\x116\x00546;\x012\x16\x04\x80\xbd\xfe\xbc\xbf\xa0\x0e\x12\xd7\x03\x06\n\t\r\x17\xe9\xd7\x03\x06\n\t\r\x17\xe9\x12\x0e\xa0\x0e\x12\x01w\x0f\x1a\x17\xfew\x01w\x0f\x1a\x17\xfew\xbc\x01\x04\x12\x0e\xa0\x0e\x12\x02\xc0\xbf\xfe\xbc\xbd\x12\x0e\x02cB\x01\x06\n\x10\x80\x17\bG]B\x01\x06\n\x10\x80\x17\bG\xfa\x0e\x12\x12\x0e\xb5t\x05\x14\x10\x80\x17\by]t\x05\x14\x10\x80\x17\by\xfe\x19\r\x01\x14\xbe\x0e\x12\x12\x00\x03\x00\x00\x00\x00\x05\x80\x05\x80\x00#\x003\x00C\x00\x00\x01\x15\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!\x1146;\x012\x16\x15\x11!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04\x80\x12\x0e\xfe\xa0\x12\x0e@\x0e\x12\xfe\xa0\x0e\x12\x12\x0e\x01`\x12\x0e@\x0e\x12\x01`\x0e\x12\x80^B\xfc\xc0B^^B\x03@B^\x80\xa9w\xfc\xc0w\xa9\xa9w\x03@w\xa9\x02\xe0@\x0e\x12\xfe\xa0\x0e\x12\x12\x0e\x01`\x12\x0e@\x0e\x12\x01`\x0e\x12\x12\x0e\xfe\xa0\x12\xfe2\x03@B^^B\xfc\xc0B^^\x03\x82\xfc\xc0w\xa9\xa9w\x03@w\xa9\xa9\x00\x00\x00\x00\x04\x00\x00\xff\x80\b\x80\x05\x00\x00'\x00/\x00?\x00P\x00\x00\x01\x06+\x015#\"&547.\x01467&546;\x01532\x17!\x1e\x01\x17\x1e\x02\x14\x0e\x01\a\x0e\x01\a7\x16\x14\a\x1764'\x01!\x06\a\"\x06\x0f\x01\x01\x0e\x01+\x01\x0332\x03#\x1332\x16\x17\x01\x1e\x043\x05!&\x02ln\x9e\x80@\r\x13\a:MM:\a\x13\r@\x80\x9en\x04Y*\x81\x10Yz--zY\x10\x81*\x0655QDD\xfbU\x03\xf7\xd9\xef9p\x1b\x1c\xfe\xe0\x1aY-`]\x1d\x9d\x9d\x1d]`.X\x1a\x01 \x04\x0e/2I$\x01\xc8\xfc\tt\x01\xa0@@/!\x18\x19\x02\x11\x18\x11\x02\x19\x18!/@@\a\x16\x03\x0f3,$,3\x0f\x03\x16\a\xfc$p$\x1e0\x940\xfe\xd6&*0\x18\x18\xfe\xe0\x1a&\x01\xd0\x01\xe0\x01\xd0&\x1a\xfe\xe0\x04\r!\x19\x15P@\x00\x02\x00\x00\xff\x80\x06\x80\x06\x00\x00R\x00V\x00\x00\x012\x16\x15\x14\x0f\x01\x17\x16\x15\x14\x06#\"&/\x01\x05\x17\x16\x15\x14\x06#\"&/\x01\a\x06#\"&546?\x01\x03\a\x06#\"&546?\x01'&54632\x16\x1f\x01%'&54632\x16\x1f\x017632\x16\x15\x14\x06\x0f\x01\x1376\x01%\x03\x05\x05\xef>S]\xac8\aT;/M\x0f7\xfe\xca7\bT\x057%>\x01\x04\xe0w\xa9\xa9w\xfc@w\xa9\xa9w\x03\xe0\x1f!\"\xc55bBBb/\xbe/\f*\n8(\x03@(87)\xfc\xc0(8=%/\xb5'\x03\x1c\x0e\x1c\x13\x18\x15\x14\x15\x18\x13\x1c\x0e\x1c\x03\x01\v#?\x05\x80\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfb\xe0\x01\xb4#\x14\x16~$EE y \b&\b\xfeL(88\x02e):8(%O\x19 r\x1a\x02\x13\t\x11\t\n\x05\x05\n\t\x11\t\x13\x02\xae\x17O\x00\x00\x00\x00\x06\x00\x00\xff\x00\a\x00\x06\x00\x00\x05\x00?\x00G\x00Q\x00a\x00q\x00\x00\x1347\x01&\x02\x01\x14\x0e\x03\a\x03\x0167>\x01&\x0f\x01&'&\x0e\x01\x1e\x01\x1f\x01\x13\x03\x0167>\x01&\x0f\x01\"$32\x04\x17#\"\x06\x15\x14\x1e\x06\x17\x16\x05\x13\x16\x17\x06#\"'\x01\x16\x15\x14\x02\a\x13654\x00 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126\x00 $6\x12\x10\x02&$ \x04\x06\x02\x10\x12\x16\u007fC\x01o\xc4\xee\x05\b\x05\x0f\b\x1b\x04L\xfe\xea.*\x13\x0e\x13\x13\xcdK\u007f\f\x11\x06\x03\x0f\fPx\xa8\xfe\xe8.*\x13\x0e\x13\x13\xcd\a \ni\x01SƓ\x01\vi\n7J\x04\x04\f\x06\x12\a\x16\x03?\xfe\x06\xed\x01\x04~\x81pi\x03{_Я\xeb;\xfc\xa2\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01U\x01Z\x01=刈\xe5\xfe\xc3\xfe\xa6\xfe\xc3刈\xe5\x02\x80\xa3\x96\xfc\x13_\x01t\x01\b\x13'<\x1cZ\r\xff\x00\x03:\x03\x05\x02!\x1d\x01\n\x01\t\x01\f\x12\x13\x0e\x01\b\xfe\xb8\xfe\b\x03@\x03\x05\x02!\x1d\x01\n\x01\xa0\xbbj`Q7\f\x18\x13\x1b\x0f\x1e\f$\x05k\xd3\xfdy\x06\x05, \x04R\xae\xc3\xd1\xfe\x9ff\x02\xa6\xa9k*\x024\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xf9\xb7\x88\xe5\x01=\x01Z\x01=刈\xe5\xfe\xc3\xfe\xa6\xfe\xc3\xe5\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x06\x00\x00\x12\x00\x1b\x00\x00\x01\x11\x05&$&546$7\x15\x06\x04\x15\x14\x04\x17\x11\x01\x13%7&'5\x04\x17\x04>\xfe\xf0\xe4\xfe\x8c\xd6\xc9\x01]\xd9\xd9\xfe\xe9\x015\xea\x03\xad%\xfd\xf3\x93w\xa1\x01\x15\xcc\x06\x00\xfa\x00\x80\x14\xa4\xfd\x92\x8c\xf7\xa4\x1a\xac&\xe0\x8f\x98\xe6\x1e\x05P\xfe?\xfezrSF\x1d\xac!|\x00\x00\x00\x03\x00\x00\xff\x00\a\x80\x06\x00\x00\f\x00&\x000\x00\x00\t\x01\x15#\x14\x06#!\"&5#5\x01!\x113\x11!\x113\x11!\x113\x11!\x1132\x16\x1d\x01!546;\x01\x052\x16\x1d\x01!5463\x03\xc0\x03\xc0\x80)\x1c\xfa\n\x1c)\x80\x01\x00\x01\x00\x80\x01\x00\x80\x01\x00\x80\x01\x00;\x1c)\xf9\x80)\x1c;\x06;\x1c)\xf8\x80)\x1c\x06\x00\xfe\x80\x80\x1a&&\x1a\x80\xff\x00\xfd\x00\x03\x00\xfd\x00\x03\x00\xfd\x00\x03\x00\xfd\x00&\x1a@@\x1a&\xc0&\x1a\x80\x80\x1a&\x00\x00\x02\x00\x00\xff\x80\t\x00\x05\x80\x00\r\x006\x00\x00\x01\x13\x16\x06\x04 $&7\x13\x05\x1627\x00\x14\a\x01\x06\"'%\x0e\x01\a\x16\x15\x14\a\x13\x16\a\x06+\x01\"'&7\x13&54767%&47\x0162\x17\x01\x06\xee\x12\x04\xac\xfe\xd6\xfe\xa4\xfe֬\x04\x12\x02>\x164\x16\x04P\x16\xfb\xa0\x04\f\x04\xfdt+8\x06?::\x02\n\t\x0f\xc0\x0f\t\n\x02::A\vW\xfe\xb3\x16\x16\x04`\x04\f\x04\x04`\x02\xbc\xfe\xc4EvEEvE\x01<\xb5\a\a\x02\x10.\b\xfe\xa0\x01\x01\xce\"\x9be$IE&\xfeO\x0e\v\v\v\v\x0e\x01\xb1&EI&\xcf{h\b.\b\x01`\x01\x01\xfe\xa0\x00\x01\x00m\xff\x80\x05\x93\x06\x00\x00\"\x00\x00\x01\x13&#\"\a\x13&\x00\x02'\x16327\x1e\x01\x12\x17>\x037\x163271\x0e\x03\a\x06\x03[\r>+)@\r(\xfe\xff\xb0]:2,C?\x8d\xc1*%\x91Zx/658:\x1c@#N\n\x92\x02C\xfd=\v\v\x02\xc3E\x01\xc5\x01(\x8b\x0f\x0fo\xed\xfe\xc4E=\xe9\x93\xcdW\x0e\x0e'c:\x86\x11\xf8\x00\x00\x01\x00\x00\xff\x80\x05\xe1\x05\x80\x00#\x00\x00\x01!\x16\x15\x14\x02\x04#\"$&\x02\x10\x126$3 \x17\a&#\"\x0e\x01\x10\x1e\x0132>\x037!\x03\x00\x02\xd5\f\xb6\xfe\xafڝ\xfe\xe4\xceyy\xce\x01\x1c\x9d\x01,\xd7\xd1{\xb7\x81ۀ\x80ہW\x92^F!\x06\xfeL\x02\xeeC=\xd9\xfe\xab\xc0y\xce\x01\x1c\x01:\x01\x1c\xcey\xc9\xc9w\x82\xdf\xfe\xf8߂0H\\R%\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\x10\x00\x19\x00\"\x00N\x00^\x00\x00\x01\x16\a\x06 '&762\x17\x1632762$\x14\x06\"&5462\x05\x14\x06\"&462\x1674&\"\a&'\x13\x17\x14\x16264&#\"\a'&\a\x03\x06\a&#\"\x06\x15\x14\x16\x17\x06\x15\x14\x0432$54'>\x01$\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04G\x10\x10>\xfe\xee>\x10\x10\x06\x12\x060yx1\x06\x12\xfe\xd34J55J\x01\xbf5J44J5\xfbFd$\x82\xb5?\xc84J55%6\x1a\xdd\x13\x06E\xb4\x81#42F%\x1f\x06\x01\x18\xc5\xc6\x01\x18\a\x1e$\x01f\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x01q\x10\x0f>>\x0f\x10\x06\x0611\x06\xd4J44%&4Z%44J54R1F$Z\x06\x01\x1b-%45J521\x05\x15\xfe\xc8\aZ%F1#:\x0f\x1b\x1d\x8e\xcaʎ \x19\x0f9\xbb\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x19\x00#\x00Q\x00a\x00\x00\x01\x16\a\x06\"'&762\x17\x162762%\x14\x06\"&5462\x16\x05\x14\x06\"&5462\x1674&#\"\a&'7\x17\x1e\x013264&#\"\a'&\a\x03\x06\a&#\"\x06\x15\x14\x16\x17\x06\x15\x14\x1632654'>\x01\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\xab\r\r5\xec5\r\r\x05\x10\x05*\xce*\x05\x10\xfe\xfe.>.-@-\x01R.>.-@-\xd7<+*\x1fq\x9a6\xab\x01-\x1f -- 0\x15\xbd\x11\x04<\x9ao\x1e,+< \x1a\x05\xf0\xa9\xaa\xf0\x06\x19\x1f\x013\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\x97\r\r55\r\r\x06\x06**\x06\x96\x1f..\x1f -- \x1f..\x1f --G*<\x1fN\x04\xf3' ,-@-+*\x05\x12\xfe\xf4\x06M <*\x1e2\r\x19\x17z\xad\xadz\x19\x18\r1\x01\xe4\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1e\x000\x00<\x00\x00\x01754&\"\x06\x15\x11\x14\x06\"&=\x01#\x15\x14\x163265\x114632\x16\x1d\x01\x055#\x15\x14\x06#\"&=\x01\a'\x15\x14\x1626\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03bZt\xa0t\x1c&\x1b\x97sRQs\x1b\x14\x13\x1b\x01\x89\x96\x1b\x14\x13\x1bZOpoO\xfe\xe5\x14\x1b\x1b\x14xzRrqP\x01\x18\x13\x1c\x1c\x136\xdfz~\x14\x1b\x1c\x13{\x1a\x1c{Prr\x01\xad\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x02\x00\x00\xff\xa3\a\x80\x05]\x00\x1e\x000\x00\x00\x0154&\"\x06\x15\x11\x14\x06#\"&5\x11!\x11\x14\x16265\x114632\x16\x1d\x01\a\x05!\x11\x14\x06#\"&5\x11\x177\x11\x14\x16265\x04&\x02'&\a\x0e\x01#\".\x01'&'\x04#\"&5467%&4>\x037>\x0132\x16\x17632\x16\x15\x14\x06\x0f\x02\x06\x1632654.\x02547'654'632\x1e\x05\x177\x0e\x03\x177.\a'.\x02*\x01#\"\a>\x057\x1e\x02?\x01\x15\x1767>\b?\x01\x06\a\x0e\x01\a\x0e\x02\a\x1e\x01\x15\x14\x03>\x0132\x1e\x03\x17\x06#\"'\x017\x17\a\x01\x16\x15\x14\x0e\x03\a'>\x023\x01\a'>\x0132\x133\x17\a\x015\x15\x0f\x01?\x02\x04\xc6K\x89cgA+![,7*\x14\x15\n\x18\f2\x03(-#\x01=\x05\x11\a\x0e\x06\n\a\t\x04\a\x0f\x1a\x12/\x0e~[\x10(D?\x1dG\b\f \x16\f\x16\xf7|\x1c,)\x19\"\x0e#\v+\b\a\x02)O\xfc\xb4\x0e8,\x11\x03+\xf7'\xb96\t\x1b\x1d\x17\x19\x02y{=@\xfe\xf90mI\x01\xa1\x03#938\x04\a\x15OA\x1c\xfeE`\x06\n-\f\x13\xd3\x1f\n)\x03y\x01\x02\x01\x02\x01\x02_\x03/FwaH8j7=\x1e7?\x10%\x9c\xad\xbc\x95a\x02\x04\x05\t\x05%\a\x1d\f\x1e\x19%\x16!\x1a?)L\x0f\x01\x15\n\x10\x1fJ\x16\r9=\x15\x02\x1a5]~\x99\x14\x04\x1ap\x16\x10\x0f\x17\x03j\x0e\x16\r\n\x04\x05\x02\x01\r \x11%\x16\x11\x0f\x16\x03(\x10\x1a\xb7\xa01$\"\x03\x14\x18\x10\x12\x13,I\x1a \x10\x03\x0e\r$\x1f@\x1c\x19((\x02\v\x0f\xd6\x05\x15\b\x0f\x06\n\x05\x05\x02\x03\x04\x01+\x1e!\x1a.\x1bS\t\t-\x1c\x01\x01L\x01__\x15$'\x17-\x119\x13L\x0f\t5V\xa5\xc6+\x03\t\n\t\x136\a\v\xfcT\x1a+\x1f6.8\x05-\v\x03$\f\xb10\xfe\xd0\x0f\x01\a\x0f\v\b\a\x01+\x02\r\a\x02t\x14\x11\x01\f\xfd|S\f\x061\x01\x01\x05\x02\x03\x04\x01\x00\x00\x04\x00\x00\xff\x12\x06\x00\x05\xee\x00\x17\x006\x00]\x00\x83\x00\x00\x05&\a\x0e\x01#\"'&#\"\a\x0e\x01\x17\x1e\x0167>\x0276'&'&#\"\a\x06\a\x06\x17\x1667>\a32\x1e\x01\x17\x1e\x0176\x014.\x02#\"\x0e\x01#\x06.\x03\a\x0e\x01\a\x06\x17\x1e\x0132>\x02\x17\x1e\x03\x17\x1667>\x017\x14\x02\x06\x04 $&\x0254>\x057>\x037>\x017\x16\x17\x1e\x01\x17\x1e\x06\x04\x8f\x05\x13\x1erJ\x81@\x05\b\v\x0f\a\x01\b\"kb2)W+\a\f,\x13\x14\x175/\x18\x1d1\x1a\x0e\t\x11\x17\x03\x0f\x06\x0e\t\x10\x0e\x13\v\x1b#\v\b\n\x05\n\x17\x01Z\n\x17-\x1e!\x80\x82$\x1bIOXp7s\xa4\x02\x02L\x1dCF9\x96vz \x1aNAG\x14#/ \x1c\x1d5|\xd0\xfe\xeb\xfe\xd0\xfe\xe6Հ';RKR/\x13\x0eJ#=\x1e$,\b\x819,\xac+\x15$UCS7'2\x13\x0e\x16\"1\x04\f\x06\x14\n \x1c\x03\x03\x04!\x1b\a\f\x84/\x0e\x0f\n\f,\x18\x14\b\a\x14\x02\r\x04\n\x04\x06\x03\x02\x0f\x0e\x0f\x11\x06\x04\f\x01/\x16--\x1cST\x01(::(\x01\x01\x9bep4\x14\x11AM@\x01\x01=I>\x01\x03\".)xΤ\xfe\xe7\xbfls\xc7\x01\x1c\xa0Y\xa7|qK@\x1d\n\b%\x14(\x18\x1cYQ\x9b&\x1dN\x1b\r\x18EHv~\xab\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1e\x00<\x00Z\x00x\x00\x00\x01\x0f\x02\x0e\x01'\x0e\x01#\"&5467&6?\x01\x17\a\x06\x14\x17\x162?\x03\x03\x17\a'&\"\x06\x14\x1f\x03\a/\x02.\x017.\x0154632\x16\x176\x16\x01\x14\x06#\"&'\x06&/\x017\x17\x16264/\x037\x1f\x02\x1e\x01\a\x1e\x01\x03\x14\x06\a\x16\x06\x0f\x01'764&\"\x0f\x03'?\x02>\x01\x17>\x0132\x16\x04.\xa0\x97\x1eA\xadU\x10pIUxYE\x16.A\f\x97\v%%%h%\x1e\x97\xa1\xbe\f\x98\f%hJ%\x1d\x98\xa0\x97\xa1\x97\x1eD,\x1bFZxULs\fT\xab\x03gxUJr\x0eV\xbbD\v\x97\f%hJ%\x1e\x98\xa0\x98\xa0\x98\x1d@/\x15Le\x02fL\x1a.C\f\x97\f%Jh%\x1e\x98\xa0\x98\xa1\x98\x1dC\xb8V\vsNUx\x01Ϡ\x98\x1e@.\x15FZyUHp\x10V\xaeA\f\x98\v%h&%%\x1e\x98\xa0\x02\x12\f\x98\f%Ji%\x1d\x98\xa0\x98\xa0\x98\x1eC\xb9W\x0fpIUybJ\x14/\xfb\x95Uy^G\x1c,D\f\x98\f%Jh%\x1e\x98\xa0\x98\xa0\x98\x1e@\xadU\vs\x04\x17Mt\vU\xb7C\f\x98\f%hJ%\x1e\x98\xa0\x98\xa0\x98\x1eC-\x1aKfy\x00\x00\b\x00\x00\xff\x00\x06\x00\x06\x00\x00E\x00X\x00[\x00_\x00g\x00j\x00\x89\x00\xa3\x00\x00\x01\x06&/\x01&'.\x01'\x06\a\x06\a\x0e\x01'67>\x017>\x017&\a\x0e\x02\a\x06\x14\a\x06\a\x06'&'&'>\x0176763>\x017>\x02\x17\x16\a\x14\x0e\x01\a\x06\a\x17\x1e\x01\x17\x1e\x01\x03\x16\a\x06\a\x06#&'&'7\x1e\x0167672\x05\x17'\x01%\x11\x05\x01\x17\x03'\x03\x177\x17\x01\x05\x11\x01\x17\a'\x06\a\x06+\x01\"&'&54632\x1e\x01\x17\x1e\x013267>\x027\x01\x11%\x06\x04#\"'4'\x11676767\x11\x052,\x0132\x15\x11\x02\x8e\x01\x17\x14\x14,+\aD\x04CCQ\x18\x04\x1f\x03\x06L\x15\x81\x0e\x11D\x02\bf\b'\x1e\x02\x02\x01\x05\x1a\x17\x18\x12\n\x04\x01\x06%\v:/d\x02\nB\v\t\x19\x04\x04\x02\x03\x19\x1c\x03\x194@\f}\x05\x04\r\xcf\x03\a\f&\x1e\x1e\x1a\x17\x0e\x04\x01\x03!\x140$\x13\x11\x02\xbe?\x8b\xfb\xf8\x02\xb6\xfdJ\x04\xd9f\xb5d\xd8f-\xd3\xfe.\x02=\xfe\xfa\x9e6(\x82\x92:!TO\xf1?\b\n\b\x04\x1c!\x04I\xadG_\x90U\x0f\x1f%\n\x01\x95\xfc\xfa\x0e\xfd.\a\r\x05\x01\x03\x01\x05\x0fk*\x02.\x02\x01=\x01;\x04\x14\x01\xca\x03\a\b\t\x14\x1d\x055\x02gN_\x0f\x02\x04\x02\x04X\x18\xb6\x1b\x1e\x89\t\x01\"\x02\v\b\x01\x02\x11\x01\n\x05\a\a\x04\x11\x06\x11\x02\x06\x03\x10\x10#\x02#\x04\x03\n\x01\x01\f\x15\x0229\x052Q\x1c\x064\x02\x011\x01\xe0\x0f\r\x17\x0f\f\x03\x17\x0f\x1a\x03\x03\x04\x04\x0e\f\x02\x92\xe3*\xfd\x99\xe8\x04\b\xe9\xfd6\x1f\x02\x91\x1f\xfd\xe8\x1fnA\x03;\xb8\x01|\xfa\x11\r\xa0BS\x19\fN.\a\t\b\v\x0f\x12\x02%1\x1d$\a\x11\x15\x06\x04\x80\xfb\xc9\xf6\x06\xf3\r\x01\x02\x046\t\x01\x06\x05$\x0e\x01\x80\xc6nk\x15\xfe^\x00\f\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00'\x007\x00G\x00W\x00g\x00w\x00\x87\x00\x97\x00\xa7\x00\xb7\x00\xc0\x00\x00\x012\x16\x15\x11\x14\x06+\x01\"&5\x11463\x05\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x1f\x01\x1e\x01\x15\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x13\x11#\"&=\x01!\x11\x01 B^^B\x80B^^B\x05\xe0:F\x96j\xfc\xa0B^8(\x02\xa0(`\x1c\x98\x1c(\xfd \x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x01\x00\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x01\x00\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12`\xa0(8\xfd\x80\x04\x80^B\xfb\xc0B^^B\x04@B^\xa3\"vE\xfd\x00j\x96^B\x06\x00(8(\x1c\x98\x1c`(\xfb\x80\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\xfe\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\xfe\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x8e\x01\x008(\xa0\xfe\x00\x00\x14\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\xbf\x00\xcf\x00\xdf\x00\xef\x00\xff\x01\x0f\x01\x1f\x01/\x01?\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x01\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x11\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x11\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x11\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x0354&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&#!\"\x06\x1d\x01\x14\x163!26\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x05@\x1a&&\x1a\xfb\x00\x1a&&\x1a\x01\xc0\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x06\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xfe\xe0@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xb2@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\xfb\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x02\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\xfc\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x00\x00\x00\x02\x00@\xff\x10\x04\xc0\x05`\x00\x1f\x00'\x00\x00\t\x01\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11\x01&4762\x1f\x01!762\x17\x16\x14$\x14\x06\"&462\x04\xa4\xfe\xdcB\\B@B\\B\xfe\xdc\x1c\x1c\x1dO\x1c\xe4\x01p\xe4\x1cP\x1c\x1c\xfe\xa0\x83\xba\x83\x83\xba\x03\xdc\xfe\xdc\xfc\xc8.BB.\x01\x80\xfe\x80.BB.\x038\x01$\x1cP\x1c\x1c\x1c\xe4\xe4\x1c\x1c\x1dO広\x83\xba\x83\x00\x05\x00\x00\xff\x80\x06\x80\x05\x80\x00\x0f\x00\x1d\x003\x00C\x00Q\x00\x00\x01\x14\x0e\x01#\".\x0154>\x0132\x1e\x01\x01\x14\x06#\".\x0154632\x1e\x01\x052\x04\x12\x15\x14\x0e\x02#\"&#\"\x06#\"54>\x02%\".\x0154>\x0132\x1e\x01\x15\x14\x0e\x01%2\x16\x15\x14\x0e\x01#\"&54>\x01\x03\f&X=L|<&X=M{<\xfe\xaaTML\x83FTML\x83F\x01\x8av\x01\x12\xb8\"?B+D\xef?B\xfdJ\xb7p\xa7\xd0\x01H=X&<{M=X&<|\x01dMTF\x83LMTF\x83\x04(\x012\x1e\x01\x02\xc0r_-\x02$\x1a\xc0\x1a$\x02-_rU\x96\xaa\x96U\x03\xf0\x91\xc5%\xfc\xcb\x1a&&\x1a\x035%ő\x80\xf3\x9d\x9d\xf3\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00\x03\x00\a\x00\x1f\x00\x00\x05\x01\x11\x05'-\x01\r\x01\x11\x14\x06\a\x01\x06\"'\x01.\x015\x11467\x0162\x17\x01\x1e\x01\x03\x80\x02\x80\xfd\x80@\x02\xba\xfdF\xfdF\x05\xfa$\x1f\xfd@\x1cB\x1c\xfd@\x1f$.&\x02\xc0\x16,\x16\x02\xc0&.]\x01]\x02|\xe9q\xfe\xfe\xfe\x02\xfd\x00#<\x11\xfe\x80\x10\x10\x01\x80\x11<#\x03\x00(B\x0e\x01\x00\b\b\xff\x00\x0eB\x00\x00\x00\x00\a\x00\x00\xff\x00\b\x80\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00B\x00\x00\x05%\x11\x05'-\x01\x05\x01%\x11\x05'-\x01\x05'%\x11\x05'-\x01\x05\x01\x11\x14\x06\a\x05\x06\"'%&'\x06\a\x05\x06\"'%.\x015\x11467%\x11467%62\x17\x05\x1e\x01\x15\x11\x05\x1e\x01\x02\x80\x01\x80\xfe\x80@\x01\x94\xfel\xfel\x05\xd4\x01\x80\xfe\x80@\x01\x94\xfel\xfel,\x01\x80\xfe\x80@\x01\xb9\xfeG\xfeG\x05\xf9&!\xfe@\x19@\x19\xfe@\x04\x03\x02\x05\xfe@\x19@\x19\xfe@!&+#\x01\xb2+#\x01\xc0\x176\x17\x01\xc0#+\x01\xb2$*`\xc0\x01:\xa4p\xad\xad\xad\xfd\x8d\xc0\x01:\xa4p\xad\xad\xadx\xa5\x01\n\xa4p\xbd\xbd\xbd\xfd=\xfe`$>\x10\xe0\x0e\x0e\xe0\x02\x02\x02\x02\xe0\x0e\x0e\xe0\x10>$\x01\xa0&@\x10\xba\x01\x90&@\x10\xc0\n\n\xc0\x10@&\xfep\xba\x10@\x00\x00\x06\x00\x00\xff\xfe\b\x00\x05\x02\x00\x03\x00\t\x00\x1f\x00&\x00.\x00A\x00\x00\x01!\x15!\x03\"\x06\a!&\x032673\x02!\"\x0254\x0032\x1e\x01\x15\x14\a!\x14\x16%!254#!5!2654#!%!2\x1e\x02\x15\x14\a\x1e\x01\x15\x14\x0e\x03#!\a8\xfe\x01\x01\xff\xfcZp\x06\x01\x98\x12\xa6?v\x11\xddd\xfe\xb9\xd6\xfd\x01\x05Ί\xcde\x02\xfdns\xfb6\x01(\xcd\xc7\xfe\xd2\x01\x19N[\xbe\xfe\xfc\xfe\xeb\x02RW\x88u?\xacrt1Sr\x80F\xfd\x9d\x04\xad|\xfe\xd2iZ\xc3\xfd\xb7@7\xfe\xcd\x01\b\xd7\xd0\x01\x13\x88މ\x11\x1eoy2\xa7\xb4\xbeIM\x90\xd7\x1cC~[\xb5R \xa6yK{T:\x1a\x00\x00\x00\a\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1e\x00%\x00,\x00A\x00G\x00K\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x13!\x11!2654'654.\x02\x03#532\x15\x14\x03#532\x15\x14\x05\"&5!654&#\"\x06\x15\x14\x16327#\x0e\x01\x032\x17#>\x01\x03!\x15!\x04\xe0w\xa9\xa9w\xfc@w\xa9\xa9w\xd3\xfe\x8d\x01~u\xa0\x8fk'JTM\xb0\xa3wa\xb9\xbd|\x02\nDH\x01\x9b\x01\x95\x81\x80\xa4\x9e\x86\xcd>\x8a\vI1q\v\xfe\x04Fj\x01?\xfe\xc1\x05\x80\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfe\x91\xfc\xedsq\x9e*4p9O*\x11\xfe¸Z^\xfe\xb1\xd9qh LE\n\x14\x84\xb1\xac\x82\x87\xa4\xbf\"(\x01nz8B\x01\nM\x00\x00\x00\x04\x00\x00\xff\x80\a\x00\x05\x80\x00\a\x00\x1b\x00'\x00?\x00\x00\x00\x14\x06\"&462\x004&#\"\a\x17\x1e\x01\a\x0e\x01'.\x01'\x1e\x0132\x014&#\"\x06\x15\x14\x163267\x14\x00#\x01\x0e\x01#\"&/\x01\x11\x05632\x17\x016\x0032\x00\x06.\x8fʏ\x8f\xca\xfd\x8d\x92h\x1b\x1bhMA\x1f\x1f\x98L\x15R\x14 vGh\x03г~\u007f\xb3\xb3\u007f~\xb3\x96\xfe\xf5\xbc\xfeK\f\u0084y\xba\x19\xe6\x01\x85O^\r\x16\x01\x1c\x02\x01\v\xbb\xbc\x01\v\x04\x1fʏ\x8fʏ\xfb\xbeВ\x06*\x1f\x97LM@\x1f\b!\b\xfeשw\x03\xc0w\xa9\xf7\x8eȍ\x8dde\x8d\x03)\xa0qrOPq\xfeȦs:0\x14\x14\x183=\x027\x16\x1b\x01'\x0e\x03\x0f\x01\x03.\x01?\x0167'\x01\x03\x0e\x01\x0f\x01\x06\a\x17\x03\x13\x17\x1667\x01\x06\x03%'\x13>\x01\x17\x1e\x05\x01\x13\x16\x06\a\x0e\x05\a&\x03%'7\x03%7.\x03/\x01\x056\x16\x1f\x01\x16\x03D\x0f\x02\xfe\\$>\x10\v\a\x0f\t\"\x02N,\xb4\x93?a0\x1f\x03\x04\xbe\x11\x02\a\b#O\x8c\x06\x80\xbc\f1\x13\x12G\x94\b\xe6\xd3\a\xaa\xe29\xfd'/\xda\xfe\xc3\x13\xe1\x14P(\x181#0\x180\x02\x97\xd4\x12\v\x16\r($=!F\v\"\xe7\x019|\x8e\xdc\xfe]\x97\"RE<\x11\x11\x01\x95\x1f6\f\v'\x01o\xfe\x90\x16\x1d\x039%\x1b8J$\\\a\f\x02:\xfe\x85\\H\x91iT\x15\x15\x01e\x1a<\x11\x12?}V\xfd\xea\xfe\x99\x1d#\x03\x04\a\x05\xa4\x01o\x01j\xad\x10\x16\x16\x03\xb2?\xfe\x8c\xbb\f\x01d\x1f\x1c\x04\x02\x14\x16,\x196\xfe\xc5\xfe\x95%N#\x14\"\x16\x16\n\x12\x03H\x01l\xc3\xedS\xfe\x8b\x14VY\x9a]C\r\r\x01\x03\x1b\x0f\x0f=\x00\x00\x04\x00\x00\xff@\b\x00\x05\x80\x00\a\x00\x11\x00\x19\x00C\x00\x00\x004&\"\x06\x14\x162\x13!\x03.\x01#!\"\x06\a\x004&\"\x06\x14\x162\x13\x11\x14\x06+\x01\x15\x14\x06\"&=\x01!\x15\x14\x06\"&=\x01#\"&5\x1146;\x01\x13>\x013!2\x16\x17\x1332\x16\x01\xe0^\x84^^\x84\x82\x03\xf8Y\x02\x18\t\xfd\x00\t\x18\x02\x05\x03^\x84^^\x84\xfe\x12\x0e`p\xa0p\xfc\x00p\xa0p`\x0e\x12\x83]\x1ci\x17\xa2b\x03\x00b\xa2\x17i\x1c]\x83\x01~\x84^^\x84^\x01\xe0\x01e\b\x13\x13\b\xfd\x19\x84^^\x84^\x01\x00\xfe\x80\x0e\x12\x80PppP\x80\x80PppP\x80\x12\x0e\x01\x80]\x83\x01\xa3^\u007f\u007f^\xfe]\x83\x00\x04\x00\x00\xff\x00\b\x00\x06\x00\x003\x00;\x00E\x00M\x00\x00\x012\x16\x15\x11\x14\x06+\x01\x15\x14\x06\"&=\x01!\x15\x14\x06\"&=\x01#\"&5\x1146;\x01\x13>\x01;\x015463!2\x16\x1d\x0132\x16\x17\x13\x00264&\"\x06\x14\x01!\x03.\x01#!\"\x06\a\x00264&\"\x06\x14\a ]\x83\x12\x0e`p\xa0p\xfc\x00p\xa0p`\x0e\x12\x83]\x1ci\x17\xa2b\x80\x12\x0e\x01\xc0\x0e\x12\x80b\xa2\x17i\xf9\xfa\x84^^\x84^\x01d\x03\xf8Y\x02\x18\t\xfd\x00\t\x18\x02\x04!\x84^^\x84^\x02\x80\x83]\xfe\x80\x0e\x12@PppP@@PppP@\x12\x0e\x01\x80]\x83\x01\xa3^\u007f\xe0\x0e\x12\x12\x0e\xe0\u007f^\xfe]\xfe ^\x84^^\x84\x01\x82\x01e\b\x13\x13\b\xfc\xbb^\x84^^\x84\x00\x01\x00 \xff\x00\x05\xe0\x06\x00\x003\x00\x00$\x14\x06#!\x1e\x01\x15\x14\x06#!\"&5467!\"&47\x01#\"&47\x01#\"&47\x0162\x17\x01\x16\x14\x06+\x01\x01\x16\x14\x06+\x01\x01\x05\xe0&\x1a\xfe2\x01\n$\x19\xfe\xc0\x19$\n\x01\xfe2\x1a&\x13\x01\x92\xe5\x1a&\x13\x01\x92\xc5\x1a&\x13\x01\x80\x134\x13\x01\x80\x13&\x1a\xc5\x01\x92\x13&\x1a\xe5\x01\x92Z4&\x11\x8d&\x19##\x19&\x8d\x11&4\x13\x01\x93&4\x13\x01\x93&4\x13\x01\x80\x13\x13\xfe\x80\x134&\xfem\x134&\xfem\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x15\x00+\x00D\x00P\x00\x00\x014'&#\"\a\x06\x15\x14\x16327632\x17\x1632674'&!\"\a\x06\x15\x14\x1632763 \x17\x16326\x134'&$#\"\a\x0e\x01\x15\x14\x16327632\x04\x17\x1632>\x01\x10\x02\x04 $\x02\x10\x12$ \x04\x04g\x1e\xc1\xfe\x85\x9a*\x1b\x16\x05 \x84o\xe2\xab\x13\x0e\x13\x1c`#\xed\xfeə\x960#\x19\a\x1ez\x81\x01\x17\xd1\x18\x0e\x19#l(~\xfe\xb2\xb0̠\x17\x1f)\x1f\v\x1d\x85\xae\x9f\x01-g\x15\x13\x1d+\xcd\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01F \x13s\"\t+\x14\x1d\b\x1bg\v\x1b\xec(\x15\x8d*\r3\x19#\b!|\r#\x01\x11/\x17IK/\a%\x1e\x1f*\b%D=\f)[\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x01\x00\x00\xff\x80\x04\x00\x06\x00\x00\x13\x00\x00\t\x01\x17!\x11!\a\x03\a!\x11\x01'!\x11!7\x137!\x04\x00\xfe\xd1\x18\x01\x17\xfe\x05,\x8e\x1e\xfe\xd3\x01/\x18\xfe\xe9\x01\xfb,\x8e\x1e\x01-\x04\xd1\xfd\xba\x1f\xfea\x1e\xfe\xef\x1e\x01/\x02G\x1e\x01\x9f\x1e\x01\x11\x1e\x00\x00\x00\x11\x00\x00\x00\x8c\t\x00\x04t\x00\x0e\x00%\x00/\x00;\x00<\x00H\x00T\x00b\x00c\x00q\x00\u007f\x00\x8d\x00\x90\x00\x9e\x00\xac\x00\xc0\x00\xd4\x00\x00%7\x03.\x01#\"\x06\x15\x03\x17\x1e\x0132%7\x034'&\"\a\x06\x15\a\x03\x14\x17\x15\x14\x17\x1632765\x01\x17\a\x06\"/\x017627\x17\a\x06#\"5'7432\x01\x03\x17\a\x14#\"/\x017632\x1f\x01\a\x06#\"5'7432\x1f\x01\a\x06#\"&5'74632\t\x01\x13\a\x14\x06#\"/\x01\x13632\x167\x13\a\x14\x06#\"/\x01\x13632\x167\x13\a\x06#\"/\x01\x134632\x16\x019\x01\x03\x13\a\x14\x06\"&/\x01\x13462\x16\x17\x13\a\x14\x06\"&/\x01\x13>\x012\x16\x13\a1\x14\x06\"&/\x02\x13567632\x17\x16\x17\x01\x14\x06#!.\x015\x1147632\x00\x17632\x16\x03\x10\x10\x10\x01\r\n\t\x0e\x0e\x0e\x01\r\t\x16\x01*\v\f\r\b\x10\b\r\x01\n\v\x06\t\x0e\v\t\t\xfb\xec\x14\x14\x02\x0e\x02\x11\x11\x02\x0eX\x1a\x1a\x02\b\t\x17\x17\t\b\x01\x1a\xbc\x19\x19\v\n\x02\x15\x15\x02\n\v^\x17\x17\x02\f\r\x15\x15\r\f`\x15\x15\x02\x0e\x06\t\x14\x14\t\x06\x0e\x01\x81\xfe\xdf\x15\x15\n\a\x10\x02\x12\x12\x02\x10\a\n^\x13\x13\v\b\x12\x02\x10\x10\x02\x12\b\vb\x12\x12\x02\x14\x13\x02\x10\x10\r\b\t\f\x01\x89\xc6\x0f\x0f\x0f\x14\x0e\x01\x0e\x0e\x0f\x14\x0fc\x0e\x0e\x10\x16\x10\x01\f\f\x01\x10\x16\x0f\xd5\x0e\x12\x1a\x12\x01\x06\x06\f\x02\n\t\v\b\a\x0e\x02\x04f\xa6u\xfc\xee\r\x12\x1cU`\xc3\x01\x1e\x1159u\xa6\xa4\xf1\x02\v\n\x0e\x0e\n\xfd\xf5\xf1\n\r4\xd3\x02J\x10\b\x05\x05\b\x10\x06\xfd\xbd\x01\xeb\x01\n\a\v\t\a\r\x01l\x80~\t\t~\x80\tF\xcf\xcb\t\n\xca\xcf\t\xfe2\x01\xeb\xf5\xed\v\v\xed\xf5\f\x05\xfc\xf4\r\r\xf4\xfc\r\x1f\xea\xf6\x10\t\a\xf6\xea\x06\t\xfe\x16\x02m\xfe\x84\xf6\a\v\x12\xf6\x01|\x12\vO\xfe,\xf4\b\v\x13\xf4\x01\xd4\x13\v \xfe\x06\xf2\x15\x15\xf2\x01\xfa\t\r\r\xfd\x11\x02\xea\xfe\x02\xef\n\x0f\x0e\v\xef\x01\xfe\v\x0e\x0e\x1e\xfe\x14\xec\v\x10\x10\v\xec\x01\xec\f\x10\x10\xfe\b\xe7\r\x12\x12\rru\x02|\x03\x0f\t\a\x05\b\x12\xfd\x94u\xa5\x02\x12\r\x03\x83\x17\n\"\xfe\xf9\xc0\x16\xa6\x00\x00\x00\x04\x00\x00\xff\x00\x06\x00\x06\x00\x00\r\x00\x1b\x00)\x009\x00\x00\x00 $7\x15\x14\x06\x04 $&=\x01\x16\x00 $7\x15\x14\x06\x04 $&=\x01\x16\x00 $7\x15\x14\x06\x04 $&=\x01\x16\x00 \x04\x16\x1d\x01\x14\x06\x04 $&=\x0146\x02\x13\x01\xda\x01\x9cw\xce\xfe\x9e\xfe`\xfe\x9e\xcew\x01\x9c\x01\xda\x01\x9cw\xce\xfe\x9e\xfe`\xfe\x9e\xcew\x01\x9c\x01\xda\x01\x9cw\xce\xfe\x9e\xfe`\xfe\x9e\xcew\x01\xb9\x01\xa0\x01b\xce\xce\xfe\x9e\xfe`\xfe\x9e\xce\xce\x03\x00VT\xaaEvEEvE\xaaT\xfc\xaaVT\xaaEvEEvE\xaaT\x01*VT\xaaEvEEvE\xaaT\x04*EvE\x80EvEEvE\x80Ev\x00\b\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00^\x00c\x00t\x00\u007f\x00\x87\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01\x16\x17632\x17\x16\a\x14\x06\a\x15\x06#\"&'\x06\a\x02#\"/\x01&'&7>\x0176\x17\x16\x156767.\x0176;\x022\x17\x16\a\x06\a\x16\x1d\x01\x06\a\x16\x0167\x0e\x01\x01\x06\x17674767&5&5&'\x14\a\x0367.\x01'&'\x06\a\x06\x05&#\x163274\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x02\xfe!3;:\x93\x1e\x10\x0e\x02\x01\x06A0\x86?ݫ\x99Y\x0f\r\x18\x01\x05\n\x04\t^U\x0e\t\x0247D$\x18\r\r\v\x1f\x15\x01\x17\f\x12\t\x02\x02\x01\x02\f7\xfe\x1b4U3I\x01\x81\x0f\r\x01\x06\a\x01\x03\x01\x01\x01\f\x01|\x87\x95\x02\x16\x05L3\x1b8\x1e\x02w\x18tL0\x0e\x04\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x02Q\x1a\x1e\a1\x16\x1e\x01\x02\x01\x01&(!\x18;\xfe\xfa\a\f\x01\x04\n\x1a(g-\t\x0f\x02\x02Up\x88~R\x9b2(\x0f\x15/\x06\x02\x03\x05\x1e{E\xa4\xfe\x1b\x18\x86(X\x03z*Z\a%\x03(\x04\x04\x01\x01\x02\x01\x16\x0e\x01\x01\xfdi6\x1b\x01\x11\x05CmVo8\v\x18\x1c\x01\x01\x00\x00\x00\x00\x04\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00T\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x13\x153\x133\x1367653\x17\x1e\x01\x17\x133\x1335!\x153\x03\x06\x0f\x01#4.\x015.\x01'\x03#\x03\x0e\x01\x0f\x01#'&'\x0335\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00iF\xa4\x9f\x80\a\x03\x02\x04\x03\x01\x05\x03\x80\x9f\xa4F\xfe\xd4Zc\x05\x02\x02\x04\x01\x02\x01\x06\x02\x90r\x90\x02\x05\x01\x04\x04\x02\x02\x05cZ\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03\x80k\xfdk\x01\xe5\x14\x1a\x10\b\x18\x03\"\t\xfe\x1b\x02\x95kk\xfeJ\x14\x1a\x15\x03\a\t\x02\x05 \t\x02!\xfd\xdf\t\x1f\x06\x15\x15\x1a\x14\x01\xb6k\x00\x00\x04\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00S\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11%\x15!5#7>\x02;\x01\x16\x17\x1e\x02\x1f\x01#\x15!5#\x03\x1335!\x153\a\x0e\x01\x0f\x01#&'&/\x0135!\x153\x13\x03\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01-\x01\x19Kg\x05\n\x05\x01\x02\x01\x04\x02\x05\a\x03kL\x01#D\xc0\xc3C\xfe\xe9Jg\x04\f\x03\x02\x02\x01\x04\x06\vjL\xfe\xdeD\xbd\xc2\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\xeajj\xa1\a\x13\b\x04\x06\x04\a\t\x04\xa1jj\x01\x11\x01\x1akk\x9f\a\x13\x04\x03\x04\x06\v\f\x9fkk\xfe\xf0\xfe\xe5\x00\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x008\x00C\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11%\x15!5#5327>\x0154&'&#!\x153\x11\x01#\x1132\x17\x16\x15\x14\a\x06\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01 \x01G]\x89L*COJ?0R\xfe\x90\\\x01\x05wx4\x1f8>\x1f\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\xeajj\xa7\x0f\x17\x80RQx\x1b\x13k\xfd\xd5\x01\x18\x01\f\x12!RY\x1f\x0f\x00\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00*\x002\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01\x11!57\x17\x01\x04\"&462\x16\x14\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x04\x80\xfc\x00\xc0\x80\x01\x80\xfeP\xa0pp\xa0p\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x01\xc0\xfe\xc0\xc0\xc0\x80\x01\x80\x80p\xa0pp\xa0\x00\x00\t\x00\x00\xff\x00\x06\x00\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00#\x00*\x007\x00J\x00R\x00\x00\x015#\x15\x055#\x1d\x015#\x15\x055#\x15\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11#\x15#5!\x11\x01\x13\x16\x15\x14\x06\"&5476\x1353\x1532\x16\x02264&\"\x06\x14\x02\x80\x80\x01\x00\x80\x80\x01\x00\x80\x03<\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\x80\x80\xfe\x00\x02\x8dk\b\x91ޑ\b\x15c\x80O\x16\"\xbcjKKjK\x04\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\x80\x80\xfa\x00\x02\xd1\xfe\xa3\x1b\x19SmmS\x19\x1b?\x01M\x80\x80\x1a\xfe\x1a&4&&4\x00\x00\x00\x00\x06\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x009\x00L\x00^\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01\x16\x15\x11\x14\a\x06#\"/\x01#\"&=\x0146;\x0176\x01276\x10'.\x01\a\x0e\x01\x17\x16\x10\a\x06\x16\x17\x16'2764'.\x01\x0e\x01\x17\x16\x14\a\x06\x16\x17\x16\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01\xec\x14\x14\b\x04\f\v\xa6\x83\x0e\x12\x12\x0e\x83\xa6\x10\x01\xb4\x1f\x13\x81\x81\x106\x14\x15\x05\x11dd\x11\x05\x15\x12\xbd\x1b\x14WW\x126&\x02\x1344\x13\x02\x13\x14\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03.\b\x16\xfd\xe0\x16\b\x02\t\xa7\x12\x0e\xc0\x0e\x12\xa7\x0f\xfdG\x18\x9f\x01\x98\x9f\x15\x06\x11\x115\x15{\xfe\xc2{\x155\x10\x0f\x94\x14]\xfc]\x13\x02$5\x149\x949\x145\x12\x11\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x003\x00C\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x05\x16\x15\x11\x14\a\x06#\"'\x015\x01632\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x02\x804LL4\xfe\x804LL4\x03l\x14\x14\b\x04\x0e\t\xfe\xf7\x01\t\t\x0e\x04\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03\x80L4\xfe\x804LL4\x01\x804L\x02\b\x16\xfd\xc0\x16\b\x02\t\x01\nZ\x01\n\t\x00\x00\x00\x06\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x007\x00K\x00[\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01>\x01\x1f\x01\x1e\x01\x0f\x01\x17\x16\x06\x0f\x01\x06&'\x03&7!\x16\a\x03\x0e\x01/\x01.\x01?\x01'&6?\x016\x16\x17\x01.\x017\x13>\x01\x1f\x01\x1e\x01\a\x03\x0e\x01'\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01`\b\x1a\v3\v\x03\b\xb6\xb6\b\x03\v3\v\x1a\b\xe2\x0e\x0e\x04\x04\x0e\x0e\xe2\b\x1a\v3\v\x03\b\xb6\xb6\b\x03\v3\v\x1a\b\xfev\r\x0f\x02\x8a\x02\x16\r?\r\x0f\x02\x8a\x02\x16\r\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03\x80\v\x03\b&\b\x1a\v\xf3\xf3\v\x1a\b&\b\x03\v\x01-\x13\x13\x13\x13\xfe\xd3\v\x03\b&\b\x1a\v\xf3\xf3\v\x1a\b&\b\x03\v\xfd\x06\x02\x16\r\x03?\r\x0f\x02\n\x02\x16\r\xfc\xc1\r\x0f\x02\x00\x01\x00'\xff\x97\x05\xd9\x06\x00\x006\x00\x00\x01\x15\x06#\x06\x02\x06\a\x06'.\x04\n\x01'!\x16\x1a\x01\x16\x1767&\x0254632\x16\x15\x14\a\x0e\x01\".\x01'654&#\"\x06\x15\x14\x1632\x05\xd9eaAɢ/PR\x1cAids`W\x1b\x01\x1b\x1aXyzO\xa9v\x8e\xa2д\xb2\xbe:\a\x19C;A\x12\x1f:25@Ң>\x02\xc5\xc6\x17\x88\xfe\xf2\xa1\x1a-0\x115r\x8f\xe1\x01\a\x01n\xcf\xda\xfe\x97\xfe\xef\xc6`\xa9\xedH\x01(\xb9\xc0\xf5\xd3\xc0\x9f\u007f\x01\x04\f' gQWZc[\xba\xd7\x00\x00\b\x00\x00\xff\x00\a\x00\x06\x00\x00\x03\x00\x06\x00\n\x00\x0e\x00\x12\x00\x15\x00\x19\x00-\x00\x00\x13\x01\x11%\x057'\t\x01%\x05'-\x01\x05'%\x11\t\x01\x17\x11\x05%\x01\x11\x05\x11\x14\a\x01\x06\"'\x01&5\x1147\x0162\x17\x01\x16\xd8\x02[\xfe\xb2\xfe\xb5\xc1\xc1\x033\x02[\xfe\xf3\xfe\xb2M\x01\x10\xfe\xf0\xfe\xf0\x8b\x01N\xfd\xa5\x04\xcd\xc1\xfe\xb5\x01\r\xfd\xa5\x033\"\xfc\xcd\x15,\x15\xfc\xcd\"\"\x033\x15,\x15\x033\"\x01o\xfen\x01g\xdf$\x81\x81\xfc\xdc\x01\x92\xb4߆\xb6\xb6\xb6]\xdf\x01g\xfen\xfe\xef\x81\x01\x02$\xb4\x01\x92\xfe\x99+\xfd\xde)\x17\xfd\xde\r\r\x02\"\x17)\x02\")\x17\x02\"\r\r\xfd\xde\x17\x00\x00\x00\x00\x02\x00\x00\x00\x00\b\x00\x05x\x00#\x00W\x00\x00\x01\x1e\x01\x15\x14\x06#\"&#!+\x02.\x015467&54632\x176$32\x04\x12\x15\x14\x06\x01\x14\x16327.\x01'\x06#\"&54632\x1e\x0532654&#\"\a\x17632\x16\x15\x14\x06#\".\x05#\"\x06\a\bo\x89\xec\xa7\x04\x0f\x03\xfbG\x01\x02\x05\xaa\xecn\\\f\xa4u_MK\x01'\xb3\xa6\x01\x18\xa3\x01\xfą|\x89g\x10?\fCM7MM5,QAAIQqAy\xa7\xa8{\x8fb]BL4PJ9+OABIRo?z\xaa\x02\xfc.\xc7z\xa4\xe9\x01\n\xe7\xa5n\xba6'+s\xa2:\x9a\xbc\xa1\xfe\xec\xa3\x06\x18\xfe\xf0z\x8ec\x14I\x0eAC65D*DRRD*\x8fwy\x8eal@B39E*DRRD*\x8d\x00\x00\x00\x00\x06\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00\x17\x00\x1f\x00'\x00/\x007\x00\x00\x00 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \a\x1762\x177\x017&47'\x06\x10\x00 7'\x06\"'\a\x12 6\x10& \x06\x10\x05\x176\x10'\a\x16\x14\x02\xca\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x02\xc0\xfe\x84\xab\xc2R\xaaR\xc2\xfb\xf1\xc2\x1c\x1c\xc2Z\x02B\x01|\xab\xc2R\xaaR\xc2\xca\x01>\xe1\xe1\xfe\xc2\xe1\x03d\xc2ZZ\xc2\x1c\x06\x00\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x0eZ\xc2\x1c\x1c\xc2\xfb\xf1\xc2R\xaaR«\xfe\x84\xfd\xbeZ\xc2\x1c\x1c\xc2\x01&\xe1\x01>\xe1\xe1\xfe\xc2\b«\x01|\xab\xc2R\xaa\x00\x01\x00 \xff \x06\xe0\x05\xd7\x00!\x00\x00\x01\x14\x02\x06\x04 $&\x0254\x12$7\x15\x06\x00\x15\x14\x1e\x02 >\x0254\x00'5\x16\x04\x12\x06\xe0\x89\xe7\xfe\xc0\xfe\xa0\xfe\xc0\xe7\x89\xc2\x01P\xce\xdd\xfe\xddf\xab\xed\x01\x04\xed\xabf\xfe\xdd\xdd\xce\x01P\xc2\x02\x80\xb0\xfe\xc0牉\xe7\x01@\xb0\xd5\x01s\xf0\x1f\xe4-\xfe\xa0\xe6\x82\xed\xabff\xab\xed\x82\xe6\x01`-\xe4\x1f\xf0\xfe\x8d\x00\x00\x01\x00\x13\xff\x00\x06\xee\x06\x00\x00c\x00\x00\x136\x12721\x14\a\x0e\x04\x1e\x01\x17\x1e\x01>\x01?\x01>\x01.\x01/\x01.\x03/\x017\x1e\x01\x1f\x016&/\x017\x17\x0e\x01\x0f\x01>\x01?\x01\x17\x0e\x01\x0f\x01\x0e\x01\x16\x17\x1e\x01>\x01?\x01>\x02.\x04/\x01&3\x161\x1e\b\x17\x12\x02\x04#\"$&\x02\x13\b\xd8\xc5\x05\x01\b(@8!\x05IH2hM>\x10\x10'\x1c\x0f\x1b\r\x0e\n)-*\x0e\rh'N\x14\x13\x01'\x15\x14\xa1\xa0!'\x03\x04\x16O\x1c\x1cg,R\x13\x13\x1f\"\x14/!YQG\x16\x15\x0154'6\x133&5\x1147#\x16\x15\x11\x14\x055\x06#\"=\x0132\x1635#47#\x16\x1d\x01#\x15632\x163\x15#\x15\x14\x1e\x0332\x014&\"\x06\x15\x14\x1626%\x11\x14\x06#!\"&5\x11463!2\x16\x02F]kbf$JMM$&\xa6N92Z2\x1d\b\x02\a\x18\x06\x15&`\x06\xe3\x06\xab\x0f9\x0eUW=\xfd\xf0N9:PO;:\x16dhe\x03\\=R\x91\x87\x01\xcd\xca\f\n+)\u007f\xb3\x17\b&'\x1f)\x17\x15\x1e-S9\xfe\xd0\x199kJ\xa5<\x04)Um\x1c\x04\x18\xa9Q\x8b\xb9/\xfc\xbe-Y\x02a^\"![\xfd\x9bY\xb1\xc4'(<`X;\x01_\x04\x02\x06\xbeL6#)|\xbe\x04\xfe\x93\x83\x04\x0etWW:;X\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x1b\x00\x00\t\x01#\x03\x06\a'\x03#\x01\x113\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03)\x01\np\x9d\x18\x14*\x9bx\x01\ae\x02שw\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02\x14\x01\xf3\xfe\xc80,\\\x018\xfe\x13\xfe\xbc\x03\x8a\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x02\x009\xff\x00\x04\xc7\x06\x00\x00\x1d\x00I\x00\x00\x00\x14\x06#\"'\x06\a\x02\x13\x16\x06\a#\"&'&>\x03767&5462\x04\x10\x02\x04#\"'.\x017>\x01\x17\x1632>\x024.\x02\"\x0e\x02\x15\x14\x17\x16\x0e\x01&'&54>\x0232\x04\x03JrO<3>5\xf7-\x01\x1b\x15\x05\x14\x1e\x02\x0e\x15&FD(=G\x10q\xa0\x01\xee\x9c\xfe\xf3\x9e@C\x15\x17\x05\x05$\x1539a\xb2\x80LL\x80\xb2²\x80L4\n\r&)\n@]\x9c\xd8v\x9e\x01\r\x04\x14\xa0q#CO\xfe\x8d\xfe\x18\x16!\x02\x1b\x14~\U000ffd42\x0172765'.\x01/\x01\"\a\x0e\x01\a#\"&'&5\x10\x01\x0e\b\x16\r\x01\x11\x0e\xb9}\x8b\xb9\x85\x851R<2\"\x1f\x14\f\x017\x12\x03\x04MW'$\t\x15\x11\x15\v\x10\x01\x01\x02\x05;I\x14S7\b\x02\x04\x05@\xee5sQ@\x0f\b\x0e@\b)\xadR#DvTA\x14\x1f\v;\x14\x04\n\x02\x020x\r\x05\x04\b\x12I)\x01\x04\x04\x03\x17\x02\xda\x13!\x14:\x10\x16>\f\x8b\x01+\x03\x14)C\x04\t\x016.\x01\x13\x00\x00\x00\x00\x06\x00\x00\xff>\b\x00\x05\xc2\x00\n\x00\x16\x00!\x00-\x00I\x00[\x00\x00\x004&#\"\x06\x15\x14\x1632\x014&#\"\x06\x15\x14\x16326\x024&#\"\x06\x15\x14\x1632\x014&#\"\x06\x15\x14\x16326\x01&#\"\x04\x02\x15\x14\x17\x06#\".\x03'\a7$\x114\x12$32\x04\x16\x01\x14\x06\a\x17'\x06#\"$&\x106$32\x04\x16\x02D2)+BB+)\x03\x193(\x1b--\x1b(3\xec1)+BB+)\x02\xac4'\x1b--\x1b'4\xfe\xf6\x1f'\xa9\xfe\xe4\xa3\x17#!\x1a0>\x1bR\t\xfdH\xfe\xde\xc3\x01MŰ\x019\xd3\x02o\x89u7ǖD\xa9\xfe䣣\x01\x1c\xa9\xa1\x01\x1c\xab\x04\nR23('3\xfe_\x1c,-\x1b\x1c-,\x01\xefR23('3\xfe_\x1c,-\x1b\x1c-,\x01\xaa\x04\x9a\xfe\xf9\x9cNJ\x03\x03\n\x04\x11\x02\u007f\xda\xcb\x01\x1f\xa9\x01\x1c\xa3\x84\xe9\xfd?u\xd5W\xb5m%\x8d\xf2\x01\x1e\xf2\x8d\x8d\xf3\x00\x01\x00\x00\xff\x00\x06\xff\x06\x00\x00\x1e\x00\x00\x01\x16\a\x01\x06\a\x06#\"'%\x03\x06#\"'.\x015\x11\t\x01%&'&7\x01632\x06\xe4!\x06\xff\x00\x05\x1b\x0e\x11\v\r\xfe;\xf2\x12\x1f\r\t\x13\x17\x03`\xfb\xd3\xfeu%\x03\x02\"\x06\x80\x0f\x11\x14\x05\xf5\x18(\xfa\x00\x1d\x10\b\x05\xb9\xfe\xd9\x17\x04\a!\x14\x01]\x04#\xfcc\xa2\x0e)(\x13\x03\xc0\t\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06\xff\x05\xf7\x00\x1a\x00 \x00\x00\x01\x16\a\x01\x06\a\x06#\"'%\x01\x06#\"'.\x015\x11%&'&7\x016\x01\x13\x01\x05\t\x01\x06\xe4!\x06\xff\x00\x05\x1b\x0e\x11\v\r\xfd\xf1\xfe\xd6\x12\x1d\x0e\t\x13\x16\xfe(%\x03\x03#\x06\x80#\xfe\xcb\xdd\xfaf\x01P\x03_\xfe\"\x05\xf5\x18(\xfa\x00\x1d\x10\b\x05\xd7\xfe\xb9\x15\x04\a!\x14\x01\xc4\xc1\x0e)'\x14\x03\xc0\x15\xfa\x0e\x05+\xfcʼn\x02\u007f\xfc\xe3\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x004\x00I\x00\x00\x00\x10\x02\x06\x04#\"$'&6?\x0163\x16\x17\x1e\x0132>\x024.\x02#\"\x06\a\x17\x16\a\x06#!\"&5\x11476\x1f\x016$32\x04\x16\x05\x11\x14\x06#!\"&=\x0146;\x01\x1146;\x012\x16\x06\x00z\xce\xfe䜬\xfe\xcam\a\x01\b\x89\n\x0f\x10\aI\xd4wh\xbd\x8aQQ\x8a\xbdhb\xb4F\x89\x1f\x11\x11*\xfe@\x1a&('\x1e\x82k\x01\x13\x93\x9c\x01\x1c\xce\xfd\xfa\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x03\x1c\xfe\xc8\xfe\xe4\xcez\x91\x84\n\x19\b\x8a\t\x02\n_hQ\x8a\xbdн\x8aQGB\x8a\x1e'(&\x1a\x01\xc0*\x11\x11\x1f\x81eozΘ\xfe@\x0e\x12\x12\x0e@\x0e\x12\x01`\x0e\x12\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00\x00\x00 \x0e\x02\x10\x1e\x02 >\x02\x10.\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x82\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x01\x91\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x05\x00f\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xfe\xb7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x01\x00>\xff\x80\x06\xc2\x05\x80\x00\x85\x00\x00\x05\"&#\"\x06#\"&54>\x02765\x034'&#!\"\a\x06\x15\x03\x14\x17\x1e\x03\x15\x14\x06#\"&#\"\x06#\"&54>\x02765'\x1146.\x04'.\x01\"&54632\x1632632\x16\x15\x14\x0e\x02\a\x06\x15\x13\x14\x17\x163!2765\x134'.\x0254632\x1632632\x16\x15\x14\x0e\x02\a\x06\x15\x13\x14\x17\x1e\x03\x15\x14\x06\x06\x92,\xb1-,\xb0,\x18\x1a\",:\x10!\x01\x01\r%\xfd]&\r\x01\x01%\x10@2(\x19\x18/\xb9.+\xaa*\x17\x19\x1f)6\x0f!\x01\x01\x01\x02\x05\b\x0e\t\x0f<.$\x18\x18.\xb9.*\xa9*\x19\x19\"+8\x0f#\x01\x01\r\x1a\x02\xbb\x19\r\x01\x01#\x12Q3\x19\x19,\xb0,+\xac+\x19\x19#-:\x0f#\x01\"\x10\x19$$\x19\x01\xf0\f/:yu\x8e\xa6xv)%$\x00\t\x00\x00\xff\x80\x06\x00\x05\x00\x00\x03\x00\x13\x00\x17\x00\x1b\x00\x1f\x00/\x00?\x00C\x00G\x00\x00%\x15!5%2\x16\x15\x11\x14\x06#!\"&5\x11463\x01\x15!5\x13\x15#5\x01\x15!5\x032\x16\x15\x11\x14\x06#!\"&5\x11463\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x05\x15#5\x13\x15!5\x01`\xfe\xa0\x02\xc0\x1a&&\x1a\xff\x00\x1a&&\x1a\x01\xa0\xfc\xa0\xe0\xe0\x06\x00\xfd \xe0\x1a&&\x1a\xff\x00\x1a&&\x1a\x03\x80\x1a&&\x1a\xff\x00\x1a&&\x1a\x02@\xe0\xe0\xfc\xa0\x80\x80\x80\x80&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\x01\x80\x80\x80\x02\x00\x80\x80\xfc\x00\x80\x80\x04\x80&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\xfe\x00&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\x80\x80\x80\x02\x00\x80\x80\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00%\x00\x00\x012\x16\x10\x06 &547%\x06#\"&\x10632\x17%&546 \x16\x10\x06#\"'\x05\x16\x14\a\x056\x04\xc0\x85\xbb\xbb\xfe\xf6\xbb\x02\xfe\x98\\~\x85\xbb\xbb\x85~\\\x01h\x02\xbb\x01\n\xbb\xbb\x85~\\\xfe\x98\x02\x02\x01h\\\x02\x00\xbb\xfe\xf6\xbb\xbb\x85\f\x16\xb4V\xbb\x01\n\xbbV\xb4\x16\f\x85\xbb\xbb\xfe\xf6\xbbV\xb4\x16\x18\x16\xb4V\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00%\x005\x00\x00$4&#\"\a'64'7\x163264&\"\x06\x15\x14\x17\a&#\"\x06\x14\x16327\x17\x06\x15\x14\x162\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00}XT=\xf1\x02\x02\xf1=TX}}\xb0~\x02\xf1>SX}}XS>\xf1\x02~\xb0\x01}\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfd\xb0~:x\x10\x0e\x10x:~\xb0}}X\a\x10x9}\xb0}9x\x10\aX}\x03\xe0\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\a\x00\x00\xff\x00\a\x00\x06\x00\x00\x11\x00/\x00>\x00L\x00X\x00d\x00s\x00\x00\x00.\x01\a\x0e\x01\a\x06\x16\x17\x16327>\x0176\x01\x17\a\x17\x16\x14\x0f\x01\x16\x15\x14\x02\x06\x04 $&\x02\x10\x126$32\x17762\x1f\x01\x13\x06#\"/\x01&4762\x1f\x01\x16\x14\x17\x06\"/\x01&4762\x1f\x01\x16\x146\x14\x06+\x01\"&46;\x012'\x15\x14\x06\"&=\x01462\x16\x17\a\x06#\"'&4?\x0162\x17\x16\x14\x02E\x140\x19l\xa6,\n\x14\x19\r\v*\x12\"\x81T\x19\x03\xb8.\xf4D\x13\x13@Yo\xbd\xfe\xfb\xfe\xe2\xfe\xfb\xbdoo\xbd\x01\x05\x8f\xb6\xa1@\x135\x13D\xfb\n\f\r\n[\t\t\n\x1a\nZ\n\xdc\v\x18\vZ\n\n\t\x1b\t[\t \x12\x0e`\x0e\x12\x12\x0e`\x0e\xae\x12\x1c\x12\x12\x1c\x12\x97[\n\f\r\n\n\nZ\n\x1a\n\t\x03\x9a2\x14\n,\xa6l\x190\n\x05(T\x81\"\v\x01\xad.\xf3D\x135\x13@\xa1\xb6\x8f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdoY@\x13\x13D\x01,\n\nZ\n\x1a\n\t\t[\t\x1b\xef\t\t[\t\x1b\t\n\nZ\n\x1a\xbb\x1c\x12\x12\x1c\x12\xa0`\x0e\x12\x12\x0e`\x0e\x12\x12EZ\n\n\t\x1b\t[\t\t\n\x1a\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x04\x00\x14\x005\x00\x00\x01%\x05\x03!\x02 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126\x016=\x01\a'\x13\x17&'\x17\x05%7\x06\a7\x13\a'\x15\x14\x177\x05\x13\a\x1627'\x13%\x02a\x01\x1f\x01\x1fm\xfe\x9d\x05\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x04m\x95f\xf0?\x86\x96\xef5\xfe\xe1\xfe\xe15\uf587>\xf0f\x95\x1e\x01F\x8btu\xf6ut\x8b\x01F\x02\xd0\xd0\xd0\xfe\xb0\x04\x80\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xfbH\xcb\xfb\x03Y\xe0\x01C\f\xceL|\x9f\x9f|L\xce\f\xfe\xbd\xe0Y\x03\xfb˄(\xfe\xd6E''E\x01*(\x00\x00\x00\f\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00I\x00Y\x00i\x00y\x00\x89\x00\xa2\x00\xb2\x00\xbc\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\"&=\x01!\x15\x14\x06#\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15!54\x05\x04\x1d\x01!54>\x04$ \x04\x1e\x04\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06#!\"&=\x01\x01\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x02@\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xfd\xc2\x1c&\x02\x02&\x1b\x02\xff\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x02@\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\xfd\xfe\xfe\x82\xfe\x82\xfd\xfe\x113P\x8d\xb3\x01\r\x01>\x01\f\xb4\x8dP3\x11\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12&\x1b\xfe\x80\x1b&\xe0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfer\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x92&\x1b\x81\x81\x1b&\xfd\xe0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfer\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01\x8a\r\nh\x02\x01e\n\r\x114LKM:%%:MKL4\xfeW\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01T\x81\x1b&&\x1b\x81\x00\x00\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\x10\x00\x14\x00%\x00/\x009\x00\x00\x01\x11\x14\x06#\x11\x14\x06#!\"&5\x11\x1363!\x11!\x11\x01\x11\x14\x06#!\"&5\x11\"&5\x11!2\x17\x01\x15!5463!2\x16\x05\x15!5463!2\x16\x02\xc0&\x1a&\x1a\xfe\x00\x1a&\xf9\a\x18\x02\xe8\xff\x00\x04\x00&\x1a\xfe\x00\x1a&\x1a&\x01\xa8\x18\a\xfc\xd9\xfe\xa0\x12\x0e\x01 \x0e\x12\x02\xa0\xfe\xa0\x12\x0e\x01 \x0e\x12\x04\xc0\xfd\x00\x1a&\xfd\xc0\x1a&&\x1a\x02\x00\x03i\x17\xfd@\x02\xc0\xfc\x80\xfe\x00\x1a&&\x1a\x02@&\x1a\x03\x00\x17\x017\xe0\xe0\x0e\x12\x12\x0e\xe0\xe0\x0e\x12\x12\x00\x01\x00\x00\xff\x00\a\x00\x06\x00\x00\x1d\x00\x00\x01\x16\x14\a\x01\x17\a\x06\x04'\x01#5\x01&\x12?\x01\x17\x0162\x16\x14\a\x01\x17\x0162\x06\xdb%%\xfeo\x96\xa0\xa3\xfe;\xb9\xfe\x96\xb5\x01j|/\xa3\xa0\x96\x01\x90&jJ%\xfep\xea\x01\x91&j\x04;&i&\xfep\x96\xa0\xa3/|\xfe\x96\xb5\x01j\xb9\x01ţ\xa0\x96\x01\x91%Jk%\xfeo\xea\x01\x90%\x00\x00\x00\x04\x00\x19\xff\f\x06\xe7\x06\x00\x00\t\x00\x15\x00:\x00g\x00\x00\x01\x14\x06\"&5462\x16\x05\x14\x06#\"&54632\x16\x13\x114&#!\"\x06\x15\x11\x1e\x052636\x17\x16\x17\x16\x176\x172\x1e\x02>\x057\x06\a\x12\a\x06\a\x06'&7\x035.\x01'\x03\x16\a\x06'&'&\x13&'&6\x17\x1e\x01\x17\x11463!2\x16\x15\x1176\x16\x03i\u007f\xb2\u007f\u007f\xb2\u007f\x01\xf6~ZY\u007f\u007fYZ~\xe1@O\xfb\xa8S;+[G[3Y\x1cU\x02D\x1b\x06\x04\x1a#\ao\x05?\x17D&G3I=J\xc6y\xfbTkBuhNV\x04\x01\b!\a\x01\x04WOhuAiS\xfby\x19*'\x04\x0f\x03^C\x04\xe9C^\x15'*\x03\x1cSwwSTvvTSwwSTvv\xfe\xf8\x02\x9bWID\\\xfd_\x17\"\x16\x0f\a\x01\x04\x01\x1c\x06\x03\x19\x1a[\x04\x03\x01\x01\x03\x06\v\x10\x17\x1f\x18\x95g\xfe\xe3\xb4q# /3q\x01F\x01\x02\b\x01\xfe\xaer2/ $r\xb4\x01\x1bg\x95%4\x1b\x02\n\x03\x02\xb6HffH\xfdJ\x0f\x1b4\x00\x00\x04\x00d\xff\x80\x06\x9c\x06\x00\x00\x03\x00\a\x00\x0f\x00\x19\x00\x00\x01\x11#\x11!\x11#\x11\x137\x11!\x11!\x157\x01\x11\x01!\a#5!\x11\x13\x03\x80\x91\x02\x1f\x91\x91\xfd\xfbV\x01F\xd9\x03\x1c\xfeN\xfe\xba\xd9\xd9\xferm\x04N\xfeN\x01\xb2\xfeN\x01\xb2\xfd\b\xfe\x03\x1b\xfb\xe7\xd9\xd9\x04\xaa\xfc\v\xfeN\xd9\xd9\x04\x86\x01!\x00\x00\x00\x00\x05\x00Y\xff\x01\x05\xaa\x05\xfd\x00\x16\x00+\x00?\x00N\x00e\x00\x00%\x15\x02\a\x06\a\x06&'&'&7>\x01727>\x01\x17\x1e\x01'\x06\x0f\x01\x04#&'&'&>\x01\x172\x17\x16\x1f\x01\x1e\x01\x01\x0e\x01\a\x06'&\x03'&676\x17\x16\x17\x1e\x01\x17\x16\x01\x16\a\x06'\x01&76$\x17\x16\x17\x16\x12\x05\x16\a\x06\x05\x06\a7\x06&'&767>\x0176\x17\x1e\x01\x17\x03\x05\x01\x05\f'6\xff#\r\x04\x01\x05\x04<\x97\x01;\x0f1\x19\x18\x1b\x96\x031x\xfe\xed\x11#\x13\f\x05\b\x12*#\r\xbdG,T\x17\x19\x039\a\xa93%\x1a\x0e\xaa/\x0e\x05\x11#0\x01v\xcbN\b\x1c\xfdZ\x05;:8\xfe\x86\b\x1b)\x01M:(\t\x03&\x02\x9b\x03\x1d\x0f\xfe\xc6C\x18\x01\x17.\x0e\x1e\x1e\x01J}2\t\x1c%0\x96\x06\xd9\u007f\xfe\xdc\r \b\t^*\x0f\x15\f\x0e\nJ\xb3F\x13\v\t\n&\xe47\x0f'X\x02\"\x192L\xb5D\x02M\x1d\x12\"\t+\xfe\xbc6\xd6\x14\x0e\x15\n\x01\x15M\x152\x15+\x11\x01'B\x1b\a\x16\x02Qf\x14\x11X\x02V#\x1b+]\x0f\n#\x12\xfd\xc1\xc8'\x14\nL\x0f\b\x02\x06\x14\x16/(\x01e\xabB\x06\x13\x11\x17\xdd9\x00\x00\x00\n\x00\x00\x00\x00\b\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00#\x00,\x008\x00\x00\x01!\x11!\x13\x15!5\x01\x11!\x11\x01\x15!5\x01\x15!5\x01\x15!5\x01\x15!5\x01\x11#\x11\x14\x1626%\x11!\x11\x14\a!26\x13\x11\x14\x06#!\"&5\x11!5\x04\x00\xfe\x80\x01\x80\x80\xfd\x80\x02\x80\xfd\x80\x05\x00\xfe\x00\x02\x00\xfe\x00\x02\x00\xfe\x00\x02\x00\xfe\x00\xfc\x00\x80&4&\x06\x80\xfa\x00\v\x05\xcb\x1a&\x80pP\xf9\x80Pp\x01\x00\x04\x00\xfe\x80\xff\x00\x80\x80\x03\x00\xfd\x80\x02\x80\xfd\x00\x80\x80\x01\x00\x80\x80\x01\x00\x80\x80\x01\x00\x80\x80\xfc@\x03\xc0\xfc@\x1a&&\x1a\x04@\xfb\xc0!\x1f&\x04\xda\xfb@PppP\x04@\x80\x00\x04\x00*\x00\r\a\xd6\x05\x80\x00\t\x00\x1f\x009\x00Q\x00\x00$\"&5462\x16\x15\x147\".\x01\"\x0e\x01#\"&547>\x012\x16\x17\x16\x15\x14\x06\x01\"'.\x01#\"\x0e\x03#\"&5476$ \x04\x17\x16\x15\x14\x06\x13\"'&$ \x04\a\x06#\"&5476$ \x04\x17\x16\x15\x14\x06\x04\x14(\x92}R}h\x02L\u007f\x82\u007fK\x03\x12\x97\nN\xec\xe6\xecN\n\x97\x00\xff\v\f\x88\xe8\x98U\xab\u007fd:\x02\x11\x96\n\x84\x01x\x01\x80\x01x\x84\n\x96\xfe\v\v\xb3\xfe\u007f\xfe8\xfe\u007f\xb3\v\v\x11\x97\n\xbb\x02\x04\x02\x1a\x02\x04\xbb\n\x97\r\x93\x14 ,, \x14|2222\x96\x12\r\nMXXM\n\r\x12\x96\x01\x10\bic,>>,\x96\x12\f\n\x84\x92\x92\x84\n\f\x12\x96\x01\x0f\t\x9d\x9f\x9f\x9d\t\x96\x12\r\n\xba\xcc̺\n\r\x12\x96\x00\x00\r\x00\x00\xff\x00\x06\x80\x06\x00\x00\a\x00\x0f\x00\x17\x00\x1f\x00'\x00/\x007\x00?\x00K\x00S\x00c\x00k\x00{\x00\x00\x044&\"\x06\x14\x162$4&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x01\x114&\"\x06\x15\x11\x14\x1626\x004&\"\x06\x14\x162\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x104&\"\x06\x14\x162\x13\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80KjKKj\x01\xcbKjKKj\xfe\xcbKjKKj\x03KKjKKj\xfe\xcbKjKKj\xfe\xcbKjKKj\x03KKjKKj\xfe\xcbKjKKj\x03KLhLLhL\xfe\x80KjKKj\x01\xcb&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&KjKKj\xcbL4\xfa\x804LL4\x05\x804L5jKKjKKjKKjK\x01\xcbjKKjK\xfe\xcbjKKjK\x01\xcbjKKjK\x01\xcbjKKjK\xfe\xcbjKKjK\x01\xcbjKKjK\xfd\x80\x01\x804LL4\xfe\x804LL\x02\xffjKKjK\x01\xc0\x01\x00\x1a&&\x1a\xff\x00\x1a&&\xfe\xa5jKKjK\x03\x00\xfa\x004LL4\x06\x004LL\x00\x02\x00\t\xff\x00\x05\xef\x06\x00\x00'\x00E\x00\x00\x01\x16\a\x02!#\"\x06\x0f\x01\x03\a\x0e\x01+\x01\"&7>\x0376;\x01\x167676767>\x01\x16\x17\x16'\x14\a\x06\a\x06\a\x14#'\"\a\x06\x03\x06#!\"&7\x13>\x013!2\x16\x17\x1e\x01\x05\xef\x12\x16W\xfe\",\x19&\x05\x047\x02\x05'\x19\xfb\x15\x18\x03\t#\x12$\t\x05&\x83\x85g\xafpf5\x18\v\x01\x03\x04\x04O\x99.P\xdeq\x8bZZd\x12\x02S\x01\v\xfe\xd9\x16\x1d\x03\xe8\x05-\x1d\x02V\"\u007f0kq\x03zTx\xfeD!\x1a\x13\xfe\xa6\x0f\x1a!\x1e\x158\xe0p\xdf8%\x02\x17'i_\x97F?\x06\x03\x01\x03;\xb3k\x81\xe9R(\x02\x01\x01`\b\xfd\xf6\n!\x16\x05\xbf\x1d&\x1a\x13)\xa4\x00\x00\x04\x00'\xff\x00\a\x00\x06\x00\x00\n\x00\x12\x00\x19\x00(\x00\x00\x012\x17\x00\x13!\x02\x03&63\x01\x06\a\x02\x0367\x12\x13\x12\x00\x13!\x02\t\x01\x10\x03\x02\x01\x02\x03&63!2\x16\x17\x12\x01\xb9!\x13\x01\n`\xfeB\u007f\xf0\f\x12\x14\x03\xa41LO\xb1(\x04\xd3\xe1\xeb\x01+#\xfe=)\xfe\x00\x04heC\xfe\xdc\x19Q\x04\x13\x10\x01g\x15#\x05s\x03`\x1a\xfe\x94\xfef\x01\xb9\x014\x10#\xfe\x9b\xc7\xc2\x016\x01\x1c\xdd\xe4\xfe\xac\x01\x8f\xfe\xbc\xfd\x13\xfeq\x02\x99\x03'\xfd\xc0\xfeX\xfe|\x020\x02\v\x01-\x01\x1b\x10\x19\x1a\x14\xfeg\x00\a\x00\x00\xff\x80\t\x00\x05\x80\x00\b\x00\x0f\x00\x18\x00\x1c\x00>\x00I\x00Y\x00\x00\x01#6?\x01>\x017\x17\x05\x03&#!\a\x04%\x03'.\x01'\x133\x01\x033\x13#\x05&#\"\x06\a\x06\x17\x1e\x01\x15\x14\x06#\"/\x01\a\x163\x16674'.\x0154636\x1f\x01%#\"\a\x03373\x16\x173\x13\x11\x14\x06#!\"&5\x11463!2\x16\a\xb7\x8a\x0e4\x03\x04\f\x03\f\xfa\x82:\v@\xfe\xf4\x02\x017\x01\x0f\xa2\x11\x1avH\x87\xaf\x01\x05%\xa6h\xa6\x02\x98EP{\x9c\x01\x01\x920&<'VF\x16\x17Jo\x82\x9d\x02\x8c1,1.F6\x0f\x01\xc0\x80A\x16\xf6\xae#\xd4\x05\x0f\x9a\x80L4\xf8\x004LL4\b\x004L\x02\"%\x8e\t\n \n7x\x01'6\rO\\\xfeJYFw\x1d\xfe\x02\x02\x81\xfd~\x02\x82\x10\x1bv^fH\x17$\x15\x1e !\v\x90\"\x01xdjD\x19\"\x15\x16!\x01\x19\b\x9b6\xfd\xb4`\x16J\x03\xc2\xfb\x004LL4\x05\x004LL\x00\x18\x00\x00\xff\x80\t\x00\x05\x80\x00\x11\x00\x19\x00+\x003\x00@\x00G\x00X\x00c\x00g\x00q\x00z\x00\x9c\x00\xb8\x00\xc7\x00\xe5\x00\xf9\x01\v\x01\x19\x01-\x01<\x01J\x01X\x01{\x01\x8b\x00\x00\x01&#\"\x0e\x02\x15\x14\x1e\x02327&\x02\x127\x06\x02\x12\x176\x12\x02'\x16\x12\x02\a\x1632>\x0254.\x02#\"\x0135#\x153\x15;\x025#\a'#\x1535\x1737\x03\x15+\x015;\x01\x153'23764/\x01\"+\x01\x15353$4632\x16\x15\x14\x06#\"$2\x17#\x04462\x16\x15\x14\x06#\"6462\x16\x15\x14\x06\"\x17\"'\"&5&547476125632\x17\x161\x17\x15\x16\x15\a\x1c\x01#\a\x06#\x06%354&'\"\a&#\"\a5#\x1535432\x1d\x0135432\x15\x173=\x01#\x15&#\"\x06\x14\x1632?\x014/\x01&5432\x177&#\"\x06\x15\x14\x1f\x01\x16\x15\x14#\"'\a\x16326\x17'\x06#\"=\x0135#5#\x15#\x153\x15\x14327\"\x06\x15\x14\x16327'\x06#\"'354&3\"\a5#\x1535432\x177&\x16\x14\x16327'\x06'\"&4632\x177&#\"\x173=\x01#\x15&#\"\x06\x14\x1632?\x01\"\a5#\x1535432\x177&\x173=\x01#\x15&\"\x06\x14\x1632?\x01\a\"#\x06\a\x06\x15\x06\x15\x14\x17\x14\x17\x1e\x013274?\x0167654'&'4/\x01\"&\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04_\x80\x99g\xbd\x88QQ\x88\xbch\x99\x80\x83^_\xa3~\\[\u007f\u007f[\\]\x82_^\x83\x80\x99h\xbc\x88QQ\x88\xbdg\x99\x02e\a\x11\a\x03\x1d\x04\x05\x06\x06\x05\x03\x06\x04\x05\b\x02\x03\x03\x02\x03\x04\x01\x01\x01\x01\x01\x01\x02\x01\x06\x03\x01\xfb\x16\x16\x13\x12\x16\x16\x12\x13\x01\xa5<\x05F\x01\x87\x16$\x17\x16\x13\x12\xfa\x17$\x17\x17$\x87\x02\x02\x01\x04\x01\x01\x02\x01\x02\x02\x02\x03\x01\x04\x02\x01\x01\x01\x01\x02\x02\x01\xfa\xbc\x1e\x1d\x19 \x0f\x0e\x1f\x18\x0f\x1e\x1e!\x1e\x1d!\x1e\xa6\x1d\x1d\x11\x1a\x1d&&\x1d\x1c\x0f\xb2/\x0e\x17\x19\x17\x14\f\x16!\x1a\x1e/\r\x18\x1f\x19\x14\r\x19!\x1d!\x82\b\r\r\x1300\x1e\x1c\x1c/\x15e\x1d&'\x1e!\x16\x0e\x12\x15\"\ae$\x83\x17\f\x1e\x1e\x1d\n\b\t\t\x12'!\x1d\x13\x0e\x12\x11\x12\x17\x17\x12\x13\x10\x0e\x14\x1c!\xce\x1e\x1e\x0f\x1b\x1d''\x1d\x1c\x0e\x85\x17\f\x1d\x1d\x1d\n\b\t\b\u007f\x1d\x1d\x0f8''\x1c\x1d\x0eN\x02\x02\x01\x02\x02\x03\x01\x01\x03\x02\x04\x03\x04\x02\x02\x02\x01\x02\x01\x01\x01\x02\x02\x02\x01\x04\x01gL4\xf8\x004LL4\b\x004L\x04\xabUQ\x88\xbcgh\xbc\x88QUk\x01=\x01(\x14\x18\"\x06\x02\x04\n\x0f\v\x18\x0e\x18\x14!\x06\x02\x04\n\x11\x0e\x17\x11\x18\x0e\x19\a\x16=\x1b))\x1b=2\x8e(\x1f '\x13\x16\x0f!\f '\x14\x10\x87L#\x04\x1c\x04(>(\x10\x18\r\x01\x18&\x18\f\x18\x10\x8bDC\x10\x14(>(\x14z\x14\x10\x87L#\x04\x1c\x04\x8bDzG\x14)<)\x14\x03\x01\x01\x02\x01\x03\x02\x04\x03\x02\x02\x02\x02\x02\x01\x01\x01\x01\x01\x03\x02\x03\x04\x02\x01\x03\x01\x01\x01\x01\x04\xe5\xfb\x004LL4\x05\x004LL\x00\x00\f\x00\x00\xff\x80\t\x00\x05\x80\x00\n\x00\x11\x00\x1b\x00\x1f\x00B\x00W\x00b\x00j\x00q\x00}\x00\x8a\x00\x9a\x00\x00\x01\x14\a\x06+\x01532\x17\x16%\x14+\x01532\x054&+\x01\x113276\x173\x11#\x054&'.\x0154632\x177&#\"\x06\x15\x14\x16\x17\x16\x17\x16\x15\x14\x06#\"'\a\x16326\x055\x06#\"&54632\x175&#\"\x06\x14\x1632\x01\x11\x0e\x01\f\x02\x05!26\x004&\"\x06\x14\x162%\x13#\a'#\x13735#535#535#\x013'654&+\x01\x11353\x01\x11\x14\x06#!\"&5\x11463!2\x16\x019$\x1d<\x11\x11=\x1c$\x06\xf0@\x13\x14?\xf9SdO__J-<\x1eAA\x01@)7\x1d\x15\x1b\x15\x1d\x18\")9,<$.%\b\x13\x1c\x160\x17*,G3@\x01\x16%)1??.+&((JgfJ*\x04\xf7A\x9f\xfe\xc4\xfe\xa9\xfe\x14\xfe\xfe\x06!\x1a&\xfc\xadj\x96jj\x96\x01\x02\x90GZYG\x8eиwssw\xb8\x01\x87PiL>8aA\t\x01!M7\xf8\b7MM7\a\xf87M\x02\xf73!\x1a\xdc\x1b\x1f\r4erJ]\xfe\xb3&3Y\x01M\xe8(,\x14\n\x12\x0e\x10\x15\x1b,%7(#)\x10\r\x06\f\x16\x14\x1b,(@=)M%A20C&M\x14e\x92e\xfd\xb7\x02\x0f(X\x92\x81\x8c0&\x02Ėjj\x96j\b\x01V\xe0\xe0\xfe\xaa\t8Z8J9\xfe\xb3\x8c\x10N/4\xfe\xb3\x85\x02$\xfb\f8NN8\x04\xf48NN\x00\x00\x00\x00\x12\x00\x00\xff\x80\t\x00\x05\x80\x00\x02\x00\v\x00\x0e\x00\x15\x00\x1c\x00#\x00&\x00:\x00O\x00[\x00\xce\x00\xe2\x00\xf9\x01\x05\x01\t\x01$\x01?\x01b\x00\x00\x133'\x017'#\x153\x15#\x15%\x175\x174+\x01\x1532%4+\x01\x1532\x014+\x01\x1532\x053'%\x11#5\a#'\x15#'#\a#\x133\x13\x113\x177\x01\x14\x0e\x04\"&#\x15#'\a!\x11!\x17732%\x15#\x113\x15#\x153\x15#\x15\x01\x15\x14\x06#!\"&5\x11373\x1735\x1737\x15!572\x1d\x01!5\x1e\x026373\x1735\x173\x11#\x15'#\x15'#\"\a5#\x15&#!\a'#\x15'#\a\x11463!2\x16\x15\x11#\"\a5#\"\a5!\x15&+\x01\x15&+\x01\a'!\x11!7\x1735327\x153532\x16\x1d\x01!27\x1532%\x14\x06\a\x1e\x01\x1d\x01#54&+\x01\x15#\x1132\x16\x01\x14\x06\a\x1e\x01\x1d\x01#46.\x03+\x01\x15#\x11\x172\x16\x01\x15#\x113\x15#\x153\x15#\x15\x01\x11#\x11\x01\x14+\x0153254&\".\x01546;\x01\x15#\"\x15\x14\x166\x1e\x017\x15\x06+\x0153254&\x06.\x02546;\x01\x15#\"\x15\x14\x1e\x01\x03\x11#'\x15#'#\a#\"54;\x01\x15\"&\x0e\x04\x15\x14\x16;\x0173\x13\x113\x175wY-\x02AJF\xa3\x8e\x8e\x01=c\xbd(TS)\x01!*RQ+\xfe\xea*RQ+\x01\xcbY,\xfc\x16B^9^\x84\x19\x87\x19Ft`njUM\x02\x98\v\x11\x1c\x18'\x18)\t~PS\xff\x00\x01\x04PR\xcfm\xfe\xdd\xd9٘\x94\x94\x05\xd4M7\xf8\b7Mo\x197\x19\xda\x13q\x14\x02\x1d\n\n\x01\x17\x17@)U\t\x198\x19\xe3\"\xb6\xb4\x19\xb9\x17\xf9E(\xac\x181\xfd\x8c++\xc6\x16\xa9NM7\a\xf87Mx3\x1e\xb17\x17\xfe\xc4\x1f8\xd1\x17D\xea62\xfe\xa3\x01W74\xd3\x15;\x1f\xae\b\b\x04\x02\x119\x1f\xa8<\xfd-\x18\x16\x19\x12A\x18\"EA\x9a0:\xfe\xeb\x19\x15\x1a\x11A\x01\x01\x05\f\x17\x12F@\x991:\x02\x11\xd8ؗ\x94\x94\xfe\xedB\x02\xf7f~~\"\"12\"4(\x82w$#11#\xef\x18@}}!\x19%+%\x195(\x81v$:O\x94\\z\x84\x1a\x86\x19K\x81\x85?\a*\x0f\x1f\f\x11\x06\x1b$\x1d\\amcr\x03Vl\xfd\x86OO176Nn\xd9\x022\x16326\x05\x136&+\x01\"\a&#\"\x06\x15\x14\x163267\x06\x15\x14;\x012\x004&+\x01\"\x0f\x01'&+\x01\"\x06\x15\x14\x1e\x01\x17\x06\x15\x14;\x0127\x01%4&+\x01\"\a\x03\x06\x16;\x012?\x01>\x022\x16326\x05\x136&+\x01\"\a&#\"\x06\x15\x14\x163267\x14\x06\x15\x14;\x012\x1354+\x01\"\a\x03\a\x14\x16;\x0127\x01\x0e\x01#\a76;\x012\x16\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\xe93%\x1d#2%\x1c%\x03\x11,, \x11\x02\v\x12\x16\x1a\x18\x01_3$\x1d$2%\x1c%\xfa\xa8M>\xa0\x13\x02A\x01\b\x06L\x14\x02\x12\x01\f\x12\x10\x16\x03Vb\x015)\x01\b\x06L\x0e\x03\x1bDHeE:\x1c<\x12\x04\rE\x13\x01\xc2\b\x05M\v\aj,\x05\x11K\x05\b'-\x01R\rM\v\a\x00\xff\x01~M>\x9f\x14\x02A\x01\b\x06R\f\x04\x12\x01\f\x12\x10\x16\x03Vb\x015)\x01\b\x06L\x0e\x03\x1aEHeE:\x1d<\x11\x04\rE\x13\xdd\rJ\v\x02A\x01\b\x06B\x13\x02\xf9I\x05*'!\x11\x02\v\x13($\arL4\xf8\x004LL4\b\x004L\x02v%1 \x1c%3!x*\x1e\x01k\v\x04\x15\xa9$2 \x1c%3!\x8e;5\x13\xfeh\x06\n\x13n\b\n\x03\x02a\xe2\x01\x05\x06\n!(lI;F\x18\x14\f\t\x10\x01\x15\n\t\n\x9c\x96\x10\t\x05\x02r\x84\x04p\b\r\n\x01p8;5\x13\xfeh\x06\n\rt\b\n\x03\x02a\xe2\x01\x05\x06\n!(lI;F\x18\x14\x01\x10\x04\x10\x01\xac\x01\x0e\v\xfe`\x02\x05\t\x13\x01\x13#\x16\x01k\v\x17\x01\xdf\xfb\x004LL4\x05\x004LL\x00\x00\x00\n\x00\x00\xff\x80\t\x00\x05\x80\x00\n\x00\x0f\x002\x00H\x00W\x00[\x00l\x00t\x00\x8b\x00\x9b\x00\x00\x01\x14\a\x06#\"'5632\x05#632\x054&'.\x015432\x177&#\"\a\x06\x15\x14\x16\x17\x1e\x01\x15\x14#\"&'\a\x163276\x017#5\x0f\x033\x15\x14\x17\x163275\x06#\"=\x01\x055&#\"\x06\a'#\x113\x11632\x133\x11#\x054'&#\"\a'#\x1175\x163276\x004&\"\x06\x14\x162\x014'&#\"\x06\x15\x14\x17\x16327'\x06#\"'&'36\x13\x11\x14\x06#!\"&5\x11463!2\x16\x06=\x15\x13!\x17\x12\x1d\x1c9\x01\xb6n\x0623\xf9\xecBD$ &:B\x12CRM.0AC'\x1f0\x1dR\x1f\x12H`Q03\x01'\x13`\x81\x12.\x11>,&I / \f*\x01\x89\x0f\r /\n\n\x83\x96\x1a8\x10/\x96\x96\x02n-(G@5\b\x84\x96$ S3=\xfe,.B..B\x03\xb002^`o?7je;\x109G+\x14\x17\x05\xf8\x02\x80L4\xf8\x004LL4\b\x004L\x02yE%#\t\xe0\x1eVb\xe9;A\x19\r\x16\x0e\x1a!p &'F:A\x18\x0e\x17\x10\x1f\x19\x12q)%)\x01#o\x87\x15r\bg\xdbT$\x1e\vv\a2\xc5\x19\x8b\x03 \x1e8\xfe)\x012\x1f\xfe\xaf\x01\xd7\xdez948/\xfd{\x19\x97\v8A\x01\xc4B..B/\xfe\xebq?@\x84r\x80<7(g\x1f\x13\x13/\x0e\x02\xb1\xfb\x004LL4\x05\x004LL\x00\x00\x03\x00\x0e\xff\x00\a\xf2\x06\x00\x00\v\x00\x17\x00?\x00\x00\x01\x12\x17\x14\x06#!\x14\x06\"&'\x0524#\"&54\"\x15\x14\x16\x01\x16\x06\a\x01\x06&/\x01&6?\x01&5>\x0454\x127&5462\x16\x15\x14\a\x1e\x01\x17\x016\x16\x17\x06\x16=\xedL4\xfe@\x96ԕ\x01\x01\x00\x10\x10;U g\x043\b\x01\n\xf8\xb0\n\x1b\bT\b\x01\n\xba\x132RX='\xea\xbe\b8P8\b|\xbe5\x01\xa2\n\x1b\b\x02\xac\xfe\x9c\xc84Lj\x96\x95j\xaf U;\x10\x10Ig\x06@\n\x1b\t\xf9\xaa\b\x02\n`\n\x1b\b\xa1 \"*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x12\x81]\x01k\b\x02\n\x00\x00\x00\x00\x04\x00\x0e\xff\x00\a\xf2\x06\x00\x00\v\x00\x16\x00&\x00N\x00\x00\x044#\"&54\"\x15\x14\x163\t\x01.\x01#\"\x0e\x02\x15\x10\x01\x14\x06#!\x14\x06\"&'7!&\x037\x12\x01\x17\x16\x06\a\x01\x06&/\x01&6?\x01&5>\x0454\x127&5462\x16\x15\x14\a\x1e\x01\x17\x016\x16\x04\x10\x10;U gI\xfd\xf7\x03m*\xb5\x85]\x99Z0\x04\xc0L4\xfe@\x96ԕ\x01\x95\x02\xf5\xa6=o=\x01CT\b\x01\n\xf8\xb0\n\x1b\bT\b\x01\n\xba\x132RX='\xea\xbe\b8P8\b|\xbe5\x01\xa2\n\x1b\xb0 U;\x10\x10Ig\x01\xeb\x02\xf8Xu?bl3\xfe\x80\xfe@4Lj\x96\x95j\x81\xbb\x01\x10a\xfe\x9c\x04\xa8`\n\x1b\t\xf9\xaa\b\x02\n`\n\x1b\b\xa1 \"*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x12\x81]\x01k\b\x02\x00\x00\x00\x00\x05\x00\x00\xff\x80\x05\x80\x05\x80\x00\x0f\x00\x1f\x00/\x007\x00[\x00\x00%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126\x01!'&'!\x06\a\x05\x15\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&=\x01463!7>\x013!2\x16\x1f\x01!2\x16\x02\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xfd\xe0\x01\xc00\a\n\xfe\xc3\n\a\x03o\x12\x0e`^B\xfc\xc0B^`\x0e\x12\x12\x0e\x015F\x0fN(\x01@(N\x0fF\x015\x0e\x12\xa0\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x03\xeeu\t\x02\x02\t\x95@\x0e\x12\xfcLSyuS\x03\xb8\x12\x0e@\x0e\x12\xa7%44%\xa7\x12\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00,\x00<\x00H\x00\x00\x01\x15\x14\x0e\x02#\"\x0054\x0032\x1e\x03\x1d\x01\x14+\x01\"=\x014&#\"\x06\x15\x14\x16326=\x0146;\x012\x16\x02 \x0e\x02\x10\x1e\x02 >\x02\x10.\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04~Isy9\xcd\xfe\xed\x01\x10\xcb\"SgR8\x10v\x10\x83H\x8c\xb1\xb7\x8eD\x8c\t\x06w\x06\n\xfc\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x01\x91\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xcem2N+\x16\x01\x16\xcf\xcb\x01\x10\t\x1b)H-m\x10\x10F+1\xb7\x92\x97\xc50*F\a\t\t\x03+f\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xfe\xb7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0e\x00b\x00\x00\x014&#\"\x0e\x02\x15\x14\x1632>\x01\x05\x14\x0e\x02\a\"\x06#\"'&'\x0e\x01#\"&54\x12632\x16\x17?\x01>\x01;\x012\x17\x16\a\x03\x06\x15\x14\x163>\x045\x10\x00!\"\x0e\x02\x10\x1e\x023276\x16\x1f\x01\x16\a\x06\a\x0e\x01#\"$&\x02\x10\x126$3 \x00\x03\xcck^?zb=ka`\xa0U\x024J{\x8cK\x06\x13\a_/\x1c\x054\x9f^\xa1\xb1\x84\xe2\x85W\x88&\x02\v\x01\t\x05v\x05\b\x05\x02x\x05\x19 \x1c:XB0\xfe\xa4\xfe܂\xed\xabff\xab\xed\x82\xe4\xb1\v\x1a\b)\b\x01\x02\nf\xfb\x85\x9c\xfe\xe4\xcezz\xce\x01\x1c\x9c\x01X\x01\xa8\x02\xf9lz=l\xa6apz\x85\xc7\x11o\xacb3\x02\x015!2BX\xbf\xae\x9d\x01\n\x9bG@\x138\x06\f\v\x05\v\xfd\x9a\x18\x18'\x1a\x01\t'=vN\x01$\x01\\f\xab\xed\xfe\xfc\xed\xabf\x90\t\x02\v1\f\f\r\tSZz\xce\x01\x1c\x018\x01\x1c\xcez\xfeX\x00\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00#\x00(\x00\x00\x00\x16\x10\x0f\x01\x17\x16\x14\x0f\x01\x06\"/\x01\x01\x06+\x01\x05'\x13547\x01'&4?\x0162\x1f\x0176\t\x01'\x01\x15\x06D\xbc^\xe1h\n\n\xd2\n\x1a\ni\xfd\xa5%5\xcb\xff\x00@\x80%\x02[i\n\n\xd2\n\x1a\nh\xdf]\xfc\xc5\x02@\xc0\xfd\xc0\x06\x00\xbc\xfe\xf7]\xdfh\n\x1a\n\xd2\n\ni\xfd\xa5%\x80@\x01\x00\xcb5%\x02[i\n\x1a\n\xd2\n\nh\xe1^\xfa@\x02@\xc0\xfd\xc0\xc0\x00\x02\x00\x00\xff\x00\x06\xfe\x06\x00\x00\x10\x00)\x00\x00\x012\x16\x15\x14\a\x00\a\x06#\"&547\x016\x01\x1e\x01\x1f\x01\x16\x00#\".\x025\x1e\x03327>\x04\x06OFi-\xfe\xb4\x85ay~\xb5\\\x02~;\xfc\xba'\x87S\x01\x04\xfe\xf5\xd7{\xbes:\aD8>\x0f)\x0e\x19AJfh\x06\x00]F?X\xfd\x8b{[\xb9\u007f\x80T\x02C6\xfb\xf6Ll\x16G\xd5\xfe\xf4]\xa2\xccv\x052'\"%B];$\x0f\x00\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00-\x00o\x00\u007f\x00\x8f\x00\x9f\x00\x00%\x11!\x112>\x017>\x0132\x1e\x01\x17\x1e\x0232>\x017>\x0232\x16\x17\x1e\x022>\x017>\x0132\x16\x17\x1e\x02\x13\x15\".\x01'.\x02#\"\x0e\x01\a\x0e\x02#\"&'.\x02#\"\x0e\x01\a\x0e\x02#\"&'.\x02#\"\x0e\x01\a\x0e\x01#546;\x01\x11!\x11!\x11!\x11!\x11!\x1132\x16\x01\x14\x06#\"&54>\x0452\x16\x05\x14\x06#\"&54>\x0452\x16\x05\x14\x06#\"&54>\x0452\x16\a\x00\xf9\x00-P&\x1c\x1e+#\x18(\x16\x16\x1d$P.-P$\x1e\x15\x17'\x18#+\x1e\x1c&PZP&\x1c\x1e+#\"+\x1e\x1c&P-\x18(\x16\x16\x1d$P-.P$\x1d\x16\x16(\x18#+\x1e\x1d$P.-P$\x1e\x15\x17'\x18#+\x1e\x1c&P-.P$\x1d\x1e+#pP@\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00@Pp\xfb\x00H85K\x13\x1c\"\x1c\x13&Z\x02\x00H85K\x13\x1c\"\x1c\x13&Z\x02\x00H85K\x13\x1c\"\x1c\x13&Z\x80\xfe\x80\x01\x80\x1c\x1b\x18\x1b\x16\x0e\x10\x13\x19\x1a\x1c\x1d\x19\x19\x13\x10\x0e\x16\x1b\x18\x1b\x1c\x1c\x1b\x18\x1b\x16\x16\x1b\x18\x1b\x1c\x01@\xc0\x0e\x10\x13\x19\x1a\x1c\x1c\x1a\x19\x13\x10\x0e\x16\x1b\x19\x1a\x1c\x1d\x19\x19\x13\x10\x0e\x16\x1b\x18\x1b\x1c\x1c\x1a\x19\x1b\x16\xc0Pp\x01\xc0\xfe@\x01\xc0\xfe@\x01\xc0\xfe@p\x03\x10MSK5\x1d,\x18 \x1f:&\x94LMSK5\x1d,\x18 \x1f:&\x94LMSK5\x1d,\x18 \x1f:&\x94\x00\x02\x00\x00\xff\x80\b\x00\x05\x80\x00\x05\x00\v\x00\x00!\x15!\x113\x11\t\x01!\x11\t\x01\b\x00\xf8\x00\x80\x06\x00\x01\x00\xf9\x80\x01\xc0\x02@\x80\x06\x00\xfa\x80\x04\x00\xfc\x80\x02@\x02@\xfd\xc0\x00\x00\x00\x03\x00\x00\xff\x80\x06\xc0\x06\x00\x00\v\x00\x10\x00\x16\x00\x00\t\x01\x06\x04#\"$\x02\x10\x12$3\x13!\x14\x02\a\x13!\x112\x04\x12\x03\x00\x02\"j\xfe\xe5\x9d\xd1\xfe\x9f\xce\xce\x01aѻ\x03\x05xl\xa4\xfd\x00\xd1\x01a\xce\x02\x86\xfd\xdelx\xce\x01a\x01\xa2\x01a\xce\xfd\x00\x9d\xfe\xe5j\x02\xa2\x03\x00\xce\xfe\x9f\x00\x02\x00\x00\xff\x80\b\x00\x05\x80\x00\x05\x00\x1f\x00\x00!\x15!\x113\x11\x01\x11\x14\x06/\x01\x01\x06\"/\x01\x01'\x0162\x1f\x01\x01'&63!2\x16\b\x00\xf8\x00\x80\a\x00'\x10y\xfd\x87\n\x1a\n\xe9\xfe`\xc0\x02I\n\x1a\n\xe9\x01\xd0y\x10\x11\x15\x01\xb3\x0e\x12\x80\x06\x00\xfa\x80\x04\xe0\xfeM\x15\x11\x10y\xfd\x87\n\n\xe9\xfe`\xc0\x02I\n\n\xe9\x01\xd0y\x10'\x12\x00\x00\x01\x00\x00\x00\x00\a\x00\x04W\x00`\x00\x00\x01\x14\x17\x1e\x03\x17\x04\x15\x14\x06#\".\x06'.\x03#\"\x0e\x01\x15\x14\x1632767\x17\x06\a\x17\x06!\"&\x0254>\x0232\x1e\x06\x17\x1632654.\x06'&546\x17\x1e\x01\x17#\x1e\x02\x17\a&'5&#\"\x06\x05\f\n\n\x1e4$%\x01Eӕ;iNL29\x1e1\v ;XxR`\xaef՝\xb1Q8\x1bT\x0f\x1d\x01\x83\xfe\xff\x93\xf5\x88W\x91\xc7iW\x90gW:;*:\x1a`\x89Qs&?RWXJ8\v\x03\xafoNU0\x01\f\x16\x1e\x04\x81\x1a\x1c\x17J1F\x03@\x06#\x1d)\x1b\r\n[\xf1\x92\xc1%6_P\u007fO\x86\x1cQiX(o\xb2`\xa0\xef_?5\x98\"$\x01\x98\x9e\x01\x01\x92iʗ\\&>bd\x86s\x926\xc8aP*< \x1f\x17-;iF\x10\x11n\xa4\x04\x03\x17*\v\x1b-\x05c1\x15\x01\x15B\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00W\x00g\x00\x00\x014'.\x02'4.\x0154632\x17#\x16\x177&'.\x01#\"\x06\x15\x14\x17\x1e\x01\x17\x1e\x03\x1d\x01\x16\x06#\"'.\x05#\"\x0e\x01\x17\x15\x1e\x0232767'\x0e\x01#\"&54632\x16\x17\x1e\a326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\x98\xea#$(\t\x04\x021$6\x11\x01\x14\x13]'\n!E3P|\x02\x10ad\x1d(2\x1b\x01S;aF\x179'EO\x80Se\xb6j\x03\x04]\xaem\xba]\x14\v<*rYs\x98\xa4hpt.\b#\x16)$78L*k\x98h\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xe4\xadB\n\r%\x1c\x02\r\v\x02$/\x0f\x0f$G6\n\x1d\x14sP\a\x10`X\x1d\b\x0f\x1c)\x1a\x05:F\x90/\x95fwH1p\xb8d\x01l\xb6qn\x1b\x18mPH\xaeui\xa8kw\x15_:[9D'\x1b\x8b\x02\xe5\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x03\x00\x00\x00\x00\b\x00\x05\x00\x00\x0f\x00\x1f\x003\x00\x00\x004.\x02\"\x0e\x02\x14\x1e\x022>\x01$4.\x02#!\x16\x12\x10\x02\a!2>\x01\x12\x10\x0e\x02#!\".\x02\x10>\x023!2\x1e\x01\x04\x80Q\x8a\xbdн\x8aQQ\x8a\xbdн\x8a\x03QQ\x8a\xbdh\xfe~w\x8b\x8bw\x01\x82h\xbd\x8a\xd1f\xab\xed\x82\xfd\x00\x82\xed\xabff\xab\xed\x82\x03\x00\x82\xed\xab\x02\x18н\x8aQQ\x8a\xbdн\x8aQQ\x8a\xbdн\x8aQZ\xfe\xf4\xfe\xcc\xfe\xf4ZQ\x8a\x01\xa7\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x00\x00\x00\x02\x00\x00\x00\x00\b\x00\x05\x00\x00\x13\x00#\x00\x00\x18\x01>\x023!2\x1e\x02\x10\x0e\x02#!\".\x01\x042>\x024.\x02\"\x0e\x02\x14\x1e\x01f\xab\xed\x82\x03\x00\x82\xed\xabff\xab\xed\x82\xfd\x00\x82\xed\xab\x04\xb2н\x8aQQ\x8a\xbdн\x8aQQ\x8a\x01\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x91Q\x8a\xbdн\x8aQQ\x8a\xbdн\x8a\x00\x00\x05\x00\x00\x00\x00\t\x00\x05\x00\x00\x0e\x00\x12\x00\x18\x00,\x00\\\x00\x00\x01!\"&?\x01&#\"\x06\x10\x16326'3&'\x05\x01!\a\x16\x17\x04\x10&#\"\a\x13\x16\x06\a\x06#\"'\x03\x06\x15\x14\x16 \x00\x10\x00 \x005467'\x01\x06+\x01\x0e\x01#\"\x00\x10\x0032\x177#\"&463!\x15!'#\"&463!2\x17\x01632\x02\xfa\xfe\xc6(#\x18\xbcAH\x84\xbc\xbc\x84s\xb0\xa3\xba\x129\x01q\x01 \xfe ci\x15\x05\x05\xbc\x84<=\xae\x0f\n\x16\x0f\x15#\x12\xae]\xbc\x01\b\x01<\xfe\xf9\xfe\x8e\xfe\xf9OFA\xfe\x9f\x12!\xc5\x17\xfc\xa8\xb9\xfe\xf9\x01\a\xb9re\x89\xe0\x1a&&\x1a\x01\x80\x01\xb3U\xde\x1a&&\x1a\x01\x00!\x14\x01\v[e\xb9\x01\x80F \xfb\x1f\xbc\xfe\xf8\xbc\x91\xefU?\x94\x01\x80\x84g\x95\xc4\x01\b\xbc\x18\xfe\xfc\x174\x0e\v\x1d\x01\x04_\x82\x84\xbc\x01\xf9\xfe\x8e\xfe\xf9\x01\a\xb9a\xad?b\xfe+\x1a\xa4\xdc\x01\a\x01r\x01\a7\xb7&4&\x80\x80&4&\x1c\xfep,\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\a\x00\x0f\x00\x1f\x00+\x00K\x00\x00\x004&\"\x06\x14\x162$4&\"\x06\x14\x162\x13\x03.\x01#!\"\x06\a\x03\x06\x163!26\x024&#!\"\x06\x14\x163!2\x01\x11#\x15\x14\x06\"&=\x01!\x15\x14\x06\"&=\x01#\x1147\x13>\x01$ \x04\x16\x17\x13\x16\x01\x80KjKKj\x04KKjKKj\x1dH\x05#\x17\xfcj\x17#\x05H\x05&\x1e\x04&\x1e&\xe7\x1c\x14\xfd\x80\x14\x1c\x1c\x14\x02\x80\x14\x01\xac\x80KjK\xfd\x00KjK\x80\x19g\t\xb1\x01\x1b\x01V\x01\x1b\xb1\ti\x17\x01\vjKKjKKjKKjK\x02\f\x01\x80\x17\x1d\x1d\x17\xfe\x80\x1e..\x02n(\x1c\x1c(\x1c\xfd[\xfd\xa5\x805KK5\x80\x805KK5\x80\x02[po\x01\xc6Nv<\x02\x01\x14\x06+\x01\x16\x15\x14\x02\x06\x04#\"\x00'#\"&546;\x01&54\x126$32\x00\x1732\x16\x05\xb72$\xfdB$22$\x02\xbe$\x01\b\x17\xfc*$22$\x03\x8cX\xfeڭ\xb1\xfeӯ\x17\x03\xd6$22$\xfctX\x01'\xad\x84\xf2\xaeh\x01s2$\x83\x11\x83\xdc\xfeϧ\xf6\xfekc\xbd$22$\x84\x11\x83\xdc\x011\xa8\xf5\x01\x95c\xbc$2\x02\xe3F33F3VVT2#$2\x8f\xa8\xaf\xfeԱVT2#$2\x8f\xa8g\xaf\xf1\x01\x84#2UU\xa7\xfe\xcf݃\x01\n\xd92$#2UU\xa7\x011݃\xfe\xf6\xd92\x00\x00\x06\x00\v\xff\x00\x04\xf5\x06\x00\x00\a\x00\x0f\x00\x1b\x00,\x00u\x00\xa3\x00\x00\x01\x03\x17\x1254#\"\x01\x16\x1767.\x02\x01\x14\x13632\x17\x03&#\"\x06\x03\x14\x1e\x0132654'.\x03#\"\x06\x03\x14\x17\x1e\x013276\x114.\x01'&$#\"\a\x06\x15\x14\x1e\x047232\x17\x16\x17\x06\a\x06\a\x0e\x01\x15\x14\x16\x15\a\x06\x15&'\x06#\x16\x15\x14\x06#\"&547\x16\x17\x1632654&#\"\x06\a467&54632\x17\x0254632\x13\x16\x17>\x0532\x16\x15\x14\x03\x1e\x03\x15\x14\x02\x0e\x01#\"'&\x02\x03\xb9ru\xa5&9\xfe\x8c\x1e\x03%\"\f*#\xfe͟\x11 \x0fO%GR\x9f=O&\x0e^\xaa\xfc\x98op\x95\xda\x04\x86\xfe\xb8\x15\x01\xc3C8\xfcpP\b*\x19\x02\a\a\x03\x85b\xfeY\n\x05\x01_\xdc#\xfc\xf5$\xa6\x8c\x1a\x0e\x18N Pb@6\xfe\x9d)?\x91\xa4\xaa\xa9\x01\x02+0L\x1215\v\x05\x1e\"4\x1c\x13\x04\x04\x02\x13\x13$\x1c\x1a\x16\x18.\x88E\x1fs\x1e\f\f\x02\n\xce\x02\a\x0e5I\x9cQ\"!@\fh\x11\f\"\xdeY7e|\x1aJ\x1e>z\x0f\x01\xceiPe\xfd\xbb\x11\x06\x10\u007fn\x91eHbIl\xfeF\x0f>^]@\x96\xfe\xfc\xben*9\x01\r\x00\x00\x00\x00\x04\x00\x00\xff\x80\b\x00\x05\x80\x00\x1a\x006\x00[\x00_\x00\x00\x013\x0e\x01#\"&54632\x16\x17#.\x01#\"\x06\x15\x14\x1e\x0232%3\x0e\x01#\"&54632\x16\x17#.\x01#\"\x06\x15\x14\x1e\x02326%4&'.\x02'&! \a\x0e\x02\a\x0e\x01\x15\x14\x16\x17\x1e\x02\x17\x16\x04! 7>\x027>\x01\x13\x11!\x11\x03\x11\xcf\x0e\xa9\x82\xa2\xb9\xba\x8c\x94\xa8\r\xcb\x05=39?\n\x1a6'_\x02\xd6\xce\x0e\xa8\x82\xa2\xb9\xba\x8c\x94\xa8\r\xcc\x04>29?\n\x1a5'17\x01m\x1f-\x06\x0f\x1c\x02V\xfd\x9d\xfd\x8fU\x05\x19\x11\x06-\x1e\x1e-\x06\x12\x17\x06,\x01\x87\x01\x13\x02bW\x05\x18\x11\x05.\x1e\xc0\xf8\x00\x02\x10\x9e\xb5\xe8\xc8\xc2뮠@Fyu0HC$\x8b\x9e\xb5\xe8\xc8\xc2뮠@Fyu0HC$L\xb6\xcf\xc8=\b\f\x12\x02??\x04\x0f\r\b<\xc7\xd1\xd0\xc7=\b\x0e\x0e\x05! A\x04\x0e\x0e\t<\xc6\x03\xcb\xfa\x00\x06\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x05`\x05\x80\x00\x1d\x00;\x00\x00\x01\x11\x14\x06+\x01\"&5\x114&#!\x11\x14\x06+\x01\"&5\x11463!2\x1e\x01\x01\x11\x14\x0e\x01#!\"&5\x1146;\x012\x16\x15\x11!265\x1146;\x012\x16\x03\xe0\x12\x0e\xa0\x0e\x12\xa0p\xfe\xf0\x12\x0e\xa0\x0e\x12\x12\x0e\x01Ї\xe4\x85\x01\x80\x85\xe4\x87\xfe0\x0e\x12\x12\x0e\xa0\x0e\x12\x01\x10p\xa0\x12\x0e\xa0\x0e\x12\x03\x90\xfe\x10\x0e\x12\x12\x0e\x01\xf0p\xa0\xfb\x80\x0e\x12\x12\x0e\x05@\x0e\x12\x85\xe4\x01I\xfc\x90\x87\xe4\x85\x12\x0e\x03\xc0\x0e\x12\x12\x0e\xfd\x00\xa0p\x03p\x0e\x12\x12\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00>\x00S\x00c\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x0554&+\x01\"\a&+\x01\"\x06\x1d\x01\x14;\x012=\x0146;\x012\x16\x1d\x01\x14;\x012=\x0146;\x012\x16\x1d\x01\x14;\x012%54&#!\"\x06\x15\x11\x14;\x012=\x01\x16;\x0126\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\x1f\x1b\x18\xca\x18\x1c\x1c\x18\xca\x18\x1b\xfe\x16A5\x85D\x1c\x1cD\x825A\x157\x16\x1b\x19^\x18\x1c\x156\x16\x1c\x18a\x18\x1b\x167\x15\x02MB5\xfe\xf85B\x167\x15\x1f?\xbf5B~\x88`\xfb\xd0`\x88\x88`\x040`\x88\x02\xb6r\x18\x1c\x1c\x18r\x18\x1c\x1c\xfe\xfa5A44A5\xfa\x16\x16\xe6\x18\x1c\x1c\x18\xe6\x16\x16\xe6\x18\x1c\x1c\x18\xe6\x16v\x9a5AA5\xfef\x15\x15\xb4*A\x02\x9d\xfb\xd0`\x88\x88`\x040`\x88\x88\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x02\x00\t\x00\x19\x00\x00\x01!\x1b\x01!\x01!\x01!\t\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x93\xfeړ\xe9\x017\xfe\xbc\xfeH\xfe\xbc\x017\x01\u007f\x02j\xaav\xfc@v\xaa\xaav\x03\xc0v\xaa\x01\xc2\x02'\xfc\x97\x04\x00\xfc\x00\x01:\x02\xa6\xfc@v\xaa\xaav\x03\xc0v\xaa\xaa\x00\x00\x00\x00\x17\x00\x00\xff\x00\b\x00\x06\x00\x00M\x00U\x00a\x00h\x00m\x00r\x00x\x00\u007f\x00\x84\x00\x89\x00\x91\x00\x96\x00\x9c\x00\xa0\x00\xa4\x00\xa7\x00\xaa\x00\xaf\x00\xb8\x00\xbb\x00\xbe\x00\xc1\x00\xcb\x00\x00\x01\x14\x06\a\x03\x16\x15\x14\x06\a\x03\x16\x15\x14\x06#\"'!\x06\"'!\x06#\"&547\x03.\x01547\x03.\x015467\x134&547\x13&54632\x17!62\x17!632\x16\x15\x14\a\x13\x1e\x01\x15\x14\a\x13\x1e\x01\x01!\x01#\x01!62\x01\x16\x15\x14\a\x13\x177\x11'\x06\a\x01!\x17%!\x06\"\x0167'\a#7\x03\x01\x17\x017\x13!\x016\x053\x01!\x11\x17\x16\x03!7\x01\x0f\x0135\a\x16\x11\x14\x16\x15\x14\a\x17\x117\x11\x17\x01/\x01\a\x117'\x06%#\x05\x17\x15\t\x02%'\x11\x05\a3\x01\x17\x13/\x02&=\x01\x03&'\t\x025\x03\x13#\x13\x01\a?\x01\x13&547\v\x01\x176\b\x00\x1a\x14\xcd\x03\x19\x14\xc1\x03!\x18\x19\x10\xfep\x114\x11\xfeq\x11\x1a\x17\"\x04\xc1\x14\x19\x03\xce\x14\x19\x1b\x14\xc7\x01\"\xd1\x04\"\x17\x1a\x12\x01\x8c\x106\x10\x01\x8e\x12\x1a\x17\"\x04\xcf\x17 \a\xbb\x13\x19\xfc'\x01\x85\xfe\xaa\x8f\xfe\xaa\x01h\x12*\xfc[\x01\x02\xd0\x0f\xbc\xbb\r\x10\x02\xa8\xfe|\xbe\x02*\xfe\xe8\x10,\x02\xaf\x01\x04@\x11\x1e\x16\xfc\xfe\xd8?\x01w\x10A\xfeU\x01M\b\xfcp\x05\x01V\xfe\x8b\x04\x0e\x12\x01\x92@\xfe˝\xc1\xa3\xa8\x04\x01\b\xab\x1e\x99\x01)\xdf\xdf\x04Ϳ\x06\x03w\x10\xfd\x93\xd5\xfe\xd7\x017\x01(\xfd{\x88\x01\xe6*U\x01%\xee\x84\x03\x01\x16\b\xd8\x05\b\xfeK\x016\xfc\xc0\xa3\xa3\xa3\xa3\x04=0\x82(\xcf\x02\x03\xab\x81M\x05\x02\x81\x15\x1f\x04\xfe\x9c\t\t\x14\x1f\x04\xfe\xaf\b\b\x17\"\x12\x14\x14\x14!\x18\b\f\x01O\x04\x1f\x14\t\t\x01d\x05\x1f\x14\x15\x1f\x04\x01X\x01\x04\x01$\x0f\x01k\n\b\x18!\x15\x15\x15\x15!\x18\x06\f\xfe\x9a\x01!\x16\r\x0e\xfe\xbc\x04\x1f\xfc\xcd\x01b\xfe\x9e\x10\x03\x1c\x04\t\n\x05\xfe\x98\x06\xc7\x01[\xc2\b\x02\x01\xc0\xc8\xc8\x10\xfbT\x06\x05DOi\x01\n\xfe\xcd@\xfe\x90\x1c\x016\xfe\xa9\x04\x0f\x01b\xfe\xb1\x06\x05\x01xB\x01A\xa6ݽ\xb1\b\x035\x01\x02\x01\x10\r\xb1\x01\r\v\xfeɝ\x01:\xec\xde\b\xfe\xf8J\xc9\x02\f\xe0\xe1+\xfe\xc5\xfe\xc1\x013\x0f\x8d\xfe\xe4\xdd,\x01\x88\xfb\x02p\x05\x01\x15\r\x10\x02\x01x\x01\x04\xfe1\xfe\xb9\x01\xf6\xdf\xfe\xe6\xfc\x89\xfe\xe5\x01\x1b\xe3\xe3F\x01i\n\x04\x01\x0f\x01(\xfd\x9cR\x03\x00\x02\x00\x00\xff\x00\x05\x80\x06\x00\x00\r\x00\x1b\x00\x00\x11463!\x01\x11\x14\x06#!\"&5%'\x114&#!\"\x06\x15\x11\x14\x163\xb7\x83\x02\xe6\x01`\xb7\x83\xfc\xf4\x83\xb7\x04а@.\xfe\x1c.@A-\x03X\x83\xbf\x01f\xfaB\x84\xbe\xbe\x84$\xb4\x01\xa9.BB.\xfe\x14.C\x00\x00\x04\x00\x00\xff\x83\x06\x00\x05}\x00\n\x00\x14\x00\x1e\x00)\x00\x00\x01\x04\x00\x03&54\x12$32\x05\x16\x17\x04\x00\x03&'\x12\x00\x01\x12\x00%\x16\x17\x04\x00\x03&\x05&'\x06\a6\x007\x06\a\x16\x03\xa6\xfe\xc3\xfe\"w\x14\xcd\x01`\xd0R\x01d]G\xfe{\xfd\xc5o]>p\x026\xfe\xa3s\x02\x11\x01c(\x0e\xfe\xdc\xfe@wg\x03\xcf\xc1\xae\x87\x9bm\x01J\xcc\x15PA\x05jy\xfe\x1d\xfe\xc1YW\xd0\x01a͊AZq\xfd\xc1\xfe{HZ\x01\x82\x02:\xfb<\x01d\x02\x14v\\gx\xfe>\xfe\xdb\x0e\x142AT\x17\xcd\x01Kn\x98\x84\xaf\x00\x00\x03\x00\x00\xff\x80\b\x00\x04\xf7\x00\x16\x00+\x00;\x00\x00\x01\x13\"'&#\"\a&#\"\a\x06+\x01\x136!2\x1763 \x012\x16\x17\x03&#\"\a&#\"\a\x03>\x0232\x1767\x03\x06\a&#\"\a\x03>\x0132\x176\x17\ae\x9b\x83~\xc8\xc1└\xe2\xc1Ȁ|\x05\x9b\xe0\x01\x02隚\xe9\x01\x02\xfe\xf1\x81Ν|\xab\xc5\xe0\x96\x96\xe0ū|iy\xb0Zʬ\xac\xf27Ӕ\x98ް\xa0r|\xd1uѥ\xac\xca\x04x\xfb\b9[\x94\x94[9\x04\xf8\u007fjj\xfb\xa69A\x03\xfdN\x8d\x8dN\xfc\x03+,#ll\"\x03\x8b\x04\x97\x9bB\xfcS32fk\x05\x00\x00\x05\x00\x00\xff\xa5\b\x00\x05[\x00\x0f\x00\x1f\x00/\x00?\x00\\\x00\x00%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x14\x06#!\"&5467&54632\x176$32\x1e\x01\x15\x14\a\x1e\x01\x05\xdc\x1e\x14]\x14\x1e\x1e\x14]\x14\x1e\xfe\xe4\x1e\x14e\x14\x1e\x1e\x14e\x14\x1e\xfe\xdc\x1e\x14e\x14\x1e\x1e\x14e\x14\x1e\xfe\xdc\x1e\x14e\x14\x1e\x1e\x14e\x14\x1e\x05\x88\xec\xa6\xfb$\xa6\xec~i\n\xa1qfN-\x01*\xbd\x95\xfc\x93\x0e\x87\xac\xa5\x02\xdd\x15\x1e\x1e\x15\xfd#\x14\x1e\x1e\x14\x02\x13\x14\x1e\x1e\x14\xfd\xed\x14\x1e\x1e\x14\x01\xad\x14\x1e\x1e\x14\xfeS\x14\x1e\x1e\x14\x01j\x14\x1e\x1e\x14\xfe\x96\x14\x1e\x1e\xa6\xa6\xec\xec\xa6t\xc52\"'q\xa1C\xb7\xea\x93\xfc\x95B8!\xdb\x00\x00\x00'\x00\x00\xff>\x06\x00\x06\x00\x00\x04\x00\t\x00\r\x00\x11\x00\x15\x00\x19\x00\x1d\x00!\x00%\x00)\x00-\x001\x005\x009\x00=\x00A\x00E\x00I\x00M\x00Q\x00U\x00Y\x00]\x00a\x00g\x00k\x00o\x00s\x00w\x00{\x00\u007f\x00\x85\x00\x89\x00\x8d\x00\x91\x00\x95\x00\x99\x00\xa5\x00\xd5\x00\x00\x11!\x11\t\x01%\x11!\x11\t\x015!\x15\x13\x15#5\x17\x15#5\x17\x15#5\x17\x15#5\x17\x15#5\x177\x17\a\x177\x17\a\x177\x17\a\x177\x17\a?\x01\x17\a?\x01\x17\a?\x01\x17\a?\x01\x17\a\x01\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5\x01\x15#53\x157\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5\x175#53\x15\a53\x15\a53\x15\a53\x15\a53\x15\a53\x15%\"&54632\x16\x15\x14\x06\x01\x14\x1e\x026\x16\x15\x14#\"'#\a\x1632>\x0254.\x01\x06&54>\x0132\x16\x1737.\x06#\"\x0e\x02\x06\x00\xfc\xf8\xfd\b\x05\x9c\xfa\xc8\x02\x95\x02\xa3\xfa\xc8Q%%%%%%%%%?\x0fi\x0f\x1f\x0fi\x0f\x1e\x0fi\x0f\x1f\x0fh\x0fOi\x0fixi\x0fiyi\x0fixi\x0fi\xfcAr\x01\x14s\x01\x15s\x01\x14r\x01\x14r\x01\x14s\x01\x15s\x01\x14r\xfb\xb8%s\xa2s\x01\x15s\x01\x14r\x01\x14r\x01\x14s\x01\x15s\xf0Ns%%%%%%%%%%\xfd\x88\x81\xb8\xb8\x81\x82\xb7\xb7\xfe\xd9'\x0232\x16\x15\x14\a\x06\x04#\".\x0154\x0032\x1e\x0532654&#\"\x06#\"&54654&#\"\x0e\x02#\"&547>\x0132\x16\x15\x14\a6\x05\x96\x01\x04\x94\xd2ڞU\x9azrhgrx\x98S\x9a\xc3Пd\xd8U\x05 \x1c\b\x0e\x15\x017\x03#\"&463!2\x1e\x04\x17!2\x16\x04\xc0&\x1a\x80&4&\x80\x1a&&\x1a\x80&4&\x80\x1a\xfd\xe6KjKKj\x03\xcbKjKKj\xcb \x19\xfb\xec\x01\a\x05\x18\x03\x98\x1a&&\x1a\xfc\x00\x1a&\x16%\x02\xb1\xcc\x1a&&\x1a\x01\x00\x10\x19\x0f\v\x04\a\x01\x04\xb1\x1a&\x03&4&\x80\x1a&&\x1a\x80&4&\x80\x1a&&\x1a\x80\xfd5jKKjKKjKKjK\x03\xc0\xfe\x00\x18%\x03z\a\x1d\x18\n\x100&4&&\x1a\x0e3D\x04\x037&4&\r\x12\x1f\x16%\a&\x00\x00\x00\x00\x04\x00\x00\xff\x80\x06\x80\x05\x00\x00\x17\x00\x1f\x00'\x00S\x00\x00\x004&\"\x0f\x01\x114&\"\x06\x15\x11'&\"\x06\x14\x17\x01\x1627\x01\x00\x14\x06\"&462\x04\x14\x06\"&462\x13\x11\x14\x06\a\x05\x1e\x02\x15\x14\a!2\x16\x14\x06#!\"&54>\x017\x03#\"&463!2\x1e\x04\x17!2\x16\x05\x00&4\x13\x93&4&\x93\x134&\x13\x01\x00\x134\x13\x01\x00\xfd\x93KjKKj\x03\xcbKjKKj\xcb \x19\xfb\xec\x01\a\x05\x18\x03\x98\x1a&&\x1a\xfc\x00\x1a&\x16%\x02\xb1\xcc\x1a&&\x1a\x01\x00\x10\x19\x0f\v\x04\a\x01\x04\xb1\x1a&\x03&4&\x13\x92\x01%\x1a&&\x1a\xfeے\x13&4\x13\xff\x00\x13\x13\x01\x00\xfd\"jKKjKKjKKjK\x03\xc0\xfe\x00\x18%\x03z\a\x1d\x18\n\x100&4&&\x1a\x0e3D\x04\x037&4&\r\x12\x1f\x16%\a&\x00\x00\x00\x00\a\x00\x00\xff\x00\b\x00\x05\x80\x00\x02\x00\x05\x00\t\x00\f\x00\x10\x00\x14\x00&\x00\x00\x13\t\x03!'\x13!\t\x02!%!\x03!\x01!\x01!%\x01\x16\x06\a\x01\x06\"'\x01.\x017\x0163!2\xd4\x02o\xfe\xd4\x01\xe9\x01]\xfdF\x89\xcc\xfe\xfa\xfe\xe0\x03\xfd\x02o\xfe\xbd\xfc\xc2\x02\xaa\xcc\xfe\xee\x02o\x01Z\xfe\xe0\xfe\xfa\x01Y\x01\x80\x0e\x02\x10\xfc@\x12:\x12\xfc@\x10\x02\x0e\x01\x80\x12!\x04\x80!\x03\x00\xfdg\x02\x99\xfc\xfc\x03\x04\x80\x01\x80\xfe\x80\xfc\xe7\x02\x99\x80\x01\x80\xfe\x80\x01\x80f\xfe\x00\x12/\x11\xfc\x00\x14\x14\x04\x00\x11/\x12\x02\x00\x1a\x00\x03\x00\x13\xff\x00\a\xed\x06\x00\x00I\x00\x97\x00\xa0\x00\x00\x0562\x1f\x01\a'\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x017\x17762\x1f\x01762\x1f\x01762\x1f\x01762\x1f\x01762\x1f\x01762\x1f\x01%\x06\"/\x017\x17762\x1f\x017\x11\x03&6?\x01\x1135!5!\x15!\x153\x11\x17\x1e\x01\a\x03\x11762\x1f\x01762\x1f\x01\a'\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\x01\x15%\x055#5!\x15\a\x13\x134\x13\x80ZSS\x126\x12SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13\x80ZSS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13S\xfa-\x134\x13\x80ZSS\x134\x13S@\xd2\x11\x14\x1e\xb1\x80\x01\x00\x01\x00\x01\x00\x80\xb1\x1e\x14\x11\xd2\x13\x134\x13SS\x134\x13\x80ZSS\x126\x12SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13S\x01@\x01\x80\x01\x80\x80\xfe\x00\x13\x13\x13\x80ZSS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13\x80ZSS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13Sy\x13\x13\x80ZRR\x13\x13R@\x01%\x01:\x1a=\n:\x01+\x80\x80\x80\x80\xfe\xd5:\n=\x1a\xfe\xc6\xfe\xdb\x12\x13\x13RR\x13\x13\x80ZSS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13S\x04\x1a\x80\x80\x80\x80\x80\x80\x00\x00\x00\x04\x00\x00\xff\x80\x05\x80\x06\x00\x00\x03\x00\a\x00C\x00v\x00\x00!\x13/\x01\x01\x13\x0f\x01\x01&'&#\"\a\x06\"'&#\"\a\x06\a\x16\x17\x1e\x01\x17\x1e\t32>\x03;\x012\x1e\x0332>\b7>\x0176\x01\x14\x06#!\"&54>\x037'3&547&547>\x017632\x162632\x17\x1e\x01\x17\x16\x15\x14\a\x16\a3\a\x1e\x03\x02@``\x80\x01\x80\x80\x80`\x01\x00\x02\x02\nVFa\a\x1c\aaFV\n\x02\x02\x02\x02\x02\v\x02\x02\v\x03\f\x05\r\v\x11\x12\x17\r$.\x13\n\r\v\f\v\r\n\x13.$\r\x17\x12\x11\v\r\x05\f\x03\v\x02\x02\v\x02\x02\x01\xa2\x92y\xfc\x96y\x92\t\x1d.Q5Z\xd6\x16\x02\xc2\xd2\x11E$ ,\x1el\x90*%>>%*\x90>*98(QO\xe1!\u007f\xa0\x8f\x00\x03\x00\x00\x00\x00\b\xfd\x05\x00\x00L\x00\\\x00p\x00\x00\x01\x16\x0e\x02'.\x01'&67'\x0e\x01\x15\x14\x06#!#\x0e\x01#\"\x00\x10\x0032\x177&+\x01\"&46;\x012\x1e\x02\x17!3'#\"&7>\x01;\x012\x1f\x0176;\x012\x16\x1d\x01\x14\x06+\x01\x176\x17\x1e\x01\x01267!\"'&7\x13&#\"\x06\x10\x16(\x016\x10&#\"\a\x13\x16\x06\a\x06#\"'\x03\x06\x15\x14\b\xfd\fD\x82\xbbg\xa1\xed\x10\fOOG`n%\x1b\xff\x00E\x17\xfc\xa8\xb9\xfe\xf9\x01\a\xb9LL\x18{\xb5@\x1a&&\x1a\x80N\x86c,\x1d\x02\x00sU\xde\x1e&\x05\x04&\x18\xfd!\x14Fr\x13\x1be\x1a&&\x1a\xb3s\x83\x90\x8f\xca\xf8\xd4s\xb0\x17\xfe\xc6#\x14\x12\x11\x93/,\x84\xbc\xbc\x05\x80\x01\b\xbc\xbc\x84<=\xae\x0f\n\x16\x0f\x15#\x12\xae]\x01\xf4g\xbf\x88L\a\v\xe4\xa0o\xc7GkP\xe4\x82\x1b'\xa4\xdc\x01\a\x01r\x01\a\x1b-n&4&\x1b2\x1d\x16\x80-\x1e\x17\x1e\x1cir\x13&\x1a\x80\x1a&\xac?\x1b\x1a\xd9\xfd\xfb\x91o\x1f \x1f\x01\x15\r\xbc\xfe\xf8\xbc\xbc\x01\b\xbc\x18\xfe\xfc\x174\x0e\v\x1d\x01\x04_\x82\x84\x00\x00\x03\x00\x00\xff\x00\x05\x80\x05\xe0\x005\x00O\x00W\x00\x00!\x14\x0e\x02 .\x0254>\x0276\x16\x17\x16\x06\a\x0e\x04\a\x1e\x042>\x037.\x04'.\x017>\x01\x17\x1e\x03\x01\x11\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&5\x11463!2\x16\x02\x14\x06\"&462\x05\x80{\xcd\xf5\xfe\xfa\xf5\xcd{BtxG\x1a,\x04\x05\x1f\x1a:`9(\x0f\x01\x030b\x82\xbfԿ\x82b0\x03\x01\x0f(9`:\x1a\x1f\x05\x04,\x1aGxtB\xfe\x80&\x1a@&\x1a\xff\x00\x1a&@\x1a&K5\x01\x805K`\x83\xba\x83\x83\xba?e=\x1f\x1f=e?1O6#\f\x05\x1f\x1a\x1a,\x04\n\x1b\x18\x17\x10\x04\v\x1f#\x1e\x14\x14\x1e$\x1f\f\x04\x0e\x18\x17\x1b\n\x04,\x1a\x1a\x1f\x05\f#6O\x03O\xfe\x80\x1a&\xfe\x80\x1a&&\x1a\x01\x80&\x1a\x01\x805KK\x01\xa8\xba\x83\x83\xba\x83\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00\x1b\x00?\x00\x00\x01!\x0e\x01\x0f\x01\x01\x06\"'\x01&'!267\x1b\x01\x1e\x013267\x13\x17\x16\x01\x14\a!'.\x01\a\x06\a\v\x01.\x01\"\x06\a\x03!&54632\x1e\x02\x17>\x0332\x16\x05\x00\x011\x05\n\x04\x03\xfd\x91\x124\x12\xfd\x90\x05\x10\x01q\x16#\x05F\xbe\x06\"\x16\x15\"\x06\x928\x12\x02'g\xfe\x8fo\b#\x13-\v\x81\xc4\x06#,\"\x05t\xfeYg\xfe\xe0>\x81oP$$Po\x81>\xe0\xfe\x02\x00\x06\t\x03\x04\xfd\xa8\x12\x12\x02Z\x02\x12\x1b\x15\x01\x19\xfde\x14\x1a\x1a\x14\x01\xe5p#\x01\xac\x91\x9b\xdd\x11\x14\x02\x05)\xfeR\x02\xae\x14\x1a\x1b\x15\xfe0\x9b\x91\xdc\xf8+I@$$@I+\xf8\x00\x00\x02\x00\x02\xff\x00\x04\x80\x05\xfc\x00+\x003\x00\x00\x01\x14\x00\a\x1132\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x01\x11.\x01\x027>\x0276\x04\x12$\x10\x00 \x00\x10\x00 \x04\x80\xfe\xd9\xd9\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x96\xf3\x81\f\v\x8bᅪ\x01*\xae\xfc\x00\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x03\xc0\xdd\xfe\xb9\x18\xfe\xfc\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x01\x04\x10\xae\x01\x12\x9b\x86\xe6\x92\x0f\x13\x92\xfe\xea\x12\xfe\x8e\xfe\xf9\x01\a\x01r\x01\a\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00'\x00/\x00\x00\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\x01\x16\x15\x14\x0e\x02\".\x024>\x0232\x17\x01!\"&=\x01463\x00 \x00\x10\x00 \x00\x10\x05\xc0\x1a&\x12\x0e@\x0e\x12\xfe\x82~[\x9b\xd5\xea՛[[\x9b\xd5u˜\x01~\xfe\xfb\x0e\x12\x12\x0e\xfdg\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\x80&\x1a\xfe`\x0e\x12\x12\x0e\x01\x06\xfe\x81\x9c\xcbu՛[[\x9b\xd5\xea՛[~\x01~\x12\x0e@\x0e\x12\xfa\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x02\x00\x00\xff\x00\x04\x80\x06\x00\x00=\x00E\x00\x00\x01\x16\x12\x15\x14\x00\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015&\x0054\x127&'&6;\x012\x17\x1e\x012676;\x012\x16\a\x06\x00 \x00\x10\x00 \x00\x10\x03>\x91\xb1\xfe\xd9\xd9`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\xd9\xfeٱ\x91\xa5?\x06\x13\x11E\x15\b,\xc0\xec\xc0,\b\x1d=\x11\x13\x06?\xfd\xa4\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x04\xc4H\xfe\xeb\xa7\xdd\xfe\xb9\x18\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x18\x01Gݧ\x01\x15H`\xb1\x10\x1b\x14j\x82\x82j\x14\x1b\x10\xb1\xfb\xdc\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x02\x00\x02\xff\x00\x05\x80\x06\x00\x00B\x00J\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x16\x15\x14\x00\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015.\x01\x0276\x0076\x16\x17%#\"&5\x00 \x00\x10\x00 \x00\x10\x04\x00\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfe~\xfe\xd9\xd9`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x95\xf3\x82\f\x10\x01 \xcbv\xdcX\x00\xff\x86\x0e\x12\xfd\x87\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\xe0\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff\x9e\xc9\xdd\xfe\xb9\x18\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x10\xae\x01\x11\x9b\xcc\x01+\x17\x0eBF\xfe\x12\x0e\xfb`\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x00\xff\x00\x06\x80\x06\x00\x00k\x00s\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x16\x15\x14\x00\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015&\x00547'\a\x0e\x01/\x01.\x01?\x01'\x15\x14\x06+\x01\"&5\x11463!2\x16\x1d\x01\x14\x06+\x01\x177>\x01\x1f\x01\x1e\x01\x0f\x01\x176 \x17%#\"&5\x00 \x00\x10\x00 \x00\x10\x05\x00\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfe~\xfe\xd9\xd9`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\xd9\xfe\xd9~4e\t\x1a\n0\n\x01\tio\x12\x0e@\x0e\x12&\x1a\x01 \x0e\x12\x12\x0e\x85jV\t\x1a\n0\n\x01\tZ9\x9e\x01\x92\x9e\x00\xff\x86\x0e\x12\xfd\x87\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\xe0\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff\x9e\xc9\xdd\xfe\xb9\x18\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x18\x01G\xddɞ5o\n\x01\b,\b\x1b\nsp\x86\x0e\x12\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12k^\n\x01\b,\b\x1b\nc8~~\xfe\x12\x0e\xfb`\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x05\x00\x02\xff\x00\x06\xfe\x05\xfd\x008\x00>\x00K\x00R\x00_\x00\x00\x01\x16\x02\x06\a\x1132\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01!\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x01\x11.\x01\x0276\x0076\x176\x17\x16\x00\x016\x10'\x06\x10\x0327&547&#\"\x00\x10\x00\x01\x11&'\x06\a\x11\x012\x00\x10\x00#\"\a\x16\x15\x14\a\x16\x06\xfe\f\x81\xf3\x96\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\xfe\x00\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x96\xf3\x81\f\x11\x01'\xcdΫ\xab\xce\xcd\x01'\xfc\x93\x80\x80\x80\xc0sg\x9a\x9ags\xb9\xfe\xf9\x01\a\x02\xf9\x89ww\x89\x02@\xb9\x01\a\xfe\xf9\xb9sg\x9a\x9ag\x03\xef\x9b\xfe\xee\xae\x10\xfe\xfc\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x01\x04\x10\xae\x01\x12\x9b\xce\x01-\x13\x15ss\x15\x13\xfe\xd3\xfdʃ\x01l\x83\x83\xfe\x94\xfe\xf69\xa5\xe2\xe0\xa79\xfe\xf9\xfe\x8e\xfe\xf9\xfe\x80\x01\x04\x0fOO\x0f\xfe\xfc\x01\x80\x01\a\x01r\x01\a9\xa7\xe0\xe2\xa59\x00\x00\x04\x00\x01\xff\x06\a\x80\x06\x00\x00F\x00P\x00^\x00l\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x1e\x01\a\x06\x00\a\x06$'.\x037>\x0276\x16\x17%#\"&=\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x16\x17\x16\x17%#\"&5\x014'\x0e\x01\x15\x14\x17>\x01%\x14\x16\x17&54\x007.\x01#\"\x00\x012\x0054&'\x16\x15\x14\x00\a\x1e\x01\x06\x00\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfeL?\x16\x1f\xfe\xf2\xb7\xd2\xfe\xa3CuГP\b\t\x8a\xe2\x87v\xdbY\x00\xff\x86\x0e\x12\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfe;\"\xb6\x92\x00\xff\x86\x0e\x12\xfe\x00\x04\xa2\xda\x04\xa2\xda\xfc\x80ޥ\x03\x01\x0e\xcb5݇\xb9\xfe\xf9\x03\xc0\xb9\x01\aޥ\x03\xfe\xf2\xcb5\xdd\x04`\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff_\ue036\xfe\xfc\x1a\x1dڿ\x06g\xa3\xdew\x87\xea\x95\x0f\x0eBF\xfe\x12\x0e@\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xffJ_\ts\xfe\x12\x0e\xfe\xa0\x14&\x19\xfa\xa7\x14&\x19\xfa\xa7\xa8\xfc\x17\x1d\x1e\xd2\x01?%x\x92\xfe\xf9\xfc\a\x01\a\xb9\xa8\xfc\x17\x1c\x1f\xd2\xfe\xc1%x\x92\x00\x04\x00\x06\xff\x00\b\x00\x06\x00\x00J\x00P\x00\\\x00h\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x1e\x01\a\x06\x00\a\x06'\x06\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015.\x01\x0276\x0076\x17632\x17%#\"&5\x016\x10'\x06\x10\x00\x10\x00327&\x107&#\"\x012\x00\x10\x00#\"\a\x16\x10\a\x16\x06\x80\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfeL?\x16 \xfe\xf7\xb5ߺu\x8b`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x9b\xf9}\x17\x19\x01\r\xba\u0e92\xaeɞ\x00\xff\x86\x0e\x12\xfd\x00\x80\x80\x80\xfd\x80\x01\a\xb9ue\x9a\x9aeu\xb9\x039\xb9\x01\a\xfe\xf9\xb9ue\x9a\x9ae\x05\xe0\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff_\ue034\xfe\xfc\x1b\"|N\x0f\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x11\xb9\x01\"\xa2\xbb\x01\x0f\x1d\"|a~\xfe\x12\x0e\xfb\xe7\x83\x01l\x83\x83\xfe\x94\x01o\xfe\x8e\xfe\xf99\xa7\x01\xc0\xa79\xfc\x80\x01\a\x01r\x01\a9\xa7\xfe@\xa79\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00;\x00C\x00\x00\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\a\x17\x16\x14\x0f\x01\x06\"/\x01\a\x16\x15\x14\x0e\x02\".\x024>\x0232\x177'&4?\x0162\x1f\x017!\"&=\x01463\x00 \x00\x10\x00 \x00\x10\x05\xc0\x1a&\x12\x0e@\x0e\x12Ռ\t\t.\t\x1a\n\x8cN~[\x9b\xd5\xea՛[[\x9b\xd5u˜N\xac\t\t.\t\x1a\n\xac\xd5\xfe\xfb\x0e\x12\x12\x0e\xfdg\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\x80&\x1a\xfe`\x0e\x12\x12\x0e\x01\x06\u058c\n\x1a\t.\t\t\x8dO\x9c\xcbu՛[[\x9b\xd5\xea՛[~N\xac\n\x1a\t.\t\t\xac\xd5\x12\x0e@\x0e\x12\xfa\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x02\x00\x02\xff\x04\x04\x80\x06\x00\x009\x00A\x00\x00\x01\x16\x00\x15\x14\x02\x04'.\x02'&\x12675#\"&=\x0146;\x015\a\x06\"/\x01&4?\x0162\x1f\x01\x16\x14\x0f\x01\x06\"/\x01\x1532\x16\x1d\x01\x14\x06+\x01\x02 \x00\x10\x00 \x00\x10\x02\x80\xd9\x01'\xae\xfe֪\x85\xe1\x8b\v\f\x81\xf3\x96\xa0\x0e\x12\x12\x0e\xa0\\\n\x1a\t.\t\t\xca\x134\x13\xca\t\t.\t\x1a\n\\\xa0\x0e\x12\x12\x0e\xa0\xf9\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x03|\x18\xfe\xb9ݧ\xfe\xea\x92\x13\x0f\x92憛\x01\x12\xae\x10\x84\x12\x0e@\x0e\x12\xa5\\\t\t.\t\x1a\n\xc9\x13\x13\xc9\n\x1a\t.\t\t\\\xa5\x12\x0e@\x0e\x12\xfb\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x04\x00\x00\a\x80\x04~\x009\x00A\x00\x00\x01\x16\x14\a\x01\x06\"/\x01&4?\x01!\x15\x14\x06+\x01\"&=\x01#\x06\x00#\"$\x027>\x0276\x04\x16\x173546;\x012\x16\x1d\x01!'&4?\x0162\x17\x00 \x00\x10\x00 \x00\x10\am\x13\x13\xfe\xda\t\x1b\t-\n\n\xb9\xfe\xda\x12\x0e@\x0e\x12\x84\x18\xfe\xb9ݧ\xfe\xea\x92\x13\x0f\x92憛\x01\x12\xae\x10\x84\x12\x0e@\x0e\x12\x01&\xb9\n\n-\t\x1b\t\xfb@\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x02m\x134\x13\xfe\xda\n\n-\t\x1b\t\xb9\xe0\x0e\x12\x12\x0e\xe0\xd9\xfeٮ\x01*\xaa\x85\xe1\x8b\v\f\x81\xf3\x96\xe0\x0e\x12\x12\x0e\xe0\xb9\t\x1b\t-\n\n\xfc\xed\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x00\xff\x00\x04\x80\x06\x00\x00\x17\x00\x1f\x00\x00\x01\x14\x00\a\x11\x14\x06+\x01\"&5\x11&\x0054>\x022\x1e\x02\x00 \x00\x10\x00 \x00\x10\x04\x80\xfe\xd9\xd9\x12\x0e@\x0e\x12\xd9\xfe\xd9[\x9b\xd5\xea՛[\xfd\a\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x03\xc0\xdd\xfe\xb9\x18\xfd\x9c\x0e\x12\x12\x0e\x02d\x18\x01G\xddu՛[[\x9b\xd5\xfd\xcb\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x00\x00\x00\x04\x80\x04\x80\x00\a\x00\x17\x00\x00\x00\x10\x00 \x00\x10\x00 \x00\x14\x0e\x02\".\x024>\x022\x1e\x01\x04\x00\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x01\x87[\x9b\xd5\xea՛[[\x9b\xd5\xea՛\x01\x87\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x025\xea՛[[\x9b\xd5\xea՛[[\x9b\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00$\x00\x00\x012\x16\x15\x11\x14\x06#!\x1137#546375&#\"\x06\x1d\x01#\x153\x11!\"&5\x11463\x05\xab#22#\xfey\xc7\x1e\xe5/Dz?s\x88\xa3\xc8\xc8\xfd!#22#\x05\x802#\xfa\xaa#2\x02S\xe8\x9488\x01\xcf\t\xa0\x92\xab\xe8\xfd\xad2#\x05V#2\x00\x00\x00\x01\x00\x00\xff\x80\x05\x00\x06\x00\x00L\x00\x00\x114>\x0332\x04\x16\x15\x14\x0e\x03#\"&'\x0e\x06\x0f\x01'&546\x127&54632\x16\x15\x14\x06\x15\x14\x1632>\x0454&#\"\x00\x15\x14\x1e\x02\x15\x14\x06#\"'.\x03K\x84\xac\xc6g\x9e\x01\x10\xaa&Rv\xacgD\x86\x1d\n$\v\x1e\x16*2%\x0e\t\x0f+Z\a hP=DXZ@7^?1\x1b\r۰\xc8\xfe\xf4\x19\x1d\x19\x1e\x16\x02\x0f3O+\x16\x03\xabl\xbf\x8eh4\x85\xfe\xa0`\xb8\xaa\x81M@8'\x93+c+RI2\x05\n\x9d\x1f\\\xe5\x01Z\x1eAhS\x92Q>B\xfa>?S2Vhui/\xad\xc1\xfe\xfd\xc7,R0+\t\x1cZ\x03\x0fRkm\x00\x00\x00\x00\x03\x00\x00\xffz\x06\x00\x05\x86\x00+\x00>\x00Q\x00\x00\x002\x16\x17\x16\x15\x14\a\x0e\x01#\"'.\x01'&7567632\x1632\x16\x17\x1e\x01\x15\x14\x06\x15\x14\x17\x16\x17\x16\x17\x1632\x032>\x024.\x02\"\x0e\x02\x15\x14\x17\a7\x16\x12 \x04\x16\x12\x10\x02\x06\x04#\"'\x05\x13&54\x126\x03\xcc\x1a\xa9\x05\x02\x11\x10n/9\x85b\x90LH\x01\x03G\x18\x1c\x06\x18\a\x13\x0f\b\b2E\x05\"D8_\f\n\x0fp\u007f\xe9\xa8dd\xa8\xe9\xfe\xe9\xa8dxO\xf2\x9e\"\x012\x01\x17\xcaxx\xca\xfe\xe9\x99ê\xfe_\x88lx\xca\x022X\t\x05\n!+'5>-\x92pkW\b[C\x16\x03\r\x15\x14\x88\a\x15I\n\a\bI@50\a\xfeOd\xa8\xe9\xfe\xe9\xa8dd\xa8\xe9\u007f˥\xe9Mh\x05fx\xca\xfe\xe9\xfe\xce\xfe\xe9\xcax^\x86\x01\x95\xb2ә\x01\x17\xca\x00\x00\t\x00\x00\x00\x00\a\x00\x05\x80\x00\x03\x00\a\x00\x0f\x00\x13\x00\x1b\x00#\x00'\x00+\x00/\x00\x007!5!\x11!5!\x004&\"\x06\x14\x162\x01!5!\x004&\"\x06\x14\x162\x124&\"\x06\x14\x162\x13\x11!\x11\x01\x11!\x11\x01\x11!\x11\x80\x04\x00\xfc\x00\x04\x00\xfc\x00\x06 8P88P\xfa\x18\x04\x00\xfc\x00\x06 8P88P88P88P\x98\xf9\x00\a\x00\xf9\x00\a\x00\xf9\x00\x80\x80\x01\x80\x80\xfd\x98P88P8\x04 \x80\xfd\x98P88P8\x028P88P8\xfd \xfe\x80\x01\x80\x02\x00\xfe\x80\x01\x80\x02\x00\xfe\x80\x01\x80\x00\x00\x03\x00\x00\xff\x80\b\x00\x05\x80\x00\a\x00+\x00N\x00\x00\x00 &\x106 \x16\x10\x01!2\x16\x1d\x01\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!\x1146;\x012\x16\x15\x01\x14\x163!\x15\x06#!\"&54>\x0532\x17\x1e\x01267632\x17#\"\x06\x15\x03_\xfe\xc2\xe1\xe1\x01>\xe1\x02@\x01`\r\x13\x13\r\xfe\xa0\x13\r\xc0\r\x13\xfe\xa0\r\x13\x13\r\x01`\x13\r\xc0\r\x13\xfd L4\x01\x00Dg\xfc\x96y\x92\a\x15 6Fe=\x13\x14O\x97\xb2\x97O\x14\x13\x84U\xdf4L\x02\x80\xe1\x01>\xe1\xe1\xfe\xc2\xfe\x9f\x13\r\xc0\r\x13\xfe\xa0\r\x13\x13\r\x01`\x13\r\xc0\r\x13\x01`\r\x13\x13\r\xfd\xc04L\xee2\x8ay5eud_C(\x11====\x11`L4\x00\x00\x00\x03\x00\x00\xff\x80\a\xf7\x05\x80\x00\a\x003\x00V\x00\x00\x00 &\x106 \x16\x10\x01\x17\x16\x15\x14\x0f\x01\x06#\"/\x01\a\x06#\"/\x01&54?\x01'&54?\x01632\x1f\x017632\x1f\x01\x16\x15\x14\a\x05\a\x06\x15\x14\x1f\x01\x06#!\"&54>\x0532\x17\x16 7632\x17\x0e\x01\x15\x14\x17\x03_\xfe\xc2\xe1\xe1\x01>\xe1\x02\xb5\xf9\t\t\x88\t\r\x0e\t\xf9\xf9\t\x0e\r\t\x88\t\t\xf9\xf9\t\t\x88\t\r\x0e\t\xf9\xf9\t\x0e\r\t\x88\t\t\xfd\x15\xb5%%S\x15\x17\xfc\x96y\x92\a\x15 6Fe=\x13\x14\x9a\x01J\x9a\x14\x13\x1c\x1d\x1c\x1a%\x02\x80\xe1\x01>\xe1\xe1\xfe\xc2\xfd\xdf\xf9\t\x0e\r\t\x88\t\t\xf9\xf9\t\t\x88\t\r\x0e\t\xf9\xf9\t\x0e\r\t\x88\t\t\xf9\xf9\t\t\x88\t\r\x0e\t\xf9\xb5%65%S\x03\x8ay5eud_C(\x11zz\x11\x06\x1b.!6%\x00\x03\x00\x00\x00\x00\b\x00\x05\x00\x00\x12\x00\x1a\x00$\x00\x00\x01!2\x16\x15\x11!\x11!\x11!\x1146;\x012\x16\x15\x004&\"\x06\x14\x162!54&#!\"\x06\x15\x11\x01\x00\x06\xc0\x1a&\xff\x00\xfa\x00\xff\x00&\x1a\x80\x1a&\x02@\x96Ԗ\x96\xd4\x05V\xe1\x9f\xfd@\x1a&\x02\x00&\x1a\xfe@\x01\x00\xff\x00\x04\xc0\x1a&&\x1a\xfe\x16Ԗ\x96Ԗ@\x9f\xe1&\x1a\xfe\x80\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06\x00\x06\x00\x00\x16\x00\x19\x00\x00\x01\x033\x15!\a!\x15!\t\x01!5!'!53\x03!\x01!\t\x01\x13#\x06\x00\xc0\xc0\xfe\xee7\x01I\xfee\xfe\x9b\xfe\x9b\xfee\x01I7\xfe\xee\xc0\xc0\x01\x00\x01C\x01z\x01C\xfe\x00l\xd8\x06\x00\xfe@\xc0\x80\xc0\xfc\xc0\x03@\xc0\x80\xc0\x01\xc0\xfd\x00\x03\x00\xfb@\x01\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x17\x00\x1f\x00#\x00\x00\x012\x04\x15\x11\x14\x06\a\x17\x16\x06#!\"&?\x01.\x015\x114$3\x12264&\"\x06\x14\x01\x11!\x11\x04@\xb9\x01\a\xfb\xb4\xd5\x10\x10\x16\xfb\xe0\x16\x10\x10մ\xfb\x01\a\xb9\xf0\xa0pp\xa0p\x03\x00\xfb\x80\x06\x00\xbb\x85\xfc\x80\x82\xb8\x05\xca\x0f((\x0f\xca\x05\xb8\x82\x03\x80\x85\xbb\xfa\xc0p\xa0pp\xa0\x01\xd0\x02\x00\xfe\x00\x00\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x17\x00\x1f\x00#\x00+\x00/\x00\x00\x012\x04\x15\x11\x14\x06\a\x17\x16\x06#!\"&?\x01.\x015\x114$3\x02264&\"\x06\x14\x01\x11!\x11\x00264&\"\x06\x14\x01\x11!\x11\x04@\xb9\x01\a\xfb\xb4\xd5\x10\x10\x16\xfb\xe0\x16\x10\x10մ\xfb\x01\a\xb9\xe2\x84^^\x84^\x02@\xfd\xe0\x03\xfe\x84^^\x84^\x01@\xfd\xc0\x06\x00\xbb\x85\xfc\x80\x82\xb8\x05\xca\x0f((\x0f\xca\x05\xb8\x82\x03\x80\x85\xbb\xfa\xe0^\x84^^\x84\x01\xc2\x02\x00\xfe\x00\xfd\xe0^\x84^^\x84\x01\xc2\x02\x00\xfe\x00\x00\x00\x00\x00\x04\x00\x00\xff\x8a\a\x00\x05v\x00\x12\x00\x15\x00\x1c\x00(\x00\x00\x01\x11\x14\x06#\"'%.\x015\x114632\x17\x01\x16\x17\t\x02\x11\x14\x06\"'%\x01\x14\x00\a\t\x01632\x17\x01\x16\x02U\x19\x18\x11\x10\xfe/\x15\x1d\x14\x13\x0e\x1e\x01\xff\x03@\x02\x16\xfd\xea\x04k\x1c0\x17\xfeG\x02\x19\xfd\xff,\xfez\x01D\x11#\x0e\f\x02\x1d\x04\x04[\xfbk\x19#\b\xe9\n/\x17\x04t\x14\x1c\x0f\xff\x00\x03g\xfc\x9e\x01\n\x02F\xfb\xe2\x19\x1f\r\xdc\x03\xe5\x03\xfc\xbfG\x02z\x02\x0f\x1c\x06\xfe\xf2\x02\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x0f\x00\x00\t\x01#\x03\x06\a'\x03#\x01\x113\x01\x11!\x11\x03)\x01\np\x9d\x18\x14*\x9bx\x01\ae\x02\xd7\xfa\x00\x02\x14\x01\xf3\xfe\xc80,\\\x018\xfe\x13\xfe\xbc\x04\xaa\xfa\x00\x06\x00\x00\x00\x18\x00T\xff\x06\b\xa4\x05\xff\x00\v\x00\x17\x00#\x00/\x00D\x00M\x00\xfc\x01\x06\x01\x12\x01\x1b\x01%\x012\x01<\x01G\x01Q\x01^\x01l\x01w\x01\xb3\x01\xc2\x01\xd9\x01\xe9\x01\xfe\x02\r\x00\x00\x05\x0e\x01\a\x06&'&676\x16\x05\x1e\x01\x17\x16676&'&\x067\x1e\x01\x17\x16654&'&\x06\x05\x0e\x01\a\x06&54676\x16\x013\"\a\x1e\x01\x15\x14\x06#\"'\x06\x15\x14\x163264&7.\x01\a>\x02\x1e\x01\x01\x16\a\x16\x15\x16\x0e\x01\a\x06&'\x04%\x0e\x01'.\x01767&76\x1767&76\x1767476\x176\x17\x16\x175\"'.\x01'&767>\x02\x16\x173\x16\x17\x16\x17>\x017&'&'47.\x01'.\x017676\x16\x17\x14\x1e\x03\x17\x16767&\a76767.\x04'$\x01\x16\x17\x1673>\x03?\x01>\x01\x17\x16\x17\x16\x06\a\x0e\x01\a\x15\x06\a\x06\a\x1e\x01\x1767673>\x01\x1e\x01\x17\x16\x17\x16\a\x0e\x01\a\x06#\x14\a676\x176\x17\x16\x15\x16\x176\x17\x16\a\x16\x176\x01\x14\a\x16\x176&'&\x06\a\x1e\x01\a6767.\x01'\x06\a\"'\x16\x17276&\x0567&54&\a\x0e\x01\x17\x16\x17&671&'\x0e\x01\a\x16\x1767\x06\x0f\x015\x06\x17\x16\x05\x1e\x01\x17\x1e\x017>\x017&\x00\"\x06\x15\x14\x162654\x03&\a5\x06\x16\x17\x1e\x017>\x01&\x05>\x01&'5\x06#\x0e\x01\x16\x17\x1e\x01%\x06\x16\x17\x1667>\x017\x06\a\x16\a\x16\x04\x176$7&74>\x01=\x01\x15.\x01'\x06\a\x06'&'&'\x0e\b#\x06'\x0e\x03\a\x06#\x06'\x06'&'&'&'\x06\a\x16\x0365.\x01'&\x0e\x01\x17\x1e\x01\x17\x1667\x16\x1767.\x01'\x06\a\x14\x06\x15\x16\a\x06\a\x06\a#\x06\x17\x16\x17\x04%&'\x06\a\x06'&'\x06\a#\x152%6767\a65&'&'&7&5&'\x06\a\x16\x056.\x01\a\x0e\x01\a\x14\x17\x1e\x017>\x01\x01\xde\b&\x12\x195\x02\x01R\x1b\x17\x16\x054\a&\x13\x195\x01\x02S\x1b\x16\x169\rW\"-J\x870(/\xfar\rV\"-J\x870(.\x02\xc9\x01)#\x1b\"6&4\x1c\x05pOPpp\xe0c\xf3|\x1bo}vQ\x02\xf2\b\x13\a\x01[\x8060X\x16\xfdQ\xfd\xc4\x17W1V\xbb\x01\x02\x05\x13\b\x06\x19\x0e\x1b\a\t\v\x1c\x1d\x1e\r\x17\x1c#\x1a\x12\x14\v\a5X\v\t\t\x0fN\x02\"&\x1c\x05\r.\x0e\x03\x02\n)\n\x0f\x0f\x17D\x01>q\x1c \x15\b\x10J\x17:\x03\x03\x02\x04\a\x05\x1b102(z/=f\x91\x89\x14*4!>\f\x02S\x015b!\x11%\n\x19\x12\x05\x12\x03\x04\x01\x05\x01\v\x06(\x03\x06\x04\x02!\x1f$p8~5\x10\x17\x1d\x01\x1a\x10\x18\x0e\x03\x0e\x02.\x1c\x04\x12.:5I\r\b\x0f\r\b\x0e\x03~\xfe\xf7T\x8a\n\x13\x03\x0e\x18\x0f\x0e\x0e\x1c\x18\x114~9p# !\x02\n\x02)\x05\f\x01\x05\x01\x05\x03\x12\x05\x12\x18\b&\x11 ?()5F\t\x021\x18\x0f\x04\a\x05\x1c\f\t\x1c\x10\x12\r\t\n\x1c\x1e\x15\b\x03\xaf\x1d\x19 d%{\x1d\x13\x04v*\x85:\r \x0e\x0e@e\x10\x0f\n\x01s|\x03D\x861d \x19\x1d\x12\x04\x13\x1d{\x8b\x1f\x0e:\x85*\x06\x0f\x10dA\x11A|o\x04\x0e\x13\x01Yk\x03'&\x8d\x13\x12\a\b\x14\x83<\x02\x02\x83\xa5tu\xa5\xa5ut\xfe&\x02\x02\x01\x1bv\a\x0e\x01\v\x03HC\xba\x04XX\x13\x01\x03\x14TR\x05\x0f\x02\xc8;w\x19\b\x06\x12\x10\x94\x1d\x02\x82\x17\r\x8d\xc671\u0099\r\x15\x02\x03\x03\x01\x01\x01\x02\a\x01Z*&'\x06\b\r1\x05\b\x06\x05\x03\x02\x02\x01\x01\t\x14\x11\x13\v\x03\x02\x01\x119?\t\b.\r\r\x1d$\x06\x04\x02\xfd\x84\x0e\x10Gv\v\f5k65P\x02\x02<\xdc?8q=4\x88a\x04\t\x01\x06\x02\x12\x13\x17\v\r\vSC\"\xcd\x15\x15\x931#\x16\x03\x03\x15\x1c<\x80\x01/6B&!\x01ML\b\x11\t\x18\x14\x12\x04\x05\x04\b\xbe^;\x8c6k5\f\vwF\x10\x0e1<\x02\x02P\x00\x00\x03\x00\x00\xffC\t\x01\x05\xbd\x00\a\x00\x0f\x00;\x00\x00$\x14\x06\"&462\x04\x14\x06\"&462\x01\x1e\x05\f\x0132\x1e\x04\x0e\x03\a\x06\a>\x05.\x03\a\x06$.\a\x05\xf4`\x88aa\x88\xfdsa\x88``\x88\xfdZ9k\x87\x89\xc3\xcd\x01'\x019؋ӗa-\x03*Gl|M\xb9e\x1d_]`F&\fO\x9a\xfe\xb1\xa8\xfe\xdcܽ\x82sDD!/+\x88``\x88aa\x88``\x88a\x051\x0154&'\"&#!\x11!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\a\x9f\x1f\x17\b\n\x99\x99\n\b\x17\r\x1e\x17\x03\f\x8b\x8b\x03\v\x01\x17\xfbi\xe4LCly5\x88)*\x01H\x02\xcacelzzlec0h\x1c\x1c\u007f\xb7b,,b\xb7\u007fe\x03IVB9@RB\x03\x12\x05\xfe9\x01\xebJ_\x80L4\xf8\x004LL4\b\x004L\x0244%\x05\x02\x8c\x02\x05\xaf2\"\x04\x01\x81\x01\x04\xe0\x014\xfe\xcc:I;p\x0f\x10\x01\x01!q4\a\bb\xbab\b\a3p\f\x0f\x02\x02\x06(P`t`P(\x06\x04\x8e6E\x05\x03\bC.7B\x03\x01\xfe\x02I\x036\xfb\x004LL4\x05\x004LL\x00\x00\x05\x00\x00\xff\x80\t\x00\x05\x80\x00\x05\x00\v\x00\x1a\x00.\x00>\x00\x00\x01\x11\x0e\x01\x14\x16$4&'\x116\x00\x10\x02\x04#\".\x0254\x12$ \x04\x014.\x02#!\"\x04\x02\x15\x14\x12\x043!2>\x02\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03Zj\x84\x84\x02b\x84jj\x01[\x9d\xfe\xf2\x9fwٝ]\x9d\x01\x0e\x01>\x01\x0e\x02\x1co\xb8\xf3\x83\xfeӰ\xfeٯ\xae\x01*\xae\x01-\x81\xf5\xb8o\x01XL4\xf8\x004LL4\b\x004L\x01'\x02\xb5)\xbd꽽\xea\xbd)\xfdJ)\x01\xd1\xfe\xc2\xfe\xf2\x9d]\x9d\xd9w\x9f\x01\x0e\x9d\x9d\xfeL\x8b\xf5\xa6`\xa2\xfeֺ\xab\xfe۪e\xa9\xec\x03\x06\xfb\x004LL4\x05\x004LL\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00\x1f\x00;\x00\x00\x05\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x01\x15#54&#!\"\x06\x15\x11\x14\x16;\x01\x15#\"&5\x11463!2\x16\x06\x80\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x80^B\xfb\xc0B^^B\x04@B^\xfe\x80\x80\x13\r\xfb\xc0\r\x13\x13\r\xa0\xa0B^^B\x04@B^`\x04@\r\x13\x13\r\xfb\xc0\r\x13\x13\x04M\xfb\xc0B^^B\x04@B^^\x01>\xa0\xa0\r\x13\x13\r\xfb\xc0\r\x13\x80^B\x04@B^^\x00\x00\x06\x00\x00\xff\x00\b\x80\x06\x00\x00\x02\x00\x05\x005\x00=\x00U\x00m\x00\x00\t\x01!\t\x01!\x01\x0e\x01\a\x11!2\x16\x1d\x01\x14\x06#!\"&=\x01463!\x11.\x01'!\"&=\x01463!>\x012\x16\x17!2\x16\x1d\x01\x14\x06#\x04264&\"\x06\x14\x01\x14\x0e\x02\".\x0254>\x03762\x17\x1e\x04\x05\x14\x0e\x02\".\x0254>\x03762\x17\x1e\x04\x06\xc0\xfe\x80\x03\x00\xf9\x80\xfe\x80\x03\x00\x01\xb5\x0e?(\x02`\x0e\x12\x12\x0e\xfa\xc0\x0e\x12\x12\x0e\x02`(?\x0e\xfe\x15\x0e\x12\x12\x0e\x01\xeb\x15b|b\x15\x01\xeb\x0e\x12\x12\x0e\xfd?B//B/\x04\x90]\x8e\x93\x84\x93\x8e]Frdh\x04\x12L\x12\x04hdrF\xfb\x00]\x8e\x93\x84\x93\x8e]Frdh\x04\x12L\x12\x04hdrF\x04@\xfd@\x02\xc0\xfd@\x03\x80(?\x0e\xfa\xf5\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x05\v\x0e?(\x12\x0e@\x0e\x129GG9\x12\x0e@\x0e\x12\x10/B//B\xfcaItB!!BtI\v\x8cѶ\xba\a!!\a\xba\xb6ь\vItB!!BtI\v\x8cѶ\xba\a!!\a\xba\xb6ь\x00\x00\x02\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x00M\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06#\x01>\x035!\x14\x1e\x02\x17\x1e\x01\x14\x06\a\x0e\x03\x15!4.\x02'.\x0146\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xfd\x8aM\x90sF\xfc\x00Fs\x90M\x13\x17\x17\x13M\x90sF\x04\x00Fs\x90M\x13\x17\x17\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xfd<\x1d\u007f\xb2\xf2\x84\x84\xf2\xb2\u007f\x1d\a!(!\a\x1d\u007f\xb2\xf2\x84\x84\xf2\xb2\u007f\x1d\a!(!\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x003\x00?\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06+\x01!\x14\x17!6\x114.\x02'#\x0e\x03\x15\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xe0\xfc\x00\t\x03\xee\tDq\x8cL\xe6L\x8cqD\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12B>=\xfaC\x82\xef\xb1\u007f\x1f\x1f\u007f\xb1\xef\x82\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x003\x00;\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06+\x01!\x14\x17!6\x03.\x01'#\x0e\x01\a\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xe0\xfc\x00U\x03VU96\xb7g\xe6g\xb76\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12β\xb2\xfc\x0e\x8d\xc9**ɍ\x00\x00\x02\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x00G\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06#\x01>\x035!\x14\x1e\x02\x17\x1e\x01\x14\x06\a\x06\a!&'.\x0146\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xfd\x8aM\x90sF\xfc\x00Fs\x90M\x13\x17\x17\x13\x89k\x02\xbck\x89\x13\x17\x17\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xfd<\x1d\u007f\xb2\xf2\x84\x84\xf2\xb2\u007f\x1d\a!(!\a3\x91\x913\a!(!\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x0f\x009\x00I\x00\x00\x052\x16\x1d\x01\x14\x06#!\"&=\x014637>\b7.\b'!\x0e\b\a\x1e\b\x17\x132\x16\x1d\x01\x14\x06#!\"&=\x01463\x05\xe0\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0eb\x03\x1a\":1P4Y,++,Y4P1:\"\x1a\x03\x04\xfc\x03\x1a\":1P4Y,++,Y4P1:\"\x1a\x03b\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e@\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12@7hVX@K-A\x1e\x1c\x1c\x1eA-K@XVh77hVX@K-A\x1e\x1c\x1c\x1eA-K@XVh7\x06\x00\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x00\x00A\x00j\x00\x00\x01\"\x06\x1d\x01#54&#\"\x06\x15\x11'54&#\"\x06\x1d\x01\x14\x17\x01\x16\x15\x14\x163!26=\x0147\x136=\x014&#\"\x06\x1d\x01#54&'&#\"\x06\x1d\x01#54&'&'2\x17632\x16\x17632\x16\x1d\x01\x14\a\x03\x06\x15\x14\x06#!\"&5\x01&=\x014632\x17>\x0132\x176\x03\x005K @0.B @0.B#\x016'&\x1a\x02\x80\x1a&\nl\n@0.B 2'\x0e\t.B A2\x05\bTA9B;h\"\x1b d\x8c\rm\x06pP\xfd\x80Tl\xfe\xccL\x8dc\v\x05\x06\x8b_4.H\x04\x80K5\x80]0CB.\xfeS\x1e\xac0CB.\xe0/#\xfe\xd8'?\x1a&&\x1a\x19)$\x01\xb4$)\xf60CB. }(A\b\x02B.\x80z3M\x05\x01\x802\"61\a\x8fd\xf639\xfeL\x18/PpuT\x01(If\xe0c\x8d\x01_\x82\x15E\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06`\x06\x00\x001\x00X\x00\x00\x00\"\x06\x15\x11#\x114&\"\x06\x15\x19\x01'&#\"\x06\x15\x14\x17\x01\x163!267\x1365\x114&\"\x06\x15\x11#\x114&\"\x06\x15\x11#\x114&2\x16\x17632\x16\x1d\x016\x16\x15\x11\x14\a\x03\x0e\x01#!\"&'\x01&54632\x17\x114632\x176\x03\x9e\\B B\\B\x9a&@5K\x1a\x01\x80&@\x02\xb0\"6\aL\x05B\\B B\\B \xb4\x88s\x1f\x13\x17c\x8di\x97\bL\x0e}Q\xfdP\x01\x03%&#\"\x06\x15\x14\x16\x17\x05\x15!\"\x06\x14\x163!754?\x01\x0327%>\x015\x114&#!\a\x06\x15\x11\x14\x1626=\x013\x15\x14\a\x1e\x01\x15\x14\x06\a\x05\x041\xb1\xa3?\x17>I\x05\xfe\xfbj\x96\x96jq,J[\x96j.-\x02t\x01\x91j\x96lV\xfe\xad\\\x8f\x9b\xa3\x1e$B.\x1a\x14\x01R1?\x01@B.\x1a\x14\xfe\xde\x1c\x12+\x10\x10?2\x14\x12\x01`\x1e$\xe8\xfdv\x18\x165K-%\x02\x0e\xfd\x805KK5\x02\x17\xe9.olRI\x01S+6K5\xfë$B\\B 94E.&\xfeʀ\x8d15\x05\x1euE&\n\x96Ԗ\x11\x1c\x83Pj\x96\x11\xef\x96j\xfddX\x8b\x15U\x17\x02\xc7GJ\x0e7!.B\n\x9a\nP2\xff\x00.B\n\x84\r\b\x1a\x15%\x162@\t\xa0\x0e7\x03\x11\xf8\bK5(B\x0e\xc8@KjKj\xc6?+f\xfc\x00\x13U\vE,\x02\x9c5K~!1\xfe\xd8.>F.\xd0\xd0F,\bQ5*H\x11\x8d\x00\x00\x00\x00\x02\x00\x00\xff\x00\b\x00\x06\x00\x00$\x00b\x00\x00\x012\x16\x17\x01\x16\x15\x11\x14\x06#!\"&=\x01%!\"&=\x01463!7!\"&'&=\x01463\x01\x114'\x01&#!\"\x06\x15\x14\x1e\x01\x17>\x013!\x15!\"\x06\x15\x14\x17\x1e\x013!32\x16\x15\x14\x0f\x01\x0e\x01#!\"\x06\x1d\x01\x14\x163!2\x17\x05\x1e\x01\x1d\x01\x14\x163!26\x04\u007f=n$\x02\x0132\x16\x17\x1b\x01>\x0132\x16\x17\x1e\x01\x15\x14\a\x03>\x0532\x16\x15\x14\x06\a\x01\x06#\x03\"\x06\a\x03#\x03.\x01#\"\x06\x15\x14\x17\x13#\x03.\x01#\"\x06\x15\x14\x17\x13\x1e\x01\x17\x13\x1e\x013!27\x01654&#\"\a\x0554\x1a\x017654&#\"\x06\a\x03#\x13654&\x01\xcbMy\x13e\r\x05t\a|]\x11\x83WS\x82\x14Sg\x14\x82SY\x85\x0e\\x\a{\n7\x160\"1\x19i\x9692\xfe\x05DU1&=\t\xa4\u007f\x91\t=&0@\x03\x84\x1ac\t>&/B\x03t\a\x04\bd\b4!\x02\xb6*\"\x01\xfb8K4+\"\xfe\xcd@H\x03\x04@/'=\tt\x1a\x96\x03?\xff\x00_K\x01\x9193-\x16\x01\xdd\x1b\x1e]\x88\nUlgQ\xfe\xa4\x01\xacQgsW\n\x8a]\x18#\xfe\x00\a+\x10\x1e\v\v\x94i>p&\xfe\x843\x06\x800&\xfdV\x02Z&0B/\x0f\r\xfd\xdd\x01\x98%3B.\x0e\f\xfe\"\x1ct\x1e\xfeo )\x1a\x01{+C4I\x1a\xe6\xe3\x04\x01\f\x01(\r\x12\v/D0&\xfe\x1e\x02p\x0e\x0e0D\x00\x05\x00\x00\xff\x00\x06\x80\x06\x00\x003\x00[\x00_\x00c\x00g\x00\x00\x01\"\x06\x15\x19\x01'&#\"\x06\x15\x14\x17\x01\x163!267\x136=\x014&\"\x06\x15#54&#\"\x06\x1d\x01#54&#\"\x06\x1d\x01#\x114&'2\x16\x1d\x01632\x17632\x17632\x16\x1d\x01\x14\a\x03\x0e\x01#!\"&'\x01&54632\x17\x1146\x13\x11#\x11!\x11#\x11!\x11#\x11\x02\x805K\x97)B4J\x1a\x01\x80&@\x02\xce\x16#\x05\\\x188P8 @0.B J65K J6k\x95\x16\ncJ/4qG\x1b\x1d^\x82\x1c\\\x10hB\xfd2.\xfe\xd81!~K5\x03y\x17?\xa3\xb1^\\\xfe\xadVl\x96j\x01\x91\x02t-.j\x96[J,qj\x96\x96j\xfe\xfb\x05I7$\x1e\xa3\x9b?1\x01R\x14\x1a.B\x87\x10\x10+\x12\x1c\xfe\xde\x14\x1a.B$\x1e\x01`\x12\x142?\x01g\x16\x18\xfdvEo.\xe9\x02\x175KK5\xfd\x80\x02\x0e%-K\xfa\xeb6+\x01SIR[\xfe\xca&.E49 B\\B$\x88\xfe\xcc5K\x00\x00\x00\x00\x02\x00\x00\x00\x00\a\xb4\x04\x00\x00\x19\x00G\x00\x00\x01\x15\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!2\x16\x05\x13\x16\a\x06+\x01\"&'\v\x01\x06+\x01\"'\v\x01\x0e\x01+\x01\"'&5\x13>\x01;\x012\x17\x13\x16\x17>\x017\x136;\x012\x16\x03Y\x13\r\xfe\xd6\x12\r\x87\r\x13\xfe\xd7\r\x13\x12\x0e\x03\x19\r\x13\x04\x0eM\x01\t\n\r\x86\f\x12\x01.\xbd\b\x15x\x14\t\xbc-\x01\x12\f\x87\r\n\tN\x01\x12\f\x8e\x14\t\xdc\n\n\x03\r\x04\xdd\t\x14\x8d\r\x12\x03\xe0u\r\x12\xfc\xd4\r\x13\x12\x0e\x03,\x12\ru\x0e\x12\x13\n\xfc?\r\v\n\x11\f\x02L\xfeW\x13\x13\x01\xab\xfd\xb2\f\x11\n\n\x0e\x03\xc1\f\x11\x13\xfd\xf8\x18\x1b\a#\t\x02\b\x13\x11\x00\x00\x00\x00\x04\x00\x00\xff\x00\a\x00\x06\x00\x00\t\x00*\x00:\x00J\x00\x00\x014'&+\x01\x11326\x17\x13\x16\a\x06+\x01\"'\x03#\x11\x14\x06+\x01\"&5\x11463!2\x17\x1e\x01\x15\x14\x06\a\x16\x02 \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02&\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04\x12UbUI\x06-\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5uu\xc5\x01ڎ\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x03AX!\x12\xfe\xe7J\xd9\xfe\x8b\x11\x0e\x10\x11\x01m\xfe\xa2\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x18\x1f\x9cf\\\x93$\n\x036u\xc5\xfe\xf0\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5\xfeK\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x04\x00\x00\xff\x00\a\x00\x06\x00\x00-\x00[\x00k\x00{\x00\x00\x01276/\x01&'&\x0f\x01\x0e\x05#\"&54632\x16\x1f\x01\x1676?\x016'.\x04#\"\x06\x15\x14\x16!276/\x01&'&\x0f\x01\x0e\x05#\"&54632\x16\x1f\x01\x1676?\x016'.\x04#\"\x06\x15\x14\x16\x02 \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02&\x00 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126\x02]\x99h\x0e\v-\x06\x12\x10\v\x04\x04\x0f\x14\x1b\x1e%\x13Lb`J%E\x10\x10\v\x0f\x10\b5\r\x0f\x03\x10,5R-\x94\xc4\xc2\x03\f\x99h\x0e\n-\b\x11\x10\v\x04\x04\x0f\x14\x1b\x1e%\x13Lb`J%E\x10\x10\v\x0f\x10\b5\r\x0f\x03\x10,5R-\x93\xc5\xc2'\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5uu\xc5\xfd\xa4\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01/h\x12\x12R\r\x04\x02\r\x03\x04\f\x0f\x0e\f\adMLc\x1c\x0e\x0e\v\x01\x02\fN\x14\x13\x04\x10\x1f\x19\x14\xc1\x90\x92\xbfh\x12\x12R\x0e\x03\x02\r\x03\x04\f\x0f\x0e\f\adMLc\x1c\x0e\x0e\v\x01\x02\fN\x14\x13\x04\x10\x1f\x19\x14\xc1\x90\x92\xbf\x041u\xc5\xfe\xf0\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5\x01\x15\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x00\x00\x02\x00@\xff\xe0\a\xc0\x05 \x00\v\x00\x17\x00\x00\t\x04\x17\a'\t\x017\t\x03'7\x17\t\x01\a\x01\a\x01\x02\xe0\x01\x80\xfe\x80\xfd`\x02\xa0\xa8`H\xfe \x01\xe0\xc1\xfe\xdf\x02\xa0\x02\xa0\xfd`\xa8`H\x01\xe0\xfe \xc1\x01!`\xfe\x80\x02\xe0\xfe\x80\xfe\x80\x02\xa0\x02\xa0\xa8`H\xfe \xfe \xc1\x01\x1f\x02\xa0\xfd`\xfd`\xa8`H\x01\xe0\x01\xe0\xc1\xfe\xe1`\x01\x80\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\v\x00\x17\x00'\x00\x00%\t\x01\a\x17\a\t\x01\x177'\t\x057'7\t\x01'\a\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x02\xcd\x01\x0f\xfe\xe9X\xc0`\xfe\xe9\x01\x17(W\u007f\xfe:\x03,\x01\xc6\xfe:\xfe\xf1\x01\x17X\xc0`\x01\x17\xfe\xe9(W\x03L\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xb6\x01\x0f\x01\x17X\xbf`\x01\x17\x01\x17(W\x80\xfe:\xfeB\x01\xc6\x01\xc6\xfe\xf1\xfe\xe9X\xbf`\xfe\xe9\xfe\xe9(X\x01\xf9\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\n\x00\x00\xff\xdc\t\x00\x05$\x00\v\x00\x13\x00\x1c\x00%\x00/\x009\x00E\x00S\x00[\x00\x80\x00\x00\x01\x14\x06#\"&54632\x16$\x14\x06\"&462\x054&\"\x06\x14\x1626$4&#\"\x06\x14\x162%\x14\x06#\"&462\x16$\x14\x06#\"&4632\x00\x10\x00#\"\x0e\x01\x14\x1e\x0132\x01&! \a2\x1e\x02\x154>\x02\x00\x10\x00 \x00\x10\x00 \x13!\x0e\x01\a\x16\x15\x14\x02\x04#\"&'\x06\a.\x01'\x0e\x01#\"$\x02547.\x01'!6$32\x04\x02\x8b7&'77'&7\x04\x827N77N\xfc'q\xa0qq\xa0q\x04\x81qPOrq\xa0\xfcE\xa3st\xa3\xa4\xe6\xa3\x04\x82\xa3ts\xa3\xa3st\xfc\xdf\xfe\xf1\xbf}\xd4||\xd4}\xbf\x03\xab\xfe\xfe\xd2\xfe\xc1\xfeuԙ[W\x95\xce\x02Q\xfe\xf2\xfe\x82\xfe\xf1\x01\x0f\x01~\x04\x01\u007f,>\tn\x9a\xfe\xf8\x9b\x85\xe8P/R\vU P酛\xfe\xf8\x9an\t>,\x01m\x95\x01\x9c\xe2\xe0\x01\x8a\x02\x1b'77'&77\x02N77N6^Orq\xa0qq\x01\xa0qq\xa0q\xc0t\xa3\xa4棣\x01棣\xe6\xa3\xfe(\x01~\x01\x0f|\xd5\xfa\xd5|\x04\von[\x9a\xd4usј^\xfd\a\x01~\x01\x0f\xfe\xf1\xfe\x82\xfe\xf1\x04\x043\u007f3\x97\xba\x9c\xfe\xf8\x99pc8{\x16y%cq\x99\x01\b\x9c\xba\x973\u007f3dqp\x00\x03\x00f\xff\x00\x04\x9a\x06\x00\x00\t\x00\x13\x00L\x00\x00\x00 \x0054\x00 \x00\x15\x14\x00\"\x06\x15\x14\x162654\x01\x1e\x01\x0e\x02\a\x06\a\x17\x01\x16\x14\x0f\x01\x06\"'&'\x01\x06\"/\x01&47\x017&'.\x0367>\x02\x16\x17\x1e\x04326?\x01>\x01\x1e\x01\x03<\xfe\x88\xfe\xf6\x01\n\x01x\x01\n\xfe\x96\xb8\x83\x83\xb8\x83\x01,\r\x04\r(-'s\xc8I\x01\v\x1e\x1e\f\x1fV\x1fC\xc8\xfe\xf5\x1fV\x1e\f\x1f\x1f\x01\vH\xcbr'-(\r\x04\r\n$0@!\x05\x14BHp9[\xa6%&!@0$\x02u\x01\n\xbb\xbc\x01\n\xfe\xf6\xbc\xbb\x01\x9b\x83]\\\x83\x83\\]\xfd\xa7\x1b-$)!\x19I\x15H\xfe\xf5\x1fV\x1e\r\x1e\x1eD\xc8\xfe\xf4\x1e\x1e\r\x1eV\x1f\x01\vH\x15I\x19!)$-\x1b\x14\x1e\x0e\x12\x1a\x04\x0e#\x1a\x163\x19\x19\x1a\x12\x0e\x1e\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x006\x00>\x00N\x00\x00\x00\x14\x06\"&462\x01.\x01\x06\a\x0e\x02\"&/\x01.\x01\x06\a\x06\x16\x17\x16\x17\a\x06\a\x06\x14\x1f\x01\x162?\x01\x16\x17\x162?\x0164/\x0267>\x01\x02\x10& \x06\x10\x16 \x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x9f]\x84]]\x84\x013\n$;\x1f\n&|\x82v\x1b\x1b\x1f;$\n\x16(CS\x8f3\x8e1\x16\x16\t\x16=\x16\xbfrM\x16=\x16\t\x16\x16\xbf4\x8dTC(G\xbe\xfe\xf4\xbe\xbe\x01\f\x02z\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\xfe\x84]]\x84]\xfd\xf6\x14\x18\x05\x19\b\x18($\x12\x12\x19\x05\x18\x14-;,5\x0e4\x8e0\x16=\x16\t\x16\x16\xbfsL\x16\x16\t\x16=\x16\xbe4\x0e5,;\x01\x12\x01\f\xbe\xbe\xfe\xf4\xbe\x01\xe8\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00\x00\xff\x80\x06\xb8\x05\x80\x00\x12\x00(\x00\x00\x012\x16\x15\x11\x14\x02\x06\x04#\"$&\x025\x11463\x0127\x01654&#\"\a\t\x01&#\"\x06\x15\x14\x17\x01\x16\x06\x1dAZ\x88\xe5\xfe\xc1\xaf\xb0\xfe\xc1\xe6\x88\\@\x02\xc1/#\x01\x94%E1/#\xfe\xbd\xfe\xbd#.1E$\x01\x95!\x05\x80[A\xfd\xf9\xb0\xfe\xc0懇\xe6\x01@\xb0\x02\a@\\\xfb\xd8!\x01\x84#21E!\xfe\xca\x016!E13\"\xfe|!\x00\x00\x00\x01\x00\x00\xff\x98\t\x00\x05g\x00L\x00\x00\x05\x01\x06\x00\a\x06&5&\x00'.\x02#4&5!\x15\x0e\x02\x17\x16\x00\x176\x127&\x02'&'5\x05\x15\x0e\x01\x17\x1e\x01\x17676&'6452>\x013\x15\x0e\x01\a\x03\x16\x12\x17\x01.\x02'5\x05\x17\a\x06\a\x00\a\x05\xd6\xfe\xd9\x19\xfe\xf5A\x015R\xfe\xa5V\x15[t,\x01\x02G'Q4\x10\x1a\x01}-\x1f\xda\x16\x13\xd6\x1d&\xa3\x02\x01r!\xd5\r\xe5\a\x01\xb9\x0eG;\x1a\x01\xcc\x01\x01\x8b>\xfd\xf2!g\x02\xb71\xfd\xff\x85\x01\x01\x01\xc1\x03\x14\xca2sV\x05&\b2\x02\x1c:#;\xfc\x90d=\x01\x9b*'\x01\xe45E\x022\x01/\x02..F\xefD֕71\x02\a$\x06\x01\x011\x02>2\xfeF!\xfd\xfe\x11\x03\xf9&1\x0e\x012\x04\x02,\x04\x8d\xfb@K\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\n\x00\x18\x00r\x00\x82\x00\x92\x00\x00\x01\x14\x06#\"&5462\x16\x17\x01\x0e\x04\a\x01>\x04%\x14\a.\x02#\"\x15\x14\x17\x0e\x01\a'&#\"\x06\x1f\x01\x06#\"'>\x0254#\"\x0e\x01\a.\x01'7654&\x0f\x01&547\x1e\x023254&/\x01>\x017\x17\x16326/\x01632\x17\x06\x15\x14327\x1e\x01\x17\a\x06\x15\x14\x16?\x01\x1e\x01\x10\x02&$ \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x03\xb5!\x19\x1a&\"2&\x0f\x01^\tu\x86\x8b_\x03\xfe\xa3\ax\x84\x8c^\x02\x8ah\x03\x1c\x19\x04\r;J݃\x10\x01\x0e\x05\x06\x01\x10HJǭ\x01\x18\x13\r\x06\x16\x17\x02q\x9e\x1fE\n\v\x05D\x0em\x02!\x1b\x04\r\x19\x14\x14M\xe0\x84\x0f\x02\r\x05\x06\x01\x0fG?̯'\f\v%o\x99\x1f8\n\v\x049\x0eU\u007f\xd6\xfe\xd8\xfe\xba\xfe\xd8\xd6\u007f\u007f\xd6\x01(\x01F\x01(\xd6ߎ\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x02\x83\x1a&!\x19\x1a&!S\x02E\bm|\x82[\x06\xfd\xbc\an{\x83[<ɪ\x02\x12\x0f\r\n\"p\x9d C\n\v\x04D\x0fi\x02%\x1e\x04\r\x1d(\x03K\xe1\x84\x0f\x03\f\x05\x06\x01\x0fHCέ\x01\x16\x10\f\x06\x13\f\fp\x9a\x1eC\n\v\x05B\rm8\t\r@Kނ\f\x02\x0e\x05\x06\x01\rH\xe7\x01F\x01(\xd6\u007f\u007f\xd6\xfe\xd8\xfe\xba\xfe\xd8\xd6\u007f\u007f\xd6\x02\x81\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x04\x00\x00\xff\x01\a\x00\x06\x00\x00\v\x00\x16\x00\"\x00*\x00\x00\x016\x17\x16\x17%&\x04\a\x016$\t\x01\x16\x047\x03&$\x025\x10%\x16\x12\x02\x06\a\x06%\x016\x02'$2\x16\x14\x06\"&4\x03}\xf0\xd3\xe8x\xfd\x1a\xa0\xfe\xf43\xfe\xec\x80\x01n\xfd\xdd\x01QH\x01\x16\x9a\xe6\xd4\xfe\xa6\xc7\x06\xc4:\x03dΏ\xe6\xfe\xf4\x01\x95X\ve\xfe8\xfa\xb1\xb1\xfa\xb1\x06\x00\x02z\x86\xee'\t\xa7\x92\x01\xa8\x9f\xad\xfel\xfdi\x8f\x94\x1d\xfe=!\xf9\x01\u007f\xdc\x01\v7\x96\xfe\xbf\xfe\xdd\xfdS\x85\x0e\x02o\x83\x01?v\x06\xb1\xfa\xb1\xb1\xfa\x00\x00\x01\x00\x02\xff\x00\a\x00\x05\xc9\x00M\x00\x00\x01 \x00'&\x02\x1a\x017\x03>\x01\x17>\x017\x0e\x01\x17\x1e\x03\x17\x16\x06\a\x0e\x02\a\x17'\x06\x1e\x027>\x02\x17\x1e\x01\a\x0e\x04'\x0e\x01'\x1e\x01>\x0276.\x01'\x1e\x01\x176\x02'\x04\x00\x13\x16\x02\x0e\x01\x04\x03\x87\xfe\xe5\xfeEl:\x12F\x98g\v\vr\r*\xedt6\x83\a\x19K3U\b\x0f\v\x19\x05\x17Z8\x0f\x8b\x12\x153P)3^I%=9\t\x01\x03\x0e\x16)\x1a<\xa9}J\xb1\xa0\x95k\x1b+\bC-Wd\x1b\x0f\x91\x89\x01\t\x01&\x04\x02U\xa2\xd8\xfe\xe9\xff\x00\x01-\xf8\x83\x01T\x01E\x01+]\xfe\xe7\x0e\x03\x11Qr\x02-\xcf<\b\v\x04\x04\x01\x05Q#\a\x170\n\xbdC+M8\x1b\a\t3'\x02\x04:$\x02\a\x12\r\b\x03_Q\v=+\x1fIf5[ˮ&&SG\xaa\x01ZoM\xfek\xfe\xc5\u007f\xff\x00ܬc\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00#\x007\x00\x00\x01&#\"\x04\a\x0e\x01\a\x15\x1e\x01\x17\x16\x04327\x06\x04#\"'&$&\x0254\x126$;\x01\x16\x04\x01\x14\x02\a\x06#\"'6\x1254\x02'632\x17\x16\x12\x05ե\u009b\xfe\xecfKY\x04\x04YKf\x01\x14\x9b¥y\xfeͩ\x1d\x0e\xaf\xfe\xc4䆎\xf0\x01L\xb6\x03\xa8\x011\x01\xa4\x9a\x88hv\x89v\x9a\xc7ƚw\x87wk\x87\x97\x05\x1cn\x92\u007f]\xfa\x8d*\x8d\xfa]\u007f\x92nlx\x01\b\x94\xee\x01D\xb1\xb6\x01L\xf0\x8e\x01w\xfc\xf8\xc0\xfe\xab~?T8\x01b\xe4\xe3\x01b9SA}\xfe\xac\x00\x00\x00\x04\x00\x00\xff\x10\a\x00\x05\xf0\x00+\x005\x00?\x00F\x00\x00\x01\x14\a!\x14\x163267!\x0e\x01\x04#\"'\x06#\"\x114767\x12%\x06\x03\x12\x00!2\x17$32\x1e\x02\x15\x14\a\x16\x034&#\"\a\x1e\x01\x176\x01\x14\x16327.\x01'\x06\x01!.\x01#\"\x06\a\x00\a\xfb\x81۔c\xad2\x01\xa78\xe5\xfeΨ\xbb\xa9\xe4\xa6\xed-\x11\\\xc7\x01\x14\xb8\xf3?\x01\xb9\x01\x19\x1e\x0f\x00\xff\xb2@hU0KeFjTl\x92y\xcbE3\xf9\xc6aVs\x97z\xb7.b\x01\xf8\x02\xd8\x05؏\x90\xd7\x02W80\x92\xc5]T\x9f\xf4\x85St\x01\as\xa0<\xa9\x01h\xf6O\xfe\xed\x01\x12\x01_\x01u\x1a7bBt\xaa\xb6\x01\xb0SbF/\xa9o\x87\xfb|V]SHކ\xcd\x02J\x8e\xbe\xbe\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x80\x05\x80\x00\x0f\x003\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\x15!2\x16\x1d\x01\x14\x06#!\"&=\x01463!5!\"&5\x11463!2\x16\a\x00\x13\r\xf9\xc0\r\x13\x13\r\x06@\r\x13\x80^B\xfd \x01`\x0e\x12\x12\x0e\xfc\xc0\x0e\x12\x12\x0e\x01`\xfd B^^B\x06@B^\x01 \x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@B^\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x80^B\x03\xc0B^^\x00\x00\x00\x00\x02\x00\x16\xff\x80\x06\xea\x05\x80\x00\x17\x00>\x00\x00\x133\x06\a\x0e\x03\x1e\x01\x17\x16\x17\x16\x17\x16\x17!\"&5\x1146)\x012\x16\x15\x11\x14\x06+\x016\x03\x05\x0e\x03\a\x06'.\x02'.\x0167>\x0176\x1e\x03\x17%&\x8a\xc5F8$.\x0e\x03\x18\x12\x13\x04\x023\x1e9_\xfe\xf00DD\x04\xe8\x0140DD0\xb2\xd4\x10\xfe+\x02\x14*M7{L *=\"#\x15\n\x12\x14U<-M93#\x11\x01\xd4D\x05\x80@U8v\x85k\x9d_Y\x13\t\xee[\xabhD0\x05\x180DD0\xfa\xe80D\xd2\x01ce-JF1\f\x1aB\x1bD\xbe\xa3\xa3\xc8N&)@\r\f\v\x17/1 d\xaf\x00\x00\x00\x00\x04\x00\x0e\xff\x00\x05y\x06\x00\x00%\x00F\x00\xab\x00\xc5\x00\x00\x05\a\x06\a\x06#\"'&'&'&'&76\x17\x16\x15\x16\x17\x16\x17\x16\x17\x163276?\x016\x17\x16\x17\x16\x01\a\x17\x16\a\x06#\"/\x01\a\x06#\"/\x01&54?\x01'&7632\x1f\x0176\x17\x16\x05\x14\a\x06\a\x0e\x01\"&'&'&5#&76\x17\x16\x173\x11567632\x16\x15\x14\x06#\"'&76\x1f\x01\x1e\x0132654'&#\"\a\x06\x15\x11\x1632>\x0254'&#\"\a\x06\x0f\x01\x0e\x02'.\x015\x11463!2\x14#!\x113>\x017632\x16\x17\x16\x17\x16\x03\x16\x14\x06\a\x06#\"'&'&#\"\a\x06'&767632\x17\x16\x05y\x06q\x92\x9a\xa3\xa5\x98\x94oq>*\f\x0443\x05\x01\x12\x1c2fb\x80\x84\x90\x8f\x85\x80a\x06\n\x0f\f\x15$\xfe\x15B?\x15\x1c\x11\x0f\n\t>B\x05\n\x0f\x10\x02\x12\bBB\x10\x1e\x12\r\x06\aAA\x12\x1e\x1b\x01\xc7.-QP\xd6\xf2\xd6PR+\x0f\x01\t42\n%<\x01\x03ci\x94\x93\xd0ђ:6\x1c\x0f\x10\x1c\x0e\x0e&\vh\x90HGhkG@n\x84`\xb2\x86I\x8d\x8c\xc7Ȍ5\x18\x02\b\n!\x16\x15\x1f\x15\x11\x03m\x1e\x1e\xfc\xd5\x01(|.mzy\xd6PQ-.\x1f\t\v\v\x1a\r\t\aje\x80\x94\x85\x81\x1b\x12\t\x01\x03\r\x82\xa9\xa4\x98\x89\v\x06q>@@?pp\x92gV\x1c\b\b\x1c\x01\x03ZE|fb6887a\x06\n\x04\x03\x13%\x02RB?\x15\x1c\x11\n=B\x05\x10\x02\x0f\x0e\a\nAB\x10\x1d\x12\x05BA\x11\x1e\x1bJvniQP\\\\PRh!\a\x1b\x11\x10\x1ccD\x01S\x02\x88`gΒ\x93\xd0\x10\v23\b\x03\x03\x06\x8fgeFGPHX\xfecCI\x86\xb0_ƍ\x8c\x8c5\"\x02\v\t\n\b\x05\x17\x0f\x02\xa8\x0f\x17n\xfe\x1d*T\x13.\\PQip\x01\xd0\b\x14\x10\r\x1a\a[*81\n/\x19\r\x10\x049@:\x00\x00\x04\x00\x1d\xff\x00\x06\xe1\x06\x00\x00\x1b\x00>\x00t\x00\x82\x00\x00%6\x16\x14\a\x0e\x04#\".\x03'.\x01>\x01\x16\x17\x16\x17\x04%6%\x16\x06\a\x06\a\x06&7>\x01'.\x03\x0e\x02#\x0e\x03*\x02.\x01'&676\x16\x01\x14\x1e\x02\x1f\x01\a.\x01/\x01&'\x0e\x03.\x0254>\x05754'&#\"\x0e\x03\a%4>\x0332\x1e\x03\x15\x01\x14\x17\x167676=\x01\x0e\x03\x06\x0f\x0f\x16\x0f\r>\x81\x99\xdfvw\ued25d\"\b\x04\x06\n\r\x05\xc0l\x01\x85\x01\x9a\xbe\x01\x98\v\x11\x14\"3\x11\x12\t\x15/\x11\x05\x15!\x1a,\x13+\x01\x06\x0e\b\t\x05\x06\x03\x03\x01\x01\x06j2.|\xfe\x84\x1b%&\x0e\r\xe3(N\x13\x13\v\x0e&w\x88\x90\x83h>8X}x\x8cc2\x15\"W\x06\x15<4<\x12\xfe\xda,Z~\xb1fd\xa2aA\x19\xfd`FBIT\x1e\x0e;hmA<\x06\x06\x1d\x13\x107QC1>[u])\t\x0f\t\x05\x01\x04u1\xb0V(\xd2\x10k1S)\x0e\n\x13-\x99\x16\a\t\x03\x02\x02\x02\x04\x01\x01\x01\x01\x01\x02\x02\x100\x06\a\f\x01\xa9\x1fB2*\v\v\xe0%M\x14\x14\v\x16;W(\x060S\x8f[T\x8c]I)\x1c\t\x02\u007fA 5\x02\x16%R7\x1b&\x1a\x80\x1a&T\x01\xa8\x01,\xfe\xd4\xfeX\xfe\xd4\x02\x00\x0e\x12\x12\x0e\x92\xce\x12\x1c\x12\xa9\x01\xc0\x0f\xfdq\x1a&&\x1a\x02\x8f\x041\xfe\xd4\xfeX\xfe\xd4\x01,\x01\xa8L\x12\x1c\x12Β\x0e\x12\x12\x0ew\xa9\x00\x00\x00\x00\x03\x00%\xff\x00\x06\xdb\x06\x00\x00\x1b\x00%\x00;\x00\x00\x01\x16\x14\x0f\x01\x06#!\"&5\x11463!546;\x012\x16\x1d\x01!2\x17\x01!\x11\x14\x06+\x01\"&5\x012\x16\x15\x11\x14\x06#!\"/\x01&4?\x0163!5!\x15\x06\xd1\n\n\x8d\x1c(\xfa\xc0\x1a&&\x1a\x02@&\x1a\x80\x1a&\x02\x00(\x1c\xfc\xbc\x01\x00&\x1a\x80\x1a&\x03@\x1a&&\x1a\xfa\xc0(\x1c\x8d\n\n\x8d\x1c(\x02\x00\x01\x00\x04\xd7\n\x1a\n\x8d\x1c&\x1a\x01\x00\x1a&@\x1a&&\x1a@\x1c\xfb\xdc\xfe\x00\x1a&&\x1a\x03\xc0&\x1a\xff\x00\x1a&\x1c\x8d\n\x1a\n\x8d\x1c\xc0\xc0\x00\x04\x00\x00\xff\x00\b\x00\x05\xfb\x00\x1b\x00\x1f\x00#\x00'\x00\x00\x01\x16\x15\x11\x14\x06\a\x01\x06'%\x05\x06#\"'&5\x11467\x016\x17\x05%6\x05\x11\x05\x11%\x11%\x11\x01\x11\x05\x11\a\xe4\x1c\x16\x12\xfd\x80\x18\x18\xfd\x98\xfd\x98\n\x0e\x13\x11\x1c\x16\x12\x02\x80\x18\x18\x02h\x02h \xfb\x18\x02@\xfb`\x02 \x04\xe0\xfd\xe0\x05\xf5\x14!\xfa\x80\x14 \a\xff\x00\v\v\xf6\xf6\x05\v\x14!\x05\x80\x14 \a\x01\x00\v\v\xf6\xf6\r\x9a\xfb\n\xe6\x04\xf6\r\xfb\n\xd9\x04\xf6\xfa\xfd\x04\xf6\xd9\xfb\n\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x11\x00#\x005\x00\x00\x012\x16\x15\x11\x14\a\x01\x06#\"&5\x1147\x016!2\x16\x15\x11\x14\a\x01\x06#\"&5\x1147\x016!2\x17\x01\x16\x15\x11\x14\x06#\"'\x01&5\x1146\x02\x00\r\x13\x11\xfe \a\b\r\x13\x11\x01\xe0\a\x04\xe8\r\x13\x11\xfe \a\b\r\x13\x11\x01\xe0\a\xfb\xa8\b\x06\x02\x00\x12\x13\r\b\x06\xfe\x00\x12\x13\x06\x00\x13\r\xfa@\x14\b\xff\x00\x04\x13\r\x05\xc0\x14\b\x01\x00\x04\x13\r\xfa@\x14\b\xff\x00\x04\x13\r\x05\xc0\x14\b\x01\x00\x04\x03\xff\x00\n\x13\xfa@\r\x13\x03\x01\x00\n\x13\x05\xc0\r\x13\x00\x00\x00\x00\x04\x00\x00\xff \a\x00\x05\x00\x00\a\x00\x0f\x00\x17\x008\x00\x00\x004&\"\x06\x14\x162$4&\"\x06\x14\x162$4&\"\x06\x14\x162\x00\x10\x02\x04#\"'\x06\x05\x06\a\x06&'&7>\a7.\x0154\x12$ \x04\x02\x80KjKKj\x01\xcbKjKKj\x01\xcbKjKKj\x01\xcb\xf0\xfed\xf4ne\xad\xfe\xfa4\"\f\x14\x03\x04\x18\x05%\x0e!\x0f\x1a\x0e\x0f\x05\x92\xa7\xf0\x01\x9c\x01\xe8\x01\x9c\x02KjKKjKKjKKjKKjKKjK\x01.\xfe\xa4\xfe٫\x12\xad8\n\x03\x01\x0e\v\x0f\x16\x05!\x0e%\x1a00C'Z\xfd\x8f\xae\x01'\xab\xab\x00\x00\x00\x00\x05\x00\x00\xff\x00\a\x00\x05\x00\x00\a\x00\x0f\x00\x17\x00.\x00W\x00\x00\x00\x14\x06\"&462\x04\x14\x06\"&462\x04\x14\x06\"&462\x02 \x04\x06\x15\x14\x16\x1f\x01\a\x06\a6?\x01\x17\x1632$6\x10&\x01\x14\x02\x04#\"'\x06\x05\x06\a#\"&'5&6&>\x027>\x057&\x0254>\x01$ \x04\x1e\x01\x02\x80KjKKj\x01\xcbKjKKj\x01\xcbKjKKj\xe9\xfeh\xfe\x9dя\x82W\x1b\x18.\x98{+9E=\xcc\x01c\xd1\xd1\x01Q\xf0\xfed\xf4FK\xc6\xfe\xfa1A\x05\x0f\x18\x04\x03\x05\x01\n\x02\f\x02\a0\x15)\x18\x1e\v\x9d\xb5\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x02\xb5jKKjKKjKKjKKjKKjK\x01\x80\x8b\xec\x89p\xcbJ2`[Q?l&\x06\b\x8b\xec\x01\x12\xec\xfe\x8b\xae\xfe٫\b\xafC\x0e\b\x15\x11\x01\x04\x10\x04\x0f\x03\x0e\x02\b5\x178.H(Y\x01\x06\x96\x82\xed\xacee\xac\xed\x00\x04\x00\x00\xff\t\x04\x00\x05\xf7\x00\x03\x00\x06\x00\n\x00\r\x00\x00\t\x01\x11\t\x01\x11\x01\x19\x01\x01\x11\t\x01\x11\x02\x00\x02\x00\xfe\x00\xfe\x00\x02\x00\xfe\x00\x02\x00\x02\x00\x01Y\x01'\xfd\xb1\xfe\xd8\x03w\xfd\xb1\x01(\x04\x9e\xfd\xb1\xfe\xd8\x02O\xfe\xd9\x01'\xfd\xb1\x00\x00\x00\x01\x00R\xff\xc0\x06\xad\x05@\x00$\x00\x00\x01\x06\x01\x00#\"\x03&\x03\x02#\"\a'>\x017676\x16\x17\x12\x17\x16327676#\"\a\x12\x05\x16\x06\xad\n\xfe\xbe\xfe\xb3\xe5\x8eb,XHU\x12mM\x18\xa8.\x9cU_t\x17,\x167A3ge\b\rz9@x\x01S\xfb\x03\xfa\xec\xfea\xfeQ\x01\a\xa0\x01B\x01\x06Lb\x15\x97(\x8a\b\t\x81\x8b\xfe\xe1V\xf9\xa1\xa1U\x8b\x1a\x01\x89\v\b\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\n\x00\x00\x11!\x11!\x01\x03\x13!\x13\x03\x01\x06\x00\xfa\x00\x04=\xdd\xdd\xfd\x86\xdd\xdd\x01=\x05\x80\xfa\x00\x01\xa5\x02w\x01)\xfe\xd7\xfd\x89\xfe\xd0\x00\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\x12\x00A\x00U\x00\x00\x11!\x11!\x01\a\x17\a\x177\x177'7'#'#\a\x052\x16\a74.\x02#\"\x06\x1d\x01#\x1532\x15\x11\x14\x06\x0f\x01\x15!5'.\x02>\x015\x1137#\"76=\x014>\x02\x015'.\x01465\x11!\a\x17\x16\x15\x11\x14\x06\x0f\x01\x15\x06\x00\xfa\x00\x03\x8c\fK\x1f\x19kk\x19\x1fK\f_5 5\xfe\x96 \x19\x01\xae#BH1\x85\x84`L\x14\n\rI\x01\xc0\x95\x06\x05\x02\x01\x01\xbf&\xe7\x06\x04\x04\x03\f\x1b\x02v6\a\x05\x02\xfe\xed\x17S\x17\f\x0eF\x05\x80\xfa\x00\x04\xc0!Sr\x1999\x19rS!``\xa3 /\x157K%\x0es}H\x80\b\xfe\x82\x0e\f\x01\aXV\x0e\x01\x01\x04\x04\n\x05\x01\x83\x80\x06\x06\x03P\x1b\x1b\x1d\v\xfc\xc3V\t\x01\x03\x03\f\x06\x02\be\x16\a\x14\xfe\x8e\x0e\t\x02\tV\x00\x00\x04\x00\x00\xffd\a\x00\x06\x00\x00/\x009\x00Q\x00[\x00\x00\x01\x14\x06\a\x16\x15\x14\x02\x04 $\x02547.\x0154632\x176%\x13>\x01\x17\x05>\x0132\x16\x14\x06\"&5%\x03\x04\x17632\x16\x01\x14\x16264&#\"\x06\x0164'&\"\a\x0e\x01\"&'&\"\a\x06\x14\x17\x1e\x022>\x01&2654&#\"\x06\x14\a\x00;2\f\xd5\xfe\x90\xfeP\xfe\x91\xd5\v3>tSU<\xda\x01)t\x03\x18\x0e\x01q\x12H+>XX|W\xfe\xb2h\x01,\xdb:USt\xfa\xa2W|XX>=X\x03*\v\v\n\x1e\v)\xa0\xa0\xa0)\v\x1e\n\v\v+\x97^X^\x97\x16|WX=>X\x02\xb2:_\x19.2\x9b\xfe\xf8\x99\x99\x01\b\x9b//\x19a:Ru?\x98\n\x02\t\r\x10\x03Q%-W|XW>J\xfe(\t\x97=u\xfe\xe7>XX|WX\xfe`\v\x1e\v\n\n*((*\n\n\n\x1f\v+2\t\t2\xf8X>=XW|\x00\x00\x00\x01\x00E\xff\x02\x06\xbb\x06\x00\x000\x00\x00\x133>\x03$32\x04\x17\x16\x1d\x01!\x1e\x03>\x017\x11\x06\f\x01'&\x02'&\x127\x0e\x01\a!6.\x04/\x01\x0e\x03E\x01\x10U\x91\xbe\x01\x01\x94\xe7\x01noh\xfb\x9b\x01i\xa8\xd3\xd7\xc9I\\\xfe\xed\xfe\xa2\x8d\xbd\xf5\x02\x03\xe4\xd30<\x10\x02{\b >ORD\x16\x16\x87\xf9ƚ\x02\xe5~\xe7˕V\xd3ƻ\xff\xbco\xa3R \x1aC3\xfe\x877J\x026I\x01`\xc4\xf2\x01Tb<\x83^M~M8\x1a\x0f\x01\x01\x05O\x82\x97\x00\x00\x00\x04\x00\x00\xff\x80\t\x00\x05\x80\x00\t\x00\r\x00\x11\x00\x1b\x00\x005\x11!\x11\x14\x06#!\"&\x01\x15!5!\x15!5\x012\x16\x1d\x01!5463\t\x00^B\xf8@B^\x02\x80\x01\x80\xfd\x00\x01\x00\x06`B^\xf7\x00^B \x02`\xfd\xa0B^^\x01\"\x80\x80\x80\x80\x04\x80^B\xe0\xe0B^\x00\x00\x00\x03\x00\x00\xff\x00\x06\xbb\x06\x00\x00\x1f\x000\x00;\x00\x00%'\x0e\x01#\".\x0154>\x0232\x16\x177&$#\"\x04\x06\x02\x10\x12\x16\x0432$\t\x01\x06\x00!\"$&\x02\x10\x126$3 \x00\x17\x03#\x15#\x1132\x1e\x01\x0e\x01\x060\xdaJ\xf5\x8d\x93\xf8\x90U\x91\xc7n\x83\xe9L\xd7n\xfe\x9fʡ\xfe\xda\xd4~~\xd4\x01&\xa1\xd5\x01q\xfe@\x02\xb5t\xfeK\xfe\xee\xb6\xfe\xb4\xf0\x8e\x8e\xf0\x01L\xb6\x01\x04\x01\xa5}\x9f'`\x88 -\f\n-\xf6ox\x8a\x90\xf8\x92nǑUyl}\xa9\xc0~\xd4\xfe\xda\xfe\xbe\xfe\xda\xd4~\xd6\x02F\xfe\xa0\xfd\xfeڎ\xf0\x01L\x01l\x01L\xf0\x8e\xfe\xf5\xe9\xfet\xa0\x01`(88(\x00\x04\x00 \xff\x00\x06\xe0\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x00\t\x017!\x01'\x11\x01\x1f\x01\x11\t\x02!\x01\x05\x93\xfd\x9a\\\x03W\xfa\xb5\xb8\x04\x9f\x14\x93\xfd\xec\x01\\\xfe\f\xfc\xa9\x01d\x03;\x01\x82\x97\xfc\xdet\x03Z\xfd\x19`_\xfc\xa6\x01O\x02\u007f\xfc\xde\x02;\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\xf0\x00\v\x00\x17\x00}\x00\x00\x0154+\x01\"\x1d\x01\x14;\x012%54+\x01\"\x1d\x01\x14;\x012\x05\x11!\x114&\"\x06\x15\x11!\x114;\x012\x1d\x013\x114;\x012\x1d\x01354;\x012\x1d\x01354>\x02\x163\x11&5462\x16\x15\x14\a\x15632\x1632632\x1d\x01\x14\x06#\"&#\"\a\x1526\x1e\x02\x1d\x01354;\x012\x1d\x01354;\x012\x15\x11354;\x012\x02\x80\x10`\x10\x10`\x10\x02\x00\x10`\x10\x10`\x10\x02\x00\xfd\x80p\xa0p\xfd\x80\x10`\x10\x80\x10`\x10\x80\x10`\x10\x80\x05\f\a\x10\x01 !,! -&\x15M\x10\x11<\a\x10F\x1b\x12I\x13(2\x01\x10\a\f\x05\x80\x10`\x10\x80\x10`\x10\x80\x10`\x10\x02\x10\xe0\x10\x10\xe0\x10\x10\xe0\x10\x10\xe0\x10\x10\xfd\x10\x01@PppP\xfe\xc0\x02\xf0\x10\x10p\x02p\x10\x10pp\x10\x10pp\x06\a\x03\x01\x01\x01\x87\x0f#\x17 \x17#\x0f\x11\n\x0f\x0f\x10\xd2\x0f\r\x0f\f\x85\x01\x01\x03\a\x06pp\x10\x10pp\x10\x10\xfd\x90p\x10\x00\x01\x00\x00\x00\x00\t\x00\x05\x80\x00j\x00\x00\x01\x16\x14\a\x05\x06#\"'&=\x01!\x16\x17\x1e\x05;\x015463!2\x16\x15\x11\x14\x06#!\"&=\x01#\".\x05'.\x03#!\x0e\x01#\"&4632\x16\x1732>\x027>\x06;\x01>\x0132\x16\x14\x06#\"&'#\"\x0e\x04\a\x06\a!546\x17\b\xf0\x10\x10\xfe\xc0\b\b\t\a\x10\xfc\xa6%.\x10\x11\x1f\x17\x1f \x11`\x12\x0e\x01@\x0e\x12\x12\x0e\xfe\xc0\x0e\x12` :,.\x1c'\x12\x13\x17\x1c,-\x18\xfe\x98\x16\x8aXj\x96\x96jX\x8a\x16h\x18-,\x1c\x17\x13\x12'\x1c.,: k\x15b>PppP>b\x15k\x11 \x1f\x17\x1f\x11\x10.%\x04Z \x10\x02\xdb\b&\b\xc0\x05\x04\n\x12\x80:k%$> $\x10`\x0e\x12\x12\x0e\xfe\xc0\x0e\x12\x12\x0e`\x14\x1b6&L')59I\"Tl\x96ԖlT\"I95)'L&6\x1b\x149Gp\xa0pG9\x10$ >$%k:\x80\x12\x14\v\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\a\x00\x11\x00!\x00\x00\x00\x14\x06+\x01\x1132\x00\x10&#!\x113\x1132\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04~O8\xfd\xfd8\x01\x02\xb7\x83\xfeO\xb4\xfd\x82\x02\x87\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x03>pN\x01\r\xfe\xf7\x01\x04\xb8\xfc\x80\x01\r\x01i\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x04\x00\x00\xff\xd9\t\x00\x05'\x00'\x00:\x00M\x00a\x00\x00\x014&'\x06\a\x0e\x01#\"'.\x017654.\x01#\"\x06\a\x16\x17\x16\x14\x06\"'&#\"\x06\x14\x163!267\x14\x06#!\"&54676$32\x00\x17\x1e\x01\x17\x14\a\x06#\"'.\x0176\x10'&>\x01\x16\x17\x16$\x10\a\x06#\"'.\x017654'&676\x16\x17\x06mD5\a\x10\a)\x18\f\f\x1f\x1c\n\x17z\xd2{\x86\xe26lP\x16,@\x17Kij\x96\x96j\x04\x16Oo\x99Ɏ\xfb\xea\xa9\xf0ȕ>\x01>\xc3\xeb\x01[\x17t\x99\xfaa\x17)\x18\x13\x1a\f\x12GG\x12\f4?\x12a\x01\x00\x86\x17)\x17\x13\x1a\r\x12ll\x12\r\x1a\x1a>\x12\x01\xb6;_\x15-/\x18\x1c\x03\n9\x1eGH{\xd1z\x92y\x1cN\x17@,\x16K\x95ԕoN\x8e\xc8繁\xe4\x16\xb8\xe4\xfe\xc3\xe7\x19\xbby\xaf\x90!\r\x11?\x1ah\x01\x02h\x1a>$\r\x1a\x8eD\xfe\x18\xc7\"\r\x12>\x1a\xa4\xc2â\x1a?\x11\x12\f\x1b\x00\x02\x00$\xff\x00\x05\xdc\x06\x00\x00\t\x00n\x00\x00\x05\x14\x06\"&5462\x16'\x0e\x01\x15\x14\x17\x06#\".\x0554>\x032\x1e\x03\x15\x14\a\x1e\x01\x1f\x012654.\x04'&'.\x0354>\x0332\x1e\x03\x15\x14\x0e\x03#\"#*\x01.\x045.\x01/\x01\"\x0e\x01\x15\x14\x1e\x03\x17\x1e\b\x05\xdc~\xb4\u007f\u007f\xb4~\xe9s\x9b!\x92\xe9m\xb8{b6#\f\t\x1c-SjR,\x1b\b\x17\x1cl'(s\x96\x12-6^]I\x1c\x0ft\x8eg))[\x86\xc7zxȁZ&\x1e+6,\x11\x02\x06\x13\x1a4$.\x1c\x14\x0fX%%Dc*\n&D~WL}]I0\"\x13\n\x02\rY\u007f\u007fYZ\u007f\u007f\xbf\x0f\xafvJ@N*CVTR3\x0e\x13/A3$#/;'\x0e\"/\x1b\x1e\x02\x01fR\x1a-,&2-\"\r\a7Zr\x89^N\x90\x83a94Rji3.I+\x1d\n\n\x12&6W6\x10\x13\x01\x01>N%\x18&60;\x1d\x1996@7F6I3\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00+\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26%\x114&#!\"\x06\x15\x11\x14\x163!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x02\xc0\x12\x0e\xff\x00\x0e\x12\x12\x0e\x01\x00\x0e\x12\x01\xc0\x12\x0e\xff\x00\x0e\x12\x12\x0e\x01\x00\x0e\x12\x01\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01`\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x01\xff\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x17\x00'\x007\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x00 >\x01\x10.\x01 \x0e\x01\x10\x16%\"&5\x1146;\x012\x16\x15\x11\x14\x06#!\"&5\x1146;\x012\x16\x15\x11\x14\x06#\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01\x9e\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x01\xee\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfb\xae\x92\xfa\x01(\xfa\x92\x92\xfa\xfe\xd8\xfaN\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04@\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x01\xc0\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01`\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x01\xff\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x17\x00'\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x00 >\x01\x10.\x01 \x0e\x01\x10\x167\"&5\x11463!2\x16\x15\x11\x14\x06#\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01\x9e\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92n\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfb\xae\x92\xfa\x01(\xfa\x92\x92\xfa\xfe\xd8\xfaN\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\v\x00%\x00=\x00\x00%\x13\x16\a\x06#!\"'&7\x13\x01\x13!\x13>\x013!\x15\x14\x1626=\x01!\x15\x14\x1626=\x01!2\x16%\x11\x14\x06\"&5\x114&\"\x06\x15\x11\x14\x06\"&5\x1146 \x16\x06\xdd#\x03\x13\x13\x1d\xf9\x80\x1d\x13\x13\x03#\x06]V\xf9TV\x03$\x19\x01\x00KjK\x01\x80KjK\x01\x00\x19$\xfe\x83&4&\x96Ԗ&4&\xe1\x01>\xe1\x80\xfe\xc7\x1c\x16\x15\x15\x16\x1c\x019\x03G\xfc\xf9\x03\a\x18!\x805KK5\x80\x805KK5\x80!\xa1\xff\x00\x1a&&\x1a\x01\x00j\x96\x96j\xff\x00\x1a&&\x1a\x01\x00\x9f\xe1\xe1\x00\x06\x00\x00\xff\x00\b\x00\x06\x00\x00\x15\x00#\x00/\x00;\x00I\x00m\x00\x00\x012\x16\x14\x06+\x01\x03\x0e\x01#!\"&'\x03#\"&463\x01>\x01'\x03.\x01\x0e\x01\x17\x13\x1e\x013%\x114&\"\x06\x15\x11\x14\x1626%\x114&\"\x06\x15\x11\x14\x1626%\x136.\x01\x06\a\x03\x06\x16\x17326\x01\x03#\x13>\x01;\x01463!2\x16\x1532\x16\x17\x13#\x03.\x01+\x01\x14\x06#!\"&5#\"\x06\a\x805KK5\x0fs\bH.\xfb\x00.H\bs\x0f5KK5\x01e\x1a#\x02 \x02)4#\x02 \x02%\x19\x01\xa0&4&&4&\x01\x80&4&&4&\x01` \x02#4)\x02 \x02#\x1a\x05\x19%\xfb~]\x84e\x13\x8cZ\xa7&\x1a\x01\x80\x1a&\xa7Z\x8c\x13e\x84]\vE-\xa7&\x1a\xfe\x80\x1a&\xa7-E\x03\x00KjK\xfdj.<<.\x02\x96KjK\xfc\xe0\x02)\x1a\x01\xa0\x1a#\x04)\x1a\xfe`\x19\"@\x01\xa0\x1a&&\x1a\xfe`\x1a&&\x1a\x01\xa0\x1a&&\x1a\xfe`\x1a&&\x15\x01\xa0\x1a)\x04#\x1a\xfe`\x1a)\x02\"\x04\xda\xfed\x01\xb9Xo\x1a&&\x1aoX\xfeG\x01\x9c,8\x1a&&\x1a8\x00\x02\x00!\xff\x80\x06\xdf\x05\x80\x00\x03\x00O\x00\x00\x01\x13#\x03\x01\a\x06#!\x03!2\x17\x16\x0f\x01\x06#!\x03\x06+\x01\"'&7\x13#\x03\x06+\x01\"'&7\x13!\"'&?\x0163!\x13!\"'&?\x0163!\x136;\x012\x17\x16\a\x033\x136;\x012\x17\x16\a\x03!2\x17\x16\x03\xdf@\xfe@\x03\xfe8\a\x18\xfe\xb9@\x017\x0f\n\n\x048\x05\x1a\xfe\xb9Q\a\x18\xe0\x10\n\t\x03N\xfeQ\a\x18\xe1\x0f\n\t\x03N\xfe\xc9\x0f\n\t\x038\a\x18\x01G@\xfe\xc9\x0f\n\n\x048\x05\x1a\x01GQ\a\x19\xe0\x0f\n\t\x03N\xfeQ\a\x19\xe0\x0f\n\t\x03N\x017\x0f\n\t\x02\x00\x01\x00\xff\x00\x01\xf8\xe0\x18\xff\x00\f\x0e\x0e\xe0\x18\xfe\xb8\x18\f\f\x10\x018\xfe\xb8\x18\f\f\x10\x018\f\f\x10\xe0\x18\x01\x00\f\x0e\x0e\xe0\x18\x01H\x18\f\f\x10\xfe\xc8\x01H\x18\f\f\x10\xfe\xc8\f\f\x00\x00\x00\x00\x04\x00k\xff\x00\x05\x95\x06\x00\x00\x02\x00\x05\x00\x11\x00%\x00\x00\x01\x17\a\x11\x17\a\x03\t\x03\x11\x03\a\t\x01\x17\x01\x00\x10\x02\x0e\x02\".\x02\x02\x10\x12>\x022\x1e\x02\x03I\x94\x95\x95\x94\x83\x01\xd0\xfe\xce\x012\xfe0\xff]\x01@\xfe\xc0]\x00\xff\x02\xcf@o\xaa\xc1\xf6\xc1\xaao@@o\xaa\xc1\xf6\xc1\xaao\x01㔕\x03\x8c\x95\x94\xfca\x01\xd0\x012\x012\x01\xd0\xfd\x9d\x00\xff]\xfe\xbf\xfe\xbf]\x00\xff\x01p\xfe^\xfe\xc7\xc9|11|\xc9\x019\x01\xa2\x019\xc9|11|\xc9\x00\x00\x00\x00\x03\x00(\xff\x00\x03\xd8\x06\x00\x00\x02\x00\x05\x00\x11\x00\x00%7'\x117'\x13\t\x01\x11\x01'\t\x017\x01\x11\x01\x02T\xad\xad\xad\xad \x01d\xfd\xe5\xfe\xd7l\x01t\xfe\x8cl\x01)\x02\x1bq\xac\xac\x01n\xac\xac\xfd\xf1\xfe\x9c\xfd\xe4\x02\xc7\xfe\xd8l\x01u\x01ul\xfe\xd8\x02\xc7\xfd\xe4\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00)\x001\x00\x00$4&\"\x06\x14\x162\x004&\"\x06\x14\x162\x00\x10\x06 &\x106 \x13\x14\a\x01\x06+\x01\"&547\x016;\x012\x16\x04\x10\x06 &\x106 \x05\x00LhLLh\xfdLLhLLh\x04L\xe1\xfe\xc2\xe1\xe1\x01>\x81\r\xfb\xe0\x13 \xa0\x1a&\r\x04 \x13 \xa0\x1a&\xfd`\xe1\xfe\xc2\xe1\xe1\x01>\xcchLLhL\x03LhLLhL\xfe\x1f\xfe\xc2\xe1\xe1\x01>\xe1\x02\xc0\x14\x12\xfa\x80\x1a&\x1a\x14\x12\x05\x80\x1a&\xbb\xfe\xc2\xe1\xe1\x01>\xe1\x00\x00\x00\x05\x00\x03\xffG\x06\xfd\x05\xb9\x00\x06\x00\n\x00\x10\x00\x17\x00\x1d\x00\x00\x13\t\x01.\x017\x13)\x01\x011\x01\x13!\x1362\x01\x13\x16\x06\a\t\x011!\x1362\x17h\x03\x18\xfc\x9c\x12\x0e\ae\x01\xce\x02\x94\xfe\xb6\xfd\xf0\xc6\xfe2\xc6\b2\x050e\a\x0e\x12\xfc\x9c\x03\x18\xfe2\xc6\b2\b\x03>\xfc\t\x02v\r+\x15\x014\xfc\t\x06[\xfd\x9c\x02d\x17\xfd\x85\xfe\xcc\x15+\r\xfd\x8a\x03\xf7\x02d\x17\x17\x00\x00\x00\x04\x00\x00\xff \a\x00\x05\xe0\x00\x03\x00\x0f\x00\x13\x001\x00\x00\x0135#\x015\x06\a\x06&'\x17\x1e\x0172\x01!5!\x05\x14\a\x16\x15\x14\x04#\"&'\x06\"'\x0e\x01#\"$547&54\x12$ \x04\x12\x01\x80\xa0\xa0\x03Eh\x8b\x87\xf9`\x01X\xf8\x94\x81\xfe(\x02\x80\xfd\x80\x04\x80cY\xfe\xfd\xb8z\xce:\x13L\x13:\xcez\xb8\xfe\xfdYc\xf0\x01\x9d\x01\xe6\x01\x9d\xf0\x02\xc0\xe0\xfd\xd4\\$\x02\x01_K`Pa\x01\x01}\xe0\xc0\xbb\xa5f\u007f\x9d\xdeiX\x01\x01Xiޝ\u007ff\xa5\xbb\xd1\x01a\xce\xce\xfe\x9f\x00\x00\x00\x00\t\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00(\x00+\x00.\x00>\x00\x00\x01\x15#5\x13\x15#5\x01\x15!5\x01\x15!5\x01\x15!5\x01\x114&+\x01\x01'\a\x01#\"\x06\x15\x11\x14\x163!26\x017!\x057!\x05\x11\x14\x06#!\"&5\x11463!2\x16\x02\x03\xfc\xfc\xfc\x03\xf2\xfe\xab\x01U\xfd`\x02\xa0\xfd`\x03'\f\b \xfe\x86\xd2\xd2\xfe\x86 \b\f\f\b\x04\xd8\b\f\xfc\xa9\xb9\xfej\x02\x8b\xdd\xfej\x02\xe2V>\xfb(>VV>\x04\xd8>V\x02q\x80\x80\x00\xff\u007f\u007f\xfe\x01\x80\x80\x01\x00\x80\x80\x00\xff\u007f\u007f\xfc\xa4\x04\xd8\b\f\xff\x00\xab\xab\x01\x00\f\b\xfb(\b\f\f\x04^\x96\x96\x96\x14\xfb(>VV>\x04\xd8>VV\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x1f\x00=\x00\x00\x01&'&'&'&\x06\x1f\x01\x1e\x03\x17\x16\x17\x1e\x04\x17\x1676'&'&\x02\x01.\x05\x02' \f\x01\x1e\x03\x0e\x01\a\x06\x15\x01#\x01\x0e\x02.\x02\x03\x80h8\x8b\xd0\"$Y\n''>eX5,\t\x04,Pts\x93K\x99\x01\x0125\x1cM\xcc\xfeRLqS;:.K'\x01\x11\x01\xc1\x015\xe9\x8aR\x1e\x05\x0e\r\r\x01Ch\xfe\xe7\x16\x8bh\xac\x95\xba\x02\xd0\xc4R\xcat\x13\x11(\x10\x1e\x1f+e\x84^T\x11\bT\x8a\xaa\x82u B\x06\x03\"$\x15:\x012\xfe~<\x82\x9d\x98\xdc\xc6\x012\x88Hp\xb1\xa8\xe5\xaa\xe3wTT\x17\xfe\xb9\x01\x1d\x02\x18\x0e\x02 V\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00/\x007\x00G\x00W\x00g\x00\x00\x00.\x01\a\x04 %&\x0e\x01\x16\x17\x16\x17\x0e\x02\x0f\x01\x06\x16\x17\x1632?\x01673\x16\x1f\x01\x16327>\x01/\x01.\x02'676$4&\"\x06\x14\x162\x04\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x00 \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02&\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x05d\f-\x1a\xfe\xfb\xfe\xe8\xfe\xfb\x1a-\f\x1b\x1a\xc2m\x02\x1b\x1a\x1c\t\n\x16\x19\t\x0e,\x10\b6\x11*\x116\b\x10,\x0e\t\x19\x16\n\t\x1c\x1a\x1b\x02m\xc2\x1a\xfe\xb7KjKKj\x02\x8bo\xbd\xfe\xfb\xfe\xe2\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbd\xfeK\xfe\xc8\xfe\xe4\xcezz\xce\x01\x1c\x018\x01\x1c\xcezz\xce\x01Ȏ\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x03U4\x1b\x06>>\x06\x1b4-\x06.\f\x9e\xdeYG\x15\x190\n\x04)\x14\x8bxx\x8b\x14)\x04\n0\x19\x15GYޞ\f.\x06\xa3jKKjKq\xfe\xe2\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\x01lz\xce\xfe\xe4\xfe\xc8\xfe\xe4\xcezz\xce\x01\x1c\x018\x01\x1c\xce\xfe0\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x00\x03\x00D\xff\x00\x05\xbb\x06\x00\x00/\x007\x00H\x00\x00\x00\x16\a\x03\x0e\x01#\"'.\x017\x13\a\x16\x15\x14\a'654&#\"\a'67\x01'\a\x06.\x016?\x01>\x01\x17\x01\x16\x17\x16\x0f\x01%\x02\"&462\x16\x14\x0127\x17\x06#\".\x01547\x17\x06\x15\x14\x16\x05|D\x05,\x04=)\x06\x03,9\x03#\x8f7\x94\x89[͑\x86f\x89x\xa4\x01\b\x95\xb5!X:\x05 \xef\x1aD\x1e\x01\xe8$\f\x11+\xcd\x01s)\x94hh\x94i\xfc\xdajZ\x8b\x92\xbd\x94\xfb\x92t\x8b<\xcd\x02\xf6F/\xfd\xd9*8\x01\x03C,\x01\xad\bq\u007f\u061c\x89e\x86\x91\xce\\\x8ar\x1b\x01,W\xa1\x1e\x05BX\x1d\xd5\x17\a\x12\xfe\xe5\x15/C2\xe8\x14\x01\xa9h\x94hh\x94\xfa\xbe=\x8bt\x92\xfa\x94\xbc\x94\x8bXm\x91\xcd\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00>\x00N\x00Z\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x14\x0e\x02\a\x0e\x02\x1d\x01\x14\x06+\x01\"&=\x014>\x037>\x0154&#\"\a\x06\a\x06#\"/\x01.\x017632\x16\x02 \x0e\x02\x10\x1e\x02 >\x02\x10.\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03p\x12\x0e\xa0\x0e\x12\x12\x0e\xa0\x0e\x12\x01\x00\x1e=+& \x1d\x17\x12\x0e\xa0\x0e\x12\x15\x1b3\x1f\x1d5,W48'\x1d3\t\x10\v\bl\n\x04\az\xe3\x81\xdb\xee\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x01\x91\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01P\xa0\x0e\x12\x12\x0e\xa0\x0e\x12\x12\x01\xe22P:\x1e\x15\x12\x14\x1c\x0f \x0e\x12\x12\x0eD#;$#\x10\r\x19$\x1f*;\x1b\x14?\f\x06R\a\x1a\n\xc0\xb3\x01Cf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xfe\xb7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x04\x00'\xff\x03\x05Y\x06\x00\x00\t\x00>\x00O\x00`\x00\x00\x00\"&5462\x16\x15\x14\x01\x14\x06&'\x01.\x01\x0f\x01\x06\x1f\x01\x13\x03\x06\a\x06\a\x06'.\x0176\x1b\x01\a\x17\x16\x0e\x02\x0f\x01\x06.\x035\x03\x13632\x17\x01\x16\x1f\x01\a\x16\x05\x1e\x01\x1f\x01\x16\x17\x16\a\x06.\x01'#&'\x03\x01\x16\x15\x14\a\x06.\x01'&\x01\x166?\x0165\x01\xae\x80\\\\\x80[\x01\x8c(\t\x01\x06\x02|\x03\x93\x1f\x03\t\v\x14\x06r\xfe\xcb\x03\b\x03\x03\v\x04\xc9[A@[[@A\xfd#2#\x16\x17\x01\xb6\f\a\x02\x03\b\r\x8b\xfe\x9e\xfe7\xc0*\x1a\x06\x1a\x19\r<\x1b\x11\x02Y\x01\xa0\xa4\xde\x18$\x13\r\x01\x02\x03\f\x14\x18\x0f\x02\x01+\x01}\"(\xfd\xf7\x05\f\x03\x01\r\xa6q\xe087] F\x1b\x16\f \x13\x10\t\x01_\xfe\xad1\b\x05\x02\x05\v)\n\xac\x01\xe9\x01\x04\x02\x02\t\b\x00\x00\x00\a\x00\x03\x00\xe3\t\x00\x04\x1c\x00\x02\x00\v\x00#\x001\x00K\x00e\x00\u007f\x00\x00\x013\x03\x054&+\x01\x11326\x01\x13\x14\x06+\x01\"&=\x01!\a\x06#!\"&7\x0163!2\x16\x04\x10\x06#!\"&5\x11463!2\x01\x14\x0e\x03\a#>\x03?\x014.\x03'3\x1e\x03\x1f\x01\x14\x0e\x03\a#>\x03?\x014.\x03'3\x1e\x03\x1f\x01\x14\x0e\x03\a#>\x03?\x014.\x03'3\x1e\x03\x17\x01\xf8\xab\x01\x03Xe`64[l\xfd\xc2\x01\x13\x0e\xd8\x0e\x13\xfe\xdd7\n\x12\xfe\xf5\x15\x13\r\x02,\t\x12\x01L\x0e\x14\x03;\xfb\xc7\xfe\xf2\x0e\x14\x14\x0e\x01\f\xc8\x01\x98\x01\x0f\x1c=+3&9\x1a\x10\x01\x01\x01\x0e\x1a8&+)>\x1d\x11\x02\xb9\x01\x0f\x1c>+3&9\x1a\x10\x01\x01\x01\x0e\x198&+)>\x1d\x11\x02\xb6\x01\x0f\x1c=+3&8\x1a\x10\x01\x01\x01\x0e\x198&+)>\x1d\x11\x01\x02\x1e\x01\t\xa6Wj\xfe|r\x01\xca\xfd\f\x0e\x14\x14\x0e>Q\x0f$\x11\x02\xf5\x0e\x14\xc6\xfe~\xdc\x14\x0e\x02\xf4\x0e\x14\xfed\v$kaw+-wi[\x1b\x1b\b\x1d[\\\x83;/xgY\x1a\x1a\v$kaw+-wi[\x1b\x1b\b\x1d[\\\x83;/xgY\x1a\x1a\v$kaw+-wi[\x1b\x1b\b\x1d[\\\x83;/xgY\x1a\x00\x04\x00\x00\xff\x00\x05\x80\x05\xf2\x00J\x00\\\x00m\x00\x82\x00\x00\x054.\x01'.\x02'&#\"\x06#\"'.\x03'&47>\x037632\x16327>\x027>\x0254&'&#\"\a\x0e\x03\a\x06\a\x0e\x01\x10\x16\x17\x16\x17\x16\x17\x16\x17\x16327>\x01\x13\"&47654'&462\x17\x16\x14\a\x06\x16\"'&476\x10'&462\x17\x16\x10\a\x16\"'&47>\x01\x10&'&462\x17\x16\x12\x10\x02\a\x02i\x1a$\x02\x01\b\t\t\x0f$\x17^\x18\"\r\x06\n\x05\b\x01%%\x01\b\x05\n\x06\r\"\x18^\x17$\x0f\t\t\b\x01\x02$\x1aW \x14\x19\"@9O?\x1d\x1f\x06\x031&&18\x1b?t\x03\x03@\"\x19\x14 W\x9f\x1a&\x13%%\x13&4\x13KK\x15\xb86\x12\x13\x13pp\x13&4\x13\x96\x96\xa36\x12\x13\x13ZaaZ\x13&4\x13mttm\x99\v^x\t\x04-\x1b\b\x0e\v\v\x05\x15\x13\x1d\x04\x80\xfe\x80\x04\x1d\x13\x15\x05\v\v\x0e\b\x1b-\x04\tx^\v\x16=\f\b\x12\x11/U7C\f\ak\xda\xfe\xf2\xdakz'[$\x01\x01\x12\b\f=\x03\xa7&5\x13%54'\x134&\x13K\xd4K\x13\xb5\x13\x134\x13r\x01\x027>\x0254\x00 \x00\x15\x14\x06\"&54>\x022\x1e\x02\x04\x14\x06\"&462%\x14\x06\"&54&#\"\x06\x15\x14\x06\"&546 \x16%\x16\x06\a\x06#\"&'&'.\x017>\x01\x17\x16\x05\x16\x06\a\x06#\"'&'.\x017>\x01\x17\x16\x80&4&&4\xe6&4&&4S\x01\x00Z\xff\x00\x01\xad&4&&4\x02\xe9\x174$#\x1f\x1d&\x0f\xe1\x9f\x1a&&\x1aj\x96\x173$\"('$\xfe\xf9\xfe\x8e\xfe\xf9&4&[\x9b\xd5\xea՛[\xfd\xfd&4&&4\x01F&4&\x83]\\\x84&4&\xce\x01$\xce\x01\x8a\n\x16\x19\t\x0e\x13!\aD\x9c\x15\b\x10\x114\x15\xb7\x01%\t\x15\x19\v\f,\x10\\\xcd\x16\a\x10\x104\x15\xeb\xa64&&4&\x9a4&&4&\x01-\xff\x00Z\x01\x00\x874&&4&\x01\x00;cX/)#&>B)\x9f\xe1&4&\x96j9aU0'.4a7\xb9\x01\a\xfe\xf9\xb9\x1a&&\x1au՛[[\x9b\xd5\xdb4&&4&@\x1a&&\x1a]\x83\x83]\x1a&&\x1a\x92\xceΏ\x190\n\x04\x16\x13\xb2u\x104\x15\x15\b\x10\x89\x85\x190\n\x04)\xee\x9b\x104\x15\x16\a\x10\xaf\x00\x00\x00\x00\x04\x00\x03\xff\x00\b\xfd\x06\x00\x00\x11\x00#\x00g\x00\xb0\x00\x00\x01&'.\x01#\"\x06\x15\x14\x1f\x01\x1632676%4/\x01&#\"\x06\a\x06\a\x16\x17\x1e\x01326\x01\x0e\x01'&#\"\a2632\x16\x17\x16\x06\a\x06#2\x17\x1e\x01\a\x0e\x01+\x01&'%\a\x06#\"'\x03&6?\x01\x136\x1276\x1e\x01\x06\a\x06\a676\x16\x17\x16\x06\a\x06\a632\x17\x1e\x01%\x13\x16\x06\x0f\x01\x03\x06\x02\a\x06#\"'&6767\x06\a\x06#\"&'&6767\x06#\"'.\x017>\x01\x17\x16327\"\x06#\"&'&6763\"'.\x017>\x01;\x02\x16\x17\x057632\x04\b;\x19\x11>%5K$\n\"0%>\x11\x19\x02s$\n\"0%>\x11\x19;;\x19\x11>%5K\xfeV\x11L#>H30\x03\r\x03\\\x9d(\x11\x1b$\x12\x15\x15\x12$\x1b\x11(\x9d\\\x06\x10\x1c\xfe\xde\xef\x0e\x0f(\x11\xa0\v\x0e\x16є\x11\x95y\x1fO2\a\x1fF/{\x90(?\x04\x050(TK.5sg$\x1a\x03\xb1\xa0\v\x0e\x16є\x11\x95y\x1a#-\x1d\x19\a\x1fF/{\x90\x04\b$7\x04\x050(TK.5sg$\x1a\x12\x11L#>H30\x03\r\x03\\\x9d(\x11\x1b$\x12\x15\x15\x12$\x1b\x11(\x9d\\\x06\x01\x0e\x1c\x01#\xef\x0e\x0f(\x02@\x025\"'K58!\b\x1f'\"5\x828!\b\x1f'\"5\x02\x025\"'K\x01\x12#\x1a\x11\x1f\x11\x01dS$K\x11\t\t\x11K$Sd\x02\x02\x1bx\a#\x01@\x171\rw\x01\v\x9b\x01\x11d\x19\a>N\x1a;ET\x11\x050((?\x04\n-\n2\x12K|\xfe\xc0\x171\rw\xfe\xf5\x9b\xfe\xefd\x16#\x1fN\x1a;ET\x11\x010$(?\x04\n-\n2\x12K$#\x1a\x11\x1f\x11\x01dS$K\x11\t\t\x11K$Sd\x02\x02\x1bx\a\x00\x00\x00\x04\x00\x00\xff\x00\a\x00\x06\x00\x00\x13\x00D\x00N\x00\\\x00\x00\x01\x14\x162654& \x06\x15\x14\x16265462\x16\x02\"\x0e\x02\x15\x14\x162654\x00 \x00\x15\x14\x0e\x01\a\x0e\x03\x15\x14\x06#\"\x06\x14\x1632654>\x027>\x0354.\x01\x01\x17\x01\x06\"/\x01&47\x01\x17\x16\x14\x0f\x03&'?\x0162\x04 &4&\xce\xfe\xdc\xce&4&\x84\xb8\x84h\xea՛[&4&\x01\a\x01r\x01\a$'(\"$3\x17\x96j\x1a&&\x1a\x9f\xe1\x0f&\x1d\x1f#$4\x17[\x9b\xfd\xc2\xe2\xfd\xbd\f\"\f\xa8\f\f\x06@\xa8\f\f\xe9\x1aGB\x81[\xcf\r\"\x02\xc0\x1a&&\x1a\x92\xceΒ\x1a&&\x1a]\x83\x83\x01\xe3[\x9b\xd5u\x1a&&\x1a\xb9\x01\a\xfe\xf9\xb97a4.'0Ua9j\x96&4&\xe1\x9f)B>&#)/Xc;u՛\xfd\x8c\xe2\xfd\xbd\f\f\xa8\f\"\f\x06\x06\xa8\f\"\r\xe9\x19G\x99i[\xcf\f\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00X\x00h\x00\x00\x01\x14\a\x0e\x01\a\x0e\x01\a\x06#\"&5467632\x16\x014&'&#\"\a'>\x0154#\"\a\x0e\x02\x15\x14\x1632\x14\a\x06\a\x0e\x01#\"54>\x0354'.\x01#\"\x0e\x01\x15\x14\x1632>\x017>\x01767632\x17\x16326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03b\r\v)\n\x02\x05\v\x14\v:4FD\x1c\x17\x1c\x11\x01\xe6N\r\x15\r[\x87\x02\x031\xf2\x18,^\x95J\xa1\x93\x19\x01\x04\x16\x0eK-*\x15\x1d\x1e\x16\a\x18E\x1f#9\x19gWR\x92Y\x15\x06\x13\x05\x03\vvm0O\x01\x03\x05\t\xb8\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\xfd\x1bC2\xc82\v\x03\x01\x02c@X\xac&\x0e!\xfe9\x0e{\x05\bM\x02\x16\xe2A\xe9\x06\x11\x91\xbc_\x92\x9e\x06\x02\"S4b/\x18/ \x19\x0f\x01\x03\a\x16\x1dDR\"Xlj\x92P\x16Y\x16\f\x06<\x12\x01\t\x02\x0f\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x02\x00%\xff\x00\x05\xda\x05\xff\x00\x19\x00e\x00\x00\x014.\x02#\"\a\x06\x02\x15\x14\x1e\x0232\x16>\x0276\x1276\x01\x14\x06#'.\x02#\"\a\x06\a\x0e\x01\a\x0e\x03#\"&54>\x0132\x16\x17\x14\x0e\x03\x15\x14\x1632>\x03754&*\x01\x06#\"&54>\x02763 \x11\x14\x02\a\x17>\x0132\x17\x1e\x01\x02\xe8\x04\r\x1d\x17''il\x11$E/\x04\x1c\f\x14\n\x02\x10@\x10\x13\x02\xf2\x0f\b\x06\x16P@\x1f\xa7\xb8\x0f\x06\n\x1d\b\x17^\x83\xb2`\x87\x9f'W6&\xa4\x01!.. ! -P5+\x16\x05\a\n\n\n\x01\xe3\xfaE{\xbdn46\x01vL\x05\x03e\xa3V\x16\x1f\x13z\x04\xcf\x18\x1d\x1f\x0f\x17:\xfe\xf7\x89,SN/\x01\x01\x05\f\nM\x015M[\xfd\xa7\a\r\x01\x03\x10\t]\b\x13$\x8b\x1f[\xb1\x98^\xa7\x885\x80iC\x1c\x01\x17'2H&!(?]v`*\t\x02\x03\x01\xf5\xe2l\xe2\u008d\x13\t\xfe\x98b\xfe\xa2$\x039>\r\a\xbf\x00\x03\x00\x01\xff\x00\x06\u007f\x05\xfb\x00=\x00R\x00\x87\x00\x00\x012\x1f\x01\x16\x1f\x01\x16\a\x03\x0e\x01\a\r\x01#\"&5467%!\"&7>\x013-\x01.\x017>\x01;\x01\x05%.\x017>\x0132\x17\x05\x172\x16326/\x01.\x0176\a\x17/\x02\x03.\x01'&676\x16\x1f\x01\x0e\x01\a\x06\x16\x01\x13\x16\x0f\x01\x06\x0f\x016/\x01&/\x01&#\"\a\x03&676\x16\x17\t\x01&676\x16\x17\x13\x03&676\x16\x17\x13\x17\x1e\x016/\x01&672\x16\x03? \x1b\xde=1\x92(\vH\x06/ \xfd\xf1\xfe\xa0\t'96&\x01\x04\xfe@)9\x02\x02<'\x01\xba\xfd\xf7)2\x06\x069%\n\x01\xe1\xfe\xa1&0\x06\x066#\x06\x0e\x01\xc0\xd9\x01\x04\x01\x17\x0f\x14\xba#\x0e\x19\x1b\x15\xba\xda\x05$\xee\x01\x03\x01\x18\v \x1fJ\x1b\x8e\x02\x06\x01 \x12\x03\xa5\x0f\x04\x0f0\f7j\x02)\x925@\xde\"*3%\xeb\x19\x0e\"!M\x18\x01\n\xfe\xfa\x15\x15%#K\x14\xf1\x88\x0f\x15\"%N\x11\xc1e\b\x1e\x18\x01\f\x028)'8\x03_\x12\x94(9\xaa.<\xfec +\x048 8(%6\x05 <)'4\x01@\x05@)#-<^\n?%$-\x02`%\x01.\r}\x17Q!&\xca}%\x02&\x01\x06\x01\x05\x01\x1fN\x19\x17\v\x1c\x93\x01\x05\x02-l\x01\xa7\xfe\xf6IJ\xdb;\x1c6>/\xaa=*\x94\x17%\x018!Q\x17\x16\x10 \xfe\xa0\x01\xc7#P\x13\x12\x18\"\xfe\\\x01Q#N\x11\x13\x1a&\xfea\xc4\x0f\x05\x14\x10\xe0)<\x019\x00\x00\x04\x00\x00\xff\x1e\a\x00\x05b\x00R\x00]\x00m\x00p\x00\x00%\"'.\x01'&54>\x0676%&547632\x1f\x0163 \x00\x17\x16\x14\a\x0e\x01\a\x16\x15\x14\a\x06#\"/\x02\x017\x06\a\x16\x1a\x01\x15\x14\a\x06#\"'\x01\x06\a\x16\x00\x15\x14#\"&/\x01\x03\x06\a\x1e\x01\x17\x13\x14%\x17$\x13\x02%\x1e\x01\x15\x14\x06\x00\x14\x1632\x16\x15\x14\x162654&#\"%'\x17\x01O\x02\x04V\xa59\x15\x04\x04\n\a\x0e\x06\x12\x02\xb8\x01\fn\x11t\f\x12\n|\\d\x01\n\x01ϓ\x14\x14[\xff\x97n\x11t\v\x13\n|@\xfeD\a:)\x03\xf8\xee\t\r;9\x03\xfe8'+\x18\x01|\v\x0e\x89\x04j\xe0,\"\x02 \a\xb0\x0341\x01\x11\xb1\xb4\xfe\xe9CH^\xfen\x1c\x14Vz\x1c(\x1c\xb2~\x14\x01R\t\a\xb4\x029\xb0\\\x1e'\t\x14\x10\x14\f\x16\b\x17\x03\xfbr\xc6\r\x13\n@\x10\xe5\x13\xfe\xed\xe8\x1fL\x1f\x8e\xdf@\xc6\r\x14\t@\x10\xe5w\x034\a\x18\x17\x05\xfe6\xfeH\x03\a\x02\x03\a\x03I\x1c(+\xfdC\x04\n,\x06\xc5\x01\x9d55\x03,\f\xfe\xb9\nf[o\x01\x12\x01\x15p@\xa9\\j\xbd\x02;(\x1czV\x14\x1c\x1c\x14~\xb2\x11\x04\a\x00\x00\x00\x00\x04\x00\x00\xff\x97\x04\xfe\x05i\x00\x1f\x00/\x005\x00O\x00\x00\x01\x14\a\x06#\"'&54>\x0132\x17\x06\a&#\"\x06\x15\x14\x16 654'67\x16'\x14\x02\x0f\x01\"'>\x0454'\x16'\x15&'\x1e\x01\x13\"'6767\x0e\x01\a&546767>\x017\x16\x15\x14\a\x0e\x01\x04\x1a\x93\x94\xe6蒓\x88\xf2\x93`V \aBM\xa7\xe3\xe1\x01R\xe0 B9)̟\x9f\x0e\x1d!S\u007fH-\x0f\x0377I\x85Xm\xfdSM\xdaH\x13\x02*\xc3k#\"\x1a.o;^\x1bJ\x18 q\x01\xaeן\xa1\xa1\x9fד\xf7\x92\x1f>@\x1c\xf6\xa8\xaa\xed\xed\xaaYM\r$bK\xc0\xfe\xced\x01\x05 \x8d\xa8ү[E\"\xa0\xa2\x02\xd6\xe2;\xff\xfe\xb9Kx\u007f%\x13^\x91\x196;%T\x1a,\x1e\x10U:i\x94m=Mk\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1a\x00)\x00.\x00D\x00T\x00\x00\x014'\x06\a\x16\x15\x14\x06\"&54632\x1767&#\"\x06\x10\x16 6\x03\x16\x15\x14\x0e\x03\a\x16;\x016\x114'.\x01'\x16\x054'\x06\a\x0e\x01\x15\x14\x17>\x017\x0e\x01\a\x1632676%\x11\x14\x06#!\"&5\x11463!2\x16\x04\x1a\x1c),\x16\x9a蛜s5-\x04\x17\x0254&#\"\x06#\"'654'.\x01#\"\a\x06\x15\x14\x17\x06#\"&#\"\x06\x15\x14\x1e\x02\x15\x14\a\x06\a\x06\x15\x14\x17\x1e\x0232632\x1e\x0232>\x0232\x1632>\x0176\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xff\x16Cf\x1d\a'/'%\x14\f(\v\x04\b\x05\x11$\x86U\xc7L\x11\x05\x04\n\f(\n\x15#'/'\a@\x86\x16\x89\x02\b\x0f\x10\f3\x0e#@,G)+H+@#\x0e3\r\x10\x0e\b\x02\x89\x01\x01\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\x84\x16\x05\x0fX@\x13\x06\x0f\x16\f\x1d\x16\x13\x19\x10\x02_\x13O#NW\xa5#O\x13_\x02\x0f\x18\x14\x15\x1d\f\x16\x0f\x06\x13\x8a\x1d\x05\x16.\x16\x05*\x13\t\x1e#\x1e\x1e#\x1e\b\x14(\x05\x16\x01\xfb\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x01\x00\x0f\xff\x80\x06q\x05\x80\x00[\x00\x00\x016\x16\x17\x16\x15\x14\a\x1632632\x16\x15\x14\x0e\x02\x15\x14\x17\x1e\x01\x17\x16\x17\x16\x15\x14\a\x0e\x02#\"&#\"\a\x0e\x04#\".\x03'&#\"\x06#\".\x01'&54767>\x017654.\x0254632\x16327&547>\x01\x03P\x86\xd59\x1b\t\x0e\x0e\x12B\x12\x1d6?K?\f%\x83O\x1c4\x1c\xdb\a\b\x14\x17\x14T\x16%\x19 >6>Z64Y=6>\x1f\x1a%\x18S\x11\x19\x14\b\a\xdb\x1c4\x1cN\x85$\f?L?4\x1d\x0fB\x14\x12\x0e\t\x1b@\xd8\x05\x80\x01\x8b{:y/\x90\a\x1b$\x1c ,\x13'\x1c\x0f\x1cR\x88!\f\v\x06\x1dF!\v8%\r\x05\x05#)(\x1b\x1b()#\x05\x05\x0f%:\v!F\x1d\x06\v\f \x8aQ\x1c\x0f\x1c'\x14+\x1f\x1b%\x1a\a\x8e0z:\x89z\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00O\x00_\x00\x00\x014'.\x01'&54>\x0254&#\"\x06#\"'654'.\x01#\"\a\x06\x15\x14\x17\x06#\"&#\"\x06\x15\x14\x1e\x02\x15\x14\a\x06\a\x06\x15\x14\x17\x1e\x0232632\x1e\x0232>\x0232\x1632>\x0176\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00\x16Cf\x1d\a'.'%\x14\v(\f\x04\b\x05\x11$\x85V\xc6M\x12\x06\n\x05\v)\n\x14#'.'\a@\x86\x16\x8a\x02\b\x0e\x10\r3\r#A,G)+H+A#\r4\r\x0f\x0f\b\x01\x8a\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\x84\x16\x05\x0eXA\x0e\v\x0f\x16\f\x1d\x16\x13\x19\x10\x02?4N$NW\xa5&M&L\x02\x10\x19\x14\x15\x1d\f\x16\x0f\v\x0e\x8a\x1d\x05\x16/\x16\x05*\x13\n\x1e#\x1e\x1e#\x1e\t\x13+\x03\x16\x03\v\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x01\x00\x00\xff\x80\t\x00\x06\x00\x00O\x00\x00\x01\x0e\x05\a\x0e\x01\a\x0e\x03\a\x06\a$\x05\x06\a>\x01?\x01>\x0376\x052\x17\x1e\x01\a\x03\x06'&#\"\x04\a\x06.\x02/\x01454327\x12\x0032\x1e\x05\x177>\x047>\x03\t\x00EpB5\x16\x16\x03\n3\x17\x0fFAP\b/h\xfe\xab\xfe\xdf\\\xd3/N\x10\x0fG\xb8S\x85L\xba\x01\x17\x01\t\v\x06\x06\xc2\x0f \x80\xe2\x92\xfe\x00\x88R\x86P*\f\x01\x06\x8a\xe9\xc0\x01m\xc9\x05\x1395F84\x0ef\x02&3Ga4B|wB\x06\x00.\\FI*/\x06\x12\xed.\x1d?&,\x06\x1f\xc8\x0e\xac5~\x10\x1e\a\a\x1bK %\r\x1f&\x03\x06\x16\v\xfe\xa7\x1d\a\x18Y\x02\x01\x1c.\"\x11\x01\x01\x01\x067\x01n\x01<\x01\t\x0f\"-I.\xb1\x04M`{\x90ARwJ!\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00F\x00X\x00^\x00d\x00j\x00\x00\x01\x14\a'\x17\x06\a'\x17\x06\a'\x17\x06\a'\x17\x06\"'7\a&'7\a&'7\a&'7\a&547\x17'67\x17'67\x17'67\x17'632\x17\a7\x16\x17\a7\x16\x17\a7\x16\x17\a7\x16\x174\x02$#\"\x0e\x02\x15\x14\x1e\x0232$\x12\x13\x11\t\x01\x11\x01\x11\x01\x11\t\x01\x11\x01\x11\t\x01\x11\x01\x05*\x05\xec\xe0\x13'ֱ,?\x9dg=OO\x0e&L&\x0eNJBg\x9d;1\xb2\xd6'\x13\xe0\xed\x05\x05\xee\xe1\x13'ֱ.=\x9egCIM\r$'&&\x0eNJBg\x9e=.\xb1\xd5%\x15\xe0\xed\x05\x1e\x9d\xfe\xf3\x9ew\u061d\\\\\x9d\xd8w\x9e\x01\r\x9dI\xfdo\xfdo\x02\x91\x02\xc4\xfd<\xfd<\x05\xc4\xfd\x00\xfd\x00\x03\x00\x02\x80-\x1f\x0eNIDg\x9e=/\xb2\xd7%\x16\xe4\xf0\x06\x06\xee\xe2\x13(ײ+A\x9ehEHO\x0e*\"#*\x0eOICh\x9f=/\xb2\xd7'\x13\xe0\xec\x06\x06\xed\xe1\x13(ֲ/=\x9fh>ON\x0e\x1f.\xa0\x01\x0f\x9d]\x9d\xdaxwڝ]\x9d\x01\x0f\x02\x1e\xfd\x02\xfe\x81\x01\u007f\x02\xfe\x01\u007f\xf9\xcb\x01\x9c\x037\x01\x9b\xfee\xfc\xc9\x03[\xfc\x80\xfe@\x01\xc0\x03\x80\x01\xc0\x00\x00\x03\x00\x00\xff\x00\x06\x80\x06\x00\x00\x14\x00)\x006\x00\x00\x01!\a!\"\x06\x15\x11\x14\x16\x17\x163\x15#\"&5\x1146%3\x01\x0e\x06\a567654'\x013\x13\x01\x11!67!\x114&'7\x1e\x01\x01S\x02\xb3\x1a\xfdgn\x9dy]\x17K-\x8c\xc7\xc7\x03\xdf\xf7\xfe\x1e\x17#75LSl>\xa39\x14\x14\xfe\xe3\xe4\xbb\x03V\xfc\xe5%\b\x02\xa6cP\x19e}\x05&H\x9en\xfc\xfd_\x95\x13\x05HȌ\x03\x03\x8c\xc8\xda\xfa\xf2=UoLQ1!\x02\xc3\x1a\x9c4564\x02\xdd\xfd\xb7\x01\xf2\xfb\xa97\x12\x04\x0eU\x8c\x1dC\"\xb3\x00\x00\x00\x00\n\x00\x00\xff\x00\a\x00\x06\x00\x00\a\x00\x14\x00!\x00-\x009\x00[\x00n\x00x\x00\x90\x00\xe7\x00\x00\x00\x14\x06\"&462\x0354&\"\x06\x1d\x01\x14\x16326754&\"\x06\x1d\x01\x14\x16326754&\"\x06\x1d\x01\x14\x1626754&\"\x06\x1d\x01\x14\x1626\x01\x06\x04#\".\x02547\x06\x15\x14\x12\x17632\x17632\x1762\x17632\x16\x176\x12'4#\"\a\x06#\"547\x06\x15\x14\x163276\x014&\"\x06\x15\x14\x1626\x014.\x01#\"\x06\a\x06\x15\x14\x16327632\x16\x15\x14\a>\x01\x05\x14\x02\a\x06\x04\x0f\x01\x15\x14\x06#\"'\x06\"'\x06#\"'\x06#\"&5\x06#\"'67&'\x16327&'&54>\x0332\x1767>\x017>\x027>\x0132\x17632\x17\x16\x15\x14\x0e\x02\a\x1e\x01\x15\x14\a\x16\x17632\x17\x16\x03T\"8\"\"8\x82)<()\x1d\x1e)\xac(<))\x1e\x1d)\xae)<))<)\xae)<))<)\x01\fT\xfeد{ՐR\x15h\x82x\x1e=8\x1e 78\x1e n \x1e8\x1c1\rp\x82\x8eH\x11\x1e_6\xe2\x1eS\xb2\x92oc\r\xfeF@b@?d?\x02uK\x97bM\x9070[f5Y$\x1135\x04KU\x01\x17C<:\xfe\xee[\x04;+8\x1e n \x1e87 \x1e8/8Zlv]64qE 'YK\xc00\x18\x12-AlB;\x16\x13\x17\x02\x14\x03\n\x1a\x18\x10W\xf9\x88#\x1b;WS9\x05\f\r\x13\x01\x11&\x10\x9d(\x19#-7Z\x04\xe8://:/\xfaTr\x1e++\x1er\x1e,,\x1er\x1e++\x1er\x1e,,\x1er\x1e++\x1er\x1e,,\x1er\x1e++\x1er\x1e,,\x02ʠ\xc7g\xab\xe0xXV\xafע\xfe\xd4e9222222\x1f\x19^\x01\x13\xb3K\x06\x13\xf3Vv\u007f\x94\x96\xddF0\x02\xb22OO23OO\xfe\xe0`\xa6lF;\x9fmhj\x13\x0684\x1a\x14D\xc3ro\xfe\xebB@\x9d\x1a\x01r+@222222C0DP\x01\x13\x1f`\a.\xc0r8h9\x89\x9c~T4\x1d\x19\x03\x14\x06\x0f.&\x14o\x84\x04@9\x05\a\x05\x11\x0f\x13\x01\x06\x18\f\x06\x13\x8a\xf0\x1e1P\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00%\x001\x00\x00\x014'!\x153\x0e\x01#\"&4632\x177&#\"\x06\x10\x16326%35#5#\x15#\x153\x153\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x95\x06\xfe\x96\xd9\f}Pc\x8c\x8cc]\x0132\x16\x06\x001\xae\xa4I\xfe\xe3U\xa4Π?L\x80\xb6\x80L?\xbe\x99cc\x0e\xc34MX\v\x8a\x14\x1a&\x04\x00\xfc\xb90\x0e4;0\xfe\xae\x05X\x19pD[\x80\x80[Dp\x19D,\x0f\x02)\x12\x02&&\x00\x00\x05\x00\x00\xffQ\t\x00\x05\x00\x00\x05\x009\x00V\x00\\\x00\x94\x00\x00\x1226&\"\x06\x05.\x05'\a\x06&'&6?\x01.\x02\x06#\"\x0f\x01#\x1126\x1e\x03\x17\x01\x16327\x1667\x167>\x01'\x1632>\x01&\x173\x11#'&+\x01\"\x0f\x01\x06\x14\x17\x1e\x01?\x016\x1e\x01\a\x1e\x01\x17\x1e\x01\x17\x16\x0426&\"\x06\x01\x11\x14\x06#!\x0e\x01\a\x0e\x01\a\x0e\x01'\x0e\x01.\x01'\x01!\"&5\x11463!>\x06;\x012\x176;\x012\x1e\x06\x17!2\x16\x98P P \x06\t\n9\x1a2#.\x16}S\xfbP9\x01:\xb1\x16:%L\v\\B\x9e\x9b\x05 \f\x1b\x0e\x15\b\x01)spN/9o\x11J5\x14 \x02\n!+D\x1f\a\x84`]\x9dBg\xa7Y9\xd1\x1c\x1b+\x86,\xc1\x199%\n\x10P\x14\x1dk\v4\x01\x00P P \x01\b&\x1a\xfeN\x1bnF!_7*}B<\x84{o0\xfe\xe1\xfe\x9a\x1a&&\x1a\x01\xa5\x0eB\x1d;*<@$ucRRc\xa7#@16#3\x1b7\x0e\x01c\x1a&\x01\x80@@@\x06\rJ\"@*4\x17\x8c^\x04`E\xb2D\xce\v\v\x01\x02B\x9e\xfd\xe0\x01\x01\x03\x06\v\b\xfe\xdco/\x1489\x062\x127\x17\n*@O\x18\x02\x00\xb4LC\xf3!T!3\x022\xda\x17\x033\x1f\x13X\x18$\x8b\x0fBJ@@@\x02\x00\xfd\x80\x1a&AS\n0C\f59\x04\"\v'D/\x01\x1a&\x1a\x02\xa0\x1a&\x0eD\x1c4\x17\x1c\v88\f\x11$\x1a5\x1fA\x10&\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00%\x00O\x00\x00\x01\x11\x14\x06#!\"&5\x1147>\x067>\x032\x1e\x02\x17\x1e\x06\x17\x16\x01$7>\x01/\x01.\x01\a\x06\a\x0e\x03\".\x02'&'&\x06\x0f\x01\x06\x16\x17\x16\x05\x1e\x042>\x03\a\x00^B\xfa@B^\v\b>\x15FFz\xa5n\x05_0P:P2\\\x06n\xa5zFF\x15>\b\v\xfd\xcc\x01\aR\v\x03\b&\b\x1a\v\xe7p\x05^1P:P1^\x05\xba\x9d\v\x1a\b&\b\x03\vR\x01\a\nP2NMJMQ0R\x03r\xfc.B^^B\x03\xd2\x0f\t\a7\x11:5]yP\x04H!%%\"F\x05Py]5:\x117\a\t\xfd\xa8\xbf=\b\x19\v4\v\x03\b\xa9Q\x03H!%%!H\x03\x86t\b\x03\v4\v\x19\b=\xbf\b<\"-\x16\x16/ ?\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x001\x00P\x00p\x00\x00\x01\x17\x16\x06\a\x0e\x02\a\x0e\x03+\x02\".\x02'.\x02'.\x01?\x01>\x01\x17\x16\x17\x1e\x03;\x022>\x027$76\x16\x13\x11&'&%.\x03+\x02\"\x0e\x02\a\x0e\x02\a\x06\a\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11476\x007>\x03;\x022\x1e\x02\x17\x1e\x02\x17\x16\x05\xc2'\b\x03\n+\xa7~\x04'*OJ%\x01\x01%JN,&\x05x\xa7'\v\x03\b%\b\x1b\v^\xd4\x05M,E\x18\x01\x01\x18E,M\x05\x01\x027\v\x1a\xc6ZE[\xfe\xd6\x03P*F\x18\x01\x01\x18F*P\x03\xd7\xc9:5\x0e\a\x13\r\x05\xc0\r\x13\x80^B\xfa@B^){\x01\xc6\x06$.MK%\x01\x01%KM.$+\xe2\xe2X)\x02o3\v\x19\b\"\x81a\x03 2\x17\x172!\x1f\x04]\x81\x1e\b\x19\v4\v\x04\tI\xa3\x04>\x1f\"\"\x1f>\x04\xc6,\b\x03\xfd&\x03\xa0S8J\xe6\x02B\x1e##\x1eB\x02\xa6\x9f12\f\a\xfc`\r\x13\x13\x03\xad\xfc`B^^B\x03\xa08&r\x01a\x05\x1e#1\x18\x181#\x1e$\xac\xb6R&\x00\x00\x00\x00\v\x00\x15\xff\x00\x05\xeb\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x1a\x00\x1e\x00\"\x00&\x00.\x002\x00v\x00\x00%\x17/\x01\x01%'\x05\x01\x17\x03'\x01%\x03\x05\x01\x17/\x01\x14\x16\x06\x0f\x01\x17\x16\x01\x05\x03%\x017\a\x17\x01%\x03\x05\x017'\a\x17\x16\x0f\x01%7\x0f\x02'\a\x14\x0f\x01\x06/\x01\x17\x14\a\x05\x06#&5'&\x03&?\x01&'\x03&?\x01&'\x03&7%2\x17\x05\x16\x15\x13\x14\x0f\x01\x17\x16\x15\x1776\x1f\x0174?\x016\x1f\x01\x1e\x01\x0e\x01\x15\x14\x0f\x01\x06\x01J\xca\"\xd8\x01\x12\x01\x12\v\xfe\xd4\xfe\xee\xe30\xf5\x01<\x01=\x0e\xfe\xa0\x01\x8d_\x02g\x02\x02\x04NU\a\xfd?\x01\x00D\xfe\xe9\x04f\x0f\xe6\x02\xfd\xe1\x01u\x13\xfeY\x03\x9a\x14\xe2\x02\x90\x06\x02\a\x01\x02\x1e\xb3\x14\x13G\b\x04\xea\a\ab\a\x04\xfe\xdb\x04\x02\b\xe4\x047\x02\a=^\x01H\x02\b^\x85\x02`\x02\t\x01\xb1\x05\x03\x01=\x06\x14\x06v~\x05\x05y\x05\x06T\x03\x05\xce\x06\x05\xf5\x04\x02\x0f\x14\x04\xbf\x06\x01\xd6\xec\xd5\xfe3\xda\xf5\xd7\x01\x86\xd5\x01G\xcc\xfd\xe2\xd6\x01D\xc8\xfe\xa3P\xefO\x01\x0f\t\x034F\x06\x02\x9e\xc8\x01ѭ\xfb\xb3\xea\xa4\xf0\x02q\xc2\x01\xb9\xa3\xfc\xbb\xe9\x8ei_\x04\x05w\\ހ\xe4!1u\x05\x03\xbb\x05\x05S\xa1\x05\x03\xea\x02\x02\x01\xf2\x04\x01\x11\a\x04%V\x06\x01_\a\x05-d\b\x01\xd2\n\x03\x87\x01\x99\x04\x05\xfe1\a\x03=U\x02\x06{J\x04\x048n\x06\x03~\x03\x03\x87\x04\x06r\x87\x03\x05\x02\x99\x05\x00\x00\x03\x00\x00\xff\x00\x06\x80\x06\x00\x00\x1d\x00'\x00U\x00\x00\x014.\x03#\x0e\x04\".\x03'\"\x0e\x03\x15\x14\x163!26\x034&\"\x06\x15\x14\x1626\x01\x15\x14\x06+\x01\x15\x14\x06#!\"&5\x11463!2\x16\x1d\x0132\x16\x1d\x01\x14\x06+\x01\x1532\x16\x1d\x01\x14\x06+\x01\x1532\x16\x04\xb1\v\x1f0P3\x067\x1e3/./3\x1e7\x063P0\x1f\vT=\x02@=T\xad\x99֙\x99֙\x02|\x12\x0e`^B\xfb@B^^B\x04\xc0B^`\x0e\x12\x12\x0e``\x0e\x12\x12\x0e``\x0e\x12\x01*9deG-\x04!\x10\x18\n\n\x18\x10!\x04-Ged9Iaa\x02\x9bl\x98\x98lk\x98\x98\xfeO\xc0\x0e\x12\xe0B^^B\x05\xc0B^^B\xe0\x12\x0e\xc0\x0e\x12\x80\x12\x0e\xc0\x0e\x12\x80\x12\x00\x00\x04\x00\x00\xff\x00\x06\x80\x06\x00\x00\t\x00+\x00Y\x00i\x00\x00\x01\x14\x06\"&5462\x16\x032\x1e\x04\x15\x14\x06#!\"&54>\x03;\x01\x1e\x052>\x04\x01\x14\x06+\x01\x1532\x16\x1d\x01\x14\x06+\x01\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06#!\"&5\x11463!2\x16\x1d\x0132\x16\x15\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x04\x04\x99֙\x99֙0.I/ \x10\aOB\xfd\xc0BO\t\x1c-Q5\x05\a2\x15-\x1d)&)\x1d-\x152\x02\xb3\x13\r``\r\x13\x13\r``\r\x13\x13\r`^B\xfb@B^^B\x04\xc0B^`\r\x13\xff\x00\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\x03|k\x98\x98kl\x98\x98\xfe\xb8\"=IYL)CggC0[jM4\x04\x1f\v\x17\t\t\t\t\x17\v\x1f\x01\x04\r\x13\x80\x13\r\xc0\r\x13\x80\x13\r\xc0\r\x13\xe0B^^B\x05\xc0B^^B\xe0\x13\r\xfb@\x05\xc0\r\x13\x13\r\xfa@\r\x13\x13\x00\x00\x06\x00\x00\xff\x80\b\x00\x05\x80\x00\x19\x00!\x001\x00A\x00Q\x00u\x00\x00\x004.\x02#\x0e\x04\".\x03'\"\x0e\x02\x14\x163!2\x024&\"\x06\x14\x162\x0154&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x01\x11\x14\x06#!54&+\x01\"\x06\x1d\x01!54&+\x01\"\x06\x1d\x01!\"&5\x11463!2\x16\x04\x00\x12)P9\x060\x1b,***,\x1b0\x069P)\x12J6\x02\x006S\x85\xbc\x85\x85\xbc\x04\"\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x15\x0f\xfd\xc8\x0f\x15\x15\x0f\x028\x0f\x15\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x01\x00^B\xfe\xa0\x12\x0e@\x0e\x12\xfd\x00\x12\x0e@\x0e\x12\xfe\xa0B^^B\x06\xc0B^\x01U\x80kc9\x04\x1c\x0f\x14\t\t\x14\x0f\x1c\x049ck\x80U\x02?\xbc\x85\x85\xbc\x85\xfe\xe6@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x128\x0f\x15\x15\x0f8\x0f\x15\x15\x01\v@\x0e\x12\x12\x0e@\x0e\x12\x12\x01N\xfb@B^`\x0e\x12\x12\x0e``\x0e\x12\x12\x0e`^B\x04\xc0B^^\x00\x00\a\x00\x00\xff\x80\b\x00\x05\x80\x00\x19\x00!\x001\x00A\x00Q\x00u\x00\x85\x00\x00\x00\x14\x06#!\"&4>\x023\x1e\x042>\x0372\x1e\x01\x02\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!546;\x012\x16\x1d\x01!546;\x012\x16\x1d\x01!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04\x00J6\xfe\x006J\x12)P9\x060\x1b,***,\x1b0\x069P)\x8b\x85\xbc\x85\x85\xbc\x04\"\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x15\x0f\xfd\xc8\x0f\x15\x15\x0f\x028\x0f\x15\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x80\x13\r\xf9@\r\x13\x13\r\x01`\x12\x0e@\x0e\x12\x03\x00\x12\x0e@\x0e\x12\x01`\r\x13\x80^B\xf9@B^^B\x06\xc0B^\x01ՀUU\x80kc9\x04\x1c\x0f\x14\t\t\x14\x0f\x1c\x049c\x01\xbb\xbc\x85\x85\xbc\x85\xfd`@\x0e\x12\x12\x0e@\x0e\x12\x12\xee8\x0f\x15\x15\x0f8\x0f\x15\x15\xf5@\x0e\x12\x12\x0e@\x0e\x12\x12\xfc2\x04\xc0\r\x13\x13\r\xfb@\r\x13`\x0e\x12\x12\x0e``\x0e\x12\x12\x0e`\x13\x04\xcd\xfb@B^^B\x04\xc0B^^\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00\x17\x00(\x00\x00%.\x01'\x0e\x01\"&'\x0e\x01\a\x16\x04 $\x02\x10& \x06\x10\x16 \x00\x10\x02\x06\x04#\"$&\x02\x10\x126$ \x04\x16\x05\xf3\x16\x83wC\xb9ιCw\x83\x16j\x01J\x01~\x01J\x89\xe1\xfe\xc2\xe1\xe1\x01>\x02\xe1\x8e\xef\xfe\xb4\xb7\xb6\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0ś\xcd\x10JSSJ\x10͛\x96\xaf\xaf\x02\xb2\x01>\xe1\xe1\xfe\xc2\xe1\x016\xfe\x94\xfe\xb5\xf1\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x10\x00$\x00,\x00\x00\x00 \x04\x16\x12\x15\x14\x02\x06\x04 $&\x02\x10\x126\x01654\x02&$ \x04\x06\x02\x15\x14\x17\x123\x16 72&\x10& \x06\x10\x16 \x02\xca\x01l\x01L\xf0\x8e\x8d\xf0\xfe\xb4\xfe\x92\xfe\xb4\uf38e\xf0\x04m\x95z\xce\xfe\xe4\xfe\xc8\xfe\xe4\xcez\x95B\xf0\x83\x01l\x83\xf0\xa9\xe1\xfe\xc2\xe1\xe1\x01>\x06\x00\x8e\xf0\xfe\xb4\xb6\xb5\xfe\xb4\xf0\x8f\x8e\xf1\x01K\x01l\x01L\xf0\xfbG\xcd\xfa\x9c\x01\x1c\xcezz\xce\xfe\xe4\x9c\xfa\xcd\x01G\x80\x80\xa1\x01>\xe1\xe1\xfe\xc2\xe1\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x1f\x00'\x007\x00\x00\x01\x1e\x04\x15\x14\x06#!\"&54>\x037&54>\x022\x1e\x02\x15\x14\x00 \x06\x10\x16 6\x10\x132654\x02'\x06 '\x06\x02\x15\x14\x163\x04\xb1/U]B,ȍ\xfc\xaa\x8d\xc8,B]U/OQ\x8a\xbdн\x8aQ\xfe\x9f\xfe\xc2\xe1\xe1\x01>\xe1+X}\x9d\x93\x91\xfe\x82\x91\x93\x9d}X\x02\xf0\x0e0b\x85Ӄ\x9a\xdbۚ\x83Ӆb0\x0e}\x93h\xbd\x8aQQ\x8a\xbdh\x93\x02\x13\xe1\xfe\xc2\xe1\xe1\x01>\xfa\xe1\x8ff\xef\x01\x14\a\u007f\u007f\a\xfe\xec\xeff\x8f\x00\x00\x00\x00\x04\x00\x00\xff\x00\x05\x00\x06\x00\x00\x11\x00\x19\x00#\x00=\x00\x00\x00\x14\x06#!\"&4>\x023\x16272\x1e\x01\x02\x14\x06\"&462\x01\x11!\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!\x15\x14\x16;\x0126=\x01!2\x16\x04\x00J6\xfe\x006J\x12)Q8P\xd8P8Q)\x88\x87\xbe\x87\x87\xbe\x01\xa1\xfc\x00\x13\r\x03\xc0\r\x13\x80^B\xfc@B^^B\x01`\x12\x0e\xc0\x0e\x12\x01`B^\x01V\x80VV\x80ld9KK9d\x01\xb9\xbc\x85\x85\xbc\x85\xfb\xa0\x05`\xfa\xa0\r\x13\x13\x05\xcd\xfa@B^^B\x05\xc0B^`\x0e\x12\x12\x0e`^\x00\x00\b\x00\x00\xff\x80\b\x00\x05\x80\x00\x13\x00\x1b\x00+\x00;\x00K\x00[\x00e\x00u\x00\x00\x014.\x02#\x06\"'\"\x0e\x02\x15\x14\x163!26\x024&\"\x06\x14\x162\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26%54&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&#!\"\x06\x1d\x01\x14\x163!26\x01!54&#!\"\x06\x15!\x11\x14\x06#!\"&5\x11463!2\x16\x03\x80\x0f\"D/@\xb8@/D\"\x0f?,\x01\xaa,?\x80p\xa0pp\xa0\x04p\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\xfe\x80\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\xf9\x80\a\x00\x12\x0e\xf9@\x0e\x12\a\x80^B\xf9@B^^B\x06\xc0B^\x01D6]W2@@2W]67MM\x01\xa3\xa0pp\xa0p\xfe\xe0@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01n`\x0e\x12\x12\x0e\xfb@B^^B\x04\xc0B^^\x00\b\x00\x00\xff\x80\b\x00\x05\x80\x00\x13\x00\x1b\x00+\x00;\x00K\x00[\x00e\x00u\x00\x00\x01\x14\x06#!\"&54>\x023\x16272\x1e\x02\x02\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x16%\x15\x14\x06#!\"&=\x01463!2\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06#!\"&=\x01463!2\x16\x13\x11!\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03\x80?,\xfeV,?\x0f\"D/@\xb8@/D\"\x0f\x80p\xa0pp\xa0\x04p\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\xfe\x80\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x80\xf9\x00\x13\r\x06\xc0\r\x13\x80^B\xf9@B^^B\x06\xc0B^\x01D7MM76]W2@@2W]\x01֠pp\xa0p\xfd\xa0@\x0e\x12\x12\x0e@\x0e\x12\x12\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfc\xb2\x04`\xfb\xa0\r\x13\x13\x04\xcd\xfb@B^^B\x04\xc0B^^\x00\x02\x00\x1d\xff\x00\x06\xe2\x06\x00\x00\x1a\x00A\x00\x00\x01\x10\x02#\"\x02\x11\x10\x12327.\x04#\"\a'632\x16\x176\x013\x16\x0e\x03#\".\x02'\x06#\"$&\x0254\x126$32\x1e\x03\x15\x14\x02\a\x1e\x01326\x04\xe7\xd2\xe1\xde\xd0\xd0\xdeJ9\x16\"65I).!1i\xab\x84\xa7CC\x01\x86u\x03\n+I\x8d\\Gw\\B!al\x96\xfe\xe3݇\x87\xde\x01\x1d\x95y\xebǙV\xa1\x8a/]:=B\x02\xed\x01>\x019\xfe\xc6\xfe\xc3\xfe\xc4\xfe\xc9\x11+\x0132\x16\x15\x14\a\x06\a\x06\x15\x10\x17\x16\x17\x1e\x04%\x14\x06#!\"&5463!2\x16\x03\x14\a\x0e\x01\a\x06#\"&54>\x0254'&#\"\x15\x14\x16\x15\x14\x06#\"54654'.\x01#\"\x0e\x01\x15\x14\x16\x15\x14\x0e\x03\x15\x14\x17\x16\x17\x16\x17\x16\x15\x14#\"'.\x0154>\x0354'&'&5432\x17\x1e\x04\x17\x14\x1e\x0532654&432\x17\x1e\x01\x05\x10\a\x0e\x03#\"&54>\x0176\x114&'&'.\x0554632\x17\x16\x12\x17\x16\x01\xc5 \x15\x01\f?c\xe1\xd5'p&\x13 ?b1w{2V\x02\x19\x0e\x14\t\x05?#\x1d\xfb\xc7\x1a&#\x1d\x049\x1a&\xd7C\x19Y'\x10\v\a\x10&.&#\x1d\x11\x03\x0f+\x17B\x03\n\r:\x16\x05\x04\x03 &65&*\x1d2\x10\x01\x01\x12\x06\x1bw\x981GF1\x19\x1d\x1b\x13)2<)<'\x1c\x10\b\x06\x03\b\n\f\x11\n\x17\x1c(\n\x1bBH=\x02ӊ\x13:NT \x10\x1e:O\t\xb7)4:i\x02\x16\v\x13\v\b \x13F~b`\f\x02e\x15!\x03\x0f}\x01\x1c\x01\x88\x01U\x01\x113i\x1b\x13\x1b?fR\xc7\xfa\xfe\xe7\xd2UX\x03\x1a\x10\x19\x16|\x1d'&\x1a\x1d'&\x02I\x86c&Q\x14\n\f\x06\t*2U.L6*\x05\f/\r\x16\x1aL\x0f:\x0f\x19\x15\x199\x01\x04\x04\x020\x1e%>..>%b>+\x14\x05\x05\x02\x03\x10\v+\xc1z7ymlw45)0\x10\t\f\x14\x1d\x1333J@0\x01!\x11!\x15\x16\v\x1c\x17\x19T\x14FL\xa0\x87\xfe\xee\xe5 P]=\x1f\x10\x0fGS\v\xe6\x01-\x83\xd0kwm\x03\x15\f\x17\x11\x14\t\x13!\xa9\x83\xfe\xe4\xac*\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x18\x00(\x00\x00%\x136&\a\x01\x0e\x01\x16\x1f\x01\x016\x17\x16\a\x019\x01\a2?\x01\x17\x16\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04\xa5\x93\t' \xfc\xa0\x1d\x15\x10\x18\xdd\x02\x01\x15\v\a\v\xfea\x10\x17\x16l\xe0@\x02l\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xe5\x02\xb5,&\f\xfe\xb3\v\x1c\x19\aE\x01C\x0e\b\x05\n\xfe\x89\xe4\x16h\xa5$\x02\x9b\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x06\x00\x00\xff\x00\x04\x00\x06\x00\x00\r\x00\x1f\x00/\x003\x007\x00;\x00\x00%\x14\x06\"&5467\x113\x11\x1e\x01\x174&'\x114&\"\x06\x15\x11\x0e\x01\x15\x14\x16 67\x14\x00 \x00547\x1146 \x16\x15\x11\x16\x13\x15#5\x13\x15#5\x13\x15#5\x02\x80p\xa0pF:\x80:F\x80D\x00F\x00N\x00V\x00^\x00f\x00n\x00v\x00~\x00\x86\x00\x8e\x00\x96\x00\x9e\x00\x00\x01\x16\x14\a\x01\x06\"/\x01&4?\x01.\x017&#\"\x06\x15\x11!\x114>\x0232\x16\x176\x16\x17762\x17\x022\x16\x14\x06\"&4\x04\"&462\x16\x1462\x16\x14\x06\"&4\x042\x16\x14\x06\"&4\x04462\x16\x14\x06\"$2\x16\x14\x06\"&4\x042\x16\x14\x06\"&4\x04\"&462\x16\x1462\x16\x14\x06\"&4\x04\"&462\x16\x1462\x16\x14\x06\"&4\x042\x16\x14\x06\"&4$2\x16\x14\x06\"&4\x062\x16\x14\x06\"&4\x062\x16\x14\x06\"&4\x05\x99\n\n\xfd\x8e\n\x1a\nR\n\n,H\x138Jfj\x96\xff\x00Q\x8a\xbdhj\xbeG^\xceR,\n\x1a\n!4&&4&\x01Z4&&4&\xa64&&4&\xfd\xa64&&4&\x01\x00&4&&4\x01\x004&&4&\xfd\xa64&&4&\x01Z4&&4&\xa64&&4&\xfe\xda4&&4&\xa64&&4&\xfe\xa64&&4&\x01&4&&4&Z4&&4&Z4&&4&\x05\a\n\x1a\n\xfd\x8e\n\nR\n\x1a\n,[\xe8cG\x96j\xfb\x00\x05\x00h\xbd\x8aQRJ'\x1dA,\n\n\xfe\xa7&4&&4Z&4&&4Z&4&&4Z&4&&444&&4&\x80&4&&4Z&4&&4Z&4&&4Z&4&&4\xda&4&&4Z&4&&4Z&4&&4&&4&&4Z&4&&4Z&4&&4\x00\x11\x00\x00\xff\x00\a\x00\x06\x00\x00\x1d\x00%\x00-\x005\x00=\x00E\x00M\x00}\x00\x85\x00\x8d\x00\x95\x00\x9d\x00\xa5\x00\xad\x00\xb5\x00\xbd\x00\xc5\x00\x00\x01\x15\x14\a\x15\x14\x06+\x01\"&=\x01\x06#!\"'\x15\x14\x06+\x01\"&=\x01&=\x01\x00\x14\x06\"&4626\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462&\x14\x06\"&462&\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x0146;\x01\x114632\x176\x16\x1776\x1f\x01\x16\a\x01\x06/\x01&?\x01.\x017&#\"\x06\x15\x11!2\x16\x00\x14\x06\"&462&\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462&\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462\x06\x80\x80\x12\x0e@\x0e\x12?A\xfd\x00A?\x13\r@\r\x13\x80\x02@\x12\x1c\x12\x12\x1cR\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\x92\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\x04R\x12\x0e\xf9@\x0e\x12\x12\x0e`\x96jlL.h)\x16\v\v*\v\v\xfe\xc6\v\v*\v\v\x16$\t\x1c%35K\x05\xe0\x0e\x12\xfc\x80\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\xd2\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\xd2\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\x92\x12\x1c\x12\x12\x1c\x01\xc0\xc0\xa9u\xc2\x0e\x12\x12\x0ev\x16\x16n\x11\x17\x17\x11\xbau\xa9\xc0\x01\xae\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12\x12\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12\xfd\xe0@\x0e\x12\x12\x0e@\x0e\x12\x02\x80j\x96N\x13\x0e \x16\v\v*\v\v\xfe\xc6\v\v*\v\v\x16.t2#K5\xfd\x80\x12\x01\xc0\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12R\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12R\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12\x12\x1c\x12\x12\x1c\x12\x00\x00\x00\x04\x00\x01\xff\x00\x06\x00\x05\xfe\x00\r\x00@\x00H\x00q\x00\x00\x01\x14\a\x06\a\x06 '&'&54 \x01\x14\x00\a\x06&76767676\x1254\x02$\a\x0e\x03\x17\x16\x12\x17\x16\x17\x16\x17\x1e\x01\x17\x16\x06'.\x01\x0276\x126$76\x04\x16\x12\x04\x14\x06\"&462\x01\x14\x06\a\x06&'&'&7>\x0154.\x01\a\x0e\x01\a\x06\x16\x17\x16\a\x06\a\x0e\x01'.\x017>\x0276\x1e\x01\x03\xe2\x11\x1f\x18\x16\xfe\xfc\x16\x18\x1f\x11\x01\xc0\x02\x1e\xfe\xf4\xd8\b\x0e\x01\a\x03\x04\x02\x01\b\x9f\xc1\xb6\xfeȵ|\xe2\xa1_\x01\x01ğ\a\x02\x03\x03\x01\b\x02\x01\x0f\b\x94\xe2y\b\av\xbf\x01\x03\x8f\xa4\x01/ۃ\xfd\u20fa\x83\x83\xba\x01\xa3k]\b\x10\x02\x06\x17\a\n:Bu\xc6q\x85\xc0\r\nCA\n\a\x18\x05\x02\x10\b_k\x02\x03\x84ނ\x90\xf8\x91\x01XVo\xd7bZZb\xd7nW\xa8\x01\x00\xf0\xfe|V\x03\f\t0\x12 \x0f\t\x03Q\x012\xb8\xb4\x01-\xa8\n\al\xad\xe7}\xb8\xfe\xcfO\x03\t\x15\x18\t/\f\t\f\x04:\xdf\x011\xa7\x8f\x01\x05\xc1z\t\nq\xd0\xfe\xdb%\xba\x83\x83\xba\x83\xff\x00z\xd5G\x06\b\n4(\n\n6\x92Ro\xbaa\f\x0fą\\\xa8<\n\n)4\t\b\x06J\xda}\x83\xe2\x89\x06\a\x86\xf1\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00\x03\x00\x13\x00\x00%!\x11!\x01\x11\x14\x06#!\"&5\x11463!2\x16\x01\x00\x05\x00\xfb\x00\x06\x00^B\xfa@B^^B\x05\xc0B^\x80\x03\x00\x01`\xfb@B^^B\x04\xc0B^^\x00\x01\x00\x00\xff\x80\a\x00\x01\x80\x00\x0f\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\a\x00^B\xfa@B^^B\x05\xc0B^\xe0\xc0B^^B\xc0B^^\x00\x00\x00\x03\x00\x00\xff\x00\b\x00\x06\x00\x00\x03\x00\f\x00&\x00\x00)\x01\x11)\x02\x11!\x1132\x16\x15\x01\x11\x14\x06#!\x11\x14\x06#!\"&5\x11463!\x11463!2\x16\x01\x00\x03\x00\xfd\x00\x04\x00\x02\x00\xfd\x00`B^\x03\x00^B\xfd\xa0^B\xfc@B^^B\x02`^B\x03\xc0B^\x02\x00\x03\x00\xff\x00^B\x02\x00\xfc@B^\xfe\xa0B^^B\x03\xc0B^\x01`B^^\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00#\x003\x00\x00%764/\x01764/\x01&\"\x0f\x01'&\"\x0f\x01\x06\x14\x1f\x01\a\x06\x14\x1f\x01\x162?\x01\x17\x162\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04\x97\x92\n\n\xe9\xe9\n\n\x92\n\x1a\n\xe9\xe9\n\x1a\n\x92\n\n\xe9\xe9\n\n\x92\n\x1a\n\xe9\xe9\n\x1a\x02s^B\xfa@B^^B\x05\xc0B^ג\n\x1a\n\xe9\xe9\n\x1a\n\x92\n\n\xe9\xe9\n\n\x92\n\x1a\n\xe9\xe9\n\x1a\n\x92\n\n\xe9\xe9\n\x04\x13\xfb@B^^B\x04\xc0B^^\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x00#\x00'\x007\x00\x00\x01\a\x06\"/\x01\a\x06\"/\x01&4?\x01'&4?\x0162\x1f\x01762\x1f\x01\x16\x14\x0f\x01\x17\x16\x14\x01!\x11!%\x11\x14\x06#!\"&5\x11463!2\x16\x04\xe9\x92\n\x1a\n\xa9\xa9\n\x1a\n\x92\n\n\xa9\xa9\n\n\x92\n\x1a\n\xa9\xa9\n\x1a\n\x92\n\n\xa9\xa9\n\xfc\r\x05\x00\xfb\x00\x06\x00^B\xfa@B^^B\x05\xc0B^\x01\xa9\x92\n\n\xa9\xa9\n\n\x92\n\x1a\n\xa9\xa9\n\x1a\n\x92\n\n\xa9\xa9\n\n\x92\n\x1a\n\xa9\xa9\n\x1a\xfe\xcd\x04\x00`\xfb@B^^B\x04\xc0B^^\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x03\x00\x13\x00\x00\t\x01!\x01\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04.\x012\xfdr\xfe\xce\x05`\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x01f\x024\xfd\xcc\x01\xd0\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\a\x00\x00\xff\x00\a\x02\x06\x00\x00\a\x00\x13\x00#\x00.\x00C\x00\xc4\x00\xd4\x00\x00\x01&\x0e\x01\x17\x16>\x01\x05\x06\"'&4762\x17\x16\x14\x17\a\x06\"/\x01&4?\x0162\x1f\x01\x16\x14'\x06\"'&4762\x16\x14%\x0e\x01'.\x01>\x02\x16\x17\x1e\a\x0e\x01\x136.\x02'.\x01\a>\x01\x1f\x016'>\x01/\x01>\x0176&'&\x06\a\x0e\x01\x1e\x01\x17.\x01'&7&'\"\a>\x01?\x014'.\x01\x06\a67\x06\x1e\x01\x17\x06\a\x0e\x01\x0f\x01\x0e\x01\x17\x16\x17\x06\a\x06\x14\x167>\x017.\x02\a>\x043\x167654'\x16\a\x0e\x01\x0f\x01\x0e\x05\x16\x17&'\x0e\x04\x16\x17\x166\x127>\x017\x16\x17\x1676\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x05\v\x0f(\f\v\x0e4\x10\xfeZ\b\x17\a\b\b\a\x17\b\a\x9e#\f#\r&\f\f#\f#\r&\fy\a\x17\b\a\a\b\x16\x10\x01\x8b\"\x936&.\x04JM@&\x02\x16\a\x13\x06\x0e\x03\x05\x03\a\xc3\x03\x17 \"\x06(XE\x13*\f\f\x02$\x06\x01\x03\x03+8\x06\njT\x01?\x013\x03\x13#'.\x01'&!\x11\x14\x163!2>\x04?\x013\x06\x02\a.\x01'#!\x0557>\x017\x13\x12'.\x01/\x015\x05!27\x0e\x01\x0f\x01#'.\x01#!\"\x06\x02\x06g\xb1%%D-\x11!g\x0e\ag\x1d\x0f<6W\xfe\xf7WZ\x01e#1=/2*\x12]Y\x063\x05\x92\xeb-,\xfd\x8c\xfe\x88\u007fC1\x01\b\x03\v\x02/D\u007f\x01x\x02\xbe\x8b\xeb\x06\x10\x04\x05] \x1fVF\xfd\xdc\x1c\x0f\x05I\xfdq\x01\x05\x03\x03\x02-H\x8e\xfe\xbe\xfe\xc1\u007fD2\x01\b\xfd\xd4NK\x04\v\x19'>*\xd8%\xfeR=\x05\x06\x01\ff\x19\r07\x02\x83\x01\x92\xf3=.\r\x18f\f\x1bD\xfd]\\|yu\x11\x00\x00\a\x00\x00\xff\x80\x06\x00\x05\x80\x00\x11\x00,\x000\x00>\x00S\x00e\x00u\x00\x00\x01\x15\x14\x16\x0e\x04#\x112\x1e\x03\x1c\x01\x05\x15\x14\x16\x0e\x02#\"'&5<\x03>\x0232\x1e\x03\x1c\x01\x053\x11#\x013\x11#\a&'#\x113\x11\x133\x13\x054'.\x05\"#\"+\x01\x1123\x166'&\x0554.\x02#\"\a5#\x1137\x16326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03\x9a\x01\x01\x02\x05\b\x0e\t\t\x0e\b\x05\x02\x01<\x01\x01\x04\v\b\t\x05\x04\x03\x04\x06\x05\x06\b\x05\x03\x01\xfb\xdezz\x01\xb2j\x9f\x1c\x14\f\x9ek-L+\x01\xa9\x05\x03\x10\x12 \x15)\x11\x15\b\x04[\x14$\xa98\x03\x01\x01=\x04\x0f\"\x1d.\x1fun\a\x1e/2 \xb4^B\xfb@B^^B\x04\xc0B^\x02\xe3\xb6\x04\x16\b\x10\a\b\x03\x015\x02\b\x03\x10\x05\x16cy\x01\x17\b\x0f\x06\t\n\x9b\x02\n\a\v\x06\b\x03\x03\x06\x06\v\x05\x0e\xee\x01\xd8\xfe(\x01\xd8ݔI\xfe(\x018\xfe\xc8\x01?\x0eC\x17\x10\x19\x10\f\x05\x03\xfe(\x013\x9b>\x9f\x85\x1d #\x0f\"\x9a\xfe(\x1e$=\x03\x12\xfb@B^^B\x04\xc0B^^\x00\x00\x00\x00\x05\x000\xff\x02\bK\x05\xf8\x00\f\x00\x15\x00\x1a\x00S\x00\x8f\x00\x00\x05&'.\x04'&'\x16\x00\x01\x17.\x01/\x01\x06\a\x16\x13\x06\a67\x014\x02&$#\"\x04\a\x06\a>\x03\x1f\x01\x1e\x03\a&\x0e\x02\a\x1e\x02\x17\x16>\x02?\x01>\x01\x16\x17\x16\a\x06\x05\x06'\x1e\x03\x1f\x01\x1676\x12\x13\x06\a\x06\x02\a\x06\a\x06'\x06# \x00\x03\"&#\x06\x1e\x02\x1f\x01\x16\x17.\x03/\x01.\x06'\x1e\x02\x177676767>\x0176$\x04\x17\x16\x12\x04w\x06\x05\r.~ku\x1f\x11\x9eB\x01R\xfe]\xa8\x19 \x03\x04T%\x05z+\",\x1e\x05\xa0|\xd3\xfeޟ\x93\xfe\xf4j\x1e\x0f<\xa6\x97\x87)(!(\t\x04\x03~ˣzF\x04\x0f8\"{\xf9\xb4\x91%%\x16#\x1a\x04\x0e5\xd0\xfe\xfd\x87\xb6)\x8a\x88}''\x8fx\xc3\xeeJ\x0e\x1aF\xdf\xcf0\"H[$%\xfe\xe5\xfeEJ\x01\x06\x02\x06\x11#%\r\x0e\b.Gk2\x1d\x03\x02\x059(B13\"\b\x13?\xa3@\x02\vS)\x87\x1c5\x0f\" \x9e\x01#\x019\x96\xdc\xe2\xc5\x01\x03\b\x1edm\xabW\x03\"\xd5\xfe\xd6\x02;\x1cL\xb765R\x8eA\x020@T.\x16\xfe\x9e\xa1\x01$\xd4}i`:f3A\x15\x06\x04\x03\x01\x1d%%\n\v\x15BM<$q\xf3:\x06)BD\x19\x18\x10\t\x13\x19a\x18a%\x14\x04`\xa1]A\v\f\x17&c\x01|\x01\t\x87M\xd0\xfe\xebs!\v\x1a\n\x03\x01Z\x01\r\x012}i[\x1a\x1a\fF&\x89\x8f\x83**\x02\x15\x0f\x1a\x18\x1b\x1b\f\n\x1f<\b \x95\x8dʣsc\x1c\"\x0fJ<&Ns\xfeF\x00\x05\x00%\xff\f\x06\xd8\x05\xf4\x00\x17\x000\x00@\x00W\x00m\x00\x00\x016&'.\x01\x06\a\x06\x16\x17\x1e\x02\x17\x1e\a6\x01\x0e\x02\x04$.\x01\x027>\x037\x06\x1a\x01\f\x01$76\a\x14\x02\x14\x0e\x02\".\x024>\x022\x1e\x01\x05.\x01,\x01\f\x01\x06\x02\x17&\x02>\x04\x1e\x02\x17\x1e\x01\x036\x00'\"'&7\x1e\x04\x0e\x03\a>\x03\x05=\x1dGV:\x87e\x12\f\x0f#\x17\x1f:\x1b$?+%\x18\x14\r\v\n\x01q4\xc1\xec\xfe\xf2\xfe\xfa\xf0\xb4g\x05\x01\x0f\n&\x043h\xf2\x01T\x01`\x01Zt\x14\x02\xf3Q\x88\xbcм\x88QQ\x88\xbcм\x88\x01pA\xe7\xfe\xed\xfe\xcb\xfe\xdb\xfe\xfe\xb6P\x1e1\x05L\x8e\xbd\xe1\xef\xf6\xe2\xceK!:<\f\xfe\xd7\xf8\b\x02\x02\x1a}҈`\x15\x17d\x91\xe1\x88l\xbb\xa1b\x02\xf0,\xab9'\x1d\x14\x1b\x17\n\x05\x03\x04\x0f\n\r%%($!\x18\r\x01\xfd\xcb\u007f\xbaa\x183\x83\xc0\x01\x17\xa4)W)x\r\xd0\xfe\x86\xfe\xfe\x9a\f\xa1\xa4\x1b\r\x04\x02\x1fо\x8aQQ\x8a\xbeо\x8aQQ\x8a\x06\x93\xd0c\bQ\xb1\xf6\xfe\xa4ǡ\x01-\xf4җe)\x17U\xa4s2\x8e\xfe\x81\xf4\x01XD\x05\x05\x03\x04\\\x94\xbd\xd1ϼ\x92Y\x02\x1ed\x92\xcf\x00\x00\x00\x00\v\x00\x00\xff\x80\x06\x00\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\x00\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543%\x11\x14\x06#!\"&5\x11463!2\x16\x01\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x0132\xc0p\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10\x04\xb08(\xfc\xc0(88(\x03@(8\x01\x00\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\xa0\xfa@(88(\x05\xc0(88\xfb\b \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\x00\x00\x00\x00\x01\x00/\xff\x00\x06Q\x06\x00\x00\x90\x00\x00\x01\a\x17\x1e\x01\a\x0e\x01/\x01\x17\x16\x06&'\x03%\x11\x17\x1e\x01\x0e\x01&/\x01\x15\x14\x06\"&=\x01\a\x0e\x01.\x016?\x01\x11\x05\x03\x0e\x01&?\x01\a\x06&'&6?\x01'.\x01>\x01\x17\x05-\x01\x05\x06#\".\x016?\x01'.\x01>\x01\x1f\x01'&6\x16\x17\x13\x05\x11'.\x01>\x01\x16\x1f\x015462\x16\x1d\x017>\x01\x1e\x01\x06\x0f\x01\x11%\x13>\x01\x16\x0f\x0176\x16\x17\x16\x06\x0f\x01\x17\x1e\x01\x0e\x01#\"'%\r\x01%6\x1e\x01\x06\x06\x1e\xa7\xba\x17\r\r\x0e2\x17\xba7\r2G\rf\xfe\xf1\xd0\x10\x02\x18!)\x10p&4&p\x10)!\x18\x02\x10\xd0\xfe\xf1f\rG2\r7\xba\x172\x0e\r\r\x17\xba\xa7\x1d\x1a\t*\x1d\x016\x01\x0f\xfe\xf1\xfe\xca\x04\t\x1b\"\x04\x1a\x1b\xa7\xba\x17\r\x1a4\x16\xba7\r2G\rf\x01\x0f\xd0\x10\x02\x18!)\x10p&4&p\x10)!\x18\x02\x10\xd0\x01\x0ff\rG2\r7\xba\x172\x0e\r\r\x17\xba\xa7\x1b\x1a\x04\"\x1b\t\x04\xfe\xca\xfe\xf1\x01\x0f\x016\x1d*\t\x1a\x01\xa3!k\r3\x17\x17\r\rj\xa0&3\n%\x01,\x9c\xfe\xc7\xee\x12*\x1f\x13\b\x12\x80\xd6\x1a&&\x1aր\x12\b\x13\x1f*\x12\xee\x019\x9c\xfe\xd4%\n3&\xa0j\r\r\x17\x173\rk!\x06./!\x06>\x9d\x9d>\x01$,*\x05!k\r3.\x0e\x0ej\xa0&3\n%\xfeԜ\x019\xee\x12*\x1f\x13\b\x12\x80\xd6\x1a&&\x1aր\x12\b\x13\x1f*\x12\xee\xfeǜ\x01,%\n3&\xa0j\r\r\x17\x173\rk!\x05*,$\x01>\x9d\x9d>\x06!/.\x00\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x12\x00&\x00\x00\x016.\x02'&\x0e\x02\a\x06\x1e\x02\x17\x16$\x12\t\x01\x16\x12\a\x06\x02\x04\a\x05\x01&\x0276\x12$76$\x05\xc1\aP\x92\xd0utۥi\a\aP\x92\xd1u\x9b\x01\x14\xac\x01G\xfe\xa3xy\n\v\xb6\xfeԶ\xfc\x19\x01[xy\n\v\xb6\x01-\xb6\xa7\x02\x9a\x02_v١e\a\aN\x8f\xcfuv١e\a\t\x88\x00\xff\x04=\xfe\xa4u\xfeʦ\xb7\xfe\xc8\xc7\x19\x84\x01[t\x017\xa6\xb8\x018\xc7\x19\x16X\x00\x06\x00\x00\xff\x00\a\x00\x06\x00\x00\n\x00\x0e\x00\x12\x00\x16\x00&\x006\x00\x00\x01\x13#\v\x01#\x13'7\x17\a\x01\x05\x03-\x01\x17\a'%\x17\a'\x04\x10\x02&$ \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x03\xb4\xa33\xaf\xab1\xb3N\x15\xf0\x15\xfeE\x010\x82\xfe\xd0\x01\xda\xf0g\xef\x01\u007f\xbfR\xbe\x02=|\xd3\xfe\xde\xfe\xc2\xfe\xde\xd3||\xd3\x01\"\x01>\x01\"\xd3\xec\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x01\xfc\xfe\xb7\x01^\xfe\xa2\x01v!1f2\x02i\x82\xfeЂwg\xeffZQ\xbeQ^\x01>\x01\"\xd3||\xd3\xfe\xde\xfe\xc2\xfe\xde\xd3||\xd3\x02w\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\f\x00&\xff\x01\aZ\x05\xff\x00X\x00b\x00l\x00w\x00\x81\x00\xab\x00\xb7\x00\xc2\x00\xcd\x00\xd8\x00\xe4\x00\xee\x00\x00\x01.\x03'&>\x01'&'&\x0f\x01\x0e\x03\".\x01'.\x06'&\x06\a\x0e\x03&'&'&\x06\a\x0e\x03\x15\x06\x167>\x0176\x127>\x01\x17\x16\a\x0e\x01\a\x06\x1667>\x0276\x172\a\x06\x02\a\x06\x16\x17\x1e\x026\x04\x16\x06\a\x06&'&>\x01\x01\x16\x0e\x01&'&>\x01\x16\x00\x0e\x01'.\x017>\x01\x17\x16\x01\x16\x0e\x01.\x01676\x16\x13\x16\x02\a\x06'\x0e\x01&'\x06\a\x06&'&'.\x0267.\x01>\x017>\x02\x16\x176\x1e\x03\a\x1e\x02\x06\x01\x16\x06\a\x06&'&676\x16\x13\x16\x0e\x01&'&676\x16\x01\x16\x06\a\x06.\x01676\x16\x01\x16\x06\a\x06&'&>\x01\x16\x01\x16\x06\a\x06&'&676\x16'\x16\x06\a\x06.\x01>\x01\x16\x056\x04/4-\x03\x05LJ\x05\x0eg-\x1e\x03\x04\x02\a\x03\a\x05\a\x03\x03\f\x06\v\b\v\v\x06\x1e$\x1b\x01\x10\t\x15\f\v6\x1e)j\x17\x102%+\x16QF\x1e)\x12\a\x90\x05\x06\x1f\x0e\x1b\x06\x02b\x01\x063F\x14\x04SP\x06\x14\x15\x1d\x04\x02\u007f\a\f21\x11DK2\xfcA\x06\x10\x0f\x0e\x19\x03\x03\x10\x1c\x02W\f\a\")\f\v\a\")\xfd\x15$?\x1a\x1a\f\x12\x12?\x1a\x1a\x05\x04\x13\f8A&\f\x1b\x1cA\x84E5lZm\x14\x81\x9e=\f\x01g\xf4G2\x03Sw*&>$\x045jD \x86\x9f\xb1GH\x88yX/\x064F\x15 \xfbr\x0e\t\x14\x131\r\x0e\t\x14\x131\xac\x04\x12\"\x1c\x04\x03\x13\x10\x11\x1c\x04\xa5\x04\x15\x14\x13\"\b\x15\x14\x14!\xfdl\x10\x0f\x1c\x1b=\x10\x10\x0f6>\x02\xfa\x04\x10\x0f\x0f\x19\x03\x03\x10\x0f\x0e\x19\xbc\x0f\t\x16\x166\x1e\n,5\x01.\x18\x14\x01\x18\x1a/\xb9\xb1'e\x02\x01\x11\x02\x02\x01\x03\x01\x03\x04\x03\x02\r\x05\n\x05\x06\x03\x01\x05\x10\x17\x01\x0f\a\r\x02\x02\x1b\r\x12.*\x1c\x8d|\x90\x01Ed\x04\x02\x1a!\r\x01u\b\v\x0e\a\x0f&\x12\xf3\v&%\x17&\b\xa8\x9f\t\x1d\x01&\x10\xfe\xf9\x1c5d\x18\t\r\x03\x1f\xa8\x1e\x19\x03\x03\x10\x0f\x0e\x1a\x06\xfe\xda\x11)\x18\b\x11\x11)\x18\b\x0366\f\x13\x12@\x1a\x1b\f\x12\x13\xfd\x01\x1cC&\f8B\x14\x13\f\x02@q\xfe\xf9L?\x03P^\x057\t\x01G-hI[\x0eq\x8f\xa1:<\x88rS\tU~9\x177\x15\aA_\x87I\x10R`g\x02p\x141\x0e\x0e\t\x14\x141\x0e\x0e\t\x01\x05\x10\x1d\b\x13\x11\x11\x1c\x04\x04\x13\xfc;\x14\"\x04\x04\x15(\"\x05\x04\x17\x03j\x1b?\x10\x10\x0f\x1b\x1c>\"\x10\xfdT\x0f\x19\x04\x03\x11\x0e\x0f\x1a\x03\x03\x10\xe2\x166\x10\x0f\n,6 \n\x00\x00\x00\x18\x01&\x00\x01\x00\x00\x00\x00\x00\x00\x00/\x00`\x00\x01\x00\x00\x00\x00\x00\x01\x00\v\x00\xa8\x00\x01\x00\x00\x00\x00\x00\x02\x00\a\x00\xc4\x00\x01\x00\x00\x00\x00\x00\x03\x00\x11\x00\xf0\x00\x01\x00\x00\x00\x00\x00\x04\x00\v\x01\x1a\x00\x01\x00\x00\x00\x00\x00\x05\x00\x12\x01L\x00\x01\x00\x00\x00\x00\x00\x06\x00\v\x01w\x00\x01\x00\x00\x00\x00\x00\a\x00Q\x02'\x00\x01\x00\x00\x00\x00\x00\b\x00\f\x02\x93\x00\x01\x00\x00\x00\x00\x00\t\x00\n\x02\xb6\x00\x01\x00\x00\x00\x00\x00\v\x00\x15\x02\xed\x00\x01\x00\x00\x00\x00\x00\x0e\x00\x1e\x03A\x00\x03\x00\x01\x04\t\x00\x00\x00^\x00\x00\x00\x03\x00\x01\x04\t\x00\x01\x00\x16\x00\x90\x00\x03\x00\x01\x04\t\x00\x02\x00\x0e\x00\xb4\x00\x03\x00\x01\x04\t\x00\x03\x00\"\x00\xcc\x00\x03\x00\x01\x04\t\x00\x04\x00\x16\x01\x02\x00\x03\x00\x01\x04\t\x00\x05\x00$\x01&\x00\x03\x00\x01\x04\t\x00\x06\x00\x16\x01_\x00\x03\x00\x01\x04\t\x00\a\x00\xa2\x01\x83\x00\x03\x00\x01\x04\t\x00\b\x00\x18\x02y\x00\x03\x00\x01\x04\t\x00\t\x00\x14\x02\xa0\x00\x03\x00\x01\x04\t\x00\v\x00*\x02\xc1\x00\x03\x00\x01\x04\t\x00\x0e\x00<\x03\x03\x00C\x00o\x00p\x00y\x00r\x00i\x00g\x00h\x00t\x00 \x00D\x00a\x00v\x00e\x00 \x00G\x00a\x00n\x00d\x00y\x00 \x002\x000\x001\x006\x00.\x00 \x00A\x00l\x00l\x00 \x00r\x00i\x00g\x00h\x00t\x00s\x00 \x00r\x00e\x00s\x00e\x00r\x00v\x00e\x00d\x00.\x00\x00Copyright Dave Gandy 2016. All rights reserved.\x00\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00FontAwesome\x00\x00R\x00e\x00g\x00u\x00l\x00a\x00r\x00\x00Regular\x00\x00F\x00O\x00N\x00T\x00L\x00A\x00B\x00:\x00O\x00T\x00F\x00E\x00X\x00P\x00O\x00R\x00T\x00\x00FONTLAB:OTFEXPORT\x00\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00FontAwesome\x00\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00 \x004\x00.\x007\x00.\x000\x00 \x002\x000\x001\x006\x00\x00Version 4.7.0 2016\x00\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00FontAwesome\x00\x00P\x00l\x00e\x00a\x00s\x00e\x00 \x00r\x00e\x00f\x00e\x00r\x00 \x00t\x00o\x00 \x00t\x00h\x00e\x00 \x00C\x00o\x00p\x00y\x00r\x00i\x00g\x00h\x00t\x00 \x00s\x00e\x00c\x00t\x00i\x00o\x00n\x00 \x00f\x00o\x00r\x00 \x00t\x00h\x00e\x00 \x00f\x00o\x00n\x00t\x00 \x00t\x00r\x00a\x00d\x00e\x00m\x00a\x00r\x00k\x00 \x00a\x00t\x00t\x00r\x00i\x00b\x00u\x00t\x00i\x00o\x00n\x00 \x00n\x00o\x00t\x00i\x00c\x00e\x00s\x00.\x00\x00Please refer to the Copyright section for the font trademark attribution notices.\x00\x00F\x00o\x00r\x00t\x00 \x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00Fort Awesome\x00\x00D\x00a\x00v\x00e\x00 \x00G\x00a\x00n\x00d\x00y\x00\x00Dave Gandy\x00\x00h\x00t\x00t\x00p\x00:\x00/\x00/\x00f\x00o\x00n\x00t\x00a\x00w\x00e\x00s\x00o\x00m\x00e\x00.\x00i\x00o\x00\x00http://fontawesome.io\x00\x00h\x00t\x00t\x00p\x00:\x00/\x00/\x00f\x00o\x00n\x00t\x00a\x00w\x00e\x00s\x00o\x00m\x00e\x00.\x00i\x00o\x00/\x00l\x00i\x00c\x00e\x00n\x00s\x00e\x00/\x00\x00http://fontawesome.io/license/\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc3\x00\x00\x00\x01\x00\x02\x00\x03\x00\x8e\x00\x8b\x00\x8a\x00\x8d\x00\x90\x00\x91\x00\x8c\x00\x92\x00\x8f\x01\x02\x01\x03\x01\x04\x01\x05\x01\x06\x01\a\x01\b\x01\t\x01\n\x01\v\x01\f\x01\r\x01\x0e\x01\x0f\x01\x10\x01\x11\x01\x12\x01\x13\x01\x14\x01\x15\x01\x16\x01\x17\x01\x18\x01\x19\x01\x1a\x01\x1b\x01\x1c\x01\x1d\x01\x1e\x01\x1f\x01 \x01!\x01\"\x01#\x01$\x01%\x01&\x01'\x01(\x01)\x01*\x01+\x01,\x01-\x01.\x01/\x010\x011\x012\x013\x014\x015\x016\x017\x018\x019\x01:\x01;\x01<\x01=\x01>\x01?\x01@\x01A\x01B\x01C\x01D\x01E\x01F\x01G\x01H\x01I\x01J\x01K\x01L\x01M\x01N\x01O\x01P\x01Q\x01R\x01S\x01T\x01U\x01V\x01W\x01X\x01Y\x01Z\x01[\x01\\\x01]\x01^\x01_\x01`\x01a\x01b\x00\x0e\x00\xef\x00\r\x01c\x01d\x01e\x01f\x01g\x01h\x01i\x01j\x01k\x01l\x01m\x01n\x01o\x01p\x01q\x01r\x01s\x01t\x01u\x01v\x01w\x01x\x01y\x01z\x01{\x01|\x01}\x01~\x01\u007f\x01\x80\x01\x81\x01\x82\x01\x83\x01\x84\x01\x85\x01\x86\x01\x87\x01\x88\x01\x89\x01\x8a\x01\x8b\x01\x8c\x01\x8d\x01\x8e\x01\x8f\x01\x90\x01\x91\x01\x92\x01\x93\x01\x94\x01\x95\x01\x96\x01\x97\x01\x98\x01\x99\x01\x9a\x01\x9b\x01\x9c\x01\x9d\x01\x9e\x01\x9f\x01\xa0\x01\xa1\x01\xa2\x01\xa3\x01\xa4\x01\xa5\x01\xa6\x01\xa7\x01\xa8\x01\xa9\x01\xaa\x01\xab\x01\xac\x01\xad\x01\xae\x01\xaf\x01\xb0\x01\xb1\x01\xb2\x01\xb3\x01\xb4\x01\xb5\x01\xb6\x01\xb7\x01\xb8\x01\xb9\x01\xba\x01\xbb\x01\xbc\x01\xbd\x01\xbe\x01\xbf\x01\xc0\x01\xc1\x01\xc2\x01\xc3\x01\xc4\x01\xc5\x01\xc6\x01\xc7\x01\xc8\x01\xc9\x01\xca\x01\xcb\x01\xcc\x01\xcd\x01\xce\x01\xcf\x01\xd0\x01\xd1\x01\xd2\x01\xd3\x01\xd4\x01\xd5\x01\xd6\x01\xd7\x01\xd8\x01\xd9\x01\xda\x01\xdb\x01\xdc\x01\xdd\x01\xde\x01\xdf\x01\xe0\x01\xe1\x01\xe2\x01\xe3\x01\xe4\x01\xe5\x01\xe6\x01\xe7\x01\xe8\x01\xe9\x01\xea\x01\xeb\x01\xec\x01\xed\x01\xee\x01\xef\x01\xf0\x01\xf1\x01\xf2\x01\xf3\x01\xf4\x01\xf5\x01\xf6\x01\xf7\x01\xf8\x01\xf9\x01\xfa\x01\xfb\x01\xfc\x01\xfd\x01\xfe\x01\xff\x02\x00\x02\x01\x02\x02\x02\x03\x02\x04\x02\x05\x02\x06\x02\a\x02\b\x00\"\x02\t\x02\n\x02\v\x02\f\x02\r\x02\x0e\x02\x0f\x02\x10\x02\x11\x02\x12\x02\x13\x02\x14\x02\x15\x02\x16\x02\x17\x02\x18\x02\x19\x02\x1a\x02\x1b\x02\x1c\x02\x1d\x02\x1e\x02\x1f\x02 \x02!\x02\"\x02#\x02$\x02%\x02&\x02'\x02(\x02)\x02*\x02+\x02,\x02-\x02.\x02/\x020\x021\x022\x023\x024\x025\x026\x027\x028\x029\x02:\x02;\x02<\x02=\x02>\x02?\x02@\x02A\x02B\x02C\x02D\x02E\x02F\x02G\x02H\x02I\x02J\x02K\x02L\x02M\x02N\x02O\x02P\x02Q\x02R\x02S\x00\xd2\x02T\x02U\x02V\x02W\x02X\x02Y\x02Z\x02[\x02\\\x02]\x02^\x02_\x02`\x02a\x02b\x02c\x02d\x02e\x02f\x02g\x02h\x02i\x02j\x02k\x02l\x02m\x02n\x02o\x02p\x02q\x02r\x02s\x02t\x02u\x02v\x02w\x02x\x02y\x02z\x02{\x02|\x02}\x02~\x02\u007f\x02\x80\x02\x81\x02\x82\x02\x83\x02\x84\x02\x85\x02\x86\x02\x87\x02\x88\x02\x89\x02\x8a\x02\x8b\x02\x8c\x02\x8d\x02\x8e\x02\x8f\x02\x90\x02\x91\x02\x92\x02\x93\x02\x94\x02\x95\x02\x96\x02\x97\x02\x98\x02\x99\x02\x9a\x02\x9b\x02\x9c\x02\x9d\x02\x9e\x02\x9f\x02\xa0\x02\xa1\x02\xa2\x02\xa3\x02\xa4\x02\xa5\x02\xa6\x02\xa7\x02\xa8\x02\xa9\x02\xaa\x02\xab\x02\xac\x02\xad\x02\xae\x02\xaf\x02\xb0\x02\xb1\x02\xb2\x02\xb3\x02\xb4\x02\xb5\x02\xb6\x02\xb7\x02\xb8\x02\xb9\x02\xba\x02\xbb\x02\xbc\x02\xbd\x02\xbe\x02\xbf\x02\xc0\x02\xc1\x02\xc2\x02\xc3\x02\xc4\x02\xc5\x02\xc6\x02\xc7\x02\xc8\x02\xc9\x02\xca\x02\xcb\x02\xcc\x02\xcd\x02\xce\x02\xcf\x02\xd0\x02\xd1\x02\xd2\x02\xd3\x02\xd4\x02\xd5\x02\xd6\x02\xd7\x02\xd8\x02\xd9\x02\xda\x02\xdb\x02\xdc\x02\xdd\x02\xde\x02\xdf\x02\xe0\x02\xe1\x02\xe2\x02\xe3\x02\xe4\x02\xe5\x02\xe6\x02\xe7\x02\xe8\x02\xe9\x02\xea\x02\xeb\x02\xec\x02\xed\x02\xee\x02\xef\x02\xf0\x02\xf1\x02\xf2\x02\xf3\x02\xf4\x02\xf5\x02\xf6\x02\xf7\x02\xf8\x02\xf9\x02\xfa\x02\xfb\x02\xfc\x02\xfd\x02\xfe\x02\xff\x03\x00\x03\x01\x03\x02\x03\x03\x03\x04\x03\x05\x03\x06\x03\a\x03\b\x03\t\x03\n\x03\v\x03\f\x03\r\x03\x0e\x03\x0f\x03\x10\x03\x11\x03\x12\x03\x13\x03\x14\x03\x15\x03\x16\x03\x17\x03\x18\x03\x19\x03\x1a\x03\x1b\x03\x1c\x03\x1d\x03\x1e\x03\x1f\x03 \x03!\x03\"\x03#\x03$\x03%\x03&\x03'\x03(\x03)\x03*\x03+\x03,\x03-\x03.\x03/\x030\x031\x032\x033\x034\x035\x036\x037\x038\x039\x03:\x03;\x03<\x03=\x03>\x03?\x03@\x03A\x03B\x03C\x03D\x03E\x03F\x03G\x03H\x03I\x03J\x03K\x03L\x03M\x03N\x03O\x03P\x03Q\x03R\x03S\x03T\x03U\x03V\x03W\x03X\x03Y\x03Z\x03[\x03\\\x03]\x03^\x03_\x03`\x03a\x03b\x03c\x03d\x03e\x03f\x03g\x03h\x03i\x03j\x03k\x03l\x03m\x03n\x03o\x03p\x03q\x03r\x03s\x03t\x03u\x03v\x03w\x03x\x03y\x03z\x03{\x03|\x03}\x03~\x03\u007f\x03\x80\x03\x81\x03\x82\x03\x83\x03\x84\x03\x85\x03\x86\x03\x87\x03\x88\x03\x89\x03\x8a\x03\x8b\x03\x8c\x03\x8d\x03\x8e\x03\x8f\x03\x90\x03\x91\x03\x92\x03\x93\x03\x94\x03\x95\x03\x96\x03\x97\x03\x98\x03\x99\x03\x9a\x03\x9b\x03\x9c\x03\x9d\x03\x9e\x03\x9f\x03\xa0\x03\xa1\x03\xa2\x03\xa3\x03\xa4\x03\xa5\x03\xa6\x03\xa7\x03\xa8\x03\xa9\x03\xaa\x03\xab\x03\xac\x03\xad\x03\xae\x03\xaf\x03\xb0\x03\xb1\x00\x94\x05glass\x05music\x06search\benvelope\x05heart\x04star\nstar_empty\x04user\x04film\bth_large\x02th\ath_list\x02ok\x06remove\azoom_in\bzoom_out\x03off\x06signal\x03cog\x05trash\x04home\bfile_alt\x04time\x04road\fdownload_alt\bdownload\x06upload\x05inbox\vplay_circle\x06repeat\arefresh\blist_alt\x04lock\x04flag\nheadphones\nvolume_off\vvolume_down\tvolume_up\x06qrcode\abarcode\x03tag\x04tags\x04book\bbookmark\x05print\x06camera\x04font\x04bold\x06italic\vtext_height\ntext_width\nalign_left\falign_center\valign_right\ralign_justify\x04list\vindent_left\findent_right\x0efacetime_video\apicture\x06pencil\nmap_marker\x06adjust\x04tint\x04edit\x05share\x05check\x04move\rstep_backward\rfast_backward\bbackward\x04play\x05pause\x04stop\aforward\ffast_forward\fstep_forward\x05eject\fchevron_left\rchevron_right\tplus_sign\nminus_sign\vremove_sign\aok_sign\rquestion_sign\tinfo_sign\nscreenshot\rremove_circle\tok_circle\nban_circle\narrow_left\varrow_right\barrow_up\narrow_down\tshare_alt\vresize_full\fresize_small\x10exclamation_sign\x04gift\x04leaf\x04fire\beye_open\teye_close\fwarning_sign\x05plane\bcalendar\x06random\acomment\x06magnet\nchevron_up\fchevron_down\aretweet\rshopping_cart\ffolder_close\vfolder_open\x0fresize_vertical\x11resize_horizontal\tbar_chart\ftwitter_sign\rfacebook_sign\fcamera_retro\x03key\x04cogs\bcomments\rthumbs_up_alt\x0fthumbs_down_alt\tstar_half\vheart_empty\asignout\rlinkedin_sign\apushpin\rexternal_link\x06signin\x06trophy\vgithub_sign\nupload_alt\x05lemon\x05phone\vcheck_empty\x0ebookmark_empty\nphone_sign\atwitter\bfacebook\x06github\x06unlock\vcredit_card\x03rss\x03hdd\bbullhorn\x04bell\vcertificate\nhand_right\thand_left\ahand_up\thand_down\x11circle_arrow_left\x12circle_arrow_right\x0fcircle_arrow_up\x11circle_arrow_down\x05globe\x06wrench\x05tasks\x06filter\tbriefcase\nfullscreen\x05group\x04link\x05cloud\x06beaker\x03cut\x04copy\npaper_clip\x04save\nsign_blank\areorder\x02ul\x02ol\rstrikethrough\tunderline\x05table\x05magic\x05truck\tpinterest\x0epinterest_sign\x10google_plus_sign\vgoogle_plus\x05money\ncaret_down\bcaret_up\ncaret_left\vcaret_right\acolumns\x04sort\tsort_down\asort_up\fenvelope_alt\blinkedin\x04undo\x05legal\tdashboard\vcomment_alt\fcomments_alt\x04bolt\asitemap\bumbrella\x05paste\nlight_bulb\bexchange\x0ecloud_download\fcloud_upload\auser_md\vstethoscope\bsuitcase\bbell_alt\x06coffee\x04food\rfile_text_alt\bbuilding\bhospital\tambulance\x06medkit\vfighter_jet\x04beer\x06h_sign\x04f0fe\x11double_angle_left\x12double_angle_right\x0fdouble_angle_up\x11double_angle_down\nangle_left\vangle_right\bangle_up\nangle_down\adesktop\x06laptop\x06tablet\fmobile_phone\fcircle_blank\nquote_left\vquote_right\aspinner\x06circle\x05reply\ngithub_alt\x10folder_close_alt\x0ffolder_open_alt\nexpand_alt\fcollapse_alt\x05smile\x05frown\x03meh\agamepad\bkeyboard\bflag_alt\x0eflag_checkered\bterminal\x04code\treply_all\x0fstar_half_empty\x0elocation_arrow\x04crop\tcode_fork\x06unlink\x04_279\vexclamation\vsuperscript\tsubscript\x04_283\fpuzzle_piece\nmicrophone\x0emicrophone_off\x06shield\x0ecalendar_empty\x11fire_extinguisher\x06rocket\x06maxcdn\x11chevron_sign_left\x12chevron_sign_right\x0fchevron_sign_up\x11chevron_sign_down\x05html5\x04css3\x06anchor\nunlock_alt\bbullseye\x13ellipsis_horizontal\x11ellipsis_vertical\x04_303\tplay_sign\x06ticket\x0eminus_sign_alt\vcheck_minus\blevel_up\nlevel_down\ncheck_sign\tedit_sign\x04_312\nshare_sign\acompass\bcollapse\fcollapse_top\x04_317\x03eur\x03gbp\x03usd\x03inr\x03jpy\x03rub\x03krw\x03btc\x04file\tfile_text\x10sort_by_alphabet\x04_329\x12sort_by_attributes\x16sort_by_attributes_alt\rsort_by_order\x11sort_by_order_alt\x04_334\x04_335\fyoutube_sign\ayoutube\x04xing\txing_sign\fyoutube_play\adropbox\rstackexchange\tinstagram\x06flickr\x03adn\x04f171\x0ebitbucket_sign\x06tumblr\vtumblr_sign\x0flong_arrow_down\rlong_arrow_up\x0flong_arrow_left\x10long_arrow_right\awindows\aandroid\x05linux\adribble\x05skype\nfoursquare\x06trello\x06female\x04male\x06gittip\x03sun\x04_366\aarchive\x03bug\x02vk\x05weibo\x06renren\x04_372\x0estack_exchange\x04_374\x15arrow_circle_alt_left\x04_376\x0edot_circle_alt\x04_378\fvimeo_square\x04_380\rplus_square_o\x04_382\x04_383\x04_384\x04_385\x04_386\x04_387\x04_388\x04_389\auniF1A0\x04f1a1\x04_392\x04_393\x04f1a4\x04_395\x04_396\x04_397\x04_398\x04_399\x04_400\x04f1ab\x04_402\x04_403\x04_404\auniF1B1\x04_406\x04_407\x04_408\x04_409\x04_410\x04_411\x04_412\x04_413\x04_414\x04_415\x04_416\x04_417\x04_418\x04_419\auniF1C0\auniF1C1\x04_422\x04_423\x04_424\x04_425\x04_426\x04_427\x04_428\x04_429\x04_430\x04_431\x04_432\x04_433\x04_434\auniF1D0\auniF1D1\auniF1D2\x04_438\x04_439\auniF1D5\auniF1D6\auniF1D7\x04_443\x04_444\x04_445\x04_446\x04_447\x04_448\x04_449\auniF1E0\x04_451\x04_452\x04_453\x04_454\x04_455\x04_456\x04_457\x04_458\x04_459\x04_460\x04_461\x04_462\x04_463\x04_464\auniF1F0\x04_466\x04_467\x04f1f3\x04_469\x04_470\x04_471\x04_472\x04_473\x04_474\x04_475\x04_476\x04f1fc\x04_478\x04_479\x04_480\x04_481\x04_482\x04_483\x04_484\x04_485\x04_486\x04_487\x04_488\x04_489\x04_490\x04_491\x04_492\x04_493\x04_494\x04f210\x04_496\x04f212\x04_498\x04_499\x04_500\x04_501\x04_502\x04_503\x04_504\x04_505\x04_506\x04_507\x04_508\x04_509\x05venus\x04_511\x04_512\x04_513\x04_514\x04_515\x04_516\x04_517\x04_518\x04_519\x04_520\x04_521\x04_522\x04_523\x04_524\x04_525\x04_526\x04_527\x04_528\x04_529\x04_530\x04_531\x04_532\x04_533\x04_534\x04_535\x04_536\x04_537\x04_538\x04_539\x04_540\x04_541\x04_542\x04_543\x04_544\x04_545\x04_546\x04_547\x04_548\x04_549\x04_550\x04_551\x04_552\x04_553\x04_554\x04_555\x04_556\x04_557\x04_558\x04_559\x04_560\x04_561\x04_562\x04_563\x04_564\x04_565\x04_566\x04_567\x04_568\x04_569\x04f260\x04f261\x04_572\x04f263\x04_574\x04_575\x04_576\x04_577\x04_578\x04_579\x04_580\x04_581\x04_582\x04_583\x04_584\x04_585\x04_586\x04_587\x04_588\x04_589\x04_590\x04_591\x04_592\x04_593\x04_594\x04_595\x04_596\x04_597\x04_598\x04f27e\auniF280\auniF281\x04_602\x04_603\x04_604\auniF285\auniF286\x04_607\x04_608\x04_609\x04_610\x04_611\x04_612\x04_613\x04_614\x04_615\x04_616\x04_617\x04_618\x04_619\x04_620\x04_621\x04_622\x04_623\x04_624\x04_625\x04_626\x04_627\x04_628\x04_629\auniF2A0\auniF2A1\auniF2A2\auniF2A3\auniF2A4\auniF2A5\auniF2A6\auniF2A7\auniF2A8\auniF2A9\auniF2AA\auniF2AB\auniF2AC\auniF2AD\auniF2AE\auniF2B0\auniF2B1\auniF2B2\auniF2B3\auniF2B4\auniF2B5\auniF2B6\auniF2B7\auniF2B8\auniF2B9\auniF2BA\auniF2BB\auniF2BC\auniF2BD\auniF2BE\auniF2C0\auniF2C1\auniF2C2\auniF2C3\auniF2C4\auniF2C5\auniF2C6\auniF2C7\auniF2C8\auniF2C9\auniF2CA\auniF2CB\auniF2CC\auniF2CD\auniF2CE\auniF2D0\auniF2D1\auniF2D2\auniF2D3\auniF2D4\auniF2D5\auniF2D6\auniF2D7\auniF2D8\auniF2D9\auniF2DA\auniF2DB\auniF2DC\auniF2DD\auniF2DE\auniF2E0\auniF2E1\auniF2E2\auniF2E3\auniF2E4\auniF2E5\auniF2E6\auniF2E7\x04_698\auniF2E9\auniF2EA\auniF2EB\auniF2EC\auniF2ED\auniF2EE\x00\x00\x00\x00\x00\x00\x01\xff\xff\x00\x02\x00\x01\x00\x00\x00\x0e\x00\x00\x00\x18\x00\x00\x00\x00\x00\x02\x00\x01\x00\x01\x02\xc2\x00\x01\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\xcc=\xa2\xcf\x00\x00\x00\x00\xcbO<0\x00\x00\x00\x00\xd41h\xb9"), } file6 := &embedded.EmbeddedFile{ Filename: "7e367be02cd17a96d513ab74846bafb3.woff2", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("wOF2\x00\x01\x00\x00\x00\x008\xf8\x00\x12\x00\x00\x00\x00\x80\x80\x00\x008\x93\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1aL\x1b\x9a\x0e\x1c\x81\b\x06`\x00\x868\bL\t\x83<\x11\f\n\x81\xc6\x1c\x81\xafi\x12\x81h\x016\x02$\x03\x86v\v\x83>\x00\x04 \x05\x82\x10\a \f\x81[\x1bYr\x15l\\e\x86\x8d\x03\x80\x05\x9f\xedx\x14\xe2q\x00{\xd4;\x8a`\xe3\x00\x02i\xee\xce\xfe\xff\x96\xdc\x18\x03{\xc0\xfc\x1b\xaa\x04\xbbH\xe2\xb0\x03\xa3w7\xed\xcdF\xbc\x05\a2\xdb.5\xfc\x14I\xe9\"P\xdd!S\n-z}n;\u05ff\x90\xac\u0097=w\x8dM\n\xe7\x8b\x0e\xfd4ijJD'8c\xe3\xc3[\x05\a.\xfc\xaf\x1e\x92\x14M\x9e\u007f\xf2O\xf8_gfn\xf2\x00\xe4A\xe8dv\x80>\xa4JTEf\x0f\xcfϭ\xf7\xb7\x01#\x1d\x1b\xa2\x82\x01\xe80\x89\x94\xac!=\xa2rT\b=rԨ\x92\x1a\x82\fz\xa3C\xa4D\xa5\xcfSP̆\xe7\xfb\xfd\xfe\xb7\xb6\x9e{\xbeXv\xd4\x12CR\x8d\xbcF\xa5QIn)\xa9fJ\xa3\x04\x1ao\xf6}\xd3|gwg[\x91V\xa5\x9et\x92}\xf2/\xad\x00\x96\xc7\r`\x00\r`\x01Є\x86\x19\xc0\x0fa\xae\xd7廫;\xddٻ\xd2)@T#u@\x0f\x0e\xfcM\xa4\x00\v6\xa0gw\x92=\xe9\xb1K\x9f\xea\xa7'(\x05\x86\x00\xb7i\xf0,M\x80\x05+\xeb\xfbmj,\xaf\xac\xdc\xc3.\x8f\xbe\xa3s%\x9dK\xf8\xba\xaa8\xf5qx\xd3\xcb\xe9\x0ed\xa9\xd08$F/\xfdI\xae\x92a\x1c\x8a\xf3X7\u05cf\x8c\x8e\x05\x18ߚ\xef\xf2\xdd\x1e$\x13\xdeХ\xc4Sb\xf7\xbd\xafBU\x85\xaa\n\x9f\a\x1e\x88\xaf\xb1\xdf}\x11\xc1D\x12\x95\n\xa9Sþ\x9d\x9b\x9f2\xd0?^\xf6\xcb\xee^i\r#~b#\x90\x00\xf3\x83\xff\xf7\xf7{\xb3]9E\xb9\x85,\xc2M\x1a\xff\x8d\xac\x92\xaf\xafxsKt\v\xaa\n\x15\x82p\x15\xaa\xba\x92\x84\x02\x054\xc8\x12\xe2'\xa3Lj\x89\x1f?\xf3\xa9\xfa[\xef\xec\x94\xdf(\x16}dce\xd8\\\xa0\x0f\v\xcbȘ\xbb\x93N\x91\xceJ\xd1)\x84\xb8$\xfd\xb7\xe2\xd0B՝\xfcA'\u007fx\x92L\x89M\xf37\xbd\x8e\xa5\xf6\r\x96\x8d\xc7\xc8T\xea\xfcOs\xf6&ku;ƌ\x91,\xdd\xe1L\xfeO\xee\xae?\xb94Mw\xae3w\xac\xdf\xfd{\xc7hw\x8e\x15\xe6\xd0\b\vn\xe7\xae+eݱv\n[\xb5U\xadp<$\x12\ajY\x8c\xc1\xba\x16\x87\x83@װ\xa7\xfe\xfcP@T\x99\xc96\xff\u007f\xa8{\xee\xc35Rc>]\x1c\x8a\xf4\x85 E\xe4\xe4DZ☓L\xcdS\x823~l\xf8\xb8\xb5\xe3\r\x12\bК\x80D\xdc.U\x80UTW7,\xb0\x94@4كDN\x04\x97 H\x92 \xa9\x82\xa4\xff\x0f!\x99\xb2AjԂi\xb6\x03\x02\x01Vb*\x04\xe2\xd0\xc3\xf5L\x01\xcf%\x91\x04_ x\x05\xc1\xcd\a\x88_\xeb\x14\xe2\x0f\x94 \x00\xf2\x8f\x02\x16\x99P&,\v~\x86k.#\xf8\x03\x9ef\x1aP\xf5\x04\x02,\xcce\x1cm\xc6^\r\x13\x90\xc4ݱ\x83\x8b\x98W\xfa[9UQ\xdd\xd5R\b\x99\x90\xb9\xd9U\xdc\xc2'jeZ\xf4r\xaf\xcf\xe2[\xb7\x92\xa2\xeag\x0e\xd1w\xfb쁯\xf0mE^w/\xddk\x1f\x9a\xfd\u007fc \n\x18\xb1\xc2\xc0\xba\xea\x9a\xebn\x10!J\x82$\x19r\xe4)R\x82\xa3K\x8f1\x13x\xe6\xac\xd9)P\xa8H\xb1f\x14-\xeek\xd5\xe6\x81vT\x1d:u\xa1\xa1\x1b0hȸy\v\x16-Y\xb5f݆M\x8fm\xf7\xc0M\x98\xf0\x011\xcf\v\x10\x9b\x8c5o^\x0f\xc6\x12\x87\x84TCBd\x8byz\xd8d\x86߲\x1f\xc4\xe4\x9a\xf5]\xf4\xa5&Gzj!\xb0㢊\x93y\x0e9\xc1\x18\x9c\x82\x8dׁ#\xe0\xd4\xff\u0b00f\bNw\x9d%q689\v\xb0^t9\x13,\xf3\x8eM\xf6\xb8\xf9y\x91\x9f\xcf\xcdF'\xcc9y\xcb&\xaf@\x12\x1cs\x1cXr\x18l\x82#\x0f\x03\xae\xd7\xcd+\xe6\xd9b\xa9\xd2࿂_\x1e\x88ɒ\x84\xd4H͘\xf4\x8c*\xcd\x1aHa\xa8 \xba\xd8A\xceB\x00\xe6n\x10\xa5\xe6w\xa1\xe5\x8a(\x01a\x82\xc0\xe6\x06)Ҕ\xe9\x82\xc8d\v\x82\x1c.\xf2\xd0\x14q\x1b\xaf+\vC\x85\x1b\xf4\x93acNx\xf9\x9d\f.\x9a٥\x9d\xd7t\x048\xee\x80\xc7̵\xca+\xd69b\xe7f\xe75r\xb8))\xa4\xc4\\\v\xf7i\xa5\x8d\a\x85\x13\xd95\xc9\x14\xd3\xcc0\xeb玘g\x81E\x96X\xf3\xeb`\x83M\x1e\x17n\xe5\x95'<\xe5Y\xe1v\x8e\xec<\t\x03\x99l\x90\xcbs\xf29\xa1\x98\x1d\xe3\xc2#,P\xfd\x1b\x83\x95\xf5+\x18\t\xe7-Yb\x98\x11F\x19\xab̉\x11\nk\xa1v\x8e\xe8\x80\xe3N0\xcb+QX\x00\xa5\x90\"\x8a\v\xe7rd\x9e\x05\x16Y\xfar\x10.\b\xc0D\x80\x11o1\x8a@\x18\x18\xb2d\xc3A!V\xda9\xa1#\xc0q\a3\xb2\x99c\x9e\x05\x16Y\x8a\x8c\xf0\x925\xd6\xd9\xf0\x9b\xe0qe\x04T\x92\xa1!S\x92JN8\xe1\x84\x13N֟`\xa1\x87\x8f!0Z\x8fO<\x8b\xe4ZS~\x1dl\xb0\xc9\xe3\u007fv\xd8t\x11\x02\x98>ې\x9cw\x16\xc8r\xbeԻ\x10\xf3\xb9\x06r7_`\v%u\xaa<\x9e\xff\x9b;\xe2(s\\\xe7?\xf5\xe8<\x87\xc6\x19z\xf2U\x06\aj\ue032\x1cS\xf3n\xe7\x85zro\x00\xeb\xe4\x9d#\xfa\r\xf7\x98\xfe<\u007f\xb1J\xa7\xa0R\x9bI(\xda\xd9\x01D\xd0\xf2\xbb\xa6+g\xab \uf2d4\xac\xa0\x00\xd9\xcaA\xda-\x8e#'2J\u007f_\x89\xceH\xe6\x97T\x00\x12\xb4|S\xeb@\x8c\xc3\tF\xe5\xac\xee\xa3|\xa2\xf3IX\x0e\x96\x83\xff^\xec\xdc|C\xaa\x9b1_X\xbeA\xa9ǒ\xc6\xf6\xebz\xe1oȏ\xa9\x9c1%D\x1e\x95\x88b\xef\xe5\xc5!\x88\\=\xc5\xc3\by\x00\x9a\xae\xecZ\xdd\xd7\xc8\tWI\xb9m\xa5e\xcb!\xa7&-\x81)7\r\x1d'\x12r&\xf4n\xe2q\xcfT\x84&Y\x0e\xf5\x10b\xae\x9ai0\x18'\x02V\x16\x04%\x18\xf4\x9b\xc0a]\x87p\x8b$f2\x94\xa0i\xd2v\x0eN\xe8<=\xf7\x02c\xa6.2\x97\x00k\xf72[\xf6\xae\xc8x\x96\xab\n\x94\xb8\xa6\xd9\x12a\xab\x1e3}\x164g\rF*\xa2\xf1\x18\x8f\xf1F^\x82$\xc8U!\\\x13\xdcu\x97\xc1-B`\x84\xc5BDLD]$1\xe2`$\xa6\xe0$\xd3L\x88\x91\xacXȉ\x95\xbc8(\x88\x8d\xa2\xd8)\xe9\x14M\xa1h\tM;p\xe6e\x98|.ޤ\xf1x\xd2i\xa6\x01\x9d\xa3\x9d\x81;\xa1\xb3\xec\xc4\xcd>\x9c\xcb7\x1fm\xa6\x10\x0f2\x02\xef\xf3\xc5ȣ\x04\xe2\xd5\xec\xe1HD\x04*\xd63\xe8\xd6\x0f\x86\xb6\xbb\x1en\xd8\x18\x16\xe3䲛0\ve\xce\xe54o\t\xc6\xeab.k\x1e;]<\xb8M\xd2\x10\x86\x88\x1c8ahq\xc0\x06\xb6\x12F\x98\x9bh-[8S\xa4\xb4\xf4\xc9*\x98ہ\xabT\xc6dpc1Fc\xcfd\xf5\xa7\xe5\f\x0e\xca\x1d\n\xa8\xf2\x18\xc6\x18\"\x86\x19\x84b\x91\xf0V\b\xc8R\x19d\x9c\x06\x05\xe6QK\x84\xc9\"\xc1d/\xa0\xe4\xc9/\x9e!\x8c\xc3\x10\x91C\x13\x16\xa3Q<\xc0V\xc2ֲ\x19\xcc\xe0\xc6\xcai\xbf$ӂ\x19f1\x83-\xcf \xa9}$4w\x8b\x0ehܳ\x1e\x89\x05t\xeai&|\xd4>\xfai\x1f\xf9,\x86]{\x95\x87\x13\xe0hVp\x03\x9a\x91\rPdķ\xbb\x1d(H\x90[\xeax\x84\x1a0\xfa\xd6<4\xb5\xc1\xc8\xf3yt\xc1\xc7\xdd\xca\xc5\x00L/\x9b$\x00\xb59-\xf4o\xe0\xde.\xa7\xb5\xfa.?\xc7\x12#\xfcw\x01`\v\x80#N0\xd4\x06*@\xfe$\xf8\v\\>/k\x80\x0f\xf4j\x80q`P\xf1\xe1k\xdb\xdd\xeb\x85M\xc0A\xear\x16\xa0.\x84\xa8\xa3\x80}S\xdf\xcc\rXU\x9c\x9a1ͷ\x1c1\xed\xa1\x17\xde\xf9\a\xbd\x10|\x9b֧\xba\xbdʵ\x0e\xebi\xdd֏\xebo\xbe\x10\xbe\xd6\r\xfd\xff?\xf8\xe4\xf3ɯu\xde\x03\xa3f\xacx\xe9\xbd\xc81)Ĭ\xdd:e~\xf8\x11\x95\xaf\xf5\x1cc\xc6\xf3\xb3ƀ\x1c\x019\x04b\x1d\xb9\xf1\xec\xb8\xe7O\xf7~\x1c\x91K\xac\x18\xbbu\xef\xe3\xee\xd3\xfe\xd8[\xfd\x01\xdb\x03\xf6\x04\x9cK\xec\x1b\xab\x1dMЬ\u007f\x9c\xf6\xf7\x13%\x196bԘq\xc9RL\x984eڌY\xa9\xd2\xc6o\xb7\x13\xa5\xdb\xf2\xc4S\xcfl˰32\xef\xa9ǀ\x03۪\x86ʣuZ\xdf\xdd\x01\v\xc0\x9d\x80\x01x\x8d\xb8\xfbM\x80\xd7\x02\xde\x01\xb8\x04\xf0N\xf1lo\x06\xbc\v\xf01\xc0\r\x80\x8f\x03>\x93\xc5\x14\xf0Y$\xee\x02>\a\xf8\x1a\xe0݀\xaf\xe3\xe7\xbf\x05\xf0\r\xc0w\x01\xef\a|\x0f\xf0}\xc0\xbd\x80\x1f\x00~\x02\xf8(ী\x9f\x01\xee\a\xfc\x1c\xf0\x1b\xc0\xa7\x00\xbf\x15w?\b\xf8\x1d\xe0\x8f\x80/\x00\xfe$>\xfc!\xc0\x9f\x01\xff\x00|\x13\xf0O\xc0\xbf\x00\x0f\x03\xfe\r\xf8\u007f\xac\xee%J\x1f\x05\xa4\xe4'@\xed\xf1\x1c\x1e\x03\xead\xa0N\x01<\x0e\xb4ϊ\x1a\x9e\x04\xda\x0fkx\nh?\xae\xe4i`\xb1\xb6\xbb\x0e\x00\xcf\x00\x8b\r܍\x00x\x0eXl\xd2x3\x00^\x04,\xb6\xac\xe4\xa5\xc0\xe2\x80\xc6\a\x02\xf02`qh\r\xaf\x00\x16\xb7\xd6\xf0J`\xf1\x8e\x1a^\x05l\xb9V\xe8\x82W\xbba\xff\xe2\xf12DX\xe7ʀ\xb54\x8e\xccI6\x05\x06M\x02\xd6Y:\xfa\x86\xad\x1c_\xe7\x87u\x1c\x80kdCMT\\\xb3\xb3\x9c\xaeF\x9c\xb2\xf2\xf73ȟ\x96\xf4\f\xfaE\xaeErF\x8d\xd4]\xbf\xb0\xee\xe6p\xc8 ;\x88\xdc\xdbB.g\xd0\x1d\x90m&\x83\xed\xa0\n\xb6\xb1\x9aq\"\xa4\xf9\xb2\xa7n\xbaZ\xd1\xeb|\x83oTh\xe5\x8b\xdcuT\x10\x98\xf3\xbb\xb9\xf4\x82\r\x0e\x8a?o\xbc\x8b\x94\x19\xbc\x8b\xacO\xe8\xc8\x14}Zg?h\xeb\x14\xffL\xec\xe8\u007fP\xdb4\xe1)\xe3\x86\xf5qЊo\f9\xfc\x9ep\x1b\xb5s\x9fH\x86L\x0f\xa9\xda\xf7\xed%\n\xdaDn\x1b\xe5\xc1\xfb\x15挃>\x10}\xa4\xadI\fn\xa8\xfa\x9a<=.\xf2\xbbk#l\x17\x1cD\xd9\xefr\x8dܸh\x1d\xbd\xd1m_\x8e\x8c\x91Dɽ\x88\xd8\f}G\u007f\xcen\x1b\x9a턚[\xaf\xaf4W`\xe1\xf5\as\xb7\xde\x1c\x10q\x91\xf1ט\xa6\x02\b.\xefG\x02\xe2\xa9s-\xb8ҏ\xae\xc6K\xdd;\xde\xc7G\xfc\x0f\x05L\x9cἿ\xf5\xfa\xfb\xfem\xf8\x80\xf5\x99\x1dX\xa0\xb11\r\xb0\xfd\xe8hB\x00\x02\x83\x04˿\xf1\xca\xff\xffS\x80\xfa0\xe0\x97\xc0*o\x03\xd6\xfe\x02\x00\xfdi\xa0\xed\f\xd8\xf5_\x80\xe1\x19\x15\x9eT!\f\xf7\xb9R\x84a\n\xd76<\xbc\xf1\xe6\x9a\x03\xf9\x85\xe1\x1c\xe91\r\xf4&1\xd7i\x1d\xf4\xa3GV\x9d\x02\x1a\xd3(\x9d,v\xee\xecB2\xd6\x0eH/H\x89W\xd8`+۴\a'\x85\"\xe8\x01\x12\xa5\xb2\xbd;\x17/D\xbb\x18\x8f\xb6(\xe7\xd4}\xaaW\xf1,\xdf2\xa6\x19\xaf\x9cKo\x04ۘ0\x9c\x05\xf54\x1e'0\x90D\x8b\x96\x87\x10ǦmщE\x86\x86\x80\x1cS\x89PП\x97\xed\x10\x05* <\xa7\xcf\x05q%\xe2\x84+\x82c5T2\xea\x97h\x88\x8eK\x84\xcc\xc9\xe1\xf7=\x0e\n\x88\x04/\x1b\xd13\xd8L\v\xb0.\x80(\xab\x8d\xeei\x00\xd0d\xea\x9a\xcaz\xa52\x9bww\x17\x92\x17\xcav]-\x06\xff\xf7\xb8\xe4©\xd5\xf5\xd1X\xed%\x8dV\xad9I\n{\xa4Lw:C\xe7O\x8cm\x8e\x8feծLy\xbc\xa6\x97'j:6\x12B\xe0Qo\xa8\x81\x1eO̐\xe4\x02\x93]\xce\x12\x86\x83\xcd\xedh\x12\x96=K\xa5\x9f\xe2~\xe0\xcaML{\xf9\xca\xebu\xaf6\xa2\xf9e|\x85\xcf?\a\xe3\x9e\x04\x80\\\xf3\x1d/\xd7#\xe1\x13\xde\xfd\xc2=\x1a\x86P\x85\a5\x91\xc0\xed\x84!a ,\x8cF(\x9c\x86\xfe\x1dKK\x11\x8c\xc6]G\x12\x14\xed\xaeg\xc3\xcdu}P\tO\nP\x8d\xea\x02\xf9C\xdc\xff\xaf-\xe0\x93\x03\xd0:HJr\x0e\x9e+-A\xf2w%)\x97\xb2\x80\xb9\x8d\xcfY'Ur=W\xdf\x007\x10\xaa\x9e\a{\xed2!w\xb2:\xbe\x1c\xe4q\xd3\xf8%\xc8Q\x00-\xd7G\xdd\xd7\xeb\x98A\x8a[]g\xed\xb8\xa7w\xd2\x00\xaa\x86\xb1x\xa1\xba\x8f\f\xef\xfcl\xbbӳ>\xbdPSN\xe7\xf4\xbf\xa0,\x8fT}\xfc\xe7\x9e\xef\x19\xfd3w\x95\x93\xd9\xe9)\xed\x02\xd5qZSW\xbex\xf6#\xf8H\x19\x8dI8\xa3\xb7\xc3E\x96\x90\x88\n\xed\xf6\xc3\x17\xdf~\xbcZEvL\x81\xd4\u007f\xd0\xc1\xaf\x87\x0eFį\xa6\x96\xa9\xb4\x00\x85C\x04k>du\xac\xa1\xb0\u007f\xa8|\x8e\xab\x80輑Q?\x82s\xe4\\\x18yѿ\x02m\x01\x14\xd0\x17dh\vj\x84j:\x89K;F\xd1\xe2\x85^\x80\x91\x90\u007f\x1b\xc1\xfd\xc8\xe7\xf0o\x99\xf0\x1c\x9dY\xae\xfam\xa2\xbc\vM\x12~3\xa35\xd4>\x96\xac\xff\x84\xbf\x94R~\xa6\x91\"\x94\xcf=)\xd9#\x9f:\x19ߓ\xb3ݐ\x1a\xfb\x06\xe2\xf8/S-s\x06Ę\xe8f\xabA<\x06\xeaC\xe0#\xe0k\x83O\xc8=\xf9g\xbb\xf1\xb9_\xb8\x88\x19\xf2\x1c\xadI\x14\x93\noA\xe6\xc63Y\xda4\x1d[[G_\xe4\x88\xee4\xf8D%֊\xd4\xd1\x11\x9c\x03G/\xf9Z\xdb\"\xba\b\xab\x0ev\xdcz\x19\x0f\nh\x00\x8e\xd5\xc6\xd0>\xc2\f\x9di\x11\xf7\xb5m\x1a\rE\x0fJjp%\x0f\xbaL0\xd5\xff\x03\x9b'n\xac:\\\xfan\v\xed1k\f9\"\xaem\xb7Gt\xc1\xf5\xf9e\xe9\x0f\x8cu\x01\x91:\x0f\xf9\v\xb1h\byJ\x8b\x85I\b\xf22\xaf\x95\xb1\x96\x9c1\x96\u007foc\xb5j\xa0\x00\xad\xf2\x82\x9a\xcaq\xe0\x8d]\xebζ\x10\x02\xf0K\x16\xfe\x9e\xd9\xe5\x89\xde]|H>K\x01J\xba\x8fM\x8b\xd6\u007f\xb8m\x8b\xd6\xd0\xe9PU`\xb0\xbby\xb95\xf5\x0e\xbb\xa7\x951\x11\xb9\xb1\xb57\x9d\n\x11\xc8DÙ\xbc\xe4˅\xfb\xf3\xb09E\xe7\xff\"S\x8ďuX\x18檞|\xdaw.\x8b\xcc\xe8\xea\x94\r\x84\xcb{\x85Z\xad\xe4v\xa4v\xed\x95gK9w\xa9\x9ee\xa1Ƹ\xda,\xadx4\xb4\xeb60\xe8\xe5\x86@\x9f\xae\x87\xd6\x18\xee\xbe]\x82d\x84\x91\xb8ЭT\xb2\x16\xe2DA^\x1c\x9b\x90\x85d\x84[g\x9c\xb7\xdd`\x87ި\xaaB!T\xd4\xdb\x17+`\xb1G\xe0\xe2b\xeb$\xfd\x86$\xd26\x8c\xdb\xe1\x91D>Ƙ\xb6sa\xb6\x14\x1c\xf0\U0001a9b6i\xf4\v\xee\xc6\xc6\xe4+\\\xe3\xd0G\x1a\xe0̨\xa4\xfe\xe1\xa9+\x0e=\xae醝\xa7=\x83e#Ԋ\x9c3uu\x9f\xf8\\\x18M\"\x18\x9d\x89\xf2pq\xb6\xb5i7\xb6\xf4\xfa\x86\xda\\w\x14\x17ъl\xdb{}\teY5\x93\xb4P\xe2\xe7\x1aT\xe6tڸ\x8ce\xf1\xc2JR\x10e\x96e\xd2\x0f\xbf̆4?r,\x10\xc6\xf7c\xef\xb5?\x101\xd2\xc9z\xffnv\xa0kֆxô\x81\xbc\x86\x03\xa6\x9a\xfb\xca*y\x81\xd1\xf9$\xb6\xb2ֱ\x1c{F\xc7\x1dU\xe4\xabV\x03\xe8\x9a\xdbh\xe5\xe9u_\n(l\x13z\x06\xf1u\x92\x8a\x83\xadw\xa9\x16\x1c\x1d\xef\xa1a\x06\xed\x9f\x15*\x8e\tj^rz\xfb\x87\xba\x1eC\xb6\xd2>4\xb2yrN\x0f\xaaOLυ\r\xe3\xdb\x11x\xfc\rie\xe6c9\xb6\x13vP\x93\x88\xd3M\xf6e\f\xa7{x\x12\x106e8c\xed\xa9sRѲ\xeb\x847\xc0#!\xe7\xf6lT>IVpjz\xcak[R\xe6DB\xce\xe2\x1acO\xcf\xdb$\x1d&\x8e֍7h\x13\xb7X6`\x9d\x04\xa2\xc1\x88\x96\xa0\x97\xc4oJ\xc8\xf8;\xf7\xf4/O\xa9\x82W\x86\x04R\xb6\x8b\xe7bO\xb2\x80\x8b(\x03vق\xa7\xcbp\xf4\xebf\x19\x85\x82\xbc\x1f\x91\"\xad\x85\xad\x01\xdf` \fL\xcc\xebܻ\x85\xad\xbe\xc6\x0f\xc0\xe4\xa5\xc0\xdb\xd8\xec\x03\x91\x17\xde\xf25\xf8\x9fo{\x1b\v\xe0\x0f\xcf\xdd{\xe2\xed\xff9\xf9\xdb\xc7@-\x1c\xb2#b\xeb\x13Ã\xac˚\xc0\xfa\xe9q62^\xc8\xd77\xc6:\xca\xd7[\x84\xd3,\xe2\x82[\x00x\x93Ş\xa6\x1a\x9f\xfb]\x8a4,%\xb9һtf\r'X\xe6\x05\xe3jRX\x90\x96\xdcm\x163_j8\x1d\xa4\xff\xfaS\x95[)\x80|F\r\x92$\x95\x10n\x1b\x1f[\"\xbe\xae2\xbeR[?\xfc\xa8\x11\xf3\x8c\xbc\xdd\xc4M\u007f\xbdԟ\x9d\xd2@\b\x8f\x8d\xf2wO\x8e\x10un\t3\xaa\xf4\xa6\xcc/\xf5\xa2G\nGh\xa8\xd5\x17Ӵ\xcc\x94\x19!\xe3t<_m\xa0\x06\xdbQ1\xa4\xb8\xb0\x84\xfc\xf6\xf2\xfc*jr\xa4\x86$\x93\x93䗎J\x81\xc7\xf0\u007f\xdf\xf2>!if\xf8\x90u\xfd\xe03\xfb\xf2#\xb8\xca\x1f\xed|\xf3f\x13\xc4#\x92Cu\xd0E3\x8d\xee\xdag\x91\xb19}\xe4\xa2\xdc~\x0353\x1dl\x8f\xce8\x96|\x96\xabdK\\iRҏ@q\x12\t\x12M\x1e\b\x1f\xfa\xbb\xb7\xb2\xfa{\u007f0,~\x88Y\x8e\"\x01\x14\xdf\xfe\u05cf\xc7FO\x1e3\a;D\x95\x95\xfe,*\xfe\x9e\x9f\x9d\x94X\x90\xc3\xe9\xca㗚\x1a\x12\x90L\xdc=\x1b*\xe9\x99N\x12\x87\xf1\xb2\x1a5x\xc78z\xb4\xdb>1\xdf\xffx!\x9a\xb8\xf5]\xed\x8c4\xc50\xd5\x04q\xa5\xe2\xeas\xf8\x97\x17\x9c?\xec*\x82.\xe3u{+\xff\x8b\x8c*\xa0\x97\x92sF-Q(\xa6\xa4\xa0ߓ\x12\xfc\xd1\xf9-\xe6\xa8In\xc5{\xc2j\xb5j*j5jX\xb1ΌBذ\x94ST~\xbc\xd1\xe8Qn\xd7R(L,]\x0f\x9f\x98\x8a\xb7\xf7\xf3\xf2\v\xf3r\x8f\xf6\xb1\xa1A\xb5\x87ӽC\xab\xf3\x1d\xed\xb5\x14_\x1f\x1d\xa3\x92Q\x85[\xef\x04e\xec[\xfb\x95\xf5\xc3\xe33=\x03\x92MR\xa6\x19&\xba9y/ы\x1c\xf5\x11\xb0\xe1\x02/\xe3\xbb7\f\x95$\xae\xa3_\x14\x04e\xee1t\xa1\x93\xb5\x8a\r\xe6nOw\x85\xb3|a\xfdF\xf2\x15\x1c}d\xdd}\xb3\xf3\nasQ\xcc\xeb\xa7\xc7~g#+\x0f+z\xa77\xfa\xed\xbb\x1f.{m\r*\a\x81\xec\xd9FŖ\n\xfen\x97\xed\x91B_cTBK\xdex*<\x8aq&FO\vLQ\x9b\x056\xa8S\x9d\xe7\xde}k֢\x00\x06\xea\xf6\x9b\xb7\x13\xa3\xee\xc1\x15.7B\xdf\"\x1c9\xf1\x93\u007f\xf6\x8ed\x9a\xd9\x14S\xcb2\xa2\x98.*\x97\n\xc4\x15\xc5\xfaFd\xfa\x92Λ\xe9',\xf3\xbc\xb8\xf8\xf3-L:H:k!W\x82\xb9\xbe\xfd\xe8\f\x8c\xfb\xf9\xd8ٶ\xc6\xf2\xa2\xfe\x95S\xbf\xb2\xdfL\x9d\xe9\xaci\xc9\xe5\xed\xd2\xec\xf6\n\xe5×\x9b\xaa\xa4\xc0\xac\xdd\xf2\xf1P\x82\r\x13\x10\xbb\x14:t~\xf7+\xe6\x9f\xd9\xf7#\xf4\x06$\xab\x04\xab\rG\xecTV\xae\xa7[P\xb5\xb6\x1en\xa00\x9e\xe4\x924\xba\xfcaS\xab\xa6?\xba\xa8菎༐\x9e\xd4NLc\x91\x044\u007f\x17'\xee\xc5\xfd\xfc\xf4\xd1ōQ)>\x81\xb1i\x0e\x00\x92/{|#\xbe\xb5\":\xda\xc6\x15(g\xa3x0V\xbb[\xdb:x\x94\xb0K\x183\v\x9f?\xe6\x05\xd1\x1e\xea\x18ӣ\x9f\xaf\x1f=\xf9\xb03;\x1aK\xccL\f\xcf'\x83Ta\x9e\xfa\x8e\xfe\x93\x8b\rWR\x1f\x1e)$\n\xde\x13z\x11\xe2U\xd1Y\x19l6\x1b\xbb\xc6s\x9aza\xb2\xfe\x8c\xfbA\x91os\xba\\x^^pa\xbd\x04\xedx\xbb\x05guY\xd5B\xeb\xce\xcec\xcc\x0f*\xf4\xd9\xc3cD\xcf(\x9f\xb2IRbj\xa2\xb627\xe7\xcb@K^\xaby\xaa,-\xad\xed,\xcd$\x95\x8da\b\xfe\x90k\xa0w\x85\v\xf6\xc5\xe7\xd7\xeaE\x1a}\x81\x03\xf6[\xd6SV.\xd07\xe3\xdc;\x8eOs\xeb\xc8\xe7\xdf\xcc+\xac\xca\xee\xe5\xa6'\xde+ρ\xb9\xea\x1b\xa1)k)\xa2\x11f\xf0\x85\xc4\b\xf1OڜF\x9dF\xa7\xb2PHJ\x1b#\x00Z\xe4\x1e\xee\x11\xfe\x9f+\x97B\xb6\xaa\xf5\x87\xf5\xcc\xf5\x93\x1a\xe6\x87_\x91{\x9b\xc4\xfc\xb9D13/\xb6\xe5\xbc㫤悘\xb4\x1aR1\x10t2#~\xd5\xda_.iE\xefo\x17\xbd\n\x8e_\x88\x1cKn**ͩ\x8a\t\xb0*\x8a\xf2v[\x8cr\xbe\x17ЁY\u007fY\x04'd\xcd\xdc\x1dK\x1e/\xb9WR\x1b\xbdI\x0eq\u0085\xa2\xd7\x01\x03z\xa4\x1ebkU\xeeº\r\xd4]\xb0\x04\x18\x9e\x9d\xe2\u007f\xc7\vA\a\xfb\xc3O\xab\xda\x1e\xd4\xdekm\xabz\xa6\x90\xf1\x97\aѱ\xca?Em\xe1\xef\xa1>\x929\x9c\xe7\x1d\xb9֊\x86\u007f\xaf\xe3,\xf8~\xb5\xfd\xe2\x04\xc7\xe5\xe7cܧ\xa9\xed\xe8I,\xb7\u007fbL\b\xb6\x9d\xfb\xda6\xc1\xea\x9az\xa2\xad\xd0\xe1£͝\xe7n\xb7=n\xff\xf7xg\xf1с\x8f\xebz\xf4ӧ\x97\\\xf2.\x8d^j\xcc\xd8\x16\x93\xa9P\xad\xbc\x92+\xf6<\x91\xb3\xea[\xb6\x06_\xcf\xd1\b`$\x8e\x94D\x02\xa6\xfeH\x01X\n\x95z\xe7JP1\xa0(>ʐ\xa3J\xc6\x1c\xb1\x8cay\xea\a\x86\x0eZol\u07bd\xbey\xff\xaal|qaN\x88\xbc\x17귪!\xec\xde\xe5k\xff\x98\x8d\xe3S\xbaj\x16\xbb3)\x80~\xdcm\xe6\x14L\brIqѦ\x1b\x13\x8a&-\xc2\x06\xd6\xe8\xf0{\x1f\xc9X\v\xd4\xe7,\x8br+\x01\xed\xdd<\xd3\x19\x8b`\xa7H5\x18\xe7\xf7\xffn\x8d.\x05\xe4\"cZ\x00#\xa0)\x06dd\xec_\x87\xf7#\x86\xf2\x96\xd2\xfb\f)릔\ue502%W\xc5D\x97\x82p&\xabv\x84mhӤ\x8b\xff\x0eB\x10\\\xaa\n\xfe\xb2\x95V\xd8\xfb\xfe\tS[;\x93gN\xaaWv\xd9\xfc\xe8\xe5\x81\xd29\x9c=\xf6\xc7k\xe1\x91a\xff\x04IZ\x87\xa3\xa0\x05\x19\xec\x9f6=x\"\U0007d7f1\xbf\xf8a*ը\xf2\x83II+\xb1\xf8\x91\xab\x1ef\x1b\xc3\xf7Q\xa06\xea\xf7vN\xc2\xda\xf1i\xb8\xfe\x0e\xbc\xbas2\xc39\xcdP\\5\xd0\"m\x05\x1d<\x1fX\\Ǚ\u007f\x91\x83)+\xad\xcc\xeb\xe5\xfd\xfb\xa4\xfb\x01\x0fhC-*\xfb\xcc\xe4#)k'Og\xc2Kҷ\x91\xe6\xe2\x80\xfds|cl\xda?\x13\x19N\xe1[\xfd\xfea5t\xcfE\xce\xdc\xc1?,\xbf5\xad\xa88\xc3\xce\xff\xee\xe8\xf0+1\xda\xc7>\xf2JZ\xbbq\xd5[Ӳ\x96\xb4\xe2GI\\\xf8\xbe\xa8\xa2)\vX\xe2\x02m\x11\x84x\x06\x04X\xa79`\xf5\xe1\x9by$\xfa\xbb\x9f\x89^\xb1\x96\xd2`\x9e\x87\xb6ȩK\xad\\\xf7\xb9\x1e\\X$֫R\x06hrݰ\x18\xeb\xdc&\xe9!-Q\xfeKw\x02M\xce\xca\xd7\xe3\r\x82\x8dR\xf6\xfb\xda\xfb\x9f\x0f\x15\xd2\xf5\xe2\xfd\x9c݂,\xb4սԹ\x14\uf6d9DX\xe4\xbd|@K\x9c\xb1\t\xacw\xa4!l3|\t^q\xa6NgCU\x18\xe7I\xf3ժR*\xaa|\xd1E:\x18͘XrF\xf5\x14i\x8a\xd1A<\xde\xc7'\xeeQ[\xf7,\x98\x1c\xa5\r|\x9d]\u05ec\xc4\xd0\xe4\xa8\xf8\xf7?Q\x13\xcaCg5\xe4\xb2\xcf&\xcb\r\xc9h\xd4\xfc\v\xe1\xf2\xb4b5+B\x05K\xa2\xe0\x92E%\x8e\xad\x9f\x1d\u007f\xe3\xd4\xc4\r\xb2\xe3\xd7\a\xef2\xb6\x9c\xb8}\x056\u007f\x14f\xf6㓞U\x9b\xf87\x1c\xbf\xc7\xdc\xcdi^\xc9\x03W\xf6m\x01\r\xb6\"\x8c\xbd\xe7\x1d~\xd7k~g\xaf\b\xcf\xe3d\x8b\"\x8bm\x88X\xfc9\xbdR\xc9$\x8dp?\xaaX\xe7\xf3\xc1\x99O\xeby\xeb\x1fW\xa0\xde\a\xfb\x9d\xe2X\u008a\x1a\xcf\xfc\u007f\x8d\x96\x8dV\xff/\xc3\xd0K\xaab\xea\xecd\xc08LƠ\u007fΫ02o\xbf\xec\xc6w\x1b\x1f/\xf0\xac\xa8a\tT\xb1\x8e}п\xfa\xce\xf7}|\x9a\x1f\xec>x &f\xf0\x89\xb9\x8a\xbe\x95\xaesW\xb2\xb9\x00w\xf9\x86\x01\xde۾\x06\xaa\xe9[\xca\xd4\xcbTn\xaf\xf91\xa6g\x1f\xec\xa0F\xb9\xdf\fֹ\xc2qs\x8e\x9e\xd0}%\xe2\xb8Dׂ\b&\xcbv\x86ةYҹcګN\xfe\x19\xde\xed\xcb\xebs\xe7\xa9|\x87\xb4=2\xf7\xc8\x1a\x03Y\xfd\xc6`ٜɨ\x1fӳ2E\xeb\x1dP\x81\xacԏ\x10K\x84\x1c\x90L\xdb\r\x955H\xf8\xcc\xe77\xb3\xc3;k\x1d#>q\xd1\x01^\t\xa5\xb1H\xb8re\x8c\xc6\xfb\xc0\xaa\xa4G\x0f\xab\x12R\xd2\x12R3\xb2<\xca\xe7\x97\x03\xea\xf2\x8b\x12\xd2\xf2ɕ\xe1\x13\x93U\x11\x85\xc5IĢR\xbf\xba\x85I\xb7\xaa\x1cRR\x9a\x1c,:\x9a\xb06\x11y8\x11\x15\xb8\x1a\xfd{\xfc\x9a\x8c\b\f\x9a\b'\xac)\v\xbap\xf8\xd4>\xab}(\x13\x1b\xdf\x10\x1f\x13\xaf6\xa6\x16\x15\x13\xd5\x10\x15+[\xfbp\xdb'\x9f\xfd\f\xfd\xcc,\xfd+\xfd\xacv^\xfb\x10\xcdTCT;͵\x19>\xfd\xcc\xc6\xe4\x91\xcf&\xd9\xc6\\\xa5͉\xb1\xd1wU\"\xc5@\xa9\xa29\x03*o\x8d\xc6wʁ\x00o\xbf[\xa1\rY\xa8J\xe3\xf4\x15\\3(\x92X~\x95\xfb\xfbr\"\xaf\x8cz\x16\x96{{\xe6\x97yy\xe7\x93=\xbd\xc8俧\xa5qZ\xb2r8]\xe9ۺ\x06\xbe\xba8\x91k\x05\xe6\xbežUMU\t\xc5\t\x95M\\\\\x9f\xd4KԵZ\xca,|o\x9e\xf3u\xd3\x14\xfb\xadpG\xeb\x8el%\xa1\xb1\x00Y\x1a\xe7\xef\xe9\xed\x8d7h\xcf%\x99\xa2\n\xddLu\xb5\x15D\x85\x8e\xb5duůW:\xd7EŤ\x97Gx\xdeִV=[x\x8d\xe4M\x89!&7\xc4\xfaT\t\x19ȫk\xe9\xc8K\x04\x95C\x96阩\xbf\xfb\xacX\xf2\xe5Ӓr\x91\xa0νF\xba\xed͂\x18\xf7\xfa쪿D:\x84UTq\xf9\xec\x94uY\x9d\xe8\xe6\xdf\x18\x88E\x153b}(\x80\x0f\xb2V\x8e\x8cǐ{\xe8a\x84=S\xf0\xcc3l,n\x9c\xe9\xc5\u007fhY:y\xb6\xa5\r\n\x9cΩ.\x8a\x88n\xff>i\xc0B\x83f\xb1\xb2\xfc\x8a\xde=\xd9Y\x1d{w^SB\xd7~\x80d\x96\xa6W=\xdf\xd1k\xdc}\xd3\xe6F٩ԛR>\x81v&\x928\x87\xc1|\xf3tc\xab\x8ceS{\x0fo˧X/\x83}.\x86\xb4.\x15\xb3\x03\xbb즁\xd6\xe2\xcaö\x9d=3\x1f\xbf(O\xafL?\xe3>4\xe9C\xf7@p\xbb\x89\x98\x89\x92A\x9b\xb9\xd7\xeaKp\x1c\xbc\xc5\xd8\xde6L.\x8c\xb8\xf3V9\xad2B9\xb0H\xa7\x12w\xfa\xcd\xf3\x10(\xd3\x0f\xaf\xefe+\x05\x97\f.lAe\xf0\\JNɟ\x96\xd0<\xfd\x9d\xa9Ԯ*\xe4\xf7cRjڳ\xf8\x88\xbdԂ\x80\xa3億{\x11A\"\xe3YYO\nt\x9cG\x12\x93\xac{Ee\xaa\xdeJ=\xc8\x18\x8c\xd7qaLb?Ö\xa1c\x9bW;ܔ\xd58\x91\x18\xe8\xee\xa0\xdfTm\xd5\xf2E\xff\v\aY\xa9\x00sU\x1e\xb6E\xc2\xf2\x81\f|\xe7w\x1d\xf5{?\x83\xe4ow]\xa7 \xc6mq\xbcT\xa5*\xfa\xcf\x1e1\xd7\xd3L\x00W\x18\xe32\xbfX\xd8/=\xee\xe8o\u07b3\xa7\x93\xd1\xf4ĭ\xbb\xd7\xdc\x1f\xdf\uea56\xd9\xf8\x94\xe6\xa2F\xf0\xa7g\xcbTC\x93\x0eL\x1a/\x1e\xd5Ռ.G\xbf\x10\x98\v\x8c:\xb8\bu\u007fș\x1c\xa0E\xaa5\x03\xf5\xa7\xa8.\x86\x82њ/\xa0\xca\xd9\xc3(\xd0V\xe1\rrc\xcf\xf1\xe5\xf0m\x1b{\x0e-ˮ\x8d\xbf\xcas\x14\xac\x89\xf8\U000d4613>\x1b\xa2\xe0\xa2\xe26A\b|x\xfc\xe7u\xc82#\xe5\xded\xea`\xb8\x0f.\x96\x98\xf4%m1@\xc9E\xd1y:0u\x9bI\xe4o?\xb2\xaf\xfaqfz\xf5\xe6ro\xcdVFZ\xed\x86\xdaʦ\v\xb9\xeet:\a\x8f\a%\x17\x89\xba\xbee)%S\n)\xa5l_N\x05J\n\x84\xc6[D\x87\xd8\xd8F\x85\xb6FE\xf6\xd7a\xaek&\xa2\xa6.&\xaa\xaeꬦ!&\xae\xaayC\xe2\xffmߨ\x14\xdb\xeb]\x8e\x8a\xad\xe7\xafW\x89E3\xfaDd\xda]\x0ev\x90\xe5\xbftSF]H\t\xf5\x02\xe3\xa5m\x1b\a\xcb'\xf6\x87\xb8;\xce\xf03\x17r{j\xdb&\xc0H\xb9}\x89\xc0\xc5q\x9a?\x8b\xc7S\xc7:\x0e\x96\x17Fs]:\xb8\xfeŬ[\x9e\xda\xd6DX\t\x8dV\xef뼺\xad\xbc\xf7\x02\xe9\xf5>\x8e+\xa2\xc8{\b\v\xff\x00[\x9b _;\xac\x85\x9f\x9f\xbd\x9d\xbf\xbf%\xf7\x05,\x94\x95-\x98\x84\x11L\xceFq\x04\xf7\f\xb0m\x99\x03eC9\xa8:?\x8f\x05\x17\xba\u007fhf\x8c\x14\xfaB<|\xbc\xebQC\x88\x1b?\xa6:\xfcB\xc4\xf7\xf3W\x18X;c:\xd3;c;\x99\x19\xce_\xfc\xe4\x9dz\x11S\xeb+\xe0_O\x9dg\x18\xb8\x1b\x1b\x9f\x1e\x9f\xa6ԙ\xa9^\xac\xce;\x1f>\xc6k۳<7\x14آp\xb1\xb4F\xd2\xe7\x98[\x96k\xaa\xe4N\xf7\xf6þ\x80TF\xa5\x9az\x15\xb0 \xb2V\xeb\x9b\xfdz\x83\x86_\xa5\x86\xf4\xa1\x95\x91>Ga\x82\xb7I\xbbPz\x8d4\xa5\x0f%\x95>'*\t\xde\td\v\x83rc\xc1\x14\x05\x89\xdc*MJF\xa3i\x0f@Ӿ\x06\xad\xf2e)\xa0Իx\xed\x82l\x00\xfef\xa6\x82,\xb4t\x8d\xa6\x9dD\x82\xb7ς\"\xcbN\xf4jR\xbe\x8d\xa2\v\x9a\x10]\xb5\xec\x19\xcd\xf5G\x95\xb9W\xf7\x19\xa7{\xab\xe2/ \xf9\x82U\xd0.\xb1]ى\xe3\xa7X\x02\x0e\xa2\xa9\x06\x02kη&x˱gVj<\xf1o1+tcX\xdc\xc3\x04\xeb\x98ƛ\xc3k\x92\x9a\xed\"\x01\x1f\xd1\xfeЉ\x17\xed\t@s\xe5\xd1a\xb9\x97\xf2\x13\xbc\x95\bV\xde\xda\x19\x8d&\x9a\x03@s\xa15\xc1[\xae`\xc7\x11y%\xad\xdc\xfb4\xf0IT\xb6\x8a0\x9c\x02.\x19\xd0\xf5\b\xa3\xff\xa4\xe6\x8b\xf8\r\rח\xbbb\xa6VtW\xdc]{\x17Q\xd2#z\x8f\xb6\xc4\xf4\xcc\xd4\xc6\b\xd6/X\x03\xae\xfc\xfb\x18P\xd72Gv\x17 7\xa6NsƞCL\af\x18\x14\x06\xb7\xa6\xce\x00\xd8\xf7\xb6\xf49\rL\r\xb0풲\xebG\xe7\x81i\xd3&\t\xa7\xb4C\xa00\u007f\xe9\x9f.O\xf8R÷b\x12\n0\x0e\x9f\xab\xc0\xa9F2\xd5>P\xf7\xd86Q\x81v\x92\xa99\x06.)\x9bȾ\x00\xe4H\xd3\n\xa1qXN&\xe6Xa\x94s\xbfcCj\x8eɒFKja\xe0\x90\x06\xc0\xa7\x80\xf1!\x9b\xc5\xef\f\xfdU\xfc\xa5\xc2\x04p\x9e8\xa7\xf4l\xd8.5\xec\xc4\r\xd8\t\U00109ae6\xc2\x04\xd4\xdfTM\x03u\xc5\xf4\x87\"\xa0퇫\xe1\x83w\x8e\u007f\xff\xbe\x03\x1dV\n\x9c\x05\xf4\x9f\x85\xb6\xd8HC\xd2W-\v\x13\xe8nל)\x88S\xfb\xb6\x9a\n\x13\x109\x05\xf7\xf2\x8eQ\x89\xa5m\xa7\xae\xa6:\f\x06\xc3'6\x1f\xa0-\xbb\xb0\xeb\xabz%8:\x13\xe5J\xd0\xdd\xfb\xef\x92\x15\xa6\x00\x14љW\x81\\:\xa4]\x85\x84\xe9\xa43\xec\xfe\xfc\xa0\x88\xd0\xe9R,\x98\xd5ڬ\x81\x88\xd3\xf2\xb3\xb5\xe5\u007f.I\xb1\xe7\xa2\U0007aacc\xd2&ۿ\x06\x91\x82\x01\xa5\x10\xd0\r9\x98\xc6\xd2\x12\t>\b8d\x06w\ueebc\xda\x1f3炏4\x01\xb1\xf9j\xa3\x06\xd8\x1eK\xd5\x05;-\b\x02(\x81b\x15\x034\xa3\x80u\xc6\xf8iS\xa0\x1e\xd2p\xb3\xb7K\xcc\xd1\x13\x8d\x064p\x92\x994g\xb6\xc1{(쵝\x1bXD\x99˴\x0f\xac4\x02dC\f\xeep\xc7%\xb9\x86\x11\x86\x16\xa1\x9b\x1e\x06;ma\a\xf3\x86[\x9c\xc6|\xb8j\xdb\f_\xc8\xea7'\xcb\xdc\xf0\x86\xbe\xf3\xbfY\xfbw\xcc\fNS\x13Na\a\xbb\x8a\xdb\xeej.\xfa0\xdf\xffi\xecı\x96.l\x97\x8b\x15\xe5|q\xb9\xa7\\3Y\xda\xc7\x04\x97\x1c\x97\x9f\x0f\x83k+\xe9\x8b+\x8e\xff\x0f\x06#\xd6\xfb\xcc\x10\x9c\xe2\r\x81\xc8p\x13\xd7h\x89\n\x9c\xa3\x15\xd0\xd85\xd9i\xc1\x9d\x0e\x8b\x1a&\xeb\x9c\x18\xcd%\x10\xf0\n\xa0\xf5WJ\x88\x0f%\x93\\U\xe3q\x95rMo\x84f̐\x8c\xf0\r\x83\a\x1aXX&\xc9ۀ\xbc\x05W\xb9\x8d\x86\xdf\x0f\xa4\x8cZu\xea{U\x91+\x81Y+\xdc\n\xdbN\xc90\x83ѓ\xa0>e'\xf1\xec\xb5^\xf5!A?\xe4a\x9f\xe99\xf4<{\v\xa0\u007f\xa0\x80w`\xab϶ඳ\xe1\x89˼-\xe9\x01x\xc16\x97Ϥ\x03\xe8\xc1\xa4\xd2\xcf\xf9`I#\xfcD`Q\x92\xbb&=\x84R\xe8\x8bB\x10\xa5]R\xa5\x99\xb9!WP(\x90\f\x112\x12\x04\xee\a\x81\x88\x8b&{\xef\x91t8\xa2\xf1\xea\xbaÃBe̪3[\xaa\x11\x85\x0e\xa18\xc4\x01R\x83\xc0\xa0)5Qn\x82\x13\xc7I\xbf\x11考Յ\x00롚\x90\f\x83ʑ\x0f\x9d\x81c\xf2\xcf\xc1\x13\xff\x98rls\xb0b<\xbb\xaca>\xc2?\xb8\x1an\xf8\x10\xe3L\x01\xcc0/\xc8f\xad\xaf\xf8\x85\x83ߪ\x99<\xa28\xb7e\xd9K\xe8\x94Rލ\x9a\u007f\x9f\x90\x01\x81:T\xda\x0e\x1agjO\xe0\x83\x9f\x8b\xa2s\xc1\xf5\x00oϩ\x14pͬ\xa1\xd1\xe5\xe4`ۿ{\"\x92b\f\x19\xbe\x8f\xafPmTh`2^\xaf7i\x88\x94\xd7+p?\x05?8\xc2>\x8e\xf0\x12\x8e\xef\xb6+\xf0Pu\x1dݿ?h\x84\xdfc(>{&ukPd\xc4&\x1d\x12h\xbaܭ&\x84\x16\xf8l\x023r\x0fڼ\xa8\xa9\xf1\xa9\a`\x1a}K\xc2@Q0LӶÂ\xa82i)\rJ\xbfwۙ\xd4+\xdcհ{p\x00}\xc7d\xe8r\v\xa1\xd90\xa8VF\x92\b{qϡ\xb2$\xee\xafv\x1dU1\xa2\x1bTС)c\xef=)ϋ\x9f\x98㰱^$\u007f\x1b\xbe\xab\x9b\xe0U\xd0\xc2\xfb\xf9\xef\xb4m>\xcb\x0e\x01\x82#1\x8a\"ز\x11m\xc4\x00AT8g\x82-\x17\xf1\x91\xc1\x12k\x86{.8+\xf6\x11?\a\xbaijW\xae\xe4%\xef\xf9J\x8d\x1f\\IEĸ\xf4\xcf0\x92\xae\xfe\xab\xe6U\xf7jX55\x9f\xe6\xeb\xfc\x98\x19\x8eEC\xbf\xc4Q\x88\xe3\xf5\x91JPf3\xb8$N'c\x15\x04 J\xaf&Y\xc6\xe8\xb2IBD\xf1\xf3V\xf3\t\xc0\xdd\x01\x94\xe4\x1bL\xfd!d\x85C|\xff\xd05M1\xacT\x06\t\xbb\x1e%(\xdaTG\u05c9\\z\x9e\xf9\x95\x9a/ƹ\x00p\xf8\xb7*\x11\xd4\x15\x1f\xe2\xa2f\x89\xac\r\x90٨\x82\x15ف\xd5P\x05G8V%\xf5̛\xf1*\x89\x9a6\u061d\\9\x96\xa9\xf26\xc5N\xa8\xad\x90\xfa'8<\x00\x06-{\xebn\xb7\xb6t\xa9\xe4\xe0B\xaaׂ\xe6*\x18\xad P\xce\xd6!\x94#'\x91\xc7\xc4k\x8b\xfd\x8a\x9c\xba\xe14\xde\x19\xb5f\xed\x84\xc4\x16\x1d\x84n\x91o\x91\xd3a\x88V\xa9\xe9\x979\x1c\xadHMX\x17\xccaS\x9b\x14;7\xaa\xd4\xedg\x84 \xac\xa3+\x91\x0e4\xf97+ۥ63^`\xb9\xcab\xd6\x1d\x138\xabLx\x85c8\x184H\x12\xa4$\x19Z\x94E\x1b=1\x87\xde\xfc\x8e:\xc1\xcf\xe58\x96\xae\xb1K\x81\xebb&\xc3\xe8PD\x16\xac\x800eS\xdb>l\x8a߹\x84_9\x84\xec\x8c8\xfcx\xe3:\xc3EX!A7\x84\x99v\xdc\u007fa\xc78Q\xe9\xbc%0|:\xf6\x94B\xf0m\xe6m\x8d\xdd>OؾT\x8e\xfeU\n4W\xee\xdf\x01s\x124\xeb&\v+m\x9d\x16Ω\x98{\xfc87\xbc\x1fo\xe1\x9aA\x02O\x13G0\xe8\ue643\xaf\x0f\x12\xc0\xac\x04\xf5\xb2xf\x92\xbdh\x00$\r\x80k\xab\xe7\xc1\x92\x1a\x06\r\xf4\x8a\xf6\xb6\x0e8e\xe3\xfc\x9d\x1as\xb8\a!\x82py\x0f\xf5\xa0\xf6\xf6\xf1\a\x9e\x98\\\xa8\xcc\xe6\x96\xfb\x02\x89\x19t\xa99w6<\xf2\xab\x89\xdd\xcdJ\xb7\xa5:\xb0\x9d\b'\xfaۖ\xff+\xc3Ș\xad\xbd\u007fc\xc9p\xab\u007fw\"\xb4\xd0K\xbb9\xdc\xfdF+x&<#\x1b\xfc\xe2\xa6z7\x8e\xfa,߄S\xed\xa4\x9a\x1bj\xd94\xa5\xd3C\xb6\xd5\xc8ra\xe0_\x02\xd9\xc4G\xbb\xb2\x8d\xb7\xcal\x92\xa9h7\x94l\xbb\x98\xba\xb2\xe6 ٸ\xf5#otdpLu\x8a\x85U\x98j\xb5\xec>\xc6]C\a\x11\x1d\xc9#aP\x83u\x17|\xa4\xc5\xdf\xf6\xbe\x0f\xf0\xf8\xcdغ\x88fI\xdc#\xf9$\xd4°\xac5\xa3\xdc\xd3:xa\x0e\xab֓U\x93\xa7\xddkĨ\xeb\x1c\x96B\\P\x86\x82R\xfd\xf7\xb7\x1dU\xcax\x8dM\x14b\xd0+\xe6\x9c~\xd5@⫧B\xd6\xf3 \x1e7.\x89R\x02r\x89E\xe6ŝ'\x8bT\x01\x8bJ\n\xb3\xacٖlF\x87\xc9\u007f\xbbQ\xadO\aKW\xf5\xe8>İ\aE8;\x11\va0.\x88t$\xd4\x18 \x86֛\x83B\x15B\xd7)N\xb2\xcd9\xfa\xf8\x0e\x96\x9fO\x96\x15\x91\xd3I^h<^﷽\xdc\u007frՄ\xf8P\\2\xf1\xba\x9dt7\x9b\xfa\xc16r㩽\xa7\x04\x01\x83\xee*\np\x84\xd2LN\nAR6\x9aQ\a\x99\xd9\x05\x05\x12y\x92\xbe\xe1~\v\x99\xbe\x15ŭ\xfb\xf9\x82{Q\xd9\xf29\xc1ř\x1dM\xbd\xf9D\x1e?h\xb29\xb1\x17\xd5z^PO\xb1p\xe7\x95[\xaaF\xbfI\xb7\x15\xa1\x87\xdf/x\xa2d\xb9\xa5\xd7\x1d9V\xaf\x9aȻ<\xc8\xe1\x8b-\xc19\x87h\x89J\xferD\\@Nb\xf7q\xc4b\n\xbdc\xb5w\xfb\xadr\x8eT\x91\x98\xa5\x933@Y\n\xf1R\xfd\xaa\xc7Wr'\xd6\x03Q0:\x98P\xb5\xb7\x10A۰\x8f*O\xfd$\x18\xaa\xf2\xacyi\x9cgi\"\xce\x13\xee%Q\xe0!\xf8c@FB\xd2\xc5\x18\xeb[I,\\ֳ\xacx\xe8\xc4\xed:&\xf1\xb2\xcd\x18F\x91\x9e'}\bJ\xab\xfe\xfe\xa5]PVa\xe8\xb0}zTX1\"\xc8X-\xa0As!\xf3~\x0f\xbeg:\x8f\xeeB\x10\x8c)\xfa\xde\b\x0e\xee@c\xfbT\r\x95`9\xc5\xc2E\\\xc0\xa2\xc5Z탛g\xd3\x03\"\xf5N\xf1l\x84\x93ROȧ뻾TɮA\xdc=÷j\xc0E\xd7\\o\xbc\x8d\x14\xfe\x01\xde\x15@u\xf2\xa7\xff\xc4)\f\xef\xfa\xf5\x1f\x19\xfc'\uf7cd.:\x04\x03V\xfc#\xfa\u07fe\x80\xf9\xb5K6\x00V<\xea?\xbc\u007fu\xbb诲\xef\xfdI\xcf\xe2\xba\xe4\xd5\u007f럵\xadNM#!\x8e7\x84\x8f8\xfe\xd36^\xe3\an\xe5ƣ\rJi\xb3\bv\x82\xf3#\x9dA\xe5\xbbФ\xc06^r\xbb\x1e4\xd7\n\x06l\x81v\xd0\x1eJ\xc12\x98\f\xf5a\xe0\x8f\xedZ\xefI\xad\x0e\xd7\a':\xc3U\xc9\x10&\xb5\xcfpg.\x82\xf7$\xc3\xf9\xf1\v\xca$E\xf8\xa4\fed\t\xe0\xfb!\xa6`QS7J\x13\xa2ox\x93\x9a\x106`01\xfb\rc\xd5\x14/lbS\x96_\xda?^\xf3\x18a4\xa6\x11\x9bDɚ\xcep~~\x0e\xc5\t\x81\xecX\xe6\xd4\xf8\x83y\xbbD~\xd7q\r/\xc6\xdc/Q\xe9\x15\x10\xed\x9a˲\xf6\xfd0\xb1\xb2\x86(\xb6#\x89\xc3PĮ͠`\xbe\x99\xe3\xf0\x94U\x1fȮ\xe3\x1a\x19\x1cr\xed\a*\xe6p2\x877\xb7\x9a\xcb\xc6gG\x16\x87\xd6C\x90\xef\xc3Vb\x1eW\x10\xfb>\xfc387\xfb7\xae\xf79\xb8\x85e\xe9.\xc7*\xf5\x89v`\xcc\x14$i\xd1*\u05f9\xc5h^\xc8\xfb\xe2-\x81J\xe8\xfbN\x9d7\xfd\x01W@\xa1\x00\x00"), } file7 := &embedded.EmbeddedFile{ Filename: "912ec66d7572ff821749319396470bde.svg", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("\n\n\n\nCreated by FontForge 20120731 at Mon Oct 24 17:37:40 2016\n By ,,,\nCopyright Dave Gandy 2016. All rights reserved.\n\n\n\n \n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n"), } file8 := &embedded.EmbeddedFile{ Filename: "9f916e330c478bbfa2a0dd6614042046.eot", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("c?\x00\x00\x99>\x00\x00\x02\x00\x02\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x90\x01\x00\x00\x00\x00LP\xff\n\x00\xe0\u007f!\x00P!\x00\x00\x00\x00\x00\x00\x00\x9f\x01\x00 \x00\x00\x00\x00\xaeQ\x1b0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\f\x00R\x00o\x00b\x00o\x00t\x00o\x00\x00\x00\x0e\x00R\x00e\x00g\x00u\x00l\x00a\x00r\x00\x00\x00,\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00 \x002\x00.\x000\x000\x001\x001\x000\x001\x00;\x00 \x002\x000\x001\x004\x00\x00\x00\f\x00R\x00o\x00b\x00o\x00t\x00o\x00\x00\x00\x00\x00BSGP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00`d\x00/\x1c\x00/\xdc\x00\"2\x12\xcd\xe9\x8a\xc8c\xdaW\th1\xb97\xb88\x91\x12\x9d2\x16+\x96\x17sǷ\xa7\xe1\xc1n\x8d\x01\"\v\x16$@6\xa4\x95\xa7Y\xb0\x95G`^4\xb6\"G\xbb\xa9\xa2\xe3\xa6#&\xd7\x1fpJ\x8fl43\xe7o\x8e/i\xbb\x1fH\xb2I1>|6<\xa7\x9d\xb1\x8ey\xe2\b\xe3]\xd9\x1f\x1e~\x91\xad\xc58\xe7\xb4\xeb\x14\xe5\xfa\tU\x04wI\xba\xb8`Ԕᬀ\xb6\xb4\xea\r\v\xb2\x18\xf7J\x91\xe14X\xa3uβ\xb3\xbf2\xaeQ\xa0am\"ֱH\x14\x19\xb4\xd8d\xed\x8ce\x10H\xb7'_\x9b*\xf1v\x90\xb8\xf9\xe3;\xa6~\x9fyV\xa3ƌ>\x99\x97\xb2\x00\x01N̫'m\x0f\"\x9aM\xcbw\xc2\x1f\xef\xc2\xf8( o\xf0\x95\x03s\x8f\x10nr\xac\v\xce(\x82\xd8ʖ\xc2XŽ\xa87\x81Am\av\x00jIPGG\xa7\xe8\xbc\xda<\xb0\x1a\xef\x94X\x97Õ\x9a\x1b\xeb{\x81\x94\x9f\v\x10\x0fׂ\xe4Γ߆=\xa6\xae\xb9\xc3\t9s\x18\xe06/\xbc=2\x8c\xef\xa2\xee\x11\xac\xdf\xc9#\x80π\x89,r\xf5o:\xbf\xb8\x02\"\x8c,\a\xd7s\x9bd\x92Bp\x86\xef\xb0T(\xb0G\x17\x18\x80\x9e`-\xb5\xb0\x1b\x18M\x83[\x02E\x8e`\xda\xda\xd1P\xdcuX\x8bNS\xf95\x85\x19`\xdd\x18FX\xd1є$\xc2\xee\xeav\x02\x1f\xa1\xbe>\x1a\x1d T\xd8\x0f \xb1\x8dP\bq\x1e\xc4>{\x90\xf7Df!\xcfM!\xcd\x1c!\xa6\x80\x98\x8cޜ\f=\xb0\x04\xbc\xb1\xe5\xac\a\xb4\x89\xcc\x1a\xec\xc1F\xbc5\x92ږֽ\xa5\xed\x9dh\xebgh1\x88֏|r\xe7.=y\xf3Ϙ|\xd3\xde\x14\xec\x87\"\x1c\xeeZ\xe5\xe9ax\xed\xb0̸P\xa9\xf8\u007f\a\xb2\xfd\xe7w\xae\x1a\x05\xfb\x1d@TƸZAf\xa44\x99\x1a]\x8a\x9b\x83\x8a/\xb4\xc1/\xe9\"`i\xf3.\x9a\xc0\xace\b\x9e\x17(\xacU]\xc0\a!\xa7\x1b\xf9\v3\x9dT|\xab/U\xa7\x13\x8cDL\xcfW2ar\xb6\x9f\x88\"/m\uee93H\x05'W\x8b1@\xbe\x15\x92\xe0#\xe3\xf2J\xb0\xa1\x86*}.\xcbk\x01\x97\xfcdd\xea\xe3}]\xd1&W\\\xb1ܺF\x17\xd5\xf2\xf6\xbcC\xae\xcb\xfc!\xc3\xfc\x80\x99\xa6\xa1\x96\xc64iEfߴv\xf5H\x89]\x9a\x1az\x1d\xe1\xa2\xd0'b\x14\x17aCa~\xcf0\xf4\xc4\x14\xd5ir\xff\vv\xbe\xbfп\xedh\xd1:d\xf4\xfdt\xcbEhH\xc1\xd7<\x1d^{\x9cL\x88\xc5\xd7Ҥ\n\xfaA\x16\x90\xc3ܣ(\xb2\x8e\rs^\x15\xa2\xe02\xdcU\x83\x91c\xad\xac\xc1l\xcb٢M\x10T\x1a\xd9\xed\x12sE\xae`\xf91P\x90\x89 \xef\x11h\x9c\xe2\v\xb3\xbff\b\x8b7\x95\xa5.\x1fؑR\xeblb,b\xc0\xa4\\\x85\xd1\x06\\\xc0i\xc8#\x04\x11\x90\x8a\x0f \xdf\x1b\xe3`\xe9\xe1\xd7÷\x97~_\xf8\xfa\xf1\xf7\x83\xf2\xc7\xed\x89*\x16\xbe\xd6>\xf6.\xec@P¢\xc6\xe6p\xc0,28\x8cq\aa\xe9\xaa\xdbPM^v\x1e;\x9e'q\xbe1\xa0\xf3\x18\xa6?\x9c&u2\x89c}d\x1fg\xf5\x9f\xb61\xc9\x02d\"R\x98\xd2\x02Ȭ\x91\xd9$\xb3\x87,\x9dHȡ\xf0\xa4mH━C\xb9N\xf5\x00\bQ!\x04\xbal\x89\xfd-\xd9\xef\xb3c\xa2M\xe2+\x10\x84O\nAۚ\x1b\xae-g)\xa1\x81M\x01\xe0a@ʙJr(\x1dS1L\xd43\x90Q#\x12\xba\xf8w\xa3 \x02E1\x002\x00\x84\x1aZRrZ@[(\"RA\xdb\x06\xccQ\x18\xb23\xc4c\x11\x8eC\x18\x8c\x92o=\xe0\xb4>B\x8a馵\x93\xddS\x95\xf1\xb5He\x13X\rp#\xc7ێnt\x94/J\xee\ny\"\xdb-\x12\x85\\Qo\xab\x1a\xa3\x15\xbc\xa5ޜ=c\xbc\x06)\x19\xde\xe2\xba#\x82\xa2\a2^\xd7Rtԓ\xe4\v\xf1\xa8)\xe6\xa2@ۙ\x1d\xb5m;\x1e`\x92\x8aP\x8a(r7\xad\xb2\xbf\x03\x0e\n\u007f\xa0\xd4\xebhwK\x96A\xfaՒ\x1eh\xfe\x8b\x1d\x80\x81h\x12C϶\xe5\x06\x15\x9d\xa4\xb4U0\xa0\xe9+)5\x16\xbeE*24\xb5\xfc\x84LVBx\xe0\xcaP\xd0H%ڑ%\xc9\xe2\xbb\b\xb5\xdex\xf9(!\xb9Qs\xc2\\\xc8^\x8a\x97\xee\fZ^\xc6\x10\xd4^\xb8\x95,\x92\xabp\xec\xdf\x02\x9f>\xb3\xbcI\xf6\xccC9f\v\x9aI\t\x17\x03kH\xc5\b`\x10\xee\x13&\x1a\xe0,\xa8\x032\x13$\x04\x8f\xed#\x8c\x96\x1a\x147\x975\xe2\x8e\x04\xde\xee\x861\x03\xb4\x9aN\xacp\\\xc8\\\x9b\x95Lf\x91uE\xfd\u05cb!\xcaE\x00\f\xeeڸ\fH\xc2?\xb4\xe1\x97\x00\xd0\x06n]\xccj\x83\x01\x88\nvM4\xafB\x03u\xbe\x05\x9dNT\xb8%/0\xe6cZ!\x95Z\xc6\x1c\xd1\xdbq\xc1\xa9\\\x87\x8c\xf4\\\xfaZU\xdf\x0f\x84\xc88\x98\x1e\xddC%>\xab,\x0e\x03B\x03\xe9\xd0\x1cF\xdau:\x1f$\xe9ʃA\x92\xcdv\xc1\xc0\x8a\x12\b,\vk>j+\xe8+\x86\xb1)\xa2\xa5ߵO\xe1;\x8f\xc9\xd5\xf1\xa8 \xaf \x8c\x02\xd4|\x8dz\u007fj\x00\x85^\x86\xbb=\xf5\x89\xeb\\\xe4:\xc8-\v\x96\v\vٺ\xabΤHCGIr\xa2w\x96M\xeaX\f;\x94g\x8cg\xd3C#\xe9\xa4M\xca1\xb4E嫌\xady\x94e>\xc5\xd1@\fL\xcb\xcc\xfflww\xd9\xfa\x89\x94z\x03iTi@6x\xeff\xf21\x10\xef^B\xb6\xecG\xfeًb\xaam\x9b\xe54\x8d\xd4ͦ\x8e\xdbS\x05i\x15\x8a\xc3\x12\xa1ZO\fw?B\xc2\xf4\x8e\xbcc\xa8\xb1y\x8e>\xf9SL\xa6\xc3\xdcw\xad3\xb9N?\xd3\x1c\xa7\x14P\xdc\xd5R\xefWN\x94\xc3aq\xa5\xa7\x19H\xe6]N\u007f\xa4̟\x87i\x0eO\x85U6'\xda\xea\x80;ݶ\x11\xde\x05\xb0\xae\xe2M\xb4\xff;\x8d2<\xefm\xb2<-l\x8f_\xca\x12\xd5Y\x16\xde\xebF@\xf9ΝlĂ\x8a\x9cIBtL\xef\xe3m\xe0\xbeOL\xd5\x1cG>\x9a\xee\x1c\xfb\xa5!(\x98\xdd0-\xba\x88\f\x93\xa7\x849\xd3\xc3r+n\xa1\xbb\xebn\xb6:h`iGƆ\x16\xebz\xbf+)]\xd2\xca\xe4\xe6匔\xa7̲=\xb3pB\u00811K\x02`\xde´,\xbfϏ\xa6]\xf5-\xa53MZ\x1dL\xaf:\"\xe0$Z\xa9o\r\xfa\xfa\xe8\x1e\xd2\n\xf4E\x05\xb8zA\x16\x9c\a\x91(\x1b\x90<(#\xc3\xc0K\xdaT\xce\xfd+\xe5\xfaVK\xe2W\xb5\xe2WG\x92VM\xf6\"Q\xa5\x13\xf3\xa8\x05f@\xf7}\xa2s\xee(\xfffh\x99\x01\xde\x1c\x89N\xb0\xe0\xc4gx0T\xfc\u0083U\xe1M&!A\v\x80P;\xe9P\xc1\xa4ju\x0392\x83\fINR\xf5 \x1c\xa6\xbc=\xd1 q\x1a\x84?\x1a\x9dpa\x92\x8d\x95\xa3\xcf\xe3\xb8\xc6\x03\xd8\xc6\x1f\xe9\x88\az\xc0\x03\x9d\x10\x86p\x02\xa60\x19\xf2\xf0\v\x88\xbe\x12\x1a\xea)\xc0\xa8\xf5\x06\x9f\xc2\no\xc7\x03j\x96n\x13\xa0\xe6\x17R\v\x16*,\x81'c\xa3\x04\xe0f\x82w!\x9eOޒ\xb6]m\xbeQ\vZ\xb4Nf\r؈-L\xe3T\xf3\xbaH\x14\x1b\xacc)\xacD;kJ%\x11\xcd\u05ed\xc9\xe5s&E\x80\x91\x00\xc2D\x99\x8al\x8e\xf3\xb9\xb5('.\x9f\xc1\t\xc8P<\xe533\xb7\x0e\xb4J~5\x06\xb4h\x13`\xd2%\x88\b\xe5)\x00WE\xf7^\xc4\x15\"\xdc\xeez\x86q\xea\xacu\xa9czB\xb8\x8ceN\x93:\xf7L˞jD\x04(cR\x18\x11\xf5\xd25\x19\x82c+\x1aU\x8e\b \x9dq0~X\xec\xccʮ\x934\x95]\x966K\x90\xf4\xd3\xe3ֆ͘@\xd1\xd3G\x8f\xb2m3\xbc4\xf1A\xb0\x00l\x8d-dڙ\xab\bA\xbc\xa7\x8c5xٳ\x12\x10n\x95\xe0\\\xf3\v\x9bJ1\xba@\xf2\xdb!+\xea\x04\xed I3f\xa6><3\x85IZXV3o4\x98\x03\xbb\x1d\xe1\x9cu\x8dp58י\xa3\x9ax\xda\xdc\x12\\\xb7\r\x1e\x17_\xcaUL\x85m\n\xfe\xde\xe5Jȁ\x10\x11\xac\xedQ\xcab\x11\x02\x9c\xcdvQ\x04\xdb_'\xecU8\x94\xf6\xf0ftg\xa3\t\xd1\xd1\x05\xf3dl\xec\x01\xc4s\xcf\r\t\xd0=)y\x14\xaa\xdbS\xa3\n\x85\xd8M\xb3r\x1f\xe9\xe4LsE\xe8\xc9Q\x93 R\xfc\xe5G+#\xaf\x95wJ)\x84\xe4ȱ\x9ev>̼̹^\xe5u\x8a\xbf+0h\xf8kᇫ\xfc\xaa\x15^\xfa\xb0ʾz\xddX\xb2\xb0\xaa\xa7u\u007fպV\xf9P\rI\x92\x88u\x10j~T|\xa0\x03c\x83\x10\xe1 \xf4AOC\xa11C\u0603\xe9\x0e\xe3=\x1c\xf2'B&2!r\"\xc2K\x81&1/D\x89\xe2|W\x04/\xd9q\x1d\xe10\xb0\xebaVlM]\xd2\x11\xc8\xc8H\x92\x86\x0f\xe9\xcd8\xa5\xb9\x82\"2fS\x98&\xe1\x8a`\xbc\xa1\xb8,\xff,Ib\x04V3\xd4O\x12\x01\xbcg\xb4O)\x03r.^\x17\x90\"\x91p\xa1\x17\x04\b\xb8\xb5\x9e܁(\x17\x93\xb7 B\xe2\xc1\x1br\x04\xdb8\xc4ۯ v\x10\r\"\xbe+)DXV\xdb\x1b\xdd\x00$\x93Td\\<\x85\b\x1f\xaa\xa8y\xa1\xea\xdb~>\xd7f\xac%Fk\xbe\x11\xd8}\xd1\a\xe1\x89B\xe3h\xdeb_\x81k\xba\xa3,\xa4j?\x00\xb3\xdblϴ\x05jD\x80\x92V\xb5_?\xb7\x123\x98~\x96\x00\v!\"+\xd6\b\xbb\xc5\xecR\xeb\xcae\xcd{71ڏ\xed\xdd\xf5\xc1\x90\v\xe2\xee\xac\x0e\xa9C\xea\xb9uw)ء\xf2\x97\x0eG`\xbdQ\x89\xe6\x01\x98\a\xc1g\t\xcf́9\xe6aE陷'F\xc0\x18:\xa2\x8cӲ^Ɨ\xbe\xff\xe4k\x14\xa6Dś\xdd\xd4\xe1\xb1\x19_\u007f\x9d`\xe3\u007f\xfb\xad\xcf\x18Fz\xe7pyq)\xcdI\xba\xc59\x9a\xa7\x1f\xd50&Y\x9c$\xfdue\x98\x8b\xa2\xdb\xef\xcf\x1c\xb0\x04T\xa9\xecw\x8cj\x97\x00\x01\xa2.ɀO\x8c\x9f\xd2mX\xab!;\x15\x98\xeacW\xfaި.5\xd0\xf9\xe9\xb2\xbe\x01T\x11tS\x16\x97\xd6$\xb66r\u007f;`O\xfeU0G\xeaͲ\x00u\x8c\xc0t\f\x01\xf0\xf1\x97F9oʵ\x9e\xf5Ē\xa0\x93g\xf6\xf8\xb0\x1b\xacQ\x87\xf5\xd2\xf1!5\xa3\xdb\n\xf20\x9eU\xe5Ҕ\xbfV\x06!\xf1\xc0\xe8\xd1r\xbb\xce7\x03\x83\xb0\xa0=\xb2\x91(\xc8\x1e\xd3\xc4\x05\x14o\xe9\xdc\tM\xfbL\xdc\x1dZL\xed\xadO}\xb0-s\x9c\xab塷j\xb8\x1f@\x12\xa5\xd20\xb9P\xafɥ֊\xa1\xd2a\"\xc1\x00{!\x91,Sl\xf3\x1e\x1c\xea\xc3f\x89\xdc\v(A\xb0\x80\ar\xef\x18\xe2\x91\x04\xf0I\xd0y\"\xcc%}\xc1dbv\xa1\xdd.\x99\x88\b\x13B\xc0\b'\xa1\xd3\xee,\xb0{\x8d#\xe3\x9e\xd5i\xa4ݢq|G\x01\x17\xd0\xe2B\xa5ߒ+Z\xf0\xd23\n\xd1\x12\x82\xbb\x9f\xd6.\xd7LK\xce\xc1\x02 \xb7ZAu\xdb\xdf\x180\n=\x90\xb6\xa4v`{\xe5Jps\xc8\xec\x95\x18P\xa3\xf9\xb3\xcc\x04\x8a\x00\xd1sW^b\xe5ȱ\xc8<\xcfzf\x8a_^\xe2\xd1\xe3|\xe85=\xbaDb\x1d& D\x06#\x12a=1\u008e\x18\x10\xd5\n\xc8\xe0\xc5Ǡ\xf3\xc6l9\x11\xa8\xfd2\xb4\x04\x8b\x94\x11\xf5,\xef\x8f/8,2,\xc7Cp6\x96,\x05\b{\xc8\x06\x96e\x9cU\xebX\x93\xc8\xd20d\x1f\xab^lm\xbbc\xba\xe2V\x1b\x12f,\xcd\x06\xc7\xc6\x11bk\x9d\u007fg\x99;>\xe0S!\x978\x95\xe0+\x9e2\xbf\xbb\xb6\xe8\x86謘\"I:\xb8З#\xb5\xb0\xbeix\xe9\xaf\u007fC,8\xaaI\xdb\xd7G\xc9~\xf81\x82\xd1\xe0\x12\v\xc2\xe6\r?@\x8b\xf4\x82l9\x06B芆\x80\xbdG\x11c\x1c\xdc$\f\x10\x80+\x9dc\x04\x95_\x89\xa3|\xf1\n\xcc\xd0\x10\xa3\x1a\xc0.\xb0\n\x1b\x18\xf3\xdc`\xe9`\xf1\x19]4\x9e\xb6\xa5\x12\xac0,Aq\u007f)\xdc\xc1M9nx\x8c4Z\xc1\xda\x1d\xd1\xc6\x1b?%m\xd8\x1a\xf7\xaa~\fL(X\x8e\b.\xa1\x8cwx\xd1:A\vx\xbb\xa3\x1c\xba\x9a\x8c\x80\xf5\x82\x82l\x8fb\xc0\xed\xa0\xd2eX\x90\x91\x81z@؉[\xfe\x88\x8a\x840\x92\b\x01\xb7h\xde\x1e\xe4V\x16\xf5\xa4\x00\xc4\x17.`|Ι<95JH\xbdE\x9a\x8c˫;3\xf0T\x94\xfa\x80O\xca@\xf4\xa9\x10a\x10\xab\x06-BP\x11\x95\bH2%\xaad近\xa7\xc5\xea\xa4\xd1!\x950(\x1aW\x80\xd4\f`\xcc\xd0>)GL*\x9f\x0f7\xfey\xe8\x82M\xd4\vd\xfcF\xfe\xc3Q\xe5x\xdd\xefJ\xbf\xc0\xa3/\xb8\t\xd4\xe3\xe3xZ.fgА\xe1-\t\xe9\t\x04\xac\xbc\x9b\x824]\x81\xceIJ\x16ޤ\x1a\x89\x86E;p\xcaR\xb4@>\x97*\x10E\xc0\x0f]\\\xa7\x0eT\x10a\xa59b\x8c\xc11\xba\xb6\x04K\xa2\xee\xa6Ӑ3\x9f\x87\xe0\xf8&Z+\x02\v%b\x18\a\f\xc55\t\x84b\v\x80\a\x85\xbd\u007f\x02<[\xfeѢ\xe1 ԡ\xebUɕQCGxL\xf9Y\x17\xa2\xedݡ\xcf^\xfe\xadVTI\xc1+{a\x99\xb0\b_;\x10\x81*\xdf\xfd0v\xd1\x184\x18\xd9\xe9+\xc3\xde\f6\xac\x9e6\xae\xaa\x89\xb6v\xba)\x89=8\x94\x06\xa7P\a\x06\x9f\x19\xad\xb1e3N\x95)ӦZ\x8c'\xaa\xf9c{0\x19ɝ\xfa\x92\fM\xfb\xdc\xf5\x92h\xa9\x10\x17\xc9:`d\x12E\x05\xc1k{\xe18\xf9;\xed\xd4|x\x86\tQ\x9cZ\xc5\xe3\xed\x0e\a\xb9\x1c\xb3R\xd2\xe8\x93ɠ\xedg\x06j\xcfd\x81\xfa\xb8=~\x90y`\xd6\x13 &\x03LA\xf79\x04\xaamݢ\x1e'\xc8GU\xdey\x87A\x1evT\xee\xac \xb8\xde\\\x19 \x81\x89\xd2\xf6\x04!\x90x=\xccb\a<݈z\xcd\xce\xf5W\x00t\xe6\xde\x06\x88\x03\\\x06\x15$\x8a~\xa7\xdd=\xc6\xdf1\xa1\xa0\x94\x1b\xc6L\xc9\xea\\D\xbd\x04\xb5\xcbI\x00\xcc*Ҋ\xeb]\xdf\x04P\\w\x98K\xd3W\xb9\xfa\xe5\x81e\xe3\xd1\xe9\x9e['\x0f98\xa7\x9f\x90\x12[t\xac\x86\x03\xce\xc0o\x06H wb|\fC\xf6.Ѫ^\x96];Dpd:(\xf4\xdd\"\xcc\xcb\xe2ɵcA\x0f\xf7\xd2\x12\xf6X\x90\x99\x95C\x11\b\xef\xe1\xa7d\aQY\xbe\xd1\xef\x8d\x18\xc1\f܆I\xfa\x01E\xa0>\xe2ם\xad\xb5\xd5\xe0\xf4\xben\x82\xc1\xe1zI\x83\x83\x89\x93r\xe8\xfd\xad\xe7N\xe0\xeado\x83\x01\xd2u\xc4\xd1\xcb\xcf\xff\xed\xe07%\xab\xd0\xc9\x14\x15\xbc%\xf3\xb0\be˵\xbb\v+{\x10M\x82]\xb5\xd5\f\xf8/\xcdK\x1e\x1c\xfdY\x10\x14MC\xa2\xb2\x801\x93\xea\x11\xaf \"`\x1d\xd1D\x86\xebS\xd0Qm\xa9S\x85\xf0\x99֚\x00\x96\xedN\xb0\x15\x19\x99Π\xf2\x9a\x1d\x93Ӣ\x8cA\xeb\xfa@Lɣl\xe9\x1c\x82\xfb9\xe0\x0e\xb2n\bW\x96\xef$\x16DC\xe7F\x9f^\x848\"\xca\aC\x13\xbf3\xa3\r\x86\xb0K\x15 \x8f32\xb3)I\x93̛\x90J\x8cap\xe2/\x90o\xe4o\x91U\xd9E\x80b\x8c\x00y\x0e\x87\x01\x85\x8bPT\xf8\x98\xbc\xfb\xa4\x87M\x1fb\xea\xb0\xe0\x87`v\x99D)'\xa4\x9b\xa8\x10\x90\xff\ne\x83\t\xaed\xb0\x96\xb0\xfc ֍-SY\f\xb9\xe3VI\x8f\x9c\x1e%`\x10\x98\xa6\x01\xbc&0\x890\x1b\xb0\x87\x833\x96\xe6\xc3\x196\xab\xa7\x12\xe7\xa7⛛\x8b\xdd{@\x04\xa2\x03\x9bd\xebت\x11\x18\x12\x84ūb\x13)Y@\xb5/\x13\xb2\x02ٹ\x10-c\xcd\xd8\x11=\xa6j\xedf\x84 \xb2Ѣ@\xd2\x02(G^B\x1eg\xf6\xa9\x97\x8e,\x85D\x91\x92\xd6u=-\x87\xf9\x18\xf9\xfa\xb5\xc4隠8\xb5\xed\x18W\xcb֣ \x13\x13\x04\xab\xfeDT\tg@N\x85&\x13\x06\xbe\xa0\v\x8be\xb6\xc9\xc5S\x00\x86\xac\xa6\x89X\xfc\xebV\n\x86\xfe\xcf\xc7\xc3K\xb5\\\xf5\xe8\x15\x90\x8e\xc5(M\xa8\xb2\x9d}\xa6u\xfb&\xf2\x8b\xf5\x86\xcen\x8c\xe4\xe52\xa8$N\xbd\xcdn`\xb2\xaf\xf2\xc7\xd8\xe4\x92! \xd4w\xa1\xec:\x86\x88\b\x93\x93\x89\xfa\x02\x93ڃH\xb8\r\f\x02\xf2\xb9\xb2\x12\xa6\xac\ue0dd!\x84\\u;`d\xed\xb0\xd8\tQ>\xc8/\xc4\b\xcf\xee卦\xc6\xc6\x1d\x9bP\xf8z\xe1J%\xee\b;\x11\xa4yc\x88\xa4&\x1b\xa9y\xa8\xf0\x18:\x0f\x16\xf0e9.\xb8\a\x05c\x161M\x86\xa8\x98\xa2\x1bR#\x9a\xe1^\xdbK\xd0#\xab7A\xfa\x929\xb7o\x05\x8e\x00\xfb74\xd0\x05\x9be'&\f|\u007f[\x93\x87\x1cx\xc0\xee}\x18\xcdf\x8beUk\xa8)\xeb\x10\xbbf\x9e\x8d-\x00)E\xccm\x96\f\xdcy\x9cR^\x1f>\xcf5W\x8dř'H\xaaӃ\x1f'\r\x1c\x94\xba\xc3F\x04\xe3Q.\xec\x18\xe8\xacW\x88\xcb\xe4:\xfb7HeX\xa4\x1b\x1eg\x9b\xb0\x0eD\xa5\xe4\xb4\xc7-\xe7͆.#v\xd0d\xb7\"\x11q\xf4C\xdb\x141\x92\x8a\xea2\xad؎\x9c\x1dX\x8c\xc0\xc4\xdbX\xe8\tDU\xa3\x99A+\x14\f\v\xff\xea\x1e\x98\xf2'T\xcb\x13\xf1F\x1a[\x9fM\fm\x8e\xa3\xaeW7\xedA\xf6\xd6\xcb:\xc1\x0eC\xc4\xe1>\xd0~w1R7\xa3\xe0\xea\xe3\xb8ƈɉ$\x8c=zQ\x82\xa6\x95\xb0\xd3E\x95H\xf3E\xcbJ^2Xo\x19\x8b\x17\xf5u(#\x9d\x99\x0e\r\xad\xf2n\x9b\xbb\x10\xfa\x93\xafx\xf1\xdc\n\x019G\x80ť\x99\x17p\xa5\xf4°Y,\xccL\xf4\xc1q\x82'\x02S\x1d\x19\x05w\x8e\x03\xd5\xc1\xf8K\xbeL\x10J'\xa5L\xe8\b\x93\xa9tj\xc0\xa2C;b\x06o.\xda\x02YN\xb2'\xd1\n&O%l\xb3[4#\xeb\xc1\x9a\xb7\xf1\xd8|F?N\a\xbb\xd4\xc1\bk2\xcd\xf8\x02\xb1\x8c\xcbt\xd0\x00,\xed\xe4?=RR\xa7\xf9\x92n=\xb2\xad\xa5Xܿ\xbd\xb9\f\xd4\xd1+\x02\xaf\x8c\xba'_\xa9\xb1\\\xbagD\x12፺\xf93R?g\x1e\x95LC;&g\xb3\x9bd\xaaĤ\xd3\xeaC\x8c\x8d~z\xf5 \xa5\xe5'T\xc9dW}-[\xbcB\n\xcco%\x96\"k\u007fW\xc8IFtS\xc0\x91\x11p\x11\x82\x81z\xe0\x81\x9fl\xea\xe9\xef\x82\xe2ʪ!\u007f\x8a\xbdmK=\xcdenf\xcb\xc2z}\x92\x85\x95f\xeb\x19f\xa7\xf4q\xa2J\x06*\x12bm\x0f3\x032\x04\x9a\x05\x82B\xde\xfa\x13\xa5P \x04\x8e0\xfb\xa3\xbah\x8d\x80\x91\r\xbc\x95%@\x04X\x962@\xac:\xcd\xd2\x1d\xa4\xbc\xb7j\bG\xb3)\x9fi\xcb\xc0\x9cA\x80\xa6bb\x1c\x00)\x82j[]ubj`\xcdjE:o|\xa5vI\x1dm:<\\\x85\x01]\x98\x99\x10:\xd6=c\x11\xabi\x8e\xba\x8e^a\xe2\xfc\xa2\xe4L]\x83\xe3\xac\xf0\xedc\x1d\xca\f\xc7@L\xc5\u007f2\x84\xb6\x8c\x81,'`V5\x91\x8aґ\x90\xcf\xd0+\x1c\xde\x14\x80|3\xcfhT\x9e9\x84\xa8i_\xd9\xf6\xad\x94zU){\x9bx1h\x17e\x86\xcfT\n\x05tG\x05Z}3v\x14\xb9\x99@\xa0\xd9ݽ\xdc\x11\r\x0f\x91\xc9@\x98\x94(1\x94\x1b\x160\x8bF\x96/\xa0\x98\xf1a\x82\xc3&\x94\x9b/\xa0\x84\x86\x10\xe3Y\x81E\xa2\xbb\xb2\xe8!\x03`&\xc0\x82Ӻ\x18WL\xc0\xe5\x81\xe6\x90<\x13\xd5*\xfa/\x88\x83\x8c\xe0>\xf4\b\x80;\x99\xf5\x83t\xb0mbXY\x06U\xabU\xa6\xa1\x0e\x95\t\xc9v\xc1,\xb2?\x9cE\xee\x02\xc1\x95\xe0R0$T\xc8@%\x0fԝ\x1ah\x04\xd6\xd1\xc0\nq\x11\xba\xb7\xe65[\xb9Þm\xc7,\x84\xb6R\x16H\xd4&\x9b\x13T:t\xa8]z\x8b\xcc\x1f\x05\xb8\xf6\xd0R\xf4\x0f\x84,\x0f\a\xb7W1\x88o\x83\x82,\t\xe6?\xdd\b2w\xb9\x03c\xe1\x91Ӻ\xbd\xa6\xe7Z\x01>i\xbb5\xa0\xf5#ʕ4\x02\x02\x9ae\xbb\x03P\x8a__(\xa2\x17|\xae\xd2\bD\xa9'@\xb9$_\vM͉$\xd0i\xb6\ah\xa5\x12\x10\xeafǝL\xceB\x047\xde\xc4\xf1\x19\x9fSZRA\xf6\x81\x84Gbx\x8c\xf0\xa5a\x81K_\xb9\v\x9b\xbe\x1d\xb9\x0ev\xaf\x1c#\xf51'\vm\xb6\x91\f\x17%\x89Z\"\xce&\x92\xa1\xaf\x05s\x8ba\xe1;I=\xdeQ\xeea\xd05\x90\xf2Ove\x14BV\bb\x1f4\xea\x14ƅ\xebS\x05\x85\xb3|\xc5\x1dlW\x10\xb2+Bŵto\xd1\x16\xcc\xd5L\x16Lj\xa5\x02n#I\xdb\xeb$1n\x84MfB\xd9\v\x9a\x0e\x94\x1d\xdbZ\a\xb98\x8dЇx8\x85\xb1]!\xe7է~7g\x85\xaf\x99\x0f\x8d悔ɛ\xf7\x1c\n#`D\x19\xb50\x12H\x18\xb6ߊth\xf23{\tA38\xad0J\xe9&ƠJ\x90\xc9\xc6B;\xc8\xf4RR\xa2\xf1\xda\x03\xe8y{\"\r\xfdL\u007f\xaa#>\x18\xad\x18ڀ\x01\x8bG\xa8\xf2\xb7Ą\\t\x1e_\x1cqS]\xedaQC\x06d\xf8\xa3\xd3H\x1e\xde\x14\xc9\x12\xe5u\x14\x18?Ċ-`\xed\xb6@GVЈ\x928^܊\x03\x90\xbep|\xc0\xec`ڱ\xe3\x11\xef\x06U͂\xcc\x10\"\xf2݇_\xc8\x0e<\xc4\x1c\x15\x90s`C\xbe5pWT\x88\xf3 \xe0\x9d\xddIa\xffN\xc7\xccO\"\\\beW\x834EO)v\xa0\x92`=\x9co\xbc{\xbaͤ\xb9\xa6g\xfd\xfa\x8aJښ4q8p\xdf\amg\x9b7\x10\xec\xadg\xe8\xfe\x88\xc3V\x134c\xbc1\xef\xeb\x11\x14\xea\xa2\"\x17\"@X\xf7p\bqK\xa2\x87\xb3o:ϱ\xfa\x9a\xf8x\x96,\xef\x00Rs9|\xc9\xf7\x10{Wp\x18\xfe$\x1dm\x8f\xa6\x17\xc1v\x18\x80\x1eܻ\xda\xc2t\x898\x0e\xe6\xb5\xf9\x02\xd6y\x11XM\xc7vKO\xd0,\xeb\"\x1e\xeb:ut\xb3\x96\x05mn\xd4\xff\xdc\xd1\x05o0\xd9b\xfc%<\xb0[\x1c\x05CEpB\xc1\x9f\x94\x85\x1a\xffȐ\x00=_(uR\xc9'ntU\x9a0Ѫ\n\xd1\x19\xfcG\xbc\xb2\x01w\x05z\xd5\t\x89\xb4\x16\x98\x1c\xf6p\xf5\x80u7\xc5P_f\xf6\xc1\x838\xa2\x85\x04\xfdb\x8eA\xe0\x88\xbdd\xe0\xf0X;\xe8\x9cNO\xc8\xc2\x05\x97܀R\xf5x!}\xf2F\xaf\x84Eh\x82\xec@DQ\x05 w\x9c\x95\xe6ya:G\xdap\x89\x80\r\x164\xa7\xc2\xe4\xe6 <.\x1d\x93\xb5\xb2f\xb6I\xc1\xf7r\xb7\xab\x878\x89\u05f7S\xcd\xf7Ik\x9a\x8b\xcan\x9cv\x11\xd4\xf6\x87\xe1pG)\x0fo\x9ej\x9a\xf9\xf9Bbi\r(\x04\x80\x84x\xa7B\x8d\x11)\x80C5\x10}a\xe6\xd1\x0fۓ\xba\xb8{=\xf2\x8a\x8fz Eۄ\x181<\x8a_K,J\xa5\xf5`5k\x92\xeb\t/\x11\xf1\xb0V\xb2VK\x10\x86\u05f6C\x12xY\x1dR{F\xb4\x9c\x1a}\x1f\xd0|\x89\xc3&\xba{\xe4\xb2\x1c\x02\x9cz-\xf7\xb0\x8c\x10\x95(\x98,\x1b3\x8cu\x11\x0f+\xf5\xc0ݦ\xc7A\x17\x83\xc2\x18\xa9@q\x14D\x88\xa7!]\xac\xbf\x9dLа\xd0˩\xeaI\x91\x80T*\xb04\xd5\xe9uE\xe7\x03\x84 \xfa\x84\xe9\x1d.\xf9\x13\xaa%\x8d\xc0\x90\xb2\xe5e\x9b\xf7\x98\x8d\x83-\x82\xfa\xb8!\x1fʐY\b\xca\x03s\xa2\xae֔p\xe4\tD(\xd40\\\xab\t\x9ej*w\x81 \xc10?q\x11\xacw\xa0\x19Α0M\xc2tN}S\xe8j\x8e\\\x89\xb3\xa0\x16\xc8e\xa5\x1dI\x8a\xb4|\x86f\xf2.\xd4TP\xe8R\x12\x10\u009e#\xd4\x14\x95\x16@%\xd5i\xee\xf6\xba\x83\x99Г\x16\xb0\x90ao\xacQ\xa0\xd4\xfcqM\x90a\xa4R\f}\xff\xa2\xb4\x86\xb0\x97\x80F~i\xd6/\xc8d1m\xafS\xe5\xa3}\xedT\xfb;aP8\xa8\xc9j\x19\xbb9\x9e2~!\xe5\xb6oE\xd8#\x17\xcat\xf0\xe8h\n[\xf0\f\xa4ff}\xab\xd5\x13;\x83\x0f9f\xb5\x11\x99\x02| \xb6AOj\xba\x13\x8e\xd1\xfdxy\xf4\xa2\x89TH\t˄hb!\x12\x8d\x97\x92\x89\xc5\xf4!\xe1\xd1\n\x8cfJQ\x1cNX\xc8\xf0 \x9d\xc1\xe2\xef\x02\xbc\xb3\xd3\xfaF,J\xd7;lh\xd1뭽\xb3:\x92\xba\x00\bf\xa27\xde\x1d\x90\xbc?\xa2\tB\xaa,\xea\xb1\xc5V\xde\xf1B\xf1\xb9,\x83zb\xf8\xa0GR\xfc\x80\x8fD\x12\fu\xc1^\x9d\x19\x1eܚ\x99\x19\x8c\xb4W\xf9+\xe1F\xd1a,\x88\xf7%\x89J\xd6ɠI&\x03C\x97\xfc\x9ah7#)_\b\x05룳\xbd\xba7hTc\xce7%\x97Y\xd0$\xa6\x83Q\x82=\xb59-|c\xe7\xd8\xe4~C^(\x84\xd4r\n\x89\x11J\xe3\xaef\x87$\x94\xd3jw\xfb\xd8\x02\x0f%\xf3\xc4\xe2t\xf3\xa4\xc6\x16\vY#\x82Cg\xc1\x06L\x1e\x94U\xa6\x03E\xf3%\x01\xe5\x06\xb09\xec&@\xec\xe1\x12\b\xdcR\xd6AF7!\xbc\x19\xe6\xede0M_>\x0f\x846\x0f\x99/\xaecU\xf5\x81\xcbk\xeb\x8c\x14A\x80\x041\x80s\x86\x00rT\x94\x18^\x80)]*\xeeT\x93U\xa4%\x02\x9c\x9bM\x14\xf7[\x03^R$\a\xd4I\v\x89)8l.\x1f\x95\xb6\xa2\xd3&J\xdb\xd8\xf3\x15\xf6b\x9b~\x8e\xd49\x80\x16D\x8cסι\xed\xedO\xe8Sp\x06\xd1\x1b\xae\x8cU\x10\x90\xc9\x17\xcaFu\xac\xed\x9aH-\x19\x1bb\xc3f\u007f\xda4B\xcaw\x04]\xef\x90\xeb\xf4\xb5,%\x97\x95kJ^\xcaPHh<=\xa2(\x00\x8b\x0e\x82ЅL\xc8\x00\x00\xe8\x943\x00@\x066v)6\x8a\x14s\"\xb3\x11Z\x119h\x94C\x99\x04\x83\x92,\xa0\xfcȐ\x11\xb1u\xdcm\x9d\n1\xd8\xff\xa7\xf1xe!\xe8\xd0O\x88r\xed\xe1S\x9d\xa6\xb9\x0e\x19p\xa6-\xf2\x87\x02\xcfx\x87\xfc\"\xfa\xf5\x95g6A\xf3\b\x86\x02e\xfb\x85\x92\xd39m|[\uf82dWl#ov֒(UZ\xf0\x99\xbfq\xd5Lg\x14\xa7P\xa0^\x16\xbe\xf54\x84C3\xe3\xaeW\xbc\xff\xae\xe3\x94Ԟ\xae\xe2\x19\x1f@\x8eϓ\xa6ڵ\xbf>\x03\x8cWH\v\x02\x14\xafy\x899\xf4\x86\x95\n\x01\xc2\xd2Z\xe5\x8bv\xdf\x01\xc5\xfezf\x15.\x1f\xb2T\x0f\xd8>Ɠs\x020#U\xdd\xe6\xb20\xd0߇\x18^\n\x17\xf5\xc7\xc7\x1d~\xa7\x88k\xc5\x17\x11\x81\xa6L\x06\r\x80\xc0*\x1d\xa9EP\x93m\xe6Z\xbd\xe6\x12\xac#>\xe7~>^\f\xee\x93|B[\xe2\xfd7\xe2D}\xcbX?,\uf543~\xc1\xec\xc3n8<,\x9f`\xc4L\x00+8\xac!\xc2/\xcet\xf7\xe4\x89\xf6X\xbb\a\xdd\xfe2\xbc\x89\xf4w\xa5]\xd4њ\x01l\xfc\xda\x03j3\xdaJ\xd2\xc6s\x90io\"{\xba\u03a2'b\xda?\xaa\n%\xfb\xfeYPa\x80\x97蕄\x10\x98Ql\xc1\xd4\u007fCW\x12\xfb\xca!-\xf9\x92\x82`\x8cq\xfc\r\xa2T\xa9\x9c@t\xc2]\xab\xa0\x01\x81\xfc\xba\xdda\xd8\f\x11\x1f]6\x9b\xd0\x04\x86H\xc2mB\x844L\xd0TD\xbd\xa8rh\xa5\x03\v@\xf1Dj!\x01L\xd95\xfb\xb5\xc7\xcdz\x80\xae^\xefh\x82\x10\tR\x05\xfaѼ{\xf2\xa0\x10\xd1w\"TOv'\x01:\x9a\xf5\x0e\xdd;\x02\xe3\"\x8f\x8c\xa8lA\x01$q\x13\x02\x9d<\x8c\x11TB\xfa~\x11\xc6\xd5\xe0\xfa\"\xa0\xd5_S\xc5ϒ\x164%U_\xd5\xff+\x01m\xab=r\x84\v\x99XD\x98\x9c\xfeT[!\xfd\x0e\xd2\x156\xd4P\xd3\x1a\xad\x01U\t\u007f\x01D\xd0I\xa7^\x1e\xdc\x0eG4Spv@9\xabG\xfaQ\x93]m\x12K\xf4\xfb\xbc(\x92J\xdcΓ(/\xc7녬\x85\xfe\xac\x1a\u007f\x04\x82w\f\x9d\x02\x80\xd5dz\x8d\x1b\xaa\xf4P\xbf$\x01:\xb7\x03\xe6Q!0\x97ᆦ%\f?,O\xbd\xd4n\x97\xbe\xdfw׀3T\xab\v$;\xc0\xa9\"\xc8$\x97\nG\xc0\xecl\u007f\x1b\tc(l\xe0\x86\xb8\f\xf4mj\x1a6\xeb6\xb0p\xf0\x81\x86\xb9\xabz\x838\xady\xf7h\x90Jy\x8fʏӽ,\"\x1e4?6\x8a;\xb9\xab\x00%\xd0&\xe1C*\xe7afb\xf0\xbeؓ\x99\xf2\xa3\xd9&\xf1\x83I\xaf@\x80\xd8zҌ\fo\xf0\x11V\x17\xb7\x81\xe7\xd4؛O0\xa1k=͘C\xb2>A\xf32\x81\x84\b\xb5\xb4\xa3\xa2\xe8#[3\xd0\xeb[3\xd6\x1f\x8c46\xbbʖ\x11\xfd\x9bʐ\x0f\xa1@\xb6\x98\x83\xa1\xc0\x9f\x04\x0e\x9c\xe0k\u007f\xc0K\x81\xca\\\xe9\x15\x1e\xdczh\x1cd\x11Eϋ\xc4H\xc0\x10\xd7\x1f\xac\xf41rO\x1ff\xa4\xf4\xbd\xcd\xc8\xdcR{K\x0f\x84rC:\x9ej\xa0KV-\x00\x00\x1e\xf1\x00ᄽ\x82\xf1A\u007f\xc7\xee\x1dUx)\x8c\xd0N[\x82\xa8fڄW3\xdd(8\xb0\xb99\x91\xee\xe8L)\xe6E\x0fE\x9d\xa4\x1ec\xa4l\a\xfb\x84\x86\xc7t\xc0\x86\x04F*^\x98[\xfe\x9dt\xdc)\x81\x88\xa1\xaf\x04Id\x04g7\x98uZ\x8ak\x1b,\x00u.\xc8e\xe6\x9c\xf2=%\xd4\x16\xe8*\x8f\xec\xc1\x96\x88\r\xf1\xc9\r\x14v\x97\x01\xf7p6\x13%\x8cp\x1c\x9bi\xf7]\x022\xe9\x8d\xdc;V\x0e\xc8+&\x97\x97\x11\x1d\xe4\xa6\f\x0e\xa8/\xe74k\xaeA\t\xd9_\x11'9$\x94b p\b\xcc\x1b\u007fƄ\x03\xc5!u\xba\xdd\xff\x9f̵D\xddTF\x92\xbaY\xf0\xfd\x17\x85\x8b;\xe8b\x1d\x04\x1b\xab\x81\x02\x1c>d\xbbE\x81ʼn\xf7@\xda\\\xddM\x1c\xe6\x92\x04I\x00\xae\xbeܞ\u007f\x1c\xb5\x15\xa0\x86O\xd4\xdem\u05cd\xfb\x16\x1c\xb92s\x02\xb6\xa4p\x05\xbf\x1b\x1e(\x17\x16\xe1)\xf3\x9e\xb7j867B\xc6z\xd0!\t\x05\fݐ\xcc,\x06\x02\x8a&\x80\xceNU\x8f\x93\xb78\x98F#\xbeԝ\x14\x8b)5\x19a4,'\xafOI\t\x12:g\f\xbdJ*~w\xe4\x1dj]\x1c\x82\xb1%\xfe\xcaG\xc4L\xd4\x05\xf0nߗsL4z\x90\xa9\xf8\x1c8\xb50~\x81\xb9\x0e\x93\x89iAOH\xcc\x12\v\"\x98\x8b\xa2\x17M\xf3l\xc0\x9a\xac\x9a\xec\x84r\x81$>\n\x98\xcfo\xf0o.rB\xf5\x94\x8ay\t,9\xb4\xf2\xa6e2\xc3\xff2M\x04\x03C\x9d\"zQ\x1f\b=\xe0\xcf.\xa4\f[wԡ\x8e@\x18\x89LXy\xfdg>\x8fjz\xbbI\xc0\x98\xad\xa3\x0e\x86\xc9\xdb\xc3\xe3\x82\b\xb5v>\x04S5Oo\"..\x1c/Ѡ\xee=\x8f\xdb\x04x\x02\x82\xf1\x11\xd0ױ\x04\x9f\x90LT\xd55i@N\xd0\x15yԿ@\x8c\xf2\xb4\xc7\xe6\x0fE\x86\x90\xfd\x9d\x8a#F\x99\xb9\xa0?\x94\x97\xca(\x10\x04\xc3\x1d\x17\x835:C\x86\x13\x9d\x98\x8e\xc6\xee\xa5%\xfc\xd0\xc4.\xe1\x86\"\x14\xde$\x84\xbc\xc8'\xf5\xc1d\tW\xe7\\\x1d\n4\xf9iFҔ\x1e\x8c\xe6@\x14\x02#\xea\xc9@dY$\x05\b2\xcb\xccg\x16\x14\x03\xd5<+\x18\xb3\x85\x9b\xbe\xcbR\xfb\xb9\x92\xe7\\y@\xf64\x9ej҅[w\x02\x86.p)\xf5\x9d]\xcc\xcc9\xb6o\x80\x1dg)y¥\x97\xd7\xcd\uf634UX:\xd7\xfe\x9b\x89#͈\x8b4\xb2\x94\xc1\x9fR\xec\xaedWb\x95\x9d)\xde\xd0z\x1a\x13*SX\xe5\x9dy\xa5\xa9h\xb2\x84F|\a\x8f\xeb;劀\xbbu\xc0\x16\x01c\x0f\xe1\xa9$\xa0X.;~\\\nD\xd1\xf4\xc9\a\x90\x16RyD\xb4{<\x1a\xf6Ҙbtg\x8c\x86\x18-\xea\x19C\x12'Ͱ }T\x01\x10\\\xeb|Q\xc3\xf2\x97\x1eh\x00\xf0R2j\xfb\xc4\n\x14\xac\xd5=\x9c\xd2\xd4\x1f\x87\xf9\x1e\xd5G\x90\xd8'\xb74\xe7\xa0\xf5\xaa\x8c\xb0u\x82\x8dйk\xf9m\xa5\x98}\x1b\x02º\xd2\xf6\x05\b\xf9\xd9\x1cg\xeb\t\xb8Ub3\xdc\x10\x01\x05\xd0E\xb3o\ry\n\xf3PI\xd1ȭD\xfe\tf\xaaNIC\xbc \xc1H\x169Øj\x1e\xea\x89sUuM\x144\a\xcb\xf4.6\x82\xfch\xe5\x11\xdc44\xfb\xfc\x97,\x9aB\x85n\xae8\x03p\x14;\x0f\t\xec|\xba8\xc0zBP;\xf6\x83q\x99\x91SD\xc2:8\xa1\x11oy\xe0\x15P\xddJ\xa8\xdcF\xdb\xfe\xe5\x11\t\xd9SC\"2S\xc0|\xa3=\f\f\x86\xfa\x17\x95HN\xc8\xf0f\xf2\xa6\xd1\xf8\xf0\x84=\x16\x91\xdd*}X\xd2\x00\x93і\xf5\xba\x83\x01\xc1\x98:3J\x96\xba\xac߉\v\x98pT\xdc\xf7Ϸ,\xffn\xa7\a!\xd5\x05c\x86s\xe4\xe4\xd2be\x9d{\xc6\xe8F\xf2\x14\x0f\xf0\xda,Rc`\x94\x11\x1cTϖ\a@~\xdaif^\xa8\x8f>\x1bDEyP\xd1Y\xc9\x0f\xe3\x9aN\xd0\xc1\x16_ȑ\xb3\n\xcc\x1f\xec\x18\x8c\xe5t\\#\x14\x1d \xd5\x14.6~\xb0\xe1\x1c\xc7\x15\x90\xbc\xdc69\x8a\xbc\xbf\xd3\x1bi\x0e\xb2[O4e4D!M\x18 \t\n\xd3O4\xb7V\xfa\xe84\xed\xaa1\x8c\xbc[sf1\x13\x01\x1c\xbe\xb0\x0eJrn\xb0\uf566\x15\x15\v\x90\xaf<\x81p\xf2\xd1\xfd\xb6\x13D@s\x0e\x11\x00\x94\xf0DK\x10T|\t\f\xcb\xd2\xca\xfc\x18\xcd)ؕ\x1f=\xa7\xf3]\xc8\xce|\x9c\xea\x1b\xf2o\x14wJOiƍ\xbc@\x9a\xb9X\x14s2\xfa\xd5\x1a\x04\xe3\xc4\x17\xc2\xca\xf5H\xb5k\x13j\x97\xb6[\xa8\f\v\x8d\xa3\x0fl\xa5\xd7\xdab/\x12\r&1\xfdYsV8J)\x93\x03\xf9<\v\xe6\xff\xc8\x02\xa4\x12\x1d\x06\x11\x17q\x9a\x1a\xa1\x96k/\xed\x93g\xcbd\x95q\x1cw\xe4\x01Ɵ\x84\xf2\xb41\xdb6\xa5\x8a\x9c\x8a\xad,0\x8aπ\xb6e\x18\x9cT`\xa4$\x87z\xff7\xdb\x16\xa2\n\xf5\x18\x9c!\xbcDûa\x8ej\xba\x11,\u2d7d\xc4kKᱪ/\xfa\rw\xd1\xf6Ȥ\xcf*\x1cU[+\xa3\xf5\xdb1\xc4b\"\x13\x86\x0e#m\xc7T\x87Y\n)8l\xf2\x98\xb0#\xb8lvK\x85\xd85E\xeb\v\x82\xfcI\x91]`\b\x89\xa3|\xbcԍ\xa7OC\xe7d\xca\xcd\xf4)UlL\u007f\xd4m\xcd\x01Hn\xeb4\xfb\r\xd7Uv\xf0\x9bk6\xber\xb8\x06\x93\x8cM\xc3\x0eO\xc9\xf1\x01\x10\x06Ɛ[\xe9<\x94\xfaI\xfcX\x0e\xea\xee\xde\xceO\x9b\xa6\xfd\x01Ͱ\xe7\xf9\x89Q\x9f\xa9{C\xe8Yf}\x012ZP\xcb9+fRHU\xdf`\x10\v\xa8\xe9SvLE|\x95\xa2\xf3't\xc4>^\xcd9$qz\x17\x8cV\x8bm(y'\x85-\x02\xc6D\xb9IK\x1c\xb6T,OT\xd6\x0e\xf1\xa8\xad_\x96q\x14\xceZh.\vʓ\xe4\xcfe7\xc2\xf1垼\x04\x84\x9f\xa6\x04\t\x9e\xad%\x88R\\\xb1\x99jjv\xe1\xc9\x12\x85\\)As\x88\xfa\x1d\xaf\x05/\x89\xb9\xac\xc6\x1fH\x06\xb0U\xb7`S\xb03\xd2!s\x1d\x96د\xc7\xf40\xfcx\x88W\xbd\xa8eHt\x87{,\x00\x1b\b\\\xeb\x05S\x0f\xd9.\xbc\xa9\xfb\x0e\xb3\xba4\xb2\xac\u007f\xceT\x8e\xed<\x11y\x11<\x1c\xd3\x0ezx\xc7\xfdKh\x9b\a5\u007f\f0-Ph\xa3\x8a\x8c7\xbe\n,)ZR\xa3\xf0\xf9J\x8b\xc3b\x0e\x1bA\f\x83\xb9\x94\x00\a\xcd\x0f\x1eb\xc6p1tLr\xdfL\xb3Zg\x9a|\x8d\xc0\x93\x05\x98\xad\f$v\xdcP\x93XJ`\x14\xa0\n\xbd\x05\xf9\x05Q\x02\xe4\xc1c\xc1j\xf4\xcf\x17\xacZ\u0530\xa6\xb6qcr\xe9K\x02\x94[!G\x03\x899\xb3\v\xb1-_\x94\xea1\xbc\xc9\xc5\xf0\xaa%\\\x90\xed\xe1\xad\xfc\vX\xeboIh!\xae\xf0\x9d\f\x0e\xff\xaa\xca:'\xa5Gb\xa8x\x95\x85\xeeE\xc3G.\x1c\xae9\x97\xf2H\xa4r@\x1c\x8e8`\u007fQD:\xdbb\xf5\x8a\xd7\x14\x010~\x05\xa28*\x86j\xdcW\x01$\xc4\xf6hK\x0f+\x01\xa0s\xd0i.\x15!\xe7\x06n\x0ei\xac\x96\xe5\xf6oS`za\xb0B\x12\x05\xbc\x16XQ\xc0\x8fF\x8c\\\xfat$ۂ\xf8\x06v\xabP\x15\xc08h\x06\xf2\x0e\x82\xa8Rf,\xc3\xe7)\x16\xa0\xa2-\xf8\x98\xb82\x1f\xc75 \n\xc9\x03@\x9f!\x8fI\xe5\xcbw|?-\xb7\xc2e\x81\x0f\x82n\x138(\x06ε\xdc\xddEn\x06\x01\xb3\xba\x90\x1c\xe1iN\x1f\xda%jr\u0605\x18ׄ\t\xb0\xa7A[}o\xf5\xfb\xeeD\xcaG*\xb9:F\xceM\xc3`3\xa9}G\xba\x11\x18\xe1\x93M{+.\xe2\xdc{\xbbޮ\x8b\xb7\xb4\x95\x05\x89\xea#M\\,\xeb\xaa\xc6&i\x05\x84~\xa6zk7,ź\xbd\x88-\xd1\xccQͰDk\xc8w'\xfc\xe3\xa2W\b\xa1\x8ex{q7\xf6\xe4\u007fv\xb1\x19:`\xfb\x83q\x9f\xbc\x1f\x9a\x14b\x8b\x82\xa5\xf2PT/r\x95\xe8RLؙ$\xf3\xbb\xc5\xceS\x81X\x89\x01\n\x99\xb6u~\xb6k\xb7F\xd1A\x90\x9e\xc2\r\xc5\x16\xab\x01W\xd8SQ$\xb15\\\xab\xf7U\x1dy\xda[E\x13hC\xa5\xb8\va\xe4\x1b(\xf2\xb2\x0f \xb7\xb4\xcd<{Q>\xa4̚\xf4\xf3,<\x8ebPT\x84\xf2\x9b\xb4\xd8\x03\xe4\x1b\x02\x02!H\x13\xe4%\x04@\xe8\x10\x0e\x89A\x04\xaf\x14\x04\x11@\x15h\x02\xf07\x10s\x98\u007f\x8b\u007f샗C\xb8{ \xe3\xf0\xd58v\xf8\xe9\xf3j\x01\xfc\x80+!\x93\x03\x80\xa5\xff\xa4\xfeܐ)k\xf7)\nl\b\xf6׃ڇ짅l\x04;\x00\x0e&\a';)\xe8\x10\x9aH\x12h\xe5-\"J\x16\xb7y\x11jh݇\x0f$E\v\xdb#\xc2\xf2\x06\x18x\xc3hQ(dž\xd0ţn5\x89\x9a=\xf2$OO\xe5\x14\xf7\xf7\xdf\x1dJ\x84sP\x9f\x88\xc0\xc3\xfc\x80\xe0\xfd4\x9e\xb8\xbd\xe55\x88\xa6\xa5oA +\xb7\xe6LkW:a\x04x7\x93\xd2\x18\xa5\x19\xd7:\xc7b\xb7e\xb7%\x88\x8e\xbb\xc8z\xd22ڹ\x90\x04\xa9\xe9\xa5y䄫@\x1fF\x85ΐ\xfaش\x8eʲ\xfd_xԒp\xe6\xc6u\x1d\nr6o\xd3D\x16=\x92\x99Og\xc4\xffӠ\xbd\xc2\x12\ak\t\xe7\xab\xf3H\xe6O\x15\xa8|I\x8cB\xa2\x03r\x03\xa5\xfb\x9e\x94\x93I.\xbb\xfe?$\x90\xd0\xd5\xe1\x1c\xf0A&M|l.b\x05A\x80\x9dD\x18\xa5&\xc5i\x88\xce\x16\u0600\x88\x87\xea\x03\b\xc2\xc0\xab\x9dJ\xc3l\xd1\at@\xc4MX\\\x90\x04Q\na\x9eOA\xe7B@RT\x13\x8f[\xe5\xa2\x136\xde\f\xa2M\xa5\x82B\x02\x0ey\x13[\xc4B\xe8\xdb]\x9fc\xbc\x88\x86\xbf=Y»\xc9p\x1e$\xb0\xbe\x8b\x17\x12\xc1\a\x191\b\x88\xb8\x9b\xa1#&\xc7\xef\x81\x19Wn[\xa1\xf3\xcd-\x99\x01Ŕq@\xdb\x05h:\x18\xf1\n\xbf0\x12\bBl#\xf0~{i\xf8*[W\xe0\xd8\x10\x00\x00i\x0f\xc9c\xc0\xe0\xec\x98a\xf9 ~F#\x93Ic\xc0\xd9\b\x865!\x16\xa2\a\v\xc7\x00px=\r\x8fCe\n\xa4\xc34Ê\xa1\xb8\x1c-\x1d\x96\x87C\xb2\xb3㓁h\xe0͋3\x80\x8d\x02ƭ\x89\xd0\xc3l5XH\xf2\xa2j0\xd5W\xa7\xd0QxR\xad\x86\xe3&HAO\tq\xa1\xfcG٪\x9a\xc3\xc8\xd5 \xc1\xf3x3\r\x89*p8\x9b\x0eGLR\xa8\x17\xca\xe7\xc4J+\x11\xd9Je\xf3]fT\x1a3\xb3iZ\x95SE\xd2\xf7.\x92Li\x9e\xbb4Ҁ\xe4\xc3.HD\xe9\xdck\xa2\x8d\x11\x80\xd2nz\xc1\x8d%,\xae\xfd\x84\xb9\x1f\xec_`v\x15\xfe\xc2\xe0\xe2r\x01>\xb9k\x87\xdc\x00\x16\xf0\"\"\xb2m\x9bMgB\xbf\xb1jd\x17\x13\xaa)d*\x9e\xa2\x97\b4Jf#\xd6\xe3\x0fm0\x83`\x984\x05\x87\x83:\xee\x95R\xdb)\xe1UD\f\x03\xb6\xc4\x11k\"\x9c+;\x0f\xba~\xbb4C\x84\xbdHf\xf8\\\xa8*\x9dT8\x93\xef\xb8\xf0\x80\xf6r\x02\xca\xf8\xf5(`\x03Mjv\xa2\xc91\\\\5\xbcTL\xa5\xec\xba%,j2[>v\x96sW\xa2\x90#\xfb\x1bw\x94+}\xc6\xd5J\x19\xc1\xf1\xf2\xe8i\xa9\xa6\x14\\\x8e\xc6y\xe0\x16/\xe7I\x00ir\xbb\xb7\x81o%\x8c\x9b-.\x1e~\x1b\xf4G\x8e*\xca9\"R\xba\xe6\xb9\xfa\xc1ǜS\xb5\x05=֍\xadG\xe0\xa2\xc2\xe3R\xd9\xc1\x81B\x1e'\x88Y\x1d\xef#\x88\x1aP\xb7\xfa\xfb\x1d\x86\xaa\xf8\af\xc9*\x86\xab\xe7\x99\xcf\xe2\x94լ\x9f\b\xc5E@\x01\xaa\xdc\xc3Z\x1b\x9c\xed8\xcd\xc1b\\\xe7\x1a\xa9,QIa\x1f\xb9\xff\b灟\x83\x1a\xa7\tA\x05c\x8f\xd3\x14\x90=\x92\xdef?\xdc\xecw\xb7\x8b\xf6\xd4\a<\xc3\x19\x94\xcd\x0e\u07b9\xbcE\xdf\"\x8cH\xe2\x1dW\xc9c\x80=\xbdđ\xc2%\xe2\x15}\xf5\x17\xd8&\xe2\xfc\x89Z\x92\xbc\v\xfd\x9f\xccI\x89\x8a\v\xf5\xc6\a7d\xe7^:z\x01\x9a\b\n\x83\x1ap\b6\x85\xa95\xee\xf6\x8fR\xee\x1a\xee\x11\xd9\xdc\xf4դ\xb6\xfe\x04q\xd8\xe9\x8e\xe8$\xcf\xd4\x14\xa3i\xa7}\xae#\x10\xf6\v\\\x91\x85\xeb\x8eq\x06\xdcN\x1e\xfb\x97%\x1f\tO41ψ(\x0f]H\x1d\xe8\xe8\xa1$a\x9aA\xe7\x05\xe2k;\x90ĉ\x00\xb0b\x99\x88\xe8\n\xe9\x04\b\xbb9\xa9\xb8DQd\x95#\x15\xe2\xf8\x92mҐ\x92ɛ\x9d,\xc2\x04\x8dI@\xa9ĻG\x12\x16\xd2)X}\x87\x1dJ>y\x06\x03\xfb\x9b\xfa\x934\"\r\x93\xca=\x12\xda\x16\x11j\x04\xf7\x19\x81\x1c}\tNNv\xd5D\x9c\x8aJeR\x98\a\x17\xe6\x14\xccgs\x1a\xa3pPWw+\x1cŎ*\x055\xb6\xa9\x85\x18\x96\xe3U\xb3\xff:\xf2,O\x84Z\xda\x004*\rjh\x10i1U\xd1&sc\xa3\x97E\xdc\t\xfc\xad\x8c)1\x95\xea[^\xd85by2\xa5\xa0(\xe4˭Y\xd0\xfa\x9e\xf5\x02\x86\x8c\xce\xc9S\xbd\x8f\xd2\ue324jU\xd3\xd3\xd2̞\xf5ij~\x83\x9a\xa1e9\x14\xd5\xee\xad9\xda\x14\xa9.@*\xdf\xc6\\ܦ\x91r\x86\x94\xa55\xed\xf8?\xa1\xfc\x93l\xf9H\xf2e\x1a\x1f\xa3\xc1g\xe9YZ\xbb\x0f\xb0\xe7-\xcd\x00\xaft\xea@r\xccL\x18/qn\xc8\xe0\xfc\xde(B\xc7q\x87\xe1I\xac\x05\x89)-\x14\xb1;\bi?Q\x17E\x89B\xa7y\xf0\x01Ys\x85\xec7y3\x02\xd8\x14\xb9V\x00\xccz\\\xf2˂ܰ'\x02\x1b'\xf2Q4O\xc6\xe2\x90\xd6\xe4\xcc)\xb7\xa7A\xad\xaf\xdeNI>\xb1\x03\xf1\xa0\xe5\x05\x9eX\x13\xd7\xeb6\x11\xdb6\aѰ:\x83\xc0\x1b\xb8\x80\xbd:3\xbb\xc8\xcaJ\xc1A\xf1I\x1d\x02y\x96;˨*y\x9d\xeex\xd3}\xa9\xf7m\x95\f\xc0É f´\xd97\xce{\xb3E٬4\xc3\\k\t\u007f\x98iI\x91\xb4\x05\x02̌\x11\x82\x81\x8e\xb0\x97\x89C\xf0v\x1a\xdc\xf6FD\x90Ġ\xf0\x96+\x19\xe8\xc2\x0e\x85\x82\xd2\x1c\x84\x8f\xb7GƵ\x16\n\xc4\xe3\x86\x1dG^\x144=\xa0Dp\x85\bM\"\xe9_`\xe9?\xac\f\x18\xd9\x17\x0e\xc8bTA\vd\xaa\x05MU%\xce\xf6\x84\xef\x028\xff\xe5E\xa2\xe6B\vI\x80b%eO\xdafY\x06``\x8f\x0e+\xeeAK\v3\xa7+\xe8Y\xda4\x80\xa1B N\xf2l\x9d\xe3h_\xa3'm\xa4@X87\x14M\xcfBk\xc4\x0f̬\xd3*1\xf5Pp|\ue256T\xa7Z\x82ʆ$\xd5J\x81\x94\x00\t\xce\x13j\x9f\xe5nۉ\x11\r\x90\x008\x9a\xc9_\xc1(\xdeC\xe4\xdb\\&\xed%\x1ci\x99\x01\xe8\xdf\xee\xc6\x0eH\xef\x1bH+y\xe4\x0eX\x93\x11\xebu@ѣ\x99ͪ\xe8\t\x13X\xf2\xb4D\xee\xbcr\xfd|1\x03\xe2Xi\xd6\b\xd1/\xf2\xbd=\xfe\r\x19X2c\xcfi\xe9\xcc{\xb6A\x80\xcc\xe7p4\xebC9`բ\x89F\xb2O\xad\xc7A}\xf5\xe5\xe4\u007f\xedI>\xa2'E^m\x95\xa1}K$\xacK\xbbp\xe0\x85!a\x13\xceo\"\x95\xdfb+t\x18\x1f\x00s#\x83\x9c\xc1=\x84\x9e\x19Z\xaf\x02\x00\xacR]\x13*ْ7߁t\xff<\xfdwR\x84\x1f\xe7'ux\x02\\\x86=\x98m8\xe0\x96\xafS6\x02\x96\x93\x12\xb1\x01H\x98\xdeÐ\x9b\x89\x91L+&\x8agO}\x11\x98\xb7s\r\xcf/\xa9\x03\xa8\x18\x15%\xd7.l4v\xb8\xcd\xd9`zY\xdb\x02\"E\x82)\xf8\xc8ق\xa0f\x10ע\xdd\x05\x97\xb4\x84\xd2e\xad\b\x0e\xb7P\xc4\x13ΤgYP\xc4\xef\\\x81\x85L7\x9a\x19\xe4\xb4=\x83\\fVZ\x12\xdfn6\x020\x97B\x16p\x9d\xcd$\x84\xcf\v\x8a\xf9\xd0\xfc\x8e.\xa0\x95\xac\xb9\x80z/5 \x02H\x97r\x81D\xef`\x0eR\xfdI`D\xf9\x02\x01\xc7\xd5n\x946\xb6\xb8Ӻ\xca\xc0\x18\xc5Ҽ\x015\xf9\xaa\x89%!\xb3=(+'Kc\xb7\x94U\xb00\xd6\xc0\xf2hX\x1bB\xb8*)\x88#\x83\x82\x05\x1d\xd0|\x86\u007f!\xf2\x05\xb6\xd6pU\xd2\xf1\xfb\x14\x8e\x13\xd9\x13b\x81\xf8(e\xf4\x9e\xfa\x8e\x96\r\x9a\b˝\x19\x8e\xa3<#l\xba\t\x16\xa0\x1e\xf2b\xe0\x04钎\xee\x1b\na\xe4@@\xd0j\xcb(Q\xe1\f}\rDtE\x05R\x8c\xcdBԵmk\xe3q\xbfK\xf3\\\x80+#{\x94Z\xab\x1b\xc9\x1bE &\xdc\xc4\xfc)\xb8\x9a(\x85 Gf\xd5\xd2\x18\x90z\x1c\xcd\x01+\xe2\xa8LI\xcfC\x00/\x94\xb8\x8d\x18ӂ\x9b!\x99\x8115\xc8\xde\xf5\xa6\xb4K\u007f7\xfb\xc5,P\\m\x86\x01\x85\x87w\x81\xb0\x8a u\x9dJ\x96\xfe\xbb\x01-\xb2\xbbU\x17\xfe!\xc4֎u\x18\x9d\xccb\xf8l\x8f\x13\xa6\x91\xd8z\x85\r\x9b\x81 \xcfE?\xeb\xe6Bݥɥ+\x96\t\x9fy\xe0\xcar 1\x19\x86\x8bYS\x98\xd4>\xb9\xe4\xad\x19e\xbe\xf4\xe7\xd4\xcdO\x18\xca/\xaa¢F\x1a\xf05n\x81\xa3\xd6\xc1\t\x90\x83v\xa62\xa0\t0\a\x1c\xed\xa1~\x02\v\xbfDz\v\xf0%u\x03\bD\x9a\xe8\xd48\xab\x93\x05\xa2L\xdc7\xee#[\xd4@\xfdWOY\xcc\xda\xc7\xc18I\xddR \x91\xc1\x95W\xf2\\-\f\xe2\x9d\x19\xb1\xa7f\xc2\"F\x84`\xa91\xf9r\x9e\xcf\xdct[\x84f`\xa0|\x15/U\x9b\xf3&\xa8o6;\xf8a\xcf\x13n\xef\xc4ē\xb1\xf5\x1e\xa2b\xb0\xb6\x81\xcc[\xe9\x9a.\x03\xa9\xb7\"\x10\x82A\x9f\xffp\xe0\xa0\xe89K\xd5\x14\x977\a\xf0\xb3\t\x17d\x04,\xd8ٜnd\xd5P=\x18f,\x87\x18N\xb5Ą\t\xce^\xd6B7/\x15\xa3\x11\f\xfe\x862Hfm\xec\xf8y&.\xff\xd9s#\xb6\xe7E\nAN\xa86\xc534\x1f\x80\x0f\xf6Ĥ\xfb1+\xb4/\x80\xc8DG\xe3z\x90ޅF\x87'\x04\xf9\x02\xe8fՁ\xe9F\x13\x84\xeaט\xe8ԑ\xa1\xa6\xd5\xe13O&[\b\x01\x97\a\xdf\x1f\x0f\x13\f\xb9:\x02 \x01\xe6a\t\xfd\xccԓ\x9a.v\x04\xf2\xceG\xd8\xed\f\xa4tf7\x03\x05D\x8d\xbc1\xaaw\x91\x8b\x8ex\xa9µ\xa2\xa0\x0eQ\xa8\x1aA\xb2\"y\r$[\r@\xc2$\b\r*l\xdd\x11BR\xc8\xfdxG)ih\r @\xbaA\x94\x88L\roS¥\xf2\xd8\u007f\x96\xccM\xe5\xb2\xe0g(\x86=\xeb\x902\xa3&\xa6\x05\xcc\xda\xd8.\x90\x18\x9b\xe8|iXt \x91\x16\x12:\x866d`\x8f\xaa\"\xaa\x0e\xa2\x8d\a\xe9\x15+?\xee\x02\xd6(m\x96\xe0\x17>\x98n#\xb5\\\xf1g\xb5\xe7m\x96\xdd\xc0\x1a1\x8co\x01\xda^\xbcև#\xa9dTZ\xd9\x03\xa0\x00\x9fI_\x1evC\xa8(\x84\x8cR\x00Ҭ\xb5\xf2\xf4\x80\u007f(\x15\x18\xd6\x16[\xbe\x88W\xe0쾳\xb38\x98r~f\"\x10\xfb\"$m\xde\r8\x94\xda|D\x9bh*\b\x02\x12\v\x87\xab,\b\x1ad|\x04\x18\x81\x06 l\xad$n\xb9\xea\x10\xc1\xbd\x89<\xfbd\x85#\xac\xa8\x85\xc8\xe89\x197h\xc8\xe6Ku1\xa2\xc1G\xb5Ĕzخ\x14\xc1\x1cVq\xc0\x9f\xd7\fB%(Mi\xc5T\x17\x82睁\xd9njD\xa2\xc51\x94\x96\xc1d\xc4\xcbbJF\xa1\xcb n\xcc\x05\xfd\xad\xd0\xfbmDC)\x9cl\x8c\xb0\x1f\xcaд\x80\t\\p\xb32\a\xce\xf5چ\x0e\xa5li\xd1h\x95\xa8\b3\xa8\xeer\x06\xd3\x03\x95e\xc2YLH΄\xe3Y\xf6b,\xd1w\r\x1d!|r\x97\x14\xdd\xc5\xe9Z%\x1c\xffu\x836\xd0\xd4$71+)\x80\n\xe9\xc2I\u007f\xe0o\x9bq\xd6##@\x8aA\x1d\x8a\xbc\x8cH;\xf2.8\x92\xfe\x15HF\xebAJ]j\xa3\xe63S\x13\x10,rl\r\f\x1c\x83\xf65qjgS\x91e\x01@@<\xa4y\x84\x81\x02\x04\xc7Dv\xd9\xc7\xc0|\xde)\xb7)\x90\xbeq`\xaa\x93\x97$\x92\xf4-,|\xb63H\xbd\x1au\x9b\xf7\x11iCr4\xef,\xcb_$\xcbpe\xbbޯ\xb7\x1a\xc2\"6Ι\x94\x80\xb1\"\x86\n?\xb0O\x17\x05\x18\x94/l\x85t\v\x15/$\xc5\x1c\x8d&0\xd9:\xad\x86G\xe0*\fb\xb0h\r[\xfbd\xe1xL\r\xf4\xb7٪\xe0\x86\tީ\xc7l\x92k&\u007f\x11\xa8@\x91\x92\xdd\xda\xe2\xf4\a~\xa7\x1a'\x1dr\x00\x81\xb1\x05\x86\x1f3K|\x89m2\xeb\x03\xc3VI\\\x0f\x02\xcc`\xf8R\x9c%X\x8eS|_\a\xe0*2I\x12\xab\xac$\x02\xa2\x81\xf8\xb1\x1e\x91\n1D\x94P\xa8j\xe0\x8c*k\xc5\x19\xe1{;:\v\x86\xb7K\xd1\f\tG\x97χV\xbe\x15E3\xc7\xc2:\xba{\x00\x95ҳ\xb8:\t&N\xbe6\b\bH^m;\xf8\xccE\xce\xeeFv\xaf\xf3\x06\xefY\xee\xd6\xd45N\xbcC\xe8~\xd7]Z&IhkpI\xcc\xd6\xc3\x0f{%\x0fj\xc5\xca\x03\x88\xa9#J\bOV$\xe6\xe4\xb5+*&!k\x91bN\xe3>\xe1\xe6\u007f[\x82\xa4t^\xaa\xe5\xd1\r<}\xach\xbb0\x97\xb2\xf5i'r\xd2Ť\xa8\x14\xe3_A2L\xd8ҽr\x10!\xfc\x0e\xa4n{5;1\x92(=j\xb71.rE\xc1\\\x1a%\x12\x82J\xd0B\xd9\xe3\x86x\x02;D\uede0TG\xfc\xb41]\xd7\xe0O\x0f\x02\xfd\xcf`a\xc1\xaf^\xaa3!+\xee \xcb<\xe76\x0e\x0f\xb9Z`\xaf&|\xc1<3\v\x01\xde\xc0L,\x11\x11\xf7\xe9\x9b\xd6\xf7\x18?\x0f\xa1Z\xba \x97d\x16\x86ʤZ[g\x89a\xb9\x06\x03\xe4\xe5N!tx\x82h\x81\x1aŠl~\xa9\fv&\xc4\a<&\xf5\xd0\xe9\x17\x93$\xea\xd1@\xec\xf9!\xcb\x1eهj\xc4GL\xc6\x1e\x18\u007fsJ M$\x810"), } file9 := &embedded.EmbeddedFile{ Filename: "af7ae505a9eed503f8b8e6982036873e.woff2", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("wOF2\x00\x01\x00\x00\x00\x01-h\x00\r\x00\x00\x00\x02\x86\x98\x00\x01-\x0e\x00\x04\x01\xcb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?FFTM\x1c\x1a \x06`\x00\x85r\x11\b\n\x89\x99(\x87\xb6X\x016\x02$\x03\x95p\v\x96\x10\x00\x04 \x05\x89\x06\a\xb4u[R\trGa\xf7\x91\x84*\xba\r\x81'\xed=\xeb:\xb5\x1a&\xd3\xcd=r\xb7*\n\x02\x19\xe5\x1a\xf1\xf6]\x04t\a\xdcE\xaan\xa3\xb2\xff\xff\xff\xff\xe4\xa41F\xdb\x0e\xdc@\xe0\xd5\xf4\xfb|\xad\x8a\x14\bf\x93m\x92`\x9b$ؑ\xa1@d[BQ\x11$([U<+(\xad\xb8@P\xd05\x1e\xe4`\x81\xb0\x0e\xda>\xf6P\x10\x1a;\xe1(\x91\xd11\xb3\xfdl\xdb\xfehԨ\xa2\xc2)\x9f\xdcYy\x94\xf2Ji\xe9\xeb\x17\xad\x85\xce|%ہ\xb7^\xac\x14G\x82\xa23\xb8\x12n\x9e\x95\xe8\xbaڕ\xdc\n\xc4͐D\x8a\x9ep\\Yr \x94L\xdfP\xf4\x0e\x8d\x1b\x83t\xa5)\xcb\x11\x98\xef\x13\xa76R\xc2^\"S\vL~\xf1YR\xd7CXR\x15\t\x8a4\x81\x8e\xe6F\xfay\\[\xbf\xe87n\xa1\xe2\xae|\x1ds໌q\x1e\xa3M\xbb\x8e%K\xc9\xff\x17.ۺ\x0e,\v\xfa\x84\xf7\xf8L\xd0t\xff'\xf6\x10\x90\xc9M\x1d,\x11\x15c\xf7\x88+b\xfa\xc4ׇ\xe7O\xfds\xf9^\xd8$\x85\x86\xd7z.\a\xf5mŠ\f\xfch&gb\xde\xf0\xe1v\xed\x87\xf3\x01\x0e\xc9'\x8a\x0f6\xa3:\xb0\xbb\xc3\xf3s\xebm\xa3b\x8c1بm0\"ǂ\x1a\xb0\xbf*V\x8c\xa8\x8d\x1e\xa3c\xa3$,0ATPT\xb41\x12\x03\xfb\xf4\xac<\v\xed;\xed\xf3\xd2`\x10\xe6\xb6'\xf1H\x0e?\xf1sΩ:\x91\x0fND\x9f\xa8\xfc\xa9\x10I\x11\xa4$\x8c\x04T\x14\x8b[\x81\x82b4\x8a\x11\xcd\xfc\u007f\xea\xec\xef,\xafμ\xd7」bl6\x91\xba\vIL\xe9i}ی&\xe04\xe3\x94m,'\xcb\x16\xcb\xf3#\xbdץ\xb3\u007fRw\x01\xa2bu\xc0\xb6,K\x96\v\xc6\x06\x9b\xe2v\x8b\x03\xdb\bm_-\xc0\xc0\xe6\\H\x8b\x9d\xfb\x8eHH\xfe\xaa\xf3\x93\xea\a\xc6\xc2?\x98\x96\xd3m\xf3\v9P\x88\xad\xd8)9\xe7J\xa8\x86$ƽ\x88\x9c\xe7\xb1\xf48\xf8\xe8\xc7\xda\xce\xce~\xb9;\xc4r\x15\x8dn\xbf=$\x11\x1a\xb5\x1f\x94Nddn\x1b!'\xae\xa6\xf0\xe3;\xb3\xf4\xac8\x99\xbc'\xd9N\xa6\xed\x1e!\u007f-\x18\x06J\x19ʶ\xd3.\xe0\xd8\xfa\x91\xeeX\xaf=\f,\x12\x04\x0e\x90\x9b\x1e\"`:\x18\xa2\xff\x1b\x0f\t\t {\x8d\xef\x9e\xdc\xe0\xbc\x12\xcbK!'\x9f\x00\xa1-FH\x94\x9c\t\x89#$~\x9bZ_\x84\xb9\x9d\x10\x12\xb5\x03N5VU8Fȯ\x1c\xa2\x8e%\x14P\xd4\xfbݫ\xda\xdb\xfb\x06\b\f\x86Cp\b\x02$\x04Q\x19\xa2\xb4\xbb\xda\x10\x9dr\xac\xec\xa2ʽ\x9b\xee\xbak\xbbk\xda\x1b\xa73ٷ\x13\xd1:R\x11%\x1e\x10\xb6\x812{\xf4\x87ީ\xf5\x92\x1dh%\xbb)8\xc9\x11\xf6\x00\xf8\xf1\x01\x0f\x18\nILK\x906v\xf4#\xed\xfb\xb3\xee\xfe\x06,;Ц6\x87\xa0N\x9a2\xcehv\xb7\x0f\x0e\b\xfe\xef\u007f\xcd\xfe\xeb\fOO\x91\xa7t#\x16\x85\x06\xe2xT\x89\xef\xaaBf\x02\x9d\x9e\x84q^\x16#\x9c\xae\x9f\x96?{\xfe5b\x8aI\xe2\xe0%-WZ\x90\xeb\xb9b\xa4A\xa3\x13\f^\xe41\xcd\xd9n5\x19\xf9\xae\xe9\x8aצNQ\x9dY'\xfa\xe6\xdd\xcd\x12\x96\x04\xfc\x91S\x04\xdc\x18\x1f\xa6!t\" \x06`b3\xe9\xf7%\x93\a\xf4\xef35\xbb\x02\x14\xd1fv;\xab\x9d\xd5\xcelά\xee9\x9c:jgf?gr\x9b\xf5p\xffx\x9f \x80\x0f|\x12\xfc $\b e\x12\x94\x1c\x12\x94Z\x14(\xc5\x14$w(ZrS\x90\xe8v+\x9bZ\xd9\x1c\xcf\xd6q\xf6M\xa2\xec\x1e\x9b\xee\xcd\xeaݙm?&s[\xf6\xe5t\x99S\x92Sj\x8e\xa79\x9c\xaf\xe3\xf7?\xad|\xda\xea\r\xf1\xf0\xc5>G\x0e\xcc\x06,\x10bDշ^\xa9\xf4\xaa^\xa9\xc7\xdd\x1b:l\x8a3\x8e\xbdNA\x9a`\xab5\v\x9c26\xb3\x01L\xf9pS\xf2\tAߧ/U\xeb\n\xc2֘\xb4\xbc\xb3\xb4'9\\\xb4\xa7Նt\xb8\x85\x10\xaf!\xdf\xf6\xf6\xfa\xb7\x1a\xfd\x1b\x81l\x00\x06\x85 PMR\xb4\x9a\x909n\x80\n\xcd`\x15\x12\x13(\xca\x10@\xd2 Hy)M\xd2dM\xca\r\x90\x1c5\xe9Ԥ\x13H'ґ\x9a\x1c\xf2mS<\xfa\xe8\xdbq&k\xbc)\\\xcf{;\xee1\xc5\xc3m\xf9\a\x9b8\xdb{\xe1\x8b\xd5X\x841\xdd-3ǚ\x90\x91)\x82B(\x84\x91,\xfb%\xf5\x93\x10\xb0\xb6\xb6\x8d\x04\xc4\v\x05\x05\xdd\xfd\xdbw\x9fo~\xbe\x98t\xed\xf0HW8l\x90\xd4Z\x11\tr\xa4\xe3\xbf\xff=e\xb3\x1a\xec\xdf1+\xe6\a\x05/Ɏ1W?ְr\xee\x12\x0e89PL\xc6\xd2\x01>uo9 \xc5\x181 tØ\xab\xc3uc\xa7\x9d\x91\xee\xc4@\xb3\xf9]K\x14\x10R\xef\u007fbN\xff\xbb\x92v\xfb\x93\xb4\xd4\x1a\xaa\xa8(\xb8\"\x8a\x83y뽻{\x1c\fc\xae\xff\x8f\xe6\xf3scz\xa6\x19&\xaep5\xa7\xe5\x04\xa1,j\xb3n \xa2kN\xc4!\x83.\x83n^\xf7\xf8Uu\x1f\x00@|\xfc\x18?v\xb5>\xf7\a\xb9\x1d\xb9\xdc\x1c\x91r\x18Ua\xdcHR \v\x1a\x8f\x10\xfa\xb9\xd7\xf8Ց\x00\x93\xf3I\rD\x89\xdfˋQ\xbf\xbd~p\xe9\n\x80܍;;\xb8n\xf9\xf9\x8dL\x18\x17$\xa1t\xf9\t:\thFCY\xa1\xed\xdeTO\xa0FN\x1dN~}\x17\xf11\"`\xb3\xa8\xfc\x9f\xd8a\xcf\a\xcb(\xcd?H \x11\x97\x96\x87\x1f\xc4\\\x96\xcd\xe5u\xca0\x13LԵ\xf5\xf1'\xb5\xed\xba\xbd\xbe͔PbnmO\xbf\x98\xeb\xb6\xdd\xfd\x8d\xbf\x02\x18Jl\x8b?\x02\x01\b\x9e\xe5s\xab\xe7\x05\x880,\x0e8\x8cx\xc9B\xf2\xfa\xf6\xbeB\x19\xc7\xe5\x91F\x89\x10\x14\x9f_\xdf\x1bRiZ\x1d\xf8\xff\xb7\xd4~e#\x15j\xb0\x11\xb1w\xfdhOc*&F6\f\xacYq\xc1\x92\x05{\x89}?\xc9\xf3>\xf7u\x1e\xfb\xb6.\xf34\x0e\xceh%\x05g\x94`\x04\xa7&\xd7\xe7 \xb8\xcb\x12)\xa9\xacR5\x18\xe5H\x8c}\xb1\xa7\xffˤ\xb0kܩ\xdb\x1c\x90'J\x99\xdeO\xbf\x00I\x88\xdb\x13\xb6\x9d_\xef\xb3\u007f\x05\x91qOb'\xd6Hǟ\x00BYEM\xa3\x03\x1c6\xc8v\x84\x815\xaa\x10NJ\r\xe5\x15\x01O\x88N\a\x1aF\vNx(1\x92:\x01\\\xba߫C\xe0k\x8ec\xa0b8\x05Q\xf4\t\x13d\xe1\x18[L\x14(el\x1c+2u-\xee\xd0a֘d\xa2\x8e5;\xa2N$\xd4\xf6\"\xa2H\xaa\x8f\x10\xabSF\xbao\xeb2i\xa8\"\x8a\xd9\\\xb3h\x047I\x8c\xf6\xa0\x10fN8\x80q\x97\x8cx\xea#v\n\xa56um\xef\t\xe0\x19\x9d\xb0`\xdbNM-J\x00\\\xf4\x10F\xb7\xc7r\xe9D\x02\x80\x1e\xb5Z\xd30\x93\a#'ꥈn\xec\x11\xd2GjL\x82چX\x14\x1bʌ\xa2A\xaa\x9d\xf1gYs\x99*\xb2Y\xca\xdc\a\xc7^ٵ\x98;\"\xd8\x1d$hb\u007f=\xb9\x16\x19ϛ\x13\x980\xdevH<\x97Vv\u007f\x81c\x9b_\x00\\Y\x8a\x03\xec\xa3\xe8w;dB\x16\x02\x83\x8b\x03N\x83\x843\x1b!$\xce\xe7\xe7\xb4\xe7\xe2\xdeI|P\x98 ~\x13&\x13\xe4\x03d\xc5.\xc8\x03\x1b\u0530\xe9\x02-a\a\xf3\xfaa\x19\t++\xc2\x13\x929\xba.mR\xf8\xe94\xe3cy\xaf\x9e\xb9#\x8bU\xd5\xc2FW\xee\bu\xbd\r\xdc\t\x17i/\U0001cbd4f~\x804\xdd\xf0l\x83\xeb\x0eXS\xf49Ä\x1b\xf71E\x9d\xf0\xbd3@\x90\xfd\xdak\x13\x88\xdc\x15@\x17'#\xa3\xbc\xb6c\xac\xbd\xd9n\xea\xa9 \xba\xf1\x15\xefS_;\xac%\xfb\xeeI\x8a\xd0\xc1+\xbb\xf8.\xf3L\x1a\xd8C\x02x\xb1\xeb\xee\x1e\x1e\xbe\xfaꆱw\t\xda\xcdVۂ\x80\x81\xba\xa8\xe1\xa4\xc0\xc3\xf9Exf~H`\xbd\xe90\x1f\xbc!d\x94\xe8@Q{Oh1\x89\x17\x17\xb1H\xc1\xb6Fë\xd5zs\x1a\x8d7\xf3݉\xb6\xb3Ɯt\x0frv\xcc\xe8\xa9\xd2k\x9d\xbb\x86heS3\xb7ۇv\x999\x84q|\xabO\x82\xef\x97K)\x95U\\\xf2\x96A\xd2%\xa3\xb9\xbb\x02\xc4o\x1e{l<\x86\xdbK\xb8\xff\xba\x9d͎\xa2\xa4\x8ei\xb3\x8f\xf1H\xebG\x80I\x85z\x9d=6\x9aWWo0\xeb\xd9\v|\x15\xde%A\xf7\x1ajdD)!\r\x80\xa9pw\xae\xc8\xee_\x82\xb3\xeb;\xb8\xf1\xa6\xdbc\x97D#\xe9\xffˁM\x90\xc0\xdeNz\x9c\xf2\x05\xb7\x04p\xb0^\x8fCDx\xde\xc2xj)\x845O\xc89\xfb\r\xa1`\xd1\xd2EDX\xeex\xf1\x8d ݒGU\xc8\xc0\xed˯\x0eę\xaa\x04\xa9\xef\xd6ډ\x9c.%\r\xb7\xbf\xb6Έ\xb1~\f\xe3\xf1\xfa=\xff\b\x1a\x16C\x13o\xa6)\rF\x847\xf3\xca\xe7$Z\xa4\xfd(\xb7g\xc1\xeboB\xad\xa7\r\xdbƜ\xc0@\xde\xe5&\xf2\xc3\xdae\xee{\x06\xea\xe2厣\xa0\bl\xa0\xb0f\x8a_\xc6Rx\x99N[\xa7]\f\xae\x9b\a8`-3\x99s\xb4\xa6\xc1\x92{\u007fPj\x01\xde\x19\xb8Wuc9\uf178\xae\xa9\x84\x1b[>\x1d\xea-\x9f.D\xa2\u070eY\xfa\x1f\xf7\x9b\x15\x0f\x80\x1ed\x87\f\xcf\xc2\xd8\a\xa2\xb2+\x18^{\x93\xae\xb9C\xee\xf7\x89\xb1m\xe0\xc4\xd9\xf0,\xa6\xb1@N<\xb2\x90\x1d\xda\xd9\xd0\xf8\xf2\x89\xaf\x1f\u007f\x0e.\x8d\x94V\xf1\xe6M\xf0\xda\x11S\xec+\xd3\\D\x9e+\xa2\xdaR\xa2|\xd16\xb5\v\xe2'q\\T\xe0\xdd\x02\xa6\x95\xf3\x1c\x1e9\x87D\x84\x06X<\u007f$\xedp\x89\x80\xde\a\"\xbb酦\x92\xfc$\x9bҷ\v\xde,\xf9p\x1f\xbbs\xf9\xa5T\xd3\xce\x1a\xdd\xfe\xafb\xbb\xff\xd4NkI\x91\x1e_\xe6\xa1`\xd5\xe6\nF\xe8\x88\x1f\xeaW\x93\x9fV\xff\x94%\xd9\xd1w\b\xad~\xa9\xa4\xc4DԐ\xd9\xc2\xcb\xdd*\xf3\xb3xi\xf2\x1e\xb8y[rZ\xaa\x87\x00\x99[S%\xb4G\x9b\xdc\x18s`F<ㅣ\x85\xb3\xac \xa2\xc1V\x8b+\xfe\xf3!+\xad\x96\xf7\xf5\x8a\xb8؍\x8b9y\x9fk\xc5\xc1fb\x9182\xde\x06s\xde}l;[)e$\xc4\xf8\x89T\x9d\x86\x96k\xad\x83\x92\xe0)\x9av\x8f\xb6\xa99\xac\x01\x84\xe3\x1b\xa7{\xd3u\x13\xf2u\x8ft\x8f\xc8\u07b3@E\xe3\xf4>|C\xf6\xda<\\4%\b\x97Rv\xe1\f\xa5\xb6\xe9\xe5\xc4\xe8\xaa\xda\xf4@\u05faC\xcc8\\\x91\xf8~)\x14#k|\xbe\xb9.a\xebo\xaa\xba\xda\x03\xba00G\x94q0\x1d\x14%\xf1\xfc\xf0\x13\xa1\x99hp\x8b\xa9\xba\rL\xff\x9c\xde\"\x8c+>\x8e\xec\xe6\xb5%\xaa\v^Mˊ\x98N\x9as\xd3\xc8\x06q\x8e\xb4=\x95\x12\xb0\x0f\xbe\xee\xe5䦆\xb0K\x114r\xe7-*\xef\xe7%\xe8\xdah#\xd7%;pP馔h\xd6C=\xf7\xce\xfa\xda\xda\r\x97\x1a\xe5&\x14)\xa9ba\x84\xc6KL\xf8@\xbf\xf9\x9e\xe4t\xba!\xa2~2\xedS]rYl\xbaZ6\xd03ўJ\xdeo\xb4\x98O\xa3V\xf5;\xb9h&gO5\x92RT\x88/}\xf9\xc1\xd2\xc6{\xae\xa5\xf2\x17\x17AZ\xd2&\x87\x19S\xf3\xe2\xd9t\xd8\xca\xf9\x9c\xcf\x03\vͯ\x01\xe9\xa2P\xea\xebC\xa1\xa4\x14\xa20\x13\b\xb4\xb6D,\xfbpbpз\xd4z)\xa7 ]\x80I\xf7>\x1a\x00\x0eQ\\Bl\xc1\"\xba\x97^3R>r\xfd*\xfb\xbb\x04C>\xc0\x1e\x1e\x83\xa7\xe1\x17xPU\xbaz\x1e\xad}Y\x11=\x02\x0f\xf5̕\x16\xbc}\xf5ж\xc7\x10\x9a\x19\n\xe7\r\x1c6-`/\"H\v\no\x85&\x92D\x97I0\xffE2Xa\x89\x95-\x1f\xa9{\x0e5\xc0\x81\x99<\r,}\x94\xd1\x13``6\xfa\xc7\xed\x18\x90ji\xa2\xe9\xd8im\x12'\xdcw\xac5\xfc\xdb\xfaRF,ч\x88%\x1eSY\x96\x84\xbb\xc6Wh\xa36L_i샣=\x9a\x9b\xbei1\xf13\xfeYI7N\xb8Cp\x8dI\xc9\xf0Ĕ\xbe\xc3\x12(\x0f\xa8r\xaf\xbe\xbd0\xab\xdd{j\x89\x19\xf7\xba\x85r\x8bK\xbc\x9e\x99\xe5Тo)l\xd9\xc4\xf4\x873na\xbfT1\u007f\\\xa7\xc3I\x19E(\xe9m\xbd\xf7\xb2\xbd߃\x9b\x93\xc2D\xf4l\xfa\xac\u007fe\xb3\xb3\xc7\xe6$Å\xdfwX\x9e\x88\xccU\xac\xfc(@\x95\x84\xae\xf7M\xf5a\"n\xac\x0e,\xe4*vG\xb3\x1d\x96\x1c\x89̨\x13x\xb9\x8a\xae>\x95G\xd9\fS\x82\x11\x83\x80\xee\xbdg\xc0̉\"\x94Q\xcbv\xa4b\x140*z\xdb\xc8PE\xf4y\xc1ɉ\x8c?7\xaf$\x13\v\xc1\x8e\xd8\xf0%\x9e\x1d\x89G\x87\xd2\xd1\xe4p\xc9dY\xc8&f\xe2\a!\xed\xfb\xc7a6\x94\x8e|\xa8\b)\xbf\x0e;u7#\xc23\xc94\x90mJij\xdb\xf8\no\xf7\xf6\x89O\xf8p\xadȁ\xa2v8j\xa0\xea\x99\xfcx(K\xcb/Z\xdcd\xbb\x03\x8a\x8fx\xc9Ń\xfdm7V\xd1_\\\xa7f\xd7L\xe57\fp\x9bX\xf3z\x16\x1e\xec\x85H7\xbe-\xab\x0e\x96\xb5,(1KHb\xade\x90\xa8,r-\x19\x1c\xa3\xa0\x11p\xa1L\xcb\xef\xc6\xed\xa03=\xe6T\x98\x02\xc62\xa9t\x912ټX\x96k:\xa9\xb4\xb5\xff\bZ\x805\xc0\xcfs\x05\x89\x0ep\x0e\x88\xd2SsT\xab\xb8\x05\x1a\xd5\xeb:.]\xb5\xb7D\"\x94@\xb0\xf5-\xb6E\xb7̑\x06!\x10\x94\x13A\x8d\xf0\x912\xbaɶ-\xf0F\x19}\xb1˒\x802Bǃ\x92\x8dQ\x1a\x99\xd5\x17\xe0\x05)t\xc5ç|\xda#4\x84|\xdc\\\xf7㨀\xf0\xc8`\xb7fc\x10,\xb6\x9f#\xd4\ag\x89\xe91:\xd9-\xd6\x16\xab\xa1\xb9ty\x17 \xdb]\xfb\x87\x98\xe0\xe42\xafZ~\x02\xb1\x84\x00.)\xe3\xe9\xae\xf3\xf4\xb3\xd7\x02nj\xe0\xd6\x1e\xb5\xa1\xdc%R\xa5K\x8d\x98\xab\xbf(y\x90\x02`\x918\x96\xb6C\x1a\xd7\xc7֍\xfa\xb7\xcb\xf7z\x93\xba\x1d\x8aK-N\x8e\f\x84\xf3\x01\x0e\xf5`^+\xa6\xf5\x8b\x9en\b\xa2\xa8\x8b3\xb9\xdbϴ\x18\xfe\x02\xe5\xf4\x80\x9d\x16\x95T\x81\xf23\x82tQ\xe1أ\xa9\x06\b4<>:J0È%\xe5ݑZab`\xe8\xbd\x03\xeavͬ\xfc\x86a\xf6T/Z\x01\x02\xfa\x14\x9caޝ\xc7ГIi\t\x9bW1\x90\x16\xfc\xf1\xfd\x83_\xa9\x90\x10>)\xc3\xfa\x97\xbeH\"\x94\x8a\xf9\xe3\xe9p\x92\xeb\v\x83|7m\x14F\xb5^Z\xcf\xcd~f\x8b\x11\x170J\xf1\xea^\x00\xc9I\x91\x17\xdc3V\xb9!\xe9\x18\xa5\xdc{\xd3<\xa5+\xe7O\x9f\x95\xdb\xd8\x1feB#\x95Bc\xd2\x1a\xd9\xc0\x02\xe8jL\\\x11\x12\xa3\xc4-\xbeZh\x95[\xd7\xfaI<\x8d\xaa\xfc\x0f\xf1\x84\x18q\xa8v\xde~\x96k]\x9bG\xc3\xce\xd5TD\xbf?S\xa9\xcb\xd9\xd6/\x1a\xc6-\xd4\xd7%ݒ\x89\x94\xd9\xe87\f\x88\xfew\xa6i|C\x16\xcbI\xa1\xc4q\xf1wc\xa8\x80W\x01\x9b\x14x\f\x9a\xb1 \xcf\f/7\x04\x81x\xac\x82HO/\xae\xe5\xb5\x1d\xf0o]\x91\x9e\x0f\x94G]\x8ay\x8f߃\xeb\xfc\x87\xfb#\x85\x87\x1f7\xa9\x8fb\xcd\xe3$\x93t\xc7\x1c\xaaR\xdb$ \x06\xbe\x98\xe9\u007f]\xe3\xb4a7\x1b\xcdF\xa3Ѯ\xaf\xb2\x8f,n!r\xcb\xdfI|2\xc0\x10\xbc\xe3\xb88\xea\x92x\xff6\xe7\x99gS\xa6h\xb1\t\xb2\x93R^^\x93D.\xf8x\x8aM\xd1MS?漞'G\xfa#\xfc~\xe1+\x9d\xac\xba\xc4\xec\bv4\x8ad!FyT\xdc9\xf1-\xb2fVa7h\xfbB\xae\xcb\x174\xfe\xab\x9f\x96\xe8\x17,\xa92\xbcƉ\xc4\xfd\x15&vTHMqp\xf04\x14?\x02R\\\xb4\xbd\xb2\xaa\xbeXa<\x91\xd84\x10\xee\x96\x1c@Mi\xacH\xd4D_\xbe\xe8\t\x9bE\x11g\xaa\xcfR\x89y\xb1M\x9a\xd3\xd1lT\xfeؠJݮ\r\xab\x96y\x0fc\xca\xce\"\x99HJ\xf4, 6\x8au\xf6/ڴ\x9b\xe2\x0e\xad\x9d\x04\xc0\x0f\x8b\x94\xe9\x89y\x8e\x00\x97V\xcb\xd9\xe6nJn۟H\\P\xc5R\xa3Bd|\xd3\x144\xb9_\xe8\x8e$k\xee\x9c\xcb\xc6\xc0\xc6.\xb9\x9aw\x12\xea\x96\xca\x1b\u0099\x14\x01I\xa0pS\xbd\xfd$\xe4\xfc\x1c\x96|}j\xce\xd6\xf49\xc8\xda\xe4\xa4\xe9\x13\xc3\xfe\x11\xa1m\x87|\x831\xd1ߘ\x9e\xb7\xb7\xfcn\xbe93\x839\xbe\x1f\x01\xeb\ueb10\xd45q\x02S\xfa|\x86\x9b\x9fxW\xed9\x13\xda\xdc\xec\xcd\xeaB\xb2\x96VZ!\xba\x99\xb8\xcbm\xa7K/\xfbLn;i\xab\xeeu\xb3\xed$\xe6\x15*\xcet3\x10\xd6Ͷ\x13\x8c\xd7@}\x00\x14\xb0\xa4\xb4B{\xf5Y\xef\xe2\xb8\xe4\x9fԑ\x94\x14z\xab2J\xe7u@\xdfa\xf7\x16\\\x19M\xfb\x89\x87\x0eR7o\xb3\xf3\x18dz\xe8\xa0\x15\xa8\xa0\x9be\xb3\xf27\xde/$4]^\x95\xc1\xbf2k\x8a\x1ch$\x8c\x13=\x1c%\x00\x89\xe51\xeeI\xe7B\xd2\u0603 \x83\xcfH|\b\x02\xf8N.[\xc9M\\\vL\x97\x8c\xfbb\xda\xed\xda\xe91Mg\xe6\xf2\x1a:\x9dNV._0\xb1\x00,\x9e+\x16\xe1,\xb8\xa4h\xddt7\xb4l8\x8ds~IV^\rN\xe5˼M\xf2\xb2\xc3\u007f\xf8ؑj\xe4\xdcك-\xa2\toܮůQ\xe4\xc1o\x10[m\u007f\x88\xa0j\xe9=r\x03\x9c\xfcm>\xf1~z4$M\x9a\xe1\x17\x1b}z \xd5\uf6c0s\x00\x8bh\"\"\x9e\x87\xecu7\xb7V{Rûݦ\x00\xb8O\x11-\x9e\x85D9V\xd6٥g\x86IʎK\xecLg۶B\x10\xffT\x0f\xa8\xf3P\xb5'\x87K\x17\xcf\xca̦\xef\n\x0eqW\x05\x84֒\xf33e\xb6\xb3\xa5\x12\x04\xffp\xe4\x86&\x90\x96\x88ے\x89\xe6L\x8b\x18hp\xb3\xaa\xa7\xd5N\x83aS\xae\xa3\fw\xf9\xa1\n&\x98\xd4\xe2\x85;e(\x87,-\x027v\xcax\xa3-\xbf\xdbw$W\xa9\x17\xcfnX\xf3U\x9f\x9e\x87\x85\xb8\x89\xea\xf7\xc6t8\x89\x9b\x11\xb5\x91\x99\x03Y\xe7\x11\x8a\xba?KM\x9fct\xbbY\u0603\xbep*Շ\x8e\xfa\x89\x05\x8b\xe2-\xed\x84\x1c\xee\xcbБfL\xac|\xc2[nL\x8a\xe7\x9c\r}4\x89{5\xd9\x18頠\xa33\u19cc\x1d\x15\v\x88n\x8a\xb0\x94$$,+\xa3DN\aԄ-H\xedV>\x98\xd7H\xa6\x8e\x98\xda\xf9Os\\\b\xb7\x05\xff\x95-\xd1;\xc0W6\x00N\xf8\x95M\x89\xdd8\xb2\x9dFi\x95\x91\xd4;\xbe\x8d\xa57\x19k\xf32\xb16%\x02֒\xcc\x0f\x1ca],:!\x8dʲڽE,\x1d\x9f\xde{U\x84\xfe\x01\vnaw\xbb\xb1\xf9\x85Ng\xa1\x86.\xad\xf2I\a9r:j\xa0\x84\x15\xff\x92\x8c\x1a\xb0\xabF\xedb\xacK\x85\xc7Ψf)*c\x96\xa6G5<\xf3\xccC\xa0\x98\x00\x9f\x95.g\xf4\x15]\x04\xa8\xeb\xafk\x96\x92\n\x19\xb6\xa5\x81\x17 A0\xe3\xbe-\xbe\xd3٣\x16\xa6\xa9v\u007f\x04\x86T \xb8\x05\x06\x0fd4K(\xa8\x16\xc5Yq`\xb2\xaa\x9b(u\xdd\xe5{,\xbd:0*$|2\xec\x0e\x83\x9a\x8b/\x04I\xba\xcb,\x93`E\xbc\x86\xe8\xd8\x14\xf1xP\x1b\xae\xb3#q\x84\xb0\xaf\xcf\xcc`\x00\x9b\xe0/\xeb:\xea\x80\xc8\xd2\xd3\xd6';\x8dىV\x06\x87\x11D)˴\f\xce\vr\xc9\xfc\xe3\x8f\xf689\xbew\xa2}[\x01\xfa\x86F\xac\x88\xcb\b\xeb\xa2\xf9ޜ\x17η\x0e\x9c\xbe\xa1\x1d+\xe5\xd2\u009ah\x9eKH\xde\\\xe0ǚU\x8a\x8e\x1d\xa8䬂J\xc0V$pUj\x99|c0\xcb\xee\xdc{\xaf\xde\xc7L\xf3\xebA\xab\xda?\xe8V\xe6=\xa74\x8d\xf2\xefS\xba\x19Ŵt`\xf5\xc5\xc6d\x99\xff\x89o\xfc\xd9d\xa5b\x06UP\x06\x94\x17\xcc\x02\xf0J\xa5x\x13\xd1g\xa4\xaeJR\xf8r\x9dO\x85\xb9\xcfs\t\xf3\x1d\xc6\xc1\x91\xa1\xda\x184Mw\x98\xa9\xdc\x1e\r\x0f\xe8\"\"\xf64\x142\xf7\xe1\xe8\xe0`M\xc4\x1a\xf1D\xba/N!\x89\xd5v\x823չ\xb7\xf2\x81\xe1.\x8c\x15f+\x93@xO\x97V\xc8q\xea\x9bj^\xd7Cߪ\xa9Km\xae\xf7\x9f\x11,\xe2\xb18H\x049\x8cZ\xa8\xac<&\xb8o\xb6\x85\x1f(\xd1@\xff\xb0k\xa9\xf1\x1e\xdf\xd6M5\x9b\xf3\x1f\x8d\xa1]\xb1M\x85\xb8U2\f=\x10vpB6DXj`\xa9r\xaa\xf2<\xf5w\x95\xc61\xe6\x99Y\xd0:\xd5 \xe7o\x91<\xbf\x009\xa2;\x8c\x01\x9d\xf8\x8eF\xc0\xd5\xfa\x93$\x1c;2֜\xd7j\xb4\xb1\x10\xfa\u07ba\xecx,\x16\x12\xcb\x1aʁ\x9dC\x04\xc5Rĉt\x18\x16\xb8\xb0\x12$\x83\x1cVJf\x1e\xc1f\x88\xc59\xc7)\x00\xafa\x999P\x89\xab&\xe0\xf8\xd1\xc56Oo\xbe\xd6\x03\x17l\xa9<\x9d\xd4\xf3\xe2\x02\x19\xf2\x19\xefds=#\x013\xc5s\x16\x8f\xb5P-\xd0bD\xa0\xd4\xfc\"\x8d\x0f\xec[:\xb0wɺ^j\xf9\x89Ӂ\xbb\xd0Qej`\x8c\x8b\x97Tq\x92\x05=\xa2\xfa\xb1\x94H&\xa3o\x8f\x1b\xa1\x1bkĉLD\xdeW\xa1O\xfb\x86\xc2\xf9\x94\xeb\xca*J3s[\x19\xce6\x9dj1\xf0@\xd9\xd8nr<\xaeξۇ\x86#\x89\xcd\xd2@\t\x880\xbf\x9bc\x06\t\xa2\x9d\x1f\x05\x99?ﵝ<2\x8aD\xd5Ӧ\t\xbf\xe8}\xaf\xb0Ts\xcd\xd9S\xce\xfd\xd0\"\xe2\vR\xcd\n\xae\x95\xa4.}\xdfoZ\xde\xcd\x18\xe3\x87\xf9Fo*\x98\x95\xa7ݗ\x87\xb6\xff\xb7\x96\xb5:\xdd\xf4\xd6\x1b\xc1\x93\xbc\xc17\xc9\xed\xf7H\xc2\xf2䍚\x0e\xa5x\xa1\xb4]\xc5\xdb\xc7a\x196\x1ev5\xed\x12R\xfd\xa0̾e1\x87\xed$XL\x85\x9b\xba\xc3\nJ\x89aa\x9a\x11\x13݆,\xc6\xf3섐\xcc\x1a\"3-\xcfG\xe4!\x1c˥8\xb3\xb7\xf28\n|\xe0T:S\xdeP\x93\x8c\x02\x1f\xb8\xb6\xf5\xf1p\x1cMR\xaeY\xdeb\xf4\xe6\xa5{\xef+\xbbO\xfdeۛ2\xf6\xf2\xd7g\x05\xa7\xa9\xd3u\xae\xe7\xf7V=\xfaU>-\xc5\x01kb6U\x9f\x12\x92\x92ЩpZ\xe2M\xbd\xd0O\xa8`\xff\xb2\xdc$W\xe8D\xc1y\x1b\x8e\x11\xb9\xf1A\u07fb\xa3[\xc04\x05\x8d\xfca\x91\xfcJ\u007f?\xaefD?=\x97\uf449d\xb0\xaf(KD䴱:\xe2\x9cD\x1e\x93/[\xe5#\xa2\u007f\xf5$A\f\xc5\xfb#KH.\x11:\x95\x9ex?%\xe6\xdbV\xf4r\xb7\x15@\x01[B$\xcc}\x9ec\xf1o\x18\xe1\x93\xdbS6`LPfM&ɔ\x80\x9bA<:\x8a\a\xaev\x90\x9eÚ\nQ\xd2~P\xdf\x1b\x10w\xa1\x92\xef[\xeb\xed+\b\xf7\x9e\x14\xad\x91\x86\x8f\xfb\x01`+j\xa3 V\xdf\xc7+\x8f\x9eR*\xe3\x01\xb6\xb5u\xa3l\u007f!\x14\x0e\xa3\xfe\xea|\xfe+'\xafKY\xfe6\x9b6\xb6\xcd_\x06\xeb\x96ud\xd2}_\x03\x87\xac\xdc[\xffyuۘ\x95j\x8e\xa5\x00\xb0\xbbo$\xc6\xe3Y=\xa0yjR\x9c\x11i)\x8b\x16\x99\x06b\x90ԋLaD(\xbdX\xfdU\xcawI\b\xabڻZ\x80\x16$\xf47\x02ڻ\xee9\x1a\xb9\xfa&\u007f\x99\xd64Z\xc3\xdd\xd6'\x8c\x93DF\x9f\xf5\xdd\x10[N]\xc5~\xe6d\x18D?V\xf0\xf6\x17Q\x95W\xa1\xcdͲ\x81}vS>\xc1N\x0e\x03m\xf7\xc9\xc3+\x03S\xdeq\xf0\xb8\x0eH\xb0\xffa\xff\xfa\xcaU!\xf7Β\xaf\xe6\x86\x1a\x17\x89\xdaWb_+\xa5\xab\x8d\x99\xe8U\xf4\xfeO]\x8a^\x1b\xbf\xec\b\xfcl5\a9\t@\xc5\xe41\xe6'\u007f\xeb\xe0\xd9A\x9f^\xfe\x83m\xec\xaa\x12\xfd\xc9\x11\xb2o\xb1\x12:\x9b\xc19\xb8ף\xa3s\x19\x0e\x9b-\x13\xdd\x00N:\x1a\xbb\x11\x98\x80tD\x01-\xc6zkS\xb7\xeaj\xb5a4\xed\x9frc\xb4\u007fz\x0fF\xfbۻ \u07bf\xe1\x84x\xff\xdav\x88\xf77[\x00\xbcäC8\x96#7\xb6p5\xdf+\xb3\x86\xb3 \x1c\xe2~\x1d*\x85bJJY\xdczֳw+\xfc\xb5\xcd\xd9\x02\xef\x10-\xc8\xeap\x99/L\xccL[cg\xd8\xf7\xd1\xcdn\xf4lc\xb8\x93a\xffP\x89\x86\xd4\xcbH\xabF\xe7\xbf\x02\xbb\xbe\x9b$}\xd29`\u007f\xa3\xa1\xcb\xd6\x18\xb0\x91\\\n\x82\xf4\x0f83\xe6\x12Ym\xf01b>\xbf~ƽJ\xae\xf9\u0602\xafϏ\xc9\xfcyBs=\"\x92\x86\xe8\xcc\xc3f\xed(zK\x89\x05\x1c\xf7M\u007f\xc5\"\x8d\x1a\bH`\xe5\xc6w\b\xb5c\xbdEd\x85\xea:b8\xae6(\x029\xb0\x18\x89<\xef\x10\xc9c\xffl\x8dݘ\x1e/\x85\xbd\x16\xfc\x10k\x9d\x9bg\xea\fG\xa4\xb4\xbd\x8c\x01\x91\xb0\xc9\xda\xe0^\x02ESE)5\xd6G\xeb_^\xbd\x8fk߇\v\xf2v\x89\xbf\xd5̚\xd3\x11}T3\xff;6\xf1\f Wv\x15TCP_\xa0\xf6\xd0k\xf3\xc2\xea\x8c._e\xbc\xe0єNJ\xd3L\x00{T\xc9!\xb3\x8a6\x93j>h\xf8\x9c0\xdd\f\xba#\xe7\x86\xdd\xd9[\xaf\xea㗚\xa9\xc0\x86\xccK\xc3\x01\xb1z\x93,\xfe!\xda\x133\x88\xa6\xd22\x98\xe7\x06\x81\xb6:6d>\x1d\xa5\x05himE\xd6\\\xcc=\xf0H\f\xba\xd4\x1cZ+{6\x9c\xac@W\xcaʯ&\x11lC'\x12\xc2,\x92\x10\x11\xd0rX \x14\x12\a!\x15\x1a8\x9f(\\\xe3̭2\x98-\xc1P8\x0eh\xe8\xef@\x0e\x92\x14C4\v\x9a\x8d<~\xb3\xa1\x06\xd9\xeeZ7j%)\fe\xfe\xb4\xc5\xebeF\xcb\xfcpZ\xea'15\xb1\v\xd3^6\x1aB\x83\xcd\x05\xc83\xb8nc\x99o#\x14~\xba\x88\xc2²q\x98\xafR\xdb@!ա\xf7\xd0 z\xc3^\xddKs]T\xde@\x83TN\xb3T \x85,S*@\x017\xd0\x13\xd9\x17C\xbe\xaf\x89ī\xdbɅ\x98\x0f\xf6\x93\x92L\x0e\x9d\xa8iQ\x86N\x95\x1c,\x84\xc4\t#:\xe5\xceRѪ\xbb\x88\xa5j\xf8\x92\x17\x179\x97\x05\x15\xe21\x80-\x82Y\x99\x97P\xc7N¿\x00\x8a\\&\xb9yL8\xafӹ\xcd\xf7\x89&0\xcbc\x92\xc1v\b\x14\xf0Ɖ\\\x8e\xc0\x8a\xa1\xf2J\xb5A\xca\xcd;\xa4\xf2Q;\x1c\x95]\x85\x89\xfdI\xb4M8\t\xd9s\xaf\x02\xaa\xd8\x14Mf\xf4?\u0530\a\x1c\xa1I\x12\x18\x9a\xb2r\x1b\xbbr!\xd2K\x869я8p\xd9}Q\xbf콍\x8b\xe7g\xfb-\x84*\x0fsm\xb5~\xc5X\x06\xd7P\x1a0d\xf8M^\b\xb5\xf0?D\xc5\x18dI\x82m<\x86\xa8p;\xa1\u007f\x06y\x8e\x06,\"ۦ\xa66\xad\xe4v\xcap\aT\\^\xcan\xf4\xdb\xf7\xbe\u007f\x053m\xa4>8\xa4eC\xae\xf1\xdcN}\x10\xad\xcd\xea\xedcà\xe6٭$s7ۼ\x13\xfa\x9c#յR\xa5{b\xba\xb94\xa6\xf7\xcbvM\x9dq\x84\xb3\x11\x83l)<\x8bV\x94{ě晐\xb12P\x99\x00\xfaT\x19\x80'\x1dD\xd8\f\nVt\xbb\x02\x90\xbc\xce\xf2\x15\x9d\x87oP\xadaU\x92\xc9\xe36`\x88\xa0\x01\xff\"\u0081\x81Qe\xef]k\x04a-\xdf^v\xf0O\xea\xf4\x86ճ\x86\xfd\xf8\xc4j\xfe\xdf\x12\x98ְr\xcd1\xe3f4cs\xba_%v%l\x93\xe3K\xdfZNi\x92+V\xf8\xee3\xdf'\xb7\xa4\xc4\xd4\xe0~\xe7\x94\xebN\x9bM\xe0G@H\xef\xe4\x1dB\xeeb+\xd3\xfd\xca\xee\xa7v\xddVFq@\x18\x9bݱuKZ\x9dh\xaap@\xec\xedE0\xf7\xf8\x81\xbf\xe4ua\x81\x97\xce\xf2SXd\x84\xee\x85U\xb8\x93\x98K}ԯ\xd28G\x81X\xc7K\x02\x19iI\x04\xac\x82\xed\xa3%\x9a\v\xc7\xfb\x83\x00\a\x19uR)\xb1E\x9e\xe0\x92I-\xa8ږ8\x9c\xc6|\x0e1\a\xd6\x12\xcbG\x12\x80Ξ\xe0\xe6f6\xebȀ\xee=!\x16K\xc0F6\x92Qf\x1e[X\xb1\x15\xd2\xda~\xc0\x10\xf4\x97_\x8f\xe0j\xa2\\^\xea͋^\x14k\x85\x94\x9a\x9d`\x93\x88\xfe\xf9D\xf8\xb5s\xf5\x05\x05\x06G]~\x96㤛y\x13\ao\x10\x02\x01\x8a}\x8e\u007f\xd1;+i%\x8b\b\x1aN}\x87Q\xbc\x9c0\xbf\xe5\xfeԥ\xedU\xbf\xc9u)M\xb6\xdd[\xc6Z`\"\x9f7\r\xb9\xe2\x1a\x0e?/[C\xe4{\xccl\xf1\x82)\xf2$\x18Mr\x89\xf5\xc0\x9a|^\xba\x04\x91\ta\x99\xbb\xe2\xb7\x10\x03\xfe\xa7:\x88\xa0\xcd\"\xe9֊\x9d\xdba\t\xc2l\x02\xf2>\x1b\xdeh\x00\xc7\xe1y\xa2\u007f\x80\x14a\xd1\xce{\x9e2>\xaf\xfeCP\xae\x89\x90\xb0L\xc5\x0f\x10 \x9aj?\xd1n\btg\xe5\x9e\xd8\xd3]\x03\xa6\x16\xe1S\x88\xb8\xf8\xf8{\xe1\xb5UӇ\x05\x91('\xb3\xb5b\xe7\xa3'f\x8f\xe6g0Ӄ\xdd\xea\x18\x95\xc4LPA\xa5Mtd\x15\a\xcc)\xe3\xb32ú\xe3Y!\xd6v\x00\x8e&`o\x85\xaf\xfe2\x12P[\v\x1b\x9daޔ\x84\xbb5\xfb\xc0\fS\x87|#+\x80\xb2\x017J\x05\xa4\x8a\n#\x1bȸ\xcc_\xab\xd5dU\xa9\xa46#V\x19D\xae\x86\xc0\x9bB\"K\x83\xf7\xd6|\xa2\x1a\xb8\xc0\xc1\x19\x16\x80)\x02o\x90\xd0\x10\xaatk\xfdl\x03\x9a\xe6\xc8,\xae\x14\xfbl\x11\xe8\xa1\xeb\x0f\xf3U\x1f\xec)ݹe\x985\x81\x15\x96O\x03\u007f\xa1\xa9\xa7\x04\x93\x93\xaf\x03\x89y\xb5UAt2\xd0_\xb7\xe0\xfd\xee\xf1\xd1\xee\x03\v\xf2n53e*\x83\x831\xbb\x93\x93v\xf4\x15\xb0\xb4\xde(K_H\xb5vV\xcbʉ3}\x12\a,\xd5\xc6A\xb4C\xe0Uƍ\u0602\x12\x94Cu\b\x99\xab\xc9t\x0e\xac\xf9i\xce-]\xb9`\xe6\x05\xce\xd6\xe9\xf9\x1b\xcc7\x84]R\xe6\x0f\r!zs\xb2N\xfb\x96t\xa3\x04\x8a\x91&\xbe\xc5̉̄k)\xbc\xceSL\xa5\xcd\xf4\x9b\x0f\x84̹\xaey\x117\x82\xaa$\xb4\xb0ϥDJ\xaeN\x1f\xcad\xe5\xec\"\x89\xd4\xf9\u007f9\x1c\xe9\n\xe631 I\xcd\x17\x97Z(^(\rlw6\r/\xf5@\x8eY\x8aB\x8e^\xde\xd8\xd9}\xb0OT~9c\xbec\xc2\xf2\x8e]\xf9\x9a\x95{\xe2)\xcb\xdd}\xee\xb9D8\xed$\x13{\x13\xfb\xf0\xde\x13\xc7yc\x96,\x12ʤ\x80{\xf6tA\xbaW3z\x10HI\xba\xabm\xc3\xf0D\xc64ܤU\x04\xfd\xdaT3d\xa6\x0fI\xd2\x0e\x9d\xf3D\x92)\r\x12\x8c\xe0I۬\x8d.\x01d\xeb~\xe9[\f-\xfbK\x96^2\x80Zc\f\x93\xda\n\xdd8\xfd\x83u\xbe,Y\xe9\xb4^\\\xd9_\xa6\xacԁ\xc1_\xf8+\xda\x17cJ\xa5\xda\x1c$\xa3\\2:ZW\xe6\f\xd5b\xd4B\xa0\xedw=\xd7\xda[1'N\x03\x9fYVz4\x93\x1e;\xb3\xfc(\x97fzN\xa7\xc4\u007f\xe0U\x81\xf3f(\u007fp֙\xe0!x\xd7#\x83\x97\xe0\xb6\xcfL\xa9=#\x16ŋT\xe5hn\x05\xd3b\x8b\xe4a˳\"\xd6\xc5,\xccT\xe2\\o\xd4!\x82\xf0@@sN%\xa6\xd6\x18|\n\x95\xc7\xe7t\xe8\xe4Xj\xbb\tj\x1b\xb3\xd3\t\xbfQo5\xba\xfd\x8a\x9d\x1a\x98\xb9\xb3\xedo\x8feF\x03)\x19o\x10\xf9\xf4\x81\x90 \xeb9˷\xcf:\xe1h*'cJ\xe5\xf5孏\x98\xbc[\xce\xd9{\x12Ȅ\x06Nf\xa5nz\x99]8F\x11\xbf/\xea\xa7\x1b|\x92\xba1\xcav\xed\f\xffg@\xd4J:\xb1Y\xcc\xefնNu\xda:\xa2d\xea\xe6hH\xac\xa2\xf0\xf6o\n\xa1\xf8\xbf\xbbt\xbcM\x90\xc6`\x84\x84R̍\xbfR\xf4\xf7i\xc1:|N\xdb_P\"\xa0\xa2\xfd\x88B@\xb0\xb9\xb4\xc8 m`a\x9e\xf5\x8b\xa2:M\xfd\v\x8b\xd0\x02\x12\tc2\xcbŨ<\xc8\x15\xa6\xfdؓ\xb4\x96U\b\x8fO\x8dS\x02\x92\\\x9d\x81\x00%a\\A\x05\xbap\xe7\xf4\f\xe4\xf8ꄯ\xf2\xb1\xbde\xc6\\\xe6\xc8A\x92\xac\xa9\xa9\x9b.̰{\xa7\xd6\xeb\xacw\x86ǿ~\xd1\xcd6\xff\x8e\xba\x99\xbf\x88\t\xf8\x1c;\bs2\x83\x14ŋ`\xf8\xb1\x85W\x8b`\xfdTyP\xf7g\xa8\x01ee0\x16\x87\xf7\f\x11\xed\xf400\xea}/ǔ\xbc\x8d;h[tG\xf9D\xbb5\xd6^E\xbf\xc6#\xe3h\xf7ȍ:f?\t\xfb\x05u3z0\xefڎ\xef$\xeaT\xa8\xa8\x9e^T\xcfAhz\x05\x12\x97\t\x97x\n\xe8I{\x0f\x81\xdd5\x1b\xa6\xe8\xce\xdd\xc0\x8c\x8b\xf8'\xe4r\xc2\xfc\x19\xa5K\n\x80\x9bz\x8eo l֢<\xba\x9e\xd5Nl\xb2\x8c\x9d\x10f\xa8\xa7M\u007f*\xcc~\xd0Uʏ\x87W\xf3\xda_\x9f\x04?\a\x96v\x1e;(A\x80\x19\x85\xa8ͺ\xdaR\xc3^\xff 3\xb7=6\xd26=2\xe6n\xeb~}c\xaf\xbd\x15\a\x90O7\x93X\f\x86\x95\xe0d\xba\xac\x01J\x19\xd6|\x8f\xdeLP\x9cޝ~\x0fͅ\xa9\xfa\xf18\x9a+QD\xec\xf4\xe0\\\xaf\xd4\xe6ҭ\víS\xc3\x17\\\xa7=\xf7U\xd9v\xcc\rM䅚\x1ec\"a\xfb\xabK;\xcfA\xae\x1c\x16=ԨĚ\xa9\x94\xb9\xd6k\x81\x01J\x84N\x80p\x9b\xe8M%AR`\xd1و;\xd8\xf9(\x13\xfb\xbd\x875\xc3\x10W\x9a\xbd\xdb=\xb5\b\x8b\x86Y \x9dg-\xe4^\bv4\x81\xd0X\u007f\xaeى\xfa\x85J\xd8@\xee\xd7=\xf8c\xc73\xc5\xea\xfc\xec}\xca*)\x12\bu\x92\xd6\xca\xffb\x1b\xbcT\xb9\x13F\x9a\x1b'\xd3|\xb9N3\x97\xc1\xb9\xe6E\xdf\xde\x00\xb2\xce9\x89\xbbڪ)1\x90\xd0!\x12G\xdb\x1a\xbd\xf7k8\x92\xad6\x94D\xef\n~H\xaf\xb8\xbdGp\xc0\x04%\xa8Fz3\xee2\xc1\xc4M\x1a\xdfJ\xa2aZ\xde\xd6?\xabc\x8d\xe0n0\x9e)?\x8bh\xe1\x04N\x17\x80\xc0u\x9e\xcf\x1f\x9a\x05\xb2\x1d\xfe\vm3\x1fH\x8e~\xb2\xc1\x1f\xdb\xca1rD\xfc'\xbd\xac\xd6\xd71\xfb\x9e\xe0\xc7\xd5\u007f\x91Kr\x9dt\x9bs\x01J\xd4J\x0fs\xbe\xa3\xd9\xf6\xb3\xa6\u007f\xbeָU\xd7\x05\xcc\xcf\xcd\xf5\x05\x10\x9e2\xb4\xf5\x14r^\xa0+hNzg\x96\xb1l0'\\\x1c/e\x91\xdbtXԐ\xd3v\x9dl \xc9j\xe7cm}!Q\xd6\x1dϼ\x8e\xe3t#\xd4\xe6z\x88\xb6#]\x16\xdb\xd5\xda\x1f\xa1\x1fϕ\xd7\xdeO\x9c\xd1ׇjE\xe1:\x93#\t\xf76\x81n:<\x91N\xe7\x10\xcd\xd1u\xc7i\xb1\xa6\x8d\xe1\x18\xc9{\x85z\xde\x18\x0f\xb5\xd71ʞ\xa4\xea\xfe\xe3\xa3\xebUV\xc9\xf2l\xfd\f+\xd1a\xc2N\xf0\xc2W\xab\xe4\x8d\xc9h\xbb\xcb)O\xa72ymEl٤\xafA\xd6\xd57\x1f\xa4\xa5\xabYQp\xf0\x15\xf6\x06\xb1fB\xac\x8a<8\x8a\x9f\x85\xbb;\x02\xc6\x1f\x05\x8f\xe5\xf4\x96\xf8'gKR5n\x99\xf6\xcc\x06\xe6\xbd\v\xc1T@\t\xaen\xbc*\xb6\x17\x83!=\x16a5\u007f\x89\x9e\xac\xa2\xa1\x83\x88\xb0Z~CW\x97\xd1P^DX-Xf\xa7j\xe8\xfcN\x80ű\x10\xbdq4\xffO\x12\x06\xc1I@\x15\xf9\xe8S\xd2\x12\xfa\xc0\xfb}\xf2Xh/\xed>\xdc,b\x96\xe3\xe7\xf389\xb5\xc9\xf4\xee-:G|\x05W\x05\x91\x92)\xdd\xdeb\xdf\x1f\xfbA\xc7\xea\xf85G\x93\xde<*ٕ\xdb\xda:ğ\xa3\x06!\x14]gj~\xabO\a\xdf&\xff\x8bU\x87N뢹8\xd7\xdb \xae\xf8\x94\xe5\x87\x13g\xf1\f]\a-W\x1aW\u007f(W\xec\x87NI\xbc3\xba\xc3\x1aN\xd2\xc6\x0egr\xe3\x9e3|\xb7\x92m\r\xf2m\x8d\xa0'=[n\x94\ud7acM,?\xe6\x8c$\xb0\b\xd1HD\xae\x93D\xb2-\xae\xdbO\x18?5u\bX\xad]˓\xec\x14\xd33\xfa\x12\x1f7\xf9>\xab\x17*\xe6\x87w\x80\x01g?\x9a\xa8\x95*!\x92\xe9\xf8\xfaJyT\xa2@\vU\xb3g\xce\xd3z\xc5\xd1I\xf1\xba\x95\xcf_\x17\xa4\x06\x857\xd2&\xf2\\t\x8c\x10\u007f\xf0H.\x1fY\xf1Z\xe6(4Y'\xebd\xc2\f\xd7T\x87\r\x91F\xb8\xd2s\xbc-\xecqy\xbaa\xad\x127\x1b\xd3\r[\x8d\x8467K&\xc3J\x88/$\xe0\xb1c/\xc3\xd4x\x8c\x10\xf7\xbd[\x17\xc4\x01\xa1\x80ᶏ\x0e;\u007f\xf7Ī\xa2z1\x1aFv\xf8\xa7]G\x84'ڏ\xeaQ\xafBSO\xc2\x03\xfb\x89\x0e\x95\xe5\xbb\xed\xe9\xfdІ$\xd0\x01\x13y\x99(\xde\x13\xf0TS\x93\xfc-\x18\x1e;\x96hűz\xbe\xcc\x11T\x9a\xc8%D\xe1\xa3\xd7ts\x04\xa0\x88\"\x89\x89=\xeagwU\x12\x81uD?b\xa1\x18$\x1a\x1eZ\x16r\xfc9\xe8\xf7G\xbc\xce\xeb<\x99\x8c&\xfcÑa<\xc5v5\x9f\x930\xd1]\x19f%S\x17\xf2\x89\b\x9ea\x1f\u007fn*\xf1\xd7\xeb؊\x9d\x9d\x1d\xb3oмb\xda\xce\xfd\x828pJ9\x9f\xc1\x82\xd8⠚\xc2'\x98-s\xce@\x86r\xd0\xf0넅\xbc\xa9T\xb0\xba\xa5AX\x81\xa4\x06I\f\xd1\\8m]{\x83Of\v\x95`#\x1c\xca\xd6\x13X\xb3T^f\xba\x195\xf7\xd4''\x1b\xd9\xec\xc4\xec\xd0\x18\x16\xf8W\xca2Ϸ v\xc5sE\xe2\\\u007f\x9e\xed\x88Q\x18s\xb5\x9e(\x99ː@A\xe8\x14jR\x14\x1e*Z\xe8\xfc\x19\xb0\xe5a\x91\x88̳\xe8\x1cSl\x01і\xa2R\x8c[\xcbܜd\xbc*)\x9dɩ\xbb\x9c\xc0\fP\xe4¢ĽHt\x18\xa3o\xfd\xbc5\x9c\xf48\x89\xc9.\x84\xc2]\xc0h\xcb\\s\xe0І؋\xe1\v\xda\x03\x10\xe6\xd7?\x18\xf5Vs\xf1\xd8\xe8h-U\x83'\x89#E\xf6g\xf2\x89\xbam]\xf4\xc42NjWl\x19\xf6rm\xa9Z\xcd\xe3\x12\x97\xa3\xc3\xc3#2\xe7\xf6BE\x1875^^\x87\xa4\x03a4\a\xda\x04\xc5wU\xa9\xedK\xcb'g?ge\x93\u007f\xc8\xdf\x13213\xce\xe6\xe7\xb8\xccǸ\x9bo`\xd4\xd7lKzP6^\x97 \x88$\xf7$9N\x8a\x90\x14\x8bWvg2\xf9H\x02Ϗ\x93\xaf\xe6CR\xefߜa7F\x0e\xa1/\xe3\xb9\xf13\xf1\\8\xad\xfb\xadF\x84\\\xb9/z\fP\xcc\xef/?\xfd\xbd\xec\x1e\xe8\a\xee\xa7\xe9\xf5{x\xd6\xe3Ӽ\xf7]\xaf\x0f\xb3\xbe\xbb\x00\xf9\x89\xeb\x17/\xb4\x17^9\xfa@7c\xa3\u007fޥ\x0e\x1aG\x1b^2\x9f\xe0\xe6_\x96\xc6\xc2Be\x9a;b\x96\xa2~\xf3փ)\xc7Ό2\xb3j\x80\x8b\xd0 \x0e\x95r\xf18]'\x877\xda\xf3\xb0\r\xf5\x8b\x89\xc9 b\x8cC\xdfh\xc7\xfaT\xe6\xc5d\x97\x87\ue635\f\xfd)\x04\xa6+\v\x92\x89\xc5\amD)\xb2\xf5\xd5.5\x871\xd1-\x14\x00\xae\xfc\x1b\xce|Yy\xf5\xf1\x92\xe0*\x12\x01\xb1\xbao\x8dڤ\x1c\xf2L\x13 \xa4\x06\xdf\xf24A她=\x02\n\xc4\xf1\xf6T\x83\x98\xb8@|\xccX$\xa6\x15i\x1an.K\xdcI|\xeeR\x84\x02\xa0\xf7@\xdfP\x82\x86\xfe\xe6@\x85\x91\xdcP\x8f\xe3\xbd\x1c*\xb1\x96\x1b\x8aa\xb2\x97\xc3\xf5\x04\xb6k@\f\xce۟\x8c\x0f\x95\u007f\xaf\xa3\x94\x97\xed=I\x9a\x84\t\xe0=\x9bl\x17\x9b\xc1\x01\xbb\x8e[\xbc\xa9\xbcג\"\xc4h\xecX0\x8fQҜ\bf\x01\xa7\xfb˒\xdc\xe2펖\xf7c\xdc\x1e<\x1d#9`|cO}$o\x01>e\xa9X<\x8c`,\xfao\xbe\x90\x11\xa9_\xb1\xe8K\x893\v\xe7\f\x8c\x8d\x8e\x83\x15p\x96{Y\x12\xb1\x05\x87\x95\xe1\x1eAn[\xed\x199\xabM\xdb\b\r\xa9T(!\"\xbe\xa8\x06?Z\xb0]\x03\x83i\x05E\xc4m\xb2\vĞ\xc2>\xd5'\x96\x86\x99\xe5{G\xc8t\x9d\x1a\xaf \xaa\xd7*\x11~\xa6\xfc\xeb\x9ay\xb9\x95\xeb`\x89'\xabA\xfb?٘#\x01\xfc\xd4)\x02\xb7\x10o\xc6($\xe2\xf5\xe3ȉەL\x9f\x8d\xd4\xfcvYO1o\x1d\xbc\x9c\xf3_<\xec/ǐ\xb8\x01M\xca(\xb9\x80\xb4\x14W\u007f\xe8\xaf藑Q\x91'^\xea\x1b#0\xfaM|\x973}x7t\x91\xc9<\x88\xcea\xfe\xd6@\xbe̻\x00\xcbH\x9dl\x8f1\xc7>\x81\xc0\x1e\x9f& .\x13\xd4m\xf3v\xa2\x9a!*\xe0\xf4)$\xf3z\x9f\xdcmr\xc7\xd8t\xbd\x85\xb4\x15(\x88:\x1e\xb0\xb7\x83G\x88\x1cG\x87beV\xa6w\xa2i$C\xbeO1\xf4\xf8\x10 \x90\xd0\xdbc\xe7Z\xdcZ\xec\x8e0\xcbG\xc6 \b7\x02\x01z@Jy\x96\xaf~\xc2\xe5p)g\x1f\xd4,g\xed\xd9Y\x1bL.$\xb9,\x1f\xab\x05\xf7 \x97\xc6-\xde<\xa0\u007fk\x17\xce\xf9\xd2\xf9{\xce\u007fy\xfcc*0\xae2\xc6/q1\xa7\xb5\xce\xe1\xf2\xbb\xc1\xab\xa2\x93\x12\xbfg\xc2\x0f\xeb\xba\x16\x81\x83\xe0\xe4K\x16\xbe\x95\xf1M&\xc0R<\xe8\x0e\xe5\xc77xC\xe6\x9ey[M\xf4\x96ʛ\r#ͺ\xf9\x00\x8e\xa3\xe7D\x18ya\xb9\xfe\xa93\\\xae\xa7wf\x9ewr\xc8\x11F\xd9ĸ\xbf\xb7M\x13\xb3]\v\\\xb3\xa8N\xf8\xd8\x1e\xb0s\xb1Wݍd\x9d<ӡ\xfb\xd2\x00\x83W\xe5\x00\xbc\x9d\b\xaa064\xdf\xfet\a\xd5ȴ\xef\xd0v\xf0Ȼ0>ԯ\f\xcc\x04\x88\xef\xd0; \x04\b\xbd\x93)f\xaf#\xaf*\t\xa22<\x02\xfb\x0eh\xfd\xcd \xf7\x1f~'B\x18\xc2w\xac\v\x92\x0f\xbam\xa2H/\xe2\x19\x9c\xbd\xd3\xec\xb7\x03\x90\xaf\xe1wqM\xb0\x1d\x0f\xf0\x14\xc9\xd8\xf6o\xeeg\f\x13\x10\x1bC)̵67\xdb#\xe5\x88B\xc6S\x90\xf8>_-\x15[\xcd\xc4L|R\xbeR\xcf\xf1\x90\xcb\xd1l\x14Q\x83}\xee\x1c\\T\xbeH)\n\x9f9Fa\xb1\xbb\"^\xe0b\xa6A:\x91ݳQ4\xb1\xbb' \xca=\xbdsO\t\x0f\xa1\x03\x10\xc3\xf1'\x83\x1b\x19@\x02.\x9a\xe8\xd9Y&8z\n\x8a,i7\x15\x1a\xb2\xb5\xa3\xea\xfd3y\xd8\xc1;\x8d\x81\xebU}p/\xdfI\x9f\f\x01\xeb\xff\vxV\xf9x\xd6\xdeil\xf8F\xbdZ\xc5\xeaf\x9b\xff\xcb\xf9hX\xdac\x87\x98\xd4\xec\x1e.b\f\xe8\xeaB*\xac|&\xbf\xe2\x8f|g\xab\xb7e/\xcak\xc8u\xbb\xfev\\_H\x87\xb6\x9e\x9eb\x82\xa0\f\a\xfad\xd9p\xe1G\x9b\xbe/\xebA\x9a}\xf3\xc0\b㬬'\xec\xf7xȜ\xaeՋ\xe1\xb9;\xd6E\xf0\xdd\xd8\xe9\n\xcb!W\xb8\xdc\xdf\xe8\x04\a\x80j\xcc\xcc{\xaa\xed\x9c\xd9ZI$\xdfz\xbe{O\xde\x14p\xaf\x1e;\xe7x\xa6\x12\xb0=\u05fa\x98q\xde{\xfd\xd3\xf5\xa0\xf5\xc95\xfal2\xb83O\x8e\f\xe5=\x01\x19\xf9@\xed\x13j\x86j#\f\x13\xf8GY\xbfT\x93n\xa1>\xf9&ެ\x0e\xaf\xcd#\x83\xcfCBϩ\xffzL\x1euy\xb5\xf9\xddl\u007fS\x1f\x02\xefa\xbda\x97\xa5\xee0\xc7LTv\x82\xc03\x96\xe2,\x8c2\n\xe5sdT\x01r\xe7U}E\xdd\x03\x95\x9c\xddl\x001\xe0\xb7z\xa2`X\x0fa*h{\x1f\x9aq\x1aiuU\xe3\\\xde\x1a\x16\xaa\"L\xfe\xbaд@\xf9T\x95\xd6X\xd9RU\x87\xbeF\x03g\xf6]s\xe5\xc4\xdd\x17E\xc1\xce\x14\xb35\xabV0\xdf\xcbX\xac\xd2\x16/\xc6\xefu\xeb\xb5\xc6k\xbaz\x85\x9dB\xf5\xbc'\aك\xc0J\xbax\x19\x1b\xa2\xcc\xe7\xdf\x1c\t\xe6Iz\xf3\xee\x01\x80\xd27\xfa\x8a\x91\xf5\x8d\x92\xd2Y\xfe\x86Ε\xed1t\xb4\xbf\xb6y\xaf\x88\xb7Κ_}\x81\xa1|\xb4xm\xe3[\x04\xc0x\x02J}z\xfcl\xf3\xc8\xfeD\x9b\xd7V\x97\xfb\x86r\xf3\xd7csdsq\xbav\xe5[\x91\xe3\xb7&\x11\x8e\x8e`\x9foU\xb6\u007f\xb8\xbel\xeb?\a<\x1cj\xdbC\xf4!\tOe\x12qB\x0e\x93\xb6=\x99J\xdc\\\x9c\x1e`\x9e\xc4Lr\xb1孈\xe1d1Mh\x1d\xf7o\xabw\xd1ѹKi\x06\xeaģ\xead\x8a\x95*;^\x18ҋ\xcb\x18\xbf$\xb5\xb5xH\xb1\xef\x18\xc7U\xb8\xb3U`]G\xb5kC\xadꆂ\xe6\xec\xf7\xb3\x03\x06\x9a\xb1\xfdO\x98\xedQS\x85C\x95\xe2w\x93o\x8f\x16\vg~\xe1\x1fyG8P\xef\x99{{H\xae\xbf.$\xf5\xfb\x17\xdf\x156\xc5!\x16\x11}\b\x1cd4,q\x1f>\xe4`\x16\x80\x04ll\xb5UMBR\x93\xb9Pe\x02\x862\xe6A\x8d1R\x84\xb9\xe2H\xfcq\xbc\xe1l\bB\x90Q\xbf\xa6\xa6$\xd2W\xa3%\x89\xc7b\xe2\xb7hB\x89\xb2\xf9\x0eÚV\x1c@(?\xfc\x1d\x1d\xb6\u007fF\xf7\b\x9eA\x98Q}dl\xed\xc5\xe0+\x8c\xe7\x9db\x8a\x9b\x10\xe1NIM\x03\x16\xc3dT\"+\x18\x0e\x82ƌ\xb9\xb0\x18o0\xed\xc5`\xc4\x1889\xc1\xfe\xd7\xcf\xfc\\|5 ޣ\xf4\xbbئ(\xc7\xfa\x93\xa0\x8b\x92\xd8y\v\xf1j\xa5q\x94\x19m(\xb2\xec\xf7\x80\xe6\xd3<\\G\x91\t\x15\xde2\xd7\a\xe9\xf1dT\x9e\x18\xf3P\x9c\x970\xaf\xf4\xe8$\x88\xa7\xb6n\xcf\xe5\x1f@\xa1\n\x02Ē!\x01\xd7X\f\xbf㺕\x9f\x96\x96\x1c\x8d\xd3N\xe9\x83\xc6\x19kճ\xadxiki\x90\xf6\xde\xf3\x9c\u007fݝͨћ\"0?\xce^2\xe5\xb6XF\xe7\x8d,{s\xc0\xe6r_e\x10@V\xae\xfa\x15\x92\xc1\xf1\x92y\x9fg\x88\x92\x14\xfe\x1a\x13\f\xceN\xca_\xe7i\xf4\b\x8e\x97\xfbw\x02q\xbd;X\xda\xc8\x1cED\x83\xa0\\\xa1\xadb1\x1a\x1eG\xa3\x92(\xb6\xa4\x10\x8d\x8aRs\xd7\xc0\x92\xaaT\xc5\xcb\xdd\xf4<\x1b\\ډQ\xfe\xf9\x91\x022tT\t\x83;\x97\x9f\v`\x92\xd5\xdc[\x1b\a\xd9,\xb0\xeaAk\xbe\xa3K\xa3\xeabDl#\xe3b8\xcd,]\x99i\\\xac\x9b\xb1\xfe|kC\x89\xd9\xe5\xd0xLq~r\x16\xdd\n\xd4Ά>|\x8bž\xbb\x0fB\x8a\xdc\xc3\xc9a\x9f\xe8b\xc4\xf7?a\xdf\x10\xf0\x80\x87a\x0eg3\xbc0\xa0\xe8\x0e\xd2\xe3(\tj\xea\xdc\"F\xb5A*\xed{ߣ\xcbd\xb4]ř+XH\xe6\x94z\xc0s\xff\xd9\xd6\xf7\v\xe5\vZ\xa9\x02S\xe9\xe2L\x1d\x0f\xe0\x0eu:\xa7\x99˅\xe1)\xd2Ҳ\xc0\xddn\xb8J\x1c\x8eEB\x0fnS\xbe\xf6\xec\x0f\xb4>Ħ\xf1\x02\x13\x93\xfa\xc2\xcd\tm\xfdh,\xf2R\xcd\xfb\xe7\xdd\xd2T\x8a~}\xe99,\t\x90/\x82\xa5\xa7\xbe\xe2\xfb\xb5\xaf.\xee\x84\x02\xe1\xfd\xaaH\x80~\x85!\xc5\xe0\x85\xcb`\xd2\xd7\x02E\xf9\xbb\x0f\xbc\x18x\x19\xfb\x14\xf8\x14O\xf9ۖ \xdb\x1bm\x04w\x01I\xb0l꧴ёUz\x9dz\xf6k*\xbd*|m\xe5\x99\xca\x02*\v.?\xfd\xf5\xf7~\xe5\xfc\xbe\r\xc8\xe5c\xeb\xf8\xb9\xc0\x02hp\xd7\xd3?e\xcdY\x97]\xb9*H|̛1\xcf\xf4\x91\xf9\xc8e?\xa9V;\tا\t\x0e2\x11\x9aPQV\x88\xbc\x92lW6m\x1c5O\x11\xa43\x1b'\x03\xdb^\xfd\xa2\xab\x1cx\x13\x12\xe0,\xa8ҹ\x1b\xb3a)T\xec\x99\xf3\xa5eU\xfb\xaes10\xab\xdb\xd9\x16\x84ft9\x17\x13\x80\xfc\x80\xc3\xfe\x1c\xfcT\xbb\xae{\xd2!\xb0\x8aL\xc0\xa7\xd1@\xefOL\x8e\x97\xd5tǽ!\xc9\x0e\xfa\xb9^\xe3L!t\xad\xe6i \xf6\xa4\x96^\xb8\xf9:C\x12\xf8\xccR\xee\xd6\t\x82\xbd\x99\xf0K\x9c\xfd\r\x12?2T\xc9\xeaYx\xff۩Fq#\x1d0\x85\x8a\xb0\n<\xf2\xbd\x16\xafhѭ\xce\xfa\x9a\xf5\xc3)\x99\x06\xf5\xae\x1bkes\xb9a\xbdT\x93l\xd5\xf1\rx\x83\xb5\xaa\xc9\xff\x019\xb4\xf1\xbd\xd2d\x8f\xc9%+\xfe\xb3b8X\xfdZ\xca \xeb\xc8;g\x9d\x03v8\fn\x177\x93ϻ\xd4\xd8a\x93\x80&\xbd^\x1c\x8a\xc1\xd4o\x9cb{w\tOO\xe1\x03\xfb\x157\xbfjϯ\xb7زΞ\xc0\x05\xc3,\xe9~\xa2\xf3WY\x16\xbe\xf0ػqÎz\x9d\x1b\xb0\x8e\xb3Voλ\xe9g\xc6'5\u008d\xf7(\xab\xea\"ե\f\xc3\n\xfeA\x96Ӄ\x06[\x9d:\x94\xd3P\xbf\xf1|\xb9Ӓ+>\xa4\xf2#\xd1\x062?$Mnd\xbdu\xea\xc0\xa8e\xd1S\x9fJ%\x9f\x8c\xad\xfe\x12e؞~\x9d\xfdU\x9fq\x97\xd5\xf1\x18\x9a\n\xbc\u07b3\xd9҈z\xd9\x05Rn\xe0п,7\x84\x05\x8b˱\xe8\xf7\x12\xb5\xf7\xd1\xc5\xc5>`\x17\xc5\x15\x06\n\xcd/\xebuF\x12g\x87\x87\x13Og)P\xe7\x95J\xf4\x85\\)X\x1b\xbf\x04k VF\"\xf1\xe8\x03\\t\xf6\xd7\xd8\xcar\xd2\x02#\xf2\xd8wE]\xb3s\x1a\xac:Y\xa0#n\x87\x9a8\xc1\x80\vLm\"6D\x1b\x8b\xda\n\x8e\x1cV\x00\x1bġ\xaeH`Q \u0be2\xaa\xe0\xa8ү\xee\xadQ\x17kG\f\xd3\xff]\x01\xf2<2\x92N\x88?\x9d\x91\xa4\xe0U\r\xe7\xd0&\xe4\x19\xf2|\x86a\x95\xb9\xbd\x02_G\u070f\xb1}\x9bdi\x9f!\x90:`Ⱦ\x05\xe5\x85\xd1\xc1\x1b\xa1\xf8\xf5[\x04\xed\\,Y\xa9\xc4]J\xb3\xba\xfe\x19\xe9Ϲߐ\x88\xac\xc4ì~\xbb\xb6\x97O\x10\x11\x93\x9e\x1c\xdeA%>\x9d\v\xb6\xf1\x99\x04]\xd5\xe4\x132P\x99l5p\xb8\xe0O\x93\xc3ѐ\b\xf6\x81[ʀ4O@\x9f¡\x11,\x00\xc7\x00Ҭ\x8a\x87\x83-\x83,\x19\x994\xbd\x1d\xecX7\x9f-#?\x903\x92\xb8\x04\u007f{\x1e\x1f\x8b\xfd\xa9\x16M·\xdfC\xbf\x8d1\xce8\xa3\x15a\xbd\xaf\x1eY)\xb3M\xbf\"k\xea\x13\xada\xe6_=4\xa7JqM\x85\xe5?\xa2\xccnh6\x8ek\x8a\x1dɜ\x98\x10\x9dP\xff\x1a \x10\t\x9a2\xa0;\x893\f\xb3g\x824\x01\x1eՍZЦө\xa7GZ\xeck(m\x8d\fp\xb6v\xab\xeb\xa6\friZ\x11F\xcd}\x8a\xb8\x8bi:\xc9/\xeb\xfb\x9a\x10\x1d\x8cczP\x9f\xc5uV\xc5Q9E\x8d\xd2&'\xcc/\xec\v\x87\x91v\xe5\x9c\xf2\xad\xf9\x04<\x802\x86\xdf\xf4ۊ\xde\xf6\x8c\x16\x85\xe9YQ)\x82j.\x1a\xcd\xf8HN\xac\xfb\xa2\xda\x1711\x99s\xcd\xd5ʗ\xad\xe9\x92\xe7؋\xe0{\xe6\r\x9d\xa5'|\xadk\x83\xaalT\xab%\xef1\xf2ꪋ\x1dC\xa5\xcf\xddg\xf6QUJ[\xee'\xf2\xc4U\xfaؔ\xfb̝\x97ֶ{\xbf81\xe9 \x1f\x8a\x8dr\xa5n\x9a\xef\xe2\x9aҹ\x85\x12\xcd}\x1d\xba\xce\n\x05:\x13\x11\x00,\xc4й\xdc\xf76\x1aX7\xa4\x9f\xae\xef\xacf\xac\xc1\x1ae\x9b'\x05\r\aNM\x19\xa1\x152p|\x824\xd9\xf4p6\x05\x90\xb6\x02Vn듁p&S=\xc8[- ߞ\xf1\xe0\xbf~\x9dNj\xc6I\x9e\xb0\x9cY\xf5/c`YAq6\x9a-\xfd\x8bY\xae30#V~\x1ah\as\x86\xfeEPT;\xa1u\x1d\x97\xf0b6\xd6\xf8\x86WD#\xc5\aN1o>\xc1\x19\xea)Θ\xa9\xffC\x1bx4\x8c$\xb5/j\bl1\xf7\ny\x97.\xd8/\x9b\xad\xf2,\x97\xee\xf2\x8dRr\xd7\xf9\xe3\x96\xd0\xf7[YE*G\x10Е\xe2Km/\xfd|7\xfd\xc7\xee\xfc\v\xef\xfc\xdfSI\xb8\xea\x82SƗ\xb8q\xa6F\xd5\xd7㍹\xf8\x8c\xa86\xfd\xcc\x15\x05:\x05c\xe6\xfaVs\a\xc2\v@\xd8\xeaw\x9a\x80+\xdak\xff1\x81\xc4c\xcd\x1c\xcf\xf5aí\xe2\xe9\xc9\xfe\xa4\xe1w\x1e0\v:Y5\xcdQ\xb4\"\n\xc0\x8a\xea\x9f+\x16g\"\x12%*\x1e\xa32\x1a\x01\xe9t\xcb\xf5\x9a`\x8dG\xa5\x90ݴ\xa5\r\xc0f:hN3\xec\x163\xc6\x16^\x05\x86~\x87yө\xdf\xf6\xe0)\x1c\xd2o)l*\xde\xee\x91H\xf0-\x1d\x96;\xfb\xc8\xf4\x03\x03+\xd9|\x91\x18\xd2+[\xa5\x8a-\xd9\xd8\x15ZG\xae\x13X\xf9f~\x8e\x12M\xc6e\x95b7\x0f5\xa4\xb7\xd3[\t\xdeHo\x16}p\x10\x03i8\u007f\xf1;\x1d`\u007f\x94$\x907\xbd\x98~\xefYw\xa24\xf7\xe5Ryp\x06J\x92s\x88\xa2\xaa\x03\x82\xbd\xe0\xc1\x87\x86\x02}\xd8!*Yf\xd7~\x91\xb0\xf1\xb0\xea\xa6W\xf9\x81]\xb4TKV\x18\xe00Fy\xf9\xdal\x83\x1b\x89$\"\x99\xcf\\\xd6\xf9\xc2A\x1a\xbb\x83E?\xfe\xa2\xd4\x04W\r,\xdd[b\xf2\x12\x1e0q\xf9\xe6\x10\xd3.\xfb|\x97\xccx\xb5Z\x1e\x8d/\xcaˁ\x04\xc1\x95\xff]\x92\xf0\xd5P*4\x1a\x03\x94$*(\x93\x94\xc1\x89\b\x19R7\xd1\x01\xb4L\xa0&\xbe\xa2\xe0\x12\xe5`go\x10Tܑ.\x19\xa2$\x9cV̇\x89h\x16\xb5U\xeeL\xf0Hn\xa8e\xbe\xfai_\xad\"\x81\x93\xd8\x03o߁\x00\xfd\x16\xb7e*mb\xee\x9d\xe4D2\x9a\xa3\x96u{\xf4\xf4\x1eݹш\r߶\\\x1b\x9f\xa8\x89\xefؿ\xe8\xa9\xec\x84\xd9\xedZ\xa7D\xc7ܚ\xc6\xf4\nv\xe8z\xfe\x0e1Ul\x87\x10\xe0Rl-wk2V\xf9x\xd9Ց;\x9d\u06004\xfd00\x85=ԑx\x8f~\u07bdګ\x03\x03\xd1\xd7\fo2\x02R\xdbm\u007fԔ\xb8\xd7=\u007f\xde\xf4_\u007f\x9d\xd1r\xd7\x1f\xfb\x96Z&\x96ן/\u007f\x1b߸\xa7\x9d\xaf\xa1\xab(\xe7\xcd[\xcd\xe1C{\xe8%b[f\x03\x85.\xe1\xcc\xaf\\l$}\xedV\x11\xb5\xf8\x82\xcb\xf7چ\x1fU\xf6\x9f\xc8\x1d*B3\xfdl\x94\x0e\x14RPf\xeb\t\xd1d\x03\xaf'\x0e\xbe\xfa\xcd\xd5\x0e\xde\xd6\x02GL\xee\xee\xe5\xdb\xd1\xc5c\x01[\xc3d\xfdN\n\x8d%C9\x8bX\xa5<\xfaQ\xed\xed^i\xf9\xbb\xda\xef\x8bp,U ȑ\x14\x17\v\x1fTÉ\xf6~\x91\xedU\xae2\x17('w|\xd6/\x9a\xabB3\x14\x00\xc2\xea\xb1\xd7\xe8\xe7J\b,\xbbt\n\xea\x1e\xdc\xf1\xacWgLN$\xf2 [\xc8V\xe3\xfc\x0e|\u07be\x99v\xc0h0X\x9dX\xfe\f\xe9\xec\x9a\xcf\xd0<\xdbj\x8eh\xe7\xf9\x9c\xb6\x19\x89j0\xd0\xce{rLNm\xb3\xad\xe6[[L\x1e\xf63S\xb1$Y\b\x83\x11\xf7\xa5\xf1ʈ\x13~\r߇\xa5\u007f\x92\x91K\x9e\xb0\xba\xad\xf1\x18\xdd!\xd5QE(؋\x9a\x93\x8b\xc3\xd9P:&\u007f\x97\xf9{\xafƼӬ4sœ\x89\x85WL3A\x8c6\x85R\r\x11iv-\x157\x05S\x10:\x9fL\xb13\xad\xb0e\x06\x9a\xf1\xee\xb9=^\xaa\x8b\x89\xaf\x19\xa2Ŧ4˳\xbf4\x06\xc2OC\xdf\xe9R~ܐ\x8c\x85NK0+c$&3\x96M\xb1\xfc\xd6\xe9u<:\xa5\"\x15Z\x93\x94\xe8\x00,\x9d\x8a\xc7n2N\x96\xc6\xd4\xceEG\x96\x10\xeb\xd1%Wթ!`\x10\xdd\x034ى\u0087\xfa\x1b\x0f\xba_\xda\xce\x1c`\xd5\xea\x80}\xb4.\xf1Kq\xdf\xf4\xc5~\x9bJ\xc7k\xa6\xa7t\xb9\xcak\xcd\xf6S\xc3y*\xcb\r\xb3\xca\xfb)\xf7I\x8c\xf9k$Q\ue18c\xde\xf6\x86r\xd5q3\x89T\x94\xb5\u007f)A\nRs\xfe\xc8=[D\xb8\nj9\x1cq\xaa\xef\xe2v\x1e\u007f\xcfC\xdcno\x03\xe2\x9dKR\x1e2\x86v\xdf)\xc6\xcd\xd41d\x9cc}D\x992k<9?\xc0]\x0e;\xc48\xfd\xa1\u007f\xfa\xbaBR)x\xf6ˣ;H\xd3i\xb4}{\xcd74\x9e\xcd\xca\x174Ϗ[\x16\xc4\v\f\xde:g\x9aV\a-}@\xb8 ݡ\x1a_׀JPz\x17\xa5\xb3\x8f\xfa\x87\x8d\x95X;\x97)aDJ\xcd?\xa5\x8f\x9c\\\x15#X\x92\xc1\xecr\xe9\x9a\xe7w\xf0m\xbe\xc9\xcd\xfeA\xf3Ў2\\\xa9\r\xdd=\x11\xe669j\x89R\xf0\vLm\x8a\xa1\x8b\x02.I\xc2eG\xe3\xb5\xfc\xa0R\xb0'\x9c\x88v\xf7$\x98\tP\xd0\x19>5h\f_\n\x80\xaa\x01c\xd5ҠW\xae?\xbc\x86+\xfa\xc2\xe0\x00\xf5\xff\xbc\xf7\xe3`ރχ\xb2#C\xbd\x80\x97\xcb\b\x18\xc5B\xea\x9b\xe9W'B\xab\xc3~\xb3\xab\x82\xcd\xfdc\xe1b\r\x00\x13\xd2\xfe\xef\xda5~}`\xf0\xd5A\xd6E(\x12(r\xa2\x1b{2me5\xfb\x14\nt>`v\xb3\xc3\x03\x03\xefd\x02,\fp*=\xaeϕƼ\x01\x18'\xd8\xe1\xb9\a o\x8e$ݥ\xaa;f\xf2`\xa2̢\xd8\xfct\xb1\xb9ɟJ\xaf$\xdf\xfb\xe3\xaaH\x1f\xfe\xe5Z\xe5\x00\x04\x12K\x95\xc3Ԋ\x83\xb1\x1c\xcfk\x03\xc0+\x10L\x01m\xed\xad\x14\xde\xdf\xefR2\x1e\xa4\xe5\xae1\xf4,\xb0q\xe1\xc7\xc3\xf5\x88\x95\x87F\x1e\xef\v\xc1\xa3p\x92̹-\x97\xbdJ%b\xc7\xeb\x1f\xa0\x95\xcf=g\xf5V\xf5\xa3\x8d^\x89y\x81\x90\xbd~\xcf\x17\xc3\u05fc\xf5\x150~-P\xc7\xcfת{\xfcƛB\xa2\xf8\xc62X\xb2Z\xda?\xeb\boG!x\xcdn.\xca\xdb}%\xae}O\x1bo\t_\xbe?b\xb8\x1bJ\xdc\x12\xe1\x89\xe7N\xbe\xf9v\x1b\xb2$bl;z\xe9\xce`\x86&K\xab\x87x^]\"\x11\x92\xe5\x15\xc3d\x98\x0f+\x11\xe4g\v\x01eI2\x80\xf9\xaf\x8b\x04\xf6 \xf2\x81\x14B#\xf0(ijNN>SwF\xc1\fW\x00\xd3\f\xa1|\x97\x03b\xb8\x18\t\xfe\x02\x88\xc0W\xf9oW^\\q\x13\x1a\x8f?\xbf\xa61>\x04B\x00L\xd0/=\xd1iR\xff\xdb,\xbe\xeb\xf8\xe0\xc8cykW\xbe\x18Z)\xc7BU\xbc\xa0\x8e\xd6kjy\xd5\x1b4X\xfe\xf5\xc7K\xc3\xe6,\r3\xf1\n\xeeF\x00\x02\xfb\xe5\xbc9\x18\xe2pK\xebu\x8e\xaf\xb0շ\x83\v\x97\x8c\xd9q\x90@\xf7\x9c\x98OAv\xfeyG4\xf0\xfa\x90\xcb\xde\xde.,m\xb9\xa3#D\"^\xccѣ\x87\x018l\xa4QZ\xf5\xa51\xf5\xf3\x82\xab\xd0C\x94\xae\x9f\xe8\\\xab4oJܨ\x8c\xe8힊\x0f\x95\xa6\xa0\x85\x1d\x93\xfddD6\xe7h[\xa6\xbc|\x1a\x99\xd7L\xd1\x1c]\xd5V\xf8~\x81.\x86\xd7:\xb6\xe9\x0e\x8c\xc6\x12\xcc\xd40z*\x05\x18\x1c\xfa\x19\x15\x9aHX\xd7,\x80Ͽ\xcf7\x9f\x91z\xae\xf6U\xf4QN\xbce.7$:\xba\x86\xc5.\xcd\xca0֣M\xbd\xe5j\xb9\x13\xeb9\xff\x17g\xaa\v{2ڬC\x8d\x1b\xfe\x1b\xabO\x19\xbd\xa2墸\xd3\xfb\xff\x90N٘\u007f\xc3@.\x8b\xd4W\xd41\x05D\xb1\az\x1c[\x10\xc1\xb0\xe2[\xb0M%V\xe65\xecr!4&U\x99r\xab\rs\xe2\xa07%y\u007f\x00\xe7N\xd9J(?\xa5nYm\x89\x1e\"T\x13\xfaC\x8dMmr\xfb.\x04\xee\x98ݴ{bSNT\x8c\x1d\xec]*\x19}\xe8\x92\x1bv`\xaa\xa4\x98\xfb\xa2\x951\xea^H\x9d\xe5\xee\x8b\xd3v\x93No\xeeUۆAS6W\x99Oىe\v[(\xda\xceB\x11\x03\xe5\xa2͝\x1e\x85to1bϫ\x15ZH\x82\xe7\xbc{\xd1\xc0\xf5\xa4~\xcbN\x80}V\x1dˋٹ\x00o\xb2\x92\x15<\x8b>#\xc7\xfd\xa2o\xaa\xf0\xee\xbeTFD\"\xef\x1b%7\xef3\xae\xac\xd7.\xf5(?\x15\xbef\xb7\x92\x83\x8a\xe5\xe6]\xdf\xd2\xf5`!\xf5\x9c\xc0\xb1\xfc\xc0\xbf\xbc1%U\xef\xf4qL:蜧\xa8ϸ|\x92\x04\xfb@8'\xf3\x17+\xae\xf5V\xa8\xb6W\x1eu۠\x82\x9d0\u007f\x8b\v\xcb} +T/\x89\xeeQn\xe9\x13\xa0\xe2l\x10\xea\x9e~\x86c\xbb\xf2{\x97p\xab\xfea\xed=\xa6\xe8\xa3V:#vm\xf8\xe9~\x90\xe2\xc81\xee\x81\xcf\xed\x90\xf6t\t0\x8dSPH\x81]\xb6/\xc8j\xe2g/!\xbb\x92\x88\x1b{/\x1b\xa3c \xe5j\x9d\xd0\xf7\xc9h\x91\x85\xe2[\xe3=\x92\x10U\xd5\xfa@ʍq\x8bIg6\xceM\x96\xad\xff\xfa\x1f\x97\x95mq\xe9\xfb%Y8\xec\xbedc\u007f\xd5`\"\x17\x8d\xdaX\x91t\xae\xa4\xe5\x01\xb3\x9d\x13\xa4\xaf>\x9d\x17\a\"\x03\xb7\xfb{\xe2\x85\xc5ri\xb8\xc5\x14\xe9P\x9dO?\xfb\xac0=\x86/\xbf9\xb3\xd2F\x01\xed\x13n\x1eV}\xa6OY[\x12՜\x0f\x1b\xbd\xb5\xaf\"I\xee\xd9\n\xe9{GEz\t`)Ӈr\xca\x12\xec\xc1\x1d\xb6OoK\xf1\xffY\x02꺧\xfeS\xef\xe9\xc7\x02\x954\x03;\xa0\xa5\xbb\x94\xbe\xeb\xb7\xd4\x1eL'\xf4\xb1\xb9\xa2>\xe1\xb0c\xe8\xc4\xe1\x92N@\x19\x95\xbd\xe0\xaa\xf18 \xe1\x17ʋ\x8a{삕z\x14b\xf6\x048\x05_x\bV\xe2\xd4\xf0\xd3\xdc\xda(\x1eX\"]Δ\xacěM6w\xfe,\xa4f\xe8gf\x94\xd3+͜)T\xaa\xd5J\x1bU\bt>\r-\xe8]z}\x03\x1b\x86o\xa3*\x91mG\aŶ\x1f\x961\x89S\xcd\x1d\x8c<\xbb\xb1\xba\xbb۵\xe5\x87\xfc\xb7&\xaa\x85\x15:\x17\x97\x99Q\x83z\xf1H\xed\xe1j\xba\xbdl\x1a\xa6j\xfe\xb9L\x01\xee\x1c\r\xd8F,\xfb\xff\xc1\xf0\xf8\x8fa\xe8Y\xaa\"'Lˬ\x86ɴ\xa5\xb2bJp{\xf6\xbc\x976\xadի\xe6\xa4\x0eh\x93\xe6\xd1]\x13\xbf\x8d\x10m\xaa\r\xfdE\xd2=\xae\v\xd2~\x92\xeaf\xa1\xbb\xda\x04Fv\x1c\x8a\xdf\xea\x87E\xbb`EWin\x16ux\xe08!GVY\xe7??7K^\xf6\x15+\x9b[2\xb7\x96\xdd%_\xacmw\xe4\xbas\xc3Z\xb5\xea\xe9MZ?\xdcv\xe2l\x9e\xb3\x939\xa3\xb9f\xa4\x88O\xca\f\xcf{\x1c\xf9\xb2\xaa,\xb3'9\xab/\x1b}\x15\x00\rT}\xe5\xcc\x1b\xeb\xca\xe76\xd9\xdeV\xebzô\xffvU\x12\xb9[\xe2\xd1dT\x1e,_u\xeaV\xca\xed\xa3\xe4E\xcd+B\x16:\x81x\xe2\xe1a\x8fY.L4\x8dr\xddP\xcb1\xf2\"\x11\xad\xa1n\xaf\xefj[)Xs\xd6\x1154\x8d\xf5\x01\xdb \xc94s\xe5S\xca6\xdd\xc1\xab\xa2\xf2\f{\x99(\x05,\x89kW\xaa\x87\x10\xfb\n\xab:\x1aDm\xaa3\xfc\x88\xb0\x8e/\n\xf0T\xc5*\xdd\x1f\xd7\x05\xa9z'\xa91\x1e\xb6o\xf1'3\xff\x18\xf8o\x8aw|Ћ\x14\xa5\xa9=\xcaY\x87<\x10\n\x04a\x9eD\xa8m\x82\x1e?F_\xa3Y3\xe7f\x02^\xb7L\x12\x95f\xebf'\x1a\x15@\xd4\x18&M7\xb7F0{\x82\x8c\xe7\x95\x12G\xa7\x8a\xb1T\xc8B\xc4\xd1\xce\xd3/\xaf\x1ff\xb8\xd5zqc\xf8].L.I\x11\xb2n^\x1e\xbd\xa4\xd9\xd2W\xa1k\x94\xd6(\xed\x10h\x1a\x8a\xddc\xa4!Ȝ\xfa\xeb\xae|\xe2\x8a%\xd8?%\xc0\xd0\\\xfd\x846\xdd\x15Q\xc6\xc0\xb4n*\xb9\x990\xc0\x02\xe4\xb3'\x18\xab\x19'\x8a\x8d\x8e\xd3W\xaehĩ\xe3\x9f=\xfb\x00\xeeŝL\x96\x1a\xdcCg\bR\x04\U0005b64c\x89\xa69V\xee\x94\xea玫؛A\xa5\x10ӚT\xbfQ\xf5\xaey\xcc\x05č&i\xfd٣h\xf5\x9c\xe3QJ,#\xf6|d驺\x05z\xcd\xca\xf1\xa2|yYH\xb4\xc8\xe7\xee\xd1{\xaaFI\x12%\x99\xacO\xd8\x11\xe8\x14\x82\x16RD\x92&\xd6k\xea'\xce\t\x17\xf5\x91(\xa8\xac\xfd\xf1k\xd5ͷ_\xdcu\xe1XT\x05\x81\xb04\x83J\xdd\x14o\xad\x81\xddtǠ\x80\xf5\x1d`\x12X\xb3\u007f\xe8\v\xec\xf3\x88l\x04\xda/\x84\xde-\xddԩ\xaa\xac\xae\xc1\x19\n\x14TBIj\xa2ԛ/\x03\x9d\n\x82\xf8J\xc0n0,\xa6ħXB\xc4\xf7\xddU\xba\x9eH\xd5h\x1d\xd7\x17Fe\xa8\x02%\f\xd66\x8e%\f\xd4/\x12\xc6\xc1\xb5\xc6\x02\x14:&zLl\xb1\x19dKT\xb5\xf4\n\xb1^\xbcGv͊\xa2\xca\x0fSA4\x8a:\xe4D\xc1\x04I\x81\x06\xf9\xde\x03\xafʯ\x91\xf0\xe8<\xb8\v\x96!.\x941?\x1enT\xe5\x9e\xe0\x19\xc8z\x1d\ahԓ尵\f\x9fZ\x88B\xe4\x18\xaa\xf2\xa9\x17\x96\xce\uf1efCn\x16\xd2\ue5f5\x1f\xf9\xeeI\x9d\x9e\x00\xf8\x8f\xb3\x89~\x03+\xdb\xefs\x02m\xa68\xb9T\u007f\xb0\xc5=f!c\xb7(\x9aK\x1bH\xf7\x8a\xea\x83S\xad\xcb\xe8H7!L\xf0S\xbf.D\xc4\x024\xe7\x8b$\xfe\xd9~]\xcb\xdaٴa\xbcG\xe9\x02\x9as\xafi\bK7\x90\xc0\xe0\"\u007fdϸ}\xbf\xac\x89\xb5\x9a|\x9f{\x9d\xa9\xd8ܰQ\x927\xder-\x0f\xca\x1dy\xa7\xfb\xc7\xe0\x88̂z\xab\x15RaV\xce]\bv4t\xbc\x96\x9e\xa0\x8f\x8b\xbf\xb0\x052\xfa\b\xed\xe4\x04\xfa\xc2-\xe6\xc7讨YD\x8eی\x19\xedS\xd8@\xaa%_\xd1B(F\xd6\x13\xe3\xddHke\u007f%&\x045\xfb\xd5='\xbc\x1bjF,\xa7\xf7\xfb\x98\xbaG\xf5\xf4oW\xf8\x109\xe8;\xfc(\xefڤ\x90\xc1\xf7X\xd03z`\xf7f\x9dM\x83\xd4<\xb5~\xdf1\xea\xcb\xf9bR\xf46t\xa6\xe8\x140l\xbdu\xd6F\x9a\xa9\xbeIj\x11˯\xfb\xe3\xcb\x1eJo\xab\x88I\xccq\xa8\xb9\xb6Ĵ\x10(\x85\x1b\xaec\u007f\x03ǘ\xc7U\xef\xc8@\xf4\xba\xab\xdb\xd1\x1dѢ#\x02e\xcd&\x9b\xb1V\x95\xb3\xf7\xa9\xcc\xf3y(\x18\xfa\t\xf2{̧Ku\xa4\xcf\xe1\x98WK\x13e\x01\x94\x90Z\n^>(wDI\xa6\x19\x92\xdb\x12\x15߹}x\xc8\xde\n\xfe\xdaƺ\xe25\xbcgY\x80G2\xa6\x1c2\x0f\xc8\xd3\xfb&\x8b\xf3\xfb\x9e\f\xd3sσ!q\x19\x80\xca\\\t\xa1\xc1\x9e\xbc\xac\x8c\x8e\x19C\xddP%U\x8b\rfb\xeb\x13\xf5\x05\xf7S\xb5\xaf\xe5\xda'\xb5H\x85Lbi\xcc,\x9cs\xb5\xe4F\x03\xc0\xb9\xf46\xe4\x04\xa6\xae7\u07fcD\xe4\n\xe6\xa6g\x1ḍoGa)j\xe8S-&\x9d>7\x94\xd2y\xc6\xcf\xe7\x15\x1eCCΖ\bi\xe2]\x00MR\x1c\x93\xa0\xbb\fA\xf1\x1a\x8f0\x99\xf0\f\rKf\x93\xb4F=z\v\x8c\xc6g\x04g\x8a\x03\xaftf\x9b7\x17Kx \x90[\xd0\x12\xe5\xab\x1f\fL^.[\xb3\xa6ԭ>\xf3Z\xd1\x01c\xfb\x197\xe4\x16\x13\a\x8d\x8536\xfe\x16c\x1e͗\x99\xd7q\xcc\a\xb2w\xac\xb9*CC\xf9V<\xb7\xfc\x0e])E\x8d\x1d\xc39\x9a\x9f)\xa8ϛ\xca0l\x94SM\xeb\x1f.$\xf4bAS\x98\xea\x96Hib\x13%z\xec\xb2qݓV\x10\x03\xdd\xcf\x1b\u07b7ʀ\x187\xe4\xf6+8\xe0\xfa{\n\\\xe2H\xa6A\xe1Z\x1e\x00#[\x978\xc4\x05\xa40\x9e*\xc6\f\xd1r\x1f[-\x9csw\x1bn\xdbxP+\ue528HEl\xfb\x19\x87\xe5Y./\xddk6w\x19Kb\xae?\xfb8\xd68G\xf6I\a\x01.\xdf\vu\xa1\x01\x96\xb7r\u07bc\x99l9\x8f\x0fEiޜ\xf8\x01\x12\xa2\x86\x99`\x86\"\x82ƃ\x9e\x91\x8fȇ\x9f˺\xf3\xf6&v\xa1\xccI\xcc\xdb\x04բu*J\\[\xd5^\x03en\x06\xf4Q\x05%j\t?{\xebnW+\x9b\x10\xb31\v\x82\xb2Z\x80\xd8\xdcC\xab\xf2\t\x16\xd2$\xaf\xd73\xf7\xaf!\xdb\xee6\xea\xf7\x95\xa3\xfe/\xa5SG\xc7 @\xa94Ό\x1d\x19E\xb8!\xc3R\x1ad\x9e\x1e8hg?\x83\xd3J~\x11\x8f\x10\xa2\xb2u?\vZi\xa1\xbfD\x18\x83\x874\xa3\xedK{j%\x18)\x88'\x19\xb7\x05x\x9a\xe9Ma\xfe\xffYvkEt\x82,l\xd6\x04\xc5\x01\xfd\x13c:\xc9\xc3w\xe1\x85Xk||2\xf3\x9c\x85\x12\xeb$\xc2.Ey\x86=\x1f\x06x\x92\xc7*-LM\x9a\xb9_\x16\xbe\xb6\xefxC\x83\xd3\xd3{\x05\xc0t\xd3\xe7\x884\x02.\x9f\xa6<\x19\xa0\x04P\xf0r\xf1͙\x99\xe1\x11s\xb91\xb9\xbc/\x9c\x16\xd5N\x1d8\xb0uu\xa2\xbd.ӿS\xaa_r\x84j\xab]\xb8\\\xad\xa5\x9aav^\x95\xe4\x1b\xa2\xa9\xe1s\xf6\xf3Q\x11\x88\x9e\fZŜ\xf5-\xc5\xd4D\x8c\x00u\xb5S\xdd\x0f\x18\x8dg\x8b6\x83\x84\x0f\x86\xb2{${\xe6\x16r\x0f\xa62\xda\xcf\xf0\x00\xfe5\xf3>\xb6\xf8\x1b\x85,\t\x93\x87h\x0fc\xa6\x1b\x16b\xb1J֊\xcf?${ou\x95o>ͨ\xfe\xa4vCl\xa1\x92(\xb6\xc2<\xdc/0\xe7x\xfb(\x0eD'\u007faԧ\xd8R\x990\xdc\xdf\"\xe0o@\x9a\x86\xa8\xa4>N\b\x9b9ߖQ\xb9]\xb6\xe2\xa8\x1a}\xb8\xb7\x003\x81(\x8b z\x1e^)(Үe\xb4\b}E1\x17\\\x04\x02\xdfp\x1a\x9dB\x80(y\xfb\x95f̷\x13\xc5H\xf0\xf1\xcf\xc8Y\x84\xc4/HI\xfe\x8d\x00;,\xff\xa7\x94\bq«=\x95\xa7\xfc\x04d\xd6\xc6\u007f&\x02\x11T\xa6\x04<\xe5\x8d\x16\x99)3\bSfV1\xf3\xe5ړ'\xac\xfd\xd4vh\x92\xca\xcaD\xe6\xf4\xa4\xfc\v\xeen\x86$4n\xab\xb5\xb7'\xdcr}b\x120\xef\xe4D\xdbxo\xfcV\xea\v\xc1\x03\xd0\xfcU\x9dJgI\x1f\x18N\xfe\x89\x9c\xa7}\x06\xb2\xd64\x83\xbb/\x12\xc1\x1b\xee\x8a|ߥ\\\x94\xec$M\x95\x8cy\x81\xbb\"\x99j\xa6}\x05j\x8f\xb9\x00\xe8\x91\xc3i\b\u007fb\xd3\xf9!\xd4\x0fNӽ\x10SB\xb6v\xf3C9\xaewp\x9d7}\xea\xca5\xb1\xfd\xf8\xac\x84\x1eq2\xe8Ѫ\xe0\xc6Ҵ\x00\xf7UÍ\xbb,\xeb\xd5鼁\x1bI\x9d\x16\xf5}\xff;\xed\xddY͜ȝ\x94DJm[\xf0\xf6\xe5\xd0O\xac\xadsޥ$Fl\xe5X\x8a\x8e~\xa6=/_\x18\xbb\xe1S\xe9L\xec\xb1\xec\x17J\xc4\xd3\xdd&\xa8\xc7^(\rqwv#\xeb\xe3\xbf\t\x1e\xef\ua48e\xfb\xff.\xd1P\xdf\xd9\xe5\x16:bB\xf8\xb1f\x1aV\x882q\xcbgn\x89ٙ\v\xaf\x8b\x05l8VӅ\xb8\xb4b\xd1\xe5\xb40\x17\x83aG-OTlO=A\x8cf\x93\xb1W\xecO\u05edOJ\x9f\xfe\x1d{\x9b\xd5̑Ͳ\xb9g\xc6\xf0 k\x19:\x87\xe1\xf9\xedI\xfe\xe73\xe4\xcf*z\x10\x15\xe6\x9bA$\x11\xff\x87\xf3\x95\xbd̊k\xbeP\r\xc9`\xe6\xe6\xb3n\xf5FGx)\xebG\xffRPE%\xbd5\xc4\\\xfd}\xb7\xae\xda\x12\xc0\xbc\xa93۵Ruu\x9b\xde\bW\xe7-\xcc\xe8\xf1\x1f\x99\xfe\x98\xdf\xfa2\xd4\x14\xd4\xe6\xfb\xb9\xfa\xf4\xa5G\x89\xa2\x12\xfe\xab\x0f\x9d\x92\x8b\xf4\x1a\x0e%v\xcdoM\xff\xdd\xcdk x\a\x86\xefB\xf8uF\x8e\xe9N7ׂkV)12\xf0dB!\b4\r\xf9\xe0\xae.\n\x92\x8aN\x91\x038O,f\xbd\xfa\xa7\x12\x9c\u007f2TiV\ru\x8b\xb1\xe4\xc4d\xdbL\xf2\xae\xedzy\xa7\xf9\x1b\xcau\x98g\xd1\xf0\u0092\xcc;\xa6\x19Ks\xdf'\x0f^\xa4\xa7\x12\xa0y+\x83\x1a7UUO\x9d\xa8\xfbB\x99\xf8ж\xea+\xad$\xb4%O\xbb9elե*\x91\xdac@\xaa\x89\x05F\x89\x04\xf5c6\x14gg\x05\xf4MU\u007f_\xd9~1f\x84\xa6v\xbdV\xbb5\r\x89\x10\xf3-V\r\x9b\xdc0\x1d\x8f\x18\xc1 \x17)_D\xf1{\xbd\xf3Գb1\x1b\x19\x96#Q|\xe6\x83k\x9e9=\xad?\x87\xf7\xe4Po\x99\xc1\x9ec\xd6s\xbb\x1f\x1f\x83$&\xf1\x16\x9d}\xd3BoWT\x87\xe2\"M\xc2\f\xa7\xbc=\xa0Dy$,I\x9c\xd7\bN\x01,چ\xb1\tw\x99\x03I\xcdx\xfbE\x8d\x8f\x11\xa76\x90\xbdx\xe0n\xa1C\xd4C\x1f-\xa1\xf8\x1c\x99,\x9dϕ\xd2\xda̲Y\r:\xddy\xa5~\xb1\xccʝ\xcb\x17،\xa8\x8e=Y\xa2\a\xc3\xe2\x9ec\xcf,Txe\xa1qU\xa2k\xe7\xe0\xdd*O\xeb\xe6Tq\xf4\\\xb1E\x1c\xd8\a\xac\x80*\x84\xe2/\x00ؒ/\x89\xaaNS\xe4\u007fU\x1ef:\xec\xf5\x89\x96b\x8d\xfc?\xd5ī\xa7H\xd2t\x03$ٶUfu\x87\x86dH\"\x85\x1a\xa4$\xb62kQ\f\xcc/\x8b\xad\xcfW\x17iX\xfb\xfe\xcd\x1aN\x1b\xee\xb6\xcex\r\x1c\xb0r6\xfd\x9c\xba_y{?2\xc2\x17ڽ\xcd\xf3C~{\x93\xee\xdau\xd58\xe5\x99\x1b|\x86\x0e܁\x1c\xb9Sf\x95\xe9+\xe3\xa2\xef{\x06\x96\xea3\x17\x980`\xbbw\x17bcC\xf3\xd5\x0f\x91Q\xd8\xf4+zƪ\\T\x9f\u007f-\x0f\x1c\xe9\x1f{\xf8]\xde\x02\xa3ξ6\xd5Ѯ\xc2c\x91?\xc28\x96Z~|\x82&\xc4e\x13\xf3\x1c\xaeD\x9d\x969qW\x932R,Y+\x96\xbd\x13\xb8y<`Ow\xefA\x9abz6|\xb6]\xdc:q\xa7\xe4Z\xdfO\x17\xf7V\xd6gM\xf1\xd2̥ic\xcckJ\xcb0\x05\xc0=,\x9e\xfa\x89\xf2\xcc\xd24\xda,am\"\xd3\x1d\x88\x1a\xa6\xf4RC#\xf8\xdb,c\xe1\ff\xa2\xac\x1a\xcaZ\x1e6RcG\xbdŢ:\xba)\x11\x1fe\x8c\t\t\xfd\x9a\xc2eI\x17\xbd\x16\x1b\xddr\x0f6.\xea\x10\xf4Z;\x97\xcdP\xb0+O\xd3)\x88\xd3$\x16\\\xc1wI\xa2V\xec\xc2(h\xef\xf6`z\xb4{%\xcffp\xba\xfbxl\t}\xe8\xa5\x1ao\x9f\xd1n\x1br\r\xb1\x957\x9a%ӧ\xff\x1c\xb8{\x9e\x1f\xaf\r\x9b\x13\xe1x\x03\xaam\xa1\x1d\xa7\x06\xbd1oВ\xb3\xf4i\x83\xf1q\x13\xb0\xe4 \x12J\xd6\xca\xdcO\x9a\xaf'V!\x89\x99\"\xb7\x12\xb0=\x89$\x15\n\rї4\x84\xc2\xf8KS\xba+\xea\x91\xc2\xca&Z\xdaۙ\x83\x91'\xa4憥Y\x8e\x0e\xe0\xd0^\xdb\x16\xb7\x05e\xbe\x05\x02\xcc\xe6~\xff\xff\xf7}\xe2\x96,\x85\xc2x'\"s\xa3o߮d\xb1\xc7\xfd\xb1\x1f\xa3\u07fd}{.\b\xe7k\xe8\xc8\xc8TJY;ff\xea\x96\uf14e\xd7j\xa7KV\x92\xb8\x1c\xbcB\xef+\xd1\xf6j\xc5\xdc\x01\x1dqM\xaeWL\xaf\"\x9b\x1be\xf5/\x9d\u05f6\xbb\u07fbYf\x99\xfd\x8c\xf4xw\xa2I\xf5\xf2:k\xeb\x91I\xeb\xe8q.\x86\x87Dz\b\x9cdLWim\xee\xfc\xb8\xb3]\x1e\x00ɗ\xf5\xc2\xc7\xfb]\rf\xcf\xe2\xb8)\x82B\xe1\xea{l\xb2ֻ\xb7\xc1\x1d`\ue331\xcaj\xd6~\xa3ކ\xde\xe9\xaf;ā;~\xad7\xb1\xc5-zA\xc1\xef\xd8\xd7X\xc1'\xac\xce\x0etb\x9f\x8d\xd8\x1c\x83WO\xd5.\x98\xcb\x1f$\x00\xf4\x06GS0R\xc9\xe4\x13\x11a\xa1\x11#\xe5Q\xb8\xde\xfe\b\xfdP\xca\x1dO\xc9|\xdaP[\xaa\x9b\f\x86\xe3\xf9%`C\x12)c\xff\x9a\xc5\xc0\"\x17\xd6ͽdD1\xca\u007fxp_s*5\xc5ac<\x9bv\x1f\xfa\xc8P\v\x1f\xdb\xecc\x9bq`{D8\x86Shv\x10\x92\xec\x90i \xfb\xfa\xceW\x03\xd9\t\xa7\xf3w\xfapk\xee\xe0\xe6R\x12|\x18\xf6\xbd\x1aO\x9a2/n\xd0@\x926M\xe1\xb2\x00\xcdR\xe8իB|\xa9\x10\x98\\\x15Un\xa1\xa4\v^\xc3l\xafs\xdd\xc2\x05\xb4=\x10[{\xac\x97\x1e\xa5A\x03\xae\x05?\xb2\xf1\fzJ_\x10R6\x02\x04\xad\xfeSA\x83\xe5\xbf\xe3\t\xbf\x8c\xfa\x8f\xa0\xb5\xfco\x98\xa8\xffwn\xb6\x96\xfb~\xe1\xe1\x9eGK\x0f+\xf7(uhK\xb97\xd9\x03\x01\xfa\xe1\x14,\xb9\a\x06\x0f\xce\x16\xe7H\x81\f\xff⺔\xc7\xf9\xc8\x11\x87Q\xb8\xdb/\xb0\x9a,\xbf\xa7\xbfZ\x8b\xf4y\x92(NZ\xe8\xfd\x8c\xf6y\n\x88\x8b\xfb\xbaɧ\xb5\xb3\xe6\xf6e+u\xceh\xadC\xbe<\xc6/\xef,s\twy\xa8\xbf#\xf1j\xd7\xd8I\xe7\xf0\xc0\xa6诵\x9f{\xf1Ҏ\x12\xfe\xb4\xfa\xc9,ٿ\xf2%\ue943\xce`\x05S\"[;\xb4\xdd\xfd_~`\x1b!>\xec]\xfa\u070e\x13*\xce\xdat]8J\x99u\x1b\u05f7\x0f\xcduO\xc1\r\x89\xd5\x0eաH>\x88\x8a\bh\xefLkq7g\xe2\x12\xb8R2\x16\xf6,ʪ\x91\xba\x18Z]\xc6|\xa0\x90$\xacCZ\xa5\xac\xe4m\r\xb6\xd6\xee\xf4\xad\xc3\xf2q\xa2\x17\x03X\x01\xa7\x81\t\x9cLrS\xf9\x8f\xdfKb\x1f\x9f\xcc홞\xfe\x97\x12\xd3%H\xbe\x89\x85/\xba\xce\xcc\x1aw\x8d>\x0fG\x9b9\xc2(|\xf2v\xedv\x0f\xf3\xa0NnNvX\rN\nЀ`p\xbb\xd2\xc7\x0e\xc3\x19\xd1+\x03\xc9{(\xa4\xbbu\x03\\\x1f\x9c s\x04\x8c\xbbQ\xc8\xfa\xb3p\v\x87\x0eݨ3\xb9\x99q\xc5\\\x92\xf6͟\x9c\xe8$\x12\x19\xd1ﵧ\x8a;Q\xd8Sřz\xc1\x0e\x89[jl\t\x8a6n\x98 \x068\x85DT\xe4\x1b}\xb5\xe9\xb9㔨\xf6P\x8aE\x02\t%\x81\xbaBW\xf0\xfe\xafح\x16\x14\x8eY\xab\x1aw\x17\x85\xb7.\xc0\xed\x1d\xd1\xe2\xb4\xe5\x89!\xa1\x9c\xbd\xa7/\x14^\xb2mdSZ~j=\xb8*Qgd\xfd⨎\xd10t\xc3\xc6\x1c\xb1]\x86\x9f\x05\x05\xa2\x81\xc3q\x16\xc1-\xee.P\xa7JBp\x96\xea1\t\xe0ث\xe0\xc4at\xffl\x89/\xfa\x83\xf1y\xf2p\xd7\xf9q\xc6{~\xc7\xd1\xf8TOH\xd3\x05\x8c\xa66\xf7\x04\xd7\xfb\xb4\x1d\tu\x85N\xa2\xff\xa3wY\xb9|\xc3\xf6\nA\x98Vr\xcf\xecwD\bh4Kk\x96\x16\xa2\xf1\xc1+\r\xe1/\xbe@\r@\xd5\xc6O\x1e\x9c\xfb\a\xfd\x19\x94\xbeJ\xfc\xec\x01ZB1[\x87?l{\xf9JՊ\xfc\xa3\xad\x1bq\xa49Pv\xado\x00Y6\xc7CJ\xb0\x82\xba\xb8\x83\xf7\xa9\xfd\xcc\x0e\xd2\xc9\xe7$\xb5\xb7\x94H`7\x06Ei\x8d\xaf\xa8)*eK\xc1\xa3\u0602Y\b8\u007f\xa9\x97{V\xbb\xa2\xab)b\xa7\xd0\fpNv/A\xd9%\xe5\x82;\xddu\x1dh\x87(w̃\xbcl}\xa2*\xd14\xcby|uV:\a&\xe0*P;L\xf1\x18Q\xee\x99\xf3\xf3g*}O\xbe\xcdW;\x13xT\xbf!\xb8\xba\xd8\xf6F\x82[\f\xa5\xf9\xado\xe9\rl\x95\x88\xbe*\xfa\x1a\xb0\xa7\xb4\xc0K\x06\x01\x95\x84K\x87\xe2Uv\xb6ܼƌ٫NY4\x9b$Gd+\x843\x95$K\xc0VZ\xd1\x18\xbe\x0e\xb8F&\x11FuR\xf6\xa5j.GN\x9b\xacۖ\xca5ƴ\xca\bre\x19\x13vv\x16\x9f\x19v\xa5\xfc\xfa\x05Ȭ2M\xfa\xeaC[\xcc)\xcf\x18|\xc8\xdf\x01eGyb\x10\x18\xa7{\xf8)ڻ\x03\x8e\xbd\x90.\x15I{l\xda1\xa7\xa6\xfc\xa0C\xc0\x06\x97e\x85sZ\xe8t\x11\x8d\xc3h\xd1\xc6ɻ\x91Ræ\x01Gp\xc6\xc3\x157?\xbd\xb8(\xa7d\xb1\xc6W\x84^=\xb7\r\b\n\xa0\xc4\xd9&\xf5f\xf0V\x03\xbb\xf1͞\a\xb7iϟ\x12\\\x1b\xb4\x87\x91G\x17\xce\xf5\x9a6$\xde\x05\xb0$\x92\xb8uP=\ao\xe7u87\xbf\xd4\x1e\xbd\xec\x9d[\x9e%>`<\x15\x0e\x9e.\xf7\xcb$\xc7Mtӗ\xb6B)G\xaajS\xaeQ\xe3\xe6\xc6Ud\xfc`\x8e\xe8S\xb6\"\xb8\xa7\x1f3\xb7\x99\x1dɽ\x85}Mױ\xb3T\vth?\xf4\x177\x9f\xb1\x06\xed]\x9b\xe1\xd0\xf7\x84\xf2\x97i\xa2EH\xe5\x15\x89\x9fzş\xfd\xe5|\x15\xa7-\x9f\xc1\x19\x14\xd5td\x86\xa7\xbfۑ,\xfd:\xcbD\xcf\a\x9a\x1b\x92\xa9\x16\xd0j7l\xc1\xdd\x04D\x006٧\x17-\xfc\x86\x8f\x02\xb7\xd4\xc1+\xa7}ZU4\x9f^\xae\xe7xO\xb5ݼ\x9d\x9ff\xc5\xfc\xd8Q\x1eH\xf8\x1b\x14\x10\v\x8d\xc2\xeaU;\"I{\xf7)\x91\xdc1\x85\x04\x8dZ\xc0\xb0\xf8\x1a.\xf5\xd8\x17\xc5\a\xe1\x94\x12@\xf3\x88\x9b2\U000c45a9b\x03+q\xbaz\xf3V\xf0\x9b\v\x81s^\xc6>\x9f\x1f\x1e\xaf\x04\x02V[ŵ\xa5\xef\xc5-\x1f\x185\xcev\xbe\x80\xa1\xb4\xf1]蚮\x96\x97\xdac\xa6\xfb\"\xfc\x8d\"f\x88\xf9\xc4\\\xa5߬\xaf\xe3\x9c<\x8cۋcy\x1e\xad\x98#\xb6\xc5Qj\x9f6dr\u007f#\x01\xa7ȑ\xb3\x82\xde\x14J\xed\xcb4l\x12\x1a\xf2O\x16\x06\xee\xdf\xc6(y\x0e\x87\x16\xefN\xf5\xcd}$m\x8e\f[\x9a\x04-\xcb|\xf3\x93Ԉ*\xfd\x11\x1d\x94S\xf1\x8a\\\xf7ќ\x8d\xfe臉@\x12\xff\v\xcb@\n\xdd\xe0\xa4\b\xa8ie'\x9am\x82\x9f'q$\x9d\xb4\x12s'B\x03\x9a\u007f\x81\xc6\u0a7bA\xa9\xb9d\xf3)\xf6\xf0.\x06\x88*\t\xf9_y\xb6\x8e#z_\x17\x19Ы_\xee\x0f\xcf\xf1\xf4{\xbd\x13\xea_a\xac_=+䊒ӌϞ'P\xf4ܺw\rG\xd4J\xee\xd6l.\x8b\xba\xfdr\xa8q\xfbZ\xc8v\x1fD(\x9dDC\x19\x18G\x8f&\xe3C\xe9ر\xc3!\x11\xa1\xf2\x93\xaf=\x92ǣz4\x85\x82\x8av(\xa3$;\x03\xbc{\x06\x88\x1b2\x1a\x14\x14 @\x8fiǘ\xfcu\xdf\xee\apc\xd2E\xc3\x14\xec\n\x1a\thh\x00\ts\xe4\xcf>\r\x13\x81\xd6L\xb6^\xeb\xa2f\xefڻw\xbe\rTWޟ\xb6\xff\aR\xd9\r\xcc/_\xb9I\x1e\xaaĦ\xc4M'B\u007f.\x9e\xb4\xd8,P\x9a-\xe8\xfaH\xcaj)\xfd\xfe%P\u007f\xf9\xbd\xbe\xf4Dp2\xdb\xc8^\xed^w\x8d`K֫\x81K\xe8\x03Pa>ξ\ufae5jϨg\xea\xf8)\xfdKS\xfc\xd3ټ\x12\xeedGFYG\xcc$\x93\x15\xaa\x95\x15\xf8X`\x88\f7%\x92Ҁc\xb2K\x91\xdaQ\x16O\xb9\x1b\xc1\x8f\x81\"Bա\x1bB\x95'\xab\xe8\xb6^\x10.\xeb`\"\xf0\x86;\xcaG\u007f\xa2\x06\xd3\xff\x80\xc8leԒ\xadO^l\x89\x83:\x99Q\x19\xa1>\xec45e\x1b\x16\xf8\x1a=[7$z\xbd\xa7\x05\x83\xb4\u007f\f\xe1i\xbf\xe5F\xf0\\*B\xf4\x0e\x14'ǝ\xc6A\x96\f\x05ko\xde\x02\xb8\x90\xe7MFc\xbc\xc0\xdd\x10\xbe\x84\x85\xa7\x02\x173|\x80Ӭ%v\xf1\xa2>!\xd2\x1c\xb4\xaf]\xd8\u0080\xa5\x9f\xd8\xf6\x89'!\t\xf0\x1e\xef}:xi\x89\x17/\xeaxcR\xb1^\x01W\xa5\x8bI\xab\xf6\x86\xb6\xdf\xe1C\xfa\uf6e6\xdfz\xe4\xf3_`~c\xfe\xa2\x9aV\xa6\xefFvf\x8f]5On\x14\xfb\xacC\x82\xf2?\xc5\xddҷ\xba7\xac9\xa1\x9f']\xa6/g}\xa1\xe7\x82փi\x04\x81\xdeUIȃ\xa9\xaeO\x92t\x16\xda\xf0\xbe\xad̒\x06\xc2?\xcd\xd5k\x8c\xe7\xe7\x17\x02:\xd2\xc8\xd5\xd0[\xf9\xdc>TSi\xc3\xf4\xa1\x10\xa0\x04\x98\x9b\x9c\x1cE<7\xf7\x1aE-\xefN\tؐw;\xe7mD\b\x8d\xafu\xd6\xc7\xe4[\xc5\x1d\xea\xca\xc5\xd3\x06z\xc4+9\xaa\xc9g_P\x1aO$\x1f\xde\xcbUYN\x94\x81[\xbb#j\xad\xa2\xdc\xfb\x0fI&\xf5\xc3\xd33\x96\\e4n\x85\xcf\xd7\x11\x85)\x1dRvcx\xeb/\xf4V\xd2C\xc4?\xddK\xf5\x87\x9d\x89\x9bg{\x1fG\xb9\xfaX\xf9\x92\xb2\xb6\"b\x17\xac\xe3(\xe36\xce\v\xbcʛ\xbd|\xba\xa9\xf6\xf2\tR\xf0r\xd1I\x9c\xc9\xe0&\xc0-Nձ\xdc\xcf\xf9*\xa7?\x8e\x862BpEYP\v\xd1\x17[\x00\xa4\xdd.\xff\xa0\x85r?\xfdgO\xa3\x01h\x92\xbf/%l\xe4\xaf\xfdRO\xb8\x13E\xbc\r\xbaf\x1d N=d&\xfeu_qb\xc0?\fX°\x82\xdc\x13f\x04:\xd8\xccJ/\xac\v\x93}?(u\x8b6\xbe\x8b\xa3\xcfP\x98\"\xf8\xb3L~\xd7iV-\xdfg1\xed\xe8YB\x1cg\x82\x06\t\x14\x98\x1c\xdc\x18\x14\xfc\f\xfe\xec\xa4}\x17H\xa4K2\xa04\x05鵖r)\b\xa9ۡ\xc8#|ti\b\xeb@@\xa3\x1d\x86J\xe6R\x18[\xc5\x19\xc4k\rx\xa6\x93\xc9\x0f\xd8c\x02\x03E^\xf1\xe0\xec\x0e\xf2I2߸\xb0dVo\x95qP\xcd\x1f\x87\xb9kZa2\xdb\xd7H\x82/\x8e\x1b=(\xb8c[lW%i\xd6\x1b\xb8\x1f\xa3\xb7cX\x00\x82c\x88\x0f\xc1\xa3hP\xc6q\xb7\xd3\xe96\x89\x12\x19c\xfcM\xf2?\xbe\v}\x1fiSh\x87Rm\xd0\xe8]\x1e\xf9\xd0\x156;\xf4\xa8\x9f\x96\x8d\xda?'\xaf\xac\xf2\x04\x83B}g\xc5\xd9M\xabm\xf4\xeeǞ\xeb\xec\xcbCj,v\xb4\x9c\xcfԱ\xaf\xa0>\x89\xfa\xa7\x0f\x97G\xf0\xc0\x16\u007f\x92+zYl?G\xe8ܦ*{\x82\xb1\x12.\xecm7\xe4A\xc7T\xaa^1D\xe5\"\xa5;R\x8eUr\xf0\xa0\x10\x84\"bh\xf8\xa6\x0elqw$\x92\xb1\xd4/gy\xbeR\xbfmZp\x87%\x14\x8b0B\xce\x15ϝ#4\xffb\xc1\xe8\xe8\\q0n\xed\b\t\xeeN]M\x89<\xbeq\xeb\x88\xceN\x97\xd4\xf5{Ԉ\x86\xcdh\xcc@\xfe\xfc1?\xbb\xdb~\x8bt\xf9\xf2\xc0\xea\xc26͜\xd8\xda\xc1T\x9d\xb8k\xa7\x8e\xd5̆\x0f\x1eҙ\x19\x0f҇\xfb\\M\b\x00\xe5|\xc0t \x10\xd35O<4> J\x0e\xe7}\xbe\xde\x19,\xffQ\x1drQ*ͯ\xf6\x9bA\\\x15'\xf5)y\x05z\xd4'\x86\x1d\x13Kdخ\x06D\x8d\xf6Wdi\xbc@gzu'1\\\x15}\xbb^q\xc1\xe7\xcfI<\x06>e^\xd6h)\xc8Q*\x1e\xb9\xf4\x14lz\xad\u007fB\x80l?\vg\xdc\xf9\xb2\x01\xd4\xf7\x8a\x8b\xca\x03\xcdG\xd6\xeeZ\x9b\x1a\x1a\x82\xbe0`\x12\xb3\a\xe5~\xfb\xdd\a\xea\x939\xa3\xf7/ie+U\xb6r\xaf\x1fW\x97\xe9\xd1Ws6\n\x94g\xed*\x9dD}\xecz\x81yn+ህwUӋ։\xeb\xcdf\xa9\x14\x0fG\xbf\x03%!\xa9\xbb\x88L[#\xfe\x83\"\xd1h2\x99fmh\xff\xd2|Fqb}*\x95H\x88\b\xa9#z\x9c\xb4\xb1\x1fnV˴\x80\x99]\xf6xA \x981\x8d\xae\xa2\xa5m\x01\xb7\xa9\x1fk\xb1\n\tׂV\x12\x10|=\b\xc4@\xbb=\x85\xc1OB\a\xb0z\xc6P\xbcd\xf3\xc0\xc95Vrl$\x00\xbd\xa1\xd1ZՄ8\x90\xbb\x83\xb48^Ϗ\x98qp(:A6J5PY2\t\tèV\x1e\x99\xb5\x0e'G\x94\x89\xce\xccpe\xe6\u176d\\\xb6\xaahj\x93\x16\x1f\x1d\x0e\xa2\xd2p\xa11a\x84\x8e\xcaw\xc5ʓS\xb9A\x03\b$\x13\xd3|\xbdH\xc1\xf5E#7ч\xd5\xd2\xe0\xb4\xdc|\xfe\xd0p\xb2\xde\x1c*\xea\xca\xc4\n\x9b\a`\xe5D]Z\xf3\x94\x8cB-\x92\x80\\6\xcb\x13iW\x0fẍG\x8d\x04\xfa\x90\xfa\xfe\xef\x0eGG\x92\x10\xa0\u05ee~\x8dY\xbaJ\xa9\xbeT7Mq^\xc2\xe8\xba#\x820\x81\xa7\xe4\xd8õq\x87\xbe\x04\x1a\xf3\xd7\xcb\xeab\x8b0\x9aKVot\xf1[\r\x8a\x19Ֆm\x11\x0e^\xe7k \x17k\xb4\xba-d\xcbp\xdaݟ\u007f\xff\xaf^\fJ\xf4\xddd\xf63\xa2\xc7ݕF\x1a\x13\x8fF\xcfT\xe6Ϻۗ\x9d9o\x93\\S\xe3\x028\xe7\x03\x19\xd1qk\x10\xea\"\x81\x02σxL_:\xaf\xf8\x1cP\x9c\x8aLh\xc0\xa50!\xdc\xdeiˌ\x0e\xd4\x17{\xe8\xe88\x1b\xb5:\xb9\x9e\xf3zE\r\x04 \x18\xaeOy\xfa\xab\x8b/И\xe0l\r,)\xf3\xb4G\xbe\x86\xf2\xe7\x8d\xc5q\xce\xf2Q\xb6\x14\x98\x1bR\x19\xf3\x0e`\x81\xb0\\\x15\vJ\xf9>[\x8b\x1b\x00\x94\xa6\xa1ip&Հ@\xa1\xac\xf1\xad\x1a\xae\xb1\r\xae\xa9\x00$\xaf\x89\x05:\xc1\x10Q8\xb2\xa2\xa0Bt:@`{>\xbc\xa7\x85\x16'\xfba\xe7ޝu9\a\xe7\xe9\xfe\xf79\x10\xf0\x9e'\xa0\x01\b\xa9L\x1e\x12\xf9cи\xcađHh\x91\xf4d͞\xf2\x87\xeb\xe1YG\x8ff\x99\xa9\x01\x04\xb6\xab\xc1\x12\xd6\xdf/\x9f\tN\xe6\x02=\xf0\xf8Sf\xbb0T\xab;WJ&\xc3 \xc6I\x9c\v2\xb8\xb4\xf2\xfa\x02\xec31\x91\x00\xdbkÉr`\xc8\xd4}\xb2\x92A̶\xae\xfc\x99\xbe\xab\x13\x81\x83\xd4\xfc\xebd\b\x84\xbc\x94@\x1d\f\\q-\x10\x9e9(\xd6B\xec\xac,v\x11ѣ\xad\x12ALX\xc2q\xaaH[\xa9!\xc9f\xe4\x85-t|\xf8\xef\x9f\xd9\x04\x89n\xb9PΤR\x03^\xb0b\xc2\xec\xd5\xf8\xc3GO\x9ff\x85\x00=+\xa5\x8c\xa4\xe5h\xfe\xb4\xb0W\xf0D;Kf\xdex1\x82\xc8^\x17\vU\xab]\x923\xa0@j\xfcK8{V\xdb. \"k5\x02\x81\x0f\x1d\x11\xb0\xd1\xfeh\xa5G¾\xb5pC鹒\xb3*\xf96\x04\x06i\xaa\xd3S+п\xb8u4495\xeadj\x90+\f\x86\x9dKk\xb9\xd2Nq\xe9B\xa5\x19\x02\x05\x94M\xf0\x88\x13\x0e++?{\a\xc62\x13M\x83\x9bNJ\u007fV\xf1u\x10\xfc90\xd1$#dV\x9c/\xd5,)\x91\xcf\rAk0\x94Ƃ^\x1b\x8c\x87\x81\xf9F\x1c\x94ߛ\xcd\x05\xc0\xdc\xdcn\xb1\x02\x8b\xf7\xfb\xba\x94<%\xa2\x11\x9cJ\xad\xc0\x9c\xf7\x8cvq\x00$\x9c\x9b\xea\x1e\x9f\xc9\xe8\xafd\t@\xe4ww\xf5\xb8?\x9d\x04\xe5R\xc0\xfd\x18s\x90\nD1\xabF\x13-\xa0_\xf1E1}\xfdzc\xe3\x91\x16ƝZ\x8c\x19h\xd0\xf4[\xdc\x11\x1e\a\x94\xc6\xcf$\xc3\xfd\x9d&\x05\x00DWx\x18&fe\xc5%\xe9\xed ~)\t~\x8e\x12\x83XL\xb7t˛\xdd҅\x96\x8eJ\x06K\xdd//(\x97F[\f\x98KY=;\xca\x1f\xcaؕb\xff\xfa\x97\x83~$Vd\xb8]\x9a\xa28\xda\xdc|\xd4\xf7\x1a\x02\x81bJ\xd3\x16):v \xff\xfa\xa3\x183R\x86R\x97Q\xb4\x8f\x04}˺\xe4\u007fO\xed\f\xd6\tk\x06\xc5UP\xc4\xc1\x8b}\xb3\x9fSV\x95\xaf\xfd\f\xcc\u007fx\u007fs\x00Q\xcaro\xaf\xb1\x9f3\x1e\xa3\xffz\xe7\x902\x89F\x10\xa4\x8c\xd2\xfa'֯\xa3\xd8\xddnN?\xf0\x1e\xd7{\"]\xf51\x16B+\x1f\xc4յ\xa5\r\xe3\xb0;*\xab\n\x8a\xed\xd1eO]\xc2\xdd\xd6-\xce\xd2N~\xb5\xbf\xf82\xf5̜\xf2\xa3u%l\xc5(Z\xbe\xad\xbb\xa3\x9db\u007f\xa19M\x99h]Z\x1f\x0e3')\xa2\x149\xa0#\x87>\xcc*\xd7\x03\xe2\xa7\xfb%\xfb)\xb7V`\x10leY\x17\xee.5*\xd0\x0f\xd9\x0f\xa5D~\xea-\f\x89\xf6\ad5J\xe1\xc6Z\xf5!Q\x03\xb5\xb9Ӧ\xa7^f\x01P\x80\xa3/fj\xbe\v\x81T\xcaX\x91\x12\xa2\x04X&(f!\b\x88Ý^\x13\xf2\x98g/j<\t\xb3/\xda\xc7륃S'J֓5\xe1V^\t\xfe\x95\xfaߟ\xbf\x9e^\xc4\x19m\xbc{\xa0\x8f\x062\xbe\xac;\xa2\xa5\r0i7\x04$\x01\xe0\x0f\a\x16&⩵\xfeӵ\x8e\x8d\x8d\xab\x8fXEOS\xe8\x1f\xc0x\xc65\x0e\x1eDZ\xbdيt\"\x8e\x15h\xaa\x11\x88v\xe7\xaf_C\xda\x16\x83\xe5S\x95\x9b\xbd~A$\xba<\x86@\v\xd1\x01\xbc\xe5\x1ef\x8a\\;S\xcca\x8c)\x9a\xa66C\xd1\xd1_\x9d\xde\xdfΊ\fg0(4i-k\xc2\x13<\x05\n#5t\x8b\\CC\xa3\xab\x19h\xf5>\f;\x8f!`\xb6\b\x90\xa9\xa8\v 3\xaa\xf3-\xd7\x00\x056\x95ht\x0f\x89D]\x1b\xe9S\xb0eN\x87\xa8\xea\xeb\r\x8d}\x83}\x8d\x8d\x8b\xa8\xe6\"\xad\xde\xce\xcf\x13#Qn\x93\xb7\xb2\x16\xba`F:\x9f\xc3\x19\x10\x04>\x15\xa179$lV\xda\xcce~\xcc\xcb\xc7̈\x16J\x10a\xfa%\x93\x89q~\xd6ܣ\xb3˴\xaf\xa7^\x0fl\x87C\xb1\xf4\xb3\x03\rf+/\x0e\xae\v\xb9\xfe\xe1\xb4eBa\xbb\xe8\x13<\xed\xba'\xb4 \\*\x00F\xf3\x99C\xdb;\xad\x8d|\x1c\x84c\r\xb7\x86ڀ\x98N\xba\u007f\xf6f\xbe!\xe0\xff\xf9L2i~\x81<[\r\xc5\xd8\xc5\xedp\xeb\x84\b&\x9aѕA\xbekn\xeen\xba\x90r\xbe틧\xfd\xec\xe9\x1bn&\xbbf\x17\u007fv\x1cnjn\x94-\xcd\xe82\x1f\xdc5(!\xec\xd1\xf2\xa2\xf4\xd5\xdd\xc4rC~\x87\xe4\x8aD\x04\xb2\x97\xcd\"`\\T\x9f'j\t\xbe\xccP`\x920i\x1a\xdcO͚\xe7\x9dF\xeckrf\xeduə\xf7\xa2کj\\'\xaf\x1e3\x83!B\xbaIEl\xdfQ?\xa6\xf3m12\xc4pQ\xf5\xa5\xa5\xe2e\x8b>\x96\xe1R\xc7w\xe7تD\xe7.ۋ\xd0\xde\xe4\rXN#\xb1'N\xa2\xa4\xcajj\x01\xa2\x91о4\x17\xd5!\x06\x99\xb1tK_\xbd\xe2\x93\xcbf\xa5R\x80\xc0!@棼C\x12\x94J-\xa8\x12ja\xc9\x1eH*\xf6\xcd\f\xe3\xfb\x8d\x81\x9dN\xee\xcbp\xb4@w\x97V\xb4[;\xbb\xdd\r\xdd\xea➄s\xecq\xc2\xe5\xd1H\x8a\xa4l\x81\xb6ڜA\xbb?\xd4\xfey\xad\t\"\xbaj\xfd!\x80\xe4\x84\xd6<\u007f\xdcU\xb4?\xddh\xbd\xe5\xc0\xe5\u007f\xa6k\xe1\xa71\xb1\xa6oa\u07bb\xf8\xdb\xd6e\xe9\xfd8S\x80\x9c1\xf7Н䋄\xb6\x04\x8c!\xd3\xf3\xba\xe29\x93h\x85\xd8I\r\x18\x87\xd6B\x8e\xeb\n\a\x119K\xabo_(\x00[\x01f\x190\xb7\xb8\x88o!\xfc\v\x85\x92\xf931\x1d\x9eC\xb7\xbe\xc1;X\x0e\x96Ih$\xa5ɀ禹@\x84\xc5@0Wl\r\x1f\xdd\x11]\x86&)s6\xf0\xe0\xa84w\xf1\xa4Y\x903c.\xfc\xe3M\x02g\v^\xbc\x14\xc81\xd2\xc7\xe9\xb9O\xb3qs#Ms\xf1\x823ZNLMi\xf6}\xf0\xf0\xc6\r\xa19\x8d\u007fU\x82~\x87\xe8\xb3x~{\x16\xae$6\xba\b\x9c\xa8\x03F\xd5ɬQ\xafEi\xee2Wv\x86Y\x14F\xa6\fA\xdb\xe7\x9eV\x10l\xa8\v\x14\xb8\x06\xed\xd6\xe4\x04VDXer\xe2(\xb4\xc0\xb0Z\xbae\x9e\x92\xebͰ\x90\x163)\xdb\xca\\t\x00\x1a\x8a\xf95\\^\xe1\"r\xf2\x19Ш\xe7s\xb3\n\xdcw\xee\x1dP\xe45\x8e\xaaf7\xa1N\xdfK$f\xb3\xc8^q{\xb0\"L\x8d\x9d\xdc]\xaf\x88\x19z`@\xf8\x95DQh\x97\xcc\x04\xe66f\xb2\x9d\x16\x95\x1c~h\xabG\xa95\xc6uU\x047G\xe4\xf0\xed\xeb\xb8\xc4\x15~\xf7\n\xc1\xb8\xe3\xf5.\x88#3\xf1P\x13\v\x8e\xfaTV\xca!\xbc\x1fn\u007f\xac\xaf\xefژ\x05Pf6\xa0Չ>l\xdb6\t9@\xddҖ\xf7\xa6\x99\x1e\x8f\xa5\x02\xe05Ϛ62\xf2\xf9t@7\n\x9e\xe1L\x8e\xce2\t\x1d\xf6\xd7\x17 t\xfc\xf5\xed'ԯ\xc0bH\xca\xe4\xefԼ\x01\x88w\x12\xcb\xe6Wf\xf2\xf1\xb7Ɋ7=\xcc\xdb\xfc.=bx\r%\x05d?\r\xba\u007f\xc1\xee\x8da\xb3\xca\xcb \x929\xe0e\xffp\x90\xa7\x8b\xbdH\x8fҩ\fK\xc8\xe5\xf7\\\xf0\x90\xc5ۏ\xfb\x9d\x84\x9e\x84\x8c$\xa0\xb0\x98\xe3\x9bC%\x960\xa8\x96\x1c\xc1\xe6\r\xc9\xc6\xca\x05\xe6\x83\x01\x9d\xddntv\xa3\xbc:\x80\xb5M\xc8`᳑B\x84\x8d\xdbasp\x93&\v)\x02\"-\x12qc\x83\xa3\xaa\t\x9b\x93@\xa9I\xe2\xc7\xe8\xf3bk\xdc\xd9\x14\xb9\x9d\xa9\xcc\x053eP\x9c\x16F8\xdd\x01\x89ZmUL(\xd9\x11(qP05\xbf\x19n\x19'\xbc\xa5\xc1C\xf8\xc7\xc2V\x89\xb5\x8ai\xc0\xb5\xaa\x9b\xda\x12\x93\xa1\x8ej\x95\xd0\xf0ɿ\x04X?q\xe9g^:ӛ\xc0[[P\xb0V8\xa9\x9c\x19\xb2\v\xa1\x9f6\xa4\x00\xda=Iɉ(\b\xb5c\x06\xcdG\x96\x9a\x15\xdb@\xa9\x15\x17\xbd\xe0Lb!l\xb7l\x05\xdc\xc68߬Mv\xda\xf9\xfa\x9dvVb\xf5q\xdf~\x15\xdc\xd2\x12/\xa4\xda\xcd%\xc7Ii\x1f\x88\x85\xc0\xb6҂ϡ֣T\xf3\x9d=\xfa!B\xb3\x83\x04P\x06S\x06:\xa6m\x9du\xacv\x8b\xd0\x04\x05P\x04\x9f\xc8s\xcfϥ\xef;\xa7\xbf\x17\xa6\xcfZ\x04|s,\x16G\x85\x0f\xcb:\xe0\xc6pH\xf6\x8a\xafg\xd9\xe9V\x15u\x80\xa3\x17Z\bR>f\xe6\xcc@\x9d\xe2\x00e\x14⋮@F\xc3<6\xe3Ͳ\xe0.\x1d\x05\x93\xbaL\x81\r\xd6\x10/\x81)\x93X\x94\v\x043\"LN>\x94^\x82m\v\xd4\x14\x98w'\xd6\xc1\xe5\x15\x95\xa9>\xdb\xfa\xd4\x1b\\\xe3C<\xeb\xf1\x85C\xfdKb`\x9e(.\xf6\xeeu\xd6\xc1ְ\x9a\x81\xa5\xd6T\x9e\x18'\xc8 \x82o\xef\xd5\x06\xf3MG\xf4\x98{\x1dx\xb8$\nv\x06\xac\x1b\xfa9\xef\n\xab|\xe8\x9eF\xb0\x8d\xc9x\xcaʀa\x10@QI\xdb֧\xf3'\xed\u007f=\xcfz|Q\xf9o\xd8\xfc^B\xecf\xd3\xff\xa2,\xb3Zf\xfaW\xfa\x014\xe6\xa9#\x9d\b\a4\x93\xcey\x81I\xac\a\x95\x989#\xdb\xf45\f\x0eZڭ\x0eE\f\xa7\xd9\xe6\xc02\x1d\a\xe2\xb7p\x93'\xc6\xdb\x16B\x87\xa4~\xbb\xa5\xf6U\xb8j}\x15\x1aۣ\x10WwE\xfd`\xee\t\r\x11m\xf7'?\xe8\x16!\xac@ \x00\x80\x83C 2C\xd9\x1e\xf9\xd1\x00pc\xad\xc4\bl\x13ݻOš\xd8\x1f{(\xbeC2\x0e\xc9\xe0kC\x89\x83\xf1\xd1k\x81\xf6'\x8cU\x98\"\xae\x16\xe9C?\b\xbf\fT\xf2Q^\x90\xee\fڝ\xb8\xa4kK\xf1\xe6m\xc53\xce\xe9m\xec\xea$\xba\u06dd\x16\x81\x00\x8e\x1fͮ\xb0\xb4]\xe0\x89\xcc]9b\xf3\xb0\t\x88Jn\x12\x99)sn\x9ct_\xf2\xf2\x19\xeb_\x0fxEK\xad\x94D\x94\x06 \xa5\xbcB\t\x99$gY\x98A\xbfV>\x1fg$\x95%L\xd00L#\xee\xe3{\x1b&Ν\x98\x96Ft\x0fd\xeb\\\xc0\xa5P\x94\x13=\x9c\xd2\x1ba4\xe2\xca\r\x8f\x1f\x0f8\"\x9c<ܝ\x8a\x8bs\xfcL^^N\xdf\xdc\xf2Ec\x9c\xc7v\xb9\xac\xedH-_>\xe9\x97\xf2\xa7\xf1\u058b\xd8\xda\xf8\x02;|+\x87c\xa9\xc8!\x8b\xa9\x04\xbf\xb6\xbf\xde\xea\v8\xbe\x1fO/\xd2.规\xc1\xf7Jn\xc68\xbc&\xa8,\xec\x11\x81\x96%\xebs\xf3t\x90]6(\vk\a\x96H6\xf1\f\x89Fq#(ۉ[\x96y\x97\x8c\x8f\xa6{\xfb\xc10(\x93^\xe0\xca\vֿ\xc1b\xd6\xf8\xebף\xacŬ\x0e\x03\x83\x92\x80\xe4\x17\x14\x00\xdc\xda\v\x9d\x9d\xfd\xb4\x91&f\x86\xcezCqI\x89\xcf\x12\x10\x15\xdd<Μ$\x92\xab((h\\\xd2ED\xe9C\xd1\xf4\x1f\x01\x1d\x15\xad\xdd\x1c\xdc\xf3\xe9c_\xe8x\xf6/\xc1\x83E\x82.:\xe4\x1c\xfd\xfci^\xf8\xc6+\x8aΟ\x9e1\u007f\x18צ\xc9\xf3҂Ji4@`l\x87x\x10N\xefL$搘6\xc3\xd2T\x97\xaa\xc5\x12.\x9d\x96?\xd0\xea\x8f\xe4\x0f4\a]\f\x8dX\xa41h|}g8<1Ȥ<\v\xee@K\x02\x8d\x9a/\x16\xc7\xf5\xa2/\v\xe3\xa5\xd15\xedp\x01\xc8\u007fל\xcao\xeb\xe8\x91t\x92\xaep\x87\x10a j\xe9\xa5t\xe0bE\x91\x12\x9c\vE\xa9y\xf6\xb7&Ц4`د\x11\x89\xbd\xb5$\xd2L\x87\x04\x9d\xf9\"\xab\x85\u007f\xad\xe7\x83Jvi\xbe\f\xc1l\xee\x00j\xa3Z%=')\xbe\x968\x96\x18e\xe6\x88\x14\xc8\xe1`8\xfcT\xca\xe1\xc7\xec*\x8fM\xe78\xf5\x9b\xbb.\xac\x80\x87\xbb\x8d\xd6w\xb8~\x0f\xc9\xe2\xf5\\(H\x94t\xa4v\vr\xab\"jDo\xbdG\x10G\r\xe6\x98i\x03\xfe\xb2\x84l\x03He\x8c\x86%ia&9\xb3d\x89\xb6\xcf\x1cd>\xf3\xfc\x0e-i\r\xb7lM\x12\xee\xbeܰ\xd9\x16\xa1TA\xef\x94$\xb6\x9cVHG|\xe7\xb3\xec\n\x88\x17$\xc7\x0f\v\v\xea\xf9:\x1d\xc7\x121R\f\x15s\\\xf0Z \x14$\x91Pj\xd7ۇ\xbd\x8f]ً\xc6g\xd88`簆 \x16\xfbzߒ\xf2\xa4\xcbV\xd4X\x8f\x19\x15\xa3ݕx\xbartX/\xa0A\xd1p\xd72\xb4\x0f\b^[1~R{\xef\b뚬\x97\x19\xb2\xbcɇ\xa2:k\x0eC\x11U\x12'5n\x98\x90\x1c%\xa1'\xd7CX\x02P06G\x83ۮ\x91\xfbl[\xe9<\x96\x1bN\x83scOFeQ\xfa\xca-\x92gi$\xf3\x18RN\xe8\x13\xb2\xfdo\x197\xd2Wz\x9a\r_t\xba\xb9\"?\xeaz\x83\xf66\x8c\x97\x04\xbb\xcfy/H\x16\xa2}\xdc\x13\x1e\x92\xf1ё\x83\x02{qL\x81\x1d\x9c\xf1$\x92\xf2\r\n\xe7\x9c\xe0-\xc4\xc4\xf4a\xa1\x8f[\x8e\xfe\xf9st\v\xddnS\x8cn2\x8eğ\xe0@\xa1\u007f\x92\xee\x1aѷ\x8a\x8d\x0f\xbf\x0e\x0f\x8f\x8cxHNp\xc1\xa2\x89\xc7\xe3\xb5\xde\x10\x1e\xd22\xf1\xd0\xe3\b&\x04\xe1\xe9\v3\x03\t\xa7\xf1\x84\xf3\x91\xcd\xc0\xc6f\x8c\xf7x\xe3\x0f)\xe7\xfa\f\x11\x01WP'h\x827f\xf2>\x91\xda \x96\x02s!\xa8;\x15\xfcp\x81\xd5&Q\xfd\xb1\xb7\xcec\x0eN\x1f\x04>OgdH\xf7E\xde1u\t\x10{\x84\xcc^\xcbگ\xfcV\xe4}\xf1\xe8\xe32\xdb@\xe7J\xcaH\x05\x95S\xe1\x16\x1e\xf6\x14>!~\xf5\x16\x9bL\xe9^d\x1b\x10\t\x98\xe6r\xc0\xd25/\xe7\x1d\x90GyN\x0fW\xfe\b-\xf5`\x05\xa4\xb1\x1b\xe2\xe4ɚLJ\x03\x9e\x8b=\xa9\xd4(R\xa5V2\xc3ȏ\xecM;\xbe:\x8b-\xcf\xecA\b0<\x19Ȥ\tL\xc3\a1L~\x18.\x81\xac\x1c\xb5ܤ\x10\x89kg\xc7\xcfLinN\xf1d\x9b\x90\x8f\xf3\xbdu'\xad\xf2\xd2\xe3f]\xa1\x1e\xf1\xb5B\xfbs\x14L\xb7\xb7A\xc9\x015S\x87h\xd5K\xa3v\xe6\x9cvn-\xca_e\xbc9\x10e\xf5V\a\"m\x93\xaf\a\x89B\x12:\xefG\x03Ϋ\xbex\xd6c\xf5ZX\n\xae\xbe\xc9o\x1a\x1b\xe9\xday\xfe\xe5\xf9\x00\xf2\xc2H\x10KgT\x9c\x1c~cN\a¸\x17\xed\xe2\x00\x87OZK:\xdcb\xdcA\a\x80%9C\t]\x9d\xc3o\xf2\x15ʗ\x17\x98\xb9\xc8\xd5w\xdb1\x8d\xdd)\x1b(\xa3t\xb8\x98^\xb2?\xb1\x81u\x80Ʀ\x98\x03-\x14A\x01\x9d\xba\x8a9\xaf\x19\x8b\xa3\x00\xa7\xe99N\xd6ل\x1dL\xad\xf6\xc1\xbd#\xdaA2Yu\xda\xe9\xe1\xe1\xbd5\xc7\xc6/_\x1e\x1f=f\x12ql\x14\xca\xc7j\xf6\xc6\xe5އ\x88\xbf\x11\xbeˡ?u\xf6\xfdAr\xa9Z\x99\x9a\x16\xcd\x02\xd1]\xd3A\xebX \x14_\xd5v\xefM\x0f\xbc\x8f1V\x1c\x96\xbf&\x19P\\\xef\xea\x1d\xc56X\xa3\x932\xeb\xeb\x9dm7䥱[lҏ'\xbb\x1eA\xa9\xc9Q6R\xbb\vS\v\xedQ}\xeb딭S\x87\xece\u007f\x95\xec\xefS\x1c\\D-wLrTC]\xb9ӎorly\xbd݂X\xa5\xcd\xdbJ^fo\xee\xa3\f-\x8a\xe9\v\xf0˰\x87\x95(\x95X3\xd3R>\\\xd6#\xea\x96\t9\xe2\x16\x89VP饘QՐۑ\x1e,\x1ea\xf9e\xa4\xcf\xe4\xa6X\x9b#\xbf*\x85\xdf\xe7gV\xecTnq\xf9\x8f\xcdGL\x97(\x98\xc2Z)\xd3o\x84M\xf3\xc1i\x8e\xe5!#Z\xd1\x18\x04\xcaH\x05.\x83\xd0$\xf1\x89ɀW\xfa\xf5\xb4\xa0\xaf\\\xb0p\xa9\xb8\x15\xd0*ȶ\xf5\x8d\v\x17/\x0f\x94\x95.g\xbe\xd2y 9\x93\xefL2\x97\x81\x1ap\xa7(\xd1#Z-)i\x8e\xc1\xb8\xf3\xb5\xddj\xeb\xb4\xd2jԭ\x19=\x1f\x1a\xbc0b\x93\xf3\xf2\x1b`n\xa30\xe0a]\x9ck2\x93I)\xbaX\xedE\xdb8f\x96nD\xb1η\xbb%8\x92CS.\xdeo\xda\x15\x8bě\x18\xf6Ng\xdf'\u007fd\xddp\xad-\x9d\xd9J\x93=a\xba\x8a\xbdY\xabɹب\xa8\xfcNk\xb1Y\t\xa5\x91Ե=\x81\x9a\xd4\xc3\xe3\xf5\x17f\x85\xe7N\xa5\xac\xec\x03H\xce^\x95\xb9\x03\x9f\xce\x19\x9ff\xe3\xa8<\x96\x97\x1f\x89(|\xe2E\xd3\xe4(SL\xb5\xd7\\\xf9\x9f\x8a>\x9cu\xc24\x0fvdN\x97\xe9\xbe\xf5~\xb4HN\xf2\x00\xab[\xb1nD\xc4\xcc\xcaeh/ڈ(2\xa91\xa2he_ʔQnV=\x91C\xe4\xf6\xf0\x92\xa8\xf9H\xff\x1c\x1b\u007fE\x8d\xefg\x1bi~\xa7%\xaaB\xa5\xe315\xfe\xc6\xe0czŕ\vv\r\x81>a\xebY\x80\xf1%\xb1e\xbe&c!\xa0\x89\x15pIB\r\xcb8г]~A-l\xa164\xc31\xa5\x98\x92/\x15\xb0[\\\x12\\Z\b\fI\xf5\nT4\xd0\x1cW\xb9\xf9\xf0aa8'l\xfa\x88xRY\xac\xdfN\x92e\xfb\x00\xc6j3:\xdc\xed-\x06:G\x90\xdb6\x17\xe5\x19v\xd3\xe1ad$$`\x83M,ܔC\xf4\xf6\xbe\x04z\xf03\xb7!q\x861\x93\x98\xf7\x8f\xe0]Ӌ\xd9\x17\xdfn#x\x8e\xb5B\x0e\x8d\xca\xe7\xc6\xc0l]\xb8\xbeK\xc3^\xb7\x0ft\x0e\x95\xf4\xb9\xd6\xc2\x13_@Y\xd0\xfc\x17u\xec\x9cgS\xfa\xb5k\xae\xe7]\xd0\x02OƤ\x97&v:\xf5\x98\x1fN\x83\xdba\xfaL\x19\xd8\x11ewɋ\x9e\xf5-hY}:\x1e\xa8\xc1xi O\xb9\x05 \x95\x8ax|+^\x8bñ\xb2Cq\x01%\x84\u007f\x03\xe6\xb8]{[[\x1b\x1e\xd8q\"\x9a\t\xdcx@L\a\xd6upՔ\x1d\xd5\xc3j\xbb\xea\x83\v\v\xdc\xd6-\x01\xdd\xe8\xa3[=\xc9\u007f\x8b\xc0\xf5\x93ئ\xb2\\\f\xf1e\x89\x8bjq[\xfe%\xef\x1c\xb3^W\xa5\x87\x94\x1a\x19'\xe2Hj\xd3y\xc2c\xea\xf4%J8\x8a\x18Imx\x95\xc2\xe5=\xe8C/\xfa].&\xc1w4\xcdD\xde\xc3,Ƙ\x17\xce\x13\xca\x1c3\xf9\xb9\xea\x94\"\xb6z\x96\x14\x9f\x83`\xc5U\xa9\x90\x16\xea\xc1\r|M:\x1f3Qc!\x93_ǣ\xe5\xf1W(\x01\x18Wj\x87\x90\xd3\xf9q\xeb\xd6S\xf2#f(G4GޗI>\xb4\xa7\xd4\xe0\x85\xe4n\xd5ڄE\x92\x06\x0e\xfc٩\xff\xf7\x8d\xf2^\x84\x9b\x92\x80\xe6\x97˗\x8b\xdan\xf1HG[\xc7\x16M\x89'\xc9C\xd4&\x92Ǹ'o\xf8rUm\b\x05\x80\x1c\x8a\xc6\x11\xfb\xcdN\xcb\xcfݾ\u007fwJ\x01?\x89\x12\xe56\x97\\A<\x88\x8fN\xc1\xb6\xf7\x0eZK5\x12\xa2D\xc1)\x98\xfc\x85\xef\x1dHi=\x17\x82i\xd9qlS\x05\xce\xd5:\x9cB\x1a2\xb6&yY\x91\xed^\x1b\xd9\x06\x17bخu}\xe6\x10Y+l\u007fc\xfc\xbeZ\xdbmL\xc7\xd5%9\xa4\x15\xe3\xa6s\xc1\xc6̪Y\xefO\xf21\xe9ߺYD2\x0eL\xb6\n\xbf\xfd\xbfʢ%\xf8\xa2\xf5c\x15+7\x9d\aV\x94_.rsIq\rp\x1bש\xf0\xb9\xe5\xba >\x10\x04\xc9bG\xecNz\x95Ž2\xb2q\x96X\x93\xfdD\xf9I\xae\xd6a\x8a\xf6\x96\xda'H\xff\x10V\x83T\xa2\xad으\x99\x9f\x1f\x95E\xc9t\x8f|\xfa\xdaG\xb53\x96(\roOtrJl\x0f\x97\x12s\x02<\xe5;\xd5\xf2\xdc3)YQ\xf9\xe8\x81`gw\x878\x05\"o\xbd&\xef\xdf7>\x17\xeecѭ\x82\x88^\xa2\xf5@&\v\xe6\xd1t\xeaT\x93\a\x12}g\xbb\x1c\xb1$\x82}\x88\xec\xb00h\xbah\xbd\x13)\x88GT\xfa\x01\xbe\x00\xc7s\x94\xe3y4r\xec\xc7\x00\r \x14\x00\u007fo\xea\rM\xf2H;\vΦw\xea~|\xaa !(\xeb\xef\xf2\xf7\xcf\xf9\v\xf6\x86\xd9\xe0\xb6\x19\xf7ad\xd8\a\"\t\xb9\x91\v\xdb-sQg#\xe2,1M\xb0\x16\x9c|\xaf\xad/\xa9u\xcdh\xf6R\x94\xb1\xd3-\xc7.k$G\xf8K,݅1a\x15=a\xab\xfc\x8cYP\xc7A\xc6,q\x94%!\x11\xd6\xff\nON\x05\x15zvN6\xd7^\xa5\xc6\x03>\xf0\xeeƬA\xe9v\xdcJ\x87F\xf0ӽ\xcd)\xb0\xc9\n/\x9d\x99\x8aުl̒\xa9B3GM\x19\x9c\xaf'[\x18\x90\x1a,n\\\x16\xbe\xe7\x8c\\\x1dk\xdeѣ\rm1\x05\xa5hm\xb4o\xb5>!\xd3\x1a\xf4\xbb\xf0\xd5jM0C <\xa6\xe9\xe7\xff\xf3\a埵\x80\xb9ߎ\\\xbd\xbc\xbb\xbb\x91\xbe`K\u007f|\a_xN\xf4\x11\x11`ǀ\x9c\xb8\x9epW\x1d\x1dJ\f\xe8\fjHL\xf6M\x0f\xa9<\x94\xea_\x94\x92\x89=\a\xf9\u007f\x9f\xaa\x90C\x05\x14\xa8\x8a\x06M@\aWޅ\xce%ꉷ\x16\xb8\xfddž\x82\xf4\xe1\x93f\x14\x97\u007f\xa3\x8e%\xe4\xa7Mn\xd8p\xe6\xa1\vZ\x00\xd43\xfd@>'\xf6M\xbc\xf0d\r\xfeY\xba,B\x04T\xb3\x97u\xb1\xc2J\xf5:\xf9\x03\x1b\x83\xe6\xd8o>\xaf\xb5b^\x1dչ\xe1ȑ\x92ދGx\x8e\xba_W\xeb`\xcf\x04H\f\x04\x8f\x92\"=\xb4ϟ\xa3\xfe\x01z&=\xde\x05\b\x1c@\xfe%ӌH\xd9\xf3\xd8qi\x93x\x17\xb0DH\xbaXx\xcfjꄯK\x01 \x16|@\x00\x04QT\x16\xb0\xc6P\xa8\xe4+\a\x18\xe3:u\x1e\xee\xb2c\xdd}О\xbdT\xb2\x9b\xcb\v\xa1\xf0\x85B5\x8cڨ\xca\x0281\x82\xd6hȩ\xaa\xaa\x9c\xa7a\xb7\x99Fu\xf6XLc[\xa1nNרxtN\xdfD\x9bX\xd8\xc5*\x11N8\x04\x02\xfd\xf0\xc6\xdc\xe1\xc3s7\x1e\x02\x16\x89\x15|\xf8\xf6\x18\x86\xcd2\r\u007f\x90\x05R{>}78\xf8\xd8.\xc7\xe1\xb9G\x8e\x9d\x04\xd5y\x11Ղ\xb0\x9fOg\xc1#\xf8Q\x0e\x96\x91\x9aq\xf3'\xa0g\x12\n\xcff\x92\xaa\xb8\x12\xc2\a\x18K\xf8Y\x88\xb4`\xb29\x8fh\xf32\xea\u0084\xc96\xda\xfd$}\x93\x1e\xae\x0e\x1e \x97\x83(\xc3T?\xbb\xef\xb2\x18}A\xad`\xf7\x0f7\x1b\xd4\x058\x94\xe8\x80\x18L\x19HFR\x12\x03\x89G\x8a\n\vE\xfdFJ\bXw!S\xc3K\xd2r\x86\x1d\x8e\x00\x04\xde@EKa\xfa\xdc2\xcb\xf6'\xbe\xe9\xb1ʌ\x94%v[؟[7\xbe\x82\x1fS\xaeF\xb3j\xbc\x01\xe8j\x86\x14\x87[5\xe8h\xa1M\x06t,\xa1\xb9\xe1^\xff\xa1\xd2i#\xda\xe1Co\xaf\xb4\xceq§\xfbZ\xa7e\xe8\xe6\xf3\x9a\xbdt\xac\xbfe\xe3\x19Wi\xfe\x95\xb7\x87\xeep_\xd7t\xa8\xff^*>\xb2\xb8\xb1\xf9Vlh\xf1\xf9\x17Z\x9aQ\xa3jX\a\xa7\xcf\x1cB\xdf㨪\x829\xa9q\x027\xd6@\xf9\x19\xb1\x8b\x82\xaf\xb8'\xb4\x12\xd0\xed\x16\x1e\xe3\x9e\xe1\xfa\x1d\xab[=e\x1e\x1c\x89\xc4H+^ї\xba\xc5\xde\xeaa/\xf5G\x85\x136\x8cz\xd5<\xf06)yж\x87\xe5DH\xe4wF\xcb\xcf\xf3\xe3\x13v\xe5\xfa\xfa2nF\xbd)%\xc3d\xf8\xf3\xef\x03\xb9\xee.\x9d)\xe1\b\x14\xc6ەP6^÷r\t\x8d\xe3\x04{\xaah\xf7\xc8<\x8aL\xca?\xb3Ih.\xa7\xb7\x98\x8e\xb4dht[$\x9d\xe5\x1e\xe3\x16]\t\xec\xe2\xb2fŘ\xb09&4.\xda;\x00\xe6s;\x8fB\x8c\xaf\x95\xfd\xf6\n\x12\x9ak\xf6\xf5\x19\xdc\xe6\xf5\xf5\x95~\xcf>\xa8j)ϰy\"T\xcb㝼j\xbdMU\x1b\x81\x02\xe9d\u0382M\xbcݱ\xad\xb4[\xff\x84D\xf1g4\x0f{\x1a+\xe1ݝ\x1f\xa9\x8c\x02\xa1\x17:\xaa<\x839q\xa9\x02\xb7\x01\xfeA\xcf\u007f\x14\x02\x05\u007f\xa1\x15w\a\tL}\xbb\xc5A=£6\x13\x1b\xaf۠ev\xf8\xddAu\x11\x94+U\xfa_\xff\x1f\xf6Q\xde3f\xde?\x9f\xe4\xe2\x95\x1bR\xb7\\\x97\b0\x05R\xc6\x00\x18\u007f\r\xa4R^ \xf3,\xf7\x98\xebV\xd1w\x85\xdcW\xa2\x1b\x1d\x15\x82\u007f\xb2\xee2\xe2`A\t\x1f\xe5v\xa9G\xa9<9\v\x0f\x93\xca4nX;\xa1?\xbb\v\x89?\xf2\xa7\x1b*uV0\xfb\xab\xe4\xb4\xd3\xed\x03{[\x014\"\xae\x10\xc3\u0382,\xb6\x17\xc4\xee\x82\x15\x1aqӼ\xb1\xe7<\xec\xe7\x92RK\x9a+\x82\b\a\xdf\x1d\x86k5\xc8WxcF\xec\x84\xf0\x18P\bO\xfc\u007f\x1f=*\xc2\xf0\xa5;E\x12\xfb\x83\x05\x99\xd5\x18D\x83~\xbf\xcf:\x17\xd4\t\x92m\\A\xb6\xbep\xf8\xb2\xda\xfd\xcb\xeb\x19\\XX\xa7\bd\xb7\xff\xab\xf5+\xbfH\x0ek6\xfb\xffZb\xde\xec\xdfWsX\f\xe4\xac/\xd4\xcd\x03$_\xba\xadQ\x84\x13\x12\xf1\x84Z_\x97\xa9\xbdhh\x90L\xf5u|\xd7\xc6\x108\xf9\r\t\xc2\xd6\xe4Z\x94\xa6\xc2\xd4}\xf1IH\xc2:ƋoK}\xbb\x8c\n\xcca/-\xd5k\xa3\xf3xVq0\xc4\x02\xc2r\xca\xce\u007fLC\x90_\xe0\x84\b\x14D\x106h&軓S\xa5q}pߨ\xf2=\x9a\x18\xb8~38\xbe\x10\xa1\xf1^x\x8eSߡc\xbe\xa8\xd58Um\x11\xb1\xf7e~7\xb0\xb8\xb6\x1e\xf4\x99VUZ\xc7:\xf4vƯ\v\xe4[m\xe9>\x85\xbf\x13\x80?\xd3\r\xd1\b\xe0p\x1b}\xf4_\x06\xe1\xf9\x10gK\x06B\xdd\xf4\xa1\xfa\xc3_\n%\xeb_\xa4g=\xf7Ih|.ݥą\xf2\xfeV\x0e^1䓺0\t\"{\x93\x967\x86\x83m\x86s\xd09\xc8\x0fꛦ\xb3\xf4\xd1B\xe0\xf5\xecN\x9e\x94I\xc1\x13p\x87\xcci{\n]J\xac \xea\x10\xf6:M\xb8\xaa\xa5\xd2y\xe4%\x89\xc5u\x86\xe7\xd5\x06\x00\xbf\xfaG\xe6\x04\xcd\xffVց\xa1\x02\xb5\v\xe6\xd2\x01k\x02k\xb4\x14\xa4\x8d\x06\x8cpy\xe0\xa7\xe6\x81j\x06p:\x1a\x04G]\xafZ\xd0\xe8\xd8\xc8$\x120\n\x8b\xf4\xfa_\xeaN+M7\xfaY2\x18l\x15\n@\ax\x87\xea6\aq\x9d\xe1\t\xb7\x9d4\x9f\xd5\xdf59O\xd4Т\x9c}\x11T\x93\x90r\xfcf5\xfa\x9f2\xffk\x16 t\x01\xaa߲\xba}\xbbp\xc4U\\\xd2ur\xba\x9b\xb7\xb1sVl\xb8ת\x85a\x87\xf5\xbc\u07b2\x8e\r}Vm\xda\xf1\xc7~3\xffgm\x86\x8d\xcd,\x18\\7m}\x95-\x8e\xc1\x84*\xe3\x9a\x10,EH\x9bq\x03\xe7$Yx\xb8=\x0eE\x15\x98\xfc\xfe_V'\x9e\xf6C\xea\x19\xb2R\x99i\x87ND\xb5\xfa9\xf5\x03\x9d\x91/\x00\vC\x19b\xb2\xb3\xc1\xd8\xf8x\x9d\xa1@8`\xc12I̪,!\xf1\xd1\xc2\x03f݄\xb2nE\xf1\xf38\xdeb\xf1\xfa+\x12Q\x90\x942쪘\xeb\xbdCZ^?G\xf7\xf8Vf\xe2\xec\xf6砱\xb2\x04\xfb\x17(B\xeb\xe0Ie\xf3\xfc+\x14\x899\xad\x16\xee:\rA\f\x0e\v\xc3\xf5\x9c\xef\n\xff\xbcv\xa8\xa5\xd4\xd04\xd4RB\xb7H \xd7z\xe7ѳy\xec|\xffx\x1b\xbe\xae\x9c֣\xdbW?\x80E\x1b\xfb\x12\x1at\xb4\xd0FO\x0e\xcfܔ\xe1\x93c\x1e\xfb\x94=\xf5\x9a1E\xe5$V(T\xf3\x92\x9a\xd7\xc1}\v\xe6\xabrY\x18\x0e\xa3!H\x1chQ!.F/\rd\x10\x9e\xf6\xf4իG\xb4\x83\x9e0\xff\xef\xd4\xd1;j\x17\a86t\x1d\xfe\xc0\xc4\xea\xaa\xc5\x16\xae\xec\xd0\t8\xdc\xd5y\xe4\xf8\v\xdfQG\xb4\xfd/Z\xb5\x90a3\x06=\x06\n\xcb\xcf\xf4O\x81\xaa\x87\xc2_\xd5ؤJ\x9a\x99Pג\xa2I\xb1Rs\xa3Z=\x8e\x90|ڼA#\x91\x8c\xac\xac\x8c\xf0\x0e#\x91su\xeb\xee曻;.\xeb\xe9\xdd\x1a\x06\xbb\xfa.t\x8aש:\xeaK\x16IT'\x17\x816\x98\x1a\x95\xcam7\x98\xd2\"\xf2:\xef\x1a\xec\xe2\xbes\a\x03\xc5b\x12\xc7q\xd7yL\xe2@Z,Y\xaf\tbg\x98\x8e\xdd\xc5,\xc1\xfe\xbfn\x9f\x06\xfb\x89\x98{\x0eO\xb8;]\x93ɪ!_\xe3\"=c\x17\xf8Ӻ\x02\x86\x9a\x82dij\x9b2\xa4G\xecB\xbfX\xb5$\x80\xe3\xb5\xe0|\xa9\xab\x17\u007fi\xdb!\xb1\xa1\x82*nT\xee\x01%\x9b\x02\x13\xfb;\xcf\x15*\xea\xc5\xf6\xe4^3\xce/c\xed\xae\xb9E\xc0\x9f\x13s\x9f\b4\xf6\xbaC\x03wLj})\x19\xb1\xf9\xe2<(\x86\x9eYpHw\xe2\xdd\xf3W\x8b^\x92\xfdHL\x8c-\vv\xcb\xdbp\x84\xf9đ@\x01w\xde\xc4Пp\xc5̹\x9dU\xfaK\xb8\x97\xc0\xa1>1뷀\xf1\xc4L˾\xa9f\xe2\x040p\x89\xd0Ύ\x97\x1b=\xd4_\xeb\x8c\xf1\x9f!\v\t9\xfeq\xc6[\xba\xba\xc1ƭ\xae\xf9t\x84-c\\\r\xd5\t@\xd5q\xf1]\xb2\x92\xe0CAJ\xb4\xafp\xcdPao|\x1ey\xa9lN\xde\xea\x91{\x0fF\x86\xf3*3\xb3F\xd0\xf8\xcbxL\u007fTv\xa2\xb4\xd00ԛV,\x91\x90\xbb\xe4\xde\xfb\xf3\xd4jH\xf5A(\\\xc9\xf4\xd4x\xe2\a\xfa\xfb\x9f\xbe\axtP\xbd\r\xf5\xaf\xaaR\xa5^\xa9\xd7\xe5\xe5\x00S\xbf\xefh\"\x91H\xb0\xc6\xfdJn#_p\xbf.\xc2$\xad\xb2\x02\x19\x8ds2\xf0i\xa8\xf1\xfbB\xcc\xe6\xe6\xf1{T\xd1uZK\vt\\\xd4L\x1aI\x87\x19%\xbe\xed\xf1*\x9a\v\x8c\xbfP\xf8=\x16{\xf9b\xe0\"U\xb8Q\x87\"V\xd3R}\t>Z\x82\xd6\xf1\x8a\xd2\f\xa5\x81ŊN\xb1\xb3Vݮ\x02\xe9-\xfbJ\xc9\xech\x02\fσ\xe1\xcd\r\xc3^\xa9\xd9\xd5\xc1\f\x11\x8c;\x92\xa6FQ\xc3\xd4,*+\xe8\xad\"\xf5\"\xd500)\xb6:;:V\x94\xa7\x12P8*e(7\xd2Jl\x97\xbd\x8e\x8b\x9b0oHe^Ɗ\xe2\xe5y%\x81`\xbc\x93\x1d4\xa7\xc7Y\xd2[eX\x16}\xb26K\xfcJ\x05˩\x84\x91\x12\xcc\x00\x00^#<ɝ\xc9\b\xcfI\xdf_/\xee\xae23-@\xb1l\xb34\x96\xcb\xfb`\xca\xecP\xe2=\x01\xe6K&=.)\xe9\xcd՜XvL\xaa\x88\xc5f\xb1o\xd9\v\x9d\x8bBG]ޮ\x8d\x16\xd2+\x02\xf2\x8b\xe6\u0602Py\xb8\xd0I\x8en\xa9V`\xe6k-~S\x8c\xc5d\xd0\xed\x11d\xfd\x02\xd8cU\xb7\x97.\xdagƗ'\xdd 1N\x8a\xfd\xf5\xad0P!\xf4\x00\b\xa1ί\x82\x87H\x95\xed\xa8]Hf\xb0\x12\x9e\x0f[\xb4Z\xc2x\xcb\x1d\xe2\xc5\x05\\.\xc2 \x91\xe0\x80\xbd+\xa2\\_\x164\x13b\x98\xa6Ov\xaf\x1a\xff\x82\x8a\x92#\xbb\xae\xf6\xb1\x88\xc5v!\x82l\xcc\x04,\xd4x<\xec\x1aD\x13xIN\x02-F\x13\x82\xece,/\x89\\m\x82\xde\xd3d\xb4Py\x98\x12\xc4Ir\x86\x83ǐ&$\x8a\x11G\x1b\xbcK\xf1K\xb6\x16\x84և1\xc4q\x12\x97zG\x9f!\xaf\xa3\xb2\xb5A\x05\xed\xbb\xc0\x11\xb938\xad\x11̍\xcd\x0497U;ȴVe\xe6g \xb0\xe9\u074c\x1e\xbfL\xbb\xf8ΐo\x9ctp\x92R#\x05\x8e\xab\r\xeeA\xa7D\x88\x16\xa2\xc3䶅\x10\xa2)\xe8m\x83\"Ǜ\xf1\x19X!\x10\x0e-\xea\x97Μa\xbeR\x1e\x95\xaa\x82\xb9\x99_\xad\xf4\x91});\xcd;6\x13\xc5\xefП\xb2(\x8e\x8eo:֔\xfcqC^\xfd\xf9Ǖ\xed\xa9\xfb\xad۵\xe9\xadA=\x8ez\xf9\x1aO\xef\ab\xca\t\x85d\x84~\xf8\xca\xc5\xdb\xdb\xfb\xb6\x8d\xd7\xeehz\x97n/J~\x98ǪŤzS\xe8,J\x82\xa6J#2ŭ\xc3\xf3\x10i\v\xe1\xffZ~_\xa2{c\xbb\x8c\x1e]o\a\x96bR:\xf4v:\xeb\xa3\xe2\xf6?e?\ttZ]ָ\xdf\u0560\xdfgժMk\x0e\xea\x16\x14&\x8ezz\x9fq\xb3\x1f\xe5%\xc3UCW\\Y\xdeڻes\xf5\xb6\x867iv\xa2\xe4\a\xb9\xacZ\x97d\xef\b\x1a\x14\x11\x9fT\xa9\x16V\xe6\xacQ\xccC\x8e\x1c$m\x04Čk\xc0i\f\xddw\xb4ƿ\x05\x12#\xf3\xe6\xaf;\x0f\xcd\xf8̋\xc9\t%y\xcbG\x1f\xad8@5:yq)\xb4\xa8\xac|⌬N\xdd\xe7=\xfd\xb4\xc6\xec\xc5\xddBց\xc5^\\\xfe\xa9S\xf98]\a\x02\xeb\xf5\xaa]\x9a?{\xbc\xa0rW\xf9\xb5\xf2\xd2[\u007f-\xec\xb9+W\x0f\x18\xa4q\xd9)^2\xd5\xf8\xdf\xfd\xc7-\xc8\xcc\x1bKK\xa0\x140g4\x89LҼ\xee&O\xf9\xb2SP\xbcd\xed\xe0\x02\x9dŞ-m\xb2\x15\x80\x05\xc7>\xf7\xb8\xdd\xf3\x80\x87n\xecx\x1b\xcaQyY崎\x00b\xe1\x1c\xe2\x89y\xa6C\x12Q\x9a\b\x9b\xce\xc7\xfaA\x00\xcf\x19\x93)\b\x98\x8fB\xcb\xebD`<`\x92\x88\xec\x99\xc1\xd7\x18\xb3\xb1\xb2\xf0\x05\xe9\xbf\xfe7\x9e\xc9\a\x91\xbb%f\"\xa5Y\x89\x16\x93\x15\x8d\xfb>\xb6\x92\xd9ШG]\xdcT}\xea_\xf3\xe2\xfa\x9d\xc3\xea\xc8T\x18\xc4,a\u007f\xd2\xec\xdf^&xԠ\xab\x91\xe7,v\xa54\x98EpW\xf0¶\x9f\xf2S\x84A\x14\xfdN\xefⅭ\bgj\xde)\xde\xc9\xec\xe2&\xaa\x8ad\x99\x00\f\xba5\v\xb84\xad\u007f\xe4\xfe(\xda\x04\b\xe7\xe4$\xc1\xfd\xe3sD\xe2Bݦx\x9d\xf0O\x9fh\x11\xaaXQ\x9f\xccL\xe2\xb2w\x98\x05\x8a`\xed\x1fq\x8en\x1dP\xee\x88sT\xa1\x17s\xdb\xd5'@\xc7Tz\xc0\xbd,\xed2\x1f\xcc\xe8J\xb5*njވ\xfb4_\x98}3\xb9\xb8\x17\x9d\x9c\xbbי\xc7j\xc5ҫ-\xab%i\xf9\xa8\xf2\xa5\xbd\xe9\n\x80\xf2\xb7\x01\xbd\xc2P\x92O\xf3\xa4F?\x8f\xadkjS\xdb#\xed\a\x04G\xfa'\xeb\xcap\x1b\xf61\xe6\x8e\x1d\xbdJ\xc3\x18m\xeab\x06\xda\xf6\xcd\xd5a[\xef2\xef\xb1\xe5?kKq\x01\x1c\xa7\xb2!\x8c\x88@-^Y97\xf7*\x9d\x96o0\xf0\x81\x91i\xc5M\xd2l\xe4=\xf5ߺ\xfc\xb6\xb4\xed\xaa\xbf\xa5\x92\xf6\xbe(\xe97g\xa9\xbc\xbf_\xab\xdfǙ\xc7\x04W\xe5أ\xa4\xd3..\xfb\x8d\n\x8c\x0ep\xcd\xdb\x1e\xe8k\xa4\x8c\x03\x9f\xae\v\xee#\x0f\xac\xf3\xf8\xf8\xc1c]\x06@\xe0\xf9\x03\ue5d2\x8dq\x03\x8bo\x9as\x16]\xacvK\x8e\xc0i]\xd3C\x15+\x1d\x9aK6\xd6\v-\xed/'S\xa2\xcb\xe8{V\xaa\xe1F#p\x05ƦuO&\xe1\xe5g\x1e\xf4z\x89\xa9u\xf2\xe0t\x92\xaaxeL\x94.\x8a\xa8v\xdes\xdcMf\xefџ@/\xe3\xce)\xf6u\x95\xb0\xef\xc8\x1bA\xe3)\x1b0!۽\xf8\x00)/Y\xb8\b\xa2\xc6_$mU?S\u007f^\x15\xde\tGq\xd1\x01\xda\xcb\x13\xc8Vċj.v\xdd\xd6\xf1UH\xa1\xf30\xc1\xcamǕ\xd6\xd8*3\x12\xa8\xbc\x87\xd0b\at3\x80\xa3\xda\xc9(\xe1\xcf$F#\xb1\xc4P\x83hzZ\x98\xb2\xb2o\x11\xb6\x14\xda\\\xa2\xc8d\xf9沠pm\x1cL\x91~\xf6L\fj\x0e\x13b\x10\xcbm\x10mK\xe5\xd0\xc7\t\xa8qsN\xf1\xb6\"Q_Qh9\xd1\t-\x91\x1b\xdb㳟CU\xcf\x03\x01џ\xfd\x8fO\xea=ކ\xf6y\xd45\xf1\x84\xd1\fYk\xc8\xed\xd4\x1f\x8a\xc3N.e\x1cu\x9bi\xad#u\xb9\xe5ڒࠠ\xb9\xad\x92p\xd5\f*\x93\xd8!\xa1\xf4C_\u07fb3\x8c\xd5Q\xdap\x83az\x04m\xa6g\x1a\xe7-\x96\x8c\x99\t\xb9-\xe5\x89\xf3\xf4\a\x00\x02k\n\v8\x89\xabZ\a\xc4\xc3莧\x16\x96YP\x94d\xcc\x1fM\xaa\x9c\xb1\xf1`TG\xbc\xb5\xd0\x1chѤ]:\xabd\xf8\xd4\xd0VN\xcdv\x02c\xaaW:w\x99\x13\xeb\x8d|kҁ.:ӫ\x1e\x8dO\xddڑs\x80w\x1c p\x00T\x12\xca\xd2\x1f\f\x80\xb6%z\u0381ه\xc1*\xec0)\xd8\f\xea\x1eA\a&3\xaf\x92\x13PPQ_i.\xde-Z\xe9!\xe1\x8d\xd1\xf6\xbd%\xd1Tt\xf6\xa8\xf8f3\xebk״\xde+\xb4\xf0\xb4\xfff\x83\xd0\xdd6\x8b\x9d\xe3\xb5\a\xa5\v\xab\xd56mP\xd0яH4\xb1ׇ\x18\x85\xc32\xfe\xd5\xfc\n\xb1umMCͥ\x06\xe7\xa1pm*Y˭\xab\x8b\xd29\x0e\x01\xc2_\xbb\x9d\xf2\xe2\x15J\x11[\xc4\xf3\xf3\xba.9\xb4\xd7&\xf3\xe1,r\x80H\xb8i߃8Ʌ\xa0\xefa\xc2\xcd\xd6\xc8\xdf\x19\x97[\xbfN\x1e\x98n\xb3\xfe<\x8eCrxL\xb5\xefr\xf9J2\xe2vc\x18\x14\xfe\xe4>x\xcd\t\xfc\x9c\xee\xd6\x11\xe4J\x1e\x82#u\xe3\xa4\a\xb7:nY\x93\x16\x9d\x98}\x1al\xf4z\xc2Ӯ\xb4\xe9^Y;\x9e\xb6\xe4\xb1z\xb4\xea\xb3\x17Ӊ\xac1\xc0`7z\x83v/\x9a\x98_眓\xb0\xca\x0e{\x90\xcf\x1b='T\x87 `Jټ]\x18\x02\xf8ȇU\xcb\x02)K{v\xfb[\xa5\x84\x14\xb0՝y\xac`\xd7-0-\xd3?\x9d\x9a\xc1^\x82\x9a\xff\xc7[\xc9m\x03\x0fSƐ\x1e\xc2=\xf9\x03O#_D\xad\xad\x9dq\xc8q\x1a\x8emR\x1f0\x0e\xcd\x1a\xc2\xd3)\x95\ni\xa2b\x11J\x9d\xb1}\xe7\xce\xed\xf8<\xddw\xf8o\xe2a\x81\x01\x196\xba[\x9e\xf6\x81\xbb^D\x85\xde\x1c\x84Zz`\xaf\xab\x9c\xe0̶.D\xe6K\xcf\xf2\xa7\u007f\xcd\x1a=b\xb1\x05\t\x9b\xc6\xe2\xeeb\x88\x00\xe4\x9dl\xad\u07b2w헂M\xe2\x18\x9a\xd3\x067d\x18ֆ\xa5\xa3\b\x85\x80\x15\x80\xed#wQ]!\xb0\xf6\xd8˘\x98\x14\xf3g1}BJ\xda9\x89\x86\xfa\xc4\xc2Ԏ\xf1I\xae\xf3=CVR\x8d\xb9%\xceL\xd5MU\xa2]C(\xf5+#O\xf71Q\xaedj\xca2\xbe\xb9~\x12&\xc7B'٩p\xbe\xd9c\xf0Q\xde\x1c4\xcc\x1b1#\xb0\x96\x9aq\x1dʸL\x8f\xc9̮\xaeL\x9b➒\x16\b\x9b\x86\x19\x0eGZt*j\x18\x96\x1eI\xb6`\xe8\x16\xdbQ\xb5\xa2\x8d/\xd0\x02HJe\u007f\xd6l\xb9\xe8\x87\xd2\x1f豎\x95\x9b\x94x[0\x94D\xa3\u058c1\xc2\xc0STK\x89af\xd4;\xa2\f\xf8\xd93`\x17L\x98\xb0}\xe4\x8d{اJ\x11&5\xb3\x80\xb1\xc4\xd9\xeaJ\x84^\xb7\xfc\xf5\xb4G\xbd\x8f\x86\xa1\x86\xeb&\xf5\xf6\x9ex\xad\xee\xb2%n\x86q##\xe9G\xb3\xa17\x96\xa4\xffp(/8\xf9\x84\x9d\xd7ʶJ\x1bGy\xd1\x1b\xbe\xf68\xd1?\x96\xa4\xff\xb0\xc8+>I\b\xb4\xa5\xae\xf8\x8f\x10克W\xe2\xbbT\xdcm\nAj\x98\xf7/b\x88\a\xa6\x88\xb3YFNG\xffu\x17c\xfe\x04\xf7\xb5\x8e\\\xae\xc1\x8b\x88\xcf:\xebi%\xba\x96\xe1fU,p\x17\xe6\x82I\xec\x8dp \xff\x96^y\xb6\xeb\x95\x1f\b\xf7B\xc0cx\xc22\x8a\xd0\xec\x1b\xf7\xca\x16\rVb\xe0\xa76N\x01\xbfd\v\xd6ٍәT\x80\xe4\xe6\x9dl\x14\xc6W\x86{tĈT{\xf0\x96\xe8S\x11\x1e/\xa5Q\x95Y\xfe\xa5K\xfd\x8e\x15\xd57\xb7\xe4#\x12\xae\xbbp\x16QcGo\x91\xde\xf0g\xfbQ\xa0\x80G?e<\x80\xf3\xb4\xe8\x05t\x19\x86\xfd\xe7J\xa0\xf8\xe9\xac8\xb73\xd7Y\xb5\xb5ި\xfcF^:\x9e̊\xe3|\xee\x8eʚ8`r}\xe0Q\xe7\xc1hF\xe94\xd5뢺j\"\xb3:k\xa22;k\xa3\xce.,\xef&\xfe\xd6z\xdbTIF\xb7Ty\xf8=\x9dK\x99\xc2;\xc2pr\x02$\xdcѲ\x808f_\x9b\x93TI\x1fV[\x85\x8a[\x01\xb2ź`\xf6\xc2\xe6.N\x0f0\xceU\xb8\xa9\xe38I\x00Y\x9e\x97\r\xc7D5\x0f\x957\xf6o-\n\x1c!\xf3mv9\xa9\x8f\x1c\\/\xb5KR\x99\x9d\xb1\xe2!\xfd\x1e\x8c\xff6\xff\x88\f\x81b\xe9\x1e\xc9\\\xb5+'I\xe7e/\xb1\x9da\xf6\xcaFz\x1bͷ\x10\xfb\xb5{\xed\x8b\xf3\xf0P|\xf3\xb5w\x8d\xc34ej-\xa5\xa0t\xc2۠^\xc2\\\xdfSK\xd7+'\xbcJ\xe6\xfbR\x9b\x82S\xf4\x8e\x00\x1df\x99\xf0\x0e\xc8\xe6\x82\x034\xbe\x96Ԗ+\xc6e\xf9\xd0\xc7\"Ӄ\xd6j\x1a\\\x91\xf3ʌ\xe0E\xcd.\x02\xc5>p\xb6\xe6\xed\x97!\xcd\\\xec\xbe\xf5B\xf5\xe0\xb8\xc3}vچN!\a\"f\xc5\xeb\xba\xe3R\x8b0r\xd6G\xc0\u07fb\xc0*\xb9\xd0\xfd\x1f \xad\xc6\xf6\x95\x8d/J\x95\xe4\xf3\xe26\x1b\x98M\x9a\xfe\xf1n\xff\xeb\xca~\x8b\xab}}\x18<\xf1\xed\x8e\x1do\x13\xe1\xb0\xe0\x98\xe7lϸ\xfbp\xef\xfc\xbdf%\xed\xde\xff\x10n~\xdd\xd8W\xd5X\xeeU\xe1\xbcl\x11A!\xb9ˍ!ӫ\xc2\xfd\xfd8\xebiD*\xb9z\x123\xc9\xd9@\xb8\x85EYo\xca\x05J\xd2N\xf1C8f\xa4\x04\xb7\x92,\x03\xe3R\x1d\x12\t\x9cƏ\x83m\xd6w\xf0\xacE\x1b\x0e\xc1\xf1(\xc9i\xfdwL\x8c\x05\xdfe\xee\xe37\xebxЬ\xbb\xf2\xd32\xed\xe7Lz\x00\xac B\xa2\b\xc1,\xe4'\x1a\\n@Oޤl\v\xef\xf7\xff\x18o\xe6s4PcX\xa1Y\xfb\x13\x96\xc5\xdf\xcf\xef\v\xe5\x11\x8f}t\xf2\xacp\x8f-\xb4\t\x89\x8eyC\x1c&\xea\x1f\x83z\xa5\xb1\xca\n\x0eZ`7\xa1)\xaaT)\xab0\xffjJ\xa2\xf9\xd8\u05ef\x8f$7\n\xa0\xbf\x93\xa2\x80۷o\x12\xc1Uck\x97\xb1w\xf1\xd4Y;8\xa0\xb6>\xcc\xeb\x80\xf1+g\xd16w&$\x1d\xd1>ނu\xcb\xeb\xb6\xee\xfa>\x1b\xc2\n\xddVZ\xeb\xc1\x88J\xb4\x05\xa3\x91\x1a\xb4\xb8g\x82˿\xe5=\x8a\xfe\x83>O\x8b\x01\xe5i\xe9\xb6]@\x9c\xc9\x01\xff\xe5QY\xd6\xf7\x86\xf9\xa7\xf7\xd8O\xda\xe6\xda\xf2\xbf\xd2ƽ\fAI\xaeN%F(\x9d\xe0\x9a\x04Y\xfe9\xcc9\xa3\xdeJ\xd5C\b4\xceQ@J\xfd\xb8\xb69\xa0\xe1u\xae3p=\x05\x820A\xa3\x8a1\n\xb8\xe4,^\x19\xf5>\x05\x84(\xb5\xdaHR\xa9\xd4Bx\x8d\xb5Lԇ\x88j-\x18\xf0\xed\xefa\xdap3\xad\xff\xfb7ub\x03\xf0NV4|\a\x11u\x87\xa0砋ale\f\x8d\x9az\b\xa2\xf6\xa8J@\xea\xd55\xfa\xab\xe2y\xb8C\x17\a\x8dQ@RRq\x82O\xf9\xa7\xb0\x86¼\x85p\xc5\xde\x18\x85\xef1\x03\xccB\xb8\x8fj\xb0*\xbd\x0e\b\x19O\xf2\x80\xed|O\xb1\x0e\f\x14,\x82\xee\x02\x8a\xfd\x91\xee0\xc9߰\xafʹ\x02н\xf3\x88,\x1cu\ue240\xc3\xe8\x84\xfc\xc0\xc2\n\xb4Hs\xfe\x125\xbd\xdeIJ\xd9\xcaR\xa5\xd5\xea(\xa5\x87\xf3\xf5\x8d+\xa4\xd0F\xbbL\xc7\x1f?Fh#~J\x17\xf4\xf2\xbd1\x0f\xf5\x94\x96\x96\x02\x1c\v\x94\xb4\xa1\x10p)O\"\xcd-J\xc8\x12\xfdq\x10\r\x9b\x02Ƀ7\xc7u6\x95\xd7(ۄ\xa2\xbf\xf7\xab\xec\xff\xee\x96!P@\x81\xea\b>\x1aÁ1\x94\n\x00\xa4&\x14\x19'\x8es3\xadه\x86X,\xeb9Y\x83\x17|\xbfs\x1a\xab\xd3A\xc8CEvp\xc6|̺%\x14\x98\xb3\x043\xb87\x1f\u007f\x94_*xC\xeb\xe68\xcf\n<\xa3\"\x8c\x1f\x1b'\"G\xc1\xb5\xf1\xe7\xd2!\a\xdd£\xc4\x10\xa4\xe7V\xf5볩\xe0s\xc0&<6D-m\x9e\x8f\xc1\x9b\xa1\x8b\x0e\x81t\xaet\x9ez\xe8q5\xfe\xb7\"m\xf7\x8c\xaf\xbcJ\xeb\xdc\xec}_\xfc(^\x0e\xbdm\xed'V\xa0\xb3\x9b\x9bs\x12\xde\x02۴F\xf7>\x12\x0f}*s\xd6V\xc9\x15Ӈ\x03\x16\"\xb0\xa6\xa0\xcc\xf7m\xfc\xb7\xb7\xad\x06\xef\xfc\xfc\xa8\xd5\x16\xf4\xa99oq\xa8\xb9\xa7\xba\xd1\xf9{\xc1\x94o\xac\x90!\f<\x0e\xa2\u007f]w\a\xbf@a\x0e\xf0#a\x8c\xc4\xe9\xb9Y\xd7Y}i\xc1\xa2\x94\xc4|#\xd2r\xef\xd2\\\xf6\x06\xf2\x89\x97I\f\x02\x94_ߙ\x89W+\xdb\xcb\"푎\xff\xdcNܞ\x820\x97|\x97\xe7\xa79\xa78\xe8ֽ\n.\xc5yf\x90\xa4\xb8\b\xefn\xec\xfesˡ\x00\xe8b\x98\xbf\xce~\x1dp*5E#\x01\xf2\x05s\n\xcbvN\xde9>\xd0c\xc9\xe4\xe8\xb3QG\xd5!\xeb\x80Ú\xc7\x0f\x818\xfe\xfcЊ\x91y\xb36&\xaa\xca\f\xbd-2\xb3~Q\xfb\x96\xcb\x00\x81[\xf4aṖ\x98\xebо)5\xd5\x1f\xe3\xe3\xd9\xe9_[\x97\x91z\xef_i\x1e\xeft\xaa\xe5\xf3b(߭\x1e\x1aO\xa4\x8d=\xe8C/\xfa\xc9\xe0\x9c\f\xed\x0e\xb0\x19P4?\f\x0f9\xe6T\xcb,\x02\xee1\x00\xa2\xafլ\xd2\x14\x869\xd1\xcd\"\xf5f\xbd\xe0\xc9P\ueb26]S\xbb\xf5\xdb\x1fԜ\u007f(\x1f0v4\xfbs\xcfJ\asb\xb5\x99nQ\xb7{\xe9\xec\xfc\x06}\xcd\f#\xc1@\xa4\xfd\xf2\xf7\xeeɏ\xa0\xf1\x9f\x19\x98U\xd9\xc1\xfb\x13\xbd\xa6^\xbe\x9cR+\x18\x81/6\xf7'\n\x12\xa4\x97\x90\x19\x8b\x8b\x11\xc8Kh\xa7\xf7-\xed\xdfF\xf8s\x815\x93X\xfe\xb4ޖX\xfcyXQ\xf73\x88\xd5\xfe\a\xd2\xe9\r\x80\xb8\x9d\xedWK\xfd\x8a\xd4b\"\x8d\xa8&\xcbâ\xd1{\xd6[\xedm\x13\x96p\xdb\xda\x1aZ\xa2\xbe\xe2ֶ/ʲ\x96\xc3Z[\xcb\xdbZ-l$\xdeN\x8ae\x94WHW\x06\xcfM\xc8_\x18\xd1\nVӧ\xe4x\xe6s\x93䀱X\r)\xe4\xd6\x1c\x1f\x99o\xc1C&\xa96\xd9l\x98ktIp\x89\xa1]\x00.@?w\x1aS\xb9\x9e\x86h\x97s-\xe2\x86$\xe19\xec\xe8n\xb1P[\x03\xb6\x9f\xf8\xf5\xb0\x8ep\x83Y\x8fӲG\xe6\xf5\x1f:\xf5\xa5\xaf\xf5\xf5\x99E\xebt\xd1\xd7\xe7\xee\x03b&\xb8<\r\x98E\xe8_\xeb\xf1\xd1p\xd10Jtz\x1f\xc0X\f\xa7\xadB\xb4\xb2\xf5\xac.R\xf6\xed\xfa\xf5\n.E\xc2\xe2Ď\xa0\x94u-0OSBþ\x18m\t\xe2\xdb\xd5Ǣ\x9c\xa0\xf1\xe2\x93\xec\x1a\a]v\x94\x16\xdfd\x9d`\xb7\u007f\xcbÝX\x19\xffP\xff\xe0\x9c\xe2\xfa\x9b[\r\x17\x8c\xeaV\xeeC\xd34O\x10\x91\x8c\x91\xb5\x90\xbf\xf70&z\xcd\xcdu\xa0\xc04\xc6&\xd1\x10\xd4E\xa1ʙ't\xd9A\xee\xf6\xcdB\xc1\xb5%\xda+\x87DˎG\x1c\u007f~A\xedx\xd6\x1e\xf0\xbcCPKZ\xaa\x00\x97n\x16\xac\x9f\xe7\xbeRg\xbc\xbcx\xc2+\xce\xf4\x1bi|\x1b\xb8o\xf1ʜ\xe2\xf1\x928\xac\xb7o\x9c\x17qJ\xb3`\x03\x9c\x14\xeb\xfcG\x14\xdc\xe1~\f\x86\x89ɕo P\n\xe78\x10\xb0y\x0fuq\x82뢵\xd9\xc2\b\xcc\xfa\xd6𐠵\xfa\x99\xa7\x8d\x8d\x9f\x99Ռ=ƶ\x9f\x13\x9bT\xdc·n2p\xad\x12\xb9\xbfaA\x1d/\xe1F[\r]+p^\xb6\xb5F\xb5\x17\x9b\xc4(\xca\xf3\xfd\xc2?ɬ3gg\x01\xf3\xb3\xf5\xd9\x0e\x04\xecQ)\xb4\x01\xc3\xc4Ċ\xb5\x0eDLm4\xf7\x98G;\xbf?81\xd7[ѫ\xd4T\xd8> =\xb1Q8\x1a\xe3\x13\xaa)ʒ\xde5\xc1\xa8ck+gdR\xbe\xf6\x00\x86\x88\x1d\xc3\x12A|\x86v\xe4\xec\xfda\xea\xaekBcz\xff\xed\xe5[\x95\xe9\xc9C8\x94^'\xb3դ\x86\xd4\xcfO\a\x80S0\xb7\x83* )\x9b5r\x8f\xeb|\x9f\x80Ȥ\x8a\x93^\xcc?\xf8z}\xb7[\xf0\xa2\xc0\x81SW\aU\xf7\xbd\x1e\xf6\x1b\xb1\x04\xecT}\x1a\x81\x8d?L\xd7\xc0\xf6\xf9\x0eU\xcd^\xbb\xc0\x8c\xcd}\x18L\t\xd26h\xb88\xdb\r\xeeb\xe2\x8dǎEڰ\x02n\xbc/\x84M\x01\xd1\xd9A\x85\x0e\x04\xec6\xca\xe2\xeb\xfb\xb6\x936Mk\x14<\xe9\xccu9\x8do5)?q\xa9\t#\xc6019u\xa3A.\xcamX\x02\x16\xa9iȪ\xf2fg\xc6Q\u007f\x1a\x93\xb7\xbaWo\xf1\xfeg\xad@\xc6\xeb\x18u\xac;\xc3\t\xc9o\x17#\b\x96\xa5\xcc\xf5&\x8fo4\x8e\x11O\x91:on\xd3\xf4M^\xa9\x9d\xa2;>\x9e\xe7r\x820\xb6.'\x9a}\x17\xbc)X\xf5\xe4\f\"\xa29\x99\xbaO\xe7\f\xb0\x8a\x8b~\xa9.7@3\xbc\xb4\xf2\xf1\x9a\xbf_~I*\xfa\x8f\x95`\xfa\x85\x0e\xeb֣\xc1\xb1\x12q\xad\x14\xc1\xca^\fQ(T\x86\x95\xcf\x03\xd8\x04ߠ1\xf7``\x06\xc2\xf3w2\x8d\xbe\xde\xc6u\xad\xa4\xe0\xafՓ\x05\xaf\xd7\x05أ\b\xcb\xda\xc50\x06\x12\x8cF\xef\xb5(zc<\xb9\xc8mL\xa9hc\xf2\xf5-p\x13\x14\x86:\x92|m\x03\xe9\xde.Ǣ\xb9VfhJ\xe2\xeaM\xf8~\x9a \xed[е\xb4}\xfc\xf4r\x8a\xca2\x8b\x04\xb8~\xfc\xe8\xa0wzJ\x05\x90:Ս{\xe9s\t\x10\x9e3\x11\xf6\xf7xԺ\xd7,G\xb7 \x9dMKd\xbc\x9a\xb0v\x17%b\xed\xb1o\xbe\x83\u007f\a\xd6|\x02\xe0\xfc\xad\xdal6\xa5z\t^aCG;zVl\xb8\x0e |_\xf5\xc3\x0f\xa6\xfdm௷E\xa9ZQl\x9c\xeeZ\x1c\xeb\xee>g\xb7\xe9\x10sSo\x98\xac\xbfl\a\x9b\x03\x87\xf3P\x8f\xe4\xae\x1b8\xffC\xe84>@\x15\x9a\x8f\xf2\x80e1b\x14ς\x1f\xdc\x1d\x9c\xe0\t \x0e\xfe\xccz\xd0\xd7\xf7\xe0F\b]\xb55\xb6\x03Qƃ\x8c/Y\r\xe1vAfG\xdbW\x12J;\xff\xa8=\xdeyw\x16@\xbcR\xbf\xdb\xe5\xfeq\xba\x19\\\x03\x12kK0{2tv\xed0\x06=\"w\r0\u007f\xc4N\xde\x17\x94\x03\xf5\xbcr\r\xafD\xeen\xda\x06\x83J`3\xc47\xf9%/-\xc3*\x9dR\x9e\xee\x80.U+\xfc[l\xafQ\x9d\x867H\xc2\x16\u007f\xc1\xd7\xf30x\x1b\xf8/{dž\x01q\xb0\x90\xc18>6F\xb2\xbe\x0e'0*\bG\\\xf2Q\xdba\xd7$;\x03\x9eh\x89f\x14\x00EB\xd7\xfc\xa7\x99C\x12\xba\x96\x84\xb7-`\x190\x81\xdd\x01)\xf0\x8f\x83y\x94[hʑ\x91\xf6\x13\xbf\xc4V\xea\x95\nH2\x9epC\x8a\x19\xb4xQ\xb5P¥\xf0\xe89\x9a\x12>&zgိ\x8f*\x95+kɼ\x84'\xc5\x14\xbf\xcaW_\xa4~I\xf9\xb5\x8e\xc1Pg_\xf7\x10CO{b\xf1\xe2\xce̖\x89\xa3\xaaaշ\xf3\xb6N\xc9\xc8 \xf0\xd0\xf4\n\xa7\xfb~A'\x0e\x95/\x02I\xd9\xe5팟o\xb2\x99\xf5\xea\x1f\"\xa7\f\xdaܬ\x1a*0w\x02\xa7\xc1\xb9\xf3\xb0\x81\xeb\x8eK\x1a\xa3OLx\x8b\xfb\x89i1\xc0M*\u007fˀzܗ\xb1{\x99 \x9bm\xcdeJ\xba!,O'\xf6\xa8Z2N\x8am\x0e\xc2\x0f\x06\xe2:\xa8\xb4\x8eܢ*G\xc7`\xe0\xf4x]sҶ#fD\\\x98\x10\xfd\x01\xf2\xba\x15\xa3\xc4FI\x82\x9dHw\xf3\xed]\x12\xab\xaa\x00\x92I\v\xfb\xa2\x91?\xb87#\x15ȂU\x89.\xf15w5ɮ\xceR?7\xfb\xd6\x17\x90\x0f\xae\xe40\x1c\x91:\xbd3\xa9\xbfn\x13\x01p&9&\xe7Vup\xb0\x1d\xb4\xa4AFs\x90\x14\x9b\x8c\x1bUc;I}\xbb!\\\x8f\x18\xaeUv\xc2\x1b\xb4\xe5}\xa8\x84\x01b\x82\xbf\x0f\x99z:\xcd\xcd\xce9y\x1e\xdc! R\xd9\xd4\xd8\xd7ξ\x93\xee\x93\xde\xfc\xa9\xca\nN@)\x890ߗDd;(A\xb0Xr\xb4[B\xf9\x12N\xfea\x1d\x1f+\x96\xe3{\xc7\xcd?X\x81\x87\x13\xfe\xdf/\xa7\xaaJڽ՜v\xd6ݶ\xc36\xf6lҤg\x8c\xb4\x87O%\xba\xa5\xc1P\n(\xc5/V\xab\xcd \xeaj\x0e\x84\xfe>MT\x8d\xf2c74bɤ^\xec~^\xec()y\xc7\x14I\xbd\x8a\xc7\xd8Єe7a'x\x8cU$u8\x95\x91\xe6/\xa1\x9c\xe2\x9c\xf4N\xf2Ψ'\x16n\xba\xe0\xceh贑\x9e51\xdb\xe8\x19;\xbe^n4\x8b8\xeb\xbeߖS\xcdq\xcfF;\xe2 Jx\x0e\x89\xa4\xdc\xca\xf0]\xf0]\xe6\xe6\xfeY \xee\x1d\xd6MG-WM\xa8\xc6\xd5_\xc9\tK\x0f\xe3\xa6\xc7\xe9V\xa4gGg\x17\xc7\xe4\xe3\xd3>\x84\x95\xddW&\xfa\xc2\x19\x19i\xbd&\r\xfb\fəۣκ5\xcbX\x98n\xbc\x13\xd6\x17F>gla\xd1⧲\x8f0\xc3\xe6\x11\xc2x){\x93\xe5\x1b\xe28\xdc\xdc}>;|\xce\xc49\ti\xb8 7?\xc4\xc9kN\xc1\xbc\x1b\xce\xc1W\xb6\xbd\xa6\xe6\xff\x16\xe2\xb3\xfc \xf9\xd5A\xf2\xf2P\xf9Ej\xf6\x87p\xe5\xa6Y\xd2rҊ\xd2\x13\xf2\x1d\x92J\x16\xe1\x12p\x187\xc3~V\xa1\xbb\xda\xc0\xb78\xe0\xb4\xcbo\x9f?\x97\xa3\xda\xef\xbb\xcb\xd7\xfe\xeb \x87\xab\xda\xe7\xad\xfc3#JF\t;Sl6\x10QA\x80i\x12\xdd\u007f\xab\x04\xa4\xf1\x05C\xdf\x0f\xecf\u007fT0Y\xf2w\x1e\x02\xc2I\xfb\xf7\xb4+~\xd4\x1e\xb4[\x97\xb0kB\x1d\xb1\x05\xe2\x934\xd11L\xf9\xa8[\xa1*\xf3\xc6;/j\xd3\xfaL\x01AM0X\xe5}>\xfd\x10\xb1\xb9\xa4\xb2\xf5.\xe4tغu\x15\xc8\xc3t\x1fj\x8d\xb2\xc4iZ\x8d\xb8\xea6\xc4\xec\xa1)\xe3\x10\xb1\xa4u\x11\xec\x1ed\xf8\x9a\xf8n\xe3\xae\xfc\x04\xc0\xb9\x95\xab\xbf\x9e\u007f?\x94\xb4\xd9\xca\xca\x15\xee\n\x83|n4oZ\xbb\xf38H\xdb/\xa3\xd0h\xd5!\xab\xdd\x04\x06}\x83I>\xa9\xd7\x16\x1f\xde\xfc\xdfd\xfe\xe4\t\xe4\u007f_\xe3\x15\bY\xf63\xa3rD\x90wc6\xd3Z\xf9\xb5\xcfK\xe5\x05ج\xf8\xf7\xc6A\x1e\xbf;\xf8\x9fT\xc9\xf8\x9d\x05\x01 GX\x19Kb4\xeep\x00:I9\xe8\xebm\x8d\xca{#?{\xff\xc7X%\xf6\xa5C\xb0\x87KM;\xe1\x8c\xeb\xc2\x00E\x8d({vT\x126\vLa\x9bY}\xc6j\xe3O\x93\x8cѭT\xf6в\xbf`\x10u \xb1\xb9\x00\x84\x95\xed\xbb\xcb\x1fJ\xc4\xdaۃ\xf52f\x841\xf6\xdfD\x9a\xe9\xe3\xae/\x9fM\x1a\xfc\xe8R\x8d1\xb3Cb\x87\x16 @#\x0f\xc4^$yH\"\xb6c\x03\xe1%\x1b߀\x1b\xe4\xc0\x90\xe2.\x13Mt\xbe\xd8B\x9b\xc9l7 \xea\x94\xd5^\x84\xa7\xc8]]]\xbd*\x97\x02\xbf\b\x10\x18\x12e\xc2g^1:\x8e\xb3\tv\"t\xaa\xbc\xc52\xed\xe5=M@f]\xe7M\xba̟D_\x9c\xf1w`tј\xfc\xef\x02\x86\xc1\xff\xa6m\x9d\xac\xaa\x95\xc8u\x1eJw\xa5\"Bh\x9b\x96\x85\xb4O;\x93\x1b\x1b\xfcֽ\x04.\x84\x85w\x893,\xc3eJ\xfbVKm\x04\xc4C2LC\x14yӝO\xd8\a\x11L\xfd\x81U\x95{\xca/\\\"\x88\xe6\xf1\xefK\xb1\th\x81\x9e\x90\xaa\t\xbfbx\x1aZ\x88\xbf\xebLR\x12\xbe\x9a\xbai\xbdO\xc7(=\xee\xb6|\xd2\xed\xa4\xfc\xc7V}\x88\xba)\xf0\xb5\u05fe\x9d\xe0[[\x01\xe9\x1f\xdaP\xf6[\xcf\xf3\xee\xf1n\x1d\xcc\xf3\xe42\xec6Y\x17\x9dK\xaa\x95\t\xbfU\x18L\x13\x9a}\x03\x92W\x190$\u007fڃR:\xa0\xa7\f\xa5O\x00\xfd3Ij\xba(Β\x1fRօJ\x8d\x80\v\xf0\x91)\xa7\x9d\xd4\x16\xf9\xed\xb7\xa6\xb1\xb1\x05\xfb\x06H\x9bI\xfb\x83\xd9n\x9cS\xb7(\xcbg\x98K\xeap\v\xa5\xa8\xef2\x9b\\\x9f\x8f\xa6\x0eoN\xdd\xef\x81\x1dya軚\xf6\xaf\xc1\x9e8\x94'\xe4\x90\xdap\xb6\x0e\xb1%\x1b\u007f\xd7\x01K\xabEE\xa9gO\x18\xc5[:*\xcc\u05f8\x10\x00\xcf\x0fp\xba\x98ⳇ\x04W\xde\x1f\xde\xfa\xfc\x19\xc7\xc8F\xcb\xe4t\xf8\xb5!\x03W\xe5o\xe3\xf6ڧ\x82\"\xb2˲\"Cրo\x88\xc5o\xbe\xe5B\xadJ\xe6d\x86\x97;'K͒\xe0__\xf6\xfa\x9eh\xa1v\xea+\xdb\xf3\x93\x17\f\f\x81d\xc0\x81\x03\xb6\xe4\xc8ލ\x8a\x8b '\x01\u007f\x97\xac\x9c\x87\x92\x86V\x94\xaf\xe1\xb8m\xac\x19\xb3\x0e\xaaI.^\xc5˅\t\xc1\xbc\x1e8\xdf\xf0B\x9f\xe3\xafs\u007f\xadf\x89G0\xd8\xfe8\xe3ռ\x1a\xb2*ʮ \xbc\x99\x14꩐\xad\x11Tҕ\x84\xa3c\xbe\x1b\xb36\x88s~\xf8\a\xa0\xee\x02Jim\xb0\x17\x16\xe9\xb1\x16\xa4xY~\xad\xeaV\xd3)\x89I\xba\xb7ƛ\x15\xaf\xc0+\b\xd4\xe1\x1eh\x17\xfc\xf3\x81\xcaΜ\xc5;]\xf5\x9eE\x9e\xc4\xd0\x1cB\x01AА\x05Q\x99\x89l\"\x94U\xd5\xda\u007f\x86\xf0\xb5,\xdeC\xec)\xfe\x10\xe4'f\x9bC{\xdcKD\x86]\xdap#(\x1c\x05^\xf1\xce\xdf\x1e\x15\xba\xe5y\xf7\x0es=\xbc\x99\x18=U\xb9\xdcjo\x16n\xfe\x10\xf0lVe\x14\xf6u\xc9\xf8iJ+$\xcf\xdad\xc5U\xdd#\xba;\xf5\xb6\x05O\xeb\xed\x9c\n\t?9\xaf2\x9c\v\x95<\xd7;\xb2q>o\tT\xafr\xa4\x1ex&\xd6\n[\xb5\xbb'-\xd2x\xa6p\xa80j\xa7[\xf0;3\x0e\x9cIw\xe9\x8a\x0e\xbc\xb5\x806N?;\x9b\xb1\xa1\x11\xa9K\x06\x9a\xa3\x9a9YR2\xba\xf0v\xf1r\xf9D3\xef\xc4'\xfa\n\xc3\xf8\x99\x0fK\xb6gՂ?h?\xcbr_\xfa\xd4\xf8K&\f`t͡\U0005f789\xa8\xce\xedy7\x85\xd4&.\x9f>\xfdt\xf5\x01\xf4u\xe74\x98ߛ\xcd\x14G\xb7\n\xb9\xee\xdd:\xe8^\x16M\x8apv\x84\xefwڴ\xdcYz~\x1cڇձ\xf7M٪\xe5!\xc1RW\x9bd\xc5;\xb3#\xf4\xdf\t^\x83z\x1c\xd7ʈ\x83\xb4\xf0\xc5\x06Q\x15\xa9\xdb\ft\\\xa3Wy\x02\xbb\\\x14OJ\x1714\xbe\xc7:5\xd2\\\xb2\xfe\f\xf0\x97SXT\x85\xb7\xdd ݓ\x12\x1c\x9a\xf9g\xe7v\xb7\x1eV9\x8fUkX,m\xb6iM\xb2\\\xea\xe8\xb6\xfe\x0f\xc6\xee(n\x8e\x9e>E\x9dI\x0f\xa1 a\xbb\xb7\xefI\xe7i\x85_\x14\x1f\x93\xbd,\x83\x98\xda\xe2\xc7\xea\x1f(\xf7\r\x15;.s\x9e)\xb0=5\x9eA\x9eI\x9e\xe9(\xdc\xcbwX\xa1g\xa7}4Y\x93\x94\xb1\x91Dp\xc44\x97\xac{\x1a\xeajq(Q\n̷ZJ\xb2U\x8dZf\xb1K\xb1\xf5*\xc5x\x00C~p\"\xd2\x122\xaf\xacr\x83#\xeb$!\x0e\x8cJ\x1e\x86\x1d\xe4zZ\x05Y\xd1.\x0e\xa0^|\x12h\xd0\xfd}\xa0\x9d\xc8z\x8e\xc0\x1b\xb2Xa\xe3\x8e\x15I\xa7\x8b\xa8E\xaaXg\xd0t^4\xabR{\xfe\xb3f\x14\x1a\x8aL\xc9\x11y\xd3\u007fp\b\xd1ᚚ\xc9\x06\x001ި|\xb0O\a\r\xde\x142\xdb\xe05\xde\"\f\f\xf2t\xc9U\xe3\x92A\xe9ޗ\xf2\x95@\xf6\xff\x11u\xfb\x1bR\xe4\xa1\xfdP\x1a\x04N\xb6X\xc61\xa2ZN\x06\x81\xfe\xff/\xa8ܨ\xbfx\xa6\xf6\xfd\xaa\x95IQ\xaf×\a\x06\x96\xc3_\x03\x04\x8ey6\x90EK\xf8\xaa\xa1\xa5 /\xbc\xba\xa1\x11\xb5\xf7\xc7 cuD\x96o\xbf\xba\x807դ\xaf\t\xd0\xfc\a\xd6\x1d\xdb|2\xfc\xd0V\xe6\xfcC\xe5\v\xcef+H\x8c\xbd\n\xb5:`\xbew\x9f\xf9\x01i\xf2\xc4y\xdb\xff~wk\xb8\xe5\x93\xd6t@\xcb4OE],\xfb<\xbb\xfa\xff\xc9ͦ?\x0fs\xe6\x12\xa1b\x181-\xab\x84\r\xf2J\xbf\x92\xdf\xc3A\x9c\xb4\xa7\xa8\x8c\xd2\xf1\xfeA2\xe0\xd2-=\x86\xc9t\xed칙C\xc2õ̍\xe0\u007f:\xf0\xc0\x0f\n\xe9\x9dBa\xb3;\x05\xf5W\x17\xe9\x92CE\xa2Ξ\x16r\xff\xe5{\xe7`\xc9&\x89\xc4,\x1f'\xf4\xdbt\xff\xdd\xec\x15\x8e\x8a[8\xc1\x17q\xe1\x89u\xee\xf8\n-(\xc7J\xec]\xbb\x984\r\xa8\xda\xf3\x99\xb2\xebʹ5\xab\xe7\xf0ay\xfc\xb7\nhh\xfaY\xb1\xfd\x99\xb8.\xef\xfe\xcb4\x9d\aj&\xb0\xb6\x1a\x934\xdf\xf2\x82\xb6a\xf6\x1c\xa2\xf6\v\x83q\xcb'(\x9a\v\x855\xe0\x97\x15\x13\x1d\x84\xf2\xb8\x14s\x1a\x8aX\xc3G\xf4jWB\x99\xec\x86\xde~\xad\x92\x85\xc9cm\b۶\x18\x8c/\xfd\x99.\x016\x1d\xcaa\xff_\x1bA5\xbc\xfd\xfb+=d\xbc\xdc\xe3\f>Ĺ_.\x19\xee\xaah\xcf\xcf\xc8\xe88tB\xe1s\xb8\xe80\xc4\x0fHJ\xc6\x1f\x8al\x9c\xe1\xa1l[UH4\xf6v.\t\xbb\xca>]\xc6(\nk\x8c9.\xa0 U\x94A:,A-\x15\xac\xbf\xc6w\xe6\x85yʰ\x90҉\xcb\xf1V\xe0jVU\f\x9c^\xbe\x98\x81}\xb0|w\u007fTH\xc1Ә,\x1f\x03Aq\xc80;,\xf3ZD*\xae\xdc#{\x9f\xbc\xadl\xccH7\xe0\xd5b\x1eRX\xef0C\x81\xc7d\xcduBѢ\xf55\xd2d\xa8=\xcdV\xb0\\T=\x9bQ3\x00\x13\x8b7o\v\xedq\x13A̐A\xfb\x8c\xebO\xfc\xfc\x06l\xeeܿ\xa2\x9f\xe7!\xfb\xad{_uD\fG_\x93\xad\xcerk\xf6\xafߘT\x94^\x98\x90\xa2}W\xd0o)\x18.8\x1a\x98\x98\xac\xa5|gW\x0fP\x95Ce\xeeJ\x9f\xbd\xf6\x9cx\xee6N\xb2\xd3\xfe\xff\a\xdf\x1f(\xc3~v\a\xfc_\x99;\xa2Ξ\x0e\xaa\xfe\xe0S?\b\x8b\x99\x8bW#\x18M\xfc˿\xdf\xd7^\x8a\xdb\u007fS\xe9\xc8\x16\xa1\xcfmG\xff\r\x1bθJ\u007f\x9f\xedQ50\a\x06 \xe5\x99i\xba<\xa5&+\xae\xc8;\x17\x1e\x85\xf1V=\xf4K\xaerU\x84\xbc\n\x9c\x1fe\x0f\x93\xb6\x83\xa0\xe6\xc4#\xf6\x02\xa7,\xcb\x10t\xac\x9eF\xa4jë\xaa\xc2Γ\x1a\x98U\xb2\x04\xc9|N'uL\xa4\xe2\xc3\x1c\xa1\xd1x\xa5\x87&\x8a)\n\xa96wrroG\xe6\xa04\x01\x17\r\xb1\xbe\xdeL\xdbR\xb7\xba\tgn\x94\x88Za\x9f\xa3\xb9\xc0\x94#t+\x152\xe4>if!\b\u007f\x12ϥ\xbd\x1c\x11)Ǿ\xf9>0$&\xdb\xcb\x04q\xa4\xc0\xbc\xd8\xedqJY\\\x92\xa7\xdaI\x02\xd6S(\xa5ˤ7\xb8^\x1c+\x0f\xad'\xd5\xc0\xd6\x19\x83\xc4w\x94ٚ\xeaze\f\x02\x8d!\xf8e\x95\x86-\xcb\xe1\xcfݙ{\x98\x85a\xba\x05\x8ew\x1c\x03τ\xc4\xfc\x18 \xa4\xed\xbdK\x1c\"\xabJd\r\xc1\xe6\x1dLy\"F\x89\xcdջ\x0f\xe3P\x8c\x00\x1cn\x88)\x0f\x87ж\xf3\xdc\xf5\fw\x97-YU\xb8\xff6L\xfa8\"\xb8!ѡ\xff\x19\xde|\xceF\x15\xfcj=c\xf4\xe8Ƞ\xec\xc0\x80\xec\xe0\x81E\x9d\xed\xf6\xc8\xceR\xfa\xd0\xc3z!\xf9<\xabnU\x17<\xa4Qc\xaa\xe6\x8c\xe7\xf0\xda\xf0\xf7\xf7o+\xf1\xfc-`(|ɍ \x87\xc8\xee\xa1O,\xf0\x8at\x1c\v\x16cR҇\x13\x9fӆ\xae\x9e\x05\x8a/j\xe4n\xed@<\x97Q\xa0g\xd8\x1a\x9b\x18\xab\xf9\xf74\xf4\xfc6\x9f\xa1\x9fW=Z\x8dڒ\x929\x99\xae\x9d4cK\xa8\xb6\xd6&{\x9e\xdb\xde}\xee8#Z\xf3X\xf3\x1e\xedWU\x17U+\xe9\xfcQG\xce\xd1\xf9Q\xf3\xdc\xc6\xdf\xe43,\xe4\xeb/\xe0%\x8aEo\x9c\xa3\x02\x8e\xdf\xc3އ\x0e\x14ռ\xe8\x0e\xe7\xf4\x95d\xcd7z}\x04\r#\x1dO\xb7t{yD\"3\xe1\xac\v\xbb\xc2\xf4K\x9e\xced\xae\x95\xf0 {Cu7\xfd\x90'\xd2\r\x1aC)n\x840{\x1e\xcf4\xack\xa6\xad\x90(|\tu(5\xfd\x95u)\"\xeb|\xb6\x14V \xfe\x94Wr\x14\x9e\x9c\xf0e\xae\x14\xf1n\xdd\u007fn\x93\xde\xebXW\x84O\x1e\xa4{\x8f\x84\x92Bu\x99WU\x87\x06\x1f\xa9\x92,2L!\x8d(\x8bK}\xb8\xa2\xe4\x8a\xf6=\xef\xf3\xa3\x86\t\xb2[\xffMP)\xe2s2\xf1\xdbl\xa26%\xe0\x18j\r#\\J\u007fg1a^\x1c9Q\xec\x1a\x1f\x1e\x17.\x99\xfd\xd8F\xce/\n\xf6\ny|x\xa4\xe4\xf8&\x8e>z\xd4|%%N\xa9\x88{\x90\x069\xb4\xb5c\u05c9S'I\xe6#\xe6\xfa\xc0ܳ&\x91QF\xbe\xe5\x8cn\xb6๕\x92\x12\x19\xa1\f!\x1aJƄe\x03\x99e\x91o\x1c},\xdd\xd0X\xd6M0c\xec\u007f\x02s9]\xe9\x98\xc3e\x0308u\xf8x\xac\xe4\xe3\xc1\u07be\xfdB䦂\xc0\xe2@h~T$\xc5%\n?\xee\xcf-&\xec\xb9=\xa5\xdd\xc4Es\x9b\x02\xa6nϨ\xdb\xecf'$\x82\xf6Є`9\xe5\xcfw\xa7\xa3\xdf\xe5\xfbvȒ\x9cߖ\xd3\xd2\xe2$sNy\xb17zԯ\xe93.ɉA\xbb>\x85\xf2c\xbe\x9f\xf2\xb1,v\x99\xfcA\xe8\xe2\xc5?p-?\xda\xf5#\xfeG\xcev˧\xa6hm,\xdd\x14Qv\x96\xa6G\xa2=KԾ\t\x86\xffn\xbck@p*\x83;r\xf4\xdeQ\xfb\xfc\xb3w\x9cZ\xba*ړ\xf8Ǥ\xcd\xe6\xf4 \xfa3ν\x93եwR\xf2\x01-\x85`Qz\\\xefӧ\x8dv\xe9\x00\x81c\xf4\x8f<\x80s\b\x00\x89\xb8\xd0*)\u007f\xe2\xc6\xdf%m\xe7\xbfgNܦ\ueeadIy\xb2~#\xe7\xe5+U`\uedf9~\x82U\x8f\xce\xed\xf2\xaf獫l'\xe3\u007f-\aq\x12\xf4'\x12\x89\x84֣\xa2\xd5\x01\x8f\xdbh&ɚ\x02,\xd8B\xbfL\x13\xa4\a\xc0<\xafg\xe4MIM\u007f\t\x00٧\x00\xe5\xfb\x8e\x12\x18\b\xe1\xce\xc2@nf\xe0\x00\x86\\\xa0\xf8}\xc1\xb4d\x1e\x1co\x98\u0087\x89\x8c\xed\x8c[\x176[\xaeB$\xac\xba9-\xb7\x9eR]Ղ\x93\xe2\x06\x06T\xcd}\xedu\x12A$\x9b+\xc1eҢ\x144\xc6k\x00\xfa\xbb\x8ev'\xef\xea\xe9\xfc\x9f\x96^K\b\xce\xec\xd2\xca\x1c\x186\ra;8d-x+\xb5\xd5\xe1J\a_\x86u\x15_2\f\u03a2G\xaf\x01\a\xcar\x03e=\x1d\x9d?\xc0\xdc\xfb\xdb(\xb6\xfd\xfd\x03\x81\x8a\x13w\x87\x94\x1b//\x94(\xc7\x02_`\xbd5\xfaw\xce/\x9e/+:X}\xb7Z\xfcX\xd6\xc2.ruȐQ\x96\x80\x97\x81W&\xed\x03\xdceUs?\u007f\x81z\xf0\u007fN\x83\xd2|jj\x04\x1a_\xc8Mw31\x87\xe4\xc7\x11\x1f\x0f#q\x16J[u\xffWF\xf0x\xf9Ԏ\xb0}\b\a\xday}M\x85r\xbd.)r\xe8\xbe1+\x89\x1b\xb5\x17)\t\xf3\xa5Qn\xb5\"|D\x1f\xc2U\x17)^8s6\xdc\xfe\xed\f\xfdc\xab\xc4#\xfb\x92\x0f\xa8A\xec;}\x97/\v\xdf\n\xa3\xc2\t\xcf\x1b\xb9\xd2?\xef\x1b\xd6K\u007f\xe4\xf0\x80θĻ\xecG\x9f\x9f\xfaMg\xddi\xbd\xa0\x0f9^#0\xf3\xcf;؎\x97\x12Jb\x18\x8b\x8fꘙ\xd8\xda\xf6#\xb2<\x9f\xe8\xebM\xebz}\x1etum\xfe\xb8k\xfb\x8ep\x87\x96aS\x16\xac\x98\xae\x9e\xd3\x16 2\xf3p\x18.A^S1\x9e_.wG\x1fao%7\xae,SUW\xa9\x94Օ\n7\xe7md\xb4%E\xf6=,\xdbP\x85\xde\xea\xa9[Ұ\xd3劚\xaa\xf5\xedl\x8a\xed\xe7\xf1K\x90=3\xba>h:pZ7\u007fן\x1f\xbbg~\xff\xde#\xef\x8a\xd4;\x1cxD\xaft\xdb\xdfO\xbb\xa6\xfe|\xd9\xdetҺ}\xd4\xd5&Y9\xfd\xe7\xe7\x14\xbaƮpbuU[]\x90\x89\xf1\x98T\xba\xa2\xd8ι#\xc2\xe1\xaaU\xd6Fo\u007f~\xf9\x16y\xceե\xaa\xa5j\xd8\xcc`a\xee\xb2\xea\xe5\x85\xcb~\xaa.;\x15&\\UB\xc1D\xae\xc8\b\x19\xbd\x93<\xe1\xa2\x12\xf0\x03j\xc0\x9a\xc15y\xa4уo\xe5)]\xba\x8d\xef\xb1,\xbd\x84\xb6+\xe8]\xc7*\xd1D\x8c\xac89\xbbż\xe2\x9a\xf6\x15\xa6\xa0mS\x8a\xddT\xa5I\xcc\xf69⺹\x03\xe3\xec\xdb\"\xaa\xfe\xad\xc4_KKgh\xc5\xf0&\xc3\\^a=\xa4\v\x12X\x86\xa7\x1e\x10\xc6\xfd(\x89\x1cu`\xee\xb3\x1d\xa5mgO\x1b,\x8bӉ\x86\x02h}\x81\xfb\x92\xdey\xa8\x06$\x99\x03ے\x12\xa4$\f\xdfE\xc6\xfb[\xd3\xe9b\x92\xc7\xe6\f\xb3\\\x1dڊ\xc8xl~[\xc5\xf0\xbe\x00\xd1\xd3l:\xb2\x0e鈼\xf6,\xb5\x12\xb3\xdd\xcdg\x03\x9f\\j\x81\x1b\x11\xadgY\x13\xea\t\x8e\x88'\a\xb4\x1c\xe5&f)\xe1\x8bGL|\x9fƭ*\xe1\x87\x1cQ\xdb\xdcpr\xa5\xa3\x83~\xee\x1a;\xceZ\xf1\xb4\x92\u007f\xc3I]\xac\xdd\r!<\xf9aP\u007fB\xb7I\xee\xf3\x0eb\xb8CUxЏ\x1c\x0e\xf6\xfdEg\xff\xc6C\u007f\xff(\xa6<\x15\u007fg\xb7d\xef\xdb\xd7РrM{LWҮG\ah\x127\xfc9\xb3W\xf7Fɜ,sR\x06\x85\xcbߕzH7\xd2zϙ\xccFrcHK\xfc\xff\xf8\x1a\xdfo\x19S\xae\xe1\xe3Fa0\x19\xc3\x16\xadz\xb2\x81\x95h\xc7C\xaa\x1c\xce\xd7:+\x06/\xa1\x9d\xaf\xd5ҭ\xfd[-W\x98\xc0 p\x02\x94\x04\xc0\xd03\x14v\xe8\xed\\uq\xc7GG+ԛDX)\xdd\xe0\x9e\x03'\xd6\x06\xe3\xb7&M\xebu\x19Ɨ\xd3Û~E\xad\xbcވ\x8cW\u007f\x17p\xa7\xf6\xdb5\x02J\xd9pG\u0560\xd3\xd2\xf4\xb40\xcc\xea\x8c_\xd5ԍ\xaa\x8e\x00\xdfqW\xffĘ\x94\x98\n\u007f]5\x98x\f\x92\x00\xc9\xf6\xa0\x9e\x16\xe0\xf91\xcbyu\xd2&\xe6\xa7\x0e\xaf8Hȏ'\x13\x9f\xfe;\xa4\xd2@\xbbq\xa2\xa6٘\xec\xee\v\xaf>\xc4\xea0S|\xdb_Ae\x91g\xae<2\xcc8\x10\xfd@\xad+\xe9\xb15\x94\t3\xfbgKp:\xd6\xe9\xf0\xacE\a\xeaL\xde\xffBv\xffK\xaf\xae\x8b\x89j\xfb:\xe2\xbd\xee*&z\x85\x94\xdd\xe0\xa9\xfa0\xe2\x0eV \v>\x0e\xa1\xcdG\xb4X\x1d\xfeCJ\xa3\xeaI\xf2O\xb2\xf7Er\xb2\x03\xb2\xf0\xe3\x10\x13W\xdf\xed\xc6\x02\xedb\x95$\x9fW+\xc2^j\x92ɒ\xac\xfb\xfb\xf6\xf5ϖ\xf6\x9e\x10\x88\x856\xe0\x1dH\xd3X#18\v\x8dˌ5\x98\x91\x15\xd6\x03\x14\x97ԋ\xa2\xf4\x96\xd9`\xa2֩\x8ewG\xaeU\x17\xca\x03,\xf0\xb7\x9f\xfe03\t\x8f\xa0\xab\xec\xab\xf4\xda\xe7̵1\v\xc1\xd0Q\x99&\xc8\x1e\xa2\x9cg;!\x8f\xd0]v\x9aX\xe6\xeb~0\x9ca\xf6\xb9\v\xb9\x9b\xc1\xd5\t\x8f\xf8\\\xbdM\x81\xb8\xfc\xa1\x86F4C&h\xe2\x99 \x8dV\xd4Ӿӗ|\x8e\xac\xae怙w9\xea\xa3}\xc1\x9c9\x8d/\xb5\xedH\x86\x14Y\xbc\xb4\xa5\x9a1\xea\xe6\x95˚\xb5W\x05\x85(\xd4u2ig\x16o\xd4}9\xa8~!V7\x1b\xd6;\x9d:H\xa9\t\xf3xǗ\xd7\x13~\b㲿\xa7\x8a\x0fv\x05W\xdcز\xfbj\xa1\xcb\r\xfbw\x8f$\x19\xe2\xc4kʪe\x9a\xbe\x881Z^\x9dW$S\xd7+ļњ,\xa9-\xab3\xad\xd6\a!\xeccm\xee\x99h\x16\xfc\xba9\x95\x8e\x9b\xa1%\x06 Q*;%\x81_\x96\x0f\xee\x00\xa88\x88FV(\xb3\xd5\xf8\x86\x9bs\xf8߷f\xcc8\x14d\xf5\x9b\x9d\x17\x9f\x10\x9cشgm5@@7V։\xb5\xc9!)\x91\xde\xc2^`\xac\xd6#m\x8c\x13܊\xa5\xbb\xbe\x87G\xb1k\x82!\xbd\x06\xe3\x06y\xc3u訦\x19\xb3\xe4\xd4(+\xcbq\xfe\xd1:\xde\u00ad\xd1D݉5/\x80\xcbb\x8d\xb3w\x19\xed\x9e\xeb\xf0\xf2\x89\xf9\xd4\xc8b+\x19\xedb\x0f\x16\x9f\xf8ᎁ\x9a\xed\xbb\xd76\xce}\xebH\xafЛ\x87m$\x9c\x9ate\xd61-\x1eě\n\xee\xfc\xb5\xbc\xbb\bG]i\xb9ܘ\xf0\xa8\x99\xe0\xb9$\xbb\xbaQ:n\x1apy\xeb\xfes\x86ǩ\x89\xe9B\xd9\xe0q8\xf9\xa6\u007fH\xafr-;\xa1-c\xffN\xbf*\uf68d\xa5\x12r\xb3J]\x15c\xc4\xf2\xf3\xe8\xfdG\xd6\x1bY\xf9\xce\xeducyUk\xdbu\f\xd4\xca\x18\xbfDQ\xa0\x1a)\x8d\x9f:4^\x9c\x9e\xadK<|\xe7XE\x95ޚ.\x14Hx\xa3r亞\xa9j\xc9\xf8Κơ\xf1\x86\x05\xfb\xe3-\x9b]\xfd\xc9eU6\xa5\xff\xa3x\xacbk\xae\x17\x13\xe4\x1e\xa0_lo\x9f\x18\xd3\u2c2fuv\xa6\xb8oL\x0e\x94zA+\xd4$^ҕ\\w\xea\x02%>\xea[\xa1P\xa0\x8fG<\xaa\x9a\x99\xa92\x1a\x95\xd3\xe2\xf6\x8f<\xbdU\xc7\xe4\xe5\x8e\xeew\xb0\x95\x9c\x98\xae+=ܧT\xf32bwݠwx\xb6\xbd\x9f\xf2\n\xc8ay\x99#G\xe5\xe5\xc1\xa8\xe7t\xc6s+\xc7s\xaa\xe4\x8a\xca\xca\xf1\x0f\x96[\xf2\xabU\xd1Y1\x8b\xcdn\x1e,\xa6,(\xa74\x9c\xf7c$\xa1U\xccS\x99\xeb9\x93\x91\x9dB\xe7\x8c%\xc5Z\xfdH\xab\\R\xe8\xb7\x15\u05ee\xf5\xc2\x19m\xe8YZ\x84,]\xe1KH\x92[\xdaE\xb7\x10ÿ\xc7\xe4/l;\xdff\xbd\xeb\x12\n\xe8\x9f$\x806\xa8\xc2![aB\x85r\xef\xeb\xd6d\a\xb7Z\xf5\f\x8c\xccV\xcfzoْ\x88\x04\x81n\xd0H\xef\xa5\xf6\xa5\x1dK\xb0V\x16U\x85%\xb6\xce\x0f\xd2\xc3)G\x92\x8eB\x99$\xbeE7\\f\xfcYֵT\xe4\xae\xed\xb8\xc7\n\xf5\x06Kg\xde\xdfɷ;\xa27\x1b\xef w\xaf\xa9Bh\xfe)\xf9k\x85\xcd\xcd4\xb2\x8d\x97\xff\xe9\\\xf3\xb3r<\xc2zu\xc1\xabt\xabS\xe5o\xd7\xf9?(\x18#\xad\b\"\xa4*G\x84\x99\f\xb6\x1d\xba\a\xeb\x0f\xa7\xaa\x19\x8bF\xa1\xacn\xe3D!\xea$\x01S\xabx8\x90\xa0;\xad\x8a\xc4\xcf;\xc5(\xba\x93\x00\xa5~\x1b\r\xaaWou\xba\\\x18\xcb\x1d\aHt\xda*GĞ\x0ev\xca:\xaa\xd3[\xe3\xbeL\xd7r\x98\xde-\xa0\xab\xa9y\xfcG\xd0m\nk\x83-6K\x17\xbf=\x829\x01\xd8D>\xb9Gk\xf6\xbe\xe2\x0e\x9baD\x18\xeel9\xe0\xab*K2\x83\xcd\x14\x8b\xedJ8Os\xfc\xd3\x1e\xbf\x8f\xb2\xf5P\"偙\x13\xf3bN\xdb%\xae\r\xbe\x16\x04p\xee\x06xc\xc8\xccN\x92&a\by\x8d{\u007fM\x8blƪ\a3\x1e#L\x8dmN̕&\xd5>\x18\xec\xf4\xb34w\x12\xcd\x10\xd8ՙި\xc3\x17\x86\x96\x18|3\x91\xe0\xe4\x96\xda}+\xade\xd5\xff}\x87\xe8\xfe\xa6\xe1\x8d\xf8\xfa_\x83\xc8\x10\xd7,\xbe,A\xcbL\xe8\xe3u\xb4[\x99\x9a\x9c\xf9\x1dϲQJ5\xda'z\x99\x95\x0f\x9a@Nԝ\xcfZ̉\xa7ED\xce@\xa3(PVdl\x93\xac\\8N\xe9&,\x9c\xb0)I]\xb6d\xab\xe6\xa4N\xf3\xfe\xcf\xdeY\x168+\xbbʞ\xea_\xbb\x06w\xa0\x04\xce\xfb\xec\xffu⥊\xec8\xd7#\xfb\xe4+\xf31\x02d\xed8s6\x81\x92\x03\xf5\x9eǬ\x1b}壯\f\x9f\xe2\xd1\xf4Uy\x8b\xf2fc\x90\xc1\x99\xb8+\xca\xd4\xfb!)\x8dȦ\xb3\xde1\x9b\xb7\x15\xbe[\xe7\xb2N\xeb\xb0\xf4\xea\xd7}3ǮIG\xac\xdbu]\x8e\xdbx~\xde^\x85ʔ\x984\n\x9d\x99\x9bqd\xe9\xe6\x98\x03\xb8[\x1e>\x94,{\xec\xae1#\xf1^3\xaa\xefID\xef\x9b\xeb=\xebq\a$\xb1%\xf2\xa0ɥ\xc2\xf3:A*\x81\xdeCg\n\vR\xc9\xc1\xc4@\f\x95B\x94\x86H\xfd\x8e\x98@\xe4!\x1eTn\xfd\x9b\x15\xdaw\xb6l\xe2\xcd˭\xf0\x15\xd8a\b\x14\xb1]\xee\xcd\xf3ɬ\xb2\xc3\x11\xf4\x03z5\x13\x9b\v\xb5\xd2\x18\xf1\b{z\xdf\x16\xc5\xf11\xd2\x04R&\x80\x8fl\x9e\\Wџ\x94\xfbg\x82\xf0\x98\x00\x92EIّt)\x87\x1e\xa2\b\x848\x90R\xa8Tp*\x03YM\x9bڋ\x9bFfR\xbe8V\x97Y\xc2bJir\x165Fč\tN4e\xacgH%\xa7<\x90ټ\xa5\r\x84\xbc\bn\x8aj\xccc*v\x84\x9d<᧼\x15 /\xef\xab\xe3\xe2U\xf6\xdbjao.lG\x05\x8b\xe5\x92vA\xb3vP\xc0\x02\xb2\xf2\x04ؠ\x84Z\xabj\xaa\x89\xfb\xea9\xb7IdA\xe0\xe7v\xeaƉ<\x9f\x8f\vjO\xef\xeb3\xb5\xafj\x1a\x0f\x895\xcfKh\xf4i\x03Mt|\xb2\x06\xa8\aen\xdf*=-A\x00B\x8a\xf3\x98\x05Q\xf5\xf8\x95\xf0\x1e\u05cd.|\xa7\"?\xa9\xe3Ïs\xb9\xee\x9d\xd8\\Z\xde\xe5\xfb\x1b%\xd2\xfe\x1bg\x92\xf3t2^L\x90#;K\x9e0>;\xb2\x1f\xc3\xce!\x90\x93\x12\xf9SSI\xd5\xc7\xfc\x04!!\xaf\xd5\aH\x87>\xa0S\xd5\x14\xce|\xbcB\xbeϵŵ\x1eQN\xaf\b,$\xab\x16,J\xef\xcb,\x01\xad\x9fy\xf3a\x8e>A\xa2\xf0\x02\xf7\"T\a\xe0S\xe8\x83M\x9d\xcf\xd6\x0eK\x96\xbe\xfe\xd1\xed\a\"\xc3I쫈\x9c+\xa9;;\xa5Ӽ\xe8\xa5[\xef\xe9\xe15\x90\xad*^\xe5\x9e1!\x89\xdd;\x00m\xc9--?wb^e\x99\xa2Ci\xe8O\x15{\xe5*\x82\x9d\x03\xd9N\xf8C/\xfa\x8d\xef.M\u007f\xe3s'\xf8\xce\v\x1f\xb5\f\xb6\xf4\xd0f+v\x9aS\xf8'\xeb̘\x88\f\x81\x02\nT\xebkO\xf4H\x8cL\x96\xc4T\xdapR\xa4\x8fs\x19\x87#2\x04\x1c\x94Y\xdf\xd6\x14@\xb02\xbc\xe6\x81N\xf76^T\x9a\xbf)u[\xcc\x15>4(n#\xe3\x11*w\x9f²Jb\x9e\xd2\xdd\xff\xae$Ȥ\x95\xd6F\x0f\xfaTxM3\x04\x13,\"&\r\xf4ܴy\xb6\xcfWm\x1b\xc2\x01\x02\xc7\xc0\x81\xce\f\xe7\xaa\xfek!o\xbf \xaa\x84\t\xe5\x16\xfa,\v\x12˒\xd2e\xbe\xaa\x8d\xac6\x99G\x8cG\\r]U2%\xb6\xe88\xccWH\xef\x85\xe5\xb0\nC\xfd\xd9\x1c\xa2\xc4Qo娣\x96)\xb3\x9f\xc0*\xe9[zb\xc32\xe5n\xa3\xae\xfc\xb5ʹ\xa8.CL?\x9b\xdcg\xfa\x9fl\xd32\xdb\\\xf5\x94#\xa7.W\x98\xaa\xf2Y`\xe9\x1d\x1eWG\x81\xc1>r8\xf4\x8e\xa6\x13\xeee1\x1b\x8a\xba\xcf\xfa\xeej\xae\xc9B\xf3\xcd\xe3\xfd\xf3\r\xff\xd5U\x95\x98\xbfq8\xa5`{\x1fl_d\x98\xe79)\\\xbe<\xad\xba\xfe\xb1r\xc1.-\x95\x0f\a^\x88\r\xa0BtR@\xe4\xbb͓\xc6f\x9d\x048\xf1w\xf1\xd1\x05\x01<_I\xc8\xee\x82\xc0\xad\x18\x05a\xce\xc0\xb7\xc2Ɵ\x10\x18̢\x04\xf8\xb3(\xc1\x81\x11C\x9bU\x82\x03\xdc/)\xab\\R\xd6\x0e~\xfb\xde\x1f\x82?~ۨ\nÿ\xb7dZ\f\xeb\xed\xabZەGƐ\x83\xde\x18\x11\x91\x80\xfbrg\xc2\x0f\xd9\xf1\xb7Jp_\xfd\"}\xfbIe\xcf\xf5g\xa6\xac\xba\xae\xbd̒\x166-\x17\x04G\xee;\x9b\xd1\xcb\xe2\x0eK\v\xc6>\xa9\x01$n\xdf\x1c\xde\xd6\xed\xf8\f\xca+\xff\xb2L\xc1[\x80\x91o\xdd\"\xa0N\x9f>\xf4\x89\x8d\xf1\xb6\xafe\xeb\xd0Y\x91f\x1a\x82\x9bC-\x9b\xbd\x02\\Qz\x84\xf4%\x81\xecse\xec\x1d\x1dg\xb4\xa7\x83\xac@\x9f\xb9%\xed\xa9\xf0\xf3\xf9\a \x16I\xf3\xc5\xfe\xb6^\u0604\xad\xbc\xcf\xfc\x8b\xd8\xf3\xb9*\xcdӬ\x88\x95\x1c\xe5\xec\x1eD\xe7\x80\x06\x98<\x92\xad\xc3\xf5\x8a\xe5!\xaa\xd80\xe9O!\r\xf4w!\xf5\x95\x86\xa9ޞ{\xfb\u007f\xc1\x1b\xc0\xbcD\xebS\x9a\xe3\xec\xd3r.\xa0\xfa~\xa7\xff\xfeB\xb6z\xe3\xe6+\x85\x1fB\x1a\xeamA\xea*\xf6+y\xbd\xaf\v\xfb\x1d\xd9(k\x00\xc3\xc8\x13\xf8\xf5w\xeb_\xed3d\xdc\x1f\xde\xe2\x16V\xae\xbby\xfe\xd4\xf94/ܺ\xc2\xdfY\xad\x80\x99hs\x92v\x84zJ\xde\x050\xefa\xe1\x14p6\x1b7\x9f\x9aX\xbd\xde6\xa4\x9e\xaf\xc5\t\x9e\xe9y\xc5n\x01\xceo\x15\xa3\xff\x9b}l\xa7\x86k\x01\xf2\x83\x8en\x9er7\nyyh\x10\xddD\xa5bK\xa5\xf4S\xebOR5p\xa58\x1f\xec\xa7.T\xaca[\f\xab\x91Y\xbbhKHCJ]c@\xb9/\xe5\x89s\x01-\x1d`ϼ\xb8\xa2\x8bE\xb6\x8ea\xa4Gk\xa6Yr\x9a\x01ʇ\x85K<\xf3E\xc3\xc1\be\x8e\x9bW\xf0V\x03\x90RP\xad\x85\xfct\xacG+$\xef\x8dд\xf7 \xb4\x8a\xf1\xe5R\xb0\x93b\xa0\xcd͇\x1eP\xe0\xc7TE[j҅\x8f\r\xde\x0e\xf4\xe8\xbd\x10\x18*\xb1\x85\xb5NU\xb6\xa6Z\x9a\xf2{\x01\xa6V<\xca\xd5\xf7\x8e\xf4\xb6\f\xad\x1cQ\xf1\xe6\xca\xfe\xe0\xad\xd8w\xfc*\x93?\x9bӃ7N\xea\x95\fs\xfbO\xa3\x82\x14\tj\x93$\x800`۱\xfb\xc1\x97/\x1c\x0e\x91N\x9cK\x1d\x9f]ϫ\x92]\riZ?;\x1a:w\x88\x81\xd9\xfe\x1f<\x82\xc6\xf4\x87\xa7\xf5\xd3\xfa7҆\xc3҇\x95\x1f\xbcߦ\xb4٨\x1fnV\xeewl\xe7}D\xc5\xc9A\x84%\xfcyv\x8f\x98\x04\x88+\x84w$,\xc6\xc8Xl\x1b\x0f>\xdd/j\xfd\x1d1\xc3'\x0f$Y\xa7\x10F\x9d\x97\x0f\xf5\\\xfc\x83(\b\xfd\xf8\xc8AЃ\xea]x\xef\xc1i\xf1Zk\x90\xb0\xe3$5\xa0\xec\x05U܈\x88?\x03Z\xaa\xb5N\xfe\x03:5\xdcZ\xbf\xbf\x01\x02C\xe6\x05'Z\xeaܤ\xca}\x1f\x14w\xa1\xa7\xda~HE\xe3\xde\x01\xadVN'\xf3O:\xa9R\xc5\xfd\x18\xa8|J%\xac\x12\x14ء\x18\x02\xcc\xd4C.^\xe0\xb9\xc7ڎ\xe5`\xcb\xdag͐(3!\xb6\x8fa\r\x8c[0ɘ\x0e\x10»\x00#\xa0\xa8c]j\xa1\xdb\x1b)\x98\x81`\x12\x1b\xef\xd2rsJ!\xa5*j\xb6\x05\x12\x13c\xf5\bf`\x14\x94\xbdo\xcb+\xe6\r\xae\xef\xed\x1d\x19\x19\x1b;mx\xa8\xbc\xb8x\xff\x81 \xb5\x1c2=\r}\bJKo\xab\x9b\x95\x19\ra\n\xe5\xa9\xeaXN\xe6\xba-\xe1\x04\xbeK\xba\x03;\x14xL@@\x1d\xc1\xb6\x89\x94a\x1d\x8b\xf0,\x04\xe2\x89\xc8\x11\xa8\xaa\xccu]\x1dϺ\xa9\x9dU,Y\xf6;I\xeda\xd8˯\xac%\x16y\xcb\xc2\\\t\x88\x1c\xc0#\xb92\"\xa0d\xb9\xc1\x11\x89aE\u07b5>P\x95~\xaf?nŠv]wZ\xc4\xef\xccY\u05ec\x02\xce\uf3b1\xb0\xf0\x80\xb7a)\x88\xe6\xde3\x9d3t2\xd0\xf1\xe3\xef\x0f\xf7\xadT\xfe\xfd۷MN\xef\xf86=\xed\x9c?Cݹ\xd3ސ\x81\ad\xff}1\xf0\x19y\"9\xf5gV\xe0\x15\x0e\xca˚!Z1\x03\xb1qz&\xdfW\xba\xb1w\x95\x8d-f\xa3R\x9c\b\x84C|K\x0f\xc2>\x1d\x0e\xc1\xfe\xa8'\xdfcw\x00A?`6$,\xd8|C\xc3kٝ\xed\xe40\xe9->\\\xf7#\xa8˽\xc8\xf7\xc2\xd5\xef\x9f\xf45K\x1c\xef\xb0Li\xd2Tom\xf1\xcc\\[کNJXu\x8e}ꕵ\aۡ\x18\x92x\xfd\xc6\xc3[@4\xabu\xab\t\xabg\x86\xf7\xf7\xf2\xfb\xa1\x00\xe2@\xc0\x1d\x8b\xfc+\xfa\"\x80\x05\x93R.\x13AS\x90T\x9b+\xae\a8S\a\x80\x99\xb13r\nP,qݕV^\x84f\x05\xba\x00\xdd\xd3b\x99\xf6ڝ]d|k\xb0\n\x1axtQ\v\xdcä=\xa2\x13:\xe2\b\x04qC/Ѿ\xdfK\xfa6\xe49@\xce\x11̦\xb4\xff8ۃ\x8d)\xaf\xe36\x06m\xd5k\x8fϋ\x1f\x85\x12z{\x89v\xfd\xb5\xb5\xc5C\x12\v\xeb\xedG\x13\xad\xfa\x1f\xd6v\xdb̠d\xf8\xbc\x85\rlC\x11\x88ȇ\xe7`\x97h\x16\x96\x95r\x80\xb1\xa4\xae\xfc\x00.\xbaS\xa7\x1eF\xdbm\xeeإ>2푈\xba\x95n\xfc\x99\\\x8fy\n\x96\xbc\xd93\xc3k\xd8\x1a\x9043b\x91?\xef\x82\xecs\xcdNj\x97\x86\xe4\xe9\x87\xfaT\x85\x88\x12\x99\xe6%\xd2a\xbf)\xb4\xf9\xf62\x9f\x10\xb2}\x1e7\n\xfd\xba\xea I\x94\x9d\xec\n}A6m\x85\"o'\x82iLI\x8c\x1d\x9eI\x0f5\xf9\xbcy\xe0\x97\xe3?\x00\x81\x84\x87\x81|\xaf\xd6Ue-\x87Ң\xcchb\xeb\xe7=Ϫ\x06\x13۱_\xd2\x02*'\x80{\x9d\x88\x10h\x9d\x1e3r\xbc\xea\xd0\a\xb3y\":\x96U@>\xe7q\xc2\xdb|J\xd9\xff!\xec\xf9\xb5\x80\x95\xf6\u05ce\xba\xef\x98\xed\xa7\xb472\x10ZΝ\xbc\xcd\t]\x82p\x1f\x1b%}\x05\x8e\x19\x80,r\t\xe4T\x0f\x83\x96\xe5āe\xb3u\xda1't\x0e\x84̖X\xfbm\xe4٩X$:Dl>\x80O\xc5\xf2\xea\xea\x05\x1e\xf3\xfe\xed\x85KX\x1d[\x92;\xb6\xf4\xe1\x844E\xcbh!\xc8BA\xad\xcb\xdfj\x17\xaf\x13Z\xc9<|:f\xb1\x1b\xa1\xa2\x03\xac^\x03\x8bO\x9b\xefh5\xad\x80\x01a\x95 \xf5\xf0\xfc\x80\x84\xaf\xf9K\xb2\x93\xa9\xbd\xe5\x84u\x1b\x90\x85\x9d\xa4/b\x03z\xact\x9a\x80w~\x9b8i$\xf9\xa8oo\xa3\xeft\xb2^\x043Q?r\xd5\xedLˊ\xcbf\xac\xf7o\xcb\xcd\bI\x0fn\xcf\xf5H\xd8\x15\x99\xcd\x1di\xd9qUgg\xea\x8d\xe5)Ӈ\xa6\x14i\x1e\xf5-\x1aa\xab\xfdui\xcc\xcb4,a{\x9e n\xbeY$\xb0\xb8\x9b\xf9H\xedkJ\x19c\xd4\xceJ\b\x018@\x89t1A\x92y\x8f\xea\xfb\xa58\xa3RQ\xce)(\xe7\r\xc1qr\xfe<\xb5\xc2\xe9\xab'T\xc4\xff\x942QUE\xbe\xf3T\x88\xa8\x87\x14\xbf\x17\xa2\fԫ\n\x9e\xe9\xd2*DWV-J(\xf2\xf4\xb8YWZ~]\xcf^\xd5\x05\xcco\x1fP\xe56{\n\x9e[\xa5\xc4\xe9=\x92\x05\xf2\x00\xdfʤ\xc3\x05Ɣڗ\x8b\xf2\x89>\xfa\xa2\x19\xf9\x82\x1d\xe1!\xc1\xfe\x88C\x05\xb0/\xe19\x17\xa0\x99\x86ky\xeey\x99r\xadL+>;\xa3\x9aʒ\xb1\x98\xaf\xb5[\xe3/\xf0\xb1\xf8\t\x05\xcff\xf7n\xeb\x90\x03\xda>\xd1O<\v\x831\xe8#\x14r\xc9\xcay\x17\xeaw\x85\x00\x1970\"\x01\xa2\x90\x13\xd8a\xc1\x1aYM\x80\xb80\u007fIb\xbe8H^-r\xe4i\xad\ra\f\x05\x9b\u07b4B\x8c\xe2\x90\x007\x91N9\xb3!\xb5\xf8gI 2\xeb\x83\x04iOB\x1f\xb9\xac\v\xba*{Ȫ!\xbd&\xbaF\xdc\x10s\xa8\xc1S\xa1\x9f\xef\xe1\xe8mt\xd3*V\xc6\xea\x91ch|\xb5\x92ʢ\x8c&\x10E=\xa3\xadE\x04\x96+\x82\xd3BJ\xa0&Q\"/q\xc2d\x91\xe5\"\x8b8Yn\xd9\xfd\xa3\xb6\x84\x80$\x9e:\x10\xef\xef\x19\xa4\x8aW\x06\xbb|\x0e\xa68\x9d\x8b\x9aa\xc1%\t\xe3F\x8e\x82\xe5\xa8\xeb\xd6\xee~\xbd\\\xd7\xda\xd8\xea\\\xe3 \xca\x04\xb0\x05\x81\a=\x0f\xf6\xe9\xa1w\xc0\x8e\x16帙\xe4\"\xaa\xe4i\xaa4\xe0\xc8}B\x95\xa9W3\x99߬\xee\xe3\xee[o4Yf\xb9\xcc\"\x8b\x1d\xfe\xd2\xd231\x03D\u007foڔ\xbd\xc0r\xc1]C\x83\xaa\x84pϼAyl\xb0\xa2k\xc97S L\xae\xcdj\xc1\xaa\r\xf5@\xd9>\xfd\xf9s\xfa%\xff\xf1\xea0\xdb)u\x11\xb5A\xba \xdd9\xfd\x88-^{\x1a#x\xf8/\xa1ަL[\x16`\xe20/\xad\x96(\x89\xdc?¨Y\xcd)\x86؛a\n\x8cwI{\xe2\xadd\xcddC\b1\xaf\x95\xb6\xd9\xdd\xf8\xedڐ\xdaG\xf0\x8cd\x85\x12j\x89\xbc<\x96\xe3\x80\xf1\x1a\xf2\xa4R\xaf\xeb0\xf0*eYC\xb1NsI(~.\xe4\xb8\x11D<\x81\x9fou\x0fwϪ/۟EP\x9bq\x02\xdc\xf7{\x82\v\xdbcۉX$6\x02\xe3\xe2\xeb\x1f\x13\xd0i\xdd\xe0\x8bE\x06\x00\xf3\xec\xf7\x98\x82<\xa8-\xb6\x8d\x80\xb5o\xe5mp\xefV\xf3\x17\x05tX\xd7\xfbb\xd1K\xbdͻ\xf9\x0e\x91\xa5\x17/\xd5mjh\xf5\xe8\xa8o\xa9\xe72,;W\x9é\xccx\xbfI\x96gƭ،90s\xd9\xf4Q\xf0\xa6N\x13O\xf2\f\xf7 \xa1H\x1eP\x04\xb21\xe8\x98\xfe'\xbc\x01gK\xa4,-\xc8\xde\xcc\"\xf8z2\xf1\xf3ט\xdam\xa5\xb4\x84q\x12\nZ\x89(Ez\x92QNe\x96sD\xa4\x12\x11\x1c\xab\xa3=Ն;\xec\a,c\xabP\x98_\"bpy\xb1\x1e\aI\fk<Ɖt\x92,_\x82\xf0B\xa5\xa6\x8e\xea-\xf7q\x92\x98\x97 \xa0\xecܐ.\x98_\xb6\x15\x06\xd3\xd6\xff\xafh\x8b\"{\xd8\x18\xf9g\xf2G\xdb\x11j\xae\xdd\xe7\x14\xe1y\x10\x06\xe1\xc2\x15\xad\x87;!X\x8c;\x8a\xc6\x1b\x83\xfc\x16C\nH\xa8\x92*g\xf6r-\x90\xfe;2I;\xb2\x81\xf0d\xe3\xfaT\x92\xd7X\xea\xc4%\x12\\\xddf\x17T\xac\xf7\a\x95\x9bǚRs\xa0\xdfm\xf9\x10\xc7-\xee/,\x8e\x1d\xcb\xc2;U\xb0\xbdU\xb2\xdb\xdf\xf9\xb1\x10\xc4v-{\x85\xa7\x93=\x95nO\t9\x82\x00\x9d\xf2k\x80\x1f\x11U\xae땐\x9d\xaa\xee\xe2\x93\x12\x1e\x04\xee\x8b\x12\xe1\xff(\xfan\x17dz\xb6Ii\xfe\x13\f\xfe\xc3P_\xed\xad\r\xa6k\xad\x90a\x8a\x15\xb76d>*;\x9f\xef\x02\tڻ\xeb{\x95V\xda\xe0\xd5\xfeq\xd0\xcdS\x82\a\xfd\x1b\xea\x87\x02[\x98B\xeeO\xefl]yW\x9e\xe9MRZ\xc1$.\xa9\xdd%\x98\x92\x9b\xf8\xaf\xa7qj\"\xb6̙\xb1\xdd.\xef\xdd9*\xed\xb7\xab\xa7\a\x14\xab\x9dH*\xee:\x9cH\xa8\xc9fc\xa1Ep\xdf\xe6R\xb5\x96o\xa5\xbe\xb9Q#\"h\xd5\xe2tL\xc2\xc9\\\x97V\x97\t\xb0\xa8\xc1Of\xf5\xda\xe2\xda\xcf\xc0\x94}\x05=Q]\xaf\x9aL\xbd\xa5\xf1\xbeH\xaf\x97|\x9b\xbf\xb9\xad_~\x99kϣ\x94\xc8\U0004f214\x94\uefeav\xeerți\x82&!\xaf*)\x81\xb3\b\x1drI\xf5\xec\xd2b@\xe5쪖%M5Нs\x93!N\x9b\xbd\xeb=\xd53h\xc4%`\x81\x1dU3\x06\xb2\x8e\x8f\xf5yV|\f\x8e\x8d\xe4p\x88\x0f\b\xd3k,6\xae\xd6խ\x8a]+{\x13\xf6\xf4\xc6EΗ\\\xc0\x13\xd4^\xd9\xd3\x04\xca\xd6\x03yn۔.*\x1dQz\x95MO\xb3\xf3տ\xff\xe9\x10D\xef\x11\xee\xdd'\xecT\x95\xffS\\\xa20\x98WU'5\xfb\x94\xbe:\x99#\xe1h΅A\x15%\xcaEZ\xb7ʜ5b\xb5Ҝ\xab\x146M.\xfb^q\xe7\xd5\xedӶ\xdaX\xd7\xdb(\x92\xa6\x021\x85\xa7\xd8]l\x87\xee\xd4(4\x17\x1c\xa2\xa7A\xd4\xe6\x98\xe2\x8dҢ\xedۋ\xc0\xc2\x02\x13\xadVXkv)^ۚ\xdcn6\xe3\xabe\xa5\bQ\xa2~\xee\x80q`\xeba4E\xa4\x9b\x95\xad\xd4\xdbl\x11\xd9Z{!\x9e\xb3\xce\xdee\xa7ٹ\x10\b\f\xc2R\xbb\x96fm\xa5\x84wš\x17|\xb5\xf8\xdaN\xf6w\x00da{%Q\xf4\tc\x9fy\xd8\xd1g\x91\xfdR\xb3\x9e\xa0\x9c\xc5A9z\xef\xa0X\xb5\aBN\x9e|5\xf9ّ\xb3\x11\x87O\x9c\xfb49_w\xec\x9c\xce9\xa5\xb5\x90\xa0\xaf\xbb\x10\x1c.\a\x99\xe8\xe5\xa2\xfff\xedo\xfa\xb1(\xfb\x1bD\xdf\xe6\x99\\\x13\x03\x1cEPl\x9d\x9c~\x8bP\xbc\x84ˢA\xab'\xa5\xf7Ǐm\x9d\xb2\xa4\xc3 \x10|\xc5\xd6)\xcc]ˍ\x83\xda1\x8f\xdf<|`){\xd9\xed\xf8y?\xcbJ;|Ɠ\xe5\x15=\xed\xd8\xc8\x04\u007fJ7\xb6\xb1M\xe5\xe5\xfdMA\xbe\xbd\xef~we\xfb\x82\xa4\x8dH\xf8\xadb^;+\xe6\xce4\xd8\xf7\x96\xf5T\x1b\xb3\xe2\x891\x1a纲ѳ\xea'ZNWR\xa8f\x10\xc3\x1dZ\xa6x\xa9\x92\rR\xb7\xcd\x1e\xaf}\x1e\x93\b\xcc\xf7\xd4\xcd\x1e\xabE\xd7ڢu^\xc0\xf7\xad}\xdb\r\x99\xac\xb0\x17\xff=ּ3\xe4\xb1CA\xeclC\\\xe3\xa3'E\x0eΩ)\xd7.\x8bb.-\xfa\xc7\xf7GB\xfc\xb4\xee\x9c\u0604\xf2\xeeHA|ZE\x87y\x8c˭\xd3y\xf1H\xe7\xdc:\xff\f\xfb$\x8d\x86\x13'\x8aX\x85\xf6v\x1f\xc93&\xbby\xf3\x02VQJ/\xb9\xb9\xff\x9aI^\xe9\xd3\xc5\xdb\t\xea'4Z\xef\xffY[\x19}>\xfcēn\x16\x9f\x9c\xd8\xeb\xc4ѭ\xe6\x19ţv\xe8\xab\xfeTo\xe4w(\xf2kxǂ\xd5 \xcf\xf2Կ\xad^gWzۼ\xb8r\xc61k\r}Pc\xf0.f\xfaŝ\xe9\xf6\x12\xfbL@\xe4\xb7^\x83-\xdd\xd07\xf0pj\x84o\xa9\xb1\xbb\x9crͤ\xf0\xfaDⶴ\vppKt\xf2\x81\xc2\x03r\x14\xfa\x8fU}\xca$gmJt\x90AP\x83\xb4\xc3v\xad\xdd\xcb\f\x1ch\x13*ٲ\xe5͛-\xf7\xb5\xfdZ\xd0v&\xc5dH\xabj|4\xb7P\xc6\x0e9\xa0\xf3\xfb\xb1\xde?]]\u007fz\x05w\x1b\x18\xdb\xee\x02 \x11\x00\x80\b\x1aw\xf8\xff\xa3L\x9b\xe1\x0f\a\x98\xc0\xdaz\xcd z\xc0\x85\xe9\x1e\xb8\xa0Щ!\xd5\xf8.\x05+',z\x9cb8\xfb\xee\b\x83\xa7\x0f*߮$\x8b\xde\xea\x86\xcejΆ,\x82\xb57\xeabC\xe6\x85\x01\x15\xe3\xeb\x92o\x9e/\xda\xf7]\xc1E\xach+\x1e\xba\x12\xb7#PN\xaa\x19\xc8:\r\x1f\xd2\xc7<\x9fD\xe2S\x10_S4\x10;\x0e\xce\xd6LG\x1f\xac\xae\x99\xdd\xef\xb3\xecV_\xfd!G\xb3\xa98\x97\x88ʜ%\xb0\xf7\x02\x1d\xadgq]\xb0wX\xb9\xdc\xc2\x1b\x18\x12\\z\v]\x88B\x97W\xb0\xfb\f\x87λ\xd7z\xfd\xecTS\xf9v\xa0\xf4l\xd3\xca\xf0\xf0\xca\x10\x85\x86V\xb8+#\xfa\x8aᡜ\xaa\xb5\xa7\xa5\x06\xb7\x02\xe6\x15\xfa\x1f\f\xf2L \xf4\x94\xbe\x15\xbeW\xff\xa7\xfe\xbaϛ=\xddu5\x0e\xd2f\x1e\x86]\xeaY:\xdd5tgq8hĢ)\xa3\xaa\xa1\xa8+\xa9\xdb<\x1e5d\xac\xb4P\xaf:9\xdb?tun$\xe1\xae{`\x1c\xe7\xfb\x1eY\xac\x10\xab\xb4?!\xc5&]ܳ\x92\xdfp\xa3a\x81\x86\xbbR\xb2\x1c<\xd4ұ\xa5nk}\xcbDpzawY\x81\xd6$\xd6z\x15\xfa:\xa1ߓ\x89H\x19z\xec\xe6\xd5\x11\x1b\x83\xef\xafdY\x9c\x10Gj\xb1a\xefr\xcd>\xf1\xef\xf7q\u0378E@\xca\xd8G\xe3\xac4\x8d\xde\x13\x9b\xbf\xda+\xee\x80\xfc\xd4\xdb\xed\x93\x02\xb7\xf35|\xa8\xd9\"\xc3E@\x9f\x9f\xe2\xe0\xcf\xe28\x1f\xfdx\x8fy>\xd7\xc1X\x03qI\xa7\xa63%\xe54&\x8d\x95\xc5Ueѣx\x8cޜ+\x1f\xd6\x1bV[\nW\xb8\x0e?\xd2$\xfeU\x8c\xa4\x8b\x997\xa9\x8a\xecH\x9d\x95\xec\x902\xb1ܘ\xeem\n\x87&\x1a\xe7\xae{}\xb43\x03\xf1}\x9d\xbd\xf7\x8c\xd7\xf5\xa2\x1d\x96`R\aU\x05\xce\xd5=}ii*\xf6\xa3\"Q:\xb3\xeb, !8\xa9\xf3\xa06\x8bܤP\xa5'\xe0T\x94s\xa4\xeb\xd6rvw\xa6\xac\x83M\x12\x00DKOx\xef\xdb\xc9inM\x82'\\W\xa7\xad\tmF\x93\x88\xd9f\x8aP\x82\xaaO\xc4\x1cV\xa6\r\t\xbb\xe6\\\x84\xb3\xd3\xca`\xbe\x0e\x95\xe3\x91\x13%~\xf2J\xc2\xf9JvCm\xa28\x8fk\x1dv9\xbd\x05E\x8a\xb7g\xddfv\x9eG١\xcew2\xef\xb10\xf1$\xdb-\xa7\\\xe6\xb4\xc6I\x98MD7\x9cO\x16ۺ\xd2\xe3\xadrU\xfa\f\xca\x14:Qڃ\x9b1<;\xd2\t-\xab\x98\xeb:\x18\xe1z\xeb\xaf\xfa^%\xf1q\x90\xbcB\xbeZK\x8aQD\xeb\xe8\xc6{\x05җ\x96x\xc7oe%\xb4*p\f\xd7\x0e\x167|\xb5-t<^\xddxأ\xd0bT\xfe\xed*n\f\x95\xe5}\xceۙo\xd0\xca˞\xd7(\xf3\xdd\x01ﴲ\x83\xa7\x04\xd7\xda\\\xb6\x9f\xa7^(\xef\x1eZn\xc4\xd13\xa0f\x82\xf1\xb1Z\xa4,2\xcb\xed\x87:\x83\xa7\xaf\xaa\"\xba\x17\xd0n\xbb@{\xf3\xb6\xd58,\xa6-\xa2^\xb8\x9dw\x12Q\xb3\xb7\x83R\x1e\xeb\x93\xe0\xed\xd0E~\xa7\u007f\xea\xeb\v\x05\xc0'>\xd1\x15@^U\xb6>\xff\x92\xddW5\v\x80\xc0%3#X\x895\xf4\x06\"߶縵\xe8mw\u007f\f\xae\xb7\xe8#,\xe8\u007f,\xb1C\xf58閅\x9dW\x8e\xcfO=Ļ\xe2\x86\x18\xdcH\xf7\x1b7\xeb\xe3=ζ\xc5\xcc\x1a:+\xd1\rᓞ(N\xe5\xac<\xcd\xc2n\"];٬\xe1D\n\n\n+\x87M}\x04\xb5Y`\xd6\xe7*L\x10\x97\xe7vl \x00\x8bq\xc1\xaa\x87\x9eZf\xa3\xfc\xb6u&\xad\xca-\u007f\x98\xa3A8\xb3M\xb2\xfb\xd4\xee\x80\xc0\x10\x1c\xb7\xe6\xea\xd6\xca6u\nt\x13\x102i{\xe65\xf0\x95\xe2\xfe\x04\xc6k\x10\xbe\x1c\xf7\xf9\r\t\xc1v@\xbe\xbe\xe5\xd9Jg\x1dv;1ph\xb7\x02\x04P\x02u\xda2[\xebp\x8c\a\xbfC\x91\x95Um\xa3\x97\n\xa9^H\x85\xbfn\xaa|:\xcc}\xe5\xea\xf4J\bt\xa0\x1e8\xdd\xe12E\x9e\xa4\xb0\x9el=\xebU-\xbbӭ}\xe8\xd5\xdb\x10\x930\x15\x13\xe4\xc4s\xa9\t.>Q\xfd\x16\xa0\xa6\x1a\xcfx\xa7\xa0T\xae\xe6\x19\x16a7$\xeem\x12\xfa}\x1c\x89;aÿ\xffmk.\xc44\xd3\x1e\xdc\xe77\xaa\xf1\xb6\x03\xb8Kt\u074b\x94B\xfb{\xfc\x10\xde\xcf\x1f\xf3\x9e\xb9Z=\xa3\x91\u007f\xfb+\xf8I\xa1\x17\xfa\xa6\xe1\xe4\x97\xf7w\xf4\xfd\xe9\x93oN\xa9.\xaf\x90R\xf1\"k\xaeO5h\xb2\xbc\x15a\xb9\xbdC\x95K\x900\x1bO\xaf\x8a\xfd\x1fP\xab$\x84/\xbb{q\xcbu[\xc1\x12\xd6\xc0_f\xeb_\".\x90w\xe4y$\xec8)\"oX\x88;3\xc04Z'\xe4\xf2\x93\xa2G&\x9e\xac\xf4o\xd9\xec5\x1b\xa0\xf2gȬ\t[\xe4푂p\xaex\xa3$~VlYy\xd2?A:\xa7O0O.?Iv\xc2{\x05\xb1\xd7~\fl\x96z]%\xf2\xbe\xe0x\x81\xf9դ\xae1\x9fG2\xf5\xb2\rͯ\x1e\xf74`\v1w\xf9\xa1\xb3\xb9\x0f^\xf8\x0e\xb1\"B\xf6~\xd9\xce<\u05cek\xd5h\xfa:\xa5\xfb\xbf&\xf59D\x83ɗ\x1f\xfc\xd9@\t\xf1\xba\x81I\xb8\xc8\xe24<\xecl\x9cC\"\xed`\xbf\xc86\xe7\xfc\xa5\xc1\xde7\x16Ћ\x95콀{\uf545\xed\xa7=V\x9f+\xaf\x95\r`\x97\xe1T\x03\xf8\u007f\x0fU\x100딎\xa0\xb1s*Oʏ\x1b\x12\xed\xbc\xb0tj\x8d\xe1\xd4y\xe82\x96Ϡ\x8a\xc2|*\x05(Tw\xd8l\xa9d\xb6\xc5b\xe2nQ/\xc8\xcb7Z[\x9bi}\x0f\x9ahím^\x89\x9bW\xa3L\xb6m\xa7?\xe4\x01,/okk\x02X\xc5Ft\x06\xbb\x15\x02\x87\xb6\x13\xbb\xb1+\x9d-\xcf\xe4{V\x9a\xaa\xbaX7\x8cNFd\f\xf7\x14\xfd\x17\xce39\xc4ȑ\x85V\xbd\xd1{\a\\\x8f\x14\xa8o\xcc\x14\xaco\x1f\xfd\xca\xfd\x977*\x0f\x1f:\xc4^\xcb.f\x9d\xe2=g\xf6\xa3\xad\n;:uP[\xb0u\x9d+\xcfZ\xea\xe5\aP\x04\xc9\xeeϸ\xe5u~({\x9c\xb9\xb4\xb7\xcb\xcd\xca\xfd\x9e\bR\xa4\xfc\xeeʑг%\xa4?\xceL\xd8\xe1'm\xa3O#\x8d\xc8\x00\x1b8\xb4x\n\x80\xf4\x0f$\x1aN>\xc0|\x92ߖ^\x1e\xb9\xaa\x1fy~r\a\xdb\x16\x89\x8f\u05eeۙ|\xd6,y-\xaen\xfb\xbfQ\x11\xe9\xa0ߖB\xd3N\xcf\"n\x0f\xf7\xfc\x06\x8d%;Ts\xd3\x03\x95\x11B֭f\xec =3\xe8EXX\x997\xa4W\x17\r\xf9\xc6s\x14\t\xec\x94\xff\x12\x98i*(*+\"AC.\xc2\xd5ڥ\x18\xed\xb8+\x9d:\x8f\x82\x9f\xd3WR^m\x91SQM\xc3\xc0\x9f\x18z+\x83\f.\xa5 \xeesS\xda\xe6\x10!\xdaF]\xfe\xfbbZxL\xb7}N\xc8\xe2N\x8f\n\x81\x01$\xeb\xd0pgv\x9d\x87E\x9e\f\xe9mA~D\xaf\x8c\x82Ph#\xe3\xa2.\xe40k\xb3\xc3\x0f\xc8㲧\xc4o\xe2\xb7n\x8d\u007f\xf3?\xef\xcd֭l\xff\x0f\x8d\x89/\x15Ox\xbf\xf9$]\x93\f\xd7L\xa2`\x1a.\\(\xa6P\x00\x82+:rj{\xd4x}cO\xfa\xee\xeb\xfd#V \xa2\x06\xfb\x1d̥)\x9d:\f\xcf\xeef\xa0\x9e\xa8(ý\xf2Q\x1e\xea\xf0 \x1c\xf4ǀ\xde*\xa9\xca[\xc3յ\x9f\x92\xac\xe1~\xc4-`h\xf8\xc01):\xbf\x88\xc3\x05\x86ҙ\x9c\x18\xb7\xc2n\x0f@-\xc8\xce݁\xda'>c\x13(\xb2\x9f\xf1\xef\xcf>,\xa6\xa9\xb1\x91U0\x8e.Q\x9f\xee\xaf\u007f\xee\xdb/\x8c\xa4sU*\xb6k\xa2ޑR1&&;{\x9c=<\xa4\x1e\tQ\x01\xa5\x16\xb5\x90dÅR\x15\x18%\xa4\x95\x1d\xa5\xb1R\xaf\t\xc0\xa4\xbd\xc5\xe2\xa7F@\"\x12\x89\xce\xf8z\x86\x86EG1\xe3M\xeb}<*:Q\xdf\x155\t\x83\xd5zW\x9a\xc1\xa3\v՟\xa8\x90\xf8D\x16\x8b\x0e\xd7Kj~\x1f\xdc\a_\xbe\xf0\xff\x1f\f\x1e\xf3\xcf\xff\f\x81\xc2[#\xa9\xd7\xc7\xcc\xe1\x10\xbcZ\x06\xb7\xee\xda/\xc0\x84\x109XMF\x9d\xa8ۇ{\x12\xe0\xc1\xdc7\xf2\xfe\x1e\x02ș\x90\xb1\x9cک\xe4\xf4\xcf\xe2\x1b\xb7+\x8a\x05h\x86\xb3\xc3\xc3\xffsDf!!/\xd7\xc5\xday\xc6\xd9\v{ܸ\xf4\xd0=\xf5\xeeg0<\x89\x15\xcc\xef)\xe58\xa04\xb0\aT\xecMʦzj\xb7^K\"\x19\xc3\u007f\x03\xe2$L\x9f+\xca\u070f\xbf!^\xa5\x86\\*\x92\xfcd%\\\xc1\xff%\x1e\xa0\x8aN\xb9\x18\xccs\xbb\xe4\xc6$\x84\xcd\xcb\xf8Z\x1b\x96\x94:˼\x18\x04\x8d\x86&\x85,\x13\xc0t\f'U\xdc}\x94~\xb4#\xce\xce\xe2\n\xbd\\\x11\xe8\xb2\xe9\x80\xf4ɝ\xb8\x8c/!-\xecmY\xc2V\xf3B-Ei8ɷ9\xac\xd0\xc92<\xc7\xc8S~N\tK۩\xc4p\xd3'\x87Â\xa0\x87\a\x80*\x91\x15֜\xd6wcWF\xdf\xf6c\xa7\x1e\xa8\x9eK?\xc2ZAJ\x10\xd9ƺ\xa5p7Է\xbc\xf3b \xd1i\x87\xf8\xf6\xcbKL\xd2`]\xc7\xebgɎp$l)\x87q\xfc\v\xb9\xa2\xc1\xb1ҍV\xb1B\x1f\xa6C\x8f*c\xef\x80\xfcK\xafirz\u007f!\x913\xe8ڇ\xb6\x1c\xb6\xb3|0\xe3\xdfF\xb3\x8a\r`\xf4\aZ\x9bB\x04\x99Q^\x9e\xf6z}\"!թM\x8f\xae\xf8r\"\v\xbf[\xde\x19R\xb2\x8c\x1a\xef\x91\xc0M?\xed\xc7\xf0\x0f\x86\xb8 \x13\xbd\x1f7\x96\xec\v\xe1\t\x87dLd\x1cH+X\xd3\xe9\xf7Tp\x9d\xbb\xb6\xf5\x97\x1f;\x9d\x8c\u07fbW\xb5\xcf\xcf\xdb3\x8f\x86k>j\xfa\xa7W]\xa0[\xf7\x05\x01\xfaт\x97QT~\xf8\a79\xb5E\x9d<\x9br\x05\x95\xa3\xa3jO\x02>\xc23\xafSѧ\x8aB0\x8fn+\xf9\x0f\\q\xc4\\Xh\x1d\xc0;ed\x91\x87\xe0I\xe6x\xf6\xe56\xe5>\xf1\xa7 \xa1\xa1XC\xa7\xfd\xa0V\x1dr\x01\x93pN\xa6F\xaf\x13\x95K\xef|99QP\xe7\x98\xf6\x1aba-\xfd~\n$\x15\x02\x91G\x16nX\x90\x80?:a.p\x01f.\x1f\x1e\x02\v!®C\xe1f\x88\x00\x84߄Z\x0f$\xf9\xdc\xef\x80\xfa\xbe\x17\n\xea\xe1\xd6ݞ\\؉j\xb6\xb4\xab\x8drv\xd2b\x9b1\xfa\xe4\xc0F4\v\n%B \x10\b\x1f\xb9B\rk\"\xbdr\xb2,\x1d$\xa7\xf4$\xec\xc9\xfd\\\x897K\x95\xad5\xdes\xf7\xf3n_\xc4\f\xbe\xd1+\xf5\xf5\xb1\xa3\x15v\xb5\xbd\xa7 \x8a\xf2P$\x92ϩ3\xcc/\xa1\xa5\x18x\xbe\x1d>J\x82\x86aw/\xcb\xf2T\xcei\xbbX\xea\x86F\x91N\xad\x87)@\x1f\xd7\xcc\x1e\x1eԅA\xfc\xa1\x02\xf5\xa4K$r>\xfaG\xd7nc\tQ\xeeR]\r\xa1\xbf]e\\\x15C \x10w\x10^\xeaʺ\xcd\x00\U000117afW6ު}LB|\xf9\xe8ұ\x99\xa8\xb361\xf3\x9dR\f\r\xb4\xd1pn=\x92\x87\f\x1d\fb\x10\x94\x1d>@k\x06\xb3\xfe\x16D\xb6R\xd7\xd7ƌB\f\xc0\xedM\x19Q\xfb\x81\x9b\x82n\x85h\xe0\xee\xa9\x1e5\xeb0\xad\x16qb9j\vC_\xc0~P\xeao\xd6\xf5\xf0a\xad\x99ʀ\xc01\xf0\x94>bש\x8a\x91\xd3\xdci\xd6v\x86\xff\x9b63\x19u_\b\x9c\xc9;fj\x1e\x03\xb8/1'y\xb0\x199\xb7D8\x9ba\xd1 \x05\xad\xban+\x16.Z\xc0\xf2\x18fq\xff\xb9>\xc1Z\x0e\xcc\xc3\xf3T\x90\x9a\x16Οά\x04\xf5s6\xa7\xea\xe9\xcb\x1a\r\xd7\xee\x1f\xed\xa0w\xa3V\f\x1f\xac@)\xf8\xc1\xea\b\x81\xf6\xe0w\x171\xa8\x11\xfe\xbb\xb3`\xd2h\xef\t\x03\xa6|Zw\xb2\x90\x8b\xaa\x86\xf7U\xb3\xffi\xf3\xb7\x0f\x1ea\xae\u007f{\xf8]\xe7\xb2\xfe\"\xaa\x97\x105\v\x88X\xf7 \xafM\xf7\xa1DXfl|6\xcfb\xa6\xc93\xcf\xd2Z\x1f=c\xa1d\xf8dž/\xc0\xe2bWO\xfc\xd0\xc5\xe2\xdag\x16\xc4L \x15\v\f\xd8Á^\xb0\f~Їo\x94;Lx\x9f\xd40e\xb7_\b\xcaZ\x9a\x92,\x99\x90\xbbCõݷ%\xf7\"\x98\xc4\b\x87\xb9\x11(\x1b{\xc1\x16\x9a\xcc>\xd4\xed9\x826\a?\xc2\xc1\x83\x04\xa5\x03\x98\xbc\x05\xfc\xfb\xef\x85\xe9C\x87\x8e`\x06\x19\x19/\x87\xa0\xc6}\xbdG\xd7(\x10\x01\x06?\x04\xa4\x00\x11\x94\f\x9aZi\xb4\x17\t\xb96\xfdm\xfd\xf2\xb9\tv{L\xaf\xc73\xecZ\xab[a\xa3\x90x\xb7\xca\xd9\xfa'\xb096\x12!\xa7\xb41\xb62\xb1\x17'\xe9p\xbd\xc0\xc8ͥ\xe6[\xa4˔\xc6\x1e)\xcd)\xb1L@ƙV~+r\x98\x9e\xae2\x9f\xd5ʑ\akk\x83\xb59\xf2\x03Z\xed\xe6\t0NG\xbb\x85\x86\xcb2\x835r\xd9aQJ\x16\v\x0e\xc3#\xce\xfd\u007f+\x8e\x16Z\xc1,\xaaO\xa8\xe9\xcfh\xedO\r\xe8:\xfa\x1a\xd6X\x15\a=`\xa7O\xbf\xe60\x88\xc1\f\x8e\x14ߋ\x1c\x1c\x8c\xbcW<\xf2N\x1d\xcc;\xc0\xca{[\xb3\xbe\x1e\x9c\xfc\x1c\xd3e0\xc5^\xb7G\xe7\xc8\xceݬ\xc0-{\f:\xbe&\x1cܖ\xf5V\xa1\x16\x19O\x1e=t]\x86\xf3\xbc\xb5\xc7\xd1\xf64ƏKF}1Q\xacW\x01P\xcay\xe2\x13@O\xa5~k\xf7\xf1\xa6[\xef+\x81\xb9c\x9f\x11\f\x04\xc0\x14D\x99\xc6\xca\xf7\xb7\xde@\xf9\x8dk,\xbf\x15UB\xd4#ű&\xd6rC\x8c\xb8e\xd6,\xe4/at[\x10XOd\xebԚ{-\xfe@\xf2a\v\xa4\x14\x19i` Q\xee\xbc/\xb0\r\xa2\x06\xa9B\x82X\xee\x18I\xf5HU\x88,}\xa3Ȥ]\xb4\vOy\xaa,\xae\xaa\xde\xed\xf4\xb9\x1btGd\b\x14\xb7\x8a\x9f.@\xe7\x87뾄\x8c\xac\x00\xf6}\xefۀ\xf9\xee\x1a\xc7\x0f9S\xadU\x12\xafW\x00\x81!O\xca\x1a\xa2ҕ\xa4\xe9`h\xc0\xba?\x9b/\x98=\x9f\xa4\xf3\n \xf0o\xc7\xf9\"\xf7\x99\x1a\x06\x8f:8\x82\xf8A6\x02VK\xc6#\xa6\x91X\xae\x1f\xb6\x88\xa0\x89Iq\xf4q\x8fK\x85\x8e\xed\xdcy,Ѹ\x83:^PAu\x10~\xdf\xdd\xf2[\xb0\x01\xb95\xa0\x80\xdf<\x8b`d\x91\x99l2u\xd8\xda\x1d\xffv\r6\x9d5b\xd2\xfd\v\x99ǭ\xad\xb0\x81K\xd3\xf7v\xeb\xfc\xed\xd1\x16\x8fo\v\x88\x80\x8a\xae\xcb\r\x9do\x13\xa3\x99\nIѐ\xa58\x10\x8a\xe8\x100\x8a\xe8\xc8M\x8c\xfc\xf9\xed\t\u007fS\u007f\xc9\xd8\x17\x12N\x1f\xf6\xfc\x05\x96\xd4&Q\xfd%\xc2\xc2\xd9\xf8\xb6\xb6x[\x1e\"\xba\xa5:\x86v\xcbE\bb\x0eJړ\xf50\x90\x94K\"\x90`\xe4G\u007f^\xba\xa4\xe0!\xf1ܾ\xf5\x143#G\xb2\xcc\xc7\xfd\x8bWT\xa0b\xf8Aý'\x9a\xf2\xf8\xef4\x14I\xce\xc3Io\xbe\xa15\xf5K@\xb3d)ƻ\xa2H9eW\x1a\x99`\x9c\xa3p\x92[\xcb\xf1':\xed\xf1q\xce\\}\xde\x01\x8e\xc74=\xee\xb0@D7\u007f\xe0Z\xc0\xf3\x13\x99w\xb6\x16Y5\xe5\xe6\x9e\xde\xe9\x1b0\x146\x93\xff\xbeӘ\xcd\xeaВ\x81 \xb4*)\x03\xb3\xe8\xec\x80\xf3z\xb7G\x16\xeaS<\x01.F9\"\xe7\xe0Ca\xce!z\x84\xae\xa3\x8a[\xbe~\xc5P>\x9a\x92ݴc\x03\x91\xd8ZB\xff\x8fb\xcc4l\x1e\x10\u0092ٟ\xe3\xdds\x9f\x9d\xeb\x9bԳܻY\x89\x16\xa7\x81j(J\x95\xb4՜\x1b\xb2:\x88qZo%9\" \x8c\x15\x9f\xc9\xe2\x85]c,:\xb7Zr\x15\xf4\xa0PA<\xd3\x05@p\x11\xc4/\x8e\"\xfb\x9c\xc6\xd4\r\x9f\x8fg\xcb]\xbf[u\x11\xb3o\xc5W\x9c(\xbeAǸ3aI\x83L\x1c/\xb1\b\x8f)^j\xc1\x8b_\x13\xd9\xees\x13\x93\xc7;\x91_\x13\"\xfe\x80\xad\xd5\x14K\x95Y\xb5\t\tm\x11Ą\x9a\"\xb8oj\xe9\xcd=1H\xf6\x9ef\xc6Τ;F \xcf\xd9\rU\\V>\xb3\xf0\x1f{\xc0\xc59Yc6J\x9c?x\xdèW\xf30M-\xab\xa57\xd2\x1a\x1fؙ\x9e\xa2\xeeHrV\xa82\n\xb7\x91\x14\x1dI\xea\xb4\xee<\xfb\xe0\b\xeb\xe2\xe6(\xb7\xdc\xe6\xb9\x15\x06\xed\x0f\x9c\r5uywjBt\x85\xcb\xff\xe5\x1c\x8a\x06A\x91\xb0\xaf֏o\x17\x0f\x86\xf5\\e\x1b3Y\xe1\x8e\xfa\xb5L\\\xeeʺkl#s\xffs\xd0\xcb\u007f\x94\x0e˯\xcc\x1b\x14G\x13\x0e\xe8b\xff/k\xe6\xd0\xc4BZ0\x96\xf2r\x15\xceD\xb9h\x85D\xd8\x1aq9\xe1\xfeW\xeb\xf3\x9f\xd2z\xb3C\x1d\x8c\x1d8 @\xac\xc9\x13\x00\xd3C\x17\xea\x054\x91\x84\xb6\xe2\x9c\xf1\x15\x8a.7\xa3\xc2U\x9b{_\xa7\\\xd1\x10\x80\x8f_}#!|z\x94(12\xb8O\x87\x8f\x9d\xaf\x04\xba\xb1d\xd9@\xfaC?\xf9x7\xee\vN.?y\x93jvGC\xf4\xfd\x02\xe5Ҍ\x8a\xb9\"\x05\xfbʚ\x14Y\xbclC\xaa`\xbd\x86\xc82\xeb'%\x12\xc6\xf7\xec\x80b[iܫ6\x87\x89\xe9hLF\x92\n\xad\x19HO]\xbd\xfe\x84\x86\xde\xe7\xa3\r\x8dM\x15\x9d\xe1\xee\"\xdb\xdcU\xed\xd21\x9fP\xe0\xd5\n\x0f[\x10\xf2\x1e\xb9\x8d9\xaf\xb2\xa4\xc8\x13\x06\xfb\xfd\x96\xd2\xc2X\xac\x9d\r\x14\xb9|U\uebf5\xea\xc3B\xd8\xf1\xb9\x1c\x89\xe9 S~z|.\xc24\xfc\xd3\xefTP\xbe\x83{.\xf5\xa5b9\x8ep\xdey\xcc-\xbe~\x00^z\xac\xb3\n\xfa\\\xfa\xd6@\x15J\xa8\xcaX`n\xeeb\x10DWpk\x0e9_c,\x1a:\xb12\xe3Ya\x01\x11\x88\xcaFμҦ\u05ed\x14b\xd41\xba\xfa\xf6DLc\xf6a\xf5u\"ҝT\xe9T\f\x937+ov\xfd\x04z\xa0\x1fӀƣ\f<\x8en\xcbsiDw\x1e١/\xb3ţ\a\xc3\xfa\x9b\xb1\xf0\xd5\x133mW.\xc9{2+\aا\xac\xee\xcbt\x92\x17b\xf0\x80\x95\xc5J\x9d\x82\x04\x96c\xd7\v\x04\"\xea\xbd\xcb\x059\v\xc5\xce\xc3\xe4ʓ\x19\x9b\xa2\xf3\v\xb9\xd38\xe3lɭ@\xb5\x9cѤ̤%\xae>i\xf0\xeb\x16\xfa\x8e\x05\xa7O\x17\xe0\xf4\x01\x02\xc7\xc0\x81\xde\xd9\xdf\x14\xf6~\x97\xd9}$\xb9\xe8\xfaf}e\xa6\xf3\xe7]\xa6Է\x0e\xbb\xef9\x93\xa49y2\xa96WL\xe2\xba\xff\xf8\xb7u\xe5S\vMv\xfa\x89\xc6\xdaq\xa1\xd29t\xd9)iG\u05c9\xe3\xe80\xa6\xce\xef\x876\xf8\xf2\xa5\xa7G\t-0I\xe5\x00#\xc8\x17\x19u\xe0\xc9\x131\x97\x8d}ŭ[c\xb4\x02\xc1\x93z\xa9\x94\xa16\x12WŁ!-pi?\x16K\xa1\xa4\xa4\x9c8\xfe'`\xb8PCrr\xbb\xf1\xec\x19\xd3\x13\xc7\xcfp\\\xfc\x8d\x9bB;\xf9k\x16\xc9\x03i\xfe\x85~8\xd4߯I\x88\x87{'\xd1D\x05\x89\xf1\x83\xd1\xf2ʪ\xf1\x9cJ\xe0\x13\"am@!\xafB\x95\xb6S҂\xb1\xb1\xfc \x8f?\x1f\x1f{\xbe\xe3\x1d\x01\xe6ł\x1ak}\xee\xa4Mq\xbaW\x85\xc5\xceW\xde,/\xbf\xf5\x1b\xa0R+O\xd8\xecC\xfd[\x12\xc6\xfc\x91Yw3|c\u007f\xf8\xcd\xc2\xfc\xe4k=}\vQ\xbe\xff\xb6\xdbc;Y\xa94\xc0\xe5\xe2\x19\xfb\xf6\xce\xcc\x15\x87\a\xfbed6n\xaa\u007f\x91\xed\x93\xfbگlc`\xe3\xd7,\xbdɩߤ@\x15\a\x0f7iM\x8f\x91=\x99\xcb\xe2Gs\x9e\xad4g\x03\xea\xe7\x85%\xd6rG\x8e\x06\xa6\xddp\xf4H\xea\xaaC\x1c\x03\b5p\xbe#\xb3\x82S/ڝ*\x91\xa6\xe4\tϓ]\x056\xf2\x0f\x83\x1f\x1c}\x17\xfcN\xe0x\xfe\x9c\xbc\xed\xbb\xa1\xcb\xf7\xbd\xbc\x8d\x8b\xcbEr\xaeP?Sr\xca\xeeb\xb7\xfd\x8fO\xd6\x1c{\x16Q\x80\xadp\x89\xc9\xd8h*L\xb5\xfe\xd4\x16\x0fb\xce\xfd\xef\x8e\xcc\xfcY\fS\xebn\xcb\r/\xfbBZ\xfc;\v}\xd6m~9a\xb94\xb2\x99\x1d\xac\xa2-\xc2h\xac[\x87\v\xb2\xd7\xf4\xe9͎\x1fϭ\x96J$\xc51\xb3\x05\x98\x00N\xc7&\xcd|'\xbbc\xc7䬥/ʺ\xe9\xe7\xfc&᧥\xb3,\xa7\x1a\xb9/\x9e94\xec\x9d\n\xe2\xb3g\x87\x91)\x19^D\x06\x14\x82\x0e\b\xc7/\xad\x15P\"\xb8܈Edӽ\v\x1f&\x1d\bS#\x96\xd5\x02pK\xa0\xc8\xef\x80D\x1e\x98\xdb\x01D\xfc\xa4\n\xfbȚ\v\xfc\xf0\xea\x1aM\x889B\xd6\xc54G\xe1\x1e\xf3e@\xf1f\x8e~\u07bb;\x03a~\xb7WOk\n\xd7CL\x01\xe8\xf1\x8e T\x86\xe7\x95|\xa0;\x96\xddv\xe8)\u2433aH\x16\x92\xb7\f\x96\xc0\xb9\xf0z\xae=lyN\xfa\x14\xa2S^\xfa\xeb\x06\xdfxG\x87\xe9\xaf0\x18\xaa\x05f\xdf\xc6x!e\xb8\x03Ƹ.\x9a9\x95\xa6\xc0\xe2\\(\n\a\xff(noAiO\xf9@ut:)\x93S\xd8P\xd6U6\xbf&*\xbd\xdf\xf6B\xf2v\xf5\xdd\xedp\fF~\xb3[\xc5\xc3\xc1\x1a\xba\xa3@\x0e\x8b\b\x87\xe1\x1c]\x1c\xdc\xcf\xedJ\x81\x15\x18\x98a0\xc8\xc8dT\xa5x͊Z\xadС\v\x8d\x98\xe4q\x1c0.\x8eW2v\xe1\xcd\xf7\xc21hd\x8d-CZ\xf1\xa4\xbfV\xd5A@\xb5G\x84ñ|g;\xee\xe5=\x19E\xd6\x184\xca'K<\xfc@\xad\xba|\b\x8b\x984^\x80q\t|\xe5\\\xe5\u007f\xfb\x86V\x15\x1f\xe2\xed\x1b\x9f1p\xa0\x0f%[\xc6#S\xd4\xef\xe0#\xecF\xe9\x8b\xfa\xe2\xf7\xcf\xd8#\xb2\xa8-\xa6C\xa4I\xf1\xda\xcc\xed̥\xbe+\\\x16),\xedWyy\xf2:#\xec\xdfs\xed\xf7Q\xa5\xbc\xa8\x98\xe5P^<\xf1\xf2\xdfE\xfe/\xebP\x98\x9e\xac\x94Ny\x87\x84\xba\xf3\x88\xffߞ\x92?)\x81\xe8e\xc0\xfdS\xfd\x92\u007fb:\tj\x1ew\xa5na\xa6\x8d\xd2\xee\\\xb8T\xc7\xf7]n>,\xb2\xe6Jz\xa3\xbb\x8dF \"穼\xa2ƹ0-\xbc\xc9\xe1h\x8b\x00\x87\x96\xfa\xcbq\xf3\xb4\xb3(B?\xd6\xe9\xe6Z\b{)\xb2\xb1\xcb6{\xb3o\xab\x8d\x9bݔ\xee\x98\xda2WC\xc1\x06\xddtˋ\x82\xddg\x825\xf3T8\xbe\xc3\xf6\x8e\xb0\xbe,+O\xcde0\x1bHU\xe0ܺ\a\xb7vRrA\xd6D\x8f\r\x99\xa76\x91\x86\x12\xdbř\xa9\xff!\x16\xe8\x02\x04\x86D)n:\xa9\x9fnc\t\xa5a\x8b\xea\xb5=2\xb6ݫ\xef\xea\x1dws9\xf4O\xddY\xd8V@^\x8d\xe3XI\xfb\x03{+\x8b\x12\xea\xfb\v\xd5#\x96\xe7b\x9f\xae\x9e\x89\xed\xb7\xaaW\xf5\x16\x1b\xa0y\x15+\x02@%\r\xb1\xaa0.\xb9{\xba'~{d\x84z\xd2r\xed\xd3/\x11\xddێl\xf3\u007f\x9e\xbf\xe5\x9dL\x8d\xfe\x18*b\xdb\xd9d\xd2_\a\xbd\xbf\xa0\x12\x9c\x0eEc\xbdf\xff\xd7\xee\xe1\x15a\x93\"\xc3sص\xe2-\t\xfb\x9dv\x05\x14\xe7$\x95\x0e95\x86]\xdc&,\xa7̋\xdfP\x17\xac\xcbL\xaeY\xe1\xdf$8\xf0\xc0\xa0>\xf6\xd2\x0f\xd1=\xf8\xc2[\xc7\xd6w\xdc<\x06*\tC\x18~\xce$\x02\\\x98\xa5\x9eY\xc9Y7\xff\x9bW$Y\xe1\x93\u007f\xb1\xac^\x90\x9cq\xe2F%E\x85\xd6\xd9\xd7A\xd3\x14W\x13\xb2Q\xa9\x147\xca{\xd5\xf0\xe8\xad\xd1EH2\x87C\x06\xe6)C\xda\x1b\x8eu͔\xbc\xe7\xfa\x12.w9A\xc5Y\x14ȓ\x92K\x9a\xb1\x12\xd6\xff\x81\x9c\xb8c\xea\xeb\x99\xeb\xc9\xe7\xe5d\n\xd1Ị\x97<\xe5\x1b\f\xf1\x8e\xd7\xe3\x1dw\x82\x94\x1aTPN\xe5\x90\xfd\bwb\xb2\x8f\xbbԡ\xbc\xa2\xb2\"~H\xa7\xd06\xc86\xca_\x02\x10\xde0wnDKAAN\x8e\x91\xdee9\xb8\xbe\xce\xd2\x18\xa9\xd3iFVg\x04\xb4?\xfc\xf8\xac\x0f\xc2#\xc0\xd1\xde|\x9f\xcf\xe7\xda\xe0ּ^\x0e2\xa5|\x88Ś{\xe8\x84A&\xf0X\x10\x84|\xb4\xe3\xac[QhY\x88^\xa1\x90\xcboG|\xbe\x9d\xe1#W*\x83\x8afe`-\xc6ޣ\xac\\\x06\a\x16\x00\x856\xc6i\xf5\x85˺\xaf.tu/^\xed\xb2\xd1\x1ey\xfc\xf9\xe0\xb5kA\xa8\x94\xf5\xed\xc9\x05\x02\x1e\x9f\xaf/\xe1˙\x82\xd45\x99\xecn\xd1nמz\xd7]1\xe7\xda\xdaZ[ϝom\x15V95\x0f˅\b_\xe16\xe3\x9c\te^\xe5^\xc1\xcf!M\xe5\xea\x1b\x19\x06\xcdM\xb1\xfd\xe9H\xa1\bчVx]m$\xef\xdaՏ\x91KJM\xcf\x0f4F-\xab\x97\x18oQ\xebC\x1b\x9d\xaa\xb6\xda2\xd1\xc7\xed\xb3\x06\xba3q/\x83T\x9f]\xbd)\xf1\x1d\x16<6.jxo\xbf/|CA^\xa6[cB\xea2\xa9\xe3|A\t\xb9{o\u007f\xd01\xad\x95K{\xf1\x062A\x15`O\xad\xf1\f\x1bF8\xbb\xc2;\xa7\xd5' \xbe\x8b9\x12\x04\x12ƀ@bR\xc3]\ue523ʷ\xd1q,V\xdao\x17\x9f<*\xf0\x92\xfa\xf8l\xbd\x11^\x94ܫ\xb5\x18QcT\xa4\x8a_\x14\x91\x145?$\xb3U\xd8\x100_9\u05ca f\xc9\xd1)\x16\xfc\x03\x1c\x86\x12C\xc2\xc5\x05ץ\x95\xb4)\xa7\x8c\v\xf3י\xe8\x84P\xa6[\"q,6\x99\x17\xc6\n\x16\xba\xa8\xdd\x84#\xcca\xc8\x00\xe6\xea\x9b\x00cd$\xa0\\\x1e\xc7ـ\x87\xee\x9d\xf8\xb5\xefݻ\xfa\x8a\xe1g\xe3\xb5y\xb4Z\xbag\x8b\xebv\x8cb\x9f\xbc\xf7Էaz\x028\x16\xa9{ț}\x0eBh\xd2A\xb7{mD\x03\xfe\x86\xae\x85\x85.\x83\x1c\xf4'*K\xb8\xdbO\x1bik;\xa2\x9dD \x96#\x99\x13\x81\x80\x9d\x1c\xae/\x00h\x90\x86\x90\xba;\x93@\x1a\xff\xe0±\r!\xf4\xdb+\xffګ\x0e\xcc-\xb2\x16c\x9ckn.\xfa\xcb\xefv\x19\x96$?:\x8b\uf266\x81\xefܗ\xdf\xc5\xc0\xfeb\xc7{\xdf\x13\xcdaz\x03\xc0K\xc0\xc3\xeaޣd\x99GkyVֶ\xbc\xc1\x85\xa7Z\xf9\xafͥ:\xb7\xcc'Z\x8e\x15sg\x9b.\x02\xbb\x01O\\\x17/+\xdfi\xfb.\x84\xa55j\xca>(\xa1\x8e\v=\x82\xff>\xf2\b\xcbv\x1e\n\xdb\xf4w=7\\\x834\xbe\x1f\xd9\xe0߈y\xf1\xfa~)\xd8\xc9q\x19NK\x01\x02\x17\x02s\xa9s~\xf29\u007f<\xa2\xd8\vk\r\x9c\xe0\xb7{d\x01\x86\xa6\xdf\xd6o\xf9Þ;\x9f\xb5\xc7Z荄\xb0\x04\xe6A\xc4R\x894\xa7vríḾ\x96Ѳ\xf9\x12ʀ\x9e\x97\x8b&\xf2_>\xb5p9U\xc7F(#eI\x94|\x95K!\xba\xda\xe1В\x93l0\xa8\xa436\xb1\xd9n\xc7LG\x97e*6Ne\r/ˌ\x1a\xedԎ\xd7Ūjj՚\xe3w\xfa\xb4\xad\xf3e\xf5\xc57r|т\x90\xe0\x84\x94\xe4֔\x94\xd6\xe4\xe4\x04\x99\xa8讞\xec\xdd\r\x83A\xea\x9aZ\xcdS\xaaCr\n֔B\xe3I\x9b\xb2nt\x87\xda\xd6~\xb5-\xcd#Z\xe3V\xbb\u007f\xbb\xda\xc8v\xfa\xd6\x1d\x1bL\xb2\xacB\x01\x1br\xf9\xa7\"9ŗ5\x9e\xd8\xc39\xe0\x1e\x808V\x06\xbe\xea\xb8x\xac\ah\xea_d^\xcb:\x93|xmW\x01(\x9f\xb7~\xab\xdf\r\xfaM\xeay\x81+\xa5)#\x12%ʂ\xb5\x99\x1fu\xfd\xaa\x85\x9d\x82~ޯ\xbe\x16\xfb\x87щ\x8b*\xb7\xf4\xa4\xb7\xc5K\x13\xa3\x94X\x8e\x83\xaf\xef<\xf8\xcf\xd1g\xab\xfb7\x00|`\x10\x80\xcd\xda\xd6z\x12\xb0\xc6H0\x95\x1c\xb4\xcfikY\x98=2\xc7\x16\x16\xe4\xe1\xef\xfb\xe5n\r_\xfb\xd7u\xffX\xbf\xfeGWVҹ\x8e\x8e\xb3\x81Y]/K\x06\xf68[\x84\xe6\xc0\xeb4XL\xef\xfd\xf0\xfb\x87{\xa2\xc0J.\xfe\x17\xca\x14.\n5|E\xbc\xcf^]sҝc\x9dC\x11~\xe9\xe1\x1b\a\xd8\x12L@\xfe!=\xe1I\xa5\x14\x17u\u007fz\xeam\x0e\x9aʐ^\xbd\xdbIU:\x94\x10\x80\xe9\x98d\xff\x8d\xc3\u074c\x12a\x0e?a2h\x02/\x90\xb0\xc4i\xeey\xc5;\xd8\xd7n\x06Q\xba\x89\xf8\xe7o \xd8\uf413(\x8b\xfe\x81\xf9\xd1\x1b&\v\x9f=X;-\xd9?\x8fvkC\xca)\xfc\x9f\x9d\v\x1bfm9\xd9\xdcҟE\xf3\x95\xecf^\x1d\x1a\xa2\xf0\x85-MזJ\xcb\xe7=\xee4o,q\x97˒\xa1i^\x81X\\lX\u07b3\xdcۓ\x86\x91\x97\x97\x0f{-:\u007f\xae\x9d\xb2\xe5\xc3\xfb\xed\xfd\xe7V{\x14\xe2\x0f\xda??\xf5&\xdc\x0e\xf3*_i\xf8\xf0\xaf\x92]\xa2Ţ@\xb7\x90\xc1\xe3T~\x11\xdc9\x19{\xcc\xec\x1dU\xb4p\xad\xfcM\xfd\xd9X\x1dא\xa1\x05j\xfa\xc4\xc9S雩W::\xef\xd1@V\x14\x93Vپ\xaf\xaa=-\xbd\xbd}_e\xfb\x9b\xe3y\x0e\x03{\x8aĎ\x9d^\xbfg\x1e\xd4if\xe7h\xe8j\xa2r\x0f\x96Ԯ\f\x9d\xf2\xd70(\x89\xc7w\x1490\xb3\x8e\xa4{\xe6T\xd6,OT\xe1<~\r\x8a\x8a\xc8\f\x11\x12\x8e>ϷX\x18VX\x15\xd0\x17\x9d\x18\xb08\xbc\xb3^tΪ\xac\xf1\xbc\x99\xc4/\xad\x98\xa4\xa7\x86y\n\xb7F\x1e&\xc0\x91$ZL\xc4\xed\x03\xa4\xe4ȏ!D\b\xe2\xe6\xc6\a\xcaHn˃8\xe1m\xb1\xc9\x1e\x12\xb7\xd6\xe8L\xba\x90\xf9\xb7:\x8fd\xedJ\x04'\xaa!\x06c\\?\xd3<ƶ}\xc4\x1c\xbf\x8e@}\xe1\xdf\x1d\xce\x00\xde\xc9\xda\xce\xe6\xd1݁\n\"\x02'\xe1||\xcb2\xf9_}\xb0\xf8W\t3:\x98\xe4\xeb\xa9\a\xa0}6)X\xb4.\xb8邈\x97I\x93\x02\x9f\xabe\x8cm\x06ś\xcd\x00[\xd0:ޝ\x17\xb7\xe6r\x88m\xac\x8cL#hd \x98\x96c^o\xa5\x80\x11\xa6\x00\xa0;\xd16\xe1\xc0\xe1\x8aa\xd4\xf8!m\xac\xf7\x18\xb5\xd3L\xdaS\x89\x85\x1d\xe3\n>\x96n\xc4\xcaN-\xba\xe6\xf0\x00j'\xdc9\x18BP\xf9B\xdf\xdf\"7\xd5%\x1c\"\xa6\xcc\xcfJ\x9f<\xf4\x8b\x93\xe1\x0e\x05\x14Z\xa7\xaf\xdf)\x8c\n}\xa7\x06B\t\xe2[\x01S\xfd\x84\x8a\xf9\xf4gԓd%\x81\x91\xa3\x1c7\r\xd9O\xb2\vM\xa0\x10\x14mf\xf1\x9bZ\x8ddQ?\xc1\xf2\xa78k\x94\x9e\xd0\xe7\r\xb0\xb38V\xe2\x80\x1a\xd0jW\xbb\xbb{z\xda\x00\n\x935\x1b\xb9zՄ\x91\x94\xf1\xff\u007fff2!\x0f\x83]\xf3J\xc5\x187\xba\xfc\x97\xed\xe53\xc6\xdf\x01\x02Cƅ2P\a\xb7\xef\xf4\xd6,\xa8Mw\x88\x8fǹ\x9f\xd7\xd6*)\xa9\xa95H\xd8\xe9\xf9\xf3\x18\xa0\xc0%\x01 s\x829ҏt\xeeI\xf1T\vH\xe6\xc9'\x05\b\x89\x0e~\x1c\xa3\x06ic\x87\xddK\x01\x16\xdb\x1c\"\xba~X\xb0\xeb=~KH\xa0^\xe9!O\xfc\x00\x04\x19q&\xfe\n\xe4\"\x8a\x1a^\xae\xf8\xf2\u007fS9c*l`t\xc9\xea\x8112\xba2\xcaQ\x85d\x16\xf9\f@\x1a\xc0\xbf\xd9Z1\xd0N\x96\xff\xa3[\n:\xfe\xa8\x90\xacH\\\xc1\xcct܆\xd0\x02\xe3\x1a\xe8\x83Ce\x97S\xacS\xf0R|D\x16\xcb\x0e\x15\xe2\x90XECyd\xe9\x83hp\xe19@\x06\x00<(\xf9+\xf9\xf3\xdf\xf8\xdd\xc8$̙4\xa6\xc0\xc5;.\x199댋)5\x99\xf7d\xa7e\x19\xa5s\u05f7z$\x01\xbf\xb2\xdf\xfeU\x9b\xd7f\x1f\xea\xdd\xd0\xd2{\u007f\xfe\xe5<&\xddv\b\xd2$\x94\b\xa2\xe9b\xf2)K\xbf\x81\vW\xf3\x89\xe4T\xff\xb1\xcc\xf2\xdf\x12\x12R\xba8Y\xc1j\x93\xa5\xf9'\xa9\xbc?\u007fK^GW\x81\xd8{\x98o\x0e%8\xee\xf3\x8c\xf9\xd5dw\xb5\a\xa3\xb2\xc3\u007f\xb5\xcdJ\xeb\a\x06g\xdcM\xfcz\t3.\x9c\x8a7S\xcd[\xfd^\xa8n\x87?\xc1ԣ\x03\xd0\x01\x8al\xbe\xde\xcdC9\xa0Xd\xbf\x13\xd2C?\x1c\x04\x92\xcb5{/\xf6\xc2\xed\x95\xcc\xec{/\xee\xdb\xd5{\xc2 \xc3\x162D{D\ru\xe7wo\x8c\xe9\xaf\xc5\xea\xff̧\x0f\xe0\x1b\x82\x90\r\xf4\x81\xfc\u007fC\x1cj\x9bc\x86\xeb\x94\xf9\xd8T\xdc#Ț\f\xdb\x13\x06\xbd\xae\xeby+L\x91\xf3@w1\xc8\xf0\xfd\xe9\xd1\xf5\x9dc\xc0@\xcf]\xdd\xe9?\xec\xa5|\xfe\x9d\xc3K\f\xf6\x8b9\xaf\x83d\xccX\xb1\x83\x89\xdb\xdbe,r\x8c\xf7\xba755\x85\xe7\xcf뼼\xd0ِ\x98\xd1\xd7\\\\5A\xb3\xd4\xeb\t\xde7\x8a\t\x81[\xf3\xcdB\xfe~\xb8\xb5\xc1\xd1b\xe9\x05\x8ds\x1c\xcf^w\xf9E\x1e)`sOrя)eަ\x05lCZ\xdc@Kg\xfe\xb1\x9eߝ\x9d\xbfz\x8e\xe6/mi\xb1M)\x1b\xba|\x19D\x11\xdfR\x9dѿ\x93\xaf=\xa5\xa8\xca/\xec\x0e\xfc|\xba\x04\xc7pzW\xa4PC<\xc1\xfcx\xd9u\x1f=(9\x96\x16m8m؊\xb7-LW\xae\xb0.n:\xb1Z}w杠6\xba\nw\xe0\x82$\"\x9d\x05\xd9O5\xa6\xf0t\x885\xff\xbe\xe9\x8c\xc0\xe5Nց\xf4;&̢\xed\xa5\x03 '|^0\xdf\xfc\xea\f\xd5\xf5R\xab.\xf2T(|$p\x82\xf9Ȳ\x9b\x18\xa7\xcb!\xbe\x85M:\xc0\rtoTĦK\xfe\x99\x94MH'\x82\xd2\x1d\x93\xba\x95\xe9O\x8a\x8b|2\xb56N\xaf5\xef\x89k 1J-\xd91\x11\xeaYYs\xf5\xba\x1dViU\xdb8o\x18\xf9\x04\xc2fYp\xc9s\x1b\xb0*\x95l\t\xc9/\xd6Evs2J\xaa\xe2/\x80?\x1d|\xb0\xb7Ÿ\x83F\xa4b-\xd1VAcF\xef\xeb:\xa2\xd4\xc1\xccl\xeal\xcd\xef{类\x13\xfb\x15.K\xf4\xf9\xbeM(\x186MY\x17W\xbf\xd5,3\xba\x1e\xabw\f\x97\x97\x97Ec©\x19Q\x9d\xe1\xc1\x8b\xec\x1b<\n\x91\xa9\xd8CT?\xdfl\xfc7UZ\xb6\xdf\x1c\xf7\xad\x8a*\xd1\x1a\xcc{\x04E\x8d\xf2ipCT\xb44\xbec)f\xd9(1/\x06\xf6\bZ\x11,\xf1O\xa0\xa2\x10,\v\xbc\xc8T\x9aeE\xd6\xfb\xc6\x16C\x95\xfa\xd0kؖ\x12\x1c\xf7\xcd\x12K\xfc,\xf1\x0eKH:\xa7&\xc8\xf0#\x1bH\xf7\xb1\x00\xe9D5mrH\xaf\xf1\xc1\xe3?3\xb1Q\xdfF\"\xad\xbf\xbcD\xd3Љ\x8f\xa4\xc26\xd8\xf1\x8aŷP\xcf>\xb8\xdf\xe2\t!\xbc\x15Uq\x86u\xf2.\xef\xddf\xdcc\xe7^\xe4\xb4t\xdd\xef\xc0\x80X\xac\\ZZ\xe3J\xb3\xb09\x00V]\xadбو+\x0f|\xf3f\xef\xe4q\x81,\xc4\xe7ҏ\xf1\xe5A\xae_/儘\xe5(#\xeb :\x94Γ\x89\x95k\x80Q\xdcn\xcf\xcb~C\xa1\n\x99\x9b\x9b\x9a\x16<\xafϳM\xf7f\x8d\x81ɥ\xa6$\x04<;\x13\x85\xe5\xeb\x94\x0ee\x8f\xa2\xe4\xceڤ1\x18\x1d%\x89\x89i\xcb\xf3\xd1EU\x0eg\xf1q*;\xf3\x8fR\xe6\xe61=X\x8e\xc3hW`\x87VU\xd8r7\xb7\xef\xce.Y\x12\"\xceq\xf7\xa6\x02y\xcdW\xb3(\x9f\x03M\xac\xee&\x9f\xfeq\xb4\xfeψ\xf7\x97\ab\xf2\xbe\xfe\x15)\xffc\x0f\x80\xb6\xf2\xd6\xd5Anj\xdeI\xdbW4y\xa5\x1a\x99t\aҝ\xd71\xa8\xda\xecQ\xca\x16\x02܃\x8a\u007f\xecj\xf6\t6\x03\xcb\x1c\x8c\xf7W!h\xeed7\x17\x177\xf2\x8b\"N\xb8\xbc\xaf˴\xf8\xf4\x8e:\xeeC\xf1M\\\x00t\x84\xbbi1r\xb1\xa8[\x1e?Ѓo\xcc{\xe3\x8dTEz\xbdr\xb2\t\xe36\xab\x17\xe3k?Z\xba\xfc\xacQ[\xbb\xeb\xdf7\xcf/\x8c\x88\xc0\x8a\x19\xc8V\xff{.\xd7\b=ծ\xdc\"\xb2\x15+\x9b\xaa\xf6\xd39=\xeb K\xd3Le,`S\xe9\f\xb2w\xcd9oW͡ɓ\xfcl\xc6\xda\n\x1d\xb8\x19_\xd4\xd2G\x97׆aR\x920\xba\x98e\xc3\xe2\xc9\xd2\x1a_ǁ\x1fu\xf6\x89\xb6\x035\x05\x93\xcbX2\x85\xeck\x9e>\x16\b\xa6\x99\xd3[\xb7:\u007f\x82\x05\x12\xfek\xe1ї/7:\xf3YÒ\x94\xf4\xf4+W.1Ad\xd6\x0fe;\xc1f\xed\xf3\x9d4\xf6\xb8\x89\xad\x8dY.\x94\xca\xdb\xe6H:\xa6\xe8\x01^\x9e\xc3\xca\xcaθ`\"\x1a<\x9dH\xe4\xa7\xfcW\x9f\xdd\xf6G!\x97u\xb8M\xc7,\x19\x1d\xea\xa6Z@L\x14T7\xddcC\x83\xc2\xdd\x12\xafގ\x86\xc0\x92\n\xb9\x1e\xde>\xd1\x1c7%1\xb5\xb0$E5\x94\x9a:D\x96kP\xa9\xbc\xe0\x8d\x85\x8c2r\xa2\x13@\x88\xa6\x83\xd8\xfb5\u007f\x85\x13\xb6\xfd\xaa\x92ݕ+\x8aZ\x85\xfa\x98\x12\xabf}\xa4\xcaG\xc6\r\v7\x9b\x9a\xe2\x8cR\x1d=\xb94GOb\xfa\xe7T˷\xb2\n\u007f\xb9\xe3ώ\x93\a#_\xfbw\xab\fTaҳ\xa6j\xfa\xa6\xa3t\xbd\xc3[\x9a\xcc\xfd\x8c\xc9\xfaH\xbf\t\x81-\x04ys\x89\xee\xe2G\xb0d\x18\xc8\x13h\x99Au.Z\x9154N^\xbe\x03\xb2\xfa\xeb\xeb\xb3R\xe1Ӳ\x13G\xdb2Qё\xf5\xfa\\I\x16\x87\x98\x1f\xbd\xde>\x19\xf3\x8e]z\x1cP\xcf\xc5\xe0\xd7=\xe8>';\xb4\xbdr\x9b\xcb\xea\x98?\xbe8D\xc5x[k5j\x934I\b\xbaT\xa7U\t\xb3W\xeb0\xb0*\xab\xa3\xa7\xaa\x16hڬ\xd8Fg\xa5\xfdLRg\xfaX,\xcd\xc7\xdccA\x8c!\x8a\xef\xec\xe6*\xfc\xec}%\x93\xe4\vs\x15\xf7Y|\xfa{\x04\xc8F\xbb\xdd\xeb\xe7+\x91\xfc\xceu]\xf8$\xd8_oI\xacr\x12+\asź\x19\xfdv8\xbd\x98\uee68s\xbb\xe7\u007f\x8f\x96R\x82\xa8\xdc?,%\x18_\x8a\xb9'N,\x17\x838+ \xcb\xd9\x04\xb1kħ\xf1F\xdfgd/$[\x865\x97\x0e'\x9c\xaeZ\xceǡ\x1e)\x95\x9b\xb4\x1aA\xe2{P\x93\xa0\xe8\r{\xad2d\xfe\xd6fܥ\xe9\xb7C(\xa8\x83\xbfQU\x99g1\xcer\xf9\\;\xe2H\xf9\x8b\bb\xbe\xcd\x15b\xd7\v\aτ\xf4\x85\xff\x1f\xae\xe0\x1e\x94\xa3e+lI\x8f\"\xdc\"\xf7Ӝ\xa4\x95\r\xac\u007f.\xbc?\b\x93\xa9>ik\xed\xe7\xd1\xe5V2Y\xd6r\x14.\x8f\xc26\xe9ы\xcf<\x1a\xdcOF}K\xdf\xe5lc\xc6+$\xa7#˧\b{ɘ\n6S\xb09Ґu\xb4\xd3\xe6\xe1d\x84\xe9\xd1`\xfb\xee\xe9*\xb4\x12ٕ\x04X\xcf\xec\xb9\xd55\xdc=\xfa\xfce\xe7\xe8\xf4\xbf\x12ou7~\xff4\xd4-\xd2x\xfd\xc8f\xb9\xcb&\x9f|ۼc\xe5\xad\xff\xa3\xb7;¼\xa5,\xbbZ\xed\x9c_ݥ\xc3&k㯩\xc2\xfd\xe9\xed\v\\&\xf5\xafcwF\xf0\b\x91c\x16렮7\xea\x1e\x87ؔ\xfe\xfc\xceWK\xc5\xf7\x8b]\xda}Q\x81Y\xa7:\xd2\xdcH\f\x12A=r/KuWT\xd2\x167\xfd\xb2\xccVoi\xa5\x01\xb0\x9e\xf9\xe0\xad\xcc\xed;\xe4\aՍ\x8c\xbd\x1c\x18+\xac\x19ݖO?e\x93\xbe\x8f\xbf\xea\xcam\x8b+\xd79W\x96\xa7\xff*\x863\xad\x8b\xd5\x06M\xaa\x93u=\x02\x17\x9b\xbb-\xf8ZR)\xc2\xc7\xe7\xc0\x86\x88Q\x89v!E\x10\xa0Qa\xb8(9\x97\x15P+Bv\xc7{@\xd4\x05E\x1c\x125\x96*\x13q\xad]?\xac\xd3\x1f\xff\xc9\xcfvS\x92\xd8\x0e\x89!W㐸\xc47\x19\x14g!\xdaN\x8c\x82\xe7£Ir\xb8\x86WO\xb0\xe6\xbc\xc7\x04ԇd\x16mb\x83\xae\x92W\xd0B\xbb\x94\xbeM\xc4!\xe3\xb8\xfd\xf5*I\x98\xb0>\x05t\xd33\xe79\xa1 \xf33\xf7\xe8D\xa0\xd6˓\xb5\u007f\x98\x1e\x1c\x86ʬ\b\xef\xdf\xea\x06\x1e\xa3\x83\xd1\xe0y\x02\xec*\x1f{+\f\xeeI\x19\xf4fD$\x025w\xbf\b\x18\x94\xa0\xa2[E\x06\x8bG\x13\xa6e\x93\x86L\xdfe\xbeur\x80H\xf2\xe0\xba\x0f\xc9\xe7\x021\x8c\x9d\x11\xb4\x1b\x95\x1f\xbcT\xff~Χ\x1dtWyw\xb3$vsj\x18\x12\xacf2(\xfdd\xe8\xdb\xefF\xd7g]kSz!~\xa0']\x1e:4\x05`\x04\x89lyi1\x9fYʸ\x80\xc07y\xf3\b\xadT\xe1\xa3\xc4)\x12IJ\xd1\xf2\x9bu\x8b\xcd \x96\xb2\xa5\xbe^\xc4\xc2\x03ճ\x12\x83ķ\x98'^D\xa1\x1f\xe7v\xd2\xd4\xd0\xd1\xf7\x83\xd3IwN{+$>\xc7|\f\xf2\xd4ؿ\xf3z\xecFd\xf9\x17\xde\x19\x9c\xdea\f\xed\x1e\xba\x91\xcc\xe6\xa2\x04O\xbc\xdfbDL\x1f\xd1{̬\xf7\xb5o\xc0\xbe\x9d<5|\xffʐ\x96\x80-DI\x9f\xa0ߚk\xf9\xad\xd7y\xeb\xb5BoW\xc5+\xf7o\x95\xcb\xc9^\xe8\xc4'^N?\xf1\xc1\xe8 \x16=8\\|7rp0\xe2~Iq\xc0\v\x87X\xbc\xf6\x8a\x9a\xec\xaa\xfa\xb3\xa3\xca3\xad\nX\x94\x9e\xdadyz\xc0\xacl\x99\xc9\xc90\xadE\x18\x98\vp)\xf5\xdbK\x15\x8cd\xad\x1e\xcaBĔ,\x8bD\xe5K\x84\xa1\vΞk\x83\xb9\xfdm\xff\xf1\xee?^$\x87\ffRd9M\"Q\xa8\xc8%\xf8\xeb\xa7ƨѣf\xf4H\xaa\xc7ç\x87]\xe99_R\xdc\xf9U\x9b\xb0Aq\xf3}<\xdc\xd8\xf9\x84\xd6=\x80^\xf1\xc6F-ڋ\xd4V\x9a\xbb\xf2\xbc\x06욽Vq\xbe*ĝ\xaa/\u007fs\xdc\x00r\x94\x9a\xe7\xfc\xdd\a\x0f\xa4\xb0\xeb\xcd\x19u!`D\xeb\xdc\xde\xf2[I\xebw\xf8=)\tEk\x8av\xde\x1a\xab\xd6kȿgou\xfdS\x94,`\xf9\xe7\xeb\xd1*\x0e糣:\x88\vg<\x84N\xb7\xa3\x89\x91Ͼ${֩ڗm\x98\xbeߕ\x9d˻:7m\xcaL̝VP\xa8\t\x89\xf9\x0f\x84Zo\x9dx\xe3\x86\xf4\\\xd9b'C\xf8\xdaL}zq!=\x15Ew\xa2\nh8\xaa\x98\x93t\x8a\x92[\xc1F3Xc\x84\xd5\xe1Xru\xf0.\xbd$\x04K|\xd83\x8ab\x93\xd38\xa5\xf5\x99r\x05\xa5ҋ\xab\xfd\xdb?M\x1czbި\xaaA\x1f\xe3ԧ?\xcak+Q\xb3\x19\xea\x10=\x8aJZ;T\xf8gr\xf9\xbf]M\x1a\x13{C\xd4}BK\xc4\xd4\x15&0\x9f\x8cF~~\xa2Y\xeb\xe3:\x8dP]\x9a\\\xc8\xe4\nB\xb6\x8c\x92T\xa4*&\xb1\xfd,F\x1f\x9fu\x04\xf4U\xb7y\x97\xa6\xe3`H\x81\xfdn\xb3\rn\x1e\xb1\x9a\r\vF\xfe\xe3\xc0|\xcaK\x8fln\x86\xd1x\xc0\x8a\x19\\.\xe5\x8d\xf0\x98\xdeH|\xae\xc3\xd2Im\x8e,\xc5i]\xb3&\x1a\xa7\x01+C\x8a\xb99\xf3\xf8\xa7\x1bD\x0eZ\x89\x86\xe8\x817\x83+\xe6\xfbg\xfd\x8f\x90\xc7Ds\xc1\xee>\x9c\xa9\x85\xf0\x01\x9e\x04\x86\x8a\xe4\xb3\xd6\fmb\x98\u007f|{\xec\x86{q\x80Ouye\xe2\x87ڬ(+7\x04\xdf\xe4oʈz0'\xf0#2\xb1VQ\a\xb2\x9e\xd8Ǘ\x14M\x86E\xb3\xaf}\x9e\n\x1c\x1f\xab\x87LK\xef4\xd4\b\xd6~\xf6I\x89:\x9dֲnj5'J\xd0e9wse>{hP\x99\x8ag\x8d\x8b\x16\x94,\xab\x88f!\xa6k\xe5\xcb\xd8土^\xd8\xc2Ɔ\x8c\xe8l|\xc0w\xfcu|Ñ߬\xba\xc9\x1b\xa6\xa6D\x91Q\x93x\x053Ck\xdfp\x16)\xbb\x9c\xf3e\x0e\xaf\x9e\x8dC>\xa8\xe5Ԟ$\x1e\xda2f=\xe1\xe4\xfc\xe6\xf1:H\xca\xfah5ڢ\b\x12\xa1\xfahF\xec\xfc\x9c\xe1L\xb6,@\x98\xbe:\x9c\x90\x9f\xa3\xb2\x18E\x98\xb2~7\x81\xe6BV?Q#\x91\x98\xbc3QA\xe6.јڬ\x90\xeexW\xeduj\xcdT\x03\x82a7\x1c`N\xba\"\xa3*\x8ckKbY\xc0\xcbJD\x1c\xb5:\n\x9e\x89\x14,T\xfa\xb3\xbf\xce3sq\xc8%̓!L\x8eo\f\x04\x12oP\x8cM\xbcZ~8_\x1eBU\xeb\x9eh\xbd2|\xa5\xf5H\x14@\x95\x87\x84\xf3\xbd\xf2m\xe2\xadEj]<\x14\xaf\xd6m\x1f\xc8\x18\xee\xbb\twFɇ\xbb|\xea![\xa3\xca$\xac\x05\xe6Q\x8b\xa5#\xcfz\xf5\x96\x98\xeb\x00T\xde\x1a֞N\xe8\xca6\x12\x84\t\t\xef讎\xa1H\x0f\x1dNb!\x1bb'\x16r\xc8V\xe1\xca\xc5!R\x05\x95n\x1e\xaf&>w\xbe\x89\xddw\xfe\xac\xbb\xda\x1e\x98\xb8\xaa)\xa4rR\x19`\xf2><\\\xba\xe7|\xf1\xa6a\x00\x91 \t\xc1\x10\x90+\x8f\x8eQ\u007f۹o\xa3\xee\xf0\xbe=\u007f\x02b$Jh\x93\x99ܒ\"A丄\xa3uu?\xf9\x04\x04\\\x1e\xef\x9e\xc4hG!\xee7\x15\xca˽&K>\xaa\xaep50\xabE\x99\xab*\xe5\xd4\xda~#>ĤR\x94>p8%\xdf\x1dq\xfc\x9b{\xff\xcd}\xcb#\v\xbb\xf2p\x87\x89\xd3\xc4q\x93Ϳ\xc6fOG[pVa\xa0\xe6r\x11N\xe0v\xc8\xff\n@`H\x94r\xear\xb7\x9dUHk\x9aέ|z\xac\x98g,t\xff\xf0Q\xb2\xf0\xe0\xecͭ\xe8Nb\xfc\a\x0e\xb4)\xe5\xedY\x18\xc40G\xdf}ws=\x97\u007f?\x111\xfd]\x06\xf8Ο\x1e.:\xe7\xd3\xe6\xd1X\f\x01\x89\xefӻ$\xa3\xcc\x1cV\xb7ލځsw\xd7\x05/\x8c\xe2@\xad@\x01\x05\xea\xee\xde{W,}v✥\"Ըz\xd0\x03\xf4\xd4\xd6EI\x1f\aIK\x90\x1dU\xed\x00\x81ŏIe\x9bP\x00\x91`\x89\x9bfq\x984ꒀ\xd9y]%]\xf0\xba \x9a\b-\x1c\"Փ9\x11\xe1s\x87\xe6z\x10\x18Ri\x11\x95 \r٪\x1fӍ럤1\x13\x8c!\xa4Sj\xf13\r\xf7\xc7^\xac\xa7-S`Y9\x00\x04\x84\x9c\x1f\xb1\x04\x80%̥ʒ\x81>\xa22\x97.\xbe-}\xb7pѷ\xe9\xf37\xf7\xb9^\x16\xe6-R\xb8\xd52\xe9U\xef\xb3[\xe2\xf2\xf7\x0eKV\x8f\x1f^\xb5\xb6\x8f\xbbj\xf1\xd0]\x12\xeeN牅\x1e\x81a\x8e\xc6\"}\xfa\xb6\xba-\xe9\xec|\xf6\xb2\xfa\xb2\xba k\x1f\xdd2a\xfa\x02^\xa6\xa0\x9e\x94!b)-D\xb4\xd2\x15*5\x0f\xcf7ho\xd9Ѡ\xfb\xe8\x82J\xfe\xf2\xd2?\\ζn<\x1a\xf4o\xff\xf1Q\x9b\xfe\xb40^\xa606\x9e%\x06g\xf2\x15>)\xc0f\xde\xc7U\xcc*7\x05\xdd\xc2\xc7U\xe8\xfd\x9c\xff'\xb9\u007f\x88M$+\xeb\xcf\xfd\xc66_7\tԤ\xa0\xfa\x1fY\xd5\a|j\xf5\xf4\xf3\x8c\xc1ip\xfa\xffUzǵA\x90\xdb\x13\xaa\xc8\xfb\xcf\xd6\xf2\x8f\xb4\x0e[\xd2\f\f\x93\x92.\x10`\b\xf4{\ff\xbb\"[ꨃ\xa3\xf6\x9c\xdfH17\xd60\x83\xaeu eeɲH\xb9k.\x92\xbe\xc2a0<\xd1bGQ\xd0Ji\x99%\xb9\x91_\xca+!\xd7\xd3\xe8}W\xf7jۑu(Gkf\xe2\xdcEsF\x98/\xa3r\xd9\x01yy\xc5#\x8aX\u007f5\xdaF\x99\xcf\x13H\xe2Ʈ5\x10\xfb\x9f\x1aY\xcbe\x99\xea8<\xf21\x1eg휨\x1a\x98}fP\xab}\xd5,-\xa6^_\x8aJ\xda\xc6\x1eϷ&}$\xf1\xdb6\xc7vƸ\xdb\xee\xa4\x02\xb1\xffe\xd7\xfe\xd6\xc4o\x910\xaf\x8b\x1e?\xeb\x1f\n\x01{\xd4d\u0096\xe1\"\x00\xe2\xf6+\x06\x95\x14=\x99\xf8\x92\xbe*\xa7\xdac\x8c\x14y\xaa\xe2xy\xcbZ%\xad=vS#C\xc6\xe99\np\"8*\xec^\x1dZx\xc4\xdd7S\xd6͊;\xcf\xd9\xc5\xfas\xbc\xb9\xb8_\"\x95̯i\xbf\xd7#'\xc9+*\xc7q2\x1a\x13I\x05\xd2\x1e\x94yl%\v\x97\x8aE\x8e\xb8\xa4\xa3^[Ɖ\xf8\x857\xb08A\xac-\x02\xd3\x164\x1c\xf3㋲\xfc.\xab\xf8A\xd3u\x1eF\xea\x8dEOZ\x9aa\xde\x18;R3G\x05\xd5F\xe7\xe4\xc6\x18\x18\xe9~\x14\x03#\x91\u009e\x18\xe8\x89T\xba]\\{jg\xa1W\xf4\x8c\x0fX\a\xb3\b~\xf6<\f\x9bpDm\xb1E\xcaݭ\xe2\xf4\xedQ\x86\x1a\x8eG\xbd\fC\x04\x1b\x0f\xf2\xf0\x88\xf8\xe7=p\x89$sC\xabT\"\xbb\x91\x8cY\x1f\xb4uG?\xdc\xd61zˠx\xab\xfci\x01\xb5v\x915:\xe8h\xac`\xd2\b\x87亟#\x9f*\x1d\x90\x90\x91\x9a\x8c,\xf7\xba\xa3\x18\xeb\x92f\x06\x10\xf8#\x15>3e\xb3Tu\xed\x93\xd4u+(\xef\x81l\x85:\xd3\xcb*o\xd8w\xc6\xe4\xd0\xfe\x03\xfaQ\x93\xfb\xe5ʑ\x8f\xd3GwE\xbf\x02\xad8\xf8\x85\xf1\xb0w\xae\x9b\xaf\xbfU\x95\xb2՛\xfcn\xeb\xa7\x0fK\x8c-\xb7 \xdb\v͎K\xa1M\xed\xb2\xe3r\x869\xbf]\xaday\xfa\xb6+\xf4\xd02\xb6\xb6\xdep+ҹ\xee\x1f\xac\xba\xa8x\xa4\x97?\xe4_Q{\xf8\x8c(Ƕ\x0e;\u007f\n\xdd-\x17!\x98\xe41FR\x1b9n\xff\x14f\x8b\f\xf7!\xadК\xda\xc7\x0f\x9a\xaa\x11\xeb\x91?n\xeb\xf8\b\xe6\x8c\n\x00 c\x03D$=K\x8e\xb6n,P\x89Yg\xfe\xcb\x1axq\xcfͩ\xd1'C\xf1\xd0\x06 \n\xde}\xd6G\xe1%3Cg\xadQ\xf9Ӝc\x0e$\xd6n\xf7%lcf\xe8\xacUˌ\xa0N\xea\xb7\x0e^\xf6ޤ\x1e\xba\b\xa3M\x86-\xee'KV\xbcϚ\xcd9\xb8y\x91\xf3e\xf2z\xb1bQ\x03\xa6\xbeȵ\xa1\x10\xb3ƏxT\xe3R\xd0Q\xa6\x97\xa65~\r^\x9d\x8du9g\x11\xe6\xf03\xff\x1cf \xcc{\x89\xae&#T\x8a\x88u\xd3H\x9d\u007f\x048%\x15\xf8\xa6\xdd2t\xf2):\xf5N#\xe3\x87s\x9a??%\x92\xeb\xa8?05\xa5\x12\xa7\xb6\xd2љ\xb4T*\xad\x03R\xbb\x03\xacg\xd3)Sאy\x1a\"҇SAܻ\xca錪\xaf\x17)q\xfe\xf7\xa5R\xd3K=W\x10\x97H\xc4\x12\x90\x9d=\xb0\xbc.(<\xbf\xd4\xcb>L}\xff,\xce\x197\xf0汫\xcdƎ\x1a\x97\xb8P\xa7\x9e\x9f \u007fs\xed+\x1e\xe6fI\x02\xbd\xd0X\\h\x96\x8f\x9e;s\xc6b)\xf4\xbd.V\x00\xe1\xaf\xd3Ħ,\xec\xd5|pU\xf7\xfc\x99\xe9YY\xb5\x18\r\xb5\xae}\xd20ӐT\xa4z\x16\xfb\xadq\xdaM\x9eeRp\f\xfc\v\x11\xc0\xcf\x16-N\x02S\\ \xe0\xc0.\xe3]\xf0\v\x93\xed\xf9H\x87d\x8fv\a\xc7i\xdfdK9}\xefdqz\x81\x0f\xe2\xe2K\xca\x105\xf3nX e\x87\xe7\x10\xfc5bF\xbf\xa0\xc16\xa9ʍm\x87C\xca\xd4@;\xe5?\xb7{\uf22eR,l\xb0=\xde\xfa\x9a\x86p\xc4\xfde\x99\xf0\x88(\xef\x16\xafFM-\xdf\xd7c\x16\xc4<\xbb:\vG\xc8Н\x1bn\xc8\xd1喊\x05\xde\xe6\x14\xdb\xdc&\x18R\xc6aRV\xce\xd5z\x14*/\x83ҴT\x99#\xf3\x9cH6\b\xac\x80\xf1\xa1\xecv\xd9\xf4\xad\xb4\xdb#\xca\x0fI\xdd(\xd7\xe1\xdb\xe9V!QҠ\x84\xcd\x18G\xe7\xf0\u007f\xe9߄\x9d\x04\x17+x\xc4m2k3\xa8\xa6\x03\x18\xbb\xe1zU\xe635հ\xa7\x03\x85\xf7\xb22o\x1a\x99~Gq\x88\xebr\x94v \x0f\x04\xc1\xb3\xbe\x82\xf3\x0f*\xe3\r\xa5\xb5[Ւ\xe1C\xea\xac[~:\xebm&\x95\xdd$\xde\a4ij\xa5\xefB8\xaf4|\x97\xba\xfa؍pH\xdc\xe6r\x9f\x1b\xd7\xd5+ƺQ)\xa9\xda\xfb\u0602\x99I\xde\n\xf2\xc9\xd0g\xb2H\xa5S\a\xcb\xea\xaeba-ui-\x81\x1f\xc6l\xff\xa1\xdb\xf4\xf8/о\xd00\xa6\\M\xcb}K\xf8?\xe4FdD\x93\x96{=\xc8{<\aԍ\x1f\u007f\xe5\xef\b\x8a^Ѡ\x92\xef\x8c;\xf0\x8a\x15\xf7|x\xbb\u074b\t\x97\x1a\xf2\xf9]\x1d94j\xfb\xcc\xdc\x1b\x11F\x11a\xfa\xa1f\xdf|\x12\xc6l\\\x16\x1b\xbcQ\xb3!\x8er5\xb9\xf43L\xdd\x1fc\x1b6\x9a?a\xe3a\xf6\xb9\xc35\xdfc\x8d\xcdG|\xaf-\xf8\x01\xc7\xc0\x87\xd9ls^\xd7\xf3\xab\xc48\x9a%\xcb\x176u\xe0\x9bO\x86\xe79\xe3Q\xb8\xbeǟ\v\x97\x8enXIx\xaf\xfd\x8d\xf24paܽ\x1af\xbdζ\xdd\xf6K~\xca?\x1c+2yI\x05b)\xa2\xb1;\xe6(\xf9\x1a\xe5J\xfc\x91\x8eΕ\xd0FH\xba\xea\x91\xf8\xf1\xce\xd8+\xe0\x84*1&\"ɰ\xe1\xe1ɍ\xac\xb0\xddP\x02\xc6a\xce%'o\x06\x91f?\xbd\x1c\x95\x84cO\xcaO\xc1K\v\xe4\xec\x1d\xdf\xc28\x9cVz\xea\xecM\x95\x8cé\x91cg\x88\xce֧\x13\xf5\x1c6Y_}\xf7\t\xdb\x0eo\xfc\x94m\xbc\xf3\xff+zgT|\xee\x85VQ?'\xa9\xf5\x11\x8e\xe9\x99\"\x91x\xa6\x18\x17\xe4R\x8f;gO^\xd4L\xdb\xc48;\xd4\xdeq\xc6\xdc\xdea\xb0\x87ߘl\xadLb\x8cL\\\xfaWw\xc8>k\xb0\xf9\x86~[gwk\xe3\xf1\x05:\xdb\xd1>\x042}Z\x8bB\x9f{\x9d\xa3W\x13\n,\xf2\x91w\xbd&\xdaS\xae k\xd7\xcb\x16\x17a@\x96Ը\xf3?\xb96>3\xa3\xa1\xb6\x91n=)?{\xc02\xf8\xdc\x01\xa5\x84\xca\xee\xf0\xf4\x86\x9a\xc6H2\x1d,\v)q\x93\xc2H\b`\f\xfa\xbeޕ\xba3jkTĞB\x19\x1e\xfa\x98\xc8?\x92\x12\xdeQm$%\xdc)\xfa\x90\x94\xf4\xe1}bU\xdaq_\xe5\xb1c\xbeqY\t-\x84\x1b\xd7\xd4\xcb\x13\b_\xcc1Ӂ\xbe)j\xd6?\x15\xa8\xa7\x87E=7>\xa7\xe3-9\xe46\xe5l\x9f\x90\x04\x93.\xaa\x9b s\xf1\x96\x82x\"\xe1h\xe0\x96\x82\x8dc\xbe\xb7\xb8[\xc0\xcfy\x9e\xbb7\x87?\xacN\r-\xfe\x16\xb1\x80\xd5\x12\xb4\x18\xa2 T\xe4\xf1\x10K\xcd\xf8\xd2\xc5\x05\x82\xf4\xf77\xf7\x049\xe5|\xb1Ѱx\x18z\x94j\xd2gmh\x1c\xe4I\xf7n\xb7H\xdfo\x92g)\xeb\x88v\x87\xaa\xd9~\xda\x16\xf5\xef\xb3\x1b\xeb\rC\xff;\x10L\x9aJq\xed\x9d\x1au\v\xf3\x9b\xd2p\xaf\x05\xfdmW\xe7<\x15\xf7\xb6\xb3˗=\x17l+(\x84lCP\xfc\xf1\x19\x9em-\x97[I\xb3H\xf7HK(\x85\x93|LQk\xe0g\x0f\x81ª?C\xf8E\x11Bx\xb8\xbf}QN\xe0\"\xcd\xf1;\xb6\xb0FNU\xb1\x8acE\x0f\xec\xa9\\k5E\xe1G\nн\xc8\x12^Jv\xd8<\x84+\x97\xa7Dk\xbd\v\x85\xcfr\xaeK\xecC\xadN\r\u007f\xbdw¹*\xae{\x8c\xf1\xc1\u007f\xc2\xf6\xb8\xabϚ\xf1\xa9>\xe8\x06\x16\x8dj\xa1\xe3h\xf8\xffÉ\xbdW~\u007f{\xaf|\xf4k\x9bÿ\xab\xcf$\x06a\xff=\xb9\xb1\xfe\xc8\xe3g\xf51\xd9i\x0fz\xe7\xdd\xc0\x83f҆M\xac\x8bm\xb3\xa5\x06 \x01\xeb\f\xf0\xd1\x1fz`\xd2\xfb\xac0\x1aX\x10\xff*\xb4\xcb+G\xe3\u007f\xa8n\x1a\xd1\xd1 ?J\x0f\x1d\f\xeb>\x03\xc8\x06\x00[St\x81d\xb0>)\x17\x8d\x12\x03`zdM\x1f\x91\xb8\xb4+\xc89\x82\xc1\xae,Z', į>\x19\x10cu}n\xc0\xaamĐ\xd8N\xe9\x90=\x99z8\x01$\x11R\x02գ\xcc3\xf2c\f\xb1\xa8\x171\x01ME\x10\xac\xc4K\xa4Y\xbd$\xb7\xb6\x8d\x8b\xa3\xd3\xf0\x86\xd8\x04\xa1\xd4\r5\xea\n\xe4]\x1cY^=\x15xܠ\xe6\x88KHUNy\x18\x8f\x9c\xaa\x88xU\xb4\xc1\xf6\x04q\x9a\xe6Y\xc9\x02\xc8\xf8d*\xd5gg\xebm\x02nL\x15\xb8%\xae\x83r\x16䰼!\xda@\x94\xd4Z\"\x13\xc6\xf2\x9c[\x1e\"(\xf2͘pf\xea\xcfk\xae\x96\x1b\"v\x87\x82\xe0\x1b$\x0e\xbeρ9&\u007fL\x9bI\xb4Q\x1c\xa1\x91\xef\xe0V\xfc:\xbe\xbd\xb2WIZ\xd4k\x8a7\xe6\x02\xe3TT\xbe!\xccX\x9b52Q\xa9\xa8\xdcI\xc1\xe9\xc1e(\x8bZ\x84\x05\xa6\x86P\x9b\xf8\x04\x15\nb\xa7\xc5}LL\xddϰ\xdf:\xd6.'\xaf\xcf\x02\x81\x18T\x12/\f\x89k\x88S\x16-\x8b>\xaa\xc1\x9cl\xac\x91T\x12\xd1\x12\xc75\xd1}T\x04\xd7r\x03\x05\x8a\xea#\xc4e\xe4(\xa7\xfcS\x9cG\x8f\x9c:\x16\xae'W\x11m\x9aP \x008\xf9\xe1\x9foV\x95V\xc7\xf5\xbf\xd0\x1d\x1a\xee\x8e7S\u007f*\xaa\x8a\x02\x176⋫\x83\x92-\x82\xff\xa87k\xc3I\xe05P|-\xe7w\xd1\x0f\x98\x85\xe0S\x9cX\xaa\xd1\xc9\xed\xbd\xe8-\x12g\xa4\xd5\xf2\v`(\b\xdaT\xac\x8fzI\u007f(\x13j\x12\xdbaZ\x92c^\x94w.\x99\x968\xaf\x9a\x14g-\xc9fV\x99\xc2\xf6\xac]hl\xf63.yO\x85u\x1c\xe22&\xf6\xda\xdb\xf7\xae\x17\xba\xa5\a8EAD|L|Z\xda3\xb8ɡ\xa5\xd0\xd12\xd6]\xe9ۑ\xf55\xecKqO\x9f[\xa1شܵ,\x04\x1fՄ>\xddk*\x99\xd5\xc4j\xd6\x1e\xb4\x0esέ\x9a\t*\x8d\xdeѮ|\\\xed\x0fA[\n\x96\xc7T\xef\xae\xcb\x05\v\x89\xa9O\xcc\xdd=5\xae@'\xfd\x0f\xe1\xd2\xd6z\x9c\xe6\x02=]Z(C\x1bG\x06E\xe9\xdff\x11M\xb6\xed8\x1d\x94G\xf2\x83W\x87\xc9P+q\xb1NE\x80\xb7m\xefF06\xe38Z:\x12b7\xb9\xcf\xc5-\x8c\x1aЬ\x9b\xb4%\u007f\xc6{\x98\xcdCh\xc7\x19\xb0\xa3\xef\x841\x9d\xf7^t\xc5\xdem,R\xac\xa9\x1f\\\x8f\x05\xe8H\xe2\fT\x01\xa3Z#x\xb0㮽\x89\x97\xc8`\xdf\xecY\xb3'\xee\xdc\xf7\xb2}?\xb1\xbc\xf6\x9e\x18}\x01\xa3\xeciou8\xccK\xd8\xd6P1\xeb\xc2\xf2\b\xbb\xbd㥙夆C\xb2\xb9\xca\x00\x9a\x1e\xbf\xed\xe2\xdb\xeb\xb0\xc0\x92Z\x91\"\xb38\x06\x8d\xb3\xac\xa7\xd6\xd8@x\xd7\n\x18µ-\xcf``\x9cPj\x8c}6Ll\x97R\x9a\xf9\xfb\xf4U\\\xee\xfb\x836[\f\xcd\xd1CZ\xad\xb7\x9f\xbb\x88N\xdc\"\x1d\xf4\xc8*Y\xb3\xd1\x1b\xf5=\x1e\x1d3C\x93Ⱦ3\xc5\xf3\xf4\x96ڣ\x81\xddx~\xaf\xd0\x12\xfb,c\x1fe\x1b\xbdG\a\x8a\x9e\x14\v;\x97,5\xceR>U\xb3w\xe6\x886Լ\xb4SAR7|\x04aq\xf7\xe9u\x04\x87\xf0^\xd8ځ\x18;V\xb2`ۼ<\xae\xaeV\x91\xf6\xbd\xf1\x92\xef\xa0V\xd3HɪE-\xea3\xe2\x12\x8ft9Ʌh\x96\x11\x86G\xeb;\xc6\xe1\x9a~\x19\x13V\xe8n:\x97\x98\x84\x99{\xd8~۔x\xfd\b9:\xd67N\xdb\xfb+m1\xdd\xde\xd6\xe3f75d\x19G\x17r\xcf\xc7z\xd9\xef\x97ZFݬ(:\xdb\xdf%\xb7P\x9f\x16\n9\xf2\xbb\xa8G\xac\x86a\x9cxLI\xe0\x03rl2}\x89\xd1\xda>\x16M\xacn?\xd6\xcfK\xfbwE/\xd9\x13\x92:T\xe3@\xd7\x02\xbfY_\xb7\xc7\xef\x93a\xee\xee\xef^O\x00\xfa\x05ME^3\xa0\t\xbf\x88\x8f\x98O\xa7\x90\\\x10\xb8\xd0\x03\x81\x93\x9c\xc0\x81\x97s\n\r_\xe6\f^\x9c\xc99\x1c$-\x17\xf0\x91\x91Q\xe6\x0e\xad\xc95\x8c\xa5y\xb9\x0e'\xad\xcbm\xb0\xd3\x0fs\a\x12\xe9\xc9\xe7\xbaс\r\x02\xc3c\xdcv\xe1V \xe5\x04I\xa8\x89\xb4\xc2߇\x9c!\x13?\xe6\x1cI\x84\xe6\x02\xd9$7\xcaܡ\xea\\ód[\xae#\x85\xae\xcbmH\xa0\xe1܁\xce\xf4\xb3\xff\xeeF\u007f\xc6\xde&8\x1c\xef$\x10*\xb5\xa4\xb0\x00\xf6p\xca\xd6w,意\xechi\x1bḩt\xdf-\x06,6\x11\xc3\x00i0\xb2\xaa\x0eI\x1c\xe9^\xaf,`\x8fŚ\x067\xa4\xf0{~\xaa5Q\xfcR\v]\x81\x88\xfa5\x9dj\x81\x9d^\x1dF\xe5i\xb5T\\?8E|ӕ\x89\xb1_\x93e\xd5\xefoH{U\x8e\x8fĠ\xfd\xa7T\xe9&L\xa1-3\xe0QW\xcdn\xbd\x92Ԥ\x02\x11u\x05\xfe\xeb\xb5\a\xab\x8eM\xb5\xfe\x91\x88\xbf\xfa*\nۥ\xb7\xa3\xaa\ue8b6\xabD\xdc\xd6+\x1e\x88%\xbbj;\xe8b\xe0ͮ\xa0\a' Y\x0f\xb2>\r(؟\x964\x8e\xee\v\x17w\xf3]|\x17\xf4\x1f\xf8\xac/\x99\xc2\xfb\x86JW#Ȥ\x00\xc6\xf5Zca7\xc4\xc1\x9e\x88\x92\xbbB'8:{\xde} \x91\xdaN\x1d\xec\xf5$\x828\xa5o\x92\x98\x13Q|W\x81\xbf\x9e\xfc \xd0mOnL\xb7\x14)\xf8\x8cQ^!\x1dW\x9c\xb0CM8\x87}:N\x8fhۑc&4ٝq\xb6o\x90\u007f\xbf\xa6_\xee\xd6@\x96xމ\x1b\x01\xfbɐ\xbb\xc55\x05\xb2\xef\x86\xc8\xda\rQ\xb7+\x14\x96\x06t\xef*\\\xda\x01\xc1]\x03\x8bw\t\xd4\xfaC!\xc3W\xd1\xc3^\"\x80yw\xc6\xden\xfe\xd0\xf2\a\x85\xc2\xe6\x97\x11e\xec\xab/\xeb\xf8\xdcR\xaa=\x98`\xbf\x1a\xb6\xee*\xa5\xe2\x865b\xfdJ\xae\x04\xd1\xf7\x92\x9dz\xaf\x03MwZ\xaa\x81\xf8\x1eN\th\xdd\xc1\f\xfeP\b\x0eQ\x997\u007f\u07b4\x14\xd6\xe1\x16\x84-\xbd␜\xa1E\xd2\xdfg\xf6\xbc\x8f\x9c\x01\xd9C\xa529\xca\xf2*X\xdf\xdd\u007f\x13\xc6\xf7\xeeYK\xb8Uk\xc4&\x05D\xf2\x0f\x03\x8d\xed\b\\\xcc4\x9e\xaa\x80\xfc]\xcd\aa\u007fw\x8f-\x9e\x0e5\xc4&_kD\xf0\x89@\xc6;\x83\x8d\xafI1f\xb5ͫ\xb3{\xceC\x8a[ŏY}E\xa9\x03\x81x\x15d\xf5S\xcf9ɇ\x88\xdc\xfd@\xf4~\xb2\xba$\x1e\xef\x97`\x83K\xfc\xdcP\xb8\xa3\xf3\x05\x98K}\xc8\xf4\x92\xca=\x05\x92wv\x17\x8d\xd5\xfeZR\n?P\xba\xa9h{\xa7%\xb0\x9e\xbfZdϙ\xe6'\x1f\xb6bi\xa6\x11\x01\x83ys-KhO\x94ü\x9e\xf6\x1a\x00\xd5\x17.\xfc\xe6 [\u007f4/%0y\x95]\xd4|\x14\xe1\x1f(\x85\x94珫D\x9d\xf7\xce\x05Bˀ(\x9eD\x8d\xd3\x15뺹\xf2\x1d\"\xf1c\xc5\xe5\x9ffw\x86\x888\xb1\xb5Ng\xa2P\x17\x94m\xfb\x91\x1c\x87zd\xeeo\f*Ģj6h\xe9\x89n\xcei[\x01\xd0}\xd7iY\nL\xe8ٱ\xf1E\x8ef\xfc9\xdd\xf3\x1aeF\xba8\xe3\xa3d\xfdǣ\x93\xd7O\xf3\xa2k\x9f@\x97p\x94\xc2\xed\xeb#\x1dB\\\xfb\x19\xa9\xe4'\x02Mo\x06\xfe=\xb4)\x05\xa7\ruĐE\xbdB>:\xea\xcd\xfa6Qlo\x85\x9b\xaa\xcc\x15\x946\xb5\xf4\x90]\xa7Z* \xa4)\xe4\r˸k\xc9\x01\x11ֿ\xf8\x90\xf5\xc1\xe9 \xdd/\x86d?6\x1c\xee\rQ\xd7\x1b\xd2\xc87D\x90\u007f\xd6x\xaa\xf6\xe3\xe3'ey:\xf3\xca\x1fK\xfd\xef\xba\x15CaM\x81۽\xd5T&\xfduf\xcf\xebT\x81\xc9\x04\xe3\xccx_\x81\xaf\xbe\x9a\x17\xbaW\xfeD\xc5){5\xeaPJ7\x9bA\xb6\x05\x86\xcc\xef\xb7\f\x832\x83\xbawW\x96q\x94\xfbo-C\xb4\xefg*\x93\xd2\xdf\xd4\xd9t\x1be\xff\r\xf4\x97\x8a\xd7\xc4j\v^\"\xdd~\xef4\xe8{;fo-\xbdW\xf2?\x83\x93*w\xa3W\xe41\xb8{\xc5\xcb\x12\xf3|\xd5\xc4\xf6\xf1k\xa9.Q\xb8Z\"\xc6\xfc\nX\xf2-\xf4\x92\xe8J\xc8\xfe\x92/~\xad\x19\xe6\xcf\f\x98\xfd\x0f\xdc۵\x8fdp\xb8\x91;\xf1\x00\xe2}\x9e\x03\x00\x00\x00\vWAD\xf4|Qķ\x8d\xe6~\xda\xfa\x8a\x94\x86\x0e\x85\x8aX\x9b\xb1\xbbC\x02\xde\xed\xfc\xf8\xde\xce\xef}\xc2\xf3\xdf\x14\xa0\xa2\xe5\x03\xd7\xef\xcf\x19\x856c\x0e\x00T\xc4\xda\xcc;k\xad\xdd\xf6\x9b#7.{7c\xae\xea\xa28\x9c\x04T\xc4\xda\xfc\xdd\xd3\xfd_\x9b\xe3\xfc\xcc4\x80\x8aX\x9b\xb1;B\x02*bm\xc6\xee\x8c\xe9#\"\"\"*\xa5\x94RJ)EDDDD\xc4\xcc\xcc\xcc̛?9\xaa\xa77\xcd\xd6\xc7t3Zk\xadg\x81\x13\x11\x11\x11\x11\x11с\xa8hz\xf6\x8a\xbf\x1c\xfd\xb7\x8c\xfet&ޯw.\x8e\xc3\xe1Y\x9bN\xf9ˋվ\x19\xbbgH@E\xac\xcd\xd8\x1d!\x01\x15\xb16\x8f\x8d\xf6\xf5\x16~\x19b\x95rݴ\xc1\x9f\xc8z\xdb\xc9]DDDDDDDfffffffVUUUUUUU\xb3i\xba\xba{z\xfb\xa6\x93\x9c\u007f\x846\xbdNd\xad\x01\x00"), } fileb := &embedded.EmbeddedFile{ Filename: "assets/.gitignore", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string(""), } filec := &embedded.EmbeddedFile{ Filename: "assets/.npmignore", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string(""), } filed := &embedded.EmbeddedFile{ Filename: "b06871f281fee6b241d60582ae9369b9.ttf", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("\x00\x01\x00\x00\x00\r\x00\x80\x00\x03\x00PFFTMk\xbeG\xb9\x00\x02\x86\x90\x00\x00\x00\x1cGDEF\x02\xf0\x00\x04\x00\x02\x86p\x00\x00\x00 OS/2\x882z@\x00\x00\x01X\x00\x00\x00`cmap\n\xbf:\u007f\x00\x00\f\xa8\x00\x00\x02\xf2gasp\xff\xff\x00\x03\x00\x02\x86h\x00\x00\x00\bglyf\x8f\xf7\xaeM\x00\x00\x1a\xac\x00\x02L\xbchead\x10\x89\xe5-\x00\x00\x00\xdc\x00\x00\x006hhea\x0f\x03\n\xb5\x00\x00\x01\x14\x00\x00\x00$hmtxEy\x18\x85\x00\x00\x01\xb8\x00\x00\n\xf0loca\x02\xf5\xa2\\\x00\x00\x0f\x9c\x00\x00\v\x10maxp\x03,\x02\x1c\x00\x00\x018\x00\x00\x00 name㗋\xac\x00\x02gh\x00\x00\x04\x86post\xaf\x8f\x9b\xa1\x00\x02k\xf0\x00\x00\x1au\x00\x01\x00\x00\x00\x04\x01ː\xcfxY_\x0f<\xf5\x00\v\a\x00\x00\x00\x00\x00\xd43\xcd2\x00\x00\x00\x00\xd43\xcd2\xff\xff\xff\x00\t\x01\x06\x00\x00\x00\x00\b\x00\x02\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x06\x00\xff\x00\x00\x00\t\x00\xff\xff\xff\xff\t\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xb5\x00\x01\x00\x00\x02\xc3\x02\x19\x00'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x01\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x03\x06i\x01\x90\x00\x05\x00\x00\x04\x8c\x043\x00\x00\x00\x86\x04\x8c\x043\x00\x00\x02s\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00pyrs\x00@\x00 \xf5\x00\x06\x00\xff\x00\x00\x00\x06\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x01\x03\x80\x00p\x00\x00\x00\x00\x02U\x00\x00\x01\xc0\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00]\x06\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\x05\x00\x00\x00\a\x80\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00y\x05\x80\x00n\x06\x80\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x06\x80\x00\x1a\x06\x00\x00\x00\x06\x00\x00\x00\a\x80\x002\x06\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x04\x80\x00\x00\a\x00\x00@\x06\x80\x00\x00\x03\x00\x00\x00\x04\x80\x00\x00\x06\x80\x00\x00\x05\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\x06\x80\x00\n\x05\x00\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\x06\x80\x00\x00\x05\x80\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x80\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x80\x00z\x05\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\x06\x02\x00\x01\x05\x00\x00\x9a\x05\x00\x00Z\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00@\x06\x00\x00\x00\x06\x80\x005\x06\x80\x005\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\r\x05\x80\x00\x00\x05\x80\x00\x00\x06\x80\x00z\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x05\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x10\x05\x80\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00Z\a\x00\x00Z\a\x80\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\x03\x00\x00@\a\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x03\x80\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x04\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x05\x80\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00,\x04\x00\x00_\x06\x00\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\x05\x80\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00@\x06\x00\x00\x02\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x15\a\x00\x00\x00\x05\x80\x00\x05\a\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\x06\x80\x00\x10\a\x80\x00\x00\x06\x80\x00s\a\x00\x00\x01\a\x00\x00\x00\x05\x80\x00\x04\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x0f\a\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x1b\a\x00\x00@\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\t\x00\x00\x00\a\x80\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x02\x80\x00@\x02\x80\x00\x00\x06\x80\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00(\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x03\x80\x00\x01\a\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00\x00\a\x80\x00\x00\a\x80\x00\x00\x05\x80\x00\x00\x05\x80\x00\x00\a\x00\x00\x00\a\x00\x00@\a\x80\x00\x00\x05\x80\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x05\x80\x00\x00\a\x80\x00@\a\x00\x00\x00\a\x80\x00\x00\x06\x80\x00@\x06\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00-\x04\x00\x00\r\x04\x80\x00M\x04\x80\x00M\x02\x80\x00-\x02\x80\x00\r\x04\x80\x00M\x04\x80\x00M\a\x80\x00\x00\a\x80\x00\x00\x04\x80\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x00\x00@\x06\x00\x00\x00\a\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\a\x80\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\a\x80\x00\x00\a\x00\x00@\a\x00\x00@\x06\x80\x00\r\a\x80\x00-\a\x00\x00\x00\x06\x80\x00\x02\x05\x80\x00\x02\x06\x80\x00\x00\x04\x00\x00\x00\x06\x80\x00\x00\x04\x00\x00`\x02\x80\x00\x00\x02\x80\x00b\x06\x00\x00\x05\x06\x00\x00\x05\a\x80\x00\x01\x06\x80\x00\x00\x04\x80\x00\x00\x05\x80\x00\r\x05\x00\x00\x00\x06\x80\x00\x00\x05\x80\x00\x03\x06\x80\x00$\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\a\x00\x00\f\a\x00\x00\x00\x04\x80\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x01\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x006\x06\x00\x00\x00\x05\x80\x00\x00\x04\x00\x00\x03\x04\x00\x00\x03\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x004\x03\x82\x00\x00\x04\x03\x00\x04\x05\x00\x00\x00\a\x00\x00\x00\x05\x00\x008\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\"\x06\x80\x00\"\a\x00\x00\"\a\x00\x00\"\x06\x00\x00\"\x06\x00\x00\"\x06\x80\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x1b\x05\x80\x00\x05\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00@\x06\x00\x00\v\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x06\x00\x00\x00\x04\x00\x00D\x06\x00\x00\x00\x03\x00\x00\x03\x03\x00\x00\x03\a\x00\x00@\a\x00\x00\x00\x05\x80\x00\x00\x06\x80\x00\x00\x05\x80\x00\x00\x06\x00\x00\v\x06\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00,\x06\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\a\x00\x00,\x06\x00\x00\x00\a\x00\x00@\x06\x80\x00 \a\x80\xff\xff\a\x00\x00\x00\x06\x00\x00\x00\x05\x80\x00\x00\x05\x00\x00\x15\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\x04\x80\x00\x00\x05\x80\x00\x00\b\x80\x00\x00\x06\x80\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\t\x00\x00\x00\x06\x00\x00m\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x80\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\a\xf6\x00)\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00@\x06\x80\x00\x00\x03\x00\x00@\a\x00\x00\x00\t\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x10\b\x00\x00\x00\b\x00\x00\x00\x06\x00\x00 \x06\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00'\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00 \a\x00\x00\x13\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00D\x06\x00\x00\x00\x05\x00\x009\a\x00\x00\x12\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00>\x05\x00\x00\x18\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x19\a\x00\x00d\x06\x00\x00Y\b\x00\x00\x00\b\x00\x00*\a\x00\x00\x00\x06\x00\x00\t\a\x00\x00'\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\b\x00\x00\x0e\b\x00\x00\x0e\x05\x80\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\x05\x00\x00\v\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\x06\x80\x00\x00\x06\x80\x00\x00\b\x00\x00\x00\b\x00\x00\x13\x06\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x05\x00\x00\x02\x06\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x02\a\x00\x00\x00\a\x00\x00\x02\a\x80\x00\x01\b\x00\x00\x06\x06\x00\x00\x00\x05\x00\x00\x02\b\x00\x00\x04\x05\x00\x00\x00\x05\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\b\xf8\x00T\t\x00\x00\x00\a\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\b\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\t\x00\x00\x00\t\x00\x00\x00\a\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\a\xb5\x00\x00\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00@\a\x00\x00\x00\t\x00\x00\x00\x05\x00\x00f\x06\x00\x00\x00\x06\xb8\x00\x00\t\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x02\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x16\x06\x00\x00\x0e\a\x00\x00\x1d\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00%\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\a\x00\x00R\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00E\t\x00\x00\x00\a\x00\x00\x00\a\x00\x00 \a\x00\x00\x00\t\x00\x00\x00\a\x00\x00\x00\t\x00\x00\x00\x06\x00\x00$\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00!\x06\x00\x00k\x04\x00\x00(\x06\x00\x00\x00\a\x00\x00\x03\a\x00\x00\x00\x06\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00D\x06\x00\x00\x00\x05\x80\x00'\t\x00\x00\x03\x05\x80\x00\x00\b\x80\x00\x00\a\x00\x00\x00\t\x00\x00\x03\a\x00\x00\x00\x06\x00\x00\x00\x05\xff\x00%\x06\x80\x00\x01\a\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x0f\x06\x00\x00\x00\t\x00\x00\x00\x06\x00\x00\x00\x06\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x06\x00\x00%\t\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x15\x06\x80\x00\x00\x06\x80\x00\x00\b\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\x06\x00\x00\x00\x05\x00\x00\x00\b\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x1d\t\x00\x00\x00\a\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\a\x80\x00\x00\a\x00\x00\x00\x06\x00\x00\x01\a\x00\x00\x00\a\x00\x00\x00\b\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x00\x00\x00\a\x02\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\b\x80\x000\a\x00\x00%\x06\x00\x00\x00\x06\x80\x00/\a\x00\x00\x00\a\x00\x00\x00\a\x80\x00&\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\x1c\x00\x01\x00\x00\x00\x00\x01\xec\x00\x03\x00\x01\x00\x00\x00\x1c\x00\x04\x01\xd0\x00\x00\x00p\x00@\x00\x05\x000\x00 \x00\xa9\x00\xae\x00\xb4\x00\xc6\x00\xd8!\"\"\x1e\"`\xf0\x0e\xf0\x1e\xf0>\xf0N\xf0^\xf0n\xf0~\xf0\x8e\xf0\x9e\xf0\xae\xf0\xb2\xf0\xce\xf0\xde\xf0\xee\xf0\xfe\xf1\x0e\xf1\x1e\xf1.\xf1>\xf1N\xf1^\xf1n\xf1~\xf1\x8e\xf1\x9e\xf1\xae\xf1\xbe\xf1\xce\xf1\xde\xf1\xee\xf1\xfe\xf2\x0e\xf2\x1e\xf2>\xf2N\xf2^\xf2n\xf2~\xf2\x8e\xf2\x9e\xf2\xae\xf2\xbe\xf2\xce\xf2\xde\xf2\xee\xf5\x00\xff\xff\x00\x00\x00 \x00\xa8\x00\xae\x00\xb4\x00\xc6\x00\xd8!\"\"\x1e\"`\xf0\x00\xf0\x10\xf0!\xf0@\xf0P\xf0`\xf0p\xf0\x80\xf0\x90\xf0\xa0\xf0\xb0\xf0\xc0\xf0\xd0\xf0\xe0\xf0\xf0\xf1\x00\xf1\x10\xf1 \xf10\xf1@\xf1P\xf1`\xf1p\xf1\x80\xf1\x90\xf1\xa0\xf1\xb0\xf1\xc0\xf1\xd0\xf1\xe0\xf1\xf0\xf2\x00\xf2\x10\xf2!\xf2@\xf2P\xf2`\xf2p\xf2\x80\xf2\x90\xf2\xa0\xf2\xb0\xf2\xc0\xf2\xd0\xf2\xe0\xf5\x00\xff\xff\xff\xe3\xff\\\xffX\xffS\xffB\xff1\xde\xe8\xdd\xedݬ\x10\r\x10\f\x10\n\x10\t\x10\b\x10\a\x10\x06\x10\x05\x10\x04\x10\x03\x10\x02\x0f\xf5\x0f\xf4\x0f\xf3\x0f\xf2\x0f\xf1\x0f\xf0\x0f\xef\x0f\xee\x0f\xed\x0f\xec\x0f\xeb\x0f\xea\x0f\xe9\x0f\xe8\x0f\xe7\x0f\xe6\x0f\xe5\x0f\xe4\x0f\xe3\x0f\xe2\x0f\xe1\x0f\xe0\x0f\xde\x0f\xdd\x0f\xdc\x0f\xdb\x0f\xda\x0f\xd9\x0f\xd8\x0f\xd7\x0f\xd6\x0f\xd5\x0f\xd4\x0f\xd3\r\xc2\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x05\n\a\x04\f\b\t\v\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00,\x00\x00\x00\x90\x00\x00\x01\x14\x00\x00\x01\x98\x00\x00\x02t\x00\x00\x02\xd0\x00\x00\x03L\x00\x00\x03\xf0\x00\x00\x04T\x00\x00\x06$\x00\x00\x06\xe0\x00\x00\bl\x00\x00\tx\x00\x00\t\xd0\x00\x00\nT\x00\x00\v(\x00\x00\v\xd4\x00\x00\f\x84\x00\x00\rd\x00\x00\x0e\xa8\x00\x00\x0f\xd4\x00\x00\x10\x84\x00\x00\x11\x00\x00\x00\x11\x9c\x00\x00\x12l\x00\x00\x13,\x00\x00\x13\xd8\x00\x00\x14\x80\x00\x00\x14\xfc\x00\x00\x15\x90\x00\x00\x164\x00\x00\x17\x10\x00\x00\x18d\x00\x00\x18\xcc\x00\x00\x19p\x00\x00\x1aH\x00\x00\x1a\x94\x00\x00\x1b$\x00\x00\x1cd\x00\x00\x1d,\x00\x00\x1e\b\x00\x00\x1et\x00\x00\x1f(\x00\x00 \x8c\x00\x00 \xf0\x00\x00!\xa0\x00\x00\"0\x00\x00# \x00\x00$,\x00\x00$\xe0\x00\x00&D\x00\x00'\xe4\x00\x00(\x9c\x00\x00)T\x00\x00*\b\x00\x00*\xbc\x00\x00,\x10\x00\x00,\xf4\x00\x00-\xd8\x00\x00.@\x00\x00.\xd8\x00\x00/`\x00\x00/\xbc\x00\x000\x14\x00\x000\xa4\x00\x001\x94\x00\x002\x90\x00\x003d\x00\x0044\x00\x004\x94\x00\x005 \x00\x005\x80\x00\x005\xb8\x00\x006 \x00\x006\\\x00\x006\xbc\x00\x007H\x00\x007\xa8\x00\x008\f\x00\x008`\x00\x008\xb4\x00\x009L\x00\x009\xb4\x00\x00:h\x00\x00:\xec\x00\x00;\xc0\x00\x00<\x00\x00>\xe4\x00\x00?h\x00\x00?\xd8\x00\x00@H\x00\x00@\xbc\x00\x00A0\x00\x00A\xb8\x00\x00BX\x00\x00B\xf8\x00\x00Cd\x00\x00C\x9c\x00\x00DL\x00\x00D\xe4\x00\x00E\xb8\x00\x00F\x9c\x00\x00G0\x00\x00G\xdc\x00\x00H\xec\x00\x00I\x8c\x00\x00J8\x00\x00K\xac\x00\x00L\xe4\x00\x00Md\x00\x00N,\x00\x00N\x80\x00\x00N\xd4\x00\x00O\xb0\x00\x00P`\x00\x00P\xa8\x00\x00Q4\x00\x00Q\xa0\x00\x00R\f\x00\x00Rl\x00\x00S,\x00\x00S\x98\x00\x00T`\x00\x00U0\x00\x00W\xf0\x00\x00X\xdc\x00\x00Z\b\x00\x00[@\x00\x00[\x8c\x00\x00\\<\x00\x00\\\xf8\x00\x00]\x98\x00\x00^(\x00\x00^\xe4\x00\x00_\xa0\x00\x00`p\x00\x00b,\x00\x00b\xf4\x00\x00d\x04\x00\x00d\xec\x00\x00eP\x00\x00e\xd0\x00\x00f\xc4\x00\x00g`\x00\x00g\xa8\x00\x00iL\x00\x00i\xc0\x00\x00jD\x00\x00k\f\x00\x00k\xd4\x00\x00l\x80\x00\x00m@\x00\x00n,\x00\x00oL\x00\x00p\x84\x00\x00q\xa4\x00\x00r\xdc\x00\x00sx\x00\x00t\x10\x00\x00t\xa8\x00\x00uD\x00\x00{`\x00\x00|\x00\x00\x00|\xbc\x00\x00}\x10\x00\x00}\xa4\x00\x00~\x88\x00\x00\u007f\x94\x00\x00\x80\xbc\x00\x00\x81\x18\x00\x00\x81\x8c\x00\x00\x83H\x00\x00\x84\x14\x00\x00\x84\xd4\x00\x00\x85\xa8\x00\x00\x85\xe4\x00\x00\x86l\x00\x00\x87@\x00\x00\x88\x98\x00\x00\x89\xc0\x00\x00\x8b\x10\x00\x00\x8c\xc8\x00\x00\x8d\x8c\x00\x00\x8el\x00\x00\x8fH\x00\x00\x90 \x00\x00\x90\xc0\x00\x00\x91T\x00\x00\x92\f\x00\x00\x92H\x00\x00\x92\x84\x00\x00\x92\xc0\x00\x00\x92\xfc\x00\x00\x93`\x00\x00\x93\xc8\x00\x00\x94\x04\x00\x00\x94@\x00\x00\x94\xf0\x00\x00\x95\x80\x00\x00\x96$\x00\x00\x97\\\x00\x00\x98X\x00\x00\x99\x1c\x00\x00\x9aD\x00\x00\x9a\xb8\x00\x00\x9b\x98\x00\x00\x9c\xa0\x00\x00\x9dT\x00\x00\x9eX\x00\x00\x9e\xf8\x00\x00\x9f\x9c\x00\x00\xa0D\x00\x00\xa1P\x00\x00\xa2,\x00\x00\xa2\xa4\x00\x00\xa38\x00\x00\xa3\xa8\x00\x00\xa4d\x00\x00\xa5\\\x00\x00\xa8\x90\x00\x00\xab\b\x00\x00\xac\x1c\x00\x00\xac\xec\x00\x00\xad\x90\x00\x00\xad\xe8\x00\x00\xae\x80\x00\x00\xaf\x18\x00\x00\xaf\xb0\x00\x00\xb0H\x00\x00\xb0\xe0\x00\x00\xb1x\x00\x00\xb1\xcc\x00\x00\xb2 \x00\x00\xb2t\x00\x00\xb2\xc8\x00\x00\xb3X\x00\x00\xb3\xf4\x00\x00\xb4p\x00\x00\xb5\x00\x00\x00\xb5d\x00\x00\xb6\x1c\x00\x00\xb6\xd4\x00\x00\xb7\xb4\x00\x00\xb7\xf0\x00\x00\xb8x\x00\x00\xb9t\x00\x00\xb9\xf8\x00\x00\xba\xcc\x00\x00\xba\xcc\x00\x00\xba\xcc\x00\x00\xbb\xa8\x00\x00\xbc\x84\x00\x00\xbd@\x00\x00\xbe\x04\x00\x00\xbf\xc8\x00\x00\xc0\xc4\x00\x00\xc2\f\x00\x00\u008c\x00\x00\xc3\\\x00\x00\xc4 \x00\x00ļ\x00\x00\xc5\x10\x00\x00Ÿ\x00\x00Ɣ\x00\x00\xc80\x00\x00\xc8\xe0\x00\x00\xc9d\x00\x00\xc9\xcc\x00\x00ʨ\x00\x00ˀ\x00\x00\xcb\xe0\x00\x00\xcc\xf4\x00\x00͔\x00\x00\xcex\x00\x00\xce\xe8\x00\x00ϰ\x00\x00Ќ\x00\x00\xd1,\x00\x00ш\x00\x00\xd2\b\x00\x00҈\x00\x00\xd3\f\x00\x00ӌ\x00\x00\xd3\xec\x00\x00\xd48\x00\x00\xd5,\x00\x00՜\x00\x00\xd6`\x00\x00\xd6\xe8\x00\x00\xd7l\x00\x00\xd8H\x00\x00ش\x00\x00\xd9`\x00\x00\xd9\xc4\x00\x00\xdaT\x00\x00ڸ\x00\x00\xdb\x18\x00\x00۔\x00\x00\xdc@\x00\x00\xdc\xc8\x00\x00\xddl\x00\x00\xdd\xf0\x00\x00ބ\x00\x00\xdf\x18\x00\x00߬\x00\x00\xe0\xbc\x00\x00\xe1l\x00\x00\xe2p\x00\x00\xe3 \x00\x00\xe3\xe4\x00\x00\xe4\x80\x00\x00\xe5\xc8\x00\x00\xe6\xc0\x00\x00\xe7\x18\x00\x00\xe7\xec\x00\x00\xe8\xe4\x00\x00\xe9\xd8\x00\x00\xea\xd8\x00\x00\xeb\xd8\x00\x00\xec\xd4\x00\x00\xed\xd0\x00\x00\xee\xdc\x00\x00\xef\xe4\x00\x00\xf2\x04\x00\x00\xf3\xf4\x00\x00\xf4\x80\x00\x00\xf54\x00\x00\xf6\x10\x00\x00\xf6\x9c\x00\x00\xf7\x18\x00\x00\xf8X\x00\x00\xf8\xc0\x00\x00\xf9$\x00\x00\xfal\x00\x00\xfb\xbc\x00\x00\xfc(\x00\x00\xfc\xb8\x00\x00\xfd\f\x00\x00\xfd`\x00\x00\xfd\xb4\x00\x00\xfe\b\x00\x00\xfe\xb8\x00\x00\xff\b\x00\x01\x00\x14\x00\x01\x05\xb4\x00\x01\x06\xf4\x00\x01\a\xf8\x00\x01\b\xd0\x00\x01\td\x00\x01\n\x10\x00\x01\n\x98\x00\x01\v\x18\x00\x01\f\x04\x00\x01\f\xa4\x00\x01\r,\x00\x01\x0e\x00\x00\x01\x0f\x88\x00\x01\x11,\x00\x01\x11\xa0\x00\x01\x12\xcc\x00\x01\x138\x00\x01\x13\xe4\x00\x01\x14\x90\x00\x01\x15(\x00\x01\x15\xa4\x00\x01\x16X\x00\x01\x16\xfc\x00\x01\x17\xc0\x00\x01\x18\x84\x00\x01\x19x\x00\x01\x1a|\x00\x01\x1bT\x00\x01\x1c\xd4\x00\x01\x1d@\x00\x01\x1d\xd4\x00\x01\x1e\x90\x00\x01\x1f\x04\x00\x01\x1f|\x00\x01 \xa4\x00\x01!\xc0\x00\x01\"x\x00\x01#\b\x00\x01#l\x00\x01$\x04\x00\x01$\xcc\x00\x01'h\x00\x01(\xe8\x00\x01*L\x00\x01,T\x00\x01.L\x00\x011t\x00\x011\xf4\x00\x012\xe0\x00\x0130\x00\x013\xb0\x00\x014\xa8\x00\x015t\x00\x016T\x00\x017$\x00\x018\f\x00\x019H\x00\x01:\x10\x00\x01:\xf0\x00\x01;\x90\x00\x01<\x84\x00\x01<\xd8\x00\x01?X\x00\x01@\x1c\x00\x01A\xc0\x00\x01B\xc8\x00\x01C\xc8\x00\x01D\x9c\x00\x01EH\x00\x01FH\x00\x01Gp\x00\x01HH\x00\x01Ix\x00\x01J \x00\x01J\xe4\x00\x01K\xd4\x00\x01L\xa0\x00\x01M\x18\x00\x01N@\x00\x01P@\x00\x01Q\xa0\x00\x01R\xe0\x00\x01SD\x00\x01T \x00\x01UL\x00\x01V`\x00\x01V\xd4\x00\x01WX\x00\x01X4\x00\x01X\xa0\x00\x01Z\x04\x00\x01Z\x88\x00\x01[d\x00\x01[\xe0\x00\x01\\|\x00\x01]\xd8\x00\x01^\xa0\x00\x01`\x94\x00\x01aH\x00\x01a\xbc\x00\x01b\xf0\x00\x01cX\x00\x01d\xac\x00\x01et\x00\x01fh\x00\x01g\xdc\x00\x01h\xb4\x00\x01i\\\x00\x01jx\x00\x01n\x84\x00\x01p@\x00\x01s\xe0\x00\x01v\x10\x00\x01w\xc8\x00\x01x\x90\x00\x01y\x88\x00\x01z\x8c\x00\x01{h\x00\x01|\x8c\x00\x01}\x1c\x00\x01}\xa4\x00\x01\u007f\\\x00\x01\u007f\x98\x00\x01\u007f\xf8\x00\x01\x80l\x00\x01\x81t\x00\x01\x82\x90\x00\x01\x834\x00\x01\x83\xa4\x00\x01\x84\xc8\x00\x01\x85\xb0\x00\x01\x86\xa4\x00\x01\x88t\x00\x01\x89\x8c\x00\x01\x8a8\x00\x01\x8b8\x00\x01\x8b\xa0\x00\x01\x8eL\x00\x01\x8e\xa8\x00\x01\x8fT\x00\x01\x90\x10\x00\x01\x91\x14\x00\x01\x93\x90\x00\x01\x94\x14\x00\x01\x95\x04\x00\x01\x95\xfc\x00\x01\x96\xf8\x00\x01\x97\xa0\x00\x01\x99|\x00\x01\x9a\xc8\x00\x01\x9c\x10\x00\x01\x9d\b\x00\x01\x9d\xd8\x00\x01\x9e|\x00\x01\x9f\x18\x00\x01\x9f\xe8\x00\x01\xa0\xc4\x00\x01\xa2\f\x00\x01\xa34\x00\x01\xa4x\x00\x01\xa5\xb0\x00\x01\xa6\x80\x00\x01\xa7L\x00\x01\xa8\x1c\x00\x01\xa8\x90\x00\x01\xa8\xec\x00\x01\xa8\xec\x00\x01\xa8\xec\x00\x01\xa9X\x00\x01\xaa(\x00\x01\xab \x00\x01\xab\xcc\x00\x01\xac\xac\x00\x01\xad\xa8\x00\x01\xae \x00\x01\xae\x88\x00\x01\xaf\x04\x00\x01\xaf\xa8\x00\x01\xb0@\x00\x01\xb0\x88\x00\x01\xb6\xbc\x00\x01\xb7l\x00\x01\xb8\xe0\x00\x01\xb9t\x00\x01\xba\x04\x00\x01\xba\x94\x00\x01\xbb$\x00\x01\xbb\xa4\x00\x01\xbc\b\x00\x01\xbcx\x00\x01\xbdL\x00\x01\xbeL\x00\x01\xbe\xa4\x00\x01\xbf \x00\x01\xc0H\x00\x01\xc1\x18\x00\x01\xc1\xc4\x00\x01\xc3\x04\x00\x01\xc3\xe4\x00\x01Ġ\x00\x01\xc5T\x00\x01\xc6(\x00\x01\xc6\xec\x00\x01\xc8\f\x00\x01\xc9\f\x00\x01ʈ\x00\x01ˠ\x00\x01\xcc\xf8\x00\x01\xce\x1c\x00\x01ϔ\x00\x01\xd0l\x00\x01\xd1d\x00\x01\xd2\xdc\x00\x01\xd3P\x00\x01\xd3\xf8\x00\x01Մ\x00\x01\xd6x\x00\x01\xd7p\x00\x01\xd7\xfc\x00\x01\xd8\xf4\x00\x01ڬ\x00\x01\xdbT\x00\x01\xdcT\x00\x01\xdd\f\x00\x01\xdd\xf0\x00\x01ވ\x00\x01\xdfL\x00\x01\xe1\x80\x00\x01\xe2\xf8\x00\x01\xe4\x18\x00\x01\xe5\f\x00\x01\xe6<\x00\x01\xe7H\x00\x01\xe7\xa8\x00\x01\xe8$\x00\x01\xe8\xd4\x00\x01\xe9l\x00\x01\xea\x1c\x00\x01\xea\xd4\x00\x01\xeb\xe4\x00\x01\xec4\x00\x01\xec\xb8\x00\x01\xec\xf4\x00\x01\xed\xf0\x00\x01\xef\b\x00\x01\xef\xa4\x00\x01\xf0\x04\x00\x01\xf0\xcc\x00\x01\xf1 \x00\x01\xf2P\x00\x01\xf3l\x00\x01\xf3\xe8\x00\x01\xf5\f\x00\x01\xf6,\x00\x01\xf6\xc0\x00\x01\xf7x\x00\x01\xf7\xe0\x00\x01\xf8p\x00\x01\xf9,\x00\x01\xfax\x00\x01\xfbt\x00\x01\xfc\f\x00\x01\xfcd\x00\x01\xfd\f\x00\x01\xfd\x8c\x00\x01\xfe4\x00\x01\xff\b\x00\x01\xff\xd0\x00\x02\x014\x00\x02\x02\x1c\x00\x02\x03,\x00\x02\x04h\x00\x02\x05\xd4\x00\x02\aP\x00\x02\t4\x00\x02\n\xd4\x00\x02\f\xe0\x00\x02\r\xf0\x00\x02\x0f\x18\x00\x02\x104\x00\x02\x11\xe4\x00\x02\x13<\x00\x02\x14,\x00\x02\x15,\x00\x02\x164\x00\x02\x170\x00\x02\x188\x00\x02\x19$\x00\x02\x1a\x88\x00\x02\x1b8\x00\x02\x1d\xb4\x00\x02\x1eT\x00\x02\x1e\xcc\x00\x02 |\x00\x02!h\x00\x02\"\xac\x00\x02$L\x00\x02%0\x00\x02&H\x00\x02'\x88\x00\x02(\xf4\x00\x02)\x8c\x00\x02*0\x00\x02*\xdc\x00\x02+\x94\x00\x02,\xdc\x00\x02.$\x00\x02.\xec\x00\x020\xec\x00\x021\x84\x00\x022@\x00\x022\xfc\x00\x023\xb8\x00\x024t\x00\x025$\x00\x026\xf4\x00\x029 \x00\x02:\x8c\x00\x02:\xd4\x00\x02;\f\x00\x02;\x88\x00\x02<(\x00\x02<\xd8\x00\x02=4\x00\x02?\xb8\x00\x02@\x98\x00\x02A\xe0\x00\x02C\xa0\x00\x02D\xfc\x00\x02F\x98\x00\x02H`\x00\x02H\xf4\x00\x02I\xcc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02L\xbc\x00\x02\x00p\x00\x00\x03\x10\x06\x00\x00\x03\x00\a\x00\x007!\x11!\x03\x11!\x11\xe0\x01\xc0\xfe@p\x02\xa0p\x05 \xfap\x06\x00\xfa\x00\x00\x00\x00\x00\x01\x00]\xff\x00\x06\xa3\x05\x80\x00\x1d\x00\x00\x01\x14\a\x01\x11!2\x16\x14\x06#!\"&463!\x11\x01&54>\x013!2\x1e\x01\x06\xa3+\xfd\x88\x01@\x1a&&\x1a\xfc\x80\x1a&&\x1a\x01@\xfd\x88+$(\x17\x05\x80\x17($\x05F#+\xfd\x88\xfd\x00&4&&4&\x03\x00\x02x+#\x17\x1b\b\b\x1b\x00\x00\x01\x00\x00\xff\x00\x06\x00\x05\x80\x00+\x00\x00\x01\x11\x14\x0e\x02\".\x024>\x0232\x17\x11\x05\x11\x14\x0e\x02\".\x024>\x0232\x17\x11467\x01632\x16\x06\x00DhgZghDDhg-iW\xfd\x00DhgZghDDhg-iW&\x1e\x03@\f\x10(8\x05 \xfb\xa02N+\x15\x15+NdN+\x15'\x02\x19\xed\xfd;2N+\x15\x15+NdN+\x15'\x03\xc7\x1f3\n\x01\x00\x048\x00\x02\x00\x00\xff\x00\x06\x80\x05\x80\x00\a\x00!\x00\x00\x00\x10\x00 \x00\x10\x00 \x01\x14\x06#\"'\x01\x06#\"$&\x02\x10\x126$ \x04\x16\x12\x15\x14\a\x01\x16\x04\x80\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x03\aL46$\xfe\xa9\xb3\u070f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdo|\x01W%\x02\a\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\xfe\x804L&\x01V|o\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\xfe\xfb\x8fܳ\xfe\xa9%\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x00\x00\x1a\x00=\x00M\x00\x00%\x11\x06\a\x04\a\x0e\x02+\x02\".\x01'&%&'\x11\x14\x163!26\x11<\x02.\x03#!\"\x06\x15\x14\x17\x16\x17\x1e\x04;\x022>\x03767>\x017\x11\x14\x06#!\"&5\x11463!2\x16\x06\x80 %\xfe\xf4\x9e3@m0\x01\x010m@3\x9e\xfe\xf4% \x13\r\x05\xc0\r\x13\x01\x05\x06\f\b\xfa@\r\x13\x93\xc1\xd0\x06:\"7.\x14\x01\x01\x14.7\":\x06\xd0\xc16]\x80^B\xfa@B^^B\x05\xc0B^ \x03\x00$\x1e΄+0110+\x84\xce\x1e$\xfd\x00\r\x13\x13\x04(\x02\x12\t\x11\b\n\x05\x13\r\xa8t\x98\xa5\x051\x1a%\x12\x12%\x1a1\x05\xa5\x98+\x91`\xfb\xc0B^^B\x04@B^^\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00\x1c\x00\x00\x04\"'\x01.\x0454632\x1e\x02\x17>\x0332\x16\x15\x14\a\x01\x03\x9a4\x12\xfd\x90\n#L\x81oP$$Po\x81>\xe0\xfe\xe5\xfd\x91\x80\x12\x02Z\b$_d\x8eC\xdc\xf8+I@$$@I+\xf8\xdc\xdd\xe5\xfd\xa8\x00\x00\x01\x00\x00\xff\xad\x06\x80\x05\xe0\x00\"\x00\x00\x01\x14\a\x01\x13\x16\x15\x14\x06#\"'%\x05\x06#\"&547\x13\x01&547%\x1362\x17\x13\x05\x16\x06\x80\x1a\xfe\x95V\x01\x15\x14\x13\x15\xfe?\xfe?\x16\x12\x15\x15\x02V\xfe\x94\x198\x01\xf6\xe1\x13<\x13\xe1\x01\xf68\x03y\x16\x1a\xfe\x9e\xfe\f\a\r\x15\x1d\f\xec\xec\f\x1d\x15\x06\x0e\x01\xf4\x01b\x1b\x15%\tI\x01\xc7))\xfe9I\t\x00\x00\x00\x00\x02\x00\x00\xff\xad\x06\x80\x05\xe0\x00\t\x00+\x00\x00\t\x01%\v\x01\x05\x01\x03%\x05\x01\x14\a\x01\x13\x16\x15\x14#\"'%\x05\x06#\"&547\x13\x01&547%\x1362\x17\x13\x05\x16\x04q\x012\xfeZ\xbd\xbd\xfeZ\x012I\x01z\x01y\x01\xc7\x1a\xfe\x95V\x01)\x13\x15\xfe?\xfe?\x16\x12\x15\x15\x02V\xfe\x94\x198\x01\xf6\xe1\x13<\x13\xe1\x01\xf68\x02\x14\x01)>\x01~\xfe\x82>\xfe\xd7\xfe[\xc7\xc7\x03\n\x16\x1a\xfe\x9e\xfe\f\a\r2\f\xec\xec\f\x1d\x15\x06\x0e\x01\xf4\x01b\x1b\x15%\tI\x01\xc7))\xfe9I\t\x00\x00\x02\x00\x00\xff\x80\x05\x00\x05\x80\x00\x15\x00\x1d\x00\x00%\x14\x06#!\"&54>\x033\x16 72\x1e\x03\x00\x10\x06 &\x106 \x05\x00}X\xfc\xaaX}\x11.GuL\x83\x01l\x83LuG.\x11\xff\x00\xe1\xfe\xc2\xe1\xe1\x01>\x89m\x9c\x9cmU\x97\x99mE\x80\x80Em\x99\x97\x03\xc1\xfe\xc2\xe1\xe1\x01>\xe1\x00\x00\x00\v\x00\x00\xff\x00\a\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\x00\x0554&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x01267\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\x00&\x1a\xfd\x00\x1a&&\x1a\x03\x00\x1a&\xfc\x00&\x1a\x80\x1a&&\x1a\x80\x1a&\x05\x80&\x1a\x80\x1a&&\x1a\x80\x1a&\xfe\x80&\x1a\xfd\x00\x1a&&\x1a\x03\x00\x1a&\x01\x80&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&&\x1a\x80\x1a&\x80^B\xf9\xc0B^^B\x06@B^@\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\xfd\x1a\x02\x00\x1a&&\x1a\xfe\x00\x1a&&\x04\x9a\x80\x1a&&\x1a\x80\x1a&&\xfb\x9a\x80\x1a&&\x1a\x80\x1a&&\x03\x1a\x02\x00\x1a&&\x1a\xfe\x00\x1a&&\xfe\x9a\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\x01\x9a\x80\x1a&&\x1a\x80\x1a&&\xba\xfa\xc0B^^B\x05@B^^\x00\x04\x00\x00\x00\x00\x06\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x01\x11\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x03\x00L4\xfe\x004LL4\x02\x004LL4\xfe\x004LL4\x02\x004L\x03\x80L4\xfe\x004LL4\x02\x004LL4\xfe\x004LL4\x02\x004L\x02\x00\xfe\x804LL4\x01\x804LL\x02\xcc\xfe\x804LL4\x01\x804LL\xfc\xcc\xfe\x804LL4\x01\x804LL\x02\xcc\xfe\x804LL4\x01\x804LL\x00\t\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x02\x008(\xfe\xc0(88(\x01@(88(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(8\xfd\x808(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(8\xfd\x808(\xfe\xc0(88(\x01@(8\x02\x808(\xfe\xc0(88(\x01@(88(\xfe\xc0(88(\x01@(8\x01 \xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x03\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x03\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x00\x00\x06\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x02\x008(\xfe\xc0(88(\x01@(88(\xfe\xc0(88(\x01@(8\x05\x008(\xfc@(88(\x03\xc0(8\xfb\x008(\xfe\xc0(88(\x01@(8\x05\x008(\xfc@(88(\x03\xc0(88(\xfc@(88(\x03\xc0(8\x01 \xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x03\xd8\xc0(88(\xc0(88\xfd\xd8\xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x00\x00\x00\x01\x00y\x00\x0e\x06\x87\x04\xb2\x00\x16\x00\x00\x00\x14\a\x01\a\x06\"/\x01\x01&4?\x0162\x17\t\x0162\x1f\x01\x06\x87\x1c\xfd,\x88\x1cP\x1c\x88\xfe\x96\x1c\x1c\x88\x1cP\x1c\x01&\x02\x90\x1cP\x1c\x88\x03\xf2P\x1c\xfd,\x88\x1c\x1c\x88\x01j\x1cP\x1c\x88\x1c\x1c\xfe\xd9\x02\x91\x1c\x1c\x88\x00\x01\x00n\xff\xee\x05\x12\x04\x92\x00#\x00\x00$\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\t\x0162\x1f\x01\x16\x14\a\t\x01\x05\x12\x1c\x88\x1cP\x1c\xfe\xda\xfe\xda\x1cP\x1c\x88\x1c\x1c\x01&\xfe\xda\x1c\x1c\x88\x1cP\x1c\x01&\x01&\x1cP\x1c\x88\x1c\x1c\xfe\xda\x01&\xfeP\x1c\x88\x1c\x1c\x01&\xfe\xda\x1c\x1c\x88\x1cP\x1c\x01&\x01&\x1cP\x1c\x88\x1c\x1c\xfe\xda\x01&\x1c\x1c\x88\x1cP\x1c\xfe\xda\xfe\xda\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00#\x00+\x00D\x00\x00\x01\x15\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x01546;\x012\x16\x1d\x0132\x1e\x01\x10\x00 \x00\x10\x00 \x00\x14\x06#\"'\x01\x06#\"$&\x02\x10\x126$ \x04\x16\x12\x15\x14\a\x01\x04\x00\x13\r\xe0\x13\r@\r\x13\xe0\r\x13\x13\r\xe0\x13\r@\r\x13\xe0\r\x13\x80\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x03\aK56$\xfe\xa9\xb3\u070f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdo|\x01W\x02\xe0@\r\x13\xe0\r\x13\x13\r\xe0\x13\r@\r\x13\xe0\r\x13\x13\r\xe0\x13\xe6\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\xfe\xb5jK&\x01V|o\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\xfe\xfb\x8fܳ\xfe\xa9\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00\x0f\x00\x17\x000\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x1e\x01\x10\x00 \x00\x10\x00 \x00\x14\x06#\"'\x01\x06#\"$&\x02\x10\x126$ \x04\x16\x12\x15\x14\a\x01\x04\x00\x13\r\xfd\xc0\r\x13\x13\r\x02@\r\x13\x80\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x03\aK56$\xfe\xa9\xb3\u070f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdo|\x01W\x02\xe0@\r\x13\x13\r@\r\x13\x13\xe6\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\xfe\xb5jK&\x01V|o\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\xfe\xfb\x8fܳ\xfe\xa9\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x06\x00\x00)\x005\x00\x00\x01\x14\x02\x06\x04 $&\x0254\x1276\x16\x17\x16\x06\a\x0e\x01\x15\x14\x1e\x022>\x0254&'.\x017>\x01\x17\x16\x12\x01\x11\x14\x06\"&5\x11462\x16\x06\x00z\xce\xfe\xe4\xfe\xc8\xfe\xe4\xcez\xa1\x92+i\x1f \x0f*bkQ\x8a\xbdн\x8aQkb*\x0f \x1fj*\x92\xa1\xfd\x80LhLLhL\x02\x80\x9c\xfe\xe4\xcezz\xce\x01\x1c\x9c\xb6\x01Bm \x0e+*i J\xd6yh\xbd\x8aQQ\x8a\xbdhy\xd6J i*+\x0e m\xfe\xbe\x02J\xfd\x804LL4\x02\x804LL\x00\x00\x00\x00\x05\x00\x00\xff\x80\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x11\x14\x06+\x01\"&5\x1146;\x012\x16%\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x01\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x01\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x01\x00\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12`\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12r\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\xf2\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x01r\xfc@\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x12\x01\xf2\xfa@\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00n\x00\x00\x004&\"\x06\x14\x162\x01\x15\x14\x06\x0f\x01\x06\a\x16\x17\x16\x14\a\x0e\x01#\"/\x01\x06\a\x06\a\x06+\x01\"&/\x01&'\a\x06#\"'&'&547>\x017&/\x01.\x01=\x0146?\x0167&'&547>\x0132\x1f\x0167676;\x012\x16\x1f\x01\x16\x177632\x17\x16\x17\x16\x15\x14\a\x0e\x01\a\x16\x1f\x01\x1e\x01\x04\x00\x96Ԗ\x96\xd4\x02\x96\x10\f\xb9\x13\x14#H\n\t\x1b\x90\x16\f\x0e\x8a,/\x10\r\a\x1d\xde\x0e\x15\x01\x1c1)\x8d\n\x0f\x0e\v~'\a\b\x0fH\x12\x1b\x0e\xb7\r\x10\x10\v\xba\x0e\x19(C\n\t\x1a\x91\x16\r\r\x8a,/\x10\r\a\x1d\xde\x0e\x15\x01\x1c1)\x8e\t\x0f\r\f\x81$\a\b\x0fH\x12\x1a\x0f\xb7\r\x10\x02\x16Ԗ\x96Ԗ\x01m\xde\f\x16\x02\x1c6%2X\f\x1a\n%\x8e\tl\x17\x0f\x882\x1c\x11\r\xb8\x10\x15k\t\vr6\n\r\f\v\x15[\x1921\x1b\x02\x15\r\xde\f\x16\x02\x1c..9Q\f\f\n\r$\x8f\nk\x17\x0f\x882\x1c\x11\r\xb8\x10\x15k\t\nw3\b\x0e\f\v\x15[\x1920\x1c\x02\x15\x00\x00\x06\x00\x00\xff\x80\x05\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00;\x00C\x00g\x00\x00\x01\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x05\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x05\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x13\x11!\x11\x14\x1e\x013!2>\x01\x01!'&'!\x06\a\x05\x15\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&=\x01463!7>\x013!2\x16\x1f\x01!2\x16\x02\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x80\xfc\x80\x0e\x0f\x03\x03@\x03\x0f\x0e\xfd`\x01\xc00\a\n\xfe\xc3\n\a\x03o\x12\x0e`^B\xfc\xc0B^`\x0e\x12\x12\x0e\x015F\x0fN(\x01@(N\x0fF\x015\x0e\x12\x03 \xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\xfd\x1e\x03\xb4\xfcL\x16%\x11\x11%\x04Ju\t\x02\x02\t\x95@\x0e\x12\xfcLSyuS\x03\xb8\x12\x0e@\x0e\x12\xa7%44%\xa7\x12\x00\x00\x00\x00\x02\x00\x1a\x00\x00\x06f\x05\x03\x00\x13\x005\x00\x00\x01\x11\x14\x06#!\x11!\x11!\"&5\x11465\t\x01\x167\a\x06\a#\"'\t\x01\x06'&/\x01&67\x0162\x1f\x01546;\x012\x16\x15\x11\x17\x1e\x01\x05\x80&\x1a\xfe\x80\xff\x00\xfe\x80\x1a&\x01\x02?\x02?\x01\xdf>\b\r\x03\r\b\xfdL\xfdL\f\f\r\b>\b\x02\n\x02\xcf X \xf4\x12\x0e\xc0\x0e\x12\xdb\n\x02\x02 \xfe \x1a&\x01\x80\xfe\x80&\x1a\x01\xe0\x01\x04\x01\x01\xda\xfe&\x02AJ\t\x02\a\x02A\xfd\xbf\b\x01\x02\tJ\n\x1b\b\x02W\x1a\x1a\xcc\xc3\x0e\x12\x12\x0e\xfeh\xb6\b\x1b\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00,\x00\x00\x01\x11\x14\x06#!\"&=\x0146;\x01\x1146;\x012\x16\x00\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x80\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x01\xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03\xe0\xfe@\x0e\x12\x12\x0e@\x0e\x12\x01`\x0e\x12\x12\xfd\xfe\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x002\x00\x00\aN\x05\x00\x00\x11\x00C\x00\x00\x015\x03.\x01+\x01\"\x06\a\x03\x15\x06\x16;\x0126\x01\x14#!26'\x03.\x01#!\"\x06\a\x03\x06\x163!\"547\x01>\x013!\"\x06\x0f\x01\x06\x16;\x0126/\x01.\x01#!2\x16\x17\x01\x16\x04W\x18\x01\x14\r\xba\r\x14\x01\x18\x01\x12\f\xf4\f\x12\x02\xf6.\xfd@\r\x12\x01\x14\x01\x14\r\xfe\xf0\r\x14\x01\x14\x01\x12\r\xfd@.\x1a\x01\xa1\b$\x14\x01S\r\x14\x01\x0f\x01\x12\r\xa6\r\x12\x01\x0f\x01\x14\r\x01S\x14$\b\x01\xa1\x1a\x02\x1c\x04\x01@\r\x13\x13\r\xfe\xc0\x04\f\x10\x10\xfe9I\x13\r\x01\x00\r\x13\x13\r\xff\x00\r\x13I6>\x04\x14\x13\x1c\x13\r\xc0\x0e\x12\x12\x0e\xc0\r\x13\x1c\x13\xfb\xec>\x00\x04\x00\x00\x00\x00\x06\x80\x06\x00\x00\a\x00\x0f\x00%\x00=\x00\x00$4&\"\x06\x14\x162$4&\"\x06\x14\x162\x13\x11\x14\x06#!\"&5\x11463!\x17\x162?\x01!2\x16\x01\x16\a\x01\x06\"'\x01&763!\x11463!2\x16\x15\x11!2\x05\x00&4&&4\x01&&4&&4\xa68(\xfa@(88(\x01ч:\x9c:\x88\x01\xd0(8\xfe\xbb\x11\x1f\xfe@\x126\x12\xfe@\x1f\x11\x11*\x01\x00&\x1a\x01\x00\x1a&\x01\x00*\xa64&&4&&4&&4&\x01 \xfe\xc0(88(\x01@(8\x8888\x888\x02\x11)\x1d\xfe@\x13\x13\x01\xc0\x1d)'\x01\xc0\x1a&&\x1a\xfe@\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x18\x00$\x000\x00\x00\x01\x14\a\x01\x06\"'\x01&76;\x01\x1146;\x012\x16\x15\x1132\x16\x02 \x0e\x01\x10\x1e\x01 >\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04`\n\xfe\xc1\v\x18\v\xfe\xc0\x0f\b\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\xcc\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02`\f\f\xfe\xc1\t\t\x01@\x10\x13\x14\x01`\x0e\x12\x12\x0e\xfe\xa0\x12\x022\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x18\x00$\x000\x00\x00\x01\x06+\x01\x11\x14\x06+\x01\"&5\x11#\"&547\x0162\x17\x01\x16\x02 \x0e\x01\x10\x1e\x01 >\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04^\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\n\x01?\v\x18\v\x01@\x0f\xd2\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\x94\x14\xfe\xa0\x0e\x12\x12\x0e\x01`\x12\x0e\f\f\x01?\t\t\xfe\xc0\x10\x01\xf9\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\x00\x00\x06\x00\x05\x00\x00\r\x00#\x00\x00\x01!.\x01'\x03!\x03\x0e\x01\a!\x17!%\x11\x14\x06#!\"&5\x1147\x13>\x013!2\x16\x17\x13\x16\x03\xff\x01<\x01\x03\x01\xd4\xfd<\xd4\x01\x03\x01\x01<_\x01@\x02`&\x1a\xfa\x80\x1a&\x19\xee\n5\x1a\x03@\x1a5\n\xee\x19\x02@\x03\v\x02\x01\xf0\xfe\x10\x03\v\x02\xc0\xa2\xfe\x1e\x1a&&\x1a\x01\xe2>=\x02(\x19\"\"\x19\xfd\xd8=\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00'\x00\x00\x00\x14\a\x01\x06#\"'&5\x11476\x17\x01\x16\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xa0 \xfd\xe0\x0f\x11\x10\x10 !\x1f\x02 \xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xa5J\x12\xfe\xc0\t\b\x13%\x02\x80%\x13\x12\x13\xfe\xc0\xcb\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x003\x00\x00\x01\x11\x14\x06#!\"'&?\x01&#\"\x0e\x02\x14\x1e\x023267672\x1f\x01\x1e\x01\a\x06\x04#\"$&\x02\x10\x126$32\x04\x1776\x17\x16\x06\x00&\x1a\xfe@*\x11\x11\x1f\x8a\x94\xc9h\xbd\x8aQQ\x8a\xbdhw\xd4I\a\x10\x0f\n\x89\t\x01\bm\xfeʬ\x9c\xfe\xe4\xcezz\xce\x01\x1c\x9c\x93\x01\x13k\x82\x1d)'\x05\x00\xfe@\x1a&('\x1e\x8a\x89Q\x8a\xbdн\x8aQh_\n\x02\t\x8a\b\x19\n\x84\x91z\xce\x01\x1c\x018\x01\x1c\xcezoe\x81\x1f\x11\x11\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00$\x00G\x00\x00\x01\x14\a\x02\x00!\"$'\a\x06\"&5\x11463!2\x16\x14\x0f\x01\x1e\x013267676;\x012\x16\x13\x11\x14\x06#!\"&4?\x01&#\"\x06\a\x06\a\x06+\x01\"&=\x01\x12\x00!2\x04\x17762\x16\x05\xe7\x01@\xfeh\xfe\xee\x92\xfe\xefk\x81\x134&&\x1a\x01\xc0\x1a&\x13\x89G\xb4a\x86\xe8F\v*\b\x16\xc0\r\x13\x19&\x1a\xfe@\x1a&\x13\x8a\x94Ɇ\xe8F\v*\b\x16\xc7\r\x13A\x01\x9a\x01\x13\x92\x01\x14k\x82\x134&\x01\xe0\x05\x02\xfe\xf4\xfe\xb3nf\x81\x13&\x1a\x01\xc0\x1a&&4\x13\x89BH\x82r\x11d\x17\x13\x03\x13\xfe@\x1a&&4\x13\x8a\x89\x82r\x11d\x17\x13\r\a\x01\f\x01Moe\x81\x13&\x00\x00\x00\x00\b\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x04\x80\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x80\x13\r\xfa@\r\x13\x13\r\x05\xc0\r\x13\x80^B\xfa@B^^B\x05\xc0B^\x01`@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd3\x03@\r\x13\x13\r\xfc\xc0\r\x13\x13\x04M\xfb\xc0B^^B\x04@B^^\x00\x02\x00\x00\x00\x00\x04\x80\x05\x80\x00\a\x00\x1f\x00\x00\x01!54&\"\x06\x15\x01\x11\x14\x06#!\"&5\x1146;\x0154\x00 \x00\x1d\x0132\x16\x01@\x02\x00\x96Ԗ\x03@8(\xfc@(88( \x01\b\x01p\x01\b (8\x03\x00\xc0j\x96\x96j\xfe\xe0\xfd\xc0(88(\x02@(8\xc0\xb8\x01\b\xfe\xf8\xb8\xc08\x00\x00\x02\x00@\xff\x80\a\x00\x05\x80\x00\x11\x007\x00\x00\x01\x14\a\x11\x14\x06+\x01\"&5\x11&5462\x16\x05\x11\x14\x06\a\x06#\".\x02#\"\x05\x06#\"&5\x114767632\x16\x17\x1632>\x0232\x16\x01@@\x13\r@\r\x13@KjK\x05\xc0\x19\x1bך=}\\\x8bI\xc0\xfe\xf0\x11\x10\x1a&\x1f\x15:\xec\xb9k\xba~&26\u007f]S\r\x1a&\x05\x00H&\xfb\x0e\r\x13\x13\r\x04\xf2&H5KKu\xfd\x05\x19\x1b\x0et,4,\x92\t&\x1a\x02\xe6 \x17\x0e\x1dx:;\x13*4*&\x00\x00\x00\x01\x00\x00\x00\x00\x06\x80\x05\x80\x00K\x00\x00\x01\x14\x0f\x02\x0e\x01#\x15\x14\x06+\x01\"&5\x1146;\x012\x16\x1d\x012\x16\x177654\x02$ \x04\x02\x15\x14\x1f\x01>\x013546;\x012\x16\x15\x11\x14\x06+\x01\"&=\x01\"&/\x02&54\x126$ \x04\x16\x12\x06\x80<\x14\xb9\x16\x89X\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12Gv\"D\x1d\xb0\xfe\xd7\xfe\xb2\xfeװ\x1dD\"vG\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12X\x89\x16\xb9\x14<\x86\xe0\x014\x01L\x014\xe0\x86\x02\x8a\xa6\x941!Sk \x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e G<\f_b\x94\x01\x06\x9c\x9c\xfe\xfa\x94b_\f\x034.\x0354632\x17\x16\x03\x00&4\x13\xfe\xb3\xfe\xfa\x1a&&\x1a\x01\x06\x01M\x134&\x01\x80UF\n\x0f\x1a&\x18\"\"\x18\x18\"\"\x18&\x1a\x0f\nF\x04\xa0\xfb\xc0\x1a&\x13\x01M&\x1a\x01\x80\x1a&\x01M\x13&\xfe\x12\x98\x83\x1c\x05%\x1b\x15\x1d\x15\x19/B/\x19\x15\x1d\x15\x1b%\x05\x1b\x00\x00\x00\x00\x04\x00\x00\xff\xb9\x06\x80\x05G\x00\x13\x00-\x00I\x00k\x00\x00\x01\x11\x14\x06\"'\x01!\"&5\x11463!\x0162\x16\x00\x14\x06\a\x06#\"&54>\x034.\x0354632\x17\x16\x04\x10\x02\a\x06#\"&54767>\x014&'&'&54632\x17\x16\x04\x10\x02\a\x06#\"&547>\x017676\x12\x10\x02'&'.\x01'&54632\x17\x16\x03\x00&4\x13\xfe\xb3\xfe\xfa\x1a&&\x1a\x01\x06\x01M\x134&\x01\x80UF\n\x0f\x1a&\x18\"\"\x18\x18\"\"\x18&\x1a\x0f\nF\x01U\xaa\x8c\r\f\x1b&'8\x14JSSJ\x148'&\x1a\r\r\x8c\x01\xaa\xfe\xd3\r\r\x1a&'\a\x1f\a.${\x8a\x8a{$.\a\x1f\a'&\x1a\r\r\xd3\x04\xa0\xfb\xc0\x1a&\x13\x01M&\x1a\x01\x80\x1a&\x01M\x13&\xfe\x12\x98\x83\x1c\x05%\x1b\x15\x1d\x15\x19/B/\x19\x15\x1d\x15\x1b%\x05\x1b7\xfe\xce\xfe\xfd;\x05&\x1a'\x14\x1d\x0f6\xa3\xb8\xa36\x0f\x1d\x14'\x1a&\x05;\xb6\xfe4\xfe\u007f[\x05&\x1a$\x17\x04\r\x04\x19\x1a[\x01\x10\x012\x01\x10[\x1a\x19\x04\r\x04\x17$\x1a&\x05[\x00\f\x00\x00\x00\x00\x05\x80\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x1f\x00#\x00/\x003\x007\x00\x00\x01\x15#5\x13\x15#5!\x15#5\x01!\x11!\x11!\x11!\x01!\x11!\x01\x11!\x11\x01\x15#5!\x15#5\x13\x11!5#\x11#\x11!\x1535\x01\x11!\x11!\x11!\x11\x01\x80\x80\x80\x80\x03\x80\x80\xfc\x80\x01\x80\xfe\x80\x01\x80\xfe\x80\x03\x00\x01\x80\xfe\x80\xff\x00\xfd\x80\x04\x80\x80\x01\x80\x80\x80\xfe\x80\x80\x80\x01\x80\x80\xfd\x80\xfd\x80\x05\x80\xfd\x80\x01\x80\x80\x80\x03\x00\x80\x80\x80\x80\xfc\x01\x01\u007f\x01\x80\x01\x80\xfe\x80\x01\x80\xfd\x80\xfd\x80\x02\x80\xfe\x00\x80\x80\x80\x80\x02\x00\xfe\x80\x80\xfe\x80\x02\x80\x80\x80\x03\x00\xfd\x80\x02\x80\xfd\x80\x02\x80\x00\x00\x00\x00\x10\x00\x00\x00\x00\a\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x1f\x00#\x00'\x00+\x00/\x003\x007\x00;\x00?\x00\x003#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113\x13#\x113???? ^\x1f\x1f\x9d\x1f\x1f\x9d>>~\x1f\x1f?\x1f\x1f?\x1f\x1f\x9d??\x9d??~??~??^??\xbd^^? ^??\x05\x80\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x81\x05\u007f\xfa\x80\x05\x80\x00\x00\x00\x02\x00\x00\xff\x95\x05\xeb\x05\x80\x00\a\x00\x1d\x00\x00\x004&\"\x06\x14\x162\x01\x14\a\x01\x06#\"'\x01.\x015\x11463!2\x16\x17\x01\x16\x01\xc0KjKKj\x04v%\xfe\x15'45%\xfd5&5L4\x01\xa05\x80&\x02\xcb%\x04\vjKKjK\xfe@5%\xfe\x14%%\x02\xcc%\x805\x01\xa04L5&\xfd6'\x00\x00\x00\x00\x03\x00\x00\xff\x95\ak\x05\x80\x00\a\x00\x1d\x005\x00\x00\x004&\"\x06\x14\x162\x01\x14\a\x01\x06#\"'\x01.\x015\x11463!2\x16\x17\x01\x16\x05\x14\a\x01\x06#\"&'\x01654'\x01.\x01#32\x16\x17\x01\x16\x01\xc0KjKKj\x04v%\xfe\x15'45%\xfd5&5L4\x01\xa05\x80&\x02\xcb%\x01\x80%\xfe\x15'4$.\x1e\x01\xd6%%\xfd5&\x805\xe05\x80&\x02\xcb%\x04\vjKKjK\xfe@5%\xfe\x14%%\x02\xcc%\x805\x01\xa04L5&\xfd6'45%\xfe\x14%\x1c\x1f\x01\xd6%54'\x02\xca&55&\xfd6'\x00\x03\x00\n\xff\x80\x06y\x05\x80\x00T\x00d\x00t\x00\x00\x01\x16\a\x01\x0e\x01#!\"&'&74676&7>\x027>\x0176&7>\x017>\x0176&7>\x017>\x0176&7>\x027>\x06\x17\a63!2\x16\a\x01\x0e\x01#!\"\a\x06\x17\x163!267\x016'\x16\x05\x06\x163!26?\x016&#!\"\x06\a\x03\x06\x163!26?\x016&#!\"\x06\a\x06g(\x16\xfe\xed\x13sA\xfceM\x8f\x1c\x18\x16\x06\x01\x01\b\x01\x02\f\x15\x06\x17,\b\x03\x05\x02\x03\x1c\x03\x15*\x04\x01\a\x04\x04$\x04\x13/\x04\x01\b\x02\x02\x0e\x16\x06\b\x11\r\x13\x14!'\x1c\x01&\r\x02\xf9JP\x16\xfe\xee$G]\xfc\x9b\x1b\v\v\n\x18x\x03\x9b\x1d6\b\x01,\a\x02&\xfb\xed\x04\f\x0e\x02`\r\x19\x04\x15\x04\f\x0e\xfd\xa0\r\x19\x04h\x04\f\x0e\x02`\r\x19\x04\x15\x04\f\x0e\xfd\xa0\r\x19\x04\x04\"9H\xfcv@WkNC<\x04.\x0e\b\x1b\x06\v\x14\x1b\n&k&\n(\b\v\"\x06$p\"\t.\x05\r#\x05\x1au&\b#\t\b\x14\x1a\b\f%!'\x19\x16\x01\x06\x03\tpJ\xfcvwE\x0f\x10\x1bF\x1f\x1a\x03\xdb\x16#\x0f\x1e\r\x13\x13\r@\r\x13\x13\r\xfe\xc0\r\x13\x13\r@\r\x13\x13\r\x00\x00\x01\x00\x00\xff\x97\x05\x00\x05\x80\x00\x1c\x00\x00\x012\x17\x1e\x01\x15\x11\x14\x06\a\x06#\"'\t\x01\x06#\"'.\x015\x1146763\x04\x8c\x17\x15!''!\x13\x190#\xfeG\xfeG$/\x17\x15!''!\x15\x17\x05\x80\t\r8\"\xfa\xf7\"8\r\b \x01\xa8\xfeX!\t\r8\"\x05\t\"8\r\t\x00\x00\x00\x00\x04\x00\x00\xff\x80\x06\x80\x05\x80\x00\x03\x00\f\x00\x14\x00<\x00\x00)\x01\x11!\x11!\x11#\"&=\x01!\x004&\"\x06\x14\x1627\x11\x14\x06+\x01\x15\x14\x06#!\"&=\x01#\"&5\x1146;\x01\x11463!2\x16\x1f\x01\x1e\x01\x15\x1132\x16\x01\x80\x03\x80\xfc\x80\x03\x80\xa0(8\xfd\x80\x04\x80&4&&4\xa6\x13\r\xe08(\xfc@(8\xe0\r\x13qO@8(\x02\xa0(`\x1c\x98\x1c(@Oq\x01\x00\x01\x80\x01\x808(\xa0\xfd&4&&4&@\xfe`\r\x13\xa0(88(\xa0\x13\r\x01\xa0Oq\x02 (8(\x1c\x98\x1c`(\xff\x00q\x00\x03\x00\x00\xff\x80\a\x80\x06\x00\x00\a\x00!\x00)\x00\x00\x002\x16\x14\x06\"&4\x012\x16\x15\x11\x14\x06#!\"&5\x1146;\x017>\x013!2\x16\x1f\x01\x00 \x00\x10\x00 \x00\x10\x03I\uea69\xee\xa9\x03\xe0j\x96\x96j\xfa\x80j\x96\x96j\xe03\x13e5\x02\x005e\x133\xfdg\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x03`\xa9\uea69\xee\x02I\x96j\xfc\x80j\x96\x96j\x03\x80j\x96\x881GG1\x88\xfb\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x80\x05\x80\x00\a\x00P\x00\x00\x01\x032\x16327&\x017>\x047\x13\x01;\x01\x16\x17\x13\x16\x12\x17\x1e\x01\x17\x16\x17\x1e\x01\x17\x16\x15\x14\x06\x15\"&#\"\x04\a4?\x012>\x0554.\x01'%\x06\x02\x15\x14\x1e\x033\x16\x15\x14\a\"&#\"\x06#\x06\x02ժ!\xcf9\x13&W\xfc\xca\x02\x17B03&\f\xed\x01\x18K5\b\x03\xcd!\x92)\x0fV\x1d\x14\x0f\x13\x8a\x0f\x06\x01?\xfe@L\xfe\xea'\x04\x83\x01\x17\b\x15\t\r\x05>R\x01\xfe>\x1ae\x1c;&L\x03\x01\x02:\xe9:\b%\x03P\x03\xd1\xfe>\x04\x02\xfd\xfcvO\a\v\n\x13'\x1f\x02h\x02\xd4\x0e\a\xfe N\xfe\x99_\"\xdd:-\f\x0f\x1d\x06&\x13\x05\x11\x04\x10\x0e\x01+#\x1c\x05\x02\a\x06\n\f\b\x10\xa1\xc2\x03\x02:\xfe\xed\x19\x16\x1f\x12\t\b\x13'\t\x12\x14\b\x0e\x00\x00\x03\x00\x00\xff\x80\x05\x80\x05\x80\x00\x15\x00+\x00a\x00\x00%\x163 \x114'.\x04#\"\a\x14\x06\x15\x14\x06\x1e\x01\x03\x1632>\x0254.\x02#\"\a\x14\x16\x15\x14\x06\x15\x14\x017>\x017>\x04<\x015\x10'.\x04/\x016$32\x1632\x1e\x03\x15\x14\x0e\x03\a\x1e\x01\x15\x14\x0e\x03#\"&#\"\x04\x02+JB\x01x)\x1bEB_I:I\x1c\x01\x02\x01\b\x06*CRzb3:dtB2P\b\x01\xfd\xe4\x02\x0f\x8c$\a\v\x06\x05\x01\x16\x04$5.3\x05\x04b\x01\xe4\x83\x17Z\x17F\x85|\\8!-T>5\x9a\xcdFu\x9f\xa8\\,\xb0,j\xfen\x0f \x01OrB,\x027676\x1a\x01'5.\x02'7\x1e\x0232>\x017\x06\a\x0e\x01\a\x0e\x03\a\x06\x02\a\x0e\x03\x1f\x01\x16\x17\x06\a\"\x06#\"&#&#\"\x06\x11\x16OA\x1b\x1c\r\x01zj\x01\x18=N\x13\x13!\xae}:0e\x8d\x1c\x05\x0e\x1e\x8f%\b\f\x06\t\x02\x1by\x11\x02\x16\x12\x0e\x01\x01\x11\xa8\x03\r\v+\v\x1dt\x1c\x8aD3\xb8~U\a\x13\x13\x0e#B\a\x024\x02\v#\x19\r\v\x05\x03g\x02\t\x05\x05\t\x02'2\n%\x0f\x13/!:\r\x94\xfd\xe1T\tbRU\x0f\x12\x04\x1b,7\x03\x14\x02\x12\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\xfa\x05\x80\x00\x1b\x00}\x00\x00%2\x16\x0f\x01\x06\"/\x01&6;\x01\x11#\"&?\x0162\x1f\x01\x16\x06+\x01\x11\x01\x17\x1632632\x163!2\x16>\x02?\x012\x163\x16\x15\x14\a\x06\a&'.\x02'.\x03\x06#\"&\"\x06\a\x06\x17\x14\x12\x15\x14\x06\x16\x17\x1e\x01\x17\x16\x15\x14\x0f\x01\x06$#\"\x06#&=\x01>\x0276\x114\x02=\x01464.\x01'&#\"\x06\a\x0e\x02\a&'\x11\x06\xd0!\x12\x14~\x14:\x14~\x14\x12!PP!\x12\x14~\x14:\x14~\x14\x12!P\xf9\xd16\f\xc7,\xb0,$\x8f$\x01%\x06\x1e\v\x15\x0e\b*\x04\x14\x04\x02\x05'\x1d\x19\x1d\x03\x10\r\x01\x06\f\x13\a\x1d\x02\x11c2N \t\x01\x04\x05\x05\n(\xa8$\x05\x03\"L\xfe\xe4A2\xca3\x03\x11Yl\x18\x13\x06\x01\x02\x04\x03\v\x97!x\x14\x13\x1e!\x1a*\x0e\x80%\x1a\xa2\x1a\x1a\xa2\x1a%\x04\x00%\x1a\xa2\x1a\x1a\xa2\x1a%\xfc\x00\x04\xff\x1b\x05\x04\x01\x01\x01\x05\r\v\x01\x01p\xe0P\x1d\x0e\x04,T\tNE\x01\b\t\x03\x02\x01\x01\x04\x04Q7^\xfd\xb4\xa1\x10oH!\x15+\x10(\n\x0e\x0f\x01\x02\x14\x123\x01\t\x1b \x1a\x0e*\x01Ue\x01\x94eu\x02\x1b\x17\x1c\x14\x04\f\x18\x0e\rwg\x02\x1a\x12\x01\u007f\x00\x00\x02\x00\x00\xff\x03\x06\x00\x05\x80\x00a\x00\x95\x00\x00\x13\x17\x1632632$\x04\x17\x16?\x012\x163\x16\x15\x14\a\x06\a&'.\x025&'&#\"&\"\x06\a\x06\x1f\x015\x14\x1e\x01\x15\x14\x06\x16\x17\x1e\x01\x17\x16\x15\x14\x0f\x01\x06$#\"\x06#&=\x01>\x027>\x024&54&54>\x01.\x01'&#\"\x06\a\x0e\x02\a&'\x11\x012\x1e\x02\x17\x16\x14\a\x0e\x03#\".\x01465!\x14\x16\x14\x0e\x01#\".\x02'&47>\x0332\x1e\x01\x14\x06\x15!4&4>\x01Q6\f\xc7,\xb0,F\x01a\x01\x00w!\x17*\x04\x14\x04\x02\x05'\x1d\x19\x1d\x03\x10\x0e\n\x11\x05=\x1e~Pl*\t\x01\x01\x02\x01\x05\x05\n(\xa8$\x05\x03\"L\xfe\xe4A2\xca3\x03\x11Yl\x18\a\t\x03\x01\x05\x01\x01\x01\x05\x04\v\x97)\xf4\x10\x13\x1e!\x1a*\x0e\x05\x1e\f<7@\x04\x1a\x1a\x04@7<\f\r\x0f\x05\x03\xfc\x00\x03\x05\x0f\r\f<7@\x04\x1a\x1a\x04@7<\f\r\x0f\x05\x03\x04\x00\x03\x05\x0f\x05\u007f\x1b\x05\x04\x02\x01\x04\x01 \x01\x01p\xe0P\x1d\x0e\x04,T\tMF\x01\r\x06\x02\x02\x04\x05Q7\x9847ƢH\x10oH!\x15+\x10(\n\x0e\x0f\x01\x02\x14\x123\x01\t\x1b \x1a\x0e\x10t\xaf\x87\xac\x03\a\x1d\b\aJHQ6\x05\f\x1b\v\fwh\x02\x1a\x12\x01\u007f\xfa\xff',6\x03\x158\x15\x036,'\x15$\x1f#\x02\x02#\x1f$\x15',6\x03\x158\x15\x036,'\x15$\x1f#\x02\x02#\x1f$\x15\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xfe\x80&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&\x01\x00&\x1a\xfa\x00\x1a&&\x1a\x06\x00\x1a&\xfe\x80&\x1a\xfb\x80\x1a&&\x1a\x04\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xfe\x80&\x1a\xfc\x80\x1a&&\x1a\x03\x80\x1a&\x01\x00&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\xfe\x80&\x1a\xfd\x80\x1a&&\x1a\x02\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&&\x1a\xfa\x00\x1a&&\x1a\x06\x00\x1a&&\x1a\xfb\x80\x1a&&\x1a\x04\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x00\x00\x04\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\a\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x01f\x80\x1a&&\x1a\x80\x1a&&\x00\x00\x00\x00\b\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x00\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x06\x00\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\xfa\x00\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x06\x00\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\xe0\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\xfc\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x04s\xc0\r\x13\x13\r\xc0\r\x13\x13\xfc\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x05\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00\x00\x01\x11\x14\x06#\"'\x01&47\x01632\x16\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x80\x13\r\x0e\t\xfe\xe0\t\t\x01 \t\x0e\r\x13\x05\x80\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x03\xe0\xfd\xc0\r\x13\t\x01 \t\x1c\t\x01 \t\x13\xfc\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x05\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00\x00\x00\x14\a\x01\x06#\"&5\x114632\x17\t\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01`\t\xfe\xe0\t\x0e\r\x13\x13\r\x0e\t\x01 \x05\xa9\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x13\r\xf9@\r\x13\x13\r\x06\xc0\r\x13\x02\xce\x1c\t\xfe\xe0\t\x13\r\x02@\r\x13\t\xfe\xe0\xfe\t\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x01s\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x01\x00\x00\x00\x00\a\x00\x05\x00\x00\x1f\x00\x00\x01\x11\x14\a\x06#\"'\x01\x15\x14\x06#!\"&5\x11463!2\x16\x1d\x01\x01632\x17\x16\a\x00'\r\f\x1b\x12\xfem\xa9w\xfd@w\xa9\xa9w\x02\xc0w\xa9\x01\x93\x12\x1b\f\r'\x04\xa0\xfb\xc0*\x11\x05\x13\x01\x93\xa6w\xa9\xa9w\x02\xc0w\xa9\xa9w\xa5\x01\x92\x13\x05\x11\x00\x00\x00\x00\x04\x00\x00\xff\x80\a\x80\x05\x80\x00\a\x00\x0e\x00\x1e\x00.\x00\x00\x00\x14\x06\"&462\x01\x11!5\x01\x17\t\x01!\"\x06\x15\x11\x14\x163!265\x114&\x17\x11\x14\x06#!\"&5\x11463!2\x16\x02\x80p\xa0pp\xa0\x04p\xfa\x80\x01@\xa0\x02\x00\x02\x00\xf9\xc0\r\x13\x13\r\x06@\r\x13\x13\x93^B\xf9\xc0B^^B\x06@B^\x04\x10\xa0pp\xa0p\xfd\xc0\xfe@\xc0\x01@\xa0\x02\x00\x01 \x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13 \xfb@B^^B\x04\xc0B^^\x00\x04\x00\x00\xff\x80\x05\xeb\x05k\x00\x06\x00\x14\x00\x19\x00%\x00\x00!7'\a\x153\x15\x014#\"\a\x01\x06\x15\x14327\x016'\t\x01!\x11\x01\x14\x0f\x01\x017632\x1f\x01\x16\x01k[\xeb[\x80\x02v\x16\n\a\xfd\xe2\a\x16\n\a\x02\x1e\a6\x01\xa0\xfc\xc0\xfe`\x05\xeb%\xa6\xfe`\xa6$65&\xeb%[\xeb[k\x80\x03\xa0\x16\a\xfd\xe2\a\n\x16\a\x02\x1e\a\xca\xfe`\xfc\xc0\x01\xa0\x02\xe05%\xa6\x01\xa0\xa5&&\xea'\x00\x00\x02\x00\x00\xff\x80\x04\x00\x05\x80\x00\a\x00\x17\x00\x00\x004&\"\x06\x14\x162\x01\x14\a\x01\x0e\x01\"&'\x01&54\x00 \x00\x03\x00\x96Ԗ\x96\xd4\x01\x96!\xfe\x94\x10?H?\x0f\xfe\x93!\x01,\x01\xa8\x01,\x03\x16Ԗ\x96Ԗ\x01\x00mF\xfc\xfa!&&!\x03\x06Fm\xd4\x01,\xfe\xd4\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x13\x00\x00%\x11\"\x0e\x01\x10\x1e\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x00\x94\xfa\x92\x92\xfa\x03\x94\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a`\x04@\x92\xfa\xfe\xd8\xfa\x92\x02\xf1\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x05\xc0\x00\x15\x00-\x00\x00\x014'.\x03'&\"\a\x0e\x03\a\x06\x15\x14\x1626%\x14\x00 \x00547>\x037>\x012\x16\x17\x1e\x03\x17\x16\x02\x00\x14\x01\x1d\x16\x1c\a\x04\"\x04\a\x1c\x16\x1d\x01\x14KjK\x02\x00\xfe\xd4\xfeX\xfe\xd4Q\x06qYn\x1c\t243\b\x1cnYq\x06Q\x01\x80$!\x01+!7\x17\x10\x10\x177!+\x01!$5KK\xb5\xd4\xfe\xd4\x01,ԑ\x82\t\xa3\x8b\xd9]\x1e\"\"\x1e]ً\xa3\t\u007f\x00\x05\x00\x00\x00\x00\x06\xf8\x05\x80\x00\x06\x00\x0e\x009\x00>\x00H\x00\x00\x017'\a\x153\x15\x00&\a\x01\x06\x167\x01\x13\x15\x14\x06#!\"&5\x11463!2\x17\x16\x17\x16\x0f\x01\x06'&#!\"\x06\x15\x11\x14\x163!26=\x014?\x016\x16\x03\t\x01!\x11\x01\a\x01762\x1f\x01\x16\x14\x03xt\x98t`\x02\x00 \x11\xfe\xa2\x11 \x11\x01^Q\xa9w\xfc\xc0w\xa9\xa9w\x03@?6\x0f\x03\x03\f1\x0e\x12\x17\x16\xfc\xc0B^^B\x03@B^\t@\x0f(`\x01 \xfd`\xfe\xe0\x04\\\\\xfe\xe0\\\x1cP\x1c\x98\x1c\x01`t\x98t8`\x02\xc0 \x11\xfe\xa2\x11 \x11\x01^\xfdϾw\xa9\xa9w\x03@w\xa9\x19\a\x10\x11\f1\x0e\x06\x06^B\xfc\xc0B^^B~\r\t@\x0f\x10\x02\xcd\xfe\xe0\xfd`\x01 \x02\x1c\\\x01 \\\x1c\x1c\x98\x1cP\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x06\x00\x00+\x00Z\x00\x00\x01\x11\x14\x06#!\"&5\x11463!12\x16\x15\x14\a\x06\a\x06+\x01\"\x06\x15\x11\x14\x163!26=\x0147676\x17\x16\x13\x01\x06#\"'&=\x01# \a\x06\x13\x16\a\x06#\"'.\x0454>\a;\x01547632\x17\x01\x16\x14\x05\x80\xa9w\xfc\xc0w\xa9\xa9w\x00\xff\r\x13\x1aM8\n\x06pB^^B\x03@B^\x12\x1c\x1a\x10\x13\x15\xed\xfe\x80\x12\x1b\f\r'\xa0\xfe\xbdsw-\x03\x17\b\x04\x10\n\n\x169*#\a\x15#;No\x8a\xb5j\xa0'\r\f\x1a\x13\x01\x80\x13\x02#\xfe\xfdw\xa9\xa9w\x03@w\xa9\x13\r\x1b\x05\x1a\"\x04^B\xfc\xc0B^^B\xd6\x13\n\r\x18\x10\b\t\x01\xdc\xfe\x80\x13\x05\x11*\xc0\x83\x89\xfe\xb0\x17\v\x02\r\x0e\"g`\x8481T`PSA:'\x16\xc0*\x11\x05\x13\xfe\x80\x134\x00\x00\x02\x00\x00\x00\x00\x06\u007f\x05\x80\x00/\x00D\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x17\x16\x17\x16\x0f\x01\x06#\"'&#!\"\x06\x15\x11\x14\x163!26=\x014?\x01632\x17\x16\x13\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x16\x14\x05\x80\xa9w\xfc\xc0w\xa9\xa9w\x03@?6\x0f\x03\x03\f1\n\r\x03\x06\x17\x16\xfc\xc0B^^B\x03@B^\t@\n\r\x06\x06\x14\xe7\xfc\xd2\x18B\x18\xfeR\x18\x18n\x18B\x18\x01\a\x02\x87\x18B\x18n\x18\x02^\xfe\xc2w\xa9\xa9w\x03@w\xa9\x19\a\x10\x11\f1\n\x02\x06^B\xfc\xc0B^^B\xfe\r\t@\n\x03\b\x01\xd4\xfc\xd2\x18\x18\x01\xae\x18B\x18n\x18\x18\xfe\xf9\x02\x87\x18\x18n\x18B\x00\x00\x00\x00\x01\x00\x00\xff\x00\a\x00\x06\x00\x00C\x00\x00\x00\x14\a\x01\x06\"&=\x01!\x1132\x16\x14\a\x01\x06\"'\x01&46;\x01\x11!\x15\x14\x06\"'\x01&47\x0162\x16\x1d\x01!\x11#\"&47\x0162\x17\x01\x16\x14\x06+\x01\x11!5462\x17\x01\a\x00\x13\xff\x00\x134&\xfe\x80\x80\x1a&\x13\xff\x00\x134\x13\xff\x00\x13&\x1a\x80\xfe\x80&4\x13\xff\x00\x13\x13\x01\x00\x134&\x01\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x13&\x1a\x80\x01\x80&4\x13\x01\x00\x02\x9a4\x13\xff\x00\x13&\x1a\x80\xfe\x80&4\x13\xff\x00\x13\x13\x01\x00\x134&\x01\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x13&\x1a\x80\x01\x80&4\x13\x01\x00\x13\x13\xff\x00\x134&\xfe\x80\x80\x1a&\x13\xff\x00\x00\x01\x00\x00\xff\x80\x04\x00\x05\x80\x00\x1d\x00\x00\x016\x16\x15\x11\x14\x06'\x01&'\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x15\x1167\x03\xd3\x13\x1a\x1a\x13\xfd:\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\t\x05s\x13\f\x1a\xfa@\x1a\f\x13\x02\xc6\t\n\xfdZ\x1a&&\x1a\x05\x80\x1a&&\x1a\xfdZ\n\t\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00+\x00\x00\x016\x16\x15\x11\x14\x06'\x01&'\x11\x14\x06'\x01&'\x11\x14\x06+\x01\"&5\x1146;\x012\x16\x15\x1167\x016\x16\x15\x1167\x06\xd3\x13\x1a\x1a\x13\xfd:\t\x04\x1a\x13\xfd:\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\t\x02\xc6\x13\x1a\x04\t\x05s\x13\f\x1a\xfa@\x1a\f\x13\x02\xc6\t\n\xfd:\x1a\f\x13\x02\xc6\t\n\xfdZ\x1a&&\x1a\x05\x80\x1a&&\x1a\xfdZ\n\t\x02\xc6\x13\f\x1a\xfd:\n\t\x00\x01\x00z\xff\x80\x06\x80\x05\x80\x00\x19\x00\x00\x016\x16\x15\x11\x14\x06'\x01&'\x11\x14\x06'\x01&47\x016\x16\x15\x1167\x06S\x13\x1a\x1a\x13\xfd:\t\x04\x1a\x13\xfd:\x13\x13\x02\xc6\x13\x1a\x04\t\x05s\x13\f\x1a\xfa@\x1a\f\x13\x02\xc6\t\n\xfd:\x1a\f\x13\x02\xc6\x134\x13\x02\xc6\x13\f\x1a\xfd:\n\t\x00\x00\x01\x00\x00\xff|\x05\u007f\x05\x84\x00\v\x00\x00\t\x01\x06&5\x1146\x17\x01\x16\x14\x05h\xfa\xd0\x17!!\x17\x050\x17\x02a\xfd\x1e\r\x14\x1a\x05\xc0\x1a\x14\r\xfd\x1e\r$\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00&\x1a\xfe\x00\x1a&&\x1a\x02\x00\x1a&\xfc\x80&\x1a\xfe\x00\x1a&&\x1a\x02\x00\x1a&\x05@\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\x05@\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x06\x05\x80\x00\x19\x00\x00\x17\x06&5\x1146\x17\x01\x16\x17\x1146\x17\x01\x16\x14\a\x01\x06&5\x11\x06\a-\x13\x1a\x1a\x13\x02\xc6\t\x04\x1a\x13\x02\xc6\x13\x13\xfd:\x13\x1a\x04\ts\x13\f\x1a\x05\xc0\x1a\f\x13\xfd:\t\n\x02\xc6\x1a\f\x13\xfd:\x134\x13\xfd:\x13\f\x1a\x02\xc6\n\t\x00\x00\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00+\x00\x00\x17\x06&5\x1146\x17\x01\x16\x17\x1146\x17\x01\x16\x17\x1146;\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\x06\a\x01\x06&5\x11\x06\a-\x13\x1a\x1a\x13\x02\xc6\t\x04\x1a\x13\x02\xc6\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\t\xfd:\x13\x1a\x04\ts\x13\f\x1a\x05\xc0\x1a\f\x13\xfd:\t\n\x02\xc6\x1a\f\x13\xfd:\t\n\x02\xa6\x1a&&\x1a\xfa\x80\x1a&&\x1a\x02\xa6\n\t\xfd:\x13\f\x1a\x02\xc6\n\t\x00\x00\x00\x01\x00\x00\xff\x80\x04\x00\x05\x80\x00\x1d\x00\x00\x17\x06&5\x1146\x17\x01\x16\x17\x1146;\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\x06\a-\x13\x1a\x1a\x13\x02\xc6\t\x04&\x1a\x80\x1a&&\x1a\x80\x1a&\x04\ts\x13\f\x1a\x05\xc0\x1a\f\x13\xfd:\t\n\x02\xa6\x1a&&\x1a\xfa\x80\x1a&&\x1a\x02\xa6\n\t\x00\x00\x00\x02\x00\x01\x00\x00\x06\x01\x05\x06\x00\v\x00\x1b\x00\x00\x13\x0162\x17\x01\x16\x06#!\"&\x01!\"&5\x11463!2\x16\x15\x11\x14\x06\x0e\x02\xc6\x134\x13\x02\xc6\x13\f\x1a\xfa@\x1a\f\x05\xc6\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x02-\x02\xc6\x13\x13\xfd:\x13\x1a\x1a\xfd\xe6&\x1a\x01\x00\x1a&&\x1a\xff\x00\x1a&\x00\x00\x00\x00\x01\x00\x9a\xff\x9a\x04\xa6\x05\xe6\x00\x14\x00\x00\t\x02\x16\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x16\x14\x04\x93\xfd\xed\x02\x13\x13\x13\xa6\x134\x13\xfd\x1a\x13\x13\x02\xe6\x134\x13\xa6\x13\x04\xd3\xfd\xed\xfd\xed\x134\x13\xa6\x13\x13\x02\xe6\x134\x13\x02\xe6\x13\x13\xa6\x134\x00\x00\x00\x00\x01\x00Z\xff\x9a\x04f\x05\xe6\x00\x14\x00\x00\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x16\x14\x04S\xfd\x1a\x134\x13\xa6\x13\x13\x02\x13\xfd\xed\x13\x13\xa6\x134\x13\x02\xe6\x13\x02\x93\xfd\x1a\x13\x13\xa6\x134\x13\x02\x13\x02\x13\x134\x13\xa6\x13\x13\xfd\x1a\x134\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x00/\x00\x00\x0154&#!\x114&+\x01\"\x06\x15\x11!\"\x06\x1d\x01\x14\x163!\x11\x14\x16;\x01265\x11!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xc0&\x1a\xff\x00&\x1a\x80\x1a&\xff\x00\x1a&&\x1a\x01\x00&\x1a\x80\x1a&\x01\x00\x1a&\x01@\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02@\x80\x1a&\x01\x00\x1a&&\x1a\xff\x00&\x1a\x80\x1a&\xff\x00\x1a&&\x1a\x01\x00&\x01+\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00\x00\x0154&#!\"\x06\x1d\x01\x14\x163!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xc0&\x1a\xfd\x00\x1a&&\x1a\x03\x00\x1a&\x01@\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02@\x80\x1a&&\x1a\x80\x1a&&\x01+\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00+\x007\x00\x00\x014/\x017654/\x01&#\"\x0f\x01'&#\"\x0f\x01\x06\x15\x14\x1f\x01\a\x06\x15\x14\x1f\x01\x1632?\x01\x17\x1632?\x016\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04}\x13\xb5\xb5\x13\x13Z\x13\x1b\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x13\xb5\xb5\x13\x13Z\x13\x1b\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x01\x83\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\x9e\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x13\xb5\xb5\x13\x13Z\x13\x1b\x1a\x13\xb5\xb5\x13\x1a\x1b\x13Z\x13\x13\xb5\xb5\x13\x13Z\x13\x01\xce\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x17\x00#\x00\x00\x014/\x01&\"\a\x01'&\"\x0f\x01\x06\x15\x14\x17\x01\x16327\x01>\x01\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x04\x12[\x134\x13\xfeh\xe2\x134\x13[\x12\x12\x01j\x13\x1a\x1b\x13\x02\x1f\x12\xfc\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03\"\x1c\x12Z\x13\x13\xfei\xe2\x13\x13Z\x12\x1c\x1b\x12\xfe\x96\x13\x13\x02\x1f\x12J\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00:\x00F\x00\x00%54&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x014.\x01#\"\a\x06\x1f\x01\x1632767632\x16\x15\x14\x06\a\x0e\x01\x1d\x01\x14\x16;\x01265467>\x04$\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x00o\xa6W\xf3\x80\x0f\x17\x84\a\f\x10\t5!\"40K(0?i\x12\x0e\xc0\x0e\x12+! \":\x1f\x19\x01\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xa0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x02\xaeX\x96R\xd5\x18\x12d\x06\fD\x18\x184!&.\x16\x1cuC$\x0e\x12\x12\x0e\x13=\x13\x12\x151/J=\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1e\x00.\x00:\x00\x00%54&+\x01\x114&#!\"\x06\x1d\x01\x14\x16;\x01\x11#\"\x06\x1d\x01\x14\x163!26\x0354&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00\x12\x0e`\x12\x0e\xfe\xc0\x0e\x12\x12\x0e``\x0e\x12\x12\x0e\x01\xc0\x0e\x12\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x02\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xa0\xa0\x0e\x12\x02\x00\x0e\x12\x12\x0e\xa0\x0e\x12\xfe\xc0\x12\x0e\xa0\x0e\x12\x12\x03\x8e\xa0\x0e\x12\x12\x0e\xa0\x0e\x12\x12\xc1\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00/\x00_\x00\x00\x01#\"&=\x0146;\x01.\x01'\x15\x14\x06+\x01\"&=\x01\x0e\x01\a32\x16\x1d\x01\x14\x06+\x01\x1e\x01\x17546;\x012\x16\x1d\x01>\x01\x01\x15\x14\x06+\x01\x0e\x01\a\x15\x14\x06+\x01\"&=\x01.\x01'#\"&=\x0146;\x01>\x017546;\x012\x16\x1d\x01\x1e\x01\x1732\x16\x04\xadm\x1a&&\x1am \xa1l&\x1a\x80\x1a&l\xa1 m\x1a&&\x1am \xa1l&\x1a\x80\x1a&l\xa1\x01s&\x1a\x8f%\xeb\xa1&\x1a\x80\x1a&\xa1\xeb%\x8f\x1a&&\x1a\x8f%\xeb\xa1&\x1a\x80\x1a&\xa1\xeb%\x8f\x1a&\x02\x00&\x1a\x80\x1a&l\xa1 m\x1a&&\x1am \xa1l&\x1a\x80\x1a&l\xa1 m\x1a&&\x1am \xa1\x01,\x80\x1a&\xa1\xeb%\x8f\x1a&&\x1a\x8f%\xeb\xa1&\x1a\x80\x1a&\xa1\xeb%\x8f\x1a&&\x1a\x8f%\xeb\xa1&\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x00/\x00;\x00\x00\x01\a\x06\"/\x01\a\x06\"/\x01&4?\x01'&4?\x0162\x1f\x01762\x1f\x01\x16\x14\x0f\x01\x17\x16\x146\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04I\x92\n\x1a\n\x89\x89\n\x1a\n\x92\n\n\x89\x89\n\n\x92\n\x1a\n\x89\x89\n\x1a\n\x92\n\n\x89\x89\n͒\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01ɒ\n\n\x89\x89\n\n\x92\n\x1a\n\x89\x89\n\x1a\n\x92\n\n\x89\x89\n\n\x92\n\x1a\n\x89\x89\n\x1a\x19\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00,\x00\x00\t\x01\x06\"'\x01&4?\x0162\x1f\x01\x0162\x1f\x01\x16\x14\x16\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x93\xfeZ\x134\x13\xfe\xda\x13\x13f\x134\x13\x93\x01\x13\x134\x13f\x13z\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xd3\xfeZ\x13\x13\x01&\x134\x13f\x13\x13\x93\x01\x13\x13\x13f\x134\xfa\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x85\x00\t\x00\x12\x00\"\x00\x00\x014'\x01\x1632>\x02\x05\x01&#\"\x0e\x01\x15\x14\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x05 W\xfd\x0e\x89\xa0oɒV\xfc\x19\x02\U000c7954\xfa\x92\x05 z\xcd\xfe\xe3\xfe\xc8\xfe\xe3\xcdzz\xcd\x01\x1d\x018\x01\x1d\xcd\x02\x83\xa1\x86\xfd\x0fYW\x92˼\x02\xf2[\x92\xfc\x94\xa2\x01?\xfe\xc6\xfe\xe2\xcezz\xce\x01\x1e\x01:\x01\x1d\xcezz\xce\x00\x00\x01\x00@\xff5\x06\x00\x05K\x00 \x00\x00\x01\x15\x14\x06#!\x01\x16\x14\x0f\x01\x06#\"'\x01&547\x01632\x1f\x01\x16\x14\a\x01!2\x16\x06\x00A4\xfd@\x01%&&K%54'\xfdu%%\x02\x8b&54&K&&\xfe\xdb\x02\xc04A\x02\x80\x805K\xfe\xda$l$L%%\x02\x8c%54'\x02\x8a&&J&j&\xfe\xdbK\x00\x00\x01\x00\x00\xff5\x05\xc0\x05K\x00 \x00\x00\x01\x14\a\x01\x06#\"/\x01&47\x01!\"&=\x01463!\x01&4?\x01632\x17\x01\x16\x05\xc0%\xfdu'43'K&&\x01%\xfd@4AA4\x02\xc0\xfe\xdb&&K&45&\x02\x8b%\x02@6%\xfdu%%K&j&\x01%K5\x805K\x01&$l$K&&\xfdu#\x00\x00\x01\x005\xff\x80\x06K\x05@\x00!\x00\x00\x01\x14\x0f\x01\x06#\"'\x01\x11\x14\x06+\x01\"&5\x11\x01\x06\"/\x01&547\x01632\x17\x01\x16\x06K%K&56$\xfe\xdaK5\x805K\xfe\xda$l$K&&\x02\x8b#76%\x02\x8b%\x0253'K&&\x01%\xfd@4AA4\x02\xc0\xfe\xdb&&K&45&\x02\x8b%%\xfdu'\x00\x00\x00\x00\x01\x005\xff\xb5\x06K\x05\x80\x00\"\x00\x00\x01\x14\a\x01\x06#\"'\x01&54?\x01632\x17\x01\x1146;\x012\x16\x15\x11\x01632\x1f\x01\x16\x06K%\xfdu'45%\xfdu&&J'45%\x01&L4\x804L\x01&%54'K%\x02\xc05%\xfdt%%\x02\x8c$65&K%%\xfe\xda\x02\xc04LL4\xfd@\x01&%%K'\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\xc0\x00,\x00\x00\x00\x14\a\x01\x06\"&5\x11#\"\x0e\x05\x15\x14\x17\x14\x16\x15\x14\x06#\"'.\x02'\x02547\x12!3\x11462\x17\x01\a\x00\x13\xfe\x00\x134&\xe0b\x9b\x99qb>#\x05\x05\x11\x0f\x10\f\a\f\x0f\x03\u007f5\xa2\x02\xc9\xe0&4\x13\x02\x00\x03\x9a4\x13\xfe\x00\x13&\x1a\x01\x00\f\x1f6Uu\xa0e7D\x06#\t\x0f\x14\x11\t\x1a\"\a\x01\x1d\xa6dž\x01\x93\x01\x00\x1a&\x13\xfe\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x17\x00/\x00\x00\x00\x14\a\x01\x17\x16\x14\x06#!\"&5\x11462\x1f\x01\x0162\x1f\x01\x01\x11\x14\x06\"/\x01\x01\x06\"/\x01&47\x01'&463!2\x16\x02\xf3\n\xfe\xb4\x90\x13&\x1a\xfe@\x1a&&4\x13\x90\x01L\n\x1a\nr\x03\x17&4\x13\x90\xfe\xb4\n\x1a\nr\n\n\x01L\x90\x13&\x1a\x01\xc0\x1a&\x01\xed\x1a\n\xfe\xb4\x90\x134&&\x1a\x01\xc0\x1a&\x13\x90\x01L\n\nr\x03I\xfe@\x1a&\x13\x90\xfe\xb4\n\nr\n\x1a\n\x01L\x90\x134&&\x00\x00\x00\x00\x02\x00\r\xff\x8d\x05\xf3\x05s\x00\x17\x00/\x00\x00\x01\x11\x14\x06\"/\x01\x01\x06\"/\x01&47\x01'&463!2\x16\x00\x14\a\x01\x17\x16\x14\x06#!\"&5\x11462\x1f\x01\x0162\x1f\x01\x03\x00&4\x13\x90\xfe\xb4\n\x1a\nr\n\n\x01L\x90\x13&\x1a\x01\xc0\x1a&\x02\xf3\n\xfe\xb4\x90\x13&\x1a\xfe@\x1a&&4\x13\x90\x01L\n\x1a\nr\x02@\xfe@\x1a&\x13\x90\xfe\xb4\n\nr\n\x1a\n\x01L\x90\x134&&\x02\x93\x1a\n\xfe\xb4\x90\x134&&\x1a\x01\xc0\x1a&\x13\x90\x01L\n\nr\x00\x00\x00\x00\x01\x00\x00\x00\x00\x05\x80\x05\x80\x00#\x00\x00\x01\x15\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!\x1146;\x012\x16\x15\x11!2\x16\x05\x808(\xfe`8(\xc0(8\xfe`(88(\x01\xa08(\xc0(8\x01\xa0(8\x03 \xc0(8\xfe`(88(\x01\xa08(\xc0(8\x01\xa0(88(\xfe`8\x00\x00\x00\x00\x01\x00\x00\x02\x00\x05\x80\x03\x80\x00\x0f\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x05\x808(\xfb@(88(\x04\xc0(8\x03 \xc0(88(\xc0(88\x00\x00\x01\x00z\xff\x80\x06\x06\x05\x80\x005\x00\x00\x01\x1e\x01\x0f\x01\x0e\x01'%\x11\x14\x06+\x01\"&5\x11\x05\x06&/\x01&67-\x01.\x01?\x01>\x01\x17\x05\x1146;\x012\x16\x15\x11%6\x16\x1f\x01\x16\x06\a\x05\x05\xca.\x1b\x1a@\x1ag.\xfe\xf6L4\x804L\xfe\xf6.g\x1a@\x1a\x1b.\x01\n\xfe\xf6.\x1b\x1a@\x1ag.\x01\nL4\x804L\x01\n.g\x1a@\x1a\x1b.\xfe\xf6\x01\xe6\x1ag.n.\x1b\x1a\x99\xfe\xcd4LL4\x013\x99\x1a\x1b.n.g\x1a\x9a\x9a\x1ag.n.\x1b\x1a\x99\x0134LL4\xfe͙\x1a\x1b.n.g\x1a\x9a\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x1b\x00-\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x03\x134'&+\x01\"\a\x06\x15\x13\x14\x16;\x0126\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x02\xb2\x12\r\xc0\r\x14\x14\r\xc0\r\x12\x02\x12\n\n\x0e\xdc\x0e\n\n\x11\x14\x0e\xb9\x0e\x13\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfb\xef\xbe\x0e\x13\x14\r\xbe\r\x14\x13\x01f\x02m\f\x06\b\b\x06\f\xfd\x93\n\x0f\x0f\x00\x00\x00\x04\x00\x00\x00\x00\x06\x00\x05@\x00\r\x00\x16\x00\x1f\x00J\x00\x00%5\x115!\x15\x11\x15\x14\x16;\x0126\x013'&#\"\x06\x14\x16$4&#\"\x0f\x0132\x05\x11\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&5\x11463!\"&4632\x1f\x017632\x16\x14\x06#!2\x16\x03\xa0\xfe\xc0$\x1c\xc0\x1c$\xfe8\xc3~\x1a+(88\x02\xd88(+\x1a}\xc2(\x01\xb0\x12\x0e`8(\xfb\xc0(8`\x0e\x12\x12\x0e\x01\xb8]\x83\x83]k=\x80\x80=k]\x83\x83]\x01\xb8\x0e\x12\xb48\x01\xd4\xc0\xc0\xfe,8\x19\x1b\x1b\x03e\xa1\x1f8P88P8\x1f\xa1\xa0\xfe\xc0\x0e\x12\xfe`(88(\x01\xa0\x12\x0e\x01@\x0e\x12\x83\xba\x83M\xa5\xa5M\x83\xba\x83\x12\x00\x02\x00\x00\x00\x00\a\x00\x05\x80\x00\x15\x00N\x00\x00\x004&#\"\x04\x06\a\x06\x15\x14\x16327>\x0176$32\x01\x14\a\x06\x00\a\x06#\"'.\x01#\"\x0e\x02#\"&'.\x0354>\x0254&'&54>\x027>\x047>\x0432\x1e\x02\x05\x00&\x1a\xac\xfe\xdc\xe3z\x13&\x1a\x18\x15\x1b^\x14\x89\x01\a\xb6\x1a\x02&\x14.\xfe\xeb\xdb\xd6\xe0\x94\x8a\x0f\x92\x17\x10/+>\x1d+)\x19\x02\b\x03\x03>J>\x1c\x02\tW\x97\xbem7\xb4\xb3\xb2\x95'\n'\x14\"'\x18'? \x10\x03&4&c\xa9\x87\x15\x18\x1a&\x13\x18^\x13|h\x01\x06_b\xe0\xfe\xc2ml/\x05J@L@#*\x04\x0e\x06\r\a#M6:\x13\x04D\n35sҟw$\x12\x0f\x03\t'%\n'\x11\x17\t\\\x84t\x00\x00\x00\x00\x02\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x003\x00\x00\x05\x15\x14\x06#!\"&=\x01463!2\x16\x01\x14\x0e\x05\x15\x14\x17'\x17.\x0454>\x0554'\x17'\x1e\x04\x05\x80\x13\r\xfa\xc0\r\x13\x13\r\x05@\r\x13\xff\x001O``O1C\x04\x01Z\x8c\x89Z71O``O1B\x03\x01Z\x8c\x89Z7\xa0@\r\x13\x13\r@\r\x13\x13\x04\x13N\x84]SHH[3`\x80\x01\x01)Tt\x81\xacbN\x84]SHH[3^\x82\x01\x01)Tt\x81\xac\x00\x00\x00\x00\x03\x00\x00\x00\x00\a\x00\x04\x80\x00\x11\x00!\x001\x00\x00\x01&'\x16\x15\x14\x00 \x00547\x06\a\x16\x04 $\x004&#\"\x06\x15\x14\x162654632\x00\x14\a\x06\x00 \x00'&476\x00 \x00\x17\x06\x80\x98\xe5=\xfe\xf9\xfe\x8e\xfe\xf9=嘅\x01\x91\x01\xd4\x01\x91\xfd\xb5\x1c\x14}\xb3\x1c(\x1czV\x14\x03l\x14\x8c\xfe'\xfd\xf2\xfe'\x8c\x14\x14\x8c\x01\xd9\x02\x0e\x01ٌ\x02@\xecuhy\xb9\xfe\xf9\x01\a\xb9yhu\xec\xcd\xf3\xf3\x029(\x1c\xb3}\x14\x1c\x1c\x14Vz\xfe\xd2D#\xe6\xfe\xeb\x01\x16\xe5#D#\xe5\x01\x16\xfe\xea\xe5\x00\x05\x00\x00\xff\xa0\a\x00\x04\xe0\x00\t\x00\x19\x00=\x00C\x00U\x00\x00%7.\x01547\x06\a\x12\x004&#\"\x06\x15\x14\x162654632%\x14\a\x06\x00\x0f\x01\x06#\"'&547.\x01'&476\x00!2\x177632\x1e\x03\x17\x16\x13\x14\x06\a\x01\x16\x04\x14\a\x06\a\x06\x04#76$7&'7\x1e\x01\x17\x02+NWb=嘧\x02\x89\x1c\x14}\xb3\x1c(\x1czV\x14\x01\x87\x01j\xfe\\i1\n\x12\fz\x10,\x8f\xf1X\x14\x14\x99\x01\xc6\x01\rY[6\n\x12\x05\x1a$\x1e!\x03\x10%\x9e\x82\x01\x18\b\x01\xc0\x14'F\x96\xfeu\xdeJ\xd4\x01iys\xa7?_\xaf9ɍ?\xc0kyhu\xec\xfe\xfe\x02n(\x1c\xb3}\x14\x1c\x1c\x14Vz\xef\a\x02\xbd\xfd\f\xbcY\x10F\n\x12\fKA؉\x1fL\x1f\xeb\x01\x10\x11a\x10\f\x13\x12\x13\x02\n\xfe0\x8b\xe52\x01\xf6-\x84F\"@Q\xac\xbe\x84\x12\uef33sp@\xb2_\x00\x00\x00\x00\x03\x00\x10\xff\x80\x06\xf0\x06\x00\x00\x0f\x00!\x003\x00\x00%54&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x03\x134'&+\x01\"\a\x06\x15\x13\x14\x16;\x0126\x03\x01\x16\a\x0e\x01#!\"&'&7\x01>\x012\x16\x04\x00\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x02\x12\n\r\v\xdc\v\r\n\x11\x14\x0e\xb9\x0e\x13\r\x03\x00#%\x11;\"\xfa\x00\";\x11%#\x03\x00\x11\x01\x05`,@L\xa1\xa0\x05\x11\x80\a\f\x04\x03\x0f\x06\xfe\xe9\xfe\xfd5\x05\r`\t\x0e\x02\x0f\t\xbd\xfc\v\x02\x01\n`\t\x0e\x06\x02\xc2\x01\x03\xfe\x04\x0e\x03\x02\v\x80\x0e\x10\x02\x99\xa0L\xc0\x05`4\xc0L\xa1\xfdH\x13\x0e`\x06\x01\x03\r\x01\xfc\xfe\xfd\xc2\x11\x0e`\t\x02\v\xfc\xbd\a\x10\r\fa\t\x015\x01\x03\x01\x17\b\x10\x10\v\x80\r\x05\x9f\xa0L@\x00\x0f\x00\x00\xff\x00\x06\x80\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x1f\x00#\x003\x007\x00;\x00?\x00O\x00s\x00\x00\x17!\x11!\x01!\x11!%!\x11!\x01!\x11!%!\x11!\x01!\x11!\x01!\x11!\x01!\x11!%!\x11!\x01\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126\x01!\x11!%!\x11!\x01!\x11!7\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x11\x14\x06#!\"&5\x1146;\x01546;\x012\x16\x1d\x01!546;\x012\x16\x1d\x0132\x16\x80\x01 \xfe\xe0\x01`\x01@\xfe\xc0\xfe\xa0\x01 \xfe\xe0\x01`\x01@\xfe\xc0\xfe\xa0\x01 \xfe\xe0\x02\xe0\x01@\xfe\xc0\xfe\x80\x01@\xfe\xc0\x03\x00\x01 \xfe\xe0\xfe\x80\x01@\xfe\xc0\xfe\xa0\x13\r@\r\x13\x13\r@\r\x13\x02\xe0\x01 \xfe\xe0\xfe\x80\x01@\xfe\xc0\x01\x80\x01 \xfe\xe0 \x13\r@\r\x13\x13\r@\r\x13\x01\x80L4\xfa\x804LL4\x80^B@B^\x01\x80^B@B^\x804L\x80\x01 \xfe\xe0\x01 @\x01@\xfe\xc0\x01@@\x01 \xfc\x00\x01 \x01\xc0\x01 \xfc\x00\x01 @\x01@\x02 \x01 \r\x13\x13\r\xfe\xe0\r\x13\x13\xfc\xad\x01@@\x01 \xfe\xe0\x01 \xc0\x01 \r\x13\x13\r\xfe\xe0\r\x13\x13M\xfb\x004LL4\x05\x004L`B^^B``B^^B`L\x00\x00\x00\x03\x00\x00\xff\xa0\a\x00\x05\xe0\x00\x12\x007\x00q\x00\x00\x01\x06\a.\x04+\x01\"&=\x0146;\x012\x00\x14\a\x01\x06#\"&=\x01\"\x0e\x01.\x06'67\x1e\x043!54632\x17\x01\x12\x14\a\x01\x06#\"&=\x01!\"\x0e\x02\a\x06\a\x0e\x06+\x01\"&=\x0146;\x012>\x02767>\x063!54632\x17\x01\x02\x9amBZxPV3!\x12\x0e\xc0\x0e\x12\x1emBZxPV3!\xc0\x0e\x12\n\xfe\xc1\x00\x00\x00\x01\x00\x00\xff\x00\a\x00\x05\x00\x00&\x00\x00\x00\x10\x02\x04#\"'\x06\x05\x06\a\x06&'5&6&>\x027>\x057&\x0254>\x01$32\x04\a\x00\xf0\xfed\xf4FK\xc6\xfe\xfa1A\x11\x1b\x04\x03\x05\x01\n\x02\f\x02\a0\x15)\x18\x1e\v\x9d\xb5\x8e\xf0\x01L\xb6\xf4\x01\x9c\x03.\xfe\xa4\xfe٫\b\xafC\x0e\b\x02\x16\x12\x01\x04\x10\x04\x0f\x03\x0e\x02\b5\x178.H(Y\x01\x06\x96\x82\xed\xace\xab\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00C\x00\x00\x01\x15\x14\x02\x04 $\x02=\x01463!2\x16\x1d\x01\x14\x1e\x032>\x03=\x01463!2\x16\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00\xc5\xfe\xa1\xfeH\xfe\xa1\xc5&\x1a\x01\x80\x1a&/\x027\x03#\"&463!2\x1e\x04\x17!2\x16\x02\x80LhLLh\x03\xccLhLLh\xcc!\x18\xfb\xec\r\x18\x03\x98\x1a&&\x1a\xfc\x00\x1a&\x10\x10\x1b\x02\xb1\xcc\x1a&&\x1a\x01\x00\x10\x19\x0e\f\x04\a\x01\x04\xb1\x1a&4hLLhLLhLLhL\x03\xc0\xfe\x00\x18%\x03z<\n\x100&4&&\x1a\v)\x1f1\x05\x037&4&\r\x12\x1f\x15&\a&\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x80\x05\x80\x00\x14\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x1d\x01!2\x16\x06\x80\x84\\\xfb@\\\x84\x84\\\x01@\\\x84\x02\xa0\\\x84\x03\xa0\xfd@\\\x84\x84\\\x03\xc0\\\x84\x84\\ \x84\x00\x00\x00\x00\x02\x00\x00\x00\x00\aW\x05\x80\x00\x13\x00*\x00\x00\x01\x14\a\x01\x0e\x01#!\"&547\x01>\x013!2\x16\x01\x15!\"\x06\a\x01\a4&5\x11463!2\x16\x1d\x01!2\x16\aW\x1f\xfe\xb0+\x9bB\xfb\xc0\"5\x1f\x01P+\x9bB\x04@\"5\xfe\xa9\xfc\xc0^\xce=\xfe\xaf\x05\x01\x84\\\x01@\\\x84\x02 \\\x84\x02H\x1f#\xfet3G\x1a\x1e\x1f#\x01\x8c3G\x1a\x01:\xa0_H\xfet\x06\x04\x11\x04\x03\xc0\\\x84\x84\\ \x84\x00\x00\x00\x01\x00@\xff\x00\x02\xc0\x06\x00\x00\x1f\x00\x00\x00\x14\x06+\x01\x1132\x16\x14\a\x01\x06\"'\x01&46;\x01\x11#\"&47\x0162\x17\x01\x02\xc0&\x1a\x80\x80\x1a&\x13\xff\x00\x134\x13\xff\x00\x13&\x1a\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x04\xda4&\xfc\x00&4\x13\xff\x00\x13\x13\x01\x00\x134&\x04\x00&4\x13\x01\x00\x13\x13\xff\x00\x00\x00\x00\x01\x00\x00\x01@\a\x00\x03\xc0\x00\x1f\x00\x00\x00\x14\a\x01\x06\"&=\x01!\x15\x14\x06\"'\x01&47\x0162\x16\x1d\x01!5462\x17\x01\a\x00\x13\xff\x00\x134&\xfc\x00&4\x13\xff\x00\x13\x13\x01\x00\x134&\x04\x00&4\x13\x01\x00\x02\x9a4\x13\xff\x00\x13&\x1a\x80\x80\x1a&\x13\x01\x00\x134\x13\x01\x00\x13&\x1a\x80\x80\x1a&\x13\xff\x00\x00\x00\x00\x05\x00\x00\xff\x80\b\x00\x05\x80\x00\x03\x00\a\x00\r\x00\x11\x00\x15\x00\x00\x01\x11!\x11\x01\x11!\x11\x01\x15!\x113\x11\x01\x11!\x11\x01\x11!\x11\x02\x80\xff\x00\x02\x80\xff\x00\x05\x00\xf8\x00\x80\x05\x00\xff\x00\x02\x80\xff\x00\x02\x80\xfe\x00\x02\x00\x02\x00\xfc\x00\x04\x00\xfb\x80\x80\x06\x00\xfa\x80\x03\x80\xfd\x00\x03\x00\x01\x80\xfb\x80\x04\x80\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x000\x00@\x00\x00\x01\x06\a67\x06\a&#\"\x06\x15\x14\x17.\x01'\x06\x15\x14\x17&'\x15\x14\x16\x17\x06#\"'\x1e\x01\x17\x06#\"'\x1632>\x0354'6\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x008AD\x19AE=\\W{\x05\x81\xe2O\x1d[/5dI\x1d\x16\r\x1a\x15kDt\x91\x1a\x18\x94\xaepČe1\x01?\x01*\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\x9e\x19\t(M&\rB{W\x1d\x13\ata28r=\x01\x19\x02Ku\x0e\b\x04?R\x01Z\x03^Gw\x9b\xa9T\x12\t-\x01\x02\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00$\x00\x00\x012\x16\x15\x11\x14\x06+\x01\x1137#546375&#\"\x06\x1d\x01#\x153\x11!\"&5\x11463\x04\xe0w\xa9\xa9w\xbc\xc7\x1e\xe5/Dz?s\x88\xa3\xc8\xc8\xfd\xecw\xa9\xa9w\x05\x80\xa9w\xfc@w\xa9\x02S\xe8\x9488\x01\xcf\t\xa0\x92\xab\xe8\xfd\xad\xa9w\x03\xc0w\xa9\x00\x00\x00\x00\a\x00\x00\xff\x80\a\x00\x05\x80\x00\x0f\x00\x17\x00\x1b\x00#\x00'\x00.\x00>\x00\x00\x004&#\"\x06\x15\x14\x1626546326\x14\x06\"&462\x01!5!\x00\x10& \x06\x10\x16 \x01!5!\x03!=\x01!\a!%\x11\x14\x06#!\"&5\x11463!2\x16\x03\xa0\x12\x0eB^\x12\x1c\x128(\x0e\xf2\x96Ԗ\x96\xd4\xfc\x96\x06\x00\xfa\x00\x04\x80\xe1\xfe\xc2\xe1\xe1\x01>\xfc\xe1\x01\x80\xfe\x80\x80\x06\x00\xfc\xc4@\xfd|\x06\x80K5\xfa\x005KK5\x06\x005K\x02\xb2\x1c\x12^B\x0e\x12\x12\x0e(8\bԖ\x96Ԗ\xfc\u0080\x01\x1f\x01>\xe1\xe1\xfe\xc2\xe1\x04\x02\x80\xfe\xc0v\x8a\x80\x80\xfb\x005KK5\x05\x005KK\x00\x02\x00\x00\xffH\x06\x93\x05\x80\x00\x15\x00G\x00\x00\x004&\"\x06\x15\x14\x17&#\"\x06\x14\x162654'\x1632\x01\x14\x06#\".\x02'\a\x17\x16\x15\x14\x06#\"'\x01\x06#\"&54\x12$32\x16\x15\x14\a\x017.\x0354632\x17\x1e\x04\x03@p\xa0p\x13)*Ppp\xa0p\x13)*P\x03\xc3b\x11\t'\"+\x03`\xdc\x1cN*(\x1c\xfda\xb0\xbd\xa3;\x012\xa0\xa3̓\x01c`\x03.\" b\x11\r\n\x06PTY9\x03\xb0\xa0ppP*)\x13p\xa0ppP*)\x13\xfe\x00\x11b \".\x03`\xdc\x1c(*N\x1c\x02\x9f\x83ͣ\xa0\x012\xbeͣ\xbd\xb0\xfe\x9d`\x03+\"'\t\x11b\n\x06MRZB\x00\x00\x00\x00\x06\x00\x00\xff\x0f\a\x80\x05\xf0\x00\a\x00\x11\x00\x1b\x00\u007f\x00\xbd\x00\xfb\x00\x00\x004&\"\x06\x14\x162\x014&\"\x06\x15\x14\x1626\x114&\"\x06\x15\x14\x1626\x01\x15\x14\x06\x0f\x01\x06\a\x16\x17\x16\x15\x14\a\x0e\x01#\"/\x01\x06\a\x06\a\x06+\x01\"&/\x01&'\a\x06#\"'&547>\x017&/\x01.\x01=\x0146?\x0167&'&547>\x0132\x1f\x0167676;\x012\x16\x1f\x01\x16\x177632\x17\x16\x15\x14\a\x0e\x01\a\x16\x1f\x01\x1e\x01\x01\x15\x14\a\x06\a\x16\x15\x14\a\x06#\"&'\x06\"'\x0e\x01#\"'&547&'&=\x014767&547>\x0232\x16\x1762\x176?\x012\x17\x16\x15\x14\a\x16\x17\x16\x11\x15\x14\a\x06\a\x16\x15\x14\a\x06#\"&'\x06\"'\x0e\x01#\"'&547&'&=\x014767&547>\x0232\x16\x1762\x176?\x012\x17\x16\x15\x14\a\x16\x17\x16\x03\x80\x96Ԗ\x96\xd4\x03\x96LhLKjKLhLKjK\xfe\x80\x0e\t\x9b\v\x15\"8\a\a\x17w\x13\v\ns%(\v\f\a\x17\xba\v\x12\x01\x17\")v\a\r\v\n\x90\a\n>\x10\x17\f\x98\n\x0e\x0e\t\x9b\v\x15\"8\a\a\x16x\x13\v\ns\"+\v\f\a\x17\xba\v\x12\x01\x17\")v\b\f\v\n\x90\a\f<\x0f\x17\v\x98\n\x0e\x02\x80\x95\f\x123\x04z\x02\bL\x0e\x14\x14\x14\x0eL\b\x02z\x043\x12\f\x95\x95\r\x113\x04\x04>8\x02\bL\x0e\x14\x14\x143)\x06\x04x\x043\x11\r\x95\x95\f\x123\x04z\x02\bL\x0e\x14\x14\x14\x0eL\b\x02z\x043\x12\f\x95\x95\r\x113\x04\x04>8\x02\bL\x0e\x14\x14\x143)\x06\x04x\x043\x11\r\x95\x02\x16Ԗ\x96Ԗ\xff\x004LL45KK\x0454LL45KK\xfe\x90\xb9\n\x13\x01\x18#)0C\v\t\f\a\x1ew\aZ\x13\fl/\x18\x0f\n\x99\n\x15Y\a\b\x85\x1b\t\n\x0eN\x16,&\x18\x01\x11\v\xb9\n\x13\x01\x18#)0C\v\t\f\b\x1ev\aZ\x12\x0el.\x18\x0f\n\x99\n\x15Y\a\b\x85\x1b\b\v\x10L\x160\"\x17\x02\x11\xfd\xe0\x8c\x10\x0f\x1b\x19q\x19\x04\x03G^\x15\x02\x02\x15^G\x03\x04\x19q\x19\x1b\x0f\x10\x8c\x10\x0f\x1d\x17q\x19\x04\x03\x02$ ]\x15\x02\x02G)\x02F\x03\x04\x19q\x17\x1d\x0f\x03\xf0\x8c\x10\x0f\x1b\x19q\x19\x04\x03G^\x15\x02\x02\x15^G\x03\x04\x19q\x19\x1b\x0f\x10\x8c\x10\x0f\x1d\x17q\x19\x04\x03\x02$ ]\x15\x02\x02G)\x02F\x03\x04\x19q\x17\x1d\x0f\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x00\x00%\x00O\x00\x00\x00\x10\x06\x04#\"'\x06\a\x06\a#\"&'&4>\x057>\x047.\x01546$ \x04\x01\x14\x06\a\x1e\x04\x17\x1e\x06\x14\a\x0e\x01'&'&'\x06# '\x1632$7>\x0154'\x1e\x01\x05\x80\xbc\xfe\xbb\xbfVZ|\x9a$2\x03\v\x13\x02\x01\x01\x03\x02\x05\x03\x06\x01\x05$\x10\x1d\x15\n|\x8e\xbc\x01E\x01~\x01E\x02<\x8e|\n\x15\x1d\x10$\x05\x01\x06\x03\x05\x02\x03\x01\x01\x03\x14\f2$\x9a|ZV\xfe\xf1\xc9:\x1e\xa1\x01(t}\x86\x17\x81\x96\x03\x8b\xfe\xea\xec\x89\x10X(\t\a\x10\r\x03\a\x06\x06\x04\a\x03\a\x01\x06&\x15%(\x18H\xd2w\x8b쉉\xfd\x89x\xd1H\x18(%\x15&\x06\x01\a\x03\a\x04\x06\x06\a\x03\x0e\x10\x01\a\t(X\x10\x84\x04ZT\\\xf0\x86MKG\xd6\x00\x00\x03\x00\x00\xff\x80\x06\x00\x06\x00\x00\a\x00<\x00m\x00\x00$4&\"\x06\x14\x162\x014&#!4654&#\x0e\x02\a\x06\a\x0e\x06+\x01\x1132\x1e\x04\x17\x16;\x01254'>\x014'654&'>\x017\x14\a\x16\x15\x14\a\x16\x15\x14\a\x16\x06+\x02\"&'&#!\"&5\x11463!6767>\x027632\x1e\x01\x15\x14\a32\x16\x01\x00&4&&4\x04\xa6N2\xfe\xa0`@`\x1a\x18%)\x167\x04&\x19,$)'\x10 \r%\x1d/\x170\x05Ӄy\xc0\x05\x1e#\x125\x14\x0f +\x801\t&\x03<\x01\xac\x8d$]`\xbb{t\x16\xfe\xe05KK5\x01\x12$e:1\x18\x17&+'3T\x86F0\xb0h\x98\xa64&&4&\x02\x803M:\xcb;b^\x1av\x85+\x17D\x052 5#$\x12\xfd\x80\x06\a\x0f\b\x11\x02I\xa7\x1a\x1e\x10IJ 2E\x19=\x11\x01\\$YJ!$MC\x15\x16eM\x8b\xa1-+(K5\x02\x805K\x18\x83K5\x19y\x84*%A\x8au]c\x98\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x05\x80\x00\a\x00>\x00q\x00\x00\x004&\"\x06\x14\x162\x014&'>\x0154'654&'654&+\x01\"\a\x0e\x05+\x01\x1132\x1e\x05\x17\x16\x17\x1e\x02\x172654&5!267\x14\x06+\x01\x16\x15\x14\a\x0e\x01#\"'.\x03'&'&'!\"&5\x11463!27>\x01;\x012\x16\a\x15\x16\x15\x14\a\x16\x15\x14\a\x16\x01\x00&4&&4\x04\xa6+ \x0f\x145\x12#\x1e\x05bW\x80\x83\xd3\x050\x17/\x1d%\r \x10')$,\x19&\x047\x16)%\x18\x1a`@`\x01`2N\x80\x98h\xb00##\x86T3'\"(\v\x18\x130;e$\xfe\xee5KK5\x01 \x16t\x80\xbeip\x8c\xad\x01<\x03&\t1\x04&4&&4&\xfe\x00#\\\x01\x11=\x19E2\x1f&%I\x10\x1e\x1aURI\x02\x11\b\x0f\a\x06\xfd\x80\x12$#5 2\x05D\x17+\x85v\x1a^b;\xcb:M2g\x98c]vDEA%!bSV\x152M\x83\x18K5\x02\x805K(,,\x9e\x89\x05Me\x16\x15CM$!I\x00\x00\x00\x01\x00\x00\xff\xad\x03@\x05\xe0\x00\x12\x00\x00\x01\x11\x05\x06#\"&547\x13\x01&547%\x136\x03@\xfe?\x16\x12\x15\x15\x02V\xfe\x94\x198\x01\xf6\xe1\x13\x05\xe0\xfa\xc5\xec\f\x1d\x15\x06\x0e\x01\xf4\x01b\x1b\x15%\tI\x01\xc7)\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00\x1c\x009\x00\x00\x014.\x03\"\x0e\x02\a\x06\"'.\x03\"\x0e\x03\x15\x14\x17\t\x0167\x14\a\x01\x06\"'\x01.\x0454632\x1e\x02\x17>\x0332\x16\x06\x80+C`\\hxeH\x18\x12>\x12\x18Hexh\\`C+\xbb\x02E\x02D\xbc\x80\xe5\xfd\x91\x124\x12\xfd\x90\n#L\x81oP$$Po\x81>\xe0\xfe\x03\xacQ|I.\x103MC\x1c\x16\x16\x1cCM3\x10.I|Q\xa8\xbb\xfd\xd0\x02/\xbc\xa8\xdd\xe5\xfd\xa8\x12\x12\x02Z\b$_d\x8eC\xdc\xf8+I@$$@I+\xf8\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06 \x05\x00\x00(\x00@\x00\x00%\x14\x16\x0e\x02#!\"&5\x11463!2\x16\x15\x14\x16\x0e\x02#!\"\x06\x15\x11\x14\x163!:\x02\x1e\x03\x00\x14\a\x01\x06\"&5\x11!\"&5\x11463!\x11462\x17\x01\x02\x80\x02\x01\x05\x0f\r\xfe\xc0w\xa9\xa9w\x01@\r\x13\x02\x01\x05\x0f\r\xfe\xc0B^^B\x01 \x01\x14\x06\x11\x06\n\x04\x03\xa0\x13\xfd\xe0\x134&\xfe@\x1a&&\x1a\x01\xc0&4\x13\x02 `\x04 \x15\x1a\r\xa9w\x02\xc0w\xa9\x13\r\x04 \x15\x1a\r^B\xfd@B^\x02\x04\a\v\x0224\x13\xfd\xe0\x13&\x1a\x01 &\x1a\x01\x80\x1a&\x01 \x1a&\x13\xfd\xe0\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\x0f\x00%\x005\x00\x0073\x11#7.\x01\"\x06\x15\x14\x16;\x0126\x013\x114&#\"\a35#\x16\x033\x1147>\x0132\x15\x01\x11\x14\x06#!\"&5\x11463!2\x16\xed\xe7\xe7\xf6\x01FtIG9\x01;H\x02I\xe7\x92x\x88I\x02\xe7\x03\x03\xe7\a\x0f<,t\x01ԩw\xfc@w\xa9\xa9w\x03\xc0w\xa9z\x02\xb6\xd64DD43EE\xfc\xa7\x01\x8e\x9a\x9eueB\xfd\x8c\x01\x84&\x12#1\x9d\x02s\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x02\x00\x00\xff\x00\x04\x80\x05\x80\x00\v\x00.\x00\x00\x01\x114&\"\x06\x15\x11\x14\x1626\x01\x14\x06#!\x03\x0e\x01+\x01\"'\x03!\"&5463\x11\"&463!2\x16\x14\x06#\x112\x16\x01\xe0\x12\x1c\x12\x12\x1c\x12\x02\xa0&\x1a\xfeS3\x02\x11\f\x01\x1b\x05L\xfel\x1a&\x9dc4LL4\x02\x804LL4c\x9d\x02\xa0\x01\xc0\x0e\x12\x12\x0e\xfe@\x0e\x12\x12\xfe\xae\x1a&\xfe\x1d\f\x11\x1b\x01\xe5&\x1a{\xc5\x02\x00LhLLhL\xfe\x00\xc5\x00\x00\x00\x02\x00\x00\x00\x00\a\x00\x06\x00\x00'\x00?\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x1d\x01\x14\x06#!\"\x06\x15\x11\x14\x163!265\x1146;\x012\x16\x01\x11\x14\x06\"/\x01\x01\x06\"/\x01&47\x01'&463!2\x16\x05\x80\xa9w\xfc\xc0w\xa9\xa9w\x02\xc0\x0e\x12\x12\x0e\xfd@B^^B\x03@B^\x12\x0e@\x0e\x12\x01\x80&4\x13\xb0\xfdt\n\x1a\nr\n\n\x02\x8c\xb0\x13&\x1a\x02\x00\x1a&\x02`\xfe\xc0w\xa9\xa9w\x03@w\xa9\x12\x0e@\x0e\x12^B\xfc\xc0B^^B\x01@\x0e\x12\x12\x03R\xfe\x00\x1a&\x13\xb0\xfdt\n\nr\n\x1a\n\x02\x8c\xb0\x134&&\x00\x02\x00\x00\x00\x00\x06\x00\x05\x00\x00\x17\x00@\x00\x00\x00\x14\a\x01\x06\"&5\x11!\"&5\x11463!\x11462\x17\t\x01\x11\x14\x06#!\"&54&>\x023!265\x114&#!*\x02.\x0354&>\x023!2\x16\x04\xa0\x13\xfd\xe0\x134&\xfe@\x1a&&\x1a\x01\xc0&4\x13\x02 \x01s\xa9w\xfe\xc0\r\x13\x02\x01\x05\x0f\r\x01@B^^B\xfe\xe0\x01\x14\x06\x11\x06\n\x04\x02\x01\x05\x0f\r\x01@w\xa9\x02\x9a4\x13\xfd\xe0\x13&\x1a\x01 &\x1a\x01\x80\x1a&\x01 \x1a&\x13\xfd\xe0\x013\xfd@w\xa9\x13\r\x04 \x15\x1a\r^B\x02\xc0B^\x02\x04\a\v\b\x04 \x15\x1a\r\xa9\x00\x03\x00\x00\xff\x80\x06\x80\x05\x80\x00\x06\x00\r\x00I\x00\x00\x01&5!\x15\x14\x16%5!\x14\a>\x017\x15\x14\x0e\x02\a\x06\a\x0e\x01\x15\x14\x1632\x16\x1d\x01\x14\x06#!\"&=\x014632654&'&'.\x03=\x01463!5463!2\x16\x1d\x01!2\x16\x01\xcaJ\xff\x00\xbd\x04\xc3\xff\x00J\x8d\xbd\x80S\x8d\xcdq*5&\x1d=CKu\x12\x0e\xfc\xc0\x0e\x12uKC=\x1d&5*q͍S8(\x01 ^B\x02@B^\x01 (8\x02\x8d\xa2\xd1`N\xa8\xf6`Ѣ\x1d\xa8\u0380G\x90tO\x056)\"M36J[E@\x0e\x12\x12\x0e@E[J63M\")6\x05Ot\x90G\x80(8`B^^B`8\x00\x00\x00\t\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00\x1f\x00'\x00,\x002\x00\x81\x00\x91\x00\x00\x016'&\a\x06\x17\x16'&\a\x06\x17\x1676'6'&\a\x06\x17\x16\x176&'&\x06\x17\x16\x176'&\a\x06\x17\x1e\x014#\"\x147&\x06\x17\x166\x014\x00 \x00\x15\x14\x12\x17\x16654'\x0e\x02.\x01'&'.\x03632\x1e\x01\x17\x1e\x0126767.\x03547&76\x16\x1f\x0162\x17>\x02\x17\x16\a\x16\x15\x14\x0e\x03\a\x16\x15\x14\x06\x15\x14\x1676\x12\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\a\x04\a\t\x05\x04\a\t\x17\x05\a\x06\x06\a\x05\x06/\x02\a\a\x01\x03\a\b\x16\x02\x01\x03\x06\b\x05\x06[\x02\v\t\x04\x02\v\t.\f\n=\x02\x16\x02\x02\x14\x02\x82\xfe\xd4\xfeX\xfe\xd4Ě\x12\x11\x01\x06\x134,+\b\x17\"\x02\x05\v\x03\v\x0e\x06\x12*\f\x10+, \x0e\a\x1a1JH'5\x18\x1d\x13G\x19\x1a:\x8c:\v#L\x13\x1d\x185\x1c+@=&#\x01\x11\x12\x9a\xc4\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01P\x06\a\a\x05\x06\a\a.\a\x03\x04\b\b\x03\x041\x04\x04\x02\x04\x05\x03\x02\x13\x01\a\x02\a\b\a\x06G\a\x04\x03\a\a\x04\x03\x04\x10\x10\x0f\a\x04\a\b\x04\x01E\xd4\x01,\xfe\xd4ԧ\xfe\xf54\x03\x10\f4+\x01\x03\x01\t\x1f\x1a;\x0f\x01\x05\v\b\a\x04\x1b\x16\x1c\x1c\a\x06/\x16\x06\x195cFO:>J\x06\x1b\x10\x10\x11\x11\a\x16\x1e\x06J>:O9W5$\x10\x04\x1f@(b\x02\f\x10\x034\x01\v\x02\x87\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x04\x00\x00\xff\x80\x06\x80\x05\xc0\x00\a\x00\x0f\x00'\x00?\x00\x00$4&\"\x06\x14\x162$4&\"\x06\x14\x162\x13\x11\x14\x06#!\"&5\x11463!\x1e\x013!267!2\x16\x01\x06#!\x11\x14\x06#!\"&5\x11!\"'&7\x0162\x17\x01\x16\x05\x00&4&&4\x01&&4&&4\xa68(\xfa@(88(\x01\xab\x15c=\x01\x00=c\x15\x01\xab(8\xfe\xbb\x11*\xff\x00&\x1a\xff\x00\x1a&\xff\x00*\x11\x11\x1f\x01\xc0\x126\x12\x01\xc0\x1f&4&&4&&4&&4&\x01 \xfe\xc0(88(\x01@(88HH88\x02`(\xfe@\x1a&&\x1a\x01\xc0('\x1e\x01\xc0\x13\x13\xfe@\x1e\x00\x00\x00\x00\x02\x00\x00\xff\x80\x05\xff\x05\x80\x001\x00c\x00\x00\x014&'.\x0254654'&#\"\x06#\"&#\"\x0e\x01\a\x06\a\x0e\x02\x15\x14\x16\x15\x14\x06\x14\x1632632\x16327>\x01\x127\x14\x02\x06\a\x06#\"&#\"\x06#\"&54654&54>\x02767632\x1632632\x16\x15\x14\x06\x15\x14\x1e\x02\x17\x1e\x01\x05\u007f\x0e\v\f\n\b\n\n\x04\t\x13N\x14<\xe8;+gC8\x89A`\u007f1\x19\x16\x18\x16\x18a\x199\xe19\xb5g\x81\xd5w\x80\x8c\xfc\x9b|\xca9\xe28\x18a\x19Ie\x16\x19$I\x80VN\x9a\xc2z<\xe7:\x13L\x14QJ\n\x04\x03\f\x02\x10\x12\x02\xc6,\x8b\x1b\x1e\x1c-\x1a\x17[\x16%\x12\x01\t0\x17\x18\x1661I\xe9\xef\x81(\xa0)\x17W,\x1d\x16\x1f$-\xd7\x01\x14\x8b\xa5\xfe\xbb\xfb7,\x1d\x1doI\x18X\x17(\xa1)o\xd5ζA;=N0\neT\x17Z\x17\r\x18\t \x04(\x9d\x00\x00\x01\x00\x00\x00\x00\x05\x80\x05\x80\x00O\x00\x00\x01\x14\x06\a\x06\a\x06#\".\x03'&'&\x00'&'.\x0454767>\x0132\x17\x16\x17\x1e\x02\x17\x1e\x02\x15\x14\x0e\x02\x15\x14\x1e\x02\x17\x1e\x01\x17\x1e\x0332>\x0232\x1e\x01\x17\x1e\x02\x17\x16\x17\x16\x05\x80\x14\v\x15e^\\\x1b4?\x1fP\tbM\u007f\xfe\xeeO0#\x03\x1e\v\x12\a382\x19W\x1b\x0e\a\x12#\v& \x0f\x03\x1d\x0e9C9\n\a\x15\x01Lĉ\x02\"\x0e\x1b\t\x1282<\x14\x0e\x1d*\x04\x199F\x13F\x06\x03\x01(\x1bW\x19283\a\x12\v\x1e\x03#0O\x01\x12\u007fMb\tP\x1f?4\x1b\\^e\x15\v\x14\x03\x06F\x13F9\x19\x04*\x1d\x0e\x14<28\x12\t\x1b\x0e\"\x02\x89\xc4L\x01\x15\a\n9C9\x0e\x1d\x03\x0f &\v#\x12\a\x00\x00\x00\x02\x00\x00\x00\x00\x05\x80\x05\x80\x00\x0f\x00\x1f\x00\x00\x01!\"\x06\x15\x11\x14\x163!265\x114&\x17\x11\x14\x06#!\"&5\x11463!2\x16\x04`\xfc\xc0B^^B\x03@B^^ީw\xfc\xc0w\xa9\xa9w\x03@w\xa9\x05\x00^B\xfc\xc0B^^B\x03@B^\xa0\xfc\xc0w\xa9\xa9w\x03@w\xa9\xa9\x00\x02\x00\x00\xff\x97\x05\x00\x05\x80\x00\x06\x00#\x00\x00\x01!\x11\x017\x17\x01\x132\x17\x1e\x01\x15\x11\x14\x06\a\x06#\"'\t\x01\x06#\"'.\x015\x1146763\x04\x80\xfc\x00\x01\xa7YY\x01\xa7\f\x17\x15!''!\x13\x190#\xfeG\xfeG$/\x17\x15!''!\x15\x17\x05\x00\xfb&\x01\x96UU\xfej\x05Z\t\r8\"\xfa\xf7\"8\r\b \x01\xa8\xfeX!\t\r8\"\x05\t\"8\r\t\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00G\x00W\x00\x00\x014.\x04'.\x02#\"\x0e\x02#\".\x02'.\x01'.\x0354>\x0254.\x01'.\x05#\"\a\x0e\x01\x15\x14\x1e\x04\x17\x16\x00\x17\x1e\x0532676\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00\x04 1.-\x06\x05\x1c\x16\n\x0f+$)\r\a\x13\f\x16\x03c\x8e8\x02\r\x06\a)1)\n\x14\x03\x03\x18\x1a\x1b\x17\n\v05.D\x05\x05\r\a\x12\x02<\x019\xa4\x060\x12)\x19$\x109\x93\x15\x16\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01W\v\n\x17\x1b\x1a\x18\x03\x03\x14\n)1)\a\x06\r\x027\x8fc\x03\x16\f\x13\a\r)$+\x0f\n\x16\x1c\x05\x06-.1 \x04\x16\x15\x939\x10$\x19)\x120\x06\xa4\xfe\xc7<\x02\x12\a\r\x05\x05D.5\x039\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x01\x00,\x00\x00\x06T\x05\x00\x001\x00\x00\x01\x06\a\x16\x15\x14\x02\x0e\x01\x04# '\x16327.\x01'\x16327.\x01=\x01\x16\x17.\x01547\x16\x04\x17&54632\x1767\x06\a6\x06TC_\x01L\x9b\xd6\xfeҬ\xfe\xf1\xe1#+\xe1\xb0i\xa6\x1f!\x1c+*p\x93DNBN,y\x01[\xc6\b\xbd\x86\x8c`m`%i]\x04hbE\x0e\x1c\x82\xfe\xfd\xee\xb7m\x91\x04\x8a\x02}a\x05\v\x17\xb1u\x04&\x03,\x8eSXK\x95\xb3\n&$\x86\xbdf\x159s?\n\x00\x00\x00\x01\x00_\xff\x80\x03\xbf\x06\x00\x00\x14\x00\x00\x01\x11#\"\x06\x1d\x01!\x03#\x11!\x11#\x11!54632\x03\xbf\x9dV<\x01%'\xfe\xfe\xce\xff\x00\xffЭ\x93\x05\xf4\xfe\xf8HH\xbd\xfe\xd8\xfd\t\x02\xf7\x01(ں\xcd\x00\x00\x00\b\x00\x00\xff\xa7\x06\x00\x05\x80\x00T\x00\\\x00d\x00k\x00s\x00z\x00\x82\x00\x88\x00\x00\x00 \x04\x12\x15\x14\x00\a\x06&54654'>\x0454'6'&\x06\x0f\x01&\"\a.\x02\a\x06\x17\x06\x15\x14\x1e\x03\x17\x06\a\x0e\x01\"&'.\x01/\x01\"\x06\x1e\x01\x1f\x01\x1e\x01\x1f\x01\x1e\x03?\x01\x14\x16\x15\x14\x06'&\x0054\x12\x136'&\a\x06\x17\x16\x176'&\a\x06\x17\x16\x176'&\a\x06\x16\x176'&\a\x06\x17\x16\x176'&\x06\x17\x1674\a\"\x15\x14727&\a\x06\x166\x02/\x01\xa2\x01a\xce\xfe\xdb\xe8\x1b\x1a\x0149[aA)O%-\x1cj'&]\xc6]\x105r\x1c-%O)@a[9'\n\x150BA\x17\x13;\x14\x14\x15\x10\x06\f\a\a\x16+\n\n\r>HC\x16\x17\x01\x1a\x1b\xe8\xfe\xdb\xceU\x03\n\n\x03\x03\n\t#\a\t\n\x06\a\t\n$\t\t\b\t\t\x122\b\f\f\b\t\r\fA\x03\x10\x0f\b\x11\x0fC\x11\x10\x11\x10:\x02\x10\x10\x04 \x05\x80\xce\xfe\x9f\xd1\xfb\xfeoM\x05\x18\x12\x03\x93=a-\x06\x186O\x83UwW[q\t(\x18\x18\x1a\x1a\v -\tq[WwU\x82P6\x18\x06$C\n\n+) (\x04\x03\t\x0e\x0e\x05\x05\n8\x17\x17&/\r\x01\x04\x04&e\x04\x12\x18\x05M\x01\x91\xfb\xd1\x01a\xfc\u007f\a\x05\x03\x05\a\x05\x06\x1a\x05\v\t\x06\x05\v\n&\a\f\r\a\x05\x1a$\b\v\f\t\b\v\f\x10\v\x05\x04\x16\x04\x06\a\r\x02\v\r\x02\x15\v\x02\x03\x18\b\x00\x00\x00\x01\x00\x00\x00\x00\x06\x80\x05\x80\x00%\x00\x00\x01\x11\x14\x06+\x01\"&5\x114&\"\x06\x1d\x0132\x16\x15\x11\x14\x06#!\"&5\x11463!54\x00 \x00\x06\x80&\x1a@\x1a&\x96Ԗ`(88(\xfc@(88(\x02\xa0\x01\a\x01r\x01\a\x03\xc0\xff\x00\x1a&&\x1a\x01\x00j\x96\x96j\xc08(\xfd\xc0(88(\x02@(8\xc0\xb9\x01\a\xfe\xf9\x00\x00\x00\x05\x00\x00\xff\x80\a\x80\x05\x80\x00\x0f\x00\x19\x00#\x00'\x00+\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x15\"\x06\x1d\x01!54&#\x11265\x11!\x11\x14\x16375!\x1535!\x15\x06\xe0B^^B\xf9\xc0B^^B\r\x13\x06\x80\x13\r\r\x13\xf9\x80\x13\r`\x01\x00\x80\x01\x80\x05\x80^B\xfb@B^^B\x04\xc0B^\x80\x13\r\xe0\xe0\r\x13\xfb\x00\x13\r\x02`\xfd\xa0\r\x13\x80\x80\x80\x80\x80\x00\x03\x00\x00\x00\x00\x05\x80\x05\x80\x00\a\x00!\x00=\x00\x00\x00\x14\x06\"&462\x01\x16\a\x06+\x01\"&'&\x00'.\x01=\x01476;\x01\x16\x04\x17\x16\x12\x05\x16\a\x06+\x01\"&'&\x02\x00$'.\x01=\x01476;\x01\f\x01\x17\x16\x12\x01\x80p\xa0pp\xa0\x02p\x02\x13\x12\x1d\x87\x19$\x02\x16\xfe\xbb\xe5\x19!\x15\x11\x1a\x05\xa0\x01$qr\x87\x02\r\x02\x14\x12\x1c\x8f\x1a%\x01\f\xb2\xfe\xe3\xfe}\xd7\x19#\x14\x12\x1a\x03\x01\x06\x01ߺ\xbb\xd6\x01\x10\xa0pp\xa0p\xfe\xc5\x1c\x14\x15!\x19\xe5\x01E\x16\x02$\x19\x87\x1d\x12\x11\r\x87rq\xfeܢ\x1b\x14\x14#\x19\xd7\x01\x83\x01\x1d\xb2\r\x01%\x19\x8f\x1c\x12\x12\rֻ\xba\xfe!\x00\x05\x00\x00\x00\x00\x06\x00\x05\x00\x00\a\x00\x0f\x00\x1f\x00)\x00?\x00\x00\x00\x14\x06\"&462\x04\x14\x06\"&462\x17\x114&#!\"\x06\x15\x11\x14\x163!26\x01!\x03.\x01#!\"\x06\a\x01\x11\x14\x06#!\"&5\x1147\x13>\x013!2\x16\x17\x13\x16\x04\x10/B//B\x01//B//B\x9f\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\xfb2\x04\x9c\x9d\x04\x18\x0e\xfc\xf2\x0e\x18\x04\x04\xb1^B\xfb@B^\x10\xc5\x11\\7\x03\x0e7\\\x11\xc5\x10\x01aB//B//B//B/\xf0\x01@\r\x13\x13\r\xfe\xc0\r\x13\x13\x01\xed\x01\xe2\r\x11\x11\r\xfd~\xfe\xc0B^^B\x01@\x192\x02^5BB5\xfd\xa22\x00\x02\x00\x00\xff\x83\a\x00\x05\x80\x00.\x004\x00\x00\x012\x16\x14\x06#\x11\x14\x06#\x00%\x0e\x01\x16\x17\x0e\x01\x1e\x02\x17\x0e\x01&'.\x0467#\"&=\x01463! \x012\x16\x15\x03\x11\x00\x05\x11\x04\x06\x805KK5L4\xfe_\xfeu:B\x04&\x14\x06\x121/&\x1d\xa5\xac.\a-\x13\x1b\x03\n\x11zB^^B\x01\xe0\x01\xb3\x01\xcd4L\x80\xfev\xfe\x8a\x01y\x03\x80KjK\xfe\x804L\x01[!\x13^k'!A3;)\x1e:2\x1b*\x17\x81\x0454\x127&5462\x16\x15\x14\a\x16\x12\x15\x14\x1e\x03\x03\x90\x10;U gI\xfdv\x05\x14\xfe\xf60Z\x99\xba\x99Z0\x04\xc0L4\xfe@\x96Ԗ\xfe@4L2RX='\xea\xbe\b8P8\b\xbe\xea'=XR\xb0 U;\x10\x10Ig\x010\x01,\x02\x143lb??bl3\xfd\xec\xfe\xd44Lj\x96\x96jL4*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x1c\xfe\xfb\x98\x8b\xf2\xaa\x93\\\x00\x00\x00\x01\x00\x02\xff\x80\x05\xfe\x05}\x00I\x00\x00\x01\x17\x16\a\x06\x0f\x01\x17\x16\a\x06/\x01\a\x06\a\x06#\"/\x01\a\x06'&/\x01\a\x06'&?\x01'&'&?\x01'&76?\x01'&76\x1f\x017676\x1f\x0176\x17\x16\x1f\x0176\x17\x16\x0f\x01\x17\x16\x17\x16\a\x05`\x8a\x1e\n\f(\xbc5\f\x1f\x1d)\xba0\n)\f\a\x1f\x14\x87\x87\x1c*)\n0\xba)\x1d\x1f\f5\xbc(\f\n\x1e\x8a\x8a\x1e\n\f(\xbc5\f\x1f\x1d)\xba0\n))\x1d\x87\x87\x1d))\n0\xba)\x1d\x1f\f5\xbc(\f\n\x1e\x02\x80\x87\x1c*)\n0\xba)\x1d\x1f\f5\xbc(\f\x02\x16\x8a\x8a\x1e\n\v)\xbc5\f\x1f\x1d)\xba0\n)*\x1c\x87\x87\x1c*)\n0\xba)\x1d\x1f\f5\xbc)\n\f\x1f\x8b\x8b\x1e\v\n)\xbc5\f\x1f\x1d)\xba0\n)*\x1c\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x00\a\x005\x00h\x00\x00$4&\"\x06\x14\x162\x014&#!4>\x0254&#\"\a\x06\a\x06\a\x06\a\x06+\x01\x1132\x1e\x013254'>\x014'654&'!267\x14\x06+\x01\x06\a\x16\x15\x14\a\x16\x06#\"'&#!\"&5\x11463!2>\x05767>\x0432\x16\x15\x14\a!2\x16\x01\x00&4&&4\x05\xa6N2\xfd\xc0\x1e$\x1eYG\x18B\x18\r(HG\x1eEG H\xbe\xc5Q\xbd\x05\x1e#\x125\x14\x0f\x01K4L\x80\x97i\xa9\x04!\x03<\x01\xac\x8d\x85\xbd\xa4;\xfe\xe05KK5\x01 \n\x17\x18\x15\x1b\x0e\x18\x02A#\r(\"/?&}\xa3\x16\x01vh\x98\xa64&&4&\x02\x803M\x1495S+C=\x8b,\x15@QQ\x199\xfd\x80@@\xa7\x1a\x1e\x10IJ 2E\x19=\x11L5i\x98>9\x15\x16eM\x8b\xa1E;K5\x02\x805K\t\x13\x11\x1c\x0f\x1c\x03J7\x15R>@#\x86zD<\x98\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x005\x00=\x00q\x00\x00%3\x11#\".\x02'&'&'&'.\x04#\"\x06\x15\x14\x1e\x02\x15!\"\x06\x15\x14\x163!\x0e\x01\x15\x14\x17\x06\x14\x16\x17\x06\x15\x14\x1632>\x01$4&\"\x06\x14\x162\x13\x11\x14\x06#!\"\a\x06#\"&?\x01&547&'#\"&5463!&54632\x1e\x03\x17\x16\x17\x1e\x063!2\x16\x05` #A<(\x1d\b\x04H(\x0e\x18\x01\x13\x12\x16\x15\bGY\x1e$\x1e\xfd\xc02NL4\x01K\x0f\x145\x12#\x1e\x04aWTƾ\x01h&4&&4\xa6K5\xfe\xe0;\xa4\xbe\u007f\x8e\xb0\x01\x01=\x03!\x04\xa9i\x97\x98h\x01v\x16\xa3}&?/\"(\r#A\x02\x18\x0e\x1b\x15\x18\x17\n\x01 5K\x80\x02\x80\x182*!\t\x05Q@\x16.\x03'!&\x17=C+S59\x14M34L\x11=\x19E2 JI\x10\x18 UR@@&4&&4&\x02\x80\xfd\x805K;E\x9b\x8c\x05Lf\x16\x159>\x98ig\x98R\x157J\x03\x1c\x0f\x1c\x11\x13\tK\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\a\x005\x00h\x00\x00\x044&\"\x06\x14\x162\x134#\"\a.\x01\"\a&#\"\x06\a\x114&#\"\x06\x15\x11\".\x02#\"\x06\x15\x14\x17\x16\x17\x16\x17\x16\x17\x16\x1d\x01!54>\x017\x14\a\x06\x15\x11\x14\x06#!\"&5\x114.\x05'&'.\x0454632\x17\x114632\x16\x1d\x01\x16\x17632\x176\x16\x05\x00&4&&4\xa6\xa7\x1a\x1e\x10IJ 2E\x19=\x11L43M\x1495S+C=\x8b,\x15@QQ\x199\x02\x80@@\x80E;K5\xfd\x805K\t\x13\x11\x1c\x0f\x1c\x03J7\x15R>@#\x86zD<\x98gi\x98>9\x15\x16eM\x8b\xa1Z4&&4&\x03<\xbd\x05\x1e#\x125\x14\x0f\x01K4LN2\xfd\xc0\x1e$\x1eYG\x18B\x18\r(HG\x1eEG H\xbe\xc5V\x85\xbd\xa4;\xfe\xe05KK5\x01 \n\x17\x18\x15\x1b\x0e\x18\x02A#\r(\"/?&}\xa3\x16\x01vh\x98\x97i\xa9\x04!\x03<\x01\xac\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x004\x00<\x00p\x00\x00\x014.\x01=\x01!\x15\x14\x0e\x02\a\x06\a\x06\a\x06\a\x0e\x04\x15\x14\x1632>\x023\x11\x14\x163265\x11\x16327\x16267\x16326\x024&\"\x06\x14\x162\x01\x14\x06/\x01\x06#\"'\x06\a\x15\x14\x06#\"&5\x11\x06#\"&54>\x03767>\x065\x11463!2\x16\x15\x11\x14\x17\x16\x05\x80@@\xfd\x80\x182*!\t\x05Q@\x16.\x03'!&\x17=C+S59\x14M34L.9E2 JI\x10\x18 UR\x80&4&&4\x01&\x9b\x8c\x05Lf\x16\x156A\x98ig\x986Jy\x87#@>R\x157J\x03\x1c\x0f\x1c\x11\x13\tK5\x02\x805K;E\x02@TƾH #A<(\x1d\b\x04H(\x0e\x18\x01\x13\x12\x16\x15\bGY\x1e$\x1e\xfd\xc02NL4\x01K#5\x12#\x1e\x04a\x03=4&&4&\xfdD\x8e\xb0\x01\x01=\x03\x1e\a\xa9i\x97\x98h\x01v\x16\xa3}&?/\"(\r#A\x02\x18\x0e\x1b\x15\x18\x17\n\x01 5KK5\xfe\xe0;\xa4\xbe\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x0154&#!764/\x01&\"\a\x01\a\x06\x14\x1f\x01\x01\x162?\x0164/\x01!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x00&\x1a\xfe\n\xbd\x13\x13[\x126\x12\xfe\x96[\x12\x12[\x01j\x126\x12[\x12\x12\xbd\x01\xf6\x1a&\x01\x00\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02@\x80\x1a&\xbd\x134\x13[\x12\x12\xfe\x96[\x126\x12[\xfe\x96\x12\x12[\x126\x12\xbd&\x01+\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x004/\x01\x01&\"\x0f\x01\x06\x14\x1f\x01!\"\x06\x1d\x01\x14\x163!\a\x06\x14\x1f\x01\x1627\x017$\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x05\x12[\xfe\x96\x126\x12[\x12\x12\xbd\xfe\n\x1a&&\x1a\x01\xf6\xbd\x13\x13[\x126\x12\x01j[\x01\r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02e6\x12[\x01j\x12\x12[\x126\x12\xbd&\x1a\x80\x1a&\xbd\x134\x13[\x12\x12\x01j[\xfe\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x004'\x01'&\"\x0f\x01\x01\x06\x14\x1f\x01\x162?\x01\x11\x14\x16;\x01265\x11\x17\x162?\x01$\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x04\x12\xfe\x96[\x126\x12[\xfe\x96\x12\x12[\x126\x12\xbd&\x1a\x80\x1a&\xbd\x134\x13[\x01\x0e\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02f6\x12\x01j[\x12\x12[\xfe\x96\x126\x12[\x12\x12\xbd\xfe\n\x1a&&\x1a\x01\xf6\xbd\x13\x13[\xfd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00+\x00\x00\x004/\x01&\"\x0f\x01\x114&+\x01\"\x06\x15\x11'&\"\x0f\x01\x06\x14\x17\x01\x17\x162?\x01\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x05\x04\x12[\x126\x12\xbd&\x1a\x80\x1a&\xbd\x134\x13[\x12\x12\x01j[\x126\x12[\x01j\x01\x0e\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02d6\x12[\x12\x12\xbd\x01\xf6\x1a&&\x1a\xfe\n\xbd\x13\x13[\x126\x12\xfe\x96[\x12\x12[\x01j\x00\xff\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x01\xd8\x02\x18\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x01\x0e\x01\a2>\x01767676\x17&67>\x01?\x01\x06&'\x14\a4&\x06'.\x02'.\x01'.\x03\"\x0e\x01#&\x0e\x02\a\x0e\x01\a6'&\a6&'3.\x02'.\x01\a\x06\x1e\x01\x15\x16\x06\x15\x14\x16\a\x0e\x01\a\x06\x16\x17\x16\x0e\x02\x0f\x01\x06&'&'&\a&'&\a6'&\a>\x01567>\x02#\x167>\x0176\x1e\x013\x166'\x16'&'&\a\x06\x17&\x0e\x01'.\x01'\"\a6&'6'.\x01\a\x0e\x01\x1e\x02\x17\x16\a\x0e\x02\a\x06\x16\a.\x01'\x16/\x01\"\x06&'&76\x17.\x01'\x06\a\x167>\x0176\x177\x16\x17&\a\x06\a\x16\a.\x02'\"\a\x06\a\x16\x17\x1e\x027\x16\a6\x17\x16\x17\x16\a.\x01\a\x06\x167\"\x06\x14\a\x17\x06\x167\x06\x17\x16\x17\x1e\x02\x17\x1e\x01\x17\x06\x16\a\"\x06#\x1e\x01\x17\x1e\x0276'&'.\x01'2\x1e\x02\a\x06\x1e\x02\x17\x1e\x01#2\x16\x17\x1e\x01\x17\x1e\x03\x17\x1e\x01\x17\x162676\x16\x17\x167\x06\x1e\x02\x17\x1e\x01\x1767\x06\x16765\x06'4.\x026326&'.\x01'\x06&'\x14\x06\x15\"'>\x017>\x03&\a\x06\a\x0e\x02\a\x06&'.\x0154>\x01'>\x017>\x01\x1667&'&#\x166\x17\x1674&7\x167\x1e\x01\x17\x1e\x0267\x16\x17\x16\x17\x16>\x01&/\x0145'.\x0167>\x0276'27\".\x01#6'>\x017\x1676'>\x017\x16647>\x01?\x016#\x1676'6&'6\x1676'&\x0367.\x01'&'6.\x02'.\x03\x06#\a\x0e\x03\x17&'.\x02\x06\a\x0e\x01\a&6'&\x0e\x04\a\x0e\x01\a.\x015\x1e\x01\x17\x16\a\x06\a\x06\x17\x14\x06\x17\x14\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x03D\x02\x0f\x06\x02\x05\x05\x01\x06\x10\x0e&\"\x11\x02\x17\x03\x03\x18\x03\x02\f\v\x01\x06\t\x0e\x02\n\n\x06\x01\x02\x0f\x02\x01\x03\x03\x05\x06\b\a\x01\x03\x06\x03\x06\x02\x03\v\x03\x0f\x10\n\x06\t\x03\a\x05\x01\x0f\x14\x03\b4\a\x05\x01\a\x01\r\x1c\x04\x03\x1a\x03\x05\a\a\x02\x01\x06\x05\x04\x03\v\x13\x04\a\t\x17\x06\x05$\x19!\x06\x06\a\f\x03\x02\x03\t\x01\f\a\x03#\x0f\x05\r\x04\t\n\x13\x05\x0e\x03\t\f\t\x04\x04\f\x0f\b\n\x01\x11\x10\b\x01\t\x05\b\b\x03\x1c\n\x13\x1b\a\x1b\x06\x05\x01\v\n\r\x02\x0e\x06\x02\r\n\x01\x03\x06\x05\x05\b\x03\a \n\x04\x18\x11\x05\x04\x04\x01\x03\x04\x0e\x03.0\x06\x06\x05\x10\x02\"\b\x05\x0e\x06\a\x17\x14\x02\a\x02\x04\x0f\x0e\b\x10\x06\x92Y\a\x05\x04\x02\x03\n\t\x06\x01+\x13\x02\x03\r\x01\x10\x01\x03\a\a\a\x05\x01\x02\x03\x11\r\r!\x06\x02\x03\x12\f\x04\x04\f\b\x02\x17\x01\x01\x03\x01\x03\x19\x03\x01\x02\x04\x06\x02\x1a\x0f\x02\x03\x05\x02\x02\b\t\x06\x01\x03\n\x0e\x14\x02\x06\x10\b\t\x16\x06\x05\x06\x02\x02\r\f\x14\x03\x05\x1b\b\n\f\x11\x05\x0f\x1c\a$\x13\x02\x05\v\a\x02\x05\x1a\x05\x06\x01\x03\x14\b\x0e\x1f\x12\x05\x03\x02\x02\x04\t\x02\x06\x01\x01\x14\x02\x05\x16\x05\x03\r\x02\x01\x03\x02\x01\t\x06\x02\v\f\x13\a\x01\x04\x06\x06\a\"\a\r\x13\x05\x01\x06\x03\f\x04\x02\x05\x04\x04\x01\x01\x03\x03\x01\a+\x06\x0f\a\x05\x02\x05\x18\x03\x19\x05\x03\b\x03\a\x05\n\x02\v\b\a\b\x01\x01\x01\x01\x01\x0f\a\n\n\x01\x0e\x11\x04\x15\x06\a\x04\x01\b\a\x01\t\a\x05\x05\x05\t\f\b\a\x05\x1f\x03\a\x02\x03\x04\x16\x02\x11\x03\x03\x12\r\n\x10\x03\f\t\x03\x11\x02\x0f\x16\x11\xbdΑ\x03\x13\x03\x12\x06\x01\a\t\x10\x03\x02\n\x04\v\x06\a\x03\x03\x05\x06\x02\x01\x15\x0f\x05\f\t\v\x06\x05\x02\x01\a\x0e\x05\x03\x0f\t\x0e\x04\r\x02\x03\x06\x02\x02\x13\x02\x04\x03\a\x13\x1b\x02\x04\x10\x10\x01\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfe\xc5\x01\x11\x01\n\f\x01\a\b\x06\x06\b\x13\x02\x16\x01\x02\x05\x05\x16\x01\x10\r\x02\x06\a\x02\x04\x01\x03\t\x18\x03\x05\f\x04\x02\a\x06\x05\n\n\x02\x01\x01\x05\x01\x02\x02\x01\x05\x06\x04\x01\x04\x10\x06\x04\t\b\x02\x05\t\x04\x06\t\x13\x03\x06\x0e\x05\a\x11\r\b\x10\x04\b\x15\x06\x02\x04\x05\x03\x02\x02\x05\x16\x0f\x19\x05\b\t\r\r\t\x05\x01\x0e\x0f\x03\x06\x17\x02\r\n\x01\x0f\f\x04\x0f\x05\x18\x05\x06\x01\n\x01\x18\b\x01\x12\a\x02\x04\t\x04\x04\x01\x17\f\v\x01\x19\x01\x0f\b\x0e\x01\f\x0f\x04\x02\x05\a\t\a\x04\x04\x01\n\x04\x01\x05\x04\x02\x04\x14\x04\x05\x19\x04\t\x03\x01\x04\x02\a\b\f\x04\x02\x03\r\x02\x0f\x1a\x01\x02\x02\t\x01\x0e\a\x05\x10\t\x04\x03\x06\x06\f\x06\x03\x0e\b\x01\x01P\x8e\a\x01\x01\x10\x06\x06\b\v\x01\x1c\x11\x04\v\a\x02\x0e\x03\x05\x1b\x01 '\x04\x01\f-\x03\x03(\b\x01\x02\v\t\x06\x05#\x06\x06\x1c\t\x02\a\x0e\x06\x03\x0e\b\x02\x14*\x19\x04\x05\x15\x04\x03\x04\x04\x01\a\x15\x10\x16\x02\x06\x1b\x15\t\b$\x06\a\r\x06\n\x02\x02\x11\x03\x04\x05\x01\x02\"\x04\x13\b\x01\r\x12\v\x03\x06\x12\x06\x04\x05\b\x18\x02\x03\x1d\x0f!\x01\t\b\t\x06\a\x12\x04\b\x18\x03\t\x02\b\x01\t\x02\x01\x03\x1d\b\x04\x10\r\f\a\x01\x01\x13\x03\x0f\b\x03\x03\x02\x04\b*\x10\n!\x11\x10\x02\x0f\x03\x01\x01\x01\x04\x04\x01\x02\x03\x03\t\x06\v\r\x01\x11\x05\x1b\x12\x03\x04\x03\x02\a\x02\x03\x05\x0e\n(\x04\x03\x02\x11\v\a\b\t\t\b\x03\x12\x13\t\x01\x05\b\x04\x13\x10\t\x06\x04\x05\v\x03\x10\x02\f\n\b\b\a\a\x06\x02\b\x10\x04\x05\b\x01\v\x04\x02\r\v\t\x06\a\x02\x01\x01\x02\n\x06\x05\xfc\x82$\x99\x03\x03\x02\a\x01\a\f\x06\n\x02\x02\b\x03\x06\x02\x01\x01\x03\x03\x03\x01\x11\x05\x01\t\x05\x02\x06\x05\x14\x03\x05\x19\x06\x06\x03\x06\v\x02\t\x03\x04\x10\x03\x04\x05\x03\n2\r\x1f\x11\x19\x0f\x16\x04\a\x1b\b\x06\x00\x00\x03\x00\x15\xff\x15\x06~\x05\x80\x00\a\x00\x15\x00/\x00\x00$4&\"\x06\x14\x162\t\x01\x06#\"/\x01&547\x01\x1e\x01\x01\x14\a\x0e\x01#\"\x00\x10\x0032\x16\x17\x16\x14\a\x05\x15\x17>\x0232\x16\x01\x80&4&&4\x02\xaa\xfdV%54'j&&\x02\xa9'\x97\x02\xdc\x17/덹\xfe\xf9\x01\a\xb9:\u007f,\x10\x10\xfe\xdb\xc1\x05\x94{\t\x0f\x11&4&&4&\x01\xe4\xfdV%%l$65&\x02\xa9b\x97\x01\x8c'C\x86\xa7\x01\a\x01r\x01\a!\x1e\v\"\v\xa9\xe0k\x03[G\x14\x00\x00\x00\x06\x00\x00\x00\x00\a\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x1b\x00+\x00;\x00\x00%!5!\x01!5!\x01!5!\x01\x11\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x19\x01\x14\x06#!\"&5\x11463!2\x16\x04\x00\x02\x80\xfd\x80\xfe\x80\x04\x00\xfc\x00\x02\x80\x01\x80\xfe\x80\x02\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\x80\x80\x01\x80\x80\x01\x80\x80\xfc@\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x01\xe6\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x01\xe6\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x00\x00\x01\x00\x05\xff\x80\x05{\x05\x00\x00\x15\x00\x00\x01\x16\a\x01\x11\x14\a\x06#\"'\x01&5\x11\x01&763!2\x05{\x11\x1f\xfe\x13'\r\f\x1b\x12\xff\x00\x13\xfe\x13\x1f\x11\x11*\x05\x00*\x04\xd9)\x1d\xfe\x13\xfd\x1a*\x11\x05\x13\x01\x00\x13\x1a\x01\xe6\x01\xed\x1d)'\x00\x00\x00\x04\x00\x00\x00\x00\a\x00\x06\x00\x00\x03\x00\x17\x00\x1b\x00/\x00\x00\x01!5!\x01\x11\x14\x06#!\"&5\x11!\x15\x14\x163!26=\x01#\x15!5\x01\x11!\x11463!5463!2\x16\x1d\x01!2\x16\x02\x80\x02\x00\xfe\x00\x04\x80^B\xfa@B^\x02\xa0&\x1a\x01@\x1a&`\xff\x00\x04\x00\xf9\x00^B\x01`8(\x02@(8\x01`B^\x05\x00\x80\xfd\x00\xfe B^^B\x01\xe0\xa0\x1a&&\x1a\xa0\x80\x80\x01\xe0\xfe\x80\x01\x80B^\xa0(88(\xa0^\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00G\x00\x00\t\x0276\x17\x16\x15\x11\x14\x06#!\"'&?\x01\t\x01\x17\x16\a\x06#!\"&5\x11476\x1f\x01\t\x01\a\x06#\"'&5\x11463!2\x17\x16\x0f\x01\t\x01'&763!2\x16\x15\x11\x14\a\x06#\"'\x05\x03\xfe\x9d\x01c\x90\x1d)'&\x1a\xfe@*\x11\x11\x1f\x90\xfe\x9d\xfe\x9d\x90\x1f\x11\x11*\xfe@\x1a&('\x1e\x90\x01c\xfe\x9d\x90\x13\x1a\f\f(&\x1a\x01\xc0*\x11\x11\x1f\x90\x01c\x01c\x90\x1f\x11\x11*\x01\xc0\x1a&'\r\f\x1a\x13\x03\xe3\xfe\x9d\xfe\x9d\x90\x1f\x11\x11*\xfe@\x1a&('\x1e\x90\x01c\xfe\x9d\x90\x1e'(&\x1a\x01\xc0*\x11\x11\x1f\x90\x01c\x01c\x90\x13\x05\x11*\x01\xc0\x1a&('\x1e\x90\xfe\x9d\x01c\x90\x1e'(&\x1a\xfe@*\x11\x05\x13\x00\x00\x06\x00\x00\xff\x00\a\x80\x06\x00\x00\x11\x001\x009\x00A\x00S\x00[\x00\x00\x01\x06\a#\"&5\x1032\x1e\x01327\x06\x15\x14\x01\x14\x06#!\"&54>\x0532\x1e\x022>\x0232\x1e\x05\x00\x14\x06\"&462\x00\x10\x06 &\x106 \x01\x14\x06+\x01&'654'\x1632>\x0132\x02\x14\x06\"&462\x02Q\xa2g\x86Rp|\x06Kx;CB\x05\x04\x80\x92y\xfc\x96y\x92\a\x15 6Fe=\nBP\x86\x88\x86PB\n=eF6 \x15\a\xfc\x00\x96Ԗ\x96\xd4\x03V\xe1\xfe\xc2\xe1\xe1\x01>\x03!pR\x86g\xa2Q\x05BC;xK\x06|\x80\x96Ԗ\x96\xd4\x02\x80\x05{QN\x01a*+\x17%\x1d\x8b\xfd\x0ex\x8b\x8bx5eud_C(+5++5+(C_due\x052Ԗ\x96Ԗ\xfe\x1f\xfe\xc2\xe1\xe1\x01>\xe1\xfd\x9fNQ{\x05u\x8b\x1d%\x17+*\x01jԖ\x96Ԗ\x00\x00\x00\x00\x03\x00\x10\xff\x90\x06p\x05\xf0\x00!\x00C\x00i\x00\x00\x014/\x01&#\"\a\x1e\x04\x15\x14\x06#\".\x03'\x06\x15\x14\x1f\x01\x1632?\x016\x014/\x01&#\"\x0f\x01\x06\x15\x14\x1f\x01\x16327.\x0454632\x1e\x03\x176\x00\x14\x0f\x01\x06#\"/\x01&547'\x06#\"/\x01&4?\x01632\x1f\x01\x16\x15\x14\a\x17632\x1f\x01\x05\xb0\x1c\xd0\x1c(*\x1e\x03 \v\x13\a8(\x0f\x19\x1a\f\x1f\x03!\x1c\xce\x1b)(\x1c\x93\x1c\xfdA\x1c\xce\x1c('\x1d\x93\x1c\x1c\xd0\x1b)*\x1e\x03 \v\x13\a8(\x0f\x19\x1a\f\x1f\x03!\x03\u007fU\x93SxyS\xceSXXVzxT\xd0TU\x93SxyS\xceSXXVzxT\xd0\x01@(\x1c\xd0\x1c \x03\x1f\f\x1a\x19\x0f(8\a\x13\v \x03\x1f*(\x1c\xcf\x1b\x1a\x92\x1c\x02\xe8(\x1c\xcf\x1c\x1b\x92\x1c'(\x1c\xd0\x1b\x1f\x03\x1f\f\x1a\x19\x0f(8\a\x13\v \x03\x1f\xfd\xe1\xf0S\x92SU\xcfSx{VXXT\xd0T\xf0S\x92SU\xcfSx{VXXT\xd0\x00\x01\x00\x00\x00\x00\a\x80\x05\x80\x00\x1b\x00\x00\x01\x14\x06#!\"\x005467&54\x0032\x04\x17632\x16\x15\x14\a\x1e\x01\a\x80\xe1\x9f\xfb\xc0\xb9\xfe\xf9\x8et\x02\x01,Ԟ\x01\x01;F`j\x96)\x81\xa8\x01\x80\x9f\xe1\x01\a\xb9\x84\xdb6\x1c\x0f\xd4\x01,\xb0\x8e>\x96jK?\x1e\xd1\x00\x02\x00s\xff\x80\x06\r\x05\x80\x00\x17\x00!\x00\x00%\x16\x06#!\"&7\x01\x11#\"&463!2\x16\x14\x06+\x01\x11\x05\x01!\x01'5\x11#\x11\x15\x05\xf78Ej\xfb\x80jE8\x01\xf7@\x1a&&\x1a\x02\x00\x1a&&\x1a@\xfe\xec\xfe\xf0\x02\xc8\xfe\xf0\x14\x80XY\u007f\u007fY\x03\x19\x01\x8f&4&&4&\xfeqD\xfeS\x01\xad\x1f%\x01\x8f\xfeq%\x00\x00\x00\x00\a\x00\x01\xff\x80\a\x00\x05\x00\x00\a\x00N\x00\\\x00j\x00x\x00\x86\x00\x8c\x00\x00\x002\x16\x14\x06\"&4\x05\x01\x16\a\x06\x0f\x01\x06#\"'\x01\a\x06\a\x16\a\x0e\x01\a\x06#\"'&7>\x017632\x176?\x01'&'\x06#\"'.\x01'&67632\x17\x1e\x01\x17\x16\a\x16\x1f\x01\x01632\x1f\x01\x16\x17\x16\a\x056&'&#\"\a\x06\x16\x17\x1632\x03>\x01'&#\"\a\x0e\x01\x17\x1632\x01\x1754?\x01'\a\x0e\x01\a\x0e\x01\a\x1f\x01\x01'\x01\x15\a\x17\x16\x17\x1e\x01\x1f\x01\x017\x01\a\x06\a\x03\xa64&&4&\x01l\x01\xfb\x1c\x03\x05\x1e\x80\r\x10\x11\x0e\xfdNn\b\x04\x0e\x04\abS\x84\x91\x88VZ\v\abR\x84\x92SD\t\rzz\r\tDS\x92\x84Rb\a\x05)+U\x89\x91\x84Sb\a\x04\x0e\x04\bn\x02\xb2\x0e\x11\x10\r\x80\x1e\x05\x03\x1c\xfb\\.2Q\\dJ'.2Q\\dJ.Q2.'Jd\\Q2.'Jd\x01\x0e`!\x0eO\x1a\x03\x0e\x05\x02\x04\x01\xd7`\x02\xe0\x80\xfd\x00\xa0\t\x02\x05\x04\x0e\x04\x1a\x03`\x80\xfd\xf8\xb1\x02\v\x02\x80&4&&4\x1a\xfer\x14$#\x10@\a\b\x01\x83B\x04\x0110M\x8d5TNT{L\x8e5T\x1f\r\tII\t\r\x1fT5\x8eL;l'OT4\x8eM01\x01\x04B\x01\x83\b\a@\x10#$\x14\x8a*\x843;$*\x843;\xfd;3\x84*$;3\x84*$\x02\xa0:\v$\x14\b/\x1a\x03\x10\x04\x02\x03\x01\xe9 \x02@@\xfeQq`\b\x02\x04\x04\x10\x04\x1a\xfe\xc0@\x01\x98\x8a\x03\x04\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\x1f\x00\"\x00%\x003\x00<\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11!\"&5\x11467\x01>\x013!2\x16\x15\x1163\a\x01!\t\x01!\x13\x01\x11!\x11\x14\x06#!\x11!\x1146\x01\x11!\x11\x14\x06#!\x11\x06\xa0(88(\xfc@(8\xfd\xe0(8(\x1c\x01\x98\x1c`(\x01\xa0(8D<\x80\xfe\xd5\x01+\xfd\x80\xfe\xd5\x01+\xc4\x01<\xfe\x808(\xfe`\x02\x00(\x03\xd8\xfe\x808(\xfe`\x04\x808(\xfb@(88(\x01 8(\x02\xa0(`\x1c\x01\x98\x1c(8(\xfe\xb8(\xd5\xfe\xd5\x02\xab\xfe\xd5\xfe\xa4\x01<\x01\xa0\xfe`(8\xfd\x80\x01\x00(`\xfc\xf8\x04\x80\xfe`(8\xfd\x80\x00\x00\x00\x01\x00\x04\xff\x84\x05|\x05|\x00?\x00\x00%\x14\x06#\"'\x01&54632\x17\x01\x16\x15\x14\x06#\"'\x01&#\"\x06\x15\x14\x17\x01\x1632654'\x01&#\"\x06\x15\x14\x17\x01\x16\x15\x14\x06#\"'\x01&54632\x17\x01\x16\x05|\x9eu\x87d\xfc\xf7qܟ\x9es\x02]\n=\x10\r\n\xfd\xa2Ofj\x92L\x03\b?R@T?\xfd\xbb\x1a\"\x1d&\x19\x01\x9a\n>\x10\f\n\xfef?rRX=\x02Ed\x97u\x9ed\x03\bs\x9c\x9f\xdeq\xfd\xa2\n\f\x10=\n\x02_M\x96jiL\xfc\xf7?T@R?\x02E\x18&\x1d \x1b\xfef\n\f\x10>\n\x01\x9a=XRr?\xfd\xbbb\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00!\x001\x00E\x00\x00)\x01\x11!\x013\x114&'\x01.\x01#\x11\x14\x06#!\"&5\x11#\x113\x11463!2\x16\x15\x01\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126\x05\x11\x14\x06#!\"&5\x11463!2\x16\x17\x01\x1e\x01\x01\x80\x03\x00\xfd\x00\x03\x80\x80\x14\n\xfe\xe7\n0\x0f8(\xfd\xc0(8\x80\x808(\x03@(8\xfe\x80\x13\r\xc0\r\x13\x13\r\xc0\r\x13\x02\x808(\xfa\xc0(88(\x03\xa0(`\x1c\x01\x18\x1c(\x01\x80\xfe\x80\x03\x80\x0e1\n\x01\x19\n\x14\xfe`(88(\x01\xa0\xfb\x00\x01\xa0(88(\x02\x00\x01@\r\x13\x13\r\xfe\xc0\r\x13\x13\x13\xfc`(88(\x05@(8(\x1c\xfe\xe8\x1c`\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x00\x01\x11\x14\x06#!\"&5\x11463!2\x16\x06\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x04`\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x05\x00\x00\x0f\x00\x1f\x00/\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x06\x00&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&&\x01\xe6\x80\x1a&&\x1a\x80\x1a&&\x01\xe6\x80\x1a&&\x1a\x80\x1a&&\x00\x06\x00\x00\xff\xc0\a\x00\x05@\x00\a\x00\x0f\x00\x1f\x00'\x007\x00G\x00\x00$\x14\x06\"&462\x12\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x16\x00\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01\x80p\xa0pp\xa0pp\xa0pp\xa0\x05\xf0\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\xfa\x80p\xa0pp\xa0\x05\xf0\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13Рpp\xa0p\x01\x90\xa0pp\xa0p\xfd\xa0\xc0\r\x13\x13\r\xc0\r\x13\x13\x03\xe3\xa0pp\xa0p\xfd\xa0\xc0\r\x13\x13\r\xc0\r\x13\x13\x01\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x00\x00\x06\x00\x0f\xff\x00\a\x00\x05\xf7\x00\x1e\x00<\x00L\x00\\\x00l\x00|\x00\x00\x05\x14\x06#\"'7\x1632654\a'>\x0275\"\x06#\x15#5!\x15\a\x1e\x01\x13\x15!&54>\x0354&#\"\a'>\x0132\x16\x15\x14\x0e\x02\a35\x01\x15\x14\x06#!\"&=\x01463!2\x16\x01\x15!5346=\x01#\x06\a'73\x11\x01\x15\x14\x06#!\"&=\x01463!2\x16\x11\x15\x14\x06#!\"&=\x01463!2\x16\x01}mQjB919\x1d+i\x1a\b1$\x13\x10A\x10j\x01M_3<\x02\xfe\x96\x06/BB/\x1d\x19.#U\x18_:IdDRE\x01\u007f\x05\xea\x13\r\xfb@\r\x13\x12\x0e\x04\xc0\r\x13\xfa\x80\xfe\xb1k\x01\x02\b*G\x88j\x05\xec\x13\r\xfb@\r\x13\x12\x0e\x04\xc0\r\x13\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13TP\\BX-\x1d\x1c@\b8\nC)\x12\x01\x025\x98Xs\fJ\x02@\x9f$\x123T4+,\x17\x19\x1b:;39SG2S.7\x19<\xfe\xc1\xc0\r\x13\x13\r\xc0\x0e\x12\x13\x03vcc)\xa1)\f\x11%L\u007f\xfel\xfe}\xc0\r\x13\x13\r\xc0\x0e\x12\x13\x01\xf3\xc0\r\x13\x13\r\xc0\r\x13\x13\x00\x00\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x00\x0f\x005\x00e\x00\x00\x012\x16\x1d\x01\x14\x06#!\"&=\x01463%&'&5476!2\x17\x16\x17\x16\x17\x16\x15\x14\x0f\x01/\x01&'&#\"\a\x06\x15\x14\x17\x16\x17\x16\x17\x16\x17\x03!\x16\x15\x14\a\x06\a\x06\a\x06\a\x06#\"/\x01&'&=\x014'&?\x0157\x1e\x02\x17\x16\x17\x16\x17\x1632767654'&\x06\xe0\x0e\x12\x12\x0e\xf9@\x0e\x12\x12\x0e\x01\xc3\x1c\x170\x86\x85\x01\x042uBo\n\v\x0e\x05\fT\x0e25XzrDCBB\xd5Eh:%\xec\x01\x9b\a)\x170%HPIP{rQ\x8c9\x0f\b\x02\x01\x01\x02f\x0f\x1e\x0f\x05#-+>;I@KM-/Q\"\x02\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12@#-bZ\xb5\x80\u007f\x13\f$&P{<\x12\x1b\x03\x06\x02\x958[;:XICC>\x14.\x1c\x18\xff\x00'5oe80#.0\x12\x15\x17(\x10\f\b\x0e\rl0\x1e&%,\x02\"J&\b9%$\x15\x16\x1b\x1a<=DTI\x1d\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00c\x00s\x00\x00\x13&/\x01632\x17\x163276727\a\x17\x15\x06#\"\a\x06\x15\x14\x16\x15\x17\x13\x16\x17\x16\x17\x16327676767654.\x01/\x01&'&\x0f\x01'73\x17\x167\x17\x16\x15\x14\a\x06\a\x06\a\x06\x15\x14\x16\x15\x16\x13\x16\a\x06\a\x06\a\x06\a\x06#\"'&'&'&5\x114'&\x0154&#!\"\x06\x1d\x01\x14\x163!260%\b\x03\r\x1b<4\x84\"VRt\x1e8\x1e\x01\x02<@<\x13\r\x01\x01\x0e\x06-#=XYhW8+0\x11$\x11\x15\a\x0f\x06\x04\x05\x13\"+d\x0e\x02T\xcdLx\x12\x06\x04-'I\x06\x0f\x03\b\x0e\x06\x15\x0f\x1a&JKkm\x92\xa7uw<=\x16\x10\x11\x19\x05V\x12\x0e\xfa@\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x05!\x02\x02X\x01\x04\a\x03\x04\x01\x02\x0e@\t\t\x19\x0ev\r'\x06\xe5\xfe\xe8|N;!/\x1c\x12!$\x1c8:I\x9cOb\x93V;C\x15#\x01\x02\x03V\n\x03\r\x02&\r\a\x18\f\x01\v\x06\x0f\x1a\a(\v\x13\xfe\x87\xc3mL.A:9 !./KLwP\x9d\x01M\xbc\x19$\xfa\x82@\x0e\x12\x12\x0e@\x0e\x12\x12\x00\x00\n\x00\x00\x00\x00\x06\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\x00%54&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\xfe\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\xfe\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x80^B\xfa\xc0B^^B\x05@B^\xa0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x03\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x03\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfe\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01\x8e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01N\xfb\xc0B^^B\x04@B^^\x00\x00\x00\x06\x00\x1b\xff\x9b\x06\x80\x06\x00\x00\x03\x00\x13\x00\x1b\x00#\x00+\x003\x00\x00\t\x01'\x01$\x14\a\x01\x06\"/\x01&47\x0162\x1f\x01%\x17\x0f\x01/\x01?\x01\x01\x17\x0f\x01/\x01?\x01\x01\x17\x0f\x01/\x01?\x01\x01\x17\x0f\x01/\x01?\x01\x04\xa6\x01%k\xfe\xdb\x02*\x12\xfa\xfa\x126\x12\xc6\x12\x12\x05\x06\x126\x12\xc6\xfa\xcbbb\x1e\x1ebb\x1e\x01|\xc4\xc4<<\xc4\xc4<\x03\xdebb\x1e\x1ebb\x1e\xfd\x9ebb\x1e\x1ebb\x1e\x03\xbb\x01%k\xfe\xdb\xd56\x12\xfa\xfa\x12\x12\xc6\x126\x12\x05\x06\x12\x12Ƒ\x1e\x1ebb\x1e\x1eb\xfe\xfc<<\xc4\xc4<<\xc4\xfd^\x1e\x1ebb\x1e\x1eb\x02\x1e\x1e\x1ebb\x1e\x1eb\x00\x00\x00\x04\x00@\xff\x80\a\x00\x05\x00\x00\a\x00\x10\x00\x18\x00M\x00\x00$4&\"\x06\x14\x162\x01!\x11#\"\x0f\x01\x06\x15\x004&\"\x06\x14\x162\x01\x11\x14\x0e\x04&#\x14\x06\"&5!\x14\x06\"&5#\"\x06.\x045463\x114&>\x03?\x01>\x01;\x015463!2\x16\x02\x80LhLLh\xfe\xcc\x01\x80\x9e\r\t\xc3\t\x05\x00LhLLh\x01L\b\x13\x0e!\f'\x03\x96Ԗ\xfe\x80\x96Ԗ@\x03'\f!\x0e\x13\b&\x1a\x01\x01\x04\t\x13\r\xc6\x13?\x1b\xa0&\x1a\x04\x00\x1a&LhLLhL\x02\x80\x01\x00\t\xc3\t\r\xfd\xaehLLhL\x04\xc0\xfc\x00\x0f\x17\x0e\t\x03\x01\x01j\x96\x96jj\x96\x96j\x01\x01\x03\t\x0e\x17\x0f\x1a&\x01@\b6\x16/\x1b\"\r\xc6\x13\x1a\xc0\x1a&&\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00J\x00\x00\x00\x10\x02\x04#\"'6767\x1e\x0132>\x0154.\x01#\"\x0e\x03\x15\x14\x16\x17\x167>\x0176'&54632\x16\x15\x14\x06#\"&7>\x0254&#\"\x06\x15\x14\x17\x03\x06\x17&\x0254\x12$ \x04\x06\x00\xce\xfe\x9f\xd1ok;\x13\t-\x14j=y\xbehw\xe2\x8ei\xb6\u007f[+PM\x1e\b\x02\f\x02\x06\x113ѩ\x97\xa9\x89k=J\x0e\b%\x1762>V\x19c\x11\x04\xce\xfe\xce\x01a\x01\xa2\x01a\x03Q\xfe^\xfe\x9f\xce ]G\"\xb1'9\x89\xf0\x96r\xc8~:`}\x86Ch\x9e \f \a0\x06\x17\x14=Z\x97٤\x83\xaa\xeeW=#uY\x1f2BrUI1\xfe^Fk[\x01|\xe9\xd1\x01a\xce\xce\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00L\x00\x00\x012\x16\x15\x11\x14\x06#!6767\x1e\x0132\x1254.\x02#\"\x0e\x03\x15\x14\x16\x17\x1667676'&54632\x16\x15\x14\x06#\"&7>\x0254&#\"\x06\x15\x14\x17\x03\x06\x17#\"&5\x11463\x04\xe0w\xa9\xa9w\xfd+U\x17\t,\x15i<\xb5\xe5F{\xb6jh\xb5}Z+OM\r\x15\x04\n\x05\x06\x112ϧ\x95\xa7\x87jX\x96բ\x81\xa8\xecW<\"uW\x1f1AqSH1\xfebd\x9a\xa9w\x03\xc0w\xa9\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1b\x00'\x007\x00\x00\x014'!\x153\x0e\x03#\"&4632\x177&#\"\x06\x10\x16326%35#5#\x15#\x153\x153\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x95\x06\xfe\x96\xd9\x03\x1b0U6c\x8c\x8cc\\=hl\x95\xa0\xe0ࠥ\xcb\x01Ymmnnnn\x01\x12\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02w\x1a&\x84\x1846#\x8eȎ;ed\xe1\xfe\xc2\xe1\xd2wnnnnn\x02\x85\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x02\x00\x00\xff\xa3\t\x00\x05]\x00#\x00/\x00\x00\x01\x14\x02\x04#\"$&\x02\x10\x126$3 \x17\a&#\"\x0e\x01\x14\x1e\x0132>\x037!5!\x16%\x15#\x15#5#5353\x15\x05\x9d\xae\xfe\xbeЕ\xfe\xf0\xc4tt\xc4\x01\x10\x95\x01\x1e\xcd\xc7u\xaf{\xd1zz\xd1{S\x8bZC\x1f\x06\xfe`\x02\xb4\f\x03c\xd1\xd2\xd1\xd1\xd2\x02o\xd0\xfe\xbb\xb7t\xc4\x01\x10\x01*\x01\x10\xc4t\xc0\xbfq|\xd5\xfc\xd5|.EXN#\xfc??\xd2\xd1\xd1\xd2\xd1\xd1\x00\x00\x00\x04\x00\x00\x00\x00\a\x80\x05\x00\x00\f\x00\x1c\x00,\x00<\x00\x00\x01!5#\x11#\a\x17673\x11#$\x14\x0e\x02\".\x024>\x022\x1e\x01\x01\x11\"&5!\x14\x06#\x112\x16\x15!46\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03\x00\x01\x80\x80r\x94M*\r\x02\x80\x02\x00*M~\x96~M**M~\x96~M\x02*j\x96\xfb\x80\x96jj\x96\x04\x80\x96\xea&\x1a\xf9\x00\x1a&&\x1a\a\x00\x1a&\x01\x80`\x01\xc0\x89P%\x14\xfe\xe0挐|NN|\x90\x8c\x90|NN|\xfe*\x02\x00\x96jj\x96\xfe\x00\x96jj\x96\x03@\xfb\x80\x1a&&\x1a\x04\x80\x1a&&\x00\x00\x01\x00\x00\x01@\x04\x00\x03\x80\x00\r\x00\x00\x00\x14\a\x01\x06\"'\x01&463!2\x04\x00\x13\xfe@\x134\x13\xfe@\x13&\x1a\x03\x80\x1a\x03Z4\x13\xfe@\x13\x13\x01\xc0\x134&\x00\x00\x00\x00\x01\x00\x00\x01\x00\x04\x00\x03@\x00\r\x00\x00\x00\x14\x06#!\"&47\x0162\x17\x01\x04\x00&\x1a\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x01Z4&&4\x13\x01\xc0\x13\x13\xfe@\x00\x00\x00\x00\x01\x00@\x00\x80\x02\x80\x04\x80\x00\r\x00\x00\x01\x11\x14\x06\"'\x01&47\x0162\x16\x02\x80&4\x13\xfe@\x13\x13\x01\xc0\x134&\x04@\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x13&\x00\x00\x00\x01\x00\x00\x00\x80\x02@\x04\x80\x00\r\x00\x00\x00\x14\a\x01\x06\"&5\x11462\x17\x01\x02@\x13\xfe@\x134&&4\x13\x01\xc0\x02\x9a4\x13\xfe@\x13&\x1a\x03\x80\x1a&\x13\xfe@\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x80\x05\x80\x00\x06\x00\r\x00\x1d\x00\x003!\x11!\x11\x14\x16%\x11!\x11!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\xa0\x02`\xfd\x80\x13\x05m\xfd\x80\x02`\r\x13\x80^B\xfa\xc0B^^B\x05@B^\x04\x80\xfb\xa0\r\x13 \x04`\xfb\x80\x13\x04\xcd\xfb@B^^B\x04\xc0B^^\x00\x02\x00\x00\xff\xc0\x04\x00\x05@\x00\r\x00\x1b\x00\x00\x00\x14\a\x01\x06\"'\x01&463!2\x12\x14\x06#!\"&47\x0162\x17\x01\x04\x00\x13\xfe@\x134\x13\xfe@\x13&\x1a\x03\x80\x1a&&\x1a\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x01\xda4\x13\xfe@\x13\x13\x01\xc0\x134&\x01Z4&&4\x13\x01\xc0\x13\x13\xfe@\x00\x00\x00\x00\x01\x00\x00\xff\xc0\x04\x00\x02\x00\x00\r\x00\x00\x00\x14\a\x01\x06\"'\x01&463!2\x04\x00\x13\xfe@\x134\x13\xfe@\x13&\x1a\x03\x80\x1a\x01\xda4\x13\xfe@\x13\x13\x01\xc0\x134&\x00\x00\x00\x00\x01\x00\x00\x03\x00\x04\x00\x05@\x00\r\x00\x00\x00\x14\x06#!\"&47\x0162\x17\x01\x04\x00&\x1a\xfc\x80\x1a&\x13\x01\xc0\x134\x13\x01\xc0\x03Z4&&4\x13\x01\xc0\x13\x13\xfe@\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x00\x00\x1a\x00:\x00\x00\x01\x11\x14\x06#!\"&5\x11\x16\x17\x04\x17\x1e\x02;\x022>\x0176%6\x13\x14\x06\a\x00\a\x0e\x04+\x02\".\x03'&$'.\x015463!2\x16\a\x00^B\xfa@B^,9\x01j\x879Gv3\x01\x013vG9\xaa\x01H9+bI\xfe\x88\\\nA+=6\x17\x01\x01\x176=+A\n[\xfe\xaa\">nSM\x05\xc0A_\x03:\xfc\xe6B^^B\x03\x1a1&\xf6c*/11/*{\xde'\x01VO\x903\xfe\xfb@\a/\x1d$\x12\x12$\x1d/\a@\xed\x18*\x93?Nh^\x00\x03\x00\x00\xff\xb0\x06\x00\x05l\x00\x03\x00\x0f\x00+\x00\x00\x01\x11!\x11\x01\x16\x06+\x01\"&5462\x16\x01\x11!\x114&#\"\x06\a\x06\x15\x11!\x12\x10/\x01!\x15#>\x0332\x16\x01]\xfe\xb6\x01_\x01gT\x02Rdg\xa6d\x04\x8f\xfe\xb7QV?U\x15\v\xfe\xb7\x02\x01\x01\x01I\x02\x14*Gg?\xab\xd0\x03\x8f\xfc!\x03\xdf\x012IbbIJaa\xfc\xdd\xfd\xc8\x02\x12iwE3\x1e3\xfd\xd7\x01\x8f\x01\xf000\x90 08\x1f\xe3\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x004\x00\x00\x00\x10\x02\x06\x04#\"$'&6?\x0163\x16\x17\x1e\x0132>\x024.\x02#\"\x06\a\x17\x16\a\x06#!\"&5\x11476\x1f\x016$32\x04\x16\x06\x00z\xce\xfe䜬\xfe\xcam\a\x01\b\x89\n\x0f\x10\aI\xd4wh\xbd\x8aQQ\x8a\xbdhb\xb4F\x89\x1f\x11\x11*\xfe@\x1a&('\x1e\x82k\x01\x13\x93\x9c\x01\x1c\xce\x03\x1c\xfe\xc8\xfe\xe4\xcez\x91\x84\n\x19\b\x8a\t\x02\n_hQ\x8a\xbdн\x8aQGB\x8a\x1e'(&\x1a\x01\xc0*\x11\x11\x1f\x81eoz\xce\x00\x01\x00(\xff\x15\x06\xeb\x05\xd8\x00q\x00\x00!\x14\x0f\x01\x06#\"'\x01&547\x01\a\x06\"'\x1e\x06\x15\x14\a\x0e\x05#\"'\x01&54>\x047632\x1e\x05\x17&47\x0162\x17.\x06547>\x0532\x17\x01\x16\x15\x14\x0e\x04\a\x06#\".\x05'\x16\x14\x0f\x01\x01632\x17\x01\x16\x06\xeb%k'45%\xfe\x95&+\xff\x00~\x0e(\x0e\x02\x15\x04\x10\x04\b\x03\x1c\x03\x1b\v\x1a\x12\x1a\r(\x1c\xfeh\x1c\t\t\x16\v\x1e\x03\x1e&\n\x10\x11\n\x11\x06\x14\x02\x0e\x0e\x01\\\x0e(\x0e\x02\x15\x04\x10\x04\b\x03\x1c\x03\x1b\v\x1a\x12\x1a\r(\x1c\x01\x98\x1c\t\t\x16\v\x1e\x03\x1e&\n\x10\x11\n\x11\x06\x14\x02\x0e\x0e~\x01\x00+54'\x01k%5%l%%\x01l$65+\x01\x00~\x0e\x0e\x02\x14\x06\x11\n\x11\x10\n&\x1e\x03\x1e\v\x16\t\t\x1c\x01\x98\x1c(\r\x1a\x12\x1a\v\x1b\x03\x1c\x03\b\x04\x10\x04\x15\x02\x0e(\x0e\x01\\\x0e\x0e\x02\x14\x06\x11\n\x11\x10\n&\x1e\x03\x1e\v\x16\t\t\x1c\xfeh\x1c(\r\x1a\x12\x1a\v\x1b\x03\x1c\x03\b\x04\x10\x04\x15\x02\x0e(\x0e~\xff\x00+%\xfe\x95'\x00\x00\a\x00\x00\xff\x80\a\x00\x05\x00\x00\a\x00\x0f\x00!\x00)\x001\x009\x00K\x00\x00\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x01\x136.\x01\x06\a\x03\x0e\x01\a\x06\x1e\x01676&$4&\"\x06\x14\x162\x004&\"\x06\x14\x162\x044&\"\x06\x14\x162\x01\x10\a\x06#!\"'&\x114\x126$ \x04\x16\x12\x01\x80KjKKj\x01\vKjKKj\x01\xf7e\x06\x1b2.\ae<^\x10\x14P\x9a\x8a\x14\x10,\x02bKjKKj\xfd\xcbKjKKj\x02\vKjKKj\x01\x8b\x8d\x13#\xfa\x86#\x13\x8d\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x01KjKKjK\x02\vjKKjK\xfe\x9f\x01~\x1a-\x0e\x1b\x1a\xfe\x82\x05M\x027>\x057&\x0254\x12$ \x04\x04L\xfeh\xfe\x9dя\x82W\x1b\x18.\x98{+9E=\xcc\x01c\xd1\xd1\x01Q\xf0\xfed\xf4FK\xc6\xfe\xfa1A\x05\x0f\x18\x04\x03\x05\x01\n\x02\f\x02\a0\x15)\x18\x1e\v\x9d\xb5\xf0\x01\x9c\x01\xe8\x01\x9c\x04\x80\x8b\xec\x89p\xcbJ2`[Q?l&\x06\b\x8b\xec\x01\x12\xec\xc7\xfe\xa4\xfe٫\b\xafC\x0e\b\x15\x11\x01\x04\x10\x04\x0f\x03\x0e\x02\b5\x178.H(Y\x01\x06\x96\xae\x01'\xab\xab\x00\x00\x03\x00\x00\xff\x80\a\x00\x05\x00\x00\x14\x00:\x00d\x00\x00\x00 \x04\x06\x15\x14\x16\x1f\x01\a6?\x01\x17\x1632$64&$ \x04\x16\x10\x06\x04#\"'\x06\a\x06\a#\"&'&4>\x057>\x047.\x01546\x01\x1e\x04\x17\x1e\x06\x14\a\x0e\x01'&'&'\x06# '\x1632$7>\x0154'\x1e\x01\x15\x14\x06\x03Y\xfe\xce\xfe\xf6\x9dj`a#\"\x1c,5NK\x99\x01\n\x9d\x9d\xfd\x9e\x01~\x01E\xbc\xbc\xfe\xbb\xbfVZ|\x9a$2\x03\v\x13\x02\x01\x01\x03\x02\x05\x03\x06\x01\x05$\x10\x1d\x15\n|\x8e\xbc\x05:\n\x15\x1d\x10$\x05\x01\x06\x03\x05\x02\x03\x01\x01\x03\x14\f2$\x9a|ZV\xfe\xf1\xc9:\x1e\xa1\x01(t}\x86\x17\x81\x96\x8e\x04\x80h\xb2fR\x9888T\x14\x13\x1f\n\x0eh\xb2̲\xe8\x89\xec\xfe\xea\xec\x89\x10X(\t\a\x10\r\x03\a\x06\x06\x04\a\x03\a\x01\x06&\x15%(\x18H\xd2w\x8b\xec\xfb\xf8\x18(%\x15&\x06\x01\a\x03\a\x04\x06\x06\a\x03\x0e\x10\x01\a\t(X\x10\x84\x04ZT\\\xf0\x86MKG\xd6{x\xd1\x00\x01\x00\x01\xff\x00\x03|\x05\x80\x00!\x00\x00\x01\x16\a\x01\x06#\"'.\x017\x13\x05\x06#\"'&7\x13>\x013!2\x16\x15\x14\a\x03%632\x03u\x12\v\xfd\xe4\r\x1d\x04\n\x11\x11\x04\xc5\xfej\x04\b\x12\r\x12\x05\xc9\x04\x18\x10\x01H\x13\x1a\x05\xab\x01\x8c\b\x04\x13\x03\xca\x14\x18\xfb{\x19\x02\x05\x1c\x10\x03(e\x01\v\x0f\x18\x039\x0e\x12\x19\x11\b\n\xfe1b\x02\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\x80\x00U\x00\x00\x01\x11\x14\x06#!\"&5\x1146;\x015!\x1532\x16\x15\x11\x14\x06#!\"&5\x1146;\x015!\x1532\x16\x15\x11\x14\x06#!\"&5\x1146;\x015463!5#\"&5\x11463!2\x16\x15\x11\x14\x06+\x01\x15!2\x16\x1d\x0132\x16\a\x008(\xfe\xc0(88(`\xfe\x00`(88(\xfe\xc0(88(`\xfe\x00`(88(\xfe\xc0(88(`L4\x02\x00`(88(\x01@(88(`\x02\x004L`(8\x01 \xfe\xc0(88(\x01@(8\xc0\xc08(\xfe\xc0(88(\x01@(8\xc0\xc08(\xfe\xc0(88(\x01@(8\xc04L\xc08(\x01@(88(\xfe\xc0(8\xc0L4\xc08\x00\x00\x03\x00\x00\xff\x80\x06\x80\x05\xc0\x00\x13\x00O\x00Y\x00\x00\x01\x11\x14\x06\"&5462\x16\x15\x14\x16265\x1162\x05\x14\x06#\"'.\x01#\"\x06\a\x0e\x01\a\x06#\"'.\x01'.\x01\"\x06\a\x0e\x01\a\x06#\"'.\x01'.\x01#\"\x06\a\x06#\"&5476\x00$32\x04\x1e\x01\x17\x16\x01\x15&\"\a5462\x16\x03\x80\x98И&4&NdN!>\x03!\x13\r\v\f1X:Dx+\a\x15\x04\v\x11\x12\v\x04\x15\a+w\x88w+\a\x15\x04\v\x12\x11\v\x04\x15\a+xD:X1\f\v\r\x13\x01-\x00\xff\x01U\xbe\x8c\x01\r\xe0\xa5!\x01\xfd\x00*,*&4&\x02\xc4\xfd\xbch\x98\x98h\x1a&&\x1a2NN2\x02D\v&\r\x13\n..J<\n$\x06\x11\x11\x06$\n\x01767\x14\a\x0e\x02\a\x16\x15\x14\a\x16\x15\x14\a\x16\x15\x14\x06#\x0e\x01\"&'\"&547&547&547.\x02'&54>\x022\x1e\x02\x02\xe0\x13\x1a\x13l4\r\x13\x13\r2cK\xa0Eo\x87\x8a\x87oED\n)\n\x80\r\xe4\r\x80\n)\nD\x80g-;<\x04/\x19\x19-\r?.\x14P^P\x14.?\r-\x19\x19/\x04<;-gY\x91\xb7\xbe\xb7\x91Y\x03\xc0\r\x13\x13\r.2\x13\x1a\x13 L4H|O--O|HeO\v,\v\x99\x91\x91\x99\v,\vOe\x9bq1Ls2\x1c6%\x1b\x1b%4\x1d\x17\x18.2,44,2.\x18\x17\x1d4%\x1b\x1b%6\x1c2sL1q\x9bc\xabqAAq\xab\x00\x02\x00\x00\xff\xa0\a\x00\x04\xe0\x00\x1a\x004\x00\x00\x01\x15\x14\x06#!\x15\x14\x06#\"'\x01&547\x01632\x16\x1d\x01!2\x16\x10\x14\a\x01\x06#\"&=\x01!\"&=\x01463!54632\x17\x01\a\x00\x13\r\xfa\xa0\x13\r\f\f\xfe\xc1\t\t\x01@\t\x0e\r\x13\x05`\r\x13\t\xfe\xc0\t\x0e\r\x13\xfa\xa0\r\x13\x13\r\x05`\x12\x0e\f\f\x01?\x01`\xc0\r\x13\xc0\r\x13\n\x01@\t\r\x0e\t\x01@\t\x13\r\xc0\x13\x02!\x1c\t\xfe\xc0\t\x13\r\xc0\x13\r\xc0\r\x13\xc0\x0e\x12\n\xfe\xc1\x00\x00\x00\x00\x02\x00\x00\x00\x00\a\x80\x05\x80\x00\x19\x005\x00\x00\x014&+\x01\x114&+\x01\"\x06\x15\x11#\"\x06\x15\x14\x17\x01\x1627\x016\x05\x14\x06#!\"\x005467&54\x0032\x04\x17632\x16\x15\x14\a\x1e\x01\x05\x00\x12\x0e\xe0\x13\r\xc0\r\x13\xe0\r\x13\t\x01`\t\x1c\t\x01_\n\x02\x80\xe1\x9f\xfb\xc0\xb9\xfe\xf9\x8cv\x02\x01,Ԝ\x01\x03;G_j\x96)\x82\xa7\x02`\x0e\x12\x01`\r\x13\x13\r\xfe\xa0\x13\r\x0e\t\xfe\xa0\t\t\x01_\fԟ\xe1\x01\a\xb9\x82\xdc7\x1e\r\xd4\x01,\xae\x90>\x96jL>\x1f\xd1\x00\x02\x00\x00\x00\x00\a\x80\x05\x80\x00\x19\x005\x00\x00\x014'\x01&\"\a\x01\x06\x15\x14\x16;\x01\x11\x14\x16;\x01265\x11326\x01\x14\x06#!\"\x005467&54\x0032\x04\x17632\x16\x15\x14\a\x1e\x01\x05\x00\t\xfe\xa0\t\x1c\t\xfe\xa1\n\x12\x0e\xe0\x13\r\xc0\r\x13\xe0\r\x13\x02\x80\xe1\x9f\xfb\xc0\xb9\xfe\xf9\x8cv\x02\x01,Ԝ\x01\x03;G_j\x96)\x82\xa7\x02\xa0\x0e\t\x01`\t\t\xfe\xa1\f\f\x0e\x12\xfe\xa0\r\x13\x13\r\x01`\x13\xfe\xed\x9f\xe1\x01\a\xb9\x82\xdc7\x1e\r\xd4\x01,\xae\x90>\x96jL>\x1f\xd1\x00\x00\x00\x00\x03\x00\x00\xff\x80\x05\x80\x05\x80\x00\a\x00X\x00`\x00\x00$\x14\x06\"&462\x05\x14\x06#!\"&54>\x037\x06\x1d\x01\x0e\x01\x15\x14\x162654&'547\x16 7\x16\x1d\x01\"\x06\x1d\x01\x06\x15\x14\x162654'5462\x16\x1d\x01\x06\x15\x14\x162654'54&'46.\x02'\x1e\x04\x00\x10\x06 &\x106 \x01\x80&4&&4\x04&\x92y\xfc\x96y\x92\v%:hD\x16:Fp\xa0pG9\x19\x84\x01F\x84\x19j\x96 8P8 LhL 8P8 E;\x01\x01\x04\n\bDh:%\v\xfe\xc0\xe1\xfe\xc2\xe1\xe1\x01>\xda4&&4&}y\x8a\x8ayD~\x96s[\x0f4D\xcb\x14d=PppP=d\x14\xcb>\x1fhh\x1f>@\x96jY\x1d*(88(*\x1dY4LL4Y\x1d*(88(*\x1dYDw\"\nA\x1f4*\x13\x0f[s\x96~\x03\xd8\xfe\xc2\xe1\xe1\x01>\xe1\x00\x00\x00\x02\x00\x00\xff\x80\x05\x80\x05\x80\x00\a\x00M\x00\x00\x004&\"\x06\x14\x1627\x14\x06\a\x11\x14\x04 $=\x01.\x015\x114632\x17>\x0132\x16\x14\x06#\"'\x11\x14\x16 65\x11\x06#\"&4632\x16\x17632\x16\x15\x11\x14\x06\a\x15\x14\x16 65\x11.\x015462\x16\x05\x00&4&&4\xa6G9\xfe\xf9\xfe\x8e\xfe\xf9\xa4\xdc&\x1a\x06\n\x11<#5KK5!\x1f\xbc\x01\b\xbc\x1f!5KK5#<\x11\n\x06\x1a&ܤ\xbc\x01\b\xbc9Gp\xa0p\x03&4&&4&@>b\x15\xfeu\x9f\xe1ោ\x14ؐ\x02\x00\x1a&\x02\x1e$KjK\x12\xfenj\x96\x96j\x01\x92\x12KjK$\x1e\x02&\x1a\xfe\x00\x90\xd8\x14\x84j\x96\x96j\x01\x8b\x15b>Ppp\x00\x04\x00\x00\xff\x80\a\x00\x05\x80\x00\x03\x00\r\x00\x1b\x00%\x00\x00\x01!5!\x05\x11#\"&5\x11463!\x11!\x1135463!2\x16\x1d\x01\x05\x11\x14\x06+\x01\x1132\x16\x02\x80\x02\x00\xfe\x00\xfe\xa0@\\\x84\x84\\\x04\xa0\xfc\x00\x808(\x02@(8\x02\x00\x84\\@@\\\x84\x04\x80\x80\x80\xfb\x00\x84\\\x03@\\\x84\xfb\x00\x05\x00\xa0(88(\xa0\xe0\xfc\xc0\\\x84\x05\x00\x84\x00\x02\x00@\xff\x00\x06\xc0\x06\x00\x00\v\x003\x00\x00\x044#\"&54\"\x15\x14\x163\x01\x14\x06#!\x14\x06\"&5!\"&5>\x0454\x127&5462\x16\x15\x14\a\x16\x12\x15\x14\x1e\x03\x03\x90\x10;U gI\x03@L4\xfe@\x96Ԗ\xfe@4L2RX='\xea\xbe\b8P8\b\xbe\xea'=XR\xb0 U;\x10\x10Ig\x0104Lj\x96\x96jL4*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x1c\xfe\xfb\x98\x8b\xf2\xaa\x93\\\x00\x00\x03\x00\x00\xff\x80\a@\x05\x00\x00\a\x00\x0f\x00\"\x00\x00\x004&+\x01\x1132\x01!\x14\x06#!\"&\x00\x10\x06+\x01\x15\x14\x06#!\"&5\x11463!2\x06\x80pP@@P\xf9\xf0\a\x00\x96j\xfb\x00j\x96\a@\xe1\x9f@\x84\\\xfd@\\\x84&\x1a\x04\x80\x9f\x030\xa0p\xfe\x80\xfd\xc0j\x96\x96\x04\t\xfe\xc2\xe1 \\\x84\x84\\\x02\xe0\x1a&\x00\x00\x02\x00\x00\xff\x00\x05\x80\x06\x00\x00-\x00B\x00\x00\x01\x11\x14\x06\a\x11\x14\x06+\x01\"&5\x11.\x015\x11462\x16\x15\x11\x14\x16265\x11462\x16\x15\x11\x14\x16265\x11462\x16\x05\x11\x14\x06+\x01\"&5\x11#\"&5\x11463!2\x16\x02\x80G9L4\x804L9G&4&&4&&4&&4&&4&\x03\x00L4\x804L\xe0\r\x13\xbc\x84\x01\x00\x1a&\x05\xc0\xfd\x80=d\x14\xfc\xf54LL4\x03\v\x14d=\x02\x80\x1a&&\x1a\xfe`\x1a&&\x1a\x01\xa0\x1a&&\x1a\xfe`\x1a&&\x1a\x01\xa0\x1a&&\x1a\xf9\xc04LL4\x02\x00\x13\r\x03 \x84\xbc&\x00\x06\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x003\x00C\x00S\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01463!2\x16\x1d\x01\x14\x06#!\"&5\x052\x16\x1d\x01\x14\x06#!\"&=\x01463\x012\x16\x1d\x01\x14\x06#!\"&=\x01463\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01\x00\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x02\xe0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03`\x0e\x12\x12\x0e@\x0e\x12\x12\x0e\xa0\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xff\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x00\x14\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\xbf\x00\xcf\x00\xdf\x00\xef\x00\xff\x01\x0f\x01\x1f\x01-\x01=\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01!\x11!\x11!5463!2\x16\x15\x01\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x02\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x01\x80\xfb\x80\x01\x80\x13\r\x01@\r\x13\x02\x00&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&\xe0@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfe\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xfa\x93\x06\x00\xfa\x00\xe0\r\x13\x13\r\x05`\xf9\x80\x1a&&\x1a\x06\x80\x1a&&\x00\r\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xb7\x00\xdb\x00\xf5\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01!\x11!\x15\x14\x06#!\"&=\x01!\x11!5463!2\x16\x15\x19\x014&+\x01\"\x06\x1d\x01#54&+\x01\"\x06\x15\x11\x14\x16;\x0126=\x013\x15\x14\x16;\x0126%\x11\x14\x06#!\"&5\x11463!\x11463!2\x16\x15\x11!2\x16\x01\x80\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x03\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x02\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x13\r@\r\x13\x13\r@\r\x13\x01\x00\x13\r@\r\x13\x13\r@\r\x13\xff\x00\x01\x80\xff\x008(\xfe@(8\xff\x00\x01\x80\x13\r\x01@\r\x13\x13\r@\r\x13\x80\x13\r@\r\x13\x13\r@\r\x13\x80\x13\r@\r\x13\x02\x00&\x1a\xfb\x00\x1a&&\x1a\x01@8(\x01\xc0(8\x01@\x1a&\xe0@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfd\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\xfe\xf3@\r\x13\x13\r@\r\x13\x13\xf3@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\r@\r\x13\x13\xfc\x93\x04\x80 (88( \xfb\x80\xe0\r\x13\x13\r\x03\xc0\x01@\r\x13\x13\r``\r\x13\x13\r\xfe\xc0\r\x13\x13\r``\r\x13\x13-\xfb\x00\x1a&&\x1a\x05\x00\x1a&\x01 (88(\xfe\xe0&\x00\x05\x00@\xff\x80\a\x80\x05\x80\x00\a\x00\x10\x00\x18\x00<\x00c\x00\x00$4&\"\x06\x14\x162\x01!\x11#\x06\x0f\x01\x06\a\x004&\"\x06\x14\x162\x1354&+\x0154&+\x01\"\x06\x1d\x01#\"\x06\x1d\x01\x14\x16;\x01\x15\x14\x16;\x0126=\x01326\x01\x11\x14\x06+\x01\x14\x06\"&5!\x14\x06\"&5#\"&463\x1146?\x01>\x01;\x01\x11463!2\x16\x02\x80KjKKj\xfe\xcb\x01\x80\x9e\x0e\b\xc3\a\x02\x05\x00KjKKj\xcb\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x01\x00&\x1a\xc0\x96Ԗ\xfe\x80\x96Ԗ\x80\x1a&&\x1a\x1a\x13\xc6\x13@\x1a\xa0&\x1a\x04\x80\x1a&KjKKjK\x02\x80\x01\x00\x02\a\xc3\f\n\xfd\xadjKKjK\x03 \xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x02.\xfb\x80\x1a&j\x96\x96jj\x96\x96j&4&\x01\xa0\x1a@\x13\xc6\x13\x1a\x01@\x1a&&\x00\x00\x05\x00\x00\xff\x80\a\x00\x05\x80\x00#\x00'\x001\x00?\x00I\x00\x00\x0154&+\x0154&+\x01\"\x06\x1d\x01#\"\x06\x1d\x01\x14\x16;\x01\x15\x14\x16;\x0126=\x01326\x01!5!\x05\x11#\"&5\x11463!\x11!\x1135463!2\x16\x1d\x01\x05\x11\x14\x06+\x01\x1132\x16\x05\x00\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\xfd\x80\x02\x00\xfe\x00\xfe\x80 \\\x84\x84\\\x04\xc0\xfb\xc0\xa08(\x02@(8\x02\x00\x84\\ \\\x84\x01\xa0\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x02\ue000\xfb\x00\x84\\\x03@\\\x84\xfb\x00\x05\x00\xa0(88(\xa0\xe0\xfc\xc0\\\x84\x05\x00\x84\x00\x00\x00\x00\x01\x00\x00\x00\x00\a\x80\x04\x80\x00:\x00\x00\x01\x06\r\x01\a#\x0132\x16\x14\x06+\x0353\x11#\a#'53535'575#5#573\x173\x11#5;\x022\x16\x14\x06+\x01\x013\x17\x05\x1e\x01\x17\a\x80\x01\xfe\xe1\xfe\xa0\xe0@\xfe\xdbE\x1a&&\x1a`\xa0@@\xa0\xc0` \x80\xc0\xc0\x80 `\xc0\xa0@@\xa0`\x1a&&\x1aE\x01%@\xe0\x01`\x80\x90\b\x02@ @ @\xfe\xa0\t\x0e\t \x01\xa0\xe0 \xc0 \b\x18\x80\x18\b \xc0 \xe0\x01\xa0 \t\x0e\t\xfe\xa0@ \x1c0\n\x00\x00\x00\x02\x00@\x00\x00\x06\x80\x05\x80\x00\x06\x00\x18\x00\x00\x01\x11!\x11\x14\x163\x01\x15!57#\"&5\x11'7!7!\x17\a\x11\x02\x80\xff\x00K5\x04\x80\xfb\x80\x80\x80\x9f\xe1@ \x01\xe0 \x03\xc0 @\x02\x80\x01\x80\xff\x005K\xfe@\xc0\xc0\xc0\xe1\x9f\x01@@\x80\x80\xc0 \xfc\xe0\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00\x00%\x114&+\x01\"\x06\x15\x11!\x114&+\x01\"\x06\x15\x11\x14\x16;\x01265\x11!\x11\x14\x16;\x0126\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\x80\x1a&\xfe\x00&\x1a\x80\x1a&&\x1a\x80\x1a&\x02\x00&\x1a\x80\x1a&\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xc0\x03\x80\x1a&&\x1a\xfe\xc0\x01@\x1a&&\x1a\xfc\x80\x1a&&\x1a\x01@\xfe\xc0\x1a&&\x03\xba\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00\x00\x0154&#!\x114&+\x01\"\x06\x15\x11!\"\x06\x1d\x01\x14\x163!\x11\x14\x16;\x01265\x11!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\xfe\xc0&\x1a\x80\x1a&\xfe\xc0\x1a&&\x1a\x01@&\x1a\x80\x1a&\x01@\x1a&\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02@\x80\x1a&\x01@\x1a&&\x1a\xfe\xc0&\x1a\x80\x1a&\xfe\xc0\x1a&&\x1a\x01@&\x02:\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00-\x00M\x03\xf3\x043\x00\x14\x00)\x00\x00$\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x16\x14\a\t\x01\x04\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x16\x14\a\t\x01\x02s\n2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\x01\x8a\n2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\xad\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\x00\x00\x00\x02\x00\r\x00M\x03\xd3\x043\x00\x14\x00)\x00\x00\x00\x14\a\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x04\x14\a\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x02S\n\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\x01\x8a\n\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\x02M\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\x00\x00\x02\x00M\x00\x8d\x043\x04S\x00\x14\x00)\x00\x00$\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\x0162\x17\x01\x12\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\x0162\x17\x01\x043\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\xed\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\n\xfe.\x01v\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\n\xfe.\x00\x00\x00\x02\x00M\x00\xad\x043\x04s\x00\x14\x00)\x00\x00\x00\x14\a\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x12\x14\a\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x043\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\x02\xad\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\n2\x01v\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\n2\x00\x00\x01\x00-\x00M\x02s\x043\x00\x14\x00\x00\x00\x14\a\t\x01\x16\x14\x0f\x01\x06\"'\x01&47\x0162\x1f\x01\x02s\n\xfew\x01\x89\n\n2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\x03\xed\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\x00\x00\x00\x01\x00\r\x00M\x02S\x043\x00\x14\x00\x00\x00\x14\a\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x02S\n\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\x02M\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\x00\x00\x00\x01\x00M\x01\r\x043\x03S\x00\x14\x00\x00\x00\x14\x0f\x01\x06\"'\t\x01\x06\"/\x01&47\x0162\x17\x01\x043\n2\n\x1a\n\xfew\xfew\n\x1a\n2\n\n\x01\xd2\n\x1a\n\x01\xd2\x01m\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\n\xfe.\x00\x00\x00\x01\x00M\x01-\x043\x03s\x00\x14\x00\x00\x00\x14\a\x01\x06\"'\x01&4?\x0162\x17\t\x0162\x1f\x01\x043\n\xfe.\n\x1a\n\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\x03-\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\n2\x00\x00\x00\x02\x00\x00\xff\x80\a\x80\x06\x00\x00\x0f\x00/\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\x14\x1e\x01\x15\x14\x06#!\"&54>\x015!\"&5\x11463!2\x16\a\x00\x13\r\xf9\xc0\r\x13\x13\r\x06@\r\x13\x80^B\xfd\xe0 &\x1a\xfe\x00\x1a& \xfd\xe0B^^B\x06@B^\x02 \x03@\r\x13\x13\r\xfc\xc0\r\x13\x13\x03M\xfb\xc0B^%Q=\r\x1a&&\x1a\x0e\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x94\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x04\xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x05\x80\x00!\x00C\x00\x00\x01\x11\x14\x06#!\"&5\x114>\x02;\x012\x16\x1d\x01\x14\x06+\x01\"\x06\x1d\x01\x14\x16;\x012\x16\x05\x11\x14\x06#!\"&5\x114>\x02;\x012\x16\x1d\x01\x14\x06+\x01\"\x06\x1d\x01\x14\x16;\x012\x16\x03\x00pP\xfe\x80PpQ\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0Pp\x03\x80pP\xfe\x80PpQ\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0Pp\x02@\xfe\x80PppP\x02\xc0h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8pP\xfe\x80PppP\x02\xc0h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8p\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x05\x80\x00!\x00C\x00\x00\x01\x11\x14\x0e\x02+\x01\"&=\x0146;\x0126=\x014&+\x01\"&5\x11463!2\x16\x05\x11\x14\x0e\x02+\x01\"&=\x0146;\x0126=\x014&+\x01\"&5\x11463!2\x16\x03\x00Q\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0PppP\x01\x80Pp\x03\x80Q\x8a\xbdh@\x1a&&\x1a@j\x968(\xe0PppP\x01\x80Pp\x04\xc0\xfd@h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8pP\x01\x80PppP\xfd@h\xbd\x8aQ&\x1a\x80\x1a&\x96j (8pP\x01\x80Ppp\x00\x00\x00\x00\b\x00@\xff@\x06\xc0\x06\x00\x00\t\x00\x11\x00\x19\x00#\x00+\x003\x00;\x00G\x00\x00$\x14\x06#\"&5462\x00\x14\x06\"&462\x00\x14\x06\"&462\x01\x14\x06#\"&462\x16\x00\x14\x06\"&462\x00\x14\x06\"&462\x00\x14\x06\"&462\x01\x14\x06#\"&54632\x16\x02\x0eK54LKj\x02=KjKKj\xfd\x8bKjKKj\x04\xfdL45KKjK\xfc<^\x84^^\x84\x04\xf0KjKKj\xfd\xcbp\xa0pp\xa0\x02\x82\x84\\]\x83\x83]\\\x84\xc3jKL45K\xfe\xe7jKKjK\x02ujKKjK\xfd\x8e4LKjKK\x03\xf1\x84^^\x84^\xfd\xa3jKKjK\x02\x90\xa0pp\xa0p\xfer]\x83\x83]\\\x84\x84\x00\x00\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x00\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x06\x00\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03Q\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x01\x00\x00\xff\x80\a\x00\x05\xc0\x00,\x00\x00\x01\x14\x03\x0e\x02\a\x06#\"&5465654.\x05+\x01\x11\x14\x06\"'\x01&47\x0162\x16\x15\x113 \x13\x16\a\x00\u007f\x03\x0f\f\a\f\x10\x0f\x11\x05\x05#>bq\x99\x9bb\xe0&4\x13\xfe\x00\x13\x13\x02\x00\x134&\xe0\x02ɢ5\x01\xa0\xa6\xfe\xe3\a\"\x1a\t\x11\x14\x0f\t#\x06D7e\xa0uU6\x1f\f\xff\x00\x1a&\x13\x02\x00\x134\x13\x02\x00\x13&\x1a\xff\x00\xfem\x86\x00\x04\x00\x00\xff\x80\x06\x80\x05\x00\x00\v\x00\x17\x001\x00X\x00\x00\x00\x14\x0e\x01\".\x014>\x012\x16\x04\x14\x0e\x01\".\x014>\x012\x16\x174&#\"\a\x06\"'&#\"\x06\x15\x14\x1e\x03;\x012>\x03\x13\x14\a\x0e\x04#\".\x04'&547&5472\x16\x17632\x17>\x013\x16\x15\x14\a\x16\x02\x80\x19=T=\x19\x19=T=\x02\x99\x19=T=\x19\x19=T=\xb9\x8av)\x9aG\xacG\x98+v\x8a@b\x92\x86R\xa8R\x86\x92b@\xe0=&\x87\x93\xc1\x96\\N\x80\xa7\x8a\x88j!>\x88\x1b3l\xa4k\x93\xa2\x94\x84i\xa4k3\x1b\x88\x01hPTDDTPTDDTPTDDTPTDD|x\xa8\x15\v\v\x15\xa8xX\x83K-\x0e\x0e-K\x83\x01\b\xcf|Mp<#\t\x06\x13)>dA{\xd0\xed\x9fRXtfOT# RNftWQ\xa0\x00\x00\x00\x00\x02\x00\x00\x00\x00\x06\x80\x05\x80\x00\x17\x00,\x00\x00%\x114&#!\"&=\x014&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x1d\x01!2\x16\x06\x008(\xfd@(88(\xfe\xc0(88(\x04\xc0(8\x80\x84\\\xfb@\\\x84\x84\\\x01@\\\x84\x02\xa0\\\x84\xe0\x02\xc0(88(@(88(\xfc@(88\x02\xe8\xfd@\\\x84\x84\\\x03\xc0\\\x84\x84\\ \x84\x00\x00\x03\x00\x00\x00\x00\au\x05\x80\x00\x11\x00'\x00E\x00\x00\x014#!\"\x06\a\x01\x06\x15\x143!267\x016%!54&#!\"&=\x014&#!\"\x06\x15\x11\x01>\x01\x05\x14\a\x01\x0e\x01#!\"&5\x11463!2\x16\x1d\x01!2\x16\x1d\x0132\x16\x17\x16\x06\xf55\xfb\xc0([\x1a\xfe\xda\x125\x04@(\\\x19\x01&\x12\xfb\x8b\x03\x008(\xfd\xc0(88(\xfe\xc0(8\x01\x00,\x90\x059.\xfe\xd9+\x92C\xfb\xc0\\\x84\x84\\\x01@\\\x84\x02 \\\x84\xc06Z\x16\x0f\x02]#+\x1f\xfe\x95\x18\x10#,\x1f\x01k\x16\xb4\xa0(88(@(88(\xfc\xab\x01;5E\xa3>:\xfe\x955E\x84\\\x03\xc0\\\x84\x84\\ \x84\\\xa01. \x00\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00\x1c\x00$\x004\x00@\x00\x00\x01\x0e\x01\"&'&676\x16\x17\x1e\x01267>\x01\x1e\x01\x00\x14\x06\"&462\x04\x14\x06\"&462\x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04n%\xca\xfe\xca%\b\x18\x1a\x19/\b\x19\x87\xa8\x87\x19\b02\x18\xfe\nKjKKj\x02KKjKKj\x01Kf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xcdy\x94\x94y\x19/\b\b\x18\x1aPccP\x1a\x18\x10/\x01\xcfjKKjKKjKKjK\xfd\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00\x1c\x00$\x004\x00@\x00\x00\x01\x16\x0e\x01&'.\x01\"\x06\a\x0e\x01'.\x017>\x012\x16\x00\x14\x06\"&462\x04\x14\x06\"&462\x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04n\b\x1820\b\x19\x87\xa8\x87\x19\b/\x19\x1a\x18\b%\xca\xfe\xca\xfe7KjKKj\x02KKjKKj\x01Kf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x013\x19/\x10\x18\x1aPccP\x1a\x18\b\b/\x19y\x94\x94\x02\tjKKjKKjKKjK\xfd\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x13\x00\x1b\x00+\x007\x00\x00\x00\x14\x06#!\"&463!2\x00\x14\x06\"&462\x04\x14\x06\"&462\x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x80&\x1a\xfd\x80\x1a&&\x1a\x02\x80\x1a\xfe&KjKKj\x02KKjKKj\x01Kf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xda4&&4&\x01\xb5jKKjKKjKKjK\xfd\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x04\x00\x00\x00\x00\a\x80\x04\x00\x00#\x00+\x003\x00C\x00\x00\x0154&+\x0154&+\x01\"\x06\x1d\x01#\"\x06\x1d\x01\x14\x16;\x01\x15\x14\x16;\x0126=\x01326\x044&\"\x06\x14\x162\x004&\"\x06\x14\x162$\x10\x00#\"'#\x06#\"\x00\x10\x003!2\x03@\x12\x0e\xc0\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x02@KjKKj\x01KKjKKj\x01K\xfe\xd4\xd4\xc0\x92ܒ\xc0\xd4\xfe\xd4\x01,\xd4\x03\x80\xd4\x01\xc0\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12gjKKjK\x01KjKKjK\xd4\xfeX\xfeԀ\x80\x01,\x01\xa8\x01,\x00\x00\x00\x0f\x00\x00\x00\x00\a\x80\x04\x80\x00\v\x00\x17\x00#\x00/\x00;\x00G\x00S\x00_\x00k\x00w\x00\x83\x00\x8f\x00\x9f\x00\xa3\x00\xb3\x00\x00\x01\x15\x14+\x01\"=\x014;\x0127\x15\x14+\x01\"=\x014;\x012'\x15\x14+\x01\"=\x014;\x012\x01\x15\x14#!\"=\x0143!2%\x15\x14+\x01\"=\x014;\x012'\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012'\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012\x01\x15\x14+\x01\"=\x014;\x012\x05\x15\x14+\x01\"=\x014;\x012\x05\x11\x14+\x01\"=\x014;\x0154;\x012\x13\x11!\x11\x01\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80\x10`\x10\x10`\x10\x80\x10\xe0\x10\x10\xe0\x10\x80\x10`\x10\x10`\x10\x04\x00\x10\xfc\xa0\x10\x10\x03`\x10\xfd\x80\x10`\x10\x10`\x10\x80\x10`\x10\x10`\x10\x01\x80\x10`\x10\x10`\x10\x80\x10`\x10\x10`\x10\x01\x80\x10`\x10\x10`\x10\x01\x80\x10`\x10\x10`\x10\xfe\x00\x10`\x10\x10`\x10\x01\x00\x10`\x10\x10`\x10\x01\x00\x10\xe0\x10\x10p\x10`\x10\x80\xf9\x80\a\x00K5\xf9\x805KK5\x06\x805K\x01p`\x10\x10`\x10\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xfd\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xfe\xf0`\x10\x10`\x10\xf0`\x10\x10`\x10\xfe\xf0`\x10\x10`\x10\xfe\xf0`\x10\x10`\x10\x01\xf0`\x10\x10`\x10\x10`\x10\x10`\x10\x10\xfe\xa0\x10\x10`\x10\xf0\x10\xfd\x00\x03\x80\xfc\x80\x03\x80\xfc\x805KK5\x03\x805KK\x00\x00\x00\x00\x03\x00@\xff\x80\a\x00\x05\x80\x00\x16\x00*\x00V\x00\x00\x01\x11\x06#\"'.\x01#\"\a\x11632\x1e\x02\x1f\x01\x1632\x01\x14\x06\a\x11\x14\x06+\x01\"&5\x11.\x015462\x16\x05\x11\x14\a\x06\a\x06#\"/\x01.\x02#\"\x04\a\x06#\"'&5\x1147>\x0332\x16\x17\x16327676\x17\x16\x06\x80\xa9\x89R?d\xa8^\xad\xe6\xf5\xbc7ac77\x1c,9x\xfbm#\x1d\x12\x0e@\x0e\x12\x1d#KjK\x05\xc0#\n\aڗXF\x1c@Fp:f\xfe\xf5_\x0f\x12\x10\x10 \x1f#W\x8d\xa4Ip\xc2p&3z\xbc\x16\t\x1f\x1f\x1f\x01\xeb\x02h[ 17\u007f\xfd\xa9q\x0f%\x19\x1b\x0e\x16\x03q#:\x11\xfb\x0e\x0e\x12\x12\x0e\x04\xf2\x11:#5KKu\xfd\x05'\x12\x05\x04t#\x0e!\x1e\x1cX:\t\b\x13%\x02\xe6#\x14\x15+=&>7\x13p\f\x05\x10\x12\x14\x00\x00\x06\x00@\xff\x80\a\x00\x05\x80\x00\x05\x00\v\x00*\x002\x00F\x00r\x00\x00\x015\x06\a\x156\x135\x06\a\x156\x015\x06'5&'.\t#\"\a\x1532\x16\x17\x16\x17\x15\x1632\x135\x06#\"'\x15\x16\x01\x14\x06\a\x11\x14\x06+\x01\"&5\x11.\x015462\x16\x05\x11\x14\a\x06\a\x06#\"/\x01.\x02#\"\x04\a\x06#\"'&5\x1147>\x0332\x16\x17\x16327676\x17\x16\x03@\xb5\xcbͳ\xac\xd4\xd7\x03\xe9\xeb\x95\x14\x13\x058\r2\x13.\x1a,#,\x16\x17\x1a\x13f\xb5k\x13\x14*1x\xad\xa9\x89-!\x94\xfb\xac#\x1d\x12\x0e@\x0e\x12\x1d#KjK\x05\xc0#\n\aڗXF\x1c@Fp:f\xfe\xf5_\x0f\x12\x10\x10 \x1f#W\x8d\xa4Ip\xc2p&3z\xbc\x16\t\x1f\x1f\x1f\x02\x18\xc0\x10e\xb9`\x01\xb0\xc5\bv\xbdo\xfe8\xb8t-\xe0\x06\t\x03\x1c\x06\x18\a\x13\x06\v\x04\x04\x03\xde:5\t\x06\xbc\x11\x02\a\xbd[\b\xc4*\x01\xee#:\x11\xfb\x0e\x0e\x12\x12\x0e\x04\xf2\x11:#5KKu\xfd\x05'\x12\x05\x04t#\x0e!\x1e\x1cX:\t\b\x13%\x02\xe6#\x14\x15+=&>7\x13p\f\x05\x10\x12\x14\x00\x02\x00\r\x00\x00\x06\x80\x043\x00\x14\x00$\x00\x00\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x16\x14\x01\x15\x14\x06#!\"&=\x01463!2\x16\x02I\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\x04-\x12\x0e\xfc@\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x02)\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\xfe-@\x0e\x12\x12\x0e@\x0e\x12\x12\x00\x00\x00\x00\x03\x00-\xff\x93\aS\x04\xed\x00\x14\x00$\x009\x00\x00%\a\x06\"'\x01&47\x0162\x1f\x01\x16\x14\a\t\x01\x16\x14\t\x01\x0e\x01/\x01.\x017\x01>\x01\x1f\x01\x1e\x01\t\x01\x06\"/\x01&47\t\x01&4?\x0162\x17\x01\x16\x14\x02i2\n\x1a\n\xfe.\n\n\x01\xd2\n\x1a\n2\n\n\xfew\x01\x89\n\x02E\xfe\x8b\x04\x17\f>\r\r\x04\x01u\x04\x17\f>\r\r\x02\x8d\xfe.\n\x1a\n2\n\n\x01\x89\xfew\n\n2\n\x1a\n\x01\xd2\n\x892\n\n\x01\xd2\n\x1a\n\x01\xd2\n\n2\n\x1a\n\xfew\xfew\n\x1a\x04!\xfa\xf5\r\r\x04\x11\x04\x17\r\x05\v\r\r\x04\x11\x04\x17\xfdh\xfe.\n\n2\n\x1a\n\x01\x89\x01\x89\n\x1a\n2\n\n\xfe.\n\x1a\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\xbb\x00\x15\x00;\x00\x00\x01\x15\x14\a\x06#\"'\x01&47\x016\x17\x16\x1d\x01\x01\x06\x14\x17\x01\x14\x0e\x03\a\x06#\"'&7\x12'.\x01'\x15\x14\a\x06#\"'\x01&47\x016\x17\x16\x15\x11\x04\x17\x16\x02\x80'\r\f\x1b\x12\xfe\x00\x13\x13\x02\x00\x1d)'\xfes\x13\x13\x06\r\"+5\x1c\x06\b\x14\x06\x03\x19\x02+\x95@ա'\r\f\x1b\x12\xfe\x00\x13\x13\x02\x00\x1d)'\x01\x9b\xbc\xa9\x01\xc6F*\x11\x05\x13\x02\x00\x134\x13\x02\x00\x1f\x11\x11*E\xfer\x134\x13\xfeM:\x97}}8\f\x11\x01\b\x1a\x01\x90\xa5GO\r\xfb*\x11\x05\x13\x02\x00\x134\x13\x02\x00\x1f\x11\x11*\xfe\xfa\x1c\xc1\xad\x00\x00\x00\x00\x02\x00\x02\xff\xad\x06~\x05\xe0\x00\n\x00(\x00\x00\x01-\x01/\x01\x03\x11\x17\x05\x03'\t\x01\x13\x16\x06#\"'%\x05\x06#\"&7\x13\x01&67%\x13632\x17\x13\x05\x1e\x01\x04\xa2\x01\x01\xfe\x9cB\x1e\x9f;\x01><\f\x01\xf5\xfe\x95V\x05\x16\x17\x11\x17\xfe?\xfe?\x17\x11\x17\x16\x05V\xfe\x94 \x12-\x01\xf6\xe1\x14\x1d\x1c\x15\xe1\x01\xf6-\x12\x02C\xfa4\n<\x01B\xfc=\x1f\xa8\x01cB\x015\xfe\x9e\xfe\f!%\f\xec\xec\f%!\x01\xf4\x01b 7\aI\x01\xc7))\xfe9I\a7\x00\x00\x00\x01\x00\x02\xff\x80\x05\x80\x05\x00\x00\x16\x00\x00\t\x01\x06#\"'.\x015\x11!\".\x0167\x01632\x17\x1e\x01\x05y\xfd\x80\x11(\x05\n\x16\x1b\xfd\xc0\x16#\n\x12\x14\x05\x00\r\x10\x1b\x12\x0f\a\x04\xa3\xfb\x00#\x02\x05#\x16\x02@\x1b,(\n\x02\x80\a\x13\x0e)\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00\x02\x00\x05\x008\x00\x00\x01!\x11\t\x01!\x01\x15\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01!\"&5\x11#\"&=\x0146;\x01546;\x012\x16\x1d\x01!762\x17\x16\x14\x0f\x01\x1132\x16\x02-\x02S\xfd\x80\x02S\xfd\xad\x04\x80\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\xfc\xa0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\xc0\x0e\x12\x03S\xf6\n\x1a\n\t\t\xf7\xe0\x0e\x12\x01\x00\x02S\xfd\xda\x02S\xfd`\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\x03`\x12\x0e\xc0\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\xf7\t\t\n\x1a\n\xf6\xfc\xad\x12\x00\x00\x00\x04\x00\x00\xff\x80\x04\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00K\x00\x00$4&\"\x06\x14\x162\x124&\"\x06\x14\x162\x044&\"\x06\x14\x1627\x14\x06\a\x02\a\x06\a\x0e\x01\x1d\x01\x1e\x01\x15\x14\x06\"&5467\x11.\x015462\x16\x15\x14\x06\a\x1167>\x055.\x015462\x16\x01 8P88P88P88P\x02\xb88P88P\x984,\x02\xe0C\x88\x80S,4p\xa0p4,,4p\xa0p4,6d7AL*'\x11,4p\xa0p\x18P88P8\x04\xb8P88P8HP88P8`4Y\x19\xfe\xe1\u007f&+(>E\x1a\x19Y4PppP4Y\x19\x034\x19Y4PppP4Y\x19\xfe\x0f\x1a\x1f\x11\x19%*\x0154&#\"\a\x06\a\x06#\"/\x01.\x017\x12!2\x1e\x02\x02\xc0\x18\x10\xf0\x10\x18\x18\x10\xf0\x10\x18\x01<\x1f'G,')7\x18\x10\xf0\x0f\x15\x82N;2]=A+#H\r\x12\f\r\xa4\r\x05\b\xa0\x010P\xa2\x82R\x01\x18\xf0\x10\x18\x18\x10\xf0\x10\x18\x18\x02H6^;<\x1b\x16\x17T\x19\x11\x1f%\x13-S\x93#\x1b:/*@\x1d\x19Z\x10\b}\n\x1e\r\x01\n>h\x97\x00\x00\x00\x02\x00\x00\x00\x00\x02\x80\x05\x80\x00\x1e\x00.\x00\x00%\x15\x14\x06#!\"&=\x0146;\x01\x11#\"&=\x01463!2\x16\x15\x1132\x16\x03\x15\x14\x06#!\"&=\x01463!2\x16\x02\x80&\x1a\xfe\x00\x1a&&\x1a@@\x1a&&\x1a\x01\x80\x1a&@\x1a&\x80&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\xc0\x80\x1a&&\x1a\x80\x1a&\x01\x80&\x1a\x80\x1a&&\x1a\xfd\xc0&\x04f\xc0\x1a&&\x1a\xc0\x1a&&\x00\x00\x02\x00b\x00\x00\x02\x1e\x05\x80\x00\x0f\x00\x1f\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x13\x03\x0e\x01#!\"&'\x03&63!2\x16\x02\x00&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\x1e\x1c\x01'\x1a\xff\x00\x1a'\x01\x1c\x01%\x1a\x01@\x1a%\x01 \xe0\x1a&&\x1a\xe0\x1a&&\x04\x06\xfd\x00\x1a&&\x1a\x03\x00\x1a&&\x00\x02\x00\x05\x00\x00\x05\xfe\x05k\x00%\x00J\x00\x00%\x15#/\x01&'#\x0e\x02\a\x06\x0f\x01!53\x13\x03#5!\x17\x16\x17\x16\x1736?\x02!\x15#\x03\x13\x01\x15!'&54>\x0454&#\"\a\x06\a'67632\x16\x15\x14\x0e\x04\a35\x03\x81\xf8\x9f\x18\b\x03\x03\x01\x03\x04\x01\n\x0f\x9b\xfe\xfe\x80Ź\x89\x01\x14\x8b\x02\x15\b\x03\x03\x03\b\x19\x8c\x01\x01}\xb8\xcc\x02\xea\xfd\xfe\x03\x044NZN4;)3.\x0e\x16i\x1a%Sin\x881KXL7\x03觧\xfc*\t\f\x03\a\t\x02\x14\x18\xfa\xa7\x01#\x01\x10\xa8\xe4\x04&\t\f\t\f*\xe4\xa8\xfe\xf5\xfe\xd8\x02\xa7\xce\x1b\x1c\x12@jC?.>!&1'\v\x1b\\%\x1dAwc8^;:+\x0454&#\"\a\x06\a'67632\x16\x15\x14\x0e\x03\a35\x03\x81\xf8\x9f\x18\b\x03\x03\x01\x03\x04\x01\n\x0f\x9b\xfe\xfe\x80Ź\x89\x01\x14\x8b\x02\x15\b\x03\x03\x03\b\x19\x8c\x01\x01}\xb8\xcc\x02\xec\xfd\xfe\x04\x034NZN4;)3.\x0e\x16i\x1a%Pln\x88EcdJ\x04觧\xfc*\t\f\x03\a\t\x02\x14\x18\xfa\xa7\x01#\x01\x10\xa8\xe4\x04&\t\f\t\f*\xe4\xa8\xfe\xf5\xfe\xd8\xd9\xce\x1b-\x01@jC?.>!&1'\v\x1b\\%\x1dAwcBiC:D'P\x00\x00\x00\x02\x00\x01\x00\x00\a\u007f\x05\x00\x00\x03\x00\x17\x00\x00%\x01!\t\x01\x16\x06\a\x01\x06#!\"&'&67\x0163!2\x16\x03\x80\x01P\xfd\x00\xfe\xb0\x06\xf5\x0f\v\x19\xfc\x80&:\xfd\x00&?\x10\x0f\v\x19\x03\x80&:\x03\x00&?\x80\x01\x80\xfe\x80\x045\"K\x1c\xfc\x00,)\"\"K\x1c\x04\x00,)\x00\x00\x01\x00\x00\xff\xdc\x06\x80\x06\x00\x00h\x00\x00\x01\x14\x06#\".\x02#\"\x15\x14\x16\a\x15\"\a\x0e\x02#\"&54>\x0254&#\"\x06\x15\x14\x1e\x02\x15\x14\a\x06#\"'.\x01/\x01\"'\"5\x11\x1e\x02\x17\x16327654.\x0254632\x16\x15\x14\x0e\x02\x15\x14\x163267\x15\x0e\x02\a\x06\x15\x14\x17\x1632>\x0232\x16\x06\x80YO)I-D%n \x01\x16\v\"\u007fh.=T#)#lQTv\x1e%\x1e.%P_\x96\t%\t\r\x01\x02\x02\x02\x1f%\x03\x96_P%.\x1e%\x1evUPl#)#T=@\xe8/\x01\x05\x05\x01\x18#,-\x1691P+R[\x01\xb6Ql#)#|'\x98'\x05\x01\x03\x11\n59%D-I)OY[R+P19\x16-,#\x18\x02\x04\x02\x02\x01\x01\x04\x00\x01\x05\x05\x01\x18#,-\x1691P+R[YO)I-D%95\x1e\x02\x02\x02\x1f%\x03\x96_P%.\x1e%\x1ev\x00\x00\x02\x00\x00\xff\x80\x04\x80\x06\x00\x00'\x003\x00\x00\x01\x15\x14\x00\a\x15!2\x16\x14\x06#!\"&463!5&\x00=\x01462\x16\x1d\x01\x14\x00 \x00=\x01462\x16\x01\x11\x14\x06 &5\x1146 \x16\x04\x80\xfe\xd9\xd9\x01\x00\x1a&&\x1a\xfd\x80\x1a&&\x1a\x01\x00\xd9\xfe\xd9&4&\x01\a\x01r\x01\a&4&\xff\x00\xbc\xfe\xf8\xbc\xbc\x01\b\xbc\x03@\x80\xdd\xfe\xb9\x18\x84&4&&4&\x84\x18\x01G݀\x1a&&\x1a\x80\xb9\xfe\xf9\x01\a\xb9\x80\x1a&&\x01f\xfe\x00\x84\xbc\xbc\x84\x02\x00\x84\xbc\xbc\x00\x03\x00\r\xff\x80\x05s\x06\x00\x00\v\x00C\x00K\x00\x00\x01\a&=\x01462\x16\x1d\x01\x14\t\x01\x15\x14\x06#\"'\a\x1632\x00=\x01462\x16\x1d\x01\x14\x00\a\x15!2\x16\x14\x06#!\"&463!5&'\a\x06\"/\x01&47\x0162\x1f\x01\x16\x14%\x01\x114632\x16\x01\x0fe*&4&\x04i\xfe\x97\xbc\x8476`al\xb9\x01\a&4&\xfe\xd9\xd9\x01\x00\x1a&&\x1a\xfd\x80\x1a&&\x1a\x01\x00}n\xfe\n\x1a\nR\n\n\x04\xd2\n\x1a\nR\n\xfez\xfd\x93\xbc\x84f\xa5\x02Oego\x80\x1a&&\x1a\x805\x02\x1e\xfe\x97\x80\x84\xbc\x13`3\x01\a\xb9\x80\x1a&&\x1a\x80\xdd\xfe\xb9\x18\x84&4&&4&\x84\rD\xfe\n\nR\n\x1a\n\x04\xd2\n\nR\n\x1az\xfd\x93\x02\x00\x84\xbcv\x00\x00\x00\x02\x00\x00\xff\x80\x05\x00\x05\x80\x00\x06\x00\"\x00\x00\x01\x11!\x11676\x13\x11\x14\x0e\x05\a\x06\"'.\x065\x11463!2\x16\x04@\xfe@w^\xeb\xc0Cc\x89t~5\x10\f\x1c\f\x105~t\x89cC&\x1a\x04\x80\x1a&\x02@\x02\x80\xfb\x8f?J\xb8\x03\xb0\xfd\x00V\xa9\x83|RI\x1a\a\x06\x06\a\x1aIR|\x83\xa9V\x03\x00\x1a&&\x00\x00\x00\x00\x04\x00\x00\xff\x00\x06\x80\x06\x00\x00\x03\x00\x13\x00#\x00G\x00\x00\x17!\x11!%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x11\x14\x06#!\"&5\x1146;\x01546;\x012\x16\x1d\x01!546;\x012\x16\x1d\x0132\x16\x80\x05\x80\xfa\x80\x01\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x03\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x80L4\xfa\x804LL4\x80^B@B^\x01\x80^B@B^\x804L\x80\x04\x00\xc0\x01 \x0e\x12\x12\x0e\xfe\xe0\x0e\x12\x12\x0e\x01 \x0e\x12\x12\x0e\xfe\xe0\x0e\x12\x12N\xfb\x004LL4\x05\x004L`B^^B``B^^B`L\x00\x00\x00\x02\x00\x03\xff\x80\x05\x80\x05\xe0\x00\a\x00L\x00\x00\x004&\"\x06\x14\x162%\x11\x14\a\x06#\"'%.\x015!\x15\x1e\x01\x15\x11\x14\x06#!\"&5\x114675#\"\x0e\x03\a\x06#\"'.\x017>\x047&5462\x16\x15\x14\a!467%632\x17\x16\x02\x00&4&&4\x03\xa6\f\b\f\x04\x03\xfe@\v\x0e\xff\x00o\x91&\x1a\xfe\x00\x1a&}c ;pG=\x14\x04\x11(\x10\r\x17\x11\f\x05\x138Ai8\x19^\x84^\x0e\x01.\x0e\v\x01\xc0\x03\x04\f\b\f\x05&4&&4&`\xfe\xc0\x10\t\a\x01`\x02\x12\vf\x17\xb0s\xfc\xe0\x1a&&\x1a\x03 j\xa9\x1eo/;J!\b#\a\f2\x18\n KAE\x12*,B^^B!\x1f\v\x12\x02`\x01\a\t\x00\x00\x02\x00$\xff \x06\x80\x05\x80\x00\a\x00-\x00\x00\x004&\"\x06\x14\x162\x01\x14\x02\a\x06\a\x03\x06\a\x05\x06#\"/\x01&7\x13\x01\x05\x06#\"/\x01&7\x1367%676$!2\x16\x05\xa08P88P\x01\x18\x97\xb2Qr\x14\x02\x0e\xfe\x80\a\t\f\v@\r\x05U\xfe\xe7\xfe\xec\x03\x06\x0e\t@\x11\f\xe0\n\x10\x01{`P\xbc\x01T\x01\x05\x0e\x14\x04\x18P88P8\x01\x80\xf9\xfe\x95\xb3P`\xfe\x85\x10\n\xe0\x04\t@\x0e\x12\x01\x14\x01\x19U\x01\t@\x13\x14\x01\x80\x0e\x02\x14rQ\xbb\x8e\x13\x00\x00\x00\x01\x00\x00\x00\x00\x06\xd1\x05\x00\x00\x16\x00\x00\x01\x03!\x136'&+\x01\x03!\x13!\x03!\x13\x03!2\x16\x17\x1e\x01\x06Ѥ\xfe\xb2\xb2\r\x1c\x1b8\xa9\xcc\xfe\xb2\xcc\xfe\xe2\xcc\xfe\xb2̙\x04\xfce\xb1;<*\x02\xfb\xfd\x05\x03@8 !\xfcG\x03\xb9\xfcG\x03\xb9\x01GQII\xbf\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00%764'\t\x0164/\x01&\"\a\x01\x06\x14\x17\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x8df\x13\x13\xfe\xcd\x013\x13\x13f\x134\x13\xfe:\x13\x13\x01\xc6\x134\x02\x86\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x8df\x134\x13\x013\x013\x134\x13f\x13\x13\xfe:\x134\x13\xfe:\x13\x02\xd7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00%\x0164'\x01&\"\x0f\x01\x06\x14\x17\t\x01\x06\x14\x1f\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x02\xcd\x01\xc6\x13\x13\xfe:\x134\x13f\x13\x13\x013\xfe\xcd\x13\x13f\x134\x03F\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x8d\x01\xc6\x134\x13\x01\xc6\x13\x13f\x134\x13\xfe\xcd\xfe\xcd\x134\x13f\x13\x02\xd7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00\x01764'\x01&\"\a\x01\x06\x14\x1f\x01\x1627\t\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x8df\x13\x13\xfe:\x134\x13\xfe:\x13\x13f\x134\x13\x013\x013\x134\x01\x86\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\x8df\x134\x13\x01\xc6\x13\x13\xfe:\x134\x13f\x13\x13\x013\xfe\xcd\x13\x01\xd7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00 \x00\x00%\x0164/\x01&\"\a\t\x01&\"\x0f\x01\x06\x14\x17\x01\x162\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03-\x01\xc6\x13\x13f\x134\x13\xfe\xcd\xfe\xcd\x134\x13f\x13\x13\x01\xc6\x134\x02\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xed\x01\xc6\x134\x13f\x13\x13\xfe\xcd\x013\x13\x13f\x134\x13\xfe:\x13\x02w\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x02\x00\x00\xff@\x05\x80\x05\x80\x00\x11\x00\x16\x00\x00\x017!\x13!\x0f\x01/\x01#\x13\x0535%\x13!'\x01!\x03\x05%\x04j\x10\xfc\x8c/\x02d\x16\xc5\xc4\r\xaf\x16\x01j\x04\x01g2\xfd|\x0f\xfe8\x05\x80\x80\xfd\xbe\xfd\xc2\x03\xab\xaf\xfd\xea\xe455\x8c\xfe\xead\x01c\x02 \xb5\x01\xd5\xfab\xa2\xa2\x00\x00\x00\x01\x00\f\xff@\x06\xf4\x05\x80\x00\x0f\x00\x00\x01!\t\x02\x13!\a\x05%\x13!\x13!7!\x01\x13\x05\xe1\xfe\xf6\xfc\xdc\xfdFG\x01)\x1d\x01\xa6\x01\xe6D\xfbH:\x04\xb9&\xfbH\x05\x80\xfa\xcb\xfe\xf5\x01\v\x01d\x93\xa1\xa1\x01S\x01)\xbf\x00\x00\x00\x02\x00\x00\xff\x10\a\x00\x06\x00\x00\a\x00U\x00\x00\x004&\"\x06\x14\x162\x01\x11\x14\a\x06#\"/\x01\x06\x04 $'\a\x06#\"'&5\x11463!2\x17\x16\x0f\x01\x1e\x01\x17\x11#\"&=\x0146;\x015.\x015462\x16\x15\x14\x06\a\x1532\x16\x1d\x01\x14\x06+\x01\x11>\x017'&763!2\x16\x03\xc0&4&&4\x03f\x14\b\x04\f\v]w\xfeq\xfe4\xfeqw]\t\x0e\x04\b\x14\x12\x0e\x01`\x16\b\b\x0fdC\xf5\x95\xc0\x1a&&\x1a\xc0:F\x96ԖF:\xc0\x1a&&\x1a\xc0\x95\xf5Cd\x0f\b\b\x16\x01`\x0e\x12\x04\xe64&&4&\xfc\xa0\xfe\xa0\x16\b\x02\t]\x8f\xa7\xa7\x8f]\t\x02\b\x16\x01`\x0e\x12\x14\x13\x10d[}\x14\x02\x87&\x1a\x80\x1a&\xa3\"uFj\x96\x96jFu\"\xa3&\x1a\x80\x1a&\xfdy\x14}[d\x10\x13\x14\x12\x00\x01\x00\x00\x00\x00\x04\x80\x06\x00\x00#\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x1146;\x01\x114\x00 \x00\x15\x14\x06+\x01\"&54&\"\x06\x15\x11\x04 (88(\xfc@(88( \x01\a\x01r\x01\a&\x1a@\x1a&\x96Ԗ\x03\x008(\xfd\xc0(88(\x02@(8\x01@\xb9\x01\a\xfe\xf9\xb9\x1a&&\x1aj\x96\x96j\xfe\xc0\x00\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00'\x003\x00\x00\x00\x14\x06\"&462\x00\x10& \x06\x10\x16 \x00\x10\x00 \x00\x10\x00 \x00\x10.\x02 \x0e\x02\x10\x1e\x02 >\x01\x12\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00\x96Ԗ\x96\xd4\x01\x16\xe1\xfe\xc2\xe1\xe1\x01>\x01a\xfe\xd4\xfeX\xfe\xd4\x01,\x01\xa8\x01\xacf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xe6\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xeaԖ\x96Ԗ\xfea\x01>\xe1\xe1\xfe\xc2\xe1\x02T\xfeX\xfe\xd4\x01,\x01\xa8\x01,\xfd~\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x02@\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\x02\x00\x05\x80\x03\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x808(\xc0(88(\xc0(8\x02\x008(\xc0(88(\xc0(8\x02\x008(\xc0(88(\xc0(8\x03 \xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(88\x00\x00\x00\x00\x03\x00\x00\x00\x00\x01\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x808(\xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(88(\xc0(8\x01 \xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x01\xd8\xc0(88(\xc0(88\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x1b\x005\x00E\x00\x00$4&\"\x06\x14\x162%&\x00'&\x06\x1d\x01\x14\x16\x17\x1e\x01\x17\x1e\x01;\x0126%&\x02.\x01$'&\a\x06\x1d\x01\x14\x16\x17\x16\x04\x12\x17\x1e\x01;\x01276\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\x00KjKKj\x01\xaa\r\xfe\xb9\xe9\x0e\x14\x11\r\x9a\xdc\v\x01\x12\r\x80\r\x14\x01\u007f\x05f\xb1\xe9\xfe\xe1\x9a\x0e\t\n\x12\r\xcc\x01\\\xd1\a\x01\x12\r\x80\r\n\v\x01\x1f\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xcbjKKjK\"\xe9\x01G\r\x01\x14\r\x80\r\x12\x01\vܚ\r\x11\x14\r\x9a\x01\x1f\xe9\xb1f\x05\x01\n\n\r\x80\r\x12\x01\a\xd1\xfe\xa4\xcc\r\x12\n\t\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x1b\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x0164'\x01&\a\x06\x15\x11\x14\x17\x16327\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x03\xb2 \xfd\xe0\x1f! \x10\x10\x11\x0f\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfd\x97\x12J\x12\x01@\x13\x12\x13%\xfd\x80%\x13\b\t\x00\x03\x006\xff5\x06\xcb\x05\xca\x00\x03\x00\x13\x00/\x00\x00\t\x0564'\x01&\"\a\x01\x06\x14\x17\x01\x162\t\x01\x06\"/\x0164&\"\a'&47\x0162\x1f\x01\x06\x14\x1627\x17\x16\x14\x04\x00\x01<\xfd\xc4\xfe\xc4\x01i\x02j\x13\x13\xfe\x96\x126\x12\xfd\x96\x13\x13\x01j\x126\x03\x8b\xfcu%k%~8p\xa08}%%\x03\x8b%k%}8p\xa08~%\x04<\xfe\xc4\xfd\xc4\x01<\xfei\x02j\x134\x13\x01j\x12\x12\xfd\x96\x134\x13\xfe\x96\x12\x02\x8f\xfct%%~8\xa0p8~%k%\x03\x8a%%}8\xa0p8}%k\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00\x00\x0154&#!\"\x06\x1d\x01\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\xfc\x80\x1a&&\x1a\x03\x80\x1a&\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02@\x80\x1a&&\x1a\x80\x1a&&\x02:\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\x00\x00\x05\x80\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x15\x14\x06#!\"&=\x01463!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04\x80\x12\x0e\xfc\xc0\x0e\x12\x12\x0e\x03@\x0e\x12\x80^B\xfc\xc0B^^B\x03@B^\x80\xa9w\xfc\xc0w\xa9\xa9w\x03@w\xa9\x02\xe0@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe2\x03@B^^B\xfc\xc0B^^\x03\x82\xfc\xc0w\xa9\xa9w\x03@w\xa9\xa9\x00\x00\x01\x00\x03\x00\x00\x03\xfa\x05\u007f\x00\x1c\x00\x00\x01\x06+\x01\x11\x14\x06#!\"'&?\x0163!\x11#\"'&7\x0162\x17\x01\x16\x03\xfa\x12(\xc0\x12\x0e\xfd@\x15\b\b\f\xa0\t\x10\x01@\xc0(\x12\x11\x1a\x01@\x12>\x12\x01@\x1b\x03\xa5%\xfc\xa0\x0e\x12\x12\x14\x0f\xc0\v\x02\x80%%\x1f\x01\x80\x16\x16\xfe\x80 \x00\x00\x00\x01\x00\x03\xff\x80\x03\xfa\x05\x00\x00\x1b\x00\x00\x13!2\x16\x15\x1132\x16\a\x01\x06\"'\x01&76;\x01\x11!\"/\x01&76 \x02\xc0\r\x13\xc0($\x1b\xfe\xc0\x12>\x12\xfe\xc0\x1a\x11\x12(\xc0\xfe\xc0\x0e\v\xa0\r\t\t\x05\x00\x13\x0e\xfc\xa1J \xfe\x80\x16\x16\x01\x80\x1f&%\x02\x80\v\xc0\x0e\x14\x13\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00$\x00\x00%\x0164/\x01&\"\a\x01'&\"\x0f\x01\x06\x14\x17\x01\x162\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\xad\x02f\x13\x13f\x134\x13\xfe-\xd3\x134\x13f\x13\x13\x01f\x134\x03f\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xed\x02f\x134\x13f\x13\x13\xfe-\xd3\x13\x13f\x134\x13\xfe\x9a\x13\x03\x86\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x06\x00\x10\x00\x15\x00\x1f\x00/\x00\x00\x01\x17\a#5#5\x01\x16\a\x01\x06'&7\x016\t\x03\x11\x01764/\x01&\"\x0f\x01%\x11\x14\x06#!\"&5\x11463!2\x16\x01\x94\x9848`\x01\xd2\x0e\x11\xfe\xdd\x11\r\x0e\x11\x01#\x11\xfe\xfb\x02 \xfe\xe0\xfd\xe0\x03\x80\\\x1c\x1c\x98\x1cP\x1c\\\x02\xa0\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xac\x984`8\x01\xba\r\x11\xfe\xdd\x11\x0e\r\x11\x01#\x11\xfd@\x02 \x01 \xfd\xe0\xfe\xe0\x02`\\\x1cP\x1c\x98\x1c\x1c\\`\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00)\x00\x00\x01\x114&#!\"\a\x06\x1f\x01\x01\x06\x14\x1f\x01\x1627\x01\x17\x163276\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00&\x1a\xfe *\x11\x11\x1f\x90\xfd\xea\x13\x13f\x134\x13\x02\x16\x90\x12\x1b\f\r'\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02`\x01\xe0\x1a&')\x1d\x90\xfd\xea\x134\x13f\x13\x13\x02\x16\x90\x13\x05\x11\x02*\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00%\x005\x00\x00\t\x0164'\x01&\a\x06\x1d\x01\"\x0e\x05\x15\x14\x17\x163276'\x027>\x013\x15\x14\x17\x1632\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\xed\x01`\x13\x13\xfe\xa0\x1e'(w\u0083a8!\n\xa7\v\x0e\a\x06\x16\x03,j.\xa8\x8c(\f\f\x1a\x02&\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xb3\x01`\x134\x13\x01`\x1f\x11\x11*\xa0'?_`ze<\xb5\xdf\f\x03\t\x18\x01bw4/\xa0*\x11\x05\x02\xc0\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x02\x00\x06\x00\x12\x00\x1e\x00\x00\x01-\x01\x01\x11\x01\x11\x00\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x02\x80\x01\x00\xff\x00\x01\x80\xfe\x00\x03 \x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xc0\x80\x80\x01O\xfd\xe2\xff\x00\x02\x1e\xfe\xdd\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\r\x00\x1d\x00-\x00\x00\x01\x16\a\x01\x06\"'\x01&763!2\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04y\x12\x17\xfe\xc0\x13B\x13\xfe\xc0\x17\x12\x11(\x02\x80(\x98\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03]#\x1f\xfe@\x1b\x1b\x01\xc0\x1f##\xfd \x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\r\x00\x1d\x00-\x00\x00\x01\x06#!\"'&7\x0162\x17\x01\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04y\x11(\xfd\x80(\x11\x12\x17\x01@\x13B\x13\x01@\x17u\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xa3###\x1f\x01\xc0\x1b\x1b\xfe@\x1f\xfe\xda\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\r\x00\x1d\x00-\x00\x00\x00\x14\a\x01\x06'&5\x11476\x17\x01\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04@\x1b\xfe@\x1f####\x1f\x01\xc0\xdb\x12\x0e\xfc@\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02\xa1B\x13\xfe\xc0\x17\x12\x11(\x02\x80(\x11\x12\x17\xfe\xc0\xfd\xec\x03\xc0\x0e\x12\x12\x0e\xfc@\x0e\x12\x12\x03\xce\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x01\x00\x00\x00\x00\x03\xf3\x05\x80\x00`\x00\x00%\x17\x16\x06\x0f\x01\x0e\a#\"\x00'#\"&=\x0146;\x01&7#\"&=\x0146;\x016\x0032\x17\x16\x17\x16\x0f\x01\x0e\x01/\x01.\x05#\"\x06\a!2\x17\x16\x0f\x01\x06#!\x06\x17!2\x17\x16\x0f\x01\x0e\x01#!\x1e\x0132>\x04?\x016\x17\x16\x03\xd0#\x03\f\v\x05\x04\r\x13\x18\x1b!\"'\x13\xea\xfe\xa2?_\r\x13\x13\rB\x02\x03C\x0e\x12\x12\x0ebC\x01a\xe0f\\\v\t\x06\x03+\x03\x16\r\x04\x04\x0f\x14\x19\x1b\x1f\x0e~\xc82\x01\xd4\x10\t\n\x03\x18\x05\x1b\xfe\x18\x03\x03\x01\xcb\x0f\n\t\x03\x18\x02\x12\v\xfe}0\xcb\u007f\x12$\x1f\x1c\x15\x10\x04\x05\r\r\f\xe5\x9f\f\x15\x04\x01\x02\x03\x06\x05\x05\x05\x04\x02\x01\x05\xdd\x13\rq\r\x1390\x12\x0er\x0e\x12\xd2\x01\x00\x17\x03\f\v\r\x9f\r\r\x04\x01\x01\x03\x04\x03\x03\x02\x80p\f\f\x0er\x1a%D\f\f\x0fp\v\x0fu\x89\x03\x04\x05\x05\x04\x01\x02\x05\a\a\x00\x00\x01\x00\x00\x00\x00\x03\xfc\x05\x80\x00?\x00\x00\x01\x11\x14\x06#!\"&=\x0146;\x01\x11#\"&=\x0146;\x0154632\x17\x1e\x01\x0f\x01\x06\a\x06'.\x02#\"\x06\x1d\x01!2\x16\x1d\x01\x14\x06#!\x11!546;\x012\x16\x03\xfc\x12\x0e\xfcD\x0e\x12\x13\ra_\x0e\x12\x12\x0e_\xf7\xbf\xb9\x96\t\x02\bg\t\r\r\n\x05*`-Uh\x011\r\x13\x13\r\xfe\xcf\x01\x9e\x12\x0e\xa2\x0e\x12\x01\x8f\xfe\x91\x0e\x12\x12\x0e\x96\r\x13\x01\u007f\x13\r\x83\x0e\x12߫\xde}\b\x19\n\u007f\v\x01\x02\t\x05\x1c$^L\xd7\x12\x0e\x83\r\x13\xfe\x85\xb5\r\x13\x13\x00\x00\x00\x01\x004\xff\x00\x03\xd2\x06\x00\x00b\x00\x00\x01\x14\x06\a\x15\x14\x06+\x01\"&=\x01.\x04'&?\x01676\x170\x17\x16\x17\x1632654.\x03'.\b5467546;\x012\x16\x1d\x01\x1e\x04\x17\x16\x0f\x01\x06\a\x06'.\x04#\"\x06\x15\x14\x1e\x04\x17\x1e\x06\x03\xd2ǟ\x12\x0e\x87\r\x13B{PD\x19\x05\x11\x0fg\a\x10\x0f\t\x02q\x82%%Q{\x1e%P46'-N/B).\x19\x11ĝ\x13\r\x87\x0e\x129kC<\x12\x06\x11\fQ\b\x0f\x0e\r\x03\x177>W*_x\x11*%K./58`7E%\x1a\x01_\x99\xdd\x1a\xaf\x0e\x12\x13\r\xaf\t,-3\x18\x06\x15\x14\x87\n\x02\x02\v\x02c\x1a\bVO\x1c2\")\x17\x15\x10\x12#\x1b,)9;J)\x8a\xd0\x1e\xb4\r\x13\x12\x0e\xb0\x06\"!*\x10\x06\x12\x14\x92\x0f\x01\x03\n\x03\x12#\x1d\x17VD\x1a,'\x1b#\x13\x12\x14\x17/&>AX\x00\x01\x00\x00\x00\x00\x03\x82\x05\x80\x00>\x00\x00\x01\x15\x14\x06+\x01\x0e\x01\a\x16\x01\x16\a\x06+\x01\"'\x00'&=\x0146;\x01267!\"&=\x01463!&+\x01\"&=\x01463!2\x16\x1d\x01\x14\x06+\x01\x16\x1732\x16\x03\x82\x12\x0e\xa8\x17Ԫ\xa7\x01$\x0e\n\b\x15\xc3\x10\t\xfe\xce\xc0\t\x13\rp\x84\xa1\x16\xfeU\x0e\x12\x12\x0e\x01\x9d9ӑ\r\x13\x12\x0e\x03@\x0e\x12\x12\x0e\xe9/\x11\xab\x0e\x12\x04*f\x0e\x12\x90\xb4\x14\xb2\xfe\x9a\x10\x12\x12\f\x01o\xcc\t\r\u007f\r\x13VR\x12\x0ef\x0e\x12q\x13\r\x85\x0e\x12\x12\x0ef\x0e\x12=S\x12\x00\x01\x00\x04\x00\x00\x03\xff\x05\x80\x00E\x00\x00!#\"&5\x11!\"&=\x01463!5!\"&=\x0146;\x01\x01&76;\x012\x17\x13\x16\x17>\x017\x136;\x012\x17\x16\a\x0132\x16\x1d\x01\x14\x06#!\x15!2\x16\x1d\x01\x14\x06#!\x11\x14\x06\x02[\xac\r\x13\xfe\xe0\r\x13\x13\r\x01 \xfe\xe0\r\x13\x13\r\xd6\xfe\xbf\b\b\n\x12\xc2\x13\n\xd7\x13%\n)\a\xbf\b\x15\xbf\x11\n\t\b\xfe\xc7\xd7\r\x13\x13\r\xfe\xde\x01\"\r\x13\x13\r\xfe\xde\x13\x12\x0e\x01J\x12\x0eg\r\x13U\x12\x0eh\r\x13\x02B\x10\x10\x10\x12\xfeW&W\x18X\x11\x01\xa4\x13\x10\x0e\x11\xfd\xbd\x13\rh\x0e\x12U\x13\rg\x0e\x12\xfe\xb6\r\x13\x00\x02\x00\x00\x00\x00\x05\x00\x05\x80\x00\a\x008\x00\x00\x004&#!\x11!2\x00\x10\x06#!\x15!2\x16\x1d\x01\x14\x06#!\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015#\"&=\x0146;\x01\x11463!2\x04\x13\x82j\xfe\xc0\x01@j\x01o\xfd\xc8\xfe\xac\x01\xf9\x0e\x12\x12\x0e\xfe\a\x13\r\xa7\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\xe0\x0e\x12\x12\x0e\xe0\x12\x0e\x02\x1b\xc8\x03g\xc8|\xfe@\x01\xa1\xfe~\xf4v\x12\x0e\x80\x0e\x12\xc0\x0e\x12\x12\x0e\xc0\x12\x0e\x80\x0e\x12v\x12\x0e\x95\r\x13\x02u\x0e\x12\x00\x06\x00\x00\x00\x00\a\x00\x05\x80\x00\b\x00\f\x00\x10\x00\x19\x00\x1d\x00n\x00\x00\x01\x13#\x13\x16\x14\x1746\x137!\x17!3'#\x01\x13#\x13\x14\x16\x1746\x137!\x17\x05\x15\x14\x06+\x01\x03\x06+\x01\"'\x03#\x03\x06+\x01\"&'\x03#\"&=\x0146;\x01'#\"&=\x0146;\x01\x03&76;\x012\x17\x13!\x136;\x012\x17\x13!\x136;\x012\x17\x16\a\x0332\x16\x1d\x01\x14\x06+\x01\a32\x16\x02\x02Q\x9fK\x01\x01\x01t#\xfe\xdc \x01\xa1\x8b#F\x01\x9fN\xa2Q\x01\x01\x01o!\xfe\xd7\"\x02\x80\x12\x0eդ\a\x18\x9f\x18\a\xa6ѧ\a\x18\x9f\v\x11\x02\xa0\xd0\x0e\x12\x12\x0e\xaf!\x8e\x0e\x12\x12\x0emY\x05\n\n\x10\x89\x1a\x05Z\x01ga\a\x18~\x18\ab\x01m]\x05\x1a\x89\x10\n\n\x05[o\x0e\x12\x12\x0e\x91\"\xb3\x0e\x12\x01U\x01+\xfe\xd4\x01\x04\x01\x01\x05\x01\xac\x80\x80\x80\xfd\xd4\x01,\xfe\xd5\x01\x05\x01\x01\x04\x01\xad\x80\x80 @\x0e\x12\xfd\x98\x18\x18\x02h\xfd\x98\x18\x0e\n\x02h\x12\x0e@\x0e\x12\x80\x12\x0e@\x0e\x12\x01X\x0f\r\f\x18\xfe\x98\x01h\x18\x18\xfe\x98\x01h\x18\f\r\x0f\xfe\xa8\x12\x0e@\x0e\x12\x80\x12\x00\x00\x03\x008\xff\x00\x04\xe8\x05\x80\x003\x00H\x00\\\x00\x00\x01\x16\a\x1e\x01\a\x0e\x04\a\x15#5\"'\x15#\x11\"&+\x017327\x113&#\x11&+\x015\x172753\x156353\x15\x1e\x03\x034.\x04\"\x06#\x112\x162>\x06\x034.\x04\x0e\x01#\x112\x16>\x06\x04\x8f\x12\x95ut\r\a3Nt\u007fR\x9aP*\x9a\x12H\x13\xc8\x1fo2\b\x10\x06\n\rLo\xd4@!\x9aR(\x9aOzh=\xd1\x1e,GID2F\xfd\x9e\n\xfe\xc1\n\r\f\v\xfe\xc0\x0f\b\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\x02\xee\x1a8PuE>.\x18\x12'\x0f\x10%&Te\x10\x02\x15Q,j\x86\x90m{\xa4\x1e\xfe+\xa7\x01\x02\a\b\x12>R\xc0{\xdf?jJrL6V\f\f\xfe\xc1\t\t\x01@\x10\x13\x14\x05`\x0e\x12\x12\x0e\xfa\xa0\x127>wmR1\x10\b\aq\a\x04\ruW\x17\x1c\x8fei\x92\xbd\x02/rr\x01\xb0\a\x18\x05\x10\f\r\x12:V\xb9\xfdr\x00\x00\x00\x00\x04\x00\"\xff\x00\x05\xce\x06\x00\x00\n\x00$\x007\x00V\x00\x00\x014&#\"\x06\x14\x16326\x01\x14\a\x01\x06#\"'\x01&76;\x01\x1146;\x012\x16\x15\x1132\x16\x05\x15!53\x1146=\x01#\a\x06\x0f\x01'73\x11\x13\x14\x0e\x03#\"'&'7\x16\x17\x163267#\x0e\x01#\"&54632\x16\x05BX;4>ID2F\xfd\x9e\n\xfe\xc1\n\r\f\v\xfe\xc0\x0f\b\b\x16\xc0\x12\x0e\xc0\x0e\x12\xc0\x0e\x12\x02\xd0\xfe+\xa7\x01\x02\a\b\x12>R\xc0{\xc3\x1a8PuE>.\x18\x12'\x0f\x10%&Te\x10\x02\x15Q,j\x86\x90m{\xa4\x04\xdf?jJrL6\xfb\xaa\f\f\xfe\xc1\t\t\x01@\x10\x13\x14\x05`\x0e\x12\x12\x0e\xfa\xa0\x12\xfcrr\x01\xb0\a\x18\x05\x10\f\r\x12:V\xb9\xfdr\x053>wmR1\x10\b\aq\a\x04\ruW\x17\x1c\x8fei\x92\xbd\x00\x00\x03\x00\x00\xff\x80\x06@\x05\x80\x00\v\x00\x1b\x00\\\x00\x00%4&#\"\x06\x15\x14\x16326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\x14\a\x16\x15\x16\a\x16\a\x06\a\x16\a\x06\a+\x02\".\x01'&'.\x015\x11467>\x01767>\x027>\x027632\x1e\x05\x15\x14\x0e\x01\a\x0e\x02\a!2\x16\x01\x00&\x1a\x1b%%\x1b\x1a&\xa0&\x1a\xfe\xe0\x1a&&\x1a\x01 \x1a&\x04\xa07\x0f\x03.\x11\x11\x0f'\t:@\x85$L\x11B\x9cWM{#\x1a&$\x19\x18h1D!\x12\x1a\t\t\a\v\x1c\x14\x13\x1a.I/!\x0f\t\x01\x13\x13\x12\x03\x0e\b\x04\x01\x15Nr\xc0\x1a&&\x1a\x1b%%\x02\x1b\xfd\x80\x1a&&\x1a\x02\x80\x1a&&\x1aV?, L=8=9%pEL\x02\x1f\x1b\x1a+\x01\x01%\x1a\x02\x81\x19%\x02\x02r@W!\x12<%*',<\x14\x13\x15\x1f2(<\x1e\x18&L,\"\x06\x18\x14\x0er\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06@\x05\x00\x00\v\x00\x1b\x00\\\x00\x00\x01\x14\x06#\"&54632\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26%\x16\x15\x0e\x01#!\x1e\x02\x17\x1e\x02\x15\x14\x0e\x05#\"'.\x02'.\x02'&'.\x01'.\x015\x1146767>\x02;\x03\x16\x17\x16\a\x16\x17\x16\a\x16\a\x14\x01\x00&\x1a\x1b%%\x1b\x1a&\xa0&\x1a\xfe\xe0\x1a&&\x1a\x01 \x1a&\x04i7\x01qN\xfe\xeb\x04\b\x0e\x03\x12\x12\x14\x01\t\x0f!/I.\x1a\x13\x14\x1c\v\a\t\t\x1a\x12!D1h\x18\x19$&\x1a#{MW\x9cB\x11L$\x85@:\t'\x0f\x11\x11.\x03\x03\xc0\x1a&&\x1a\x1b%%\xfd\xe5\x02\x80\x1a&&\x1a\xfd\x80\x1a&&\xaf=XNr\x0e\x14\x18\x06%(M&\x18\x1e<(2\x1f\x15\x13\x14<,'*%<\x12!W@r\x02\x02%\x19\x02\x81\x1a%\x01\x01+\x1a\x1b\x1f\x02LEp%9=8=L \x00\x00\f\x00\x00\xff\x80\x06\x00\x05\x80\x00\t\x00\x0f\x00\x17\x00+\x00=\x00\\\x00d\x00\u007f\x00\x8c\x00\x9e\x00\xb2\x00\xc2\x00\x00%54#\"\a\x15\x16327354\"\x15%\x15#\x11#\x11#5\x05\x11#5\x06#\"'&5\x113\x11\x14\x17\x16327\x11\x05\x15\x14\a\x06#\"'\x15#\x113\x15632\x17\x16\x17\x15\x14\a\x06\a\x06#\"'&=\x014762\x17\x16\x1d\x01#\x15\x143274645\x01\x15\x14\"=\x0142\x014'.\x01'&! \a\x0e\x01\a\x06\x15\x14\x17\x1e\x01\x17\x16 7>\x0176\x01\x13#\a'#\x1e\x01\x17\x16\x17\x153%54'&#\"\a\x06\x1d\x01\x14\x17\x163276\x173\x11#\x11\x06#\"'&5\x11#\x11\x14\x17\x16327\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x97\x1d\x11\x10\x10\x11\x1d\xb8BB\xfd\xc5PJN\x01\xb1C'%!\t\x06B\x01\x01\x0e\x14\x16\x01?\a\f)#!CC $)\f\a\xfb\x02\x03\f\x1b54\x1d\x15\x14\x1df\x1b\x15\x85\"\x18\x06\x01\xfe\x81@@\x02\x15\x13\nB+\x88\xfe\xec\xfe\xed\x88,A\n\x14\x14\nA+\x89\x02&\x89+A\n\x14\xfd\rZK35N\a \b#\vJ\x01!\x15\x1d13\x1b\x15\x15\x1b31\x1d\x15\xb5CC\x16\x14\x0f\x01\x01C\x06\v $)\x01\xf7\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xe9\x9d2\x10\xe0\x10\xab\"33\xe8F\xfeY\x01\xa7F~\xfe\x91(-\x1c\x11%\x01\"\xfe\xf2\x18\x02\x0f\x1f\x01\x18o\x924\x15*)$\x01\xed\xa1(*\x15\xb6\t\x1d\x0e\x16\x12(&\x1b;\x81;\x1b&&\x1d9LA3\x1a\x01\f\x15\v\x038\x9c33\x9c4\xfd\x03\xb1S,;\x05\x0f\x0f\x05;,W\xad\xb0T+<\x05\x0f\x0f\x05<+T\x03;\x01(\xc3\xc3\x17\\\x17g7\xc9x\x82:\x1d&&\x1d:\x82:\x1d&&\x1b<\x01r\xfe\xe5\x1f\x10\x02\x18\x01\x10\xfe\xdb%\x12\x1b-\x01\b\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\v\x00\x1b\xff\x00\x05\xe5\x06\x00\x00\t\x00\x0f\x00\x17\x00+\x00=\x00[\x00c\x00}\x00\x89\x00\x9b\x00\xaf\x00\x00\x01\x15\x14#\"'\x11632\x05\x15#542%35!\x153\x113!3\x11#\x11\x06#\"'&5\x11#\x11\x14\x17\x16327%54'&#\"\a5#\x1135\x163276%5#\x14\a\x06#\"=\x01354'&#\"\a\x06\x1d\x01\x14\x17\x16327676\x0154\"\x1d\x01\x142\x01\x14\a\x0e\x01\a\x06 '.\x01'&547>\x0176 \x17\x1e\x01\x17\x16\x013\x03\x11#\x11&'&'3\x13\x05\x15\x14\a\x06#\"'&=\x0147632\x17\x16%\x11#5\x06#\"'&5\x113\x11\x14\x17\x16327\x11\x03\xcb'\x17\x16\x16\x17'\x01RZZ\xfc:k\xfe\xc8id\x01 YY\x1e\x1b\x12\x03\x01Y\b\f.06\x01\xad\t\x1162+YY-06\x11\t\x01R[\x02\a!.\xb3\x1b'CD'\x1c\x1d'EH$\x12\x03\x02\xfd\xa0VV\x02\xcf\x1a\x0eX:\xb8\xfd\x1a\xb8:Y\r\x1a\x1a\x0eX;\xb7\x02\xe6\xb8:Y\r\x1a\xfc\x1afyd\x0e/%\x1cjG\x01\xb6\x1c&DC&\x1c\x1c&CD&\x1c\x01O[52.\r\b[\x01\x03\x12\x1b\x1e\x01$\xd3C\x16\x01-\x16D..D\x96^^\xfd\xc7\x01\xee\xfe\x86*\x15\x03 \x01l\xfey1\x18%=^\xc5I\x1a86\xd9\xfdi077\x1bS\r3\n$EWgO%33%O\xadO%35\x1b\x1b\t\x03\xc2\xd2EE\xd2F\xfdW\xeat;P\x06\x15\x15\x06P;p\xee\xeat;P\a\x14\x14\aP;p\x04\x0e\xfeq\xfe\xf1\x01\x0fJ\x8agT\xfe\xf9F\xafQ%33&P\xafP%33%R\xfe\r7>%\x183\x01\x8a\xfe\x91!\x02\x16+\x01}\x00\x00\x02\x00\x05\xff\x80\x05{\x05\xf6\x00\x13\x00'\x00\x00\x01\x06\x03\x06+\x01\"&7\x132'\x03&76;\x012\x17\x01\x16\a\x01\x15\x01\x16\a\x06+\x01\"'\x016\x016;\x012\x02U\n\xf7\x1b&\xef\x15\x14\n\xfd\x01\x01\xa1\f\v\t\x17\xef(\x1a\x03\xca\v\v\xfd\xf0\x01P\v\n\n\x16\xef*\x18\xfe\xad\x12\x02\x01\x19'\xf1\x16\x03e\x12\xfeJ.\"\x13\x01\xc0\x01\x01\x17\x16\x0f\x0f-\x01d\x10\x15\xfcZ\x01\xfd\x99\x14\x11\x0f-\x02n \x03\x8e-\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x13\x00'\x007\x00\x00\x014'&+\x01\"\a\x06\x1f\x01\x15\x03\x06\x17\x16;\x0127\x01&+\x01\"\a\x01\x16\x01\x16;\x01276'\x015\x016\x17\x11\x14\x06#!\"&5\x11463!2\x16\x02\xad~\x15\x1f\xb8\x12\b\a\b}\xc4\t\t\b\x10\xb9\x1f\x13\x037\a\x11\xbb\x1e\x13\xfee\x01\x01\x05\x14 \xb8\x12\a\b\t\xfe\xfc\x01\x99\b۩w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\x03\x01\xdd\"\v\f\x11\xd8\x01\xfe\xa6\x0e\x0e\r$\x03Q\f#\xfd'\x02\xfe!#\f\r\x0f\x01\xdc\x01\x02\xd3\x10\x88\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x02\x00\x00\x00\n\a\x00\x04\xf6\x00\x02\x00I\x00\x00\x01-\x01\x132\x04\x1f\x012\x1e\x05\x17\x1e\x02\x17\x1e\x01\x17\x1d\x01\x16\a\x0e\x01\x0f\x01\x0e\x06#\x06!&$/\x02.\x02'.\x02'.\x01'=\x01&7>\x01?\x01>\x0636\x02\xc7\x01\xe4\xfe\x1c\xb9\xa8\x019II\x01 \x0e!\x18 \x1e\x0e\x06\x13'\a\b\t\x01\x01\x13\a$\x0e\x0e\x0e\x1e \x18!\x0f\x1f\x01\xfb\xfe\x88\xcf\xfe\xcf01$$%A\x18\x06\x13'\a\b\t\x01\x01\x13\a$\x0e\x0e\x0e\x1e \x18!\x0e \x01\xfb\x01\x98\xfa\xfd\x01g\t\x05\x04\x03\x03\x06\n\x10\x17\x0f\x06\x19\\7@\x91)(\x88\x91\x917Y\x11\x11\x0f\x17\x0f\n\x06\x03\x03\x13\x02\t\x03\x04\x04\x05\n \x19\x06\x19\\7@\x91)(\x88\x91\x917Y\x11\x11\x0f\x17\x10\n\x06\x03\x03\x12\x00\x00\x05\x00@\xff\x80\x06\xc0\x05\x8a\x00\x03\x00\x13\x00\x17\x00\x1b\x00\x1f\x00\x00\t\x04\x15\x01\x15'\a5\x015\x17\x015\x177\x15\t\f\x01\x92\x01\xee\xfe\xaa\xfe\x16\x05,\xfe\x16\x01\x01\xfe\x17\x93\x01V\x01\x01\x01W\xfdQ\x01V\xfe\x12\xfe\xae\x05.\x01R\xfe\x17\xfe\xa9\x01W\x01\xe9\xfe\xae\xfe\x12\x03=\xfe\xcf\xfe\xe3\x01?\xfe\xe4l\xfe\xdb\x01\x01\x01\x01\x01%l`\x01\x1c\x02\x01\x01\x02\xfe\xe4\x04\xd8\xfe\xe3\xfe\xd0\x01\x0e\xfe\xf2\xfe\xf1\xfe\xc1\x01\x1d\x03~\xfe\xc1\xfe\xf2\x010\x00\x06\x00\v\xff\x00\x05\xf5\x06\x00\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00\x00\x05!\x11#\x11!\x11#%7\x05\a\x017\x01\a\x017\x01\a\x03\x01\a\t\x015!\x15\x05\t\xfb\xa2\xa0\x05\x9e\xa0\xfcR!\x03\x0f!\xfdXC\x02\xd5C\xfd\xf4f\x02ff\xd9\x01݀\xfe#\xfd\xb2\x03 `\x01\xe0\xfd\x80\x02\x80,\x9d\xa5\x9c\x02\x1a\x92\xfe\xad\x91\x02\xb6{\xfd\xff{\x03{\xfd\u007f`\x02\x81\xfa\xa1\x9f\x9f\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00O\x00g\x00\x00\x004&\"\x06\x14\x162\x00\x10\x06 &\x106 $\x14\x06\"&462$\"&\x0e\x02\a\x0e\x01\a\x0e\x03\x16\x14\x06\x1e\x02\x17\x1e\x01\x17\x1e\x0362\x16>\x027>\x017>\x03&46.\x02'.\x01'.\x03\x00\x10\a\x0e\x01\a\x06 '.\x01'&\x107>\x0176 \x17\x1e\x01\x17\x04\x00\x96Ԗ\x96\xd4\x01 \xe6\xfe\xb8\xe6\xe6\x01H\x01R6L66L\xfeG\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\x01\x01\x01\x05\x0f\v\x14L2\x1dUyH\x8b\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\x01\x01\x01\x05\x0f\v\x14L2\x1dUyH\x02n\x05\n\xe4\xd0X\xfe6X\xd0\xe4\n\x05\x05\n\xe4\xd0X\x01\xcaX\xd0\xe4\n\x02\x16Ԗ\x96Ԗ\x01\xa4\xfe\xb8\xe6\xe6\x01H\xe66L66L6\x80\x01\x01\x05\x0f\v\x14L2\x1dUyH\x8b\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\x01\x01\x01\x05\x0f\v\x14L2\x1dUyH\x8b\x0e\x8bHyU\x1d2L\x14\v\x0f\x05\x01\xfen\xfe6X\xd0\xe4\n\x05\x05\n\xe4\xd0X\x01\xcaX\xd0\xe4\n\x05\x05\n\xe4\xd0\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x17\x00\x1f\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x004&\"\x06\x14\x162$4&\"\x06\x14\x162\x04\xe0w\xa9\xa9w\xfc@w\xa9\xa9w\x01\x9a|\xb0||\xb0\x02\xb0|\xb0||\xb0\x05\x80\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfc\xa8\xb0||\xb0||\xb0||\xb0|\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x02\x00\t\x00\x15\x00\x00\x01\x13!\x053\t\x0137!\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x00\xc9\xfen\x026^\xfe5\xfe5^h\x02\n\x01\xfb\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x03\x92\xfe\xce\xe0\x02\xb3\xfdM\xa0\x011\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x05\x00\x00\xffP\x05\x81\x05\xa3\x00\n\x00\x16\x00*\x00C\x00g\x00\x00\x01\x16\x06'.\x01676\x1e\x01\x17.\x01\a\x0e\x01\x17\x1e\x017>\x01\x13.\x02'$\x05\x0e\x02\a\x1e\x02\x17\x167>\x02\x13\x0e\x03\a\x0e\x01&'.\x03'&'?\x01\x16 7\x1e\x01\x06\x13\x06\x03\x0e\x02\a\x06%&'.\x04'.\x03'>\x04767$\x05\x16\x17\x1e\x01\x03/\bu5'\x1d\x1c&$I7o\x0e\xc6b?K\x03\x04\x93\\[z\xe4\x14H,1\xfe\xdd\xfe\xed+.@\x12\x1e\\7<\xe4\xdc?5\\V\b\x0f\r,$V\xcf\xc5g.GR@\x14\x19 \x06\x12\xdf\x027\xe0\x15\x06\x10\xb5\x1aU\x05,+!\xfc\xfe\x9a\xf8\x92\x0f\x15\r\x05\a\x02\t#\x15\x1a\t\x03\x1d\"8$\x1e}\xbc\x01{\x01)\x9b<\x10\x01\x02\xa5?L \x11RR\x11\x12\f;\x11kr,\x1cyE[\x80\b\b\x98\x02z\x1b#\t\b/1\a\n\"\x1a\x1c#\t\a\x1d\x1c\b\b#\xfc\x12\x1aeCI\x140/\x03\x11\b\x14\"5#`\xc4\x10\t\x94\x94\x06\"8\x03\xb8\xa7\xfe\x18\x1e4\x1c\x11~&\x1bp\f\x1d)\x1b4\t2\xc8{\xacH\x1a-\x1e\x1e\x0f\v.\x12%W.L\x14>\x00\x06\x00\x00\xff\x80\x06\x00\x05\x80\x00\b\x00\x13\x00'\x00:\x00Y\x00i\x00\x00\x014&\a\x06\x16\x17\x1667\x16\x0e\x01&'&676\x16\x13\x0e\x02\a\x06'.\x02'>\x0276\x17\x1e\x02\x1346&'\x06 '\x0f\x01\x16\x17\x16\x17\x167>\x02\x136'&'&\x05\x06\a\x0e\x02\a\x1e\x02\x17\x1e\x03\x17\x16\x17\x047>\x027\x12\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03PR$+\x01+'TJ\bX\x84j\x03\x027-F\x8f\xb6\x14C',\x9b\xa9,&C\x15\r.\"\x1e\xc6\xd2!$28\v\x05\x0f\xa1\xfeh\xa2\f\x05\x1a\x0f/\x9d\xf9\xb3\"\x1e\x0f\x87\t\x11+p\xd8\xfe\xf1\x84^&+3\x04\b\x16$\x06\x01\b\x06\x12\ri\xb3\x01\x03\xb5\x18\x1f\x1f\x040\x01(\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02\x9a+.\x16\x14i\x12\x176=Bn\f\\C1X\x14\x1fR\x01:\x15\x1a\x06\x05\x14\x14\x06\a\x19\x14\x13\x18\a\x05#\"\x05\a\x19\xfd\x03\a'\x19\x04jj\x06\f\x9a8Q\x1b.c\x13Aj\x02\xc75\x167!?\x1b\f\"\x0f\x140\x1eD\x8c\xca$\x054\x14\"\vP\x14\x1c[\r\x14&\x15\x01\v\x012\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x01\x00D\xff\x80\x04\x00\x06\x00\x00\"\x00\x00%\x17\x0e\x01\a\x06.\x035\x11#5>\x047>\x01;\x01\x11!\x15!\x11\x14\x1e\x0276\x03\xb0P\x17\xb0Yh\xadpN!\xa8HrD0\x14\x05\x01\a\x04\xf4\x01M\xfe\xb2\r C0N\xcf\xed#>\x01\x028\\xx:\x02 \xd7\x1aW]oW-\x05\a\xfeX\xfc\xfd\xfa\x1e45\x1e\x01\x02\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1f\x00/\x00\x00%'\x06#\x06.\x025\x11!5!\x11#\"\a\x0e\x03\a\x153\x11\x14\x1e\x027>\x01\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04p>,;$4\x19\n\x01\x01\xff\x00\xbc\b\x01\x05\x195eD\x82+W\x9bcE\x87\x01\xa2\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9K\xb7\x16\x01\x17()\x17\x01\x8e\xc2\x01F\n,VhV\x19\xa5\xfe^9tjA\x02\x010\x04/\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x01\x00\x03\xff@\x02\xfd\x06\x00\x00\x17\x00\x00\x00\x16\a\x01\x06#\"'\x01&76;\x01\x1146;\x012\x16\x15\x113\x02\xf5\x10\r\xfe\xa2\n\r\x0e\n\xfe\x9d\r\b\t\x14\xe0\x12\x0e\xc0\x0e\x12\xe0\x01\x00&\x10\xfe\x80\n\n\x01\x80\x10\x13\x13\x04\xe0\x0e\x12\x12\x0e\xfb \x00\x00\x00\x01\x00\x03\xff\x00\x02\xfd\x05\xc0\x00\x17\x00\x00\x01\x06+\x01\x11\x14\x06+\x01\"&5\x11#\"&7\x01632\x17\x01\x16\x02\xfd\t\x14\xe0\x12\x0e\xc0\x0e\x12\xe0\x15\x10\r\x01^\n\r\x0e\n\x01c\r\x04\x13\x13\xfb \x0e\x12\x12\x0e\x04\xe0&\x10\x01\x80\n\n\xfe\x80\x10\x00\x00\x00\x00\x01\x00@\x01\x03\a\x00\x03\xfd\x00\x17\x00\x00\x01\x15\x14\x06#!\x15\x14\x06'\x01&547\x016\x17\x16\x1d\x01!2\x16\a\x00\x12\x0e\xfb &\x10\xfe\x80\n\n\x01\x80\x10\x13\x13\x04\xe0\x0e\x12\x02\xe0\xc0\x0e\x12\xe0\x15\x10\r\x01^\n\r\x0e\n\x01b\x0e\b\t\x14\xe0\x12\x00\x00\x00\x01\x00\x00\x01\x03\x06\xc0\x03\xfd\x00\x17\x00\x00\x01\x14\a\x01\x06'&=\x01!\"&=\x01463!546\x17\x01\x16\x06\xc0\n\xfe\x80\x10\x13\x13\xfb \x0e\x12\x12\x0e\x04\xe0&\x10\x01\x80\n\x02\x83\x0e\n\xfe\x9e\x0e\b\t\x14\xe0\x12\x0e\xc0\x0e\x12\xe0\x15\x10\r\xfe\xa2\n\x00\x00\x00\x02\x00\x00\xff\x80\x05q\x06\x00\x00&\x008\x00\x00\x01\x06\a\x06#\"'&#\"\a\x06#\"\x03\x02547632\x17\x16327632\x17\x16\x17\x06\a\x06\x15\x14\x16\x01\x14\a\x06\a\x06\a\x06\a6767\x1e\x01\x17\x14\x16\x05q'T\x81\x801[VA=QQ3\x98\x95\x93qq\xabHih\"-bfGw^44O#A\x8a\xfe\xe1\x1d\x1e?66%C\x03KJ\xb0\x01\x03\x01\x01\x01A}}\xc4 !\"\x01\x03\x01\x05\xf2䒐\x1e\x1e\"\"A$@C3^q|\xc6\x04z=KK?6\x12\v\x06\x95lk)\x03\x10\x03\x04\f\x00\x00\x04\x00\x00\xff\x00\x06\x80\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x00\x01\x11%\x11\x01\x11!\x11\x01\x11%\x11\x01\x11!\x11\x02\xaa\xfdV\x02\xaa\xfdV\x06\x80\xfcu\x03\x8b\xfcu\x02\x12\xfdu^\x02-\x02\xe7\xfdm\x025\xfdw\xfc\xee}\x02\x95\x03n\xfc\xe6\x02\x9d\x00\x00\x00\x06\x00\x00\xff\x00\x05\x80\x05~\x00\a\x00\x0f\x00\x1c\x007\x00M\x00[\x00\x00\x00264&\"\x06\x14\x04264&\"\x06\x14\x052\x16\x15\x11\x14\x06\"&5\x1146\x05\x11\x14\x06+\x01\x15\x14\x06\"&=\x01#\x15\x14\x06#\"&5'#\"&5\x11\x01\x1e\x01\x15!467'&76\x1f\x0162\x1776\x17\x16\a\x01\x11\x14\x06#\"&5\x114632\x16\x01\xdd \x17\x17 \x16\x01\xbc \x16\x16 \x17\xfc\xfb*<;V<<\x04O@-K\x03<\x01&\x014'>\x03&4.\x02'.\x01'\x16\x17\x16\a\x06\a\x06.\x01'.\x04'.\x03'&6&'.\x01'.\x01676\x16\a\x06\x167645.\x03'\x06\x17\x14#.\x01\x06'6&'&\x06\a\x06\x1e\x017676\a\"&'&6\x172\x16\x06\a\x06\a\x0e\x01\a\x0e\x01\x17\x1e\x03\x17\x167>\x0376\x17\x1e\x01\x06\a\x0e\x01\a\x06\a\x06'&\x17\x16\x17\x167>\x05\x16\x17\x14\x0e\x05\a\x0e\x02'&'&\a\x06\x15\x14\x0e\x02\x17\x0e\x01\a\x06\x16\a\x06'&'&76\a\x06\a\x06\x17\x1e\x01\x17\x1e\x01\x17\x1e\x01\x06\a\x1e\x02\x156'.\x027>\x01\x17\x167676\x17\x16\a\x06\a\x06\x16\x17>\x0176&6763>\x01\x16\x016&'&\x15\x16\x172\a\x0632\x05.\x02'.\x04\a\x06\x16\x17\x166'4.\x01\a\"\x06\x16\x17\x16\x17\x147674.\x01'&#\x0e\x01\x16\a\x0e\x02\x17\x16>\x017626\x01\x1e\x02\x0e\x05\a\x0e\x01\a\x0e\x01'.\x03'&#\"\x06\a\x0e\x03'.\x01'.\x04'&676.\x0167>\x017>\x015\x16\a\x06'&\a\x06\x17\x1e\x03\a\x14\x06\x17\x16\x17\x1e\x01\x17\x1e\x027>\x02.\x01'&'&\a\x06'&7>\x027>\x03767&'&67636\x16\x17\x1e\x01\a\x06\x17\x16\x17\x1e\x01\x17\x16\x0e\x01\a\x0e\x03'.\x04'&\x0e\x01\x17\x16\a\x06\x1667>\x017>\x01.\x01'.\x0167\x1e\x05\x02\x97\v\t\x04\x05\x13\x05\\\x04\x0f\n\x18\b\x03\xfe\x9b\x04\x04\x05\x03\x03\a\n\t\x04\x11\x04\x01\x02\x02\x01\x02\x03U7\x04\a\x03\x03\x02\a\x01\t\x01\nJ#\x18!W!\v'\x1f\x0f\x01\v\t\x15\x12\r\r\x01\x0e\"\x19\x16\x04\x04\x14\v'\x0f;\x06\b\x06\x16\x19%\x1c\n\v\x12\x15\r\x05\x11\x19\x16\x10k\x12\x01\t)\x19\x03\x01\"\x1c\x1b\x1d\x02\x01\t\x11\a\n\x06\x04\v\a\x11\x01\x01\x14\x18\x11\x14\x01\x01\x16\t\b'\x01\r\x05\n\x0e\x16\n\x1b\x16/7\x02*\x1b \x05\t\v\x05\x03\t\f\x14I\t,\x1a\x196\n\x01\x01\x10\x19*\x11&\"!\x1b\x16\r\x02\x02\x06\x06\v\a\r\x03\x1cO6\x16\x15*\x16\x03\x01\x1e\x1d\r\x12\x17O\b\x02\x01\x06\b\x15 \x04\x02\x06\x04\x05\x02\x02$.\x05(\x04\x14\xa8\t\x10\x03\x1f\x1e\b*\x0e.'\x04\r\x06\x01\x03\x14\n.x\x85,\x17\v\f\x02\x01\x16\t\x06\x15\x03\x17\x02\x02\x11\x02\x16\x0f$\x01CN\xfd\xa1\x03\v\x06\t\x02\x03\n\x03\x03\v\x03\x01\xa3\x02\t\x11\x06\x05\t\x05\x06\x02\x03\x0e*\x12\t\v\xb4\n\f\x03\x06\x04\x04\x03\x0e\x04\b\x026\x05\r\x03\x0f\t\t\x05\x03\x02\x01\n\x02\x04\x04\b\x0e\b\x01\x10\x0e\x027\x14\x16\x02\a\x18\x17%\x1a&\b&_\x1c\x11f&\x12\x17\n\"\x1e,V\x13L\x14,G$3\x1c\x1d\xa4@\x13@$+\x18\x05\n\"\x01\x01\n\n\x01\n\x0eV\x11\x1e\x18\x155 3\"\t\r\x12\x02\f\x05\x04\x01\"\x03\x03\"\x14\x81#\x18dA\x17++\x03\x12\x14\ny0D-\v\x04\x03\x01\x01\x12\x1e\a\b%\x16&\x14n\x0e\f\x04\x024P'A5j$9E\x05\x05#\"c7Y\x0f\b\x06\x12\v\n\x1b\x1b6\"\x12\x1b\x12\t\x0e\x02\x16&\x12\x10\x14\x13\n8Z(;=I50\v' !!\x03\x0e\x01\x0e\x0f\x1a\x10\x1b\x04e\x01\x13\x01\x06\f\x03\x0e\x01\x0f\x03\v\r\x06\xfeR\x01\b\x11\x05\x05\b\v\x01\x01\x10\n\x03\b\x04\x05\x03\x03\x02\xfe\x9a\x12\x18\x0f\x19\x1b\x10\x1d\n\"\a+\x050n\x14\x14?\xa2t(\x02\x04-z.'<\x1f\x12\f\x01>R\x1e$\x16\x15A\"\b\x03\x1e\x01\x0124\x01\x03B\x19\x13\x0f\a\x04@\x05\x1e(\x15\t\x03\b~\x0f\t\x03\x04\a9B\x01\x019\x1f\x0f,\x1f\x02\x03\v\t\x01\x1d\x13\x16\x1e\x01*$\x04\x0f\x0e\f\x17\x01\x0e\x1a\x05\b\x17\x0f\v\x01\x02\x11\x01\f\t\x11\t\x0e\x06\x03\v\r\x03\x06\x1f\x04\x13\x04\x05\a\x02\x04\x04\x0f\x17\x01\x01\f\x10\x13\x0f\t\x04\t\x02\x05\x05\x04\x06\x03\a\x01\x0e<\x1a\f\v>\x1f\t\x03\a\x19?0D\x1d\x06\xa89\x12f\b\x18\x15\x1f?\x1c\x1c\x13\x01\x01\x04Ae\f \x04\x17\x87\t\x0f.(\x03\x0f;1.\x18D\b\x10\b\x02\x05\t\a4\x10\x0fH&\b\x06.\x19C\x17\x1d\x01\x13t \x15iY\x1a\x12% \v\x03*\x11\x1a\x02\x02\t\x05\x01\x0f\x14\xc2\b\a\x03\x04\x03\n\x06\a\x01\x02\x107\x04\x01\x12\xe0\v\x11\b\x01\x04\x04\x01\x04\x1b\x03\x05\x02\xea\x02\x06\b\x02\x0f\x01\r\r\x06\x04\r\x05\x06\x03\x06\f\x03\x01\x04\xfa\xc8\f\x19\x17\x16\x16\x11\x14\r\x12\x04\x13J\x1b\x10\a\x12\t\x1d\x16\x11\x01\x01\x03\x01\x01\x1c \x19\x01\x01<\r\x04\v\a\f\x11\v\x17W\v\x100%$\t\f\x04\n\x12\"\"I!\x14\x05\x03\r\x0f*\x06\x18\f\x16\v\x0fD\x0e\x11\t\x06\x19\b\x06 \x0e\x03\x06,4A'\x11\xbe4J\"\t\x18\x10\x16\x1d.0\x12\x15f6D\x14\x8f4p\xc6Z{+\x15\x01\x1d\x1b*\x9fD_wqi;\xd0W1G(\x02\x02\"%\x1e\x01\x01\b\x13\f\x1d\x05%\x0eT7F}AG\x05!1#\x19\x12% \x19\v\vJG\f\x1f3\x1e\x1b\v\x0f\x00\b\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0e\x00 \x00'\x00.\x002\x00>\x00V\x00b\x00\x00%&\x03#\a\x0e\x04\a'\x1632\x03&'\x04!\x06\x15\x14\x16\x17>\x03?\x01>\x01'&'\x0e\x01\a \x05&\a\x16\x17>\x01\x01\"\a6\x05&#\"\a\x16\x17>\x04\x13&'\a\x0e\x04\a\x16\x17\x1e\x01\x17>\x012\x1e\x04\x176\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00*b\x02\x02\x106\x94~\x88#\x0f\xb8\xea\x84=\x15 \xfe\xc9\xfe\x96\x01XP2\x93\x8a{&%\x04\x12gx|\x8a\xc0 \x01.\x03\xdc\xd2\xc7W)o\x94\xfc\xf1\x01\x01\x01\x02O\xb9\xf8LO\x83sEzG<\x0f\xe4\x03\x92\x01\t\x14CK}E\x19\x13\x02\t\x03$MFD<5+\x1e\nz\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a$\xf1\x01\x01\x01\x06\x15MW\x8eM\v\x96\x02\x931>]\a\x0e|\xe1YY\x9b^D\x0e\r\x01\x05\xd6եA\xf2\x97\xef<\x1f\xef\xe6K\xe5\x03m\x01\x01\x91\xa4\x13\xaa\xd4\x1aE6<\x15\xfe\"\xe8\xb2\x01\f\x19@9I\x1c5*\x05\x18\x05\x05\x04\x03\x05\x06\a\x05\x02\xc8\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00>\x00^\x00\x00\x014.\x03/\x01.\x045432\x1e\x0332654.\x01#\"\x0e\x02\x15\x14\x1e\x02\x1f\x01\x16\x17\x16\x15\x14\x06#\".\x03#\"\x06\x15\x14\x1632>\x02\x05\x14\x06#\"'\x06#\"$&\x02547&54632\x17632\x04\x16\x12\x15\x14\a\x16\x04\x95':XM1h\x1e\x1c*\x12\x0f\x90+D($,\x1a/9p\xac`D\x80oC&JV<\x92Z\x16 PA3Q1*2\x1d23\xf4\xa9I\x86oB\x01kែhMI\x8f\xfe\xfb\xbdo\x10PែhMI\x8f\x01\x05\xbdo\x10P\x01\xd92S6,\x18\v\x18\a\a\x10\x10\x1a\x11M\x18!\"\x18@-7Y.\x1f?oI=[<%\x0e$\x16\x0e\x14('3 -- <-\\\x83%Fu\x90\x9f\xe1P\x10o\xbd\x01\x05\x8fIMh\x82\x9f\xe1P\x10o\xbd\xfe\xfb\x8fIMh\x00\x00\x00\x03\x00,\xff\x80\x04\xcb\x06\x00\x00#\x00?\x00D\x00\x00\x0176&#!\"\x06\x15\x11\x147\x01>\x01;\x01267676&#!\"&=\x01463!267\x06\n\x01\a\x0e\x04#!\"\a\x06\x01\x0e\x01'&5\x11463!2\x16\a\x036\x1a\x01\x03\xe8%\x05\x1c\x15\xfd8\x17\x1f\x06\x01#\x17\x1e!\xef\x16\x1e\x03\x18\r\x04\x1f\x15\xfe\xda\x1d&&\x1d\x01Z\x12\"\xe6\x0fM>\x04\x06\x06\x16\x1b2!\xfe\xf1\r\t\b\xfe^\x16I\f7LR\x03x_@\x16\x9e\x04>M\x04N\xc2\x17\"\"\x14\xfb\xb3\a\x06\x01`\x1a\x0f\x1d\x0f\x82=\x15&&\x1d*\x1d%\x1b\xeeI\xfe}\xfe\xc7\x11\x16\x15,\x16\x14\n\t\xfe\x1b\x19\a\t\x16L\x05\x827_jj\xfc\xea\x11\x019\x01\x83\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00%\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x02\xc0\x12\x0e\xfe \x0e\x12\x12\x0e\x01\xe0\x0e\x12\x02\xa0\x12\x0e\xfe \x0e\x12\x12\x0e\x01\xe0\x0e\x12\xa0&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&\xc0\x04\x00\x0e\x12\x12\x0e\xfc\x00\x0e\x12\x12\x01\x8e\x02\x80\x0e\x12\x12\x0e\xfd\x80\x0e\x12\x12\x03\x0e\xfa\x80\x1a&&\x1a\x05\x80\x1a&&\x00\x00\x00\x00\x02\x00\x00\xff\x00\x05\x00\x05\xe0\x001\x009\x00\x00\x01\x14\x06#\"'\x03#\x15\x13\x16\x15\x14\x06+\x01\x11\x14\x06+\x01\"&5\x11#\"&547\x135#\x03\x06#\"&547\x0163!2\x17\x01\x16\x00\x14\x06\"&462\x05\x008(3\x1d\xe3-\xf7\t&\x1a\xc0B.\xa0.B\xc0\x1a&\t\xf7-\xe3\x1d3(8\x10\x01\x00Ig\x01\x80gI\x01\x00\x10\xfe`\x83\xba\x83\x83\xba\x01\xe0(8+\x01U\x84\xfee\x0f\x12\x1a&\xfe\xf0.BB.\x01\x10&\x1a\x12\x0f\x01\x9b\x84\xfe\xab+8(\x1d\x18\x01\x80kk\xfe\x80\x18\x03`\xba\x83\x83\xba\x83\x00\x02\x00\x00\xff\x00\x04\x00\x05\xe0\x00%\x00-\x00\x00\x01\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11463!2\x16\x00\x14\x06\"&462\x04\x008P8@B\\B@B\\B@8P8pP\x02\x80Pp\xfe\xe0\x83\xba\x83\x83\xba\x03@\xfe`(88(\x01`\xfcp.BB.\x01\xd0\xfe0.BB.\x03\x90\xfe\xa0(88(\x01\xa0Ppp\x01ͺ\x83\x83\xba\x83\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x15\x00!\x00\x00%\x01>\x01&'&\x0e\x01\a\x06#\"'.\x02\a\x0e\x01\x16\x17$\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x05\x01^\x10\x11\x1d/(V=\x18$<;$\x18=V).\x1d\x11\x10\x04X\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xea\x01\xd9\x16J`\x1f\x1a\x01\"\x1c((\x1c\"\x01\x1a\x1f`J\x16\x8e\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00,\xff\x00\x06\xd4\x05\xff\x00\x0f\x00I\x00\x00\x004.\x02\"\x0e\x02\x14\x1e\x022>\x01%\x06\a\x05\x11\x14\a\x06'%\a\x06\"/\x01\x05\x06'&5\x11%&'&?\x01'&767%\x11476\x17\x05762\x1f\x01%6\x17\x16\x15\x11\x05\x16\x17\x16\x0f\x01\x17\x16\x05\xc0[\x9b\xd5\xea՛[[\x9b\xd5\xea՛\x01o\x04\x10\xfe\xdc\r\x0f\x0e\xfeܴ\n \n\xb4\xfe\xdc\x0e\x0f\r\xfe\xdc\x10\x04\x05\t\xb4\xb4\t\x05\x04\x10\x01$\r\x0f\x0e\x01$\xb4\t\"\t\xb4\x01$\x0e\x0f\r\x01$\x10\x04\x05\t\xb4\xb4\t\x02\v\xea՛[[\x9b\xd5\xea՛[[\x9b5\x0f\x05`\xfe\xce\x10\n\n\x06^\xf8\r\r\xf8^\x06\n\n\x10\x012`\x05\x0f\x11\f\xf8\xf8\r\x10\x0f\x05`\x012\x10\n\n\x06^\xf8\f\f\xf8^\x06\n\n\x10\xfe\xce`\x05\x0f\x10\r\xf8\xf8\f\x00\x02\x00\x00\xff\x80\x05\xbe\x05\u007f\x00\x12\x001\x00\x00%\x06#\"$\x02547\x06\x02\x15\x14\x1e\x0232$%\x06\x04#\"$&\x0254\x126$76\x17\x16\a\x0e\x01\x15\x14\x1e\x013276\x17\x1e\x01\x04\xee68\xb6\xfeʴh\xc9\xfff\xab킐\x01\x03\x01&^\xfe\x85\xe0\x9c\xfe\xe4\xcezs\xc5\x01\x12\x99,\x11\x12!V[\x92\xfa\x94vn)\x1f\x0e\a\xe9\t\xb4\x016\xb6\xc0\xa5<\xfe\xaeׂ\xed\xabf{\xc3\xcb\xf3z\xce\x01\x1c\x9c\x99\x01\x17\xcc}\x06\x02))\x1fN\xcfs\x94\xfa\x923\x12\x1f\x0e(\x00\x03\x00@\xff\x80\x06\xc0\x05\x80\x00\v\x00\x1b\x00+\x00\x00\x004&#!\"\x06\x14\x163!2\x01\x11\x14\x06#!\"&5\x11463!2\x16\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04@&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a\x02f&\x1a\xfa\x80\x1a&&\x1a\x05\x80\x1a&@&\x1a\xfa\x00\x1a&&\x1a\x06\x00\x1a&\x02\xa64&&4&\x01\x00\xfc@\x1a&&\x1a\x03\xc0\x1a&&\x01\xa6\xff\x00\x1a&&\x1a\x01\x00\x1a&&\x00\x00\x02\x00 \xff\xa0\x06`\x05\xc0\x00B\x00H\x00\x00\x00\x14\x06+\x01\x14\a\x17\x16\x14\a\x06\"/\x01\x0e\x04#\x11#\x11\".\x02/\x01\a\x06#\"'.\x01?\x01&5#\"&46;\x01\x11'&462\x1f\x01!762\x16\x14\x0f\x01\x1132\x01!46 \x16\x06`&\x1a\xe0C\xd0\x13\x13\x126\x12\xc6\x05\x14@Bb0\x803eI;\x0e\x0f\xb7\x14\x1c\x18\x13\x13\x03\x11\xca:\xe0\x1a&&\x1a\xe0\xad\x13&4\x13\xad\x03L\xad\x134&\x13\xad\xe0\x1a\xfeF\xfd\x80\xbb\x01\n\xbb\x02Z4&\xabw\xd1\x134\x13\x13\x13\xc5\x05\x10) \x1a\x03\x80\xfc\x80\x1b''\r\x0e\xcf\x15\x10\x125\x14\xe3r\xa0&4&\x01&\xad\x134&\x13\xad\xad\x13&4\x13\xad\xfe\xda\x02\x00\x85\xbb\xbb\x00\x00\x01\xff\xff\x00\x01\a}\x04G\x00\x85\x00\x00\x01\x16\a\x06\a\x0e\x02\x1e\x02\x17\x16\x17\x16\x17\x1e\x02\x0e\x01#\x05\x06&/\x01.\x03\a\x0e\x04\x17\x14\x06\x0f\x01\x06\a#\x06.\x02/\x01.\x03\x02'&4?\x0163%\x1e\x01\x1f\x01\x16\x17\x1e\x01\x1f\x01\x1e\x0327>\x04'.\x01/\x01&'&7676\x17\x16\x17\x1e\x03\x14\x0e\x01\x15\x14\x06\x1e\x02\x17\x1e\x01>\x02767>\x01?\x01>\x02\x17%6\x16\x17\a}\x17\xad\x18)(\x1e\x1f\a\x13.\"\x04\x01\x8d2\x03\a\a\b*&\xff\x00\x18@\x14\x14\x1eP9A\x18\x03\n\x18\x13\x0f\x01\a\x04\x04\x12#sG\x96q]\x18\x19\n#lh\x8d<\x06\x03\x04\x0f*\x01\x12\f\x16\x05\x05\x10\b\x144\x0f\x10\x1d6+(\x1c\r\x02\x06\x12\t\n\x05\x02\x0e\a\x06\x19<\r\x12\x10\x165\xbaR5\x14\x1b\x0e\a\x02\x03\x02\x01\x06\x11\x0e\b\x12\"*>%\"/\x1f\t\x02\x04\x1a+[>hy\n\x0f\x03\x03\x01\x03\x03\x01\x02\x05\x0f\t\x00\a\x00\x00\xff\xaa\x06\xf7\x05K\x00\n\x00\x15\x00!\x00/\x00U\x00i\x00\u007f\x00\x00%6&'&\x06\a\x06\x1e\x01676&'&\x06\a\x06\x17\x166\x17\x0e\x01'.\x017>\x01\x17\x1e\x01%.\x01$\a\x06\x04\x17\x1e\x01\x0476$%\x14\x0e\x02\x04 $.\x0154\x1276$\x17\x16\a\x06\x1e\x016?\x0162\x17\x16\a\x0e\x01\x1e\x01\x17\x1e\x02\x02\x1e\x01\a\x0e\x01'.\x0176&\a\x06&'&676%\x1e\x01\a\x0e\x01.\x0176&'.\x01\a\x06.\x01676\x16\x02\xa3\x15\x14#\"N\x15\x16\x12DQt\b\t\r\x0e\x1d\a\x11\x1e\x0e\x1e\xb5-\xe2okQ//\xd1jo_\x01\v\t\xa0\xfe\xff\x92\xdf\xfe\xdb\x0e\t\xa0\x01\x01\x92\xdf\x01%\x01&J\x90\xc1\xfe\xfd\xfe\xe6\xfe\xf4Ղ\x8b\x80\xa9\x01YJA-\x04\x06\x0e\x0f\x06\x06\x8b\xd6.--\x02\x05\x0e\n\f9\\DtT\x19\x13\b+\x17\x17\x16\a\x14X?\x18*\x04\x05\x1a\x18<\x01UW3'\t26\x1a\b\x1c$>>\xacW\x1c0\f\x1f\x1c{\xf2\xfc\"F\x0f\x0e\x1a!\"E \x1b\x9b\r\x1b\x05\x05\v\r\x1f\x0e\x05\v^f`$\"\xb9_]\\\x1b\x1d\xb5<`\x94F\x0e\x17\xed\x92`\x94F\x0e\x17\xed\x8eD\x8f\x83h>Cw\xb7ls\x01\x04\x80\xa9\x86J@\x91\x0e\f\x02\x03\x02\x02;=?s\r\x0e\v\x04\x04\x12:i\x02_^{8\x17\x16\a\b+\x17?`\r\x05\x1a\x18\x18)\x05\rO`\xfds\x1b\x1a\x122\x1bR\xb4DE5\x12\x06\x1f8/\x06\x1aK\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05r\x00\t\x00\x13\x00\x1d\x00\x00\x05\x06#\"'>\x017\x1e\x01\x01\x11\x14\x02\a&\x114\x12$\x01\x10\a&\x025\x11\x16\x04\x12\x04m\xab\xc5ī\x8a\xc3\"#\xc3\xfe\x9b\xfd̵\xa7\x01$\x045\xb5\xcc\xfd\xb3\x01$\xa7\"^^W\xf8\x90\x90\xf8\x05=\xfe\x1b\xfc\xfeac\xd7\x01\x18\xbb\x01E\xd6\xfd*\xfe\xe8\xd7c\x01\x9f\xfc\x01\xe5\x1e\xd6\xfe\xbb\x00\x00\x00\x01\x00\x00\xff\x00\x05z\x06\x00\x00k\x00\x00\x01\x0e\x03.\x03/\x01\x06\x00\a\"&4636$7\x0e\x02.\x03'>\x01\x1e\x02\x1767\x0e\x02.\x05'>\x01\x1e\x05\x1f\x0165.\x0567\x1e\x04\x0e\x02\x0f\x01\x16\x14\a>\x05\x16\x17\x0e\x06&/\x01\x06\a>\x05\x16\x05z X^hc^O<\x10\x11q\xfe\x9f\xd0\x13\x1a\x1a\x13\xad\x01+f$H^XbVS!rȇr?\x195\x1a\a\x16GD_RV@-\x06F\u007fbV=3!\x16\x05\x04\f\b\x1bG84\x0e&3Im<$\x05\x06\x14\x12\b\a\x01\x01\x03\x0e/6X_\x81D\x02'=NUTL;\x11\x11\x172\x06\x18KPwt\x8e\x01\xb1Pt= \x03\x0e\x1e\x19\n\n\xe4\xfe\xf9\x01\x1a&\x19\x01ռ\x0e\x12\b\r,J~S/\x14#NL,\x83\xa0\x01\x03\x02\x03\x11\x1d8JsF\x1c\x11\x13);??1\x0f\x10zI\x06\x14EJpq\x8dD\x19IPZXSF6\x0f\x0f\x04\\\x1a\a\x17?5:\x1f\x02\x17N\u007fR=\x1e\x12\x01\x03\x03\x03\x93\x88\a\x17;.&\x021\x00\x04\x00\x15\xff\x00\x04\xeb\x05\x00\x00\f\x00\x10\x00\x14\x00\x1e\x00\x00\x01\x15\x14\x06+\x01\x01\x11!\"&=\x01\x01\x15!\x11\x01\x15!\x11%\x15!5463!2\x16\x04\xebsQ9\xfe\xfc\xfd\xefQs\x04\xd6\xfb*\x04\xd6\xfb*\x04\xd6\xfb*sQ\x03NQs\x01\x1bBUw\xfe\xf3\x01\rwUB\x01F\xff\x00\xff\x01H\xff\x00\xff\x8cCCTww\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00%\x001\x00\x00\x00\x14\a\x01\x06#\"&=\x01!\"&=\x01463!54632\x17\x01\x16\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x80\t\xfe\xc0\t\x0e\r\x13\xfe\xa0\r\x13\x13\r\x01`\x12\x0e\f\f\x01?\xa9\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\x8e\x1c\t\xfe\xc0\t\x13\r\xc0\x13\r\xc0\r\x13\xc0\x0e\x12\n\xfe\xc1\xab\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00%\x001\x00\x00\x01\x15\x14\x06#!\x15\x14\x06#\"'\x01&47\x01632\x16\x1d\x01!2\x16\x12\x10.\x01 \x0e\x01\x10\x1e\x01 6\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x80\x13\r\xfe\xa0\x12\x0e\f\f\xfe\xc1\t\t\x01@\t\x0e\r\x13\x01`\r\x13\xa0\x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xe0\xc0\r\x13\xc0\x0e\x12\n\x01?\t\x1c\t\x01@\t\x13\r\xc0\x13\xfe\xff\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x02_\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00\x00\x01\x11\x14\x06#\"'\x01&47\x01632\x16\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04\x00&\x1a\x14\x11\xfe@\x1b\x1b\x01\xc0\x11\x14\x1a&\x01\x00\x13\r\xfc@\r\x13\x13\r\x03\xc0\r\x13\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\xc0\xfd\x80\x1a&\f\x01@\x13B\x13\x01@\f&\xfc\xc6\x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x13\x00\x1f\x00\x00\x00\x14\x06\"&462\x12 \x0e\x01\x10\x1e\x01 >\x01\x10&\x04\x10\x02\x04 $\x02\x10\x12$ \x04\x04\x00\x96Ԗ\x96\xd4*\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\x92\x92\x01r\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x02\xeaԖ\x96Ԗ\x01 \x92\xfa\xfe\xd8\xfa\x92\x92\xfa\x01(\xfa\xbd\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06]\x05\xe0\x00\x15\x006\x00\x00\x01\x17\x06\x04#\"$\x0254\x127\x17\x0e\x01\x15\x14\x0032>\x01%\x17\x05\x06#\"'\x03!\"&'\x03&7>\x0132\x16\x15\x14\x06'\x13!\x15!\x17!2\x17\x13\x03\xfff:\xfeл\x9c\xfe\xf7\x9bѪ\x11z\x92\x01\a\xb9~\xd5u\x02\x1b:\xff\x00\r\x10(\x11\xef\xfe(\x18%\x03`\x02\b\x0eV6B^hD%\x01\xa7\xfei\x10\x01\xc7(\x11\xe4\x01]̳ޛ\x01\t\x9c\xb5\x01*>\x836߅\xb9\xfe\xf9\x82\xdd\x1ar\x80\a#\x01\xdd!\x18\x03\v\x11\x193?^BEa\a\xfe߀\x80#\xfe9\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00#\x003\x00\x00\x016'&\x03632\a\x0e\x01#\"'&'&\a\x06\a\x0e\x01\a\x17632\x17\x1e\x01\x17\x1632\x13\x12\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\f\n\xab\xe7Q,&U\v\x04\x8c#+'\r \x1e\x82;i\x1bl\x1b4L\v92\x0f<\x0fD`\x9d\xe2\xdc\xfa\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\x82\xd8\x06\b\xfe\xf3\x13`9ܩ6ɽ\f\a]\x18`\x18C4\xb37\xdb7\xb3\x01&\x01\x1b\x01\u007f\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x01\x00\x00\x00\x00\x04\x80\x05\x80\x00D\x00\x00\x01\x14\x02\x04+\x01\"&5\x11\a\x06#\"'&=\x014?\x015\a\x06#\"'&=\x014?\x01546;\x012\x16\x1d\x01%6\x16\x1d\x01\x14\a\x05\x15%6\x16\x1d\x01\x14\a\x05\x116\x00546;\x012\x16\x04\x80\xbd\xfe\xbc\xbf\xa0\x0e\x12\xd7\x03\x06\n\t\r\x17\xe9\xd7\x03\x06\n\t\r\x17\xe9\x12\x0e\xa0\x0e\x12\x01w\x0f\x1a\x17\xfew\x01w\x0f\x1a\x17\xfew\xbc\x01\x04\x12\x0e\xa0\x0e\x12\x02\xc0\xbf\xfe\xbc\xbd\x12\x0e\x02cB\x01\x06\n\x10\x80\x17\bG]B\x01\x06\n\x10\x80\x17\bG\xfa\x0e\x12\x12\x0e\xb5t\x05\x14\x10\x80\x17\by]t\x05\x14\x10\x80\x17\by\xfe\x19\r\x01\x14\xbe\x0e\x12\x12\x00\x03\x00\x00\x00\x00\x05\x80\x05\x80\x00#\x003\x00C\x00\x00\x01\x15\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!\x1146;\x012\x16\x15\x11!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04\x80\x12\x0e\xfe\xa0\x12\x0e@\x0e\x12\xfe\xa0\x0e\x12\x12\x0e\x01`\x12\x0e@\x0e\x12\x01`\x0e\x12\x80^B\xfc\xc0B^^B\x03@B^\x80\xa9w\xfc\xc0w\xa9\xa9w\x03@w\xa9\x02\xe0@\x0e\x12\xfe\xa0\x0e\x12\x12\x0e\x01`\x12\x0e@\x0e\x12\x01`\x0e\x12\x12\x0e\xfe\xa0\x12\xfe2\x03@B^^B\xfc\xc0B^^\x03\x82\xfc\xc0w\xa9\xa9w\x03@w\xa9\xa9\x00\x00\x00\x00\x04\x00\x00\xff\x80\b\x80\x05\x00\x00'\x00/\x00?\x00P\x00\x00\x01\x06+\x015#\"&547.\x01467&546;\x01532\x17!\x1e\x01\x17\x1e\x02\x14\x0e\x01\a\x0e\x01\a7\x16\x14\a\x1764'\x01!\x06\a\"\x06\x0f\x01\x01\x0e\x01+\x01\x0332\x03#\x1332\x16\x17\x01\x1e\x043\x05!&\x02ln\x9e\x80@\r\x13\a:MM:\a\x13\r@\x80\x9en\x04Y*\x81\x10Yz--zY\x10\x81*\x0655QDD\xfbU\x03\xf7\xd9\xef9p\x1b\x1c\xfe\xe0\x1aY-`]\x1d\x9d\x9d\x1d]`.X\x1a\x01 \x04\x0e/2I$\x01\xc8\xfc\tt\x01\xa0@@/!\x18\x19\x02\x11\x18\x11\x02\x19\x18!/@@\a\x16\x03\x0f3,$,3\x0f\x03\x16\a\xfc$p$\x1e0\x940\xfe\xd6&*0\x18\x18\xfe\xe0\x1a&\x01\xd0\x01\xe0\x01\xd0&\x1a\xfe\xe0\x04\r!\x19\x15P@\x00\x02\x00\x00\xff\x80\x06\x80\x06\x00\x00R\x00V\x00\x00\x012\x16\x15\x14\x0f\x01\x17\x16\x15\x14\x06#\"&/\x01\x05\x17\x16\x15\x14\x06#\"&/\x01\a\x06#\"&546?\x01\x03\a\x06#\"&546?\x01'&54632\x16\x1f\x01%'&54632\x16\x1f\x017632\x16\x15\x14\x06\x0f\x01\x1376\x01%\x03\x05\x05\xef>S]\xac8\aT;/M\x0f7\xfe\xca7\bT\x057%>\x01\x04\xe0w\xa9\xa9w\xfc@w\xa9\xa9w\x03\xe0\x1f!\"\xc55bBBb/\xbe/\f*\n8(\x03@(87)\xfc\xc0(8=%/\xb5'\x03\x1c\x0e\x1c\x13\x18\x15\x14\x15\x18\x13\x1c\x0e\x1c\x03\x01\v#?\x05\x80\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfb\xe0\x01\xb4#\x14\x16~$EE y \b&\b\xfeL(88\x02e):8(%O\x19 r\x1a\x02\x13\t\x11\t\n\x05\x05\n\t\x11\t\x13\x02\xae\x17O\x00\x00\x00\x00\x06\x00\x00\xff\x00\a\x00\x06\x00\x00\x05\x00?\x00G\x00Q\x00a\x00q\x00\x00\x1347\x01&\x02\x01\x14\x0e\x03\a\x03\x0167>\x01&\x0f\x01&'&\x0e\x01\x1e\x01\x1f\x01\x13\x03\x0167>\x01&\x0f\x01\"$32\x04\x17#\"\x06\x15\x14\x1e\x06\x17\x16\x05\x13\x16\x17\x06#\"'\x01\x16\x15\x14\x02\a\x13654\x00 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126\x00 $6\x12\x10\x02&$ \x04\x06\x02\x10\x12\x16\u007fC\x01o\xc4\xee\x05\b\x05\x0f\b\x1b\x04L\xfe\xea.*\x13\x0e\x13\x13\xcdK\u007f\f\x11\x06\x03\x0f\fPx\xa8\xfe\xe8.*\x13\x0e\x13\x13\xcd\a \ni\x01SƓ\x01\vi\n7J\x04\x04\f\x06\x12\a\x16\x03?\xfe\x06\xed\x01\x04~\x81pi\x03{_Я\xeb;\xfc\xa2\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01U\x01Z\x01=刈\xe5\xfe\xc3\xfe\xa6\xfe\xc3刈\xe5\x02\x80\xa3\x96\xfc\x13_\x01t\x01\b\x13'<\x1cZ\r\xff\x00\x03:\x03\x05\x02!\x1d\x01\n\x01\t\x01\f\x12\x13\x0e\x01\b\xfe\xb8\xfe\b\x03@\x03\x05\x02!\x1d\x01\n\x01\xa0\xbbj`Q7\f\x18\x13\x1b\x0f\x1e\f$\x05k\xd3\xfdy\x06\x05, \x04R\xae\xc3\xd1\xfe\x9ff\x02\xa6\xa9k*\x024\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xf9\xb7\x88\xe5\x01=\x01Z\x01=刈\xe5\xfe\xc3\xfe\xa6\xfe\xc3\xe5\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x06\x00\x00\x12\x00\x1b\x00\x00\x01\x11\x05&$&546$7\x15\x06\x04\x15\x14\x04\x17\x11\x01\x13%7&'5\x04\x17\x04>\xfe\xf0\xe4\xfe\x8c\xd6\xc9\x01]\xd9\xd9\xfe\xe9\x015\xea\x03\xad%\xfd\xf3\x93w\xa1\x01\x15\xcc\x06\x00\xfa\x00\x80\x14\xa4\xfd\x92\x8c\xf7\xa4\x1a\xac&\xe0\x8f\x98\xe6\x1e\x05P\xfe?\xfezrSF\x1d\xac!|\x00\x00\x00\x03\x00\x00\xff\x00\a\x80\x06\x00\x00\f\x00&\x000\x00\x00\t\x01\x15#\x14\x06#!\"&5#5\x01!\x113\x11!\x113\x11!\x113\x11!\x1132\x16\x1d\x01!546;\x01\x052\x16\x1d\x01!5463\x03\xc0\x03\xc0\x80)\x1c\xfa\n\x1c)\x80\x01\x00\x01\x00\x80\x01\x00\x80\x01\x00\x80\x01\x00;\x1c)\xf9\x80)\x1c;\x06;\x1c)\xf8\x80)\x1c\x06\x00\xfe\x80\x80\x1a&&\x1a\x80\xff\x00\xfd\x00\x03\x00\xfd\x00\x03\x00\xfd\x00\x03\x00\xfd\x00&\x1a@@\x1a&\xc0&\x1a\x80\x80\x1a&\x00\x00\x02\x00\x00\xff\x80\t\x00\x05\x80\x00\r\x006\x00\x00\x01\x13\x16\x06\x04 $&7\x13\x05\x1627\x00\x14\a\x01\x06\"'%\x0e\x01\a\x16\x15\x14\a\x13\x16\a\x06+\x01\"'&7\x13&54767%&47\x0162\x17\x01\x06\xee\x12\x04\xac\xfe\xd6\xfe\xa4\xfe֬\x04\x12\x02>\x164\x16\x04P\x16\xfb\xa0\x04\f\x04\xfdt+8\x06?::\x02\n\t\x0f\xc0\x0f\t\n\x02::A\vW\xfe\xb3\x16\x16\x04`\x04\f\x04\x04`\x02\xbc\xfe\xc4EvEEvE\x01<\xb5\a\a\x02\x10.\b\xfe\xa0\x01\x01\xce\"\x9be$IE&\xfeO\x0e\v\v\v\v\x0e\x01\xb1&EI&\xcf{h\b.\b\x01`\x01\x01\xfe\xa0\x00\x01\x00m\xff\x80\x05\x93\x06\x00\x00\"\x00\x00\x01\x13&#\"\a\x13&\x00\x02'\x16327\x1e\x01\x12\x17>\x037\x163271\x0e\x03\a\x06\x03[\r>+)@\r(\xfe\xff\xb0]:2,C?\x8d\xc1*%\x91Zx/658:\x1c@#N\n\x92\x02C\xfd=\v\v\x02\xc3E\x01\xc5\x01(\x8b\x0f\x0fo\xed\xfe\xc4E=\xe9\x93\xcdW\x0e\x0e'c:\x86\x11\xf8\x00\x00\x01\x00\x00\xff\x80\x05\xe1\x05\x80\x00#\x00\x00\x01!\x16\x15\x14\x02\x04#\"$&\x02\x10\x126$3 \x17\a&#\"\x0e\x01\x10\x1e\x0132>\x037!\x03\x00\x02\xd5\f\xb6\xfe\xafڝ\xfe\xe4\xceyy\xce\x01\x1c\x9d\x01,\xd7\xd1{\xb7\x81ۀ\x80ہW\x92^F!\x06\xfeL\x02\xeeC=\xd9\xfe\xab\xc0y\xce\x01\x1c\x01:\x01\x1c\xcey\xc9\xc9w\x82\xdf\xfe\xf8߂0H\\R%\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\x10\x00\x19\x00\"\x00N\x00^\x00\x00\x01\x16\a\x06 '&762\x17\x1632762$\x14\x06\"&5462\x05\x14\x06\"&462\x1674&\"\a&'\x13\x17\x14\x16264&#\"\a'&\a\x03\x06\a&#\"\x06\x15\x14\x16\x17\x06\x15\x14\x0432$54'>\x01$\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04G\x10\x10>\xfe\xee>\x10\x10\x06\x12\x060yx1\x06\x12\xfe\xd34J55J\x01\xbf5J44J5\xfbFd$\x82\xb5?\xc84J55%6\x1a\xdd\x13\x06E\xb4\x81#42F%\x1f\x06\x01\x18\xc5\xc6\x01\x18\a\x1e$\x01f\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x01q\x10\x0f>>\x0f\x10\x06\x0611\x06\xd4J44%&4Z%44J54R1F$Z\x06\x01\x1b-%45J521\x05\x15\xfe\xc8\aZ%F1#:\x0f\x1b\x1d\x8e\xcaʎ \x19\x0f9\xbb\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x19\x00#\x00Q\x00a\x00\x00\x01\x16\a\x06\"'&762\x17\x162762%\x14\x06\"&5462\x16\x05\x14\x06\"&5462\x1674&#\"\a&'7\x17\x1e\x013264&#\"\a'&\a\x03\x06\a&#\"\x06\x15\x14\x16\x17\x06\x15\x14\x1632654'>\x01\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\xab\r\r5\xec5\r\r\x05\x10\x05*\xce*\x05\x10\xfe\xfe.>.-@-\x01R.>.-@-\xd7<+*\x1fq\x9a6\xab\x01-\x1f -- 0\x15\xbd\x11\x04<\x9ao\x1e,+< \x1a\x05\xf0\xa9\xaa\xf0\x06\x19\x1f\x013\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\x97\r\r55\r\r\x06\x06**\x06\x96\x1f..\x1f -- \x1f..\x1f --G*<\x1fN\x04\xf3' ,-@-+*\x05\x12\xfe\xf4\x06M <*\x1e2\r\x19\x17z\xad\xadz\x19\x18\r1\x01\xe4\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1e\x000\x00<\x00\x00\x01754&\"\x06\x15\x11\x14\x06\"&=\x01#\x15\x14\x163265\x114632\x16\x1d\x01\x055#\x15\x14\x06#\"&=\x01\a'\x15\x14\x1626\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03bZt\xa0t\x1c&\x1b\x97sRQs\x1b\x14\x13\x1b\x01\x89\x96\x1b\x14\x13\x1bZOpoO\xfe\xe5\x14\x1b\x1b\x14xzRrqP\x01\x18\x13\x1c\x1c\x136\xdfz~\x14\x1b\x1c\x13{\x1a\x1c{Prr\x01\xad\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x02\x00\x00\xff\xa3\a\x80\x05]\x00\x1e\x000\x00\x00\x0154&\"\x06\x15\x11\x14\x06#\"&5\x11!\x11\x14\x16265\x114632\x16\x1d\x01\a\x05!\x11\x14\x06#\"&5\x11\x177\x11\x14\x16265\x04&\x02'&\a\x0e\x01#\".\x01'&'\x04#\"&5467%&4>\x037>\x0132\x16\x17632\x16\x15\x14\x06\x0f\x02\x06\x1632654.\x02547'654'632\x1e\x05\x177\x0e\x03\x177.\a'.\x02*\x01#\"\a>\x057\x1e\x02?\x01\x15\x1767>\b?\x01\x06\a\x0e\x01\a\x0e\x02\a\x1e\x01\x15\x14\x03>\x0132\x1e\x03\x17\x06#\"'\x017\x17\a\x01\x16\x15\x14\x0e\x03\a'>\x023\x01\a'>\x0132\x133\x17\a\x015\x15\x0f\x01?\x02\x04\xc6K\x89cgA+![,7*\x14\x15\n\x18\f2\x03(-#\x01=\x05\x11\a\x0e\x06\n\a\t\x04\a\x0f\x1a\x12/\x0e~[\x10(D?\x1dG\b\f \x16\f\x16\xf7|\x1c,)\x19\"\x0e#\v+\b\a\x02)O\xfc\xb4\x0e8,\x11\x03+\xf7'\xb96\t\x1b\x1d\x17\x19\x02y{=@\xfe\xf90mI\x01\xa1\x03#938\x04\a\x15OA\x1c\xfeE`\x06\n-\f\x13\xd3\x1f\n)\x03y\x01\x02\x01\x02\x01\x02_\x03/FwaH8j7=\x1e7?\x10%\x9c\xad\xbc\x95a\x02\x04\x05\t\x05%\a\x1d\f\x1e\x19%\x16!\x1a?)L\x0f\x01\x15\n\x10\x1fJ\x16\r9=\x15\x02\x1a5]~\x99\x14\x04\x1ap\x16\x10\x0f\x17\x03j\x0e\x16\r\n\x04\x05\x02\x01\r \x11%\x16\x11\x0f\x16\x03(\x10\x1a\xb7\xa01$\"\x03\x14\x18\x10\x12\x13,I\x1a \x10\x03\x0e\r$\x1f@\x1c\x19((\x02\v\x0f\xd6\x05\x15\b\x0f\x06\n\x05\x05\x02\x03\x04\x01+\x1e!\x1a.\x1bS\t\t-\x1c\x01\x01L\x01__\x15$'\x17-\x119\x13L\x0f\t5V\xa5\xc6+\x03\t\n\t\x136\a\v\xfcT\x1a+\x1f6.8\x05-\v\x03$\f\xb10\xfe\xd0\x0f\x01\a\x0f\v\b\a\x01+\x02\r\a\x02t\x14\x11\x01\f\xfd|S\f\x061\x01\x01\x05\x02\x03\x04\x01\x00\x00\x04\x00\x00\xff\x12\x06\x00\x05\xee\x00\x17\x006\x00]\x00\x83\x00\x00\x05&\a\x0e\x01#\"'&#\"\a\x0e\x01\x17\x1e\x0167>\x0276'&'&#\"\a\x06\a\x06\x17\x1667>\a32\x1e\x01\x17\x1e\x0176\x014.\x02#\"\x0e\x01#\x06.\x03\a\x0e\x01\a\x06\x17\x1e\x0132>\x02\x17\x1e\x03\x17\x1667>\x017\x14\x02\x06\x04 $&\x0254>\x057>\x037>\x017\x16\x17\x1e\x01\x17\x1e\x06\x04\x8f\x05\x13\x1erJ\x81@\x05\b\v\x0f\a\x01\b\"kb2)W+\a\f,\x13\x14\x175/\x18\x1d1\x1a\x0e\t\x11\x17\x03\x0f\x06\x0e\t\x10\x0e\x13\v\x1b#\v\b\n\x05\n\x17\x01Z\n\x17-\x1e!\x80\x82$\x1bIOXp7s\xa4\x02\x02L\x1dCF9\x96vz \x1aNAG\x14#/ \x1c\x1d5|\xd0\xfe\xeb\xfe\xd0\xfe\xe6Հ';RKR/\x13\x0eJ#=\x1e$,\b\x819,\xac+\x15$UCS7'2\x13\x0e\x16\"1\x04\f\x06\x14\n \x1c\x03\x03\x04!\x1b\a\f\x84/\x0e\x0f\n\f,\x18\x14\b\a\x14\x02\r\x04\n\x04\x06\x03\x02\x0f\x0e\x0f\x11\x06\x04\f\x01/\x16--\x1cST\x01(::(\x01\x01\x9bep4\x14\x11AM@\x01\x01=I>\x01\x03\".)xΤ\xfe\xe7\xbfls\xc7\x01\x1c\xa0Y\xa7|qK@\x1d\n\b%\x14(\x18\x1cYQ\x9b&\x1dN\x1b\r\x18EHv~\xab\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1e\x00<\x00Z\x00x\x00\x00\x01\x0f\x02\x0e\x01'\x0e\x01#\"&5467&6?\x01\x17\a\x06\x14\x17\x162?\x03\x03\x17\a'&\"\x06\x14\x1f\x03\a/\x02.\x017.\x0154632\x16\x176\x16\x01\x14\x06#\"&'\x06&/\x017\x17\x16264/\x037\x1f\x02\x1e\x01\a\x1e\x01\x03\x14\x06\a\x16\x06\x0f\x01'764&\"\x0f\x03'?\x02>\x01\x17>\x0132\x16\x04.\xa0\x97\x1eA\xadU\x10pIUxYE\x16.A\f\x97\v%%%h%\x1e\x97\xa1\xbe\f\x98\f%hJ%\x1d\x98\xa0\x97\xa1\x97\x1eD,\x1bFZxULs\fT\xab\x03gxUJr\x0eV\xbbD\v\x97\f%hJ%\x1e\x98\xa0\x98\xa0\x98\x1d@/\x15Le\x02fL\x1a.C\f\x97\f%Jh%\x1e\x98\xa0\x98\xa1\x98\x1dC\xb8V\vsNUx\x01Ϡ\x98\x1e@.\x15FZyUHp\x10V\xaeA\f\x98\v%h&%%\x1e\x98\xa0\x02\x12\f\x98\f%Ji%\x1d\x98\xa0\x98\xa0\x98\x1eC\xb9W\x0fpIUybJ\x14/\xfb\x95Uy^G\x1c,D\f\x98\f%Jh%\x1e\x98\xa0\x98\xa0\x98\x1e@\xadU\vs\x04\x17Mt\vU\xb7C\f\x98\f%hJ%\x1e\x98\xa0\x98\xa0\x98\x1eC-\x1aKfy\x00\x00\b\x00\x00\xff\x00\x06\x00\x06\x00\x00E\x00X\x00[\x00_\x00g\x00j\x00\x89\x00\xa3\x00\x00\x01\x06&/\x01&'.\x01'\x06\a\x06\a\x0e\x01'67>\x017>\x017&\a\x0e\x02\a\x06\x14\a\x06\a\x06'&'&'>\x0176763>\x017>\x02\x17\x16\a\x14\x0e\x01\a\x06\a\x17\x1e\x01\x17\x1e\x01\x03\x16\a\x06\a\x06#&'&'7\x1e\x0167672\x05\x17'\x01%\x11\x05\x01\x17\x03'\x03\x177\x17\x01\x05\x11\x01\x17\a'\x06\a\x06+\x01\"&'&54632\x1e\x01\x17\x1e\x013267>\x027\x01\x11%\x06\x04#\"'4'\x11676767\x11\x052,\x0132\x15\x11\x02\x8e\x01\x17\x14\x14,+\aD\x04CCQ\x18\x04\x1f\x03\x06L\x15\x81\x0e\x11D\x02\bf\b'\x1e\x02\x02\x01\x05\x1a\x17\x18\x12\n\x04\x01\x06%\v:/d\x02\nB\v\t\x19\x04\x04\x02\x03\x19\x1c\x03\x194@\f}\x05\x04\r\xcf\x03\a\f&\x1e\x1e\x1a\x17\x0e\x04\x01\x03!\x140$\x13\x11\x02\xbe?\x8b\xfb\xf8\x02\xb6\xfdJ\x04\xd9f\xb5d\xd8f-\xd3\xfe.\x02=\xfe\xfa\x9e6(\x82\x92:!TO\xf1?\b\n\b\x04\x1c!\x04I\xadG_\x90U\x0f\x1f%\n\x01\x95\xfc\xfa\x0e\xfd.\a\r\x05\x01\x03\x01\x05\x0fk*\x02.\x02\x01=\x01;\x04\x14\x01\xca\x03\a\b\t\x14\x1d\x055\x02gN_\x0f\x02\x04\x02\x04X\x18\xb6\x1b\x1e\x89\t\x01\"\x02\v\b\x01\x02\x11\x01\n\x05\a\a\x04\x11\x06\x11\x02\x06\x03\x10\x10#\x02#\x04\x03\n\x01\x01\f\x15\x0229\x052Q\x1c\x064\x02\x011\x01\xe0\x0f\r\x17\x0f\f\x03\x17\x0f\x1a\x03\x03\x04\x04\x0e\f\x02\x92\xe3*\xfd\x99\xe8\x04\b\xe9\xfd6\x1f\x02\x91\x1f\xfd\xe8\x1fnA\x03;\xb8\x01|\xfa\x11\r\xa0BS\x19\fN.\a\t\b\v\x0f\x12\x02%1\x1d$\a\x11\x15\x06\x04\x80\xfb\xc9\xf6\x06\xf3\r\x01\x02\x046\t\x01\x06\x05$\x0e\x01\x80\xc6nk\x15\xfe^\x00\f\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00'\x007\x00G\x00W\x00g\x00w\x00\x87\x00\x97\x00\xa7\x00\xb7\x00\xc0\x00\x00\x012\x16\x15\x11\x14\x06+\x01\"&5\x11463\x05\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x1f\x01\x1e\x01\x15\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x13\x11#\"&=\x01!\x11\x01 B^^B\x80B^^B\x05\xe0:F\x96j\xfc\xa0B^8(\x02\xa0(`\x1c\x98\x1c(\xfd \x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x01\x00\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x01\x00\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12`\xa0(8\xfd\x80\x04\x80^B\xfb\xc0B^^B\x04@B^\xa3\"vE\xfd\x00j\x96^B\x06\x00(8(\x1c\x98\x1c`(\xfb\x80\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\xfe\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\xfe\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x12\x01\x8e\x01\x008(\xa0\xfe\x00\x00\x14\x00\x00\xff\x00\x05\x80\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\xbf\x00\xcf\x00\xdf\x00\xef\x00\xff\x01\x0f\x01\x1f\x01/\x01?\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x01\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x11\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x11\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x11\x15\x14\x16;\x0126=\x014&+\x01\"\x06\x0354&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&#!\"\x06\x1d\x01\x14\x163!26\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x0154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x05@\x1a&&\x1a\xfb\x00\x1a&&\x1a\x01\xc0\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x02\x00\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x06\x00&\x1a\xf9\x80\x1a&&\x1a\x06\x80\x1a&\xfe\xe0@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfe\xb2@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\xfb\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x02\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\xfc\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x00\x00\x00\x02\x00@\xff\x10\x04\xc0\x05`\x00\x1f\x00'\x00\x00\t\x01\x11\x14\x06\"&5\x11#\x11\x14\x06\"&5\x11\x01&4762\x1f\x01!762\x17\x16\x14$\x14\x06\"&462\x04\xa4\xfe\xdcB\\B@B\\B\xfe\xdc\x1c\x1c\x1dO\x1c\xe4\x01p\xe4\x1cP\x1c\x1c\xfe\xa0\x83\xba\x83\x83\xba\x03\xdc\xfe\xdc\xfc\xc8.BB.\x01\x80\xfe\x80.BB.\x038\x01$\x1cP\x1c\x1c\x1c\xe4\xe4\x1c\x1c\x1dO広\x83\xba\x83\x00\x05\x00\x00\xff\x80\x06\x80\x05\x80\x00\x0f\x00\x1d\x003\x00C\x00Q\x00\x00\x01\x14\x0e\x01#\".\x0154>\x0132\x1e\x01\x01\x14\x06#\".\x0154632\x1e\x01\x052\x04\x12\x15\x14\x0e\x02#\"&#\"\x06#\"54>\x02%\".\x0154>\x0132\x1e\x01\x15\x14\x0e\x01%2\x16\x15\x14\x0e\x01#\"&54>\x01\x03\f&X=L|<&X=M{<\xfe\xaaTML\x83FTML\x83F\x01\x8av\x01\x12\xb8\"?B+D\xef?B\xfdJ\xb7p\xa7\xd0\x01H=X&<{M=X&<|\x01dMTF\x83LMTF\x83\x04(\x012\x1e\x01\x02\xc0r_-\x02$\x1a\xc0\x1a$\x02-_rU\x96\xaa\x96U\x03\xf0\x91\xc5%\xfc\xcb\x1a&&\x1a\x035%ő\x80\xf3\x9d\x9d\xf3\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\x80\x00\x03\x00\a\x00\x1f\x00\x00\x05\x01\x11\x05'-\x01\r\x01\x11\x14\x06\a\x01\x06\"'\x01.\x015\x11467\x0162\x17\x01\x1e\x01\x03\x80\x02\x80\xfd\x80@\x02\xba\xfdF\xfdF\x05\xfa$\x1f\xfd@\x1cB\x1c\xfd@\x1f$.&\x02\xc0\x16,\x16\x02\xc0&.]\x01]\x02|\xe9q\xfe\xfe\xfe\x02\xfd\x00#<\x11\xfe\x80\x10\x10\x01\x80\x11<#\x03\x00(B\x0e\x01\x00\b\b\xff\x00\x0eB\x00\x00\x00\x00\a\x00\x00\xff\x00\b\x80\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00B\x00\x00\x05%\x11\x05'-\x01\x05\x01%\x11\x05'-\x01\x05'%\x11\x05'-\x01\x05\x01\x11\x14\x06\a\x05\x06\"'%&'\x06\a\x05\x06\"'%.\x015\x11467%\x11467%62\x17\x05\x1e\x01\x15\x11\x05\x1e\x01\x02\x80\x01\x80\xfe\x80@\x01\x94\xfel\xfel\x05\xd4\x01\x80\xfe\x80@\x01\x94\xfel\xfel,\x01\x80\xfe\x80@\x01\xb9\xfeG\xfeG\x05\xf9&!\xfe@\x19@\x19\xfe@\x04\x03\x02\x05\xfe@\x19@\x19\xfe@!&+#\x01\xb2+#\x01\xc0\x176\x17\x01\xc0#+\x01\xb2$*`\xc0\x01:\xa4p\xad\xad\xad\xfd\x8d\xc0\x01:\xa4p\xad\xad\xadx\xa5\x01\n\xa4p\xbd\xbd\xbd\xfd=\xfe`$>\x10\xe0\x0e\x0e\xe0\x02\x02\x02\x02\xe0\x0e\x0e\xe0\x10>$\x01\xa0&@\x10\xba\x01\x90&@\x10\xc0\n\n\xc0\x10@&\xfep\xba\x10@\x00\x00\x06\x00\x00\xff\xfe\b\x00\x05\x02\x00\x03\x00\t\x00\x1f\x00&\x00.\x00A\x00\x00\x01!\x15!\x03\"\x06\a!&\x032673\x02!\"\x0254\x0032\x1e\x01\x15\x14\a!\x14\x16%!254#!5!2654#!%!2\x1e\x02\x15\x14\a\x1e\x01\x15\x14\x0e\x03#!\a8\xfe\x01\x01\xff\xfcZp\x06\x01\x98\x12\xa6?v\x11\xddd\xfe\xb9\xd6\xfd\x01\x05Ί\xcde\x02\xfdns\xfb6\x01(\xcd\xc7\xfe\xd2\x01\x19N[\xbe\xfe\xfc\xfe\xeb\x02RW\x88u?\xacrt1Sr\x80F\xfd\x9d\x04\xad|\xfe\xd2iZ\xc3\xfd\xb7@7\xfe\xcd\x01\b\xd7\xd0\x01\x13\x88މ\x11\x1eoy2\xa7\xb4\xbeIM\x90\xd7\x1cC~[\xb5R \xa6yK{T:\x1a\x00\x00\x00\a\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1e\x00%\x00,\x00A\x00G\x00K\x00\x00\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x13!\x11!2654'654.\x02\x03#532\x15\x14\x03#532\x15\x14\x05\"&5!654&#\"\x06\x15\x14\x16327#\x0e\x01\x032\x17#>\x01\x03!\x15!\x04\xe0w\xa9\xa9w\xfc@w\xa9\xa9w\xd3\xfe\x8d\x01~u\xa0\x8fk'JTM\xb0\xa3wa\xb9\xbd|\x02\nDH\x01\x9b\x01\x95\x81\x80\xa4\x9e\x86\xcd>\x8a\vI1q\v\xfe\x04Fj\x01?\xfe\xc1\x05\x80\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfe\x91\xfc\xedsq\x9e*4p9O*\x11\xfe¸Z^\xfe\xb1\xd9qh LE\n\x14\x84\xb1\xac\x82\x87\xa4\xbf\"(\x01nz8B\x01\nM\x00\x00\x00\x04\x00\x00\xff\x80\a\x00\x05\x80\x00\a\x00\x1b\x00'\x00?\x00\x00\x00\x14\x06\"&462\x004&#\"\a\x17\x1e\x01\a\x0e\x01'.\x01'\x1e\x0132\x014&#\"\x06\x15\x14\x163267\x14\x00#\x01\x0e\x01#\"&/\x01\x11\x05632\x17\x016\x0032\x00\x06.\x8fʏ\x8f\xca\xfd\x8d\x92h\x1b\x1bhMA\x1f\x1f\x98L\x15R\x14 vGh\x03г~\u007f\xb3\xb3\u007f~\xb3\x96\xfe\xf5\xbc\xfeK\f\u0084y\xba\x19\xe6\x01\x85O^\r\x16\x01\x1c\x02\x01\v\xbb\xbc\x01\v\x04\x1fʏ\x8fʏ\xfb\xbeВ\x06*\x1f\x97LM@\x1f\b!\b\xfeשw\x03\xc0w\xa9\xf7\x8eȍ\x8dde\x8d\x03)\xa0qrOPq\xfeȦs:0\x14\x14\x183=\x027\x16\x1b\x01'\x0e\x03\x0f\x01\x03.\x01?\x0167'\x01\x03\x0e\x01\x0f\x01\x06\a\x17\x03\x13\x17\x1667\x01\x06\x03%'\x13>\x01\x17\x1e\x05\x01\x13\x16\x06\a\x0e\x05\a&\x03%'7\x03%7.\x03/\x01\x056\x16\x1f\x01\x16\x03D\x0f\x02\xfe\\$>\x10\v\a\x0f\t\"\x02N,\xb4\x93?a0\x1f\x03\x04\xbe\x11\x02\a\b#O\x8c\x06\x80\xbc\f1\x13\x12G\x94\b\xe6\xd3\a\xaa\xe29\xfd'/\xda\xfe\xc3\x13\xe1\x14P(\x181#0\x180\x02\x97\xd4\x12\v\x16\r($=!F\v\"\xe7\x019|\x8e\xdc\xfe]\x97\"RE<\x11\x11\x01\x95\x1f6\f\v'\x01o\xfe\x90\x16\x1d\x039%\x1b8J$\\\a\f\x02:\xfe\x85\\H\x91iT\x15\x15\x01e\x1a<\x11\x12?}V\xfd\xea\xfe\x99\x1d#\x03\x04\a\x05\xa4\x01o\x01j\xad\x10\x16\x16\x03\xb2?\xfe\x8c\xbb\f\x01d\x1f\x1c\x04\x02\x14\x16,\x196\xfe\xc5\xfe\x95%N#\x14\"\x16\x16\n\x12\x03H\x01l\xc3\xedS\xfe\x8b\x14VY\x9a]C\r\r\x01\x03\x1b\x0f\x0f=\x00\x00\x04\x00\x00\xff@\b\x00\x05\x80\x00\a\x00\x11\x00\x19\x00C\x00\x00\x004&\"\x06\x14\x162\x13!\x03.\x01#!\"\x06\a\x004&\"\x06\x14\x162\x13\x11\x14\x06+\x01\x15\x14\x06\"&=\x01!\x15\x14\x06\"&=\x01#\"&5\x1146;\x01\x13>\x013!2\x16\x17\x1332\x16\x01\xe0^\x84^^\x84\x82\x03\xf8Y\x02\x18\t\xfd\x00\t\x18\x02\x05\x03^\x84^^\x84\xfe\x12\x0e`p\xa0p\xfc\x00p\xa0p`\x0e\x12\x83]\x1ci\x17\xa2b\x03\x00b\xa2\x17i\x1c]\x83\x01~\x84^^\x84^\x01\xe0\x01e\b\x13\x13\b\xfd\x19\x84^^\x84^\x01\x00\xfe\x80\x0e\x12\x80PppP\x80\x80PppP\x80\x12\x0e\x01\x80]\x83\x01\xa3^\u007f\u007f^\xfe]\x83\x00\x04\x00\x00\xff\x00\b\x00\x06\x00\x003\x00;\x00E\x00M\x00\x00\x012\x16\x15\x11\x14\x06+\x01\x15\x14\x06\"&=\x01!\x15\x14\x06\"&=\x01#\"&5\x1146;\x01\x13>\x01;\x015463!2\x16\x1d\x0132\x16\x17\x13\x00264&\"\x06\x14\x01!\x03.\x01#!\"\x06\a\x00264&\"\x06\x14\a ]\x83\x12\x0e`p\xa0p\xfc\x00p\xa0p`\x0e\x12\x83]\x1ci\x17\xa2b\x80\x12\x0e\x01\xc0\x0e\x12\x80b\xa2\x17i\xf9\xfa\x84^^\x84^\x01d\x03\xf8Y\x02\x18\t\xfd\x00\t\x18\x02\x04!\x84^^\x84^\x02\x80\x83]\xfe\x80\x0e\x12@PppP@@PppP@\x12\x0e\x01\x80]\x83\x01\xa3^\u007f\xe0\x0e\x12\x12\x0e\xe0\u007f^\xfe]\xfe ^\x84^^\x84\x01\x82\x01e\b\x13\x13\b\xfc\xbb^\x84^^\x84\x00\x01\x00 \xff\x00\x05\xe0\x06\x00\x003\x00\x00$\x14\x06#!\x1e\x01\x15\x14\x06#!\"&5467!\"&47\x01#\"&47\x01#\"&47\x0162\x17\x01\x16\x14\x06+\x01\x01\x16\x14\x06+\x01\x01\x05\xe0&\x1a\xfe2\x01\n$\x19\xfe\xc0\x19$\n\x01\xfe2\x1a&\x13\x01\x92\xe5\x1a&\x13\x01\x92\xc5\x1a&\x13\x01\x80\x134\x13\x01\x80\x13&\x1a\xc5\x01\x92\x13&\x1a\xe5\x01\x92Z4&\x11\x8d&\x19##\x19&\x8d\x11&4\x13\x01\x93&4\x13\x01\x93&4\x13\x01\x80\x13\x13\xfe\x80\x134&\xfem\x134&\xfem\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x15\x00+\x00D\x00P\x00\x00\x014'&#\"\a\x06\x15\x14\x16327632\x17\x1632674'&!\"\a\x06\x15\x14\x1632763 \x17\x16326\x134'&$#\"\a\x0e\x01\x15\x14\x16327632\x04\x17\x1632>\x01\x10\x02\x04 $\x02\x10\x12$ \x04\x04g\x1e\xc1\xfe\x85\x9a*\x1b\x16\x05 \x84o\xe2\xab\x13\x0e\x13\x1c`#\xed\xfeə\x960#\x19\a\x1ez\x81\x01\x17\xd1\x18\x0e\x19#l(~\xfe\xb2\xb0̠\x17\x1f)\x1f\v\x1d\x85\xae\x9f\x01-g\x15\x13\x1d+\xcd\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01F \x13s\"\t+\x14\x1d\b\x1bg\v\x1b\xec(\x15\x8d*\r3\x19#\b!|\r#\x01\x11/\x17IK/\a%\x1e\x1f*\b%D=\f)[\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x01\x00\x00\xff\x80\x04\x00\x06\x00\x00\x13\x00\x00\t\x01\x17!\x11!\a\x03\a!\x11\x01'!\x11!7\x137!\x04\x00\xfe\xd1\x18\x01\x17\xfe\x05,\x8e\x1e\xfe\xd3\x01/\x18\xfe\xe9\x01\xfb,\x8e\x1e\x01-\x04\xd1\xfd\xba\x1f\xfea\x1e\xfe\xef\x1e\x01/\x02G\x1e\x01\x9f\x1e\x01\x11\x1e\x00\x00\x00\x11\x00\x00\x00\x8c\t\x00\x04t\x00\x0e\x00%\x00/\x00;\x00<\x00H\x00T\x00b\x00c\x00q\x00\u007f\x00\x8d\x00\x90\x00\x9e\x00\xac\x00\xc0\x00\xd4\x00\x00%7\x03.\x01#\"\x06\x15\x03\x17\x1e\x0132%7\x034'&\"\a\x06\x15\a\x03\x14\x17\x15\x14\x17\x1632765\x01\x17\a\x06\"/\x017627\x17\a\x06#\"5'7432\x01\x03\x17\a\x14#\"/\x017632\x1f\x01\a\x06#\"5'7432\x1f\x01\a\x06#\"&5'74632\t\x01\x13\a\x14\x06#\"/\x01\x13632\x167\x13\a\x14\x06#\"/\x01\x13632\x167\x13\a\x06#\"/\x01\x134632\x16\x019\x01\x03\x13\a\x14\x06\"&/\x01\x13462\x16\x17\x13\a\x14\x06\"&/\x01\x13>\x012\x16\x13\a1\x14\x06\"&/\x02\x13567632\x17\x16\x17\x01\x14\x06#!.\x015\x1147632\x00\x17632\x16\x03\x10\x10\x10\x01\r\n\t\x0e\x0e\x0e\x01\r\t\x16\x01*\v\f\r\b\x10\b\r\x01\n\v\x06\t\x0e\v\t\t\xfb\xec\x14\x14\x02\x0e\x02\x11\x11\x02\x0eX\x1a\x1a\x02\b\t\x17\x17\t\b\x01\x1a\xbc\x19\x19\v\n\x02\x15\x15\x02\n\v^\x17\x17\x02\f\r\x15\x15\r\f`\x15\x15\x02\x0e\x06\t\x14\x14\t\x06\x0e\x01\x81\xfe\xdf\x15\x15\n\a\x10\x02\x12\x12\x02\x10\a\n^\x13\x13\v\b\x12\x02\x10\x10\x02\x12\b\vb\x12\x12\x02\x14\x13\x02\x10\x10\r\b\t\f\x01\x89\xc6\x0f\x0f\x0f\x14\x0e\x01\x0e\x0e\x0f\x14\x0fc\x0e\x0e\x10\x16\x10\x01\f\f\x01\x10\x16\x0f\xd5\x0e\x12\x1a\x12\x01\x06\x06\f\x02\n\t\v\b\a\x0e\x02\x04f\xa6u\xfc\xee\r\x12\x1cU`\xc3\x01\x1e\x1159u\xa6\xa4\xf1\x02\v\n\x0e\x0e\n\xfd\xf5\xf1\n\r4\xd3\x02J\x10\b\x05\x05\b\x10\x06\xfd\xbd\x01\xeb\x01\n\a\v\t\a\r\x01l\x80~\t\t~\x80\tF\xcf\xcb\t\n\xca\xcf\t\xfe2\x01\xeb\xf5\xed\v\v\xed\xf5\f\x05\xfc\xf4\r\r\xf4\xfc\r\x1f\xea\xf6\x10\t\a\xf6\xea\x06\t\xfe\x16\x02m\xfe\x84\xf6\a\v\x12\xf6\x01|\x12\vO\xfe,\xf4\b\v\x13\xf4\x01\xd4\x13\v \xfe\x06\xf2\x15\x15\xf2\x01\xfa\t\r\r\xfd\x11\x02\xea\xfe\x02\xef\n\x0f\x0e\v\xef\x01\xfe\v\x0e\x0e\x1e\xfe\x14\xec\v\x10\x10\v\xec\x01\xec\f\x10\x10\xfe\b\xe7\r\x12\x12\rru\x02|\x03\x0f\t\a\x05\b\x12\xfd\x94u\xa5\x02\x12\r\x03\x83\x17\n\"\xfe\xf9\xc0\x16\xa6\x00\x00\x00\x04\x00\x00\xff\x00\x06\x00\x06\x00\x00\r\x00\x1b\x00)\x009\x00\x00\x00 $7\x15\x14\x06\x04 $&=\x01\x16\x00 $7\x15\x14\x06\x04 $&=\x01\x16\x00 $7\x15\x14\x06\x04 $&=\x01\x16\x00 \x04\x16\x1d\x01\x14\x06\x04 $&=\x0146\x02\x13\x01\xda\x01\x9cw\xce\xfe\x9e\xfe`\xfe\x9e\xcew\x01\x9c\x01\xda\x01\x9cw\xce\xfe\x9e\xfe`\xfe\x9e\xcew\x01\x9c\x01\xda\x01\x9cw\xce\xfe\x9e\xfe`\xfe\x9e\xcew\x01\xb9\x01\xa0\x01b\xce\xce\xfe\x9e\xfe`\xfe\x9e\xce\xce\x03\x00VT\xaaEvEEvE\xaaT\xfc\xaaVT\xaaEvEEvE\xaaT\x01*VT\xaaEvEEvE\xaaT\x04*EvE\x80EvEEvE\x80Ev\x00\b\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00^\x00c\x00t\x00\u007f\x00\x87\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01\x16\x17632\x17\x16\a\x14\x06\a\x15\x06#\"&'\x06\a\x02#\"/\x01&'&7>\x0176\x17\x16\x156767.\x0176;\x022\x17\x16\a\x06\a\x16\x1d\x01\x06\a\x16\x0167\x0e\x01\x01\x06\x17674767&5&5&'\x14\a\x0367.\x01'&'\x06\a\x06\x05&#\x163274\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x02\xfe!3;:\x93\x1e\x10\x0e\x02\x01\x06A0\x86?ݫ\x99Y\x0f\r\x18\x01\x05\n\x04\t^U\x0e\t\x0247D$\x18\r\r\v\x1f\x15\x01\x17\f\x12\t\x02\x02\x01\x02\f7\xfe\x1b4U3I\x01\x81\x0f\r\x01\x06\a\x01\x03\x01\x01\x01\f\x01|\x87\x95\x02\x16\x05L3\x1b8\x1e\x02w\x18tL0\x0e\x04\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x02Q\x1a\x1e\a1\x16\x1e\x01\x02\x01\x01&(!\x18;\xfe\xfa\a\f\x01\x04\n\x1a(g-\t\x0f\x02\x02Up\x88~R\x9b2(\x0f\x15/\x06\x02\x03\x05\x1e{E\xa4\xfe\x1b\x18\x86(X\x03z*Z\a%\x03(\x04\x04\x01\x01\x02\x01\x16\x0e\x01\x01\xfdi6\x1b\x01\x11\x05CmVo8\v\x18\x1c\x01\x01\x00\x00\x00\x00\x04\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00T\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x13\x153\x133\x1367653\x17\x1e\x01\x17\x133\x1335!\x153\x03\x06\x0f\x01#4.\x015.\x01'\x03#\x03\x0e\x01\x0f\x01#'&'\x0335\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00iF\xa4\x9f\x80\a\x03\x02\x04\x03\x01\x05\x03\x80\x9f\xa4F\xfe\xd4Zc\x05\x02\x02\x04\x01\x02\x01\x06\x02\x90r\x90\x02\x05\x01\x04\x04\x02\x02\x05cZ\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03\x80k\xfdk\x01\xe5\x14\x1a\x10\b\x18\x03\"\t\xfe\x1b\x02\x95kk\xfeJ\x14\x1a\x15\x03\a\t\x02\x05 \t\x02!\xfd\xdf\t\x1f\x06\x15\x15\x1a\x14\x01\xb6k\x00\x00\x04\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00S\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11%\x15!5#7>\x02;\x01\x16\x17\x1e\x02\x1f\x01#\x15!5#\x03\x1335!\x153\a\x0e\x01\x0f\x01#&'&/\x0135!\x153\x13\x03\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01-\x01\x19Kg\x05\n\x05\x01\x02\x01\x04\x02\x05\a\x03kL\x01#D\xc0\xc3C\xfe\xe9Jg\x04\f\x03\x02\x02\x01\x04\x06\vjL\xfe\xdeD\xbd\xc2\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\xeajj\xa1\a\x13\b\x04\x06\x04\a\t\x04\xa1jj\x01\x11\x01\x1akk\x9f\a\x13\x04\x03\x04\x06\v\f\x9fkk\xfe\xf0\xfe\xe5\x00\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x008\x00C\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11%\x15!5#5327>\x0154&'&#!\x153\x11\x01#\x1132\x17\x16\x15\x14\a\x06\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01 \x01G]\x89L*COJ?0R\xfe\x90\\\x01\x05wx4\x1f8>\x1f\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\xeajj\xa7\x0f\x17\x80RQx\x1b\x13k\xfd\xd5\x01\x18\x01\f\x12!RY\x1f\x0f\x00\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x00*\x002\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01\x11!57\x17\x01\x04\"&462\x16\x14\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x04\x80\xfc\x00\xc0\x80\x01\x80\xfeP\xa0pp\xa0p\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x01\xc0\xfe\xc0\xc0\xc0\x80\x01\x80\x80p\xa0pp\xa0\x00\x00\t\x00\x00\xff\x00\x06\x00\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00#\x00*\x007\x00J\x00R\x00\x00\x015#\x15\x055#\x1d\x015#\x15\x055#\x15\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11#\x15#5!\x11\x01\x13\x16\x15\x14\x06\"&5476\x1353\x1532\x16\x02264&\"\x06\x14\x02\x80\x80\x01\x00\x80\x80\x01\x00\x80\x03<\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\x80\x80\xfe\x00\x02\x8dk\b\x91ޑ\b\x15c\x80O\x16\"\xbcjKKjK\x04\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\x80\x80\xfa\x00\x02\xd1\xfe\xa3\x1b\x19SmmS\x19\x1b?\x01M\x80\x80\x1a\xfe\x1a&4&&4\x00\x00\x00\x00\x06\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x009\x00L\x00^\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01\x16\x15\x11\x14\a\x06#\"/\x01#\"&=\x0146;\x0176\x01276\x10'.\x01\a\x0e\x01\x17\x16\x10\a\x06\x16\x17\x16'2764'.\x01\x0e\x01\x17\x16\x14\a\x06\x16\x17\x16\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01\xec\x14\x14\b\x04\f\v\xa6\x83\x0e\x12\x12\x0e\x83\xa6\x10\x01\xb4\x1f\x13\x81\x81\x106\x14\x15\x05\x11dd\x11\x05\x15\x12\xbd\x1b\x14WW\x126&\x02\x1344\x13\x02\x13\x14\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03.\b\x16\xfd\xe0\x16\b\x02\t\xa7\x12\x0e\xc0\x0e\x12\xa7\x0f\xfdG\x18\x9f\x01\x98\x9f\x15\x06\x11\x115\x15{\xfe\xc2{\x155\x10\x0f\x94\x14]\xfc]\x13\x02$5\x149\x949\x145\x12\x11\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x003\x00C\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x05\x16\x15\x11\x14\a\x06#\"'\x015\x01632\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x02\x804LL4\xfe\x804LL4\x03l\x14\x14\b\x04\x0e\t\xfe\xf7\x01\t\t\x0e\x04\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03\x80L4\xfe\x804LL4\x01\x804L\x02\b\x16\xfd\xc0\x16\b\x02\t\x01\nZ\x01\n\t\x00\x00\x00\x06\x00\x00\xff\x00\x06\x00\x06\x00\x00\x13\x00\x1a\x00#\x007\x00K\x00[\x00\x00\x01\x1e\x01\x15\x11\x14\x06#!\"&5\x11463!2\x16\x17\a\x11!&'\x01&\x01\x11!\"&5\x11!\x11\x01>\x01\x1f\x01\x1e\x01\x0f\x01\x17\x16\x06\x0f\x01\x06&'\x03&7!\x16\a\x03\x0e\x01/\x01.\x01?\x01'&6?\x016\x16\x17\x01.\x017\x13>\x01\x1f\x01\x1e\x01\a\x03\x0e\x01'\x05\xbc\x1c(8(\xfa\xc0(88(\x03\x80(`\x1c\x84\x01x\n\f\xfe\xc7\f\x01c\xfe`(8\xfd\x00\x01`\b\x1a\v3\v\x03\b\xb6\xb6\b\x03\v3\v\x1a\b\xe2\x0e\x0e\x04\x04\x0e\x0e\xe2\b\x1a\v3\v\x03\b\xb6\xb6\b\x03\v3\v\x1a\b\xfev\r\x0f\x02\x8a\x02\x16\r?\r\x0f\x02\x8a\x02\x16\r\x04\x84\x1c`(\xfb\x80(88(\x06@(8(\x1cD\xfe\x88\x1d\f\x019\f\xfa\x12\x04\x008(\x01\xa0\xfa\x00\x03\x80\v\x03\b&\b\x1a\v\xf3\xf3\v\x1a\b&\b\x03\v\x01-\x13\x13\x13\x13\xfe\xd3\v\x03\b&\b\x1a\v\xf3\xf3\v\x1a\b&\b\x03\v\xfd\x06\x02\x16\r\x03?\r\x0f\x02\n\x02\x16\r\xfc\xc1\r\x0f\x02\x00\x01\x00'\xff\x97\x05\xd9\x06\x00\x006\x00\x00\x01\x15\x06#\x06\x02\x06\a\x06'.\x04\n\x01'!\x16\x1a\x01\x16\x1767&\x0254632\x16\x15\x14\a\x0e\x01\".\x01'654&#\"\x06\x15\x14\x1632\x05\xd9eaAɢ/PR\x1cAids`W\x1b\x01\x1b\x1aXyzO\xa9v\x8e\xa2д\xb2\xbe:\a\x19C;A\x12\x1f:25@Ң>\x02\xc5\xc6\x17\x88\xfe\xf2\xa1\x1a-0\x115r\x8f\xe1\x01\a\x01n\xcf\xda\xfe\x97\xfe\xef\xc6`\xa9\xedH\x01(\xb9\xc0\xf5\xd3\xc0\x9f\u007f\x01\x04\f' gQWZc[\xba\xd7\x00\x00\b\x00\x00\xff\x00\a\x00\x06\x00\x00\x03\x00\x06\x00\n\x00\x0e\x00\x12\x00\x15\x00\x19\x00-\x00\x00\x13\x01\x11%\x057'\t\x01%\x05'-\x01\x05'%\x11\t\x01\x17\x11\x05%\x01\x11\x05\x11\x14\a\x01\x06\"'\x01&5\x1147\x0162\x17\x01\x16\xd8\x02[\xfe\xb2\xfe\xb5\xc1\xc1\x033\x02[\xfe\xf3\xfe\xb2M\x01\x10\xfe\xf0\xfe\xf0\x8b\x01N\xfd\xa5\x04\xcd\xc1\xfe\xb5\x01\r\xfd\xa5\x033\"\xfc\xcd\x15,\x15\xfc\xcd\"\"\x033\x15,\x15\x033\"\x01o\xfen\x01g\xdf$\x81\x81\xfc\xdc\x01\x92\xb4߆\xb6\xb6\xb6]\xdf\x01g\xfen\xfe\xef\x81\x01\x02$\xb4\x01\x92\xfe\x99+\xfd\xde)\x17\xfd\xde\r\r\x02\"\x17)\x02\")\x17\x02\"\r\r\xfd\xde\x17\x00\x00\x00\x00\x02\x00\x00\x00\x00\b\x00\x05x\x00#\x00W\x00\x00\x01\x1e\x01\x15\x14\x06#\"&#!+\x02.\x015467&54632\x176$32\x04\x12\x15\x14\x06\x01\x14\x16327.\x01'\x06#\"&54632\x1e\x0532654&#\"\a\x17632\x16\x15\x14\x06#\".\x05#\"\x06\a\bo\x89\xec\xa7\x04\x0f\x03\xfbG\x01\x02\x05\xaa\xecn\\\f\xa4u_MK\x01'\xb3\xa6\x01\x18\xa3\x01\xfą|\x89g\x10?\fCM7MM5,QAAIQqAy\xa7\xa8{\x8fb]BL4PJ9+OABIRo?z\xaa\x02\xfc.\xc7z\xa4\xe9\x01\n\xe7\xa5n\xba6'+s\xa2:\x9a\xbc\xa1\xfe\xec\xa3\x06\x18\xfe\xf0z\x8ec\x14I\x0eAC65D*DRRD*\x8fwy\x8eal@B39E*DRRD*\x8d\x00\x00\x00\x00\x06\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00\x17\x00\x1f\x00'\x00/\x007\x00\x00\x00 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \a\x1762\x177\x017&47'\x06\x10\x00 7'\x06\"'\a\x12 6\x10& \x06\x10\x05\x176\x10'\a\x16\x14\x02\xca\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x02\xc0\xfe\x84\xab\xc2R\xaaR\xc2\xfb\xf1\xc2\x1c\x1c\xc2Z\x02B\x01|\xab\xc2R\xaaR\xc2\xca\x01>\xe1\xe1\xfe\xc2\xe1\x03d\xc2ZZ\xc2\x1c\x06\x00\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x0eZ\xc2\x1c\x1c\xc2\xfb\xf1\xc2R\xaaR«\xfe\x84\xfd\xbeZ\xc2\x1c\x1c\xc2\x01&\xe1\x01>\xe1\xe1\xfe\xc2\b«\x01|\xab\xc2R\xaa\x00\x01\x00 \xff \x06\xe0\x05\xd7\x00!\x00\x00\x01\x14\x02\x06\x04 $&\x0254\x12$7\x15\x06\x00\x15\x14\x1e\x02 >\x0254\x00'5\x16\x04\x12\x06\xe0\x89\xe7\xfe\xc0\xfe\xa0\xfe\xc0\xe7\x89\xc2\x01P\xce\xdd\xfe\xddf\xab\xed\x01\x04\xed\xabf\xfe\xdd\xdd\xce\x01P\xc2\x02\x80\xb0\xfe\xc0牉\xe7\x01@\xb0\xd5\x01s\xf0\x1f\xe4-\xfe\xa0\xe6\x82\xed\xabff\xab\xed\x82\xe6\x01`-\xe4\x1f\xf0\xfe\x8d\x00\x00\x01\x00\x13\xff\x00\x06\xee\x06\x00\x00c\x00\x00\x136\x12721\x14\a\x0e\x04\x1e\x01\x17\x1e\x01>\x01?\x01>\x01.\x01/\x01.\x03/\x017\x1e\x01\x1f\x016&/\x017\x17\x0e\x01\x0f\x01>\x01?\x01\x17\x0e\x01\x0f\x01\x0e\x01\x16\x17\x1e\x01>\x01?\x01>\x02.\x04/\x01&3\x161\x1e\b\x17\x12\x02\x04#\"$&\x02\x13\b\xd8\xc5\x05\x01\b(@8!\x05IH2hM>\x10\x10'\x1c\x0f\x1b\r\x0e\n)-*\x0e\rh'N\x14\x13\x01'\x15\x14\xa1\xa0!'\x03\x04\x16O\x1c\x1cg,R\x13\x13\x1f\"\x14/!YQG\x16\x15\x0154'6\x133&5\x1147#\x16\x15\x11\x14\x055\x06#\"=\x0132\x1635#47#\x16\x1d\x01#\x15632\x163\x15#\x15\x14\x1e\x0332\x014&\"\x06\x15\x14\x1626%\x11\x14\x06#!\"&5\x11463!2\x16\x02F]kbf$JMM$&\xa6N92Z2\x1d\b\x02\a\x18\x06\x15&`\x06\xe3\x06\xab\x0f9\x0eUW=\xfd\xf0N9:PO;:\x16dhe\x03\\=R\x91\x87\x01\xcd\xca\f\n+)\u007f\xb3\x17\b&'\x1f)\x17\x15\x1e-S9\xfe\xd0\x199kJ\xa5<\x04)Um\x1c\x04\x18\xa9Q\x8b\xb9/\xfc\xbe-Y\x02a^\"![\xfd\x9bY\xb1\xc4'(<`X;\x01_\x04\x02\x06\xbeL6#)|\xbe\x04\xfe\x93\x83\x04\x0etWW:;X\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x1b\x00\x00\t\x01#\x03\x06\a'\x03#\x01\x113\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03)\x01\np\x9d\x18\x14*\x9bx\x01\ae\x02שw\xfc@w\xa9\xa9w\x03\xc0w\xa9\x02\x14\x01\xf3\xfe\xc80,\\\x018\xfe\x13\xfe\xbc\x03\x8a\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x02\x009\xff\x00\x04\xc7\x06\x00\x00\x1d\x00I\x00\x00\x00\x14\x06#\"'\x06\a\x02\x13\x16\x06\a#\"&'&>\x03767&5462\x04\x10\x02\x04#\"'.\x017>\x01\x17\x1632>\x024.\x02\"\x0e\x02\x15\x14\x17\x16\x0e\x01&'&54>\x0232\x04\x03JrO<3>5\xf7-\x01\x1b\x15\x05\x14\x1e\x02\x0e\x15&FD(=G\x10q\xa0\x01\xee\x9c\xfe\xf3\x9e@C\x15\x17\x05\x05$\x1539a\xb2\x80LL\x80\xb2²\x80L4\n\r&)\n@]\x9c\xd8v\x9e\x01\r\x04\x14\xa0q#CO\xfe\x8d\xfe\x18\x16!\x02\x1b\x14~\U000ffd42\x0172765'.\x01/\x01\"\a\x0e\x01\a#\"&'&5\x10\x01\x0e\b\x16\r\x01\x11\x0e\xb9}\x8b\xb9\x85\x851R<2\"\x1f\x14\f\x017\x12\x03\x04MW'$\t\x15\x11\x15\v\x10\x01\x01\x02\x05;I\x14S7\b\x02\x04\x05@\xee5sQ@\x0f\b\x0e@\b)\xadR#DvTA\x14\x1f\v;\x14\x04\n\x02\x020x\r\x05\x04\b\x12I)\x01\x04\x04\x03\x17\x02\xda\x13!\x14:\x10\x16>\f\x8b\x01+\x03\x14)C\x04\t\x016.\x01\x13\x00\x00\x00\x00\x06\x00\x00\xff>\b\x00\x05\xc2\x00\n\x00\x16\x00!\x00-\x00I\x00[\x00\x00\x004&#\"\x06\x15\x14\x1632\x014&#\"\x06\x15\x14\x16326\x024&#\"\x06\x15\x14\x1632\x014&#\"\x06\x15\x14\x16326\x01&#\"\x04\x02\x15\x14\x17\x06#\".\x03'\a7$\x114\x12$32\x04\x16\x01\x14\x06\a\x17'\x06#\"$&\x106$32\x04\x16\x02D2)+BB+)\x03\x193(\x1b--\x1b(3\xec1)+BB+)\x02\xac4'\x1b--\x1b'4\xfe\xf6\x1f'\xa9\xfe\xe4\xa3\x17#!\x1a0>\x1bR\t\xfdH\xfe\xde\xc3\x01MŰ\x019\xd3\x02o\x89u7ǖD\xa9\xfe䣣\x01\x1c\xa9\xa1\x01\x1c\xab\x04\nR23('3\xfe_\x1c,-\x1b\x1c-,\x01\xefR23('3\xfe_\x1c,-\x1b\x1c-,\x01\xaa\x04\x9a\xfe\xf9\x9cNJ\x03\x03\n\x04\x11\x02\u007f\xda\xcb\x01\x1f\xa9\x01\x1c\xa3\x84\xe9\xfd?u\xd5W\xb5m%\x8d\xf2\x01\x1e\xf2\x8d\x8d\xf3\x00\x01\x00\x00\xff\x00\x06\xff\x06\x00\x00\x1e\x00\x00\x01\x16\a\x01\x06\a\x06#\"'%\x03\x06#\"'.\x015\x11\t\x01%&'&7\x01632\x06\xe4!\x06\xff\x00\x05\x1b\x0e\x11\v\r\xfe;\xf2\x12\x1f\r\t\x13\x17\x03`\xfb\xd3\xfeu%\x03\x02\"\x06\x80\x0f\x11\x14\x05\xf5\x18(\xfa\x00\x1d\x10\b\x05\xb9\xfe\xd9\x17\x04\a!\x14\x01]\x04#\xfcc\xa2\x0e)(\x13\x03\xc0\t\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06\xff\x05\xf7\x00\x1a\x00 \x00\x00\x01\x16\a\x01\x06\a\x06#\"'%\x01\x06#\"'.\x015\x11%&'&7\x016\x01\x13\x01\x05\t\x01\x06\xe4!\x06\xff\x00\x05\x1b\x0e\x11\v\r\xfd\xf1\xfe\xd6\x12\x1d\x0e\t\x13\x16\xfe(%\x03\x03#\x06\x80#\xfe\xcb\xdd\xfaf\x01P\x03_\xfe\"\x05\xf5\x18(\xfa\x00\x1d\x10\b\x05\xd7\xfe\xb9\x15\x04\a!\x14\x01\xc4\xc1\x0e)'\x14\x03\xc0\x15\xfa\x0e\x05+\xfcʼn\x02\u007f\xfc\xe3\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x004\x00I\x00\x00\x00\x10\x02\x06\x04#\"$'&6?\x0163\x16\x17\x1e\x0132>\x024.\x02#\"\x06\a\x17\x16\a\x06#!\"&5\x11476\x1f\x016$32\x04\x16\x05\x11\x14\x06#!\"&=\x0146;\x01\x1146;\x012\x16\x06\x00z\xce\xfe䜬\xfe\xcam\a\x01\b\x89\n\x0f\x10\aI\xd4wh\xbd\x8aQQ\x8a\xbdhb\xb4F\x89\x1f\x11\x11*\xfe@\x1a&('\x1e\x82k\x01\x13\x93\x9c\x01\x1c\xce\xfd\xfa\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x03\x1c\xfe\xc8\xfe\xe4\xcez\x91\x84\n\x19\b\x8a\t\x02\n_hQ\x8a\xbdн\x8aQGB\x8a\x1e'(&\x1a\x01\xc0*\x11\x11\x1f\x81eozΘ\xfe@\x0e\x12\x12\x0e@\x0e\x12\x01`\x0e\x12\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00\x00\x00 \x0e\x02\x10\x1e\x02 >\x02\x10.\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x82\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x01\x91\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x05\x00f\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xfe\xb7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x01\x00>\xff\x80\x06\xc2\x05\x80\x00\x85\x00\x00\x05\"&#\"\x06#\"&54>\x02765\x034'&#!\"\a\x06\x15\x03\x14\x17\x1e\x03\x15\x14\x06#\"&#\"\x06#\"&54>\x02765'\x1146.\x04'.\x01\"&54632\x1632632\x16\x15\x14\x0e\x02\a\x06\x15\x13\x14\x17\x163!2765\x134'.\x0254632\x1632632\x16\x15\x14\x0e\x02\a\x06\x15\x13\x14\x17\x1e\x03\x15\x14\x06\x06\x92,\xb1-,\xb0,\x18\x1a\",:\x10!\x01\x01\r%\xfd]&\r\x01\x01%\x10@2(\x19\x18/\xb9.+\xaa*\x17\x19\x1f)6\x0f!\x01\x01\x01\x02\x05\b\x0e\t\x0f<.$\x18\x18.\xb9.*\xa9*\x19\x19\"+8\x0f#\x01\x01\r\x1a\x02\xbb\x19\r\x01\x01#\x12Q3\x19\x19,\xb0,+\xac+\x19\x19#-:\x0f#\x01\"\x10\x19$$\x19\x01\xf0\f/:yu\x8e\xa6xv)%$\x00\t\x00\x00\xff\x80\x06\x00\x05\x00\x00\x03\x00\x13\x00\x17\x00\x1b\x00\x1f\x00/\x00?\x00C\x00G\x00\x00%\x15!5%2\x16\x15\x11\x14\x06#!\"&5\x11463\x01\x15!5\x13\x15#5\x01\x15!5\x032\x16\x15\x11\x14\x06#!\"&5\x11463\x012\x16\x15\x11\x14\x06#!\"&5\x11463\x05\x15#5\x13\x15!5\x01`\xfe\xa0\x02\xc0\x1a&&\x1a\xff\x00\x1a&&\x1a\x01\xa0\xfc\xa0\xe0\xe0\x06\x00\xfd \xe0\x1a&&\x1a\xff\x00\x1a&&\x1a\x03\x80\x1a&&\x1a\xff\x00\x1a&&\x1a\x02@\xe0\xe0\xfc\xa0\x80\x80\x80\x80&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\x01\x80\x80\x80\x02\x00\x80\x80\xfc\x00\x80\x80\x04\x80&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\xfe\x00&\x1a\xff\x00\x1a&&\x1a\x01\x00\x1a&\x80\x80\x80\x02\x00\x80\x80\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00%\x00\x00\x012\x16\x10\x06 &547%\x06#\"&\x10632\x17%&546 \x16\x10\x06#\"'\x05\x16\x14\a\x056\x04\xc0\x85\xbb\xbb\xfe\xf6\xbb\x02\xfe\x98\\~\x85\xbb\xbb\x85~\\\x01h\x02\xbb\x01\n\xbb\xbb\x85~\\\xfe\x98\x02\x02\x01h\\\x02\x00\xbb\xfe\xf6\xbb\xbb\x85\f\x16\xb4V\xbb\x01\n\xbbV\xb4\x16\f\x85\xbb\xbb\xfe\xf6\xbbV\xb4\x16\x18\x16\xb4V\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00%\x005\x00\x00$4&#\"\a'64'7\x163264&\"\x06\x15\x14\x17\a&#\"\x06\x14\x16327\x17\x06\x15\x14\x162\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00}XT=\xf1\x02\x02\xf1=TX}}\xb0~\x02\xf1>SX}}XS>\xf1\x02~\xb0\x01}\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\xfd\xb0~:x\x10\x0e\x10x:~\xb0}}X\a\x10x9}\xb0}9x\x10\aX}\x03\xe0\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\a\x00\x00\xff\x00\a\x00\x06\x00\x00\x11\x00/\x00>\x00L\x00X\x00d\x00s\x00\x00\x00.\x01\a\x0e\x01\a\x06\x16\x17\x16327>\x0176\x01\x17\a\x17\x16\x14\x0f\x01\x16\x15\x14\x02\x06\x04 $&\x02\x10\x126$32\x17762\x1f\x01\x13\x06#\"/\x01&4762\x1f\x01\x16\x14\x17\x06\"/\x01&4762\x1f\x01\x16\x146\x14\x06+\x01\"&46;\x012'\x15\x14\x06\"&=\x01462\x16\x17\a\x06#\"'&4?\x0162\x17\x16\x14\x02E\x140\x19l\xa6,\n\x14\x19\r\v*\x12\"\x81T\x19\x03\xb8.\xf4D\x13\x13@Yo\xbd\xfe\xfb\xfe\xe2\xfe\xfb\xbdoo\xbd\x01\x05\x8f\xb6\xa1@\x135\x13D\xfb\n\f\r\n[\t\t\n\x1a\nZ\n\xdc\v\x18\vZ\n\n\t\x1b\t[\t \x12\x0e`\x0e\x12\x12\x0e`\x0e\xae\x12\x1c\x12\x12\x1c\x12\x97[\n\f\r\n\n\nZ\n\x1a\n\t\x03\x9a2\x14\n,\xa6l\x190\n\x05(T\x81\"\v\x01\xad.\xf3D\x135\x13@\xa1\xb6\x8f\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdoY@\x13\x13D\x01,\n\nZ\n\x1a\n\t\t[\t\x1b\xef\t\t[\t\x1b\t\n\nZ\n\x1a\xbb\x1c\x12\x12\x1c\x12\xa0`\x0e\x12\x12\x0e`\x0e\x12\x12EZ\n\n\t\x1b\t[\t\t\n\x1a\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x04\x00\x14\x005\x00\x00\x01%\x05\x03!\x02 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126\x016=\x01\a'\x13\x17&'\x17\x05%7\x06\a7\x13\a'\x15\x14\x177\x05\x13\a\x1627'\x13%\x02a\x01\x1f\x01\x1fm\xfe\x9d\x05\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x04m\x95f\xf0?\x86\x96\xef5\xfe\xe1\xfe\xe15\uf587>\xf0f\x95\x1e\x01F\x8btu\xf6ut\x8b\x01F\x02\xd0\xd0\xd0\xfe\xb0\x04\x80\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xfbH\xcb\xfb\x03Y\xe0\x01C\f\xceL|\x9f\x9f|L\xce\f\xfe\xbd\xe0Y\x03\xfb˄(\xfe\xd6E''E\x01*(\x00\x00\x00\f\x00\x00\x00\x00\a\x00\x05\x80\x00\x0f\x00\x1f\x00/\x00?\x00I\x00Y\x00i\x00y\x00\x89\x00\xa2\x00\xb2\x00\xbc\x00\x00%\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16%\"&=\x01!\x15\x14\x06#\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x03\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x15!54\x05\x04\x1d\x01!54>\x04$ \x04\x1e\x04\x11\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x11\x15\x14\x06#!\"&=\x01\x01\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x02@\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xfd\xc2\x1c&\x02\x02&\x1b\x02\xff\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x02@\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\xc0\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x01\x80\xfd\xfe\xfe\x82\xfe\x82\xfd\xfe\x113P\x8d\xb3\x01\r\x01>\x01\f\xb4\x8dP3\x11\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12&\x1b\xfe\x80\x1b&\xe0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfer\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x92&\x1b\x81\x81\x1b&\xfd\xe0\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\xfer\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01r\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01\x8a\r\nh\x02\x01e\n\r\x114LKM:%%:MKL4\xfeW\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x01T\x81\x1b&&\x1b\x81\x00\x00\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\x10\x00\x14\x00%\x00/\x009\x00\x00\x01\x11\x14\x06#\x11\x14\x06#!\"&5\x11\x1363!\x11!\x11\x01\x11\x14\x06#!\"&5\x11\"&5\x11!2\x17\x01\x15!5463!2\x16\x05\x15!5463!2\x16\x02\xc0&\x1a&\x1a\xfe\x00\x1a&\xf9\a\x18\x02\xe8\xff\x00\x04\x00&\x1a\xfe\x00\x1a&\x1a&\x01\xa8\x18\a\xfc\xd9\xfe\xa0\x12\x0e\x01 \x0e\x12\x02\xa0\xfe\xa0\x12\x0e\x01 \x0e\x12\x04\xc0\xfd\x00\x1a&\xfd\xc0\x1a&&\x1a\x02\x00\x03i\x17\xfd@\x02\xc0\xfc\x80\xfe\x00\x1a&&\x1a\x02@&\x1a\x03\x00\x17\x017\xe0\xe0\x0e\x12\x12\x0e\xe0\xe0\x0e\x12\x12\x00\x01\x00\x00\xff\x00\a\x00\x06\x00\x00\x1d\x00\x00\x01\x16\x14\a\x01\x17\a\x06\x04'\x01#5\x01&\x12?\x01\x17\x0162\x16\x14\a\x01\x17\x0162\x06\xdb%%\xfeo\x96\xa0\xa3\xfe;\xb9\xfe\x96\xb5\x01j|/\xa3\xa0\x96\x01\x90&jJ%\xfep\xea\x01\x91&j\x04;&i&\xfep\x96\xa0\xa3/|\xfe\x96\xb5\x01j\xb9\x01ţ\xa0\x96\x01\x91%Jk%\xfeo\xea\x01\x90%\x00\x00\x00\x04\x00\x19\xff\f\x06\xe7\x06\x00\x00\t\x00\x15\x00:\x00g\x00\x00\x01\x14\x06\"&5462\x16\x05\x14\x06#\"&54632\x16\x13\x114&#!\"\x06\x15\x11\x1e\x052636\x17\x16\x17\x16\x176\x172\x1e\x02>\x057\x06\a\x12\a\x06\a\x06'&7\x035.\x01'\x03\x16\a\x06'&'&\x13&'&6\x17\x1e\x01\x17\x11463!2\x16\x15\x1176\x16\x03i\u007f\xb2\u007f\u007f\xb2\u007f\x01\xf6~ZY\u007f\u007fYZ~\xe1@O\xfb\xa8S;+[G[3Y\x1cU\x02D\x1b\x06\x04\x1a#\ao\x05?\x17D&G3I=J\xc6y\xfbTkBuhNV\x04\x01\b!\a\x01\x04WOhuAiS\xfby\x19*'\x04\x0f\x03^C\x04\xe9C^\x15'*\x03\x1cSwwSTvvTSwwSTvv\xfe\xf8\x02\x9bWID\\\xfd_\x17\"\x16\x0f\a\x01\x04\x01\x1c\x06\x03\x19\x1a[\x04\x03\x01\x01\x03\x06\v\x10\x17\x1f\x18\x95g\xfe\xe3\xb4q# /3q\x01F\x01\x02\b\x01\xfe\xaer2/ $r\xb4\x01\x1bg\x95%4\x1b\x02\n\x03\x02\xb6HffH\xfdJ\x0f\x1b4\x00\x00\x04\x00d\xff\x80\x06\x9c\x06\x00\x00\x03\x00\a\x00\x0f\x00\x19\x00\x00\x01\x11#\x11!\x11#\x11\x137\x11!\x11!\x157\x01\x11\x01!\a#5!\x11\x13\x03\x80\x91\x02\x1f\x91\x91\xfd\xfbV\x01F\xd9\x03\x1c\xfeN\xfe\xba\xd9\xd9\xferm\x04N\xfeN\x01\xb2\xfeN\x01\xb2\xfd\b\xfe\x03\x1b\xfb\xe7\xd9\xd9\x04\xaa\xfc\v\xfeN\xd9\xd9\x04\x86\x01!\x00\x00\x00\x00\x05\x00Y\xff\x01\x05\xaa\x05\xfd\x00\x16\x00+\x00?\x00N\x00e\x00\x00%\x15\x02\a\x06\a\x06&'&'&7>\x01727>\x01\x17\x1e\x01'\x06\x0f\x01\x04#&'&'&>\x01\x172\x17\x16\x1f\x01\x1e\x01\x01\x0e\x01\a\x06'&\x03'&676\x17\x16\x17\x1e\x01\x17\x16\x01\x16\a\x06'\x01&76$\x17\x16\x17\x16\x12\x05\x16\a\x06\x05\x06\a7\x06&'&767>\x0176\x17\x1e\x01\x17\x03\x05\x01\x05\f'6\xff#\r\x04\x01\x05\x04<\x97\x01;\x0f1\x19\x18\x1b\x96\x031x\xfe\xed\x11#\x13\f\x05\b\x12*#\r\xbdG,T\x17\x19\x039\a\xa93%\x1a\x0e\xaa/\x0e\x05\x11#0\x01v\xcbN\b\x1c\xfdZ\x05;:8\xfe\x86\b\x1b)\x01M:(\t\x03&\x02\x9b\x03\x1d\x0f\xfe\xc6C\x18\x01\x17.\x0e\x1e\x1e\x01J}2\t\x1c%0\x96\x06\xd9\u007f\xfe\xdc\r \b\t^*\x0f\x15\f\x0e\nJ\xb3F\x13\v\t\n&\xe47\x0f'X\x02\"\x192L\xb5D\x02M\x1d\x12\"\t+\xfe\xbc6\xd6\x14\x0e\x15\n\x01\x15M\x152\x15+\x11\x01'B\x1b\a\x16\x02Qf\x14\x11X\x02V#\x1b+]\x0f\n#\x12\xfd\xc1\xc8'\x14\nL\x0f\b\x02\x06\x14\x16/(\x01e\xabB\x06\x13\x11\x17\xdd9\x00\x00\x00\n\x00\x00\x00\x00\b\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00\x17\x00\x1b\x00#\x00,\x008\x00\x00\x01!\x11!\x13\x15!5\x01\x11!\x11\x01\x15!5\x01\x15!5\x01\x15!5\x01\x15!5\x01\x11#\x11\x14\x1626%\x11!\x11\x14\a!26\x13\x11\x14\x06#!\"&5\x11!5\x04\x00\xfe\x80\x01\x80\x80\xfd\x80\x02\x80\xfd\x80\x05\x00\xfe\x00\x02\x00\xfe\x00\x02\x00\xfe\x00\x02\x00\xfe\x00\xfc\x00\x80&4&\x06\x80\xfa\x00\v\x05\xcb\x1a&\x80pP\xf9\x80Pp\x01\x00\x04\x00\xfe\x80\xff\x00\x80\x80\x03\x00\xfd\x80\x02\x80\xfd\x00\x80\x80\x01\x00\x80\x80\x01\x00\x80\x80\x01\x00\x80\x80\xfc@\x03\xc0\xfc@\x1a&&\x1a\x04@\xfb\xc0!\x1f&\x04\xda\xfb@PppP\x04@\x80\x00\x04\x00*\x00\r\a\xd6\x05\x80\x00\t\x00\x1f\x009\x00Q\x00\x00$\"&5462\x16\x15\x147\".\x01\"\x0e\x01#\"&547>\x012\x16\x17\x16\x15\x14\x06\x01\"'.\x01#\"\x0e\x03#\"&5476$ \x04\x17\x16\x15\x14\x06\x13\"'&$ \x04\a\x06#\"&5476$ \x04\x17\x16\x15\x14\x06\x04\x14(\x92}R}h\x02L\u007f\x82\u007fK\x03\x12\x97\nN\xec\xe6\xecN\n\x97\x00\xff\v\f\x88\xe8\x98U\xab\u007fd:\x02\x11\x96\n\x84\x01x\x01\x80\x01x\x84\n\x96\xfe\v\v\xb3\xfe\u007f\xfe8\xfe\u007f\xb3\v\v\x11\x97\n\xbb\x02\x04\x02\x1a\x02\x04\xbb\n\x97\r\x93\x14 ,, \x14|2222\x96\x12\r\nMXXM\n\r\x12\x96\x01\x10\bic,>>,\x96\x12\f\n\x84\x92\x92\x84\n\f\x12\x96\x01\x0f\t\x9d\x9f\x9f\x9d\t\x96\x12\r\n\xba\xcc̺\n\r\x12\x96\x00\x00\r\x00\x00\xff\x00\x06\x80\x06\x00\x00\a\x00\x0f\x00\x17\x00\x1f\x00'\x00/\x007\x00?\x00K\x00S\x00c\x00k\x00{\x00\x00\x044&\"\x06\x14\x162$4&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x004&\"\x06\x14\x162\x01\x114&\"\x06\x15\x11\x14\x1626\x004&\"\x06\x14\x162\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x104&\"\x06\x14\x162\x13\x11\x14\x06#!\"&5\x11463!2\x16\x01\x80KjKKj\x01\xcbKjKKj\xfe\xcbKjKKj\x03KKjKKj\xfe\xcbKjKKj\xfe\xcbKjKKj\x03KKjKKj\xfe\xcbKjKKj\x03KLhLLhL\xfe\x80KjKKj\x01\xcb&\x1a\xfb\x00\x1a&&\x1a\x05\x00\x1a&KjKKj\xcbL4\xfa\x804LL4\x05\x804L5jKKjKKjKKjK\x01\xcbjKKjK\xfe\xcbjKKjK\x01\xcbjKKjK\x01\xcbjKKjK\xfe\xcbjKKjK\x01\xcbjKKjK\xfd\x80\x01\x804LL4\xfe\x804LL\x02\xffjKKjK\x01\xc0\x01\x00\x1a&&\x1a\xff\x00\x1a&&\xfe\xa5jKKjK\x03\x00\xfa\x004LL4\x06\x004LL\x00\x02\x00\t\xff\x00\x05\xef\x06\x00\x00'\x00E\x00\x00\x01\x16\a\x02!#\"\x06\x0f\x01\x03\a\x0e\x01+\x01\"&7>\x0376;\x01\x167676767>\x01\x16\x17\x16'\x14\a\x06\a\x06\a\x14#'\"\a\x06\x03\x06#!\"&7\x13>\x013!2\x16\x17\x1e\x01\x05\xef\x12\x16W\xfe\",\x19&\x05\x047\x02\x05'\x19\xfb\x15\x18\x03\t#\x12$\t\x05&\x83\x85g\xafpf5\x18\v\x01\x03\x04\x04O\x99.P\xdeq\x8bZZd\x12\x02S\x01\v\xfe\xd9\x16\x1d\x03\xe8\x05-\x1d\x02V\"\u007f0kq\x03zTx\xfeD!\x1a\x13\xfe\xa6\x0f\x1a!\x1e\x158\xe0p\xdf8%\x02\x17'i_\x97F?\x06\x03\x01\x03;\xb3k\x81\xe9R(\x02\x01\x01`\b\xfd\xf6\n!\x16\x05\xbf\x1d&\x1a\x13)\xa4\x00\x00\x04\x00'\xff\x00\a\x00\x06\x00\x00\n\x00\x12\x00\x19\x00(\x00\x00\x012\x17\x00\x13!\x02\x03&63\x01\x06\a\x02\x0367\x12\x13\x12\x00\x13!\x02\t\x01\x10\x03\x02\x01\x02\x03&63!2\x16\x17\x12\x01\xb9!\x13\x01\n`\xfeB\u007f\xf0\f\x12\x14\x03\xa41LO\xb1(\x04\xd3\xe1\xeb\x01+#\xfe=)\xfe\x00\x04heC\xfe\xdc\x19Q\x04\x13\x10\x01g\x15#\x05s\x03`\x1a\xfe\x94\xfef\x01\xb9\x014\x10#\xfe\x9b\xc7\xc2\x016\x01\x1c\xdd\xe4\xfe\xac\x01\x8f\xfe\xbc\xfd\x13\xfeq\x02\x99\x03'\xfd\xc0\xfeX\xfe|\x020\x02\v\x01-\x01\x1b\x10\x19\x1a\x14\xfeg\x00\a\x00\x00\xff\x80\t\x00\x05\x80\x00\b\x00\x0f\x00\x18\x00\x1c\x00>\x00I\x00Y\x00\x00\x01#6?\x01>\x017\x17\x05\x03&#!\a\x04%\x03'.\x01'\x133\x01\x033\x13#\x05&#\"\x06\a\x06\x17\x1e\x01\x15\x14\x06#\"/\x01\a\x163\x16674'.\x0154636\x1f\x01%#\"\a\x03373\x16\x173\x13\x11\x14\x06#!\"&5\x11463!2\x16\a\xb7\x8a\x0e4\x03\x04\f\x03\f\xfa\x82:\v@\xfe\xf4\x02\x017\x01\x0f\xa2\x11\x1avH\x87\xaf\x01\x05%\xa6h\xa6\x02\x98EP{\x9c\x01\x01\x920&<'VF\x16\x17Jo\x82\x9d\x02\x8c1,1.F6\x0f\x01\xc0\x80A\x16\xf6\xae#\xd4\x05\x0f\x9a\x80L4\xf8\x004LL4\b\x004L\x02\"%\x8e\t\n \n7x\x01'6\rO\\\xfeJYFw\x1d\xfe\x02\x02\x81\xfd~\x02\x82\x10\x1bv^fH\x17$\x15\x1e !\v\x90\"\x01xdjD\x19\"\x15\x16!\x01\x19\b\x9b6\xfd\xb4`\x16J\x03\xc2\xfb\x004LL4\x05\x004LL\x00\x18\x00\x00\xff\x80\t\x00\x05\x80\x00\x11\x00\x19\x00+\x003\x00@\x00G\x00X\x00c\x00g\x00q\x00z\x00\x9c\x00\xb8\x00\xc7\x00\xe5\x00\xf9\x01\v\x01\x19\x01-\x01<\x01J\x01X\x01{\x01\x8b\x00\x00\x01&#\"\x0e\x02\x15\x14\x1e\x02327&\x02\x127\x06\x02\x12\x176\x12\x02'\x16\x12\x02\a\x1632>\x0254.\x02#\"\x0135#\x153\x15;\x025#\a'#\x1535\x1737\x03\x15+\x015;\x01\x153'23764/\x01\"+\x01\x15353$4632\x16\x15\x14\x06#\"$2\x17#\x04462\x16\x15\x14\x06#\"6462\x16\x15\x14\x06\"\x17\"'\"&5&547476125632\x17\x161\x17\x15\x16\x15\a\x1c\x01#\a\x06#\x06%354&'\"\a&#\"\a5#\x1535432\x1d\x0135432\x15\x173=\x01#\x15&#\"\x06\x14\x1632?\x014/\x01&5432\x177&#\"\x06\x15\x14\x1f\x01\x16\x15\x14#\"'\a\x16326\x17'\x06#\"=\x0135#5#\x15#\x153\x15\x14327\"\x06\x15\x14\x16327'\x06#\"'354&3\"\a5#\x1535432\x177&\x16\x14\x16327'\x06'\"&4632\x177&#\"\x173=\x01#\x15&#\"\x06\x14\x1632?\x01\"\a5#\x1535432\x177&\x173=\x01#\x15&\"\x06\x14\x1632?\x01\a\"#\x06\a\x06\x15\x06\x15\x14\x17\x14\x17\x1e\x013274?\x0167654'&'4/\x01\"&\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04_\x80\x99g\xbd\x88QQ\x88\xbch\x99\x80\x83^_\xa3~\\[\u007f\u007f[\\]\x82_^\x83\x80\x99h\xbc\x88QQ\x88\xbdg\x99\x02e\a\x11\a\x03\x1d\x04\x05\x06\x06\x05\x03\x06\x04\x05\b\x02\x03\x03\x02\x03\x04\x01\x01\x01\x01\x01\x01\x02\x01\x06\x03\x01\xfb\x16\x16\x13\x12\x16\x16\x12\x13\x01\xa5<\x05F\x01\x87\x16$\x17\x16\x13\x12\xfa\x17$\x17\x17$\x87\x02\x02\x01\x04\x01\x01\x02\x01\x02\x02\x02\x03\x01\x04\x02\x01\x01\x01\x01\x02\x02\x01\xfa\xbc\x1e\x1d\x19 \x0f\x0e\x1f\x18\x0f\x1e\x1e!\x1e\x1d!\x1e\xa6\x1d\x1d\x11\x1a\x1d&&\x1d\x1c\x0f\xb2/\x0e\x17\x19\x17\x14\f\x16!\x1a\x1e/\r\x18\x1f\x19\x14\r\x19!\x1d!\x82\b\r\r\x1300\x1e\x1c\x1c/\x15e\x1d&'\x1e!\x16\x0e\x12\x15\"\ae$\x83\x17\f\x1e\x1e\x1d\n\b\t\t\x12'!\x1d\x13\x0e\x12\x11\x12\x17\x17\x12\x13\x10\x0e\x14\x1c!\xce\x1e\x1e\x0f\x1b\x1d''\x1d\x1c\x0e\x85\x17\f\x1d\x1d\x1d\n\b\t\b\u007f\x1d\x1d\x0f8''\x1c\x1d\x0eN\x02\x02\x01\x02\x02\x03\x01\x01\x03\x02\x04\x03\x04\x02\x02\x02\x01\x02\x01\x01\x01\x02\x02\x02\x01\x04\x01gL4\xf8\x004LL4\b\x004L\x04\xabUQ\x88\xbcgh\xbc\x88QUk\x01=\x01(\x14\x18\"\x06\x02\x04\n\x0f\v\x18\x0e\x18\x14!\x06\x02\x04\n\x11\x0e\x17\x11\x18\x0e\x19\a\x16=\x1b))\x1b=2\x8e(\x1f '\x13\x16\x0f!\f '\x14\x10\x87L#\x04\x1c\x04(>(\x10\x18\r\x01\x18&\x18\f\x18\x10\x8bDC\x10\x14(>(\x14z\x14\x10\x87L#\x04\x1c\x04\x8bDzG\x14)<)\x14\x03\x01\x01\x02\x01\x03\x02\x04\x03\x02\x02\x02\x02\x02\x01\x01\x01\x01\x01\x03\x02\x03\x04\x02\x01\x03\x01\x01\x01\x01\x04\xe5\xfb\x004LL4\x05\x004LL\x00\x00\f\x00\x00\xff\x80\t\x00\x05\x80\x00\n\x00\x11\x00\x1b\x00\x1f\x00B\x00W\x00b\x00j\x00q\x00}\x00\x8a\x00\x9a\x00\x00\x01\x14\a\x06+\x01532\x17\x16%\x14+\x01532\x054&+\x01\x113276\x173\x11#\x054&'.\x0154632\x177&#\"\x06\x15\x14\x16\x17\x16\x17\x16\x15\x14\x06#\"'\a\x16326\x055\x06#\"&54632\x175&#\"\x06\x14\x1632\x01\x11\x0e\x01\f\x02\x05!26\x004&\"\x06\x14\x162%\x13#\a'#\x13735#535#535#\x013'654&+\x01\x11353\x01\x11\x14\x06#!\"&5\x11463!2\x16\x019$\x1d<\x11\x11=\x1c$\x06\xf0@\x13\x14?\xf9SdO__J-<\x1eAA\x01@)7\x1d\x15\x1b\x15\x1d\x18\")9,<$.%\b\x13\x1c\x160\x17*,G3@\x01\x16%)1??.+&((JgfJ*\x04\xf7A\x9f\xfe\xc4\xfe\xa9\xfe\x14\xfe\xfe\x06!\x1a&\xfc\xadj\x96jj\x96\x01\x02\x90GZYG\x8eиwssw\xb8\x01\x87PiL>8aA\t\x01!M7\xf8\b7MM7\a\xf87M\x02\xf73!\x1a\xdc\x1b\x1f\r4erJ]\xfe\xb3&3Y\x01M\xe8(,\x14\n\x12\x0e\x10\x15\x1b,%7(#)\x10\r\x06\f\x16\x14\x1b,(@=)M%A20C&M\x14e\x92e\xfd\xb7\x02\x0f(X\x92\x81\x8c0&\x02Ėjj\x96j\b\x01V\xe0\xe0\xfe\xaa\t8Z8J9\xfe\xb3\x8c\x10N/4\xfe\xb3\x85\x02$\xfb\f8NN8\x04\xf48NN\x00\x00\x00\x00\x12\x00\x00\xff\x80\t\x00\x05\x80\x00\x02\x00\v\x00\x0e\x00\x15\x00\x1c\x00#\x00&\x00:\x00O\x00[\x00\xce\x00\xe2\x00\xf9\x01\x05\x01\t\x01$\x01?\x01b\x00\x00\x133'\x017'#\x153\x15#\x15%\x175\x174+\x01\x1532%4+\x01\x1532\x014+\x01\x1532\x053'%\x11#5\a#'\x15#'#\a#\x133\x13\x113\x177\x01\x14\x0e\x04\"&#\x15#'\a!\x11!\x17732%\x15#\x113\x15#\x153\x15#\x15\x01\x15\x14\x06#!\"&5\x11373\x1735\x1737\x15!572\x1d\x01!5\x1e\x026373\x1735\x173\x11#\x15'#\x15'#\"\a5#\x15&#!\a'#\x15'#\a\x11463!2\x16\x15\x11#\"\a5#\"\a5!\x15&+\x01\x15&+\x01\a'!\x11!7\x1735327\x153532\x16\x1d\x01!27\x1532%\x14\x06\a\x1e\x01\x1d\x01#54&+\x01\x15#\x1132\x16\x01\x14\x06\a\x1e\x01\x1d\x01#46.\x03+\x01\x15#\x11\x172\x16\x01\x15#\x113\x15#\x153\x15#\x15\x01\x11#\x11\x01\x14+\x0153254&\".\x01546;\x01\x15#\"\x15\x14\x166\x1e\x017\x15\x06+\x0153254&\x06.\x02546;\x01\x15#\"\x15\x14\x1e\x01\x03\x11#'\x15#'#\a#\"54;\x01\x15\"&\x0e\x04\x15\x14\x16;\x0173\x13\x113\x175wY-\x02AJF\xa3\x8e\x8e\x01=c\xbd(TS)\x01!*RQ+\xfe\xea*RQ+\x01\xcbY,\xfc\x16B^9^\x84\x19\x87\x19Ft`njUM\x02\x98\v\x11\x1c\x18'\x18)\t~PS\xff\x00\x01\x04PR\xcfm\xfe\xdd\xd9٘\x94\x94\x05\xd4M7\xf8\b7Mo\x197\x19\xda\x13q\x14\x02\x1d\n\n\x01\x17\x17@)U\t\x198\x19\xe3\"\xb6\xb4\x19\xb9\x17\xf9E(\xac\x181\xfd\x8c++\xc6\x16\xa9NM7\a\xf87Mx3\x1e\xb17\x17\xfe\xc4\x1f8\xd1\x17D\xea62\xfe\xa3\x01W74\xd3\x15;\x1f\xae\b\b\x04\x02\x119\x1f\xa8<\xfd-\x18\x16\x19\x12A\x18\"EA\x9a0:\xfe\xeb\x19\x15\x1a\x11A\x01\x01\x05\f\x17\x12F@\x991:\x02\x11\xd8ؗ\x94\x94\xfe\xedB\x02\xf7f~~\"\"12\"4(\x82w$#11#\xef\x18@}}!\x19%+%\x195(\x81v$:O\x94\\z\x84\x1a\x86\x19K\x81\x85?\a*\x0f\x1f\f\x11\x06\x1b$\x1d\\amcr\x03Vl\xfd\x86OO176Nn\xd9\x022\x16326\x05\x136&+\x01\"\a&#\"\x06\x15\x14\x163267\x06\x15\x14;\x012\x004&+\x01\"\x0f\x01'&+\x01\"\x06\x15\x14\x1e\x01\x17\x06\x15\x14;\x0127\x01%4&+\x01\"\a\x03\x06\x16;\x012?\x01>\x022\x16326\x05\x136&+\x01\"\a&#\"\x06\x15\x14\x163267\x14\x06\x15\x14;\x012\x1354+\x01\"\a\x03\a\x14\x16;\x0127\x01\x0e\x01#\a76;\x012\x16\x01\x11\x14\x06#!\"&5\x11463!2\x16\x02\xe93%\x1d#2%\x1c%\x03\x11,, \x11\x02\v\x12\x16\x1a\x18\x01_3$\x1d$2%\x1c%\xfa\xa8M>\xa0\x13\x02A\x01\b\x06L\x14\x02\x12\x01\f\x12\x10\x16\x03Vb\x015)\x01\b\x06L\x0e\x03\x1bDHeE:\x1c<\x12\x04\rE\x13\x01\xc2\b\x05M\v\aj,\x05\x11K\x05\b'-\x01R\rM\v\a\x00\xff\x01~M>\x9f\x14\x02A\x01\b\x06R\f\x04\x12\x01\f\x12\x10\x16\x03Vb\x015)\x01\b\x06L\x0e\x03\x1aEHeE:\x1d<\x11\x04\rE\x13\xdd\rJ\v\x02A\x01\b\x06B\x13\x02\xf9I\x05*'!\x11\x02\v\x13($\arL4\xf8\x004LL4\b\x004L\x02v%1 \x1c%3!x*\x1e\x01k\v\x04\x15\xa9$2 \x1c%3!\x8e;5\x13\xfeh\x06\n\x13n\b\n\x03\x02a\xe2\x01\x05\x06\n!(lI;F\x18\x14\f\t\x10\x01\x15\n\t\n\x9c\x96\x10\t\x05\x02r\x84\x04p\b\r\n\x01p8;5\x13\xfeh\x06\n\rt\b\n\x03\x02a\xe2\x01\x05\x06\n!(lI;F\x18\x14\x01\x10\x04\x10\x01\xac\x01\x0e\v\xfe`\x02\x05\t\x13\x01\x13#\x16\x01k\v\x17\x01\xdf\xfb\x004LL4\x05\x004LL\x00\x00\x00\n\x00\x00\xff\x80\t\x00\x05\x80\x00\n\x00\x0f\x002\x00H\x00W\x00[\x00l\x00t\x00\x8b\x00\x9b\x00\x00\x01\x14\a\x06#\"'5632\x05#632\x054&'.\x015432\x177&#\"\a\x06\x15\x14\x16\x17\x1e\x01\x15\x14#\"&'\a\x163276\x017#5\x0f\x033\x15\x14\x17\x163275\x06#\"=\x01\x055&#\"\x06\a'#\x113\x11632\x133\x11#\x054'&#\"\a'#\x1175\x163276\x004&\"\x06\x14\x162\x014'&#\"\x06\x15\x14\x17\x16327'\x06#\"'&'36\x13\x11\x14\x06#!\"&5\x11463!2\x16\x06=\x15\x13!\x17\x12\x1d\x1c9\x01\xb6n\x0623\xf9\xecBD$ &:B\x12CRM.0AC'\x1f0\x1dR\x1f\x12H`Q03\x01'\x13`\x81\x12.\x11>,&I / \f*\x01\x89\x0f\r /\n\n\x83\x96\x1a8\x10/\x96\x96\x02n-(G@5\b\x84\x96$ S3=\xfe,.B..B\x03\xb002^`o?7je;\x109G+\x14\x17\x05\xf8\x02\x80L4\xf8\x004LL4\b\x004L\x02yE%#\t\xe0\x1eVb\xe9;A\x19\r\x16\x0e\x1a!p &'F:A\x18\x0e\x17\x10\x1f\x19\x12q)%)\x01#o\x87\x15r\bg\xdbT$\x1e\vv\a2\xc5\x19\x8b\x03 \x1e8\xfe)\x012\x1f\xfe\xaf\x01\xd7\xdez948/\xfd{\x19\x97\v8A\x01\xc4B..B/\xfe\xebq?@\x84r\x80<7(g\x1f\x13\x13/\x0e\x02\xb1\xfb\x004LL4\x05\x004LL\x00\x00\x03\x00\x0e\xff\x00\a\xf2\x06\x00\x00\v\x00\x17\x00?\x00\x00\x01\x12\x17\x14\x06#!\x14\x06\"&'\x0524#\"&54\"\x15\x14\x16\x01\x16\x06\a\x01\x06&/\x01&6?\x01&5>\x0454\x127&5462\x16\x15\x14\a\x1e\x01\x17\x016\x16\x17\x06\x16=\xedL4\xfe@\x96ԕ\x01\x01\x00\x10\x10;U g\x043\b\x01\n\xf8\xb0\n\x1b\bT\b\x01\n\xba\x132RX='\xea\xbe\b8P8\b|\xbe5\x01\xa2\n\x1b\b\x02\xac\xfe\x9c\xc84Lj\x96\x95j\xaf U;\x10\x10Ig\x06@\n\x1b\t\xf9\xaa\b\x02\n`\n\x1b\b\xa1 \"*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x12\x81]\x01k\b\x02\n\x00\x00\x00\x00\x04\x00\x0e\xff\x00\a\xf2\x06\x00\x00\v\x00\x16\x00&\x00N\x00\x00\x044#\"&54\"\x15\x14\x163\t\x01.\x01#\"\x0e\x02\x15\x10\x01\x14\x06#!\x14\x06\"&'7!&\x037\x12\x01\x17\x16\x06\a\x01\x06&/\x01&6?\x01&5>\x0454\x127&5462\x16\x15\x14\a\x1e\x01\x17\x016\x16\x04\x10\x10;U gI\xfd\xf7\x03m*\xb5\x85]\x99Z0\x04\xc0L4\xfe@\x96ԕ\x01\x95\x02\xf5\xa6=o=\x01CT\b\x01\n\xf8\xb0\n\x1b\bT\b\x01\n\xba\x132RX='\xea\xbe\b8P8\b|\xbe5\x01\xa2\n\x1b\xb0 U;\x10\x10Ig\x01\xeb\x02\xf8Xu?bl3\xfe\x80\xfe@4Lj\x96\x95j\x81\xbb\x01\x10a\xfe\x9c\x04\xa8`\n\x1b\t\xf9\xaa\b\x02\n`\n\x1b\b\xa1 \"*\\\x93\xaa\xf2\x8b\x98\x01\x05\x1c\x13\x14(88(\x14\x13\x12\x81]\x01k\b\x02\x00\x00\x00\x00\x05\x00\x00\xff\x80\x05\x80\x05\x80\x00\x0f\x00\x1f\x00/\x007\x00[\x00\x00%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126\x01!'&'!\x06\a\x05\x15\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&=\x01463!7>\x013!2\x16\x1f\x01!2\x16\x02\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x00\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xfd\xe0\x01\xc00\a\n\xfe\xc3\n\a\x03o\x12\x0e`^B\xfc\xc0B^`\x0e\x12\x12\x0e\x015F\x0fN(\x01@(N\x0fF\x015\x0e\x12\xa0\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x03\xeeu\t\x02\x02\t\x95@\x0e\x12\xfcLSyuS\x03\xb8\x12\x0e@\x0e\x12\xa7%44%\xa7\x12\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00,\x00<\x00H\x00\x00\x01\x15\x14\x0e\x02#\"\x0054\x0032\x1e\x03\x1d\x01\x14+\x01\"=\x014&#\"\x06\x15\x14\x16326=\x0146;\x012\x16\x02 \x0e\x02\x10\x1e\x02 >\x02\x10.\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04~Isy9\xcd\xfe\xed\x01\x10\xcb\"SgR8\x10v\x10\x83H\x8c\xb1\xb7\x8eD\x8c\t\x06w\x06\n\xfc\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x01\x91\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\xcem2N+\x16\x01\x16\xcf\xcb\x01\x10\t\x1b)H-m\x10\x10F+1\xb7\x92\x97\xc50*F\a\t\t\x03+f\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xfe\xb7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0e\x00b\x00\x00\x014&#\"\x0e\x02\x15\x14\x1632>\x01\x05\x14\x0e\x02\a\"\x06#\"'&'\x0e\x01#\"&54\x12632\x16\x17?\x01>\x01;\x012\x17\x16\a\x03\x06\x15\x14\x163>\x045\x10\x00!\"\x0e\x02\x10\x1e\x023276\x16\x1f\x01\x16\a\x06\a\x0e\x01#\"$&\x02\x10\x126$3 \x00\x03\xcck^?zb=ka`\xa0U\x024J{\x8cK\x06\x13\a_/\x1c\x054\x9f^\xa1\xb1\x84\xe2\x85W\x88&\x02\v\x01\t\x05v\x05\b\x05\x02x\x05\x19 \x1c:XB0\xfe\xa4\xfe܂\xed\xabff\xab\xed\x82\xe4\xb1\v\x1a\b)\b\x01\x02\nf\xfb\x85\x9c\xfe\xe4\xcezz\xce\x01\x1c\x9c\x01X\x01\xa8\x02\xf9lz=l\xa6apz\x85\xc7\x11o\xacb3\x02\x015!2BX\xbf\xae\x9d\x01\n\x9bG@\x138\x06\f\v\x05\v\xfd\x9a\x18\x18'\x1a\x01\t'=vN\x01$\x01\\f\xab\xed\xfe\xfc\xed\xabf\x90\t\x02\v1\f\f\r\tSZz\xce\x01\x1c\x018\x01\x1c\xcez\xfeX\x00\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00#\x00(\x00\x00\x00\x16\x10\x0f\x01\x17\x16\x14\x0f\x01\x06\"/\x01\x01\x06+\x01\x05'\x13547\x01'&4?\x0162\x1f\x0176\t\x01'\x01\x15\x06D\xbc^\xe1h\n\n\xd2\n\x1a\ni\xfd\xa5%5\xcb\xff\x00@\x80%\x02[i\n\n\xd2\n\x1a\nh\xdf]\xfc\xc5\x02@\xc0\xfd\xc0\x06\x00\xbc\xfe\xf7]\xdfh\n\x1a\n\xd2\n\ni\xfd\xa5%\x80@\x01\x00\xcb5%\x02[i\n\x1a\n\xd2\n\nh\xe1^\xfa@\x02@\xc0\xfd\xc0\xc0\x00\x02\x00\x00\xff\x00\x06\xfe\x06\x00\x00\x10\x00)\x00\x00\x012\x16\x15\x14\a\x00\a\x06#\"&547\x016\x01\x1e\x01\x1f\x01\x16\x00#\".\x025\x1e\x03327>\x04\x06OFi-\xfe\xb4\x85ay~\xb5\\\x02~;\xfc\xba'\x87S\x01\x04\xfe\xf5\xd7{\xbes:\aD8>\x0f)\x0e\x19AJfh\x06\x00]F?X\xfd\x8b{[\xb9\u007f\x80T\x02C6\xfb\xf6Ll\x16G\xd5\xfe\xf4]\xa2\xccv\x052'\"%B];$\x0f\x00\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00-\x00o\x00\u007f\x00\x8f\x00\x9f\x00\x00%\x11!\x112>\x017>\x0132\x1e\x01\x17\x1e\x0232>\x017>\x0232\x16\x17\x1e\x022>\x017>\x0132\x16\x17\x1e\x02\x13\x15\".\x01'.\x02#\"\x0e\x01\a\x0e\x02#\"&'.\x02#\"\x0e\x01\a\x0e\x02#\"&'.\x02#\"\x0e\x01\a\x0e\x01#546;\x01\x11!\x11!\x11!\x11!\x11!\x1132\x16\x01\x14\x06#\"&54>\x0452\x16\x05\x14\x06#\"&54>\x0452\x16\x05\x14\x06#\"&54>\x0452\x16\a\x00\xf9\x00-P&\x1c\x1e+#\x18(\x16\x16\x1d$P.-P$\x1e\x15\x17'\x18#+\x1e\x1c&PZP&\x1c\x1e+#\"+\x1e\x1c&P-\x18(\x16\x16\x1d$P-.P$\x1d\x16\x16(\x18#+\x1e\x1d$P.-P$\x1e\x15\x17'\x18#+\x1e\x1c&P-.P$\x1d\x1e+#pP@\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00@Pp\xfb\x00H85K\x13\x1c\"\x1c\x13&Z\x02\x00H85K\x13\x1c\"\x1c\x13&Z\x02\x00H85K\x13\x1c\"\x1c\x13&Z\x80\xfe\x80\x01\x80\x1c\x1b\x18\x1b\x16\x0e\x10\x13\x19\x1a\x1c\x1d\x19\x19\x13\x10\x0e\x16\x1b\x18\x1b\x1c\x1c\x1b\x18\x1b\x16\x16\x1b\x18\x1b\x1c\x01@\xc0\x0e\x10\x13\x19\x1a\x1c\x1c\x1a\x19\x13\x10\x0e\x16\x1b\x19\x1a\x1c\x1d\x19\x19\x13\x10\x0e\x16\x1b\x18\x1b\x1c\x1c\x1a\x19\x1b\x16\xc0Pp\x01\xc0\xfe@\x01\xc0\xfe@\x01\xc0\xfe@p\x03\x10MSK5\x1d,\x18 \x1f:&\x94LMSK5\x1d,\x18 \x1f:&\x94LMSK5\x1d,\x18 \x1f:&\x94\x00\x02\x00\x00\xff\x80\b\x00\x05\x80\x00\x05\x00\v\x00\x00!\x15!\x113\x11\t\x01!\x11\t\x01\b\x00\xf8\x00\x80\x06\x00\x01\x00\xf9\x80\x01\xc0\x02@\x80\x06\x00\xfa\x80\x04\x00\xfc\x80\x02@\x02@\xfd\xc0\x00\x00\x00\x03\x00\x00\xff\x80\x06\xc0\x06\x00\x00\v\x00\x10\x00\x16\x00\x00\t\x01\x06\x04#\"$\x02\x10\x12$3\x13!\x14\x02\a\x13!\x112\x04\x12\x03\x00\x02\"j\xfe\xe5\x9d\xd1\xfe\x9f\xce\xce\x01aѻ\x03\x05xl\xa4\xfd\x00\xd1\x01a\xce\x02\x86\xfd\xdelx\xce\x01a\x01\xa2\x01a\xce\xfd\x00\x9d\xfe\xe5j\x02\xa2\x03\x00\xce\xfe\x9f\x00\x02\x00\x00\xff\x80\b\x00\x05\x80\x00\x05\x00\x1f\x00\x00!\x15!\x113\x11\x01\x11\x14\x06/\x01\x01\x06\"/\x01\x01'\x0162\x1f\x01\x01'&63!2\x16\b\x00\xf8\x00\x80\a\x00'\x10y\xfd\x87\n\x1a\n\xe9\xfe`\xc0\x02I\n\x1a\n\xe9\x01\xd0y\x10\x11\x15\x01\xb3\x0e\x12\x80\x06\x00\xfa\x80\x04\xe0\xfeM\x15\x11\x10y\xfd\x87\n\n\xe9\xfe`\xc0\x02I\n\n\xe9\x01\xd0y\x10'\x12\x00\x00\x01\x00\x00\x00\x00\a\x00\x04W\x00`\x00\x00\x01\x14\x17\x1e\x03\x17\x04\x15\x14\x06#\".\x06'.\x03#\"\x0e\x01\x15\x14\x1632767\x17\x06\a\x17\x06!\"&\x0254>\x0232\x1e\x06\x17\x1632654.\x06'&546\x17\x1e\x01\x17#\x1e\x02\x17\a&'5&#\"\x06\x05\f\n\n\x1e4$%\x01Eӕ;iNL29\x1e1\v ;XxR`\xaef՝\xb1Q8\x1bT\x0f\x1d\x01\x83\xfe\xff\x93\xf5\x88W\x91\xc7iW\x90gW:;*:\x1a`\x89Qs&?RWXJ8\v\x03\xafoNU0\x01\f\x16\x1e\x04\x81\x1a\x1c\x17J1F\x03@\x06#\x1d)\x1b\r\n[\xf1\x92\xc1%6_P\u007fO\x86\x1cQiX(o\xb2`\xa0\xef_?5\x98\"$\x01\x98\x9e\x01\x01\x92iʗ\\&>bd\x86s\x926\xc8aP*< \x1f\x17-;iF\x10\x11n\xa4\x04\x03\x17*\v\x1b-\x05c1\x15\x01\x15B\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00W\x00g\x00\x00\x014'.\x02'4.\x0154632\x17#\x16\x177&'.\x01#\"\x06\x15\x14\x17\x1e\x01\x17\x1e\x03\x1d\x01\x16\x06#\"'.\x05#\"\x0e\x01\x17\x15\x1e\x0232767'\x0e\x01#\"&54632\x16\x17\x1e\a326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\x98\xea#$(\t\x04\x021$6\x11\x01\x14\x13]'\n!E3P|\x02\x10ad\x1d(2\x1b\x01S;aF\x179'EO\x80Se\xb6j\x03\x04]\xaem\xba]\x14\v<*rYs\x98\xa4hpt.\b#\x16)$78L*k\x98h\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\xe4\xadB\n\r%\x1c\x02\r\v\x02$/\x0f\x0f$G6\n\x1d\x14sP\a\x10`X\x1d\b\x0f\x1c)\x1a\x05:F\x90/\x95fwH1p\xb8d\x01l\xb6qn\x1b\x18mPH\xaeui\xa8kw\x15_:[9D'\x1b\x8b\x02\xe5\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x03\x00\x00\x00\x00\b\x00\x05\x00\x00\x0f\x00\x1f\x003\x00\x00\x004.\x02\"\x0e\x02\x14\x1e\x022>\x01$4.\x02#!\x16\x12\x10\x02\a!2>\x01\x12\x10\x0e\x02#!\".\x02\x10>\x023!2\x1e\x01\x04\x80Q\x8a\xbdн\x8aQQ\x8a\xbdн\x8a\x03QQ\x8a\xbdh\xfe~w\x8b\x8bw\x01\x82h\xbd\x8a\xd1f\xab\xed\x82\xfd\x00\x82\xed\xabff\xab\xed\x82\x03\x00\x82\xed\xab\x02\x18н\x8aQQ\x8a\xbdн\x8aQQ\x8a\xbdн\x8aQZ\xfe\xf4\xfe\xcc\xfe\xf4ZQ\x8a\x01\xa7\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x00\x00\x00\x02\x00\x00\x00\x00\b\x00\x05\x00\x00\x13\x00#\x00\x00\x18\x01>\x023!2\x1e\x02\x10\x0e\x02#!\".\x01\x042>\x024.\x02\"\x0e\x02\x14\x1e\x01f\xab\xed\x82\x03\x00\x82\xed\xabff\xab\xed\x82\xfd\x00\x82\xed\xab\x04\xb2н\x8aQQ\x8a\xbdн\x8aQQ\x8a\x01\xfe\x01\x04\xed\xabff\xab\xed\xfe\xfc\xed\xabff\xab\x91Q\x8a\xbdн\x8aQQ\x8a\xbdн\x8a\x00\x00\x05\x00\x00\x00\x00\t\x00\x05\x00\x00\x0e\x00\x12\x00\x18\x00,\x00\\\x00\x00\x01!\"&?\x01&#\"\x06\x10\x16326'3&'\x05\x01!\a\x16\x17\x04\x10&#\"\a\x13\x16\x06\a\x06#\"'\x03\x06\x15\x14\x16 \x00\x10\x00 \x005467'\x01\x06+\x01\x0e\x01#\"\x00\x10\x0032\x177#\"&463!\x15!'#\"&463!2\x17\x01632\x02\xfa\xfe\xc6(#\x18\xbcAH\x84\xbc\xbc\x84s\xb0\xa3\xba\x129\x01q\x01 \xfe ci\x15\x05\x05\xbc\x84<=\xae\x0f\n\x16\x0f\x15#\x12\xae]\xbc\x01\b\x01<\xfe\xf9\xfe\x8e\xfe\xf9OFA\xfe\x9f\x12!\xc5\x17\xfc\xa8\xb9\xfe\xf9\x01\a\xb9re\x89\xe0\x1a&&\x1a\x01\x80\x01\xb3U\xde\x1a&&\x1a\x01\x00!\x14\x01\v[e\xb9\x01\x80F \xfb\x1f\xbc\xfe\xf8\xbc\x91\xefU?\x94\x01\x80\x84g\x95\xc4\x01\b\xbc\x18\xfe\xfc\x174\x0e\v\x1d\x01\x04_\x82\x84\xbc\x01\xf9\xfe\x8e\xfe\xf9\x01\a\xb9a\xad?b\xfe+\x1a\xa4\xdc\x01\a\x01r\x01\a7\xb7&4&\x80\x80&4&\x1c\xfep,\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\a\x00\x0f\x00\x1f\x00+\x00K\x00\x00\x004&\"\x06\x14\x162$4&\"\x06\x14\x162\x13\x03.\x01#!\"\x06\a\x03\x06\x163!26\x024&#!\"\x06\x14\x163!2\x01\x11#\x15\x14\x06\"&=\x01!\x15\x14\x06\"&=\x01#\x1147\x13>\x01$ \x04\x16\x17\x13\x16\x01\x80KjKKj\x04KKjKKj\x1dH\x05#\x17\xfcj\x17#\x05H\x05&\x1e\x04&\x1e&\xe7\x1c\x14\xfd\x80\x14\x1c\x1c\x14\x02\x80\x14\x01\xac\x80KjK\xfd\x00KjK\x80\x19g\t\xb1\x01\x1b\x01V\x01\x1b\xb1\ti\x17\x01\vjKKjKKjKKjK\x02\f\x01\x80\x17\x1d\x1d\x17\xfe\x80\x1e..\x02n(\x1c\x1c(\x1c\xfd[\xfd\xa5\x805KK5\x80\x805KK5\x80\x02[po\x01\xc6Nv<\x02\x01\x14\x06+\x01\x16\x15\x14\x02\x06\x04#\"\x00'#\"&546;\x01&54\x126$32\x00\x1732\x16\x05\xb72$\xfdB$22$\x02\xbe$\x01\b\x17\xfc*$22$\x03\x8cX\xfeڭ\xb1\xfeӯ\x17\x03\xd6$22$\xfctX\x01'\xad\x84\xf2\xaeh\x01s2$\x83\x11\x83\xdc\xfeϧ\xf6\xfekc\xbd$22$\x84\x11\x83\xdc\x011\xa8\xf5\x01\x95c\xbc$2\x02\xe3F33F3VVT2#$2\x8f\xa8\xaf\xfeԱVT2#$2\x8f\xa8g\xaf\xf1\x01\x84#2UU\xa7\xfe\xcf݃\x01\n\xd92$#2UU\xa7\x011݃\xfe\xf6\xd92\x00\x00\x06\x00\v\xff\x00\x04\xf5\x06\x00\x00\a\x00\x0f\x00\x1b\x00,\x00u\x00\xa3\x00\x00\x01\x03\x17\x1254#\"\x01\x16\x1767.\x02\x01\x14\x13632\x17\x03&#\"\x06\x03\x14\x1e\x0132654'.\x03#\"\x06\x03\x14\x17\x1e\x013276\x114.\x01'&$#\"\a\x06\x15\x14\x1e\x047232\x17\x16\x17\x06\a\x06\a\x0e\x01\x15\x14\x16\x15\a\x06\x15&'\x06#\x16\x15\x14\x06#\"&547\x16\x17\x1632654&#\"\x06\a467&54632\x17\x0254632\x13\x16\x17>\x0532\x16\x15\x14\x03\x1e\x03\x15\x14\x02\x0e\x01#\"'&\x02\x03\xb9ru\xa5&9\xfe\x8c\x1e\x03%\"\f*#\xfe͟\x11 \x0fO%GR\x9f=O&\x0e^\xaa\xfc\x98op\x95\xda\x04\x86\xfe\xb8\x15\x01\xc3C8\xfcpP\b*\x19\x02\a\a\x03\x85b\xfeY\n\x05\x01_\xdc#\xfc\xf5$\xa6\x8c\x1a\x0e\x18N Pb@6\xfe\x9d)?\x91\xa4\xaa\xa9\x01\x02+0L\x1215\v\x05\x1e\"4\x1c\x13\x04\x04\x02\x13\x13$\x1c\x1a\x16\x18.\x88E\x1fs\x1e\f\f\x02\n\xce\x02\a\x0e5I\x9cQ\"!@\fh\x11\f\"\xdeY7e|\x1aJ\x1e>z\x0f\x01\xceiPe\xfd\xbb\x11\x06\x10\u007fn\x91eHbIl\xfeF\x0f>^]@\x96\xfe\xfc\xben*9\x01\r\x00\x00\x00\x00\x04\x00\x00\xff\x80\b\x00\x05\x80\x00\x1a\x006\x00[\x00_\x00\x00\x013\x0e\x01#\"&54632\x16\x17#.\x01#\"\x06\x15\x14\x1e\x0232%3\x0e\x01#\"&54632\x16\x17#.\x01#\"\x06\x15\x14\x1e\x02326%4&'.\x02'&! \a\x0e\x02\a\x0e\x01\x15\x14\x16\x17\x1e\x02\x17\x16\x04! 7>\x027>\x01\x13\x11!\x11\x03\x11\xcf\x0e\xa9\x82\xa2\xb9\xba\x8c\x94\xa8\r\xcb\x05=39?\n\x1a6'_\x02\xd6\xce\x0e\xa8\x82\xa2\xb9\xba\x8c\x94\xa8\r\xcc\x04>29?\n\x1a5'17\x01m\x1f-\x06\x0f\x1c\x02V\xfd\x9d\xfd\x8fU\x05\x19\x11\x06-\x1e\x1e-\x06\x12\x17\x06,\x01\x87\x01\x13\x02bW\x05\x18\x11\x05.\x1e\xc0\xf8\x00\x02\x10\x9e\xb5\xe8\xc8\xc2뮠@Fyu0HC$\x8b\x9e\xb5\xe8\xc8\xc2뮠@Fyu0HC$L\xb6\xcf\xc8=\b\f\x12\x02??\x04\x0f\r\b<\xc7\xd1\xd0\xc7=\b\x0e\x0e\x05! A\x04\x0e\x0e\t<\xc6\x03\xcb\xfa\x00\x06\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x05`\x05\x80\x00\x1d\x00;\x00\x00\x01\x11\x14\x06+\x01\"&5\x114&#!\x11\x14\x06+\x01\"&5\x11463!2\x1e\x01\x01\x11\x14\x0e\x01#!\"&5\x1146;\x012\x16\x15\x11!265\x1146;\x012\x16\x03\xe0\x12\x0e\xa0\x0e\x12\xa0p\xfe\xf0\x12\x0e\xa0\x0e\x12\x12\x0e\x01Ї\xe4\x85\x01\x80\x85\xe4\x87\xfe0\x0e\x12\x12\x0e\xa0\x0e\x12\x01\x10p\xa0\x12\x0e\xa0\x0e\x12\x03\x90\xfe\x10\x0e\x12\x12\x0e\x01\xf0p\xa0\xfb\x80\x0e\x12\x12\x0e\x05@\x0e\x12\x85\xe4\x01I\xfc\x90\x87\xe4\x85\x12\x0e\x03\xc0\x0e\x12\x12\x0e\xfd\x00\xa0p\x03p\x0e\x12\x12\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00>\x00S\x00c\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x0554&+\x01\"\a&+\x01\"\x06\x1d\x01\x14;\x012=\x0146;\x012\x16\x1d\x01\x14;\x012=\x0146;\x012\x16\x1d\x01\x14;\x012%54&#!\"\x06\x15\x11\x14;\x012=\x01\x16;\x0126\x13\x11\x14\x06#!\"&5\x11463!2\x16\x05\x1f\x1b\x18\xca\x18\x1c\x1c\x18\xca\x18\x1b\xfe\x16A5\x85D\x1c\x1cD\x825A\x157\x16\x1b\x19^\x18\x1c\x156\x16\x1c\x18a\x18\x1b\x167\x15\x02MB5\xfe\xf85B\x167\x15\x1f?\xbf5B~\x88`\xfb\xd0`\x88\x88`\x040`\x88\x02\xb6r\x18\x1c\x1c\x18r\x18\x1c\x1c\xfe\xfa5A44A5\xfa\x16\x16\xe6\x18\x1c\x1c\x18\xe6\x16\x16\xe6\x18\x1c\x1c\x18\xe6\x16v\x9a5AA5\xfef\x15\x15\xb4*A\x02\x9d\xfb\xd0`\x88\x88`\x040`\x88\x88\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x02\x00\t\x00\x19\x00\x00\x01!\x1b\x01!\x01!\x01!\t\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x93\xfeړ\xe9\x017\xfe\xbc\xfeH\xfe\xbc\x017\x01\u007f\x02j\xaav\xfc@v\xaa\xaav\x03\xc0v\xaa\x01\xc2\x02'\xfc\x97\x04\x00\xfc\x00\x01:\x02\xa6\xfc@v\xaa\xaav\x03\xc0v\xaa\xaa\x00\x00\x00\x00\x17\x00\x00\xff\x00\b\x00\x06\x00\x00M\x00U\x00a\x00h\x00m\x00r\x00x\x00\u007f\x00\x84\x00\x89\x00\x91\x00\x96\x00\x9c\x00\xa0\x00\xa4\x00\xa7\x00\xaa\x00\xaf\x00\xb8\x00\xbb\x00\xbe\x00\xc1\x00\xcb\x00\x00\x01\x14\x06\a\x03\x16\x15\x14\x06\a\x03\x16\x15\x14\x06#\"'!\x06\"'!\x06#\"&547\x03.\x01547\x03.\x015467\x134&547\x13&54632\x17!62\x17!632\x16\x15\x14\a\x13\x1e\x01\x15\x14\a\x13\x1e\x01\x01!\x01#\x01!62\x01\x16\x15\x14\a\x13\x177\x11'\x06\a\x01!\x17%!\x06\"\x0167'\a#7\x03\x01\x17\x017\x13!\x016\x053\x01!\x11\x17\x16\x03!7\x01\x0f\x0135\a\x16\x11\x14\x16\x15\x14\a\x17\x117\x11\x17\x01/\x01\a\x117'\x06%#\x05\x17\x15\t\x02%'\x11\x05\a3\x01\x17\x13/\x02&=\x01\x03&'\t\x025\x03\x13#\x13\x01\a?\x01\x13&547\v\x01\x176\b\x00\x1a\x14\xcd\x03\x19\x14\xc1\x03!\x18\x19\x10\xfep\x114\x11\xfeq\x11\x1a\x17\"\x04\xc1\x14\x19\x03\xce\x14\x19\x1b\x14\xc7\x01\"\xd1\x04\"\x17\x1a\x12\x01\x8c\x106\x10\x01\x8e\x12\x1a\x17\"\x04\xcf\x17 \a\xbb\x13\x19\xfc'\x01\x85\xfe\xaa\x8f\xfe\xaa\x01h\x12*\xfc[\x01\x02\xd0\x0f\xbc\xbb\r\x10\x02\xa8\xfe|\xbe\x02*\xfe\xe8\x10,\x02\xaf\x01\x04@\x11\x1e\x16\xfc\xfe\xd8?\x01w\x10A\xfeU\x01M\b\xfcp\x05\x01V\xfe\x8b\x04\x0e\x12\x01\x92@\xfe˝\xc1\xa3\xa8\x04\x01\b\xab\x1e\x99\x01)\xdf\xdf\x04Ϳ\x06\x03w\x10\xfd\x93\xd5\xfe\xd7\x017\x01(\xfd{\x88\x01\xe6*U\x01%\xee\x84\x03\x01\x16\b\xd8\x05\b\xfeK\x016\xfc\xc0\xa3\xa3\xa3\xa3\x04=0\x82(\xcf\x02\x03\xab\x81M\x05\x02\x81\x15\x1f\x04\xfe\x9c\t\t\x14\x1f\x04\xfe\xaf\b\b\x17\"\x12\x14\x14\x14!\x18\b\f\x01O\x04\x1f\x14\t\t\x01d\x05\x1f\x14\x15\x1f\x04\x01X\x01\x04\x01$\x0f\x01k\n\b\x18!\x15\x15\x15\x15!\x18\x06\f\xfe\x9a\x01!\x16\r\x0e\xfe\xbc\x04\x1f\xfc\xcd\x01b\xfe\x9e\x10\x03\x1c\x04\t\n\x05\xfe\x98\x06\xc7\x01[\xc2\b\x02\x01\xc0\xc8\xc8\x10\xfbT\x06\x05DOi\x01\n\xfe\xcd@\xfe\x90\x1c\x016\xfe\xa9\x04\x0f\x01b\xfe\xb1\x06\x05\x01xB\x01A\xa6ݽ\xb1\b\x035\x01\x02\x01\x10\r\xb1\x01\r\v\xfeɝ\x01:\xec\xde\b\xfe\xf8J\xc9\x02\f\xe0\xe1+\xfe\xc5\xfe\xc1\x013\x0f\x8d\xfe\xe4\xdd,\x01\x88\xfb\x02p\x05\x01\x15\r\x10\x02\x01x\x01\x04\xfe1\xfe\xb9\x01\xf6\xdf\xfe\xe6\xfc\x89\xfe\xe5\x01\x1b\xe3\xe3F\x01i\n\x04\x01\x0f\x01(\xfd\x9cR\x03\x00\x02\x00\x00\xff\x00\x05\x80\x06\x00\x00\r\x00\x1b\x00\x00\x11463!\x01\x11\x14\x06#!\"&5%'\x114&#!\"\x06\x15\x11\x14\x163\xb7\x83\x02\xe6\x01`\xb7\x83\xfc\xf4\x83\xb7\x04а@.\xfe\x1c.@A-\x03X\x83\xbf\x01f\xfaB\x84\xbe\xbe\x84$\xb4\x01\xa9.BB.\xfe\x14.C\x00\x00\x04\x00\x00\xff\x83\x06\x00\x05}\x00\n\x00\x14\x00\x1e\x00)\x00\x00\x01\x04\x00\x03&54\x12$32\x05\x16\x17\x04\x00\x03&'\x12\x00\x01\x12\x00%\x16\x17\x04\x00\x03&\x05&'\x06\a6\x007\x06\a\x16\x03\xa6\xfe\xc3\xfe\"w\x14\xcd\x01`\xd0R\x01d]G\xfe{\xfd\xc5o]>p\x026\xfe\xa3s\x02\x11\x01c(\x0e\xfe\xdc\xfe@wg\x03\xcf\xc1\xae\x87\x9bm\x01J\xcc\x15PA\x05jy\xfe\x1d\xfe\xc1YW\xd0\x01a͊AZq\xfd\xc1\xfe{HZ\x01\x82\x02:\xfb<\x01d\x02\x14v\\gx\xfe>\xfe\xdb\x0e\x142AT\x17\xcd\x01Kn\x98\x84\xaf\x00\x00\x03\x00\x00\xff\x80\b\x00\x04\xf7\x00\x16\x00+\x00;\x00\x00\x01\x13\"'&#\"\a&#\"\a\x06+\x01\x136!2\x1763 \x012\x16\x17\x03&#\"\a&#\"\a\x03>\x0232\x1767\x03\x06\a&#\"\a\x03>\x0132\x176\x17\ae\x9b\x83~\xc8\xc1└\xe2\xc1Ȁ|\x05\x9b\xe0\x01\x02隚\xe9\x01\x02\xfe\xf1\x81Ν|\xab\xc5\xe0\x96\x96\xe0ū|iy\xb0Zʬ\xac\xf27Ӕ\x98ް\xa0r|\xd1uѥ\xac\xca\x04x\xfb\b9[\x94\x94[9\x04\xf8\u007fjj\xfb\xa69A\x03\xfdN\x8d\x8dN\xfc\x03+,#ll\"\x03\x8b\x04\x97\x9bB\xfcS32fk\x05\x00\x00\x05\x00\x00\xff\xa5\b\x00\x05[\x00\x0f\x00\x1f\x00/\x00?\x00\\\x00\x00%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x114&+\x01\"\x06\x15\x11\x14\x16;\x0126%\x14\x06#!\"&5467&54632\x176$32\x1e\x01\x15\x14\a\x1e\x01\x05\xdc\x1e\x14]\x14\x1e\x1e\x14]\x14\x1e\xfe\xe4\x1e\x14e\x14\x1e\x1e\x14e\x14\x1e\xfe\xdc\x1e\x14e\x14\x1e\x1e\x14e\x14\x1e\xfe\xdc\x1e\x14e\x14\x1e\x1e\x14e\x14\x1e\x05\x88\xec\xa6\xfb$\xa6\xec~i\n\xa1qfN-\x01*\xbd\x95\xfc\x93\x0e\x87\xac\xa5\x02\xdd\x15\x1e\x1e\x15\xfd#\x14\x1e\x1e\x14\x02\x13\x14\x1e\x1e\x14\xfd\xed\x14\x1e\x1e\x14\x01\xad\x14\x1e\x1e\x14\xfeS\x14\x1e\x1e\x14\x01j\x14\x1e\x1e\x14\xfe\x96\x14\x1e\x1e\xa6\xa6\xec\xec\xa6t\xc52\"'q\xa1C\xb7\xea\x93\xfc\x95B8!\xdb\x00\x00\x00'\x00\x00\xff>\x06\x00\x06\x00\x00\x04\x00\t\x00\r\x00\x11\x00\x15\x00\x19\x00\x1d\x00!\x00%\x00)\x00-\x001\x005\x009\x00=\x00A\x00E\x00I\x00M\x00Q\x00U\x00Y\x00]\x00a\x00g\x00k\x00o\x00s\x00w\x00{\x00\u007f\x00\x85\x00\x89\x00\x8d\x00\x91\x00\x95\x00\x99\x00\xa5\x00\xd5\x00\x00\x11!\x11\t\x01%\x11!\x11\t\x015!\x15\x13\x15#5\x17\x15#5\x17\x15#5\x17\x15#5\x17\x15#5\x177\x17\a\x177\x17\a\x177\x17\a\x177\x17\a?\x01\x17\a?\x01\x17\a?\x01\x17\a?\x01\x17\a\x01\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5\x01\x15#53\x157\x15#5!\x15#5!\x15#5!\x15#5!\x15#5!\x15#5\x175#53\x15\a53\x15\a53\x15\a53\x15\a53\x15\a53\x15%\"&54632\x16\x15\x14\x06\x01\x14\x1e\x026\x16\x15\x14#\"'#\a\x1632>\x0254.\x01\x06&54>\x0132\x16\x1737.\x06#\"\x0e\x02\x06\x00\xfc\xf8\xfd\b\x05\x9c\xfa\xc8\x02\x95\x02\xa3\xfa\xc8Q%%%%%%%%%?\x0fi\x0f\x1f\x0fi\x0f\x1e\x0fi\x0f\x1f\x0fh\x0fOi\x0fixi\x0fiyi\x0fixi\x0fi\xfcAr\x01\x14s\x01\x15s\x01\x14r\x01\x14r\x01\x14s\x01\x15s\x01\x14r\xfb\xb8%s\xa2s\x01\x15s\x01\x14r\x01\x14r\x01\x14s\x01\x15s\xf0Ns%%%%%%%%%%\xfd\x88\x81\xb8\xb8\x81\x82\xb7\xb7\xfe\xd9'\x0232\x16\x15\x14\a\x06\x04#\".\x0154\x0032\x1e\x0532654&#\"\x06#\"&54654&#\"\x0e\x02#\"&547>\x0132\x16\x15\x14\a6\x05\x96\x01\x04\x94\xd2ڞU\x9azrhgrx\x98S\x9a\xc3Пd\xd8U\x05 \x1c\b\x0e\x15\x017\x03#\"&463!2\x1e\x04\x17!2\x16\x04\xc0&\x1a\x80&4&\x80\x1a&&\x1a\x80&4&\x80\x1a\xfd\xe6KjKKj\x03\xcbKjKKj\xcb \x19\xfb\xec\x01\a\x05\x18\x03\x98\x1a&&\x1a\xfc\x00\x1a&\x16%\x02\xb1\xcc\x1a&&\x1a\x01\x00\x10\x19\x0f\v\x04\a\x01\x04\xb1\x1a&\x03&4&\x80\x1a&&\x1a\x80&4&\x80\x1a&&\x1a\x80\xfd5jKKjKKjKKjK\x03\xc0\xfe\x00\x18%\x03z\a\x1d\x18\n\x100&4&&\x1a\x0e3D\x04\x037&4&\r\x12\x1f\x16%\a&\x00\x00\x00\x00\x04\x00\x00\xff\x80\x06\x80\x05\x00\x00\x17\x00\x1f\x00'\x00S\x00\x00\x004&\"\x0f\x01\x114&\"\x06\x15\x11'&\"\x06\x14\x17\x01\x1627\x01\x00\x14\x06\"&462\x04\x14\x06\"&462\x13\x11\x14\x06\a\x05\x1e\x02\x15\x14\a!2\x16\x14\x06#!\"&54>\x017\x03#\"&463!2\x1e\x04\x17!2\x16\x05\x00&4\x13\x93&4&\x93\x134&\x13\x01\x00\x134\x13\x01\x00\xfd\x93KjKKj\x03\xcbKjKKj\xcb \x19\xfb\xec\x01\a\x05\x18\x03\x98\x1a&&\x1a\xfc\x00\x1a&\x16%\x02\xb1\xcc\x1a&&\x1a\x01\x00\x10\x19\x0f\v\x04\a\x01\x04\xb1\x1a&\x03&4&\x13\x92\x01%\x1a&&\x1a\xfeے\x13&4\x13\xff\x00\x13\x13\x01\x00\xfd\"jKKjKKjKKjK\x03\xc0\xfe\x00\x18%\x03z\a\x1d\x18\n\x100&4&&\x1a\x0e3D\x04\x037&4&\r\x12\x1f\x16%\a&\x00\x00\x00\x00\a\x00\x00\xff\x00\b\x00\x05\x80\x00\x02\x00\x05\x00\t\x00\f\x00\x10\x00\x14\x00&\x00\x00\x13\t\x03!'\x13!\t\x02!%!\x03!\x01!\x01!%\x01\x16\x06\a\x01\x06\"'\x01.\x017\x0163!2\xd4\x02o\xfe\xd4\x01\xe9\x01]\xfdF\x89\xcc\xfe\xfa\xfe\xe0\x03\xfd\x02o\xfe\xbd\xfc\xc2\x02\xaa\xcc\xfe\xee\x02o\x01Z\xfe\xe0\xfe\xfa\x01Y\x01\x80\x0e\x02\x10\xfc@\x12:\x12\xfc@\x10\x02\x0e\x01\x80\x12!\x04\x80!\x03\x00\xfdg\x02\x99\xfc\xfc\x03\x04\x80\x01\x80\xfe\x80\xfc\xe7\x02\x99\x80\x01\x80\xfe\x80\x01\x80f\xfe\x00\x12/\x11\xfc\x00\x14\x14\x04\x00\x11/\x12\x02\x00\x1a\x00\x03\x00\x13\xff\x00\a\xed\x06\x00\x00I\x00\x97\x00\xa0\x00\x00\x0562\x1f\x01\a'\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x017\x17762\x1f\x01762\x1f\x01762\x1f\x01762\x1f\x01762\x1f\x01762\x1f\x01%\x06\"/\x017\x17762\x1f\x017\x11\x03&6?\x01\x1135!5!\x15!\x153\x11\x17\x1e\x01\a\x03\x11762\x1f\x01762\x1f\x01\a'\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\a\x06\"/\x01\x01\x15%\x055#5!\x15\a\x13\x134\x13\x80ZSS\x126\x12SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13\x80ZSS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13S\xfa-\x134\x13\x80ZSS\x134\x13S@\xd2\x11\x14\x1e\xb1\x80\x01\x00\x01\x00\x01\x00\x80\xb1\x1e\x14\x11\xd2\x13\x134\x13SS\x134\x13\x80ZSS\x126\x12SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13SS\x134\x13S\x01@\x01\x80\x01\x80\x80\xfe\x00\x13\x13\x13\x80ZSS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13\x80ZSS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13Sy\x13\x13\x80ZRR\x13\x13R@\x01%\x01:\x1a=\n:\x01+\x80\x80\x80\x80\xfe\xd5:\n=\x1a\xfe\xc6\xfe\xdb\x12\x13\x13RR\x13\x13\x80ZSS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13SS\x13\x13S\x04\x1a\x80\x80\x80\x80\x80\x80\x00\x00\x00\x04\x00\x00\xff\x80\x05\x80\x06\x00\x00\x03\x00\a\x00C\x00v\x00\x00!\x13/\x01\x01\x13\x0f\x01\x01&'&#\"\a\x06\"'&#\"\a\x06\a\x16\x17\x1e\x01\x17\x1e\t32>\x03;\x012\x1e\x0332>\b7>\x0176\x01\x14\x06#!\"&54>\x037'3&547&547>\x017632\x162632\x17\x1e\x01\x17\x16\x15\x14\a\x16\a3\a\x1e\x03\x02@``\x80\x01\x80\x80\x80`\x01\x00\x02\x02\nVFa\a\x1c\aaFV\n\x02\x02\x02\x02\x02\v\x02\x02\v\x03\f\x05\r\v\x11\x12\x17\r$.\x13\n\r\v\f\v\r\n\x13.$\r\x17\x12\x11\v\r\x05\f\x03\v\x02\x02\v\x02\x02\x01\xa2\x92y\xfc\x96y\x92\t\x1d.Q5Z\xd6\x16\x02\xc2\xd2\x11E$ ,\x1el\x90*%>>%*\x90>*98(QO\xe1!\u007f\xa0\x8f\x00\x03\x00\x00\x00\x00\b\xfd\x05\x00\x00L\x00\\\x00p\x00\x00\x01\x16\x0e\x02'.\x01'&67'\x0e\x01\x15\x14\x06#!#\x0e\x01#\"\x00\x10\x0032\x177&+\x01\"&46;\x012\x1e\x02\x17!3'#\"&7>\x01;\x012\x1f\x0176;\x012\x16\x1d\x01\x14\x06+\x01\x176\x17\x1e\x01\x01267!\"'&7\x13&#\"\x06\x10\x16(\x016\x10&#\"\a\x13\x16\x06\a\x06#\"'\x03\x06\x15\x14\b\xfd\fD\x82\xbbg\xa1\xed\x10\fOOG`n%\x1b\xff\x00E\x17\xfc\xa8\xb9\xfe\xf9\x01\a\xb9LL\x18{\xb5@\x1a&&\x1a\x80N\x86c,\x1d\x02\x00sU\xde\x1e&\x05\x04&\x18\xfd!\x14Fr\x13\x1be\x1a&&\x1a\xb3s\x83\x90\x8f\xca\xf8\xd4s\xb0\x17\xfe\xc6#\x14\x12\x11\x93/,\x84\xbc\xbc\x05\x80\x01\b\xbc\xbc\x84<=\xae\x0f\n\x16\x0f\x15#\x12\xae]\x01\xf4g\xbf\x88L\a\v\xe4\xa0o\xc7GkP\xe4\x82\x1b'\xa4\xdc\x01\a\x01r\x01\a\x1b-n&4&\x1b2\x1d\x16\x80-\x1e\x17\x1e\x1cir\x13&\x1a\x80\x1a&\xac?\x1b\x1a\xd9\xfd\xfb\x91o\x1f \x1f\x01\x15\r\xbc\xfe\xf8\xbc\xbc\x01\b\xbc\x18\xfe\xfc\x174\x0e\v\x1d\x01\x04_\x82\x84\x00\x00\x03\x00\x00\xff\x00\x05\x80\x05\xe0\x005\x00O\x00W\x00\x00!\x14\x0e\x02 .\x0254>\x0276\x16\x17\x16\x06\a\x0e\x04\a\x1e\x042>\x037.\x04'.\x017>\x01\x17\x1e\x03\x01\x11\x14\x06+\x01\x11\x14\x06#!\"&5\x11#\"&5\x11463!2\x16\x02\x14\x06\"&462\x05\x80{\xcd\xf5\xfe\xfa\xf5\xcd{BtxG\x1a,\x04\x05\x1f\x1a:`9(\x0f\x01\x030b\x82\xbfԿ\x82b0\x03\x01\x0f(9`:\x1a\x1f\x05\x04,\x1aGxtB\xfe\x80&\x1a@&\x1a\xff\x00\x1a&@\x1a&K5\x01\x805K`\x83\xba\x83\x83\xba?e=\x1f\x1f=e?1O6#\f\x05\x1f\x1a\x1a,\x04\n\x1b\x18\x17\x10\x04\v\x1f#\x1e\x14\x14\x1e$\x1f\f\x04\x0e\x18\x17\x1b\n\x04,\x1a\x1a\x1f\x05\f#6O\x03O\xfe\x80\x1a&\xfe\x80\x1a&&\x1a\x01\x80&\x1a\x01\x805KK\x01\xa8\xba\x83\x83\xba\x83\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00\x1b\x00?\x00\x00\x01!\x0e\x01\x0f\x01\x01\x06\"'\x01&'!267\x1b\x01\x1e\x013267\x13\x17\x16\x01\x14\a!'.\x01\a\x06\a\v\x01.\x01\"\x06\a\x03!&54632\x1e\x02\x17>\x0332\x16\x05\x00\x011\x05\n\x04\x03\xfd\x91\x124\x12\xfd\x90\x05\x10\x01q\x16#\x05F\xbe\x06\"\x16\x15\"\x06\x928\x12\x02'g\xfe\x8fo\b#\x13-\v\x81\xc4\x06#,\"\x05t\xfeYg\xfe\xe0>\x81oP$$Po\x81>\xe0\xfe\x02\x00\x06\t\x03\x04\xfd\xa8\x12\x12\x02Z\x02\x12\x1b\x15\x01\x19\xfde\x14\x1a\x1a\x14\x01\xe5p#\x01\xac\x91\x9b\xdd\x11\x14\x02\x05)\xfeR\x02\xae\x14\x1a\x1b\x15\xfe0\x9b\x91\xdc\xf8+I@$$@I+\xf8\x00\x00\x02\x00\x02\xff\x00\x04\x80\x05\xfc\x00+\x003\x00\x00\x01\x14\x00\a\x1132\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x01\x11.\x01\x027>\x0276\x04\x12$\x10\x00 \x00\x10\x00 \x04\x80\xfe\xd9\xd9\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x96\xf3\x81\f\v\x8bᅪ\x01*\xae\xfc\x00\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x03\xc0\xdd\xfe\xb9\x18\xfe\xfc\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x01\x04\x10\xae\x01\x12\x9b\x86\xe6\x92\x0f\x13\x92\xfe\xea\x12\xfe\x8e\xfe\xf9\x01\a\x01r\x01\a\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00'\x00/\x00\x00\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\x01\x16\x15\x14\x0e\x02\".\x024>\x0232\x17\x01!\"&=\x01463\x00 \x00\x10\x00 \x00\x10\x05\xc0\x1a&\x12\x0e@\x0e\x12\xfe\x82~[\x9b\xd5\xea՛[[\x9b\xd5u˜\x01~\xfe\xfb\x0e\x12\x12\x0e\xfdg\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\x80&\x1a\xfe`\x0e\x12\x12\x0e\x01\x06\xfe\x81\x9c\xcbu՛[[\x9b\xd5\xea՛[~\x01~\x12\x0e@\x0e\x12\xfa\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x02\x00\x00\xff\x00\x04\x80\x06\x00\x00=\x00E\x00\x00\x01\x16\x12\x15\x14\x00\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015&\x0054\x127&'&6;\x012\x17\x1e\x012676;\x012\x16\a\x06\x00 \x00\x10\x00 \x00\x10\x03>\x91\xb1\xfe\xd9\xd9`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\xd9\xfeٱ\x91\xa5?\x06\x13\x11E\x15\b,\xc0\xec\xc0,\b\x1d=\x11\x13\x06?\xfd\xa4\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x04\xc4H\xfe\xeb\xa7\xdd\xfe\xb9\x18\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x18\x01Gݧ\x01\x15H`\xb1\x10\x1b\x14j\x82\x82j\x14\x1b\x10\xb1\xfb\xdc\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x02\x00\x02\xff\x00\x05\x80\x06\x00\x00B\x00J\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x16\x15\x14\x00\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015.\x01\x0276\x0076\x16\x17%#\"&5\x00 \x00\x10\x00 \x00\x10\x04\x00\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfe~\xfe\xd9\xd9`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x95\xf3\x82\f\x10\x01 \xcbv\xdcX\x00\xff\x86\x0e\x12\xfd\x87\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\xe0\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff\x9e\xc9\xdd\xfe\xb9\x18\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x10\xae\x01\x11\x9b\xcc\x01+\x17\x0eBF\xfe\x12\x0e\xfb`\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x00\xff\x00\x06\x80\x06\x00\x00k\x00s\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x16\x15\x14\x00\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015&\x00547'\a\x0e\x01/\x01.\x01?\x01'\x15\x14\x06+\x01\"&5\x11463!2\x16\x1d\x01\x14\x06+\x01\x177>\x01\x1f\x01\x1e\x01\x0f\x01\x176 \x17%#\"&5\x00 \x00\x10\x00 \x00\x10\x05\x00\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfe~\xfe\xd9\xd9`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\xd9\xfe\xd9~4e\t\x1a\n0\n\x01\tio\x12\x0e@\x0e\x12&\x1a\x01 \x0e\x12\x12\x0e\x85jV\t\x1a\n0\n\x01\tZ9\x9e\x01\x92\x9e\x00\xff\x86\x0e\x12\xfd\x87\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\xe0\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff\x9e\xc9\xdd\xfe\xb9\x18\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x18\x01G\xddɞ5o\n\x01\b,\b\x1b\nsp\x86\x0e\x12\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12k^\n\x01\b,\b\x1b\nc8~~\xfe\x12\x0e\xfb`\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x05\x00\x02\xff\x00\x06\xfe\x05\xfd\x008\x00>\x00K\x00R\x00_\x00\x00\x01\x16\x02\x06\a\x1132\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01!\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x01\x11.\x01\x0276\x0076\x176\x17\x16\x00\x016\x10'\x06\x10\x0327&547&#\"\x00\x10\x00\x01\x11&'\x06\a\x11\x012\x00\x10\x00#\"\a\x16\x15\x14\a\x16\x06\xfe\f\x81\xf3\x96\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\xfe\x00\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\x96\xf3\x81\f\x11\x01'\xcdΫ\xab\xce\xcd\x01'\xfc\x93\x80\x80\x80\xc0sg\x9a\x9ags\xb9\xfe\xf9\x01\a\x02\xf9\x89ww\x89\x02@\xb9\x01\a\xfe\xf9\xb9sg\x9a\x9ag\x03\xef\x9b\xfe\xee\xae\x10\xfe\xfc\x12\x0e@\x0e\x12\xe0\x0e\x12\x12\x0e\xe0\xe0\x0e\x12\x12\x0e\xe0\x12\x0e@\x0e\x12\x01\x04\x10\xae\x01\x12\x9b\xce\x01-\x13\x15ss\x15\x13\xfe\xd3\xfdʃ\x01l\x83\x83\xfe\x94\xfe\xf69\xa5\xe2\xe0\xa79\xfe\xf9\xfe\x8e\xfe\xf9\xfe\x80\x01\x04\x0fOO\x0f\xfe\xfc\x01\x80\x01\a\x01r\x01\a9\xa7\xe0\xe2\xa59\x00\x00\x04\x00\x01\xff\x06\a\x80\x06\x00\x00F\x00P\x00^\x00l\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x1e\x01\a\x06\x00\a\x06$'.\x037>\x0276\x16\x17%#\"&=\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x16\x17\x16\x17%#\"&5\x014'\x0e\x01\x15\x14\x17>\x01%\x14\x16\x17&54\x007.\x01#\"\x00\x012\x0054&'\x16\x15\x14\x00\a\x1e\x01\x06\x00\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfeL?\x16\x1f\xfe\xf2\xb7\xd2\xfe\xa3CuГP\b\t\x8a\xe2\x87v\xdbY\x00\xff\x86\x0e\x12\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfe;\"\xb6\x92\x00\xff\x86\x0e\x12\xfe\x00\x04\xa2\xda\x04\xa2\xda\xfc\x80ޥ\x03\x01\x0e\xcb5݇\xb9\xfe\xf9\x03\xc0\xb9\x01\aޥ\x03\xfe\xf2\xcb5\xdd\x04`\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff_\ue036\xfe\xfc\x1a\x1dڿ\x06g\xa3\xdew\x87\xea\x95\x0f\x0eBF\xfe\x12\x0e@\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xffJ_\ts\xfe\x12\x0e\xfe\xa0\x14&\x19\xfa\xa7\x14&\x19\xfa\xa7\xa8\xfc\x17\x1d\x1e\xd2\x01?%x\x92\xfe\xf9\xfc\a\x01\a\xb9\xa8\xfc\x17\x1c\x1f\xd2\xfe\xc1%x\x92\x00\x04\x00\x06\xff\x00\b\x00\x06\x00\x00J\x00P\x00\\\x00h\x00\x00\x01463!2\x16\x15\x11\x14\x06+\x01\"&=\x01\a\x1e\x01\a\x06\x00\a\x06'\x06\a\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06+\x01\"&=\x01#\"&=\x0146;\x015.\x01\x0276\x0076\x17632\x17%#\"&5\x016\x10'\x06\x10\x00\x10\x00327&\x107&#\"\x012\x00\x10\x00#\"\a\x16\x10\a\x16\x06\x80\x12\x0e\x01 \x1a&\x12\x0e@\x0e\x12\xfeL?\x16 \xfe\xf7\xb5ߺu\x8b`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x9b\xf9}\x17\x19\x01\r\xba\u0e92\xaeɞ\x00\xff\x86\x0e\x12\xfd\x00\x80\x80\x80\xfd\x80\x01\a\xb9ue\x9a\x9aeu\xb9\x039\xb9\x01\a\xfe\xf9\xb9ue\x9a\x9ae\x05\xe0\x0e\x12&\x1a\xfe\xe0\x0e\x12\x12\x0e\x86\xff_\ue034\xfe\xfc\x1b\"|N\x0f\x84\x12\x0e@\x0e\x12`\x0e\x12\x12\x0e`\x12\x0e@\x0e\x12\x84\x11\xb9\x01\"\xa2\xbb\x01\x0f\x1d\"|a~\xfe\x12\x0e\xfb\xe7\x83\x01l\x83\x83\xfe\x94\x01o\xfe\x8e\xfe\xf99\xa7\x01\xc0\xa79\xfc\x80\x01\a\x01r\x01\a9\xa7\xfe@\xa79\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00;\x00C\x00\x00\x012\x16\x15\x11\x14\x06+\x01\"&5\x11\a\x17\x16\x14\x0f\x01\x06\"/\x01\a\x16\x15\x14\x0e\x02\".\x024>\x0232\x177'&4?\x0162\x1f\x017!\"&=\x01463\x00 \x00\x10\x00 \x00\x10\x05\xc0\x1a&\x12\x0e@\x0e\x12Ռ\t\t.\t\x1a\n\x8cN~[\x9b\xd5\xea՛[[\x9b\xd5u˜N\xac\t\t.\t\x1a\n\xac\xd5\xfe\xfb\x0e\x12\x12\x0e\xfdg\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x05\x80&\x1a\xfe`\x0e\x12\x12\x0e\x01\x06\u058c\n\x1a\t.\t\t\x8dO\x9c\xcbu՛[[\x9b\xd5\xea՛[~N\xac\n\x1a\t.\t\t\xac\xd5\x12\x0e@\x0e\x12\xfa\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x00\x00\x02\x00\x02\xff\x04\x04\x80\x06\x00\x009\x00A\x00\x00\x01\x16\x00\x15\x14\x02\x04'.\x02'&\x12675#\"&=\x0146;\x015\a\x06\"/\x01&4?\x0162\x1f\x01\x16\x14\x0f\x01\x06\"/\x01\x1532\x16\x1d\x01\x14\x06+\x01\x02 \x00\x10\x00 \x00\x10\x02\x80\xd9\x01'\xae\xfe֪\x85\xe1\x8b\v\f\x81\xf3\x96\xa0\x0e\x12\x12\x0e\xa0\\\n\x1a\t.\t\t\xca\x134\x13\xca\t\t.\t\x1a\n\\\xa0\x0e\x12\x12\x0e\xa0\xf9\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x03|\x18\xfe\xb9ݧ\xfe\xea\x92\x13\x0f\x92憛\x01\x12\xae\x10\x84\x12\x0e@\x0e\x12\xa5\\\t\t.\t\x1a\n\xc9\x13\x13\xc9\n\x1a\t.\t\t\\\xa5\x12\x0e@\x0e\x12\xfb\x80\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x04\x00\x00\a\x80\x04~\x009\x00A\x00\x00\x01\x16\x14\a\x01\x06\"/\x01&4?\x01!\x15\x14\x06+\x01\"&=\x01#\x06\x00#\"$\x027>\x0276\x04\x16\x173546;\x012\x16\x1d\x01!'&4?\x0162\x17\x00 \x00\x10\x00 \x00\x10\am\x13\x13\xfe\xda\t\x1b\t-\n\n\xb9\xfe\xda\x12\x0e@\x0e\x12\x84\x18\xfe\xb9ݧ\xfe\xea\x92\x13\x0f\x92憛\x01\x12\xae\x10\x84\x12\x0e@\x0e\x12\x01&\xb9\n\n-\t\x1b\t\xfb@\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x02m\x134\x13\xfe\xda\n\n-\t\x1b\t\xb9\xe0\x0e\x12\x12\x0e\xe0\xd9\xfeٮ\x01*\xaa\x85\xe1\x8b\v\f\x81\xf3\x96\xe0\x0e\x12\x12\x0e\xe0\xb9\t\x1b\t-\n\n\xfc\xed\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x00\xff\x00\x04\x80\x06\x00\x00\x17\x00\x1f\x00\x00\x01\x14\x00\a\x11\x14\x06+\x01\"&5\x11&\x0054>\x022\x1e\x02\x00 \x00\x10\x00 \x00\x10\x04\x80\xfe\xd9\xd9\x12\x0e@\x0e\x12\xd9\xfe\xd9[\x9b\xd5\xea՛[\xfd\a\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x03\xc0\xdd\xfe\xb9\x18\xfd\x9c\x0e\x12\x12\x0e\x02d\x18\x01G\xddu՛[[\x9b\xd5\xfd\xcb\x01\a\x01r\x01\a\xfe\xf9\xfe\x8e\x00\x00\x02\x00\x00\x00\x00\x04\x80\x04\x80\x00\a\x00\x17\x00\x00\x00\x10\x00 \x00\x10\x00 \x00\x14\x0e\x02\".\x024>\x022\x1e\x01\x04\x00\xfe\xf9\xfe\x8e\xfe\xf9\x01\a\x01r\x01\x87[\x9b\xd5\xea՛[[\x9b\xd5\xea՛\x01\x87\x01r\x01\a\xfe\xf9\xfe\x8e\xfe\xf9\x025\xea՛[[\x9b\xd5\xea՛[[\x9b\x00\x00\x01\x00\x00\xff\x80\x06\x00\x05\x80\x00$\x00\x00\x012\x16\x15\x11\x14\x06#!\x1137#546375&#\"\x06\x1d\x01#\x153\x11!\"&5\x11463\x05\xab#22#\xfey\xc7\x1e\xe5/Dz?s\x88\xa3\xc8\xc8\xfd!#22#\x05\x802#\xfa\xaa#2\x02S\xe8\x9488\x01\xcf\t\xa0\x92\xab\xe8\xfd\xad2#\x05V#2\x00\x00\x00\x01\x00\x00\xff\x80\x05\x00\x06\x00\x00L\x00\x00\x114>\x0332\x04\x16\x15\x14\x0e\x03#\"&'\x0e\x06\x0f\x01'&546\x127&54632\x16\x15\x14\x06\x15\x14\x1632>\x0454&#\"\x00\x15\x14\x1e\x02\x15\x14\x06#\"'.\x03K\x84\xac\xc6g\x9e\x01\x10\xaa&Rv\xacgD\x86\x1d\n$\v\x1e\x16*2%\x0e\t\x0f+Z\a hP=DXZ@7^?1\x1b\r۰\xc8\xfe\xf4\x19\x1d\x19\x1e\x16\x02\x0f3O+\x16\x03\xabl\xbf\x8eh4\x85\xfe\xa0`\xb8\xaa\x81M@8'\x93+c+RI2\x05\n\x9d\x1f\\\xe5\x01Z\x1eAhS\x92Q>B\xfa>?S2Vhui/\xad\xc1\xfe\xfd\xc7,R0+\t\x1cZ\x03\x0fRkm\x00\x00\x00\x00\x03\x00\x00\xffz\x06\x00\x05\x86\x00+\x00>\x00Q\x00\x00\x002\x16\x17\x16\x15\x14\a\x0e\x01#\"'.\x01'&7567632\x1632\x16\x17\x1e\x01\x15\x14\x06\x15\x14\x17\x16\x17\x16\x17\x1632\x032>\x024.\x02\"\x0e\x02\x15\x14\x17\a7\x16\x12 \x04\x16\x12\x10\x02\x06\x04#\"'\x05\x13&54\x126\x03\xcc\x1a\xa9\x05\x02\x11\x10n/9\x85b\x90LH\x01\x03G\x18\x1c\x06\x18\a\x13\x0f\b\b2E\x05\"D8_\f\n\x0fp\u007f\xe9\xa8dd\xa8\xe9\xfe\xe9\xa8dxO\xf2\x9e\"\x012\x01\x17\xcaxx\xca\xfe\xe9\x99ê\xfe_\x88lx\xca\x022X\t\x05\n!+'5>-\x92pkW\b[C\x16\x03\r\x15\x14\x88\a\x15I\n\a\bI@50\a\xfeOd\xa8\xe9\xfe\xe9\xa8dd\xa8\xe9\u007f˥\xe9Mh\x05fx\xca\xfe\xe9\xfe\xce\xfe\xe9\xcax^\x86\x01\x95\xb2ә\x01\x17\xca\x00\x00\t\x00\x00\x00\x00\a\x00\x05\x80\x00\x03\x00\a\x00\x0f\x00\x13\x00\x1b\x00#\x00'\x00+\x00/\x00\x007!5!\x11!5!\x004&\"\x06\x14\x162\x01!5!\x004&\"\x06\x14\x162\x124&\"\x06\x14\x162\x13\x11!\x11\x01\x11!\x11\x01\x11!\x11\x80\x04\x00\xfc\x00\x04\x00\xfc\x00\x06 8P88P\xfa\x18\x04\x00\xfc\x00\x06 8P88P88P88P\x98\xf9\x00\a\x00\xf9\x00\a\x00\xf9\x00\x80\x80\x01\x80\x80\xfd\x98P88P8\x04 \x80\xfd\x98P88P8\x028P88P8\xfd \xfe\x80\x01\x80\x02\x00\xfe\x80\x01\x80\x02\x00\xfe\x80\x01\x80\x00\x00\x03\x00\x00\xff\x80\b\x00\x05\x80\x00\a\x00+\x00N\x00\x00\x00 &\x106 \x16\x10\x01!2\x16\x1d\x01\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!\x1146;\x012\x16\x15\x01\x14\x163!\x15\x06#!\"&54>\x0532\x17\x1e\x01267632\x17#\"\x06\x15\x03_\xfe\xc2\xe1\xe1\x01>\xe1\x02@\x01`\r\x13\x13\r\xfe\xa0\x13\r\xc0\r\x13\xfe\xa0\r\x13\x13\r\x01`\x13\r\xc0\r\x13\xfd L4\x01\x00Dg\xfc\x96y\x92\a\x15 6Fe=\x13\x14O\x97\xb2\x97O\x14\x13\x84U\xdf4L\x02\x80\xe1\x01>\xe1\xe1\xfe\xc2\xfe\x9f\x13\r\xc0\r\x13\xfe\xa0\r\x13\x13\r\x01`\x13\r\xc0\r\x13\x01`\r\x13\x13\r\xfd\xc04L\xee2\x8ay5eud_C(\x11====\x11`L4\x00\x00\x00\x03\x00\x00\xff\x80\a\xf7\x05\x80\x00\a\x003\x00V\x00\x00\x00 &\x106 \x16\x10\x01\x17\x16\x15\x14\x0f\x01\x06#\"/\x01\a\x06#\"/\x01&54?\x01'&54?\x01632\x1f\x017632\x1f\x01\x16\x15\x14\a\x05\a\x06\x15\x14\x1f\x01\x06#!\"&54>\x0532\x17\x16 7632\x17\x0e\x01\x15\x14\x17\x03_\xfe\xc2\xe1\xe1\x01>\xe1\x02\xb5\xf9\t\t\x88\t\r\x0e\t\xf9\xf9\t\x0e\r\t\x88\t\t\xf9\xf9\t\t\x88\t\r\x0e\t\xf9\xf9\t\x0e\r\t\x88\t\t\xfd\x15\xb5%%S\x15\x17\xfc\x96y\x92\a\x15 6Fe=\x13\x14\x9a\x01J\x9a\x14\x13\x1c\x1d\x1c\x1a%\x02\x80\xe1\x01>\xe1\xe1\xfe\xc2\xfd\xdf\xf9\t\x0e\r\t\x88\t\t\xf9\xf9\t\t\x88\t\r\x0e\t\xf9\xf9\t\x0e\r\t\x88\t\t\xf9\xf9\t\t\x88\t\r\x0e\t\xf9\xb5%65%S\x03\x8ay5eud_C(\x11zz\x11\x06\x1b.!6%\x00\x03\x00\x00\x00\x00\b\x00\x05\x00\x00\x12\x00\x1a\x00$\x00\x00\x01!2\x16\x15\x11!\x11!\x11!\x1146;\x012\x16\x15\x004&\"\x06\x14\x162!54&#!\"\x06\x15\x11\x01\x00\x06\xc0\x1a&\xff\x00\xfa\x00\xff\x00&\x1a\x80\x1a&\x02@\x96Ԗ\x96\xd4\x05V\xe1\x9f\xfd@\x1a&\x02\x00&\x1a\xfe@\x01\x00\xff\x00\x04\xc0\x1a&&\x1a\xfe\x16Ԗ\x96Ԗ@\x9f\xe1&\x1a\xfe\x80\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06\x00\x06\x00\x00\x16\x00\x19\x00\x00\x01\x033\x15!\a!\x15!\t\x01!5!'!53\x03!\x01!\t\x01\x13#\x06\x00\xc0\xc0\xfe\xee7\x01I\xfee\xfe\x9b\xfe\x9b\xfee\x01I7\xfe\xee\xc0\xc0\x01\x00\x01C\x01z\x01C\xfe\x00l\xd8\x06\x00\xfe@\xc0\x80\xc0\xfc\xc0\x03@\xc0\x80\xc0\x01\xc0\xfd\x00\x03\x00\xfb@\x01\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x17\x00\x1f\x00#\x00\x00\x012\x04\x15\x11\x14\x06\a\x17\x16\x06#!\"&?\x01.\x015\x114$3\x12264&\"\x06\x14\x01\x11!\x11\x04@\xb9\x01\a\xfb\xb4\xd5\x10\x10\x16\xfb\xe0\x16\x10\x10մ\xfb\x01\a\xb9\xf0\xa0pp\xa0p\x03\x00\xfb\x80\x06\x00\xbb\x85\xfc\x80\x82\xb8\x05\xca\x0f((\x0f\xca\x05\xb8\x82\x03\x80\x85\xbb\xfa\xc0p\xa0pp\xa0\x01\xd0\x02\x00\xfe\x00\x00\x00\x00\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00\x17\x00\x1f\x00#\x00+\x00/\x00\x00\x012\x04\x15\x11\x14\x06\a\x17\x16\x06#!\"&?\x01.\x015\x114$3\x02264&\"\x06\x14\x01\x11!\x11\x00264&\"\x06\x14\x01\x11!\x11\x04@\xb9\x01\a\xfb\xb4\xd5\x10\x10\x16\xfb\xe0\x16\x10\x10մ\xfb\x01\a\xb9\xe2\x84^^\x84^\x02@\xfd\xe0\x03\xfe\x84^^\x84^\x01@\xfd\xc0\x06\x00\xbb\x85\xfc\x80\x82\xb8\x05\xca\x0f((\x0f\xca\x05\xb8\x82\x03\x80\x85\xbb\xfa\xe0^\x84^^\x84\x01\xc2\x02\x00\xfe\x00\xfd\xe0^\x84^^\x84\x01\xc2\x02\x00\xfe\x00\x00\x00\x00\x00\x04\x00\x00\xff\x8a\a\x00\x05v\x00\x12\x00\x15\x00\x1c\x00(\x00\x00\x01\x11\x14\x06#\"'%.\x015\x114632\x17\x01\x16\x17\t\x02\x11\x14\x06\"'%\x01\x14\x00\a\t\x01632\x17\x01\x16\x02U\x19\x18\x11\x10\xfe/\x15\x1d\x14\x13\x0e\x1e\x01\xff\x03@\x02\x16\xfd\xea\x04k\x1c0\x17\xfeG\x02\x19\xfd\xff,\xfez\x01D\x11#\x0e\f\x02\x1d\x04\x04[\xfbk\x19#\b\xe9\n/\x17\x04t\x14\x1c\x0f\xff\x00\x03g\xfc\x9e\x01\n\x02F\xfb\xe2\x19\x1f\r\xdc\x03\xe5\x03\xfc\xbfG\x02z\x02\x0f\x1c\x06\xfe\xf2\x02\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x0f\x00\x00\t\x01#\x03\x06\a'\x03#\x01\x113\x01\x11!\x11\x03)\x01\np\x9d\x18\x14*\x9bx\x01\ae\x02\xd7\xfa\x00\x02\x14\x01\xf3\xfe\xc80,\\\x018\xfe\x13\xfe\xbc\x04\xaa\xfa\x00\x06\x00\x00\x00\x18\x00T\xff\x06\b\xa4\x05\xff\x00\v\x00\x17\x00#\x00/\x00D\x00M\x00\xfc\x01\x06\x01\x12\x01\x1b\x01%\x012\x01<\x01G\x01Q\x01^\x01l\x01w\x01\xb3\x01\xc2\x01\xd9\x01\xe9\x01\xfe\x02\r\x00\x00\x05\x0e\x01\a\x06&'&676\x16\x05\x1e\x01\x17\x16676&'&\x067\x1e\x01\x17\x16654&'&\x06\x05\x0e\x01\a\x06&54676\x16\x013\"\a\x1e\x01\x15\x14\x06#\"'\x06\x15\x14\x163264&7.\x01\a>\x02\x1e\x01\x01\x16\a\x16\x15\x16\x0e\x01\a\x06&'\x04%\x0e\x01'.\x01767&76\x1767&76\x1767476\x176\x17\x16\x175\"'.\x01'&767>\x02\x16\x173\x16\x17\x16\x17>\x017&'&'47.\x01'.\x017676\x16\x17\x14\x1e\x03\x17\x16767&\a76767.\x04'$\x01\x16\x17\x1673>\x03?\x01>\x01\x17\x16\x17\x16\x06\a\x0e\x01\a\x15\x06\a\x06\a\x1e\x01\x1767673>\x01\x1e\x01\x17\x16\x17\x16\a\x0e\x01\a\x06#\x14\a676\x176\x17\x16\x15\x16\x176\x17\x16\a\x16\x176\x01\x14\a\x16\x176&'&\x06\a\x1e\x01\a6767.\x01'\x06\a\"'\x16\x17276&\x0567&54&\a\x0e\x01\x17\x16\x17&671&'\x0e\x01\a\x16\x1767\x06\x0f\x015\x06\x17\x16\x05\x1e\x01\x17\x1e\x017>\x017&\x00\"\x06\x15\x14\x162654\x03&\a5\x06\x16\x17\x1e\x017>\x01&\x05>\x01&'5\x06#\x0e\x01\x16\x17\x1e\x01%\x06\x16\x17\x1667>\x017\x06\a\x16\a\x16\x04\x176$7&74>\x01=\x01\x15.\x01'\x06\a\x06'&'&'\x0e\b#\x06'\x0e\x03\a\x06#\x06'\x06'&'&'&'\x06\a\x16\x0365.\x01'&\x0e\x01\x17\x1e\x01\x17\x1667\x16\x1767.\x01'\x06\a\x14\x06\x15\x16\a\x06\a\x06\a#\x06\x17\x16\x17\x04%&'\x06\a\x06'&'\x06\a#\x152%6767\a65&'&'&7&5&'\x06\a\x16\x056.\x01\a\x0e\x01\a\x14\x17\x1e\x017>\x01\x01\xde\b&\x12\x195\x02\x01R\x1b\x17\x16\x054\a&\x13\x195\x01\x02S\x1b\x16\x169\rW\"-J\x870(/\xfar\rV\"-J\x870(.\x02\xc9\x01)#\x1b\"6&4\x1c\x05pOPpp\xe0c\xf3|\x1bo}vQ\x02\xf2\b\x13\a\x01[\x8060X\x16\xfdQ\xfd\xc4\x17W1V\xbb\x01\x02\x05\x13\b\x06\x19\x0e\x1b\a\t\v\x1c\x1d\x1e\r\x17\x1c#\x1a\x12\x14\v\a5X\v\t\t\x0fN\x02\"&\x1c\x05\r.\x0e\x03\x02\n)\n\x0f\x0f\x17D\x01>q\x1c \x15\b\x10J\x17:\x03\x03\x02\x04\a\x05\x1b102(z/=f\x91\x89\x14*4!>\f\x02S\x015b!\x11%\n\x19\x12\x05\x12\x03\x04\x01\x05\x01\v\x06(\x03\x06\x04\x02!\x1f$p8~5\x10\x17\x1d\x01\x1a\x10\x18\x0e\x03\x0e\x02.\x1c\x04\x12.:5I\r\b\x0f\r\b\x0e\x03~\xfe\xf7T\x8a\n\x13\x03\x0e\x18\x0f\x0e\x0e\x1c\x18\x114~9p# !\x02\n\x02)\x05\f\x01\x05\x01\x05\x03\x12\x05\x12\x18\b&\x11 ?()5F\t\x021\x18\x0f\x04\a\x05\x1c\f\t\x1c\x10\x12\r\t\n\x1c\x1e\x15\b\x03\xaf\x1d\x19 d%{\x1d\x13\x04v*\x85:\r \x0e\x0e@e\x10\x0f\n\x01s|\x03D\x861d \x19\x1d\x12\x04\x13\x1d{\x8b\x1f\x0e:\x85*\x06\x0f\x10dA\x11A|o\x04\x0e\x13\x01Yk\x03'&\x8d\x13\x12\a\b\x14\x83<\x02\x02\x83\xa5tu\xa5\xa5ut\xfe&\x02\x02\x01\x1bv\a\x0e\x01\v\x03HC\xba\x04XX\x13\x01\x03\x14TR\x05\x0f\x02\xc8;w\x19\b\x06\x12\x10\x94\x1d\x02\x82\x17\r\x8d\xc671\u0099\r\x15\x02\x03\x03\x01\x01\x01\x02\a\x01Z*&'\x06\b\r1\x05\b\x06\x05\x03\x02\x02\x01\x01\t\x14\x11\x13\v\x03\x02\x01\x119?\t\b.\r\r\x1d$\x06\x04\x02\xfd\x84\x0e\x10Gv\v\f5k65P\x02\x02<\xdc?8q=4\x88a\x04\t\x01\x06\x02\x12\x13\x17\v\r\vSC\"\xcd\x15\x15\x931#\x16\x03\x03\x15\x1c<\x80\x01/6B&!\x01ML\b\x11\t\x18\x14\x12\x04\x05\x04\b\xbe^;\x8c6k5\f\vwF\x10\x0e1<\x02\x02P\x00\x00\x03\x00\x00\xffC\t\x01\x05\xbd\x00\a\x00\x0f\x00;\x00\x00$\x14\x06\"&462\x04\x14\x06\"&462\x01\x1e\x05\f\x0132\x1e\x04\x0e\x03\a\x06\a>\x05.\x03\a\x06$.\a\x05\xf4`\x88aa\x88\xfdsa\x88``\x88\xfdZ9k\x87\x89\xc3\xcd\x01'\x019؋ӗa-\x03*Gl|M\xb9e\x1d_]`F&\fO\x9a\xfe\xb1\xa8\xfe\xdcܽ\x82sDD!/+\x88``\x88aa\x88``\x88a\x051\x0154&'\"&#!\x11!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\a\x9f\x1f\x17\b\n\x99\x99\n\b\x17\r\x1e\x17\x03\f\x8b\x8b\x03\v\x01\x17\xfbi\xe4LCly5\x88)*\x01H\x02\xcacelzzlec0h\x1c\x1c\u007f\xb7b,,b\xb7\u007fe\x03IVB9@RB\x03\x12\x05\xfe9\x01\xebJ_\x80L4\xf8\x004LL4\b\x004L\x0244%\x05\x02\x8c\x02\x05\xaf2\"\x04\x01\x81\x01\x04\xe0\x014\xfe\xcc:I;p\x0f\x10\x01\x01!q4\a\bb\xbab\b\a3p\f\x0f\x02\x02\x06(P`t`P(\x06\x04\x8e6E\x05\x03\bC.7B\x03\x01\xfe\x02I\x036\xfb\x004LL4\x05\x004LL\x00\x00\x05\x00\x00\xff\x80\t\x00\x05\x80\x00\x05\x00\v\x00\x1a\x00.\x00>\x00\x00\x01\x11\x0e\x01\x14\x16$4&'\x116\x00\x10\x02\x04#\".\x0254\x12$ \x04\x014.\x02#!\"\x04\x02\x15\x14\x12\x043!2>\x02\x01\x11\x14\x06#!\"&5\x11463!2\x16\x03Zj\x84\x84\x02b\x84jj\x01[\x9d\xfe\xf2\x9fwٝ]\x9d\x01\x0e\x01>\x01\x0e\x02\x1co\xb8\xf3\x83\xfeӰ\xfeٯ\xae\x01*\xae\x01-\x81\xf5\xb8o\x01XL4\xf8\x004LL4\b\x004L\x01'\x02\xb5)\xbd꽽\xea\xbd)\xfdJ)\x01\xd1\xfe\xc2\xfe\xf2\x9d]\x9d\xd9w\x9f\x01\x0e\x9d\x9d\xfeL\x8b\xf5\xa6`\xa2\xfeֺ\xab\xfe۪e\xa9\xec\x03\x06\xfb\x004LL4\x05\x004LL\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00\x1f\x00;\x00\x00\x05\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x01\x15#54&#!\"\x06\x15\x11\x14\x16;\x01\x15#\"&5\x11463!2\x16\x06\x80\x13\r\xfb\xc0\r\x13\x13\r\x04@\r\x13\x80^B\xfb\xc0B^^B\x04@B^\xfe\x80\x80\x13\r\xfb\xc0\r\x13\x13\r\xa0\xa0B^^B\x04@B^`\x04@\r\x13\x13\r\xfb\xc0\r\x13\x13\x04M\xfb\xc0B^^B\x04@B^^\x01>\xa0\xa0\r\x13\x13\r\xfb\xc0\r\x13\x80^B\x04@B^^\x00\x00\x06\x00\x00\xff\x00\b\x80\x06\x00\x00\x02\x00\x05\x005\x00=\x00U\x00m\x00\x00\t\x01!\t\x01!\x01\x0e\x01\a\x11!2\x16\x1d\x01\x14\x06#!\"&=\x01463!\x11.\x01'!\"&=\x01463!>\x012\x16\x17!2\x16\x1d\x01\x14\x06#\x04264&\"\x06\x14\x01\x14\x0e\x02\".\x0254>\x03762\x17\x1e\x04\x05\x14\x0e\x02\".\x0254>\x03762\x17\x1e\x04\x06\xc0\xfe\x80\x03\x00\xf9\x80\xfe\x80\x03\x00\x01\xb5\x0e?(\x02`\x0e\x12\x12\x0e\xfa\xc0\x0e\x12\x12\x0e\x02`(?\x0e\xfe\x15\x0e\x12\x12\x0e\x01\xeb\x15b|b\x15\x01\xeb\x0e\x12\x12\x0e\xfd?B//B/\x04\x90]\x8e\x93\x84\x93\x8e]Frdh\x04\x12L\x12\x04hdrF\xfb\x00]\x8e\x93\x84\x93\x8e]Frdh\x04\x12L\x12\x04hdrF\x04@\xfd@\x02\xc0\xfd@\x03\x80(?\x0e\xfa\xf5\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x05\v\x0e?(\x12\x0e@\x0e\x129GG9\x12\x0e@\x0e\x12\x10/B//B\xfcaItB!!BtI\v\x8cѶ\xba\a!!\a\xba\xb6ь\vItB!!BtI\v\x8cѶ\xba\a!!\a\xba\xb6ь\x00\x00\x02\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x00M\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06#\x01>\x035!\x14\x1e\x02\x17\x1e\x01\x14\x06\a\x0e\x03\x15!4.\x02'.\x0146\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xfd\x8aM\x90sF\xfc\x00Fs\x90M\x13\x17\x17\x13M\x90sF\x04\x00Fs\x90M\x13\x17\x17\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xfd<\x1d\u007f\xb2\xf2\x84\x84\xf2\xb2\u007f\x1d\a!(!\a\x1d\u007f\xb2\xf2\x84\x84\xf2\xb2\u007f\x1d\a!(!\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x003\x00?\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06+\x01!\x14\x17!6\x114.\x02'#\x0e\x03\x15\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xe0\xfc\x00\t\x03\xee\tDq\x8cL\xe6L\x8cqD\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12B>=\xfaC\x82\xef\xb1\u007f\x1f\x1f\u007f\xb1\xef\x82\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x003\x00;\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06+\x01!\x14\x17!6\x03.\x01'#\x0e\x01\a\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xe0\xfc\x00U\x03VU96\xb7g\xe6g\xb76\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12β\xb2\xfc\x0e\x8d\xc9**ɍ\x00\x00\x02\x00\x00\xff\x00\x06\x00\x06\x00\x00-\x00G\x00\x00\x01\x10\x02\a\x16\x12\x1132\x16\x1d\x01\x14\x06#!\"&=\x0146;\x01\x10\x127&\x02\x11#\"&=\x01463!2\x16\x1d\x01\x14\x06#\x01>\x035!\x14\x1e\x02\x17\x1e\x01\x14\x06\a\x06\a!&'.\x0146\x05\x80\u0560\xa0\xd5`\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e`\u0560\xa0\xd5`\x0e\x12\x12\x0e\x05\xc0\x0e\x12\x12\x0e\xfd\x8aM\x90sF\xfc\x00Fs\x90M\x13\x17\x17\x13\x89k\x02\xbck\x89\x13\x17\x17\x05\x80\xfe\xfb\xfeojj\xfeo\xfe\xfb\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x01\x05\x01\x91jj\x01\x91\x01\x05\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\xfd<\x1d\u007f\xb2\xf2\x84\x84\xf2\xb2\u007f\x1d\a!(!\a3\x91\x913\a!(!\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x0f\x009\x00I\x00\x00\x052\x16\x1d\x01\x14\x06#!\"&=\x014637>\b7.\b'!\x0e\b\a\x1e\b\x17\x132\x16\x1d\x01\x14\x06#!\"&=\x01463\x05\xe0\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0eb\x03\x1a\":1P4Y,++,Y4P1:\"\x1a\x03\x04\xfc\x03\x1a\":1P4Y,++,Y4P1:\"\x1a\x03b\x0e\x12\x12\x0e\xfa@\x0e\x12\x12\x0e@\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12@7hVX@K-A\x1e\x1c\x1c\x1eA-K@XVh77hVX@K-A\x1e\x1c\x1c\x1eA-K@XVh7\x06\x00\x12\x0e\x80\x0e\x12\x12\x0e\x80\x0e\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x00\x00A\x00j\x00\x00\x01\"\x06\x1d\x01#54&#\"\x06\x15\x11'54&#\"\x06\x1d\x01\x14\x17\x01\x16\x15\x14\x163!26=\x0147\x136=\x014&#\"\x06\x1d\x01#54&'&#\"\x06\x1d\x01#54&'&'2\x17632\x16\x17632\x16\x1d\x01\x14\a\x03\x06\x15\x14\x06#!\"&5\x01&=\x014632\x17>\x0132\x176\x03\x005K @0.B @0.B#\x016'&\x1a\x02\x80\x1a&\nl\n@0.B 2'\x0e\t.B A2\x05\bTA9B;h\"\x1b d\x8c\rm\x06pP\xfd\x80Tl\xfe\xccL\x8dc\v\x05\x06\x8b_4.H\x04\x80K5\x80]0CB.\xfeS\x1e\xac0CB.\xe0/#\xfe\xd8'?\x1a&&\x1a\x19)$\x01\xb4$)\xf60CB. }(A\b\x02B.\x80z3M\x05\x01\x802\"61\a\x8fd\xf639\xfeL\x18/PpuT\x01(If\xe0c\x8d\x01_\x82\x15E\x00\x00\x00\x00\x02\x00\x00\xff\x00\x06`\x06\x00\x001\x00X\x00\x00\x00\"\x06\x15\x11#\x114&\"\x06\x15\x19\x01'&#\"\x06\x15\x14\x17\x01\x163!267\x1365\x114&\"\x06\x15\x11#\x114&\"\x06\x15\x11#\x114&2\x16\x17632\x16\x1d\x016\x16\x15\x11\x14\a\x03\x0e\x01#!\"&'\x01&54632\x17\x114632\x176\x03\x9e\\B B\\B\x9a&@5K\x1a\x01\x80&@\x02\xb0\"6\aL\x05B\\B B\\B \xb4\x88s\x1f\x13\x17c\x8di\x97\bL\x0e}Q\xfdP\x01\x03%&#\"\x06\x15\x14\x16\x17\x05\x15!\"\x06\x14\x163!754?\x01\x0327%>\x015\x114&#!\a\x06\x15\x11\x14\x1626=\x013\x15\x14\a\x1e\x01\x15\x14\x06\a\x05\x041\xb1\xa3?\x17>I\x05\xfe\xfbj\x96\x96jq,J[\x96j.-\x02t\x01\x91j\x96lV\xfe\xad\\\x8f\x9b\xa3\x1e$B.\x1a\x14\x01R1?\x01@B.\x1a\x14\xfe\xde\x1c\x12+\x10\x10?2\x14\x12\x01`\x1e$\xe8\xfdv\x18\x165K-%\x02\x0e\xfd\x805KK5\x02\x17\xe9.olRI\x01S+6K5\xfë$B\\B 94E.&\xfeʀ\x8d15\x05\x1euE&\n\x96Ԗ\x11\x1c\x83Pj\x96\x11\xef\x96j\xfddX\x8b\x15U\x17\x02\xc7GJ\x0e7!.B\n\x9a\nP2\xff\x00.B\n\x84\r\b\x1a\x15%\x162@\t\xa0\x0e7\x03\x11\xf8\bK5(B\x0e\xc8@KjKj\xc6?+f\xfc\x00\x13U\vE,\x02\x9c5K~!1\xfe\xd8.>F.\xd0\xd0F,\bQ5*H\x11\x8d\x00\x00\x00\x00\x02\x00\x00\xff\x00\b\x00\x06\x00\x00$\x00b\x00\x00\x012\x16\x17\x01\x16\x15\x11\x14\x06#!\"&=\x01%!\"&=\x01463!7!\"&'&=\x01463\x01\x114'\x01&#!\"\x06\x15\x14\x1e\x01\x17>\x013!\x15!\"\x06\x15\x14\x17\x1e\x013!32\x16\x15\x14\x0f\x01\x0e\x01#!\"\x06\x1d\x01\x14\x163!2\x17\x05\x1e\x01\x1d\x01\x14\x163!26\x04\u007f=n$\x02\x0132\x16\x17\x1b\x01>\x0132\x16\x17\x1e\x01\x15\x14\a\x03>\x0532\x16\x15\x14\x06\a\x01\x06#\x03\"\x06\a\x03#\x03.\x01#\"\x06\x15\x14\x17\x13#\x03.\x01#\"\x06\x15\x14\x17\x13\x1e\x01\x17\x13\x1e\x013!27\x01654&#\"\a\x0554\x1a\x017654&#\"\x06\a\x03#\x13654&\x01\xcbMy\x13e\r\x05t\a|]\x11\x83WS\x82\x14Sg\x14\x82SY\x85\x0e\\x\a{\n7\x160\"1\x19i\x9692\xfe\x05DU1&=\t\xa4\u007f\x91\t=&0@\x03\x84\x1ac\t>&/B\x03t\a\x04\bd\b4!\x02\xb6*\"\x01\xfb8K4+\"\xfe\xcd@H\x03\x04@/'=\tt\x1a\x96\x03?\xff\x00_K\x01\x9193-\x16\x01\xdd\x1b\x1e]\x88\nUlgQ\xfe\xa4\x01\xacQgsW\n\x8a]\x18#\xfe\x00\a+\x10\x1e\v\v\x94i>p&\xfe\x843\x06\x800&\xfdV\x02Z&0B/\x0f\r\xfd\xdd\x01\x98%3B.\x0e\f\xfe\"\x1ct\x1e\xfeo )\x1a\x01{+C4I\x1a\xe6\xe3\x04\x01\f\x01(\r\x12\v/D0&\xfe\x1e\x02p\x0e\x0e0D\x00\x05\x00\x00\xff\x00\x06\x80\x06\x00\x003\x00[\x00_\x00c\x00g\x00\x00\x01\"\x06\x15\x19\x01'&#\"\x06\x15\x14\x17\x01\x163!267\x136=\x014&\"\x06\x15#54&#\"\x06\x1d\x01#54&#\"\x06\x1d\x01#\x114&'2\x16\x1d\x01632\x17632\x17632\x16\x1d\x01\x14\a\x03\x0e\x01#!\"&'\x01&54632\x17\x1146\x13\x11#\x11!\x11#\x11!\x11#\x11\x02\x805K\x97)B4J\x1a\x01\x80&@\x02\xce\x16#\x05\\\x188P8 @0.B J65K J6k\x95\x16\ncJ/4qG\x1b\x1d^\x82\x1c\\\x10hB\xfd2.\xfe\xd81!~K5\x03y\x17?\xa3\xb1^\\\xfe\xadVl\x96j\x01\x91\x02t-.j\x96[J,qj\x96\x96j\xfe\xfb\x05I7$\x1e\xa3\x9b?1\x01R\x14\x1a.B\x87\x10\x10+\x12\x1c\xfe\xde\x14\x1a.B$\x1e\x01`\x12\x142?\x01g\x16\x18\xfdvEo.\xe9\x02\x175KK5\xfd\x80\x02\x0e%-K\xfa\xeb6+\x01SIR[\xfe\xca&.E49 B\\B$\x88\xfe\xcc5K\x00\x00\x00\x00\x02\x00\x00\x00\x00\a\xb4\x04\x00\x00\x19\x00G\x00\x00\x01\x15\x14\x06#!\x11\x14\x06+\x01\"&5\x11!\"&=\x01463!2\x16\x05\x13\x16\a\x06+\x01\"&'\v\x01\x06+\x01\"'\v\x01\x0e\x01+\x01\"'&5\x13>\x01;\x012\x17\x13\x16\x17>\x017\x136;\x012\x16\x03Y\x13\r\xfe\xd6\x12\r\x87\r\x13\xfe\xd7\r\x13\x12\x0e\x03\x19\r\x13\x04\x0eM\x01\t\n\r\x86\f\x12\x01.\xbd\b\x15x\x14\t\xbc-\x01\x12\f\x87\r\n\tN\x01\x12\f\x8e\x14\t\xdc\n\n\x03\r\x04\xdd\t\x14\x8d\r\x12\x03\xe0u\r\x12\xfc\xd4\r\x13\x12\x0e\x03,\x12\ru\x0e\x12\x13\n\xfc?\r\v\n\x11\f\x02L\xfeW\x13\x13\x01\xab\xfd\xb2\f\x11\n\n\x0e\x03\xc1\f\x11\x13\xfd\xf8\x18\x1b\a#\t\x02\b\x13\x11\x00\x00\x00\x00\x04\x00\x00\xff\x00\a\x00\x06\x00\x00\t\x00*\x00:\x00J\x00\x00\x014'&+\x01\x11326\x17\x13\x16\a\x06+\x01\"'\x03#\x11\x14\x06+\x01\"&5\x11463!2\x17\x1e\x01\x15\x14\x06\a\x16\x02 \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02&\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04\x12UbUI\x06-\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5uu\xc5\x01ڎ\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x03AX!\x12\xfe\xe7J\xd9\xfe\x8b\x11\x0e\x10\x11\x01m\xfe\xa2\x0e\x12\x12\x0e\x03\xc0\x0e\x12\x18\x1f\x9cf\\\x93$\n\x036u\xc5\xfe\xf0\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5\xfeK\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x04\x00\x00\xff\x00\a\x00\x06\x00\x00-\x00[\x00k\x00{\x00\x00\x01276/\x01&'&\x0f\x01\x0e\x05#\"&54632\x16\x1f\x01\x1676?\x016'.\x04#\"\x06\x15\x14\x16!276/\x01&'&\x0f\x01\x0e\x05#\"&54632\x16\x1f\x01\x1676?\x016'.\x04#\"\x06\x15\x14\x16\x02 \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02&\x00 \x04\x16\x12\x10\x02\x06\x04 $&\x02\x10\x126\x02]\x99h\x0e\v-\x06\x12\x10\v\x04\x04\x0f\x14\x1b\x1e%\x13Lb`J%E\x10\x10\v\x0f\x10\b5\r\x0f\x03\x10,5R-\x94\xc4\xc2\x03\f\x99h\x0e\n-\b\x11\x10\v\x04\x04\x0f\x14\x1b\x1e%\x13Lb`J%E\x10\x10\v\x0f\x10\b5\r\x0f\x03\x10,5R-\x93\xc5\xc2'\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5uu\xc5\xfd\xa4\x01l\x01L\xf0\x8e\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01/h\x12\x12R\r\x04\x02\r\x03\x04\f\x0f\x0e\f\adMLc\x1c\x0e\x0e\v\x01\x02\fN\x14\x13\x04\x10\x1f\x19\x14\xc1\x90\x92\xbfh\x12\x12R\x0e\x03\x02\r\x03\x04\f\x0f\x0e\f\adMLc\x1c\x0e\x0e\v\x01\x02\fN\x14\x13\x04\x10\x1f\x19\x14\xc1\x90\x92\xbf\x041u\xc5\xfe\xf0\xfe\xd4\xfe\xf0\xc5uu\xc5\x01\x10\x01,\x01\x10\xc5\x01\x15\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x00\x00\x02\x00@\xff\xe0\a\xc0\x05 \x00\v\x00\x17\x00\x00\t\x04\x17\a'\t\x017\t\x03'7\x17\t\x01\a\x01\a\x01\x02\xe0\x01\x80\xfe\x80\xfd`\x02\xa0\xa8`H\xfe \x01\xe0\xc1\xfe\xdf\x02\xa0\x02\xa0\xfd`\xa8`H\x01\xe0\xfe \xc1\x01!`\xfe\x80\x02\xe0\xfe\x80\xfe\x80\x02\xa0\x02\xa0\xa8`H\xfe \xfe \xc1\x01\x1f\x02\xa0\xfd`\xfd`\xa8`H\x01\xe0\x01\xe0\xc1\xfe\xe1`\x01\x80\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\v\x00\x17\x00'\x00\x00%\t\x01\a\x17\a\t\x01\x177'\t\x057'7\t\x01'\a\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x02\xcd\x01\x0f\xfe\xe9X\xc0`\xfe\xe9\x01\x17(W\u007f\xfe:\x03,\x01\xc6\xfe:\xfe\xf1\x01\x17X\xc0`\x01\x17\xfe\xe9(W\x03L\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xb6\x01\x0f\x01\x17X\xbf`\x01\x17\x01\x17(W\x80\xfe:\xfeB\x01\xc6\x01\xc6\xfe\xf1\xfe\xe9X\xbf`\xfe\xe9\xfe\xe9(X\x01\xf9\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\n\x00\x00\xff\xdc\t\x00\x05$\x00\v\x00\x13\x00\x1c\x00%\x00/\x009\x00E\x00S\x00[\x00\x80\x00\x00\x01\x14\x06#\"&54632\x16$\x14\x06\"&462\x054&\"\x06\x14\x1626$4&#\"\x06\x14\x162%\x14\x06#\"&462\x16$\x14\x06#\"&4632\x00\x10\x00#\"\x0e\x01\x14\x1e\x0132\x01&! \a2\x1e\x02\x154>\x02\x00\x10\x00 \x00\x10\x00 \x13!\x0e\x01\a\x16\x15\x14\x02\x04#\"&'\x06\a.\x01'\x0e\x01#\"$\x02547.\x01'!6$32\x04\x02\x8b7&'77'&7\x04\x827N77N\xfc'q\xa0qq\xa0q\x04\x81qPOrq\xa0\xfcE\xa3st\xa3\xa4\xe6\xa3\x04\x82\xa3ts\xa3\xa3st\xfc\xdf\xfe\xf1\xbf}\xd4||\xd4}\xbf\x03\xab\xfe\xfe\xd2\xfe\xc1\xfeuԙ[W\x95\xce\x02Q\xfe\xf2\xfe\x82\xfe\xf1\x01\x0f\x01~\x04\x01\u007f,>\tn\x9a\xfe\xf8\x9b\x85\xe8P/R\vU P酛\xfe\xf8\x9an\t>,\x01m\x95\x01\x9c\xe2\xe0\x01\x8a\x02\x1b'77'&77\x02N77N6^Orq\xa0qq\x01\xa0qq\xa0q\xc0t\xa3\xa4棣\x01棣\xe6\xa3\xfe(\x01~\x01\x0f|\xd5\xfa\xd5|\x04\von[\x9a\xd4usј^\xfd\a\x01~\x01\x0f\xfe\xf1\xfe\x82\xfe\xf1\x04\x043\u007f3\x97\xba\x9c\xfe\xf8\x99pc8{\x16y%cq\x99\x01\b\x9c\xba\x973\u007f3dqp\x00\x03\x00f\xff\x00\x04\x9a\x06\x00\x00\t\x00\x13\x00L\x00\x00\x00 \x0054\x00 \x00\x15\x14\x00\"\x06\x15\x14\x162654\x01\x1e\x01\x0e\x02\a\x06\a\x17\x01\x16\x14\x0f\x01\x06\"'&'\x01\x06\"/\x01&47\x017&'.\x0367>\x02\x16\x17\x1e\x04326?\x01>\x01\x1e\x01\x03<\xfe\x88\xfe\xf6\x01\n\x01x\x01\n\xfe\x96\xb8\x83\x83\xb8\x83\x01,\r\x04\r(-'s\xc8I\x01\v\x1e\x1e\f\x1fV\x1fC\xc8\xfe\xf5\x1fV\x1e\f\x1f\x1f\x01\vH\xcbr'-(\r\x04\r\n$0@!\x05\x14BHp9[\xa6%&!@0$\x02u\x01\n\xbb\xbc\x01\n\xfe\xf6\xbc\xbb\x01\x9b\x83]\\\x83\x83\\]\xfd\xa7\x1b-$)!\x19I\x15H\xfe\xf5\x1fV\x1e\r\x1e\x1eD\xc8\xfe\xf4\x1e\x1e\r\x1eV\x1f\x01\vH\x15I\x19!)$-\x1b\x14\x1e\x0e\x12\x1a\x04\x0e#\x1a\x163\x19\x19\x1a\x12\x0e\x1e\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x006\x00>\x00N\x00\x00\x00\x14\x06\"&462\x01.\x01\x06\a\x0e\x02\"&/\x01.\x01\x06\a\x06\x16\x17\x16\x17\a\x06\a\x06\x14\x1f\x01\x162?\x01\x16\x17\x162?\x0164/\x0267>\x01\x02\x10& \x06\x10\x16 \x01\x11\x14\x06#!\"&5\x11463!2\x16\x03\x9f]\x84]]\x84\x013\n$;\x1f\n&|\x82v\x1b\x1b\x1f;$\n\x16(CS\x8f3\x8e1\x16\x16\t\x16=\x16\xbfrM\x16=\x16\t\x16\x16\xbf4\x8dTC(G\xbe\xfe\xf4\xbe\xbe\x01\f\x02z\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\xfe\x84]]\x84]\xfd\xf6\x14\x18\x05\x19\b\x18($\x12\x12\x19\x05\x18\x14-;,5\x0e4\x8e0\x16=\x16\t\x16\x16\xbfsL\x16\x16\t\x16=\x16\xbe4\x0e5,;\x01\x12\x01\f\xbe\xbe\xfe\xf4\xbe\x01\xe8\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x02\x00\x00\xff\x80\x06\xb8\x05\x80\x00\x12\x00(\x00\x00\x012\x16\x15\x11\x14\x02\x06\x04#\"$&\x025\x11463\x0127\x01654&#\"\a\t\x01&#\"\x06\x15\x14\x17\x01\x16\x06\x1dAZ\x88\xe5\xfe\xc1\xaf\xb0\xfe\xc1\xe6\x88\\@\x02\xc1/#\x01\x94%E1/#\xfe\xbd\xfe\xbd#.1E$\x01\x95!\x05\x80[A\xfd\xf9\xb0\xfe\xc0懇\xe6\x01@\xb0\x02\a@\\\xfb\xd8!\x01\x84#21E!\xfe\xca\x016!E13\"\xfe|!\x00\x00\x00\x01\x00\x00\xff\x98\t\x00\x05g\x00L\x00\x00\x05\x01\x06\x00\a\x06&5&\x00'.\x02#4&5!\x15\x0e\x02\x17\x16\x00\x176\x127&\x02'&'5\x05\x15\x0e\x01\x17\x1e\x01\x17676&'6452>\x013\x15\x0e\x01\a\x03\x16\x12\x17\x01.\x02'5\x05\x17\a\x06\a\x00\a\x05\xd6\xfe\xd9\x19\xfe\xf5A\x015R\xfe\xa5V\x15[t,\x01\x02G'Q4\x10\x1a\x01}-\x1f\xda\x16\x13\xd6\x1d&\xa3\x02\x01r!\xd5\r\xe5\a\x01\xb9\x0eG;\x1a\x01\xcc\x01\x01\x8b>\xfd\xf2!g\x02\xb71\xfd\xff\x85\x01\x01\x01\xc1\x03\x14\xca2sV\x05&\b2\x02\x1c:#;\xfc\x90d=\x01\x9b*'\x01\xe45E\x022\x01/\x02..F\xefD֕71\x02\a$\x06\x01\x011\x02>2\xfeF!\xfd\xfe\x11\x03\xf9&1\x0e\x012\x04\x02,\x04\x8d\xfb@K\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00\n\x00\x18\x00r\x00\x82\x00\x92\x00\x00\x01\x14\x06#\"&5462\x16\x17\x01\x0e\x04\a\x01>\x04%\x14\a.\x02#\"\x15\x14\x17\x0e\x01\a'&#\"\x06\x1f\x01\x06#\"'>\x0254#\"\x0e\x01\a.\x01'7654&\x0f\x01&547\x1e\x023254&/\x01>\x017\x17\x16326/\x01632\x17\x06\x15\x14327\x1e\x01\x17\a\x06\x15\x14\x16?\x01\x1e\x01\x10\x02&$ \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x03\xb5!\x19\x1a&\"2&\x0f\x01^\tu\x86\x8b_\x03\xfe\xa3\ax\x84\x8c^\x02\x8ah\x03\x1c\x19\x04\r;J݃\x10\x01\x0e\x05\x06\x01\x10HJǭ\x01\x18\x13\r\x06\x16\x17\x02q\x9e\x1fE\n\v\x05D\x0em\x02!\x1b\x04\r\x19\x14\x14M\xe0\x84\x0f\x02\r\x05\x06\x01\x0fG?̯'\f\v%o\x99\x1f8\n\v\x049\x0eU\u007f\xd6\xfe\xd8\xfe\xba\xfe\xd8\xd6\u007f\u007f\xd6\x01(\x01F\x01(\xd6ߎ\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x02\x83\x1a&!\x19\x1a&!S\x02E\bm|\x82[\x06\xfd\xbc\an{\x83[<ɪ\x02\x12\x0f\r\n\"p\x9d C\n\v\x04D\x0fi\x02%\x1e\x04\r\x1d(\x03K\xe1\x84\x0f\x03\f\x05\x06\x01\x0fHCέ\x01\x16\x10\f\x06\x13\f\fp\x9a\x1eC\n\v\x05B\rm8\t\r@Kނ\f\x02\x0e\x05\x06\x01\rH\xe7\x01F\x01(\xd6\u007f\u007f\xd6\xfe\xd8\xfe\xba\xfe\xd8\xd6\u007f\u007f\xd6\x02\x81\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x04\x00\x00\xff\x01\a\x00\x06\x00\x00\v\x00\x16\x00\"\x00*\x00\x00\x016\x17\x16\x17%&\x04\a\x016$\t\x01\x16\x047\x03&$\x025\x10%\x16\x12\x02\x06\a\x06%\x016\x02'$2\x16\x14\x06\"&4\x03}\xf0\xd3\xe8x\xfd\x1a\xa0\xfe\xf43\xfe\xec\x80\x01n\xfd\xdd\x01QH\x01\x16\x9a\xe6\xd4\xfe\xa6\xc7\x06\xc4:\x03dΏ\xe6\xfe\xf4\x01\x95X\ve\xfe8\xfa\xb1\xb1\xfa\xb1\x06\x00\x02z\x86\xee'\t\xa7\x92\x01\xa8\x9f\xad\xfel\xfdi\x8f\x94\x1d\xfe=!\xf9\x01\u007f\xdc\x01\v7\x96\xfe\xbf\xfe\xdd\xfdS\x85\x0e\x02o\x83\x01?v\x06\xb1\xfa\xb1\xb1\xfa\x00\x00\x01\x00\x02\xff\x00\a\x00\x05\xc9\x00M\x00\x00\x01 \x00'&\x02\x1a\x017\x03>\x01\x17>\x017\x0e\x01\x17\x1e\x03\x17\x16\x06\a\x0e\x02\a\x17'\x06\x1e\x027>\x02\x17\x1e\x01\a\x0e\x04'\x0e\x01'\x1e\x01>\x0276.\x01'\x1e\x01\x176\x02'\x04\x00\x13\x16\x02\x0e\x01\x04\x03\x87\xfe\xe5\xfeEl:\x12F\x98g\v\vr\r*\xedt6\x83\a\x19K3U\b\x0f\v\x19\x05\x17Z8\x0f\x8b\x12\x153P)3^I%=9\t\x01\x03\x0e\x16)\x1a<\xa9}J\xb1\xa0\x95k\x1b+\bC-Wd\x1b\x0f\x91\x89\x01\t\x01&\x04\x02U\xa2\xd8\xfe\xe9\xff\x00\x01-\xf8\x83\x01T\x01E\x01+]\xfe\xe7\x0e\x03\x11Qr\x02-\xcf<\b\v\x04\x04\x01\x05Q#\a\x170\n\xbdC+M8\x1b\a\t3'\x02\x04:$\x02\a\x12\r\b\x03_Q\v=+\x1fIf5[ˮ&&SG\xaa\x01ZoM\xfek\xfe\xc5\u007f\xff\x00ܬc\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00#\x007\x00\x00\x01&#\"\x04\a\x0e\x01\a\x15\x1e\x01\x17\x16\x04327\x06\x04#\"'&$&\x0254\x126$;\x01\x16\x04\x01\x14\x02\a\x06#\"'6\x1254\x02'632\x17\x16\x12\x05ե\u009b\xfe\xecfKY\x04\x04YKf\x01\x14\x9b¥y\xfeͩ\x1d\x0e\xaf\xfe\xc4䆎\xf0\x01L\xb6\x03\xa8\x011\x01\xa4\x9a\x88hv\x89v\x9a\xc7ƚw\x87wk\x87\x97\x05\x1cn\x92\u007f]\xfa\x8d*\x8d\xfa]\u007f\x92nlx\x01\b\x94\xee\x01D\xb1\xb6\x01L\xf0\x8e\x01w\xfc\xf8\xc0\xfe\xab~?T8\x01b\xe4\xe3\x01b9SA}\xfe\xac\x00\x00\x00\x04\x00\x00\xff\x10\a\x00\x05\xf0\x00+\x005\x00?\x00F\x00\x00\x01\x14\a!\x14\x163267!\x0e\x01\x04#\"'\x06#\"\x114767\x12%\x06\x03\x12\x00!2\x17$32\x1e\x02\x15\x14\a\x16\x034&#\"\a\x1e\x01\x176\x01\x14\x16327.\x01'\x06\x01!.\x01#\"\x06\a\x00\a\xfb\x81۔c\xad2\x01\xa78\xe5\xfeΨ\xbb\xa9\xe4\xa6\xed-\x11\\\xc7\x01\x14\xb8\xf3?\x01\xb9\x01\x19\x1e\x0f\x00\xff\xb2@hU0KeFjTl\x92y\xcbE3\xf9\xc6aVs\x97z\xb7.b\x01\xf8\x02\xd8\x05؏\x90\xd7\x02W80\x92\xc5]T\x9f\xf4\x85St\x01\as\xa0<\xa9\x01h\xf6O\xfe\xed\x01\x12\x01_\x01u\x1a7bBt\xaa\xb6\x01\xb0SbF/\xa9o\x87\xfb|V]SHކ\xcd\x02J\x8e\xbe\xbe\x00\x00\x00\x00\x02\x00\x00\xff\x80\a\x80\x05\x80\x00\x0f\x003\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x13\x11\x14\x06#!\x15!2\x16\x1d\x01\x14\x06#!\"&=\x01463!5!\"&5\x11463!2\x16\a\x00\x13\r\xf9\xc0\r\x13\x13\r\x06@\r\x13\x80^B\xfd \x01`\x0e\x12\x12\x0e\xfc\xc0\x0e\x12\x12\x0e\x01`\xfd B^^B\x06@B^\x01 \x03\xc0\r\x13\x13\r\xfc@\r\x13\x13\x03\xcd\xfc@B^\x80\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x80^B\x03\xc0B^^\x00\x00\x00\x00\x02\x00\x16\xff\x80\x06\xea\x05\x80\x00\x17\x00>\x00\x00\x133\x06\a\x0e\x03\x1e\x01\x17\x16\x17\x16\x17\x16\x17!\"&5\x1146)\x012\x16\x15\x11\x14\x06+\x016\x03\x05\x0e\x03\a\x06'.\x02'.\x0167>\x0176\x1e\x03\x17%&\x8a\xc5F8$.\x0e\x03\x18\x12\x13\x04\x023\x1e9_\xfe\xf00DD\x04\xe8\x0140DD0\xb2\xd4\x10\xfe+\x02\x14*M7{L *=\"#\x15\n\x12\x14U<-M93#\x11\x01\xd4D\x05\x80@U8v\x85k\x9d_Y\x13\t\xee[\xabhD0\x05\x180DD0\xfa\xe80D\xd2\x01ce-JF1\f\x1aB\x1bD\xbe\xa3\xa3\xc8N&)@\r\f\v\x17/1 d\xaf\x00\x00\x00\x00\x04\x00\x0e\xff\x00\x05y\x06\x00\x00%\x00F\x00\xab\x00\xc5\x00\x00\x05\a\x06\a\x06#\"'&'&'&'&76\x17\x16\x15\x16\x17\x16\x17\x16\x17\x163276?\x016\x17\x16\x17\x16\x01\a\x17\x16\a\x06#\"/\x01\a\x06#\"/\x01&54?\x01'&7632\x1f\x0176\x17\x16\x05\x14\a\x06\a\x0e\x01\"&'&'&5#&76\x17\x16\x173\x11567632\x16\x15\x14\x06#\"'&76\x1f\x01\x1e\x0132654'&#\"\a\x06\x15\x11\x1632>\x0254'&#\"\a\x06\x0f\x01\x0e\x02'.\x015\x11463!2\x14#!\x113>\x017632\x16\x17\x16\x17\x16\x03\x16\x14\x06\a\x06#\"'&'&#\"\a\x06'&767632\x17\x16\x05y\x06q\x92\x9a\xa3\xa5\x98\x94oq>*\f\x0443\x05\x01\x12\x1c2fb\x80\x84\x90\x8f\x85\x80a\x06\n\x0f\f\x15$\xfe\x15B?\x15\x1c\x11\x0f\n\t>B\x05\n\x0f\x10\x02\x12\bBB\x10\x1e\x12\r\x06\aAA\x12\x1e\x1b\x01\xc7.-QP\xd6\xf2\xd6PR+\x0f\x01\t42\n%<\x01\x03ci\x94\x93\xd0ђ:6\x1c\x0f\x10\x1c\x0e\x0e&\vh\x90HGhkG@n\x84`\xb2\x86I\x8d\x8c\xc7Ȍ5\x18\x02\b\n!\x16\x15\x1f\x15\x11\x03m\x1e\x1e\xfc\xd5\x01(|.mzy\xd6PQ-.\x1f\t\v\v\x1a\r\t\aje\x80\x94\x85\x81\x1b\x12\t\x01\x03\r\x82\xa9\xa4\x98\x89\v\x06q>@@?pp\x92gV\x1c\b\b\x1c\x01\x03ZE|fb6887a\x06\n\x04\x03\x13%\x02RB?\x15\x1c\x11\n=B\x05\x10\x02\x0f\x0e\a\nAB\x10\x1d\x12\x05BA\x11\x1e\x1bJvniQP\\\\PRh!\a\x1b\x11\x10\x1ccD\x01S\x02\x88`gΒ\x93\xd0\x10\v23\b\x03\x03\x06\x8fgeFGPHX\xfecCI\x86\xb0_ƍ\x8c\x8c5\"\x02\v\t\n\b\x05\x17\x0f\x02\xa8\x0f\x17n\xfe\x1d*T\x13.\\PQip\x01\xd0\b\x14\x10\r\x1a\a[*81\n/\x19\r\x10\x049@:\x00\x00\x04\x00\x1d\xff\x00\x06\xe1\x06\x00\x00\x1b\x00>\x00t\x00\x82\x00\x00%6\x16\x14\a\x0e\x04#\".\x03'.\x01>\x01\x16\x17\x16\x17\x04%6%\x16\x06\a\x06\a\x06&7>\x01'.\x03\x0e\x02#\x0e\x03*\x02.\x01'&676\x16\x01\x14\x1e\x02\x1f\x01\a.\x01/\x01&'\x0e\x03.\x0254>\x05754'&#\"\x0e\x03\a%4>\x0332\x1e\x03\x15\x01\x14\x17\x167676=\x01\x0e\x03\x06\x0f\x0f\x16\x0f\r>\x81\x99\xdfvw\ued25d\"\b\x04\x06\n\r\x05\xc0l\x01\x85\x01\x9a\xbe\x01\x98\v\x11\x14\"3\x11\x12\t\x15/\x11\x05\x15!\x1a,\x13+\x01\x06\x0e\b\t\x05\x06\x03\x03\x01\x01\x06j2.|\xfe\x84\x1b%&\x0e\r\xe3(N\x13\x13\v\x0e&w\x88\x90\x83h>8X}x\x8cc2\x15\"W\x06\x15<4<\x12\xfe\xda,Z~\xb1fd\xa2aA\x19\xfd`FBIT\x1e\x0e;hmA<\x06\x06\x1d\x13\x107QC1>[u])\t\x0f\t\x05\x01\x04u1\xb0V(\xd2\x10k1S)\x0e\n\x13-\x99\x16\a\t\x03\x02\x02\x02\x04\x01\x01\x01\x01\x01\x02\x02\x100\x06\a\f\x01\xa9\x1fB2*\v\v\xe0%M\x14\x14\v\x16;W(\x060S\x8f[T\x8c]I)\x1c\t\x02\u007fA 5\x02\x16%R7\x1b&\x1a\x80\x1a&T\x01\xa8\x01,\xfe\xd4\xfeX\xfe\xd4\x02\x00\x0e\x12\x12\x0e\x92\xce\x12\x1c\x12\xa9\x01\xc0\x0f\xfdq\x1a&&\x1a\x02\x8f\x041\xfe\xd4\xfeX\xfe\xd4\x01,\x01\xa8L\x12\x1c\x12Β\x0e\x12\x12\x0ew\xa9\x00\x00\x00\x00\x03\x00%\xff\x00\x06\xdb\x06\x00\x00\x1b\x00%\x00;\x00\x00\x01\x16\x14\x0f\x01\x06#!\"&5\x11463!546;\x012\x16\x1d\x01!2\x17\x01!\x11\x14\x06+\x01\"&5\x012\x16\x15\x11\x14\x06#!\"/\x01&4?\x0163!5!\x15\x06\xd1\n\n\x8d\x1c(\xfa\xc0\x1a&&\x1a\x02@&\x1a\x80\x1a&\x02\x00(\x1c\xfc\xbc\x01\x00&\x1a\x80\x1a&\x03@\x1a&&\x1a\xfa\xc0(\x1c\x8d\n\n\x8d\x1c(\x02\x00\x01\x00\x04\xd7\n\x1a\n\x8d\x1c&\x1a\x01\x00\x1a&@\x1a&&\x1a@\x1c\xfb\xdc\xfe\x00\x1a&&\x1a\x03\xc0&\x1a\xff\x00\x1a&\x1c\x8d\n\x1a\n\x8d\x1c\xc0\xc0\x00\x04\x00\x00\xff\x00\b\x00\x05\xfb\x00\x1b\x00\x1f\x00#\x00'\x00\x00\x01\x16\x15\x11\x14\x06\a\x01\x06'%\x05\x06#\"'&5\x11467\x016\x17\x05%6\x05\x11\x05\x11%\x11%\x11\x01\x11\x05\x11\a\xe4\x1c\x16\x12\xfd\x80\x18\x18\xfd\x98\xfd\x98\n\x0e\x13\x11\x1c\x16\x12\x02\x80\x18\x18\x02h\x02h \xfb\x18\x02@\xfb`\x02 \x04\xe0\xfd\xe0\x05\xf5\x14!\xfa\x80\x14 \a\xff\x00\v\v\xf6\xf6\x05\v\x14!\x05\x80\x14 \a\x01\x00\v\v\xf6\xf6\r\x9a\xfb\n\xe6\x04\xf6\r\xfb\n\xd9\x04\xf6\xfa\xfd\x04\xf6\xd9\xfb\n\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x11\x00#\x005\x00\x00\x012\x16\x15\x11\x14\a\x01\x06#\"&5\x1147\x016!2\x16\x15\x11\x14\a\x01\x06#\"&5\x1147\x016!2\x17\x01\x16\x15\x11\x14\x06#\"'\x01&5\x1146\x02\x00\r\x13\x11\xfe \a\b\r\x13\x11\x01\xe0\a\x04\xe8\r\x13\x11\xfe \a\b\r\x13\x11\x01\xe0\a\xfb\xa8\b\x06\x02\x00\x12\x13\r\b\x06\xfe\x00\x12\x13\x06\x00\x13\r\xfa@\x14\b\xff\x00\x04\x13\r\x05\xc0\x14\b\x01\x00\x04\x13\r\xfa@\x14\b\xff\x00\x04\x13\r\x05\xc0\x14\b\x01\x00\x04\x03\xff\x00\n\x13\xfa@\r\x13\x03\x01\x00\n\x13\x05\xc0\r\x13\x00\x00\x00\x00\x04\x00\x00\xff \a\x00\x05\x00\x00\a\x00\x0f\x00\x17\x008\x00\x00\x004&\"\x06\x14\x162$4&\"\x06\x14\x162$4&\"\x06\x14\x162\x00\x10\x02\x04#\"'\x06\x05\x06\a\x06&'&7>\a7.\x0154\x12$ \x04\x02\x80KjKKj\x01\xcbKjKKj\x01\xcbKjKKj\x01\xcb\xf0\xfed\xf4ne\xad\xfe\xfa4\"\f\x14\x03\x04\x18\x05%\x0e!\x0f\x1a\x0e\x0f\x05\x92\xa7\xf0\x01\x9c\x01\xe8\x01\x9c\x02KjKKjKKjKKjKKjKKjK\x01.\xfe\xa4\xfe٫\x12\xad8\n\x03\x01\x0e\v\x0f\x16\x05!\x0e%\x1a00C'Z\xfd\x8f\xae\x01'\xab\xab\x00\x00\x00\x00\x05\x00\x00\xff\x00\a\x00\x05\x00\x00\a\x00\x0f\x00\x17\x00.\x00W\x00\x00\x00\x14\x06\"&462\x04\x14\x06\"&462\x04\x14\x06\"&462\x02 \x04\x06\x15\x14\x16\x1f\x01\a\x06\a6?\x01\x17\x1632$6\x10&\x01\x14\x02\x04#\"'\x06\x05\x06\a#\"&'5&6&>\x027>\x057&\x0254>\x01$ \x04\x1e\x01\x02\x80KjKKj\x01\xcbKjKKj\x01\xcbKjKKj\xe9\xfeh\xfe\x9dя\x82W\x1b\x18.\x98{+9E=\xcc\x01c\xd1\xd1\x01Q\xf0\xfed\xf4FK\xc6\xfe\xfa1A\x05\x0f\x18\x04\x03\x05\x01\n\x02\f\x02\a0\x15)\x18\x1e\v\x9d\xb5\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x02\xb5jKKjKKjKKjKKjKKjK\x01\x80\x8b\xec\x89p\xcbJ2`[Q?l&\x06\b\x8b\xec\x01\x12\xec\xfe\x8b\xae\xfe٫\b\xafC\x0e\b\x15\x11\x01\x04\x10\x04\x0f\x03\x0e\x02\b5\x178.H(Y\x01\x06\x96\x82\xed\xacee\xac\xed\x00\x04\x00\x00\xff\t\x04\x00\x05\xf7\x00\x03\x00\x06\x00\n\x00\r\x00\x00\t\x01\x11\t\x01\x11\x01\x19\x01\x01\x11\t\x01\x11\x02\x00\x02\x00\xfe\x00\xfe\x00\x02\x00\xfe\x00\x02\x00\x02\x00\x01Y\x01'\xfd\xb1\xfe\xd8\x03w\xfd\xb1\x01(\x04\x9e\xfd\xb1\xfe\xd8\x02O\xfe\xd9\x01'\xfd\xb1\x00\x00\x00\x01\x00R\xff\xc0\x06\xad\x05@\x00$\x00\x00\x01\x06\x01\x00#\"\x03&\x03\x02#\"\a'>\x017676\x16\x17\x12\x17\x16327676#\"\a\x12\x05\x16\x06\xad\n\xfe\xbe\xfe\xb3\xe5\x8eb,XHU\x12mM\x18\xa8.\x9cU_t\x17,\x167A3ge\b\rz9@x\x01S\xfb\x03\xfa\xec\xfea\xfeQ\x01\a\xa0\x01B\x01\x06Lb\x15\x97(\x8a\b\t\x81\x8b\xfe\xe1V\xf9\xa1\xa1U\x8b\x1a\x01\x89\v\b\x00\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\n\x00\x00\x11!\x11!\x01\x03\x13!\x13\x03\x01\x06\x00\xfa\x00\x04=\xdd\xdd\xfd\x86\xdd\xdd\x01=\x05\x80\xfa\x00\x01\xa5\x02w\x01)\xfe\xd7\xfd\x89\xfe\xd0\x00\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\x12\x00A\x00U\x00\x00\x11!\x11!\x01\a\x17\a\x177\x177'7'#'#\a\x052\x16\a74.\x02#\"\x06\x1d\x01#\x1532\x15\x11\x14\x06\x0f\x01\x15!5'.\x02>\x015\x1137#\"76=\x014>\x02\x015'.\x01465\x11!\a\x17\x16\x15\x11\x14\x06\x0f\x01\x15\x06\x00\xfa\x00\x03\x8c\fK\x1f\x19kk\x19\x1fK\f_5 5\xfe\x96 \x19\x01\xae#BH1\x85\x84`L\x14\n\rI\x01\xc0\x95\x06\x05\x02\x01\x01\xbf&\xe7\x06\x04\x04\x03\f\x1b\x02v6\a\x05\x02\xfe\xed\x17S\x17\f\x0eF\x05\x80\xfa\x00\x04\xc0!Sr\x1999\x19rS!``\xa3 /\x157K%\x0es}H\x80\b\xfe\x82\x0e\f\x01\aXV\x0e\x01\x01\x04\x04\n\x05\x01\x83\x80\x06\x06\x03P\x1b\x1b\x1d\v\xfc\xc3V\t\x01\x03\x03\f\x06\x02\be\x16\a\x14\xfe\x8e\x0e\t\x02\tV\x00\x00\x04\x00\x00\xffd\a\x00\x06\x00\x00/\x009\x00Q\x00[\x00\x00\x01\x14\x06\a\x16\x15\x14\x02\x04 $\x02547.\x0154632\x176%\x13>\x01\x17\x05>\x0132\x16\x14\x06\"&5%\x03\x04\x17632\x16\x01\x14\x16264&#\"\x06\x0164'&\"\a\x0e\x01\"&'&\"\a\x06\x14\x17\x1e\x022>\x01&2654&#\"\x06\x14\a\x00;2\f\xd5\xfe\x90\xfeP\xfe\x91\xd5\v3>tSU<\xda\x01)t\x03\x18\x0e\x01q\x12H+>XX|W\xfe\xb2h\x01,\xdb:USt\xfa\xa2W|XX>=X\x03*\v\v\n\x1e\v)\xa0\xa0\xa0)\v\x1e\n\v\v+\x97^X^\x97\x16|WX=>X\x02\xb2:_\x19.2\x9b\xfe\xf8\x99\x99\x01\b\x9b//\x19a:Ru?\x98\n\x02\t\r\x10\x03Q%-W|XW>J\xfe(\t\x97=u\xfe\xe7>XX|WX\xfe`\v\x1e\v\n\n*((*\n\n\n\x1f\v+2\t\t2\xf8X>=XW|\x00\x00\x00\x01\x00E\xff\x02\x06\xbb\x06\x00\x000\x00\x00\x133>\x03$32\x04\x17\x16\x1d\x01!\x1e\x03>\x017\x11\x06\f\x01'&\x02'&\x127\x0e\x01\a!6.\x04/\x01\x0e\x03E\x01\x10U\x91\xbe\x01\x01\x94\xe7\x01noh\xfb\x9b\x01i\xa8\xd3\xd7\xc9I\\\xfe\xed\xfe\xa2\x8d\xbd\xf5\x02\x03\xe4\xd30<\x10\x02{\b >ORD\x16\x16\x87\xf9ƚ\x02\xe5~\xe7˕V\xd3ƻ\xff\xbco\xa3R \x1aC3\xfe\x877J\x026I\x01`\xc4\xf2\x01Tb<\x83^M~M8\x1a\x0f\x01\x01\x05O\x82\x97\x00\x00\x00\x04\x00\x00\xff\x80\t\x00\x05\x80\x00\t\x00\r\x00\x11\x00\x1b\x00\x005\x11!\x11\x14\x06#!\"&\x01\x15!5!\x15!5\x012\x16\x1d\x01!5463\t\x00^B\xf8@B^\x02\x80\x01\x80\xfd\x00\x01\x00\x06`B^\xf7\x00^B \x02`\xfd\xa0B^^\x01\"\x80\x80\x80\x80\x04\x80^B\xe0\xe0B^\x00\x00\x00\x03\x00\x00\xff\x00\x06\xbb\x06\x00\x00\x1f\x000\x00;\x00\x00%'\x0e\x01#\".\x0154>\x0232\x16\x177&$#\"\x04\x06\x02\x10\x12\x16\x0432$\t\x01\x06\x00!\"$&\x02\x10\x126$3 \x00\x17\x03#\x15#\x1132\x1e\x01\x0e\x01\x060\xdaJ\xf5\x8d\x93\xf8\x90U\x91\xc7n\x83\xe9L\xd7n\xfe\x9fʡ\xfe\xda\xd4~~\xd4\x01&\xa1\xd5\x01q\xfe@\x02\xb5t\xfeK\xfe\xee\xb6\xfe\xb4\xf0\x8e\x8e\xf0\x01L\xb6\x01\x04\x01\xa5}\x9f'`\x88 -\f\n-\xf6ox\x8a\x90\xf8\x92nǑUyl}\xa9\xc0~\xd4\xfe\xda\xfe\xbe\xfe\xda\xd4~\xd6\x02F\xfe\xa0\xfd\xfeڎ\xf0\x01L\x01l\x01L\xf0\x8e\xfe\xf5\xe9\xfet\xa0\x01`(88(\x00\x04\x00 \xff\x00\x06\xe0\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x00\t\x017!\x01'\x11\x01\x1f\x01\x11\t\x02!\x01\x05\x93\xfd\x9a\\\x03W\xfa\xb5\xb8\x04\x9f\x14\x93\xfd\xec\x01\\\xfe\f\xfc\xa9\x01d\x03;\x01\x82\x97\xfc\xdet\x03Z\xfd\x19`_\xfc\xa6\x01O\x02\u007f\xfc\xde\x02;\x00\x00\x03\x00\x00\xff\x00\x06\x80\x05\xf0\x00\v\x00\x17\x00}\x00\x00\x0154+\x01\"\x1d\x01\x14;\x012%54+\x01\"\x1d\x01\x14;\x012\x05\x11!\x114&\"\x06\x15\x11!\x114;\x012\x1d\x013\x114;\x012\x1d\x01354;\x012\x1d\x01354>\x02\x163\x11&5462\x16\x15\x14\a\x15632\x1632632\x1d\x01\x14\x06#\"&#\"\a\x1526\x1e\x02\x1d\x01354;\x012\x1d\x01354;\x012\x15\x11354;\x012\x02\x80\x10`\x10\x10`\x10\x02\x00\x10`\x10\x10`\x10\x02\x00\xfd\x80p\xa0p\xfd\x80\x10`\x10\x80\x10`\x10\x80\x10`\x10\x80\x05\f\a\x10\x01 !,! -&\x15M\x10\x11<\a\x10F\x1b\x12I\x13(2\x01\x10\a\f\x05\x80\x10`\x10\x80\x10`\x10\x80\x10`\x10\x02\x10\xe0\x10\x10\xe0\x10\x10\xe0\x10\x10\xe0\x10\x10\xfd\x10\x01@PppP\xfe\xc0\x02\xf0\x10\x10p\x02p\x10\x10pp\x10\x10pp\x06\a\x03\x01\x01\x01\x87\x0f#\x17 \x17#\x0f\x11\n\x0f\x0f\x10\xd2\x0f\r\x0f\f\x85\x01\x01\x03\a\x06pp\x10\x10pp\x10\x10\xfd\x90p\x10\x00\x01\x00\x00\x00\x00\t\x00\x05\x80\x00j\x00\x00\x01\x16\x14\a\x05\x06#\"'&=\x01!\x16\x17\x1e\x05;\x015463!2\x16\x15\x11\x14\x06#!\"&=\x01#\".\x05'.\x03#!\x0e\x01#\"&4632\x16\x1732>\x027>\x06;\x01>\x0132\x16\x14\x06#\"&'#\"\x0e\x04\a\x06\a!546\x17\b\xf0\x10\x10\xfe\xc0\b\b\t\a\x10\xfc\xa6%.\x10\x11\x1f\x17\x1f \x11`\x12\x0e\x01@\x0e\x12\x12\x0e\xfe\xc0\x0e\x12` :,.\x1c'\x12\x13\x17\x1c,-\x18\xfe\x98\x16\x8aXj\x96\x96jX\x8a\x16h\x18-,\x1c\x17\x13\x12'\x1c.,: k\x15b>PppP>b\x15k\x11 \x1f\x17\x1f\x11\x10.%\x04Z \x10\x02\xdb\b&\b\xc0\x05\x04\n\x12\x80:k%$> $\x10`\x0e\x12\x12\x0e\xfe\xc0\x0e\x12\x12\x0e`\x14\x1b6&L')59I\"Tl\x96ԖlT\"I95)'L&6\x1b\x149Gp\xa0pG9\x10$ >$%k:\x80\x12\x14\v\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\a\x00\x11\x00!\x00\x00\x00\x14\x06+\x01\x1132\x00\x10&#!\x113\x1132\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04~O8\xfd\xfd8\x01\x02\xb7\x83\xfeO\xb4\xfd\x82\x02\x87\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x03>pN\x01\r\xfe\xf7\x01\x04\xb8\xfc\x80\x01\r\x01i\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x04\x00\x00\xff\xd9\t\x00\x05'\x00'\x00:\x00M\x00a\x00\x00\x014&'\x06\a\x0e\x01#\"'.\x017654.\x01#\"\x06\a\x16\x17\x16\x14\x06\"'&#\"\x06\x14\x163!267\x14\x06#!\"&54676$32\x00\x17\x1e\x01\x17\x14\a\x06#\"'.\x0176\x10'&>\x01\x16\x17\x16$\x10\a\x06#\"'.\x017654'&676\x16\x17\x06mD5\a\x10\a)\x18\f\f\x1f\x1c\n\x17z\xd2{\x86\xe26lP\x16,@\x17Kij\x96\x96j\x04\x16Oo\x99Ɏ\xfb\xea\xa9\xf0ȕ>\x01>\xc3\xeb\x01[\x17t\x99\xfaa\x17)\x18\x13\x1a\f\x12GG\x12\f4?\x12a\x01\x00\x86\x17)\x17\x13\x1a\r\x12ll\x12\r\x1a\x1a>\x12\x01\xb6;_\x15-/\x18\x1c\x03\n9\x1eGH{\xd1z\x92y\x1cN\x17@,\x16K\x95ԕoN\x8e\xc8繁\xe4\x16\xb8\xe4\xfe\xc3\xe7\x19\xbby\xaf\x90!\r\x11?\x1ah\x01\x02h\x1a>$\r\x1a\x8eD\xfe\x18\xc7\"\r\x12>\x1a\xa4\xc2â\x1a?\x11\x12\f\x1b\x00\x02\x00$\xff\x00\x05\xdc\x06\x00\x00\t\x00n\x00\x00\x05\x14\x06\"&5462\x16'\x0e\x01\x15\x14\x17\x06#\".\x0554>\x032\x1e\x03\x15\x14\a\x1e\x01\x1f\x012654.\x04'&'.\x0354>\x0332\x1e\x03\x15\x14\x0e\x03#\"#*\x01.\x045.\x01/\x01\"\x0e\x01\x15\x14\x1e\x03\x17\x1e\b\x05\xdc~\xb4\u007f\u007f\xb4~\xe9s\x9b!\x92\xe9m\xb8{b6#\f\t\x1c-SjR,\x1b\b\x17\x1cl'(s\x96\x12-6^]I\x1c\x0ft\x8eg))[\x86\xc7zxȁZ&\x1e+6,\x11\x02\x06\x13\x1a4$.\x1c\x14\x0fX%%Dc*\n&D~WL}]I0\"\x13\n\x02\rY\u007f\u007fYZ\u007f\u007f\xbf\x0f\xafvJ@N*CVTR3\x0e\x13/A3$#/;'\x0e\"/\x1b\x1e\x02\x01fR\x1a-,&2-\"\r\a7Zr\x89^N\x90\x83a94Rji3.I+\x1d\n\n\x12&6W6\x10\x13\x01\x01>N%\x18&60;\x1d\x1996@7F6I3\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1f\x00+\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26%\x114&#!\"\x06\x15\x11\x14\x163!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x02\xc0\x12\x0e\xff\x00\x0e\x12\x12\x0e\x01\x00\x0e\x12\x01\xc0\x12\x0e\xff\x00\x0e\x12\x12\x0e\x01\x00\x0e\x12\x01\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01`\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x01\xff\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x17\x00'\x007\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x00 >\x01\x10.\x01 \x0e\x01\x10\x16%\"&5\x1146;\x012\x16\x15\x11\x14\x06#!\"&5\x1146;\x012\x16\x15\x11\x14\x06#\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01\x9e\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92\x01\xee\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfb\xae\x92\xfa\x01(\xfa\x92\x92\xfa\xfe\xd8\xfaN\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00\x1b\x00\x00\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04@\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x01\xc0\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01`\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x12\x01\xff\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\v\x00\x17\x00'\x00\x00\x00 \x04\x12\x10\x02\x04 $\x02\x10\x12\x00 >\x01\x10.\x01 \x0e\x01\x10\x167\"&5\x11463!2\x16\x15\x11\x14\x06#\x02/\x01\xa2\x01a\xce\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01\x9e\x01(\xfa\x92\x92\xfa\xfe\xd8\xfa\x92\x92n\x0e\x12\x12\x0e\x02@\x0e\x12\x12\x0e\x05\x80\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xfb\xae\x92\xfa\x01(\xfa\x92\x92\xfa\xfe\xd8\xfaN\x12\x0e\x02@\x0e\x12\x12\x0e\xfd\xc0\x0e\x12\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\v\x00%\x00=\x00\x00%\x13\x16\a\x06#!\"'&7\x13\x01\x13!\x13>\x013!\x15\x14\x1626=\x01!\x15\x14\x1626=\x01!2\x16%\x11\x14\x06\"&5\x114&\"\x06\x15\x11\x14\x06\"&5\x1146 \x16\x06\xdd#\x03\x13\x13\x1d\xf9\x80\x1d\x13\x13\x03#\x06]V\xf9TV\x03$\x19\x01\x00KjK\x01\x80KjK\x01\x00\x19$\xfe\x83&4&\x96Ԗ&4&\xe1\x01>\xe1\x80\xfe\xc7\x1c\x16\x15\x15\x16\x1c\x019\x03G\xfc\xf9\x03\a\x18!\x805KK5\x80\x805KK5\x80!\xa1\xff\x00\x1a&&\x1a\x01\x00j\x96\x96j\xff\x00\x1a&&\x1a\x01\x00\x9f\xe1\xe1\x00\x06\x00\x00\xff\x00\b\x00\x06\x00\x00\x15\x00#\x00/\x00;\x00I\x00m\x00\x00\x012\x16\x14\x06+\x01\x03\x0e\x01#!\"&'\x03#\"&463\x01>\x01'\x03.\x01\x0e\x01\x17\x13\x1e\x013%\x114&\"\x06\x15\x11\x14\x1626%\x114&\"\x06\x15\x11\x14\x1626%\x136.\x01\x06\a\x03\x06\x16\x17326\x01\x03#\x13>\x01;\x01463!2\x16\x1532\x16\x17\x13#\x03.\x01+\x01\x14\x06#!\"&5#\"\x06\a\x805KK5\x0fs\bH.\xfb\x00.H\bs\x0f5KK5\x01e\x1a#\x02 \x02)4#\x02 \x02%\x19\x01\xa0&4&&4&\x01\x80&4&&4&\x01` \x02#4)\x02 \x02#\x1a\x05\x19%\xfb~]\x84e\x13\x8cZ\xa7&\x1a\x01\x80\x1a&\xa7Z\x8c\x13e\x84]\vE-\xa7&\x1a\xfe\x80\x1a&\xa7-E\x03\x00KjK\xfdj.<<.\x02\x96KjK\xfc\xe0\x02)\x1a\x01\xa0\x1a#\x04)\x1a\xfe`\x19\"@\x01\xa0\x1a&&\x1a\xfe`\x1a&&\x1a\x01\xa0\x1a&&\x1a\xfe`\x1a&&\x15\x01\xa0\x1a)\x04#\x1a\xfe`\x1a)\x02\"\x04\xda\xfed\x01\xb9Xo\x1a&&\x1aoX\xfeG\x01\x9c,8\x1a&&\x1a8\x00\x02\x00!\xff\x80\x06\xdf\x05\x80\x00\x03\x00O\x00\x00\x01\x13#\x03\x01\a\x06#!\x03!2\x17\x16\x0f\x01\x06#!\x03\x06+\x01\"'&7\x13#\x03\x06+\x01\"'&7\x13!\"'&?\x0163!\x13!\"'&?\x0163!\x136;\x012\x17\x16\a\x033\x136;\x012\x17\x16\a\x03!2\x17\x16\x03\xdf@\xfe@\x03\xfe8\a\x18\xfe\xb9@\x017\x0f\n\n\x048\x05\x1a\xfe\xb9Q\a\x18\xe0\x10\n\t\x03N\xfeQ\a\x18\xe1\x0f\n\t\x03N\xfe\xc9\x0f\n\t\x038\a\x18\x01G@\xfe\xc9\x0f\n\n\x048\x05\x1a\x01GQ\a\x19\xe0\x0f\n\t\x03N\xfeQ\a\x19\xe0\x0f\n\t\x03N\x017\x0f\n\t\x02\x00\x01\x00\xff\x00\x01\xf8\xe0\x18\xff\x00\f\x0e\x0e\xe0\x18\xfe\xb8\x18\f\f\x10\x018\xfe\xb8\x18\f\f\x10\x018\f\f\x10\xe0\x18\x01\x00\f\x0e\x0e\xe0\x18\x01H\x18\f\f\x10\xfe\xc8\x01H\x18\f\f\x10\xfe\xc8\f\f\x00\x00\x00\x00\x04\x00k\xff\x00\x05\x95\x06\x00\x00\x02\x00\x05\x00\x11\x00%\x00\x00\x01\x17\a\x11\x17\a\x03\t\x03\x11\x03\a\t\x01\x17\x01\x00\x10\x02\x0e\x02\".\x02\x02\x10\x12>\x022\x1e\x02\x03I\x94\x95\x95\x94\x83\x01\xd0\xfe\xce\x012\xfe0\xff]\x01@\xfe\xc0]\x00\xff\x02\xcf@o\xaa\xc1\xf6\xc1\xaao@@o\xaa\xc1\xf6\xc1\xaao\x01㔕\x03\x8c\x95\x94\xfca\x01\xd0\x012\x012\x01\xd0\xfd\x9d\x00\xff]\xfe\xbf\xfe\xbf]\x00\xff\x01p\xfe^\xfe\xc7\xc9|11|\xc9\x019\x01\xa2\x019\xc9|11|\xc9\x00\x00\x00\x00\x03\x00(\xff\x00\x03\xd8\x06\x00\x00\x02\x00\x05\x00\x11\x00\x00%7'\x117'\x13\t\x01\x11\x01'\t\x017\x01\x11\x01\x02T\xad\xad\xad\xad \x01d\xfd\xe5\xfe\xd7l\x01t\xfe\x8cl\x01)\x02\x1bq\xac\xac\x01n\xac\xac\xfd\xf1\xfe\x9c\xfd\xe4\x02\xc7\xfe\xd8l\x01u\x01ul\xfe\xd8\x02\xc7\xfd\xe4\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\a\x00\x0f\x00\x17\x00)\x001\x00\x00$4&\"\x06\x14\x162\x004&\"\x06\x14\x162\x00\x10\x06 &\x106 \x13\x14\a\x01\x06+\x01\"&547\x016;\x012\x16\x04\x10\x06 &\x106 \x05\x00LhLLh\xfdLLhLLh\x04L\xe1\xfe\xc2\xe1\xe1\x01>\x81\r\xfb\xe0\x13 \xa0\x1a&\r\x04 \x13 \xa0\x1a&\xfd`\xe1\xfe\xc2\xe1\xe1\x01>\xcchLLhL\x03LhLLhL\xfe\x1f\xfe\xc2\xe1\xe1\x01>\xe1\x02\xc0\x14\x12\xfa\x80\x1a&\x1a\x14\x12\x05\x80\x1a&\xbb\xfe\xc2\xe1\xe1\x01>\xe1\x00\x00\x00\x05\x00\x03\xffG\x06\xfd\x05\xb9\x00\x06\x00\n\x00\x10\x00\x17\x00\x1d\x00\x00\x13\t\x01.\x017\x13)\x01\x011\x01\x13!\x1362\x01\x13\x16\x06\a\t\x011!\x1362\x17h\x03\x18\xfc\x9c\x12\x0e\ae\x01\xce\x02\x94\xfe\xb6\xfd\xf0\xc6\xfe2\xc6\b2\x050e\a\x0e\x12\xfc\x9c\x03\x18\xfe2\xc6\b2\b\x03>\xfc\t\x02v\r+\x15\x014\xfc\t\x06[\xfd\x9c\x02d\x17\xfd\x85\xfe\xcc\x15+\r\xfd\x8a\x03\xf7\x02d\x17\x17\x00\x00\x00\x04\x00\x00\xff \a\x00\x05\xe0\x00\x03\x00\x0f\x00\x13\x001\x00\x00\x0135#\x015\x06\a\x06&'\x17\x1e\x0172\x01!5!\x05\x14\a\x16\x15\x14\x04#\"&'\x06\"'\x0e\x01#\"$547&54\x12$ \x04\x12\x01\x80\xa0\xa0\x03Eh\x8b\x87\xf9`\x01X\xf8\x94\x81\xfe(\x02\x80\xfd\x80\x04\x80cY\xfe\xfd\xb8z\xce:\x13L\x13:\xcez\xb8\xfe\xfdYc\xf0\x01\x9d\x01\xe6\x01\x9d\xf0\x02\xc0\xe0\xfd\xd4\\$\x02\x01_K`Pa\x01\x01}\xe0\xc0\xbb\xa5f\u007f\x9d\xdeiX\x01\x01Xiޝ\u007ff\xa5\xbb\xd1\x01a\xce\xce\xfe\x9f\x00\x00\x00\x00\t\x00\x00\xff\x80\x06\x00\x05\x80\x00\x03\x00\a\x00\v\x00\x0f\x00\x13\x00(\x00+\x00.\x00>\x00\x00\x01\x15#5\x13\x15#5\x01\x15!5\x01\x15!5\x01\x15!5\x01\x114&+\x01\x01'\a\x01#\"\x06\x15\x11\x14\x163!26\x017!\x057!\x05\x11\x14\x06#!\"&5\x11463!2\x16\x02\x03\xfc\xfc\xfc\x03\xf2\xfe\xab\x01U\xfd`\x02\xa0\xfd`\x03'\f\b \xfe\x86\xd2\xd2\xfe\x86 \b\f\f\b\x04\xd8\b\f\xfc\xa9\xb9\xfej\x02\x8b\xdd\xfej\x02\xe2V>\xfb(>VV>\x04\xd8>V\x02q\x80\x80\x00\xff\u007f\u007f\xfe\x01\x80\x80\x01\x00\x80\x80\x00\xff\u007f\u007f\xfc\xa4\x04\xd8\b\f\xff\x00\xab\xab\x01\x00\f\b\xfb(\b\f\f\x04^\x96\x96\x96\x14\xfb(>VV>\x04\xd8>VV\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x1f\x00=\x00\x00\x01&'&'&'&\x06\x1f\x01\x1e\x03\x17\x16\x17\x1e\x04\x17\x1676'&'&\x02\x01.\x05\x02' \f\x01\x1e\x03\x0e\x01\a\x06\x15\x01#\x01\x0e\x02.\x02\x03\x80h8\x8b\xd0\"$Y\n''>eX5,\t\x04,Pts\x93K\x99\x01\x0125\x1cM\xcc\xfeRLqS;:.K'\x01\x11\x01\xc1\x015\xe9\x8aR\x1e\x05\x0e\r\r\x01Ch\xfe\xe7\x16\x8bh\xac\x95\xba\x02\xd0\xc4R\xcat\x13\x11(\x10\x1e\x1f+e\x84^T\x11\bT\x8a\xaa\x82u B\x06\x03\"$\x15:\x012\xfe~<\x82\x9d\x98\xdc\xc6\x012\x88Hp\xb1\xa8\xe5\xaa\xe3wTT\x17\xfe\xb9\x01\x1d\x02\x18\x0e\x02 V\x00\x00\x05\x00\x00\xff\x00\a\x00\x06\x00\x00/\x007\x00G\x00W\x00g\x00\x00\x00.\x01\a\x04 %&\x0e\x01\x16\x17\x16\x17\x0e\x02\x0f\x01\x06\x16\x17\x1632?\x01673\x16\x1f\x01\x16327>\x01/\x01.\x02'676$4&\"\x06\x14\x162\x04\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x00 \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02&\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x05d\f-\x1a\xfe\xfb\xfe\xe8\xfe\xfb\x1a-\f\x1b\x1a\xc2m\x02\x1b\x1a\x1c\t\n\x16\x19\t\x0e,\x10\b6\x11*\x116\b\x10,\x0e\t\x19\x16\n\t\x1c\x1a\x1b\x02m\xc2\x1a\xfe\xb7KjKKj\x02\x8bo\xbd\xfe\xfb\xfe\xe2\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbd\xfeK\xfe\xc8\xfe\xe4\xcezz\xce\x01\x1c\x018\x01\x1c\xcezz\xce\x01Ȏ\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x03U4\x1b\x06>>\x06\x1b4-\x06.\f\x9e\xdeYG\x15\x190\n\x04)\x14\x8bxx\x8b\x14)\x04\n0\x19\x15GYޞ\f.\x06\xa3jKKjKq\xfe\xe2\xfe\xfb\xbdoo\xbd\x01\x05\x01\x1e\x01\x05\xbdoo\xbd\x01lz\xce\xfe\xe4\xfe\xc8\xfe\xe4\xcezz\xce\x01\x1c\x018\x01\x1c\xce\xfe0\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x00\x03\x00D\xff\x00\x05\xbb\x06\x00\x00/\x007\x00H\x00\x00\x00\x16\a\x03\x0e\x01#\"'.\x017\x13\a\x16\x15\x14\a'654&#\"\a'67\x01'\a\x06.\x016?\x01>\x01\x17\x01\x16\x17\x16\x0f\x01%\x02\"&462\x16\x14\x0127\x17\x06#\".\x01547\x17\x06\x15\x14\x16\x05|D\x05,\x04=)\x06\x03,9\x03#\x8f7\x94\x89[͑\x86f\x89x\xa4\x01\b\x95\xb5!X:\x05 \xef\x1aD\x1e\x01\xe8$\f\x11+\xcd\x01s)\x94hh\x94i\xfc\xdajZ\x8b\x92\xbd\x94\xfb\x92t\x8b<\xcd\x02\xf6F/\xfd\xd9*8\x01\x03C,\x01\xad\bq\u007f\u061c\x89e\x86\x91\xce\\\x8ar\x1b\x01,W\xa1\x1e\x05BX\x1d\xd5\x17\a\x12\xfe\xe5\x15/C2\xe8\x14\x01\xa9h\x94hh\x94\xfa\xbe=\x8bt\x92\xfa\x94\xbc\x94\x8bXm\x91\xcd\x00\x00\x00\x04\x00\x00\xff\x80\x06\x00\x05\x80\x00\x0f\x00>\x00N\x00Z\x00\x00\x01\x15\x14\x06+\x01\"&=\x0146;\x012\x16\x01\x14\x0e\x02\a\x0e\x02\x1d\x01\x14\x06+\x01\"&=\x014>\x037>\x0154&#\"\a\x06\a\x06#\"/\x01.\x017632\x16\x02 \x0e\x02\x10\x1e\x02 >\x02\x10.\x01\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03p\x12\x0e\xa0\x0e\x12\x12\x0e\xa0\x0e\x12\x01\x00\x1e=+& \x1d\x17\x12\x0e\xa0\x0e\x12\x15\x1b3\x1f\x1d5,W48'\x1d3\t\x10\v\bl\n\x04\az\xe3\x81\xdb\xee\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xabff\xab\x01\x91\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01P\xa0\x0e\x12\x12\x0e\xa0\x0e\x12\x12\x01\xe22P:\x1e\x15\x12\x14\x1c\x0f \x0e\x12\x12\x0eD#;$#\x10\r\x19$\x1f*;\x1b\x14?\f\x06R\a\x1a\n\xc0\xb3\x01Cf\xab\xed\xfe\xfc\xed\xabff\xab\xed\x01\x04\xed\xab\xfe\xb7\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x04\x00'\xff\x03\x05Y\x06\x00\x00\t\x00>\x00O\x00`\x00\x00\x00\"&5462\x16\x15\x14\x01\x14\x06&'\x01.\x01\x0f\x01\x06\x1f\x01\x13\x03\x06\a\x06\a\x06'.\x0176\x1b\x01\a\x17\x16\x0e\x02\x0f\x01\x06.\x035\x03\x13632\x17\x01\x16\x1f\x01\a\x16\x05\x1e\x01\x1f\x01\x16\x17\x16\a\x06.\x01'#&'\x03\x01\x16\x15\x14\a\x06.\x01'&\x01\x166?\x0165\x01\xae\x80\\\\\x80[\x01\x8c(\t\x01\x06\x02|\x03\x93\x1f\x03\t\v\x14\x06r\xfe\xcb\x03\b\x03\x03\v\x04\xc9[A@[[@A\xfd#2#\x16\x17\x01\xb6\f\a\x02\x03\b\r\x8b\xfe\x9e\xfe7\xc0*\x1a\x06\x1a\x19\r<\x1b\x11\x02Y\x01\xa0\xa4\xde\x18$\x13\r\x01\x02\x03\f\x14\x18\x0f\x02\x01+\x01}\"(\xfd\xf7\x05\f\x03\x01\r\xa6q\xe087] F\x1b\x16\f \x13\x10\t\x01_\xfe\xad1\b\x05\x02\x05\v)\n\xac\x01\xe9\x01\x04\x02\x02\t\b\x00\x00\x00\a\x00\x03\x00\xe3\t\x00\x04\x1c\x00\x02\x00\v\x00#\x001\x00K\x00e\x00\u007f\x00\x00\x013\x03\x054&+\x01\x11326\x01\x13\x14\x06+\x01\"&=\x01!\a\x06#!\"&7\x0163!2\x16\x04\x10\x06#!\"&5\x11463!2\x01\x14\x0e\x03\a#>\x03?\x014.\x03'3\x1e\x03\x1f\x01\x14\x0e\x03\a#>\x03?\x014.\x03'3\x1e\x03\x1f\x01\x14\x0e\x03\a#>\x03?\x014.\x03'3\x1e\x03\x17\x01\xf8\xab\x01\x03Xe`64[l\xfd\xc2\x01\x13\x0e\xd8\x0e\x13\xfe\xdd7\n\x12\xfe\xf5\x15\x13\r\x02,\t\x12\x01L\x0e\x14\x03;\xfb\xc7\xfe\xf2\x0e\x14\x14\x0e\x01\f\xc8\x01\x98\x01\x0f\x1c=+3&9\x1a\x10\x01\x01\x01\x0e\x1a8&+)>\x1d\x11\x02\xb9\x01\x0f\x1c>+3&9\x1a\x10\x01\x01\x01\x0e\x198&+)>\x1d\x11\x02\xb6\x01\x0f\x1c=+3&8\x1a\x10\x01\x01\x01\x0e\x198&+)>\x1d\x11\x01\x02\x1e\x01\t\xa6Wj\xfe|r\x01\xca\xfd\f\x0e\x14\x14\x0e>Q\x0f$\x11\x02\xf5\x0e\x14\xc6\xfe~\xdc\x14\x0e\x02\xf4\x0e\x14\xfed\v$kaw+-wi[\x1b\x1b\b\x1d[\\\x83;/xgY\x1a\x1a\v$kaw+-wi[\x1b\x1b\b\x1d[\\\x83;/xgY\x1a\x1a\v$kaw+-wi[\x1b\x1b\b\x1d[\\\x83;/xgY\x1a\x00\x04\x00\x00\xff\x00\x05\x80\x05\xf2\x00J\x00\\\x00m\x00\x82\x00\x00\x054.\x01'.\x02'&#\"\x06#\"'.\x03'&47>\x037632\x16327>\x027>\x0254&'&#\"\a\x0e\x03\a\x06\a\x0e\x01\x10\x16\x17\x16\x17\x16\x17\x16\x17\x16327>\x01\x13\"&47654'&462\x17\x16\x14\a\x06\x16\"'&476\x10'&462\x17\x16\x10\a\x16\"'&47>\x01\x10&'&462\x17\x16\x12\x10\x02\a\x02i\x1a$\x02\x01\b\t\t\x0f$\x17^\x18\"\r\x06\n\x05\b\x01%%\x01\b\x05\n\x06\r\"\x18^\x17$\x0f\t\t\b\x01\x02$\x1aW \x14\x19\"@9O?\x1d\x1f\x06\x031&&18\x1b?t\x03\x03@\"\x19\x14 W\x9f\x1a&\x13%%\x13&4\x13KK\x15\xb86\x12\x13\x13pp\x13&4\x13\x96\x96\xa36\x12\x13\x13ZaaZ\x13&4\x13mttm\x99\v^x\t\x04-\x1b\b\x0e\v\v\x05\x15\x13\x1d\x04\x80\xfe\x80\x04\x1d\x13\x15\x05\v\v\x0e\b\x1b-\x04\tx^\v\x16=\f\b\x12\x11/U7C\f\ak\xda\xfe\xf2\xdakz'[$\x01\x01\x12\b\f=\x03\xa7&5\x13%54'\x134&\x13K\xd4K\x13\xb5\x13\x134\x13r\x01\x027>\x0254\x00 \x00\x15\x14\x06\"&54>\x022\x1e\x02\x04\x14\x06\"&462%\x14\x06\"&54&#\"\x06\x15\x14\x06\"&546 \x16%\x16\x06\a\x06#\"&'&'.\x017>\x01\x17\x16\x05\x16\x06\a\x06#\"'&'.\x017>\x01\x17\x16\x80&4&&4\xe6&4&&4S\x01\x00Z\xff\x00\x01\xad&4&&4\x02\xe9\x174$#\x1f\x1d&\x0f\xe1\x9f\x1a&&\x1aj\x96\x173$\"('$\xfe\xf9\xfe\x8e\xfe\xf9&4&[\x9b\xd5\xea՛[\xfd\xfd&4&&4\x01F&4&\x83]\\\x84&4&\xce\x01$\xce\x01\x8a\n\x16\x19\t\x0e\x13!\aD\x9c\x15\b\x10\x114\x15\xb7\x01%\t\x15\x19\v\f,\x10\\\xcd\x16\a\x10\x104\x15\xeb\xa64&&4&\x9a4&&4&\x01-\xff\x00Z\x01\x00\x874&&4&\x01\x00;cX/)#&>B)\x9f\xe1&4&\x96j9aU0'.4a7\xb9\x01\a\xfe\xf9\xb9\x1a&&\x1au՛[[\x9b\xd5\xdb4&&4&@\x1a&&\x1a]\x83\x83]\x1a&&\x1a\x92\xceΏ\x190\n\x04\x16\x13\xb2u\x104\x15\x15\b\x10\x89\x85\x190\n\x04)\xee\x9b\x104\x15\x16\a\x10\xaf\x00\x00\x00\x00\x04\x00\x03\xff\x00\b\xfd\x06\x00\x00\x11\x00#\x00g\x00\xb0\x00\x00\x01&'.\x01#\"\x06\x15\x14\x1f\x01\x1632676%4/\x01&#\"\x06\a\x06\a\x16\x17\x1e\x01326\x01\x0e\x01'&#\"\a2632\x16\x17\x16\x06\a\x06#2\x17\x1e\x01\a\x0e\x01+\x01&'%\a\x06#\"'\x03&6?\x01\x136\x1276\x1e\x01\x06\a\x06\a676\x16\x17\x16\x06\a\x06\a632\x17\x1e\x01%\x13\x16\x06\x0f\x01\x03\x06\x02\a\x06#\"'&6767\x06\a\x06#\"&'&6767\x06#\"'.\x017>\x01\x17\x16327\"\x06#\"&'&6763\"'.\x017>\x01;\x02\x16\x17\x057632\x04\b;\x19\x11>%5K$\n\"0%>\x11\x19\x02s$\n\"0%>\x11\x19;;\x19\x11>%5K\xfeV\x11L#>H30\x03\r\x03\\\x9d(\x11\x1b$\x12\x15\x15\x12$\x1b\x11(\x9d\\\x06\x10\x1c\xfe\xde\xef\x0e\x0f(\x11\xa0\v\x0e\x16є\x11\x95y\x1fO2\a\x1fF/{\x90(?\x04\x050(TK.5sg$\x1a\x03\xb1\xa0\v\x0e\x16є\x11\x95y\x1a#-\x1d\x19\a\x1fF/{\x90\x04\b$7\x04\x050(TK.5sg$\x1a\x12\x11L#>H30\x03\r\x03\\\x9d(\x11\x1b$\x12\x15\x15\x12$\x1b\x11(\x9d\\\x06\x01\x0e\x1c\x01#\xef\x0e\x0f(\x02@\x025\"'K58!\b\x1f'\"5\x828!\b\x1f'\"5\x02\x025\"'K\x01\x12#\x1a\x11\x1f\x11\x01dS$K\x11\t\t\x11K$Sd\x02\x02\x1bx\a#\x01@\x171\rw\x01\v\x9b\x01\x11d\x19\a>N\x1a;ET\x11\x050((?\x04\n-\n2\x12K|\xfe\xc0\x171\rw\xfe\xf5\x9b\xfe\xefd\x16#\x1fN\x1a;ET\x11\x010$(?\x04\n-\n2\x12K$#\x1a\x11\x1f\x11\x01dS$K\x11\t\t\x11K$Sd\x02\x02\x1bx\a\x00\x00\x00\x04\x00\x00\xff\x00\a\x00\x06\x00\x00\x13\x00D\x00N\x00\\\x00\x00\x01\x14\x162654& \x06\x15\x14\x16265462\x16\x02\"\x0e\x02\x15\x14\x162654\x00 \x00\x15\x14\x0e\x01\a\x0e\x03\x15\x14\x06#\"\x06\x14\x1632654>\x027>\x0354.\x01\x01\x17\x01\x06\"/\x01&47\x01\x17\x16\x14\x0f\x03&'?\x0162\x04 &4&\xce\xfe\xdc\xce&4&\x84\xb8\x84h\xea՛[&4&\x01\a\x01r\x01\a$'(\"$3\x17\x96j\x1a&&\x1a\x9f\xe1\x0f&\x1d\x1f#$4\x17[\x9b\xfd\xc2\xe2\xfd\xbd\f\"\f\xa8\f\f\x06@\xa8\f\f\xe9\x1aGB\x81[\xcf\r\"\x02\xc0\x1a&&\x1a\x92\xceΒ\x1a&&\x1a]\x83\x83\x01\xe3[\x9b\xd5u\x1a&&\x1a\xb9\x01\a\xfe\xf9\xb97a4.'0Ua9j\x96&4&\xe1\x9f)B>&#)/Xc;u՛\xfd\x8c\xe2\xfd\xbd\f\f\xa8\f\"\f\x06\x06\xa8\f\"\r\xe9\x19G\x99i[\xcf\f\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x14\x00X\x00h\x00\x00\x01\x14\a\x0e\x01\a\x0e\x01\a\x06#\"&5467632\x16\x014&'&#\"\a'>\x0154#\"\a\x0e\x02\x15\x14\x1632\x14\a\x06\a\x0e\x01#\"54>\x0354'.\x01#\"\x0e\x01\x15\x14\x1632>\x017>\x01767632\x17\x16326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03b\r\v)\n\x02\x05\v\x14\v:4FD\x1c\x17\x1c\x11\x01\xe6N\r\x15\r[\x87\x02\x031\xf2\x18,^\x95J\xa1\x93\x19\x01\x04\x16\x0eK-*\x15\x1d\x1e\x16\a\x18E\x1f#9\x19gWR\x92Y\x15\x06\x13\x05\x03\vvm0O\x01\x03\x05\t\xb8\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x03\xfd\x1bC2\xc82\v\x03\x01\x02c@X\xac&\x0e!\xfe9\x0e{\x05\bM\x02\x16\xe2A\xe9\x06\x11\x91\xbc_\x92\x9e\x06\x02\"S4b/\x18/ \x19\x0f\x01\x03\a\x16\x1dDR\"Xlj\x92P\x16Y\x16\f\x06<\x12\x01\t\x02\x0f\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x02\x00%\xff\x00\x05\xda\x05\xff\x00\x19\x00e\x00\x00\x014.\x02#\"\a\x06\x02\x15\x14\x1e\x0232\x16>\x0276\x1276\x01\x14\x06#'.\x02#\"\a\x06\a\x0e\x01\a\x0e\x03#\"&54>\x0132\x16\x17\x14\x0e\x03\x15\x14\x1632>\x03754&*\x01\x06#\"&54>\x02763 \x11\x14\x02\a\x17>\x0132\x17\x1e\x01\x02\xe8\x04\r\x1d\x17''il\x11$E/\x04\x1c\f\x14\n\x02\x10@\x10\x13\x02\xf2\x0f\b\x06\x16P@\x1f\xa7\xb8\x0f\x06\n\x1d\b\x17^\x83\xb2`\x87\x9f'W6&\xa4\x01!.. ! -P5+\x16\x05\a\n\n\n\x01\xe3\xfaE{\xbdn46\x01vL\x05\x03e\xa3V\x16\x1f\x13z\x04\xcf\x18\x1d\x1f\x0f\x17:\xfe\xf7\x89,SN/\x01\x01\x05\f\nM\x015M[\xfd\xa7\a\r\x01\x03\x10\t]\b\x13$\x8b\x1f[\xb1\x98^\xa7\x885\x80iC\x1c\x01\x17'2H&!(?]v`*\t\x02\x03\x01\xf5\xe2l\xe2\u008d\x13\t\xfe\x98b\xfe\xa2$\x039>\r\a\xbf\x00\x03\x00\x01\xff\x00\x06\u007f\x05\xfb\x00=\x00R\x00\x87\x00\x00\x012\x1f\x01\x16\x1f\x01\x16\a\x03\x0e\x01\a\r\x01#\"&5467%!\"&7>\x013-\x01.\x017>\x01;\x01\x05%.\x017>\x0132\x17\x05\x172\x16326/\x01.\x0176\a\x17/\x02\x03.\x01'&676\x16\x1f\x01\x0e\x01\a\x06\x16\x01\x13\x16\x0f\x01\x06\x0f\x016/\x01&/\x01&#\"\a\x03&676\x16\x17\t\x01&676\x16\x17\x13\x03&676\x16\x17\x13\x17\x1e\x016/\x01&672\x16\x03? \x1b\xde=1\x92(\vH\x06/ \xfd\xf1\xfe\xa0\t'96&\x01\x04\xfe@)9\x02\x02<'\x01\xba\xfd\xf7)2\x06\x069%\n\x01\xe1\xfe\xa1&0\x06\x066#\x06\x0e\x01\xc0\xd9\x01\x04\x01\x17\x0f\x14\xba#\x0e\x19\x1b\x15\xba\xda\x05$\xee\x01\x03\x01\x18\v \x1fJ\x1b\x8e\x02\x06\x01 \x12\x03\xa5\x0f\x04\x0f0\f7j\x02)\x925@\xde\"*3%\xeb\x19\x0e\"!M\x18\x01\n\xfe\xfa\x15\x15%#K\x14\xf1\x88\x0f\x15\"%N\x11\xc1e\b\x1e\x18\x01\f\x028)'8\x03_\x12\x94(9\xaa.<\xfec +\x048 8(%6\x05 <)'4\x01@\x05@)#-<^\n?%$-\x02`%\x01.\r}\x17Q!&\xca}%\x02&\x01\x06\x01\x05\x01\x1fN\x19\x17\v\x1c\x93\x01\x05\x02-l\x01\xa7\xfe\xf6IJ\xdb;\x1c6>/\xaa=*\x94\x17%\x018!Q\x17\x16\x10 \xfe\xa0\x01\xc7#P\x13\x12\x18\"\xfe\\\x01Q#N\x11\x13\x1a&\xfea\xc4\x0f\x05\x14\x10\xe0)<\x019\x00\x00\x04\x00\x00\xff\x1e\a\x00\x05b\x00R\x00]\x00m\x00p\x00\x00%\"'.\x01'&54>\x0676%&547632\x1f\x0163 \x00\x17\x16\x14\a\x0e\x01\a\x16\x15\x14\a\x06#\"/\x02\x017\x06\a\x16\x1a\x01\x15\x14\a\x06#\"'\x01\x06\a\x16\x00\x15\x14#\"&/\x01\x03\x06\a\x1e\x01\x17\x13\x14%\x17$\x13\x02%\x1e\x01\x15\x14\x06\x00\x14\x1632\x16\x15\x14\x162654&#\"%'\x17\x01O\x02\x04V\xa59\x15\x04\x04\n\a\x0e\x06\x12\x02\xb8\x01\fn\x11t\f\x12\n|\\d\x01\n\x01ϓ\x14\x14[\xff\x97n\x11t\v\x13\n|@\xfeD\a:)\x03\xf8\xee\t\r;9\x03\xfe8'+\x18\x01|\v\x0e\x89\x04j\xe0,\"\x02 \a\xb0\x0341\x01\x11\xb1\xb4\xfe\xe9CH^\xfen\x1c\x14Vz\x1c(\x1c\xb2~\x14\x01R\t\a\xb4\x029\xb0\\\x1e'\t\x14\x10\x14\f\x16\b\x17\x03\xfbr\xc6\r\x13\n@\x10\xe5\x13\xfe\xed\xe8\x1fL\x1f\x8e\xdf@\xc6\r\x14\t@\x10\xe5w\x034\a\x18\x17\x05\xfe6\xfeH\x03\a\x02\x03\a\x03I\x1c(+\xfdC\x04\n,\x06\xc5\x01\x9d55\x03,\f\xfe\xb9\nf[o\x01\x12\x01\x15p@\xa9\\j\xbd\x02;(\x1czV\x14\x1c\x1c\x14~\xb2\x11\x04\a\x00\x00\x00\x00\x04\x00\x00\xff\x97\x04\xfe\x05i\x00\x1f\x00/\x005\x00O\x00\x00\x01\x14\a\x06#\"'&54>\x0132\x17\x06\a&#\"\x06\x15\x14\x16 654'67\x16'\x14\x02\x0f\x01\"'>\x0454'\x16'\x15&'\x1e\x01\x13\"'6767\x0e\x01\a&546767>\x017\x16\x15\x14\a\x0e\x01\x04\x1a\x93\x94\xe6蒓\x88\xf2\x93`V \aBM\xa7\xe3\xe1\x01R\xe0 B9)̟\x9f\x0e\x1d!S\u007fH-\x0f\x0377I\x85Xm\xfdSM\xdaH\x13\x02*\xc3k#\"\x1a.o;^\x1bJ\x18 q\x01\xaeן\xa1\xa1\x9fד\xf7\x92\x1f>@\x1c\xf6\xa8\xaa\xed\xed\xaaYM\r$bK\xc0\xfe\xced\x01\x05 \x8d\xa8ү[E\"\xa0\xa2\x02\xd6\xe2;\xff\xfe\xb9Kx\u007f%\x13^\x91\x196;%T\x1a,\x1e\x10U:i\x94m=Mk\x00\x00\x00\x05\x00\x00\xff\x80\x06\x00\x05\x80\x00\x1a\x00)\x00.\x00D\x00T\x00\x00\x014'\x06\a\x16\x15\x14\x06\"&54632\x1767&#\"\x06\x10\x16 6\x03\x16\x15\x14\x0e\x03\a\x16;\x016\x114'.\x01'\x16\x054'\x06\a\x0e\x01\x15\x14\x17>\x017\x0e\x01\a\x1632676%\x11\x14\x06#!\"&5\x11463!2\x16\x04\x1a\x1c),\x16\x9a蛜s5-\x04\x17\x0254&#\"\x06#\"'654'.\x01#\"\a\x06\x15\x14\x17\x06#\"&#\"\x06\x15\x14\x1e\x02\x15\x14\a\x06\a\x06\x15\x14\x17\x1e\x0232632\x1e\x0232>\x0232\x1632>\x0176\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x04\xff\x16Cf\x1d\a'/'%\x14\f(\v\x04\b\x05\x11$\x86U\xc7L\x11\x05\x04\n\f(\n\x15#'/'\a@\x86\x16\x89\x02\b\x0f\x10\f3\x0e#@,G)+H+@#\x0e3\r\x10\x0e\b\x02\x89\x01\x01\xce\xfe\x9f\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\x01\x84\x16\x05\x0fX@\x13\x06\x0f\x16\f\x1d\x16\x13\x19\x10\x02_\x13O#NW\xa5#O\x13_\x02\x0f\x18\x14\x15\x1d\f\x16\x0f\x06\x13\x8a\x1d\x05\x16.\x16\x05*\x13\t\x1e#\x1e\x1e#\x1e\b\x14(\x05\x16\x01\xfb\xfe^\xfe\x9f\xce\xce\x01a\x01\xa2\x01a\xce\xce\x00\x00\x01\x00\x0f\xff\x80\x06q\x05\x80\x00[\x00\x00\x016\x16\x17\x16\x15\x14\a\x1632632\x16\x15\x14\x0e\x02\x15\x14\x17\x1e\x01\x17\x16\x17\x16\x15\x14\a\x0e\x02#\"&#\"\a\x0e\x04#\".\x03'&#\"\x06#\".\x01'&54767>\x017654.\x0254632\x16327&547>\x01\x03P\x86\xd59\x1b\t\x0e\x0e\x12B\x12\x1d6?K?\f%\x83O\x1c4\x1c\xdb\a\b\x14\x17\x14T\x16%\x19 >6>Z64Y=6>\x1f\x1a%\x18S\x11\x19\x14\b\a\xdb\x1c4\x1cN\x85$\f?L?4\x1d\x0fB\x14\x12\x0e\t\x1b@\xd8\x05\x80\x01\x8b{:y/\x90\a\x1b$\x1c ,\x13'\x1c\x0f\x1cR\x88!\f\v\x06\x1dF!\v8%\r\x05\x05#)(\x1b\x1b()#\x05\x05\x0f%:\v!F\x1d\x06\v\f \x8aQ\x1c\x0f\x1c'\x14+\x1f\x1b%\x1a\a\x8e0z:\x89z\x00\x00\x00\x02\x00\x00\xff\x80\x06\x00\x05\x80\x00O\x00_\x00\x00\x014'.\x01'&54>\x0254&#\"\x06#\"'654'.\x01#\"\a\x06\x15\x14\x17\x06#\"&#\"\x06\x15\x14\x1e\x02\x15\x14\a\x06\a\x06\x15\x14\x17\x1e\x0232632\x1e\x0232>\x0232\x1632>\x0176\x01\x11\x14\x06#!\"&5\x11463!2\x16\x05\x00\x16Cf\x1d\a'.'%\x14\v(\f\x04\b\x05\x11$\x85V\xc6M\x12\x06\n\x05\v)\n\x14#'.'\a@\x86\x16\x8a\x02\b\x0e\x10\r3\r#A,G)+H+A#\r4\r\x0f\x0f\b\x01\x8a\x01\x00\xa9w\xfc@w\xa9\xa9w\x03\xc0w\xa9\x01\x84\x16\x05\x0eXA\x0e\v\x0f\x16\f\x1d\x16\x13\x19\x10\x02?4N$NW\xa5&M&L\x02\x10\x19\x14\x15\x1d\f\x16\x0f\v\x0e\x8a\x1d\x05\x16/\x16\x05*\x13\n\x1e#\x1e\x1e#\x1e\t\x13+\x03\x16\x03\v\xfc@w\xa9\xa9w\x03\xc0w\xa9\xa9\x00\x00\x00\x00\x01\x00\x00\xff\x80\t\x00\x06\x00\x00O\x00\x00\x01\x0e\x05\a\x0e\x01\a\x0e\x03\a\x06\a$\x05\x06\a>\x01?\x01>\x0376\x052\x17\x1e\x01\a\x03\x06'&#\"\x04\a\x06.\x02/\x01454327\x12\x0032\x1e\x05\x177>\x047>\x03\t\x00EpB5\x16\x16\x03\n3\x17\x0fFAP\b/h\xfe\xab\xfe\xdf\\\xd3/N\x10\x0fG\xb8S\x85L\xba\x01\x17\x01\t\v\x06\x06\xc2\x0f \x80\xe2\x92\xfe\x00\x88R\x86P*\f\x01\x06\x8a\xe9\xc0\x01m\xc9\x05\x1395F84\x0ef\x02&3Ga4B|wB\x06\x00.\\FI*/\x06\x12\xed.\x1d?&,\x06\x1f\xc8\x0e\xac5~\x10\x1e\a\a\x1bK %\r\x1f&\x03\x06\x16\v\xfe\xa7\x1d\a\x18Y\x02\x01\x1c.\"\x11\x01\x01\x01\x067\x01n\x01<\x01\t\x0f\"-I.\xb1\x04M`{\x90ARwJ!\x00\x05\x00\x00\xff\x00\x06\x00\x06\x00\x00F\x00X\x00^\x00d\x00j\x00\x00\x01\x14\a'\x17\x06\a'\x17\x06\a'\x17\x06\a'\x17\x06\"'7\a&'7\a&'7\a&'7\a&547\x17'67\x17'67\x17'67\x17'632\x17\a7\x16\x17\a7\x16\x17\a7\x16\x17\a7\x16\x174\x02$#\"\x0e\x02\x15\x14\x1e\x0232$\x12\x13\x11\t\x01\x11\x01\x11\x01\x11\t\x01\x11\x01\x11\t\x01\x11\x01\x05*\x05\xec\xe0\x13'ֱ,?\x9dg=OO\x0e&L&\x0eNJBg\x9d;1\xb2\xd6'\x13\xe0\xed\x05\x05\xee\xe1\x13'ֱ.=\x9egCIM\r$'&&\x0eNJBg\x9e=.\xb1\xd5%\x15\xe0\xed\x05\x1e\x9d\xfe\xf3\x9ew\u061d\\\\\x9d\xd8w\x9e\x01\r\x9dI\xfdo\xfdo\x02\x91\x02\xc4\xfd<\xfd<\x05\xc4\xfd\x00\xfd\x00\x03\x00\x02\x80-\x1f\x0eNIDg\x9e=/\xb2\xd7%\x16\xe4\xf0\x06\x06\xee\xe2\x13(ײ+A\x9ehEHO\x0e*\"#*\x0eOICh\x9f=/\xb2\xd7'\x13\xe0\xec\x06\x06\xed\xe1\x13(ֲ/=\x9fh>ON\x0e\x1f.\xa0\x01\x0f\x9d]\x9d\xdaxwڝ]\x9d\x01\x0f\x02\x1e\xfd\x02\xfe\x81\x01\u007f\x02\xfe\x01\u007f\xf9\xcb\x01\x9c\x037\x01\x9b\xfee\xfc\xc9\x03[\xfc\x80\xfe@\x01\xc0\x03\x80\x01\xc0\x00\x00\x03\x00\x00\xff\x00\x06\x80\x06\x00\x00\x14\x00)\x006\x00\x00\x01!\a!\"\x06\x15\x11\x14\x16\x17\x163\x15#\"&5\x1146%3\x01\x0e\x06\a567654'\x013\x13\x01\x11!67!\x114&'7\x1e\x01\x01S\x02\xb3\x1a\xfdgn\x9dy]\x17K-\x8c\xc7\xc7\x03\xdf\xf7\xfe\x1e\x17#75LSl>\xa39\x14\x14\xfe\xe3\xe4\xbb\x03V\xfc\xe5%\b\x02\xa6cP\x19e}\x05&H\x9en\xfc\xfd_\x95\x13\x05HȌ\x03\x03\x8c\xc8\xda\xfa\xf2=UoLQ1!\x02\xc3\x1a\x9c4564\x02\xdd\xfd\xb7\x01\xf2\xfb\xa97\x12\x04\x0eU\x8c\x1dC\"\xb3\x00\x00\x00\x00\n\x00\x00\xff\x00\a\x00\x06\x00\x00\a\x00\x14\x00!\x00-\x009\x00[\x00n\x00x\x00\x90\x00\xe7\x00\x00\x00\x14\x06\"&462\x0354&\"\x06\x1d\x01\x14\x16326754&\"\x06\x1d\x01\x14\x16326754&\"\x06\x1d\x01\x14\x1626754&\"\x06\x1d\x01\x14\x1626\x01\x06\x04#\".\x02547\x06\x15\x14\x12\x17632\x17632\x1762\x17632\x16\x176\x12'4#\"\a\x06#\"547\x06\x15\x14\x163276\x014&\"\x06\x15\x14\x1626\x014.\x01#\"\x06\a\x06\x15\x14\x16327632\x16\x15\x14\a>\x01\x05\x14\x02\a\x06\x04\x0f\x01\x15\x14\x06#\"'\x06\"'\x06#\"'\x06#\"&5\x06#\"'67&'\x16327&'&54>\x0332\x1767>\x017>\x027>\x0132\x17632\x17\x16\x15\x14\x0e\x02\a\x1e\x01\x15\x14\a\x16\x17632\x17\x16\x03T\"8\"\"8\x82)<()\x1d\x1e)\xac(<))\x1e\x1d)\xae)<))<)\xae)<))<)\x01\fT\xfeد{ՐR\x15h\x82x\x1e=8\x1e 78\x1e n \x1e8\x1c1\rp\x82\x8eH\x11\x1e_6\xe2\x1eS\xb2\x92oc\r\xfeF@b@?d?\x02uK\x97bM\x9070[f5Y$\x1135\x04KU\x01\x17C<:\xfe\xee[\x04;+8\x1e n \x1e87 \x1e8/8Zlv]64qE 'YK\xc00\x18\x12-AlB;\x16\x13\x17\x02\x14\x03\n\x1a\x18\x10W\xf9\x88#\x1b;WS9\x05\f\r\x13\x01\x11&\x10\x9d(\x19#-7Z\x04\xe8://:/\xfaTr\x1e++\x1er\x1e,,\x1er\x1e++\x1er\x1e,,\x1er\x1e++\x1er\x1e,,\x1er\x1e++\x1er\x1e,,\x02ʠ\xc7g\xab\xe0xXV\xafע\xfe\xd4e9222222\x1f\x19^\x01\x13\xb3K\x06\x13\xf3Vv\u007f\x94\x96\xddF0\x02\xb22OO23OO\xfe\xe0`\xa6lF;\x9fmhj\x13\x0684\x1a\x14D\xc3ro\xfe\xebB@\x9d\x1a\x01r+@222222C0DP\x01\x13\x1f`\a.\xc0r8h9\x89\x9c~T4\x1d\x19\x03\x14\x06\x0f.&\x14o\x84\x04@9\x05\a\x05\x11\x0f\x13\x01\x06\x18\f\x06\x13\x8a\xf0\x1e1P\x00\x00\x03\x00\x00\xff\x80\x06\x00\x05\x80\x00\x19\x00%\x001\x00\x00\x014'!\x153\x0e\x01#\"&4632\x177&#\"\x06\x10\x16326%35#5#\x15#\x153\x153\x00\x10\x02\x04 $\x02\x10\x12$ \x04\x03\x95\x06\xfe\x96\xd9\f}Pc\x8c\x8cc]\x0132\x16\x06\x001\xae\xa4I\xfe\xe3U\xa4Π?L\x80\xb6\x80L?\xbe\x99cc\x0e\xc34MX\v\x8a\x14\x1a&\x04\x00\xfc\xb90\x0e4;0\xfe\xae\x05X\x19pD[\x80\x80[Dp\x19D,\x0f\x02)\x12\x02&&\x00\x00\x05\x00\x00\xffQ\t\x00\x05\x00\x00\x05\x009\x00V\x00\\\x00\x94\x00\x00\x1226&\"\x06\x05.\x05'\a\x06&'&6?\x01.\x02\x06#\"\x0f\x01#\x1126\x1e\x03\x17\x01\x16327\x1667\x167>\x01'\x1632>\x01&\x173\x11#'&+\x01\"\x0f\x01\x06\x14\x17\x1e\x01?\x016\x1e\x01\a\x1e\x01\x17\x1e\x01\x17\x16\x0426&\"\x06\x01\x11\x14\x06#!\x0e\x01\a\x0e\x01\a\x0e\x01'\x0e\x01.\x01'\x01!\"&5\x11463!>\x06;\x012\x176;\x012\x1e\x06\x17!2\x16\x98P P \x06\t\n9\x1a2#.\x16}S\xfbP9\x01:\xb1\x16:%L\v\\B\x9e\x9b\x05 \f\x1b\x0e\x15\b\x01)spN/9o\x11J5\x14 \x02\n!+D\x1f\a\x84`]\x9dBg\xa7Y9\xd1\x1c\x1b+\x86,\xc1\x199%\n\x10P\x14\x1dk\v4\x01\x00P P \x01\b&\x1a\xfeN\x1bnF!_7*}B<\x84{o0\xfe\xe1\xfe\x9a\x1a&&\x1a\x01\xa5\x0eB\x1d;*<@$ucRRc\xa7#@16#3\x1b7\x0e\x01c\x1a&\x01\x80@@@\x06\rJ\"@*4\x17\x8c^\x04`E\xb2D\xce\v\v\x01\x02B\x9e\xfd\xe0\x01\x01\x03\x06\v\b\xfe\xdco/\x1489\x062\x127\x17\n*@O\x18\x02\x00\xb4LC\xf3!T!3\x022\xda\x17\x033\x1f\x13X\x18$\x8b\x0fBJ@@@\x02\x00\xfd\x80\x1a&AS\n0C\f59\x04\"\v'D/\x01\x1a&\x1a\x02\xa0\x1a&\x0eD\x1c4\x17\x1c\v88\f\x11$\x1a5\x1fA\x10&\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00%\x00O\x00\x00\x01\x11\x14\x06#!\"&5\x1147>\x067>\x032\x1e\x02\x17\x1e\x06\x17\x16\x01$7>\x01/\x01.\x01\a\x06\a\x0e\x03\".\x02'&'&\x06\x0f\x01\x06\x16\x17\x16\x05\x1e\x042>\x03\a\x00^B\xfa@B^\v\b>\x15FFz\xa5n\x05_0P:P2\\\x06n\xa5zFF\x15>\b\v\xfd\xcc\x01\aR\v\x03\b&\b\x1a\v\xe7p\x05^1P:P1^\x05\xba\x9d\v\x1a\b&\b\x03\vR\x01\a\nP2NMJMQ0R\x03r\xfc.B^^B\x03\xd2\x0f\t\a7\x11:5]yP\x04H!%%\"F\x05Py]5:\x117\a\t\xfd\xa8\xbf=\b\x19\v4\v\x03\b\xa9Q\x03H!%%!H\x03\x86t\b\x03\v4\v\x19\b=\xbf\b<\"-\x16\x16/ ?\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x001\x00P\x00p\x00\x00\x01\x17\x16\x06\a\x0e\x02\a\x0e\x03+\x02\".\x02'.\x02'.\x01?\x01>\x01\x17\x16\x17\x1e\x03;\x022>\x027$76\x16\x13\x11&'&%.\x03+\x02\"\x0e\x02\a\x0e\x02\a\x06\a\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11476\x007>\x03;\x022\x1e\x02\x17\x1e\x02\x17\x16\x05\xc2'\b\x03\n+\xa7~\x04'*OJ%\x01\x01%JN,&\x05x\xa7'\v\x03\b%\b\x1b\v^\xd4\x05M,E\x18\x01\x01\x18E,M\x05\x01\x027\v\x1a\xc6ZE[\xfe\xd6\x03P*F\x18\x01\x01\x18F*P\x03\xd7\xc9:5\x0e\a\x13\r\x05\xc0\r\x13\x80^B\xfa@B^){\x01\xc6\x06$.MK%\x01\x01%KM.$+\xe2\xe2X)\x02o3\v\x19\b\"\x81a\x03 2\x17\x172!\x1f\x04]\x81\x1e\b\x19\v4\v\x04\tI\xa3\x04>\x1f\"\"\x1f>\x04\xc6,\b\x03\xfd&\x03\xa0S8J\xe6\x02B\x1e##\x1eB\x02\xa6\x9f12\f\a\xfc`\r\x13\x13\x03\xad\xfc`B^^B\x03\xa08&r\x01a\x05\x1e#1\x18\x181#\x1e$\xac\xb6R&\x00\x00\x00\x00\v\x00\x15\xff\x00\x05\xeb\x06\x00\x00\x03\x00\a\x00\v\x00\x0f\x00\x1a\x00\x1e\x00\"\x00&\x00.\x002\x00v\x00\x00%\x17/\x01\x01%'\x05\x01\x17\x03'\x01%\x03\x05\x01\x17/\x01\x14\x16\x06\x0f\x01\x17\x16\x01\x05\x03%\x017\a\x17\x01%\x03\x05\x017'\a\x17\x16\x0f\x01%7\x0f\x02'\a\x14\x0f\x01\x06/\x01\x17\x14\a\x05\x06#&5'&\x03&?\x01&'\x03&?\x01&'\x03&7%2\x17\x05\x16\x15\x13\x14\x0f\x01\x17\x16\x15\x1776\x1f\x0174?\x016\x1f\x01\x1e\x01\x0e\x01\x15\x14\x0f\x01\x06\x01J\xca\"\xd8\x01\x12\x01\x12\v\xfe\xd4\xfe\xee\xe30\xf5\x01<\x01=\x0e\xfe\xa0\x01\x8d_\x02g\x02\x02\x04NU\a\xfd?\x01\x00D\xfe\xe9\x04f\x0f\xe6\x02\xfd\xe1\x01u\x13\xfeY\x03\x9a\x14\xe2\x02\x90\x06\x02\a\x01\x02\x1e\xb3\x14\x13G\b\x04\xea\a\ab\a\x04\xfe\xdb\x04\x02\b\xe4\x047\x02\a=^\x01H\x02\b^\x85\x02`\x02\t\x01\xb1\x05\x03\x01=\x06\x14\x06v~\x05\x05y\x05\x06T\x03\x05\xce\x06\x05\xf5\x04\x02\x0f\x14\x04\xbf\x06\x01\xd6\xec\xd5\xfe3\xda\xf5\xd7\x01\x86\xd5\x01G\xcc\xfd\xe2\xd6\x01D\xc8\xfe\xa3P\xefO\x01\x0f\t\x034F\x06\x02\x9e\xc8\x01ѭ\xfb\xb3\xea\xa4\xf0\x02q\xc2\x01\xb9\xa3\xfc\xbb\xe9\x8ei_\x04\x05w\\ހ\xe4!1u\x05\x03\xbb\x05\x05S\xa1\x05\x03\xea\x02\x02\x01\xf2\x04\x01\x11\a\x04%V\x06\x01_\a\x05-d\b\x01\xd2\n\x03\x87\x01\x99\x04\x05\xfe1\a\x03=U\x02\x06{J\x04\x048n\x06\x03~\x03\x03\x87\x04\x06r\x87\x03\x05\x02\x99\x05\x00\x00\x03\x00\x00\xff\x00\x06\x80\x06\x00\x00\x1d\x00'\x00U\x00\x00\x014.\x03#\x0e\x04\".\x03'\"\x0e\x03\x15\x14\x163!26\x034&\"\x06\x15\x14\x1626\x01\x15\x14\x06+\x01\x15\x14\x06#!\"&5\x11463!2\x16\x1d\x0132\x16\x1d\x01\x14\x06+\x01\x1532\x16\x1d\x01\x14\x06+\x01\x1532\x16\x04\xb1\v\x1f0P3\x067\x1e3/./3\x1e7\x063P0\x1f\vT=\x02@=T\xad\x99֙\x99֙\x02|\x12\x0e`^B\xfb@B^^B\x04\xc0B^`\x0e\x12\x12\x0e``\x0e\x12\x12\x0e``\x0e\x12\x01*9deG-\x04!\x10\x18\n\n\x18\x10!\x04-Ged9Iaa\x02\x9bl\x98\x98lk\x98\x98\xfeO\xc0\x0e\x12\xe0B^^B\x05\xc0B^^B\xe0\x12\x0e\xc0\x0e\x12\x80\x12\x0e\xc0\x0e\x12\x80\x12\x00\x00\x04\x00\x00\xff\x00\x06\x80\x06\x00\x00\t\x00+\x00Y\x00i\x00\x00\x01\x14\x06\"&5462\x16\x032\x1e\x04\x15\x14\x06#!\"&54>\x03;\x01\x1e\x052>\x04\x01\x14\x06+\x01\x1532\x16\x1d\x01\x14\x06+\x01\x1532\x16\x1d\x01\x14\x06+\x01\x15\x14\x06#!\"&5\x11463!2\x16\x1d\x0132\x16\x15\x01\x114&#!\"\x06\x15\x11\x14\x163!26\x04\x04\x99֙\x99֙0.I/ \x10\aOB\xfd\xc0BO\t\x1c-Q5\x05\a2\x15-\x1d)&)\x1d-\x152\x02\xb3\x13\r``\r\x13\x13\r``\r\x13\x13\r`^B\xfb@B^^B\x04\xc0B^`\r\x13\xff\x00\x13\r\xfb@\r\x13\x13\r\x04\xc0\r\x13\x03|k\x98\x98kl\x98\x98\xfe\xb8\"=IYL)CggC0[jM4\x04\x1f\v\x17\t\t\t\t\x17\v\x1f\x01\x04\r\x13\x80\x13\r\xc0\r\x13\x80\x13\r\xc0\r\x13\xe0B^^B\x05\xc0B^^B\xe0\x13\r\xfb@\x05\xc0\r\x13\x13\r\xfa@\r\x13\x13\x00\x00\x06\x00\x00\xff\x80\b\x00\x05\x80\x00\x19\x00!\x001\x00A\x00Q\x00u\x00\x00\x004.\x02#\x0e\x04\".\x03'\"\x0e\x02\x14\x163!2\x024&\"\x06\x14\x162\x0154&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x1154&#!\"\x06\x1d\x01\x14\x163!26\x01\x11\x14\x06#!54&+\x01\"\x06\x1d\x01!54&+\x01\"\x06\x1d\x01!\"&5\x11463!2\x16\x04\x00\x12)P9\x060\x1b,***,\x1b0\x069P)\x12J6\x02\x006S\x85\xbc\x85\x85\xbc\x04\"\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x15\x0f\xfd\xc8\x0f\x15\x15\x0f\x028\x0f\x15\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x01\x00^B\xfe\xa0\x12\x0e@\x0e\x12\xfd\x00\x12\x0e@\x0e\x12\xfe\xa0B^^B\x06\xc0B^\x01U\x80kc9\x04\x1c\x0f\x14\t\t\x14\x0f\x1c\x049ck\x80U\x02?\xbc\x85\x85\xbc\x85\xfe\xe6@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x128\x0f\x15\x15\x0f8\x0f\x15\x15\x01\v@\x0e\x12\x12\x0e@\x0e\x12\x12\x01N\xfb@B^`\x0e\x12\x12\x0e``\x0e\x12\x12\x0e`^B\x04\xc0B^^\x00\x00\a\x00\x00\xff\x80\b\x00\x05\x80\x00\x19\x00!\x001\x00A\x00Q\x00u\x00\x85\x00\x00\x00\x14\x06#!\"&4>\x023\x1e\x042>\x0372\x1e\x01\x02\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x165\x15\x14\x06#!\"&=\x01463!2\x16\x13\x114&#!\"\x06\x15\x11\x14\x163!546;\x012\x16\x1d\x01!546;\x012\x16\x1d\x01!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x04\x00J6\xfe\x006J\x12)P9\x060\x1b,***,\x1b0\x069P)\x8b\x85\xbc\x85\x85\xbc\x04\"\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x15\x0f\xfd\xc8\x0f\x15\x15\x0f\x028\x0f\x15\x12\x0e\xfd\xc0\x0e\x12\x12\x0e\x02@\x0e\x12\x80\x13\r\xf9@\r\x13\x13\r\x01`\x12\x0e@\x0e\x12\x03\x00\x12\x0e@\x0e\x12\x01`\r\x13\x80^B\xf9@B^^B\x06\xc0B^\x01ՀUU\x80kc9\x04\x1c\x0f\x14\t\t\x14\x0f\x1c\x049c\x01\xbb\xbc\x85\x85\xbc\x85\xfd`@\x0e\x12\x12\x0e@\x0e\x12\x12\xee8\x0f\x15\x15\x0f8\x0f\x15\x15\xf5@\x0e\x12\x12\x0e@\x0e\x12\x12\xfc2\x04\xc0\r\x13\x13\r\xfb@\r\x13`\x0e\x12\x12\x0e``\x0e\x12\x12\x0e`\x13\x04\xcd\xfb@B^^B\x04\xc0B^^\x00\x00\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x0f\x00\x17\x00(\x00\x00%.\x01'\x0e\x01\"&'\x0e\x01\a\x16\x04 $\x02\x10& \x06\x10\x16 \x00\x10\x02\x06\x04#\"$&\x02\x10\x126$ \x04\x16\x05\xf3\x16\x83wC\xb9ιCw\x83\x16j\x01J\x01~\x01J\x89\xe1\xfe\xc2\xe1\xe1\x01>\x02\xe1\x8e\xef\xfe\xb4\xb7\xb6\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0ś\xcd\x10JSSJ\x10͛\x96\xaf\xaf\x02\xb2\x01>\xe1\xe1\xfe\xc2\xe1\x016\xfe\x94\xfe\xb5\xf1\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x03\x00\x00\xff\x00\a\x00\x06\x00\x00\x10\x00$\x00,\x00\x00\x00 \x04\x16\x12\x15\x14\x02\x06\x04 $&\x02\x10\x126\x01654\x02&$ \x04\x06\x02\x15\x14\x17\x123\x16 72&\x10& \x06\x10\x16 \x02\xca\x01l\x01L\xf0\x8e\x8d\xf0\xfe\xb4\xfe\x92\xfe\xb4\uf38e\xf0\x04m\x95z\xce\xfe\xe4\xfe\xc8\xfe\xe4\xcez\x95B\xf0\x83\x01l\x83\xf0\xa9\xe1\xfe\xc2\xe1\xe1\x01>\x06\x00\x8e\xf0\xfe\xb4\xb6\xb5\xfe\xb4\xf0\x8f\x8e\xf1\x01K\x01l\x01L\xf0\xfbG\xcd\xfa\x9c\x01\x1c\xcezz\xce\xfe\xe4\x9c\xfa\xcd\x01G\x80\x80\xa1\x01>\xe1\xe1\xfe\xc2\xe1\x00\x00\x00\x00\x03\x00\x00\xff\x00\x06\x00\x06\x00\x00\x1f\x00'\x007\x00\x00\x01\x1e\x04\x15\x14\x06#!\"&54>\x037&54>\x022\x1e\x02\x15\x14\x00 \x06\x10\x16 6\x10\x132654\x02'\x06 '\x06\x02\x15\x14\x163\x04\xb1/U]B,ȍ\xfc\xaa\x8d\xc8,B]U/OQ\x8a\xbdн\x8aQ\xfe\x9f\xfe\xc2\xe1\xe1\x01>\xe1+X}\x9d\x93\x91\xfe\x82\x91\x93\x9d}X\x02\xf0\x0e0b\x85Ӄ\x9a\xdbۚ\x83Ӆb0\x0e}\x93h\xbd\x8aQQ\x8a\xbdh\x93\x02\x13\xe1\xfe\xc2\xe1\xe1\x01>\xfa\xe1\x8ff\xef\x01\x14\a\u007f\u007f\a\xfe\xec\xeff\x8f\x00\x00\x00\x00\x04\x00\x00\xff\x00\x05\x00\x06\x00\x00\x11\x00\x19\x00#\x00=\x00\x00\x00\x14\x06#!\"&4>\x023\x16272\x1e\x01\x02\x14\x06\"&462\x01\x11!\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!\x15\x14\x16;\x0126=\x01!2\x16\x04\x00J6\xfe\x006J\x12)Q8P\xd8P8Q)\x88\x87\xbe\x87\x87\xbe\x01\xa1\xfc\x00\x13\r\x03\xc0\r\x13\x80^B\xfc@B^^B\x01`\x12\x0e\xc0\x0e\x12\x01`B^\x01V\x80VV\x80ld9KK9d\x01\xb9\xbc\x85\x85\xbc\x85\xfb\xa0\x05`\xfa\xa0\r\x13\x13\x05\xcd\xfa@B^^B\x05\xc0B^`\x0e\x12\x12\x0e`^\x00\x00\b\x00\x00\xff\x80\b\x00\x05\x80\x00\x13\x00\x1b\x00+\x00;\x00K\x00[\x00e\x00u\x00\x00\x014.\x02#\x06\"'\"\x0e\x02\x15\x14\x163!26\x024&\"\x06\x14\x162\x0154&#!\"\x06\x1d\x01\x14\x163!26\x0154&#!\"\x06\x1d\x01\x14\x163!26%54&+\x01\"\x06\x1d\x01\x14\x16;\x0126\x1154&#!\"\x06\x1d\x01\x14\x163!26\x01!54&#!\"\x06\x15!\x11\x14\x06#!\"&5\x11463!2\x16\x03\x80\x0f\"D/@\xb8@/D\"\x0f?,\x01\xaa,?\x80p\xa0pp\xa0\x04p\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\xfe\x80\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\xf9\x80\a\x00\x12\x0e\xf9@\x0e\x12\a\x80^B\xf9@B^^B\x06\xc0B^\x01D6]W2@@2W]67MM\x01\xa3\xa0pp\xa0p\xfe\xe0@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x01n`\x0e\x12\x12\x0e\xfb@B^^B\x04\xc0B^^\x00\b\x00\x00\xff\x80\b\x00\x05\x80\x00\x13\x00\x1b\x00+\x00;\x00K\x00[\x00e\x00u\x00\x00\x01\x14\x06#!\"&54>\x023\x16272\x1e\x02\x02\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x01463!2\x16%\x15\x14\x06#!\"&=\x01463!2\x16\x05\x15\x14\x06+\x01\"&=\x0146;\x012\x165\x15\x14\x06#!\"&=\x01463!2\x16\x13\x11!\x11\x14\x163!26\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03\x80?,\xfeV,?\x0f\"D/@\xb8@/D\"\x0f\x80p\xa0pp\xa0\x04p\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\xfe\x80\x12\x0e\xfe\xc0\x0e\x12\x12\x0e\x01@\x0e\x12\x01\x80\x12\x0e\xc0\x0e\x12\x12\x0e\xc0\x0e\x12\x12\x0e\xfd@\x0e\x12\x12\x0e\x02\xc0\x0e\x12\x80\xf9\x00\x13\r\x06\xc0\r\x13\x80^B\xf9@B^^B\x06\xc0B^\x01D7MM76]W2@@2W]\x01֠pp\xa0p\xfd\xa0@\x0e\x12\x12\x0e@\x0e\x12\x12\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\x0e@\x0e\x12\x12\xf2@\x0e\x12\x12\x0e@\x0e\x12\x12\xfc\xb2\x04`\xfb\xa0\r\x13\x13\x04\xcd\xfb@B^^B\x04\xc0B^^\x00\x02\x00\x1d\xff\x00\x06\xe2\x06\x00\x00\x1a\x00A\x00\x00\x01\x10\x02#\"\x02\x11\x10\x12327.\x04#\"\a'632\x16\x176\x013\x16\x0e\x03#\".\x02'\x06#\"$&\x0254\x126$32\x1e\x03\x15\x14\x02\a\x1e\x01326\x04\xe7\xd2\xe1\xde\xd0\xd0\xdeJ9\x16\"65I).!1i\xab\x84\xa7CC\x01\x86u\x03\n+I\x8d\\Gw\\B!al\x96\xfe\xe3݇\x87\xde\x01\x1d\x95y\xebǙV\xa1\x8a/]:=B\x02\xed\x01>\x019\xfe\xc6\xfe\xc3\xfe\xc4\xfe\xc9\x11+\x0132\x16\x15\x14\a\x06\a\x06\x15\x10\x17\x16\x17\x1e\x04%\x14\x06#!\"&5463!2\x16\x03\x14\a\x0e\x01\a\x06#\"&54>\x0254'&#\"\x15\x14\x16\x15\x14\x06#\"54654'.\x01#\"\x0e\x01\x15\x14\x16\x15\x14\x0e\x03\x15\x14\x17\x16\x17\x16\x17\x16\x15\x14#\"'.\x0154>\x0354'&'&5432\x17\x1e\x04\x17\x14\x1e\x0532654&432\x17\x1e\x01\x05\x10\a\x0e\x03#\"&54>\x0176\x114&'&'.\x0554632\x17\x16\x12\x17\x16\x01\xc5 \x15\x01\f?c\xe1\xd5'p&\x13 ?b1w{2V\x02\x19\x0e\x14\t\x05?#\x1d\xfb\xc7\x1a&#\x1d\x049\x1a&\xd7C\x19Y'\x10\v\a\x10&.&#\x1d\x11\x03\x0f+\x17B\x03\n\r:\x16\x05\x04\x03 &65&*\x1d2\x10\x01\x01\x12\x06\x1bw\x981GF1\x19\x1d\x1b\x13)2<)<'\x1c\x10\b\x06\x03\b\n\f\x11\n\x17\x1c(\n\x1bBH=\x02ӊ\x13:NT \x10\x1e:O\t\xb7)4:i\x02\x16\v\x13\v\b \x13F~b`\f\x02e\x15!\x03\x0f}\x01\x1c\x01\x88\x01U\x01\x113i\x1b\x13\x1b?fR\xc7\xfa\xfe\xe7\xd2UX\x03\x1a\x10\x19\x16|\x1d'&\x1a\x1d'&\x02I\x86c&Q\x14\n\f\x06\t*2U.L6*\x05\f/\r\x16\x1aL\x0f:\x0f\x19\x15\x199\x01\x04\x04\x020\x1e%>..>%b>+\x14\x05\x05\x02\x03\x10\v+\xc1z7ymlw45)0\x10\t\f\x14\x1d\x1333J@0\x01!\x11!\x15\x16\v\x1c\x17\x19T\x14FL\xa0\x87\xfe\xee\xe5 P]=\x1f\x10\x0fGS\v\xe6\x01-\x83\xd0kwm\x03\x15\f\x17\x11\x14\t\x13!\xa9\x83\xfe\xe4\xac*\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x18\x00(\x00\x00%\x136&\a\x01\x0e\x01\x16\x1f\x01\x016\x17\x16\a\x019\x01\a2?\x01\x17\x16\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04\xa5\x93\t' \xfc\xa0\x1d\x15\x10\x18\xdd\x02\x01\x15\v\a\v\xfea\x10\x17\x16l\xe0@\x02l\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\xe5\x02\xb5,&\f\xfe\xb3\v\x1c\x19\aE\x01C\x0e\b\x05\n\xfe\x89\xe4\x16h\xa5$\x02\x9b\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\x06\x00\x00\xff\x00\x04\x00\x06\x00\x00\r\x00\x1f\x00/\x003\x007\x00;\x00\x00%\x14\x06\"&5467\x113\x11\x1e\x01\x174&'\x114&\"\x06\x15\x11\x0e\x01\x15\x14\x16 67\x14\x00 \x00547\x1146 \x16\x15\x11\x16\x13\x15#5\x13\x15#5\x13\x15#5\x02\x80p\xa0pF:\x80:F\x80D\x00F\x00N\x00V\x00^\x00f\x00n\x00v\x00~\x00\x86\x00\x8e\x00\x96\x00\x9e\x00\x00\x01\x16\x14\a\x01\x06\"/\x01&4?\x01.\x017&#\"\x06\x15\x11!\x114>\x0232\x16\x176\x16\x17762\x17\x022\x16\x14\x06\"&4\x04\"&462\x16\x1462\x16\x14\x06\"&4\x042\x16\x14\x06\"&4\x04462\x16\x14\x06\"$2\x16\x14\x06\"&4\x042\x16\x14\x06\"&4\x04\"&462\x16\x1462\x16\x14\x06\"&4\x04\"&462\x16\x1462\x16\x14\x06\"&4\x042\x16\x14\x06\"&4$2\x16\x14\x06\"&4\x062\x16\x14\x06\"&4\x062\x16\x14\x06\"&4\x05\x99\n\n\xfd\x8e\n\x1a\nR\n\n,H\x138Jfj\x96\xff\x00Q\x8a\xbdhj\xbeG^\xceR,\n\x1a\n!4&&4&\x01Z4&&4&\xa64&&4&\xfd\xa64&&4&\x01\x00&4&&4\x01\x004&&4&\xfd\xa64&&4&\x01Z4&&4&\xa64&&4&\xfe\xda4&&4&\xa64&&4&\xfe\xa64&&4&\x01&4&&4&Z4&&4&Z4&&4&\x05\a\n\x1a\n\xfd\x8e\n\nR\n\x1a\n,[\xe8cG\x96j\xfb\x00\x05\x00h\xbd\x8aQRJ'\x1dA,\n\n\xfe\xa7&4&&4Z&4&&4Z&4&&4Z&4&&444&&4&\x80&4&&4Z&4&&4Z&4&&4Z&4&&4\xda&4&&4Z&4&&4Z&4&&4&&4&&4Z&4&&4Z&4&&4\x00\x11\x00\x00\xff\x00\a\x00\x06\x00\x00\x1d\x00%\x00-\x005\x00=\x00E\x00M\x00}\x00\x85\x00\x8d\x00\x95\x00\x9d\x00\xa5\x00\xad\x00\xb5\x00\xbd\x00\xc5\x00\x00\x01\x15\x14\a\x15\x14\x06+\x01\"&=\x01\x06#!\"'\x15\x14\x06+\x01\"&=\x01&=\x01\x00\x14\x06\"&4626\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462&\x14\x06\"&462&\x14\x06\"&462\x01\x15\x14\x06#!\"&=\x0146;\x01\x114632\x176\x16\x1776\x1f\x01\x16\a\x01\x06/\x01&?\x01.\x017&#\"\x06\x15\x11!2\x16\x00\x14\x06\"&462&\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462&\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462&\x14\x06\"&462\x16\x14\x06\"&462\x06\x80\x80\x12\x0e@\x0e\x12?A\xfd\x00A?\x13\r@\r\x13\x80\x02@\x12\x1c\x12\x12\x1cR\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\x92\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\x04R\x12\x0e\xf9@\x0e\x12\x12\x0e`\x96jlL.h)\x16\v\v*\v\v\xfe\xc6\v\v*\v\v\x16$\t\x1c%35K\x05\xe0\x0e\x12\xfc\x80\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\xd2\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\xd2\x12\x1c\x12\x12\x1c.\x12\x1c\x12\x12\x1c\x92\x12\x1c\x12\x12\x1c\x01\xc0\xc0\xa9u\xc2\x0e\x12\x12\x0ev\x16\x16n\x11\x17\x17\x11\xbau\xa9\xc0\x01\xae\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12\x12\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12\xfd\xe0@\x0e\x12\x12\x0e@\x0e\x12\x02\x80j\x96N\x13\x0e \x16\v\v*\v\v\xfe\xc6\v\v*\v\v\x16.t2#K5\xfd\x80\x12\x01\xc0\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12R\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12R\x1c\x12\x12\x1c\x12.\x1c\x12\x12\x1c\x12\x12\x1c\x12\x12\x1c\x12\x00\x00\x00\x04\x00\x01\xff\x00\x06\x00\x05\xfe\x00\r\x00@\x00H\x00q\x00\x00\x01\x14\a\x06\a\x06 '&'&54 \x01\x14\x00\a\x06&76767676\x1254\x02$\a\x0e\x03\x17\x16\x12\x17\x16\x17\x16\x17\x1e\x01\x17\x16\x06'.\x01\x0276\x126$76\x04\x16\x12\x04\x14\x06\"&462\x01\x14\x06\a\x06&'&'&7>\x0154.\x01\a\x0e\x01\a\x06\x16\x17\x16\a\x06\a\x0e\x01'.\x017>\x0276\x1e\x01\x03\xe2\x11\x1f\x18\x16\xfe\xfc\x16\x18\x1f\x11\x01\xc0\x02\x1e\xfe\xf4\xd8\b\x0e\x01\a\x03\x04\x02\x01\b\x9f\xc1\xb6\xfeȵ|\xe2\xa1_\x01\x01ğ\a\x02\x03\x03\x01\b\x02\x01\x0f\b\x94\xe2y\b\av\xbf\x01\x03\x8f\xa4\x01/ۃ\xfd\u20fa\x83\x83\xba\x01\xa3k]\b\x10\x02\x06\x17\a\n:Bu\xc6q\x85\xc0\r\nCA\n\a\x18\x05\x02\x10\b_k\x02\x03\x84ނ\x90\xf8\x91\x01XVo\xd7bZZb\xd7nW\xa8\x01\x00\xf0\xfe|V\x03\f\t0\x12 \x0f\t\x03Q\x012\xb8\xb4\x01-\xa8\n\al\xad\xe7}\xb8\xfe\xcfO\x03\t\x15\x18\t/\f\t\f\x04:\xdf\x011\xa7\x8f\x01\x05\xc1z\t\nq\xd0\xfe\xdb%\xba\x83\x83\xba\x83\xff\x00z\xd5G\x06\b\n4(\n\n6\x92Ro\xbaa\f\x0fą\\\xa8<\n\n)4\t\b\x06J\xda}\x83\xe2\x89\x06\a\x86\xf1\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00\x03\x00\x13\x00\x00%!\x11!\x01\x11\x14\x06#!\"&5\x11463!2\x16\x01\x00\x05\x00\xfb\x00\x06\x00^B\xfa@B^^B\x05\xc0B^\x80\x03\x00\x01`\xfb@B^^B\x04\xc0B^^\x00\x01\x00\x00\xff\x80\a\x00\x01\x80\x00\x0f\x00\x00%\x15\x14\x06#!\"&=\x01463!2\x16\a\x00^B\xfa@B^^B\x05\xc0B^\xe0\xc0B^^B\xc0B^^\x00\x00\x00\x03\x00\x00\xff\x00\b\x00\x06\x00\x00\x03\x00\f\x00&\x00\x00)\x01\x11)\x02\x11!\x1132\x16\x15\x01\x11\x14\x06#!\x11\x14\x06#!\"&5\x11463!\x11463!2\x16\x01\x00\x03\x00\xfd\x00\x04\x00\x02\x00\xfd\x00`B^\x03\x00^B\xfd\xa0^B\xfc@B^^B\x02`^B\x03\xc0B^\x02\x00\x03\x00\xff\x00^B\x02\x00\xfc@B^\xfe\xa0B^^B\x03\xc0B^\x01`B^^\x00\x00\x00\x02\x00\x00\xff\x80\a\x00\x05\x80\x00#\x003\x00\x00%764/\x01764/\x01&\"\x0f\x01'&\"\x0f\x01\x06\x14\x1f\x01\a\x06\x14\x1f\x01\x162?\x01\x17\x162\x01\x11\x14\x06#!\"&5\x11463!2\x16\x04\x97\x92\n\n\xe9\xe9\n\n\x92\n\x1a\n\xe9\xe9\n\x1a\n\x92\n\n\xe9\xe9\n\n\x92\n\x1a\n\xe9\xe9\n\x1a\x02s^B\xfa@B^^B\x05\xc0B^ג\n\x1a\n\xe9\xe9\n\x1a\n\x92\n\n\xe9\xe9\n\n\x92\n\x1a\n\xe9\xe9\n\x1a\n\x92\n\n\xe9\xe9\n\x04\x13\xfb@B^^B\x04\xc0B^^\x00\x03\x00\x00\xff\x80\a\x00\x05\x80\x00#\x00'\x007\x00\x00\x01\a\x06\"/\x01\a\x06\"/\x01&4?\x01'&4?\x0162\x1f\x01762\x1f\x01\x16\x14\x0f\x01\x17\x16\x14\x01!\x11!%\x11\x14\x06#!\"&5\x11463!2\x16\x04\xe9\x92\n\x1a\n\xa9\xa9\n\x1a\n\x92\n\n\xa9\xa9\n\n\x92\n\x1a\n\xa9\xa9\n\x1a\n\x92\n\n\xa9\xa9\n\xfc\r\x05\x00\xfb\x00\x06\x00^B\xfa@B^^B\x05\xc0B^\x01\xa9\x92\n\n\xa9\xa9\n\n\x92\n\x1a\n\xa9\xa9\n\x1a\n\x92\n\n\xa9\xa9\n\n\x92\n\x1a\n\xa9\xa9\n\x1a\xfe\xcd\x04\x00`\xfb@B^^B\x04\xc0B^^\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x03\x00\x13\x00\x00\t\x01!\x01\x00\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x04.\x012\xfdr\xfe\xce\x05`\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x01f\x024\xfd\xcc\x01\xd0\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\x00\a\x00\x00\xff\x00\a\x02\x06\x00\x00\a\x00\x13\x00#\x00.\x00C\x00\xc4\x00\xd4\x00\x00\x01&\x0e\x01\x17\x16>\x01\x05\x06\"'&4762\x17\x16\x14\x17\a\x06\"/\x01&4?\x0162\x1f\x01\x16\x14'\x06\"'&4762\x16\x14%\x0e\x01'.\x01>\x02\x16\x17\x1e\a\x0e\x01\x136.\x02'.\x01\a>\x01\x1f\x016'>\x01/\x01>\x0176&'&\x06\a\x0e\x01\x1e\x01\x17.\x01'&7&'\"\a>\x01?\x014'.\x01\x06\a67\x06\x1e\x01\x17\x06\a\x0e\x01\x0f\x01\x0e\x01\x17\x16\x17\x06\a\x06\x14\x167>\x017.\x02\a>\x043\x167654'\x16\a\x0e\x01\x0f\x01\x0e\x05\x16\x17&'\x0e\x04\x16\x17\x166\x127>\x017\x16\x17\x1676\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x05\v\x0f(\f\v\x0e4\x10\xfeZ\b\x17\a\b\b\a\x17\b\a\x9e#\f#\r&\f\f#\f#\r&\fy\a\x17\b\a\a\b\x16\x10\x01\x8b\"\x936&.\x04JM@&\x02\x16\a\x13\x06\x0e\x03\x05\x03\a\xc3\x03\x17 \"\x06(XE\x13*\f\f\x02$\x06\x01\x03\x03+8\x06\njT\x01?\x013\x03\x13#'.\x01'&!\x11\x14\x163!2>\x04?\x013\x06\x02\a.\x01'#!\x0557>\x017\x13\x12'.\x01/\x015\x05!27\x0e\x01\x0f\x01#'.\x01#!\"\x06\x02\x06g\xb1%%D-\x11!g\x0e\ag\x1d\x0f<6W\xfe\xf7WZ\x01e#1=/2*\x12]Y\x063\x05\x92\xeb-,\xfd\x8c\xfe\x88\u007fC1\x01\b\x03\v\x02/D\u007f\x01x\x02\xbe\x8b\xeb\x06\x10\x04\x05] \x1fVF\xfd\xdc\x1c\x0f\x05I\xfdq\x01\x05\x03\x03\x02-H\x8e\xfe\xbe\xfe\xc1\u007fD2\x01\b\xfd\xd4NK\x04\v\x19'>*\xd8%\xfeR=\x05\x06\x01\ff\x19\r07\x02\x83\x01\x92\xf3=.\r\x18f\f\x1bD\xfd]\\|yu\x11\x00\x00\a\x00\x00\xff\x80\x06\x00\x05\x80\x00\x11\x00,\x000\x00>\x00S\x00e\x00u\x00\x00\x01\x15\x14\x16\x0e\x04#\x112\x1e\x03\x1c\x01\x05\x15\x14\x16\x0e\x02#\"'&5<\x03>\x0232\x1e\x03\x1c\x01\x053\x11#\x013\x11#\a&'#\x113\x11\x133\x13\x054'.\x05\"#\"+\x01\x1123\x166'&\x0554.\x02#\"\a5#\x1137\x16326\x13\x11\x14\x06#!\"&5\x11463!2\x16\x03\x9a\x01\x01\x02\x05\b\x0e\t\t\x0e\b\x05\x02\x01<\x01\x01\x04\v\b\t\x05\x04\x03\x04\x06\x05\x06\b\x05\x03\x01\xfb\xdezz\x01\xb2j\x9f\x1c\x14\f\x9ek-L+\x01\xa9\x05\x03\x10\x12 \x15)\x11\x15\b\x04[\x14$\xa98\x03\x01\x01=\x04\x0f\"\x1d.\x1fun\a\x1e/2 \xb4^B\xfb@B^^B\x04\xc0B^\x02\xe3\xb6\x04\x16\b\x10\a\b\x03\x015\x02\b\x03\x10\x05\x16cy\x01\x17\b\x0f\x06\t\n\x9b\x02\n\a\v\x06\b\x03\x03\x06\x06\v\x05\x0e\xee\x01\xd8\xfe(\x01\xd8ݔI\xfe(\x018\xfe\xc8\x01?\x0eC\x17\x10\x19\x10\f\x05\x03\xfe(\x013\x9b>\x9f\x85\x1d #\x0f\"\x9a\xfe(\x1e$=\x03\x12\xfb@B^^B\x04\xc0B^^\x00\x00\x00\x00\x05\x000\xff\x02\bK\x05\xf8\x00\f\x00\x15\x00\x1a\x00S\x00\x8f\x00\x00\x05&'.\x04'&'\x16\x00\x01\x17.\x01/\x01\x06\a\x16\x13\x06\a67\x014\x02&$#\"\x04\a\x06\a>\x03\x1f\x01\x1e\x03\a&\x0e\x02\a\x1e\x02\x17\x16>\x02?\x01>\x01\x16\x17\x16\a\x06\x05\x06'\x1e\x03\x1f\x01\x1676\x12\x13\x06\a\x06\x02\a\x06\a\x06'\x06# \x00\x03\"&#\x06\x1e\x02\x1f\x01\x16\x17.\x03/\x01.\x06'\x1e\x02\x177676767>\x0176$\x04\x17\x16\x12\x04w\x06\x05\r.~ku\x1f\x11\x9eB\x01R\xfe]\xa8\x19 \x03\x04T%\x05z+\",\x1e\x05\xa0|\xd3\xfeޟ\x93\xfe\xf4j\x1e\x0f<\xa6\x97\x87)(!(\t\x04\x03~ˣzF\x04\x0f8\"{\xf9\xb4\x91%%\x16#\x1a\x04\x0e5\xd0\xfe\xfd\x87\xb6)\x8a\x88}''\x8fx\xc3\xeeJ\x0e\x1aF\xdf\xcf0\"H[$%\xfe\xe5\xfeEJ\x01\x06\x02\x06\x11#%\r\x0e\b.Gk2\x1d\x03\x02\x059(B13\"\b\x13?\xa3@\x02\vS)\x87\x1c5\x0f\" \x9e\x01#\x019\x96\xdc\xe2\xc5\x01\x03\b\x1edm\xabW\x03\"\xd5\xfe\xd6\x02;\x1cL\xb765R\x8eA\x020@T.\x16\xfe\x9e\xa1\x01$\xd4}i`:f3A\x15\x06\x04\x03\x01\x1d%%\n\v\x15BM<$q\xf3:\x06)BD\x19\x18\x10\t\x13\x19a\x18a%\x14\x04`\xa1]A\v\f\x17&c\x01|\x01\t\x87M\xd0\xfe\xebs!\v\x1a\n\x03\x01Z\x01\r\x012}i[\x1a\x1a\fF&\x89\x8f\x83**\x02\x15\x0f\x1a\x18\x1b\x1b\f\n\x1f<\b \x95\x8dʣsc\x1c\"\x0fJ<&Ns\xfeF\x00\x05\x00%\xff\f\x06\xd8\x05\xf4\x00\x17\x000\x00@\x00W\x00m\x00\x00\x016&'.\x01\x06\a\x06\x16\x17\x1e\x02\x17\x1e\a6\x01\x0e\x02\x04$.\x01\x027>\x037\x06\x1a\x01\f\x01$76\a\x14\x02\x14\x0e\x02\".\x024>\x022\x1e\x01\x05.\x01,\x01\f\x01\x06\x02\x17&\x02>\x04\x1e\x02\x17\x1e\x01\x036\x00'\"'&7\x1e\x04\x0e\x03\a>\x03\x05=\x1dGV:\x87e\x12\f\x0f#\x17\x1f:\x1b$?+%\x18\x14\r\v\n\x01q4\xc1\xec\xfe\xf2\xfe\xfa\xf0\xb4g\x05\x01\x0f\n&\x043h\xf2\x01T\x01`\x01Zt\x14\x02\xf3Q\x88\xbcм\x88QQ\x88\xbcм\x88\x01pA\xe7\xfe\xed\xfe\xcb\xfe\xdb\xfe\xfe\xb6P\x1e1\x05L\x8e\xbd\xe1\xef\xf6\xe2\xceK!:<\f\xfe\xd7\xf8\b\x02\x02\x1a}҈`\x15\x17d\x91\xe1\x88l\xbb\xa1b\x02\xf0,\xab9'\x1d\x14\x1b\x17\n\x05\x03\x04\x0f\n\r%%($!\x18\r\x01\xfd\xcb\u007f\xbaa\x183\x83\xc0\x01\x17\xa4)W)x\r\xd0\xfe\x86\xfe\xfe\x9a\f\xa1\xa4\x1b\r\x04\x02\x1fо\x8aQQ\x8a\xbeо\x8aQQ\x8a\x06\x93\xd0c\bQ\xb1\xf6\xfe\xa4ǡ\x01-\xf4җe)\x17U\xa4s2\x8e\xfe\x81\xf4\x01XD\x05\x05\x03\x04\\\x94\xbd\xd1ϼ\x92Y\x02\x1ed\x92\xcf\x00\x00\x00\x00\v\x00\x00\xff\x80\x06\x00\x06\x00\x00\x0f\x00\x1f\x00/\x00?\x00O\x00_\x00o\x00\u007f\x00\x8f\x00\x9f\x00\xaf\x00\x00\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543\x13\x15#\"=\x01#\"=\x014;\x01543%\x11\x14\x06#!\"&5\x11463!2\x16\x01\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x01325\x15\x14+\x01\x15\x14+\x01532\x1d\x0132\xc0p\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10\x04\xb08(\xfc\xc0(88(\x03@(8\x01\x00\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x100\x10pp\x100\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\x01\x00\x80\x10\x10\x10 \x10\x10\x10\xa0\xfa@(88(\x05\xc0(88\xfb\b \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\xf0 \x10\x10\x10\x80\x10\x10\x00\x00\x00\x00\x01\x00/\xff\x00\x06Q\x06\x00\x00\x90\x00\x00\x01\a\x17\x1e\x01\a\x0e\x01/\x01\x17\x16\x06&'\x03%\x11\x17\x1e\x01\x0e\x01&/\x01\x15\x14\x06\"&=\x01\a\x0e\x01.\x016?\x01\x11\x05\x03\x0e\x01&?\x01\a\x06&'&6?\x01'.\x01>\x01\x17\x05-\x01\x05\x06#\".\x016?\x01'.\x01>\x01\x1f\x01'&6\x16\x17\x13\x05\x11'.\x01>\x01\x16\x1f\x015462\x16\x1d\x017>\x01\x1e\x01\x06\x0f\x01\x11%\x13>\x01\x16\x0f\x0176\x16\x17\x16\x06\x0f\x01\x17\x1e\x01\x0e\x01#\"'%\r\x01%6\x1e\x01\x06\x06\x1e\xa7\xba\x17\r\r\x0e2\x17\xba7\r2G\rf\xfe\xf1\xd0\x10\x02\x18!)\x10p&4&p\x10)!\x18\x02\x10\xd0\xfe\xf1f\rG2\r7\xba\x172\x0e\r\r\x17\xba\xa7\x1d\x1a\t*\x1d\x016\x01\x0f\xfe\xf1\xfe\xca\x04\t\x1b\"\x04\x1a\x1b\xa7\xba\x17\r\x1a4\x16\xba7\r2G\rf\x01\x0f\xd0\x10\x02\x18!)\x10p&4&p\x10)!\x18\x02\x10\xd0\x01\x0ff\rG2\r7\xba\x172\x0e\r\r\x17\xba\xa7\x1b\x1a\x04\"\x1b\t\x04\xfe\xca\xfe\xf1\x01\x0f\x016\x1d*\t\x1a\x01\xa3!k\r3\x17\x17\r\rj\xa0&3\n%\x01,\x9c\xfe\xc7\xee\x12*\x1f\x13\b\x12\x80\xd6\x1a&&\x1aր\x12\b\x13\x1f*\x12\xee\x019\x9c\xfe\xd4%\n3&\xa0j\r\r\x17\x173\rk!\x06./!\x06>\x9d\x9d>\x01$,*\x05!k\r3.\x0e\x0ej\xa0&3\n%\xfeԜ\x019\xee\x12*\x1f\x13\b\x12\x80\xd6\x1a&&\x1aր\x12\b\x13\x1f*\x12\xee\xfeǜ\x01,%\n3&\xa0j\r\r\x17\x173\rk!\x05*,$\x01>\x9d\x9d>\x06!/.\x00\x00\x00\x00\x02\x00\x00\xff\x00\a\x00\x06\x00\x00\x12\x00&\x00\x00\x016.\x02'&\x0e\x02\a\x06\x1e\x02\x17\x16$\x12\t\x01\x16\x12\a\x06\x02\x04\a\x05\x01&\x0276\x12$76$\x05\xc1\aP\x92\xd0utۥi\a\aP\x92\xd1u\x9b\x01\x14\xac\x01G\xfe\xa3xy\n\v\xb6\xfeԶ\xfc\x19\x01[xy\n\v\xb6\x01-\xb6\xa7\x02\x9a\x02_v١e\a\aN\x8f\xcfuv١e\a\t\x88\x00\xff\x04=\xfe\xa4u\xfeʦ\xb7\xfe\xc8\xc7\x19\x84\x01[t\x017\xa6\xb8\x018\xc7\x19\x16X\x00\x06\x00\x00\xff\x00\a\x00\x06\x00\x00\n\x00\x0e\x00\x12\x00\x16\x00&\x006\x00\x00\x01\x13#\v\x01#\x13'7\x17\a\x01\x05\x03-\x01\x17\a'%\x17\a'\x04\x10\x02&$ \x04\x06\x02\x10\x12\x16\x04 $6\x12\x10\x02\x06\x04 $&\x02\x10\x126$ \x04\x16\x03\xb4\xa33\xaf\xab1\xb3N\x15\xf0\x15\xfeE\x010\x82\xfe\xd0\x01\xda\xf0g\xef\x01\u007f\xbfR\xbe\x02=|\xd3\xfe\xde\xfe\xc2\xfe\xde\xd3||\xd3\x01\"\x01>\x01\"\xd3\xec\x8e\xf0\xfe\xb4\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x01\xfc\xfe\xb7\x01^\xfe\xa2\x01v!1f2\x02i\x82\xfeЂwg\xeffZQ\xbeQ^\x01>\x01\"\xd3||\xd3\xfe\xde\xfe\xc2\xfe\xde\xd3||\xd3\x02w\xfe\x94\xfe\xb4\xf0\x8e\x8e\xf0\x01L\x01l\x01L\xf0\x8e\x8e\xf0\x00\f\x00&\xff\x01\aZ\x05\xff\x00X\x00b\x00l\x00w\x00\x81\x00\xab\x00\xb7\x00\xc2\x00\xcd\x00\xd8\x00\xe4\x00\xee\x00\x00\x01.\x03'&>\x01'&'&\x0f\x01\x0e\x03\".\x01'.\x06'&\x06\a\x0e\x03&'&'&\x06\a\x0e\x03\x15\x06\x167>\x0176\x127>\x01\x17\x16\a\x0e\x01\a\x06\x1667>\x0276\x172\a\x06\x02\a\x06\x16\x17\x1e\x026\x04\x16\x06\a\x06&'&>\x01\x01\x16\x0e\x01&'&>\x01\x16\x00\x0e\x01'.\x017>\x01\x17\x16\x01\x16\x0e\x01.\x01676\x16\x13\x16\x02\a\x06'\x0e\x01&'\x06\a\x06&'&'.\x0267.\x01>\x017>\x02\x16\x176\x1e\x03\a\x1e\x02\x06\x01\x16\x06\a\x06&'&676\x16\x13\x16\x0e\x01&'&676\x16\x01\x16\x06\a\x06.\x01676\x16\x01\x16\x06\a\x06&'&>\x01\x16\x01\x16\x06\a\x06&'&676\x16'\x16\x06\a\x06.\x01>\x01\x16\x056\x04/4-\x03\x05LJ\x05\x0eg-\x1e\x03\x04\x02\a\x03\a\x05\a\x03\x03\f\x06\v\b\v\v\x06\x1e$\x1b\x01\x10\t\x15\f\v6\x1e)j\x17\x102%+\x16QF\x1e)\x12\a\x90\x05\x06\x1f\x0e\x1b\x06\x02b\x01\x063F\x14\x04SP\x06\x14\x15\x1d\x04\x02\u007f\a\f21\x11DK2\xfcA\x06\x10\x0f\x0e\x19\x03\x03\x10\x1c\x02W\f\a\")\f\v\a\")\xfd\x15$?\x1a\x1a\f\x12\x12?\x1a\x1a\x05\x04\x13\f8A&\f\x1b\x1cA\x84E5lZm\x14\x81\x9e=\f\x01g\xf4G2\x03Sw*&>$\x045jD \x86\x9f\xb1GH\x88yX/\x064F\x15 \xfbr\x0e\t\x14\x131\r\x0e\t\x14\x131\xac\x04\x12\"\x1c\x04\x03\x13\x10\x11\x1c\x04\xa5\x04\x15\x14\x13\"\b\x15\x14\x14!\xfdl\x10\x0f\x1c\x1b=\x10\x10\x0f6>\x02\xfa\x04\x10\x0f\x0f\x19\x03\x03\x10\x0f\x0e\x19\xbc\x0f\t\x16\x166\x1e\n,5\x01.\x18\x14\x01\x18\x1a/\xb9\xb1'e\x02\x01\x11\x02\x02\x01\x03\x01\x03\x04\x03\x02\r\x05\n\x05\x06\x03\x01\x05\x10\x17\x01\x0f\a\r\x02\x02\x1b\r\x12.*\x1c\x8d|\x90\x01Ed\x04\x02\x1a!\r\x01u\b\v\x0e\a\x0f&\x12\xf3\v&%\x17&\b\xa8\x9f\t\x1d\x01&\x10\xfe\xf9\x1c5d\x18\t\r\x03\x1f\xa8\x1e\x19\x03\x03\x10\x0f\x0e\x1a\x06\xfe\xda\x11)\x18\b\x11\x11)\x18\b\x0366\f\x13\x12@\x1a\x1b\f\x12\x13\xfd\x01\x1cC&\f8B\x14\x13\f\x02@q\xfe\xf9L?\x03P^\x057\t\x01G-hI[\x0eq\x8f\xa1:<\x88rS\tU~9\x177\x15\aA_\x87I\x10R`g\x02p\x141\x0e\x0e\t\x14\x141\x0e\x0e\t\x01\x05\x10\x1d\b\x13\x11\x11\x1c\x04\x04\x13\xfc;\x14\"\x04\x04\x15(\"\x05\x04\x17\x03j\x1b?\x10\x10\x0f\x1b\x1c>\"\x10\xfdT\x0f\x19\x04\x03\x11\x0e\x0f\x1a\x03\x03\x10\xe2\x166\x10\x0f\n,6 \n\x00\x00\x00\x18\x01&\x00\x01\x00\x00\x00\x00\x00\x00\x00/\x00`\x00\x01\x00\x00\x00\x00\x00\x01\x00\v\x00\xa8\x00\x01\x00\x00\x00\x00\x00\x02\x00\a\x00\xc4\x00\x01\x00\x00\x00\x00\x00\x03\x00\x11\x00\xf0\x00\x01\x00\x00\x00\x00\x00\x04\x00\v\x01\x1a\x00\x01\x00\x00\x00\x00\x00\x05\x00\x12\x01L\x00\x01\x00\x00\x00\x00\x00\x06\x00\v\x01w\x00\x01\x00\x00\x00\x00\x00\a\x00Q\x02'\x00\x01\x00\x00\x00\x00\x00\b\x00\f\x02\x93\x00\x01\x00\x00\x00\x00\x00\t\x00\n\x02\xb6\x00\x01\x00\x00\x00\x00\x00\v\x00\x15\x02\xed\x00\x01\x00\x00\x00\x00\x00\x0e\x00\x1e\x03A\x00\x03\x00\x01\x04\t\x00\x00\x00^\x00\x00\x00\x03\x00\x01\x04\t\x00\x01\x00\x16\x00\x90\x00\x03\x00\x01\x04\t\x00\x02\x00\x0e\x00\xb4\x00\x03\x00\x01\x04\t\x00\x03\x00\"\x00\xcc\x00\x03\x00\x01\x04\t\x00\x04\x00\x16\x01\x02\x00\x03\x00\x01\x04\t\x00\x05\x00$\x01&\x00\x03\x00\x01\x04\t\x00\x06\x00\x16\x01_\x00\x03\x00\x01\x04\t\x00\a\x00\xa2\x01\x83\x00\x03\x00\x01\x04\t\x00\b\x00\x18\x02y\x00\x03\x00\x01\x04\t\x00\t\x00\x14\x02\xa0\x00\x03\x00\x01\x04\t\x00\v\x00*\x02\xc1\x00\x03\x00\x01\x04\t\x00\x0e\x00<\x03\x03\x00C\x00o\x00p\x00y\x00r\x00i\x00g\x00h\x00t\x00 \x00D\x00a\x00v\x00e\x00 \x00G\x00a\x00n\x00d\x00y\x00 \x002\x000\x001\x006\x00.\x00 \x00A\x00l\x00l\x00 \x00r\x00i\x00g\x00h\x00t\x00s\x00 \x00r\x00e\x00s\x00e\x00r\x00v\x00e\x00d\x00.\x00\x00Copyright Dave Gandy 2016. All rights reserved.\x00\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00FontAwesome\x00\x00R\x00e\x00g\x00u\x00l\x00a\x00r\x00\x00Regular\x00\x00F\x00O\x00N\x00T\x00L\x00A\x00B\x00:\x00O\x00T\x00F\x00E\x00X\x00P\x00O\x00R\x00T\x00\x00FONTLAB:OTFEXPORT\x00\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00FontAwesome\x00\x00V\x00e\x00r\x00s\x00i\x00o\x00n\x00 \x004\x00.\x007\x00.\x000\x00 \x002\x000\x001\x006\x00\x00Version 4.7.0 2016\x00\x00F\x00o\x00n\x00t\x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00FontAwesome\x00\x00P\x00l\x00e\x00a\x00s\x00e\x00 \x00r\x00e\x00f\x00e\x00r\x00 \x00t\x00o\x00 \x00t\x00h\x00e\x00 \x00C\x00o\x00p\x00y\x00r\x00i\x00g\x00h\x00t\x00 \x00s\x00e\x00c\x00t\x00i\x00o\x00n\x00 \x00f\x00o\x00r\x00 \x00t\x00h\x00e\x00 \x00f\x00o\x00n\x00t\x00 \x00t\x00r\x00a\x00d\x00e\x00m\x00a\x00r\x00k\x00 \x00a\x00t\x00t\x00r\x00i\x00b\x00u\x00t\x00i\x00o\x00n\x00 \x00n\x00o\x00t\x00i\x00c\x00e\x00s\x00.\x00\x00Please refer to the Copyright section for the font trademark attribution notices.\x00\x00F\x00o\x00r\x00t\x00 \x00A\x00w\x00e\x00s\x00o\x00m\x00e\x00\x00Fort Awesome\x00\x00D\x00a\x00v\x00e\x00 \x00G\x00a\x00n\x00d\x00y\x00\x00Dave Gandy\x00\x00h\x00t\x00t\x00p\x00:\x00/\x00/\x00f\x00o\x00n\x00t\x00a\x00w\x00e\x00s\x00o\x00m\x00e\x00.\x00i\x00o\x00\x00http://fontawesome.io\x00\x00h\x00t\x00t\x00p\x00:\x00/\x00/\x00f\x00o\x00n\x00t\x00a\x00w\x00e\x00s\x00o\x00m\x00e\x00.\x00i\x00o\x00/\x00l\x00i\x00c\x00e\x00n\x00s\x00e\x00/\x00\x00http://fontawesome.io/license/\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc3\x00\x00\x00\x01\x00\x02\x00\x03\x00\x8e\x00\x8b\x00\x8a\x00\x8d\x00\x90\x00\x91\x00\x8c\x00\x92\x00\x8f\x01\x02\x01\x03\x01\x04\x01\x05\x01\x06\x01\a\x01\b\x01\t\x01\n\x01\v\x01\f\x01\r\x01\x0e\x01\x0f\x01\x10\x01\x11\x01\x12\x01\x13\x01\x14\x01\x15\x01\x16\x01\x17\x01\x18\x01\x19\x01\x1a\x01\x1b\x01\x1c\x01\x1d\x01\x1e\x01\x1f\x01 \x01!\x01\"\x01#\x01$\x01%\x01&\x01'\x01(\x01)\x01*\x01+\x01,\x01-\x01.\x01/\x010\x011\x012\x013\x014\x015\x016\x017\x018\x019\x01:\x01;\x01<\x01=\x01>\x01?\x01@\x01A\x01B\x01C\x01D\x01E\x01F\x01G\x01H\x01I\x01J\x01K\x01L\x01M\x01N\x01O\x01P\x01Q\x01R\x01S\x01T\x01U\x01V\x01W\x01X\x01Y\x01Z\x01[\x01\\\x01]\x01^\x01_\x01`\x01a\x01b\x00\x0e\x00\xef\x00\r\x01c\x01d\x01e\x01f\x01g\x01h\x01i\x01j\x01k\x01l\x01m\x01n\x01o\x01p\x01q\x01r\x01s\x01t\x01u\x01v\x01w\x01x\x01y\x01z\x01{\x01|\x01}\x01~\x01\u007f\x01\x80\x01\x81\x01\x82\x01\x83\x01\x84\x01\x85\x01\x86\x01\x87\x01\x88\x01\x89\x01\x8a\x01\x8b\x01\x8c\x01\x8d\x01\x8e\x01\x8f\x01\x90\x01\x91\x01\x92\x01\x93\x01\x94\x01\x95\x01\x96\x01\x97\x01\x98\x01\x99\x01\x9a\x01\x9b\x01\x9c\x01\x9d\x01\x9e\x01\x9f\x01\xa0\x01\xa1\x01\xa2\x01\xa3\x01\xa4\x01\xa5\x01\xa6\x01\xa7\x01\xa8\x01\xa9\x01\xaa\x01\xab\x01\xac\x01\xad\x01\xae\x01\xaf\x01\xb0\x01\xb1\x01\xb2\x01\xb3\x01\xb4\x01\xb5\x01\xb6\x01\xb7\x01\xb8\x01\xb9\x01\xba\x01\xbb\x01\xbc\x01\xbd\x01\xbe\x01\xbf\x01\xc0\x01\xc1\x01\xc2\x01\xc3\x01\xc4\x01\xc5\x01\xc6\x01\xc7\x01\xc8\x01\xc9\x01\xca\x01\xcb\x01\xcc\x01\xcd\x01\xce\x01\xcf\x01\xd0\x01\xd1\x01\xd2\x01\xd3\x01\xd4\x01\xd5\x01\xd6\x01\xd7\x01\xd8\x01\xd9\x01\xda\x01\xdb\x01\xdc\x01\xdd\x01\xde\x01\xdf\x01\xe0\x01\xe1\x01\xe2\x01\xe3\x01\xe4\x01\xe5\x01\xe6\x01\xe7\x01\xe8\x01\xe9\x01\xea\x01\xeb\x01\xec\x01\xed\x01\xee\x01\xef\x01\xf0\x01\xf1\x01\xf2\x01\xf3\x01\xf4\x01\xf5\x01\xf6\x01\xf7\x01\xf8\x01\xf9\x01\xfa\x01\xfb\x01\xfc\x01\xfd\x01\xfe\x01\xff\x02\x00\x02\x01\x02\x02\x02\x03\x02\x04\x02\x05\x02\x06\x02\a\x02\b\x00\"\x02\t\x02\n\x02\v\x02\f\x02\r\x02\x0e\x02\x0f\x02\x10\x02\x11\x02\x12\x02\x13\x02\x14\x02\x15\x02\x16\x02\x17\x02\x18\x02\x19\x02\x1a\x02\x1b\x02\x1c\x02\x1d\x02\x1e\x02\x1f\x02 \x02!\x02\"\x02#\x02$\x02%\x02&\x02'\x02(\x02)\x02*\x02+\x02,\x02-\x02.\x02/\x020\x021\x022\x023\x024\x025\x026\x027\x028\x029\x02:\x02;\x02<\x02=\x02>\x02?\x02@\x02A\x02B\x02C\x02D\x02E\x02F\x02G\x02H\x02I\x02J\x02K\x02L\x02M\x02N\x02O\x02P\x02Q\x02R\x02S\x00\xd2\x02T\x02U\x02V\x02W\x02X\x02Y\x02Z\x02[\x02\\\x02]\x02^\x02_\x02`\x02a\x02b\x02c\x02d\x02e\x02f\x02g\x02h\x02i\x02j\x02k\x02l\x02m\x02n\x02o\x02p\x02q\x02r\x02s\x02t\x02u\x02v\x02w\x02x\x02y\x02z\x02{\x02|\x02}\x02~\x02\u007f\x02\x80\x02\x81\x02\x82\x02\x83\x02\x84\x02\x85\x02\x86\x02\x87\x02\x88\x02\x89\x02\x8a\x02\x8b\x02\x8c\x02\x8d\x02\x8e\x02\x8f\x02\x90\x02\x91\x02\x92\x02\x93\x02\x94\x02\x95\x02\x96\x02\x97\x02\x98\x02\x99\x02\x9a\x02\x9b\x02\x9c\x02\x9d\x02\x9e\x02\x9f\x02\xa0\x02\xa1\x02\xa2\x02\xa3\x02\xa4\x02\xa5\x02\xa6\x02\xa7\x02\xa8\x02\xa9\x02\xaa\x02\xab\x02\xac\x02\xad\x02\xae\x02\xaf\x02\xb0\x02\xb1\x02\xb2\x02\xb3\x02\xb4\x02\xb5\x02\xb6\x02\xb7\x02\xb8\x02\xb9\x02\xba\x02\xbb\x02\xbc\x02\xbd\x02\xbe\x02\xbf\x02\xc0\x02\xc1\x02\xc2\x02\xc3\x02\xc4\x02\xc5\x02\xc6\x02\xc7\x02\xc8\x02\xc9\x02\xca\x02\xcb\x02\xcc\x02\xcd\x02\xce\x02\xcf\x02\xd0\x02\xd1\x02\xd2\x02\xd3\x02\xd4\x02\xd5\x02\xd6\x02\xd7\x02\xd8\x02\xd9\x02\xda\x02\xdb\x02\xdc\x02\xdd\x02\xde\x02\xdf\x02\xe0\x02\xe1\x02\xe2\x02\xe3\x02\xe4\x02\xe5\x02\xe6\x02\xe7\x02\xe8\x02\xe9\x02\xea\x02\xeb\x02\xec\x02\xed\x02\xee\x02\xef\x02\xf0\x02\xf1\x02\xf2\x02\xf3\x02\xf4\x02\xf5\x02\xf6\x02\xf7\x02\xf8\x02\xf9\x02\xfa\x02\xfb\x02\xfc\x02\xfd\x02\xfe\x02\xff\x03\x00\x03\x01\x03\x02\x03\x03\x03\x04\x03\x05\x03\x06\x03\a\x03\b\x03\t\x03\n\x03\v\x03\f\x03\r\x03\x0e\x03\x0f\x03\x10\x03\x11\x03\x12\x03\x13\x03\x14\x03\x15\x03\x16\x03\x17\x03\x18\x03\x19\x03\x1a\x03\x1b\x03\x1c\x03\x1d\x03\x1e\x03\x1f\x03 \x03!\x03\"\x03#\x03$\x03%\x03&\x03'\x03(\x03)\x03*\x03+\x03,\x03-\x03.\x03/\x030\x031\x032\x033\x034\x035\x036\x037\x038\x039\x03:\x03;\x03<\x03=\x03>\x03?\x03@\x03A\x03B\x03C\x03D\x03E\x03F\x03G\x03H\x03I\x03J\x03K\x03L\x03M\x03N\x03O\x03P\x03Q\x03R\x03S\x03T\x03U\x03V\x03W\x03X\x03Y\x03Z\x03[\x03\\\x03]\x03^\x03_\x03`\x03a\x03b\x03c\x03d\x03e\x03f\x03g\x03h\x03i\x03j\x03k\x03l\x03m\x03n\x03o\x03p\x03q\x03r\x03s\x03t\x03u\x03v\x03w\x03x\x03y\x03z\x03{\x03|\x03}\x03~\x03\u007f\x03\x80\x03\x81\x03\x82\x03\x83\x03\x84\x03\x85\x03\x86\x03\x87\x03\x88\x03\x89\x03\x8a\x03\x8b\x03\x8c\x03\x8d\x03\x8e\x03\x8f\x03\x90\x03\x91\x03\x92\x03\x93\x03\x94\x03\x95\x03\x96\x03\x97\x03\x98\x03\x99\x03\x9a\x03\x9b\x03\x9c\x03\x9d\x03\x9e\x03\x9f\x03\xa0\x03\xa1\x03\xa2\x03\xa3\x03\xa4\x03\xa5\x03\xa6\x03\xa7\x03\xa8\x03\xa9\x03\xaa\x03\xab\x03\xac\x03\xad\x03\xae\x03\xaf\x03\xb0\x03\xb1\x00\x94\x05glass\x05music\x06search\benvelope\x05heart\x04star\nstar_empty\x04user\x04film\bth_large\x02th\ath_list\x02ok\x06remove\azoom_in\bzoom_out\x03off\x06signal\x03cog\x05trash\x04home\bfile_alt\x04time\x04road\fdownload_alt\bdownload\x06upload\x05inbox\vplay_circle\x06repeat\arefresh\blist_alt\x04lock\x04flag\nheadphones\nvolume_off\vvolume_down\tvolume_up\x06qrcode\abarcode\x03tag\x04tags\x04book\bbookmark\x05print\x06camera\x04font\x04bold\x06italic\vtext_height\ntext_width\nalign_left\falign_center\valign_right\ralign_justify\x04list\vindent_left\findent_right\x0efacetime_video\apicture\x06pencil\nmap_marker\x06adjust\x04tint\x04edit\x05share\x05check\x04move\rstep_backward\rfast_backward\bbackward\x04play\x05pause\x04stop\aforward\ffast_forward\fstep_forward\x05eject\fchevron_left\rchevron_right\tplus_sign\nminus_sign\vremove_sign\aok_sign\rquestion_sign\tinfo_sign\nscreenshot\rremove_circle\tok_circle\nban_circle\narrow_left\varrow_right\barrow_up\narrow_down\tshare_alt\vresize_full\fresize_small\x10exclamation_sign\x04gift\x04leaf\x04fire\beye_open\teye_close\fwarning_sign\x05plane\bcalendar\x06random\acomment\x06magnet\nchevron_up\fchevron_down\aretweet\rshopping_cart\ffolder_close\vfolder_open\x0fresize_vertical\x11resize_horizontal\tbar_chart\ftwitter_sign\rfacebook_sign\fcamera_retro\x03key\x04cogs\bcomments\rthumbs_up_alt\x0fthumbs_down_alt\tstar_half\vheart_empty\asignout\rlinkedin_sign\apushpin\rexternal_link\x06signin\x06trophy\vgithub_sign\nupload_alt\x05lemon\x05phone\vcheck_empty\x0ebookmark_empty\nphone_sign\atwitter\bfacebook\x06github\x06unlock\vcredit_card\x03rss\x03hdd\bbullhorn\x04bell\vcertificate\nhand_right\thand_left\ahand_up\thand_down\x11circle_arrow_left\x12circle_arrow_right\x0fcircle_arrow_up\x11circle_arrow_down\x05globe\x06wrench\x05tasks\x06filter\tbriefcase\nfullscreen\x05group\x04link\x05cloud\x06beaker\x03cut\x04copy\npaper_clip\x04save\nsign_blank\areorder\x02ul\x02ol\rstrikethrough\tunderline\x05table\x05magic\x05truck\tpinterest\x0epinterest_sign\x10google_plus_sign\vgoogle_plus\x05money\ncaret_down\bcaret_up\ncaret_left\vcaret_right\acolumns\x04sort\tsort_down\asort_up\fenvelope_alt\blinkedin\x04undo\x05legal\tdashboard\vcomment_alt\fcomments_alt\x04bolt\asitemap\bumbrella\x05paste\nlight_bulb\bexchange\x0ecloud_download\fcloud_upload\auser_md\vstethoscope\bsuitcase\bbell_alt\x06coffee\x04food\rfile_text_alt\bbuilding\bhospital\tambulance\x06medkit\vfighter_jet\x04beer\x06h_sign\x04f0fe\x11double_angle_left\x12double_angle_right\x0fdouble_angle_up\x11double_angle_down\nangle_left\vangle_right\bangle_up\nangle_down\adesktop\x06laptop\x06tablet\fmobile_phone\fcircle_blank\nquote_left\vquote_right\aspinner\x06circle\x05reply\ngithub_alt\x10folder_close_alt\x0ffolder_open_alt\nexpand_alt\fcollapse_alt\x05smile\x05frown\x03meh\agamepad\bkeyboard\bflag_alt\x0eflag_checkered\bterminal\x04code\treply_all\x0fstar_half_empty\x0elocation_arrow\x04crop\tcode_fork\x06unlink\x04_279\vexclamation\vsuperscript\tsubscript\x04_283\fpuzzle_piece\nmicrophone\x0emicrophone_off\x06shield\x0ecalendar_empty\x11fire_extinguisher\x06rocket\x06maxcdn\x11chevron_sign_left\x12chevron_sign_right\x0fchevron_sign_up\x11chevron_sign_down\x05html5\x04css3\x06anchor\nunlock_alt\bbullseye\x13ellipsis_horizontal\x11ellipsis_vertical\x04_303\tplay_sign\x06ticket\x0eminus_sign_alt\vcheck_minus\blevel_up\nlevel_down\ncheck_sign\tedit_sign\x04_312\nshare_sign\acompass\bcollapse\fcollapse_top\x04_317\x03eur\x03gbp\x03usd\x03inr\x03jpy\x03rub\x03krw\x03btc\x04file\tfile_text\x10sort_by_alphabet\x04_329\x12sort_by_attributes\x16sort_by_attributes_alt\rsort_by_order\x11sort_by_order_alt\x04_334\x04_335\fyoutube_sign\ayoutube\x04xing\txing_sign\fyoutube_play\adropbox\rstackexchange\tinstagram\x06flickr\x03adn\x04f171\x0ebitbucket_sign\x06tumblr\vtumblr_sign\x0flong_arrow_down\rlong_arrow_up\x0flong_arrow_left\x10long_arrow_right\awindows\aandroid\x05linux\adribble\x05skype\nfoursquare\x06trello\x06female\x04male\x06gittip\x03sun\x04_366\aarchive\x03bug\x02vk\x05weibo\x06renren\x04_372\x0estack_exchange\x04_374\x15arrow_circle_alt_left\x04_376\x0edot_circle_alt\x04_378\fvimeo_square\x04_380\rplus_square_o\x04_382\x04_383\x04_384\x04_385\x04_386\x04_387\x04_388\x04_389\auniF1A0\x04f1a1\x04_392\x04_393\x04f1a4\x04_395\x04_396\x04_397\x04_398\x04_399\x04_400\x04f1ab\x04_402\x04_403\x04_404\auniF1B1\x04_406\x04_407\x04_408\x04_409\x04_410\x04_411\x04_412\x04_413\x04_414\x04_415\x04_416\x04_417\x04_418\x04_419\auniF1C0\auniF1C1\x04_422\x04_423\x04_424\x04_425\x04_426\x04_427\x04_428\x04_429\x04_430\x04_431\x04_432\x04_433\x04_434\auniF1D0\auniF1D1\auniF1D2\x04_438\x04_439\auniF1D5\auniF1D6\auniF1D7\x04_443\x04_444\x04_445\x04_446\x04_447\x04_448\x04_449\auniF1E0\x04_451\x04_452\x04_453\x04_454\x04_455\x04_456\x04_457\x04_458\x04_459\x04_460\x04_461\x04_462\x04_463\x04_464\auniF1F0\x04_466\x04_467\x04f1f3\x04_469\x04_470\x04_471\x04_472\x04_473\x04_474\x04_475\x04_476\x04f1fc\x04_478\x04_479\x04_480\x04_481\x04_482\x04_483\x04_484\x04_485\x04_486\x04_487\x04_488\x04_489\x04_490\x04_491\x04_492\x04_493\x04_494\x04f210\x04_496\x04f212\x04_498\x04_499\x04_500\x04_501\x04_502\x04_503\x04_504\x04_505\x04_506\x04_507\x04_508\x04_509\x05venus\x04_511\x04_512\x04_513\x04_514\x04_515\x04_516\x04_517\x04_518\x04_519\x04_520\x04_521\x04_522\x04_523\x04_524\x04_525\x04_526\x04_527\x04_528\x04_529\x04_530\x04_531\x04_532\x04_533\x04_534\x04_535\x04_536\x04_537\x04_538\x04_539\x04_540\x04_541\x04_542\x04_543\x04_544\x04_545\x04_546\x04_547\x04_548\x04_549\x04_550\x04_551\x04_552\x04_553\x04_554\x04_555\x04_556\x04_557\x04_558\x04_559\x04_560\x04_561\x04_562\x04_563\x04_564\x04_565\x04_566\x04_567\x04_568\x04_569\x04f260\x04f261\x04_572\x04f263\x04_574\x04_575\x04_576\x04_577\x04_578\x04_579\x04_580\x04_581\x04_582\x04_583\x04_584\x04_585\x04_586\x04_587\x04_588\x04_589\x04_590\x04_591\x04_592\x04_593\x04_594\x04_595\x04_596\x04_597\x04_598\x04f27e\auniF280\auniF281\x04_602\x04_603\x04_604\auniF285\auniF286\x04_607\x04_608\x04_609\x04_610\x04_611\x04_612\x04_613\x04_614\x04_615\x04_616\x04_617\x04_618\x04_619\x04_620\x04_621\x04_622\x04_623\x04_624\x04_625\x04_626\x04_627\x04_628\x04_629\auniF2A0\auniF2A1\auniF2A2\auniF2A3\auniF2A4\auniF2A5\auniF2A6\auniF2A7\auniF2A8\auniF2A9\auniF2AA\auniF2AB\auniF2AC\auniF2AD\auniF2AE\auniF2B0\auniF2B1\auniF2B2\auniF2B3\auniF2B4\auniF2B5\auniF2B6\auniF2B7\auniF2B8\auniF2B9\auniF2BA\auniF2BB\auniF2BC\auniF2BD\auniF2BE\auniF2C0\auniF2C1\auniF2C2\auniF2C3\auniF2C4\auniF2C5\auniF2C6\auniF2C7\auniF2C8\auniF2C9\auniF2CA\auniF2CB\auniF2CC\auniF2CD\auniF2CE\auniF2D0\auniF2D1\auniF2D2\auniF2D3\auniF2D4\auniF2D5\auniF2D6\auniF2D7\auniF2D8\auniF2D9\auniF2DA\auniF2DB\auniF2DC\auniF2DD\auniF2DE\auniF2E0\auniF2E1\auniF2E2\auniF2E3\auniF2E4\auniF2E5\auniF2E6\auniF2E7\x04_698\auniF2E9\auniF2EA\auniF2EB\auniF2EC\auniF2ED\auniF2EE\x00\x00\x00\x00\x00\x00\x01\xff\xff\x00\x02\x00\x01\x00\x00\x00\x0e\x00\x00\x00\x18\x00\x00\x00\x00\x00\x02\x00\x01\x00\x01\x02\xc2\x00\x01\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\xcc=\xa2\xcf\x00\x00\x00\x00\xcbO<0\x00\x00\x00\x00\xd41h\xb9"), } filee := &embedded.EmbeddedFile{ Filename: "favicon.ico", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("\x00\x00\x01\x00\x01\x00 \x00\x00\x01\x00 \x00\xa8\x10\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x907,\x17U\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f?0\x9a\x82&\x1b^\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9dA.'\xa0?0\xfb\x82&\x1e\xbbq\x1c\x1c\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9e?0\xb1\x9f?0\xff\x88*\x1f\xc3}#\x1b|\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9d@/<\x9f?0\xff\x9f?0\xff\x8f3%\xc9~#\x1a\xf1y$\x18\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x01\xa3D6\xb9\x9f?0\xff\x9f?0\xff\x9a:+\xe5~#\x1a\xff~$\x1a\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ڨ\x9fR\xbcn_\xc1\xa5C4Ɵ?0\xff\x9e>/\xfe}#\x1a\xf6~#\x1a\xfa|!\x1a'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbf\xbf\x80\x04ڨ\x9e\xd6͎\x83\xc0\xabI9\xff\xaaI8̠@2\xe2\x80%\x1c\xd5~#\x1a\xff}#\x1a\xb1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ۨ\x9fjڨ\x9f\xff֣\x9a֫I9\xff\xabI9\xff\xabI9\xf8\x8a1(\xc0~#\x1a\xff~#\x1a\xff\x80%\x19>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ժ\x95\f٨\x9f\xe7ڨ\x9f\xffڨ\x9f\xfc\xaeP@\xf0\xabI9\xff\xb1UG\xeb\xb1tk\xc4~#\x1a\xff~#\x1a\xff~#\x1b\xca\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00٧\x9f\x80ڨ\x9f\xffڨ\x9f\xffڨ\x9f\xff\xbeoaȫI9\xffܮ\xa6\xc2Х\x9e\xda~#\x1a\xff~#\x1a\xff~#\x1a\xff}\"\x1aZ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbdh^\x1b\xadUH\xb7\xc0ym\xc6٥\x9c\xf9ڨ\x9f\xffϑ\x87\xc1\xacI:\xd9\xe7\xc9\xc4\xd8\xe7\xc9\xc3\xf9\xabM@\xb6\x923(\xee\x913'\xef\x925(َ9\x1c\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbfk\\\x98\xaeRCƟ?0\xff\xa0A3\xe3\xb5fY\xc0Җ\x8f̬J:S\xe8\xc9\xc6Z\xe9\xca\xc5\xff\xc3qd\xbb\xafL<Õ6)\xf7\x935(\xff\x924'u\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbei\\'\xc0k\\\xfa\xb4[L\xbf\x9f?0\xff\x9f?0\xff\x9f?0\xff\xa0>0\xc1\x00\x00\x00\x00\xff\xff\xff\x02\xe9\xca\xc5\xd0\u2daf\xb6\xb5Q?\xff\xb4O?Ԗ8+\xe7\x935(\xed\x96<-\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0j\\\xaf\xc0k\\\xff\xb9aS\u009f?0\xff\x9f?0\xff\x9f?0\xff\x9e>.B\x00\x00\x00\x00\x00\x00\x00\x00\xe9\xc8\xc5F\xf2\xdfܿ\xb7UCߵQ?\xff\xb5P>\xe9\x9d=.ғ5)\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbfi[8\xc0k\\\xfe\xc0k\\\xff\xbegYϟ?0\xff\x9f?0\xff\xa0?0\xb3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xdbٛ̇y\xbf\xb5Q?\xff\xb5Q?\xff\xb5Q?\xf9\xa5D4\xbe\x965&\"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x01\xbfm`\xa8\xc0k\\\xff\xc0k\\\xff\xc0j\\\xe6\x9f?0\xff\x9f?0\xfd\x9f@00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf3\xe2\xe2,\xed\xd5\xd1\xe1\xb5Q?\xfe\xb5Q?\xff\xb5Q?\xff\xb5Q?\xff\xaeQ<\x94\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9e>/W\xa5J<\xcf՜\x93\xc2\xc0k\\\xff\xc0k\\\xfd\xa0@2\xf8\x9f?0\x9f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xe9褺[JҵQ?\xff\xb5Q?\xff\xb5Q?\xff\xabZN\xb6}%\x1c7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xaa\xaa\x03\xbesh\xa0\xa0?0\xf1ӟ\x98\xcf⸱\xc2\xc1n_\xf8\xa5G8ע<3\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf7\xe6\xe6\x1fє\x89\xbc\xb5Q?\xff\xb5Q?\xff\xbddUʲf\\\xce~#\x1a\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ک\x9fȟ\x82\xbf\xaaI8ѤG7\xd0\xe9\xc9\xc4\xfd\xe7\xc6\xc0\u05fdrbp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xeb\xd1̀\xb5Q?\xfa\xb6QA\xfd˄w׳h\\\xd0~#\x1a\xff\x80\"\x19R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00㪪\tک\xa0\xe3і\x8b«I9\xff\xaaH9\xcbʍ\x83\xc4\xe9\xcb\xc5\xee\xe4\xc9\xc9\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xf1\xe3\x12\xbc_PÿiZ\xc4ˆy\xff\xb3i]\xd1~#\x1a\xff~#\x1aր@\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ک\x9e|ڨ\x9f\xffԝ\x92ɫI9\xff\xabI9\xff\xacN=\xc8⽺h\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc2p``ʅx\xe4ˆy\xff\xb6k_\xd2~#\x1a\xff~#\x1a\xff~$\x1ak\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00٦\x99\x14ڨ\x9f\xf1ڨ\x9f\xff֢\x98ӫI9\xff\xabI9\xff\xabJ9\u07b6II\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ےm\aˆy\xdeˆy\xff\xb6m_\xd3~#\x1a\xff~#\x1a\xff~#\x1a\xe8v'\x14\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ڨ\x9f\x90ڨ\x9f\xffڨ\x9f\xffإ\x9c\xe1\xabI9\xff\xabI9\xff\xaaI8W\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ʇxWˆy\xff\xb8ma\xd5~#\x1a\xff~#\x1a\xff~#\x1a\xff\u007f#\x1b\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ק\x9f ڨ\x9f\xf8ڨ\x9f\xffڨ\x9f\xff٨\x9e\xf2\xabI9\xff\xabI9\xcb\xff\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x01ˆyʹoc\xd6~#\x1a\xff~#\x1a\xff~#\x1a\xff~#\x1a\xf5{&\x1c\x1b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ڨ\x9f\xa5ڨ\x9f\xffڨ\x9f\xffڨ\x9f\xffڨ\x9f\xff\xacL;\xf8\xabH9C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00͇xB\xbbpd\xd8~#\x1a\xff~#\x1a\xff~#\x1a\xff~#\x1a\xff\u007f#\x1a\x9f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00٨\x9d/ڨ\x9f\xfdڨ\x9f\xffڨ\x9f\xffڨ\x9f\xffڨ\x9f\xff\xb3XI\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4h^\x9a~#\x1a\xff~#\x1a\xff~#\x1a\xff~#\x1a\xff~#\x1a\xfc\x80!\x1c.\x00\x00\x00\x00\x00\x00\x00\x00۩\x9eGۧ\x9fwۧ\x9fwۧ\x9fwۧ\x9fwۧ\x9fwŃu#\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x97H@ ~\"\x1aw~\"\x1aw~\"\x1aw~\"\x1aw~\"\x1aw~$\x19G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xfe\u007f\xff\xff\xfc\u007f\xff\xff\xfc?\xff\xff\xf8\x1f\xff\xff\xf8\x1f\xff\xff\xf0\x0f\xff\xff\xf0\x0f\xff\xff\xe0\a\xff\xff\xc0\a\xff\xff\xc0\x03\xff\xff\x81\x83\xff\xff\x81\x81\xff\xff\x03\xc0\xff\xff\x03\xc0\xff\xfe\a\xe0\u007f\xfe\a\xe0\u007f\xfc\x0f\xf0?\xfc\x1f\xf0?\xf8\x1f\xf8\x1f\xf8?\xfc\x1f\xf0?\xfc\x0f\xe0\u007f\xfe\a\xe0\u007f\xfe\a\xc0\xff\xff\x03\xc0\xff\xff\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), } filef := &embedded.EmbeddedFile{ Filename: "fee66e712a8a08eef5805a46892932ad.woff", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("wOFF\x00\x01\x00\x00\x00\x01~\xe8\x00\r\x00\x00\x00\x02\x86\xac\x00\x04\x00\a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FFTM\x00\x00\x010\x00\x00\x00\x1c\x00\x00\x00\x1ck\xbeG\xb9GDEF\x00\x00\x01L\x00\x00\x00\x1f\x00\x00\x00 \x02\xf0\x00\x04OS/2\x00\x00\x01l\x00\x00\x00>\x00\x00\x00`\x882z@cmap\x00\x00\x01\xac\x00\x00\x01i\x00\x00\x02\xf2\n\xbf:\u007fgasp\x00\x00\x03\x18\x00\x00\x00\b\x00\x00\x00\b\xff\xff\x00\x03glyf\x00\x00\x03 \x00\x01_y\x00\x02L\xbc\x8f\xf7\xaeMhead\x00\x01b\x9c\x00\x00\x003\x00\x00\x006\x10\x89\xe5-hhea\x00\x01b\xd0\x00\x00\x00\x1f\x00\x00\x00$\x0f\x03\n\xb5hmtx\x00\x01b\xf0\x00\x00\x02\xf4\x00\x00\n\xf0Ey\x18\x85loca\x00\x01e\xe4\x00\x00\a\x16\x00\x00\v\x10\x02\xf5\xa2\\maxp\x00\x01l\xfc\x00\x00\x00\x1f\x00\x00\x00 \x03,\x02\x1cname\x00\x01m\x1c\x00\x00\x02D\x00\x00\x04\x86㗋\xacpost\x00\x01o`\x00\x00\x0f\x85\x00\x00\x1au\xaf\x8f\x9b\xa1\x00\x00\x00\x01\x00\x00\x00\x00\xcc=\xa2\xcf\x00\x00\x00\x00\xcbO<0\x00\x00\x00\x00\xd41h\xb9x\x9cc`d``\xe0\x03b\t\x06\x10`b`d`d:\x04$Y\xc0<\x06\x00\f\xb8\x00\xf7\x00x\x9cc`f\xcbd\x9c\xc0\xc0\xca\xc0\xc0\xd2\xc3b\xcc\xc0\xc0\xd0\x06\xa1\x99\x8a\x19\x18\x18\xbb\x18\xf0\x80\x82ʢb\x06\a\x06\x85\xaf\fl\f\xff\x81|6\x06F\x900#\x92\x12\x05\x06F\x00\xd0\xcb\bn\x00\x00x\x9c͒\xdfJ\xe2q\x10\xc5\xe7gje\xe6\xf9>\xc0\"\xea\x03D\xbd\x80\xc8>\x80\b{\xd3E\x88O >\x81\xf8\x04\xe2\x13\x88\x97\x1b,\"\xd1u\xc8^\xec\xa5\b[[[\xf9\xc3\xddj\xfbos\xa6\xf5\xd6_\xbfM\xe8\xa2\xeb%:0g80\xcc\a\x86\x11\x91\x05\x99\xd7\a\xf1B\x17\xef.L\xdes\x8ezðפ 1Y\x97\x8clKWv\xe5\x9b\x1ces\xb9t\xae\xa2)Mk^\x8bZֵ֪\xa9m\xed\xea\x8e\x0e\xd4\u05c9Θb\x9ak̳\xc82\xab\xac\xb3\xc96\xbb\xdc\xe3\x80>'\x9cY\xcaҖ\xb7\xa2\x95\xadjukZۺ\xb6g\x03\xf3m2\x95 \b\xc9\x19\xf9\xfc\x8a(\xea4\xab\x05-iEk\xdaЖv\xb4\xa7}\x1d\xeaX\x95B\xc7\f\xd7Y`\x89\x15\xd6\xd8`\x8b\x1d\xf6\xd8\xe7\x90c\xaa\x899\xcbZ\xc1JV\xb1\x9a5\xace\x1d\xebY߆6\xfeG\f\u0382\xad`3\xf8\x14|\f6\xfc\xab\xd1\xfd\xe8\x8b[uI\x97p\xcbn\xc9-\xba\xb8\x8b\xb9\xa8[p\x11L\xf1\x17\x8f0\x10\x8a\aLp\x8f;\xdc\xe2\x06\u05f8\xc2%\xfe\xe0\x02\xe78\xc3o\xfc\xc2\x18>F8\xc5\t\x8e\xf1\x13G8\xc4\x0f\x1c`\x1f\xdfW\xbfί\xfd\xb6\xf2\xe2\xf2\x82\xf5\"\xa1E^\x0f\xcc_\xe1=(\x1eK,F\x93K\xcb+\xffy\xef\x13b\xf9\xc3\xf3\x00\x00\x00\x00\x00\x00\x01\xff\xff\x00\x02x\x9c\xbc\xbd\t\x80TՕ0\xfc\xee\xbdo\xa9}{\xb5uuuwUWի\xean\xe8njmz-\x9a\x9dnv\x04\x04\xc4\x16E\x91EAA\x10\x17J!*\x88\x1b(\xe2\xdehD\xc92c\x16\xf3%F\x9cʦ\x93Eb\x12b6\xbf\xf9\xda$&\x99\xb8\x8c\x93\xe47\x11\xba\x1e߹\xf7UUW7\r\xe8\xcc\xfc\x1ft\xbdw\xf7\xf5\xdc{\xcf9\xf7\x9c\xf38\xccm\xe68b\x13\xe1\xc1I\x1c\x97\tڃ\xc4\x1e\xb4\x0f\xa1\xbc\x9a\u074c\a7\v\x81S\x9bE\xee\x14G\xff!\xee\x923\x9c\xf8\x8c\x90\xe3j\xc0㔐=\x18w;\xc5P\xb0^I\xa6\x13A;RR\xc9n\x94\b\xc6k\x91\xf8LS\xe1.\x94\xf5)\x8ao8G\x9f([\xb8\xab)\x1c\xf3\b9O,,\xcc\bAt\x81S\x92\n\xfc\x11\x0e\xefh\ny\xaau\xbajZ\a\aupPG\x13x\xecN\v\xaeo\xc6\xc9n\x9c\x88{\xec\xc2ho2\x9dA\xe9D\xdc-r\xd3\xd6]\xb5\xe2\xaau\xd3\xe05\xf1\xeae\x85\xd1^\xa5\x96dM\xb6X\x9b\x108=\x18\x9f\xd7\xe4r5ͻ\x02^Q\\\xf5~\xa1\xa32\x80\xbcV\x970 \x8eo\xe30kC\x0e\xda qA跍\v\xd0\x1f\x82\xae\xd6G\x11<\xc2\n\xb69\xd2\xe1\x00\xefv\xb8`\x18\xdc|N\xfdX\xbdW\xfd\x18I\xe8:\"\xf5'\xd3a\xf5\xd8W\u07baO=}\xfc\xdak\x8f#\x01\xd5\"\xe1\xf8\xb57\xa3e\x11\f\t\x90\xa4%Vs\xc9~\x05-\xbdy$ŵ\xc7\xd5\xd3\xf7\xbd\xf5\x15\xf5X\x84\xce\x06w&'q\x02\xc7\xf9\xb8.n.\xc7E\xec\xa2\xc4K\x16\xdc\x04#\x80\xa2JD\x89ڝn\x18봽\x137\x13\x98\x03\xd1\xe5\xf4\xb8=\xb5|\a\x8ew\x93L:Ӎ2vmrRv:=0P\xb9@D\xfdۓ\x89\xec\xa6V\x84Z7e\x13O\xaa\u007f\x8b\x04d\xb3\x907\xcbH\x10M\xbaSY\xb3|\xf0[o\x88\xed\xf5\x99f'B\xce\xe6L}\xbb\xf8Ʒҗ\xe4V\xf7\x9e\xca\xf6\xae^\xdd+\xe4{W\a\b\x17\xae=\xb1\xa7\xa9uҤ֦='j\xc3\x05\xce,\xcb|\f;\xf4v\x9dA\x90\xcd\xcfo=\xfc\xac0\xc9\x17q8\"\xbeI³\x87\x9b\x1e\x188\x9d\xa7\xb9yZ\x866Ǵo9\xce\xcfq<\fi3\x9f\x82\x16\xc6k\xb1\xa7\x9b\xc0\x84\xd21%\x8f&\x1d\x85\xfb\r\xa1\xfe\xce\x16u\xa8\xfb\xb6k\x17\x84\xc3\v\xae\xbd\xad{H}\xa7\xf0@\u0381W\xe8\u0097^q\xefԷ\xfe\xd14;\x1b\x0egg7\xfd\xe3\xad\xff\xfdN\xe1y\xad\xec/\xc2\xdc\rq\xf5\x1a\x8c\xcaP\x1c\x9d\xb7\x88\x00O\x00ЌL\xc14\x13\x91\xd3q\x8f,\xc0\x98\xf8ԇ\x96\"\x97Sv\xa9=j\x0fL\xa8\v/U\x1f\xacjC\x1f\xbd-w\xcao\xa3\x8f\xdaȍn\x9f\xfa\xa4j\x92̮\x1a\xd3{\xef\x99j\\\xa2\x05\xfd\r\xad\xa9vE\xf4\xb3\xd1k\r\r\xea\xe4\xd9z\xba>p\xb9n=\x85^=\x8a\x18ajID(\xb5\xe3\xdc\xcd\u0de0\xb8\xba\xe2\xf8qu\x05\x8a\xcfF;э\xe85֮\x86s7\v;QC7\xbaU\xbd\xbd[\xfd\x85\xba\xf2\xb5\u05c8\xa1\xd4\xcc\xf8yZIۘ\x13\xe8ػ\xe8*\x8e\x14!$\t\x03\xef\x0edⵄ\xb3\x89\x01Ŗ\x0e\b\xdc-ˇ?\xb7\xfc\x16{\xf3\xccm\xfd\xbb\xd1\xc6\xdd\xfd\xdbf6\xdb\xcfpo\xab\xdf~\xfbmԽw\xd3\xe3\x8fo\xba\xe8\xe1G6M\xcf\xe5\xa6oz\xe4a\xf2--\xfcm\x18\a#]?\x12]?V\xae\x8ek\xe1z\xb8\xf9ܥܵ\xdc.\xee>\xee)\xee\x9f9NH%\x95&T/\xd6 \xa7\xbb\x03\x01\b_\xc0\x8f\xecI\x85Ax\x11\xe4\xd1\xd8\xf8O\x99\xfeB\xf5\x8d]8(\xa7\xf8\xd8.v\x8e\a\xcf)\xbe\x02G=\x04\x9e\xc3\xdcH\x8cP\x91S\xcdU\xa6\xbaP\x99\xb0\xe4>f\x8bF\x84E\x93-G\xa1G\xc7s\x16|\x98\x15\xac\xd2'?\x12~z\xc4I*\x93\xa8\xe3\x96R\xe1|\xe9\x14\xab[`\v\x96\xa7\xc0-V\xce'ݙG\x8dP\x15\x1a3b\x17\x88'\\\u007fR\xe5\x92\xfd\xfdI̞#n\x92;W\f\xe6\xe8\x96ٟD\xf4\x89\u007fT\xe1\x19\xfeѹb8\xb60\xd9^s6,rȥ\xb5\xaa\vi\xad\xb2\x8f\xf1\x8f\x8d\xff\x9f\xf6\x8f\xad\x0fsm15\x1fkk\x8b\xa1,}\x8e\xb8q\xae\xd2Wȝ;\ue4e7\xact\xa3\x00s\xd2\a\xfae\xd9Y\x18q\x92qC/\x98\xa0\xa20\x00\xa1q\xe7\xe2\u007f|\x16>\xf9\xa8\n\x103\xcc\xc2\b\x84\x9d\xe6\xce\x1dW\xe9\xfe/\x8eը\xa1\x80s\xeaF\xce\"\xde\xc9\u007f\x99s\x83\x0f\xce\aI\xacoAHI\xf6 8\x11\xf4\xf0\xa8C\xe2\x9d\xfe\u0084\xbb\xfc\v\xfcw\xa9\x87\xfc~\xea@\n\xbe\x9f\xfa\xc9_\x16\xb0(\xff]h=\xf5\xfb\xfd\xea\xaf\xf0\x03\xe0\x85r\xaf9\xf3\x81\xe0\xe0\x0fp!\x8e\v;\xadH\xac\x8f\xea\x11-[If\xf4\xa3\xcbw;%=\x12\x1c\xacd\xf5\xd7꯵\x92\x90\x02\xaebmH)\x96\xfek\b=o\xac\xbf\\\x8a\x86\xbbh\xfbE\b\xce\xc1i\xda\f7i\x0f:-!mn:`[\x86G\xdc]\x83\xe0\xecGE,\x8b;\x17\x96\xc5s\xb2yH6\x03\x8a2\x04\xe8ƈs\x14\xee\u0557:\x17\ue147\xce\xcaI\x9d\u007f\xa8@\xc8^\\\xdfw\x0e\x84\xac\xb2OV\xceõ\x9e\x05\xb5\x9f\xac\xfd\x85<\xad\x15g?]\xabY{?qK\x8bg\xbcH\x97[\x03\x97\xa2X\x10\x16\xf9\x00\xb4&\x95td\xd2n\x8f[\x94,\xd0\xfaZ\xc0\x13!H\x896#\xc0\x15=n\aݳ\xb5\x1d\x9a\xe2\xd4;O\xa8\xbfW\xffU\xfd\xfd\x89\x9dG\x0e4]]\x17\xb06\xaeٰp\xdf\xf17\x8e\xef[\xb8aM\xa35P\xb7\xbe\xf1\xc0\x91B\xae\u007f]?\xfc\xe1\xdc\xe34\xe5\xce\x13\xc8\xff\xf8\xd7P呂\xa5\xa9\xf1\xea\xc0\x9c7o\\\a\xc9!\u05fa\x1bߜ\x13\xb8\xba\xb1\xc9\x12ؤ\xbe\x82\xe7\x14\xd8\x06\x8d\xd9\x06\r\xff\x842>8\xb2/p\x912\xb8h@\x12\xb1k~ھ\xf1\xfc\xe8B~\xcea\xc9[\x1c\xec\x81r\x9f\xce=Pr8\xaeS\x99\x13e\xe1\xf9\x97\x02sc\xeaF\xd7\rӗ\x85\xd0 \xf4\x97S\xcc#P\xcf\xc8|0z\xe2\x1a\xf0'\x95z\xd1\xe9\x8eS\b\x82\xf5)\xc1\x8c8aFB\xb0FE\t\xfe\xd3V\xc3r\x8dJ\x14\x90\x94(E\x13\x01\x97\x87\xa0fD\a\x03\x16p\xa6\x14\x9a\x80U\x9c\x06\\\x9f\xf5\x10\x16\xb4'\x03h4P\x01\x14\x85\xb6 \t\x82j\x01\xee\x0e\x9dd3}Cv\x86f\x19\xf4\xd5\xf7\xbbM\x96}\x13Zlf\xa9\xe6\xdf,.\xe4\x9f\xd4p\x8f\xc1j1\xde\x1a\x95t\xd6Y\x8ej\xcb\xff2\xdblƗ,U\xb1\xa9\x06\xbd\xef\x01\xb7\xd9<:\xf1\xbdz\xab\xd9t[\x98%\xf6Y!1v\xd3\x1a\x0e\xa1M\xfffrc\u007f:\x12_n\xf2\x19\"\xf7\xea7z\xacw\xc5\xfdv\xf3\xd7m\xae\rz\xe3ui\x83\xd9dt\xad\xac\x8aO\xaa\xc6.3K\xdb\xdcu\xe2\x04\xba\f=\x8d.#Cj\x19n(,\x14THu\xa0\x98\x03_Z\x99\xf4\xc4\t6\x8fq\xc0\x97\xe6\xc1\xf6h\ah\xe7P\x8a4#J\xa6H\xc4%jt\x8e3\x04\xd4M\x14\x82)\xb5#\x11\x11\xa8\x9dz\xd8\x18\x10\x85\xdcz\xba\xb9\xb0dt\xe7\b\xd11Dn~\x99\x179\xcd/\x99\x9dȋ\x1c\xa6\xbf\x99\x1c\xf8\xa3\xe6B\xd6\xec@N\bV?\x84p'r\x98\v\xd9f\x1f:\xa2\v;\xd1b\b\xb1B\xc8QHb\x85$h\xb13\xacCG|\xd8\xcf#v2\xa9y\xded\xb3\x01m)\x9b\x11e\x03\x98\xcf\xc0sv\xba\x9bw\xca~٬\xed\x9bfp\x9e~\xaf\xbbDG\x88\x14\t\xb4r\x11\xae\v0\x94\xe2^Xzˣ\xa6\xdd\xe3\x8e\xf7\xd0Շ\xdc\x12\xc5c\x90\x92\xa1l&\r \\\xf6`\\\xd08HHa\xaf\xa3\x00\x0e\fIC?\xb9\xb3\xfd\xf1\xf6\xbb\xd0\x1b\xb16\xf5\x9b\xf6:5\xebH;\xd4l\x9d\xddވ\x804C\x94\xf8\xe2\x1a\x8f&\xb5\\\xf4\x0f\x05F\xf0\xbe\xbb\xda\xe0\x0f\xdb\x1bjԬ,\xa3|MC\x14\xe5\x19ݔ\xad\x80\x15/\x17f8\x80\xb3ܮ2\xa8\xb8\xec\t\xd8.\x8a\xc0ҍl\n_\x01/\xfc\x80A\xfd\x96\xd1kT\xf3V\x9dΝg\v\a\xfe~T\x86\x99\x03\a\u0382\x1a<`2\xa9\xdf\xd2\xebQ\xd6&;\x19\xdcX\xd4A\a\x8eW@\xda\xf1\xb3@g\x9c\xb6j{\x9c\xb6\x19j,\b\x8a\t\xa2s\xb7uuE\v\r\xa8\a\x9a\x8d\xb2֟\x9e\xb7\xa9\x0f:\xd5A\xb6\xc9\r8,&\x13\xea\xd1\xebռ\r}|\x9e\xa6b\x06\x13\x14\x050\xb3\x95\x1flFQ\x12$px\x05=\xc1\xc8\b4ddm7\xf6\xc8nr\x06u\"\x82N\x16:O\xc2\vu^\x8a\xb2x@\U0005d08d\xaa\xea\x03C\xcaG\xb2\xbe\x94\xe1\x83*\x9c%F\x8c>Tm\xf0\xca?\xad\xd62.\xe4o\xbb\xbbp\xac\xaa\xbe\xbe\xaa\xf0ˮ\x8a1\xb2r\xd5\\T\xc3\xc3١K+L\xc3\u061cc\xd9\xf3\x83\x81\u0090\xd5n\xb3\x05\x02\xc1:\x1c8\xef\xa2\xc7\xcf\xceq\xa8y\xbdN\x8e\xe0\\Dv\xc8j\xfe\x87\xe7[\xf5\xa8ܦDy/\x8a*=H\t\xd5[0\xe0l\x898=\xef\xe3\xf4`\x97D\xbe\x8cd&\xe2<\x9c\xfd\x80\xdaq\x14R\x1b\xed\xf6\xba}\x0f~\xbf\x84|m?9[\xb2Y\r{\xf5H\xb7I\xfd\xc1\x17FP\xb5\x83H\xdep;@\xb8\xc0\xa9Y\x9f\x12\x8b\xd6\xee\xdb[D\xf1\xd6]j\xc0\xfa}\xba*Þ\ahJԆ\xfc'v^\xbb\xf66XD\x95\xf8L\x98\x9b\xc9V\x01\xe6\x82\xf5a@XF\xf6k\xa0<\xe0\x98\x8d\x97\x91\x93\xd2\xc2N\xb2\xae\x94\xf1\x9c.\xe4\xe0\x82\xac\xedpV\xff\x11e\xd5u\xea\a\a\xd4\xff\xd8p\x9b\x9c\xa4\xd3\x05+O\xde;\xf3\xab\x97\xdd\xf1\xa7\x19\xc6F\x00G\xb3\\E\xfb\a\xa1нb\xe0kfy\nz\x14\xc9\a\x90s\xc3\xed\x90\r\r\tX\xfd\x9b\xfa\x95k\xae\xbcM֊P\x92\xf2\xde\xdeY\xb7_g\xbf\xc2#\x13\x99f\x87\x90}{\xb5\x00\xb3\x84Lh.tMV((\xea\xce\xc9/\xe0\xd0\x18\x8c4u\x01\xffX\xba4u\x01\xbf<\x86k%\x9fŅ\xd2\b\x81s=x\x88\x1ff\x1e\xc2Ȍ\xf1ݐ\xe8\x14\xf3P\xae\xef(\xbe.\x1a(\x17\xf7\xd7q\\\x85\xf1\x02+\xa2\x13\x84\xb9\x87i}\xfc\xdcJ\x86/[\xe3\xbc\xc6O\xaf\xa3k\xa5\xce\xd5\xfe\xde76\xbct\xab\x12O\xef\xbad\xb1٧\b\xdc,崅v\x9c\xff\x8b2+\xd5\u05f7\xad TU[\xb6NHN8\xa0W|\xf8\x0f\x01\x8f\xa5fG{\x87ܘlT\x18\xdd_\xe2\xa5\xf5\xd1\x16Z1 \xfe\xae\xd18j\r\xfc`A\xa4\x92\x18\xb6\x1a\xecr\xd6\xd1㼌\x9a\x95`\fh\x04\f\xbd*\x12\x98b\xae\xd3\xf9\r\xf7\xde\xe5#\xd8\xe8\xcc\x1b\xea\xa7ռ\xa0\xfeB\xfd\xb2\xfa\x8b\x17j\xa6\xd5\xdf0s$n\xf9^\xf77\x9c\x9dw\f\xa1$\xeaGɡ;\xf0\xbe\xa3\x0fN\n.\xde\x10\x18A>\x033;M\x97\xaey\x10\x89\x8f?\xae\x9ezpͥ\xa6Ι\x81\x11\xa44\xb0aqp҃G\x1fF\xde\xd7w\xee|]\xfd\x93֯\x00\xe1\xf8!\xc0\xe1ؾ\x05\xc7b\x19v\xe1\xa0q\x138\xabe\xf5+\xea)\xb6\x0f\x8bh.,U~\xf04]\xe1h.\x84P4s\xae\xb6\x06)\xbc\x04\xf8\x1c+k\xe2\xf8\xa5q\xdaD2\xc6u\xb2\x99\xb0\xeb\x03ϸu\xa0\xdcE3\fV\x9f⭯\xf7ҟ\xe2\xb3\x1af\x8cS\xb1\xea8\xbc\xdb/D\xaa]5\xae\xaa\x96ޖ*xWG\x84j\x06\xba\xb0\xdf}\x03\xe6l&k\xcflnçi\x13\x1c\xa9\xc5Pv'\x03\xf46#\xde\xc6\xc6\x01\x85\a\x80\xe8\xb0\xe1(%\xc8\xcb)>q\x8f\xd0E\x9f\xdbo6U+\xd16\xe7\x9cŋ\xe78ۢ\x8a\xcflޏ>\xa7\xfe\xcc\f`\x1a\x95\xea\xa4\xe6\xf0M\xfb\xf6\xdd\x14n\x06'\x8b\xfc\xd9'\x1f\x85\x8czB-t\b\x8a/ꬱ\xa6\x9f\xf9\xfa3ik\x8d3\n\xd0\xdf\xf155\xa9\xeeZ\t1a\x0fo\xe6\xab|+\x91\rőm\xa5\xaf\n\xbc\x9e0$Yə\x18\x91Oa\x9e\xde1\x1aag\x959\x0f\x9c\xb8up\xe2\xb7\xc09Gת+\x94\x92\xe1\x17\x84\x1fb\xb4\x8a=H߀\xee\xdbQ1\x1ch\xb7T\xc8\x1e\xb2\a]\x89\x14Ғ\xd8Q\x0e\xfe\x91\x1c\x90^\x94\xec\xa0?\xc2\xd1\xe7\x19\xae\x90\xe3s9\x1a\xad\xe6ػ\x00\xff\x05\xf8\xd1 \xc2\xd1l\xc3\b\xedB\xc5|4\x16\xe7T\x16N\xf9\xcc\x10\x88YB\x1aL\u007f\x1c\xbb\a,\x9d\rg\xf7#\xca5\x15\xfb\xd2A\xf9㉐=!\xff7~=\xf0/\x10X]W\xf7\x04\xfcuw\xdfZW\xd7\xc3\xfe\x9e\xe8避[\xd9\xdfꞞ\xe3\xabW\xd3d==B\xee\xd4m®\xffҏ\u038bv\xa6?$\xbc\xcb\xf6\xe8\x9a\n\x1eE\x11#\x02\n\xa2L\x89!7\xca\xc3\xe6ط\x9e\xbf!\xa2\xba\xa2\xc9T\xa4\x90RR\xfdI4\x98\xca)\xf8\x87\x11\xdeH#\xfb\xd4l*\xa2:#\x11\xfc\xa3H.\x85\x06\x93\xfd)\xa5\x90\x8e\x96pӇ\xa4\rźR\x17\xaaM\xd0B\x81\xfa\x83=\x91ƅ\x12\x9f\xa0\x15(ǂ\xc3͵\xe8\xcd\b\x8d˥\x86>A\xfb\x92,\xd0_\a\x99\xa02\xfc\x03%\xa55\x9bp\x06\xc0yn\x846/\xe1\xae\xe0\xb6\x02\xc4\x02Mb\xa1t\x17,\xe7L\x12֮\x92\xe9\xc6l\x19+\xf49\xd6\x01Q\xa2Gb]*\xe6\x93D\x0f;\xe6\x01\r\x8f\xba\x05\x91\xb9{PZ\x19!\xe5*\xfc\xe2U1\xb7\xfa\xbe|\xfd\x94\xe1\xb5s\xef\xf3{\xdd\"\x823\x11\x9b\\\xa2g\x82\x8e\b\x98\xf8\x89\xab\x91G\x12χy\xb9\x85G:\x8c-nQg7\xcb\xce`ԏ\x143\xfex\xce\x02\xb7\xfaAx\xe6%ÏU\x1b\x8d\x06\xef\x0e\xf2XMZ\x87&HX9\xfd>o\xb2\xe0\x01s\x15\xef\x02Ga\x10\x1c\xeb\xce\n\xe1\xeb'\xcf\x1a\xbe!\xbblü\xa9\x9d|\xb3EW-\x1a\x9d\xd5\x06e\x83b\x88\xe9\x8c\xf5bxs\xbd\xbeY0\x87\x04\xdf6E\x17\xd2\xeb\x9c>\x9d)\x12\x8cV\xb9\x91H\xf4\x9b\xe7\f߰}\xba\xd5V=\xa3\xceG~\xe3\x0eYk\xcbh\x8b\x9a/;\xb5\xbbۇ\x85\xe2\xbd0\x8a{4.\x00c\x83\xe9\x19\\h`\x01\xe75\xbf\xdf\xe3\nF\xa3A\xb9\xaa5\xa4\xceTg\x86[4\xbf\xcb#\xe4\xf4\xe6\xb6\xfaS\u007f\xafo3\xeb\x02\xe8yuy\x90\xfa\x05=\xf8\xf5\xa5\xbd<'j{\x91\th\xfeN\x8ek\xd06\x13\xc6\xf7\t\x96@1c/\xb2\xac5\f-T:\x9e\x8b`Y\xc7X\x14\x14]\x80\xddg\x18~\x83\xf4\xae\x85\xcfi\x84\xa8l\x1e\xd2p\x97!\xb3\xbce>\xe01x06\xe0?\xec\x8fe\xe7oA\x1c\xdds\xdab\x83\x05\x8d\xf6̪\x03fy\x90b3\x83@B\x0f\xce߂\x03\x94Yq\xd8?\x10;\xc3m)\xca\x01h4s\x90k\x80\x1eP\x91\v\xc0\xa3\x8b\xc8\xc0\b\x02UfW\x15\xf9\xd062\xfb\x83c\xc7>8F\x86(\xcat*G\x9fC\tym\ns\xa9\xb5r\xa2p\xd5\b?\x99\f\x1c\xa3I\xf1\xecC\xeb\x87Y:\x02ϻ&͜9\xe9\xae\xd39T\x96Y\x18\xe1-k\xb8\xdc\x02\x98%\x12\a\xc4)\xa3@\xed|FF\x1d\x88\x12h\x0e\x989*\x8a\x80(\x1fRt\xd5\x03\xc2\xcfK\x80\xf8ǻ\x85T\x12\x0e\xb7\x88\bXM-IP.%\x8d\x14C\"\xfe\xf9\xe7\x82?\x9e,+ˆ\u007f\x80=\xbd\xad\t\xc5\xf4>\xf2\xf6\xa5t\xe4\xf5\xe0\x81\x06\xeb\xd2\x1a\xa7U\xdeg\x15Q\x8f\x9a\xedW\xff\x1c\xe5w#\x8fΥ7\v\u074b\x90\xda\xed[\xeb\xefP\xfa\t\xc2\xed\xffޮ\x8b\x90\x05\xe4'j7\x8f\v\xc37̗\x8c\x069Z\x87\xd7\xe1\x93\x16I\r\xccS\x1f\xb9\xb4\xfe\u007f\xb7O4YkDE\x16\xec\xbc͂\x9aB~\x01\xce`\x83Ig;\xf2m\x82\xdb\xd5\xf7\xab\xdcu\x0e\xa0֢z\x87Sg)\xd2\xd1\xec\xecr\xc1\x0e\u007f\x19\xc7E܉\x80=\x19m\x06\xdaK\x82\xce9\xc5ZD\x18\xee\b]\xc34\x8c\xf5\xd9\xc9\x16~7߉R6Hۂ(\x89\x06\xc9j\x89\xcbi!\x12\x00\x0f\xbcBldpӜ^\xb4\xa3\xa1zz拾\xdbg\xfb\x11F:\xb1qꢝk\x12\xedWl\xed\x8d/С\xc2\xef\xb1u\u007fX2\x8a\x02r\xf3\xe1TsB\xe0נ\xdf\xef\xf6\xac\xf0\xcc\xf8\xccͫڂ\x13\x97t\xa7\x1e}}ƶ\xa7\x9e_5\xe1\x85\t\xeb\xd5k\xac\x014\xff\xba\xde\t\x9dA;oH\x9dL\xea\xb6Ϲ\x04\xbf)\xf9z\xb6.\x99qu\x87\xdf\x1c\xffA\xa2z\xbd\xafyx\xf3j\xdek5\xd5F\xfc-\xae\xb8@\xdelҙ\xf5\x02\x8f\x16c\x19\xf9ڗ\xdcҗ\\6\xb9=\xe0\r\xbd\xfa\xd0\x15O]9\xdd/\xba5ڔ\xa7볝\xe3\\tO\x83\xb5\xe8C\xd1T3\x8ef(i\n]\xa6w\v\x12\xf4P\xc4\xf0\xa4\x1ciQ\xaa\xa7\x9bw\x88γ\xdd=\u007fJ\xb5ߌv\xaeGޮy\xb2\x1c\xfc\xa7[\xda[\xd7\xde\xe3\x17,\xb5\xf7Et&Q\x8f\xabo\xb4c\xb7Â\x90\xfdyb66\x19k\xb6\xfa\xf7MK|\xfd\u058b$Y\xb6\x84z%\x9c\xc4\xc6P\x95\xd9(\x90\xab\xb0^\x10\xf48\x1a7D\xacrK\xb0\xdd\xfc`\xe1\xed%\xfa5\x8b.\xb2:\xf8\xea\t\x19\xe2Ď\x11x=\x05m\xae\xe6n\x81ً\xbb\xad\xdam\x18]\xcbЮ\x1e\xed&\x8c2G\x10\xa5\x01(-@\x97Q7\x06xu3\x10\x15%@\xc9p\xb4\x99\xd0~н\xd8\xe3t\xc0\x1c\x97\x00\x1c\xca\vS\x18\x86]\x82\x8e\x88=\x89)AG\xe1\x9c\x11\xba\x16\f\xd9\xed\xe2\x1bA\x87\xf3Vg;\xfc\x1c\xc1\x05\v*=\x1f\xff$mz\r\xe6-|_\x18E\xc4Z\xa3ˢk\xe4\x9d<\x16\xa25U5\xc4fF\xa2I\x96j\xb0\xfd\xf2\xf8\xbc\x80\x1e\xf1\x82`\x88=\x1f\x16H}\xbf\xfa\xfb)0\x93\xc4~\xf1F\xaf,\"\xcc\x13\xe3\xc3\xc1\x1dN\xb96\xe8k\xb4\xe4\"\xbe\xa7}\xf0\x17Ṓk\x98\xe3\xcfT\xc3\xfc\"$\x98\x8d\bm\x1eZPc\xe1',\xd1ϛ\x8etz\x82\x11\xe2\xf9\x85\x99Յ\xaf\x1e\xb1];+\xe8j\xb2\xc5\f\x16+\xc2NG\x02\xe9\xab\x03>K#\xbah-zp\xed6\\\xed\xf1;y\x93\xd7b\xde~\x15\xf69\xd0.m\x8c\t\xe3\a\\\xc6=\xc4qrq\x1cü\xc7=f\x10S\n\x1d\x146\x88u(\xe5\xa4\xcb\xe0\x1c\xe3؍\x93\x80\x033\x81\xb2\xd1#\x89\xa8\x98\x10\xbd0\x81\xe5\xd3\f\x03\x9d\n:\xddNz{\x02S\xa4\x00\x1eM\xe8]\"\xac\xc0`R\x81\xbc\v\xb5\xb1\x9d\x81.C\xdc\xf6\xa0\xa7r`-\x06\xbb\xd0U{낍\x8dz\x04\x8bn\xdcq\x95\xf4\x04\tt\xc4x\xe3\xc3\r\u007f\xb3ic+Ԛ:3Y\xde\xe7㳙N\x93\xd9*\x90a\x8e\bV\xf3\xd8P\x9e\x86\n\xbb`\xcc1\xe2Q\xa0b\xc8\xe7\xce@f\x11c^X\x989\x9c̼\xfa\xf4\xac\xf1\x87ܶ\xf5\x9f\xef\xfc\x02\x91jtҜY\vӂ\xa9\xdahھ\x8e\x8d\xf9\xa93\xd1\ti\xe2js\x91\xf4\x84\xa8+\\\x17\xc28T\x17v\x8d\x1b\xc8i|\xd7Q<\rv\xbb\xf6߹c\x978\xc5\xf71\x93\x16\x11\x8b\x92-\xa7\x99t\x89\xc0\x98\xab\x8a\xef\x14\xf3\x89\\1\x8e\xa5\xe4\xe1\x99\x1f\x116\xb9\xf2\x02\xce\xffG\xed\xd6\xc4\x13I\xae\xd8n\xe6\x13J\xd2:̇\xff\u007fh\xf7X\x19\x85\v\xf9G\xb7\xbbr\xb4+\xc7\xfa\xbf<\xd2\xffO\xda|a\xf7\xa7l\xf3yxuco\x8f\xed\x17\xf0\x8f\a7狿P\xdf\x11'\x9b\x19j{\x8e\x87\b\xf1\xa7\x98G\x00\xe4\xf7\x14w\xae\x98s\xb9\x87ʥ\xa1\xeb\xc7s\x0e\xff\xb5\xec\xe4\xc7\r\x1d?\x1b\xbb\x8b?kL5>4\xbd\xe7Hj\x12\xb7\x9fv4\xce\x1a\x9d\x9cl\xb6\xe8\xd5!\xbd\x1e\x05\xf4\x16\xb3,\x80\xffc\xd6C\x915\xe54{\xf2ٱ\xee\x914d\x88\x8aR\xd0\xec~\xfa\xb8p\xcf*;9n\x1f\x8b\xbc\u007f\x8d\x9fC%d\xfe\xbb}\x1c\xa0\x1dd\x9cA\v4Q8\xf6i\xbb\x88O\xf8i\t\x9a\xb8\b\xb8T\xfd\xa7\xe8\xa2\xc6gd\xf2\xc3ul\xfe\x18\x15U\xeaS\x89\xb8\xaeA\x88q\x99$.j6U;\xd4MǶ\x17\xb2ۏ\x1dێ\xf3ۏ\xa1\x83\x8ej\x939J\x99D\x8dvAF\a\x8f\x96b\x8em\u007f\x16\x1d\x00\x04\xbaLOI\x1a=`\xe1j\xb9f:\x92\x94>Iǁ\xacJ!\x18\xc8\n\x966T\xacxư\xb5qn\xf3\xe0\xe6̓\xfc\xe6S9\x94\x1dĀM|\xcc\xfa!ґ8X)\x05\xc9\xdbh\xc2ͅ\xbc\x9aͳ\xa4(\x00\x83\xc7\x06\x8c\x87,\x81ӌ\x95\xcd\xe7\x8b2\x8b\x80\xb7\xbf+l\xe0D\xa0\xf2\xaa\xb8\b\xc7\x053Qɕp\xa1$`\xe9\bPt\xa0[\x80\xf4\x86\xf6\xd9\x11 \x1f\x88\xb2\x81\x01\x13D\x1bV\xbe\xbb2\x87op\x1b\xa4\xc2o%x\xe2Z)\x8d\x06\x87\xf3\xea\x80\xf0n\xe4\xa8:p4\x9cN)\xefF Ն\x1c\x19t\xd3T\x067M\xf5\x03u`8\x8f\x06\xf1P*r\x14\r>\xab(\u007f\x8e\x16\xf1O^\x93\xfd\xf0\x8c\xe6tX\x10\xe5i(\x8c\xf1M4!\rt(\xa8>h\xeb\x99\xd5cU\x0f\x06\xd1\x04\xf4<\x9a@\x8a\xb2\x15ܦ\x19ç\x82\x8a\x12$\xe2\x8cM'\xd1\x04\xf5\xe4(\xb9\x12\x99J\x88׳\xbb\xaaQ\x17\xd4܃\xf4\xae\x89<8\xe6Vj\x80\xcfj7P\xf8?Ͼ;\xd4\xf8\xf5\x9c\x90\a\x9a\x87\xf2_\x81\xda!Q\xa5\x9e\xa1\xf5.h|:B\xef\xca)Ӓ\x00\x95\x13\a\x04\x8exܘs\xa2\x1a\xb7_\xe2\x81\xd6\xf3\x03d9\xfb\xd6\xf7aN=\xa9.WO.\x14\xb7\\|\x8d_\x1fO&t\xfek.\xde\".D\xb9p\x105\x053\x1e\x9b͓\t6\xa1`8\xd5\xd7\xf7\xe2I\x15\xfau\xf2\x81\xdb\xf5\xcf\xdc\xfd\xabKj\xeb\xebk/\xf9\xd5\xdd\xcf\xe8wi\xebU\xfc\a\xf4S\x04\x18\x9b\xccus\xb3\xa0U\xdalr\n̥;\x83\xe4ѠMe`\xacT\xb8\xa1\xf2B\x05\x96&\xa0\xfbn¦\\\x82\tg2pd\xc7\xd6\xc3[\a0\x17\xb0\xabO\xdb\x03v\xb4z\xe1\xb1\xed\xc3\f\xcaI\xb6'm%\xc44\xc9\xe2\xf0\xb8\x87\x19\x18\x12\x001}\xd6\x1a\x1b@\x81\u0080:įZ\xa5\x0e\xad\xf2/\x00r\x1d\r@1m\x038_.\xa7\xf0\xe3W\xb4R\xb6\x1f\xab\x92lv(F\x145A\x90ս\xb7\x9a\xa1\x14\x1b~]\x1d*@Qؿ\n\x05V\xf9\xa1\x94\x05\xe5\xf1g\xf7\xe4M܊\xb1\xb2\xb6\x93\xe2\x1a:M\x8f\xa7ʞQZ\xcc㖵\xfb\xcb.\x14\nH\xa2\xecf\xab\x9eJ\xd7wK\xf4\x86\x85I\aA\x97\x85\\\xa9\x8b\xdc\x19\xb3\xec\x9b\xdbf\x107\x97z\xe7\xf0\xfbl\xb2\xeb}5\xc7V\xff\xa0z\xfc\xfa\xed\x13\x89G\xc7\xdb\f\x06\xf7\xe4Ɛ\xe4\nu̻vߋ\xeb\aa\xcb\xf0ɰ\x93\xe3\x90Z(\xf5S6W\v\xbez\xbe\xd4\xcb7e\x83\xd9k\xd3\xe9\xd1[j\x0e\xf6\x8b\xc6\xfc\xee\xbd\xea\v\x1e#6[\xea\xaf\x1a\xd8\xd36i\xc9\xc0\x82\xc5Sڣn\xb6\xc1@\x92d\xa9\xef\xbb`\xae[\x98\xe4\xa0}\xbci\xa5]<{b\xe9N&k\xf7\xf1\xa3\xa5\x1cG\xba[\x9eQ\x83\x99\x88\xa3\xe7\xd4`\x16E\xe7\x1f\x87\u007f\xea\xed\xf5\xaa\x8b\xbc\xdek\xe0\x8d$|'\xbc\xae\xf1\xe2\xd5\xea\xb7GO\xa5\x01\x97\xa7R\x85\xa94\x10\x1d:\ty\xbd\xe8\x9fX\x06\xaf\xfa1d\xa5\x85hz3T\x1e\x9eʷL-\x9e3\x8c\xd5D\xd9G%\x01\x02\xc6Z\n\xba\x9cb锥\x9b3\xe3I\x95\xae陌R\x90^cy\x10,\xf73\x9c\x9c\x04\x1c\x15P!\xe6\xa4\x0f@\x8c\x00ieN\x19q좀FS'}@4\xa2шÏ~\xba\xf4\xf2\xe8\xda*\xf8\x00T\x0f(\xcd\xf8P\xd0\xec\xe8\x18Y+\x97=\x9d!?\x93}>\xb9Ю\xe7+\xa4\xe5\xf5\xc2\xf5\xb2\xc9w*\xeb3\xc9\xf8U\xbd\xa1\xb0\xa2\x84s\x03ƽ\u00a0\xaf\xd0\x0fi\x1a[\xfe9\xaaa\x89\xd2\x19\xb1\\\xdb\xd9u\xe2We\xdfY5\xb7\x9f\xa3\t\x90\xd8\xe4+\xb4\xb3\xb6\xec,\xf2\xbb\xaa\xc6iK\xb2\\\xf3\xe2ʚe\xf9<\xb5\xc1\xe8\x8e\x14\xcf\xfaz\xb3\xb0K\xd8C\xb5&\xf4Hd\xddbktݩ7<\xc1\xa0Gh\xf5\xe0\xcb\n\xb5f\xa7O\xc8\xfb\x9cfp\x85+d<\x8b8\xccY\xf2\xf8\xc2X\xd5\x1f\xae(\xf9ϴ\x00\x86s\x95>!;B\xf5TR@J\tvK\xf5\x9cU\x8b8\x8ab\x1aU\xceH^Q\x1b;O\xb9k\x9eb\x0f%\xd6[Q\x9aH\x87\x0e\x06\x88\xa7O\x99\x0e\x1f\f\x1d\x1d9谉\x8e\xa9\x01\xbf\xca\x1c0r\xf00\xf9\xf0\xab\x06}\xb9\xfc\x12\x9c\x9cU\xbe\xc7>\xfaʔV5^\x8d\xa3\xc1\xe4ܵ\x83\xe3\xe8\xc8\x00\xe1\xa3\x06}ecF\xad\x89\xd1m\x19ۈr\xed\x95\xf5\x8e\xaeqLEl\xbe\xa1\x06\x11\t\"\xc0I5\xc7\xc9ڦ\xc0f\x03U\xcc\b\xad\xc72\x02c\x14\xf6\x84W+\xa6\x05O,\r\xb1\xaf\xf0\aM\x16J\U0005d0676\x9e\x8f\x9ey\x94?*\xfc\x010&N\x8fݚ\xf4xq?\xa2\x1b)\u007f\xb0\xf0>\x96e\xf9(\x9d\t\x1f@\xf9\x1f\xc0qT\xe6\u007fVx\xbf\xf0>sjA\xf0\xa0i\xb42W@\x99W\x16\xcb\xea\xf0U\x83\xf3\xec\x10\xb4{L\xdbГ\xe7K^\fA'\xce96\x1e&\xe7E[\vh8\xa0\x95\xac\xad\x1e\xdaJ*\xb7X\xd1>\x81w\xac\x84yW\xd7\xfd\x16\x9e+\x1d\x0e\xb4\x9eV\x81\xeb\x1c\xc3c\xe5*\xeb\xfd\x0e\xa8Y\xbd\xfa\xb7P\xb5\xc3\x0f\x84\xcc!\x99\xa6\x9b3\xa6\r\x95\xf2^\xed\xdc\f\xc0\x98\xc7\xea\x8d%\x9b\x11\x10\a\"\x1b\xb3\xa2`\b\x9cɒ\x05\x95R\xc0\xf9\x9b\xe9\xe6\xc3cD@\xcb2\xe3ܵG\x97\xfd5g\xf5\xec\x91L6}*X\x9fl틵\xf6\\\xcd\"\x9b\x82\x81\xfa\xf6\xba*\x94\x1b\xd3\xfa\xc1\xb2p9\xfe\xa7\xe5\x87\x16\xfd\xdc\xeb\xb8B4M\xf3z\x93A\xa5\xd9\xed\xdf65L\xa3\xe5.\xd9\xe1\x9a\xd42\xa7k,0\x8c\xf4\x89\xd2^\xed\xa5>\xd9G@\x8f\xf1\xb9\xcb@H\xc6ty\x94\xc4\x1f\xe7\xb0\f\x94\x04Z\a4iepWt\x10\x9f\xd5\xf8A\b\xe4h,8\xd4<{9Ƚ\x83\xc5\x10Ƿƶw\x04\x0eZ\x98\xeeOY\x17\xa4\x19E\xcb<\x17\v\x92\x12\x14\x01\xa2\xfeZ\xe4)\xab\x89t#\x8d/\x03\xf1崐\xaf\\F7ʔ\xd3B>(\x83\xff\xe2&\xba\x906\x05\x8eld\xcbi\xe3\x91\xc0\xd8\x00t\xbd\xe2\xbb/\xf2\xee\x11\xe6=\xf2n\xe4>\x1a?&\x00s\xe7\xca]\x0e@\x13Ν\xbd\x180Z.3Ĥ\xd89M\xf1G\xd26\xc6\x1eX\x10I\xedJ\xa2H\x9dXa\xe5:\xd3\xe7\x92C\x9c}\xc0\xe03\xec\xdd\v\x8f\x03\x06\xfa6\x8c\xf1\xbf~>\xc9D\xf4\xfd\xf13\x95\xfdU\xe7\x17O>[vZ_\x81}ס\xe2\xa1qN!ʃ\xea\n\xba\xbc\u007f-\xcbW\xc2\xfb \xa2\x9b\xfc\x95\xf2\xce\xf3\nS\xfe\f\xf2\xc8Ha)Y\x16\x9a\xf7\xd4'l\xe7g8=\xe7`z\xc1\xc9(bwv\x02\x93\xab\x04\xa0\x82\x86i:2E\x8d\x1d!\xb0\xac`\xd9;x\xed\xf7\x0f,\x1d\xae\xc2\u007f\xbd\xf3Y \xa3\x85\xc0\xce\xd7\xd5ߩ\xff\xaa\xfe\x8e\n=\xc1\x96Іj^ǻ\x8f\xdcQ\xb0^\xbc\xec\xc0\x0f_\xc6\u007fYy`\xf8\xc1\xa7Q\x8f\xfa\xaa\xfa[&aY\x8b\xdaQ\ru\xd1s0{&\x05m胑*\xea\x0e\xb1\xd9\xd5\xf8j)\x8d\xb1\xc6T\xb2\x10C\xac\xa6$\vY\x14Q\x94>*\x86P\xd8\x16\x89\xe0\xbb\xe9}H\x9f\xa2\xa8\xbf\xc1\xf9\xe4\x14\x9c˥\xfa\xd4_\x877\x86\xfb!n?\x13Vا(s\x94\xf5\x90\xa0O\xc3GRB\xbeX\x9f\xc6\xdbbG/*\U000e8d09b\x14\xa1\x90\x8f\x14\xb6E\x93\x89(\x94\x8f\"\x85lrʔ$Ϋ\xbf\x81\xfa\x95dJ\xc1wGp6\x1d\xa1\xcd\xe8\x83\nP\xa4/\x05\xb5#\x05j\x87\f\x85mtC\xe7R0\xc6}B\x96j̣R\xc7\xcaX\x8fv\xf0\x17\xbbI\x11\x97>(\x89j=\xfd\x9a\x15\xc5:\x02E\xe1\xbbC\x99t\x84V\x97:O[h[5\xdc\"u\xe6E\xa83W\xd2\xf5.\rf\xb1[eܫ8\xb6P)\xede\n\x9e0Rԁ\x94\xfed.ُ\x14:~}\x11\x9c\x87\xb8\xadt<)\x8f\xa7/\x12Q\u007f\rc\xdd\xdfO\xe7B\x81\xbeGGp\xce<\x85\xf7\"-G\xe5\xa9-\x02\x1cb\u03a2\xaey3\x8eb\xe8\xab#\x98\xb0\x97\xe85\x95RPCk\x1e{d˚\xee\x90 ح6\x93d\xb2\x92]\xa9\xa7\xf1\xf7\x87\x80\xca\xc2\x1c\x01\xaaL\xa5d\x17\xe2Lu鋶\r\xae\xcdL\x13Cz\xabӮ\xf7\xc1IYs\xf4\xb5;\xd0A\x8a\x89@*n\xd4yڢ\xb5\xc4\xe3\x1e\xc1\xceKˏɩ\xb5\xa0\x12\xdeE\xb7\x17\x8d\xe9\xf6W\x83\xfa\xd5\xfbeM\x98\x16\xaa\xbf\x1f\xf5â\xbf\x8ex\xa8[\xfd*u\x1b\f\xa8\xff\xfe\xa2\x04-z\xdf\xc7җ\x85rizH>\x9b\xc9\xe2\xd2\f\x90\xde\xc72$\x95\xa2\f\x9f\xf9\xcc=\xc2_\x85\xeb\xb5\xf6\x9d\xab\x1d\xe7j7\x93{\x1b\xa7!\xe7h7Ύ\xdb\x10|p\xdcfs%9LA\xd3Q,\xae\xc72\xb0\x96WH\x19\x80(EEug\a\x98\xfe&\xd5/\xa1\n$̃\x06cm$0^(K_\xac\vC]D\xa3\xa1\xc6\xf0\x89i\xb9\xa7\x99\x98+\x9f/\x96TR\x12\xd5hOJ?\xa5\x98N\x8c\x15\x0e\xbe\xe8\x88ޛ j\xaa;\x13\xe1\f쁳\xcf#\x94\xdb\x1bISm0Q\x12\x84\x1f4W\xfb\xb2\xbe\xab\x9aՏ\x18\xa4\xab\x1f5_\x05\xfe\xeafd\x00\xa7\x16\x85\f\xda\"0\x14\xa3ԏ\xd0\x1f \xf8\x1a\x88~D}\x9d\xa9R'\x1e\x81\xf0k \xfe\xd1GK1(\xc1\xb4\xb3_/\xc7T\x9e\a\x94F\x99Ȥ8\x1d\xa5\x1d\u007f\xac\x0e>\x91\x93Q\x1a\x008\xaa\xac\x85\xe0\x16m\xcb.m\xf9\xf8\xcb\x0es\xde\xect\xc2Á\x1d\x06\x83\xe5-\x8b\xc1`wZ\xbea\x91\x85\xb1x\xc8\xe9\xffx\xc5\";ͯ\x98\x9d2\xba\x12o2\x89:\x9dh*\x1c4X\xad\xa5\xbb-hW\x963sn\xa0\x96\xe7P,ɞ\n\xba\xec\xae\"ޗ`7\xccNw8ɐ\xe7D\\\xd3\x1b\xab\xd4\t\xd3(,f鄝\xce\t\xcd\xf4I\xdcM\x06\xd5|؟\xf7\x87ն\xef\xdc\xeak\x82\x99ÿl\x8b5\xf9n\xf9v\f\xbd\x00x\x14L/L\xa7\x86M}\xfd\x92ݻ/\xd9Е\xcbum\xa0.\xf4u\x8b\xe3\xabm\xe8d>\xafNh\xab\xaa\xae&k\x8fԵ-h\x83\xbf\xba#\x83\x14\r+\xc1\x94\xa6q\xb8\xfb\xa5\xdds\x9f}v.\xbc\x1c\x1a\x9fL\xd2\xec8\xccc|\xe5P=/2\xfe,\xa0\xaeT\xe8,\x9c\xa0\\f\x91\xd3x\x80\x88\xea\x0fP!:*\x98\xaa\xe9}\xd2\xcbuL\xe5v\xbayj{C\x00\x1c\xe7\v\xea[\xbf\xdb\t\xab\xcb\xeb\xaa^\xed܋\xa4\xaf\xf9\xb0\xe2lV\xdf\xfd͛C\x0f\xee\xb3\x1e\xf0\xd8Z\x9a\xbak\x9a\x1a\xaa\xb0\x8e\x90\xee9\xdd~\xac_\xf6\xf0+\x9b2_\xfdʗ\x1f\x8a\x1a\xa2\xce\xfa\xa87\xda\x13\xb0\x11%\xa9\\~\xecN\x97\x17V\x9cw\xb5|\xf3:$^\xbafH\xfd\xf6\xa6\x8d-\u009cl\u007f6\xd4\xc8[D\xb3\x14\x9a\x9bn\x97\xf9i\x86D\xea\xfa\x9f>\xb5=\xec\xb0\x12}4b\x88\xda=\xfaU{\xb6\x96x\x1c\xd0C\x91\x83u\x93\x80\xd5:\xf6\x86\xc5\xc96ݨ\x8718\x05\xd8\xc1=\xd1Z\x9e\xde%\x8dܓ\x9d\xe1&\xcd\x1f\x18\x98?i*\x8fV\xec\u07fb\"\xa3\xf9z\x89\xe6\x1b,K\xaf\xf3\xf2\xbc=\x97,\x9e5keb \x87PÒ\xad\xb7}aM)d\xf5\xedŐ\".Aǝ\xa72\xe6An\x12\xacK%\n\xbb\xbe\xc6\x0f\x17%7\x00;\x9b\v\x8dQΤx9:\v\x01\x8eJ'\xa4\xe1\xed\x11s\x87\xdf\xe9\xd2\x04\xaf\xba\xde9\xfc\x19\xf4\x00:\x89\x1e(\xbc\xe8w\xde\xf2\x15\u007f̿s\xa9\x93lt\xeeW\xa3\x85\xbf\xa8\xd1\xfdN\xe7~\xf4+lA\xbfڏ\xb3\xefm[w\xe37\xa8\xca\xf07n\\\xb7\xed\xbd\xd7\xff\xfaW<9\xe6\xff\xca-N\xbf߹t\xa7\xfa\xd3i\xa1?\xa8\xef\"\xf7;\xa1i\xa1w\x90[\xfd\xf3;L\xafvP\xa22\xd9z\xae\x8a\xeb\xe2\xa6r\x17\x01\xe4g\x9a\x11k\xaacl;#\xb4\x9dE\xee*\xa4\xa0b\x17\xb4\xc5\xc18\xd3*\xa5<~\x19h!\xe4\xe6\x19\xfb\x99\x87\x93:\x9cQ\xa2\x19@\xb4qӼek\xa0/\xcf\xe1\xbd#\xbd@w\xa2\xf5ꪫ'\x19\x1c\xa6\x9d\xb6\t\xf7\xfd\xe7r\xa7\xf3\x11\xf4*2_\xbc2mp\b\xbepm\x90\xd8\"Oގ\xbc:\x94wFg\x1cR\xb7\xfdۜ\x93\xe8\xea\x1b\xaf\u007f\xae\xe7\xd2\u007f\x9e\xfc\xfd{z\xf2\x1bh?U\x15_3\xd2\xcd\xff\x90\xf0\xf1\x82\xe9\xe5\x8bm3\xa0ؾ)\xbf\xdc[\xd7_\xf7.\xb2\xd9/\xb3\x99d\x87\x8c\rj\xeb\xdd\xef\xc4\xd1G\x13\xf7̨\xcf.\xfc\xc2+{\x1c\x1f\xbc\xfc\x95\xeb7g\xbf|\xa96w6؟>d\xf0\x14\xa4\x105\x96\xfe;{O\"\xc8-\x95\xa5<\x11\xbd\xec\xe0+\xeejaW2\x1b\xdf2\x9a\x8b\xbb\x92\x99p\xa1\x88\xbd\xa3\xfe\x14W\xdfa\x8f\x84\bg\xef\x9c\xd1y\x046&\xd9B\x1fh\x0f\xfa\xa1I2\x1a%\x8b\x9a1\x98\xcd\xe4\x85S\xb9\x9e\x9e\x9a\xfa\xfa\x1a*\xbe[\x17\x0e\x17Ϥ\x8d\xc2F\xaa\xf7\a۷\x15\xc9%nwT\x8f\x18\xe7\xbb\tQ\xb1\u007f\xaa\x10Ķ!=\xd2\xdc\x1e\x01\x96\xbc00!\xdb\u007fdP\xb0\xe7$\x13O\xac\xa2\xfa\xefj!%\x98\a\xf4\x16l\xd5\x1f\x1f6bd\x00\xb7\x88\xbf\x8d\x88\xca[\b6\xe6,6\xfc\xc8`\u007f^\x18H\xe6\xfb\x8f\x14fɖ\x01\x11\x113\x1aV\v߶[\x06\xf4\xd88|\\\xb2\x99M\x97\xe9Q\n\x11\xe4\xd1\xd9lƜYxj\xb0?KO\xb23\xda\x1d\xc5ٲ\xd0%)\xe8\xf9\xdc\xf5\x1c\xe7)JrGƼQ\xa5\xbf̼)\xee\xc7\x15\xe92c\xe2\"\xa3\x84\x1e\xcb\xe4^\xb0\u0096\x80;\x87\x02\xea\x10\x1a@Y5\xaf\x0e\x8eu\xe3!\xe6\xce\xd1'\xe1h\x88\xe6V\aGTi M9\x1c\xb1\xd2\x02#\x91(ן<\xc54\xd8s\xab{\xb3\xbd\xab\x91\xf6\x82\x10\xad\xde@\x96e\xcbfQ`\x18\xcaGy\xed\r\xa18\x80\x02L\xea\x95\x1a\x1f\x18\xfe\"KB3\xe4+\x82\xe7\x9ef\x06O\x04x\x0e\xd0\xeb\x85\x01\xed\xd9_\xa4c`=\vC@\xc5d\xb8-T\xa7Oj\xe6+\xc4\x14Jw\xd8]\b\x88\x9af1\x9a\xce\xd4\U00089826J\x80\x1c\xe5\xc8 \x1c\x05\xb0\x84-\x95\x12\x0eL\x98\xaf[,\xa7Əv\xceu\xd7&\x12}\x13\x86\x98z\xeb)Aԫyz\x9f\x1dX߶\"\xd9\x1f\xefM\xb6Ww\x14\x93P-蒺\x1fMr\x86k\x9d\xd7\xd9\xe8\r4\xd74L\xedZvɎiZ\x19c\x02K\xb9\xf8\xbaU/N\xcc\xccj\xa8a,\x86a\x8b\x9f\x96\x02\xeb\v!\"Y<\xf5\xcd]\xd1K\xbe\xca\xe2\xa9\x1e\xa2\xfa-\xb2\xbd\x94\xa0\xb6\xb3\xa7\xb9{S\xef\x8a\x1d\v\x96&\x82,\xf3\xa8\x10-\xf9\xc8\xfd\vl\x87\x145\x05\x84\x04V\x94(\xc0\x1e\xa6DSJZ\xa1\x87\xa0\x90\xa1\xe6\x11\xba\x11U\xa4\x93\xb8\x0f\xd5+\xfe6\xa3\xefU\xf5Ԥ)\xf6j\x9e\bȀMXju5xk\x8dO\xbcx\uf1e8\xffk\u007fC\x8f\x93f\xf5\xb3\xea\xaf>\xaf\xfb\xe7\xa9\x16\x1dv;\x10o\xe3\xadĂu)O[\xf3\xac\xd8\xc5H\x8b\x1b\xfc\xfa\xb3%\xee\xee\xb3rJ\xc6\xef\xc8r\xa1ZN\x95\xf8\xc3C\xe2Q\xf2n\x89?|\xd6\xed\x9dx\x90\xf1\x87\xcf\xe2\x06\xe3_B\x19\xb4\xac\x14\x14*k\x8c\xe9gY\xa1\x1an\x94\x833:B\xb5\xac4\x1bWͤ\x13u\xa1Q\x9a\xaf\x9a\xde\u007f\x90\xdd\xfa\x97.\xe2R\xdaM\x9cF\xad2\xc3\x1e\xc4\x0f\xa4>\xbd\xab\x138\xaaG\xa73J<\x06Z\u008a\xb2r\xa7\x8c\xb2Vŗ\xc7Y\x9b~P\xc29\xab\x9aw\xb6;\xd5<\r+\xe4i\x18չ+\xe5\x805\xce\x1bD\xbb\xe8D\x83h\x10p,;ʹ\xddj\xce\xee\xa5\xc2fƼ\x01=䵫9\x8f\a\xb1 \x943\xe5\xf5Ƒ,\xea@\x05\xff('h:\xd4\xed\xd4Ƌ&m\xc1\x17\xdfTkP\x12\xe8\x1dq\xd98\x1f\xc3\U000e8d31\xd6!\xdeä.\x98\xe5\x0e\xf2#\xf6\xfaQ\xd0{\xfa=\xb3\x97\x1c\xa6\xf3=\f4\xa6\xcdV\x8d\xbf\xf4#\x8d\xd1m\xab\xb2\x98x\t\xf1_\xf2)If\xedC\xfb#y\x95\xf3F\xc8\xceN\x83\xad\x95\x8a\xb2\xfb\x8c\ru\x93\x04\x92\x01\xb7\xd9Q\xe7R\xa4\xd2\x1dPQ\xdf\xcdy\xf6\xfdQ\r\xe3u\xe4\xf6\xac:\x9d]\xb5g\xcf*\x04O<\xb8j\x0f\x19,0?\xc9\xd3g`O\xf9N\\Z\x06\xe5\xc8\\\xa3F\xedk\xe7rIݝJ%QM\t$%G\x97/-\xabS_hz\xac\xf7t\xbe>U\x87\x16\x80\x8b\xcf֧\xd4c\xc3\xf9\xd5'\xba\xd4\u007f\x16P\xb1\xe2\x00\xfcfՅԭ\x89\x99\xbeں\x10\xda\x0fo\xd4>x\xe9,u\xab\xc8\xdb\xf9\x8a\xc6P^\x0e\x87\xf3\"\x93\xad\xe1\x18\x10\x8d\xbd\xca\x1d\xb9\xb8\xc5yXdc\xaei+\xaeY\xf9_'\xe9z\xad\xb8\x85\xe5\xb9\xf2\xa5\xab6~(+q$\xaf\xd5U\xba;\x1e{S<\xfa^x\xdc\x02Gn}\xc7\xdc\xf2\x16ou\x8bvXt%\xfd&3`\xb8.:gA\xbb\xa6\x85\x15\xb4'\xec%\x1f\xce\xc1\b\xc0O\xe0\xfe\xc1\x010j\x1e\xaa@E\xe5w\x86\x81\xe2:\x9d\x83\x1d\xef\x14м\x05\x8ejd\x9d\xa6\xabq\x84g\xd1\xcae\xd9\xe9\x95\x06\xff\xa7\xc2\x17\xb5\xba4\x12c\xc4&\x0eū\xa8\x86Y3\xd7]\xa4\xf4*\xb0\xf6tI\xda*\x98\nr6% \xda\xdc\x01\xea&A\x00\x13\xa9R\x85^3\xbc\x06\xb4$\x15\xf2p\xb4\xc5,\u007fa2GÇ\xa8}O>W4\xec7\xfc6Ո\x83n\x0e\u007f7[\xb8Y\xcc\xf5\xa5Nq\xa9\xbe\xbe\x94\bO\xfce\xbfcu/=\xcbcm:&\x964\xfc\xed\x1c\xaaC\xddo\xd3\xcc<\xcc\u007f\xfe\x86}\xb9\xdci\x96A\xa0O6\xe7\xb3ă\x8cN\x9dY\x94\u007f\xa2\xd3\xcc\xc8m\xda\x05:\xc1̲f3\x8eJ\x9e\"\x03M\x93\xc4K:\xc2Ek\x90\x99\x92:e-O\xb2\x9b\a7\xcb\r\x8d\v6\x17\xdf\xe4;k\xec\xfah}\x13\x19x\xcb?\xaf1\xe6/\\\xf6\xc2\xf1g^\u007f\x05\xc5\a\x9fy}7\xba|\x804\xd7\a\xd6\xd8\xcd\x06q\xc1\x92\x8b'\x93\x17\x067o^\xd0\xd8 o.\xbeUξ&\x00\x87\x03d\x8e5\xce\xf3\xe3\xa7v\xbf\xfe\xcc \x8a\xbf\xf2\xfa3\xc7_P\x9f\x18 Mp\xc8\xd9\xd7\x18Ĺ\x8bV\xf4jl\x04\xee\x8cU\xca\t\x1f\xc2\f\xd9a^vqǹ\xd3܈\\\x97\xd6?虽쪰\xf3\xe3:\x8f\xa1\x9fOo\xe6\xa7\xc2\xc8\x0fb2AL2\x88\x12\xf9\xb09\xd0zXv\x85\x8a\x03Q\xf1 V\x16Uq\xf6\xc0^\x01\xe5k%@\xab\xec\xff\x9d\xcc$Ǡ\x88\x1c\x82#\xa1o}\x9f\xf6Ts\x16\xfdcFW}\x9b$y\xb6\xcbF\xc3\xf5\x91\x98\xd1$y^2:\x90\xa7\xbe\xe1\x06\xc9l4\xdc/\x19\xbam\x1e\xd3a\x83\xa5\x9cԽ\x83&\xado\xaaL\xaa3Ѥ\xa6N\xab\xc7\bIq\xee!\x93#\xc1\xefĺ~\x8b\xd3\xe9\xb4\xf4\xeb\xf0N>\xe10=\xf4\x90ٞ\xe0\xf9\xee\xb6bD\xa2A\xe4w\xf0\t\xbb\xf9\xa1O\x9b\xbeh\xca\xe8\fC\xc2\x01\x80\xf9Tѡ\xde\xff\r\x83\x8c\xbc\xa1\x86֩F\xbdI\xaa\xdd.\xad\x90M\x1b[\xbcV\xc3#\x06\xd7Œ\xee3\xd5z\x83e\x9e{\x82\xe2EvceR]\xed\r\xd2\n\x87ecsER\x9d\xd1\xd6\xefn\xad\xf7`{ah\xbf\xcdZ]\xb5\xa5\x8a'3W\xbb0v\xad\x9eIx\xf0V[m\x10Q\xe3\xa1\x118\x1c\xb8\x04\xa2f6\xe0\x194\xceSc%\x1f\xfeWr\x95\xe5F\x18.\x1caR\xaf6\x91a\xc3\xcc\x1c\x10Lv\x120\xe1n\x9e\xb1\b\xe8=\b,L\t\xf0\x85Z\x91B\x1aU\\\x8e\x8a\xa1\x00]\xb1a\x80JX\xbb\xd4L\xd0\xcb\xea7\xffe銛\x1f\rljQƀ\xb4c\x81\x88H\b\xdbj\\\x86\x9b\xef}\x19MG\xb7\xa2\xe9\xb8\xf3ޛ\r\xae\x1a[X@\"\xd5W\x84dNS<\xfc\xe8\xcd+\x96\xaa\xff\xf9\xfd\xf6\xda#(\xb6\xf5\x96;<\xb7\x1d\"w\xab\u007f~o\xafmyL\x0f\x94'\x91D\x91\x97\b\x15\xdbpEb\xdeY?\xdd~\xf7{{\xf7\x16\xf6\xee\xf8\xc9,o,\xe2RD\x04\x91\xbc(J\xc4bC\x92>\xb6ܶ\x87_\xb1dՇw\xcc\xed\x9b\xf9f\x19\xeff\xbas\x9dܦ\x11k3\x88ގ&\xd3\xf4~\xbeL\t\xc1\x11\x0e=\xa5$&\xf4\xab\x1b\xc1\x81Cyd\xb0\"\x9dle\xb0\x9f\u0604\xe9 \x8d\xec\xa7tQRʉ\xea\x9c@\x02*\x1a\xad\x99\xa4\xe1\x8f\u038b\xab\x83\x03\xd9\x01\x9f7\xd2\xe0\xce\xf0JՄpC\xd4\x16\b\x98#5-\x9eV\xe1g\xbbo\xcc\v\xb5!G\xcai\r4\xe5&\xe9\x15\xc0N\xbfpO\xf8\x92\x81o\u07b4խ\x0e\xd1\xfd\x139\xc2k\xdb'y=JS4\xb1\xe4\x8e\x19\xad/\xac;\xac٬\xc1\xb9\xc4\xdc\xf6\x1fv\xacY\xed\xbb\xe13M\x9eiB<\x90\n\x85\x1d\x85\x9c(Yuv<\xfb9_\xadm\xf6\x9c@|zU\x97\x1d\xad\n_<'\x18\x9e;\xd5\xe5^;\xf7\xee#\x13\x9bb})\x9cK\xf5yw\xf7\xa5\xaan\xdc\xd3\x18\x99\xb2o\xdb%\x97\x1f\xe6\xca6\x98\x98,i7\xa5-+v\xb4(\x9bk6\x1ei\x8dc\"Y\x04m\xc4\x04\xaa\xe7\x87=t#WRT\x99\x95\x9e\x88\xa5m\x8e\x89\x8dR\x1b[\xe5\xe3\a\xa0\x86na\x92\xab<\xa4\xe5\xf1j\nX\x9d)G\xa8VX\xb3,\xb7\xfbgB\xab\xa7\xa5&b\x0e\x04lц\xf0\x84*\x85ϸ\x1b\"^\x1f\f(\x1a\x88\xcf\xcb\x1d^\xf7Bk(tǒD\xb4>f\xf4ʭ\x1dk\xc3\xea\al\xd0\x02\ueb79W\xae\u07bc\xff\x8b\xa8\x93(\xfaI\xbc\xa6d\xa9r\xa1U\xc8\xdeU5=^\xa7Df\xdbj}\x17-\x9a\x8d\xed:\xab$\x16r\x8ep(\x15\x88\v\xd3%\x12\\\xb3x\xa9+>w\xb7W\x1b\xb7\u0604\tO\xee\x15\xe6\xaeu\xbb\xa6\xce\r\ag\x17q\x82/\x92,\xa3\xc9\x01W:˺/Ɏ\xb5\xde+\f\x9d\xfa\xde\xd9\xe6y+\xd7&\xd5\u007f\x9dLo)\x9b\t\xa3\xd4\xe9@\xd6[\b\x1c\x9b@\x13e\x8ax\xe6\x18\x9b\xc9b\xaei\xea\xc0\xaau;\xd6\xce\xf2:\xba\x1d\xdeYkw\xac[50\xb5\xe9\x9bx:\x9e\xf6r\xee\x9d\xc2\x03\x8es\xd8S&_Xx\xf3\xecf[b\xeeT\xbf\xdb\xed\x9f:7ak\x9e}\xf3\xc2\xe7\xbfYx\x03\xb7\xbc\xfc<5\xaa\xec\x18\xcf\xdcr\x19'\x17\x03\xb0\x97\xc4(>\x17q\xba-\xb8\x12\xd7p\x15\x03\x8ar\x9a\xed\xb8\x96\x94o\xcc\xcaɴ\x1b2\x9c\xc3H\xb0\x9aU&\xa5I-K\x15\xbd\x94\xf7\x81\xa8\xe5m\xbbh\xe0ɠ\\\x18\xa2\x82\x88YF\x98\xe4\xe9\xb5Y`\x80\x0f\xb8|fM0]63\xcf\xea\xdeB\xb6w5\xe6%#\x8e'i\x16H\x1c(\xda8\b\x00\xb2[\x18*\xe9\x01k\xb8.\xb5E\x05tc&a\x0f\xc1\xce\xc7Nm\xedV\x80JQ\xd4K\x89T\xc8M\xc0\xcd\x0eb\xd7X4\xf4\xfd?\xfe\xf1#4c\xeb왓Q\xc7,<\xfb\x8f\av\xdc5\x1b\xff\x91\x90?J\xd6\xce\t[\xd1\xc9J\xd4s'\xfeڛ\xc9iӒ\x89\xe9Ӈ\x9fC\xf7>\xfa䶵\xbd\x85\xfdh\x8f\xe2\bMz\x02__\x89m2\xde7\xb3\x99b\xa42\xf5HC'\xec\f\x9f\xa0\r \x16j\n,J\x18\x05\x94N؋\xc4\x1d\xa5\xf8\xec\xb0\v\r\x01\x86\a\u007f\x18\xe8Luq\x02\xdbM\xa8Z\xe8W7\xfa\x94'./\x9b^L^\xfe\x04\x1eDL\xb4\x83\xd9%S\xff\t\b\xd1\x1a\x93\xbd\x1a\xbd\xa3\xf8n\xfa\x1e\xe64:O\xe5\xbeW\xa6\xbf\xe8^\x1a\x1d\xcf\xfeo\x8df߷Rпl\x85q\xbc{\xbf\x11\x91\\\xcc\x1aPȖ叙y4*x\xfaBa\xabv\xfd\x87\xf7\xbf k\xa2\x8ax@͗\x04qY\u0092\x10.3\xc8H\x16Q\xa1F\x96\x8f\xdd\x02\xd2|\xf4:r\xc4Ɣ\x87\xc2\xe99`P_\xeeSR\x01L\xa7\xa8\xad\x10\n6b\x8a|jAn~<\x10D\xd7\x1f\xdbN\x15\xd9\x19\xcc\"\xd6\x13u\xa8\b\xb3\xc50\xc0\xf4\x1f\x1d\x1f\x14Q\xa2\x90\xad\x80\\\x9c\xd7 W\xa7\x81u\x99ާ\xb2\xeffn6\xbdoH\x01\xf5玤\x82N\tN'\x97S;\xc1\x10\xbb\xf8)̓vG\xc2vejO\xa4\xc8XJUP\xc5\xe8\as\xcep\xc7\xf9\xef\x9c\xe1\xe6\xdcs<\xb7\xf8\x9e\u05f74\xa6\x94\x9a\xae\xa9}\xdb\x1c\x96a\x98\x92m}S\xbbj\x94T\xe3\x96\xd7\xefY\xdc\x16C\x01h\x19eu\x06bm\xf8\x9e\xa7\u007f20\xef\xf9\x8f\x06~\xf2t\xcd\xf3'r3\xef\xdf:_H7\xd4\xcfM\xa4笜\xaeY\x98\x99\xberN:1\xb7\xbe!-\xcc\xdfz\xff\xcc\\\xacM\xe3a\xd2\xcbP}\x85\xce\x02\xa5l\xea\x00\x9e&pq\xee6\xee\x01*_\x1bU\xa8Y\x04\xed\x99IG\x8b~O\x1a\xba\xc1\xde\xcc_KU8\x9c\x19\x1a\x92FT{\xc2\xe5t\x80\x13\xce(\v\xa6\xb7\x16\xd0av\"CBf\x80\x8a\xce_F\xc9\xd0;Q\x98\xe7n\xecqӳ\xc7B$MU\x1b*r\x8c\x85g,\xf1\x92^\x80\x9fG\x00\x1cD\x12\xc4\x16,I\x88H:7FD\xd4\t\xe2Jl\xd4\xf3\xf0k6\x19\xba\xb0\x1bc'\xbe]\xd3u\xf8\xee\xa3\x0e;\x12\xe5\xe4\x84&\x9d\xa7\x1e\vFb\xb4\x88\x8eF\x93\xadiB\xc0\"\xf9&͙\x15Myk\xe4\x99U\xbe\xf6\xfd\xed\xc6P\xbf\\\xe3M\xf9\x9b\xb2]J\b\xd9\x1d\x8f~\x17q\x95\xfb\x05Z JP\xb7$5K\x84\xd7\xe9\b?\x89\xe71/\x10,#\tK:I\x9c)\xf1D\x82\x1fo\xb3Y\xa1\xc5:\x1eMg\xaa!'\x9fS\xff\xbf$\xb1\x99\x92M\x88 }\x9d\xafÊ\x04\xa3N\xe2\xab\xdd~\xbf$\xb6\xb8Ū\xd4\xe53\xe6\xb7w\xcf\x11\xabm6\xbb]r\u05cas\xba\xdb\xe7O^\x96\n\xdb\xf8\xball\r6\xd9H\x12\x19\xf1\x9d\x95{R\xc9vB\x9e\xcd\x1d\xdb\a\xceo\xbf\x8f\x19\xdaLg(\xb8i\xf7\xebZ\x14\xb5hVd\xc1\x9d˂\xdf\xe7]\x97w!\xae\xebr\x17\xfa<3\xe2\xd7H\x85/\xa9\xe4\xe5\x19\x8e\x1a7Cy\a\xccY\xbeN9\x87\x11\xbf\xb6Y\xb3\xda\xda\xf0@\xac\xb4Lc\x80\xa9\xe6eY\xcd֖\xcfY\xe1\f\xc0\xe0$\xeerz\xce2\xd5d\x8a\x0f\xb2k`\xa6\x96\x0f\x84\x15\xd58v1\x91\x0egI1\x9c\"0\x8e\x8c\x13k\x86~\xb4\xb4,\xa3\xc6c\xd3$\x06\xb5\xb4\f\xb4\xe0t\x06\x14y\x97\xc5h2\xe8\f\x06^/\xcfsv\xfe\xa9\xa3骩m{\xa7\f\xec\x9aT\xe5\xf6\xba\xbd\x97UM~{\xf2\x8bW\xdd\xf6\xf3\xed\xb9\xfdÏ\xdd\xfc\x83ɿm\x83\xb0\xd9k\xddU\xe1ٹ\xa5\xf3\x1e\xfd\xf6\xce\xce?\xb6\xcb\xfd΅s\xe0\x044a\x9b\x03\xbf:\xe1\xee\xeaZ\xffD\x9fg\xa5;\xe2@\xfaV\x8fם\x9e4\xfb\xdf\xff\xe3\xb6\xd8`\x83gل\x1aw]x\xe2/\x90\xf3\xeeg\xd5o\x9e\xceL\xa8\xa9\xb9v\xb6w\xb9'v\xa4\xe1ڟ\x9f\xf8ڔ\x8e\xaey\xad\x86\xb5K<+\xc0Ǟ\xa8\x94\x85\xa0\xba~NF\x9b\x02=ΐ7\x8e.'\xbeh\xbc\x88ٖ\xa5}\xc2t\xdb)v\xcfSK4\xd3Yԉ\xa9\x86\x86\x90s\x1a]kW\xaf\xaaN\xf6\xd4-Я\x99\xbbK\xfd`~k\x88\xd4\x1a\x1dR\xa2-^\xb5\xac\xda\"9BF%`%5\x96\xc9S'\x1b$\x17\xea\xff\xee^\\o\xa9\xd6;\xda\xe2\x9dNKM#_5y\x86\xe8r\x8f\xd9֖\x19\r\x9ej\x9f\x97\x10\xa7\x01\xf2K\xa2\x19g\ueedc\xb8M\xb2dn\b7Y\rn\xbf Nl\x9e\x14\xe0ݮ\x83\x93m\xe1\xaa\x06G\xab\xf8Y\xf5\xb5N\xec\x90̂0\xad9E&W\xeeK\x88\xcab\x89K\xe0|\x9fĸ\x8a\xb0\xf6\xb0\x05\xf1\x1a\xa5JﱵW\x17r{\xd8ݷ\x9b\xf7\x14\xedkQ\xfecZ\\2\xf5R\xd4\xff؛\xeaO\xbf\xa0\xfe\xe7ۡ\xa6\xb7_\xb8\xfah]\xd0\xdfԸ\xf9\xe0\xb4y\xbd\xf3&܈V\xbe\xaa;~\xc7\xfe\x81M\x03\x91\xab/\xe1\u05ed\x99n\xf1߮\x16>\xf8_\x9b\x1e\xe0\xf7\xe1[.\x13\x8c\x9e/m\xe3\x152\xe1\xde\xc5\xcb\xfb\x1e\xfa\x8aA\t\xdfq\xfcJ\xd7\xe4\xeb{\f\x8c>\xb8\xf4L\x8e\xfc\v\xe0M\x8c\xff\xcd8\x84A\x12\xa2\xf6f\xec\xda]\x1b\xf9\x97'\x96v\xa2HTUO\x9c\xe1μ\xf1Ń\xc2\xdf\xd4\u007f̚u\\\xfdeA\x8f\xff\x8eb\xbf~\xe9u\x8e\xe9:\x9fy\x8e\xcd\xeb\x12n\x15w\x05\xb7\x81\xbb\x9e\xdb\xc9\xdd\xceݥIٸ\x9c\x9c$j[Q\xb4\x9b\xa7\xd4\x1a\x1c\x98V*b\xdd\f聇nE\xc4C\xd1\x04*\x01\x82Z\x00ɭE\x94o\x04?҃\xd8&\ak=\x95t\xc8\xe5#\xb7\xf2=\xcaK\x8f\xe6\xa4T\xefrf\xe2\x19\x1aW\x12\xdcQ\u007f\xf3\xa7j\x1fJN^yٔ\x86\xf9\x91\x89\xfe\xf5Q\xe5\x92W/\xb1\xa5\xae\xf3O\x8c\xcco\xc8^\xb6rr\xd4\xe0j\xed\x9d\xe2\x91;\x9cN\x97M4I\x92\xbb\xc9`0wϚ\xea\xf6 _\xf5\x9f\xd4ߜ\xb8\x88\x18\f\x84\x18\xf4!Io\x10\xe1\x17\xd6\xebuz\xbd#\xae3\x99tz\xb3i\n\xb1\x01\x8dk\x9dj\xb7\xd9m\xed\xd8f\xe3\x03L\x12\xe8'\xa7\xd5k\xe7\n^\a9\xd8u\xd9Dћ\x9e\xbf\xfb\xa2\xed\xcbVn\xd1Ǽ^\x9f\xcf\x18\x98\xa8߲r\xd9\xf6\x8bn_\x90\xf6\x8a\xe1\xa9\x06CSC \xc6\x13\xbd\xc5\"\b\x866\x8fGi1#\x9eW\xd6\xf2\x0e\xaf0\x17=p\xfa'\xe8\xb2\xe1]\x92@\x048z}\x82Q/\nF\x83\"\x99̒\xe0\v\xeb\x8c&=\xfclF\x81w\xf3\xa2d\xc6F3v\x191\xf1\xeaF\xdduDFY\xa6V\xe8\xa4'F\xdb\xeb\xa1\xd8\x18`.bN\xf1\xc1\x19u\xe8䡁\x91\x8f\f\x00V\xcel\xee\x90|I׀\xda\xe4ɷ\xc5*\xac\x1e~\x83\x9a\xe4)\xf1\xec\x8b\xdfZ\xa9*\xda!+\xf1\xa6\xcbu\xb9\x18\xd4Qv\x87\x9d\xe9C\xd0M/\x93\xa2vԂ.q\x88\xeec\x9a\x92\xadY\x16s\xb2\xd9,\u007f\f\xcf\x01\xc4\xe5\x10\x94\xbb\xbawD\xad\x16\x82\x87\x86\xcc\xf2iN6\xe3\x81\u00a0Y\xa6\xe6\xd2r\x9aL\x8c\xa0\xe9\x00\a\xa9U߲\x06\xb0[cr\xd2c\x84q5)\a\x13V\x9f\xdb!\x94c0\x17\x1e\x8931;\xf4\x9aB0ތeG͝Ua\xecV\xbf\xf9NU\xd0e\xf7\t\x83(\xbc\xe5\xba;\xb1\x19;\x1d\xfe\xfb|\x11d\xfa\xb2\xfa;\xf5\x96_T\x85\x9c\x0e\x1fA\"\xfa?/}\xf3M\xa4i\t\xab\xdf\xf3;]\xc1\xaaw\xd0t7\x0eW\xddY㰛\xef\xbcn\x8b\xfa\xd6\xd3\xd5Ng\xa8\xea\x17h7\xaa\xf9\xb2\x19E\xaa\xee\x03B\xc9\xfc\xe67_R\x83E=S\xaex\xb7V\xc75P\f\x87\x1bs\xbf\xe6\x19\xfbm\x99`\xc9\xfcržYa\xad\x96\xb7\xb5\xf4\xb6\xb4\xf4\xa2\x16\xf6z\xaaRa\xf9t\x9c\u007f\xfc\t\xdek\x19\xfe\x8b\xc5\xcb\xf3_\xd2F\xda\xf6=\xfb\xaa\f\xb1dVٿgC\x97\xf5j\xd9\xe8߇%\x83\xcbT\xd5\xfb}\xf4[\xb3\xddn.\xdcZ$\x91\xb3Uq\xbc:\xd5ۛ*<\x1dgg\xc0n\xc6Gh\xe6\x92\f\x1a(U\b?.b\x81=Ђ\xe0\x98\xb6 z\xae\xa73ek\xe7\x01\n4\xc4\xce\tv^\xccQVJ\u007fR\xbdT\xdd\xd6\xde\xcb+N\xd11\xa9E\xa9y\xf6\v\xcd\xd2D\xb9\x9a\x18\xec;Y\x9dC\xe8+\xe8\xf5d\u007fN\xbdA݇n$9\xc6\xf7M\xf6\xa3\x95Ay\xf5\x86hpJ\xa2\xa3\xa1\xb6=^\xdd蹭\xf3\x86%[ҫ{\xa9\x8d\xd1\\\u007fr8L^R\u007fڠ\xfe\xa5\x91\xf1\x9d\xb2g8\x91ޥ\x19\x01~\xa7\x00a\x9ed8U=\xa5gP\x90\xb3'\x9b\x011\xc0.\x1b#l\xd8\a\n\xe8\x10\xd3=ΑѬzR6\x92\x83np\xe4~[\xc7E\x81\xabf\x17n\x10\x9c\xeaG\xad+\x1ey\xe9\x91\x15\xad|\x1e:\x92\x85\x05\xa6f\x93\xfd\xf1E˻\xa2\u007f~E\u05f6\xa0M\xf7ʟ\xa3]\xcb\x17\xbd\x10\xb8\xa8\xc3f\x9b}\x15jE\x13\xb03\xb1qMOϚ\x8d\x89\xc2{\xea\xc9d?]u\xfd\xc9\xc6U\a?\xf7\x97\xbb\x0f#\xc1/;\xe9\xf2s\xca~\xf5\xf4\xe1\xbb\xff\U000b90ebؚǀK\xaa\xc2-\x8c6\x03\xcaB\xb4\xb2'\x15闘̵\xc4L\xac\xd3g\x0fc\xa1\xd2g&\xad=\xeb\x98\xd5G\xfa\xf4\xb8\xb5'\xcd\r\xf9\x85\x81}\xb5\x06S\xec唩\xae\xa6\xe1\xa5VC\x83I\xaas\xdey\xa7\xbf\xb1\xc1\xd0\xfaRCM\x9d)\xf5r\xccd\xa8\xdd7&UC͝w\xd64\x8cN\x83sc\xb2a7\xcdfl\x18\xc9\xd6\xe8\x1f]t\x83\xc1Tw\xf7ݵFè4\xe5o\x92\xd1u\x9e\xe2֍\xe5\xa72\xa9B\xaa>#\x15o7(\x83\x10\xf6\xbdJ~j\x89E(\x16\x19\xaaEM\xde\x12\xae\xd1-P<\x93\xd7n}\x82en\xaapt^\xbc\x90\xaf\r\xd7^<\xd3\xdb\xeb5\xc7fͬ\x9d>3\x10\x98\xf5\xca\xf7\x16\x1e/rQQ\x1f@\xe2\xc3W\x1fヌ\x93\xfa\x99\xe3\x9f\xed(\xb2Q\x03\x06\x8f\xd7Um\xf1\xe2)!s\xac\xbe\xa5G\xb9\xe5\x197\xba\xa1\x92\x99ꜜZ\xdc4\xb5\xeb\xee\t\xae\xec\u0085U\x93\v\xb9l\xb6\x92\x89ڟ\xba\xfap\xf7d\x8d\x83:\xbdCc\x04\xeae\xbb\xdf\xea's2\xaeE\xdd\xd9\xd0\x1d;\xa7u\x1e\xe6*\xc6'\x05\xbb\xe0\x16\x8e\x8b$\xec\x9a]\"\xf6\x9f\x9a\fc4\x81\x8b}\xac\rv\x0f\x8azyDz\xdfɨ\xf7n4\x86\x8cbTF\x0f\xd2.b4R#\x11\xd4P\x17*\x8e\xe7\xa9\x15\x19~6\x10\b\x84\xa6t\xc6jt\xfc\xac\x98ŋd\x87ۥ\x9by1\fW!\x1f\x9fןD}\x1ag\x95\xbflْW_A\xeb4R\xab/\xa5\x0eu|\xf6\x95]\xf7\xbe\x80P\x17\t\xf2Ǯ~\xf8\xf0:t\x83\xfb\x99[\x94\x9e\x96\xfa\x9894\x05{-\xd5.\xafǀ\x02\xa9\xbe\x1c\xcey\xe3\x8dA\xbd\xb00\xeb\x06\xc2 \xa8x\xba\xa66-NMv\xceM$\xfb\x19c50g\xb6\xcd\x1b\xb8hQ6\xab\x14\a\xb6\x00\xe3\xd41\xfd\xb1\xfdB\xff\x95n\xd7\xe4\xee\xc3W_u\xb8s\xda\xce;B\xd9\xeeE\xae\xcc\x1c\x02\xe3g\x97\xf5}\\\xa5\xfd\xff\"\\\xf1\xa5a\xa0\xa6\x1a\x9aQ=\xbb#\x96\xec\xdaͧ\x9d\x99\xefվv\xc7\xfe\xb3\xa3\x8d1\xeaŊS\xafY(R.i\xed[\x81\x8c9\x02\xf8\v\xc5\xfaJd\xe7\xd1QӜ<\v\x120@B\x8eNya\xfc)\xbfj\x040Vh\x9f2쬄\xc7s\x01\xed\xd2O\x00\x9deP\x1e5>I\xae\x93~\xaf\x91\xea\xa91!\x00\xc6\xfb\xa1\xff-\xbc\x06A8ag\xc4j\xcaNq^7\xac6\xea\xc3e\xab\x13\xb0/\x89쾇ݳRuԢ\xc1Z&\xfdU\xa1EJ\xa9\xb8l\xb6p\x9eYo\x9e<2\xe9\xb9\"\x15_\x9c\xec\xf4\x14:\xd9\xe997\xde9f\xb2\xd9\xda阎\xb3\x00\x88\xb3.\x04\xb5!\rhI\x17\x1b\xda\xc24\rRk\xa5\xf3Cj\x11\xaeG\xd3Bu\f+b\xfa\x98\x99tQ\xf5P\x12\x9du\b\xd1/\fА1\xfa\x99T\x18Z5\x1c\x97\xe5\x95\xf4\xdb\x01\x87V:\x1c+\xd1zp\x82\xe38\xfa\x88j\x84\x8e\xa7\xb1y\\ST\xa4\xe9!\xa9z\br\x81\xe3\xf8\xf9u8Y۸$\xfd\xf8\x13Յ\x84F\x95uFY\x13\xdd\xf1\f\xcaTj\x1c\n\x02+[k\x8cj`\x18\xe2GŦ\xa2\xf5+\x91yl\xd3֦Y닍\xf1\x8d4\x14R\xab\xe7\xd4\xd3,\xb6+\xaa\xe9h\"\xad)=\xa8\xacU\xcb>\xce\x10\x1e\xad\xafyV\xcf˕!\xcb\xd8V]\xc9Z\xeb8G_\n\x17jW\v\x1b\xae\xb2p\xa8\xa6H\xea\xf4 ֬Q6P\xa8\x1e\xe98=w\xb0Q9\xbb]W\x8c\xcc\xf280\xc0\x9d9\x9f\x1e\x9c\x11\xfd\x12{\xb9\xd1z\x0f\x16$\xc55\xeb\xcep\xbe+\x94\x8d҃D%ꔒ\x8a\x18-\x91\xe4\xf5\x16\x14R`5CbJi\xa5\x95h\x82EI@\xa7\xb9\xdc\xf4x\xa0Q@\x87\xb9-\xd8J\xb3\xc3\u007f\x89\xfeh\xean\x94\xa2א!7\x93\xfb\x87#ם\x8e\xbaY\nѣX\x10\xad\xa1\x9e\x16\x99\xa6\x052\xec\xcfM\x8d\xe9\x89n\x89\xd2Ɣ\x18\xa4\xd2\xcdi&\x82#i\x85x2n\x8fB\x85\x02\x80~\x8c\xd2#\x9d\xf2}2n)Ͱ\x13\xda.w\x06\xb6\x14\xc9\x03o\xb1\xc8\x14B\xe0\xa1\xc6\x1e\x19\x1f(\x93\xd6\fY\xc7k\xa1\"\x16\x1b\x8a\xbb5n\x11\xb3\xf7G\x19\rPTF\x8b\xa3\x84\xb8;\x93N\x89Q@\xf7(\xe3\x9a奣$\xba\xea\xe9%l7Q\x18?\x8e\xcalR\x1a\xbe\x1b\xb1P\xe4fB\x12!w\x9aҤJƝa\x95îG\xdbٍ\x00\xf1J\xa6 \x83vK\x1b\x8dg\xea\x01WOӬ\x94\xbbL_\xe9$\x9b\x90t\x88\xb1\x9ba\x8c\xe8[!i&\xfe\x1eM\x17\xed>J\x16\xe2\xa1LBf\xddR\x81\x04\x16\x9e\xba\xa0%\xb5\fۣ6!\xe1o\xac\x06\v\x99\x86\xad\"\x16\x04$\xda,J\xbd\x1d{\b\xf1\x12l2\"Qo\xc1\x06\x83\x88\xb0\x15#B\x04Q'!\"\x12\x11\x13#\xb1\xda\f\xa2\x9eH\x02\xb2:\x89.\to\t\x99\xfd<\xf1\x019*a$\n<1ʔ/-\n᪠(J&\x82\x89\x1e\x99$\x12\xb2\nf^o\x90\x05\vћ\xf4}\xd5\x1d\x8b6\xdf,\xf6\xed\xe8\x98\xda+\xf0\xb9\x037\x0e\x1f\xba\xf1\x80\xe4\n\xa4g\xac\xed2\xf4.\xb8\xe3\xae;\x16\xf4\x1a\xba\xd6\xceH\a\\Ұf\x97\x8f,-Jǒ\xe0\xe6Ew\\\xf5\xf4B\xa1wjǎ>\xf1fM\xf8\x11\x03\x14.\x9c\x87.kl\xf2Dj\xee.Xv\xdc}\xf7\x8e\xd4\xdamW\\:5֔j\x82\xbf\xd8\xd4K\xafضV\x883\xd9B\xb5\xae\xf8\xa9\xec\xc2S\xf3\x16\xde$l\xbb\xbb&\xe2ijD\xebYdIO\xed~q\xb3\xf0!\x17\xe4\xa6rW\x17\xad\xa5\x00)\\\xcb3\xb2\rH\xb1\x11\xc3.iT2\xfcR\n˔D\x83\x88'\xcdi\x9a\xf8\xda>\x13-\xda\x00(*\x8fQ\x1e\x8b\x87\xb9\x84\x17\xfco\xf8c\x8d\xb5$`\x94\xa5\xb6\x98\xb5\xcag\xaa#A\xff\x89ꆘ\xff\xa0\xbf0\xc5\u007f\xc2\x1f\x8b\xd6\x1c\xf4\xfbߨn\x18\x9b\x8a\xec\xba\xe8\xe0\xe2\x1d7.>\xb1x\xf9\xf2\xa5;w,yc\xc9\x18?\xcaƠ\xf4\x00\xa93\xf9\xaa\xac\xb16I6\x82\xbb1\xe6\xffq\xb5\xef\x80\x1f\xff\t\x1c\xfe\xea\x03\xfe($\xaa\xae\x1b\x9d\xa8\xf0\xf6\x87\x8b\x0f,\xbe\xe8Njwܴt\xf9r(y\xb4\xb7h\xe32\xc7l{s\x1a\\p\xd4@\v5\xa9H?\x88\xa5]\xc3J\xb5Hʽ\xfd\xd4\xe9\xb0g\xd1\x1aIhh\xbah\xef\x03{\x16\xaf\x91 \xa5\xee\x1a\xfce\x8b\xddf\xce\xd5\n\xc4\u007fzUs|\xe1\xaa+\xe6D\xb5W\xf3\xc2xst\xce\x15\xab\xb4\x17\xb2\f\x04-\xf3}\xc4\"\x00\x9e\xf4\x8b\x01<\x04;\xe6\xa0\x1ep>\v\xef#\x03\xb9\xc2?\xbe\x84\x8dX;$}\xeau\xcepȖ\x05\x94ow/\x8f&\xb5ν'\xb5dޒ\x9b\xfa\xefM-\xa93\xebg\xcf֛떤\xee\xed\xef\xd8\x18\x9d\xbf$y\xef\xdc\xd6I\x88\xefE\xbbuR\xd6\x16\n;\xf75\xeeIt\x84\xe9\xa3Б\xd8\xd3\x18f\x0f<\xd8n\f;u->b\x03\xb4\b\xfd{\x00g\xb3\xea\xc2-\x03:\xcc\xf36ާ\xe6\xb3\xe8\xf0>\xc2k\xf70ڹQ\xc7\xd5s\x11.A\xbf,1\xea\x1e\xa6xB\x96\xb4U\\\xf6tBBA=\n\xca\xf4\x10)~\xa23\x99.{\xc4\xc1ҍPa\x88~\x11\x02\xd1OBP\xbb\x02\xd3:s\xea\xcfQS\x81=\xbf\x8b:Uf\x99\x00s1\xf2K\xcdɗM\n\xa0@\xf1\xdb\x13\x90\x19\xcaP\xbf\x1e\xfb\xb9\xfas\xfcy\xf5\xe7\xeagQ'\xd5)\xa2_\xad@\\l`\xf8\x1f|N\xf31\x9e6\u007ff\x8fp\xb3p3\xb3\x02\xed,Y\xd5\xd0,w\x14\x05\xf4\x8bZ\x1b\x881\x9b\x92\x15~ט\xf4\xc2\xcdOn\xbb\xf3\x8a\xe1\xbfoy\xeb\xa9'\xafǗ\x18\xbalfC\xe1\xe9\xf9W\xae?\xd0Ot=\x8b\xb2Kz\n\xdf\xf4\xd5\xd7(U\xe8QC\xb7\xcddP\xaf\xec\xb9n\xd1\xf2.<\xfd\x8a\x87\xb7=y\x05\xd1]\xff\xf8S\xff\xb6\xa5\xf0\xb4\xc1d\xeb2\xe0K\xe7\x1eZ\u007fu\xff\xf0\xdf{\x96d\x17\xf5\xe0\xe9^\xa5&P\xad^\tq\xdd\x06\xf4h\xd7\xf2E\xd7AakF\xc9\xf6Q\x1d\xed\xe9\xda7>\x98<\x1f\xfb~̈^\xbf=Qby\x8d\xd5A\x1d\xabs\xe7\xa1X\x1a\xfd \x10Gr9\xa7A\xfd\xa3\xa1ժ\xdd\xc8\xe5`\xb8\t\f\xb7\x9a\xab\xd0\fΕ\xbfMʆ\xdf돱\x8f\x13\xe5,\x93\f\xa8\xca\xe0,)\xf6\x9f\xe64K\x11\x98\xab\xb8ݑ\x87Y\xac\xc0\xbeZ\xaa\xfe\xc9?0J\xf7d\\;|\x95\xd7\xeb\xfc\xc0h\xb5\x15\xed~\x8e\xddki\xf6\x8e?\xb5e\xef\xd1v\xfe\xce宰\xde\xfd\x87\xf1\x9c\xec\x8c\xcfK\xd4\x0e\x83v\xbf\x16\xe52\x14\xa3)i\xb09J\xb7\x86cj\xe7\xce\x11~\xb6Uivo\xa8\xfd\t\x1fV\u07b4\x9dʍ\x13X\xe9~\x83eC\xf7k\x06\x86\aˆ\x94\xc9\xefƆ\xa0\x11K\xd3ڰZ\x99n\xe9߹ZX\xf1\xfd\xb0ko\xe4n\x86퀭\x82\x8c\xb6:\xa4h7Τ\xea\xc5\x10\xfb\x90\x15\x9cG\xb2+Ș\xae\xda}I\xb4\x9b]\x0eSfn\"u\xb6!\xf2`*\x91\xa4ئ(E3\t\xfb\x05\a\xe1\x96M\v\xd7\xf7N\x9e4\xb9\xa6\xe9j\x9fnRX\xb6M\xb1\xadGs/Mtb\xf5\x90\xd8\xd2\xdb\xdbRS\xd5\x1c\xba\xc8{i\xfb\xec+\xa6-\x9a\x8ev\t\u007f\xd6\xc6\xc1a\xd1\x06J\xfd\xd2\x06\x84u\x8d3\xefZ/\xbcW\x19S9ZK\x16\xac\xea]>\xb1Ɵյ\x19\xa668\x10N\x1d^~\xbdi\x0e\xce>\x15v$\x96$\x9b&x\xaa\xaa\xdb;\x12\x93\x17ό/n\xceTu\xaa\xdf\xd2\xc6\xcc\xe2\x90\xc9\r\x97_\xdep\xa4\xc1d\x8f\xf4\xefR7\xaa\xb7\x94#ƌ\xeb\xc8]\x8a\x95Kqk\xd9^:J\xb81\xa2)Ǥ5\xa3\xb0\xda\a$\xa82\r;\xd8ʗ\x03$X\xb4\x96[\xba\x8d\xd3\x14Z(ޜ\xcah\xd2J\x9e\xa2\xad7*\xfd%2E叙\x14#\xfa\x8e\xdf\xd3z\xc7g\x10\x1f\xdf\xd6{\xad\xc1h\x11LK,\xf1\xd4\xf2\x9d\xd7M\x9b\xda\xdb\xfb\xf3\xe9\xeb\xda#\xef\xa1Ǥ\x06Okdւ\xd9\vn\xban\xe1\xfe\xc9V\x1d\xa5\x1b\xaf\xb4\xd6Z\x85\xd0Ħ\xee\x8e\xd9پ\xb9\x13[\x16\xd6\xe3\xdcȷ\xf7\xb2\xa1\x89kV\xbc\x98\xdb%\x9b\xc2ʂ\x9b:\x1d\xd5@S>Զ\xb2\xa3}\xf9\xec\xa9S\xbb\x9d\xcd~\xef\x19.\x9a\xbavm[k\xa8\xb9\xd5\xe1\xf2\xc4l&\x9dż\xb1\xb5V\x89L\xc0\xf5s\x14\xdd\xe4H\xd8\xe5\xae\xf6uvM[2\xbb\xa6\x82/z9ն\x97\x95\x16\xcd\x10.\xebS<#y\\\xa26 n\x97G\xae\xe8\xad\xd6\xe3fmȬ\b@\xcb\xe3\xcexʃEӻe\xf7\xc8\xc8iwX\xb0\xe1D\x95\xb1v\r[#:b\xae\xeeL\xee\xa9_\xbahkm[-\u009d\xd9Nٌ\x90E\x9c\x18\xeaZ~\xf1\xbaemM\xad\xf6\xb0\xdd%Y\x81\xe6\x96뛮\xb0\xe0%\xaf\xf7\xef\x00Z\u007fbt\xb6h%:\x8b\xe8\xb2\xfa\x949}\x1b6\x1dxn\xdb\xf6\xce.\xb7\xcd^%,uXF>\xa3.\x041^\x8ex\x89\x00\x8do\xc9\xea\xf5U\x96\x1b\xccQ\xf1\x1d\xf5O7\xcf\xeb\b\xb6\xf8\x1d\xc1\xb0\xbf\xad}\xf6\xe3\xf3\xd7\x1c\\\xda1\xd5\x15B\x98,5\x103V̒ׄ\x8c\xa2\xd5'Ō\xb2z\xe7w6\xf57Oi\x9f\x1c\b6\xb7\xf4\xf5o_\xf0\x04\x9a\xfbrU\xf8\xd4\xed\xa5\xb9qp\x9c\xa1,\xc31\xf6\x9b\x02\xf7qOi\x16#*\xfbn\x1f\xe3\x1f;6\xff\xd3\xfe\xb1\xf5\x8d\xfdF(\xfdNy\xc5'\xea+ܣcT\xee\xdcq\x9f\x88\xb5\xb3\xbb\xb3\xbb3\xb3\xb33\xcf3\xcf\xf3\xfc~x\xa4h\x03\xe04[\xe6\x87 \xb8A\xfe=,\x13O\x9e\x14c⋢\xc8rx{\xf2\x95\x95+=\x1e\xf4\a.z\xfe\xf9\xe6f\xf4G\xffA=\x92\xbeSM\xd0ϒk߉\xe1kѥ1|\xad\xf8\xe2u\xe4\xa4g\xa5\xbd\\=\x02=j\x82\xac=$\xb2\xf2\xbf\x99rR3\xf2,\xec\x18\v\x1a\xabxٰ\x1c\x8bU`B\x01\xc5!\x82\xfc\"\x01LQ\xfc\x90 \xd3Jc@(\x02\x8f\x11\xc9\x05{˯\x82\xe4\x9dF\xed/\xb5\xac\xe2\xda\x0f\xba4\x92\xe03\x84i\xac\x94bM6A\x87\r>A\xd2 \xc5\x1d0Z\xc9\xf8\xa2\xd4\xe6\xee\x0f\xb9\x18\xa4\xc9(\xb1\x00\x10\t\x8b\xe8zc\xfa\xc7d\x97I\rQ&\x87\xa0\xa5\x01\xc0\xbe\x12\xf8\x0f\x00Z+8L\x18\xd3T\x13\xb3W\xbb\xfd\xe8&\xae\x94\x02 \x90\x93a\xa6Q\x19\x1c\x06<\x10a\xd5\x1e\x87\x18\x11\x90\"\x1a\xc7\xff*F\b\x95\x05S\x11)1\x87^T\x8d}uМ\x03\xe35`\xeb\x0e\x87-q'6nh\x92\xb4\x95־\x96\v\u007fڻ\xe3O\u05ec\u007f\xf2\xe2%\xe5\xdd3<\x1ah\x80\x9c%r\xe2\xc1\x9b\x1eܿ\xa1e\x9a\xa0\t:b\xb5\xad\v\nVY\x98\xd7\xe5\fz\xe8l\xb2N\xeb]6\xc5\xff\x93p\xc3\xfe/\x0foyiOc\xcf\xee\x1f\xb4\xf7\xde\xe95x\xf9\xf1\x9c\xc3\xdar\xd6M\xef\xdd{\xe9\x8f>_\xd8\x12ؾ\xb8\xb8v\xe2\x96\xf9\x9d5\xf2\xf2\xc9\x1b\x96\x80\x8b>9\xa1X\x81ru\xebʓ\xfb3\xb5\x13\x15r0\xb5rd\xf0\xfd\xce\xcae\xf0\xa6\x94\xf8\xe1t\xf9|\x87\xb6¶\xbe\xf9\x89\xbfL\xde\xf5d_\xef\x13\xbb\xcf*\x9f5\xc3hct,g\xa9}\xe3\xfe\x1b\xaf\x19W\xce\x1e\xadi\x99\xef\\\xe9\xb4<\x95\x1fc\xbcs\x91\xff\xe1p=\b\xffi\xde\x1d\x17v6\xf4\xec\xbal\xe2\xda۽\xacN\xa8\xb08\xa4\xd6E\x87߹\xfb\x92\a\xfe\xbe\xb0ٿ}aq̈́\xcds\xa7\xd6\xc8+Wߚ\rD\xceٶ\xdcD^\xc3؉>[DP\xe1\x04j\x1dq\\j3\x16t\x82\x91h \x8ad\x1c[\xc4\x16\x19)\xa1\xd27r\xf2\xe1\xf7h\xf7\xf8\xf9\xb1UW]\xb5jiK\xef97\xf6\x0f\f\xf4\xdf\xf7\nX|\xee\xb9\xe7\xa1\xff\x80\x98/\xc3\xc2\x1d\xae\xd0>g],p\xcdK\xd74\xadY\x8dW_\xdeځ\xb3\x9d\a/\x1b&\xdd\xe2\xf9\xef\x1e-\xc5.S\xb1\x80\xad0\x8f\x14\xdb\xeb\xe0\x89+\xb7\x95\xf40:\xee\x8b\xfa\xecA\x1b\x16\xc3\x02\xd1H4bc\xef\xf8\xb1\xfc\xd37o\x94\xbf|~۶\xe7\x81\xf9F\xe0y\xedW\xdb\x1f\xdeub\xe7\xce\x13\xbb\xe6^yV{1\x87\xf4\xaa\xc7\r\xf4\xaa\x13o\x9d8\xf1\x16\xdc\xf8\xa6\xfc\xecS8#(\x03\xe6緥~\xb6\xf9\xa2w\x86\u07b9\xa8jҢ\x99\x81\xa1\xb66\x9c\xe7ĉ\xec\x1a\"\xc6h0P\x85T\x05\xd1\x04\tu)\xef\x88\xe1 \xa3\x12$\xea\xf9\xab`]+\xacE:\x85E\xf9\x82q؎\xcdW7jD\xc7\xd1\xf3\xc9-7\xcc(3\xe2uŲ\x19{\x0e\xef\x99Q\xa6l`Y\xdf\xe1\xc1$\xfe\xee\x98\xe4\xe1OC\xaeoɊ\x03\x8f\x01\x85\x93= \xb5\xbf;h\x95\a>\xbe\xea\xe0E3g^tP\xd9\xc8e\x90\xc2\x17\xc8\xe4\x97N\xe4\xf8\x82B*\xd6\x00\x83\xf4\x1bʘ\x89\x92!x\x06\xa8\x18\f%\xc9\t\x8c֙\x90\bY\x12}\x16IK %\xd5ep\x1e\x90\xdaH\xd1\t\xe5ZR\x05\x15\x01\x01\x03\x90\f\x11́\x14\xc6\x1cH\x01\xe2+!)\x0e\xf9ʵ\t*\t1B\x801ˬ\xabB\x1b`\xcd>\xf7 &\x91\u007f\x1f)ç\x9c\x84\t&\xaf\xcc\n\x90\xa0\x03\xc0\x04)\xb3\xf2,\x1c~\x93)|H}\xee\xf0ؚ\"\x8a\x8a\xf8\x88od\x10\xf3A\x8e\x9e[\xfbaO:)\xb1禓\xb0G\xa1\xce\xce\xcewLr\xb0\xdf(y\x99\x9e\xc1\xa4ļ\x96\xcfC\x82\xfbg\x8aQ\xb0\xe7\xdc#[U\x1c\xd1N\xc3\xdb84\xa2\xd9~\x97\xd7\x12c\xb4!y\x0ez\xdc\x19\xdfݰ\x1b\xa9\xd7ҔZ\xc63\xbc;z\xe4ss\xf1\xc4.\x8cF\x92\xfb\xee\xed\x0e\xc6\xe1\x81M\x90ؾ\x831\f\x1dF\xf1\x02S\x0eI`A\t\xb1\xe2\xaa4Q\xcaByE\x03軼a\xca\xf9\x11\x00\"\xe7Oi\xf8\x11\x98\xdaP\xbe\xb2S\xbeb\xa9nByḰ\xa6\xe7XK\xf9\x04\xdd\x12\xf9G\xfe\xd6\xf3\xe6\xce`S\x13VЍC\x1f\x13/|WM\xe8߫ʪkj\xaa\xcbv\xfd!\f\x16\xcc:\x18\x91\a\x13|uQ\x89(\x96\x14U\xf3\x89Ϝe\u05f7\xcd\xec]N\xde\xf9#h<;\x87\xc4\xfd\x95\xab\xf8\x16v\xc5U\x17{\x13\x92\x15}\x85\xa2\xdef\xf1\x89\xe6j\xe0\xb3\x05H\x88%X&?\tV\x80u\xf3\xe0\x9c\xd5\xeb~\xb8\x9a\xb9V~j\xf6\x82\xb6\xf96\xbd\xfc\x14\x12\xfbA'\xb4\x96MY\xd7v\xf4M\xfa\xda!\x1f\xfdGP۹re紳\xcf\x1e\xfa \xfd\x12\x14\xd7\xef\x98\x14\xf1D\xd2\xef\x82k\xc1\x97\xe3\xc7\x1f\xf4\x8e\xaf/\xfes\xe6\xbd)\xe3k\x1d\x99\x13q8vI8\x84\xc3\xff#x\xd5\r\xfb\xf4\x90\xb9\x83\xe3G,\xf3c\x80?\x06\xa9\xe6;_\x97?\xba\xfd!\xf9\xe5sy\xa0ٯ3\x99\xf9ηw\xf4>w`\xf6\xec\x03\xcf\xf5\xae||\xf2\xfe\xbc\x95\xf9\xbd\x1b\x80t\xfd\xed\xa0\xf0u\xbaP~I\xfe\xe8\xf5\x9d\xd7\xed\xd3\x15h\x0eh\xa1nE/\xca\xfe&\xbaj\xca\xc4\x03y+\xf7\x97\xacٸ\xf3uT\xc6\xd2S6\xeeo\xeco\xb1O\x9bo\x18h-\x0eN\xf5p8ޗU\x8f\xb52$\x1c\x9au\xa8]\xa8\x8a\xc3\xc8\x01\xac\xbav$0$\xb4\x84\rc\xec\xdb\f\x16\xec߂\x1b\b\xef\ue361\xf2S\xd4\x1e\xa1T\x806\xc6\xcch\xe8Bڭw\x89.ci\xa1\xdc[\xa8\xd5\xda\xf5\x1e\xda\x13ҙ-:\vg\x85\x82\x00\x96\x8e\x95\x15\xdc\x02\xf6\xb8*\x05\xb7K\xde\xcb\xceh\x9eq\xa0\xb4{F\xf3\x16A\xc9\xf1\n\xd9lW\xf2\xa5\xe4\xc1?\x14\x15}\x00\xb8'\xf1M\xae\xf9R~<3.(\x98[v<\xffQHPC\xb2\x0f\x86\xa4\xe7c\n\x1a}I\xcc\x1cbr`\\\x18~`8\x1a\x17\x01\x89\xee\x92{\xe5;N\\\xbbw\xa1\xdbYu\xf3\xae\xf2\x86I-\xaf\x82U'N\x80\xd9y\x18]\xac\xc99\n\xa4\xebKp;\xf8+\xb8\x9dI^\xf9\xf7\xfd\x9b^\x99V۳dv\xdb9!Ns\xe5߁\xf8\xf7_倻l\x961p\xbb~\f\xc2G\x8f\xe6\xd6 p\xecF#\xb5:\xbf\x16\xd9:ԅ\xf0[\xf8\x0e\x14\x05\xf0\xdd\xf8\tH\xfc\xa3\x17˯\xcb\xff\xbe\xa3\xaf\xe7쀿\xb0\":s\xfa-@w\xc7\x1d\xe9;1n\xc2\xf13\xa0+\xb0\x8d\xdf\vU\xe1\x1a&\xd9\xfb\xe8\xda97\xd7\xd7ϳJ\xc5:\xa1\xf7\xd1W\x1f\xfd\xeb\xfe\xbf\x9f\x01ja\xf0\x9b3\xa3,\xec\xba\xe0\x04\x1a\x1f\xc0)\x8a\xbe\b\x8da>\xc5\x0e\xab\x18 \xe2\x12\xab\x18'Tgx4J\xd0A\x1c\xf0\xb2]ԧ?2\x161:\x8b\x85yA\xeec4\xa2Qd\u007f\xcd8\xcd`\xaa\xe4b\x8f\x82\xab4\x8cD\xbflu\x0e\xee*\x80l\xa1\x99.]\x03\xf4&'\xdd \x88\x05\x16\x8dN\xaeY\t\xf3\xb9?\xe6\x0f_\x0fEJ\x0f\x92OG\x92#\x8fy\x8c\x18n\x03\xb9\t^\xc1\xf3\xb1\xf9T\x14A\x9e\xaa/UB\n{d\x8a\xacȎ\xb9\xd7U\a\x95}\x12xX\xda\x031r_i}~8b*\x95\xc9=\xc6^]W*s->\x9a\xea\xaaK\xd5\xe7d\x93\x14\xd2fgQ\x8bU\xb9(\xe3\xf0\x8e\x91s,\xb1Ze\x95\x11\xabM\\\xc6\x14\x84\xbd\xc9\xc0\x88]2\x99)\xb8\x041\n\xcf\x1a\xd8$\x04l!?OnG'o~\xf3\xe6P]h\xe6꙾V\xda'\x19\xf5\x86\x9aE\x8d\x1d\x17\x94\xf36Fo\x11\xf5\x8c\x8d/\xdfq\xc5\x0e\xb2+Z\xc8\xee\x05\x1d\x8d\x8bj\fz\xa3\x04*\xa9S`\xfeO\xaf\x02Ɓ\xfb| M\x95U\x94a\xdf\xdf\xe7\xd3\xc7{o\xbe\xb9\x17\x8b0\xb53g\xd6\xc2\x0e}\xc8(骪\xa65\xebJ8\x8b\x85+\xd15O\xcbOWU\xe9$#\v\x9f\x02\x96+\xba\xaf\xff\xf3\x01\b\xdfZ\t\xe1J,\x942Y\xbb\x8a\x06i\xc4n\xac\x81\xb0>Ŗ\xe2\x1b\xb5X\xe2\xcb\xc6p\xb7\f'E!\xab\xf74\x92l\U0007aedc\xc4\xec\x8ci\xb2\x9a\tS\xa8\x0e\x14(\x97߁T\xce\xd8R_ʠ\x94̈́\xe7\x82$^\xca\a\xfd\xc0\x9bŊM\x9f\x8b\xf2\xcfO\x93wޯ,\xd3cӊф惞\xac\\I\xb8`\xccT)\xb5\x94\xd8&IX\xb9\xaa3\xa1\xe6W\xa3\x1a\xac\nSv$F\xf4ݸ\x15{\xb8e\xa11\xb0fH\x02\xb6ț\xcb\xfcaw\xf2(Q\x1d \x1c\x90\\\xd29u\xb8\\\xb5\xab\xa6\xf7O\xdax\xf9\x81\xcb7N\xeaЍ\xd3%\x8d\x1f\x19\x93hۑ\\W\xd9\xd4\xccT\x17\x14T\x1a۪\xac\xdd˻\xadUm\xc6ʂ\x82j\xa6\xb9\xa9r\xdd\xe2\xeb\x9e\xfa\xe9S\xd7-\xa6\xc9\xcakU-\xba\x9b\xb7\xabn\xeaE\xb3*+g]4u\xcd,}\x85\xfe\x96뮻\x05mf\xad\xb9msM\xd7\xd6\xda\xc2X\xd0\xed\x0e\xd6\x159\x9cU\xb5\x15uu\x15\xb5UNGQ\x1d>\x16+\xac\xdd\xdaU\xb3\xf9\xb6UG7O\x98\xb0\xf9(\x19\xff\x15\xecY\x17\x89A!\xcb\xd49ې\xc2#I\xdc%\xccy\xb8\x94\xa1\\\xa0\xba\x02gf<\xd9/\x19\r\x06\xf9\xe7Z-H\x10\xaa\xc8\x1eL\x86HP&O\xf6\x13\x94\xdf\x1e\x05E\x12\xf4\xa0Z\xa0\u007f:\x94\x0f3.&0B\xa4\x04}\x19\xb0H\xb2\xb4\x9c\x85\x84\xcc`\x04\x12n\xa2(\x89\x01.\xcfY\x802\xb6,L\x10\xc8~\x87]\x99\xa5Da\x80\xdcx\x00\x93Q\xf6`2\xca\x15:\x98\xb16_u>\xb66\xdf\x0e\xe8\xa6)+\xfa\x0e\x8f\xdb{?\xec\x11D\xd0C\xec<\xfd\x84\x01\xb3\x1fUk\x85\xe1mb\x83\xde\xfb~\xdcc|\x1bT\xfc\xf8`\xebᾮ\xd6\xe2\x13\xa3\xcb\x18&\x8e\xcb\n>E\xd6\x0f7\xa2\"B\x9c\xb6\x8c\xf81\xa8\x15\xee\xd2\xe5\x15\xf6;\xca\xd8/\xe0\x9a\xa0\xfc\x06\x83 ʤ\x8dA\x8f$\u007fv\x9aBf\xfa\xbb\x1a\xff\xb5\x88\xea\xc9Ytج\xaf\x06\x1dG_)\x01)P@\tp\x14\xa67\x8e:\x00\xfez3hfa2\x1c\r\xdb\x0f\x85\xeb\xb0\x0f\xa6\x87\xc9:v(\xa6^&\xa4\xb8m胍\xbd\x1d\xf6\xc6ɛ\xfa7Mi(\xd8\a&\xef+\xe8;\xec\xad\xef\xae\xf7v\xf5v\x91\xed\xa4&\x00\x18\x9d\xa6\xa3\xb71\xa8\x97S\xaa\x1b\xc7\xef\x88\t{\xf7\x85\a\x0e\\ر\xe7\xf0\xd6%\xa6\xba\x8eW\xac\xab[\xba7m\xeanYm}\xa5\xb5\xb8\xb7\xb7\xb85q\xb8oqQ\x19\xfe\xb8ˊ\x16c\xbc\x8c\xdc^\xc7\x0e\xbfnBq]\x99dZ\xb2\xf5\xf0\x1e\xfa\xb7\xaaCG6\xb6\\i\x8b\x199I/\x8e\xd4\x1f\x8b\x95\xf1\x96`\xc2\x12\x95b\x94\x98}\xc8\x17\x81ޥ7\xa6\xf8\xe2\x935!\xe5\xed\xe1pa\x9br\x86H\x12\xb5ٰ\x85)\r\n|\xf4\xbd\xef\x87\\\x9c\xce\xd2\x1c\xc0n\xef\xbe\xe2\xe3@s\xbc؇Ӂf\x8b\x8es\x85\u07bf\x17\x1fj\x98\x82Z\x87V\x9c\x0e\x12\xad+m\xf2\xf6#\x1f~xd\x9f\xf5\xb7\a\t\xa4\x86\xa7\x04Iq\xa2|\x1eY\xbd;$\xa2\x9d\x12\x0f\xc4\xfc`\a\u007fk\xddG\x0e^i[ي\x9aF\xe5\xfaT\xec\xaaX\x9b\r*\xbeQl\x0e\x1e\x1d\xe9N\x91\xac+\x94\x8a\xa1\x1e\xc9xD\xc9\xfd\x04Ց\xe9\x1f\xa2\x92\x8a\v\x14\xa4\xf6-M\xa0\x83L\x12\x83\xc6\xed[J\xa3\xf4 \x92\xb7\x14ϧ\x81\xa1\xd4\xd2},\xb5\x0f\xb5i.F,2\"B\xec\xfbG\x85щ\xef\x19\b\xf6\xbd\x02\xbf\x14\xd90\xa1\xca\xf6~\xf2\xa6Ie\x81O\xe9\xf4\xa8Ö\x8f\xe0[咛\xba\x13\x89\xeeo\xbf\xe4\xa9\xc3}\x83T\xdfa>\xf1\xe1\x91ľ\xa5\x18\xed\x12/\xc2\x1c\xa1\xc7\xf7o\x92\x93\xe9\x14z>\xa3E}ʋ\xdb\v\x0e`v\xae\x1c\x16z%5Q\x91\x06\xf8l\x84\xa9ҥH\x97\xb1+\xd8+\xc3\xd3l6g\x9e\xb4\x03\x93S\x1a\b\xf0\u007fÔ|\x18\x05\x1c\xc5B\x91\xe3h\x988\xbe\x0f\xbbڱ\xa9t\x12}\x16C_Ꮐ֣\x0f\x05*\xf0\xb0=\xc4\x19\xaf\u007fd\xfa[\u0099\x01\xd1\xe8M{\xf7\x1dW\xec\xbeJ\xfc\x8a\x84f\x03\x05\x03w.a4\xb0\x8d4\xb2\xf3\x16\x1f\xe6D\x05\xea\fo\xc9\x01\xe4*\xf9\xd8\x11V\xc0\x91VA\xf68\xf6sP-\xef\xc9Ҟ\xc2}\xe0\x02\x9dA\xfe\x95\x01\xac\"\xee\r\x14\x06\x1d\xce@\xce\b\"\x1cȤ\xf2\x8f\x8a\x02\xb3\xaf\xb0\xa7t0\x89\xef\xc2\x11+|\x87|E\x91\x014\x18N\x8a\f\x85Ł\x93\x14ݓ1\x1a\t\xfd9\xeb\xde)*\x97\xc6\x11\xdfY\xfc\xfaѶ\xa4\x87\xa9\x9fQoP\u007f\xa4\xbe@\x12\x94\t\x14\x83J\xd02\x9a\xb7::b\x9f\x1d\xb1?2\xffH\xde\xea\x91\xe7ϴ\xff\xff\xfa\xfa3\xe5\x1fY_\x8c\bn\xc9x[\x8e\xc2b¼\xd2Y1-\x87\xd7M\xe5ҧ\xf2\xd2\xf4i\x8e\x9f.\xfd\u007f#?<\xcd\xf1\xe1e\xc6\xf8\xa9\xb8n\x04\x18\x8b\xcag\u007f\x1f\xc8\xd6\xf4_\xa3+\x9ew,\xfd\xaf1\x0e\x8e\x95\xfa?\x95Q\x1e\xeb`\xee\xe7\xe4\xf5\x18tt@\x11\xe0\xf2܁\xf1\n\xe4w|3OQ\xbf\xa7\xbe\xfa\u007f\xff\x95\xfcozi\xd6/#\xaf\xbf\x16\x80\f\xdf@ :\xdcۨ\x05Dl\xa3\xf1\xed#\xbe\xac\x06\xf3\u007f\xa5w\u007f\xdf\xdew\nk\xc2h\x1c\xc4i\xa5\x17\x92Sy\xe5I\xaa\xf7\xcb\xf4M\x90@\xa3$\xe6\xc1I\xfc\x1f\xeb\xa3g\xe8QC\xd73I/\x1e\xb0\xbd\x83Iү\xe8\x94RО\x9e\xacc\x95\x92\xae\xcc}>\x80\\!\x0f\x84\x90Б\xc8\xf2\x98c\xdbk3F\x06ʷ\xbe\x12\b\u05cc8'\x91חe\x8f\bd($lٷY\x1bS\x00 \x86\x19hC\xc4:\x1bSl\xb3\xd9i\x98,\xbbɯ\x80䝂\xe6\x97F\xf6i\xef\xa8\xfb\xe0$\xac\xc2柌\xe5\x16\xfbt\xf6\xbb\x12\xe8n\xc4_=\x94\xc1\x96Pp\xeb\xc3T\r\xfa\x16;\x95(\xca3V\xfd{I\x85D{\x1a\xa3\x8aiEZL\x12\xe9\x87I\r\xa6\xfasҢ\x17\x1d\x04\xfdc\xd7\xe6\xf3\xef\x14\"3\xf8\x1c\x04\x17\x1e[*8#\xe0\x03\x8a\b^NG#\x96\x00\x1f\bc\xab`4\x1c\x8dcCf4\x1eq\xa0\xa3\xd1&\xa8\xf8\xfa\x82\x88\x83E\xda:\x9f\x04\xf2\x87r\xff@B\xfe\xfd$\xdc\xfc=\xfd\x89D\u007f\xaa\xc7\xebM\xa6RI\xaf\xb7'\x85\xf7\x8904\t\x04\x13\x03\xa0'yP\x03\x13^\xf4?R\xc3\x04\xad\x17\xf4\x0fxS^\x8d3\xe9Ԡ\xed\x00\xe8\xf7j\xb1\"\x98\xf0\x16\x8e\xd7\x11\xfd!\xa1\xfa\x9fp\xa8\x17\x12\xeb\x04\x16sm\xbeh\x9c\xb4g8\xee\x8b\xfb\x90\x98\x84\xf1\xb6\xa7G\x1941$\x93G>Lx\xc1\x80\x97Ny\x138\xde\xe2\x14\x15\x9d.'R\xa9ԇG@\"\x91L\xa6\xbcC\x03\xc38S1\xf3I\x8e.u\x84ߣ\x02\x0fB\xf0\x0fG\xe1\x00\x11?>\x99\xca\xf1\xd6\xc2\fsj\xbe\xed6\xa5خ0\x05FƆ\x85\a\x04\x19{\x01\xd0\xff1\xc27qD\xb9\xbe\x0f\x97\xebX\xe5\x92SJ\xd9Rʳ\x94R%F\x96L!sM(\xa5\x1b~\x01l\x1c^0\x88\xe4\xec\x19\xf4\xbf\x98\b\x92\xe2\xc6a\x8dv$\x17.\xaf\x05\xccX\a\xe1V]\xadΥ\x93\xabt:\xf0\x16J\xd4\xeat\xf2\x0e\xb0\x1f\x1c\x18\xf3\xf01\x92\"GЏ\x92e\x87\xbcC7\xf6aR.#*\xd7\u007ff\xcaE\xe5|[r\x9c\xba\xccX\a\xe1\\\xfcp\xe5\xbe\xfb\xd1\x13\xc8M\xc1[\xa8\\c\x1d\x863\x94\xb2\x92\xbd\xfd`\xbfZ\xe2*\xdd؇q\xb9fPW3\x11f\xee\xb0\xf6\x1a\xce\x0f!\x8eu\x90\x89\x9c\xa9\xd6\xc3\x0e\u007f6\xaa\xa8\xf8\xf9\xe0\xfc1\x0fSJ\xb9\x8e\xa1rm\xcdo\xaf\x11\x1c\x13\xe2X\aQ\xb9N[\xdd1\x0e\xc3c\xa3_.ʁ\v6\xc6a<\x16\xa1\xfe\x05\xb7\x92\xf7\x88K\xa5\x05#\xe9\x96QGRs\x0f\xeb7\xf4gc7\x16\x19\xdfP߀s\xb3\xf7\xfcޝ\xe0to\x9b\xdcs\x0602\x11z\xaer\xcf\xff\xc1\v\x04\xe7\x9e\xee\x9d\xe0{V\xa2{n͕\xf3{6>]y\x9a\xe6T\xedЊ\xdcX\xad\xe0\xa5\xe6\xa3\xf4(\xb6|\xab'\xab\x91\u05f5\x82h\xde\x18\x82\x97\x1a\xbf%\"\x02\x97 \xb6\xfd\xf4\x80\u05eb\x90\xa4{\xbdi\x02\x91\xc4\xe1`./Md\x8a!\x9c\x95\x9e\x81]Ђ\xb3[\x8cx\f\x11\x9a\xbbC9w\xb4<\x1f\x10\x13\x89X\xc7c\xdbpKC\x00\xe4a\xcf\xe1\xb2b\x11P\x95\x19#lm\x1d\x1a\x01\xad\x11Пur\x9b8\xd8/\x19\x19\xf2\xf8\xc1\x14^\b\xedW`\x9b\xfa\xe9Mfs\xbf\xd9\f(\x05=TA\xbf\xa5{r\v\xdc\xd2\xd0\\\xb2X݃f\xa9\xac?8\xa3\xc8:\x0e4\xb3g\xe5\x9c\xe0\x98\xad\x96\xbfd\xa0\xe0<\xfcPm\x01#\xad4V\x0e\vo-Y@\x18PV\x94\x87p\t\xe8׆9\xea1J\x01Ⱥ\x89C\xf1F?\xdd\xd3!i\x82&0\x92\xc2\x00\xbcI\x00\x91\xa8SH\xaa\xa3H\x1d\xd1o\n7A?\x18\xdfU'S\xca\xeaC]\xd7\n\x057\x894\x81\xb2\xdeO\xcf\xf0z\xbdC$\x03\x83\u007f\xf3\xe7\x1f=*\x0fE\xa9L\xb5\xad@1NfY\x9eoȒ\xd2\x1e:4\x8a\x96\x96\xe9\xcf#\xad}n,\xac\auN\xf7\x11\xf6\x9f\\}Za\x13\xc8\x10\x1dgi\xc8\xf2\xe9~\xc6\xce@S\x9b\xba\xe5d\xf7&l\xe2'\xb3Y\xa2\xefp}\xe9@\xf7&:y\x9a\x130\x81\x0fo\xea\x86)\xec\x1a@\xa6\xbe\xc3}H\xf8U\xb2\x8fq\x9c\x1a\xb3\xdc\x02\xccSs\x90\xacG\xe6\xe9|\x9a\xa2\xef\xce@S\xa3\n\xb6\xa9\x1b$q\xb9Os\x82I\xa5\x13#K\fH\x89Os\x1c\x17Y\x83d\xf9\x04Y/\xd4R\x16\x82\x8a\x86\xbf\xbf&5\xd6@\xc1ѩ\xcdF\ff\xa2\v\xac\xca\xf2k.\xde`\xec\x1c\x8a\xd7\x1e\x14\xa6G뺦\xf7\xc1\x16Ÿ~%\xd90iB\x15\xd07}\xa8y\xf9\xbe\xe5\xcb\xf71_\xaa\xa6w\x05\xd0lᆬ\x98\xf5q\xe9\xbe_\xf6M\xc7\x19\xe5\xffR\xa4uŐ\x9e\xbe\x06\xdfp\xfat\xfa\x1f\xf8\xd2\xe5\xe9{\x94\x93JH\x82\xbcE\xb92#\xc7f\xfb,\x95\x8ft\xc1\x8dD%Q}\x1b\xf3:\xe30\xfeZ`\xa51\xa4\x82\nb\x8a\xfd\xc1\xd9\xf2\xe1\xfeW6K\xc4+\xa1\xd1\xf1b\xdad\xe0\rf\x93\x85e\x03\xad+7\xdfr\xdbJLZ+S\x12\xd6!\xd1\a\x0f\u007f}w\x14\xf4\xffP\xfe3\xefwi-V\x936\xc0u\xc4\xd7\xf4o\x9f\x1f+6\xe0\x98]\x92\r\xff`\x14W\xf9\xdc\x1fd\xb1d)\xf2\xdd\xd5P\x8b\xf0L \x00\u007f\x15\xa8#,{yi\x87\x82*\xe5\x0f+\xfe\x91\x1e\x1aӕђ\x95\x17\x98\x80\xbf\x8a\tg,cʺ9^V'\x8b\xbf0Y\xd02\xaf\xa5\x00\xff\xc0[\xb2\xc9g\x0e\x9c?\xee\xd6)\x0fM\xb9\xb9\xfc\xfc\x03\x89\x95\x87~0\xe7\x819?8\xb421\xd0\x12\xba\xfc\xfa\x9f\x1f^:3y\xff\x81+\xfa|\xadW\xb8#\xe7ܻ\xe1\xfa\xbboط\xfe\xde\r\x11\xf7\x15\xa0\xb7{^GǼ\xe1?\x17]\xf0\x80M\xaf\xb7=p\xc1\xa2K\xa7W\nB\xe5\xf4K\x81捋flj\x0eh9i\\\xeb\xea\t\xbb\xde\xfc\xecȜE\xdb\xd6Κ\x17\xf0Ι\xb9v\xdb\xc2\xd9\xfdÿ+\a~\v긇\xbf\x9a\xef\x1c}\x15\xb6$\xa4\x8a\xa7\x139\xf33&\x8d\x1dE\xa04\x00ɹD\x16R\x10\xfeu$\xa3\x92\xc2c\xb9\x9d\xc5<\x96a\x1c!\x05\xea\x14\x10;\xd4\xc2\x04\xb4\x17Ă\xbe\xe8Ȃ!ŕ\xcd1/嗋X\xcc\x1dv\xee\xab\xe8`\xaat\x89K\xfe\x9d\x18e\x12\xa5K\v@H\x1c\xbc\x92\xa62\u0605\xb8Ѐ\xaa8\xc86T\xc9\xef\x95\x1fj\x1fLeˍ4\xbbT\xec,\xbb\t.\v\x94\x17\xcb7:́\x8ab\xb0\xc1\xfex\u007f\xae*GASt\xd2=\xad\x8d\xf2\x8d\xd1I\xb9\xca,\xed\xaf\xa9\"\xf3\x1a\x9b\xc7G^H\x95Pu\x84e\x88\x98PC\x04n\x84\xe0A\xb7\x02\x0f\x18\t\xeaG\x99\xab\xa0W\x80f\x0fD#\xbf\x98OR~^\xf0e\xf9\xe5\xa0\xc6\xe9*\xa8\xd6\x14\\\xfe\xc0\xe5\x05\x9a\xf1\xb5NY\xa7\xf8\xd2LW|i\xa6\xaf=\xfa\x99<\xf4\xd9ѵh\v\x98ώ~<\x92h\xfd\xb5\vo\xb8\xe1Bt\x03t\x9b\xeeU\xab\xba]Ns5x\xa3O\xb9\x9a|\xfa2\xbelm\xee6h\xb8\x1e\xf1ݎ]7;\x81\xf1S\xec\xfd\xd8\xe3\x02\u007f.\xff\x83\xbai\x9c\xb5\xe3\xd5ZU\x17\xb8\x9c\x1a\\W9\xfe?\xab[\xa4\xa0ڜ\xa9\x96\x06\xdd\x06U\x15j\xff\xb7u\xd3\x13\xdf\xfdrl\xe5\xcf\xf8!\xe2.\xf6\xfd\xab\x94\f\xb9\xd2D߄I\x97\x1c\xfa\x9f\xd5D1\n\x82'\xfeG\x85W\xe5<\xb4Qf\x99\xf6\xef\xb7B\u008c\xf0\xef*1S\x01\u007f8\xc0)\x10\x10\xbeZ:!\n)QH\n\xa2\x12\xf1\x90I\u0084Z\x19u#\xbf\xfdv\xea\xd0\xfb\x87Ro\xcbo\x83\x8a\xb7\xe9\xe4\xdb 5\xea\x1a\x9c\\G\xaa\xa3zx\x11\x9c\xf2d\x12T\x80\a\x00f17e\xd7E\xf0X\x8c\xfd\xa8\xf1\\9\x97ZAm\xa0vP\x97\x92\x95\xd7{\xa8Lj\x15\x1f\xd5\t\r\a\xa8\x1e\xf1\xbct8/\x8d\xf2\xa0\xf7\x86Ҩ\x16\xc1\xd3\xe79\xe3\xf1ӥ\xd9\xfc\xb4%\x9b\x8e\xe2}\x89\xb0\x93\x8d\xb4\t\x98{\xcc\xe8_\xd2<`F\xff\xd4=\x862\x0f!\x81\x91\xee1\xa7\xb3\xe7\xc9\x06\x8c\xbd\x9b\xd9ʔ\xba\x9fۢ\xdbn\xc2\x17|\x8b\xa6\xd5\xe9\xd1o\tv&F\xd0\x04\x9bH\x8e/\xf3~\xd3_\x8e:$\x8f\xb1\xa3n\x80\xb2Q\xff\xc9\xfd$\x9f\x19ǟ\x0e%\xf1\x1f~\x10\x8d\u007f\x15\x91:\xa1\xae\xd5٩2j\x01\x96\xd62\xbeA\xbc\x85\xf0\x84\x10l\x000\xc2l\xa8Z\a3\xd1q\xd8єɢGĉ\xdbk&b\f\r\xee\xc9\a\xf7\xcfi[\xfd\xc0\xf2c\x1f\u007fu<~\xf6\xaax\xbc\xb0\xa2\xe1\x82\xc1s\x03E\xc4\xdeU\x14@}\x8bM\x05t\xfc\xefnZ4\xb901yS\xe3Z\xf9\xab\x15&\xd1l\xf6\x16\a\x16^}o\xe7\xa6_l\nEv\x1e\xb7k\x8b\x8b\x8b\xc1\xdf`\xef\x12oM\xfc\xe2\xf4\x83\x9bM\xc1\x02\xb7`\xa77\a\x1a-\x83\x02\xb1\xbf\xfd\xd3҈\x8d\xda\xdb\xd3lXd\x99m\x01\xc1\xe7)\\Ԩ\xd5HA\xf8q\xc0j+o\t\xb5ƥM\x06\xd6,Zq\xecO\xa6\xee,\xea\xc1eT-5\x99ڂ\xbfC\x8e\xb7\xc5$\xf2\x8b\xd2\xe1(\x1a*\xb5\xa89l\xa4R\x0e\x1b\xaa\x17:\x89\xeaj\xb3\xff\xffj\x16:\xf1\xc4+\xaf=\xf6\xd0\xdb\xefҟ\xfc\xedF\xab\xc4\xd6\x1bk\xa5*WE\xa0\xc2\xeepIk\x9f\xd8 Y\xcbj.8\xf6\xe0\xfeJ\xdf\r\x83\x0f\xfd\xaf\xda\n:S\xe65\xcf\xf4\x80G^М\xff\xdcF\xb9\xfe\xe9m\x95\x03\x9c\x96.䜼\xc4\xe9\x19\x86\xfeCcT\xcb\x1d\xb7@\xfe\xb9%\x9a\xe7\xcb\xc0\xe7\xff\xbb\x86\xc4kKH.!\xeb\a%\n\x1b\xe7\x88\xf5\x03\xbbud\xfc)\xec\x1ckAA\xc7T\x8a\xc2\x101\x84\xd2x\xd4\x1a7\xf6*\x8a\\\x99\x17y\x87\xfbp\xe5\xa9\xeb\xf9\xb9\xccg\xe4\xf9\r*\xc7\xe8\xf0\xe55\xbbU\x8bftL\x92\x86\x03\xe91\x84\xf5\x98ń\xeb\xc7Zm\xd3\xc1I\xf2\x95\x8c\xc3\xd0j42`\xbb\x92\x80W\x8fY\x81\xfdc\xafD1\xbe\x93_\xa1\x8b-\x8c\xc3\xc8\xea\x95D\xbaw\xec\xca\xe5|㟥l\x18S\a\xd82\xf04\xb8B\x18\xb7\x92\x00\xd6a\"\n\xc5OR\xc4\xeez#2\xd9\xd0\x13\x90(K\x10l\x14q\xbbh\\X\xde*I\x9c\xd1_\x1e-\xe44V\x8e.\x80\xe57&\u07b9kx\x1ep\xdb\xf1\a\xc1\x8b\x931\xba\x8a*{cG\xf0I\xf2\x16\x1c\t0\xa3\xf1\xa6ݻ\xeb\r\x16\xa0q\x81\x83\xf7M\x99e\x1c\x1c\x91O>Y\xf8\xf3c\x8a\xac\nO\x1d\xe3\xf6\xb0\x03\x94\x8e*Eu\xa8DmO[\x1c,\x1d\xd6\x02\x89\xe0\xb7\x06\t\xe7\x11f<\x8aa\xc2#$\x81K\xac\a0w\x03 \xdf>\xd1s\xa4\t\xb46\x1b\xc0W\xf2\x8d\vX\xbb\xc3\xe2\x90\xdb\xe46\xb4\xb1\xb3\v\xe4\x1b\xbcb%\xf8\xf7\x87֢Bۇ\xe0ߕ\"l?Y\xa7k\x06\x13\x87Z\x8a\x1f\x00\xab&\x82\xa8|\xa7l\xf0\x05\r\u007f\xff\xbb!\xe8\xc3\\I\xde8\x8f\xa9\x92\xc6\xc9\r\x9d|\x9c\xca`\xef&\x89\x8f1\x95\x03\xd5\xf7\xf91\xe0\x1bP\xb0/\xd8\v\xd3IK)\xab\xb3\xbb\xd3){@'ZY\xcahv\x8b&\x9e\xb9g\x90\n@6`\x87\twE\xa9\x0e&yI\x18\x97\xc1\xdaIJ9D\xa3I=A\xf0\xd7\x02\x9fb\x01̚\xf9|\xaa/\x85\xa2\xe8\xe6H\xa8\xe3\xa8\xf7\xe1u<\xe2\xf4R\t禓\xe8\xef\x18\x93̘*\x86\xfa\x87Y.\xe8\xb9\xffF\xfdE\xab\xfd\x9a\x18vP\xd6ߡ\xbf\x9e<\xeb\x06ݓg\xe1\xf8Z\xabE\xb9\xff=tL\xa4T\xfe\"&ǣ2=\xb7\x8e\"\x8e\xf0\x19ǾG\nGL\x11 \v`D\u074bg\xc69\x1b\x1e\xfb\x90X\xccF\xd5\x03\x00\xfbMe\xfe\xc1\xa7\xc9\xe6\xe6\xba\n8\xd0~ErnE\x1d\xd2F\xeb*\xd4Mlu|BWY\xd8Bv\x9d\xe4\x12\xe6i\xb2\x99J~{\xea\x16\x17\xc8\x1f^\x1c*/m\x9d\xe4*X\\\x87\x15wt\x88\xae˥e\x93\xab\xd8R\x10,k\x9e\xa5\x1eT\xb0\xee\x93$\x96ӈ\xb4\xf7 \x92t\x97R\xbd\xd46j\xaf\xca\x10\xac\xae<ڭ\x0e\xc5'\x96\xf8\xb8\x84\xf2\xe4E6\x1b\xa3\x10\xc6\xe0ZhP\xc0\xce\xffq;\x1a\x19\x00\x9f\x05\xcbq\x00>D\x9c\x11\xdb\xd4@\x04&\xef\x16 \xef\xd6찇\x82\xa7NQz\xa7^\xab\x05\x14~y\xfd\n\xd3\xd2@^,,\v\x15\b\x1c\xf9Q\x9b\xed\v`q\xcfq__X(\u007f.\x06l\xa0{^\xfa\xa6/\xe4/T8\x1d \xa2c\xf2#*b\x0e\x98i\x83\xd7\xe4\xdd&\xfdO\xe5\xd6\xe0\xa6a\x0f\x04\xdaS\x14\xe9\t\x80l\"y\xe1\xb8\x03$\u007f\xff\x05\x18&\a̲\x05D\xf9s7P\xc0u\x80\xf4\x85\r=j\x01\\.\x02Q\x05ܑ?\xff҆\x8a\xb4\xe0|r\x81\xfc\x13\xdbz\x854\x8aʻ\xe5}\xc3\x1e\x86ǃ\x1e\xf4\x91\f\x91u\xcdfůs\x98\xe5\x1b\x8ffB\xfeQ\xe2\xe8\xae\xc2B\x93\xb9\x14\xc4E\x1fv^M9\x914\xe3$?\xa0\xb98<\xa5\"<.\x8e\xf6L\xb6\xbd3\x9bj\x97\xb5L(\x0fL5\x8a\x06\xe3\xbdFV\xd3\x0f\xc6w\u07fdw\x0epf.p©\xb1\xe5M\xcdn\xbbc^\x81\xa58(Uν>\xe0n\xac.K\x14\x15\x9ce\xd6\xec\xd6y\x8c@\xd7\xda{SF׆\xf8{\xf6`\x1e\xad|\xe4\v\x85\xa673\x91\xd9\xf07K\x8f\x9cݒ\xca\x1apȕHd\xa8\xb0Q\"\xa9p\xd1(@dY\xd8\v\x90T\rc\xe9T\x88YK\fKJ\xe0+\xa4V\xa2\x1fO\x96wd\xc4C$Zѧ\xc3tH\x8d\xe1ο\xbd\xa7\x10\x84\xf1n\x18\x14\x82 \xb6\xcc\x06\x81w\x00\x9f\xc4?\f\x97&\x19i\x02\x88\x86G,\x8a\x95\xd9\r蛙\x86\xeb\x1a\xc0\xd1\xfd\x84\xe2\xd0\x04|шD\a\xa2>\x02y\x10\x89\xb5A\x9f-@K\xc0\xe6#\xee\xc4L\xe6\x1d\x85\x15\x0e\x1b\x12\xa9\x13\x89җ|sĩ\xa1i@3@g\xbaM\x96\x93/<\xb3\x1fX\xaf\x846t\x90\xd6\x14\\\x05\xc0\xee\xa7_\x85\x9f\xa6e\x9a\xa9\x9by\xd6̺\xa6q\x91*\xc1\xbe\xde\x15\x9c\xbb\xfe\xbc+j\xa6/\xea\x8a\xd3\u007f\xbd\xff\xfe\xa12\xad\x81\xe6\xb5\xd0\xea\xe7\xbc+&\xadZ=\x8d9s\xd5\xde{\xdd]\tF\xd7l\xe2\xfa\xf6Ǝp7\xa9\x17@\xba\xd7Ŭ\x827G\x05\xb1\x0f\xbb\x1d/Ð\x1e\x11\"\xf3\x18^9M\x82\xee4%?\xc2}e\xd2\x17\f%C\x8di*\xd4fFi\x1a\xa5i\x94&8{L\xd4?\xbdp\x88\xaa\x18\xe7G[\x06m\x95\xf5\xbe\xf7\xc9Xګ`d\x11\xf4l\x8c'k\xf3\xf3\x98\x14&\x14\xad\xcb\xc5\x1ecb\x1f5n\xa0\x1a\xf8\xc3\xfe\xa8\x05cd`A\x17\a0g\x02\x96\t-\x12\xa6\x8a\xb1\xe1\xe6\xc7X\x1b\n\x01\x10R\x10\x16\xcf\x1a\xd7Y\xd9\x11<\xcf\v\xecz\xffŽU-\xf3\x02\xe3\x02\xe7̞w\xbe'\xe8\xa9\nv\xaf8\xac\rj\x8d\x00BX\x1c\xa4\x0f\xaf\xe8\x0eV\xa1\xe3\xe7\xcf\xef>\a\xe5\x9aג\xf8k5`Y\xe0\fTT\xda\x1bj\xba\xcb\xe7,\x01O\xceƧ.\n\xdf\x1cf\x91ء\x8b6\x04;*;\xc7\xcdZ\xbcdNywM\x83\xbd\xb2\"\xe0\x84\f\x84\x000ԈKՒ4D=#\x9e\xa6\xcaeL\x92p\xd9E\xc8\xf7H\xf16_\x86-\x9d8\xa0\x87(\xfcu\x92\x95wʫ\xa6\xf1\x94\xe0%S\x82\xd7\xce$\xe5\xf7\xde#0\x84\xeaz\x03\xa0ޓ\xdf\xc3\xcb\a\x04d\x11%NQ\xc7\xe5o\x8ec\xff[:\x91\xfc@~ƹOq\xae\xdc\xe7\x04S>P\x86\f\x05\xbf\x91\xa0䬕\xa9}Ǐ\xef\x83\xf8\x17{\xd7\"\xb9f+\xf1wm\xc73;\xbaa\xb68Z\xa0x\xd1\xf3\xa8\xd1\xf3\n9\xaa\x02a>\x1f\xf3\xc0n\r\x02\x05\x04\x1d\x98\xd6\xe0\x10\rf\xbd|\xd3\xf1}\xf1X\xcf\xd9\xe7\xbbϓѸ?G\xa7c\xde\"[yg\xfa\xfa\xe3\xfb\xd6\xde\ag\xadY\xb7Q\xa9@\x14z䛒\xfb\x8eK=\x11\xb5\"\xaeaU5v\xc8:t\xa5\v\xdf\x02o\xd1\x1dp\r\xcf\xcf\xfaI+\xbf\x9c\xe3'\x9d\xe7\x04\x8a\t/N\x12\xb6\xc5\xe4\xf2\x89\x98AO٠#HzK\x01/\x01\xd1 \xf2]^z\xe6 1Q\xb18\x92\x880\xe7)\xbf]\xa4\xddh\"\xf3\x0e \xb9\xaf+\xe7_\x1eTa\x1d\x83U8\xd4i\xb8cm<\x8a\x91\a\xd4\xcf\x1aǥe\x1d\xa4}\xe8d\x90\xc0\xc5@ų\x9c\xfe\xa1Ac`h9\xa1\x17NQ\x1b\xafS&\xbfݫ\xbcM\x9b\xa6\xb4X\x19K\xa9\xd9\xe8\xb0\x18X\xa9~\xc2\xfa\xfa\x82\xe5\xfb\x96\v\xa0JЃ\x14͠\xabX\xe5\x9d\xf7\xc8)\xb3\x96\a=Pԯu<\xb2u\x88LU\xb4\xb7\xefA\xcf\xc6\xea\xa6i>M\x807\xd4:u\xde\xe9\x13&\x89e\x15\xb8V\xbeb\xbd\b{\x00\xaf\xc5u+9\xe5\xe5\x14\xbbde\xaen\xc0\x8a\xa5W\x1a\xb3\xf0\x11\x04\x1f\xa4jdS\xb8\xfc\xf1X\t6>\xf5\x13A\x158ozt\xf6\x16+\x14\xe4$\xaf5\xe8\x13Fv\xbe\xfc_\xf2\xdfiN\xd0&,\x86\x01\x9d\x19\xec\xea\xe9>\x0e\xe6\x01V\xb02\x8a\xc4\n\x92\xdf\xca7>\xd6\xdd#_f\xd6\r0Z\xfcҬ\xa0`>\xd0&$+H\nкe\xf6\xb3\xd7H\x19\xff!\ue122o\x00ڇ\xb9\x92\xca\xf1և\xfeh\x1f\xc1\xb7\xe6N\xdc+?\xfa\xa8\xb1\xd0]\xff\xe0\xab\xf2\xa3\xaf\xca\u007f¿\xb70Ck~\xd2\xd4\\\x06\a\xd3,\x9d\xa8\xf7\xfa\x86\xa6\xd0\xcf\xe0?0evg\xe7φ\xfb\xc1\xe0\x01\x87\n\xc6cuH\xc3\xca`\xd5s$\x1a%\xdf\xd4C_\xbdV\x92\xe4\xd7@D\x92\xd6b\x8d\xaeQ\x92\xc0\x8bR\x1d\xfc\xc1\x88Uͫ\xf1Y\x10A\xf9\xea$|E\xa3\x92\x19\xbe{Z|u\xe5\xf9\xe8\xd1a\x15\x90ޡU\xc1\xdf\xf3\x9f\x0f_C\x8fSn\x87n\v\"\xf2k\xa4 \xf4\xe4\x91\xcfǥ\xc2ES\x8a\xf9\x1aʇ\xaf8\xd3\xf3A<\x96\x89vQ \xf0\xb5#\x9e\xcf\\\x9dW\x1b)WI0\xb2\x01\x80\xd2\x02#\v\vF\x16`\x8cw\x90i~m\xa6!F\xbe\x83\xcaQ\xf5R^\xc2ȥ\xe5\xcfH#\x8c|ap\xc7\x18m\x90 \xb1#\x16\xd2\xc3\xe2\xa8gaH\x9a\x80\xc4F\xa2A\xc9\x17\x06>\x9a\r2}桫\xaa\xe1j\xfb\v\xcf\x1b\x1f\xb6\x83>\x06\xac\xabM_d\x92\xeb\xd9d2\xfd\xd3\xf4/\xe8\xa3\x0f\xa7?\xfd(\x1a\xbdJ\xfet5X\x05\xbdO\x80wN\xae\xbc\xfbn\xd2\u007f\r\xa7\x12\xdc\u007f\xab\x18r>-\x94|<\x8b\xee+\xf9\xe2> \xb1\x1f\xca\xff\x1ez?=y\n\x18W\x04~\b>\xee\x18\x9c\xda\xc8<\x13\x1a\x9c\x8a\x86\xb7W䯀\x1e\xac\xbe\xfe\xae\xbb\xc0\\0\xeegj[\x99y\x85\xb3c~\u07b7\xaa\x8cCՀC\xad\x14\x1e\x85C\xeb\x01\x8e<\xb59O\x01\xb5E2VnK+\x88g\xc0j\xe9\x942*\xad\xb5j\x18\x83~\xd9\x0ey\xb3\\'oޱL+0\x1a+\x1a1{\xec\x1a\x8diu\xfbW7*\xc2v\xe3\xe4\xc3o\x1f\x9eܨ\xec\xdc\xf8U\xfbj\x93Fc\a=\x82\xc8|LƦ\xa1~\xb9߮\x81\xdae\xd7\xde\u007f\xff\xb5˴P9i\x95̫\x97\xec\xb6\xc2ˉ\xf4~\x8f\u007f\xfbd\xec\r9y\xbb\xff\x1er }\xa1u\xf7\x92\xd5f\xc9**\xdf?\x91\x1b\x02\xa38\xb6\xb0?'a\"U\x91\x04\b[/\xe3͑zyU\xc9@\xa5\xfbʙ\xc4\b\x16p\x82\xf0y=\x83K\x8e\x9f.\xa7\x86۳\x14\x1d\x9fH+9\xa4ې\x973۽\x94\x99R\xffNg\x13Q l\x81]\x8d}\x02g+D\xa5d\xf9\xff\xa13\x18E\xe0\xa7\nd\xed٠\xf5C|=\x9c\x97\xbd\xb4\"\xbd猖\x1d\xb2\x9e\x82D\xf7$\x9d\xc1\xd3\x1a\xa51\x8e\xf4\x9d\x1e\xe5K\x9d\xac/%\xed\x94\xc2\x1e\xa0c\xa7io&5\xe6O\xd6\xe7\x05\xe4p\xbdF\x95\xc3r\x86\xfd\xfcr\x8c\xf5\x03re\x00\xbf\x1d+\x99\xcf9\xcdSn*\x8a\xad\xaeY\xdf\x17L\xb4I\xecD\x84#\x01\x10\xd9#\x04\xab@\tfq \xc7패\x9c\x18\xcd\xd0\b\x15\x97a\xf0#\xa3\xfc\xcc'\x82\xd5b\xbc\xf5}=\x10\x8dI\xa3\x15\\̮\xfd\xc9'\xf2\x87\xb7\nZ\x9dh|\x15,=\xc1\x93\x13:=(\xce\xf7\x8cT\"\xfa\xfd\x9f\x80)F`E\xe7E\xa0\u007f\xffV\xa3\xc5j\xbc\x15\x14\u007f\xf2\x93\xb5,\xd0\xe9\xc8Q\xfe\x84|\xef\xabFQ\xa7\xa5_\x1b\xe9/\x99\xb3\xe1a\x9c\x93|\x06\f2\x94\x13r\x1e\xa2K\x8cbIx\x14\xbbX\x15\xfb\xbc^\xb3\xd9b\x1a\x85\x9c\x9f\xbeI\x9c&\x82\x84$J\xc1t2(i\xb4\xe8]\xc6NE\xb9Wؗ\x89,\x87ޥ\x96\xcd\xcd\x16x\x90V\x96\x84c\xa8m\xf9pF\x02&+a\x0e\xbb\x15)\n\xcd\xe9\xe7\xe5\xe7\xc1z؇\x06d\xcc=\x92>\x8c\xc6\xed>1F_9\xb4=\xb8!\xb8\xa7~S\u007f\xfd\xee`\x90\xbe\x12\xed\xec\xc6;{\x82L\xb3\xfc|\x1ac\xad\xe2\xab\xeapn|U\x1d\xbe\x1e^;\xb4-\x88.\xea߄\xf2m\b\xd2\a\x82\xe8\"\xb4\xb3;\xb8aX\xbb(\xba\xffȐ\xe51|Y\x15\x87Yz\x94_-\xf1^U\x96\x18\x86{\xab\xe6\xf8\xf7\xf2\xfa\xf6\xb0\x15\x863\xf8u\xe1\x05\xca!\xb2\xe6C+Hn9\x87\xaed>\xef)\x1cȮ\xd7˵\x84\x16U\xc9I\xefͧ@E\xe3$*\x11}\x92\xbd\x98*\xc4~\xd6\xe5 \aV\x8e\xbd\xc1\x039\xfa_\xfa\xa4X\x9a\xc2AW6\x8d\xc6Я5\x83D\xaaT\xb4\xb8@BlE\xaf\xdcM\xdf\x17\xc4+\xa6\xa2Ք\xd2\xc3d0X\f\x92v\xbb\x9c\xf4\x92\xb9\f\xc9\xc1\xe8\x19\x14\xeemRf\xfdFu%\xc4T\x82\x16\x1f\x91\x10c^\xec\xfe\x95*-q\xcb)tS9岠G\xca)A\xdfo\xd4jYJ\x12\x86\xee\x9a\xe6\x95\xd1}A\xb28\x14\x84I}J\xb0J\xc3e\x81\x92\xd2r(\x9f\xc4Z`Y~Ir\xe5\b\xe2Ximf\xd0)\x02~\x81U\x96(\xe2\xb10\xc4$\xc6\xca\xde(\x84\xb2\xcf@\x0fz)\xfd\x9ep\xe9\x8e_\\zv\xbdOw\xbf^\xe09;]\xd1W\xf5\xc0U\xa5\x06\x83\v\x86\x865\xd7c(?\x1a\tz\xb0\xb9\xa4?ܶ\xa2g\xe7\x9a\xe6'\xfeh\xa0\xb5N\xb0rG]u\u007f\x99\x85\x85\xa9a\x8d\x95\x1b\xff!z\xb3\"\xe5!\xf6\x14`\x01\x164y\x03\xd5\xf3p\x18\r\x15\x0e\xe4\xc0A72E{\xf3\\\fG9 \x82T2\tf\xa5\xfft\x8aB\x1a\xf9\a\xc4IQ\xc9\rW\x8c\x98\x92sxn\x18\xf1\xaaR\xc5\xebP>\x1a\xd4\f#G\x8a\x91\xad\xc4\\(:\xe4\x944Q\x92S\x0e\xd1R\n\x93\xa57\xab~\x9eF\xec\xf39\xfc\xcd\xd1\xcb\x02\xc5r\xc2\xed\x06\xa9\xe2@ \xed\x1d\xe6\x14:b\xfc\x1aQ&e\xb8P\a\x893\x97\xc9R\x9aN\x96ZD\a\x9a%&J \xe1\xd8~\xfa2\x81{\x02\x81@1H\xb9\xddr\xa2X\xfe\xdd\xf7/\x13\xf1SV\xec\xbf1\a8c\x99\x12\xf8\xfe\x01\xe5Y\xbfϷ\x85\x8e\xe8\xdcw\xe55\xa5\x05\xb7m\xfa\xef4\x19\x89\xc9\x15\xf4\xeb\xc3y\x8e\xb1 \xf3/T\xa6\x1e4\"9\xec\x9c\t\b|\xc0O\x85\xb3\"u(\x9eM\xc6(\xc2֍\x84nb.e1\b\x88\"\x84\xa3\x82r\x0e%\x89\x17\x9e\t\xc6\x14ӆ\x8d\x8d\xf4\x9b\x01ڠg\x19\xa3\xe4t\xa3\x17 }*\xdfݶ\x027\xd0DH\xb7\xe3B\xadl\ag\x0f\xac]\xaa\xd7rt9m72\x8c\xc9Z\xe0.\x16\xf6\xbcT\v\xde6ku\xb4\x93u\xcbN\x9a\x06\xaf\x98\x90\x84\xe0\x84\xa2^\xde=\xfe\x95\x8bŒ\xe2B\x9b\x99a\x8dF\xc3_\x8e\x18l\x98\xa6\x85cY\x96\x81\x80\xfd@2n6J\r\xe3Ea\x8b \xbe\x05(\az\xbe\xf1\b6\xcf\x02\x9a\xa1i\x98\xdcd0\b[\\\xc1\x0e\x83\xc1\xb4Ioھ\x9ffЅ\x00\xb2<\xaf\xea\xe3\xf4\x10j\x8f\xb6\x9cW\xed\xf0\x95}\x05\xe5\x05\x1b\x02q\xf8\x16G\xa8\xb09\xd5aM\x81\\WWr\xe8!\xd4\xe4\x1d\x82(\x19\xcf^\x81k\xba\xe2\xeb\x9f=s\x18\xa9\b\xeb\xb4F\xa3\x8e-멜\xdf\vjH \xd9\x1b\xe0NQ\xb8\x1b\xbd\xc8k\xe5\xebp\xceè\x8b],\x19/\x15\xc4?\x1e\xfd\xc3nM\x81\xeeb=\x80Z\xb6\xb0dy\u05fb\xa2p\xa9Q\x92/{B\x015\x06T\xdd)\x8a~\v\xe9\x0f+\x15\x9e\xf5\xac\x88\x89\xbd\x18\xdb0\xf0\x93c\xbc\x02ы\xd7[\xe9p\x95\x06\x1b\xea\xb2kM\x98\xa5[\xad\x86J%\x89\xa1\x85\xe8\xb7~uD\x14.7J\x13wuw\x14\xb0\x16\xd3:\xdel\xd2\xc2\xcd{\x83\xc1ٻ<\xc1\xee\xbaX\xb8rf\xf5\xc4qU\x05\x96\xe7\uf40c\x97\vbÆ\xf6f\x91\xb3\x18fkL\x82\x91v\xc4[\x17\x96\xad\xb8\xc0R\x16\x9c^U\x1d\xad\xef\x89O\n\xba\xc0\x8a[>p=\x8c[\xe3amEeĉ\x9eu\xb9\x0eB=\\\xe5\xd2,\x98UX\xeb\x1f簙ŀ\xbbb\\CӴq\a\xde\xf4<\x8ea\xa2\x1f\xe1\xfc\xbe23'Z\x0f\x99\x00\xad\xa3\xc5@\x91cA\x87\xab\"\xec\x0eH\xa2\xd5Q\x1dj\x9d\xb0H}g{\xd1;k\xcd\xc8\xe0\x02\xe0\xed*Sp\x98\ng\x9d\x87\xe3Y\x01&\x94\x91\xc33\xa1\xe0\xe5\xc0\xee\xc0֚\xbd\xa2\xf0\x80\xe3\xed\x1f\xdd\x0fJ\x04\x9d\xc6\xf6K\xb3V~\x1dc}l\xdaw\x97]\x9eO\xd6\xd4\xeeh\xf8\xcf\xebp\xd1h\xf2\xfd}Rm9\x8a\xb4\xc1\xb2\xb5\x82x\xf0q\xeb\xa3\xf2\xadfQ4\x80\x8d\xafj\x8d\x17\x1b\xa5\x05sD\x01\x9d\xd8,\x19/\xc3yQ\xb2e\xaeH@\r\x91\xa8\x81ʋ\xa4u_@\x05\xf2WaJ\xb2\xddM\x119j1\xc22R_%\x92F\xe3j$\xd3\xcdl\xb9\x0eg\xe5\xe0\x92\x87P\xa7 1\x8a\xc0\xabl\u007f#\xffL\xa3щ\xbf\x90t\xefJA\xdd8\xfeg\x1a\xdb\xcf,:\xadF\xfeջ\xa4\xcf\xfd\x01\xf8\x95-\xaa\n\x98&\n\xeb\x8c\xd2|Q\xe85Jp\xa2\xd9l\x16兡\x85\xceE\x16p\xafd\x16,\xe9\xe7$c\xaf Η\x8c\xeb\x04Q~\xd2(\xa9\xbc\xf7\x8a\xdeQOtu\xdc\xf11WJ~ɲ\x9d1\xf7\xe9dSʨ\xc6H{\xfbpTW\x1fؘ~I~\b|K\x16,y\xc9x\u007f\xc6D\x9d\xb1[C\xf7K\xf4\xba\x97.\x92\x13\xe0.y\xcf\u007f\x9f?ґ\r\x1d\xb8\x11\x95}\xbb \xe6\xf1\x0fi(\x03\x92v\n\xd0h{\x1e\xea\x19R@\xb2[\x1du1)\xees\xf8\"\xe1\x00>\x80\x94 倢#Ҥ\xc7\xd0\x01Za\x92\xa6\xb3\xa5͍\x87t\xe6\xbd\xf8\xa4a[;Og\x17\x1cxl\xab\x87\xb3\x8fL\a\x00l\v\xc8\xef{\xc1]W\x06&\x83#3\uf78d\x8el\xf4\xc9\xef\x12\xfc\xeew\xee\xe5\x9dG\x9c\xfc\x0fO\u070f\xb6z\v\xec\u007f\x13\xd7\xe7a\xdf5xs\xeebV\xa73\xefw\xb1g\x81ug\xf3\xce=N~%8w\x19\xeb\xdao\xd6\xe9\xd8%\x1bq\x96\xeb\xfc\x8f\xa11c>(G\xea3\x83\x19\xbe\x1eJ&\x93i\xa4J\xcb\xef\xa0\x1dt\xe8X2\xe9E\xbd4}\xb3\xd3\t{ѯ\xa0\x83\xbdD\xd6VV\x96\xc1\"\x93\xd1\xe0\x94o\x06\xbdN\xe5\xd7`4\xc9\x0f\xa8\x19\xb0~[\u007f\x8ab\xfe\x8a\xda1BM%\x98CvL|\"0\xbc-\x10\xf5\x87m\x01\x8b\x1f}Fq$\x05Y\"\xa1\x80\x05;(:j\xe3ш-\x86\x81P=4]W\xc5\xf8\t\bim+\x87w\xd0ԀvZ9\xe6Z\xf1\xc6\xedی|d涋\xe7\xdc\xda]v\xab8Uz\xa9xc\xad\xc6\xcc\xe9\x8c]\x1b\xdfN\xf8n\x9dSz묝\xbd-'<\x15S\x9a\x17\xd5\xce\xd2h\x1aC\x1d5\x13\xaaj<Ҕ\x82\x92\xe6\xda\xce\xf2\t<\xdb\xe4\x9fX\xd1\x14*\x11\xe9\xe4\x93]\x85\x87\xaf\x9cr\xce\xe4j;sj\x10\fQ\xa7\xc0S\x11p\b\x80\xe2\x8e{\x01\x18\xfa\x1a~5\xc4\x177\x9d\x9d\xbe\xa3\xa4\xbe\xa4\xc0\xc0A\xf9ǀf\rf\x97\xbf\n|\xe3\x8b\xf8\x1c:\x0e\x00\xf954=h\x04Gq\x95\x82\x8bA\xb0%\xd4xIl\xe4w\xb0J\xcc`ޔ\xccPv\x01\xdc,\b\xe9\a\xeaK\xa17\v\x11\xe1E\xea\xe0o\x05A\xee\x15\xec\xde\xd2\xfa\xc1\x81\f\xe2\x83\xc2瑽o)\xfan\xa6\xe26u\xf8,\x18T~x\x8c\xb6\xd5.\x9d\x01\xa6{\xe4>{\x1c=\xb3\xd4.t\xe4\x17\xa5\xfe\xa5\xb1\xa0(F\xa6\x99\x12\xc1\x8e\x8b\x9c~>WZ\x8cY\x95\xf6fu3 \x8c\x95\xc4\xf2\xa7\x1f\xd5i7\x87Q\xceK\xa8\x89\xd4\x1cT\xa3\b\xa6\x06\n\xf0h2\x02\n\x0eSF}R&\x1d\xa2U\xb1\x98\xe8*\xd6\x060\x85\x01\xf6\x82\xc1,\x06\x00\t\x1f6\x9c1*a\x86\x82p\x80\x8f\xe0\xad\x14\x91\x98\xfb\u007f2Հ\xa9\xf0\x98\xf4\x97:\xf9\xe7:\xa3A/\xa7\xf0J\\\x8a\xf8\xb2`\xb7\x97\x8e\xf4\xd3`\xb3A\x8bI\xd3\f\xe2_/\x80q\xf9ZΤ\x17\xb4\xb6oޒ\a\xa6W\xff\xabz\xba\xfc\xe1\xe4\x8f\xef\xfe\x98\xe9\xfd]\xb5\x99\xb1\x02\xbfaГ\x01\x812KV\x96@o\x9c\xec\x17/\xfb\xe4,h\x11\xb5Z\x1a\xd0[\xff\xb28\xfd\xb9F\xd4C\bwЗ\xf4\xf5\x1d<\xd8\xd7\a\x0f\xa7\xfb\x14\xdbO~\xbd\xebp\xbd\x83\xb9z\xb3\xa7\xad7\x18Q3\xfa;\xdb\xe1{\xd4\xfb\x8ea\xb5\x93N\xdb\n\xd9j\xffi\xacZ\xcbC\xb9\xea1\x17\x8fj\x02\x1d\x92\xbfv\xa0\xfe\xebWqӰ^\xd6@ub\f\xb9\xe0w\xbc\xe2\xe1+\x06#\x1d!δ\x0f\aƮ2\xe3\xcd_Y\xc0\xaa~\x92t\xe4$ّI\xe7\x04)\xb2s\x8a\";\xe8\xb7g\xacZ\xe7A\xbf\xff\xf3\fIe\xba\xcb\xd4ߔ\xab\xff\xc8Z\x9e\xbe=F\xad\xa0\x9ca\x9f\x19V\x01\xd9;vk\xc0\xfe\x11u\x1e\xd6\x1a\xb9v\xf2f\xab\xb2e\xac\xa6\x00[\xce\xdc\x00\xa4ϳ\xaf\xab}\xbe\x1d{\x04\a\x89\x91\x9fX\xeeO\xdf\xe7\x83V\f\xed\x1d\x0e\x85\xe3\x8a\x1c\x1a\x0f`^B5\xea\t\u007f\x00\x18\xc0\x00\xc9\b\xd8\xed\x02\xf3\x91\xb0\x13\x175յvv\xd4NN\xdfy\x9aJ\u007f\xee\xaa\xef\xde>\xa9\xb5\xca)\x86M\xe6`h\xde\x1a3\xb4ͮ\xe8\xfb\xc1\xc1sw\xdd\xeb\x91\xcb\xef\a\x90\u05c8\xadsR\xbb\xfe\xd8\xd67mKWl\xc1Xu\x8e\xb7\xee8wN\x8dY\xc3o\xe6\x19\xe3\xf6\x85\x8e\xc2k\u05ec?\xf4\x1c\xac\u07b2\x05<\xc2;Y\xb3\xc1(6.x&\xbd\x85\x1aU\xf78\xf1\x86\xce\xd5\xfd\xbbǹ\x11Փ\xbe\xab9\xbeG\xdd\xdf̯\xdf/\xbf\xa3!\x18\xb5\xf2\x83?\x1a\xab\xf6C#\xab\xc9F\xc6l\x8f\fndB]\x87]\x9ay\xeb\x8a\xc3\xc6\xc8u?\x16\xa3\f\xday;\xe1\x12\xe3x\x8c\xcd\f\bm/1\x1b\x13HB\f\xc7\n\x15D_\x9b\x15\x93\x82A\x1e//Q!\x97;\x18t\xbbB\xfd!\x97Ll\xbc\xc0\xeb\n1\xfdq\x13]e\xb1\x98\xc2\xda\xc6\xc4e%]\x96\x89\xb7/\x9c\xb1+\xe0\n\x95\x148{k:|\xa2K\xab\xe5\xf5\x85V\xc9U\xd5Y\xed3i\x81$\x89\xb4\xa0a\x80m\xe6\x16b\xb5A\xf7\x84\xeel\x00\a\xfa]\xd0V\xe1\xedj\xa9oi\bn\x9a\xd4\x05\x8bݮr\x00\x82.xIA\x10\xc2-\x89\x85>\xb19X\x16\xaeh\xb6J\xb6\xe2\xda\xd2f\x8f3\xd4U\xe1\xe7\x9cVa\x8b\xba\xe6\x8f\xc6\xfd\x04\x891s\xab8\x8cٗ7R\x83\x0f\xdamD\x1b\x86\x0e\xec\x04C\xe0\x8c1\xf9/Th\x8c\xd5&\xc1\xed\xd1Dc\x0e5\xf2\xc7[O\xd7\x10\xeb\xe3`\xf3L\xf9o\x8cF\xa0E\xd1\n\xb4&_ug\x95K\xb2\x16\xeay\xad\xd6%\xfa:jz\x9d\x05%!W`\u05cc\x85\xb7O\xb4t\x95\\\x96hԆM\x16K\x15MgZ\"\xfd\x17\xa5\rH{<ܲh\xe6\x16\xc1\xea䂥3BNOsim\xb1M\xb26W\x84˂͢oab\v\x84\xc1\x02x\x89+\b@\xb9\xcb]\f\xbb&m\n6\xa0\x86\xeb\xf2b\x14\xfa\xccZ\x86\x96ؑʩ\x16\xd4\x1a\xab\xa9\x8b\xa9\xab\xa8;\xa9G\xa9_\x10^\x13\xec\x19\x8fW\xc9\"\x18Z-\x88\x04F\xf4\u007f\x94E\u007f\xaa\x11/\xa2.\xdf[X\xd5G\be\xc1\xe2#^e\xb0Y3,1h@$N\xb0E `\xb3\xa2\xdcu\xb1:\xcci\x84\x834jA\x1d\xa1\xa5\xf3y\t:\xa9\n~\xe9%\xfd\f\x89\xf7|8@\xc00m\x11LtJ<\xb6\x90\xb8\xa4,\xdca \x0e\x8bZ\x8e\x80Z\x8eQ\vx7\x15Y\xccfK\xd1\xd3\x13'\xa6_\xe8\x9e6\x13\xfc\xa4=\x1c\xf4i\xb9\x89\x00\bV;h\xe3\r\xe3\x02\xbe\xf6vo\xc98\x03?\bi\x83;ZWd\xb3\x16\xadu\xdb.\xf3;9 _\x92H@\x9b\xa4\x9bX~\x85\xfcw\xf9\xb3+*&\xe8\xacV݄\xf2\xfd0\xb4\xbf\x1c\xa5\xd3Ƴ\xa6G\xa23y\xaf&\xa0\x9f\x06|\xb6\xa2\x9a\x88\xdbfsGj\x8alO\xb4\xb7\x138\xebvN\x8f\xee\x0e\xbe\xce_\xe0\xf9\xe4\x8eZ\xf3\x80\xf9\xa8?\x12\xf9\xebdy1\xb8\u007f\xf2\x1e\xf9\xba\xd2\xcaBK\x10\xf8\xe5\u007f:\xa1\xa9\x1887\x1e\xaa\xb3\x95\x8d+\x01\x9f\xddUZf{R[$\xd8\xc5Ґ\xbb\xe9\x92&w(T\xd4\xd05!\xe2\x02\x06\x9b\x9e\xae\xbf=\x12\xb9\xbd.M\xffdnE\x13k2\xb1M\x15\v\x8f=2\xaf\xbc\x19\xa7\x9b\xcb\xe7\xd1M\xa0\xf4\x97\xbft,u\xac\x8b\xff\xfa\x82\xbd\x8dE\xe8\xdaF\xb2q7\x83-\xf2_\x8a\xcd\xd0\t\xcc\xf2\uf0e2\xbb\x12h\x86\xaf\u18af\x03\x8d\x97\u007f!\xf1\xb2\x99\xfe\xb1\x84ZE\xed\xa6\xf6S\xb7Q\x0f\x13=\x1d\xa3\x14\xa2w\xcd\"\xa1\xa7\xae6\x18\xc1x\xba\x96\x88o\x8cגyyQ\xd4;\xa2\xe4\xe5\x05\xa3\x01\xd2aZ@dԋ\x8dc\x86\x1b?ڭ%\f\xb8<\xe7%]\x04C\x86\xa3^\xe1%=\x04Dhtw\f\x9e\x1c\x912}O\xe9g\xb8\xef\x05\xc7\xe8\xa1\xf4+a\x87\xdd\xee\b\x839g\x9d5ԸA~i\xfdj\xe0]\xbc\xd8\xe3\x16i\xb0Xc\xa8\x1a\x1f\x03Ǵ\x96Xm\xf9\xe2ŕ\xe3c\x16-\x98\xb3\x04\rkU\x8f\xb9\xc3\xed\x1d\xe1¢\xf0\xa4\xa9HQ\x81\xe9\xfe\x05\v\xe0\x1b.aQ\xe3\xd3i\xd7Ӎ\x8b\x8d.\x94nz\n~L\xd2C\xae\xb5\x17\xae\x16\xaa\x83\x85}S\xc0\x93\x85\xa1\x8e\xf6Paa\xa8\xbd#T\bf-\x89\xd6V\x195K\x00-\xba=\xa0\xe4?\xdb\xed\xa0\xd2\xdeQU\xd5qx\xf9\xf2\xf4\xaf\xc0\xe7\xf2\x0f\xcal\xb4\x17\x9c#_X\xe3\f\xb6,\u007f\xa1\xd3U\x1f{/\xbd~|<\xee\x9ek\x8c\xe8J&-\\7+\x18\x89\x04g\x1dC\x9b\xa8ۭ\xa5\u007f\xf1֤IoMN/\xfct[S7g\xb3q\xddM\x9b>\xc7i\xdej\xe5Q\x9a\x11\xe4\xcd\xf2?\x80iځu\xf3\xe4o'?<\x1b]\x1d\xea~\xb8\x1b\xdfd\x8el\x8c\xb7\x06\x9d\x11p@\xbe\xce\a\xed\xe5`\xb7\xe2K\x89ys\xffMI8\xfa\x1fp\x8a\x06\x1d\x97j\xc3\x19\x85\x19\xaf\n\xdb2\x8b2 \x06\xf0A8_\xf7\xb5;\xf4\x85ͪK\x03p\x97A\xafu|Q\xea\xa2_\xd6\xeb\xd3_\x82n\xbdNg\xff\xa2\xcc)\x1f\x13!(\b\xff\xc3N\xaf\x11\xe5iU~\xcc[\x80^\xa1\xc9T\tV\x9bmCg\x81\xf4-V\x8b\xa9\x12\x9e祯\xa9̌\xd1\xca\xd8$e\xf9E\xf0z\x0f\xb6 \xd8h\u0381\xbd\xb0\xe2\x80\x1c\x01v@\xf6ba\x80\xc4p\xc7(\xe3\xcb\x1e[\xf1Ӣ\x86\xd7\xec~^\xab\u0558\x9f)\x96\xe88oy\xd6#\xc9k\x90\xbam\xf5>-\xf2\x1a\xad<\x04n\xd1\xfc~\xd8\"5\r>\xf0\xeb\r\x96\xdf\x02\xf9\x87\x82`,\xa1g\x1b\x02\xe90\x94}\x01\xa4`\x83\xf7\x01\xfcO\xf3\x15\xa31k(\x1dO1\xffF\xa9N\xb2\x86/\xd52\xc5\x00\x83\xde+l\xf6E\x00Ss\x9a\x80\x80\xf4\x04_\xa8\xa4\x1a*3\t\xea\xd9- D\xf8\xe6[\xb9H\f\xfe\n|$\x17>\xf3\x00h\xe8\xec\x04^\xc1\xe7\xf4z\x04N\n\xa3R\x02 \xf1%\x82 x\xbcN\x1f\x1a!\x06\xe5+ސ\xdf\x18_SR\x12\x9c\xe0\x1c\x9dC\xf0\x82Ap\xf3\xc94X\xa7e\x19\x9a\xe6tf\x87\x89+X\x1aO\\7\xae\xf4\x8a뮋/F\x13\xb2ä\xe3hZ\xc2,\xd5\f\xab\xf3\x16\x8c:o\xc6\xe7EJ\xc1\xc1\xe2R\xec\x01b[\xc5\f\xc8\xc5hX`l\xc0\x16\xe6\xa3 \xea@\xff\xe26\xad\x01)\xec\x9f\xcb?\x92\xedl\x85lG\xfa\xb8\xe3z\xb0\x00\x00\xb00=\x1b,\x90E\xf9\xc7l\x15\x98#;\xe4\a\xc1B\xf0\x89\xfccY\xa4[\xe47\xe4?\x836\xf9\xa3s\xe4\xdf\x13>\xf6\xe09=\xa0\x10\xb3\xa5\xc9\x1f1\xbf\x95\xff,\xbf\t\x04\xf9\x9f\xf2?䟃\"z\x8f\xfcs\xf9\x9f`<\x12\xde\xf5h\\\xfa\x8a\xf8\x98\xe8\xd1Ȥ\x94\a\xe3?\a,\xe8/\x18gyLI\x8a\xffh\xc0k\xb1\xe7\x1b\xab\x1d\xbc\xbb\x9f\xbd\xb3\u007fh\x8e\x8f6\xf9ҋ\xda\xe1;\xed\xe9\xff^\v\u05ee}\x0f|\x90\x94\x03\xe9Gio\x0f\x18H'a\xb2\xe2\x8e\xfbn\x87\xaeC\xf2\xb1\xeb\xe0\x93\xbbҧvѻ\xd2\x17\xf7\xc0KN\xdeu\xe4\xc8\x18\xbe\x17\xb3\xa8u9/\x97\f\x18m\x06\xe7\xb6\xc4\x1fBr\x11\x96\x8eh\xbb\x95S\xfa\x80\x87\x8e\xd5ڱ\xf4\x04\xe2\xadt\x88\xa0\xd8b9\x82\xa6\xccy\xe3\x9c97\xcce\xdc4\xbc\x1f\xcbO\u007f\xfc1\x98\n\xe6ĺb\xb1.y\x8ap\xe5\xd4\v\xe7\x17\xd5vY\xf5&\x16\xb7\x1ck\xd2[\xbbj\x8b\xe6_8\xf5\xcaӟ\x82籺\x8f\xde\\$\xc7\x16\xbd\xf9\x91\x8e%i\xf02NC;q\xe8\x00\xf7*O\xf9\x98<$\x96\xfc\x9e\xb7\x1d~J>o\xf4\xfdIz\xd8wm\"8\x1f#\xfde\"\xd9\xc8\xd7L\xb4\n\xa1:R\xbe4p\xebE\x8f\\t\xd1#\xf0\x11\xb2\xc9\xf0\x18)_\xe0\xd0\x03\xf8\x98\xfa/\xff9\x10\xcd^\x98\a\\\xf2\xb1\x11-\x88\xc4}\xc3\\\xb5\xa8_\xcb\xe7\xc1\xd8r9*G\x97\xf7B\x1d\x18\x1c\x89\x94pH~}\x00>\x96\x9e\xd1\x0fjƊO\xeef/a\xefA\xfa\x04\x8e\xael\xc7}\x01ع0\x8e3\x8a\xa1wW\x85\xc9r\xd1KDoSB﹄E\xbd\x01;N#iQ\"\xf1\x10H\x86\xa4\xd1\xfc\xd5\x06\x90\xb8\xe3\x01\x9c\xc4\x11܅ :\xcc\xe03\x983#^\xc2b\xdf\x0f\xbaZ\xb3=\x1a.*\f\x95t\xc67\n/\xael\x9bN3\xd7/]\xb2\xf3#\xebԊ\x1a\xf9\x03\xf9\xb3\xf2\xaa\x84\xe8Y\x1ao\xfe\xe8\xfd\xb6\xe8\xd2\x05\x1a\x93\xb1\xa2d\xc1\x1b/\xac\xab\x9a2'a-\xf0r\xe2\x1fa|\xc0ƙ\x9fp\xcdg+\xca}C\xf2\xad\xdf\x1c2ٌ,\x0f\xb5\x01\x9bKK\x17\xf9\xebK<\xbb\x8f\x83]`\xdcm\xcdf\x00\xefk\xeb\xf2Z\xe6̱\x88\x86&ˆ-\x15\x85\x17NZ\x92\xd4hn\x86;\xdd\x01\xad\xa6\xba\x86\xd7\xf9]\x85\x01-_T\xa8\xd1\x04\x86Dך\xf6N\xeb\xf8jڢ\xb1\xfa\xa3\x81\x9e\xe7\xcd\xda\x1bn\xe0\xfc\xf5\xf4\xd3\xf7\xcbNO]\xa1eOȽ\xc9P4\xce]\xa7\xad}i\xd7CS]\x95\x1e\x8fI_%\x06\x17VuY[\t\x0e\xac\xf2\xae4d\xb4oD:9a\xb7\x0e\x11*\xe2X\x9c\x84\xb3\x93P}\t\xb7\x0f\x1e3\xb1\xf2\x81FU\xa9.\x16\n\xa3\x8f\xc6\x04\b\x87!n\xd8\x18\xe6S`9^ik\x0f\x8d\x8e3XW\x11G\t\x86\xddsJ\xcaAyx\xde4͢}}4\x8cWN\xbe\xf6Ik{\xb8\xe2\xb6\a+B\xed6c\x95\xdf\xf3\xe2[\xbe\x92\xdaz=k\xbaK\xee\xbd\xdb\xc0\xbaL\xd5w|\xfb\x98\xdfc\xba\\k)\xdf\xf4[\xf9\x1f\xfb\x96\x87\xca#\x8c\xc6^\xc2\x01\r'\x1a\xd7?\x06\xe8'\x9c\xc5\xc5\xccxP:̚wky\x95ݺ^t\xc4Z&\x9egX\xda^\xb3\xc8Z<\a4\xda\\\x1ck\xb5r|\x81Ur\xf2H\xb1`\xf9\x824͇\v\x98\xbe>\xcepk\xfdlw\xd5*iB\x1f\xfcU\xd4\x1e\xf7\xb5\xb9\r~\x93u\xbc\xa7㪗K\xd8:\xab_\xdfm-\\b\xb4\x86l@\x0fjG\xccC\x80\xea\xc01`\xa8Y\xfd\xd8\x1e\x88\x87\x95*\x1aIbQԟ\b\u00a0\xcf\xe6\xb3X=\xa8\x05\xe9G\xba\x1d\x8f,\xee=\xb6i\xa6\uf069[:\xc6[Y\xc03\xff\rfȏ\x1a\xbd\xed\xe3g\xbe\xf1Y\xa0\x15\xc0\xfa\xa5\x17\\\xd0\b\xbd\xef\xba\x16.۸\xb0\x92\xe5\xe5EC铞\xba\xa8\a\xc0|;\xbf\xc2 \x1bFS[\x15\x8cZ|Q\xecЁ\x06>\x1e\t\x84\xf8Y\xad`\x94-tSkESI]\x81\x0e\x80S\xd4q\r`\v\xa2k:\xf6\x96/\xbcmդ\xcb\xc1\xdd\xf9\xed7\xfd);p\x94\x8es\x80k~\x01&\xeb*\x16\xf4.(\xb8O^ް\xado\x02\x04\xe3\x99\xea\xe1\xb6P\xfaT\x02\xa6Q\xdd1j\x8f}l\x95\x1e~e6\xcaw댂N\xbeè\xd1ZU\xbc@\xa4\xb4\x99\xe5\xa4N\a\x92fIb\x88\xcdb0\xe3SB\xc14\x9b\xc2\xf7T\xfdV\xb2\xb0\xc9q5H\f\xa6\xb3\xf7\xb1\x99\x8d`9\xbe;Xed$i\x908p3\x03!3@7\x97\x93f\xe5\x1d%\x00\xcdSt\x9a\xdc3\x83\x82\x9f\xc1\xc0w(\xe0\x19<\x85K0\xa2Pp`\xf83V\n\xa4\x06\x8a\x1f2\xa0\xb9\x94zO\xc5.=\x1c=\x1f\xb3\xea\xa4p\x11F\x94\n^\x8a\x9a\xe2NA\x93_\x05\xd4@Y\u007f\xfcͨ=C$\xeaQU簰\x1e\xf0\xd30\xaaJ\xdbXf'\x1a\x9f\xc2\x11\n2ܪ\nѝ\xc3jg7\x87\xe7]\x92\xacY\xb2`B\xcb\xecّ\x9bo\xbc~\xf3\xe6\xa3S\xd7\xf7\xfa+W\xae\x9d\xb2cy]ݬ\xc0\x84\x03\xf2\x87E\x9e\xb6X,\xd8NO\x9f\xf6\b\xa0\xd1\f3a\xf7\xee\xe7\xbd^\x9f\x1f\xed\xb0\xff\xfc\xe8\xd0A\x8f\xc7\xef\x9fP\x92h\x8f,\xdf|ы\xccΖ\xe9\xd3\xdbb\xa2\x9e\xbb\xf1\x9c\r\xe3h3\xcd\x18\xb2\xfe\xfc\x04\x8b\\\x91\x0e(`\tZ\b\x9b\x93\xba\x85?J/\xc0\u007f\\rh;v\xed\x82bz\xfbrX\t\xff+}.\x8c\xa6w\f}\xbe\x1b\xdeH\x9f7\xf41\xbc\x03\xbbu+\xb8\xb3\xec\x1e2\xdf\x17\"It\x06ҁ(\xaa6F\xe6'Fݲ\xca,\xa6tn\x05ʒ\x04T\xb6`u\x97,.\x84\x89\x8d\x10\aZb\xefz\xec\xc9Z\x8c\xdd\x18p\xa08O\xbe\f\xf5è\xb5\x83\x0f\xbc\x0e\x87\xd7\x0e\x8e{\xedv\xafch\xb0\xac\xb9iAs33+Q9\xbdyA\xf3\x81\xe6\xf2\xb2f0\xad*\x01\u007f\xbc!9\xb4*y\xce\x14\xde`䧮x{\xc5T\xdeh\xe0\xc1a|\xbe\xb9\xac\xbc\x99)r\xe0\xfb(\xff\xdeh.\x93\xe7\x9477\x97\x83\x1f\x975K\xe9\xb5U\x89?\xe3\xbd?+\xbf\x89*x+\xb81\xfe\xc2\xf6\xed/\xc4/5\xf2\x9ca_Y\xd9>\x03\xc7\x1b\xd37f\xae*ojB\xf3(\x96\xbb\xbe%\x9c\x1b&\xca\x0f4H\x15\b\x82\b\xe8\x04\xff x*\x01L\xe9T\xeb\xe0B<\xaa\x14\b\xe1q\x87\xe7\xf0\xf8\xddJ7\x83\x10\x12\xe0\xb1\xc0\xa3\xc8;x\x99\x04\x9dĒ\x0f\x99\xe9B1u9\x05\x0f\xf2hԏ\xc7\xea\xa2\xe80\xe7\xb0\x06\xaaP7\xc6\xc4\xf4\x1c\xe6@\u009a!O\x02\xa0\x1c\xb5v\x8e\x04\xaf\x92)\x96\xc6c?\x8d\xa7\x04\xa0p\x9c\xa0Y\"\xa4\xcc\bh\xfa\xc4\xc1#\x02^\xb1\xc1ކV\x01\x92!ю\xb3\xe0\xf7@JI\xbc+\xc9\xd5\x1eh\x8b\xa1\x19\x06\rX\xe8j\x12ȏ3\x10\x03n\f\xcfA\x91V\xa4p\xe0\x02\xd9\xec\x8eZ\x9eC\xba/\xae\x12\xa3LU\xe1:4\xe7\xfbq\xd2aE\x17\xd7aa. `\xb1\x1fM\xfb\xf8\x0e\xb51\xe0\x81\xb88\x80@\xb3\xd0\x04\xbc\b\r\x93a\xa5)\xf0\x03p#`\xe9\x10DI\x11q\xe1hފ\x1a\x92\x94\x10\xaf\xbb\x91ո\x10>I\xd6\xe1P\xbd\xe3\xca\xfc\x18!`6\xbc\x9a\xd7N$Or[\xd4F\xb8Y\xd5\x1b\xab-\xeda\xe1Mz-\xc3J\xecRƤsjh\xf96\xa4\x05\xd04\xaf\xd32\x16\x06@\b =?\xce\xf04\ry\xa0\x05\xbai\x01\xa7o\xa1O\x1f.6\x01\xbd\xd6&\x1a\x8d@\xf0\x17\xd8\x19ƪ\x0f\x9b\x9a8\rg/\b\x16\xea\xf4\"\x92*,\x05v\xf3\x06\x11h\xc7\x15\xd0\xc0_\xe8.\x82@k\xe1u\x1c\xa3\xe7-\x00X\x9d\x16+\x00v\xad&\f\x8c\xacN\xb0\xeb\xdc\xf6\xea8,s{Y\xad\x9e\xa5\xb5\x06k\xa7\xb6\xc2U\x10Cӂ\xb9\xa0\xcc\x12\xf2\xfb\xdcv#\x84\x1c\xa7\xe7\x8dtᬘ\xddVf\xa7\x81\xa7\xc8(:fi \xe046/\x039\x86\x85\xb0\xa4\x8a-e\xac\x0fh\xcdt\xb1GS&T\x85\x19#\ah\xab\xae\xea\x82\xcb*\x1cz\x03D\xcf\xe4l\xb4\x03B\v\xb4\x9bJ@\xfb\xcc\xf4]\xb4\x9e\xd3BZG\xd3z\x1a\xdc\x03\xb5\x16\x8eղ\x1c\xa4\x852Q\xab\u007f\\g\xa09\x86\xa1\x05F\x03c\xac\x916i\xb5,\r\x81\x0e2\x8cF\xd0\x00\xb3\x00\xe3V;䝎\xa0+\xa4\t\xad(\xb4\xac\r\x89\x0e\x9d\xdfS\xb1@\xea\xb2VL)\x89\x14\x16ݛ\x90\x12%\xe5NV\xe7\a\x00\r\xe1:a\x81\xc5\xe3\xb4E\xbd\x11\xbf\xd6(B\x03\xcb\x00?M\xfb\xad\x97\x04\x9c\xab'8\xca\xcbiѪ\xbbp|G\xa5\x9eA\x83\x9f\xe8\xe15A{\xc8z\x9e```]wxB\xb4\xaf\xa4a\x12\x8b\xe4\x84U\xf1\xc5&$n\xe8unw\xcc/\xbaE\xad\x00\xed!\xd1l\x95t\xf5g\x956\xb5tF\xc7\xeb\xc3^\x9f\x8f\x16\x80`r\x99\xdd\xcc\x1a \x01\u0380vM\xb4\xde\xc8\xc9s\x80\xc6²\x1a=j_\x1d\xad\xc1/\x1cʷ\x8aNS\x81\xdb\\\xa4\xf3\xf3\xe5\xec\xf8\xf3\xacֶ\xbb\xb7\x95B\xa6rgU\xb8\xb9X4\x80\xd69\x9e\x12\xbbm\x82_C{\x00\xa8\xad\x03\xf4\xc4\x02\xc9\xc43\t\xd6Sj\xd3Қ=&\xa4@\xf2\r\x13\x01h(6U\x14CZ\xaf\x05E\x92\xdd\x03\xcaJ\x18\x93`p\x00\xc1\xc5j\x1c&=\x80\x16`\xd0Z\xb4\x02\x87JBsŌ\xc4 \t\x94aL\x0e\x00\ffɤe\xb4\x90e\x19\x8e\xe6\x81\xd0\xec2\xe8[\x8b\xb54_\xd06\xbe\xa3\x88{\xa0A\\\xabqڊ\xdb\n\v%\x00\x98\tk\f^\xc6q\xb9\xd6TUJ\x9b\x9aj\xaa\x9c\x1d\x1a\xb3\x06\xb2Z\xbe\xcel\x9a\x1a\xd2pU\x05\xedHݖ\xb6ym\xeb\x17\xbbĠWO\x97Y\\\x10jY`\xb2\xfeB\xc3\xd3\f\xad\xe3x\x00\xcdq\x06\x88\x03z\x8b\x060\f`\xdc4\v?\x85\x9c\x06\x9a\x80\xd1\xc81F\x96\xa3Q\xbb\x01\xe6\xe4K\x86\x02\x87\xddn\xb1\x1aEF\x9a\xe66\xf3\xa2\xb6Ȏz2zK\x85\xde\x02\x00\x9a\x8d\xa8g\x1b,z\xc7B\xbdy|\xb0Dk`t\xa2\xdf\xdf鳲\xb4\xd1T\xc69\rv\xbd\xa9C\xb0h\xb9\x02\r\xe7\x15h\xae\xa2nB\xd8\xf2Ӻi~\xad\xd3l/\xc2t\xdekc\x1d\xd6k\xeb6\xbdx֮r\x1b(r\x97\x1d\xe9X\xb1c\xf3\xfa\xa67\x17\xd6L)\x85\xd0\x1fD\xad\xae\x91\fElP\x98\x17\x9f\xbc{\xc2\x14\xd6W\x13(@\xd5*\xd0\xeb\xa7M1\x14G\xbf3Q\x19_\\Uܶ\xb1\xb3eIsP(\xb1\v\xd6\xd2p\xc4[Y\xe9m\xae\\zip\xf2\xf6\x83G>\xec6o|\x0e\xb0\xd7v\xce\xe8ݫ\xecȃx\x87\xe8\xe7\x15Hwx\x85IJ\xb4Q\x1d$\xe2*c\x0f\x89\x13|\xf2ZBS\x1cʳr\xc6\xe2\x9c\x0e\xbb\x94\x10\a_\x80\t\xed\xb2\xf0\xa9t\xcc\x05\xe8\xbf\x06\xd9B[\xba\xdeQ́\x80\xc3\xe3\xfb\xc2\ue85dF\xa6\xd8&\xff\x0e\xafF\x83\xb3D\xffǦ\x19\xad\f\xc7\xd9ݵ>\xf9\x1fF\xadF^n\xef4Ļ\xe6\xd0\x17\xacH\xd8\xefdZg03\u007f\xe1\xf0\xfb\xad\x83\x8f\xa1\a\xf4\xb8LE\xa6\xbd-6tmYQ\xd0\xfdy\xa7\xbc[\xfe\x95\xc5n\xab\xb0[uZ\xd9]\xc0k\xed]\xec\xde\xf8\x8a\xbe\xbe\xa1O-\xa0\x01\\J\x8dXwP4\x95Q\x9e\x9ag\xc08\xc5vi\"3\x83\x01\xd5b\x9b\xdd\xeb\x0f\xb9N\x12\x93\f\x8b~S\f\xb1\xf7\x0eQ\x84\x98\x1c\x12K.\xb1\xe7\xd2B.S(\x13W\x86\xfd\xaf\x06\b\xff\xa3b\x85\n\xd3\x01\x9bd'~L\xc3\xc8Y\xea\xe2R4@\xablm$\xf6\x1b\xc9\xf2\x99\x98\x1f\x96\xaa/\x8d\x14\xfd\xb9\xf2kmȕ\x9aX\xd5_51\xe5\ni\xbf\xae\xfcsQ\xa4\xb4\xde\f\xa8\xceu \xb9\xae\x13Pf\xb9\xe7\xd2\xff\xb8\xf4\xd2\xff\x00\x03\xa5\xf5\xe5`\xfe>y\x8dIt\x85\xe4/\xab&N\xac\x02\xe6\x90K4\x81\xdb\xf6\xc9G\xcb\xebK\x8b\x9c \xb9a\x83\x9ct\xd2=\xf8\x82K\x95\xb22\xb8\xacA≫\n\xbb\x81\xd3l\x956\xcb\xe2\xb3Q\xf5\xdd\xf5\x89\x89K'\x92?\x94\xde\xd4\r\x93ݛ\xe4\x01R\x1a:!+\vt\xcf³\xeaBGw\x9c\x90\xea$Iz\x815\x8f\xf3\xba08\x9c;\x1c6\no\xd8\xccb\xd4\xfa\xe7-\xfd\xb8b!\xe5B\xe56\xf2\xef uٳϢ\x0f\xfc\xd4)\n\xf0\xbb\x99)\xd4e\xc4g\x10\xaf\xa7\xe1\xa5K\xacY@\xa4\xea\xb1\\\b͍4\x1a\xf5\x1dVB\x81\x81\x97}\xf0\x11\xa4f\x11\xe0\x1c$\xb2\xe09\x12\xffz\xe8\xdax+C\xb0#\x88\u0085{\n\xd2i\xac\x04\x0f\x86\xac\x8a\xe3\xd5<\xc5\xfe\x01\x1dA\xa4\xc3\xf0\xbb\x1dǜ\xe3J=żT\xe5g\xc0յ4\xcfk\xcaB\xa7(g\xc2j\xf5t7Lp\xd2:\xa7d\x02<È\x81\xadS\x0eo^\xe6,\xd0\x05\xce齺\x99\xa3\x19S\x19\x10\rv\x965k\xacu&sQ\xac\xbc\xb4\xd0\b9Q\xabc\xa1\xc0s\x05\xcdF\xd1l\x8f\xfeǜ\xa8\xd5-\xf0\x10\t\xf4\x9cEЈ\xfe\xb2\xd6`s5\x83Dr\xc8Yu\xc0\x1b\xae\xe5\xe8o\x12\x1f{\xa3\x91\xb2\x06w\x19\x12i\xe1\xa5g\xb1\xa6\x90\xa7\x80a\xad\x06\x83m\xc1\xa4j\r`\x9d\x81I\xe5\xa6\x02\x8e\x95hf܄v\xa7SWzM?\xe0\xae6\xdbYNB\xb2&C\xebm\xb5\x1b\n\x8b\x9a\x17\xd5\x14\xb2@S\xd2\xd8\xdbY:\xd1h\xf0k\xa1]һ 0\xb0\x96b_c\xdd\u243e\xd5_]\xac\x85\x8c\xab|Ik\xef\x85:\x13\x06\x1f\xa1\x01dMZ\xc2\x15\xfc#\xeekv:\xa5##^55\x9fZO]\x8c\xbeƬN\x8cgc\x92D\xfa\xa7#\x83\xf7\x89\x1a5X\x05Jx\x8e\xc1\x1fb[\xa2ZB\x0fP\x01CcH\xbbTT\xca 9F\x0e\xa1\x13Xe\xc7*:\xbc\a\x9b~g\xda\xecb\xc7\xecm\x1a\xadQ(\xe2-\x1e\xc1\xf3D\xe5\x9f6n\x98]]}\xa2o\xe3\n\xa4#\xf6˧\x0e\xfdQ\xfe\xbd\xa0\xed\a\xe0\xd0\x1fA\x10\x84\xa6\x1d\xfc\xb9\x9c\x96?\x96\xff\xfb\x9d\xbdW&\x1f\x04\x8b\xa7M\xa8d8\xc1\xc4qW\xfe\xa6\xaa\xb2\x12\xb2\x82\xceа\xb4cۼ\x02IS\xee@\x05\xb3.js\x961\xac\xcb\xd9\f\xe6/\x8c\x84\xb5\xb51\x97\xa6\xb0\xa4\xb5\xf5\xa1\x85\x85\xe3\rŅ\xbb\xfe9\xe4\x9fl\x12\\>\xff$\xaf\xfb6\xa3\x9be\xf5\xc6b\x81\xd5/_\xdbS\xe2\u007ffŲ\xa5\xee\xa2'\x9a{n\x98,8>;\xa4l\xae\xe9\xb8\xf6\xd2\xde\xd6\xf6\x1dO\x9d\xb3\x150\xc9\a\u007f0-q\x9d`@\xbd\x006\xb5\xb4m5\nzԡ\x1a\xd7\xc3\x15\xcbwգ\xa7\xa32\xb4\xf5\x18\xd1ӝ\xe3X㬞\xf4V\xb7K\xacu\xcfy\xbccRT\xe4\x8a\xeb\xab9\xd7\xf4|\xd9b\v\xa5\xa5$\xcc\x17O\xf8m\x91\xa6\xed\xc1k\x9e\x90Ǥ\xcc%\xc0̣\x81\xd2bgD\xe6ܣ/<\u007f\xf4\xc0/\xfd\x81_ʷ\xa5_}\xe2~P\xc2D\x9fx5\xfd\x18(\xb9\u07ff|\xf9\xc2o\x0e\x1e\xfc\x86m\x91\xddC\xf2٫\xde\x05\xceg\xc1\xa4ߤ\xcb俾\xbb\n\x1c\x19\x02\u007f\xf1\xfcF~VY\xebC\xb2\xc3N$\xa7m\xc0k/4\x16U9\x8a'(\x1fh,\x16 6\x1f\x00\xf4q\xc5p\x9a\xc5i\xb6\x18ĢU,\xd2\xfb\x19\x01i8hx\xc2k#\x02\xfe\x909\x9cdwz\x17-\xef]\xb5|V\xb3ٲY>\xf2\xa6\xe4rI\xc7@\xf9ڒ\xa9\xcb\x17\xad\\0\u05f7\xe5\xa5˷\xb4\x15D]\xbc}JNJ9\v\x12\x95\xdc\xe4\x8bW.h\x89\xf8\xec,cи\xa7\xd4\xd7\t\xa1H\xe7\xb9\xcd%,g\x155\x92\xbf\x05\xaeP\x01x\xe7\xb8 j\x8c\x15\xd3\xf6̭\xb6\x06fvU\\\xda\x0fhH[\x8a\xea\xa7m\x9d\\h\x91\xc65\xb5\xb5\u0558\xcc;;9\xeb\xa4i\x9b6_\xddQ\xd0\xd9}֢\xb9\x93c&\x13\xb3\xd4\xc5;ڢ\x8d\xc5\xd01\xf3\xe29-\x1e\x11}>\xf4\xf5W\xf0\x8e\xa6\xaa\x10\xacAb\x8b\r\xc9.\u007fc)\xe2In%\xf1UD\xc2\x02\x8a\xcf>\xb0\xf9,\xf8/h\xcb021\u007f\xdb:\xbbA\x1eJ\u007f1{+\xf3\x9b\xc1\xb2\xcc\xdf\xd6\xd9\xf4\xcc\xd9[\x81{\xe2\xfc\x1d\xf2\xbf\x80q\xc7\xfc\x89`\xf2)\xea\x14\x98\x8a~\xaejo\x9f\xb7cG\x9e\x9c\x89\x11\xcaj\xd4\xf8\xa01iL\xed\xa7\t\xeeb\x92*\x91i\x86dS!2}\xf0\xbb\x02\xbd\xe05c\xf0\x99\x1e\xfd\xae\x80\xafa2\xb1Z\xd6\xe1l\xac\xf9d\xac\xe2iˊ9Kq\x01sT\xac\x98ɴ\xff;\v;\xa0\x16\x11\xb4afT\x85\x91U>\xf5\x9d\xa5\x1d%\xbf+k\xa6\xb9b\x8e\x94\xdfGY\x1b\xa8\x90\xcbjQ\xc2\xce,V\x1cC\xfb\x1d\x01j)\xec[eP\xc3\xc6\f\xa1\xa1\x17\xbfG<\x18\x8f\xbe\xfd\xe2\\\x1c\xbex\x1a\xc6\x00՞[\xf6]\xbc\x01jt=\xf0~'}\x80\xea\xe3\xbe\f\xc9\xe46*\x86#A\x898\x86\xa5\xb1\xb8\x03ϭT\x04\v\xa5\x0e2\x1a\xd1\n\x90X\x9c\x90\x8bbK\x83\xe4\xb3\xf9p\xb4\x97D\x9fZ\xdb(\xbf\xf9\xec\xed\xf2\u05f7\x9d\xf8\x91e\xe7!\xc0?\xb3\xe7\x9d\xed\xd0\xddx\x8a2\x9aK-_ȥ\xce \xdd\x035\u0082\xd8\xc4\xe5\xbd\x1dAp\xbf\xbc\xde\f~Uj\xf9\b,{\xf5\xb1?\xdc\x06\xb4\xb7?\x01\xcaZ/\x8d\xfd\xf1\xb2g\xe4o\xf7~\xe0ڒ\xe4\x03\xe0\x03\x9f\x93\xd6[\n\"m\xcb'N:\x9b\x97\xff\x98L\x06\xe4\x86a:\xb6\xc2\xeb\x13\v\x87h\xf4\xfax\xec>\xa9,j\xe2\xa5Q\x87\x12\x9b\x85\xed\n\x928\xca;Ѡ;\xfa_\xb3+B\xf3\xf5\xccU\x81\xf2\xb0\xd1\xeb\xd9۴\xde}\x8e\xbb\xaeK\xdfPkj6u\xf4\xdc\xf1\xa7\xf7O\x0e{\x9f{\u007f\xcbi\xe4\u007fI=\r\xef?\x18\xfb\xf5s\x06~\x99\xb3\xc7\xd9^\xf7X\xfc\xf7\xf1\xc7@\b\xb8\xc1\xc5\xc3,h*\x9e\x03*#\xd6\u007f\xad\x90Q\u0530\xac\x83Q\x1b\x88\xe6\xa73aXHp)Br\x1fk\xcb$,1J=\xc9$\x9f\x93\x8f\xff\xac_\x10ߥ9\x9d\xd6\xe8\xf8$\xb3\x15\x05t\x10\xec0\xb9\x1c\xf2\x0eus\x1c0\xe4(L\xfdL>\xfe\x9c(\xc0U\x13\x01\xa73'\x1d\x9a)˲\xa9\x93X\xab|b\x1bk\xc5{\x17.\xcb$\xe4\x02#\xb0\xfe\x14{\xfa\xe6b\xbf\x03*\xfa\xb4M\r3R*\x93\x1d\x8c\xb2V\xb1\xef\x19\x0f.+\xac\x96r?Q~{\x94\x18\x94\x9e3F\x87\x8f\xcaO\xee\xf4]\xd1\xe2j\\\xac\x06\xe3x\x86\t\xe3_\xb7b}\x8b*Jp\x15Ph\x8d\xb1\x8f=\xea->\"\xc1\x10WT\xc0\xc7\xd1\xe4\xe6\xc0\x91\xbe>\x8e\xf7#\xf1\x14\b\xa0\x1cБZ: a\x88^\xe0a\"\xac/\x04\xcf9\xef\xce$\xfa\xa4\xf9\xc6\x193\x1ayɘH\xdey\x1e\xb3\xb8\xec\x12\xf3❕\x95;\x17\x9b/)\xe3\xa2\xd1\xd9\x1d\x1d\x83\xf3\xe9\xaf\xdf\xfb\xa2a\x93\xbbP\x1ep-\xae\xecYVt\xc7\x1dE\xcbz\xaa\x16\xb9\x80\x97\x11\xaak;K\xc0KC\xdam\xa0?\x91\xa8\xf69\v\xa0\xc5i\x81\x05N_u\"\xc1\xdbiS\xa4\xa2\xa4\"b\xa2\xed\xfcPɦ\x12\xcf\xf8\x1b\xc6˿\t\x95\x8dw:\xb1W(x\x13\f\x807\xb1\x87(c\xf4\x15غ\x13\xea\xf7\x81\xb1D\xe6\x10\xffd\xfc\xb1b\rQ\xb1\"!\xa52\x97\xcc\x104\xb4\x01:\x97\f\xabn\xacH\xc3\xcc%Ux;\xd4\x10R<\x06\x824\xcb~\xd1:w\xd9C\xf5\xfc\xbc\xa6\xea\x19\xa6\xb8\xfcr\\3\xaf\xb9\xba\xcb\x14\xbf\xa5\xc8\xd62;^q\xfb\xfa\xdb]\xf6\xe69\xf1\x8a;\xa2ʉ\x18\x88\xc54\xf3q\xe6\xe8\xdd6{\xf3\xfc\xe6\x8a;\xd6\xdf\xeb\x1c\x1a\x02\xb1\xf5\xf2\xcb\xf0\x9b\xd9-g\xfb\x9a\uedf9\x9a\x16\xc4*\xef\xeb\xbb\xd7\xe9\xc0\x89{\xa2\xda\xee\x16t\xed\xffG\xdbw\xc0GUe\xff\xbf{\xdf{\xf3\xa6\xb77\xbdf\xfa\xa4'3\x99\x99\xf4N\x80\x90\x84\x10zh\xa1w\t \x1dahb\x03\x15\xa5\xa8(Q\x11\x1bv,(\xbaY\xfbZP\x17\xb7\xe0ς\xbb\xb8\xbb\xb6\xb5\x17 s\xf9\xdf\xfb\xdeL\b\xc8\xfet?\xff\xff?0\xef\xdd\xfa\xca}\xb7\x9cs\xcf9\xdf\x13\x03eq\xe9\br\x95\xd8>\x8b\xb9rt<\xbfgn\x0f)\x92Ȼ=!\x19^\x91?T\x13G/\x96J\xd1鹠b\xfe\x85{5ق&\xd5\x05:\"@\x97vd_\x06Ү\xecC\xe9\x1e\x9b\xe9\xc0i\xbc\fI\xa4\x9aM\x04\xaa\xc1@%\x12\xfa\x84\xdb\xe3})6\xad\xbe~Z\xe1s\x85\xca\x1cyi\x98\xae\r\x97&\xb2\xfbzåU\x81\xc2\xc7C\xb4C\xed\xe0-F\x83\xd1\xc2\xe3\x10\r\x14\xbe\x9a\xf3uMΜ\x00\x87|\x06\xd3:\xff\xa0AY\xab\xb2\xa4A)j!\xce\x14ff\x97\x97\x86\x03íYKl\x90\x97\xe9dD\xe9\x05\x9fx\xf8\xa0y8\x95\x91%\n\xb6\a,\xee̓\xa9\x11\xd4Tj1E\xf1x\x05\vB\x01!\x93\x16D?A\x8d\xb8\xa7Ax'\xbe?\xc9\x1b\xf4\xc5\xfdą\xb7h≩}\x967\x99\x855\x10\u007f[\xc8\xf1\xf1X\t\x95\xc5\xe0\xa5\x1a\x12\x90\x9d ^n\xe2T\x96?\x8e\xe3A\xe2\x03\x04\xc7M\xeb\x1b\xc0\xa2\x17\xff\xcdJY\x8d\xd4δ\xa0\xcf\nrx5Ͽ9l\xbdR'\xa15\xca\xf6\x95\xf7\xa0\u007f\xa5Ӹ,\xf9\\0\xf2\xe5\x1b\x80b\xae<\xd1\xcc0J\x89\x1e\xf7\xe6\x1a$\xf9\x120\xeb6tϥ\xd7Ly\xfb\xa1\xcf+\xfa\xee\x00\v@\xcb\xd7۷\u007f\x8d\x0e\xa1\x1b\xd1!\x12\x02\xa3A'\xa8\xfa\xe4\x8a+>A/\xa0\x03\xe8\x05\x12\x82\xc9;w\xf5\xf1S\xc0\xa5@ʇ*\x1d\x9d\xaa\xb3\x14]N\xb3\xd0\xe3\x04r \x03J=\xaf\x06R\xf4\x14\x92ҵ\x99Ԟg\xe6u\x8dH(-\xbc]\xe3R\xfa\xd9\xf9\xc7R\xab$l^\x16\xd3\xf1\xe0\v\xef\xa0}\xb3\xe0\x81{\xe7\xe7\xc0\x92\xf3n\xdc\"<̩'\xaf\xf8\x04T]\xf0\f\x99\xb5Gh\u007f=\xd1\x17\x03:6\xe8'cğ0J\x18\xa3\x811\xeb\x00\x1fH\x04C1\xc6\xccT\xa3\xafO\xa2k\xfe\xfc\a0\xe9\xf8q\xf4)\x88}F?\x10H}wÊہ\xf1\r\xe2\xa24i؟\xdaq\xcdO\xfbm\a\x83'\xae\xdd\xf3\x0f\x17ێj\xd0\xea%#\x9b\x9c\a=k3:\xe6\x82\xdf)%\x15\xa4\x8a\b\xf2\x80ї\xee¾\x18\xf0袺\x01\xbfs\xd8ql&\x18\xa5{\xe9\xded\xb6\xe3\xb4ܑ\x9d\x04xMJf\xfeW8\xb2O\xe1\x8c\n\t\x0e\xfc\x8c\x03\x12\n%E\x04\x90\xb3T\n\xdf\xf8\xdcO\x84'%\x1e\xed\x92\x19_\xd1I\x99h\xbbN\xe8\x05\xde$t\x94\x04Ϛ\"\x89\xb4\xbf\xad\x00\xb1\xba5\x1a8>\xad\xe3\x89s\xc9d\x1bO\x042~\xbd$\xff\xd63\aџ\xd1~\xf4烌\x1eV\x9bJLL\xbb\xe9L\x0f\xa3dR\x97\xe6\x96Jj\xcaˡ\\\xa6\xe9\xd5\xc8䰼\xbcN1\x16=f21]8\x9b\xe9\x82GЋ\x83\x96\x0f\xc2\xffA\xe5\xe3\x1c\a\xb5\x05R\x84yã\xde[f\xfa\x87\x0e\n\xa2\xe1j\x05\xfeS\x83G\x82\x83\x86\x06\xdfZ3GZ \x05]\x00\xa0\x1e\xfc\xfe\v\xcf&\xd9\x1bD\x9d\x16\xc0\x13\x01\x05\x1f\xa4 \x91g`6Ko\xae\xa6\x13$XL\x00\xed\xe8\t\xaa\xea\xdc\xecZU\x18\x9d}xRy$\xaf\xa1f\xdb\xefs\x02\xd7w\xae,\x8c\xc7J\xcb\x1d\xb5\xbe6\xf9\x0eؐ\xaaR(\xe0\v\x83\xc0K |\xb5F\xb3\xe8K\xfcdU\x9f\xde\xf0\xe6X\xb5:4\xbd\xfcr\xdd\xcfi\x9f8\xec\xc7\xc2\x1aJ\x01\x0f\x19e\xe2~\x17\x1eY\xfeh\x96\x99\xc3O!\x10y\x98\u03a2\x13\x1e\x9a\x82\u007fR>\x81\x1ez\xefVt\xf2\xe8\xaaUG\x81\xe3V\x90\xf7\x97w\xd6<\xb9\xe1\u007f\x92\xc9\xff\xd90v\xc7\xe4&\x8f\x04\xb5\xc0\u007f7T\x1dG\xf7\xf7\x92\x02\xa0\x1c8\x8e\xae\xfa\xc3\x1fVl\xfc\b\xfd\xfc\xd1Ƣ!\x13;\x02\xa2^\x998O\x10\xbbW/\xd5&H#LD90((ѓ\xfd\xb5\x88?\r\xd8\x1ca\xd3\x14\xa7)A\x80m\x82!\xdeL<|\vئ\x98\x9e\xa2%\\\xda\xd0Ì\x0fL4⏕`n\xd0?`\x96\xc0\xb3\x83\x89\x19\xac\xd5V\xa3\u007fWk\xb5\x12\xbd\xa4h\xd5\xcab\x89\x1e\x1d+i\x8eŚ\xc1\xefb\xcd%8t\xa6i\x86\u007f\xe3\xe35\xaf\x92\xc4@\xdc\xf6\x01/\x19th\x83\xaf$\xd2\x14pK\x80套\x81\x85s\xf9\xc1\xac\x8b\x8cG\xb0X\xab\xa9\xae\xd6h%\x92\xe2bɻ\xf8b\xb8/u\x06\xc85K:\x8a\x9b\xfc\x9d\x12`\xcf\x0f\x94Ěc\x91b\u0588^\xe5:\x03Mžr\x8dݹ\xfd\xb5\u05f6gY5e\xcf\\pA\x1c:\x1f\aK#xs\"\xf3\xa9\xd0N\xdet;\x91f\nd\x9a\xc9\xc4\xf6\x87\x12B\xe3\x04C\t3\xf9v\xff\xa1\xa9Dk\xfb\xd0/\xf5\x96\xe8\xfbU\xaa\xd8\x171\x95\x8aղ9GsX-B\x05\xd5\x05\xf9\xb5\xf9\xa0C<\xff\xa527ǽ\xf8\xe6\xf8\xfd \xdfM\xe4.E\xc6guL\xe5͋\\y\xb9\x95Y6\xf6\xeb{\xef\xfbZbu\x83\xe8y\xf8\x13\xbb\xf1E\xf15%\x92\x9c\x1c\xc9.wA\x81P3}\x1e\x9cS\xe9nc\xbe\ve\xe5\xe1\xab\xe7\xe6\xb0z\xf4\xbd\xa45\xab2\xc7\x15QYͫ\x1fx`\xb5բ*\x06'/Η\xb8\xf0\xecC\x10\x99\x13i\xf0\xb1~\xb5\x14\xe1\x05E\x95\x13'`ciE\x95*\xc0\x85\x8c\xa4\x03\x9d\xa7&9\xad\xf3ҞK\x1dA\xfb\xee\xa5\x1d#\x96\xda\r\xbc\x1d\\\xb9\x8b\x9c:+/\xbdc)\x18q!\xffr\xd8^=\xbc{\xd1p\xf4\x89\xc1n7\xac\\ݱdq;\xc0\x8b\xa9\x83\x8f\u007f\xb4z\x9d\xc1\xee\xe0\xd7\xd8\x1ckڗ,\x01\x0f\\\xc8Ր9\xeaN.\xc9N\x12\x9e[\xc0E\x12\x1fZ4\xb1\xefw^/<4\xc7z29愘\xc5\x04+GU\xf6=\xf2\xe8\x190\x04\aR\x0f=\xdc\xf7\x02\xb8\x16\f9\xf3\xe8#}\x9b^\xc0)t\xe9r\xa2\x1e\x93\xda\xfb\xd0\xcfg\x1e\x05rt:\xb7\xa2\"\x17.\xb8\xff\xdb\xef\x0f^Q~;\xfa\xf1\xd13\xa7\x1e\x06ʪr\xf4mNEE\xce@~\x85\xe0}P\x01\xe2f\\t\x8fz\x11\xfa\x98\xedMբ\xacI\x9b`/81iS\xed\xc0\xef\xdb\x03N\xc0\xdeM\x93PV\xaav\x13\xe3<_aO\x8a\u007f6)\xc5\xfc\x03\xf7h\x19\xbe\x8fN\xb0v\x0f\b\xb89dy\xf0X\x81O\a\xf0JA\x1b\xa31\x9e`S\xe0\u007f\x01\x1dN\x1b\x18\x1e\xf2F\xea\v0d\r\xb8\xe9\xcd7\xdf\xec\x80\xc6\xd4\xe7`\bz\x8a$\xdc\f\r8g0:\f\x06\xafa\xfeї\r\x0f\xe3\xbc\xc5\xe8Z\\f0<\f\\o\xbc\x81\xfe\xd6\xd7qg\xc7~1\xb1?8`|\xc9\x04l\xd4\"\xe2[\x88\x12\xd8nb\xf31 \xa4M\x03ys\xba\x04\xb1\xed\x80B\xdc'F~\xc1\x88\x9bZb\xb9vGN\f\xfd\x90\x0e\xc0u\x0f_f\xe0͉\xb1k\x8fE\xeb/\xbb\xfb\x91˚\x1b\x9e>\x96\xa8\xba\x8c6\x9f\xa7D٘\xec\xd4\x00\xa3\x0e\x8cHN \xe7T1P>G\xb7\x95O\x91\xa46g\x1f\xe5\xe1\\\x1c\xf5\xf7=\x85\x83\xe0\xe7\xf3\xdbWNe\x9f\x95qo\xe2\xf9t#u\x84z\x8d:J\xbdO\xfd\x9d\xfa'\xf5)\xf5%\xf5\x15\xe1A]4Q\xd0WC\xae\x80\xf5\x11MR\x17\xe7\x06&\x1c\r\x8a\x06$%\x89j\x88\xa7\a¢\n\x9a7\x8cHl\x93%\x11\xcf\xfb\x02Gm\xceP\xd8P\x92F\xe9 \x82\x92\x10\x99@\x04\x1b9sBM\x9b\x13\x05\\\xa8\x00\xe6\x10\xd7+\x98,u\xc1\x1a`4c\xe2NZ#\xea,\x11\x85U̥\xd1\xe4\x82\xf8\x89\x04\xca.a\xe6\x80\bL\x1d\xaa\x86Q<4I&\x1fũ1\xa3\x06\xd4@\xe6\xe5aWN\x9f]\x97\xeb\x99P9\xa8h\xd5^\u007f^\xa5=T0}\xa8\\\xc2\xc8$y\x9c\x9b\xd5\xd3\x12\x00\x00'\xd5Ѿ\xcdY!\x0f\xa4aE\x02\x8fD\xff\xee*\xeb\xccn\x87Ĉ\\n\xadE\xa7\x06\xff\x90*\x8c\xbc\x9de\xcc\x12\x8d\x8d\xbbS\xa6\xb3\xea4O\x00p\x97\xa9\xf0\xba\xc2D\xa1\xbc1\x97\xed\xa8\xceK\xe4\x18\x8cr\x8b2B\x87\xf3}\xa0\x8a\xd5qj\x89\x9c\x931\x9cƦ/T\xaf\x9b\xa0\r7\xd68\aK\x95YY&\xa5駵\x8e\xbcl\xabW\xedS\xe4J9\x98=\xbc\uf43a4OG\xe7\xfe\x14:\x1c\x97ٝf+\\\xb5\xa6\xaa\x16\x9d*Z8\x14\xdcN\xfbʢ\xa5\fg\x1c^\xe7@\x83\xba$\xf2|%\u007f\xcc-ϦW\x01H\xfeM\xa1\v\x9bVL\x1dR:/Q\xe5J\xd4h\x03{\x1f8\xb2s*dX\x19\x1b\xe0\x9cJ\x975`\xf2\xd8j\xb2[p\x9f\x90k\xdd\xcd&UY\x95\x11\xdab\x93\xd6\xddd`l\xdd&\xad\xc6L\xcfS\x9bTr\x86\x85@\x95\xa5\v\x98t\x1a\x13\x1d\xd6ڞ\xec)\xf6{i\x83E\xab\xe7\xf3\x86ڲ\xb4\xb4Z\xe5w\xd7:\xac\xe10Th\xfe\xcc\x1a\xa5\x1a\t&\xe0!̀\\\x97\xc7V`\x1f)\x93\xe5;\x00^\x81\xa6L1\xfaC\xe6|]\x19ߢ\x91\xc5\xc6\xdc\xf5r.-\x93\xcb\xf88\xa7\xe8\x1be\xcbu\xc7\vJ\xd9|\x05\xedW>R\x84\xde\xd6\x00N\xa3\x90r \x17\xaa8x\xa9A\a\x94\xa9\xb5#\x95\x92b\x00\x84+\x8b<\xae\x1e\x8f\xb1\u007fSfL\x93M\xa26\xe1e-\x98\xde\r!\xfa\xb3d#_\xb0\xb6\x14Ԛ\xc5Q&\xa8\xd4q\xb8\x93\b\xba\xe5qPB\xd0k\x88\xfa\x1d\x91\xc7\x00A\x99\x8d(#\bZq\x82Ɨ!\xbd\xd6\xc7Jp\xbf\x13\xbal\"\xcd\x1f1ײ\xbckI\xf3\x86ZV\xaa\xd0p@\xea\x9d?-\x92=6\x97S\xe6\xf1\x06s\xac\xd0\xe2,\xb6\xa9e:3\xad\x91\xa8eZ5\xaf\xb0\xfb\x14R9+7\x83N\xb99\xdf\xe5In\xf4ۇ\x0e\x1fםX\xba\x1f\xc2\x16gCSٮ嫳lmu\x83\r\xbe\xc2,\x873\xb6\xf6m\xf49z\x1b\xfd\xe3O\xc9PEǰ\x8eB^\xdd\xec\xabr\xf9\xf3\xa4\x1b\xca\xf2\x0e\xe6\x1a\xfd\xa3\x1bF&B\x11^m\xf2\x16c\x0e\xc3 \xcfr\xd04\xe3\xb1s\xcaͅj\x8d\\\x99g1H9\x03T1rFBC\x8dZ\xa3\x930JPh\xca\xcfw\x8c\x1c\x05\xc2\xe5\xe5a\x00n\x99\xd9]b\xd0յ\xd6\x02P5\xb4\x1a\xd0ނ\xec\x95G\xf7\xa3\u007f\xfen\xc1\xd2W\x80\xa3g\xfc\xddk\x17\x0f\xabuʥ\x01C\xd8\xe2\x18?■\xb3ͮ\xb2\f\x1a\xb2|\xdd\xfd\xd4@\xec-\x17^%;\xa9\x95x>\xd0@5\be\xecy\x13A\xccU\x9b9\x89\x01\x93\x1354mƄ\x82Wbp\xd3\\!,\x00\x89\x02\x11G\b\x8f\u007f\x93hD\x1a\"\xdb\xe9\t3!\xc0\n鄛HT\\\x806H8\x93`9L\xb4E5t\xa8\x06V\x13\x85\x1a\\\x91)\xe8\xd9\xed\xaa{`\xb4\xb6{\xe8\xe8\x95\xe3\a\x99\nꔻ\x15\x81@`N\xc0\xb5\xfb\xf6\xe7\x94{\x94\x819\xcd\x01瞞ݷ\xefv5\xe6ٛ:W\x8enY\xaa\x1cu?={\xe5\xe8\xe6%\xea1\xcf4*v\ve\\{z\xf0?gm\xa1\xb1e&\x9c\xd5b+hP\xe2\x8c\xe69B\xc6\xed{\x9c\rO\x8dQ,m\x1b\xbd\x12\xbcճ\xc7U[`l\xea\\5zH\xb7ṽu\xca=\x8a\xc0\x9c`\x80\x14\x84zr\xc7\xe6\xb9\xe4\x8e\xf8\x9f\xab\xe1\xf0X\r~\xb0UӚ\r\x85gv\x8e^5y\xb0#\xafQ(2'}CW\xed\x03\xa3\x15K\x19s륊\xd1O6\xa4\x9f7\x9dՐo\x1b6k\x95\xe8\xb7C\xc4\xcc\x18D\x8d\xa3&PS\xa8\xd9\xd4<\xeaJ\xeaN\xb2\x9f\x13,\x14\\ՅDe\xcePZC1\x11$ӡ\xc4 *r\xe2\u007f\x82\xd11ѽ\xc4c\x81ȅ\x04\x1dOQe\x93\x16\x144}\xa4TB\x90\x86%\"\xac9\x04\x02:\x16\x98\xe9\x10\x9ev̀\xd5\xe1OHn! \u0088\xfb\"B]b\xba\x8d\a\x17\xd0\t\xa2\xecPIH'h\xb7$tl$\x0fg\x1aup;0\x1b\fy\xb9\\#\xd3\xd00\xc2¸iI\x8bq\x83Z\xd7\b\xa5\xb3\xa4!\x17\x84\x80\xb5\x99-z9\x03$\x01Ey\xe1\f(\xafWȬ\f\x03i\xab\x83\xb6\x96\xd4*/c\x19\xd5[4\xa7\f\xba\\6\xb3\x9a\x01\xb4\xc7P\xe4\xe7u\U000396ab\xcf\xfc\f\x9fH53\xc7g=>㯳\xf2\x8f\xa1\x02X\x85N\xdf\x16\x0fo\xdcQ\xee\x195\xfc\x9b\x1a\xa9\\\xca8<\xcc\xd0\a\x06O\xb9n\xb4\xc6\x1d\x90\x83\x9d}\xa7թ\x02N\xc5\x12\x85h\rf\u007f\v ft+\x18\x03x\x8d\xe6\xa42\x83\x93\x8d\xc1\xd9mS4\x90\x81\xcc8\xcb\x13vו2\xe0\x85\n)ѻ\x93\xb3\x1c\xc7\xe8$:(\xa1\xb5Z\x1f\xf41\xb4\x1c\x00\xa5\x11F\xca\xd8\xc8\b\x87\xa4\x04\x82bpB\xa32k\x94\xb4Yc\xc3ÐQ+Ꮏ\xe7\xa4n\xfa\x17#\xfd4\x15w\xc3\xebݩ\u007f\xb9/\xa9\xa3+\x9e\x02kO\xebT=\xf5#\xadʶ\x02N\x86\xa7\x0e=\f\x14;\xfd\x9c\x0e3\xd2\xc93\u007f\xf8Q\xf2\x9d\n@&.\x03\x12֯\x06ɗ/\x99oD\x93\x05{\xe3\f\xf6\x02\xb1\xe9\x1bL\x8d\xc5=a\x05\xb5\x95\xdaM\xddM=I\xf5\xf6\xef\xf4\xf4;\x87eχ,'\xf4\x03\xf1\xedd<\xe7FO\xc4c\xd7\xfdJ\xfc\xffwy^\x04\x16\xf3\xe8@\x16\xd9\xcfL\x92\x03{\xa2\xbci\u05fc\xbe\x9e\xfaɥa\xd8\x13\xeer\xecq\x84SY\x02\xd0\xd1\u007f<\x00\xea\xff.\xbf\xab'\\\x9aJ2\xc9\xc9\xf5\xe7\xbc+\xdf\xe9]>(E\xcd\xdb5\xb9^B\x85K\xc3\xf81\xba\xc2g\x92\xfdՀ\xfabAt\xd1\xd4\xff\xa6\x00\xd8\x0e\xa8\xd2p\x0f\xa2\x887o\xa2C/\xa1Ҳ\x9b\x1aj8\x9e\x03\x16Q\xeb\x05\x0f\x82\x0fQ\xbf\xa3ޢ>\u0094\xd8Y\xa0\x01nP\bj.\xb2\xe3\xd7\xef$Qlw\xdd\u007f\x19\xa7\xff\xcb\xef\xf9[\xfaDž@>\xff\xb7\xd7\xfb\u007f\xf9|\xac\xa0\xacrF\xd4R\xe9=\xe7v\xe0\u007f?$\u007fk\xc1s\aH\r\xf0L\xf4\x9bk\x01꿿\x93\x84\n\xdaN\t\xfb\\\x12|D\x03 g\xbf\xfd\xb5\xe0\xa3\xfd\xc1\x8bC ]\xf9ݭ\xb7~w\x0eS$cw\xe4\xc6T<б\xa1|\xa0\"\x1fQDRMc\x85\b\x9b\xa8\x98jI\xc2d*Y\v\x9fN5\xa5\x9a\xd8\xd3~w\xaa\xd6Q\xefHպ\xfd\x05A\xd8k\xca3\xc1\xde`\xc1$0\t\xae\xfdt1B\b\xa6(_\xa5\x0e%\xb5Z\x90\xd4U\xfah*\\\xaf\x06\x94Tz\x96R\u05cbPy\xf8\xfeRя\xc99\xabh\x9c\xc5\x06\x84\xe7`A\xfa\x1c\xca\xc4\xc9s\xb1d\xc7\x17Ӭb \xfd\x80\x01ဟRX|\xf1\x01\nN\x84j\xc1\x8dh\x01Z\xc0\xbe; \x92'\x86\x0f\xa3\xc1h0{*\xe8A\xb5\xd6Z+\xaaehȦ\x83\x9e`\xae\x0f<\x8a\u007f\xbd\xe6\xb8\x19\xf4\xfar\xc1\xa3\xfe\x9c\xae^P\xbe\xbf\xfb\x81\a\x1eHm˄V\xde\x05\xe4\xfb\xbb\x9f}\xf6\xd9T\x15\xea\xf2WkO\xa8\xd5' \xfe#gm\xb5\x1f\xf4\x04k\xb5O\x83\xeb\xf0\xb1W.\xef\xd5\xd6\x06Q\xf7\xd3\xdaZQ\xa6\x82\xa4\x14\v\xf1{\xcbp\xbb\a\xa9\x02\xaa\x8e\xec\xd6\x1a=4A6\rҘ\u008bB\x8f\x173?\x94\xd8#9\x8f\xc1\x14\xf0Db%>O\xccCxu\x9f'@<\x8f\xe1\x1c\xa1\xc3\xd2>\x0fW\x8a\x008\xdb\xd7\xd9-\x01{\xf4\aj\x96\xeb>\x98\x81\x0e\xff9\x05أW\xbd9\x13\xa6.Yz&\x0e\xc2o\xbe\x82\xfe\b\xacm\x13\x9eC}\xe8s\xd81\xf6\x8ae5\a\x97\\Z|犩\x87\x9f]\v\xe5\x8dC\xc0-`\xe7\x86\xe4\xfe\xdb.\u007f\xb3\xfa*\xc5\xd0\xe2\xc5\n\xc44\xcd\x035\xe8\xf7\xe7K\xc1\xd0\xf5}_.]|[NIw\xd9\xf0\x1c\x1dz\xfe\xa9\xce\xc9\xe8\x91\xe3\x8b\xe7d\xb5\f\x92\x1b6?rp\xe3\xd6\xfd\xbf\xf3\x86\xc1%\xabK끼5\xc3kq\x19\x9c\xfb\x10Ag\xed\xf7Z \xec\xc1\x9a3\xfag!B\x99\x83\x01\bE\t\x03\xe5\x03\xc2\x1cR\x88Ǖ\xa0>C\x80l)I\xc1\xb5\xaf]{\xedk\xa9m;\xe6\xd8\xedsZ\xeb\xdc\xee=-\xc6\x0eC\xd6\xf2\xc1s\xe8\xb7\x1f[\xb7\xfe\xb1\xc7֯{l\x17\xfa\xe1\b\x1a\xa6|~\U000ea9ed\xff\x00[\x86OV\x99\bƀ\xe2\x99#@\xc1\xb8I\xfdk\xcf<\xf7\xf6\x0eI\x8e{wKk\xad[\xea\x91V\x0e\xa5?Z\xf7\x18\xae\xff\xe8\xa3\xeb\x9fE?\xa2\xdfoxtϥ\x13\xc1\x03\xb7\x16A\xb0\xfb\x19 E?P\xe7\xf1\x8eR\xfc>\rTk\x1a\t\x80l\x9fR\"7(\x98/\xc7\xf1C\xc7\xcfm\x84Ue\x18\x8f@$\xfd\x9d8\x9a\xbc} \xb3\xbf,\xb6\ta\x0e\xdf[ҳxq\x0f\xd2^\xdaQ:\xd9ZRP\xb9\xd2j\x89Vu\x98\f\x1dt\x9f\xf8%\x0e\x1an\x982\xe7f9\x18\xbf\xebر]7\xfe\x11~,\xe3\x87U\xa3\xbf\x88\x1f\xe8\xa7\xed\xafn\xdb6c\xe66:\xbbg\xf1\x92\xe1\xed\x8bѫ\a\x96\x96\x17\x19\f\xf8\x1a\x95+-\x1e\x16.\x14?\xe6M\x83&\xae\xbcfv߱\x9d\xbb\x8e\xbds#z\x0e\x04V\x80wq:꙱m۫۷\x11\xb4\xf1\xb3c$_\xb1g)\x15\xee\x97\xf9\x98O\x1e&\xa0&\xd1\\@\x10\xbeb\x06\xcad\xc7\x1c3\xad\x014\xd1n\x8d'B\x80X\x1b\x01̡\xd1\x9a\xb4\xdb\xdb1\xa8R\xa7\x03;\xddq\xa5\"\x04\x16\xa1\xebLN\xba,`/m\xf6O䔰\x1cm\x998\xe4\xfa\xb9\xa3\x8cF0\xd3V\xa9\xd3\xd7\\6&\xf5\x19\xba\xc9\xe9\xa3\x19\x8e\xdd\x0f\x16\x81y\x0fhM&\xfa\xd1\x1at\xcd3J0\xc3\xed`\xa0\xc1\x94g\x8d\xa3\x97\xd0\xce@\x9b\xcf\xe05\x99\xe4zz\bX\xf0\u0097#\xd1Ն1\xe3o\x9eԠR\x01ڮ\xd1T\x89}\xa4V*\xf6y\xb2\xaf\xdbp\x0e-\x82\xf7\xe0\xd6\"D$ן2\xd0pԓ1 \xcd8\x12\xc1\xedG\xba\x87\x99\xa80\x80\x13\x937O\x9e\xbcy#\xfd\xf3xh\x91\xa5(\x99\x05\xb2\xb4\x90\x84\xf4\xea\xae\xee\x9e\xee>\n\x1f\xba\xd4\xfaM\x93\x1cs\xcdwL\xa3\xa9iw\x98\xe7:&m\x02\xebH\xa1\xc9\xe0\x04\x98)\xe5yi\xca*F)\x84I\xf6$q\xbd\x99\x14\x8f\x98\x9eK\xe2\xd2wN^\xbf~2\x9a\xb4I\xb4\xab\x95\x92\xe96JU`>\xbeu\x00\xaf\xf6\xbf<\xb0\x88\xb3\xecI{\xc42\xf3\x19\xbbYp\xee\xdd\xd3)\\֤M\x17}\xf4\xa4\x88\x8a\x97$/p\xea\xb4\xf8\xb83\x06\xbc7\xe3\x11\xd2`r\xd3$\xf2\x12\xb5\xe4\xf1k\xc5㹗8AȬ\x13\xe4UP\x96\xd0L`\xa3\xd8\x00}\xcf\bQL\x0fda\x1e\xe5\x84\xf0~\x94\x9f\fTW\x06\xeb\x8fl\x02\x11\xf7i\t\xf2f\xfdGџ0Q\"\x14\x8f쉠\rE\x80\xdcoE\xbdV\xbf\x1c\xa0\x88-ȃ\x1d\x9f\bǗ\xc81I`\xe1\x93|\xd0\xf6\x12\u0601\x8f\x9f\x80\x1d\x9d%Aݶ\xa0\xd5\xe7\xb3\x06\xb7\xe9\x828\xf7\x86\xfeC\x92\xe7\x11\xae\x10D\v\x85À\xb9\xc6H\xe5R\x8d\x82.L\x1a4I\x9c\xe5\xd3f\xd8\xf1\x04N\xf5\fH\xcd\x12Ry\x9c\xea\x17\xf6\x13\xfbK3\x82{>0P5m\xb6\xeb\xf7h\xcb\xcd9v\x13\x9b\xb5y\xd1\xdf\xee\xe7ռ\xa3\xcb\xf7%\xfa\xc3M\xbb\x8a|Vεz\x030\xbfcQ[}\v\xc2\xebУ\x0f\xbf\xd1cvg\xbb\x15\xce-\x0f\xee\x03\xf9\xb3\x8d\xbc3\xf7\xcd\v\xe1盲\xf8\xa5^Y\xae\xc1)\xb5\xcfVؿ\b\x1b\xb7娢V\x9fԳV\xe5\x03\xbaB\xf3\xd0a\x85\\\xc0\xe5Α\x06\x1a\xab\x94\xd9\x13.\x10\x06\x01ї-\xfe&<\xa1\x86\x89_6\x8e\xe60\x8f\x1d¡\x04\x9f\xf00\x14z\xc7\x02̈\xcd\xdb\xeeB\xc7@\xa1\x05}\n\xce\xe00\xc8g\xdeI=\xedFS]\xe8+\x17(\x84\x83]`\x9f\v\xe8\\x\xec\xe9\xf0\xef\x1a\x19\xc5\\J\xa9\xf1\nK<\xdaWRC\xa8Q\xd44j:\xb5\x18s\xa4ۨ\xeb\xa8ۨ\x83T/\xf5.\xf1\xb6Ez\xa9\x97\x18\x8d\x92\x19\x1bGq3\x92\xb6\xe5h\x839\xe3< Fv\a\xbd\x85Ķ7a&\x8a8\xb1P\xa2\x04\xcf\xf6\xb4\x993\xf8\x84\xf4(\xa6\xd9\xcfe\xb8ӊ;8\x82sd\x80\xe7\f\x82g$\xe2\"ٔ\xb80&FD\xbb\xf02@\x93l\xb2\x06\xf2D\x8ci\xee\x8faz\xd5\xc4s\xc5B\f\xf2\xb1x\xda\x18_\xc0o\x16\xa8:\x92@\tB\nZ\x8bIH\x95\\\xa6V\xab\x81Jf\x029\n\xa5J\xaa\x95\xaa\x80\\!\x91\xa9\x152ٙ/\f\x06\xa8\x86:\x1dT\x8f\xb3٠Tf6ˤ\xc0v\xc4jUȡ\xd1\b\xe5\x8a\xc9f3T\xaa\x8cF\x95\xb2\v\xc7\xd5\x12\x99\xc1 \x93\xa8\xc1\x06\xf4\x91\xd1(\xe7\xb4\x10\xf3KZN>\x99\xe7\x15R\x1c\xc2q\xa9b\x1aN3\xf08\xa2\x92ʔ\xe0ʗ5\x1a\rf\t\xd4j\x8dA3]\xad֚\xb4@\xa9\x04Z\x93\xe6Oj\xbdM\x0f$\x12%\x94\xcb\x14RN\r\x99Y\a\x96\xf5\xfd[\xa5w\x8c\xeez\x01\xb8t\xb1\xb2e\a\xf6\u007f\x03\x15r\xb5Z\x9e\xfa\xe1\x1b\xb9\xaa\xe4\x18l\xd6JYV\xaa\x95\xa4\x9e\x05\x9f\x039\xa7\x90q*\xb0 \xb9N&[\x97\x945\xbd\xf5\xbaL\xfe\xda[2<2?\xff\xe1K\x85\xe2\xcb\x1f\x94l\xdf\xf7*\xd5\xf7}*\xf7g?je\u070f\x9fId\xc8\x04\x17\xa2\xcd?r\n\xfd\x8f`\xad^1\x1c\xe5}/U\xf0߃wyE\x16\x92|k4~\vN\xcbT\xaa\x94\x0e~\x86\xe0Wr\x8dZ\xf1\x15@\n\xb5څ\f_(\xb4Z\xc5\x17\xe0\v\xa5V\x8b\xa4\xffT\xe9\xf5\xaa%\xcb\xe0ZZ#\xe3X\xa9>u㲻\xa0^Eo2˽\xe8T\xaf\xe9\x00\x95\xc1'\xa0\x04\x1f\xc6v\x01\x81\x94\xa2\xb2\xfc\t<Ր\x1d\xfa*`\xfa\xdfc\x8c\x00N-FK\xe2\x90\a\uf07d+\x8e\xa2\xdbP\x17\xba\xed\xe8\n\xb0\xf7W\xe2\x87A\x0f\x98v4\x13?JScF\xdd'\xeac\xdc7\xaa\xef\xbe\x01\x11\x903 \xc2\xe4\xe0SR\x8c\xe1Ӏ\xfd\\\x9e\xb2Q>j2\x1e;\x97ⱳ\x15\xcfI\xbfܯ3s:\x0f\xf1\xa7,([\x13\x11.\x10\xa4ed\x13W\xc2\x19\xc5=s\x0e\n~\xfb\x88=; \xd6!F\xb2\aKl\x0e*`D\xb0\xbfǯ\x8d\x0f\x98\xb2P\x03 1\xe3I\x8e\x98\xbc\xc7\U0003f401\xa3I\xd1\x10\xb9\x8a\x84\r\xfaȘ,a\x8f8\xc2\xfd\x00\xca\xc9p\x97c3X)W\xa2W\x94`:\xb15KQ\x10y\xa2\x15\xe57\xb8\xb4j\b$uE\x97\xd7|p\xffM\xe35*\v`\xe5\x8cl\xf2h\xb5\f\x96$\x1a\xfd\x16\x95J\xe16\x02\xb3R/#\xc6\xf0\xca\x04\xb2\x97\x8c\x8e\x0e\x05\x1b4*\xfc8\x02B\x85\x12\xacݺ\x13\x9aؖ\xa8\xbd\xd4\x05WX.m)R3\xccfa\x8b-\x03\xc3\x1cv4\xa2+\x9cJP\xa6<\xadg(b\xd0v\x9a\x82#l.\xae\u0604\x99+\x00\x82a\x8f\xa5\x02\x9d攀\x91\xdb³\xf3e\x1a\bGw_\xb1\xae\xe3\x96HXc,\x94@\x9au\xad\x19\xb4\x1f\xd9-\x97\x87\xc7ѫs:\xb9\x00\x1df\x18\x80\xeb\x9ap{\xa4\xe6\xc6\xed\x98(nX8fQ\xa9\xc2\xe2\x00\x80:\xaf\x9f\x89\xdfh\xd4o\xfb6\xbc\x91\x00\x1a\xe3֏E\xc9\xe6:\x0e\v\x90~\xb4D\x03|%\x05\xc45\x1b\x01V'\xb4\x9d\x0f\xb78\x1d\x8d\xfdjK\xcfmڿ/\xc9ѐ\xa1\x01K'\xf7\xedoB\xefvNg!d\xf0\xd3K\xe0uK\xae\x83,`\x18\b\xd9靿\xa1\xd9\xe8\xe4\xfc\xd4|\xf0\x89\xc1\xa6\x95Zh\xaf\f\xd9\xe1\xce\xf9\xf3Q\xb3\xc1f$\xcev\xd9,\x19\xf4\xa4>\x92\xb9%F\xa3\xcd\x00\x9e\x98\xff\xcbv\x18\xf9\xdbځ\x98\x02\xf8\b\xa8'\x91\x06C7\xf0\x918-6\x86\x00\xe1F\xc4\r\x85@\x88\xf3\xf4\xaf6\x02\xc8\a\xd6a\xb3Y9\x8b_\x9a\x81,G\xcfo\x01\xbe\xc6\xde\x17\x1aЧͳ\x19%\x8d{\x17#Q\xcckA\x1f6>\xfb\xfcoh\x86\xcf\xe6ͻ\x9d㥌\x84\xe1d\xcc\xed\xf3\xe6\x01\x1d\xb0͟\xbf\x8f\xe3\x19\x1a_G\xb9\x0f\xb7\xc9\xd7蓌\x8e\xcc\xc0\xf7/\x15t\x81\u007fk\v`\x8eR\xf4Ӎ)\r\x82\xec\b|:2r\t\xd8\u2bffs\x16\x18<\xe9ʖ\x9c\x86\xe1\xcd5E\x1d躉\x80]\xb1\xb2\xc4]Z\xed\xfem/x\xb7Ɯ\xec\x18\xb1\xd2\xce\xcfO\xfd\tX\x80R\xef\xe9\x18\xef\xd6\\\xec\x9dr\xa8\xc8o\x9cyt\x9eX\xc2\f\x18Q]\xca\xf0\xab\xaf\xc0$\xfb\xa8^\xb2\xf9\xd1\xdeӍi\xd2\xdf\xf0ܠ\x17\xf5\xf6\x92*\xc9nR\x85 gf\x9e5\xb3/C\x9e7A5\v(\xeb1\x9f\x91\x8d\xf9\x9c\xe9\xb3\xf1\xd7\xdf\xc1G\xc0\xc5u@\xb0\x10\x16\xb4\xa0\xe3|,J\x9c$\xc24\x19\r\x93DI\x90\xfc\xe8\xca\xff\xf5\xed\x92ID\xc1m\xf3\xa5\xd7\u007fx\xbd\xd48=9\xdc\xe4=\"\xf8zc\x92\x03\xfe\xc0\xaf\xbdq2\x89\xa7\xb2wНv\xebȅ\vGZ\xed5\xa05\x99\xb4!\x9b\xe0\x9f\xb1_\xe7u\xc0\xb7*\xa3Z\x04m\xb6ߴN\x183^#\xfb\x1d7$\xe2\x04\xe8Q\x1b\x12\x91\xd5LZ\x01u%\x14!^A\v\x00I1\b)\xbf\xde91\x91C|G\x1e\xd8D\x18\x83M\a\xb4\xe0\x90\x9b߰A\x1b7\x18Y\u074c\x19:֨\u007f\xd6n\x18;V\x1f\x0fB\xbe\xa4\x84\x87\xbc\xe1\xb7\xccN\x05RS\xea\x04q%y\xb7\xb0o|\xb7&5ز\x0f\xec\xd9g\x94\xe8t1\xe3\x1a\xf4\xfc\x1acL\xab\xb9\xd10\xa9o\x12\x0f\xfd1Cٍe\x86\x98^w\x91>\x1d\xfd\xad\xe3\xf4½!6\xd3j\x02\x1af4\xf2\xebK\xa1\xe0\x85\x18\tGz\x01i\x16\xb5\f\xfd\x04d\xb2ߴ\x8e\xd1\xc9L]\x80\x8f\x10\xbf\u007f/y\u007f \xef\x04r\xd9E\xbe\u007f\x82\x1aF\xf0\x93~ӛU\x13\xdbQ@\xb4߉\x85\xa9`\xbe\xe21q\xb4\xe0\xb7\b\x10Uwb\xbe\x88\xa9\\L(\xf0bY\x92\x19\xfa\xf5\x8f\xdf%\xb5)\xa2\nZ\xfa\xc4\x13R\x1a\alҿ\xa9\xf1˪\xd5\u007f\xbb0\x1d-Wi\xe0UФ\xaaI\x9f\u007fS\x8b\xe0+\x04\xf1\x95\xbe\xfb\x0e_!\x88\xaf\x04\xf2y\xfc\x87\x8e]\x98\x9e\x92\xe0+\xd2\xe4\xd2r\x1c\xe8\xfb=\x0e`\x9e'tv7{\x1c\xb7\x17\xd1\xd0\xc5\xe4\x91\x04\x8a\x8e}\xe4\x98\xeb1\xd9\b\xed\x94\b\xf6{\\ǃ\x80\xa8$\r\xdc\xeec\x8fϜZ\xf7\x87;\n\xdb;\x1cusg,\xed\x1ak\av۸U\xab\x87\u07fb|\xfb\x1do\x1fz\xf4\xb9r\xce\xdaPQ\xa7w\x97Gb\xb5\u007f\xbc\xa3\x1a\xbe\xf4\xb2\xf9\n\xf4\xed\xed\xb6\xfc\"]lɵ\x1f\x03\x0e\\\xf2\xd6{h7\xfa\xea\xe5\xae{\xbf\x1c\x02\u0087{\u007f8ֻo=`\x94\xa1\xac\xd9#\xc6vN\x9f\xf0\xf4_\xd22}N\x9c\xd7$\x94\x1csSz̙Z\t6\x00\x0ft\x016\x11\x92\x81@f\xc3\x19\xf3n:6\x80i\x14\x9d!\xedT\x8c\xb0$\"\v\xfdW8\x01=\x8a\x1e\xff\xfd\xef\xe9(\x0e}\x87\x1em\x05Z\xbcx}}5hK\xddż\xf9{\xf48P\xa5\ue8a3\u07be7\x8dyƾ7\xbd^:\x8a\x038\x01,B\x97\x80\xd9\x1f\xf97l\xe8{\x1f\xec8\xf4\xd1\xe5O<\xf1Ĥ\x8f\xc0lt\t\xfaj\x03\x80\xfeC`\a\xba)7\xf5a\xb69\xf5\xa1J\x05\xbd\xe6l\xe8\xcd6C/\xa6\xe4?4g\xf0Z\xf1\x8b\xb0+q\xbf\x1c+\xf6Ia\xd7\xce\xe7Ʌ\x82\x84\xa3\x1f\xc0\x83\xe8\xdd\xebq&\x10\x98g\xa2\xad\x90\xc1\vw\xb1\xd1\xf4.\x1e\x97\xd1\x01\xf3yE\xafZ\xd2EW~q7\xa3\xa1\xcf\f\x06\x90\xbd\xef\x8bK&*\xf7/\x9b\xd2:\f\x84\x1e;\x00,w\x82\xd3oܳ\xf6\xca\xd9\xda\x1aeCk\xa2\xb55\x967\xa2\xaen\xe8\x88\xc5u\xab\xee\xbeg͵\xd3&շ\x94\xb47\x97\xe5\x0e\xaf\xab\x1fڱ\xa8f\xf5}\xb0\xaf\xe0\x95\xd5\xfb?\x05\xf2\u007f\xdeu\xc9\xd3\xf1P\xee\xd2;\xcao>r;\xfa\xe2N\x89\x05}\xbdz\xfbt\xc3Pu]C<֘\xd3\xd8\xd1јs\xed\x8aUۧ.\xa8\xad\x8f\x96\r\x12\x13\xb6\x9do\u007f bo\x12\xab\x9a\x04\xe1?\xce7\x1a\xf0g\xe1W\x89\x98\x13 \x11,I\x84$Z*\v\x1f\xbd!N\x9f\x15\x17|˲f<\x01s&\x03|헪\xff\xb0\x17m\xbe\xff\xf9\x8e\xfb:\x9e?\xf3\xcd\xf3\x0e\xc7\xf3\x9d\xb0\x1e\xac\x15\x13^K\xbb\x8a\xa5g<\xdf\xd9\xf9\xbcCB]DSX\xddI*᪤\xc2\xfdhs\xea9!\x01\x04?\x16+K\x9f\xbf_\xbc\x9c\xb0_\x93%9\xc1\xfe\x85\xa0@\x80s\nNz\xa2\xccO\x11|\x82\xacj\xb2\xe5\x1f\x8a\x99\x18\xbd\xe4ĕ\xffD\xbd\xa8\a\xf5\xfe\xf3\xca\xe7A\xfb\xd1\x0f\xd0\ai\xbf\xb6\xb3\xd0\a\x1f\x1c\x05\xed\xcf\xc3\xe4\xc3$\xf3\xca\u007f\x82ڇ\xff\x04\x96~\xed>\x99\x8fz\xfe\xb1Qtc\xbb\xf1\x1f\xa0+\xff\xa4\xfbk\xb4\x8d\xe8\x84\xf3x>\xfb7n\xc3鸧\xc7\xf5\x89H1\x1e\x85\x8c\xa0L\"\x18\xb0\x03b\xe6N65\x13\xc4|#.h\x03\x11\x82\x91d\n\x01\xb5`\x15/\x1a\xbb\x170\x98뉚\x8a]R\xb3>\xad[\xceK\xff\xfa\x12\v\xa4\xe1\xdaR\x0f;tHdNk\xb5V\x1brh\xec*\xb5<;?G\xad\x9a\x13j3\xf0 d4\xdc\xde\xe3\tьi\xb8\xc31;\xaf\x83\xe7\xdd^C\xa1g\xfc\x88\xc1&c\xe5P\v\x93\x95S\x9c\xadV\xa99y8\u007fxqcn\x91\x83\a\xf4\x87蒳\x87ѡϷ\xc0]\xc7\xc1j^\xb0\xd8\xeb\t\f\xd6閪\x878\x9d\xa5\xb7\x1c\xae\xcdw\x1b<:ml튵ݳGV\xe9t*\xda魏\xb47Ϛ\xb3q0J\xa1\x19\xff\xb8\xf1g\xd0!\xd2=B_Sb>7L\xb5S\x93\xa8\x05\xd4*\xeaJ\xea&\xe2o#\xe8'\x9e\x13\xf0\u007f\xcc\xd4q\xf8\x18\xd4&\xcc\x12\x8e\xa8]\x13+F.\x16O\x84\xe2\ts\x9c\xe6\x88!\x97\x84\xa8\xee\x98q\x17L\x04CDk\x9btK\x92\x8b\x8f\x11|\x01|\x19\xbc\xa2\xdc4c\xceLzbU\xc7\xf5[\xc1\x9b\xaf)\xe5\xb9\xd9\xeb\x1f3K\x83!w\xb6\xd9\xe8\xca\x1fY\x86\u07b6\x96\xcdo\xbe\xab\x92\xc9\x1e\xbd\xd0\xc1X\xee\x1dq\xf5\xe1¾\xe7\xf2\xc7é\x93\xbd\x9e\t\xa9[\xc6?\xf2b(\\\xd95\xae\x02La\xa0乖\xb8/{\xeds\f\xbaa\x13\xa3\xbet\xec\xd8\xf2\x8aq\xd4/\xfcRˀ\x8fƓ\a\xed\x03\xba\xe8/\xec=\xb2\x81\xbc\xfbV\x8b!疕\x80\x9b\t\xffr\x9eR\xba\x01|\x87\xbbB\xdeDP\x8axt\x84\xbe\xea|߳eg)\xe6\x15\xfc\x8d\x9c\x02V\x90\b\x0e\xc6A\"\x01#\xdb^A\x11\xb9\x91\x98\xa4\x10\vq\x01\x1bF\x80\x9d$ڻ\"\xb0\x10\xd9d\x16\xc0\x8f\x89b\x05&B\xe8\xe6%\xc3+\xa3ձ\x9f\xf2\x81\xdd\xc8\xe2a\xa26\x06\x9b\x1a\xc3U\x83\xb5\x8b{\xc0\xbf\xf7\xa2\xefn\xabm0\x9aY\xd6o\x8c\x96M}4\xd9Ғ|\xf4y|*\x91\xab\x82\xd9\xf2\xdaI{\xff\xba\xfc6\xa0b\f=\x8b}\r\xc3\xd16d1y\xa0ݰ\xee\xbb\xdf=\xbe\xb1\xb2s\x98/\xa7}q\x01\x1e\xd8\xdf\xefU\xb3\x01|gF\x95\xae\x8eOS\x97\xcc1\x84\rj~\xcd\xf6\x15\u007f\xdd;q/^\a\xf5\xe9u\x90 5\xa7\x15e\x13\x04Z\x84XnK\xdcDk\x9d\x8cc`LSU\x04\x85\xd2\xc7\x11xM\xb3\x88֔v)#(\xda\xe2\xde&:\x96!\xfb\xe9\x02P\f\x11U\x88\x8d\x14\xd3\x02\xb5Ԥ\x02:\xf5\xe1ˮ>\xbceKqGe\xc4\xeb6(ABO3\xadcC~\x99QgTh\x01&\xb2*\x86\x1aF&\xa4\x90ak\xff\x1d[:\xa2V#U\xd7J\xb3\x1f\xe8\xf05.\x1fUgp+*\f\x8c\x1c¢\x95*\x96\x91\xea\x87f\x03\x86\xa1\xcd\xf0=\xdec(ך\xaa\x95W\x83\xdc\xca\xfa\x841^\xde\xd64\xbd\xbd\x9c\x1d٠.Q\x02\x96\x05K\xfe\xb0 w\x89ƐetC\xc0\xdc<\xc8\x10(\xc8a,\x92\xa9z\x13\xcfB\x06\x80\xfc0\xad\xb1\xc5\x03\xe1\x90\x13\x9a\x00\x84\x90V<[M\x1b\xb2\x1b\x18\x19\x88\x17\x00>CwUc:\xf3y\x01'܃i\xe4\xa1\x02\x86\xec9\xa2}\xa0\xa8\x1b^<\x19\xe0 C\xfa\x8308C\\\xc2OPE\b\xb2\x1c\xd1^1\x8b\xa0sZ\x81R5\xc1\xc6Hvn}}n6m\x8d\x86\xed\xf9\xf9\xf6p\xf4\x8bb1\x05\x1e,\t\x91\x94P\t\xfa\xd1\x1d\xba\x17\x9d\xbc\xd3\xec\xf3؊\xaa\xed\x1d\xb2\xd4\x10\xf4\xe1\v\xa0\xf5\xa5\x87A\xd91\xb8\xe8\xcae\x89Wv5\x92\x02w\x02ǽ\xb7\x03\xc7\xfd\x8c\xbc#\x12\r\x87\xa2h\x8a#/\xdf\xee\xc8\xcf\x03_]\x98p\x1fs3:\xb5\xb7\xad\x99\xa6\xe5\x8c\x0e\xae\u007f\xefu\xe0\xbe\x178\xee\xdc\xfci\xaafٟ\xc6>\xbe0\xb0\xed[\xe0\xfav۶\xefD\xfc\x12\xc9Y\xdc4\xae\xb4\xafa\x81g\r\xd0\"DR\f\xf3\f\x049K\xc0v\x90\x9c\xf4H\xceR\xac]\xadS\xa8Pŷz\xb7Jƛ\xe9\xae3\xc7в\x00\r\xbd\x92\xa4\x06\xaf\b?X§)\xa7V\xca\x1eF\xc7\xcd\f\xe71\x80I\x8c\xafo\xfa\x1d\xea\xec0O\xf7\xca\xce\xe1%\x9ce\u007f\u009ch\xd6yw\x05\x99\xbb\xa6\xef\tx\xc0\xca\xc0\xc0\xfb\xa6\xbeA\u007f\xd6;\xd52ބ\xc2\x01\x9a\xf6I\x92>\xf4\xfa\a\xa7g\x81vz\n\xf2\x9e\xbb\xfb_\xd0a\xa3p\xf7\x17\u007f\xaf\xce\x0e\x19\xe8^\xe3i5\x9b\xdb\xf7ҕp}\xdf\xdfϛwJ\x849\x81\xd0\x1f\xf8ˉ\xbclԔV\xdd\x174\xfa\xf1W5q\x99\x99H\x80\v\x16>.{\xbeC[\xd1|_B\xad>\x8aN\xee=\x88^[\xc8\x01\xe9\x95r\x8d\x96\x1b\xfa\xee\x8a9\xcf^5b\xc4U\xcfΙv\xa8\xe9J\xe2\x8e\x1a\xd5ڂ\xe1\x90k\xe3|\xc0߰\x178\x8e\xa6Ng\x94\xf7N\bJh\xb4\x03\xbdJ\xb0\xb9\xae\xdf,\xb7J\xaf\x92A\xf9\x949\xb8\xfa\xdb\xf8*\x83\xeb\xafr\x85\xc2D\x97\x90x\xe6\xde0s\xd1\xea\xa3{P\xbf6_WF\u007f\xed\x9c\xfe\x8a\x9d\xf0\x15j\xa8\x15\xe8pm\x018Ϛl#\xea\x13)k\x81\x04\x9fu?\xb8\xfe\x02\xc1!K\xe1́\x85Г\xbf\x90\rV\xe3{=\x8f\xef\xb5\x05ӓi\xed3a\x96\xc43\b\x11\xd3\t`\x84F\xda`v\xd1i\xaen`\x89\x10n7\x82<\f2n\x9f\xf0\b\x13\xb87\xa2un\xe4\x89h\xcfC\xe6\"\xbe$T\x00/^B\xb8\xaedG\xde#\xf9y\x0f\xe7Yl\u07bcr\xad\a\x00U 5)\xa8\x02 \xa0\xad\x8d\x84\xad\x96\xc2\xc3\x05\xb9\xf7嘭\xee\xec\xb8\xc6C\xb0/Y\xa9Z\xa6\xa9,\xf0[,\x05\x87\vr\xeeͱZ\xbd\xb9\xa5\x1a\x1f\xaeh\x83\xcfXqE\x9f~D\xd4jŗ\xcc=\x98k\xb5\xfa\xf2\xcbq\xa6W[Y\xe8\xb7$9.\xdb\xeav1r\xb9q\x05\xd8j\x943\x8c܈\xb6m7\xc9%\xc0\xe9\xb6\xe5q\\\x8e\xc5\xe5b\xe5r\xf3\xca2:\x9f.\xb0G\xbc!\x8bD\xce8\x84\xbc<\x9b\xcb\x0e%r\xe3ըר\xa0i\x85\x11\xd4^\x8d\x03\xe6`:\xd3\x01X\xb9\xf9\xaa\xbe\x11+\x8cr\x0e:]\xb6<\x01c\xc8r6\xc9 \xdc\xc6yi\xfc\b\xc1\xfc䜂\xb6\xaf?D\x94\xefE;\xe1x6\xc1\xbb@\x01K\x84\xb1Ih\xbfu\x81\xd5\u007f\xad϶\xc0\xe6\xbbaں\xfa\xdaq\xe3V-\x02\x11\xf0\x91\xd5\xcf6\fu\xd6\x02\x89U\x11;\x93\xb4\xfa\xfdV\xe6\xf93\xd5\xe4\f\xbeV\x16\x96\xafZ\xb6\xfd\xc0\xca\xe5\xd9\x01\xbf\xc0G\x90>E\r\xf0;B4\x88\x1b\xa8\xc1\x98\xda1zb\x81_h\n{b\xbc\xd1\x17#g\xfa¼\v\xf7\xcap9\xe2\xa6\x12t\xa1\x1e(\xb8\xd7J\xe3\xba\xf5\xf4\xf5\x9c8!\xa1RY'\xce%\xd2\xc9saX{\xe2D_\x0f\xd9!\x1d\x00\"\x17\x048\x0e\xa9d\xb2\x0f\xff\x98\xf3r\x1050\x96.&ʷӾ\xe9\x896\x05\xf1\x1e\x82ې\xe0\xec\xe1\x99\x1c\xaf\xa3\x01\xd29\xb3p:\x9e\x9dX\xcc\t\xb1q\xa6w\xcb3Ϡ\x1f\x9f\x81h\xcf\xc4u8\xb8e\xddD0\a\x12\xb87\x12D{ \x04s&B\x8a\x14yf\x8b\xd2th\f\xc9\x1asȤ\x14\xab\xe1\x90\x05'\x9e7V\x03T\x8c\xa2\xfc\xa2\rl\x1c\xb3L\xa6\xa8\xb8\x95\x8c\x97\x1a.\xe3\x1c/!\x18\xc4\xfe\u0094\x8f\xa5.\x1b7\xaa\xea\x1b\b\xbf\xa9\x1a5\xee\xb2\xcb\x1e^\a\xbf\xa9\x1e\x89\x03\xe3FV\u007f\x03\xd7=\f.\x1bH*\xa5\x1e^W\xbeR\xab֮,_\xf70.\xc2iW\x96]\xf6\xf0ee+\xb5ܸ\xcb\xe8\x13\x03\xe9&\xae\x9fw\xd4\xe1o]M\xb5P\xe3\xa8\x19\x98{\xa0(a\xdbW\xd8\xe1\x15\x04\x13\x8980\x13\x9c=\x8d\x80\x80p\x8e\x91\x8b\x12\\\xf5\x88\x1b\xf0\xc2\xe6qZkք\x97\u0381\xb1\xb8\xd8w\x85\xf93\x94V]\x11\xa4\xea\"\xb6K\x89\b\x8df\x80\x83\fE\xd6\x05\a\xf2\xe4\x06\xabJ\x91\xa3\xf7n\x18e\xa5\x9f*\xf8\xbe\x91\xe7k\xc7\x13\xdcT\xf47\x02\xcb*\xc0\xa9>q{-\x1f\xe3\x1b\xcfȕ*\xf9\x04\x99Ln\x93w\xca\xdfWX\x14\x9dr\xb9\xcc.\x9b \xcbҫ\x05\xe0\x93.\xf5\x83z\x87\x1e\xff\xdf=\x81\x14\x95\xe3b6\xb9\x8c\xbe9b\x90\xe7\x1dX`-\x92\xb3\xe1Q\x1b\xbc\n\xf0@\xc1w\x8d\xf8\x82\xb5\xb7?qm\xe6\x1e\xc0Ep_\xc7\xd7\xf2|#\xc8KW\xc4W\xb6\u007f%\x1ceB\xca3µ{ҷ\xd2\xeb\ae\ue3df(\x8dK@ږ\xa1\f\xe4˃\x00K{\xe0\x05[@ Ǹys0df\x03\t\t\x97\xe0\x89Q\xb09\xc1\xf2\x9c)\x92\b\xf1\x018\x15\xb8\x81{!\xba\x95\xfd\xe5\x1e\x10\xb3p笯k.\xdf\xf5U\f}\x8c>\x8e}\xb5kk\xf5׳v\xba@\xd3\u0557.\xfbq٥W\x83&\xf8\xf6\xdbo\xa3\x87\x99\xe4E\x18\xdc3C^?C\x8f?\x01\x1a\x94G[\xd6\xee۷\xb6\xe5\xa8\x12={b<}\xe6\xf5\xcda\xf4\xe7A\xa1\xd0 \x90\x13\xa6\x04\xdfui\xff\xd0\x19\x9b\x82\xa1\x82\xd7\x10\xb2\xc3p\a\xf5(u\x84\xcc\x0e\x19\xcf\xd5iW\xee\x17\xc4\xc1\xaf\xe4\a2JM\xbe_+\xf9\xeb\xf9\x9eX\t\xcb\b\xc0\x0e\xd5\f^\x01]\x8c\xee\x82\"\xba~ǡ@\xf4\x12)\xba\x8a<\x17\x84\xb5\x17MN=\xef\bB\x18\xb4ó\xffM-\x90L!\xb4\x11mL!]\xb4}\xdbc@\x05\xaa\x81\xf2ж\xf6\xa8\xee\\\x99\xa0\x1d%\xed\xc1\x13\xfd:\xf0\x03\xbc\x8b\xa2%\x17K\xdd\x11\xb4o\xd8`\x0f\xa6\xfe\x8b*\xe0*\x95|\x0e\x043\xe5*]I˰\xd6\xf2@\xa0\xbcuXK\t\x1a{\xae\xc4(|I|\xe1~\xb9_\x1a\x17\xc1 h\uf525q\xc0\xfa\xe7%\x9e \x1a\x11A_&A\x10%D̠\x1fڍ\xed\x0f\xc1ޠ-hCxB>\xc5Y\xe0\xbf\b\xbc\xad\x18\xc53\xf9=\x16\xae\xef8\x81:\x02Y\x04\xec7\x13bzS8?%,\x15\x90\xa2\xe7\x9aS\xb5\xb0\xb7/\x89ҋ\x02^$(3H\xa4ݝ\x93\xa3H\xe7\n\xcf\xec$\x14\xa7\x81#\xdaBL\b\xe0\x05*\xa8\xaf\x01f@\x04\x91\x9cp\x96\xfcO \x80\x16\xed\xea\xb9\x13U\x1cF\xbb\x1e\a\xf3\xd6\x16\xdeٳ\v\\\x17\x9c\xd7\x1c@ݟ\x81\xeb\x83\xf3\x98\x8a\xe0\xdc \xea\xc6e\n\xd7\nE\x0e\x83\x97H\x99\xeb\x03\xcd\xf3q\xdd\xcf\xc0u\x01A\xf6o=\xab\x94\xfcS\xf0\xdbg\xa4\xca\x05\xafD\x03Q\x10.\xe2\xeb\xd2\xc5b\xea&.\xc0{\xc6\xcd\x11\x17\xacf\xf1\xa8\u05cb\xd6w\tZ\x10\xfb\xa7\xfd%\xf0\x82\xf3\x05\x170\xa7\xd7\x00\xa3.\x117\xd1s\xd7?\xba\x1e\xff\a?\xae\xeb\x1c\xbf~\xfd\xf8\xceu\x1f\xd7\x0e?s\xcfȊ\xdc\t\x83'D\xc7;F\xc3F\xbb\x84\xb1\xf9\xb8El\x8d\xb9188:\xb4\xaa\xf9\xe5UgFͯ_6\xa7m\f\x03\xa4\x1e\x0e0c\x87\xcfYV7w\xe4\x99U֜\x10\xa3\xa1'70\x9f6L6\x86rh\xc7\xc8\x15+F\x8eZ\xbe|T\xfa\x8c~\x86\xb7\x8c\x1d\xda815\xc5\xec5ipM\xe0\x90\xd0V\xdb\x04\x82\x9aOK\x14Z\xb3۲s6\xfa\xfb\xa1ž\xac\xc2\xe8b\xd0\x04\xa0\x14\xa0\a\x97D\n\xb3\xfcK\x0e\x01\xfb읁\x12;\x94\xd3\xf0\x89!\xb3f\rI5k\xec%\xa4\xcdf\xe0\xf5poZNK\xf0$p\xcf\x12܉\xe9\xf8\x04\xb1\xc17&\x80\x0ex8\"~\xe5\xe9\xe4\xf5\xd0}\xfd\xf5\xa93c@\xd3qL4\xb7\xa1\xa7\x8f\x1fGK\x162m\xa8\rL\xb7\x12\x9f5\xf8\xeb\tT\x9eN\x80y\x8c\x9d\xf7#4\xac\xb0I\xe8\xd1\x19\xb8\xf3 \x1d<1\x86BD\x92\x9f\x12,5X\na\xa2\xe9\xdc\x0f\x13y\xc1\x92\xa0$y\x9aR\xb0\xafc\x9a\xae\xbb\xfdT\xb2\xbd\x1bP\xa4\xd2YL\xddєP\x8f\xea\x97\xc7\v\xbf\xbeZ\xbaW\x04\x12fj\xcf\xf4z\xdcA\xe6\xbd3\x82\xae*S\x9b\xc4Us(\x15\xf7g\x01o\xc1\x8d\xe7\xb6\x11\x98.K\x8fZ\xa3!\xe1\xc5\xccJڊ&A\xf4\xfd\x04 0\x01\xff\xca\xe7%Έ\x05\xf2\x1b\xaf-B:\x8f)\x18\x1cNゝKg\f\xe1\x1d\x97u\\6\a\xb6\xac߸~\x18\xad\xdf-o\xfb\xe2\x1f_\xb4\xc9wSg\x15\xca+\xfe\xb5g\xf4\xfd\xebg\x94C\xdd.\xf9f\xb0\x12$\xc1\xca\xcd\xf2]H\xa1x\f\xadG\xa5h\xfdc\n\x85n\xb7\xfc\x19\xc8@\x1bd\x9e\x91\xefV\xdd`\xc8\xca\xcb\xcb2\xac\x8d\xe0\xbf]z\x95\xbcuܸV\xb9J\xbf\vh\xa5s\xa7\xe7UW\xe7\xed\xd2+\xe5\x9bw\xec\xd8,W\xe2D\x8d\xec\xd6}\xfbn\x95\x91\x82O\xbf\xf1\xc6Ӥ т\x13\xecf\x84}́R\xa9\x1aj\x185\x92\x9aNͧ\xd6\xe0\xc1y\x81O8\xea\xbf<\x13lH\x11\xd5.\x12\x1f\x986\x10\xebN;@\a{ \x8d\v\x92È^\x04x]8!\xf1D\x0f\x1b\x18\xbbh\"=\xaceN\v\xfe\x8f2\xf53\xe6x,>\nI\xaf\xb7\x94\x9c\x16$\xe7,>\xc6扵\xc9\u007f\xf0\xbapB\xaf\x0f\x8c]41\x95\x04\xe7\xa4\xf7\xf0\xac\x98\xd5+\xc0R\x8b\xfc\x06\xbaKH\xa3\xa9\xd3\x14)'!G\xe2\x11\xef,\xc5~%!\xb8z\x83\x84}\x10\xe8\xf1\x11\x98<\x02\x1f \x18A\x91\x8d\xc9\n \xb8\xbf\x11&\x10\xa2d!\xbat2\xf8B\x98\t\xa4\x85&Jd4\xd21Q\xfa\x95\xde4\x16y\xf3\xacA\x96I@6d=c\xb4\xd02\x9f\xde/c\x83\x9b\xb6\xcc~\xa8{V̢\x004\xc3\f\xbf\xa9\xa0\xfd\xc3\xc5Wwv\xce\xd0Ñ@\x81\x8e\x9b\x9c\xf4\xbf\xd8|'\x1c\xe3]_4\u007f1\xbdz\xd4J\xd4\xe8\xb1\xf1\xe8\x80\xc6\xe6q\x19KOt\u007fT\x1a\x80\xe6\xd0\xdc)\xbb\x9bj$4\xa0+\x1e\x9b\xbf\xe1ӎ0\x04\xa0K\x9a\xfaQ\xee1\xb1\xbfs\x06m|\xf6~2\x87\x87\xd2k\xad\x9c\xd2\xe3\x19\x115\x8foZ\xe1D\x10nH\xad\x83\x1b\xb5\xf6\xe5\x93g\r1\xfb\x8d\xae,\x8f\xe2:/X9c^\xa3\xd5k4y\x80UzK\xe4q\xf2j\xbdNo6\xeby\xad\xda\xe0\xf0\x1cu\xb94vg(\xe4t\xa8\xb7\x98\x95N')&]\xeftjJC!\x87S\xddF4\x86!\xa1H!C3\x90Ą'$O={\xe0\x00b\xee\x1f\x8d\x9bj6i\x96\xd1\xf3A\x15\xa8\x1c9\x1d\x1dC\xefN\x9f\x0e\xf2@\xfe\x9a\xf9\xe8\x05\xf4\xc2q\xc8z\xd8|jP\xf8\xa0\xa58uMn\xee˦{\xdb\xc4n\xb82\xeaz$aF/\xbaK\xdf17~\x16\x8f\xa0;\xc1\xd8D\xc91c\x85\xfbA\xa9\x94\x81\xba2\xf7=\x95\xa9|\x8bɪ\xaf\xb3x\a\xd5\xdd\\T\x8e>\xb7\x1am\xba:\x80\x99V\xb3\xbe\xa9\xf6\xa6b̗\xfc\xf5\xaf\xbbo\xbc\x11}Y\x0f\u007f\x9a\xb5n\x9d\xd7[\x1c\xf1\x96\x847\xae\xf0\xfb\x8a\x8b}_Yj/\xbb\xccc\r\xe4\x06\xac\xb1\xf0\x86\xe5\xfe\xf2\xe17N\\\xbd\xd9v\xb9u؆-5\\\x8eƭ\xd4I\xec~\xe7ĩ\v\xa7/\xa1\xc7,H]>|xq\"\xdev\xc9\xf1JϠ\xb0\xb3\n|\xeb\xac\f.(D\u07fc\x8b\xff*+\x81\x06\x9d\x05੧R\xef\x1a\\\x06\x15\a\xc1\x84\xceN\xa0\x19?\xbe\xaf\x14h\xcap\xbd\xd4;\x9f$\x86\x0fO\xc0\x03UU\x05\x05\x85\x85Ӂz\x8cY\xa9\x04\xb0\xaa\xaa\xbc\x1c\xac\xce\xc3\u007f&\xfc7uj^\xdec`+)\x99\xea4\xa5\xff\xca\xcb\xd1\xe5\x15\x15\xe3U\xb3\xa63ұ\x16\xcb\x19sX&\xf3:\xe3\xf9\x1e\xe3t\xa0q\x81{,8\xeeq\xc5d>\x8dI\xceM\x03\x1a\xe0L]\x8a\xefZ\x8a\xef\n\xefE\xdf\x00M\xea\xd21\xe5V\xad\x9c\v\xfaC9eV\xad\fH\x02꙾r\xabJ\tXE\xc0E\x12\r\x8c\x04֣o_\u007f\xbd\xb2r\xcbU\x15xv\x95\xeb\x9c|0\xfc'\xfc5\xa9#G\xc8\xf8T\xf4\x8fO\x05\xe6\xba|x\\\x8e\xa4.\xa1\xb6P\xfb\xa8\a\xa9\xc3\xd4\x1f\xd2ި\xd2\xfbD\xb8K\xfb8\xc2\x11\x10ć\x81\xe9\x02\xe8\bGK\b\xe6\b\xd1g\x13\xa4d,\x1f\x17\x92\aXo\xe33.A\t\xc55 $@\x95\x90\xdek\x163\x12\xe07_\xc9 \xd6\xe0c%ByN\x80;I\x10\xd3p\xf1\x01M\x17\xce\xc3\xf0\xd3h\xc0\xe9\x8b\x04\x1c\x01Z\x87\x99U\x1dT\xe8M6\v\x98\x12\xf5;\xfd$\xf5\xf4=\xad\xd5=<\xac\x03RI\x8b\x01\xea\x81R\xaf5\xd1c\xa6\x81X6IQ\xd3\xf6\xc6!3\a\x95;*\xf5\x8cj\x10\x0f\x9e\x97\xb2\xad\nn^\x1e\xab\x1b\xc6JC\xf9\xa0C\x85\xa3\xd4Y\xb0\xae\xb5z\x9fA\xb8H\x87\x92\xf9\xe5El\x83\xc8E\xf0z@.\xf2\x81\xaaY!\x14\xad\xe7ᩡl\x0e\x9eI\xa0\x82\x0f\xfb\xb9%\xe7\xd1\xd5\xcb\x03\xc5Y\x8e@Գ2\xc7\x05\xe6+\x18\xe3\xbd\xfe\x88\x10\xdf^\x11\xe3\xd1\x1c\x89\x9c\xbfD*\xa7\xe1Կ\x01V\"\xf7\x84\x17\f\xadh\xb2\x18\x942-0\xcae\U000bdef42\x16.\xd9\xcctKUr\xd0]\x9a\xae\xa2\xba\xf4\x97U\x80\x96т\x83@\xad@]\x90\x95\xf1\x80\xf7\x99\xf0\xed\xcc\xe0\xa3\xf3\x96b\xb2\xc7ҿ\x16k\xa8\b5\x04\xaf\xc4\x13\xa8\x05ԥ\xd4\xd5\xd4-\xe2:\x8c\x17TB\xfd\xb2\xbe\xb8\xb0\n\v\xebnz\xd9\xe5҈܄\x96\r\n\xcbn\"\x0e\x12\xbe\x98\x86\x8e\xa6\xcd(E\x85.VX\x80\xf1䫋\x12\\I^X\xc1\x05+\xd7P\x1aM2q\x8e\x81\x172$\xe9\xfa\x02\xf9\x1b\fE\u007f\x81\xc9)\xa92\xf2\x1e\xb3\xde\xe9(\x03O\\\"\x89DO}Q\xdf\xe8\xcf\n\x96\xd7\xeb\x1b:Z\v\x8a\xea\x1aB\xee\"g\x87[?\xa4kDQ\x143[]\x1b\xf4\x05\xba\xea\xbc\xe0Ь\xc2,e\x0e\xb8R\xa3\xca*\x94\xcb7\xed\xb2\x95j\vw킗\xe4\x87\a\xd7Ƥ\x9bw\xf9\xb3FF\xabP^A}AA=\xfdpQdrע\x9aļ\x99\x15ڲ\xc1\xb9\x063\xfb3<\x9fKZ5(\xe0\x93\x9dp\x8d\x99\xf6iE\x9dUeR\xdb<\xddY\xc1PSy\x9dEmֺ\xad\xfa\xc5فl\xe0[\xb4ոD:\xfb\u007fF\xf9]\x8a\xe5\\\xe4%\xeb\xd5t\x96\xab\x14e\x83\x88\x1b=\x04\xfe\xf2\xe1겒\xd2\xc2\xd4\x1a\xebnEi\x1dx\x91ܹ\x10}\xbe\xb8\xa6v\xf3\x92de\"<\xdb\xcd\xf3\x85j\xf8\xc8y\x1f\x8e\xa6Ԙ'\xfeVB\t\xe3\x9c +\xe9ͤ\x81\xc8~p\x88\x8d\x94\bc\x99\xac2\xc0D`J\b\x1a[\x9c\xf8\xa7\xaaf\x88\x1b\x89\xcc\xe6\x13^\xbc\xccD\xd5^b\xaa\xfa\xb2\xa5\x04\xd5\xeezw'\x00\x94V[1:k6\x13\x95\x02\xf9\xcf\x0f\xcb\xed\xd2Q8\xf04\x1f\xe9\x18W\x15\xfa\xec9ii{\xa9t\xeds1p\a\u0381\a\xd1\xdeWKZ\xe6\xed\xda9\uf86c\xd1\x15Z\xed\xd0ْZ\xb9]v\xea>)\x94w\xe1\x02\xb7gys&\xdep߷W\xef\x01\xac\x837\x10\xfdz\x03\xaf\xdf0\t\xcc\xc7\x05D{\xb6s\xefa\xc2tD\x1b\xd9\x15\xea\u007f\xf8\xa8\f\xa4]3jA\xff\xdb%\xeb\xcc\x16\xc1\x8f\x9f\xe0\x9f\x0f\x8c\x03\xf7\xc0S\vVW-80\xb5{\xf5\x96Wt\x8b\x0eN\x8bB\x10\xf3D\xea\xc7\xfd\xee\xc1[\x81\xfc\x96\xc1\xb5|\xa9D\xa9`\x15\xa9\x9b-\x96\x90\r\xc8BU\xcb\xdb0\xf5?1\xd3D\xd7ɠ\xa2X\xa9T\xc9Fv\x92K\x82R\xe08\xba\x1a\x8d\xeb\xd7\xdb\x12\xf6\xf5|dO\x8f2i\x89M\x90A\x03\x88<\x9fx\x04aC<\xf1\x8a\x99\x16\xda\x13\u007f\x172\x10\x02FI\xe3\x91\xc9\x1fϑ\xcb\xff(\xb7\xc9\xe7\xa6\xee\n\xc4^?K\xd5&\x03p\xc2\\1m\xceG\x93\xfa^\x82\xb5\xbd\xa9^\tu\x04\xfd4\xe9\xa398\xf1\x8fr\xa1l\xb2\x16P\xafDŽ\xb2Bڜ\x8f'\x9f\xae\x15\xca\xf6\xa6\xf5Ȑ \x87\xccN\xfb\xe8\xe0\xa8\xcc^;\x88\v\x8e\x1bL\x94\x8fh\xda\x12]\xe4D5#\x19\xde47\x1f\x1d\xda2uպ\xc7'\xc2u\x15}O\x87\xb6\x8e\x04\f\xfa\xe1/k\x9e[Z\xce5\x96Vk\xb2\xd5ֺ\xe6Ys$Ԥ\xa6\x9aq\xa9\xab\xd7L8\xbc>9\n6\xc4\xcf\xfcز\xc04\xf8O\xe8\xfbIw\xbc\xb1\x9c\x8d\x84\xbc\x81\xfaI\x15~\xcdy\xf2\xd0\xfc~4=\x01\xa1:\"`h\x8a\x10\x970*\xc4 \xe96\x82\x97\xca4`\xb1\v\xf2F\xa2\x83)b\xc7r\x02\x84\xd7\xc5#\x84\x93!\x9af\x9e\xfe\u007f\"G#jS1\xb1s2\xc5_F8\xea\x14\x95\xdf\x1et\xb8r}\x96\xb0\xc9\xe4\xf4\xb7\x17\xe4\xb7\xfb]Fs\xc8\xe2\xcbu9\x82\xed\x9db\xa6W\x88\xe4\xa7\xcb\xe4\x17\xb4\xfb\x9d&S\x98\x94\xf9e\x15!\x17\xd7\xe9n\xaf%~\x11\xc4\u007f\xb5\xed\xddg\xa8!\xa5\xb1a\xbc\xc3\xeb\xe0\x83\x9d\xf0?F\x92D\xa8\xe3\xb0[\xec&\xb5\x96\xb7\xda\x1cN\xab\x95תM8\xc1!\xa4\n!P\xdb+\xe6:lb\xee\x05\x05mV\xbb\xa9\xb7\xbd\x1b\xf4\xa2\xda̯\x9bֶ\x8e\x1c\x16s\xe6Y\xb2\xdc\xe5\xc1\x1b[\xfecD\x1c\U000c2f0a%t\xb8\xc7H\xbc@`\xb6\x1d\xff\xa4\xd4\xcf\x14\x9e\x0e\x00u*\tza-\x0e\x9eN2T_\x12⾗\xea\xed\xf7\x8d\xd2+\xac\x83Z\xbc\x12R\x98\xfc\x17\xbc>\xe1Y-\xca{\x88\x1f\x10\xfc\xfd\x19=MA\xef<\xf4ɭ\xef\x88\xf3\xcd;\xcf\xd0\xec\xca\x05\xfbS\xd4;xށ\x97\xa7>\\\xb023\v\xa5\xa8[\xd1'\xf3\xe0\x1d4\x85'\xb8\xf3\x9e͝y6\xb2d\x90\x91F\x86[H\x18a\xc4\xf8\x8e,\x19\xc2\xf3rTH\xbb*\xb5\x15\x0f\x94OQW/\x1cJ\x02\xe0\xedUZ\x9d\x11<\xa6\u058b\xefp\x02\xb5\x1auB\xa9L!\xb1LH\x9f\xf6\xb7\xc4Q\xccX\xaa\x8bP\x92\x04\xb7\x98\x11u\x86%!\xe2]\xb9\x1f\xb4D\xb4\x11\xc1k\x17\x14ա\x89\x13\x14\x11m[\"\x80\xba\x12)\xa7\xcf\x05\xcd\\0$\x10\x92\xacR.w\x95\xf8\x03`б\x9d\x15s\xdbZ\"e\xaebEVŸ\x95\x1d]\x0f\xce\xfaӭ\x8f\x8c(\xb5\x8f\xd28\xc1&t\xf6\x86\x1f\xae\x18{\xfd+s\xc7^7{lyEN\xb9\xad\xeb\xca\x11K\x835\x1dc\xc75\x97*\xe8\x87\x16\xb5\x8d.\x02J\x93\x8b\xd9`s\x98\x9b\x8b\x9b\xe8Z\x89ϙmW\xc9'|\xb3\xe3\xf7\x81\xf8\x94\xf6\xf5\xc3/w\x8c\x98;.\xbc\xe8Ѯ\x9e\xaf\xa6\xd4\xc4\xf6x\xfd`\xcfm\x00\xec\x98\xfb\xda\xee\x89\xc1\xeai3._\xba#\xfe\xea\xd4\xf6\x9c\xca,\xb79\xbfbn\x93Vw\xc9~\x866\xe7(\xec\xf9\xec\xf4b#0֟\xb7\x16\x8c\x15d\xf6D\xf70T\x92پ\xf2\x990)\x1d\x12\xf1H\f\x02\xf2-^\xf8L\x82\xe2*K\xda\xc8l\x14\xe7\xfeD?t\xb10̹\xe8Ep\xea\xf7|\xe6\xf3\x87e\f,\xf6\xc7u\xc0\xc0O\n\xc9=\x83\xa2\xedk\xa1v\xea\fg8b\a#+\xa66\x99\xcbB\x83\x86'G\xce|b\x1e\xcdLzp\xe1ӓ\f\x8aʜ%\xe3\x97\xee\xd9?\xa7\xfb\xd2\x02\xa9ϔ\xedO\x94\xb6\xe4\xcc\xdf3\xe7\xa1\xac1d\xbf\x1a~r\xbe\xc1\x03\x9d\x96\xf1\x8a\x18\xa2Q\xea|ϻ~\xb2\xb1\xef!*L\x9c\aG\xf5Z<\x83\x11C\a-\xee%\x1e<\r2ɴ\x1e\xabx\xa2\x05\xadX\xb4n\xc5\xd5W\xaf\x00\x1b\xe7<{\xd5;dmKQ\x99U\x8e&!h9W!s\xeaDߣ7\xd0\xf7\x9d#\xae\x02w_@\x1f\f\xb0'\xa4\x04\xb4|\xca\x02Ļ\xc3\xf4\xd3\x00\xa6_\xad\x1f\xb3\xbe\xfd\xf7\x99\xd5\u007fo\xe6\xd1\xf3\xee\bP\xfa\xd2\x19\x1a\xe2\xfa\xf3\x1eF\x98\xff\x89\x8a\a>\x11\xebK\v\xa6\x8e\b*\x17\xfe\xce5D\x14\"ђ\xb1\x11\x8ab2\x1fx8\x93\x99\x11@\xf9\x89\xed\x81\xe0\x0f\x8e\xd0\rYx\n\xc9\">\xca\x12!\xb2\x8c\x92~\x83S\b\xcf&\xb8\xa7\x8d\xe2\xd1\x1fJ\x87\b\xe0Z4\x02O\xa3\x97\xc3>ˑ\xba!\x9b\x8f\x1cټ\xf4\xe1;\x9f֗\x81\xc5 \veM\x9fkd\xd9#\x9b+\xab\x1e\xd4\xc8M\x1a\xa3O\xff\xe0\xa4#@\n*\xd1)\xb4\x1d\x9d\x1a\xdeT\x87\xf6\xe9=/\x99\xfb\xee9\x8cN\x01\xee\xf0\x92\x99W\n\xaa\x95 \t\x1e\x1b\xfd\xa1\xa8\x18\xe91\x00ń\x99\x87A\xb2)\xeb\x8c\xfb\b\xfa\xf9\xc8\xf5_\x8d\xae\xb9\x11$7\xcf\xde\xf9\"\x90\x1e\xb1\xa0>s\x89Z\xe1\x04̔\x8d\x9b\x8f\x00\xe1\xba\xf8JS\x1f\xa8\x99\x86rm\xfb\xdf\a\x1cX\x02\xb8ē\xc1\x92`\x92\x88\xe6\x1d\xa8;o\xa0]5'\xf4\x9c\\\x82\xa7G] O\xe63`T\xb4D\x90\x05\xc3\xf3\xfc\b\xfb.ķҕ\x10\xc9'1\x173\xf3\x19\xf90#\xcan\x9dCX\x9f\xb9o\x9e\xd9\xc7\x0ea\x83.&\xe8\n\xfe\xd3aH%\r\x0e\x87\x01&\r\xe0 )\x9c\xa2\xf0!i\x9d-{\x04\xd8\xc1\x18`\u007fD6\xd7\f\x14\x03\xe4\xbfP\t\x92f\xa7ӌ\x92\xae\x82\x02xI\xd8\xe1\b;R\x13Rw%cÆŒ\xe2\x11N\xe8^\x04^n[^Y\xb9\xbc\r\x95\xcf\x12օ+p\xdf\xfb\x19\xaf\v\x05\x04[\x80\x12\x87\xbc\xf0\xed0\x0f-\xe2XE=\x04\x05J0#\xf0\x88\x82,\x8f\x89!\x921@\x18\x03Q\x89\x12\xf7\x81\x908\u007fT\x00\x81\xe0\xf4\x13\xe8 <\x97\xb0OF\xfc\xa9z\u007f$\xe2\x87\xcf\xf9\x81\xd4ܗC\xc2\xf45\xe3\xd0{\x0f<\x82\x8e=d\xa6\xffL\x12\xfa.\x1d\aB\x0fl\xfe\xf6\xc19`iĿI\xb7\xe9}\xf4\xd6\xdd?\xa2\xf9ӟ%\xb9\x9bq\x1c\x14\xdf\xf3\x03\xd89\xfd\x88?\x02\xff\xde\x14\x8d6Enj\x19\x15\xf1\xf9#\xd7\xde\xf3\x10z\xf7\x91Lx\xf6C߀;\xc8\xe8\xd1w\xa3\xb7>\xd8\x04\xe4\xc7#~!\x06\x8a?\u0604~<\x1e!v\x15\x8a\xb3\x14\xf3C\xfa\xdb\xdaq\xff_&`\x8a\xd3f}\f\U000c60adt\x01~5\x82\xa5d&\x10{\x12ZpNM\x84Wd]\x91\xd0i\x91V\\WB\xacQ\xfc\xe2F\x85\x8bID\x04\xf8$\x11\x92\x1c\x8f\x13#N\x0e\x86$\xbe\xb4\xeb5L\xe4\x99\xd2\v\x8f\xb0]q\xcePXT\x15\xe7M\xe6jV\x10\x1b\xd2DI\x1c\x8ah\xfe\x90>\xbcd\xd9]\xc12t\x8d\x8b\x0ex\x959>\xf4\xe6>]\x96\xa6rհ\"\xde0|\xf6f\xafڜ\xa5\n\x96\xd5;\r\xd1۬\x15\xa7n\xfd\xfb-{\xf0w*E\u007fX\x1aP*s\x1bǎ\xebpj9\x8bV\xc38\x1a\xab\xb2j\xc7\ah\xe6J\x99\xd4\x03G\xc4;\xee\xf5\x94H[K\x95·\x9c\xb9\xf1%\xa3';VW9\xb3\xef\xech\xdb\xf4\xbc\x04J\n\xb2\x1b\xaa\x87\a\x06w\xec\xab\x1a\x1eTO\xbe\xafoϢ\xee\x9d\xef1\x97\xa3\xa7\x8c\xe0\x85\x86Ҿ\xeevi\x8e\x15r\x1c\xbde\x1a\x1a/g\xc1\x94\xf7}}?\xf8\x0f\\cS[ڲڧ\xd5\xc6ѭ\xd95\xd7\xef\xbf\xef^\x00s\x8bZ\xf4\xc51\x05\xeb\xf2\x968x\x86\x81<\xefw\xd8L\x96\x82+\x06\xb9\x97\xba\x94J(?\n9ul\xe8\xde\x11^O\xadr\x8eN\xe9\xfdp|b\xe6Z[\xb3\xabz\xb5\x06\x1c\x9d\xdb>3\xf5\x8cN\xa2]\u007f\xc9\xf53\x87L\x1b\xba\x005i\xaa'O\xaa݅\xfa\x9e\xbb$\xa7\f\xa8\xce\xf9\xfb#럍\x8a\v8\xf1\x14\x88\x0e\\\xcc|\xe9Տ,t\x81\xff\x98\x13\x0f\x90\xcd'\x18\nz\xb2\b\x88\xbc\xf0\x05\x89\u007f\x0f\x13\xe3\xc9\"`\xefՀ\xc74,\xad{K}\xef\xc6;\x0e?}͍\xf7\xa8^g\xab\xa2e5r[<4\x05\xfe\xf9\xa8\xfa\x9eL\xfa\x1bLu\x84\xa4\xc7B\xc5\t\xb0Н/\xd18\xe0\x98ԭ\xa9kG\xb3V\x9d$\xdf\xe5ʗ\xe8͒<\xb0\x15\xf0p\xdaX֢c\v\\\xbd?SP{\xdb\xe3\xffz\xf5\xf9\xcf\x1f\xec\xa9mZ\xb5\xachH\x83\xff\xea\v\x13Z\x9ex\xeb\xd5*\xa9R\x0fkj\x18\x8dJZ\xf9\xca;o\xbfR%U\xabYOV\x1d\xa3V\xcb*_\xa6_?M\xa6\xad̺\xc2v\xe1vqR\x15\xa2\xc6c\x1a =8\xc0\xa3\xa30\xd2\x05\x8f\xc3j\x90Y\xec3\x9e\x1d\xe3\x99\b}B\xf0-\xd9Ӎ\xbe\x16\x02\x98a\u007f{\xeb\xc9- \xb9\xe5\xe4VTD\xe28\x11h\xbb{\x84\x00}\x1d\xd2\ne\xbe\xee\xee9\x93$!\x16\xb3\xe5[N\xfe\x1f\xe6\xbe;\xb0\x89#\xfb\u007fg\x8bV\xbd\x17[\x96eɲ$W\xb9Ȓl\x83e٘bl\xc0\x98f\xba馛N\x80\x80\xe8$@BO\x80@\xb8\x10R\b)\xe4\x9b\xde0\xb9KB\n\x1c\xc9A\x0e\x12\x928\xb94\xee\x92\\\xbe\xb9K\x0eli\xf8\xcd\xccJ\xb6l\xb8\xdc}\xef\xfb\xfd\xe3\a\xd6\xee\xec\xec\xec\xec\xcc\xec̛7o\xde\xfb\x89D\x8fNO\x8aŦt\xad^\xaf\xb7'\x89\xe5`\b\x9b\xa6\x97H\xc0T.M\x8fҀF\xc0\x82\f\x15\x98-\x15'\xd9\r\xe8\x9f=I$\x87\a\x80ݨP\xc2Wش\xc8Y0\x19\x1eV3\x16V\"\xe5ར\xb7\xc0\xd8\xd7\xc54h=sF\xdd1\\\xc4U\x0f\x99\t\xa4\xf0l\b\xee\xb0\x00?|\x94U\xa1\xd4'E\x1cX^\x05*\x1f\xfa\xe4Փb\xc6\ah\xa0V\x9c\x04\n\x19|\xfb\x10(\xfb\xeeS1\xbc6\xf0mZ\xde\xf6y\x0e|\x03\x9e\x06^\xd5v\xf8\xe5'\xb9`K\a\x8d\x1a\u0080\xda\v,\a,,\x84/\x82_>\x83_G\xee\x80_\x81\x94?\xfd\xa9\x1f\x98)e\xd1gΌ\xde\xd7\xc0\b\xf2\x12\x82\xff\x8f1\xef(\xd2\xfd;\a\x03\xfa\xd6\t\x8axϯ\xa7\xbf\x06Mϯ\x8f\xfc}\xfd\xf3\xec\xf9\xa7B\x1eh\xf1\x84*\xf3\x98\xc6\xf5\xa7\xc0\xf4\xf6\xaa\r\xaf\xbd\xb6!\xe3\x19\xf0(\xc60\x87zO\x1f\x81ެG\xe3\xedvJJ<{cy\fK1\x98qA|\v\x87\xd8^t\x81\x16\x99@K9\x85\v\x0e#\xc5\xf9\xa9\x80\x8872\x0f\xc1\xdf\xc2\xf4e\xfa\xb3\xa0\xe9|\x03\x98:\xbe?\\\x19}c\xfe\xf8`\v\xed\x87G\x17\xd1\x1a0%S\t\xaf\xc0в\x19\xcc\xefO?\xb1\xf9\xe0\\0\xf0=C}%7\xeb6\x98\nO\x8f\x1eu\x1eL:{g\xe5\x98\x05\xd1\xd3p\xe5\x801`\x1d]\xd6\xd1\x1bL\xa5\xf5K\xc7\xcdX\x0e\x83\xf0c\xa5\xbe\xa8r\xb8\xe9,\xa8\x9dw\xef\x86'c\xb4AL\xb1\xff \xba\xbf\x98\x92\xeb\x04/?d\x87$\a\xe8\xfc\x88\xcd\xf6{mX\xb9\x93\x89\xc73x\xa1\x8b\x18\x19\xc19\x1dO\xbcC\x99\xfc&~\xda\xc1\xf5\xabϜ\xfebϞ/N\x9f\t\xaf\xe2\x0e\xb6\x01\xfa\xea\x81\x03W\x01\r\xff{\xed\xb9C\xab\x1e{\xa3m\u07fe\xb67\x1e[5\xf3\xb6\xa7Ƽs\xe2\xc4O\x81?\xec\xb9\xf7ӧ\x8e,\\\xf5\xfe\x92\xf7\x8f\x9dx\x87]\xde!.\x1d\xbbg\xcf\xd8R\xf6ښY\xb3:\x1e*\xadd\xa2\x83\xb7o\x1f\x1carr\x1ds\xe6\xa43[\xd9{\x0eVE\x86y\x8b\xa6\xcf\xe6\x04>\xfa\x18\x9a\x9b\xc7v\xda[\x8c\xfb\x9fˡo\xba\xee\x02XM@Z!\xb4\xc5\n\xb8+V\xfdx\xbd\x95\x1c\xe0\x97V\xfd4\x1cF\ax\xe5\xd6an\xcbw\x0fud<\xf4\xdd\xea\x99\xd2\xdf,\x98>8\x0fd\xbf\xba7\xb2[\xb9\xf9\xc41\xfa\x13\x83\xd5j\x88:pBZ\x87\x8f\xd1\xef\xf1\x11<\x8e\x8fp\x18\t\xcf\"\xe1}\xe8\xf8\xd0C\xdf}\xf7\xd0\xe27\x8a\xd2\xdd\v~\xd3\xe7\xf9?\xef\x8e\xec\xad*\xb1\u007fLamI\xeaFP$\xd8\xce\b~\xda\f\xc4S\x9b\x9d\xf8jˣ\n)\x1fUJ\x95S\x95T_\xaa\x06\xd1塈2\x8f\xa6\xc6#\xea<\x83\x9aMͧ\x16Q˨\x95\x88BoD\x14z;\xa2\xd1{\xa9\xfd\xd41\xea\"\x1a\x11X\xf4\xe3$G\x9f݀\xad\xd7L=\u007f\x01\x13\x9f\xf8\xc3.\x89\x12\u007f\x00\xe3\x82\xfd\xca\x0f\xdf\xf7\x1a\x02\xff\xe4\xae\t\xeb\xb3\x18\xf8[\xfc\x9cq\x0e\x8b\x80\xdfXi\xbf\xa0\x8e\xe6\xe8ԧ\x03\"\x97\x80\xf5o4y\x03\x1e\x11\x16^\x8b\xa8ȵ\xa8\x98\xbb\xaf\xfd\f\xbd\x97>\xda~f\xa83\xfe\xafB5S\x95\x86~VrnV\r\x99\xa9\x9a\xb9\x1c\xfdn\x8b\x9d#\x95\v\x81~\x110,\x02\xfa\x85\xe4/\x16\xeex\xc1\xb9聞\xf1?\x0e^ԙ\xb13\xbae\xed\v/\xac]\xf7\xfc\xf3\xf0\xb2\xbbwuow\xcb$3\x93\xd6gbj\xa0\xc4\x11\xa8\x1f\x12\xc8\xca4\xa4ר\x107\x9e!\xb1*\xcdFyj\xc0g\x17Q\xed;\xe0\x13\xa0\xa1\x929\x1c\x99\f?\xe22\xdf~\x1b~\xb8hў\x84\xbf\xbb\xd3\xf3\xed\xcatO:\xfe)\xec\x9e\xf4t\x8f=\u007f\x82'݃\u007f\xe3\xf3\xd3=\xec\xfb\x19=\xfe\xc1\x13C\x16u\x8fY4$\xa3[\x9e\xe8\xcf\xf1\xfc:\xa1\xb4\xe0\xf6\x8c,\t\at\x86BoE\xb6Ԙ\x9b\xe6\xc9\xe7\x81LoH\x12\x19Me@\xc5\xc8\x18\x11-5\xe5\xc5\xfd\v,B\xe3o;\xc1{\xc8\uec46\xbd\x95q^̍,f4&\xde\xdfv\xf8p\x1b\x03\x0f\xb7\xdd\u007f\u007f\x1bh\xabȻv)\xaf\xa2\"\x0f<\x99\x1b\xa2\u007f\n\xe5\x82'\xf3*\xc0\x16|\xef0Nز\xe00[\xd2\xfeJnEE.W\x8d\x8f\xbf\xf9\r:\xc6\xf8\xd0LD\xbf.\xa3\xf3\x18D\xbd\xb88\xdc\x11ߵ?O\x04\xcd\x18\x1f\x90\x15\xbcDP\x89\x10I1\xcd\x01\x9f\xb0\xc9\x11We\x17\x1e\xf0s\xfb\x00\xbb烏\x0e\x8f8\xb0ba\xf3\x8c\x85\xcb\xef\x1dv\xe0\xb7\xe7\xef\x9fzi\x04g\xb3\x88\x95\x86\xde\xd3\xe0\xcfk6~\xbe\x19\xa4\x9c[~\xf1\xf0\u038d\x9b\x8e\x8d\x99\xbeq\xedD\xeb\f\x8d>M\xf3\xc7\xfb\xcbf\x97\x17\x89U\x86\xe4^OM8\x05\xd9R\xe6\xc5\xf7\xde\xd8u\xe8\xfd\xc0\xb8\xe5\x1b6.\x1f\x17x~\xff\xa1\x97j\xcb\xd9T\x9dA\x99\xe4k\x9c\xb3\xf8\xc3Mg\x81z\xd4և\x1f\xd9:j崉a\xa7U\xaf\x1d\xac\xbf\xff\xbc3\xd7iP\xe9R\xfa\xd4t\xbc\xe6LU\xc5xY\xec\u007f\x1c\xdb\x12\xe4`\x8c(\xa2\xc2@|R\xa6\x02\xa2*\xd6\v\x10\x90\x11\x8cE\x12Dzgcg\x1d\xf1\xb0@\xfc\n\xa0\x8f\x10'uA\x10`\xe22\x15+\x8b\x97\xe1,vЋ\xa5\x16ć/\tD\xbf\x16t\xc8\x05U\xf2wm\xc9\x1d\xdf\x01\x9eKb\xee\xc5I\"\x94\xd9et\xd2'\xdf\x13\xc4&\xead\x95\x8c\xe5\x01{\xd2\xecb\xba\xe7\x82\x03\xd1D\xc5r\xa6\x15RINf\x05\x9f\x9a$U\x17`\x8c>\xb3\xd2[\xcd2\x01\x14ThӌN\xdeՅk\x8f\xeb-\xe8\xe3\x0f\x13z\x9b*\xaeFO<\xa0\x99\x80\xb1(\x00\xfe\xd3:s\x94\xabX\xb7\x1b\xbdv7b2u\x80\xc2\xd0\xd8Tt\xf7\u007f\\k\xdd.\xe0\xc47\xe0ǻt(\xe7\x1b\x94\x0e\xe7\x97\xfe?\xaf\xbb\xe0GC\xe0߱7N9\xb15C\xb7t\x12\xc6\xee\xd6\xd9%\xb4\xddig\bC\xef\x14\xb6̉g\x0f\x8cS`/\xba@χ\x17\xc0U0>\xda\xef\x8e\xf7`;lc\xa2(\xe6\xd5\xc8\xeb\xf4\xf1\xf7\xe0\x0f\xf4|0\x06\xb6\xc1v0\x1a\x84\x95\xb4:\x12Җi#!5\xad\x04a\xad\x9d\r\xdb\x19*:\x83\xde\x1f\x890,\xf1\xb7\x11\xf9\x86\xdeO\x02 <\x1dR\xda|M\x84\xd2\xebYJ\x93\xaf\xa5)llj*\xc9\u007f\x8f\xe6\xa2\x1a\xea\x1e\xc4\xe9S\x1c\x16\xcb\xf3n\x02E\xfd뇀`\x1c\xfbO\x0f\xce\xc4D\x1a\x06o\x9bk\xbc\xd8ͨ\x01C\x80b\x1f\r\x8c\xa6+\xf5\xbfz%089\x1f\x9efx\x1d\xfa\xc8\xe11Æi\xfd\xdaa\xc3P\xf8\x9f\x1ep\xa2_\xbb?\xac=/!U\xe8\x03\x8d\xdez2,l\x03\x85OZ\xf5\x9a\x0ft\x899\xfd\xea\xeb@\b`\x13\x1f\x88\xfa\x8b\x90\xa1\xee\x9f\xfd~\xe5\xeem\xf8nC\x83N\xd7\x10\x02NPf.\x97\x96\x81\x1cl\x1c\x0e/\x96I\xcb\xcd\xf0M\xf8\xb1\x16\xddl\xf8\xd5LX\xb3\x00\xb9\x19\x1f\u007f\\ܗK\x1fj)E\xd9u\xa8%u*\x00bN \xd3c\xbe \x057\x8e\x12\xec\x80\x1akg1( \x16\f\xa3;\xc7\x1e\x13p{1\xa1\x15\x88-\xc6g1\x16\x11\x90\x16@\x80\\\x8d\xbc\x97\xb72t\xa8\xa9\t7D\xb8\tP4-\x1d\xd9o\x12o\xe1'\xf5\x1b)%z\xba2\xf4\xc7\xc89\x85L\xa35)2<:\xa9B&\x97)\xa4:O\x86¤\xd5\xc8\x14\x9c\x9c\x91\x91T\xe0\x81]\xb7E\xf6ݶK\x92\xea\x19\xea\x1b\xf3\xa1\x91~\xfd\x03M\xdf\f[\xaeuN\xef9\xd6\\[F_\xcd\a\xaf\xf3)\x1f6T\x8c\xceV\x83\xd6p\b\x9bH\x85\xc2t\x11K\x8bu4\xad\x13ӬV\xc2\xf0\x10\xf6\xd56\x11_W\x15\xd3\xca\xd3\xd2ʧU\x14\x0e\xf1;\xe4(+\x94\xa14%ɤfei\x0e\xab^o\xcdH\x93\xb3\xca$S\x8a\x14\xe5\x84\xf2\x93;\xfcC\x98!\x10;\x14\v\v\xfb\x11\xf8竭\x05\x8f\b>\xb1\xe8N\x9f5)D{ʎ!\xfc\x04\fK\xb7\x1d}\xff\x14 \xf8rљ\x8c\x00\xcdy\x18\x98V\xc4\xcbЌ\x80w\x13\xec1\x8cH\xda\x14d\xb0\x00\x9d\x02\x85\x9c\x94e\xa2;\xb5\xc5\xda\xe8\x0eN\r\x16\x18\x1d\\\xbf\xd7D\xe9FC\xbahW\x89\x96vπw\xcf\x17;ty\xb2\xb5\xbf\x139rӹ\xc5p\xf4\f\xd8\x16\\;\xbf>#\xa3~\xfe\xda`\x1b\xa4)\x91\x84a\xa3\x8fh\xb5\xf4\x18Z\x9bb\x00\xc9\xd1iz\xb3Y\x0f\xbejq\x80\x13;\x0f~\xa2\xd1\xd3\\\x16l\xa0\x9fЛS\f\xb0\xe0\xe0\xce+\xd7rjB\x19\x19\xa1\x9a\x9ck\x98\x87\xa3oPl\x98\x8b\x10\xdb\x1a\n\xe8)^\xe3\x8d\xf7\xeaNA]'ޮ\xc6\x03h\xe2}\x96\xd5f\xe0\xbd\"\xf4c\xc3\xf0\xf2\xe5\xb6.\xd0\x18!\xb8\xefok岭\x9fo<\x0e\xb2\x9f\x88PB\x8f\xc3{?L\xeb'\xf0Eԗ\x12\x92\n\xeaD\xac\xfa\t\xa0=\xb8\xe9\xeb]*\xdd.\xf8g\xad\xb0\x9b\x83\x9fJ\xdc\aŶ\x80\xdd}B\x12o\xc8t\xba\x87&\x1b\xc0&\x10\x03\xc2\xf1\ne\xa3\xd4\\\xab\xd9E^\x00\u05ed\x1e{\xf0\xe2\x9f/\x1e\x1c\x8bNK\u07bd\x0f\xac\x86\x1dDX9#^4x\x9dC_\x1b\njK\"\xb8\xf6\xbew\x97\b\xa9\xf1C\xab\xc1j\x92M{\xb8\xab.\x9d\xba(,\xa6\xcd\xe5\x82\xed\x9cր\x9a\xd0\xf0+M\xe8sQD\xe3\fQ\x1c\xac\xb2c\xc5T\x83P\x12^\x14/4\x13\xdcy\x125\xaa\x80\xd8@^+\x04/\xc3\xcb'w\x1e\xab\x10\xe94}\r\xe2\xdc\xd6\xefZsũ\xe5\x1a\x9d\xa8\"\xfa`W%\xd8\xdf\r\x80\u007fy\x18\xb7\xf2\x86\x84GIpC\x12\xe8\xff\xc9\xc3\xc00\xa0\xe9\xa4:E?kݺY\xfa\x14\xf5Ɏ+\tU\"\xfd\x81\xcc5U\xd4@\xbc\xe7\x1cSx\x8fW\x03\x83\xc7\xfd\x8b\xfa\xe1.\xe2\xa70\x11p\xe2\xf1\x1d\xaf\x14Ka\x9b\xf9\xf8\xd7X}\xab\xfa\xed\xfd\xdb:\xb9\x1a\xd8\xde]z\xa5\x91\xba\xb1I\xa9\x8dnN\xf86\xa8\xb3\xa0\xafC\xba̦\x1b\x87\u07feu\x05Q'\xd2\x1c|\x0f䘔U\xfd\xa0V\xd9є\xf8\xb5\xe8N\xdb\xd2\xd9\x18=\xe4?\xa9\x1b\xfev\x017\xdf\t9l\xe8&m\x8fO\x06\x81N\xa4b\xbf\xad{#p\xff\xba\x11\xd0G^]\x1f/\xd7\xec\t\xb1\x1bSJV\xaf\xbe\xa9\x15\xb1\xec\x87\xc6\xfaN\\\x94*\xa1\x82T-\xd5@vf\x8c\xb4\xe8V\xa4\xc3\xfeO\x88\b\xee!h\xd64Rh\x92t\x8b\xd4L\x11aH\\d\xe2\x05\x1a,\x93Ӏ\"\x14F\xf3'aKDP\xbe\xf6o\xfb\x12(\x06\xa4z\x90\x1b\rp\x9f=\xf7\xd8c\xe7\xce\x02wd7b]Z\x17\xcd8p`\xc6\"2\xb3\xd2\xd7\xefX\xb6\xec\x0e:\xf4\"\xaeŋ\xe4\x06\xf3׃\xf0\x87'\xd4\xddH\xd1\xcd\x04\xe9\x1c\xc8\xd3\x19\x16-2\xe8\xe0\x1f\xa2\xef\xac\as֯\x87{\xe0/\xa5Ǿh{\xb8ThrĐ\xb3\xaa!CT0\x02b\xb4\xa1\xf4\xe1\xb6/\x8e\x95b\xbe\r\xdc\x10\xf1\xb8\xbf\xf5\xa3\xea\xa9\tԜ[\xf59\xc4>\x8b(^\x94\xe1\xf60\x01a\xeatv\xeaav\uf726\u0600\x02ńQ1\x05\x81So4\xa1V\xa3\x02x\xb7\v\xd1E\n\x1b\x16\x92Nl\x05\xa2n=\xad\xae\u0098\x06\u007fz\xfe\x03x\xb4ϒ\xf3\xbb\xebŒ;\xbfؼ\xf4\xe3Ѥ\xff$\xa6\xeb\x95\xfe\xdc.\x12\t)\xf6\x81\x8f\xd0_$\xfc\xe91\x06(\xdf\xf5}\xb2\x195$ӊ\x1a\x10E\xc0\x9fP\x04۔\xd8\xd7&\xfe\x10~\x0eF̩\x1f\x9d\x12\xcd8\xfa\xe9\xb2\xcd\u007fޫ\x12\xc6`(1\xd5\xc0\x89\x92E(\x0e\x1eѻ\x92\xdb\x1f&\x87G\"\xa6T\xeb\a\xa0¹|\x17\xbc\x1e\xe1\x11\x17\x84b,i\x1f\xc0\xd3(\x06\xb5\xa1(\xb6\xaf1\x10\xb5\xe18\xaa\xf9W\xda\x10\xf5\x99\u007f\x8b0\x11w#BS\x92\xbeGX\xbd\x80K\x8d{_g\x9fS\xa3.\x17\xeeф6\xf8\x8fg?{i\xc9֛\xc6\xec\xc1뷛\x92\x81⥶\x97v=\xf1vlTRa\f\x15\x80\xaa\xb3dځ\x03Ӗ\xbcȔ\n\x9d\x8f\\v\x1f\xa7\xa8힁\x91\x94\xf4U\x83U7\x0fV͋ \xfd\x81\x97\x81*5}\xd5$2\x1a\xbf\x89uC0\x1fw\xbf҇A\xebå\x91ή\aC\x0f\x97v\xd3\x1d\xeaE\x90\xe3\x13\xe7L\xbeSY\x92\xef>{\x06\xba4&\xff\xe9\xaf~\xb8MjF\t%ۇt\x9f_\a\x9f\x10\xe2O\\\xfc'\xf3,}\x83%\xf3l)\xf1\xe7h\xa4\fz\x9a%ۺZ\u007f\xc0\xd7\xf5\x91y\x01\xd4I\xa8F\xbc\x9e]݂\x8eՇ\x0e_\x06\xee'\xe0\x87\xc77~\xbeU\x86)\v\xd9\xfc<2N(\xc4;h-\xf8\x8eP\x9fq\u008d\xeb]\xb5aV%\xc1\x17?y\x18\xfey\x97N\xb5\xeb\xebM\a\x81\xf6\t\xb5\xf0َ\x8d\x13\x9ey[\xa7{[\xc8h\xdc1r\xa3#\xdc}\x1eB+:>̮\x8eׅ\xa0\xa0\v\xa5N \x97\"\n\xeb\xf1\tܖ\xd1\xe4\xf5\xc57A\xedq0\xaa\xf8\xb7\xe1\xe7\xeat\xf0#I\x8a$O*}\x11~\x14\xa3\xf1\xff\xa4\x8c\xc0\xf5\xa2T\x9a\x87\x12w\x84\xba\xaaD\xcfE\x15\x86\x1f\t7^\x14\xa8 \x9a\x87\x9e\x00ٝ\xed#D\xbe(\xbc%\xf2\xfdM\xf3*\xf96X>$\xf0\x90\x9d@p\x14^\x11 \x96\xb7\x93\r\xc0\xec\")\tzA\xec\xe3G\xf9\x84\xd6%\fb\xf4>\xa2\x13>\x15\xcfT\xb1^\x12}\xb7\xc7;\x11\x81\r\xb3\x18O\x18\x83\xb7ǘQ\xaa\xb3;c-/\n^\xef\xe4#7w\xf5Vt\x02\ts&\xed\xeb\x8aG'*\x01\x87-#\xc1צ\xc6\x1b\xc0\x8a\xaf\xde\x00Q%\xc4\x00\xc3^M'p\xdcc\x8e\xa2\"\a\xbc\xed-\xebW\xf9\xd5+*\x16m9z\xe6LԎ\xe3\xb8p\x91\xa3\xfd\xb8\xa3\x88\x1e\xf6힒\x12\xf0{ɑ]\x8f}\x1b}\x1c\xdd\x18\xe9(\xa2b\xef\xe20}\xab\xc3;ax]\xc0\x1a\x89\xd3\xd1t\x97[)\xc2\xc6Q\xe8\xa5\xda@\x97\xd8]\xd0\x05g\x89Д\x00\x9b\v\xbb\xb6\x1e\xa6vÉ7g\x1c\x06\xea㮆\xa5'fToJ\x95fȬ\xc6\xec\"\xa7R\xa2\xca\x19\xc3ۚ\xeb˫\x1bDŽ\x02\x13*\nS\x14\x1f?u\x06\xfe=95\xd9j\xa4U\xde!9F\xe6\xb19\xa7\xeej.\xde\b\x8f4\xbdp|\xed\xa0P\x89{wΔ\x9c\x86\x9a\"Nz(m\xdcW`\x8c\xb5\xb2yخ\xa1\xc1\xaa\xf6`Ű\xa2\x91\xcdKf\xe6?~\x1aF\xdf\xcam(ȑX\xc60\xaa\x86\xd9s\xe3r\xe9\x15\xa8\xed6\xa1\xf5D\x10#\x96P\x022\t\xd1='\xeb\xec\x80\xe0\x8e\xccH\xb4\x11\x01\xa9\x10\xc1\x19B\x11L\"\xce-\x1f0j\xe30d\x18\xefNG\x94\x8f\x98\xf7̏r\xb4F=/\xbft\xe3\xe4\x1du\x03\x00\xd3?\xc9\"J\xe2u*\xb1\xb8\xa8/\x97^]2Q.U\xb5\xac\xb9\xfa\xc8ԩ\x8f\\\x85\xe8\xb4|\xc8O\x87\x11Y\a\xa6w\x96/\u007f\a^\xdd\xff\xdb\xe3p\xe2\x969\xcbߡ\x8b\x1a%\x9cԞ\xe3\xf6\x05\xf3v\xb5\xcc\x1e%\x1e\xdb\xc7\xc8(\f\xfa-\xbc\xa1FʋkB\xbe\x02\x1e\x0e\x89e\x82Nk\xde=vuP37\x1dg\x02\xcf\xc1\xab\xef,\x9f\xb0\t\xec}\xfa\x0f\xfbQ\xceįK\f\u007fL\xc0\v\xd2\x11\x19\xb1\x1b\xb5\x02Z\xb1\x04\xec>\xbb\x06\xfd:M\x95\x12\xc2\xdaN\x9c\x11⏆\xfc\xb0\x8a/\x85\u007f\"[I}II}{R\u0085\xf0w\xefu\n\xebR\xe3_\x98\x80\xd2\xdcKn\xb0\xb6x\x88\x16RFmX`HS]\xc7\xce}G\x82ŝ\x83-g(\x9bK\x8dq\x05A\x8c\x85M\xd0\"\x89\xcf\x03\xf6\xb8\xf6\bqpn\x88\v\x938o|\x05\x835R\xb1g\xbc\x89\xf0\xf5\xcf1:?\x1d\x02M\n\x9dN\x01\x8f\xe8\x14\xad\n\x1d<\x82/@\x13\xb9\x88\xda\xea\x8a\x01U=\x03\x8b\x86x\x83\xcd\xdfoZ\xb9N?䞧\xef\x19\xa2\xd7m\x18\xf1Yq\x1d\x1d\x8e\x01\xfc\xc3\xfbo~Z\xc87\xdaZ\\\xf7Cѝ\xb7\xf9\xa6-\x99:\xb1O\xa6\xa6\x1c\xfd\xd34\xd5\x15\xc7u\xa2\xf9\u007f\x90\xfay\xa9\x91\t\xf5\xc3=Q\x05\x04\xb4\f\x01\v\xd0W\\A\x86\x18F[%2\x1d|\xc4\xfd\x95\xe3\t\xdcBbE\x8d6RM\xc2|u\xd5\xf3\xd9\xeb\x12\xc9\x16\x89B)\xb9~]\xa2T\xa0 \x0e\xf4\x88\x89\x1a\x9eu:\x87\x19L\xdd*|\x00\f<\xa0\xd7YR-fgg}\xa3\x9f\xfd\xf3L\xbab\x9eu\xfa}\xceaLW\xe5W\xacЈR\xbd\x951a\x80%\x17O0\x96<\xac;\x03\xc3,\x05\xbcA\x06[w\x99\x04\x91*\xe0\rX\v\x15\x95\x18'\xf2\x06\x81\x95\xdc!(\xa8\x0e=\xef\x17\xdei\xc0\xa8}\xa8\x80&?\xd0\xe3#\xae\x19^$\t^\x14\xd12)\xddm4\x15\xa1\xdasD\xd1\xc2E|g\x99P\xc3\x14b\xbb2\xfcD\x00q\x1e>\x91\x89\xb4\x93\x15\xcbn\x03.*\xe6?\xc1W̸x\x9f\xc8(Ļ8\xf4s\xfbD\x0e\xc1\x8d\x89SD<\\\xa3\xf4\"\x1e\x15\x805\xf93Ps\x14\aA90\x907\x13\xcc@\xb7R\xec\x10\xb9\x95\fF\xc3q\v1x\xc1od\xfc\x18YХ\x04&\xe1\xf3\x10\x85]\xfc\x14b\x12\x8c\x04nʁ\xcadb\x89\x8bz\x11y\xc6a(r\xe2j\xf1~\x9f\x00\x97\x87}@\xa2\xac8\xbf\x80\r\xab\x17\x8a\t>\x15\xbb\xb4\xc9>\x1a4\xa4\xa0J\x15\xf3.]\xb2\x0f\xd0\xc3R\x8c\xc6RŨ\xf4\xbc\x81\x9b\v2\xf3\xdb\x17*F\nA\x0f\xfd6\xc8r\xa4\xa4\xfb]\xc5\x16\xaeeH}KK۔\xbf\xadJ\x99\u007f\xfbҡ\xf4Ob\x1d\x0fƆ\xfd\x05\x8d\xc6\xe8\xd0\xe8\xefL\xa3\nG\xbe\fhN'\x16%+Sx\x89̒jU\x98,\x0e\xb3V/\xe3}\x8d2\x89D5\x98NwY8\x85G\xc9\xd0\xd2,\xa9Je\xaa\x06\xc1\x05\x16\x9bA\xac\x1eh*c\x18\x9a幔\u0082\xa2\xcc\x15\xf9\xe5\xd3wޡ\xcf.\xb6\a\xe5\xf40\xe0\x9b\xdc{D\x06\xe0x\x96\xa6\x01Sf\xaaѢ\x89\xc32\xbfw\xff$\xa5F\x96-\x01\xac:W\xc1Y\\\xe9\xf4\x10\xa5D,o\xf4Iy\xa0ך\x1d\x16\x93\xd2nN\x91I\xc5\x16\x85\t\xfe,i\xb0\xb2)\x16\xbdm\xb0#Y\xd1Ǫ\xe0\x98\x12\xafj\xa0U\x99-3\x18\xd5\xd6\xeb\xafY\x1b$v\x9d%%3\xb5Z\x91\xecp\xaa\xbc\x01V\U00092c97.#\xcfcNf.\x8b5\f\xa3\xd0d\xe6\x82$\xd8\xf6\xedC\x0f}\xfb\x90\u007f\xe6,\xc0KSצIX\x0e\xfe$fX\xfa\x02͊D\xb2\xf4M\xf0^uV\xa9J\xcb0R\xae\xef\xeb\x8cs\x030=t\x02\x18\x0e\xda\x19@k\xaaT\xe6\x12o\x1a\xc7\xf2RZ$\xe1\xe5b\xb5X\xc7\xce*e\xe5V\xb5E\xc4\xfcW\x12\xed\xcfϕ\x8b5\x92\xb2T0\x94\xd1T\xbb\xb3nk\xe4\x1c\xeb\xfcޑ\n\x13\xfb\xdb7&\x1f\x9b$2\xd1i\x12y\xaeT\ahF7\x82\xd6\xd3\xd3\xe0\x13u\xf5bqe\xe8\xfcy\x00\xd8#l\x92R\a\x18\x95*[)I\xa3\xd5\xf2\xf7\xfe\xebM\xba\x89k\\\x9e\xed\xea\xaba\xa4#\xbd\xfeu[\xd5N^\x92\xac3Vq\xacא\x10nL\xa9\x94(\x1cv\xcf\\\x8e\x1b\x91\x9e\x10f\xabT\xe2\xbc\x14GQ\x8eI7p\xe6\xcc=3?\x9a\x9bקw\x8d(sn\xfb\x15Y\x9aIS\xb2\xa0\x1fM\xe7g''g\x15\xd0\xcc\xc1aFm\x9aL*1\xa6\xa6J\xa4J\xbd2U,\xb7\xa0O\xa6\xaa\xa1\xa5}}\xae\x9c\xa0]\xe3\x94&k9-\xc3\x02\x0e\xc8D\x99\x8c\x88\xa5\xedi\x19-%\xab}jS*0\xab\x93\x94\x8c\x92\xf6XX\xad\xa7\xccW\xa3\x10\xab\x14b%\xb3\x1a\xfec\xf8\x9dR\x1d\xa3LR)\x95\x96$M\xf1\xea\xd2\x16\x87\xcdNK\xe9,N\x8e\xf2\xe1\x18\x94c\x92إ\xb1Udf\xf9\xfaI\xe8\xc2$\x15\xeaD\x16\xb9Ģ\xd6*$R\x8b\xd5 f\x9eLM\xb6Mu\xaeLձK\xb37\x96)lJeh\x9aZ%\x05\x8bV1՛\n\xa7ڒS\xb5\xac.u\xe5\xd64e\xd9\xc6l\x91J=\xb5RS\xb9j>\x8b\xdar\xf4l\xc6\xedڮ\xd3\xf2b\xfd\xfa\xde4\xbd\xfe\xd8\xe2%ǎ-Y\f]\xa8#\xa6,E\x83J\xc6\f\xe8\xf3\x12\xdb؈\x9a]?\xbc\x81S\xd1gz-K\x16\x8b\xb4\xea=\xa9\xf4:\x93b\xfb\x9b\x81\xc2\xd7\xf7+\f4\x83A|h\x1e\x8c\xc9FCR\xac(\xe4\xc4\"\x0e\xbb\xb6\x04\x12\xbdF'ch\xa0)\xad\x90\x88=\nEj\x06j\x96\xe8\x06\xa5\xba\xffR\x99\xdc7\xdb\ufae7\xe9\xdeW*J\x16\x94\x17o\x99\xc4J\x80\x88\xd6\xeaL2\x85lX\x9f\xf4\xb3\x06\xc3\xeeB\x87\x91a\f\x96\xdea\x90\xef\xafr\xd9\xc1\xa0:\xd4\u007f\x92\xf4Z\x96cůM\xe8\xb5\xcd?\xdb'\x97-\xeb\xa7V\x16\xa2\xe2\xd7\v\x83\xef\xf5x\xdb\xefn\x81\v\x1d\x1d\xac\x86\xf8]m\xeaP\xad\xcfQ\xa6Y\x88\xe5\x05\v5e\x0e\x1fS\xdb\xc30\x1b\xfe \x88\xd3Ư_?^\b\xed:w.r\x17MP\x11\tToܞL\"\xe0ʛ\b_\x87\xd7b^\x8d\xa3GS\xf8\b7e\xe8\xa9ZUd\xe4\xc3\xd7\xc3\nl\x06\x8cX\xa5\xd1\xdb\xd1\xd1\xe6\x16\xd9\x1d>\xafͧAGM1\t\x9b\xfc\xe8\x0e\x13\x82\xad\xe10\b\x85B\xf0ǖ\x16\xf8c(\x04B\xe10lEguK\vP\x87\xb8p\x1bl\nG\xdb\xda»v\x85\xdbh[\x18\x1c!A\xa19\xe3v\rqo\x0f9\x04\xf5\xa2\x17\x91\x9cb\\\x18\xa2\x88\xa4\xc1#\x15\x9d}v\xce@\x9c0\xfb4>\x87\xc1\x89\nB4ZQ)\x89?ݘ\xc1:>\x13\xfbu\x83\x18\x8dX\x18\xee\xa0 vn\x1b\xe6(\x80\xb1z\xb1HE\x84~\x1d\xc2\x19\xa2\xd8\bJń\xb1s\xd4(\xea\xc17Pz\xec\vXx\x8a\xa5@\xdc?n;\x16\xfa\xa3\b\xc1\xe7\x03\x0eE)҃\xc24\xeeE\xf8\x01J\xc0\xceA\x15CuJ\xeb\x94\x11yc>,Fu\xafU\xf7\xbai\x1c\xf1\x1a\x82\xc4Z:\f^\xa7\xbd\xab\xaa\xd8{\xb0\x1d\xfdP?\xf3\xd9cY\xa1ոOB\xea\x8ak\x82\xfe\x84\xa23Xt\x845\x9eP\xad\xa3T\xb8\x1dEr\xe8ׁn\xa0*\b~)pD\xfc\x11\x86\xc0\x18\v\xcf\xe2\x1fM\xce0\xe6\x1c\x98\x1c;bMA\x93폨p\a\xb5[인\x01\xbaւ\x1a\x8c\xef\t4\x89\xa3\x03]\x88Lv\xdcky\x96\x8a4a\x00\x14.\x94YB\\\x14\x83\xdbUE/\x145\x00\x1bl\x12bK2#M%\x03\x1aP\xa4\x8aJ\xb4\xb5\x11\x11\xbf\xca\x14v\x02W\x01\xfcθ\xf4\x03\xd3n\xecp\xb1\x9b\xa5\xd0\xf2k\xa9\xf2\x17\xe5\xf0'`\xeb@\x1d\xbb\x04\x9cɴ\xbc`iʌP\xf1W\x03\xea\x9a\x14\xa58Ġ\x0e\x01l\x99%\xcc\x11t\xaf\t%ʌ\x15\x02\xf7SQ\x02~V\x12\xfa\xa6\x95\xd4pj*\xb1\xba\xec\x04$\xf4w\x86\x8d^#G\x9c\xa5\xa01i\xc0\xe0\x106\xa7\x0f\xe3}\x17\x93\xf5\"vw\xe5\"\x96\xcc\x01b\xba\xe6\x13\xeb\xfezrMZښ\x93\u007f]\xd7].\x8c\xcb\xde\xeb?+;\x83z\xbb\x03\x8d\x83\u007f\xa7\xe8#\x98\x91#J\xfd\xcf\xcf\xf8z\xc6\xf3\xfe\u007f\xbf\xe4\xe7\x9e~:\xa2\xdc\xfevv\xf6\xdbۻ\xf7\xa7\xfe\xff\xbb\xfe$\xe2\xed\xae\xff\xac3\xdd1\x9b~e\xf6\x1d\xff\xbb\x8e\xe4ݹ\xd3+t\xa1\x84\uf822J\xb1G;\xae\aI\t\x04\xc5\x01\x8f\xd8mW\x8ay\xabؤ\xebq\x97k\xeb*\xf9dƜ^VX_<:7''wtq}aY\xba\x99a#\xb7\x8a\x9d\xdc\xf5TH\xab\f\xe33:\x84\x02\xcd#\x1bC\xb5y\x95V\x8b\xc5Z\x99W\x1bj\x1c\xd9\x1c\xb8U\x1c֕\x89?\x94\xa0;A\xa1Y|\x16\xfa.d\xb77\xe6R]\xe3\x16\x02\xa8\xd4&\xbcODh<*z@\xe7\x8f\x01\x8e\ti݉Aw\x11\xf1\x05N\x0e\xe8A\x02\xd2%L\n@\xa8v\x91I0\xe3c\xd0\xe2\xc8\x16*\xf0T\x91\x83\x03\xf8\xdd.3\x8d\x96\xc5\xd29R\x12[\xe4VJЩ\xb2\x88\x13\x0f\xaf,\xad\xea՜\x9eb\x9b\xbaM1W\xd4R\x1f\r\x0f\x9f\x03߫\xdb>EƉ\xb6N,\xf6\f`õ\xbe\xf0\xf8\x82>U\x1e8\xccz\x02\x9f\xdb\xf2\x1d\xf0\x92\xbb\x02/{\x93\xb32\xc03\x19Y\xbf\xe0h\xdb확b\xba\xca\x13^\xe1\x1dāpQ\xba\xbf\x90\xbf{\xea/\xdeRX\x97\x94_߲d8Ȭ\x99\xde6e;\x98\xb8\xceзk\xaf\xa7\t}\xe3B\n\x03r\xe1fq\b\xb6 \xc9 \x0e\xc2\bHˠf\xf1ŬD\x1c\t\xe7ζ\xf0\xe3%\x12\xcf\b,\x99\x1bĬ\xfe\x84m\n?sx\\\x95\xadj\\\xd5\x01W\xc8W\x8bUqC\xf4S\xe9~\xbe\x8e\xab\x12\xe2m\xcflY\x94\xa63M\xd9>\xf3\x1eq\x9d\xf2\xf6\xa1\xd1\xfa\xdes3`ػo\xd6\xe0\xa2\xedSL\xba4.\\剶\xd0jl\x1e\x1a\xfd\xf1\x06u\xd6[\xeb\xcbI\x87\x9477\x1d쵥\x80\x9f\x88\xdd\xe8\x8f\xf1\x04\xf4\x0e\xcf\xeb\xfd\xcb\xd9\xedS4\xa2m3\xa123\a\xce\x19\xd2\x1cȧ\xa9ꑳ\xf6\xa5\x83\xa7\xa7lg\xcb\xe3\xfb@\x82\x0e\xb0\v͢\x03\xa9\xc9؏1\x87\xd7W\x82\x98%`\x17T\xc6;\x91\xa6\xb9\x98\xba\x12ω\x18\xcc\u007f\n\xf0ExC\x86t\x19\x9e#\x18\xbd8*\b\x18g\x1c\x96\x9a3\xc40G\x02x{\x92!w\x05\x1b\u007f>滢\x1cx\x89i$\x16\xf4pl\xe1ɣ\x15\xa6`\r\a;f\xed\xdb7kA\xee\xc0\xb1\xfbfy\xf2\xe8\xc5h\x00\xef\x9b3\x12>>\xee\xee\x83G\xad\x19U\x1e\xb3\x1e4\x14V\x80\x10\x0e\xc1O-\xda\x1c\xb5\xba\xa2H\xaf\x05M\u058co\xa3K\x93\x8c\xbe\xda<'\xad\x8c\x92\x15)m\xba\xea\x99?\xa7\xa1\x06\f\xcb\xf1\xa3%\xe8{[2p\xbb\x97\x16\xf7\xf5\xb8\xe0;\xe1\xed\x85>κ\xa4\xafK\xba\xef\xc2>\x8de}\xfd\xac}\x9a\xbf\xee\x9b\x15\x9dڸ\xd50\xc2D\xbf\xd5\u007f\xa02`\xf7TI\x0fH\xeb\x8bnP(\xb0A!6\x1b\x9cƢ\x90\xe4\x882\xc0h\xae\x89k}\x99U\xca3\xa1\xdaY\xb5\xb3ެș\x1e\xa1t#d}s\xe9\xfb|\xb5\xab\xed\x85\xf0\x92'\xd8\xcfs\xfe|\xbf\\\xf1P_\xf6\x00\xcd\xf6ξGփ\x19\x04\x1f\x0f\xf5$\xd0\xe9į\x1c8;\x99\x15\xfc\x11\xdcB\x18h\x8aQ\xd7\",\x17Ƙ\xc5\xe0\x86\x06{\f5\xc9k'Z\x15\xb1U\x01\x1eָߚ8\x8c\xce\x1c\x13~\xad)\x9f\x97A\xf7^\xdaR\x0f\xc3\xf5-\xf0\x8b\xe8\xa7\xf5-\x8f.\x03\x0ffG\x1b\xa6\xee\x16W\xb6ԋZ\xc7G\u007f\xeb\x0eE*\xcd.F\xad\x91zӘP\xa4\x15\x85\xc5\x03\xf2\xe8\xf0\xd8\xcc\x12.$-J\x83}\xab&\xa0\xb1\\\xa8V\x80\xf2\xa4T\xacTnv\x89\xa8\x92\xc2\xc8\xdf\xee?\x03\x0fa\x8f/'\xefn\xa9\xb7-{4\xbcyʐ\x19\xb6\xfa\x96\xeb\xad`ʡ5\x8c\xa2\xd8e\xb69<\xfa4\x97\xcde\xceU斕d\xaaT\xad\xa9\xce\tU6\xb3\x8b?\xac\xf0\xa4\xbcA\x04X\x02&\x1e\xe6튨Řf\xa15?\x1aM\xe4\xe0\x8fA\xbc\xa5\x80\x18\xd4\x1b6\xa8eb\x90$\xd8d\xc9\xc1`\xedt\a\x13\a\x93\xd2%\x04Q\x83\xe9p3\x15\x05\x80`\xa8\xc7s\xbeb3\b\xc4\xc1N\x18\x87\x0e\a\xc1\xbb\x83n\xd3MSp\x8b\xf9U\xe35\xebG\r[\xa7\x1f6C\xbfn\xd8\xe8\x8d\xcaq\xcb\xf9\x95Ҁ\xb1 \xbd0y\xe6\xbe\xd2\"\xc8U\x8f(t\x95K\x1e\\\xb3SR\xee*\b1\x1b\xccS$AW~\x15\xb3\x98g\xc5S\xc5\xc5v\xfa\xb9\xect\xd0QR[\x8c\x86\xea\xd9\xd0\x00\x86\r\xe5\xbb\xcb%\x8b\xcd\xfb\x98\x8a\x1b\xd4\xc4Z\xb0\xb3ԛg\x04\x9f\xa4X\xc7o\x91\x8e\x983c(|\x10\x9c\x18:c\xd1(\xe9\x9d\xe3\x93\x1c\x90\xe2s\xd4V\x99l\xcf\xcc`\x8b\vn\xf0\x8a\xc2\x05\xae\xe8Hz\x8c\xab\xa0*_\xa5\x88~\x02\xeeuz\xabP5\xa0\xf8\xacX\"\xd6ݫ\x17\xbf~P\xab\x14\xf4\xa0]\xe1\xe0\x88\xc9#jDy\xf0\x02\xfc\xf1\x8d%K\xde\x00j\x90\v\xd4$\xf4\xd1-v!\x98\xcaF\xbb\x16~3\xf02ܪQ\xaa5`.|\x00\xe7\x83ap\x92\xd2\xee\x9b>nw\x86\x94\xf1/y\x03\xfe\xd8#?X\xdb##\x14J\xacw\x1e\xa25\xc4\xd3\x15(\n\xf8\xf3\xb1\x91\x1f\x9a\xa0\xb8Nx\xa34슩\x02q\x87\x1eV\xe0\x15\xec\xff^\xb2\x9b\x9a\xa6\xc7\xfe\x11=~\u007f\xb3R\x96'Ҫe,\xabҧX\x9d\xba\xba\xc9M\x03\x9d}\xd5j\x99J-\xf6)T\x8c:\xd7א\xb7\xe7w\xaf3r\x94T\x9a'\xd6\xfc\x8b\xa4\xbb\xdfx\xdd}scF\x1f\xbcy\xf3\b\xe47k\xb5\r\n\x96V0\xac\\\xa5\x94\xf3S\a\xd5M\xb1(\x952@\xcb\a\xebu\xac:-Y\u007fzǮS8\x95\x92\xf9W\xa9\xd8\xc2[4;0\xdc\xe2\x1b\xe2q\x14\xba\xd1Ʒr6\xa2SBIX\x13\uf580\x80\x84q\aL\x12\xc0\xa3\xfft\x1b&t\xd1&\xfa\xc8#M\x03\xa0\r\xb4\x9d\x86\x9f\xd1G\xe8#\xd1&t\rڠ\xed4\xb07\xc10݆\x85\x9c\xf8\x06I\x86\xa3\xd3p\xa2X2\xfc\xd8\xe7M Lu\x93\x1b\xe1w\xba\x11\xe9Do2\xf1\x12`\n\xb8%\\\xc0\x1d\x90\x007߳\xeb\xd2g\x81\n^mlm\x82W\x81)s\xd4\x1aX\xc6\xe4\x827a\x19\xfco`B\xb1\xc0\x04\xaff\x8eb\xeanQ\xc9\xe7\xb01J\xe3)\x94\x04?\x18F\x8fT\x817ѣ\xff\x8d\xb2;\x85\xb2C\x0f6\x82\xeb\xb7\xe8\x94XV}EBq\x19\xa8\x9c:\xca\x12\xf3\xb2ٗ\x1a\x86zh\xb8\xbbW\x80\xf8\xae*\x17S/\xf3\x13'\xc2\xc4W\nI\x85\xa9~F,\xe4\x15\xd4앀\x00\xb0\x01\f\xcdUd\xa5\r\xc5A:\xaeɫ\xb3+\x89z:\x96\x06b\x8d\x0e\xb44'\xf0Ŵ\x8f\xa8\xcd؉\xf3szk\xc0\xe5\x0e\x04ܮ\x00\xbb.08\x10\x18\x1cq/8\xb2\x00\xfd\xb1k\x17\xd4\x0fY\xb8\xe0H\xa4\xef\xd1E\x8b\x8f>\xf8\xf5Qv\xdd\xd1ŋ\x8e\xa2\x8b\xc8g\xf0\xbfO\xdd~aժ\v\xb7\x9fb\x1e\x83\xf0\x03x\x1a.\xb9\xb0\u007f쨽\xe7\xe8\xa1\xf0'\xb8\x0e\xbbT\x00\xabY\xb0&7(\x99w\x00^;\xb8\xf1\xdb\xfa\xfc\x06\xd9\b[\xfdՍ\a\xe1\xb5\x03\xf3$\xc1\\0w/\xb8\xef\x8b6p'\x9d\"\xbc>@\xe3\xb7\xfb'\xe0w.X\x00H\x19Zɋ\x8f\x02\xf4\xfb\xfa(\xcc\x04\xab\x81j\xd5\xc5\xf6\x8b\xabX\xd9\xfcyc\x0f\\X\xb2\xe8\xfd{'Dy\x1c\x8d>\x03z-\xcbz\xd7x\xefy\xe9>xm\u007f˔\x92\x95\xc6ۜS\x16\xec\a\xe2\xfb^\xba\a\xc5O]Ђ\xfa\xcc\xf4\x1b\x14{\x80\xd0E\x1d\xd6\x17&`\x8d\xe8`\xd0w)\xe7\x00+\xc0\x1e\xc9ySL\xf9\x1d\xad>c\xca\xe5\x01\xacu\xe4a\x04=$+\x8bh)V,\xb2\x02\xa67\xdc\x02\u007f\x01R\xb0\x1cH\xe1\xbe\x17֯\u007fa=\xc8U\xb0\x8a\xcc<\xf7\xa235@f\xb5\xca\xd3F\xa6\xf59\x03\u007fN\x1b\x89\x82i@6\xe0݅\xee\xbcL\x94D\x9aQ\x10\xb2s\xfa\xaa\x01-\xa5c\x1fr\xba졂\fz\t\x90\xbe\xfc\n\xca\xe9\x97W^\x06\a\u05cf\x1f\xb7~\xfd\xb8\xf1чS\xf22\xb2\xec\xc95\x86\x01$\x17\x85\xd5Z}\x06\xfe݊\x02#q~\x86\x9ad{VF^\x8aުԚY\xa5\xc3l\xf4&'\x9b\xb5Jk\x02~\x18O\xf9\xa9 \xd1V\x8d\xef\xda{\x80\x88W\xd2\xe9\xae|\x12\u009aG&\xac$\x84\x9dd\xa1\x19\x15]\xa2\xa3\xbf8\x9f\xc6\xfc/\xadv\xd9Dj\xa3\xed&\xf1\xf1\xfd\xe37\x8c\x1f\xbf\x01x\xa5\x19\xbdҤ\xaeU떦\xa4\xa4\xf5ʐ\x1a3\xfb\f\xbb\xdb{W\xa1\xd1(1\x96\x1bO-\x1c\x84\x8e\x12\xa3\xf1T\xf1\xf6\xe1}2\xfb\xbf\x06\xff\xfe\xdak@N\xafH\x84:e \xcei|\xf4\x17}\x12\x97,N\xca\xcc\xd0j\x93\xb9$}^\xaf\\\x9f\xb2\xf8\xae\x82X\x06\x8b\xea\x84,_+V\xfar{\x01-\x90\xbf\x86s\x03\xdfv\xc77\x15d\x10/\xa0zk\x05\xbfux\x91C4\xa1\x119\x889/\xef\xe4\xc8%\xa0\x93\x15\x15\xa5V\x8e\xd9\xf2\x15<\xfd\xe4S\xf0\xf4\xd7[ƅ\xe8\xd3\xf9\x0e\xb0\xc7ٷ\x10\xad\xfd_\x85\xaf:<\x85}3\xc0^;\x17\x1e[\x19\xbd\xfe\x14l\xfdz\xf3\xe6\xafA\xe8)\x9a\x0f\x8d\xeb\xb8d\xc7\x00\x8b\x85}\xed\xf0\x1d\xe0\xb7\xf7-\xf4\xa6\xc3U\xf6\x98\x8e\xfa\xbd\x88\x06\xcc\xc0}\x8e\x03\xd8\xd4\xc6\xe5sQ\x18\x10\xba\xd8\xe5\xb3\x1b\x94\xb4\xc9H\x99\xb0\x92:\x8dz\x9b\x8f3\b\n\\D\xbd\xce_\xec+B\xab\x0e\x14\xc53F\xad\txh\x94\x00\u007f&\x8a\xe7>\x84\x97\x93\xe1ϕ\xc0\xd7\x00\x8f\x8d4\x8c]\x9c\v\xe8\xfe\xee\xa1\xc5j3\xb8=/\xed#\xa3\xee\xc3T\xd7Q\x1a\xf4\xeec\xb0ϱͫH\xaa\x9e\bB\x17w\xeb\x82\v\xed\x17\x15_\xf1\xe0Ee\xff^f\xf0\x1e\x00[\x83џ\xec3\xe8\xe7\v\xa376\x02\x00N3\xfaw\x8a\x16\x8d\xe4\\\xe2\"\xdaR\xe6\xe8\x15\xd91\xb5\x1c\x1c\xccv\x83/}}\xe9\"\x90O{<\xfd\xfeZ\xfd\xe1\xde@!\xcdg\x88\x00(\xa4\x83E\xb0\x9f=\n5\xccuW\xa1\x12 \xaa\x92\xcbn\xef\b\xd5&\xe0iK\xa9$j!\xe2jw%P<\xbc\xf2T\xb2<\b\xb2N=\x8f\xb1\xb1Q\xfb\xa3Z\x92UA\x1a\xd9nŀ8\x042\x1b\xd1+\xb2^Ra>?\x801\t\xd1E>\xf1\xec\x879\xdd|\xb2.\x10\x11\xf7mV\xec\x0f\x18\r\xe5\n40\x05\r\x8f\xc4\x19\xbb\xdbl\xc0\x84\x97\xe0K\xf0҇k\xd6|\b2A?\x90\xf9\xe1g\xb7\x98`\xe8\xf5f\x17.\x8e}\x18\xddW\xe7\x1e\aF\xb5\\\xa4\x93\xcb[\x0eXQ:\xaeJ1\xb7D\xa2\b\xd5~\xbaNN\xe3*(\x06|C^\xbe&\xb1@\xf4\xda\u007f\xc6G\x81\xd8\xdcj\xa4\xd21:\x0e\xc0;kN\x17\xfa\\\xfe\f\t0\xb2\x01ƅf\x12\xb5Ө\xa5\x11\xc5p\x02?\xed\xce\xc0\xf8$\x88\xb00\xb7\xff\xf8\x87o\x97G\xcdG\xe0\u07fd\xf0\xbb0\x98\x87\x16\x8dC\a\x00は/\xc0\x87\xde\x12\xfd\xae\x8c\x99z\xee\xee\xaf\xe1\xdf\xc1\xdeF\xd94X\xd2~\xf2d\xfbI\x11E\xaf\xd8\xf4\x83[\xf2\xf0.\xf0\xc8\xfd\x8f\xc39љw\xefI\x85\xe5\xf6\xeb`\xcd\x15 \v샧\xe0'\xd1a\x1b\x95\xf4\xfc\xf5\xa0b\xa9\xe8$~\b\x8f+\x1a\xf7/\xeem\xb2\x9b`\xa3\xdc.\x1a-\xac\x99 \x16Q\x04\xf0\x18b\x88\xce'͛\xdc\"+6\x04\xc2X\x1bJ\x16̓n+\xc0fA\x1e\x1c0\xa1\xb2\xb3\x94\xceH+\x01\xcbl\x86_\xc1\xbesʴ\xfd\xee\x9d!\x93-Td\u007f\xbfؿ\x9eO\xae\xf5\x8e\x10\xabdɜiL\x89j\xab\xd6\xe0\xad\xcf\xf2N\xa8q\x96\x97J\xd0\xf2ɘe\xee\xfd\xe8\xed\x03O\x1e\xd9;;%G\xdc'o\xd4\xd4\x14\xd5\xce;\x00\"),=\xe2\x81K\xf0\xea\r\n\xe4][\x0f\x86\x83\xbe g<\xfcF\xc9h\x86.\xa4\xf3~\xdf[\x8c\x18?\xc0\ru\xf0\xa6\x02\xe9\xab}r\x06\x95\xa4\xf0\x12\xaf\x9bf\xcb2h^\xab\x103\x13\x87\xca\xcas\xd2j\xa6\xfbƾ\xfb\x84\xcb5\xac\xffq0f\xfe 8\x1b\xbe\xb1\xe6\x06u\xe5Ĕ\xb8,'\x86\xe3\x1f\x10\xfc5\xb2D\xc5\x15k\u007f\xa2\xf9)@\f?\\\x98\xe0a\xd8\xd9^\xe8\xbb\x01=M\xa0\x14\xfcZ_1\xed&\xbe\x15\xb5\xdc\xc5c\xaf\x1f\x84\xdfM\xaf\x1dͲ\xa3k\xa7\x03\xfd\xc1\u05cf\xdd\x06\xcf>\x9a\xaa|\x12\xfe\xee\xcbM\xb8o<\xc7<\x02\n\xc1\x83\a\xb64/\xbdc遷\xde<\xb0l\xf3\xb2ٛ\xef\xe1,\xf3v\xad\x19\u07fe={{\xfb\xf85\xbb\xe6\xcdY\x0e\xc4{~\x00\xd5'\x9f\xc3=\t,\x8b\\k\x85\x8f\xad\xae\x18^\x02&\u007f\xf9'0\xb9tX\xe5\xed\xf0Dl}\xa2F\xdf\xedG*\x87\xf2Q\x15T?\xe2\xef\xc6.\xacZ\x11ۂK\x8d\n\x89u-\x02Z\xa7\x88\xd1Rhu\x82\x81\xcc0\f\x8e\x91!$\x1b\u007f7@d~X\xc1\x15\xd8ɢ\x16\x11Ŏ\xb5\x1f\xef\x99\xf2x\x11x\xb8\xe4+x\ue457\x1f\xfd\xf2\xa1\xef\xf34\xe3\xde\x02\xfa\x17\xfeV\x01^\x04\xc9V\x15u\xe3\xe9P\xf3\x88\x82\xdai\xfdf\r\x9f\xb3\xeb\xb6w\xfbz\xaf\xbf9i\xe4\xa2{V<\xef\x99\f\xaeї\xb8Kw\xef\xf8#=\xaa\xa4`\xd7\x1b\xe3\x87\xdf\xff\xf7\x8d\xc3\x16\x03~ёޏ\x82\xe6_\x86\xc0\xefф3\x11,1\a&W->\xfe\x1cxj\xd8\xe4~\xf9\x8f\xce\xdfܱj\xe4\xf8a\x03>\xddt\x96\x1ex\xd7k\xaf\xc5\xe5la^\xf03\x82q\x01n\xb9\xabi\xb8i\xbfЗ\xb81M\xe9\x14\xd7Ɏ\xa5H\xd8͌\xda\x00و\x88\x90\x8d\b\xd0\x14\xb5\xe1\rKQ\xa8j\x02\xb01$a\x04\xefg2g#\x82\xfeK|\xbf!\x1c\xd3y\x11\xcaeD\xf3\xe2\x9fQ\xb9Lx\xe7X\xe7\xc5{i\x82\x124\xfa\x1f{{V\xa7\xb9\xa3\x9f\xe1\xb0N\x9f\x9bl\xb5\t\xae\xafѨr\xba\xee|\xa3_I\x86G\xc9$iu,\xed\xb5\x96N\x84?\x16TW\xb3߂bt*x\xfa\x82\x1a\xe6\xd0\xfa\xecA\x81\x95u\xb6\xec\xf2t\x87A\xaaՏ\xe8\x9d7\xa8\xd4\xebЀ\v\xd5\\84\xa2d\xe9\xc6ه&\x8e\xd6I~\x18\xfbXsu\x01\x97\x84\x1fl\xff\xb6\xa0\xfa\x030eZ\xde\xc0~\x85rsUJ\xf5kG\x8f\x9e\x19\xec\xca\n)\xe42S~\xa1m\xea\x93\xc2\xfaVy\x83\xe2n#\xf2\x92~\xd4c\xd4\x1bhV\xe5\x05\x88\x10A\x15\x1a+\x90c%\xee\x98Y\x14Y\xc4\xe1 Z!\x18\xf9\x9b\xadW\x021\xd3\x15\x93\x91\xd3\x13\x88\xe2t\x92\x89\xcfA\xf21y51\x8b+A\x95\x1dE\xa6\x818\xec\xb1\xe0\x8bI\x13Co\x13.\xd1\x1a\x12\xb7V\xec3\xea\xb1\xe5[\f'\x06\x97\x811\xea;\x8b\x8aS\x13\xcdv2\x10Q\x8d\x16\xec:p\xf4ؽ{\xe6/\bf\xcb\xd9b/\a\xb4\x96\xa2\xe9\x93\xc3\x1bvܽ1\xda\xe8l\b\xa4\xf4^:\xa7\xa1\xb0\xa8f\xfc\x90\xf4\xe8\xe1\x91\xf9\xb9\xc6\xe4\xc9y%\x0f\xd0\xfa\xfc\x89\x9d6?a2Gy\x89\x86\xd6\xec\x04\x9b\xd08\xaar\x97mng\xc8\x19ô\xf4\xc50.\xb9\x1eׂ~\xe9\xafX\xabnj\xb5\x88\x8b\xee\x98cpD\b1\xb1\x14N7\x12\x1d0%\x84\xd9p{\x98\xa1\x12\x90\f\x12\x82\x1cUWܥ\xbf҄\xc9oS\xec(آ\v\x1a\x85\t\xe1v-6=\xa4C=s\"\xc1n\xed\xa3\"^\x0e\xbcD\x83͐8'\x14\x19\xb1\xbeݿ\xc6\x02\xfd\x17\r\x8aڊE\x93BTP\x1bAE\x88\xa0U!\x96DwU\x9dI\xf4O\x15\xbee\xab\xa1\x18p$\x9eF\x1b\xfd\x80\xa5Zo\xaa\xb3\x10\x1e|놪\xeb\xde'܈s!}\xc2\xd9\x05q\xe6\"T\xb9\xd3\xf7P\xcc\x0e\xddd\xd4\xff\x9f\xb5\xc3(le\xfe\xca+\x82\x8d\xf9\xab\xaf\nV\xe7\xf1\xebW^\x91Dl\xffY\xd3\xdcs\xeb\xec:\xafa\xdb\xff\xae\xbd\xf4h\x1d\x95I\x95`\xacX\x89\x00\x9a\x14k\xa5\x98\xb5\xfe\xffU\x03q&HI\xcdR\xd8&\x14\xfd\n\x10\xea\xd2\xd1\xf4\x9f5\v\xdd\x1bR\x12\t\xb0\t\r\x82r#\xd9F\xcb\xfe\x83\xc6\x00\x9d\xf0-\xb8\x8f\xaeM\x80\xf5\x14\xfe\x80\a>\b/?\xa6}\xbcD\xca\x00\xa5Le\xe4\xecJ\xa7\xb9\xa0\xa0\x8f{L\xf4\xee'\x80\xfb\xb1\xc7:\xedy\x13\xca\xed!\x88\xae=l\x83\xe2g\xbcw\x82\xc6K\x1a\xc6sC\xfc8\xe6\xcb3\xfcj\x17\xd0wV\b˩}.\xbf\v\xbb\x94\xe0\x02\xc4'\x15v\nc\x05\xb7\xac\xd9U\xd8\f\x0f\xbd\u007f\xf7\xbaQ)I\x9e{W\xe6\x94\xf6-\u007f\x0fLy\xff}0\x14W\xb8_훰\xbd\xb0\x92S%\xb1\f\a\xa4\xb4\x9c\xe6\v\fYIV١g\xbbD\x1d\xf4\xb37\xd7;\xbc\xf5\xbb;Z\xde\x1dX\xd44vh\xc5\x1c\x97H\xbc\xf5;\xa0\xfd\x0en}\x025\x86\xf8\xc9>J1\xa23\xac\x9aU!\xb6P\xec3\x95x\x06d\x8e\x06\xa2}\xeb\xbe?1mډ\xef\xc9w\x94\xb0\x14\xf7\x0f\xd4\x03E\x94\x94R`*\xadA\u007f \x19\x9036\xe3\x85\xe8?M~h\xc0\x8d\x06\xee\xe8Ix\x89Y\x16=\t2\xd9\xc38L\x0f\x81\x97q,\x91\x1b6\xdch\x15=΅\x88\x1d\xba\bP\x8et\xc6\xc5\xd0\xd8{k0f\xf5\xaa\x15\xd67\x01?\x8a\xd4rF\xd1\xe3R\xf8\x1a\xfc\xaf\xaf\ue69c\xdb8`\x84v\ue824G<\xf7\x8d\x98\xb8ؔk\fTzgL\x13+V\x94\x86\x96\x83a\x1dL\xfbwp\x12\x1c\n\xf8#\xa0\n\x88\xea&\x1b\xeeɼS,Y\xbb\x15~>\xf2\xfao~3b\xab\x19\xdc!\x13w\xaecE\x02.\x83\x94 k\xdb\x01\xa3\xb3\xa3\x0e,\xa2\xda)\xb6\xfc\x93O\xa2\x9b>\xf9\x04\x94\xa3\x89\x81\x02\xc7\xe8e \v\xfe1z\a<\x1f\xef\xd7\xf1g\xb5T%5\"\xf6\x98\x9f\x9f<\xa9\xacaIŽRZ\xa2P3C\x9dy(\x97Q\xc1\x810SrO\xf9\x12\xf8\ryI#l\x92YeRivff\xb6T*M\x93\xe5\x14I$E\xd7\xf0\xcbF\xad\"}\xba\xef\rZ\xf42j\x97\x02,}\b2x\x1b\nk:ح\f\xea\xd4\x1a\x91\x1c\xb1~\x18\x94(\xa0\x04\xbc\xdd\xefa\xf3\xd1\n\xaa/P\x8f\xd8\xf9\x1a\x00{\xbe\x01\xf3\xe67w\x1c\x043\x1f\xf9\xc3\x1f߮\x19\a\xbf\x87\x0fl\u007f\xf5g\x9a\xf9\xf2\x0f\x05\xbd\xd5\xf4J\xb1-8\xa4\xa1\xdah\xdc|\xfd\xcd\x03\xf4W\xab\xbfyw\xef\xc8?\xbc\xf9\xf2\x8dW\xe6\x1fm\xb0\x99\xfbx\xe1\xe6\xc0@\xda_\x03\x9a~\xf7\x13\x18>\xb9\xf7\xfa\t\x83V\x0f*1\xab\x00\xe0\x86\xac\xbb'\xde_\x89n\xbd\x80F\x9fBQ\xa8\xa7\xc5X\n\xdc!\xb1\x11I'\xb3\xe4\x95P\x13\xaa\xae!\x06\a\x1bq`3\x15\x11\xe2Q\xfe\x81\xe2ltS\x14\xab\x98\x83tb\u0082\xb8\xa0\xb6\xb6\xaa\t\x9d\xba\x9d/\x13\xbb\x95\x02<\xbf;ɖ\x1c\xee\b\x82?\xc9\x00\xf1&%\b\xac\x11yD,\x1eeOp8jb\x1c\xd8>\xa5\xc8\n\x94@T\xf0\xd1\xc0\x9f\xb7ᄊc\xc4η歿Z\xf7\xc7y\xf0\xfew~\x03?\xba\xb0z\xf5\x05\xe0\xfa\xcdE\xb0\x00\x86\xe8g\x17\xc3Z\xf8\xc3sq\t\xefs\x80\x05\xc7n\xbf\xdfݴŖ'\x97\xe6\xfd2\u007f\xf9\x9d;\xae\xed\x9a\xf7\xd6\xce\x11\xb7\u0379\xfd\xd1\xd6\xd5\x17\xe0G\x88z\xa0,>\xa4\xfb\xc1#Q\xf8Q\x17\xad\x84?_\x85\x8b\x8f\x00bN\x82\xdaɆ\xea\xd1\x16\xc3Ӎ\xe1\x11\x04\xec\xc0\xad\x01i\x88\xce\xd1v\xc0\xed\x8e\x1e\x18njj\u007f\xf6\x05\xf6~\xfd\xee\xe8w`\x1c\x94G\x1e\x05S\x99^`\xdd=\x91O\x173c\xa2\xc9M\x13#\x0f\x81!\xf4\x9aȧt\xafxۄ\xb9\x1f\xc9~\xee\xed\xa8\xa3\x10\x8f\xe4\x9d.k:\xc3\x1c\xb6D!\x9a,茮\x11\xbf\x1a?\xfb:\xcfA\xda\xe8\xd5t\xfa$6\bp9\xe8\x98*\xec> bi(\xf2[\xe9nϠ\xb3A#\x9c鰺I\x8d\xfeh*~\x8e\x86[\x8e\xb4Dqt珓\xf3j`\xb3\xe7\xdamy.\xc3 \xb5\xa67\xaf\ue5e2\xad\xd1e\x16\x015/\xe7\x12\xd3\xd2\xea6u\xd7_T\rBXa\r\xb6\xd2?\xaa\xd5-t\v:\x90\x9f\x88\xc7\x06\xbf\x9bU\x0e\x93\xcdfr\xa84R\x95J\xfd\x81J\xa1\x92o\x04\x80\xe1E-\xb1\x84\xd1\x1d-j\xc1\xc7#髳\x04\x04,\x81\xdd*\av\xa3\xc9\xca\x11\xbe>\x0e\xf2&\xac$Q?㰗.;Q\xf8\x11\xbc\xbe\xa6 ]'\xa5\x15\xa3\u05ec\x19=f͚S\xaa'\x97\x0e\f\r\xce\xee3rx\x83W\xa9˯\xf4f8\xf2{\xb9\x95\xe9\xf9)V\x1aLo0\xe7床\xf2\xd2\x15|`\xcc\xc2;&\fޱ~Riqì\x99^OMN\xaaT\xaau\xf9G\xf9\xd5:\x00\x82\x83\x9dI.\u007fA\xaf\xd4\xe4R\u007f(\xd0\xcf_\xe3M\xb4\xc3\x13\xec\xd7o\xda=p\xf6\xb8Nt\xc4M\xb7j\x957\xc8\xda\x13\xa0#\xe8~\x15\xee\xe9s\xbb\x89&#\xb9K(\x14\v\x83\x1b=q0:]\xea\xf5p\xaaN\xc78D\xe3G^\x1f>\x92\xc9H\x06\x14Y4\xe1\x03\x95\x9c\x01\u05fb\x8a]\x88F\xa3#\x86\xe3\n÷,F\x83\xc1h\x01\xa5L\xff\xc8u\x86O\xb2'zܴ\xff\xe6\x06%\xf8\x9e\xc0\x14*\x1e\xbe\xff\xf3\xcfcvv\xf8d \bE\xbd\xa8\x1alg\a\xf0\x14\x95\x03\xe2:\xbf1\x0f\x18h\xeer3\x1e\xc4\x11\x115kg\xbc\xe4Fa\x94t\x86u~\xac\xe3\u0088\xf0\xa4\xe7\a\x8c\x83\xe8m\xc6>\x03џ\xc5z\xb19q\x94\u007fL\x9fI)U\x8b\xc4\x03<\x1d\x94g\x80x\x91\n_\x83if\am\xa3\xb3\x8a\xf1љ\f\x8e`\xc7\x17\xc5.\x10\x8e\x9d\x9bl\xb4\xa38\v\xdds\x98\xb9dg\xc7\xea\xf1\x1b\xa6鶍yX\xd0W\u007fx\xcc6ݴ\r\xe3e}\xf3\x1eư_(\"\xaf/\x83[0:\xcbӻ\xb7\x87އ\x82\x916:\xcb\f\x8e\x98\x1dl\x96\x196%\xa7\x87P\x18\xc3,4\x91\xe6\xe9\n\x1bP8\x8bu\xe0\x8b,:\x9d\xfd\bN\x05/6\xceǷ\xe77\xc2\xfe\xe0\xbe\xdc\x12\x1c.A\xfdߎ\xfa\xe5gd\r6\x04{\xc8r0x\xf3\xcb\xce؋LF\"\\b\x886(\xea\x15\x8e\xce\x10\xee%D\x8e\x94\x10\"`\xdbF\x9e\xf1v\x86p\x0e\xccg!\x18b`\t\x9f\x04_\f\x81\x80J*eK83|q(\x9fԦ\x96J\x98\xc1\x10\x85>W\x91\xd0\xdb\xf8\x84R\x82\xfe!\x1c&)A\xff\xa1|r\x9b*\x962\x16\xc2\xf9H\xb08\xea\x06\x05\xae\xb5%ݠ\xe4Je[\x12|\x01MojP\x12?\xa3C[\x12\x10\xee\x81\x018\x0e\x9e\x89\x9f\xe5ra\xfd9\x1b\xcd3{c\xf6\x9a\x1abqo\xe25&\x9e\x910\x1a\x06\xeb\n\x024\xfe\x89\xb5%\x1a\xa4\x04e\x93\xa9ٳw\xef\x9e\xf5\xe0<<\a\x8a`\xc1\x8d\xf1 \x04[\xc7S7\xe8߇\xe6\x1f?\xfd\xcb\xe9\xe3\xf3C\xf1\x00\xf8Ӟ\xbd̶\xbd{\"\x93\xc0yP\x84\xfe\x9f\x8f\x1e\xa2n\x8c\x87\xa7\xe0)\xf4\x00hAc\xf5\xad\xb7W\x15\x16\xaez\x1b\x94\xa2\xf1Z*\x84\x85\xb1\x99y\x83b.u\x96\x8br\x06ܚ\x80[\x87%\x05Xq\x12\x9d\xe8Ꮳ\u007f605\xfa\x15\xfc\xe3\x1c\xb0\x18n\x9b\x03\xb2\xe8\x94\x05'N\x80y'ND\xff\x1b\xde\x17\xfd\x92~\v^\x9a\x03\x96\x80%s\xe0%\xfa\xad藂]ML\xd7\v\xcbc\xb2\xa8B\x8a\xea\x94\x1cuJ\x90D\x04\xcdO\x87\xa5_D~\x88\xa5_\x988\xb3\xb1;\x1cU\xd7\\W\xd7\x1c\xad#'\xb6\xees\x01\xa9o\xad\xa2\xa3MgC=P\xc1\xda\xc89\xda\x14\xbb\xf3\x1eNWǐ\xe4u0-\x0e\xecת\u05f6\xa3Nn\xd6k9tz9\x16M\xe4F̍\xfe\xa2(\xf7\"\x91\x92\xa8QIS\xb1?\x18\xec\xf6E\x97\x05@!&O\xfe\"\x80\xdd>H@!\x0e\x9b\x9a\x99\xa4\xc8}Z%?\r\x9c\xa3\xf7\xc0\xe7\xa2?\xbe\t\x8b\xde\x14\x17q\x05\xd3x\xa56r\x1f\x93D.\xc5L0\"\xa1\x97*r\f\xa08\"\x11\x8d\x8d\xdeGO5E7\xc2\xf7\f9\x8a\xe8\x9d\xcc?Е)A\xdeֆ\xbe\x04\xdeu)\xc4~Q}\x0e@l\xc8\xdd\x18\xf0\x8a Lrz\xbc\\\x17\xd4'\xd3\x05\xe5I\xc1\xed\a\x9e,\xd0\\z\xe4\bӷy\xeb\xe6\xebM\xa0\xf1ڞ\xb50\x93`\x1b\x84\xa7\x8c\x86\xd1\x17V\x9c+\xd3\xd5\xe9\xcaέx\x01FGO\xf9\x11\x1c\x02_\x83C?ҭm\xd1\v\xe32h0\xb1\xb6\xa9~\x12\x00\xb7\xb7\xb5\xbe|l\xfa\x9aC\x9f\xcel\x04\xa0q槇\xd6L?\xf6\xf2\xfb\xc2d\x10\xc7n\x88\xcbO\x84u\x96\x8e\xcaD\xfc\x80`\xf3mp\xf8t\xc4\x13\x99\xbd\xebGD\xfd\xc0\xcd\x13\xe3\x93\xd8\x14\x87Vf\x1c\xfa롷G3\x91H\x84\xf9\t>\x06F`\xb5\xdch\x13㖋mp\xd3\a\x1f\xc0M6\xb1\\.f/\x89ђ\xedE8\x8b\xde\xfa\t:|12ؑ\x19\x1c92\xc8^\n\x8e\xa4\x17\x84\xc3ԍ5k F?\xa0\x84p\xe4A\xfc\xc4\r\xea\xb1\xc7И\x14wd\xa2<\xd8\t\xfb\xf6\xed\xd3w=6\xb2\x9b\xceJ\x1a\x9e\x95@l\xc3^\x94\x06\xb0\xb6\x8e\xc9\xca\xe2}S\x1cC\x03\x0fG\xbbmr\x14\x8f\xc1\xd9\f\xc0\x01\x94\xb4\x87f\xc2\xcd%[ϧg\x8c\x96\xba\xdd\xc1i\x8d\xbe\\\t\x9b[\xbfx\xd1\xee\xda\xfd\x00\x14\xf9,\x83ރ\ru\v\x86\xf5*\xf3Ժ\xd10:\r|W\xefl\xb0rJ\x85\x02\xf4i\x86\xdf\x18\xb76\x9f\xd8\xfb\x12}\xfew\r\xef,\xd6i2\xd5ִ\x9ci\x1b&\f\u05c8\x87\xdfy|\xdd\x12[\x95\x88I\xcf0\x94\xa1\x91\xbf\xba\xf7\xbaC\xf7^y\x13\x14m\x19\xd0r\U00091bce\xffi\xd9\xf0\xe1&\xf8\"H\xa5\x93\x94\xb4m$\x95\xa0ۖOv\xb0\x88\x87y\xca\x03x\xd6\xe6t)\xc9^\xb2\x92F\xf4\x95( \n\x1a\xf0buroQ \x88\xa1\xefi7\xe6\xf1c#\x92\xed\xb1\x16\xe9\x89R\xd4s\xad\xc2M\x95\xe7\x99a\a\xfc\x16v\x98\xf3\xe4)\xe6\xd7\xe7\xd2)f\x8bDjL\x96(s\xd5b\xbf&[\xe3\x17\xabs\x95\x92d\xa3Tb1\xa7\xd0s_7\xc3牀\x93\xde:\xffU\xf4\xe4\x17\xb0\xe3\xd5\xf9\xf3_\x05\x1c\xb0\x02\xeeUX\v\xcf\xc0/ϭXq\x0eX@\t\xb0\x90Й[\xad\u007fF\x14\xa7\x88\x82AQJq\x9e\xc8#?\xfc\xe9\xe8\xfe\x86\xe4\x02)\x9b\xa5ߺ|\xf9V}\x16+-H6\xf4\x1f\xfd\xe9a\xb9Gt\x94\x88S\x17\xf4x\x13\x0e\xcdYq\x0e~\xd9ㅰ\xe0Vjh\xa8\xd7W#\xfa\xfdr\xac\x8d\a\xa0\x18#1\x87!\xab\x1f\x1d\x81\xb5w\xc748Q{\xa2n/\xf2\x00\xa2\x93\x8b\x11\x1a\xd1\xe4\xe6\xa4\x05\xa5i=(\n\x90U\x05\xb6-\xc4z\x82FnU5\x97˖g\x89\x98\xdcR\xc6qw`\xcf\x1dc\xcf\xee\xdc4\xfd\x8e\xe5\x0f\x02\xf1\xdeg\xed\x8de\x9c\xed\xaf\xe6j+\xf86C\xae\xc99\v\x16e\xedin\xde33\xf2Ѭ1[w\xbd\xba\xa7c\xd7⭽\xcfҿ\xf4ˏ^\xce.\x01L\x9f\\\xf0\xb8x\xc1\x9aK\xf7\xdd1m\xd3\xces\xe3\xee\\\x98\x02rG\xfd\xc6\xcaU5\xa6^4\xf1Z\xf8\x95!\xbfOѷz\xf0h3Φ\xfd\xb5\xf2\xad\x8bw\xb5\xefye\xcf\xd6ƹ;\xcfR=}\xfc\x0e&\xbe\xe0z\xf8\xf8\xc5(\x01\xbc\x92\x166\xbbIt\x90\xf9\u007f\xd5}\t|\x1b\xc5\xd9\xf7\xce\xec\xa5\xfbZieݲNˇdK\xb2\xe4ۊ\xed8\x89\x1d'\x8eslj\xe3\xdc\xf7\t9I\x88!\xe4\xe0\x86\x04R \xe4j\x80p\xb6%\xd0p\x15\x1a\xa0%\x94\x16H\xb9\x1bZ\x9a\x04\u07b6\x94\x12(\x94\xb6\x90h\xf3\xcd\xccʎ\xed\x84Ҿ\xef\xf7\xfe~ߗX;;\xb3\xb3\xb3\xb3\xb3\xcf\xcc<\xcf\xcc\xf3<\xff4\xf1kA`\x8b\x886KTV2\x81^\x1d4\xb8\xa1\xb7\x0e\"\x96\xb3?\u007fK/\xcfA\x14\xef\xc7nyܵE!\xaf\xcbJb\x16G\xc2\xe3*/\x9e\x98\xac\b\xbb\x12J\x83Z\xb1X\xc5\xf0\xeb?\xbc\xea\xfd3ҹO\x1f\x9a;\xf7\xa1O\x01CBp\xeb`\xa6\xb8\xbd\xb7D\x138\x1do\xafr[Lf\xa7\x9e\xec\xe35\xf9\xab\x03~\x83\xd6\x16\xf0\x14V;\xcc\xf5\x1a\xae\x83\xb7\xab\x8e>\x06\x1aQq\xfd\x8b\x95\x9e\x18\xc4J\xa3\xf6\b\x9f\xa7\xd9\xc9D>\xacC\\K7\xb6\x91\xed]\xcdA-\x10\xc2\ba\xa8oy\x80@\x13]\r\"\r;\xb0v\x0f\xe9sHH\xa4\x05\xd9'\xa7\a\xf0\xd8\a\xa3\a\x13\x04&\x8f\xb0\x1f\xfb\xf2!\x80zX\xa5\x03\x88X5\xa8\x1c\xfc\xa0g\xeaԞNpCM\xa3N\xba\x95\xd714\xaf^\x0f\x0e4\xda\xf4\xdax\xb9\xcbF\xc3\x17\xd9\xf1~Fe2\xf3\xbc\xe01\xaa\x99\xe8\x9b\xd6)\xad^p?\xcf#fJZRԙ\x97\x17\xe0\xd41\u007f]\x01\xf6\xb5\xb6\x81\xde顕j3\xb7R\xfa%\xad\xa0i5\xf3\x8b\xce!\x99\xce\xce̐\xac?\xee\x17\xad\xe0\x88\x86\x87\xb4B{\xbd\xb4WJ\x1f-\xb4sv\x9b\xb6\xc6a\x84\x93\xc1\xfe{>\xc8\v\bZ\x00i\x8d9O\x0f\x11?\xba\xd1W\x90\xfd\a\xab\xa1\x81\xf6\xbe\x15'+\xd2Ӽ\xcd\x0eQ\xe3\x15\fJ0]z\xa4L\xc1BV\x1dQ=\f>\x06\f\x84J\x05\xf1}FS\x1f*)ƉFZ5\xe2\x9e˨Vj\x0e\xb5\t\xcfp4\x9b\xcc\xd9\x00\x01\xa1\x0f]\x82\xc8\x04!\x19n\rs\x00\xfd\xe6\bDs4\xef\xc7\xee3\x93Q:\x9cpӞ\u007f#\xc5\n\xfe\xf9\x10\xa0'\xcd\xe9N%;\x17g_\x00\x82\xee=\x9d \xfd.\xad2I_Y\x04-,V\x9a\xc0H\x9d\x99\xae>{L\xfaBg6\xeb\x80\xe6ep\a\xd0;k\x8b\x12\xa1J\xbb\x01\x00\xa0\xb3W\x84\x8a\"u.#|\n\xa5\xd7]H\xb7\xf5\xa6\x1f\xc9\xe5\xaf\x18\x98\x0e\xa0\x1b(\uf6f8Pڰ\x12\xbc\x92\xd5\xe0\xd2\xeb\xc6\xe8\x03F\xf8\x95\xce\xfc\x92t\xe5oQ\x1f\xfa\x9b\xce,\xcdV\a\x16\xcdXST\xb2fA\xa7ápuN\xdd\\\x1d[;o\xb2\xdd\xfe\x1f\xa6\xcb\xfb\x9fl\x0f\xfb\x05\xd5BME\x12\xca\xd5hZ\xc0\xc0\xf7ة>\x01g\f\xa36\t%\xd3ؑ\aY\x81$h\xe90g\xdd\xce\x13gyX$\xb4^\xd0T\xab\x03\x02\x1a\xb3\xe4\xa5\x1dD\xefV\x11\x11\xaa\xe8Å\xe0\x05 \x92b\xe0\xe5\x04$R\x86rIh\x96\xe0\xe1\x02;\xe2,\x14J\xa5>`\xed\xca\xf3i9\x15\xab\x00\xc1 P\xb0*N\xeb\xcb\xeb\xb2\x06\xf4J\xa5\x02\xc0\x80}\xa2\u05cc\xe4\x8a\xca\xd1\xf5.\x0fG\x97\x85Be\x15\x8e\xfa\xcbi:㳙\xbd\x13\xf7\xd9CB0\x881\xffZ[-O\xa6L\x82\xb0|9\x8e\xed\xdau\x10G\xa6̘1\x05G\x97\\~\xf9\x92;\xd5]k\x95L\x89C\xa1S\xabY\x8b\xe0bz\xa4\x1e\f\tɪ\xd5:\x85\xa3\x84Q\xae\xedR\x8b\xb5\x1a\x85\xc9\x18\x1b\x9fn\xd4\xf0\x8bNH_\x9cX\xb4>\xdc\x19\x00\xc0\xa4\xd0\xd4҇B\xe5B\x10\xbd)\x86,l}\xabU\xf81ƙ[\tjV\xe2\x84]Ҥ]/\xe3\x84\xce?\x03\xeaϝ8i\t\x9a\xf2~%\xfd\x89\xf8I7モ\xa7\x8f\xd7\xf5\x10l\xa34U\x8f\xe6b\x8c\xa75\x1d\xcd\xc7˨5\x88\xf2\xb7Q7Sߣ\xf6\x11;{\xb2\xa3\x12ȅ0\x17\x0eN\xff\xd6|\x83v4\xbf-\xfe]\xe1\xb7\xdd\x0f\xa0\xec\xb9\xf8\xfb$\x90\xff\xe0\xf7\xfb\xa7e\xbf\u007fq\x8e\xaf\xbd\xc4\xcb2\\N\x02i\xf9%b\xac\x1cd\a\xc4.\x993\x17\x03\xdd]\x17\x9e\x00\xe5@\xea\xba8m@\xe4\x9cn\xbf|7\xfe\x03\xb7\\\x1c9+\a\xf4\x80إ2\xca\u007f9\xdfe\xdc9\x8e\xea\xf3\b?\x92\x1aG-\xa0\xae\xa0n@\xac@\xae\xd5R\xbdH\x99\x80\a\xbd\x16T\xf2lI[\xfa\x8c\xa5R\x04V\rw=b\xedC\xd6\x1b\x89\xbc\xd7\xdb\xf6A9MVꐗ#\xbdbPƝ\xc3\f\xaa\x8c\xbf&\xb2b\xaf\xeef.A\x96\xfb\xff@\x8ec\x015\x05I\xac\x8f\x90\b\xfcؚ\f\xf8=\xae\x90\xfe\xd4>,\x89/\xdceM\x04|\x05ဌ\x99\x80\xf2\xf4b7dI~Ќ\x82\xcdӦ^\x83\x82\xd7@\xe05p\x1d\xe1\xe7\x84|\xbe\xe9n\x8b\xc2`LZ\x9e\x00A\xa5Ŧ\xd6\x14\x1b\xa6\xbe*\xf2\x06C\xd2\xf2\xc9}d\xd1\xe1.y\xe9\xa1\xe4<5\x05P\xdb\xe4\bU=kR,\xe2\x0f\xd55D\xf6\x9d«2\v+g\x8c/\rG\x933\xd22\x8a\n\xaeS\x0e\x18\xe2}r\v\xd1\xf6\xc0.\x1eq\xb8\xe3\xb5\xd7nƬ\x9d(\x1c^\x85\x1e\x84*p\xed\x16\xc2\xea\x9dٍ\xa2\xe8\xe99=\\\xfa<\xa5\xc8\xe6\xf4,\xe6Q?\"\xfc|\xce\xf2\x9dp\xbb)\xec\x81+F\xackr\xe0\x8ax\xf6\xd1\x11\xd3o>.\xab\xc4\xe2\x16\x8d\x13\xcd|4߅\xc2A\x19\xea\x0f\xa3d\n)S:\xe5ƦI|*\x87\x89\x87Έ\xc7\xfc q\xccGs\xb26;^\x05O\xf7~+r.\xf3\x88uD\xcd \xed뻐\xc8%WC\xd1\xca\xe2A\x99QTیu\xc1\xf2ր\xcaW\x1a\xac3\xda\xe0e\xbdgչ+\xd2\x04\xe3H\u007f\xdd\xf0D)\xad\xa5\xa7\xee)0:\x02&\x8b\xc5\x14p\x18\v\xf6L\xe5\fN\xe9\x83\xcft\xfa\x02\xe3~\xb5N\xfc\xf5m\xc6\xdb\xd7yF\xc7yOs\xec\x8a[\n\xea\x19\xb6\xb4`\\k\xb4\xfc\xb2y\x01;\xfdh_\x0e\xbb\xbf\xc4e\x93\xf30\x8a@\xba\u007f.ӿx\x14\xd09\x81\x1f?\vf`\xb9/\xdcZ^\x91\xaf\xf0\x84}\xe5W\xe7BHR\x81\xc9o7z\x8c`\xf6\xd8@\xabQ\xa94\xb6\x06\xc6Άб\x96\xf7\x83\x8c\xb5L\xbb\x06\xa8w\x03\xe3l\x1b_7\xca^=t\x9c\x11=\x1b\xd5SU\xa2\x8a\x9bZ7HGq\x0e\xe9\xab\xdd\xd2g\xb3E\xbfG\xce\x01J\x03}9\x02\xdfR\xf6\x00\x9bc\x81jB<\xecT\xbc\xdfM\xb4\xa3B\xde>=)Ĝ\x12l\n\x12#=\x11\x89v\xb9~\xca\x11\xf5\u007f\xb9\x9f\xd2x\x8f\x04q\xf1\xbdvw\x18o\x87\x0e\x85\xb1\xaa\x1b\xe3\xc5\x1dD\xfa\xedk(\xb8\xe6\xc9k\xe6\xe3.\x84\t\x9e@\x9b\x04\xc2\x05\xbe@ºk!\xa6\xe1}\xa7\xf4!\x97\xc7\x1fHZ;wg_8\x9d}V\xe3\xd3ܯ\xd1p\x19t\xf8\xd8>\xac\xe1\xaa\xce\u05f5>x4G\xee;r\xe4\x0f>\xc4p<8\"\xf7\x93\xf4\x8cd4\\:~F\xe5B\xb2\xac\xb9/\xd2P\x17\xf2Gb\x93fU\xa3ޓ\xbd\x01\x17\x8bJ\xf5i8\x0e\x1d\xb5\x1fۆݹ\xa0\xf3uM\xff5|35\x89\xa0.a\b\x0f\xd9\xcfxn\x8f\ao\xf1\xe7X\x810f\xe51K\x00\x89\xed\xbe\xac\x85\xee\xf7\xe1\xbd\x0e4?\xe2\x1e\x95szRG|\x89\xe7\x14\xc6\x13\x17{G\xa3gj\x11C\x88\xb8B\xb3\xba*\xd9\xdc\xe4\xb4:\x8d\xe0\x0f\xa3\xb4\x16m\xe76H\x97}\x91W\xdcu{ˁ\x9d6\xc0\x88\xba֒B\x8b\xcb-\xf2yC=\xfeJۼ\x89\x1d;&[8\x81\xa5ի\x97\x94\x8e\x064\xab|r\x80q^\xd6\xd1\x18\u007f9\xae\xa6\x01\x9c\x95\x99\xf4pH\x97/U\xea\xae`\x15mP<=\xe4c\xcex\xebO\xa6\xef\xd8\xcbA\xdf\xd8\xe4\xccX^\xcckC\x9d\x93\x17]M\x1d\xbeI\x8b\x17\xeeh\x17'\x8b\x1a\xae\xc6\x04\x94P?\xd0L\x0fq\xa9A\xc4C\x9d`\xcfS6ħR\xc4_\x1d\x92h \x06fA\r\x84\xadtL2\xfajX\xbeBZ\x91\xf6\xfb\xfa`\xd2͘\x80p\xd3\xd0iDl\x85\xb2J\x17A\xd1Lx\x8df\xc8ˮ\x81\xdc\x00\xfe\x89Ѻ\xac\xe1\xf0\x82\xc5\xc6\xc0\xd0\x18\xe3ԘUА1\b\xf0\v\xbd\x82\x13\xdb3\x9eCO\xea9\x95Ka\xed\xda|\xb8{۾\xf0\xc4T\xe8\x1e\x90\x1f\x8dz\xf3\xbd%\xed\xe5E\"˫T*\xf0\xe17C\xafxvi2\x05V\x8fd\xe99\a'\x88\x1ea=\xf3z\x9eˣ\xb7VI\xff\xb8\xb6x\xec\xa8\x18\x00\xacF\xd5\x06\xca\xdb:\xb3\x87x-\xa0\r\xcai\n!p\xbd\xa7\xf3\xd1;\xba\x0em/\xefY\xd0\xe8\x04\xd6p|x(\xbf\xa0~\xda\xea\xeeB%\xa4\xc1W\xa7\x17\x9f~\xe1FA)\xdd1S\xfa~\x80\xae\xac\xd3\xf2?E4\x04\xd0\xfc\xb7\x89=K\xd5R\x1d\x88\x8f\xa10j*^F\xc0R0*9GH\xd8g\v\xf6PR\x02\xe4\xb1\r\xb0Ar\x82\x1d\xe7X㲁\x1c\x16\x90xk\fҽ\x8a\xec\x1e쯎\x13\x81\x80\x17\x81\xf5\x00[\xbf\x93q\x9a\xa7\xe5-E%\x90C!\x17\x17P\v\xe2L\xa94\"\xb2z\xaf\xe3\x83ڲ\x1d\x05\xea\xe1\\̛\xfd\xab\xb4_\x19\xaeL\x85\x00#e\"\x95\x10ք\xc1\xd3\xd9\u007fD\xe2\x1cW\x19T\x81SҁP)ǥ\xfc\x9c\x0e\x1c\xfd\r`\x80Uo~گ\xb39,O\x9f`\x03g\x00\r\xf2\xd4^O\x8b\xe3&\xc8\x01\xaf\x89\xbeW\xcf\xe8K5\xe9\x850\xb2\xa3<\xf3\x81\xaf0\x11\xfcĦ\xf3\xe5\xb7\xe5\x01\x95\xf4\x8d\xc5\x12\xf4\xb7\x9a\xff\xba]o\xf1\x05G\x19\x9f\x9f\xa3p\xe7\x01\r\xac\x88\x84+\xe8\xe9\xa6\xdb\n*\x1f\x8c\xd6H\xb3\xbcEL\x85\xb7\xa2 \x98b\xbd5\x91p\x12d\xd8L\xc4_Rӥ\xaa\x0f\x06J`w\x10D\xb5\x1b\xadc\xf2C\xafl\f\xc2\x10\xe0\x00\v<\xa3lV\xb5s'`a\xc9bpH\xfa\xfb\x88\x96\xf7\xab\x9d\xa9\xba\u0603\xb5\x85\xb7Y\x83\xa0\"\u007f\f⺽\xd2~p\xcc\xdf.\x98\xf2|\xd2T0\xc6?\xca(\xd8CҌ\x9f\xe9Y\xb3\xe1d\xa4\x06T\xcac\xa0\x9b\xa7ؙ\xe8kMC\xf2\x00ba\x822x\x01\xa2G\x0eM\x85x\xad\x95ؚ\xa4\xb06\x87HF\x04\"\x8b\"\xf1\x1d\x12\x97\xffv G\xc3h\xd6\xc3\x10]~l\x9dK\x13\xc0\x02\xc1\x1c\xb4\x06\x04\x18$n\x1a(L\xf1bn$E\x9f-\x18\xb6\x82ѐ\x99po\xa5\x85aT\xbc\x8e3\xc1'\x81f\xa9\xf1r\x8dI\xb5a\xeal\xa0\x02\xaf\xef4\x9b;\xcf\u007f\x0f%\xa9\x05Ն\x8c\xd4\xc4WE\xe8\u007f\x9eQj\xab+i\xa9\"\\\x94\a6\xa8u\xd72\vO\x16\xfb\xa0\x97\xff\x11\x9d,\x03\xc6G\x1f\x97>n\x1c\xde%-u\x9a'\xacw\x168\x0f_i\x06\x1dJ\xfeqX\xf9\xa3\xa9\xee\xb0\xd2l0kD\x85\x95>\xbb\xf2%\xad\xa0\xca\x18\xfeK\x90>\xfd\x93g\xa4\xe7\xa6\xdfg^Қ\x95(a\r\x9d\xe4\U000ecb14\x92\x86\xd3H\xe6\xe5\xe9\x11\u0382\xa2l#\xa3*\xe6~\x0e\xf6\x94\x97\xd3\xc5\x1a\xe9)\xd5\xdc\xcee\xc0\x04,\xcb3\x0fL]\xf8,\xac.p\xae\x9f`v:\xcdW\x1e62|\xaf\x1e\xd9\xf7\x18\x89]\x80\xb8\xfc\x18\xc1\xcbţ\xa9\xac^J\x06\x01+\xc7\xe7\xd4q\xbdXrJ\xa5Ű\x19\xea\x81/\x8ca\x9e\xc2b\xd8\x12\n\xbb\x91`\x85\x17ݰZ\x91<\xfc\xe2\x81T\x06Vb\xec;o\xfbßv\xec\xdc\xfe\xc5\xce\xee\t^\xbe\xa1\xedЇ\xa7@\xc7IoCe\xe4W\xfb\xf6\xe9\\\xf9c7\r/\xd1\xd3\xe9\xf4\x88-\x93\x96dǶ\x9d\x18.\xc0\xc2\x17\x17\xf9}\xf6\xe8\xb2\xea.GK\x9ew\x05\xf8\xc1\xbb\xfb\x0e\x1c\xd8\xf7\xee\xce\u007f\xec\xf0\xd4e\x9c\u007f\xbf\xff\xc1O?}pr\x9b60\xb3\xf5\xa8\xf4\xdal\xc0zo\xbc\xff\x8d\x1fv\x0e\xf5\xed\xff>|\xe7t\xf5y\xe9\xa9ֵ\x9b\x82B\u05ed\xb6Tup\x9c\xbd\xd8m\x18_\xb5\xe0\xb6%\xb5m\x8bz\xfdc\x91\xb9\xc3NE\xa8(\x9aO\xc7\x11\x0f\x1eD}\x8c˹\v\xc08\x19\x04%ٛ\xa2\t\xe0\x15/V\x83\x94\x11\xcf\x16a\x91M\x12\x8d\x03\x02r\x88\xdf8NJ\r\x9a,\x18\xbb3R,\xde\xf5\xa7\xddw_V^\xc2Xk\x86\xdc\xf5\xfa\xeb \xf9\xfaa\xa8\xf2\xc4'VZ,\xaa\xf7CL{\xd5TpU\"2vh{^\xcb\x16\x17scS\xb2*1\xcab\x04#\xfaO\x0e\xe0\xb3QCm\xcaxf\xd5\xc1\x83\xab.{@(*\xb6\xfcFz孷A6/V\xbf\xf6\xd6\xcbf\x88\xf4\xf5\xc0p\xf9\x92\xf6'\xc2wG\xe6\x0e\x9f`\x15\x86\x0e)\b\x1ag\x0fI\xae\t%[\xca\v?\xbfhN\xe8}\xff\xd1Do.\xd9ۇ\xa0̡c\xfb\xc5ܴ\x88m}\xad\xb2J\x0f'cy\xe1\xf5 \xec\x99\n\x12\x85\x1f\x1c\x10\xdd*2u\x0e\xdc\xee=/6\xceu\xf1\xe1X8h\xd6\x14\xa8\x19\x05k\fl\x1d\u007fl\xa4\x91eT\x9a\x02\x95ŏ\xae\xf0\x99\xad\xe2\xb5P\xa17h\x12:\u007f\xa6xX\xa4hxQƯKh\r:\x05\xbc\x16\x80\xc1\xaba\u05c8\xac~RF\xe0\xf4\xa2\xc6%\n6\x03\x9c.\x8c\xf6\x8f\x9ax\xaf\u007f\xb40\x1d\xea\xf3\xcc\x16\x97F\xd4s\xc2u.V\x8c\x8al\xa1\xa0t\xfb\xdd\xe8Oa.`Epv\xf0:\x18\xa0\xf4\xa8\x1dV\xa0v\xc0\xad\x90\x92q\xc0dE&\x82;H\x1cpYs\xf0`P\xd6k\xca\xd93\xc9\xcd$7\x9bL\xeaX\x83\x02ʎ&\x12\xf1\x1cx9ݾ\xf5\xedJ\x87R\xa735\x98\\\xa9\xfa\xd6zMp\xf3hg\xd2\xf9>\xaf0[\xcd\xe3Ġ\xcd[\x97\xaa\x9b\x92JN\xaeM\xd5y\xec\xc1\xbc\xb1F\x9bY\xc1\xbf\x8f\xb2\x8c\xda\x12\xd0ԏ\xacO\xba\xf4\rf\x93N\xe9ȼ\xc7\xf6\x80믨Z\x17\xbb\x85w\x04\x9c\xdeb!\xec\xd4;;\xb6\xe7kԜ\xab9_]\x11Բ\xac?R\xe0p\x14D\xfc,\xab\x0fV\xa9\xf3\x9b]\x9cZ\xe3\xbdn\f\xca\x186\x17y\x1cA;\u007fS\xe9\xfa\xaak\xd7\x0f\xa2\x81\xe9\xffWi`\xb0\a\x03\x96\x92\xe9 \x8a\xe8@]\xa0!t\xb0e\xc2Km&N\x85\x17\xd7̈\xf1 tp\x1dT\xe8\fڄ\xd6?D\xa6\x83!~mR\xab\xd7+\xc0u\x80\x1a\xd0\x19\x10\x11\xe8&\r\xc1Z\xd39\"\xa8O\x8e\n \"\b\xb5\x85FB\x83M&\x02\xb5\x0e\x13A\f\x13\x81J&\x02\xa5PD\x8b\xb4zP_\x00\xb2N\"\xe2\xab\xf1\xa8\xa7ce\xf6\a\xbd`\x80\xe5\xf8:P\x0f\xf0\xe2\x12K\xe4'\x9a\v\x13\v`.\nc Y\x9eD\x9f\xd9D\xa1\xd7g\xad\xe9:\x061\xd5Jj\xe8\xf2\x86rQ\xa4U\t\xab\xbeyH\xbb\"6_zH\xfa\xfd\xd47c\xa3\f\xfaaO\x8e\xdd2\xf2i\xc4s+\xd5\x1c\xf7\x82\xde\xdbsz\x87Dm\xef\xd8\xda^\xa8\x01\xdcu\x1f\x1f\x05K~\xc1\n\x95\xe5\xcd\x15I\xdd\\\x18J\f\x9b\x91lذ\xa6\x81\xa3\xa2S\x9bG\x14\xc68ӧQW}\xa8\x98\xf3\xbc\xac{\xb8\xfcJ\x83\x9b\xe7\x1d\xadޠ\xd6\x13\xa29Q-\x1dr\xf1y\x93!pF}F\x00\x00\x97\x06KA\rP\xea}%#\xa2\x8f2m\xddW\xdc2\xa4cMK~??X͈g\xee\xa2f\x13\xdd63\x1fF\xe3{\xbf\x9f/\x9c\xe6C\xfd\u007fxU\x1f\x8d\xed\xfd~hx\xe4\xd3\xe2\x80_\x12\x06\xfcD\xf8\xc0\xe4\x100\tD/\xdb(\xabg\x93\x03[\xc8\xfe\xe5\xa4\x10~\xe7\xd1\xe2\xfa=\xf3jG\x8fօF\x86t\xa3Z\x1a\xe6\xed\xa9.;\xfcNX8\xf9)˞9\x853Dk\xf7\xcek\x1c\x81\x06\xf7pHα\xb76\xfa\xe8\xdbA\v\xca\xe1\xde#}\xb9w\xcd{{\xa6N\xdd\xf3ޚ\xbd@\xbbgDvYv\x19\xbc\x15\xfe,[\x93\xada\u007f\x96%\xf8\x05\xb0\xa7ģ\x1b5\xa2\t\xdd\x18;\xfcnP\xfc\xe8s\x8e;sZ(x\xf7pѐ\xbd\xf3\x87\x0e\x1f\xad+\xf4\xf9\vu\xa3G4\xce߇s\xa0\x87\xff\x85\xe3>=%\x14\xbcs8V\xbbo~\xdd\xe8Q:Ot?\xd0\uf676\xe7\xc4\xda5'\xb0\xc7f=tg\xa1t\x15\xd8\x04%\xb0\xe9\xeb_\x82\xbb\xe94\xd8-\xcd9\xf7\v\xba\xf3\\\x8f\x94\x01G\xe9\x1ep\xb4Oϒ\xd8\x12E\xa8\x14\xc67\xe3s\xfa0H\xa0\xe8u\xa6\x1cL\x00\x1d\xc7c\xb3W\xd4\xc7@B\x00\xc6\xfcT\x1a\xeb]\x86\xd3n\x00\xc6\xc2\xc7\xec\xd9yK\xf7\xac\x9bfm-\xb9\xe1\xd81\xfa\xf7\xff\x90\xdcV\u007f\xba|\xe4\xd8\xc5u\a+\xcdf\xe9Ï\x9e\xa1'\x9c\xfb\xaf\xa0\x02\xde7\xab\xdd6g#\x1b\x1a\xbew\xe9\xb9\xec\xf4\xdb\x05v\xf8\xcb7\xd0\xf4\r/\x9f\xf8\xe6\x8b\xda\xf1\xcbF\x8e)ˇ/\xda\xefN\x96\xa7\x92\xf0w\xd9'\xc0\x17g\x1fH\x9b\x18\xdd\xf8\x1b\\\x8d\xbeǨ^_\xef9]>3\x95O\x95P\x95h4\\J\xad\xa5n\xa1\xfex\xc1\xda\x00\x89I\xa1\x9c\xf7A4\xd3]:2\xf0\x1cp97\xd9i4T\x98z\xbd\xcaYS\xbd\xaeFMa,\x90qX\bK\xcb\x1e\xd5\xd0\xd0A\x14J\xc8\xdd9%\xbe\xde+dDF\xfd\x91\xc5\xf6\xf5\x8c\xbe\x17\xf5\x8aر\x87\x89DBF\xact(LF_2\x92\xd1d\x12\xc7\x12\x1d\x94\xb9u\"\xd4ၝ\xb8\xa4\xe4E9\x81\x1e\xe7\xab\xf0\xf9*\xae\x8e\xd4\x14D\\\xee\xc8\xc3\x055\x91\x88\xdb\x15\xf9A\x04\x855\xbd\x01Ќ\x93\xde\xfb\xe1\x15o\xdf\xd2a\x99\u007f\xf5Zwm\x85ۛF\xbf\xa5^w\x85\xb3L\xbb\xfcꛆ\x1b\xdd\xd3S\xa7\xddc\x0f\xefX6K+5gff\xeag\xd7\xc3U\xadߛ\xd9vK\xba\xb4sn\xf9\xe4\x801Qδ\x8e\a\xd6ƚ*\xe9L'S]\x94+ \x8d~\xb1\x8a)\x8bWOK%W\f\xf5\x86'\xb7\x1e-\xcd3\x95\fY\xdcP-\nVh\xa6U\xf6<\xc3į\xb7\xfb\x1d\xd5\x13\xc7V\xb2\x1a-\"\x97\x90aO\x81\xcd_\x92\x9e\xc2\xfc\xa9*\x16\xab\x8a}3n\xa5\xbb\xa8Ƚ\xd2]\\\xec\xfe\x97g\xf0\x95\xfd\xc7\xe6=tr\xed\xa4\t?|\xf7\xfb\xd2[s*\xe3\xe4\x9f\xc7\xd6\x05\x84\xc7Z9\xe1\xcb\t\xab7ݶ\xebwͥ\xf0p|\xf4\xe8xb\xf4h\xe9d\xf7}\x8b\x9b\xab\xf7-\x99\xbfP\xe0*\x92vsӋ+\x97I\x9f4d\xf6\xd8\xc1ʢ\x8c|\u007fciS;\x10<\xdd|\xf4\xe8ʊ\xf9\x95\xd7\xde}帤\xcbF\x9b9}4d^v\r\x93\xa9dy֨\x17\x00\x97\xa7A\xf3\xf3\xe7\xee\xb2\xf6\xfe2\xbc\x8d\n\x12-\x81d8ߒ\xe8S\xa0\xb5\xe680De\xc1D\xb9\xbf\xdco\xf1[\x12\x96Ā=\xb7\xdb9i\xd7o4\x1b\xdbg\xddpìi5\xf3\x17\u07fe\xff\xe4\xc9\xfd\xf7\xfe\x12L^\xb2d)\xfa\aL\x83X\b\xb8&\xdfs\xcd\xc8\xc97\xbfts\xf5\x9c\xd9X\xbf\xe2\x8d5KI\xc6Ճ\xb9\x03<7\x04s\xe3e\x98\xa0\xd4ajE\x83\x1co\xf4\x1b\xa39'\x81\x18\xc1F^1#\x9b\v\x88L9\xaa\xec\a\xf7\x8c\x90>\x1c\u007f\xcfk\xfb\xebG\xf6\x1c\xe9\x19Y\xffܝ\xb3f\xe9^L\xb6MR_g\xb6\x87\x18\xea\xdcS\xa5\xbadu\xa9\xf4\x03v\x92mySgOOg\xd3r[S\xb1\x1eFL\x10\xfb\xca\xc4\xe3\xf4\x18\x82\xd3\xc1\xa2\xde8\x81\x9aJ\xddFQ\xa6x\nu\x0e6ʆe\x90\xb8z\x10\x85\xa8>z\xe07Ʊ\xcf\x00\xb2\xf9\x8d1ɰ]5\x99\x8aCքя\xddҡLؤ\x01Mf)7\x06\xa3&\\\rCʓ'ky\xe1D\x87=X!.\x00\xf4M\xdeXu\x19u\x1btpsر\xfa\x1d\xed^o\xbb\x97S\xaa*\xedq\u007fT\xdc8\xf6l{%\xa8zT\xac\n\x8eTOmػ\x9b\xf5j\x1c:\x8b\x02D.[>*V\xb9\xcc\xd8Rn\xf6BU~Q\x93\x87\xbf\xa6{ڞ\x86y\x87&W\xfe\xda\xe9(\xdaZ\xfc\xbc\rɮ\x86v\xb3k\x91:\t(R,P\x84\xec\xd2(\xc7\xd2\xe6\xfc\xe9\xe9\u008d\r5\xd7\\\xb1\xacT:%\xddE\x14\xb3\xee\xd55\xb8\xaa\vk2\x81U\xb3::f\x1d\xf2g\xcaR\xfe\x84\x03\xb1\u07b3\xec!Г\xc9d8m\x8b/S\x98\xb4\xde\xd0\xc5t\x0f=\xdc\xf4\x9aZ\r`\xc3\xde\xecI\x80\xa4;\xb5B\xfa\xed\xb2\x98\xb9\xa2\x92\x8b\x9b\xd2VUaft\x1e\xa4\x1e\x1f\xd9\xf8e\xfe\xb8\xfc\x04\x8c\x9f\xb0\xd2\t\x8f0)/p\xbd\xbe\xa1\x05\x15\x85\xb5\xd1\xed\xa1!cU\xa5\x8d\x9a\xf2JƧ\x0e7ŀ=d\x87\xfb\xed!]\x933iu\xaa+*4ƀ\xbd\xdc3\xc4\x10\x1a\xa0s\x11$\\\xc4\x05\x06(\x8d\xc4R\xacg\v\xad\xa8\x01E\x10\x90\xb5\x13\xb0\x89\x96\x8e\xf6\xe1m\xacp\x88\x93\x95\x18X7\x13\xaf\xa3y\xaa\xab\xe1\x9bLC\x97ZQgin^\u007f\xefRvzi{U{|*\xb7\xf4\xde\xf5\xcd͖:\x85:\xfb+\xc0w\xa8iEHaW\xffq9\xdbU\x86\xae\x97u\xb1O\xefQ\xdbQ\x1a\xad\xee\x00\xbc\xaa=>\xaa\xad\xa5mLi\a\xbd\xf2\\\x94@\xb2\xbc\xa1W\xf2icU\xf9\xb4u\xed\xcc\xf0\xfc`\xd0\xd7̶\xaf\x9bV^eL\xf3\xca\xec\xfd?\xadU\xd8\xd4IT\xe8\x03ch|5\u007f8\xbd\xf5rTVRmS\xd4\xfeTQ\xe3+\x11Ř\xb7~\xa0\xdec\x19Վ%p\x90\xf3\x01\xa3\xa3\x8b\xb0\xa2\x97\xec\xc9\"\x87\b覫!V\x81\x0f\xa4S\xa2`D/\x1b\x8c\xe2\\d\xab\x1d\xb5\xc1\xc5\b\x04\x18\xfe\xbc\x0e߄[\f\xa2Vy!\xac\xa0UE\x87\xaed\u0085\xa3[\x82\x00\x04[F\x15\x87ص\x87¨\xb2A\x85C\xdd\xf5\x16\xdbV<4\x0f\x80\xbc\xa1\xc5m,\x80i\xb5\xfd\xa5)C;\xa5w\xe8\xf6\xc2f\x9c\xdc\\\xd8N\xbf\xfb\x8b\xaar\x1d\x8f\x8d\x04\x89\x9b\x0f\xdc\xc0\x91+\xc0K\\ \xda֊\xcblm\x8b\x06\x8aN\x9f\x9e\x14\x81\xcb\x12\xe8\x8d}W͠\xbd\u07b8\xd5\x1a\xcf\xf70Ӯr\x93\xb6a\x94#\x0e2u\x1e\x9f\xcfSǼT\xac\xa0\xb3!z\xff؊\x96?\xc0\x06\xb7\xdf\xefn\x80\xf7\xed+\x8bk\xf8s\x18\xfa\x95~\xe4\x1c\xb1\xa6\xa5\xf7W\x84V\x82\x19\xac\xdb_\x96\x97W\xe6w\a\x1e>ҁɅRS\x96\xf3\x14\xfbI?\xfb\x0e;\xe5\xa6|T\bɢqj5\"#k\fU+\xcc\x02+\x1d\x06A\x1a\x8514\xa7r\xd8F\x1b\xb0t\x10\xa4y+IN\x87y\xa2\x87\x91\xd6\xc30\x8fMYcX\xa3\x9d\xe5\xfc\xa1\xf2p\x88\x0e\xd5\x03\xecdW>\xa6\x83q++Z\x04b\xe8m\xb1b\x1f\x1bilˊ]m`A\x16\xdd\fZ^\xf1\xbd\aL\xc0\xa4\x96ޒ\xce|X\xfa\x15b\"ku\xd2~p\xe3t8\x0fBf\xd4x>[\x0f\xa8&\xe9cf\xae\xfe\x0f0{\n\xac\x12\xa4\xc9\xf4]\xe6\xd3\xf0\x16\x0e\xf2\x00\xba\x1f3\v\xc3\x14̟y~&\xcfH\xef3P\xf1\x11\x93\x86|m\x17\x18\x0e\x15][`7T\x82GY\x1a\xd4rfn\xf5\x95,\xbb\x8e\xe5\xc6\xd1\xeck\x1c\xfb\x15\x03\xf5f\xe6\xa7\x1cx\xe7/oK\x89\x13_\xbd\v\xb6\xbe\r\x86\xfd*{\xfa\x1d\xd0\xf4\xb2t\xb0\xfd\xb3\xd1@\xaf\xa4\x93\xcd\x1c\xdc\xfb2\xf8\xf5#g\x1f\xfb\xf3=\x9f\xc3\x15/\x80\xa7\x0e\x9e{\xe6\xe3\x9b\x16Lg\xd85S?\xe8\xf9(\xbfl\x15K?òc\x0f\xb0\xf4\x9f!\x04_0\xc0\xc83\xc1\t\x1c\x98γ%\xb3\x15\xe0\r\x15\xbd\r\xdcɰR\x19O\u05ce\x87\xdc\x15-\fS\xb1\x94\xa3\xaf\xa4\xe9m\f\xb7r\x1b\xcd\xc2;\xd9\xfe<\x9c\v\x8d\xfc\xe3ɪ)\xed\xd71X\xf0\xf3ɫ\xa1\x88l\xe9\v̊%\xe7\"a \xe6\xd6\x05\xe7\t\x03ΘG՞\xd2\xf6\x04\x97v'b\xd1X\u009d\xe6\x12\xed\xa5\x1e\xf5\xb8Z\x98\xa9\x1d\xf7ȝ\xef܉\xfe\xe0\x06\x93\xae\xbb\xab\xe1l\x86 f\x1cm\xe8\"&\x1b\xdd}GPX9{ΰ\x12&ߐ\xa7R\xe5\x19\xf2\x99\x92asfW\x8e\x981\x03\xee^|\xc7\x1d\x8b\x17\xddq\x874\xfa\xa8\xcet\x12\xdf\xce\x12؍\x93Dӻ'w\xcc\xed'\x90wTRE\xd4dj\x01\xb1\x9f\xcbi\x81\xa0\x11\x8b\xe9}\x1d\xc4GU\x037\x1b\xafc.\xf1.}\xce!.zs\xcbE\x18m\x8c\xfcj\xa5\xd1\x111\xaf\x81\x1fݐ=\xda0Z\xe9,\x19S\xce\xf2qK\x89+\x12\x8a\xb8J,q\xf8\x98\xa0\xed&\x00ʹ\xe3\x80V\xd0\n\xe7)A{\x96\xa0\x8a0\xa87\xd3\x1bЫ.B\xaf,=\xe9\xab\x1d1yd\xa4q\u07bc\xc6\xd2΅mIƣ\xb6*\xd1?\xab\xda\x03\x18\xd4\xed\t2\xb3|\xec\xdf*\xb80\x96\xb8\xf6\xc1V\xc3B\x0e'G!\xf3$\xf9h\x8c\x1bB\x8d\xa1Va{\xe1(\xec\xa3\x00H\xde\b\xf6\x82e\xe7\xe0Rz\xfdq\x1b\xbf#.O\x0f\xe5\xb2\xd3\x00b\xea\xd6{\xd6o\xe5\x882E\xda+\xb9RGqaaa\xb1\xa3\x94\xabl\x8f\x98ZR\x90J\x8d\xdd\xf2\x93-[~\xc2\xf8\xfa\xab\xd2[\xf4ٗ\xf5\x16\x8b\x1eV\xe8-\x03T\xec\xd1l\"\xed\xef\xef\x80C\"\xd0,\x1czw0\xbegѬJƩ7+\x95f\xbd\x93\xa9\x9c\xb5\xa8g<\xacDžo\x91\xfe\xd0\xe7\x80\x02\x98*p\xc9\xf8\x00\xd4\x17RG\xe1oҟ&\xe5\xef\xd3%c\x03^\xdc~[\x88$\x81\x89\x04[⑩.\x1dw\x03ػ\x9f<\xc8\x1fG\xf9wą\x01\xb4u\t\x17\v\x97\xc0\xb5a\xa8\x96\x94D\xa5Z.n\xd8\xeb\xff\x83&E\x14\xf4uF\xc6\xfa\xee\xef\x9f\x01C~\xa3\x99\xe5\xebL_\xf3\xbe\xdd3\xfe\xa2\x16\x06ϐ\xe6\xcdv\xf75䙾\xd6\xfd\xaa/\xed\\\x9c!\x04\x89\xa9\xbc\u007f\x13\v̫\x17zB\xd7\xc0\xb9\x18kh\x14\xa0y\x838!\xf5\xc9 \xb9\x84G\x96\x9dJR\x86^ό\xbd*\x8e_\x8a\x9b\xd74>\xf5\xdaS\x8dk6\x8b\vA\v\xb8\x12\xb4\\\x9b\xd36\x86\xa7n\xfaLz\xfc\x89#\x03\x14\x06\u007f\xbe\xfbUC\xcbر-\x86Ww\xef\xfa\xe1\x0f\xe1a\x19\r\xfc\x14HI\xb7I?\xfe\xeb \xc5\xc2\v\xf52P\x01\xaa\x98\xd8j\x88&\x8b\xf9\x82\x9a%v\f\x99s\x1eh1[M\tћ\x8e\x87r\x95\x85\xaf\xc8%݈\x15$wH\x8f\u007f\x86\xcad\x96\xdc~A\xad\xf1\xf6\x86\xcf7\x83ś?\u007f Wa\x8eº\x94G~\x8c*|\xf3M\u007f\x05\xad\xe4\xf6\xb3\xc3^\xfd\xe6nY\xd7R\xfa\xe8\xeeo^\x05\xc3zz\x0e\xe4j=\x10\x8f\xc5#[ۀ\x01C^\xbaW\x81\xc1b\xa6H\xadR\x06\x01o\x05\xc00\xe7\rsd\x17\x91y46~ZC\xf1\xcb7\x9e{\xf0Ɨ\x8b\x1b\xa6\x8d\x8f\x8d\x1esݳǟ\xbdn\f\x928d]\xec\xa2I\x1b\xf7\xec\xbcU\xba\xfa֝{6N\x82\x9f\xebJgnys\xf3]\xef\xbf\u007f\xd7\xe67\xb7\xcc,\xd5m\xdc9\x1f\xe5F7\xcd\xdf\t\x85\xdc\xcb|s\xea湟\x013\xbfi\x13/\xfd峹7\xf7\xf9\x9bfe\u007f\v6ʏ\xf5z\xfb\xf5&1>\xa0+a\x83\xdaK\x00;\xf5\xa1i\x0e\xe8\nc*\xda\xdfk\xaf\x18\x13پ\xed\xb9m۞\x03\aΡѕ\x96\xb9\xa4s\x84\xd60\x99\x1f\xc5\xf4\x8dHzBτ\t=\x8bgW\xb6\xb6V\xce\x06O\x11R>\xbb\x9f\xed\xfe\x06#9\xb1\xaf~\x93\xe9\x1dVs#\x02\x86y\xef\x1d\v\xb0nI\x11UM\xb5R\x9d\xd4\x1c<\x9e\x92}H$\xbe\xcb\xdbո\xba\xdf6\x9c\x0e\x8e\a\xfb\xc6K\xf9\x8d.\x1a^\xfbP\xe3\xf3\a\xef\xdd\xf6\xe8}M\xb1̓\x99X\x93O__\f\x1e,\xae\xef!\xaa0\xccr\xd4\xc5I\xff\x83\xe8]\xa5\x9e^\xe3H@\x9c\xae\xe4L\xa4d\x93\xa9\xbe\\\x18LӤ\xfb\x1a\xc5\xf9\x81\x1d\xbf)5mb<\x93\x89O\x9c\x96J\xb7\xb5\x81\x83D\xd7F:ya\xec\xec\xf3\xe7\xd2\xef\xd0/\x11,%\xed\u05ff\xab\xff\xabv\xec#\xd9\x1c!\xc0o\x1bS\x83\x83\xe2\xec \r؋\xc7\xd8K\x13\x12n\xc7\xfabiBq}\xbf\xd6\xfc\xef\xb7c\xcf\u05c8丣\x83\x87\xcf&\xd4v龖\x04\xef\x90V\xcc^p\x8c\xf4\xc5%\x1a\xf1Bڹ\xc3L\xf7YL\x96\x03\x87LH|\x94\x9dF\xfd\xc8N\xd0\x19\r\xd0\xef\x83F\x83\t;9d\x88\x923Y\x99\x02\t\x11o\x80#A\x86\x93\xbd\xd9b玲I$^9J ^\xe6\x8fo\x9c\xfa\xe0\xf8\xf1\x0fZ*E_\xaa|D$\x9a_\xb6\xe0\xa1k\x0e56\x82\xad\xab\x90\xb82\xe2Ʃ\xc3\xd6Lmȟ\xb1x\x97\xf4\xe1\xef\xb6m\xfb\x00\xb8n_\xf7ɱ;'\x1c\xb8.6\xad\xaa\xb6\x01~\x8aģJ\xe9%\xe9E\xe9g\xd2/\x8cE5\xcdE.Ì\xce\xc5sn\x97\xb68ڗv\x0e\t\xb5t\xa4\x1d\x97\xff\x02D\x1ex\x10\x14\xbdr\xf9\xf0\x1b\x9e\xfd\xfa\xda礟/j\x1e\xd1\xda;\x1e\xccQR\xecnʋ$\x86;\xa9\x9f\x12\x1bO\xa26\x85^G \xcb\x10\xb9Ez\x03\xd1\xf9\x0f\xf6Y\xbe\x92\xefg\xbe\xa0&\xd1\xebU\x0fuB\xb2\xa4\x86\xb2\xf4W\x88\xb0\x90\xcd\u007fY{\x12o\x96\x12\xc4\x18\xa23A\x96\xd8\xf0\xc2\ac5\xbbY\xa2Y\x12\"q\xc0\x1a.\xe8\x0f\xa4SF\x82/\x84\xedMeg\x98H\x82\xf9\xb9\xd7\x024\xf5\xb3N\xbd\x1d^\x1e\x12\xbc\xf53\xcb\xd6\\\x11\x9f\x00m:\xb3\x92\xad\xf7\xbb\xce\x1e\xb3\x87\xfc.\xa6\xd2\x1ez\xb7\xd169lP\xf3\x86P\x14\xa5\x18i}\x91\xb5\x81Vi\xabD\x96\xa1\xbd\xa1Ty\xa8\xd0\x157\x00`\xe2\x1ck\xee(\x1b\xd6\\fs9\x84H\xbc&R\x13v\x1a\x14\x1c\xadPi\x8c*\xab\xb3@\xe5h\x18^\v\u07fcN\xa8\x1a5\xcekpW\x8dV>\x11IV-\x80\xa2ZP+\xbcB\xf3\x953\xbb5p\x8e%\x9f\xd6o\x04N\xb0\x1d\x8c\a\xc6\xc4\x02\x87ਟ\xdbq\xec\x1b\xe9\x8fo\x8c\x9fD\xdb\r6q\x83+\x1c\xb2\xa3\x1f\x1c\xb1uVh\x8cY\xa5ᔅ\xf1\xf1ё\xa9BV\x13ӊ\xf6\x91\xfa*\xbd\xcdb\xab\x04\f\x03K\xdd\xc1\xbah\xb4.8\xb3\xae\xc8̲\x906\xa8\x8b\x9e_\x9f^\xb7d\xf1\x9ady\xa4ԠԘ]B\"ђ)\xc5\xfe\xa4,\xa2\xdai\xb5\x8d37\x8fܿM:\xf3_\xde\xf6i\xb5\x1e\x83~\xd8X\xf5\x1f@\xc9\xe6\xe3\x8b\xd6,\xa1-\x1a\xabѬ\x14\xf2\x1f\xd8,}\xf4pa\xff\xf5\x86<2\xeb\v\xa9\x10\x8f\x848\x11\xbb\xa9\xb2\x8a<\xa8\x04|\x1c{\x80\xb9\xc8\b\xfbޝʰ\xf7\xdc~\x97Ő\xf7;\b,j^-\xcd@\x14\xb2\xf8d\x06.\xbe\x84=\xc2\u007f\xc1\x1f\x17\x874\xd2cj\xa7\x8d\x1f\n\x1au\nV%]\xfb\x918\xff\xde\x00\xdc})\x83\x02\xaeϷ\x93\x96\xec$'\b\x0e*\x95\xd3\xfdK\xa5\x8d\t\xa3\x1bX1\x8a\xa2l8HH̛J\x9b\x89\x9f\xf041\x84\xb4\x18E!gy\x83\u007f\x10\x8f,\xcdU=U\xcd=M5账\xe9\x19\xa0z\xa6GV\xf2\xeb!\xe7=G\xc9?l\xfc^3\xdbB_\u007fn\x95evM\xdb\xd6\x12\x9a\xc2IY\xaadkۖg\x9e\xd9\xf2\x94\xf45\xe0\x9f:\xb2\x19\x1eñl\xe5fp\x9dl\\C\fl\xfe\x9f\xa8;\xbc>\xfb\xffm\xdd\xc1\xf5\xd2\xffJ\xdd\xcb\x13\x96\xff\xf5\xba_\u007f\xfd\u007f\xa7\xe6\xfd\xeb\xae$\xf3\xb2\\\xfb\xbe\xba\xa3\xb9\xe4?\xaf7\xfa\xfbwj=zŊ\xd1\xffq\x8d\r}\x18Lx\xa5\t{\xabo\xa6FQ\x13\xa8.j.\xb5\x94ZM]Im\xa5n\xa2vQ{e\x8f\x17\xa0\xd7W`\x14\xa4el\xb9|cΑJJ\xb4b\xecL\x98sI\xcd\xe4\xec\x80R\xbd\xf1\xde0)\xa7\x04\x06\xa7\x0f\xce\xff-\xf7\xf7\xde\xc7\r\n\xd9;U\xaa\xecM*\xbb\xaaC\xa5*\x1e.T\xb4\xcc]\xb8\xeb<\x85\x19\xe9\x85\xcf\r\xebz\xad\xa3\x18]ʗ\x15u\xa7\x90@V\xe4\xcdޗSޕ5\x82\xa9\x01\x89\xfd3J'\xfaGr\x19d\v\xe4)\xfd\x8e,\x8f\x9e\x83ꁪaW\x15w\xfeiְ]\v\xcf\"F\x1ds\xf5\x1d-aאb\x95J:D\xee\x9br\xd11I\x8a\xe8\xf9\x96\xab'.J\t]\x94\x82-[\xfb|\xf5\x05\xa9\x12\x82\x98:\x94j\xa36\"y\xfbF\xeavj\x0fu/\xf5\b\xf5c\xeaY\xec\xc1\x17\xefx\xf5\xb1|\xc4P\xbd/\x86\xfe\xa8A\xdaޡ\\(\x0e\x8a\x87.\xc1]V\x83\x1c\x1e\x9eH\x96\x15\xd1\x04\xc4!\xba\xe9O6q\x91\xfa\x96r\xbe\xad\xfc\xc1\xe9\xbdq\xaeGv\x8eX?$K\r\xa9\x17\xb4\xd8q3̘\x9c&\x93\xb3\x83\x1c\xa3丣߹|d:dn\x1dI&\xbb\x16.\x1e\x19\x9d\x1f\x11\xd5\xeaB\xb5Zz\x89\x04b@\xe9\f&\xca[1\xbe㹞\x8b\xee~\xe3_\xa6\xc8O\x03G\x8f>\xb0\xea\x05\xfc\x84բ\xb8\xd4h\xb5\x1a\x9f^\xf5\xc0Q\xf0\x03|\xcd\x14\xedw4]\x94\x92\xed\x13\x0f`\xcf\xc2]\xa3\x04\x9dw`増\xc7\xfd\x18s\xc9\x04\x8e^toǿL\x91\xff\bψu\x1d)VB\xe3l\x86\x1aN\xad\x90u\xbcx$\xce\x12V\xce\v\xcc\x186\x01\xab\xbe\xe2\xff\xd8}y\x00\xf1q\x84c\xc3<$^\xe6G\xdc\x1fV\x17M\x05\xd2)$\xcf\xf7\x99U\x98e_y\xd8[\x1e\xc0\x8ań\xbb$\xf6`xK)J\xf6_Sn@\x9f6z\xf2D霘\xe71\x82\xa3\xd0-\xfd\xed=\x05F]` P\xec{\xfe\x88\xf4\xf2\x8f7\x9c>0\x1d\x80\x9f\xed\xe3!M\x03\x05\x04z\xc5m\xa7\xd7)\xf8\xd5?\x05\xf4\xcd\xf7\x80\xd8\xfb\x9b\xb3\xa77?\xbdy\xf3\xd3\xe0\xe0\xa2i\n\xc4\xdbXyUUê\x97Vl9\xaaU5\x0eQ\xf1y,4(\xa6/\x82\xf45\x1f\\}\xcb?o\x05\x93&,{w\xe6\x94)3\xdf]:\xf1~@}.m\x98@k\x94\xa5&\xaf^I\x8f\x01\xf1'\x1f\a%\xf7\xab\xf8ŏ\xfcq\xe3\x93\xd2\xeb\xa3i\xa5%O\x19\xd3(5L\xd5\xefA١\x9b\x01\xfb\xfcz\xa5j\xc5q\xe9\xfd ~\xe6\xe6\xf3\xd4\xfa\xb7\x87q\nU\xb2@\xa5J\xed\xe8X\xf6\xf4\f\x8d\xfeg[\xa6\xde_\xa3RE\x92J\x05\xd7rb\xe3\xe6\xd3\xd7r\xfcֿ\xe6|\x93\xcbv\xc5\x02\x9a\x0f\b\x9a\xfb \x94e4L\x9cE\xdfCލ\x90\xe5f\f?\xdc\xdd_^\x01r9\x00\xdb-R\x83\xe57~\xc0\xbd'\xc9rfna\x88\xee\xf3\xef@S\x1a4\xd6S\x11`\x8c@4z\xcb\xeb\xb29\x14\xae\v\xd5\xe9\xab\x13Me\x11\xa1`(x$\xef\xd3\xe8\x01\xd9\xfd\xbd\v\x01\xb0[vr\x0eQ\xa6\xf3\xe8\n\xa4p\xba\xbc\x9e\x88\xd3\xf1\nAW\x9f_v\xfc\xee\xd8\xd7.\x15L\xa7\x921@\x0e!\x9f\x1e\x84Cd/\x12;)̡\xa0X\x91\xc4?x\xa5\xee{;T\xaa\x8f?V\xa9v\xa0a\x15\x85v\u05608\xbc\xac\xff\xab\xbf\xfbm\xd9rqF\xe8ߦt\xbf\xfa\xc9\xeb>\xff\xb6_\xdcA\xb5\xfc\x18?\xe7\x81\a\xe4\xe7\xa0P5(~N{\xf1'\x06\x0f\\:o_\\z\x95\xa1\xba\aʬ\xbdc<\xa1%\f\x84}\x11[\x1f\x05\xf1\xecJ\xe95\xb6\xfb\x12<<\x98\v\x93\xd9_\x81\xe3\x97\xe2\xd7yR6$\xba\x1f\xd8\x17k\x94j\xa4~F\xbd\x85\xadLt\xe8\xb5\xeb\x00\xcbɦq\xd8N\xce\xda\xd7DrÄ{\xaf\x89\xe6 \xe9\xe6\x04\xb5\a\xf5x!E\xb6\x00\xf9:\xe0\x01\xa90\xde\xf0\xc4r \xde\xe9D\x17\xd18\x82\xf5\xccҡ\xb0\x8fhWaY\x13[\x9fp\xe8\x02\xbe\x8eq.\xd1p\x83Qr\xf0Fv:\n\xf9:&!\x12=\x19Q\xbeΊ\xd6PXǠ\x01&e\":\xa6آ}0\xf6\b\xab\xd6\x17hԺ\xa4A\x9a\xa2\xb0\xf2\n\x05oU\xf0{\xfd\x1a\xbf6\xa4\xd1\xc8\xc1:\x9c\xc4+D\x03\xb8\u07b73\x15\x8a2-m\x99\x10\x14y\x81\xd3\xd1,ͿH[\xbd>\xae`\xd2P\xa1P\xa3\x81\x01\x0e\xd0tQ\x05\xa7Z8\xaef\xb1\xd3\xcd\a\x12\x9e\x92\t:g\x8dA\x1b\x0f\vQ\xadV\xab*)\xd3Bȃ\xa0\xdb&\xfa\xe7\xf8\xf2\xa7\x1c1\x00\x95^o)*\x8c\f\x17\xa0\xd2k\xb4V\xe4y,Z\x9d\x82/X\xc8\x02\xa7V˸E\x8f\xa0\x87J?\x14m\x85\x82N+\x94\xbc\xf4\x84g\xc2jGl\xd1\xfc\xfa\xf0\xdfч|\f}\xb1\xc7\xc8\x17kC_\xac\xeds&`4\x16\x98\x8cl\xe0-\x85B!\xe2W\x12;\xfcZmH\xeb\xd3\xfa5\x9a\xb0ƿ\x1a\xa7+\x14\x06qJ\xa6(\xe4l\x9b9\xc1\xec\x0e@\vgQY\xf4\xa29O2\x99]:\xb3jXڠU\x03PRb\x8e\xa8Ty\x1d\xf1q[T|\xa2,1\xbb%\xa5g2\x15\x8bWZ\xd4B\x9e\x1d\x80\xb8\x13\xdd\xe4bh\xe7\xf4\xeb\xcbu\xa2aI,\xea{b\x98A\xad1٪D\xa3P놜\x12\xb0z\x96\a|$X>\xb7t\xde\xe5\xaeB\x8e\xe3\xe3\x91\xfa\xea\xc6\x06wʞ\xe7N\x85\x8a\xbdj\xdba\xa0\xecNn\xaa\x986~,\r\xc1\xbaKڠ\x83\xbeuX\x8c\x10h\x14\x89}y=HЂ\x9fh$\xe6\x16\xa1\xea\x18\x94\xc6A\x8cT\xe3\xcfg\xcb\xf1w\x17La엪\x9c͏\xa71a\xe0\xfcx\xdd\x0fr\xf3\x1e\r\x06\x9bJ\x8c\xf9\xf3t\xfc<\x97\xbe&5Q\xfa\xc7\xc4)`\x8e\xbf\xac6\x16/4M\x9b\xcc%\xd8\x1d\x9f\x94\x14go\x90\xb6oj,\x03\nZ\rcM\x9b\xc0Z\xf8\xdc\xf5\x9fp\x06\x86\x9d\xe6\xf5Lh\xce\xfe֩gGdW\x00\x96\xa6a\xc9\xf0\x9b\xa4\xe7\xa4\xe775Ł\"\xfb֨VFm\v\xd7\x15\xbe\x17\x94:jY\x0eh\xe6ڴ\xa5i\xb8\x19\xec\xf8\xb26\xaa͛\xabq4e\xa7Mݰn\x951\xb7\x1fBt\\\x8cT1U\x8ax\uec79\x95;$\x0f\xe8\x18\xbf1\ue99d\x80\xc5\x11H\x14\xaekh\xccb\x93Ą\xd1\x0fЏ\x0f\x85\xfdH\x84\x13\x12\x02\x8b\xba\v\xeb\xf3\xfb\x8a\x801\x9e\x10S\xe1\x10[.\xdbs\x94\xa3\f\xe9Kګ\xdc\x05\x00d\x15:\xa5\x12I\xef\x10\xd4\x00\xc0\xa8\x15J\x96\xa1\x19\x8e\xe5\x14,\r\xce~\xb0~=8\xbcp\x9fӬٻ\xa8dd\x11x\x80\xa5\r&\xaf%b\xb4(\x98Ns\xe0\x81\n\x1a\x80ZF\xefsE=\xab\x96\xf2\xeeX\xdc\xfbx\xff-9\xf8\xe1\x11FT\x18x\x05\rʡ\x826\xb0\xe2\xacu\xc0\xaa\xd0sJ\xd5n\xa8\xe2\xd5\x1c\x06\x18\xe0Ԭ\xee\fxO*\x00\xef\xfd\xee\xb6\x11(\xa8\x90^\x06\xf5\xbaF\xab\xc1fа4JH\xec\xae۷\xc5\xe5\xf5\xeb}wI\x05\xee@-m\x1a\xb4\xd7\xc1R\xa5硢\x95\xfd'\x9a\xd1,\x94\x1d\xb5\xe2\xcd(1\x14&^\xcbD\n\x0f+1\xac\xd3/\xe0\xd1\x04\xe0\xcd\x05\xa2\xaa\xc9\xd7\xd1\xd8\xf3\x05\x1f\xc2J_\x10\x8di\xb0^F\"\xe0\xd1\xfb\x87\xb1\x815\x1e<\xd0M\xd8ҍ\xe7\u009c\xdfKѾ\x90\x9f\xc30\x04\xa25J\xc7@\x14\xe5\x83\xd6\x1cc\x84\x87\xb2\x00\x83\xb8\"f\r\xc7j\xa3W.Z\xe51\xeem\x00\x1dҴ\xfbm^\x9a\x19\x17d\xd7\x17\xf9\x8a\xdd\xec\xfe\roJ\x1f\xec\xdb)\xfdm\xa1[_s\xdf\xf7\xb6E\n\xf2\v\x94\f}\xe5/\x0f\xaeof\xf4\x15\xbe+\xbe~\xfc\xd6`P\xf4\xdb\x19]\xf9q)\xbb\xedH\xe4\xba\xed\x1b\xc3\xe1\x9b\u05fex\xa6Ego\xfe\xfd륾ᝁ F\xcbi\x01\x88\xa4\x8d\xfe \x1a<\xa2\xc3\x16\xc5]4d+\v\x1a\xca\x12>\x85P\u007f0\x03\xd5c#ۜ\xe5z\x9fw/\xf0\x83\xca]\xbf=\xfds@+ܳ\x97<4\x91\xf6\xbd-\xbd\x03\xab\x9d#\x9fH\x95w\xdc4\x04\x96f\xc6EEi\xef\x01\x10xk\xe3\x82\ueab9\x89!\x16\x8e\xa1\x81+\x18T\xa9-\rm5\x81\x15_Vq\x91\x86&[\x9eA)\xd8f\xe4\xcd\b\x9a\x99\xee\x03ӆ\xa85\xd6\xd0,\xb0\x01(\xb7\xb5\x1d\x97>\xb9,_mW\xd1`\nЂ\xf8\xc6\x05\x9dv\xbb\xa69t\xed͛\v\v\xa1Eo\xcfs84*O\x8d\xc2{\xfb\x8d\xaf\x1c\xbcl\x96ӧo\xa9\t\x8d\xbaLjF\xdf/x^ý\xc7\xfe\x8d\xb2\xa2^\x90\xa1&\x12\x8fS\xa9P8\a\x8d\x86\x15>\xf8\x14\xd0A&\x80\xb9\xcc::\xcdف\x06 V\x937C3\xf1!D6d\x00\x1b\x05\xc5\xe8\x02\a\xad!X\xc7\x10|y:E\x85\xb1_%7\xa3\xa3\xd1\agk]\xc3&Tm\x9bc\xd2\xe8\xfdVO\x95#P_\x14\xcc3k\xd5*\xb0\"\xf9\xfc_\xa4/\xa4o>\u007f|\x1e\v\xf4\xaa\x10\x93\x98\xff\x05\x18\a\xba\xc1\x94\xcb\xcd\xf0\xcb1\xdb\u007fr\xfc'\xdb\xc7\xc8\x01X>\xe4\x8fҧ\xd2/\xa5\xf7%\xe9H\xbb\xbb\x8c\x1dyӳ\xa7>\xfb\xfb\xe9\xd7Z\xf3\xabj4һ\xffT@h\xdf\xf8\xc6\xf6n\x8bu\xf6\xad\xa7\xb6/~\xe6\xc0L\xf8y\xf1C\x95a\x97\xd9aU\xb14\xa3Wi\x83\xc1\x82@~\x9e\x16d\u007f\xb9\xe9\xe9\x19y\x89\xcdG\x81\xf5\x9e\xc8\xc4\xc8Z\xedqi\xab$ݥ9p\x8fC\xcb@\xcf\xf1\xe7\xf0&\xd0sr\xc0\xed<>K1\xe6ѿK\xf7\x1c;\x00J\xfe\xf6\xc6\xf7\xe6D\xac\xe3\xef\xb9,~\x93t\xd5\xdf\xc0\xa4&\x16\x95<\xf5\xb6g\u007f\xfd\xfaOvL\x86\xee\xd9;^\x97\xf5I\xc8\x18C\xf6\x01\xf1\x1aJ=\xd1\xe9^FmB}d\x1f\xf5C\x8a\x12,~\x1f\xf6P\x89xG\xec\xb92\xf1?\x8d\x0f\xe6\x85\xd0XVD~\xe5\xd8\x05h\"^\xfe?\x8c\x1f]n(5\xa0\xbf\xe5\xdf\x112?\xaa(8w\x14\xfbL\xa53\x05\x15\x881\xfa\xee[H\b\xa8\x1e\x83\xc1\xe0E\xbf\u007f\xf7l\xff7\x19\xfc\x18\x16?\xec\xac\x02\xa7\xa0+\x9f\u007fG(\xeb\x10\xc6\xd0\xfc6\x06}\x9b[0\xaf)\xdb\xe1ư4\x15\n\xd3A\xa3\x15k߄b\x80\u061d\xd4\xe2k\xc4Ŋ\x91\xa5uX\x84\xeeU\xf5#\xf8)V\xb6\x04\xb0\xc4\xea\xa07Ń\x8d\xcdD\xab\xc0\x1ae\x80\x95\x1ct\xb1\v[\xa7\xb9\xb1ٙ\x11\xfb>\x16\xf5@\xb6\xee\xd5\x03\xf284\xcd\x04\xb5 \x88-\u007f9\xf7\xa1\xa7\xadZ\xad.n}:\xad\x8d\x0f\xd3Ε\xfez\xdc\x00\xf3\xf2#\x86\xe5\xa1dh\xb9!\x92\x9f\a\rǥ\xbf\xce\xd5\x0e\x8bk\xd3O[\xe3:\xad\xd6\xfa\xf4!\x97]Y\xe8\x02)\x02\f\xf9\n\xa3t\xf8\x18\xbb\x03\x17dO\x8a\xb9r\x80\xfe\x12\xe5\x00\xfd\xa0r\x1cv\xc6\xe7P2\xd2+\x04\xd32\xe5*T\xda\xc1\xc1\xfcEڄ\x15Uj\xe1\xfePB\x15\x04\xc5wK\xc7Θ\n=\x82\xc2\xd4\xf3\x0e\xd6\x05|\xa7Ǥ\x10<\x85\xa63\xa0\xf2n魠*\x11ڿ\x10\x95fMh\x17\xe5s\xd1X>W\xb7gO\x1d\b\x14\x17\xb2\xb8\xa4\xa8N'\x17$\xbdu7\xa8\xbctAұ\xbbA\xf1\xc0\x82\xd8\xc2\xe2\x00\xc0\x05q\xf9\xb1h\xaf͌̇\x9b\xb0D\x050\x93\x8b'\x15\x0e\xcf*\x01\x93\x12\x88&4\x870<\vBXFF\xe3V\x80}\x9eo\xdfq|\xd5\xe5\xef\u07fb\x80Gg\xbf^\xb5\x1b\x98\x1f\x06ä\x83kש\xd4G\xa4\xb7\x8e\x9c\xb3\x81Nr\x0eJ\x8e\x1c\x82w\xc1\xe9\xab\u007fs`\x0eϏ\xba\xf9\xf5U\xe4L\xb9\x9d:\xcf\xd4J\xf7\xac\x92^\xb9\xef\t\xe9\xe5c\xb6k@\xe7\xe5 }ߓ\xa0\xe2\x98M\x9c$\xaf?\xe6\xf0\xfft\xa8^\"\xaaY\x8a\xf8\xa0S\x03\xbf\x10N[yļ\x94\x00+\x1f\x0e\xa2\x1f\xf3]p}\x8f\x1fL\xfc\xf0\xa1\xb2\xc7FY>\xb7HCA\xe9\xd5\xd2qp\xe2\xf3y\x9f\x81M?\xedx\x0e\xd6\xe2\tMzA\xfa\xe0\xcd\r\x1b\xde\x04>Dm\xbe7\xffr)y\xe3\x9c\xf4\x04蒾\x0fV\xe7\x97͍\xc3\x05\xa8\x94\xab\xd7\xcc\xfbl\xee\x941ύ\xe9\"wm\xe8_\x12\\s\t\xae\x10ɬ\xe7\x01?\x85=OM\xa2fR\x8b\xa95\xd4U\xd4C\xd4\x13\xd4\vԫ\xd4{\xd4G\xd4\x19\xf4\x8e\xd8\x06\xa7\x0e\x84eHa\x1a[\xe2\xa0y\x1a\x8b\x18\xb4\xec\xef\n\x9b=sD\x84 R\x82U\x94W%Rd1\xc2\x1a'\xf3=\x9euR\x8c(/_\xd4\x01 \xea\x009\x11\xa9ܺ\x05ַ\x13I\x97\xc4\n\x8c\"\xba%\x8c\xb3\xe4\xd6;\xa20\x95\xc6ݎ\xe0\x95\xa6\x10\x93\x81\xd8b\x90+M\xbe\x81\x94G`\x8dp\xb2\\\f\xe8{\x9e\xd8?sX\u0381R\xd9\x14\x13K\x96\xd0\xec\xc8\x16V7\xaf\xc4M3\x90\xa7y\x96\xc7>\xd0\xd5\n\xb5\x9as\a\x1c\xc0\xa0\xb4h\xd4)wd\xa1\xd5\x10\x0f\x16\x89c\x9a\xdd\x11\x13\u007f\v\xcbyt\x0e\x0e\xce\x04\\\xa2\xd9̌m\xe7\xcc\x16\x17\x037\xf1\x9ax\x99\xb1\xa95~n\bg\xd0\xebl4mp\u0089\x1a\xde\x17Ѩ\xd1!k\tԣI\xdcdBG\x96\x114\x15CB\x1a\x87s\xc85C\xcb\x17OYb\xbejo\xad\x06\xcc\xfb۰8=vMa\xa8.\xc0\x94/l\xf2n\xdd\xf7\xe8\xb0\xe1\xdb\xd7M\x8aq\xc9f\x8b\xf7\xecJ\x9d\xd2,\x94i\xc9\xf1a\xc6\xe4s2\xb4`0:\x99{\x19\x8bY\xf0),fs~v\xb1A\xeft\xd4\x1a\f\xfaT\x1d\xfc\x861\xe8\xf5\xb8\x1a\xa82?\xd1+E1\xe5V\x15\x97\x83h\x9e\x19\xe4\xd9cO=\x1a\x9e\x03\x81\x11B@\x03\x9a\xa1\xa1\x96U\xb1\x1c\rX\x83\x15\xe8y$c9\xb4\xa6h\xa1\xf3\xc6\r\xb7\x80\xa1\xb3\x19h\xcfׂU\n\xb5\x8eׇL_\xaaCAkHq\xff>\xa5\v\x84\f\xd2\xd7\xce\xf2\xd9yJ-\xed\xb9\xdf-?\xcc\xceI'\x8c\x91<\x85\x11\x1f\xe8TJ#\x982v\x87\xc6$d\x81\xb31\xa4\xa9h0\v\x1a\x98Y!}=\xb2\x9en\xefb\xd3J0\xacd\xfe\x88N݊\x9b\x0fT\xd5l_9V9\xfe\xcaJk\xda\xc2\x0f\x99\xbem\x84\xa1\xa3{\x1e\\n.ӡ\xb7>A\x97B0\xa2\xd7f\x84s\xd5f\x1f\xc3X\n|,c\xa5\x17:\xea\xd1k;\x9cu>Cv\x9c\xde\xc6\xd0F\x9dގ\xeasZL\x19\xf4\xaa\xe2\x94W\xf5\u007f\x00T\v\xe1\xb6\x00\x00\x00x\x9cc`d```a<=\xe1|Ed<\xbf\xcdW\x06nv\x06\x10\xb8b|\xd6\bF\xff\xff\xff\x9f\x81\x93\x91\r\xc4\xe5``b\x00\xea\x00\x00d#\v\xd7\x00x\x9cc`d``c\xf8\xcf\xc0\xc0\xc0\xc9\xf0\x1f\b8\x19\x19\x80\"Ȁi+\x00{\n\x05\xce\x00x\x9c\x8dVKk\x14A\x10\xaey\xf4<\x8c\x9b,\x86\x155\x04VIL\x94,\x8a\xa8\xe8E\xe6\xb0\x1e\xbd\x88\x1e\f\x88\"\xe2E\"\x82'sj\xfc\x19\xfe\x0f\xc1\xa3\xbfJ\xbc\xadU3U=ߴ\x93\xacK>\xaa\xbb\xba\xba\xdeՓ\xcc\xd3g\xe2_\xfa\x92(\xf9ET\xd2\u007f\xe1u\xc1\xb4\xf0=O\xd6\x02'{?\x99Law\x18\xae\xe3]+t\xcfw\xef\x99\xcc\x18\xe4^\xde\xe9nD.\xd3}k\xdb\xf7zՇ\v\xce\xfc\x04\x99\x1c쯍U}ɩ\xa7\x18o9\xe4\x9d:\xf4\x1d\u038b\x94\x12\xf6\xe5;\xe3\xf8\xac\xd8FШ\x9e\a\x82\xb2\xe7O\x9c\x0f\xb9;\xc5\xdcX\x0e\x14S\x17\xd5B\xe5\x8e[x\x88\xa3\xcbe#2U\x94o\xcb\x03ا\xccC\x1e\xfa\x9a\x84\xb8\x91\x9a\xaf\xae??✼\t9\xb2Xz{\x12w\x8a>\x9f\tO3\x8d\xdbE\xb5\x9f*\xfd\xc2\xfcDe\xf2\xb8\x97\x18[\x03\xbf=픖wE\xad:s\xade\xea\xa9I5o\xc6ÞR݇\xd8G\x9c\xaf\xc4\xf2\x85=SB\xfcP\xbb\x06s\x12\xce|W\x1f\x8d+\xf8Ⱥ\x16\x8c\t\xd7\xe0\x99\x80}[0\xc2\xdel\xe4]\x8d1V\xcb\xf5\xb0\xa7\xa3~\x81ٴ\xb3F\xe7o\xc2\xfb\x85\xdeM\xd9\xc7\xd4r\xa2\xf4\xad\xe4\x89\xf1\x8e\xef;\x01\xcb'\x85\xfa\xc2\xf2\x13\x9bO^gL\xf7\xc6\xea\xady\xb9h\xb3o\xbc\xc4\x0f\xf2\xfd\xd0\xf8l7\x13\xac\x9b/\xa8\xdd\xfd\xec\x1bӌr\xa7q3}\x04=vC\xc0\xfc\x16\x85\xc1C\xaf\x13\xedH\x0fF=\xbc\x11ǡv\x97\xed\f\xf1\x9f@\xf3i\xf3lr\xe1.\xfbr\xa44\xbcC\xa5\xf2\xb4\x0e\xf3үV\xf8\xa6\xb2\xecldV¬\x8aL\x05\xfe[\xbde\xf6\xebN\xfe\x04\xdf\xe90WԿ\x0f\xe5o\xba\x15ϓ\xfaios\x1bW\x8dwz:\xadz\xfe\xdc\xeaQ\xaf\xa9Y\x84\x03\xf3Y\xfd\x9f3\xb6\xc1\xc6R\xfdy\xcc\xebK \x87>?\xe1\xf3+#\xefB\x8c\xab\x8c\xf7|\xfeJ\xfd\xbe\xadzj\xa6\a\xb5\xfa\x1e\x83\xe56\x05\xf8]@\xbdUD-\xbfP\x87v\xcd>n໌u\xc0;\x83\xb5\xd6W\xf7\xdb\x05\xe8\xd4\x1eO\xad\xaf\xe0\xbdMeFY\xbeг\x94\xd7\xed\\\xd8l@\x9d\x83\xae*\xf2!\xee\x89\xea\x0f\xbd\xa8u?\x96'\xebm\x8c\xbb\x80\xf3\x92\xce\xef\r\xcc'\xe6\xb2\xfc18\v\xfd\xc8\xf6>\xb4w\u007f\xf6\xfaC\xfcÚ\\f\xb9M\xa6\xbbc}\xa1\xef\xf8~5\xce\u007f\x1e\xf9\xf7\x14l\xcd\xcd\x17\x88mo,.\xd5}\x9d\xf7\x1fY\xef\xa1\xf2\xb3r\xa8[\xd6K\x9d\u007f\xe9\xc7\xccf\\\xf5\ay\xb7\xa2\xfdB\xbfG\x0e\xedy\xda\xc2\x1e\xb1o\x95\x9e\xef\xd7C[\xb3\xa8\xbf\xfe\xe9\r\xe0\xef\xda\xdd|\x04\xf0\xbfE\x12\xcfE@\n\xbd\xee\xe9\xae\xe4\\}\xbc\x13d<\xdd\x14z\xde\xef/\xf5\n|\xf1x\x9c\xa5\x96{T\xcfg\x1c\xc7\xdf\xcf\xe3N.\xb9\x86\x10\x1ai\xcdB\x91d\x8a\x10b!\xe3\x10\x8b\xb93\xb3i\x9bM\x86\x89e\x8c$\u05f94\x97\xadM\xc8=\xe4\x1e'4r\x0f\xb9\x87\x90i\xae!\xece\xff\xf8\u007f\xeb\x9c\xf7\xf9\xfe\xbe\xcf\xf3\xb9\xbc\xdf\xef\xcf\xf3}Nҿ\u007f\x1e\xff\x011\x92\xa9\b\x16H6\x02dH\x85\x82A\x9eT8T*\xea\n\xaeH\xc5GJ%\xc6\x02\xf6K\xb2^\xca\r\x9c\x92\x1c\xa2\xa4\xd2\x03\xa52\tRY\xde\xcb\xf1\xeeHY\xc7\xc5Ryr*УB\xa6Tq\"(\x90*ѯ\xb2\x97T\xa5\x9c\xe4D\x9eS\xbaTu\xb4T-\b\xc4I\xd5\xe9\xe7\xccz\r\xf2j\x16\ap\xaaE/\x97\x19\x00N\xb5\xe3\xa5:\x9eR]\x17ɕ\x18W\xb8\xd5\v\x94\xeagKn\xf4l\x007w\xf2\xdcS\x90G\x0f\x8fG\xd2{\xf4o\xe8\x0fx\xbe\x1f\x0e\xd8\xf3D\xb3\xe7J\xa9\x11=\x1bé\t=\xbd\xe0\xe5Ż7\xb5\xbd\xe1\xeb\x9d,5\xe5w\xd30@N386C\xa7\x8f\x03\xa0\x8e\xcf&\xa99^5\xe7\xe9;\x14\xe4J-\xf6H~\xe8i\t>\xf0\x03\xf0j\xc5^+z\xfb\x93\xefO\x9d\x00\xf8\aP\xbbu/\x90/\xb5\xa1w\x1b\xb8\aR+\x90\xf8\xb6\xec\xb5\xe3\xbd=q\xed\xb3\xa4 \xeav@\u007fG\x1f\xa9S\xa2\x14LLgr\xba\xa0\xbf\v<\xbb\xe0\xfb\x87IR\b\x9cB\xe0\xd7\x15\x1d]\xf1\xa9\x1b<\xbb1\x83\xee\xc4ug\xbe\xa1\xec\xf7\xa0fO|\xecE\xdd\xde\xf8\xd4\a_\xfaP;\f\xaeap\xe9K\\?\xfc\xeeG\x8f\x8f\xa9\x11\x8e\x8e\x01\xe4\x0f\xc0ǁ\x85\x01\\\x06\x85\x00\xce\xc1\xe0Ti\b5\x87\xd0s\x18܇\xa1}8\xb3\x18A\xdf\x11p\x1a\t\xb7O\xa9?\x8a\xfc\xcf\xd8\x1f\xcd\xd9\xf8\x1cϿ\xa0\xf7\x18\xceR\x04\xf3\x89 \xf7Kr\xc6Q'\x92s\x13\x89?\xe3Y\x1fO\xdcw\xd1\xd2\x04\xe61\x91\xb5IN\x00\x9e\x93\xf10\x8a\xd9EQ\u007f\n\xb9S\xe09\x15\x8d?\xd0'\x1a\xee\xd3\xe00\x9d\xfc\x19i\xd2O\xc4\xcfdo\x1691\xcc1\x86\xbdٜ\x8fX\xf8Ų\x16\v\x9fX\xd6b9\x97s\xe8?\x87\x9c84\xc6Q+\x0e\x8f\xe6\xc2\u007f\x1e\xe7q>\xf3_\xc0\xac\x16:K\x8bຈ\xf9\xfcL\xaf\xc5\xf8\xb3\x84zK\xd9[\x8aw˘Y<\xfb\xbf\xe0\xcfr4/G\xc3\nf\xb6\x02\x9e+\x99\xd7*\xea$p\xd6V\xe3{\"\xb5\x12\xf1r\r\xefkr\xa4\xb5\xf4Z\x87\xc6u\xcc1\tnI\x9c\xeb\xf5x\xb4\x9e\xefc\x03\xdc7\xf0\x1dl\x80\xdfFzmd\x16\x9b\x98\xcbf\xfc\xdaL\xdd-\xd4ڂ\x1f[9\x87[\xe1\x9dL\xde6\xe2\xb7\xc1i{\xfa[\xec\x80G\n\x9aw\xa2o\x17:wSo\x0f3܇\x1f\xfb\xf0j?\xfcR镊\xdf\a\xf0\xf0\x00:\x0f\xe2\u007f\x1ag&\r>\x87\xe8u\x88:\x87\xa9s\x04\xbeGXK\x87˟\xc4\x1c\xa5\xe7Qt\x1c\x83\u007f\x06\xb5\x8e\xa3\xff8\xf3;\x81\xd6\x13\xde\xe7S\xeb\x19g\xe29\xbd^г\x80o\xa5\x00\x8e/\xd1\xf7\x12\xde/\xe1\xff\nͯX{]\\F\x15e\x8al\x92)\xfaH\xa6X\xbeL\xf1\f\x99\x12\x03eJ\x96\x03\vdJ9\xc98\x14\x06+eJ{Ȕ\xe1*.\x1b-\xe3\xc8o\xc7x\x99\xf2\xe92\x15|@6\xd7t\x8cL%7@l\xe5^\xa0@\xa6\xca\x1e\x19\xa7(\x99\xaace\xaaE\xcaT\x0f\x95q>%S\xc3\x1f\xf0\xacIN-\xea\xd7bυ\xbc\xda\xc4\xd7!\xb6.\xdcꎔqeϕ\x1e\xf5\x87ʸ\xe5ȸ\a\xcbxP\xa3!\xcfF\x11\x00ލ\xaf\xc84\xf1\x04I2^\t2ެ7%\xa6\x99\xab\fw\xa1i\x1e$\xe3\x8b\x1e\xdf<\x99\x16\xf4\xf3\x83\x8f_\xa6L+8\xfa;\xcb\x04гu\x9aL\x1b\x10\xb8X\xa6-\xf1\xed@\xfb\xd12A<;\xc0\xa7\xa3\v@c'8\a\xa3\xbb3\xbetAC\b\xe0.3\xdd\xe0\xd0=P&\x94\xb8\x1ep\xfb(\x1c\x10ۓ\x1e\xbd\xbc\x001\xbd\xd1\xd2\x1b\xef\xfa\xe0o\x1f\xe2\xc3\xd0\xdc\x17\x1e\xfd\xd8\v\x8f\x93\xe9O\xaf\xfe)2\x03\xe0\xf9\ty\x03\x13e\x06\xa1e0ڇd\xc9\feN\xc3\xc2d\x86\xc3g\x14\x1aF\xd3{\fu\xbeB\xdb\xd7p\x1f\x8b\xb6o\xa8\xff\xed\f\x99q\xe4D\xf2\x1c\x8f>\xee*3\x81\xbc\t\xccs\x023\x9e\x88\xaf\x93\xa8\xfb=\xf1\x93\xe16\x99\xfd(\xf2\xa7\xe0\xdfT~G\xb37\x8d\xdc\x1f\x99\xeb\xf47\x80\xdfL4\xcd\xc4\xdbY\xf8\x1aC\xbfٜ\x9b9\xe4\xc7\U0005c2ee\xb9\xccz\x1e\xb5\xe7\x13\xbb\x10]\x8b\xa8\xb7\x98\xb8%\xccq\t\x1a\x97\xb2\xb6\fϖ1\xc3\xf8T\x99\xe5\xcce\x05\xbdW\xa2e\x15}\u007f\x9d(\xf3\x1b=\x12\xe0\xcb\x1dd\x12r\xdf\xe2w\xbc\xf8\x83s\xb5\x1aoWs\x16\x12\xf1e\r\\\xd6\xf2\xbe\x16\xbd\xeb迎\xf7$\xfcH\xe2}\v\x1enEc2\xdepϘ\xed\xeco\xc7\xdf\x1d\x9c\x87\x1d\xe8K\x81S\n}w\xd2o\xd7\x1b\xb0\xb6\x9bZ{\xf0\u007f/\x1c\xf7\xc2o?9\xfb\x99w*z\x0e\xa0\xf9 \xfd\x0f\xc2%\rއ\xc0a\xfa\x1ca/\x1d\xcdG\xe1|\x8c\xfa\x19\xf4<\xcelO0\xab\x93!\x80\xbd\xd3\xcc\xe9\f\xbd\xcer\x96\xce\xe2Q&\xe7\xf5<\xfc/P3\v\\\xa4\x16w\x85\xb9D\xbd\xcbp\xb9\x8a\x0f\xd7\xc8ˆ\xc7\r\xf6n\xfa\xc9܂\xd7-\xf4\xe5\xc0?\x873u\x9b>w\xe8y\x87\xdfwᘋ\x8f\xb9ɀ\xda\xf7\xa8u\x1f}\xf7є\x87\x86<\xfc\xfa\x1bm\x0f\xe0\U000d0f07\xf8\xff\x98\xbaO\xf8~\x9fp\xee\x9f\xc2\xed)>\xe5\xc3\xef\x19k\xcfy\u007f\x81g\x05\xc4\x14\xa0\x85{ü\xe4,\xbc\xa2Ǜ\xfb\xe2u\x86\xac\xf1\x92\xb5β\x85\x03\x00\x1d(\x01M\x00x\x9c\x95\x93\xdfj\x13A\x14ƿݤMk\xa4`\xd1R\xbc\x90AD\xc1\x8bݴ\x94\x16\x827\xdb?\xe9Mhb\b\xa5W\xea6;I\x96&\xbbav\x92\x90k_@\xf0\x05\xc4+\x1f@\xbc\x17\xbc\xf4U\x04\x1f\xc1o'c\x13\xb1BMH\xe67g\xe6\xfc\xf9\xce\xd9\x05\xb0\xed<\x85\x83\xf9\xc7\xc7\x1b\xcb\x0e\xca\xf8d\xd9E\t\xdf,\x17p\x0f?-\x17Qv\x1eZ^\xc1\xa6S\xb7\xbcJ\xfb\xd4r\t/\xddg\x96\xd7p\xd7}oy\x1dw\xdc/\x96\xcbx\xe0\xfe\xb0\xbc\x81G\x85\x80Y\x9c\xe2:w\xafLƜ\x1dl\xe1\x9de\x97\xb7>[.\xe01\xbe[.b\xcbq-\xaf\xe0\tu\xcdy\x95\xf6זK\xf8輵\xbc\x86mwfy\x1d\xf7\xdd\x0f\x96\xcbx\xee~\xb5\xbc\x81\x17\x85\x02\x8e\x90b\x84\x19\x14b\xf4Ї\x86\xc01BL I\xa7\xa4\x04\x11\xcf\x05vQ\xc1\x0e\xf6\xe1\x91\x03\f\xf8\x15K^\x99\xd9I\xae\x92k\xee\x1d\xf1&\x8e\xd2\xd1LŽ\xbe\x16\xc7\xe1D\x8a\xd30\x89fb\xb7\xb2\xb3\xef\x89`0\x10\xe6(\x13JfRMdD\x87\x1a\xebI\x18/\xc0\xd4DK1\xe4\x8aZ\x9a\xe8`*\xb3t\xc8M\x8b\x96\x1eƬ d.\xb4do<\bU\xee\xdb\xc0\x19ڨ\xd3\xfb\x10U\xeeڴ\x9d\xe0\x02Mr\x8b;\xd4\x1ag\xedzpXm\xb4k'\x17\xcdF\xab}\xbb\x8c\xe7FUF\xb5\xf9]\x81=j;௲\xd4\x17\x9cK\x95\xc5i\"\xf6\xbc\x03\xafbD\xde.x\x93B$\xa5d\xa6\xe5y\x13\xbb&\x9d\xa0_j\xfe\xfb\xe6\xe4\xa6Q\xe5>\x1d\xd2\xefº\\ՒO\xd7\xe6\xcf-\x8a9\"Z\x87\xa6mW\xb4\x85\xb4j\x13\xef\x92\xed\\DI\xb8滎\xa9\x99Si\x0ed\x98IΩ+\x95Щ\xd0})\x16\xa3\xcddG\xe7»\xa92']\xaa\x13Z\x85\x91\x1c\x86\xeaJ\x84Z\xab\xf8rl\xae$\xa9\x8e;2\xb3\x83V\xa6\xb2\xbfz\xa3\xb4\xb8n\xceM\xcf\"\x16\xcf\x12L\x1f4\xfbR\xe5+\xee_\xeb\r\xff\x88\xe9\x19e\xe8k=\xaa\xfa~^^8\x8f\xef\xc5\xe9\xffD\xf09\xa9yW\x12\xd3y\xff\x1f1\xfd\x01E&\x99\xf4\xf1\vϋ\xd8\x11x\x9c}W\x05t\xe3ȲuU\x99b'\x19X\xa6\xb7̔ؖ\x9c,O`\x99\x99\xbd\xb2ݶ5\x96-\x8d 0\xcb\xcc\xcc\xcc̏\x99\xf61\xbf}\xcc̰\x8f\x99\xaa\xa4\xf6L\xe6\xfcs~N\xd2$ݾ\xdd}oW))L\xfd\xbf?\xf8\x1a\x17\x90\xc2\x14\xa5nJ]\x9f\xba.uc\xea\x96ԭ\xa9\x1bR\xb7\xa5n\x06\x04\x824d \v9\xc8\xc3\x10\x14\xa0\b\xc30\x02\xa3\xb0\f\x96\xc3\nX\t\x1b\xc1ư\tl\n\x9b\xc1\xe6\xb0\x05l\t[\xc1ְ\r\xbc\t\xb6\x85\xed`{\xd8\x01v\x84\x9d`g\xd8\x05v\x85\xdd`w\xd8\x03\xf6\x84\xbd`o\xd8\a\xf6\x851\x18\x87\x12\x94\xa1\x02\x06\x98P\x85\t\x98\x84\xfd`\u007f8\x00\x0e\x84\x83\xe0`8\x04V\xc1\x14L\xc3\f\xcc¡p\x18\x1c\x0eG\xc0\x91p\x14\x1c\r\xc7\xc0\xb1p\x1c\x1c\x0f'\xc0\x89p\x12\x9c\f\xa7\xc0\xa9p\x1a\x9c\x0eg\xc0\x99p\x16\x9c\r\xe7\xc0\xb9P\x83\xf3\xc0\x82zj4\xf5Fj\x04\x1a\xd0\x04\x05-hC\alX\r]p\xa0\a}p\xc1\x835\xe0C\x00!D0\a\xf3\xb0\x00\x8b\xb0\x16·\v\xe0B\xb8\b.\x86K\xe0R\xb8\f.\x87+\xe0J\xb8\n\xae\x86k\xe0Z\xb8\x0e\xae\x87\x1b\xe0F\xb8\tn\x86[\xe0V\xb8\rn\x87;\xe0N\xb8\v\xee\x86{\xe0^\xb8\x0f\xee\x87\a\xe0Ax\b\x1e\x86G\xe0Qx\f\x1e\x87'\xe0Ix\n\x9e\x86g\xe0Yx\x0e\x9e\x87\x17\xe0Ex\t^\x86W\xe0Ux3\xbc\x05\xde\no\x83\xb7\xc3;\xe0\x9d\xf0.x7\xbc\a\xde\v\xef\x83\xf7\xc3\a\xe0\x83\xf0!\xf80\xbc\x06\x1f\x81\x8f\xc2\xc7\xe0\xe3\xf0\t\xf8$|\n>\r\x9f\x81\xcf\xc2\xe7\xe0\xf3\xf0\x05\xf8\"\xbc\x0e_\x82/\xc3W\xe0\xab\xf05\xf8:|\x03\xbe\t߂o\xc3w\xe0\xbb\xf0=\xf8>\xfc\x00~\b?\x82\x1f\xc3O\xe0\xa7\xf03\xf89\xfc\x02~\t\xbf\x82_\xc3o\xe0\xb7\xf0\x06\xfc\x0e~\x0f\u007f\x80?\u009f\xe0\xcf\xf0\x17\xf8+\xfc\r\xfe\x0e\xff\x80\u007f¿\xe0\xdf\xf0\x1f\xf8/\xa6\x10\x10\x910\x8d\x19\xccb\x0e\xf3\xa9\x1dp\b\vX\xc4a\x1c\xc1Q\\\x86\xcbq\x05\xaečpc\xdc\x047\xc5\xcdps\xdc\x02\xb7ĭpk\xdc\x06߄\xdb\xe2v\xb8=\xee\x80;\xe2N\xb83\ue0bb\xe2n\xb8;\xee\x81{\xe2^\xb87\xee\x83\xfb\xe2\x18\x8ec\t\xcbXA\x03M\xac\xe2\x04N\xe2~\xb8?\x1e\x80\a\xe2Ax0\x1e\x82\xabp\n\xa7q\x06g\xf1P<\f\x0f\xc7#\xf0H<\n\x8f\xc6c\xf0X<\x0e\x8f\xc7\x13\xf0D<)\xf5:\x9e\x8c\xa7\xe0\xa9x\x1a\x9e\x8eg\xe0\x99x\x16\x9e\x8d\xe7\xe0\xb9X\xc3\xf3\xd0\xc2:6\xb0\x89\n[\xd8\xc6\x0eڸ\x1a\xbb\xe8`\x0f\xfb袇k\xd0\xc7\x00C\x8cp\x0e\xe7q\x01\x17q-\x9e\x8f\x17\xe0\x85x\x11^\x8c\x97\xe0\xa5x\x19^\x8eW\xe0\x95x\x15^\x8d\xd7\xe0\xb5x\x1d^\x8f7\xe0\x8dx\x13ތ\xb7\xe0\xadx\x1bގw\xe0\x9dx\x17ލ\xf7\xe0\xbdx\x1fޏ\x0f\xe0\x83\xf8\x10>\x8c\x8f\xe0\xa3\xf8\x18>\x8eO\xe0\x93\xf8\x14>\x8d\xcf\xe0\xb3\xf8\x1c>\x8f/\xe0\x8b\xf8\x12\xbe\x8c\xaf\xe0\xab\xf8f|\v\xbe\x15߆o\xc7w\xe0;\xf1]\xf8n|\x0f\xbe\x17߇\xef\xc7\x0f\xe0\a\xf1C\xf8a|\r?\x82\x1fŏ\xe1\xc7\xf1\x13\xf8I\xfc\x14~\x1a?\x83\x9f\xc5\xcf\xe1\xe7\xf1\v\xf8E|\x1d\xbf\x84_Ư\xe0W\xf1k\xf8u\xfc\x06~\x13\xbf\x85\xdf\xc6\xef\xe0w\xf1{\xf8}\xfc\x01\xfe\x10\u007f\x84?Ɵ\xe0O\xf1g\xf8s\xfc\x05\xfe\x12\u007f\x85\xbf\xc6\xdf\xe0o\xf1\r\xfc\x1d\xfe\x1e\xff\x80\u007f\xc4?\xe1\x9f\xf1/\xf8W\xfc\x1b\xfe\x1d\xff\x81\xff\xc4\u007f\xe1\xbf\xf1?\xf8_J\x11\x10\x12Q\x9a2\x94\xa5\x1c\xe5i\x88\nT\xa4a\x1a\xa1QZF\xcbi\x05\xad\xa4\x8dhcڄ6\xa5\xcdhsڂ\xb6\xa4\xadhkچ\xdeD\xdb\xd2v\xb4=\xed@;\xd2N\xb43\xedB\xbb\xd2n\xb4;\xedA{\xd2^\xb47\xedC\xfb\xd2\x18\x8dS\x89\xcaT!\x83L\xaa\xd2\x04M\xd2~\xb4?\x1d@\a\xd2At0\x1dB\xabh\x8a\xa6i\x86f\xe9P:\x8c\x0e\xa7#\xe8H:\x8a\x8e\xa6c\xe8X:\x8e\x8e\xa7\x13\xe8D:\x89N\xa6S\xe8T:\x8dN\xa73\xe8L:\x8bΦs\xe8\\\xaa\xd1ydQ\x9d\x1a\xd4$E-jS\x87lZM]r\xa8G}rɣ5\xe4S@!E4G\xf3\xb4@\x8b\xb4\x96Χ\v\xe8B\xba\x88.\xa6K\xe8R\xba\x8c.\xa7+\xe8J\xba\x8a\xae\xa6k\xe8Z\xba\x8e\xae\xa7\x1b\xe8F\xba\x89n\xa6[\xe8V\xba\x8dn\xa7;\xe8N\xba\x8b\xee\xa6{\xe8^\xba\x8f\xee\xa7\a\xe8Az\x88\x1e\xa6G\xe8Qz\x8c\x1e\xa7'\xe8Iz\x8a\x9e\xa6g\xe8Yz\x8e\x9e\xa7\x17\xe8Ez\x89^\xa6W\xe8\xd5\xd4\x1d\x99\xb6c\x05A\xa6\x17\x05v#\x1b(\xcbot\xf2\xaa?\xa7\x1c\xd7S\x99\x0e\xf7\xc3t\x10Z~A\x8a\x9a\xeay\xe1b:\n\x94\x9fn\xd9N/\x1fvj\x8e\xe5\xb7\x15\x86\x9d\x9c\xb4\xed D\xb7\x9b\xf5UϝS\xb9\xb5\xae۫\xd9\xfd|\\\xbbQHn\xab\x95\r\xecv\xdfr\xa8\xe1\xb63\xa1o\x05\x9dt\xc7\xed\xa9<Ϧj\x96\x13\xa6C\xbb\xa7Ҿk5\x87\x9b\xee|\xdf\xe1\x86\f\xe7\a\x9dl\xe4I\x95\xb1\xfbuw\xa1\xe89\xd6b\xada\xfb\rG1\xa7\xa7\xac0竖\xaf\x82N^\x96\x12O踍n\xba\xe5X\xed\x02o\xa6\xe9uܾ\n\ns\xae\x13\xf5T\x8d\xd7S\xd4M!\x18\xd2\xed\xc8ˮ\xf1\x1bnS\xe5\xeaV\\Sh\xb5\xd3\xfc\x17\xa4\xeb\xae\xdb\xcdKѳ\xfcn\xc6\xf3\xed~\x98mX=\xe5[\xe9\x96\xdb\x0f\xf9\xb9\xd3\xccڡ\xe5؍b\xa8\x16\xc2ZG\xd9\xedNX\x88\xdb\xf3v3\xec\x14\xf8Y\xbb_sT+\x1cN\x9a\r\xd5\x0f\x95_L:\xbe\xbc>\x92\xb4WGAh\xb7\x16Ӳ\x97\xa2\xddo\xf2{\tN\xb7\xe3wG[VCɩ\xd5\xe6\xec\xa6rs\x9e\xdd\b#_e=\xd5o\xd8N\xa1gy5Y\xab\xf2\xb3VS&\xe4\x13\xe6u\xaa\xa6\x1df\x82\x8e\xe5\xabL\xa3\xa3\xf8\x84D\xb0\x91 T^\xadn5\xba\xf3\x96\xdf\x1ciY|\x84\x83^~\xd0Hˡg<\x8bM\xc0\xc6p\xbd\\\xcb\xf5e|8~}Љgҝ\x8cZ\xad\x1a\xe10\xf3\xcc\xf9n\xb2\xf3\x91A'\xde\u0090\xe7DAM\x8cQ\xe8\xd9}\xdd,&&\x8a\xdb9\xb7\x1b\xd7#k\"\xc5G\xc28\xe9\r\xd9\xfd\x96\x9b\xc0\x82\x86\xafT?\xe8\xb8ሆ%\xae\x18b`\xd2*ԭ\xfe\xa0i\xf9\xbe;\x1f\xaf\xa3\x984\xe3U\xe4\x93v\xe4\xe9\xe7\xb1#\xe2#\x12\x1f\xf1r\x02{\xad\xaa\xb5\"\xc7\x19\xd6\xed\xa0g9\xcer\xb5\xd0p\xac\x9e\xb5nY\xe9\xb6\xddb\xdb)\xab\xc5w\xc4Wy\xb5\xc8Fc5\x86\xa4\xd1p\xdc@\r\xf3\xa9\xf4\xed~;~=\xc3\xe7\xd9W\xf9\x86\xe5\xa8~\xd3\xf2\xb3\xbe\xd5o\xba\xbd\\\xc3\xed\xf5X\xe3l\xcfj\xf7UX\x18\x9cW\xe4\xad;GY\x1f\xdb=\x9cW*\x1c\xe1\xad{\x9eL\xd9\xe0\v;\xdcb\x17*?!+\xea\x8e,a\x99^\xf8\x9c\xf2C\x9b\x19W\xe8~\xc7\xf5\xed\xb5l_\xcb\x19b\xc7\xd7\x1a\x1d\x99$\x9c\xb7C\xf6er\xf0b2\xb1}\xdc\x1bN\x1c_crߥ\xaeZL\xf3m\x0e\xf2z\xc9\xc1H؉z\xf5\x80\xd7*\a\xb7L\xf7d\xb9\xd2\x1f\x8a\x03I\xc7rZ\xc58\xba$1%'\xf3r\x88\x18q\xec~\x97͙\x1ce\u038b\x82\x0eok\x84o\x8f\xf29l\xd4\xe4q\x1cB\xec~\x96ɽ\xceb\xb1m3C=\xf1A\x12\x1d\x84&\xe3\xb0\x0f\xf8p\xe5\xbe\x17c\x8b'D\xa3\x83˛t\v\xf1\v\t\x99\xdep~\xb0\xd7l2s6\xeaK\f)\xb2\xc5\xf8\xd2\xc8\x017\xc9\x0f\x02\xea4\xf9R\xb0\x1b\xf8\xf0\xfa\xe9\xbar\x9cbC\x8e\xb5\xc5\a\x1b\xaaB\x87e\xd4\ue39b\xe2\xb6\\܊\xbcdD\x0edE\xe2\xc8\xdazG\xae\xdc`$\x9e`\xd9\x06C\x91\xb7!H\xa6\xe1\x18\xee\xd6Uv\xde\xe7;\xdfɄV\xd0\r\xb2\x1cQy3Cu\xdfV\xad\x86\x15\xa8\x8287\xb9'\x99\xb6\xefF^Z\xce2\xc3\x1e\x89\x9aٺ\xb28BP#\nYJ\x8fO\xc5\xf2b\xff\xd8^:\xb0\xe6TAΧVg\xa3v\xd9q\xae\xcf~\xc2\xc8A\xd7\xe1\x88\xe1\xdb]\x15vx\xc2vg(\xe2\xb8\xe4\xf3\xb4\x8a\xd7PwT\x86\xcdk78\xccG\x8d\xee\x10\xcb\xc8\xeb\xe1\xeb;\xba\xae\x15\x1f\xfb\xf2\xb6\xeb\xb6y7\xebb@q\xc9@\x865T\x8b\x05>s\x15\xc6;\xcd'M\xbe\xa4I#\xbe\xc4I3>+\xbe7\x1c\xc2\xfbA:p}\xb6\x1a\x17\xc9=\x89[|y\x06\x99-N*\x03\xaf\xa5y\xdd.\x1b\xa6\xcd\xfeorJ\xaa\xbb\xacqQ\xdbY\xde\x1c\x1eX;\xce(\x1c\xe3C\xf6k\xa88\xb6\xe6\xd9\xdb>koqD\xe4\x98Wpd\x115\xb6E=\xcfq\x81un\xab\xd1\xf8\x88k\x83\f6\x9ct\x13\xa7\xe6$\x95\xd6z\xcd\"cÎ\x1b\xf0\xe1\xab|\x10١(\x96\x17S\tc\xb6\xc1\x89J)\xce0.Geɔq:\x91-\xd4#\xdb\xe1\x1d\xb4\xf3\f\xf6$\xef\fY=f\xb7\xfa\r\x95\xed\xa9f\xd7\x0e\x8b-Y\x12\xb3\xacV\xbct\xc5y\xa0\x93\x84\xa9\xd6XK\xadh\xbaQ]\xacԗ\x13\x8f\xfd\xb7\xc1H\xe2\xbf\r\x86\xd8\u007f\x1b\xf4e_\x85\xf5\xf8\xe2\x12`~\x80(\xac\u007f5\xd7TA\x97\xd3Fֱ<\xa9b\xa3\x84\xc3=\xb7.\xfb\x8ao\xe3\xb0\xf6w\xec\xb7\u009a\xc8\r\xf5\xd4I3љw\xdb\xef\xf3f\x92w3\x9c\xfd\x9dł\x0e\x05|0˗\x86\xc08\f-\t\x83\xd2/\xa8\x05Ona\xa2.\v\xe8%\xefe\x82\x1e/$\xd3\xe2\xabէ\x9e\xea\xe4\xda\x1c\xeb<\xab\x99\xe70\x17\xfb\"/\xdf\x12\xf2\xe6h܈C\v\xbb\xb9\x99\xe73\xe6\xece9i\xf9b\x18\x8a\x17į9\xcb\xd6\xc5;\x1d\x808\x98$\xc9\"\xbe\xbf\xe9\x06G\xb1!\x81H\xba\xecJ\xb0aW\xa6k\xa5\xeadqIf)\x06\x11\xdfH\xbe\xbe\xb6Ƕ\x8e\xeaI\x8b_\x9b(\x0f{\xd1ڵrv\xb6j(N\xa02\xa1\x1c\xe3\xe8\xfaf-\xfe\xf0\xea\xd8\xcai\x8e\x0e\x12M\xb2\x9a\x15\x92\xa2j\xec&\xf6Pd\a\x1d>Q\x9f\x83\x9d\x92ij\xd0hr\x80\xd2\xd9&\x18|\xb4\xac\xdc`D\a\xa8\xa5C\x12\xa0\x96\xf6\xe3\x00\xd5\t{\x8e\x91n\x04A9\xcb\xde\xe4\x90YH\xa2\xaa61G&Ύ\x1b\xb1\xdfm/\xb0\x83%\tiź\xb1A\xd2J\xd7\xcac\xe5\xa1\xf8\xd3O\xe6\xcf\xf2 \xafwt\xfd\x97C\x9c\xae\x93\x90\x1f\x0f\xe6\x1dŗ^l\x984b\xc7&\xcf\xe3ψ8\xac\xc7W\xa2V\x1e/\x15\x92\x94\x1fg\x04\xbe\xf6|\xad%\xb3%\x06Y\xef\x14\xb6\xae\xbc]%\x15\xf9Ԯ{\x14\x05M\xb2\xfb>\xad\xf6\x16ɏ\xea\xd4\xf5\xe7\xa9\x1e6\xe43Y\r\xad\xbb\xb3\xcb\xe38T\x17cx\x1d\xab\xce7\xb2V.M\xae\\7\x1ar8\xadG\xa1\n6\xfd\xbfC\xb2\xad\x91\xc1p\x1c\x83WlЋcS\xad\\\xaeHa\f/r6\x8d\xeaz#\xba\x93^`\x99\x87\x16\x06\x9f\x1e\xebޑ\xc3\xcc5\xd9,\xfcQ\xcd!\x9d\xbf\xf4\x06\xc1\x8b\xbf\xb1\xb8\xdf\xf6\xad^\xb6\xc5ߴ]\x9f\xac&\x87\x8e\xf1\xea\xf8h\xdd\x0e\xeb\x91\x1c\xbd\x96\x81#\xa1\xe3\x17\x93*\x1eZ\xe6\xb8L\xb4>K\x8d,\xe9G\xdeҧ\xe2\xab\xe5K\xfa\xc9\x15\x9f\xe7\xcf\\w>\xc8\xf15\xf5]\xbb\x99\xe1\x8b\x11-\xf02\xed\xba䖠\xbb\xe8qRs#?X\x13\xb1b\xfc9\xc0Vq\xb3-\x0eˎJK!\t<\xb4=\n\"\x91\xd64s\xf2ύ=\xa7\xa8\x1e\xb5q\xae\x9b\x99Wv\xdd\xe5\u007f\x1c\xfa\xfc\xcb/TK\xa3\xf1\xdek\x83\xcd\xcbXe\x93dI\x83\x9c\xeb$9G\x1e\x99\xa3M7\\\xf2@\xc6&\x86\xe7\xf8S\x9c\xbfJ\xe35\xf1\xc8\xc4\xd8H\x92\xd9⁚+C%)\xcaR\x88V\x13\x86\x14\xa6\x14U)&\xa4\x98\xccE}\xfb\xd0\xf1Uc|\xd6\xd68\x8fL\nh\xb2,]\x01M\nhR@\x93\x02\x9a\x14\xd0\xe4d\xbaV\x19\x8b\x11ui\x95\xa4(KQIf\x9b\x1a\x97\x8e)EU\x8a\t)\x044>&\x85<\x1d\x17и\x80\xc6+R\x18R\bb\\\x10\xe3\x82\x18\xd7k\x9b\x1eӵ\xe0J\x82+\t\xae$\xb8\x92\xe0J\x82+\t\xae$\xb8\x920\x95\x85\xa9,\x88\xb2 ʂ(\xeb\xe5\xcd\xe8\tg\xc6u\x1d\xbf!в\xa6\x9c1tm\xeaZ&\xaf\xc8\x1c\x15a\xad\bkEX+\xf1\x03\x81V4tV\x88\r!6dZC@\x86\x80\f\x01\x19\x022\x04d\bȐ\xa5\x9a\x820\x05a\n\xc2\x14\x84\xa9\x97zh\xfcL@f\x95ϻ\x15?\x13PU\x1eT\x05T\x15PU\x1eT\x85\xa6*4US^nHKh\xaa\x82\x98\x10Ą \xc4\x17\x15\xf1EE|Q\x11_T\xc4\x17\x15\xf1EE|Q\x99\x10Ĥ &\x05!\xa6\xa8L\nb\xb2\x92n\x95b\x19\xd9\x14܊\x1f\bBLa\xb0)\xb8\x18\x97\xa2$EY\x8a\x8a\x14\x86\x14\xa6\x14U)&\xa4\x98\xcc\xcc)\x0e\x9b\xdc\x14K\x182\x97!\x960\xc4\x12\x86X\xc2\x10K\x18b\tC,a\x8c\vIIHJ\x82\x103\x18b\x06C\xcc`\x88\x19\f1\x83!f0\xc4\f\x86\x98\xc1\x103\x18b\x06C\xcc`\x88\x19\f\t_FY\x10eA\x94\x05!\x1e0ʂ\xa8\b\xa2\"\x88\x8a DzC\xa47DzC\xa47DzC\xa47*\x820\x04!\xba\x1b\xa2\xbb!\xba\x1b\xa2\xbb!\xba\x1b\xa2\xbb!\xba\x1b\xa2\xbb!\xba\x1b\xa2\xbb!\xba\x1b\xa2\xbb!\xba\x1b\xa6 LA\x88\xe8\x86)\bS\x10,z\xab\xc4\b.\x04\xc1\xa2sK\x10\"\xba!\xa2\x1bUAT\x05!\xa2\x1b\"\xba!\xa2\x1b\"\xba!\xa2\x1b\"\xba!\xa2\x1b\"\xba!\xa2\x1b\"\xba!\xa2\x1b\"\xba!\xa2\x1b\"\xba!\xa2\x1b\"\xba1)\b\x89\x04\x86D\x02C\"\x81\xc1\xa2\xb7JU\x15۴41\xa6kƙ\"\xbd)қ:\x1e\x94&\f]\x9b2X\x95bB\n\xe63\xc5K\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfe\xa6\xe8o\x8a\xfef)\xb9\x96\xa5Uz\x85\xab\xc6u]\xd2uY\xd7z\xa9\xab\xf4RW\x99\xba\xae\xeazB׃\xf9V\xe9zJ\xd7Ӻ\x9e\xd1\xf5lROi\xde)\xcd;\xa5y\xa74\xef\x94\xe6\x9dҼS\x9awJ\xf3Ni\xde)\xcd;\xa5y\xa74\xef\x94\xe6\x9dҼS\x9aW\a\xcdҴ\xe6\x9dּӚwZ\xf3Nk\xdei\xcd;\xady\xa75\xef\xb4\xe6\x9dּӚwZ\xf3Nk\xdeiͫckI\xc7\xd6Ҍ\xe6\x9dѼ3\x9aWGؒ\x8e\xb0\xa5\x19\xcd;\xa3yg4\xef\x8c\xe6\x9dѼ3\x9awF\xf3\xceh\xdeY\xcd;\xabyg5\xef\xac\xe6\x9dռ\xb3\x9awV\xf3ΊS&5\xe9\xac&\x9dդ\xb3\x9atV\x93\xcej\xd2\xd9\xd9\xff\x01\xba\t\a\x8c\x00\x00\x00"), } fileg := &embedded.EmbeddedFile{ Filename: "index.html", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("\n\n\n \n Vitess\n \n\n \n \n \n \n\n \n \n\n \n \n\n \n\n\n Loading...\n\n\n"), } fileh := &embedded.EmbeddedFile{ Filename: "inline.js", - FileModTime: time.Unix(1576651902, 0), + FileModTime: time.Unix(1577899068, 0), Content: string("!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(82))},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(217),o=n(41),s=n(215),a=n(897),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(15);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},[1104,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(41),a=n(899),l=n(215),c=n(517),u=n(317),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(81),i=n(514),o=n(217),s=n(42),a=n(38),l=n(513),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(1082);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(86);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(115));var i=n(162);t.forwardRef=i.forwardRef,t.resolveForwardRef=i.resolveForwardRef;var o=n(163);t.Injector=o.Injector;var s=n(571);t.ReflectiveInjector=s.ReflectiveInjector;var a=n(250);t.Binding=a.Binding,t.ProviderBuilder=a.ProviderBuilder,t.bind=a.bind,t.Provider=a.Provider,t.provide=a.provide;var l=n(253);t.ResolvedReflectiveFactory=l.ResolvedReflectiveFactory;var c=n(252);t.ReflectiveKey=c.ReflectiveKey;var u=n(251);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(374);t.OpaqueToken=p.OpaqueToken},[1104,32],function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){\"use strict\";var r=n(13);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(75);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(1100)(e),n(82))},function(e,t,n){\"use strict\";var r=n(0);t.NG_VALUE_ACCESSOR=new r.OpaqueToken(\"NgValueAccessor\")},52,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(313),o=n(47),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(123),o=n(72),s=n(16),a=n(126),l=n(278);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(24),o=n(13);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){var r=n(133),i=n(75);e.exports=function(e){return r(i(e))}},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(313),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(83),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\";/**\n * @license\n * Copyright Google Inc. All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nvar 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 g=(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=g,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(106),i=n(133),o=n(50),s=n(44),a=n(676);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,g,y=o(t),v=i(y),b=r(a,f,3),_=s(v.length),w=0,S=n?h(t,_):l?h(t,0):void 0;_>w;w++)if((d||w in v)&&(m=v[w],g=b(m,w,y),e))if(n)S[w]=g;else if(g)switch(e){case 3:return!0;case 5:return m;case 6:return w;case 2:S.push(m)}else if(u)return!1;return p?-1:c||u?u:S}}},function(e,t,n){var r=n(30),i=n(109);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(472),i=n(2),o=n(199)(\"metadata\"),s=o.store||(o.store=new(n(798))),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(48),i=n(50),o=n(300)(\"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,n){\"use strict\";var r=n(347),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(\"{{\",\"}}\")},[1107,263],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(47),i=n(88),o=n(32),s=n(54),a=n(169),l=n(170),c=n(587),u=n(266),p=n(172),d=n(173),h=n(174);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},[1106,418,419,419],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(74);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(304),n(497);var r=n(1),i=n(211),o=n(141),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){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(137)(\"meta\"),i=n(15),o=n(48),s=n(30).f,a=0,l=Object.isExtensible||function(){return!0},c=!n(13)(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(197),i=n(109),o=n(57),s=n(97),a=n(48),l=n(453),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){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,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(308),s=n(80),a=n(98),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;o0?\" { \"+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(207),o=n(63),s=n(73),a=n(74),l=n(281),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,n){\"use strict\";var r=n(43),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 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(192);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;r1?\"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(83),o=n(7),s=n(58),a=n(144),l=n(145),c=n(526),u=n(227),p=n(146),d=n(147),h=n(228);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:\"\u2003\",ensp:\"\u2002\",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:\"\u200e\",lsaquo:\"‹\",lsquo:\"‘\",lt:\"<\",macr:\"¯\",mdash:\"—\",micro:\"µ\",middot:\"·\",minus:\"−\",Mu:\"Μ\",mu:\"μ\",nabla:\"∇\",nbsp:\"\u00a0\",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:\"\u200f\",rsaquo:\"›\",rsquo:\"’\",sbquo:\"‚\",Scaron:\"Š\",scaron:\"š\",sdot:\"⋅\",sect:\"§\",shy:\"\u00ad\",Sigma:\"Σ\",sigma:\"σ\",sigmaf:\"ς\",sim:\"∼\",spades:\"♠\",sub:\"⊂\",sube:\"⊆\",sum:\"∑\",sup:\"⊃\",sup1:\"¹\",sup2:\"²\",sup3:\"³\",supe:\"⊇\",szlig:\"ß\",Tau:\"Τ\",tau:\"τ\",there4:\"∴\",Theta:\"Θ\",theta:\"θ\",thetasym:\"ϑ\",thinsp:\"\u2009\",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:\"\u200d\",zwnj:\"\u200c\"}},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(17),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 g=function(){function InjectMethodVars(){}return InjectMethodVars.token=l.variable(\"token\"),InjectMethodVars.requestNodeIndex=l.variable(\"requestNodeIndex\"),InjectMethodVars.notFoundResult=l.variable(\"notFoundResult\"),InjectMethodVars}();t.InjectMethodVars=g;var y=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=y},99,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(462),o=n(288),s=n(300)(\"IE_PROTO\"),a=function(){},l=\"prototype\",c=function(){var e,t=n(451)(\"iframe\"),r=o.length,i=\"<\",s=\">\";for(t.style.display=\"none\",n(452).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(464),i=n(288);e.exports=Object.keys||function(e){return r(e,i)}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},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(502);t.multicast=multicast},[1107,219],function(e,t){\"use strict\";var n=function(){function ElementSchemaRegistry(){}return ElementSchemaRegistry}();t.ElementSchemaRegistry=n},function(e,t,n){\"use strict\";function getPropertyInView(e,t,n){if(t===n)return e;for(var o=s.THIS_EXPR,a=t;a!==n&&i.isPresent(a.declarationElement.view);)a=a.declarationElement.view,o=o.prop(\"parent\");if(a!==n)throw new r.BaseException(\"Internal error: Could not calculate a property in a parent view: \"+e);if(e instanceof s.ReadPropExpr){var l=e;(n.fields.some(function(e){return e.name==l.name})||n.getters.some(function(e){return e.name==l.name}))&&(o=o.cast(n.classType))}return s.replaceVarInExpression(s.THIS_EXPR.name,o,e)}function injectFromViewParentInjector(e,t){var n=[a.createDiTokenExpression(e)];return t&&n.push(s.NULL_EXPR),s.THIS_EXPR.prop(\"parentInjector\").callMethod(\"get\",n)}function getViewFactoryName(e,t){return\"viewFactory_\"+e.type.name+t}function createFlatArray(e){for(var t=[],n=s.literalArr([]),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(16),a=n(62),l=n(55),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(16),a=n(62),l=n(55),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===g.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(140),n(499),n(305),n(500),n(493);var r=n(0),i=n(20),o=n(309),s=n(141),a=n(623),l=n(624),c=n(625),u=n(626),p=n(627),d=n(628),h=n(187),f=n(129),m=n(91),g=n(63),y=n(73),v=n(74),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 w=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=w;var S=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=S;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=y.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 y.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 y.UrlTree)return y.containsTree(this.currentUrlTree,e,t);var n=this.urlSerializer.parse(e);return y.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 w(n,this.serializeUrl(e))),Promise.resolve(!1)):new Promise(function(i,o){var l,u,h,f,m=r.currentRouterState,g=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 w(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=g,r.routerEvents.next(new S(n,r.serializeUrl(e),t)),o(t)})})},Router}();t.Router=E;var R=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}(),T=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 R)return v.andObservables(o.from([e.runCanActivate(t.route),e.runCanActivateChild(t.path)]));if(t instanceof T){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 R?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]}),v.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?(v.shallowEqual(i.params,o.params)||this.checks.push(new T(s.component,o),new R(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 R(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 T(t.component,e)))},PreActivation.prototype.deactivateOutletMap=function(e){var t=this;v.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?v.wrapIntoObservable(r.canActivate(e,t.future)):v.wrapIntoObservable(r(e,t.future))});return v.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 v.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?v.wrapIntoObservable(r.canActivateChild(n,t.future)):v.wrapIntoObservable(r(n,t.future))});return v.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?v.wrapIntoObservable(i.canDeactivate(e,t,n.curr)):v.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=v.merge(e.data,t.flattenedResolvedData),null})},PreActivation.prototype.resolveNode=function(e,t){var n=this;return v.waitForMap(e,function(e,r){var i=n.getToken(r,t,n.future);return i.resolve?v.wrapIntoObservable(i.resolve(t,n.future)):v.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]}),v.forEach(i,function(e,t){return r.deactivateOutletAndItChildren(n._outlets[t])})},ActivateRoutes.prototype.activateRoutes=function(e,t,n){\nvar 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;v.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){\"use strict\";var r=n(43),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(43),i=(n.n(r),n(0)),o=(n.n(i),n(484)),s=(n.n(o),n(283)),a=n(644),l=n(284);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;r=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(505);r.Observable.prototype.map=i.map},function(e,t,n){\"use strict\";var r=n(79);t.of=r.ArrayObservable.of},function(e,t,n){\"use strict\";var r=n(51),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=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(233),o=n(5),s=n(68),a=n(236),l=n(151),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(85),l=n(68),c=n(546),u=n(102),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 w.CssSelector,r=y.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=g.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 y=s.removeIdentifierDuplicates(n),v=s.removeIdentifierDuplicates(r),_=new b.ProviderViewContext(e,c.rootNodes[0].sourceSpan),w=new j(_,y,v,i,this._exprParser,this._schemaRegistry);l=h.visitAll(w,c.rootNodes,z),u.push.apply(u,w.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,v.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 w.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=w.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=v.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,v.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=R.preparseElement(e);if(i.type===R.PreparsedElementType.SCRIPT||i.type===R.PreparsedElementType.STYLE)return null;if(i.type===R.PreparsedElementType.STYLESHEET&&S.isStyleUrlResolvable(i.hrefAttr))return null;var o=[],s=[],a=[],l=[],c=[],u=[],d=[],f=[],m=[],g=!1,v=[],_=y.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&&g&&n._reportError(\"Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with *\",e.sourceSpan),t||r||(v.push(n.visitAttribute(e,null)),o.push([e.name,e.value])),r&&(g=!0)});var T=createElementCssSelector(r,o),x=this._parseDirectives(this.selectorMatcher,T),M=[],k=this._createDirectiveAsts(C,e.name,x,s,a,e.sourceSpan,M),I=this._createElementPropertyAsts(e.name,s,k).concat(c),A=t.isTemplateElement||g,O=new b.ProviderElementContext(this.providerViewContext,t.providerContext,A,k,v,M,e.sourceSpan),D=h.visitAll(i.nonBindable?G:this,e.children,U.create(C,k,C?t.providerContext:O));O.afterElement();var N,V=p.isPresent(i.projectAs)?w.CssSelector.parse(i.projectAs)[0]:T,L=t.findNgContentIndex(V);if(i.type===R.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++),g?null:L,e.sourceSpan);else if(C)this._assertAllEventsPublishedByDirectives(k,u),this._assertNoComponentsNorElementBindingsOnTemplate(k,I,e.sourceSpan),N=new E.EmbeddedTemplateAst(v,u,M,l,O.transformedDirectiveAsts,O.transformProviders,O.transformedHasViewContainer,D,g?null:L,e.sourceSpan);else{this._assertOnlyOneComponent(k,e.sourceSpan);var F=g?null:t.findNgContentIndex(V);N=new E.ElementAst(r,v,I,u,M,O.transformedDirectiveAsts,O.transformProviders,O.transformedHasViewContainer,D,g?null:F,e.sourceSpan)}if(g){var j=createElementCssSelector(P,f),B=this._parseDirectives(this.selectorMatcher,j),W=this._createDirectiveAsts(!0,e.name,B,d,[],e.sourceSpan,[]),H=this._createElementPropertyAsts(e.name,d,W);this._assertNoComponentsNorElementBindingsOnTemplate(W,H,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,v.ParseErrorLevel.WARNING),this._parseVariable(h,c,t.sourceSpan,a)):(this._reportError('\"var-\" on non
0.0010.0011e-08PASS_SELECTSelectselect name from test_table limit 10001none0.020.0011e-08PASS_SELECTSelectselect name from test_table limit 10001none0.50.0011e-08PASS_SELECTSelectselect name from test_table limit 10001none
select name from test_tabletest_tablePASS_SELECTTABLESelect102.0000001.000000insert into test_table values 1test_tableDDLDEFAULT10.0020000.001000
show tablesOTHER_READDEFAULTOtherRead10.0750000.050000
insert into test_table values .* \[TRUNCATED\][^<]*OTHER_READDEFAULTOtherRead10.0010000.001000